Commit 205fbfe0 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] DVB drivers AV7110 (Fujitsu, Nova etc)

parent f910e6f2
CONFIG_DVB_AV7110
Support for SAA7146 and AV7110 based DVB cards as produced
by Fujitsu-Siemens, Technotrend, Hauppauge and others.
Simple cards like so called Budget- or Nova-PCI cards are
supported as well as fullfeatured cards with onboard MPEG2
decoder.
Say Y if you own such a card and want to use it.
CONFIG_DVB_AV7110_OSD
The AV7110 firmware provides some code to generate an OnScreenDisplay
on the video output. This is kind of nonstandard and not guaranteed to
be maintained.
Anyway, some popular DVB software like VDR uses this OSD to render
its menus, so say Y if you want to use this software.
All other people say N.
dep_tristate ' SAA7146 based AV7110 and Nova/budget cards' CONFIG_DVB_AV7110 $CONFIG_VIDEO_DEV $CONFIG_DVB_CORE
if [ "$CONFIG_DVB_AV7110" != "n" ]; then
bool ' AV7110 OSD support' CONFIG_DVB_AV7110_OSD
fi
#
# Makefile for the kernel AV7110 DVB device driver
#
dvb-ttpci-objs := saa7146_core.o saa7146_v4l.o av7110.o av7110_ir.o
obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
include $(TOPDIR)/Rules.make
This source diff could not be displayed because it is too large. You can view the blob instead.
#ifndef _AV7110_H_
#define _AV7110_H_
#define DVB_FIRM_PATH "/lib/DVB/"
#include <linux/interrupt.h>
#include <linux/videodev.h>
#include <linux/socket.h>
#include <linux/netdevice.h>
#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
#endif
/* DEBI transfer mode defs */
#define DEBINOSWAP 0x000e0000
#define DEBISWAB 0x001e0000
#define DEBISWAP 0x002e0000
#define ARM_WAIT_FREE (HZ)
#define ARM_WAIT_SHAKE (HZ/5)
#define ARM_WAIT_OSD (HZ)
#if LINUX_VERSION_CODE < 0x020300
#define net_device device
#define DECLARE_MUTEX(foo) struct semaphore foo = MUTEX
#define DECLARE_MUTEX_LOCKED(foo) struct semaphore foo = MUTEX_LOCKED
#define WAIT_QUEUE struct wait_queue*
#define init_waitqueue_head(wq) *(wq) = NULL;
#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL }
#define set_current_state(state_value) \
do { current->state = state_value; } while (0)
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#else
#define WAIT_QUEUE wait_queue_head_t
#endif
#include <linux/dvb/video.h>
#include <linux/dvb/audio.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/ca.h>
#include <linux/dvb/osd.h>
#include <linux/dvb/net.h>
#include "../dvb-core/dvbdev.h"
#include "../dvb-core/demux.h"
#include "../dvb-core/dvb_demux.h"
#include "../dvb-core/dmxdev.h"
#include "../dvb-core/dvb_filter.h"
#include "../dvb-core/dvb_net.h"
typedef enum BOOTSTATES
{
BOOTSTATE_BUFFER_EMPTY = 0,
BOOTSTATE_BUFFER_FULL = 1,
BOOTSTATE_BOOT_COMPLETE = 2
} BOOTSTATES;
typedef enum GPIO_MODE
{
GPIO_INPUT = 0x00,
GPIO_IRQHI = 0x10,
GPIO_IRQLO = 0x20,
GPIO_IRQHL = 0x30,
GPIO_OUTLO = 0x40,
GPIO_OUTHI = 0x50
} GPIO_MODE;
typedef enum
{ RP_None,
AudioPES,
AudioMp2,
AudioPCM,
VideoPES,
AV_PES
} TYPE_REC_PLAY_FORMAT;
typedef struct PARAMSTRUCT
{
unsigned int wCommand;
int error;
unsigned long pdwData[100];
} PARAMSTRUCT, *PPARAMSTRUCT;
typedef enum OSDPALTYPE
{
NoPalet = 0, /* No palette */
Pal1Bit = 2, /* 2 colors for 1 Bit Palette */
Pal2Bit = 4, /* 4 colors for 2 bit palette */
Pal4Bit = 16, /* 16 colors for 4 bit palette */
Pal8Bit = 256 /* 256 colors for 16 bit palette */
} OSDPALTYPE, *POSDPALTYPE;
typedef enum {
BITMAP1, /* 1 bit bitmap */
BITMAP2, /* 2 bit bitmap */
BITMAP4, /* 4 bit bitmap */
BITMAP8, /* 8 bit bitmap */
BITMAP1HR, /* 1 Bit bitmap half resolution */
BITMAP2HR, /* 2 bit bitmap half resolution */
BITMAP4HR, /* 4 bit bitmap half resolution */
BITMAP8HR, /* 8 bit bitmap half resolution */
YCRCB422, /* 4:2:2 YCRCB Graphic Display */
YCRCB444, /* 4:4:4 YCRCB Graphic Display */
YCRCB444HR, /* 4:4:4 YCRCB graphic half resolution */
VIDEOTSIZE, /* True Size Normal MPEG Video Display */
VIDEOHSIZE, /* MPEG Video Display Half Resolution */
VIDEOQSIZE, /* MPEG Video Display Quarter Resolution */
VIDEODSIZE, /* MPEG Video Display Double Resolution */
VIDEOTHSIZE, /* True Size MPEG Video Display Half Resolution */
VIDEOTQSIZE, /* True Size MPEG Video Display Quarter Resolution*/
VIDEOTDSIZE, /* True Size MPEG Video Display Double Resolution */
VIDEONSIZE, /* Full Size MPEG Video Display */
CURSOR /* Cursor */
} DISPTYPE; /* Window display type */
// switch defines
#define SB_GPIO 3
#define SB_OFF GPIO_OUTLO //SlowBlank aus (TV-Mode)
#define SB_ON GPIO_INPUT //SlowBlank an (AV-Mode)
#define SB_WIDE GPIO_OUTHI //SlowBlank 6V (16/9-Mode) nicht realisiert
#define FB_GPIO 1
#define FB_OFF GPIO_LO //FastBlank aus (CVBS-Mode)
#define FB_ON GPIO_OUTHI //FastBlank an (RGB-Mode)
#define FB_LOOP GPIO_INPUT //FastBlank der PC-Grafik durchschleifen
typedef enum VIDEOOUTPUTMODE
{
NO_OUT = 0, //disable analog Output
CVBS_RGB_OUT = 1,
CVBS_YC_OUT = 2,
YC_OUT = 3
} VIDEOOUTPUTMODE, *PVIDEOOUTPUTMODE;
#define GPMQFull 0x0001 //Main Message Queue Full
#define GPMQOver 0x0002 //Main Message Queue Overflow
#define HPQFull 0x0004 //High Priority Msg Queue Full
#define HPQOver 0x0008
#define OSDQFull 0x0010 //OSD Queue Full
#define OSDQOver 0x0020
#define SECTION_EIT 0x01
#define SECTION_SINGLE 0x00
#define SECTION_CYCLE 0x02
#define SECTION_CONTINUOS 0x04
#define SECTION_MODE 0x06
#define SECTION_IPMPE 0x0C // bis zu 4k gro
#define SECTION_HIGH_SPEED 0x1C // vergrerter Puffer fr High Speed Filter
#define DATA_PIPING_FLAG 0x20 // fr Data Piping Filter
#define PBUFSIZE_NONE 0x0000
#define PBUFSIZE_1P 0x0100
#define PBUFSIZE_2P 0x0200
#define PBUFSIZE_1K 0x0300
#define PBUFSIZE_2K 0x0400
#define PBUFSIZE_4K 0x0500
#define PBUFSIZE_8K 0x0600
#define PBUFSIZE_16K 0x0700
#define PBUFSIZE_32K 0x0800
typedef enum {
WCreate,
WDestroy,
WMoveD,
WMoveA,
WHide,
WTop,
DBox,
DLine,
DText,
Set_Font,
SetColor,
SetBlend,
SetWBlend,
SetCBlend,
SetNonBlend,
LoadBmp,
BlitBmp,
ReleaseBmp,
SetWTrans,
SetWNoTrans
} OSDCOM;
typedef enum {
MultiPID,
VideoPID,
AudioPID,
InitFilt,
FiltError,
NewVersion,
CacheError,
AddPIDFilter,
DelPIDFilter,
Scan,
SetDescr,
SetIR
} PIDCOM;
typedef enum {
SelAudChannels
} MPEGCOM;
typedef enum {
AudioDAC,
CabADAC,
ON22K,
OFF22K,
MainSwitch,
ADSwitch,
SendDiSEqC,
SetRegister
} AUDCOM;
typedef enum {
AudioState,
AudioBuffState,
VideoState1,
VideoState2,
VideoState3,
CrashCounter,
ReqVersion,
ReqVCXO,
ReqRegister
} REQCOM;
typedef enum {
SetVidMode,
SetTestMode,
LoadVidCode,
SetMonitorType,
SetPanScanType,
SetFreezeMode
} ENC;
typedef enum {
__Record,
__Stop,
__Play,
__Pause,
__Slow,
__FF_IP,
__Scan_I,
__Continue
} REC_PLAY;
typedef enum {
COMTYPE_NOCOM,
COMTYPE_PIDFILTER,
COMTYPE_MPEGDECODER,
COMTYPE_OSD,
COMTYPE_BMP,
COMTYPE_ENCODER,
COMTYPE_AUDIODAC,
COMTYPE_REQUEST,
COMTYPE_SYSTEM,
COMTYPE_REC_PLAY,
COMTYPE_COMMON_IF,
COMTYPE_PID_FILTER,
COMTYPE_PES,
COMTYPE_TS,
COMTYPE_VIDEO,
COMTYPE_AUDIO,
COMTYPE_CI_LL,
} COMTYPE;
typedef enum {
AV7110_VIDEO_FREEZE,
AV7110_VIDEO_CONTINUE
} VIDEOCOM;
typedef enum {
DVB_AUDIO_PAUSE,
} AUDIOCOM;
#define VID_NONE_PREF 0x00 /* No aspect ration processing preferred */
#define VID_PAN_SCAN_PREF 0x01 /* Pan and Scan Display preferred */
#define VID_VERT_COMP_PREF 0x02 /* Vertical compression display preferred */
#define VID_VC_AND_PS_PREF 0x03 /* PanScan and vertical Compression if allowed */
#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */
#define DATA_NONE 0x00
#define DATA_FSECTION 0x01
#define DATA_IPMPE 0x02
#define DATA_MPEG_RECORD 0x03
#define DATA_DEBUG_MESSAGE 0x04
#define DATA_COMMON_INTERFACE 0x05
#define DATA_MPEG_PLAY 0x06
#define DATA_BMP_LOAD 0x07
#define DATA_IRCOMMAND 0x08
#define DATA_PIPING 0x09
#define DATA_STREAMING 0x0a
#define DATA_CI_GET 0x0b
#define DATA_CI_PUT 0x0c
#define DATA_PES_RECORD 0x10
#define DATA_PES_PLAY 0x11
#define DATA_TS_RECORD 0x12
#define DATA_TS_PLAY 0x13
#define CI_CMD_ERROR 0x00
#define CI_CMD_ACK 0x01
#define CI_CMD_SYSTEM_READY 0x02
#define CI_CMD_KEYPRESS 0x03
#define CI_CMD_ON_TUNED 0x04
#define CI_CMD_ON_SWITCH_PROGRAM 0x05
#define CI_CMD_SECTION_ARRIVED 0x06
#define CI_CMD_SECTION_TIMEOUT 0x07
#define CI_CMD_TIME 0x08
#define CI_CMD_ENTER_MENU 0x09
#define CI_CMD_FAST_PSI 0x0a
#define CI_CMD_GET_SLOT_INFO 0x0b
#define CI_MSG_NONE 0x00
#define CI_MSG_CI_INFO 0x01
#define CI_MSG_MENU 0x02
#define CI_MSG_LIST 0x03
#define CI_MSG_TEXT 0x04
#define CI_MSG_REQUEST_INPUT 0x05
#define CI_MSG_INPUT_COMPLETE 0x06
#define CI_MSG_LIST_MORE 0x07
#define CI_MSG_MENU_MORE 0x08
#define CI_MSG_CLOSE_MMI_IMM 0x09
#define CI_MSG_SECTION_REQUEST 0x0a
#define CI_MSG_CLOSE_FILTER 0x0b
#define CI_PSI_COMPLETE 0x0c
#define CI_MODULE_READY 0x0d
#define CI_SWITCH_PRG_REPLY 0x0e
#define CI_MSG_TEXT_MORE 0x0f
#define CI_MSG_CA_PMT 0xe0
#define CI_MSG_ERROR 0xf0
typedef struct ring_buffer_s {
u8 *data;
int size;
int pread;
int pwrite;
WAIT_QUEUE queue;
spinlock_t lock;
struct semaphore sema;
int error;
} ring_buffer_t;
#define PROG_STREAM_MAP 0xBC
#define PRIVATE_STREAM1 0xBD
#define PADDING_STREAM 0xBE
#define PRIVATE_STREAM2 0xBF
#define AUDIO_STREAM_S 0xC0
#define AUDIO_STREAM_E 0xDF
#define VIDEO_STREAM_S 0xE0
#define VIDEO_STREAM_E 0xEF
#define ECM_STREAM 0xF0
#define EMM_STREAM 0xF1
#define DSM_CC_STREAM 0xF2
#define ISO13522_STREAM 0xF3
#define PROG_STREAM_DIR 0xFF
#define PTS_DTS_FLAGS 0xC0
//pts_dts flags
#define PTS_ONLY 0x80
#define PTS_DTS 0xC0
#define TS_SIZE 188
#define TRANS_ERROR 0x80
#define PAY_START 0x40
#define TRANS_PRIO 0x20
#define PID_MASK_HI 0x1F
//flags
#define TRANS_SCRMBL1 0x80
#define TRANS_SCRMBL2 0x40
#define ADAPT_FIELD 0x20
#define PAYLOAD 0x10
#define COUNT_MASK 0x0F
// adaptation flags
#define DISCON_IND 0x80
#define RAND_ACC_IND 0x40
#define ES_PRI_IND 0x20
#define PCR_FLAG 0x10
#define OPCR_FLAG 0x08
#define SPLICE_FLAG 0x04
#define TRANS_PRIV 0x02
#define ADAP_EXT_FLAG 0x01
// adaptation extension flags
#define LTW_FLAG 0x80
#define PIECE_RATE 0x40
#define SEAM_SPLICE 0x20
#define MAX_PLENGTH 0xFFFF
#define MAX_VID_PES 0x1FFF
typedef struct section_s {
int id;
int length;
int found;
u8 payload[4096+3];
} section_t;
#define MY_STATE_PES_START 1
#define MY_STATE_PES_STARTED 2
#define MY_STATE_FULL 4
#define MASKL DMX_MAX_FILTER_SIZE
#define MAXFILT 32
struct dvb_filter {
int state;
int flags;
int type;
u8 ts_state;
u16 pid;
u8 value[MASKL];
u8 mask[MASKL];
};
enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM};
typedef struct ps_packet_s{
u8 scr[6];
u8 mux_rate[3];
u8 stuff_length;
u8 data[20];
u8 sheader_llength[2];
int sheader_length;
u8 rate_bound[3];
u8 audio_bound;
u8 video_bound;
u8 reserved;
int npes;
int mpeg;
} ps_packet_t;
typedef struct a2p_s{
int type;
int found;
int length;
int headr;
u8 cid;
u8 flags;
u8 abuf[MAX_PLENGTH];
int alength;
u8 vbuf[MAX_PLENGTH];
int vlength;
int plength;
u8 last_av_pts[4];
u8 av_pts[4];
u8 scr[4];
u16 count0;
u16 count1;
u16 pidv;
u16 pida;
u16 countv;
u16 counta;
void *dataA;
void *dataV;
void (*write_cb)(u8 const *buf, long int count,
void *data);
} a2p_t;
typedef struct p2t_s {
u8 pes[TS_SIZE];
u8 counter;
long int pos;
int frags;
dvb_demux_feed_t *feed;
} p2t_t;
/* place to store all the necessary device information */
typedef struct av7110_s {
/* devices */
struct dvb_device dvb_dev;
dvb_net_t dvb_net;
struct video_device video;
struct saa7146 *saa;
struct tasklet_struct debi_tasklet;
struct tasklet_struct gpio_tasklet;
struct tasklet_struct vpe_tasklet;
struct tasklet_struct fidb_tasklet;
int adac_type; /* audio DAC type */
#define DVB_ADAC_TI 0
#define DVB_ADAC_CRYSTAL 1
#define DVB_ADAC_NONE -1
/* buffers */
void *iobuf; /* memory for all buffers */
ring_buffer_t avout; /* buffer for video or A/V mux */
#define AVOUTLEN (128*1024)
ring_buffer_t aout; /* buffer for audio */
#define AOUTLEN (64*1024)
void *bmpbuf;
#define BMPLEN (8*32768+1024)
/* bitmap buffers and states */
int bmpp;
int bmplen;
int bmp_win;
u16 bmp_x, bmp_y;
int bmp_trans;
int bmp_state;
#define BMP_NONE 0
#define BMP_LOADING 1
#define BMP_LOADINGS 2
#define BMP_LOADED 3
WAIT_QUEUE bmpq;
/* DEBI and polled command interface */
spinlock_t debilock;
struct semaphore dcomlock;
int debitype;
int debilen;
int debibuf;
/* Recording and playback flags */
int rec_mode;
int playing;
#define RP_NONE 0
#define RP_VIDEO 1
#define RP_AUDIO 2
#define RP_AV 3
/* OSD */
int osdwin; /* currently active window */
u16 osdbpp[8];
/* CA */
ca_slot_info_t ci_slot[2];
int vidmode;
dmxdev_t dmxdev;
dvb_demux_t demux;
char demux_id[16];
dmx_frontend_t hw_frontend;
dmx_frontend_t mem_frontend;
int fe_synced;
struct semaphore pid_mutex;
int video_blank;
struct video_status videostate;
int display_ar;
int trickmode;
#define TRICK_NONE 0
#define TRICK_FAST 1
#define TRICK_SLOW 2
#define TRICK_FREEZE 3
struct audio_status audiostate;
dvb_demux_filter_t *handle2filter[32];
p2t_t p2t_filter[MAXFILT];
pes2ts_t p2t[2];
struct ipack_s ipack[2];
u8 *kbuf[2];
int sinfo;
int shsize;
int swsize;
int tsf;
u32 ttbp;
int feeding;
int arm_errors;
int registered;
/* AV711X */
u32 arm_fw;
u32 arm_rtsl;
u32 arm_vid;
u32 arm_app;
u32 avtype;
int arm_ready;
struct task_struct *arm_thread;
WAIT_QUEUE arm_wait;
u16 arm_loops;
int arm_rmmod;
void *saa_mem;
void *debi_virt;
dma_addr_t debi_bus;
u16 pids[DMX_PES_OTHER];
ring_buffer_t ci_rbuffer;
ring_buffer_t ci_wbuffer;
dvb_adapter_t *dvb_adapter;
dvb_device_t *video_dev;
dvb_device_t *audio_dev;
dvb_device_t *ca_dev;
dvb_device_t *osd_dev;
int dsp_dev;
} av7110_t;
#define DPRAM_BASE 0x4000
#define BOOT_STATE (DPRAM_BASE + 0x3F8)
#define BOOT_SIZE (DPRAM_BASE + 0x3FA)
#define BOOT_BASE (DPRAM_BASE + 0x3FC)
#define BOOT_BLOCK (DPRAM_BASE + 0x400)
#define BOOT_MAX_SIZE 0xc00
#define IRQ_STATE (DPRAM_BASE + 0x0F4)
#define IRQ_STATE_EXT (DPRAM_BASE + 0x0F6)
#define MSGSTATE (DPRAM_BASE + 0x0F8)
#define FILT_STATE (DPRAM_BASE + 0x0FA)
#define COMMAND (DPRAM_BASE + 0x0FC)
#define COM_BUFF (DPRAM_BASE + 0x100)
#define COM_BUFF_SIZE 0x20
#define BUFF1_BASE (DPRAM_BASE + 0x120)
#define BUFF1_SIZE 0xE0
#define DATA_BUFF_BASE (DPRAM_BASE + 0x200)
#define DATA_BUFF_SIZE 0x1C00
/* new buffers */
#define DATA_BUFF0_BASE (DPRAM_BASE + 0x200)
#define DATA_BUFF0_SIZE 0x0800
#define DATA_BUFF1_BASE (DATA_BUFF0_BASE+DATA_BUFF0_SIZE)
#define DATA_BUFF1_SIZE 0x0800
#define DATA_BUFF2_BASE (DATA_BUFF1_BASE+DATA_BUFF1_SIZE)
#define DATA_BUFF2_SIZE 0x0800
#define Reserved (DPRAM_BASE + 0x1E00)
#define Reserved_SIZE 0x1C0
#define DEBUG_WINDOW (DPRAM_BASE + 0x1FC0)
#define DBG_LOOP_CNT (DEBUG_WINDOW + 0x00)
#define DBG_SEC_CNT (DEBUG_WINDOW + 0x02)
#define DBG_AVRP_BUFF (DEBUG_WINDOW + 0x04)
#define DBG_AVRP_PEAK (DEBUG_WINDOW + 0x06)
#define DBG_MSG_CNT (DEBUG_WINDOW + 0x08)
#define DBG_CODE_REG (DEBUG_WINDOW + 0x0a)
#define DBG_TTX_Q (DEBUG_WINDOW + 0x0c)
#define DBG_AUD_EN (DEBUG_WINDOW + 0x0e)
#define DBG_WRONG_COM (DEBUG_WINDOW + 0x10)
#define DBG_ARR_OVFL (DEBUG_WINDOW + 0x12)
#define DBG_BUFF_OVFL (DEBUG_WINDOW + 0x14)
#define DBG_OVFL_CNT (DEBUG_WINDOW + 0x16)
#define DBG_SEC_OVFL (DEBUG_WINDOW + 0x18)
#define STATUS_BASE (DPRAM_BASE + 0x1FC0)
#define STATUS_SCR (STATUS_BASE + 0x00)
#define STATUS_MODES (STATUS_BASE + 0x04)
#define STATUS_LOOPS (STATUS_BASE + 0x08)
#define RX_TYPE (DPRAM_BASE + 0x1FE8)
#define RX_LEN (DPRAM_BASE + 0x1FEA)
#define TX_TYPE (DPRAM_BASE + 0x1FEC)
#define TX_LEN (DPRAM_BASE + 0x1FEE)
#define RX_BUFF (DPRAM_BASE + 0x1FF4)
#define TX_BUFF (DPRAM_BASE + 0x1FF6)
#define HANDSHAKE_REG (DPRAM_BASE + 0x1FF8)
#define COM_IF_LOCK (DPRAM_BASE + 0x1FFA)
#define IRQ_RX (DPRAM_BASE + 0x1FFC)
#define IRQ_TX (DPRAM_BASE + 0x1FFE)
#define DRAM_START_CODE 0x2e000404
#define DRAM_MAX_CODE_SIZE 0x00100000
#define RESET_LINE 2
#define DEBI_DONE_LINE 1
#define ARM_IRQ_LINE 0
#define DAC_CS 0x8000
#define DAC_CDS 0x0000
extern unsigned char *av7110_dpram_addr, *av7110_root_addr;
extern int av7110_dpram_len, av7110_root_len;
extern void av7110_register_irc_handler(void (*func)(u32));
extern void av7110_unregister_irc_handler(void (*func)(u32));
extern void av7110_setup_irc_config (av7110_t *av7110, u32 ir_config);
extern int av7110_init (void);
extern int av7110_ir_init (void);
extern void av7110_exit (void);
extern void av7110_ir_exit (void);
#endif /* _AV7110_H_ */
/*
* Local variables:
* c-indent-level: 8
* c-brace-imaginary-offset: 0
* c-brace-offset: -8
* c-argdecl-indent: 8
* c-label-offset: -8
* c-continued-statement-offset: 8
* c-continued-brace-offset: 0
* indent-tabs-mode: nil
* tab-width: 8
* End:
*/
This source diff could not be displayed because it is too large. You can view the blob instead.
#include <asm/types.h>
#include <asm/bitops.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/proc_fs.h>
#include "av7110.h"
#define UP_TIMEOUT (HZ/2)
static int av7110_ir_debug = 0;
#define dprintk(x...) do { if (av7110_ir_debug) printk (x); } while (0)
static struct input_dev input_dev;
static
u16 key_map [256] = {
KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7,
KEY_8, KEY_9, KEY_LAST, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO,
KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0,
0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0,
KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0,
KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW,
KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN,
0, 0, 0, 0, KEY_EPG, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_MHP
};
static
void av7110_emit_keyup (unsigned long data)
{
if (!data || !test_bit (data, input_dev.key))
return;
input_event (&input_dev, EV_KEY, data, !!0);
}
static
struct timer_list keyup_timer = { function: av7110_emit_keyup };
static
void av7110_emit_key (u32 ircom)
{
int down = ircom & (0x80000000);
u16 keycode = key_map[ircom & 0xff];
dprintk ("#########%08x######### key %02x %s (keycode %i)\n",
ircom, ircom & 0xff, down ? "pressed" : "released", keycode);
if (!keycode) {
printk ("%s: unknown key 0x%02x!!\n",
__FUNCTION__, ircom & 0xff);
return;
}
if (timer_pending (&keyup_timer)) {
del_timer (&keyup_timer);
if (keyup_timer.data != keycode)
input_event (&input_dev, EV_KEY, keyup_timer.data, !!0);
}
clear_bit (keycode, input_dev.key);
input_event (&input_dev, EV_KEY, keycode, !0);
keyup_timer.expires = jiffies + UP_TIMEOUT;
keyup_timer.data = keycode;
add_timer (&keyup_timer);
}
static
void input_register_keys (void)
{
int i;
memset (input_dev.keybit, 0, sizeof(input_dev.keybit));
for (i=0; i<sizeof(key_map)/sizeof(key_map[0]); i++) {
if (key_map[i] > KEY_MAX)
key_map[i] = 0;
else if (key_map[i] > KEY_RESERVED)
set_bit (key_map[i], input_dev.keybit);
}
}
static
int av7110_ir_write_proc (struct file *file, const char *buffer,
unsigned long count, void *data)
{
u32 ir_config;
if (count < 4 + 256 * sizeof(u16))
return -EINVAL;
memcpy (&ir_config, buffer, 4);
memcpy (&key_map, buffer + 4, 256 * sizeof(u16));
av7110_setup_irc_config (NULL, ir_config);
input_register_keys ();
return count;
}
int __init av7110_ir_init (void)
{
static struct proc_dir_entry *e;
init_timer (&keyup_timer);
keyup_timer.data = 0;
input_dev.name = "DVB on-card IR receiver";
/**
* enable keys
*/
set_bit (EV_KEY, input_dev.evbit);
input_register_keys ();
input_register_device(&input_dev);
av7110_setup_irc_config (NULL, 0x0001);
av7110_register_irc_handler (av7110_emit_key);
e = create_proc_entry ("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL);
if (e) {
e->write_proc = av7110_ir_write_proc;
e->size = 4 + 256 * sizeof(u16);
}
return 0;
}
void __exit av7110_ir_exit (void)
{
remove_proc_entry ("av7110_ir", NULL);
av7110_unregister_irc_handler (av7110_emit_key);
input_unregister_device(&input_dev);
}
//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>");
//MODULE_LICENSE("GPL");
MODULE_PARM(av7110_ir_debug,"i");
MODULE_PARM_DESC(av7110_ir_debug, "enable AV7110 IR receiver debug messages");
/*
the api- and os-independet parts of the saa7146 device driver
Copyright (C) 1998,1999 Michael Hunold <michael@mihu.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "saa7146_defs.h"
#define TRUNC(val,max) ((val) < (max) ? (val) : (max))
#ifdef __COMPILE_SAA7146__
struct saa7146_modes_constants
modes_constants[] = {
{ V_OFFSET_PAL, V_FIELD_PAL, V_ACTIVE_LINES_PAL,
H_OFFSET_PAL, H_PIXELS_PAL, H_PIXELS_PAL+1,
V_ACTIVE_LINES_PAL, 1024 }, /* PAL values */
{ V_OFFSET_NTSC, V_FIELD_NTSC, V_ACTIVE_LINES_NTSC,
H_OFFSET_NTSC, H_PIXELS_NTSC, H_PIXELS_NTSC+1,
V_ACTIVE_LINES_NTSC, 1024 }, /* NTSC values */
{ 0,0,0,0,0,0,0,0 }, /* secam values */
{ 0,288,576,
0,188*4,188*4+1,
288,188*4 } /* TS values */
};
/* -----------------------------------------------------------------------------------------
helper functions for the calculation of the horizontal- and vertical scaling registers,
clip-format-register etc ...
these functions take pointers to the (most-likely read-out original-values) and manipulate
them according to the requested new scaling parameters.
----------------------------------------------------------------------------------------- */
/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */
struct {
u16 hps_coeff;
u16 weight_sum;
} hps_h_coeff_tab [] = {
{0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8},
{0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8},
{0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8},
{0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8},
{0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8},
{0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8},
{0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
{0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
{0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
{0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8},
{0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8},
{0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8},
{0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16}
};
/* table of attenuation values for horizontal scaling */
u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0};
int calculate_h_scale_registers(struct saa7146* saa, u32 in_x, u32 out_x, int flip_lr, u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale)
{
/* horizontal prescaler */
u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0;
/* horizontal scaler */
u32 xim = 0, xp = 0, xsci =0;
/* vertical scale & gain */
u32 pfuv = 0;
/* helper variables */
u32 h_atten = 0, i = 0;
if ( 0 == out_x ) {
printk("saa7146: ==> calculate_h_scale_registers: invalid value (=0).\n");
return -EINVAL;
}
/* mask out vanity-bit */
*hps_ctrl &= ~MASK_29;
/* calculate prescale-(xspc)-value: [n .. 1/2) : 1
[1/2 .. 1/3) : 2
[1/3 .. 1/4) : 3
... */
if (in_x > out_x) {
xpsc = in_x / out_x;
} else {
/* zooming */
xpsc = 1;
}
/* if flip_lr-bit is set, number of pixels after horizontal prescaling must be < 384 */
if ( 0 != flip_lr ) {
/* set vanity bit */
*hps_ctrl |= MASK_29;
while (in_x / xpsc >= 384 )
xpsc++;
}
/* if zooming is wanted, number of pixels after horizontal prescaling must be < 768 */
else {
while ( in_x / xpsc >= 768 )
xpsc++;
}
/* maximum prescale is 64 (p.69) */
if ( xpsc > 64 )
xpsc = 64;
/* keep xacm clear*/
xacm = 0;
/* set horizontal filter parameters (CXY = CXUV) */
cxy = hps_h_coeff_tab[TRUNC(xpsc - 1, 63)].hps_coeff;
cxuv = cxy;
/* calculate and set horizontal fine scale (xsci) */
/* bypass the horizontal scaler ? */
if ( (in_x == out_x) && ( 1 == xpsc ) )
xsci = 0x400;
else
xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc;
/* set start phase for horizontal fine scale (xp) to 0 */
xp = 0;
/* set xim, if we bypass the horizontal scaler */
if ( 0x400 == xsci )
xim = 1;
else
xim = 0;
/* if the prescaler is bypassed, enable horizontal accumulation mode (xacm)
and clear dcgx */
if( 1 == xpsc ) {
xacm = 1;
dcgx = 0;
} else {
xacm = 0;
/* get best match in the table of attenuations for horizontal scaling */
h_atten = hps_h_coeff_tab[TRUNC(xpsc - 1, 63)].weight_sum;
for (i = 0; h_attenuation[i] != 0; i++) {
if (h_attenuation[i] >= h_atten)
break;
}
dcgx = i;
}
/* the horizontal scaling increment controls the UV filter to reduce the bandwith to
improve the display quality, so set it ... */
if ( xsci == 0x400)
pfuv = 0x00;
else if ( xsci < 0x600)
pfuv = 0x01;
else if ( xsci < 0x680)
pfuv = 0x11;
else if ( xsci < 0x700)
pfuv = 0x22;
else
pfuv = 0x33;
*hps_v_gain &= MASK_W0|MASK_B2;
*hps_v_gain |= (pfuv << 24);
*hps_h_scale &= ~(MASK_W1 | 0xf000);
*hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12);
*hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0);
return 0;
}
struct {
u16 hps_coeff;
u16 weight_sum;
} hps_v_coeff_tab [] = {
{0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8},
{0x0502, 8}, {0x0708, 8}, {0x0F00, 8}, {0x011E, 16},
{0x110E, 16}, {0x1926, 16}, {0x3906, 16}, {0x3D42, 16},
{0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16}, {0x01FE, 32},
{0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32},
{0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32},
{0xF10E, 32}, {0xF906, 32}, {0xF906, 32}, {0xFD02, 32},
{0xFD02, 32}, {0xFF00, 32}, {0xFF00, 32}, {0x01FE, 64},
{0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
{0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
{0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
{0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
{0x01FE, 64}, {0x817E, 64}, {0x817E, 64}, {0xC13E, 64},
{0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64}, {0xF10E, 64},
{0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64},
{0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128}
};
/* table of attenuation values for vertical scaling */
u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0};
int calculate_v_scale_registers(struct saa7146* saa, u32 in_y, u32 out_y, u32* hps_v_scale, u32* hps_v_gain)
{
u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0; /* vertical scaling */
u32 dcgy = 0, cya_cyb = 0; /* vertical scale & gain */
u32 v_atten = 0, i = 0; /* helper variables */
/* error, if vertical zooming */
if ( in_y < out_y ) {
printk("saa7146: ==> calculate_v_scale_registers: we cannot do vertical zooming.\n");
return -EINVAL;
}
/* linear phase interpolation may be used if scaling is between 1 and 1/2
or scaling is between 1/2 and 1/4 (if interlace is set; see below) */
if( ((2*out_y) >= in_y) || (((4*out_y) >= in_y) && saa->interlace != 0)) {
/* convention: if scaling is between 1/2 and 1/4 we only use
the even lines, the odd lines get discarded (see function move_to)
if interlace is set */
if( saa->interlace != 0 && (out_y*4) >= in_y && (out_y*2) <= in_y)
out_y *= 2;
yacm = 0;
yacl = 0;
cya_cyb = 0x00ff;
/* calculate scaling increment */
if ( in_y > out_y )
ysci = ((1024 * in_y) / (out_y + 1)) - 1024;
else
ysci = 0;
dcgy = 0;
/* calculate ype and ypo */
if (saa->interlace !=0) {
/* Special case for interlaced input */
/* See Philips SAA7146A Product Spec (page 75): */
/* "For interlaced input, ype and ypo is defiend as */
/* YPeven= 3/2 x YPodd (line 1 = odd)" */
/* */
/* It looks like the spec is wrong! */
/* The ad hoc values below works fine for a target */
/* window height of 480 (vertical scale = 1/1) NTSC. */
/* PLI: December 27, 2000. */
ypo=64;
ype=0;
} else {
ype = ysci / 16;
ypo = ype + (ysci / 64);
}
}
else {
yacm = 1;
/* calculate scaling increment */
ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10;
/* calculate ype and ypo */
ypo = ype = ((ysci + 15) / 16);
/* the sequence length interval (yacl) has to be set according
to the prescale value, e.g. [n .. 1/2) : 0
[1/2 .. 1/3) : 1
[1/3 .. 1/4) : 2
... */
if ( ysci < 512) {
yacl = 0;
}
else {
yacl = ( ysci / (1024 - ysci) );
}
/* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
cya_cyb = hps_v_coeff_tab[TRUNC(yacl, 63)].hps_coeff;
/* get best match in the table of attenuations for vertical scaling */
v_atten = hps_v_coeff_tab[TRUNC(yacl, 63)].weight_sum;
for (i = 0; v_attenuation[i] != 0; i++) {
if (v_attenuation[i] >= v_atten)
break;
}
dcgy = i;
}
/* ypo and ype swapped in spec ? */
*hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1);
*hps_v_gain &= ~(MASK_W0|MASK_B2);
*hps_v_gain |= (dcgy << 16) | (cya_cyb << 0);
return 0;
}
void calculate_hxo_hyo_and_sources(struct saa7146* saa, int port_sel, int sync_sel, u32* hps_h_scale, u32* hps_ctrl)
{
u32 hyo = 0, hxo = 0;
hyo = modes_constants[saa->mode].v_offset;
hxo = modes_constants[saa->mode].h_offset;
*hps_h_scale &= ~(MASK_B0 | 0xf00);
*hps_ctrl &= ~(MASK_W0 | MASK_B2 | MASK_30 | MASK_31 | MASK_28);
*hps_h_scale |= (hxo << 0);
*hps_ctrl |= (hyo << 12);
*hps_ctrl |= ( port_sel == 0 ? 0x0 : MASK_30);
*hps_ctrl |= ( sync_sel == 0 ? 0x0 : MASK_28);
}
void calculate_output_format_register(struct saa7146* saa, u16 palette, u32* clip_format)
{
/* clear out the necessary bits */
*clip_format &= 0x0000ffff;
/* set these bits new */
*clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16));
}
void calculate_bcs_ctrl_register(struct saa7146 *saa, u32 brightness, u32 contrast, u32 colour, u32 *bcs_ctrl)
{
*bcs_ctrl = ((brightness << 24) | (contrast << 16) | (colour << 0));
}
int calculate_video_dma1_grab(struct saa7146* saa, int frame, struct saa7146_video_dma* vdma1)
{
int depth = 0;
switch(saa->grab_format[frame]) {
case YUV422_COMPOSED:
case RGB15_COMPOSED:
case RGB16_COMPOSED:
depth = 2;
break;
case RGB24_COMPOSED:
depth = 3;
break;
default:
depth = 4;
break;
}
vdma1->pitch = saa->grab_width[frame]*depth*2;
vdma1->base_even = 0;
vdma1->base_odd = vdma1->base_even + (vdma1->pitch/2);
vdma1->prot_addr = (saa->grab_width[frame]*saa->grab_height[frame]*depth)-1;
vdma1->num_line_byte = ((modes_constants[saa->mode].v_field<<16) + modes_constants[saa->mode].h_pixels);
vdma1->base_page = virt_to_bus(saa->page_table[frame]) | ME1;
/* convention: if scaling is between 1/2 and 1/4 we only use
the even lines, the odd lines get discarded (see vertical scaling) */
if( saa->interlace != 0 && saa->grab_height[frame]*4 >= modes_constants[saa->mode].v_calc && saa->grab_height[frame]*2 <= modes_constants[saa->mode].v_calc) {
vdma1->base_odd = vdma1->prot_addr;
vdma1->pitch /= 2;
}
return 0;
}
/* ---------------------------------------------*/
/* position of overlay-window */
/* ---------------------------------------------*/
/* calculate the new memory offsets for a desired position */
int move_to(struct saa7146* saa, int w_x, int w_y, int w_height, int b_width, int b_depth, int b_bpl, u32 base, int td_flip)
{
struct saa7146_video_dma vdma1;
if( w_y < 0 || w_height <= 0 || b_depth <= 0 || b_bpl <= 0 || base == 0 ) {
printk("saa7146: ==> calculate_video_dma1_overlay: illegal values: y: %d h: %d d: %d b: %d base: %d\n",w_y ,w_height,b_depth,b_bpl,base);
return -EINVAL;
}
/* calculate memory offsets for picture, look if we shall top-down-flip */
vdma1.pitch = 2*b_bpl;
if ( 0 == td_flip ) {
vdma1.prot_addr = (u32)base + ((w_height+w_y+1)*b_width*(b_depth/4));
vdma1.base_even = (u32)base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2);
}
else {
vdma1.prot_addr = (u32)base + (w_y * (vdma1.pitch/2));
vdma1.base_even = (u32)base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2);
vdma1.pitch *= -1;
}
/* convention: if scaling is between 1/2 and 1/4 we only use
the even lines, the odd lines get discarded (see vertical scaling) */
if( saa->interlace != 0 && w_height*4 >= modes_constants[saa->mode].v_calc && w_height*2 <= modes_constants[saa->mode].v_calc) {
vdma1.base_odd = vdma1.prot_addr;
vdma1.pitch /= 2;
}
vdma1.base_page = 0;
vdma1.num_line_byte = (modes_constants[saa->mode].v_field<<16)+modes_constants[saa->mode].h_pixels;
saa7146_write(saa->mem, BASE_EVEN1, vdma1.base_even);
saa7146_write(saa->mem, BASE_ODD1, vdma1.base_odd);
saa7146_write(saa->mem, PROT_ADDR1, vdma1.prot_addr);
saa7146_write(saa->mem, BASE_PAGE1, vdma1.base_page);
saa7146_write(saa->mem, PITCH1, vdma1.pitch);
saa7146_write(saa->mem, NUM_LINE_BYTE1, vdma1.num_line_byte);
/* update the video dma 1 registers */
saa7146_write(saa->mem, MC2, (MASK_02 | MASK_18));
return 0;
}
/* ---------------------------------------------*/
/* size of window (overlay) */
/* ---------------------------------------------*/
int set_window(struct saa7146* saa, int width, int height, int flip_lr, int port_sel, int sync_sel)
{
u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
/* set vertical scale according to selected mode: 0 = PAL, 1 = NTSC */
hps_v_scale = 0; /* all bits get set by the function-call */
hps_v_gain = 0; /* fixme: saa7146_read(saa->mem, HPS_V_GAIN);*/
calculate_v_scale_registers(saa, modes_constants[saa->mode].v_calc, height, &hps_v_scale, &hps_v_gain);
/* set horizontal scale according to selected mode: 0 = PAL, 1 = NTSC */
hps_ctrl = 0;
hps_h_prescale = 0; /* all bits get set in the function */
hps_h_scale = 0;
calculate_h_scale_registers(saa, modes_constants[saa->mode].h_calc, width, 0, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
/* set hyo and hxo */
calculate_hxo_hyo_and_sources(saa, port_sel, sync_sel, &hps_h_scale, &hps_ctrl);
/* write out new register contents */
saa7146_write(saa->mem, HPS_V_SCALE, hps_v_scale);
saa7146_write(saa->mem, HPS_V_GAIN, hps_v_gain);
saa7146_write(saa->mem, HPS_CTRL, hps_ctrl);
saa7146_write(saa->mem, HPS_H_PRESCALE,hps_h_prescale);
saa7146_write(saa->mem, HPS_H_SCALE, hps_h_scale);
/* upload shadow-ram registers */
saa7146_write( saa->mem, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) );
/*
printk("w:%d,h:%d\n",width,height);
*/
return 0;
}
void set_output_format(struct saa7146* saa, u16 palette)
{
u32 clip_format = saa7146_read(saa->mem, CLIP_FORMAT_CTRL);
dprintk("saa7146: ==> set_output_format: pal:0x%03x\n",palette);
/* call helper function */
calculate_output_format_register(saa,palette,&clip_format);
dprintk("saa7146: ==> set_output_format: 0x%08x\n",clip_format);
/* update the hps registers */
saa7146_write(saa->mem, CLIP_FORMAT_CTRL, clip_format);
saa7146_write(saa->mem, MC2, (MASK_05 | MASK_21));
}
void set_picture_prop(struct saa7146 *saa, u32 brightness, u32 contrast, u32 colour)
{
u32 bcs_ctrl = 0;
calculate_bcs_ctrl_register(saa, brightness, contrast, colour, &bcs_ctrl);
saa7146_write(saa->mem, BCS_CTRL, bcs_ctrl);
/* update the bcs register */
saa7146_write(saa->mem, MC2, (MASK_06 | MASK_22));
}
/* ---------------------------------------------*/
/* overlay enable/disable */
/* ---------------------------------------------*/
/* enable(1) / disable(0) video */
void video_setmode(struct saa7146* saa, int v)
{
hprintk("saa7146: ==> video_setmode; m:%d\n",v);
/* disable ? */
if(v==0) {
/* disable video dma1 */
saa7146_write(saa->mem, MC1, MASK_22);
} else {/* or enable ? */
/* fixme: enable video */
saa7146_write(saa->mem, MC1, (MASK_06 | MASK_22));
}
}
/* -----------------------------------------------------
common grabbing-functions. if you have some simple
saa7146-based frame-grabber you can most likely call
these. they do all the revision-dependend stuff and
do rps/irq-based grabbing for you.
-----------------------------------------------------*/
/* this function initializes the rps for the next grab for any "old"
saa7146s (= revision 0). it assumes that the rps is *not* running
when it gets called. */
int init_rps0_rev0(struct saa7146* saa, int frame, int irq_call)
{
struct saa7146_video_dma vdma1;
u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
u32 clip_format = 0; /* this can be 0, since we don't do clipping */
u32 bcs_ctrl = 0;
int count = 0;
/* these static values are used to remember the last "programming" of the rps.
if the height, width and format of the grab has not changed (which is very likely
when some streaming capture is done) the reprogramming overhead can be minimized */
static int last_height = 0;
static int last_width = 0;
static int last_format = 0;
static int last_port = 0;
static int last_frame = -1;
/* write the address of the rps-program */
saa7146_write(saa->mem, RPS_ADDR0, virt_to_bus(&saa->rps0[ 0]));
/* let's check if we can re-use something of the last grabbing process */
if ( saa->grab_height[frame] != last_height
|| saa->grab_width[frame] != last_width
|| saa->grab_port[frame] != last_port
|| saa->grab_format[frame] != last_format ) {
/* nope, we have to start from the beginning */
calculate_video_dma1_grab(saa, frame, &vdma1);
calculate_v_scale_registers(saa, modes_constants[saa->mode].v_calc, saa->grab_height[frame], &hps_v_scale, &hps_v_gain);
calculate_h_scale_registers(saa, modes_constants[saa->mode].h_calc, saa->grab_width[frame], 0, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
calculate_hxo_hyo_and_sources(saa, saa->grab_port[frame], saa->grab_port[frame], &hps_h_scale, &hps_ctrl);
calculate_output_format_register(saa,saa->grab_format[frame],&clip_format);
calculate_bcs_ctrl_register(saa, 0x80, 0x40, 0x40, &bcs_ctrl);
count = 0;
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG_MASK | (MC1/4)); /* turn off video-dma1 and dma2 (clipping)*/
saa->rps0[ count++ ] = cpu_to_le32(MASK_06 | MASK_22 | MASK_05 | MASK_21); /* => mask */
saa->rps0[ count++ ] = cpu_to_le32(MASK_22 | MASK_21); /* => values */
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | ( saa->grab_port[frame] == 0 ? MASK_12 : MASK_14)); /* wait for o_fid_a/b */
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | ( saa->grab_port[frame] == 0 ? MASK_11 : MASK_13)); /* wait for e_fid_a/b */
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG | (6 << 8) | HPS_CTRL/4); /* upload hps-registers for next grab */
saa->rps0[ count++ ] = cpu_to_le32(hps_ctrl);
saa->rps0[ count++ ] = cpu_to_le32(hps_v_scale);
saa->rps0[ count++ ] = cpu_to_le32(hps_v_gain);
saa->rps0[ count++ ] = cpu_to_le32(hps_h_prescale);
saa->rps0[ count++ ] = cpu_to_le32(hps_h_scale);
saa->rps0[ count++ ] = cpu_to_le32(bcs_ctrl);
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG | (1 << 8) | CLIP_FORMAT_CTRL/4);/* upload hps-registers for next grab */
saa->rps0[ count++ ] = cpu_to_le32(clip_format);
saa->rps0[ count++ ] = cpu_to_le32(CMD_UPLOAD | MASK_05 | MASK_06); /* upload hps1/2 */
/* upload video-dma1 registers for next grab */
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG | (6 << 8) | BASE_ODD1/4);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.base_odd);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.base_even);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.prot_addr);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.pitch);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.base_page);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.num_line_byte);
saa->rps0[ count++ ] = cpu_to_le32(CMD_UPLOAD | MASK_02); /* upload video-dma1 */
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG_MASK | (MC1/4)); /* turn on video-dma1 */
saa->rps0[ count++ ] = cpu_to_le32(MASK_06 | MASK_22); /* => mask */
saa->rps0[ count++ ] = cpu_to_le32(MASK_06 | MASK_22); /* => values */
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG | (1 << 8) | (MC2/4)); /* Write MC2 */
saa->rps0[ count++ ] = cpu_to_le32((1 << (27+frame)) | (1 << (11+frame)));
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | ( saa->grab_port[frame] == 0 ? MASK_12 : MASK_14)); /* wait for o_fid_a/b */
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | ( saa->grab_port[frame] == 0 ? MASK_11 : MASK_13)); /* wait for e_fid_a/b */
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG_MASK | (MC1/4)); /* turn off video-dma1 */
saa->rps0[ count++ ] = cpu_to_le32(MASK_06 | MASK_22); /* => mask */
saa->rps0[ count++ ] = cpu_to_le32(MASK_22); /* => values */
saa->rps0[ count++ ] = cpu_to_le32(CMD_INTERRUPT); /* generate interrupt */
saa->rps0[ count++ ] = cpu_to_le32(CMD_STOP); /* stop processing */
} else {
/* the height, width, ... have not changed. check if the user wants to grab to
another *buffer* */
if( frame != last_frame ) {
/* ok, we want to grab to another buffer, but with the same programming.
it is sufficient to adjust the video_dma1-registers and the rps-signal stuff. */
saa->rps0[ 20 ] = cpu_to_le32(virt_to_bus(saa->page_table[frame]) | ME1);
saa->rps0[ 27 ] = cpu_to_le32((1 << (27+frame)) | (1 << (11+frame)));
}
}
/* if we are called from within the irq-handler, the hps is at the beginning of a
new frame. the rps does not need to wait the new frame, and so we tweak the
starting address a little bit and so we can directly start grabbing again.
note: for large video-sizes and slow computers this can cause jaggy pictures
because the whole process is not in sync. perhaps one should be able to
disable this. (please remember that this whole stuff only belongs to
"old" saa7146s (= revision 0), newer saa7146s dont have any hardware-bugs
and capture works fine. (see below) */
if( 1 == irq_call ) {
saa7146_write(saa->mem, RPS_ADDR0, virt_to_bus(&saa->rps0[15]));
}
/* turn on rps */
saa7146_write(saa->mem, MC1, (MASK_12 | MASK_28));
/* store the values for the last grab */
last_height = saa->grab_height[frame];
last_width = saa->grab_width[frame];
last_format = saa->grab_format[frame];
last_port = saa->grab_port[frame];
last_frame = frame;
return 0;
}
int init_rps0_rev1(struct saa7146* saa, int frame) {
static int old_width[SAA7146_MAX_BUF]; /* pixel width of grabs */
static int old_height[SAA7146_MAX_BUF]; /* pixel height of grabs */
static int old_format[SAA7146_MAX_BUF]; /* video format of grabs */
static int old_port[SAA7146_MAX_BUF]; /* video port for grab */
static int buf_stat[SAA7146_MAX_BUF];
struct saa7146_video_dma vdma1;
u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
u32 clip_format = 0; /* this can be 0, since we don't do clipping */
u32 bcs_ctrl = 0;
int i = 0, count = 0;
/* check if something has changed since the last grab for this buffer */
if ( saa->grab_height[frame] == old_height[frame]
&& saa->grab_width[frame] == old_width[frame]
&& saa->grab_port[frame] == old_port[frame]
&& saa->grab_format[frame] == old_format[frame] ) {
/* nope, nothing to be done here */
return 0;
}
/* re-program the rps0 completely */
/* indicate that the user has requested re-programming of the 'frame'-buffer */
buf_stat[frame] = 1;
/* turn off rps */
saa7146_write(saa->mem, MC1, MASK_28);
/* write beginning of rps-program */
count = 0;
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | MASK_12); /* wait for o_fid_a */
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | MASK_11); /* wait for e_fid_a */
for(i = 0; i < saa->buffers; i++) {
saa->rps0[ count++ ] = cpu_to_le32(CMD_JUMP | (1 << (21+i))); /* check signal x, jump if set */
saa->rps0[ count++ ] = cpu_to_le32(virt_to_bus(&saa->rps0[40*(i+1)]));
}
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | MASK_12); /* wait for o_fid_a */
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | MASK_11); /* wait for e_fid_a */
saa->rps0[ count++ ] = cpu_to_le32(CMD_JUMP); /* jump to the beginning */
saa->rps0[ count++ ] = cpu_to_le32(virt_to_bus(&saa->rps0[2]));
for(i = 0; i < saa->buffers; i++) {
/* we only re-program the i-th buffer if the user had set some values for it earlier.
otherwise the calculation-functions may fail. */
if( buf_stat[i] == 0)
continue;
count = 40*(i+1);
calculate_video_dma1_grab(saa, i, &vdma1);
calculate_v_scale_registers(saa, modes_constants[saa->mode].v_calc, saa->grab_height[i], &hps_v_scale, &hps_v_gain);
calculate_h_scale_registers(saa, modes_constants[saa->mode].h_calc, saa->grab_width[i], 0, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
calculate_hxo_hyo_and_sources(saa, saa->grab_port[i], saa->grab_port[i], &hps_h_scale, &hps_ctrl);
calculate_output_format_register(saa,saa->grab_format[i],&clip_format);
calculate_bcs_ctrl_register(saa, 0x80, 0x40, 0x40, &bcs_ctrl);
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG | (6 << 8) | HPS_CTRL/4); /* upload hps-registers for next grab */
saa->rps0[ count++ ] = cpu_to_le32(hps_ctrl);
saa->rps0[ count++ ] = cpu_to_le32(hps_v_scale);
saa->rps0[ count++ ] = cpu_to_le32(hps_v_gain);
saa->rps0[ count++ ] = cpu_to_le32(hps_h_prescale);
saa->rps0[ count++ ] = cpu_to_le32(hps_h_scale);
saa->rps0[ count++ ] = cpu_to_le32(bcs_ctrl);
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG | (1 << 8) | CLIP_FORMAT_CTRL/4);/* upload hps-registers for next grab */
saa->rps0[ count++ ] = cpu_to_le32(clip_format);
saa->rps0[ count++ ] = cpu_to_le32(CMD_UPLOAD | MASK_05 | MASK_06); /* upload hps1/2 */
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG | (6 << 8) | BASE_ODD1/4); /* upload video-dma1 registers for next grab */
saa->rps0[ count++ ] = cpu_to_le32(vdma1.base_odd);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.base_even);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.prot_addr);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.pitch);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.base_page);
saa->rps0[ count++ ] = cpu_to_le32(vdma1.num_line_byte);
saa->rps0[ count++ ] = cpu_to_le32(CMD_UPLOAD | MASK_02); /* upload video-dma1 */
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG_MASK | (MC1/4)); /* turn on video-dma1 */
saa->rps0[ count++ ] = cpu_to_le32(MASK_06 | MASK_22); /* => mask */
saa->rps0[ count++ ] = cpu_to_le32(MASK_06 | MASK_22); /* => values */
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | ( saa->grab_port[i] == 0 ? MASK_12 : MASK_14)); /* wait for o_fid_a/b */
saa->rps0[ count++ ] = cpu_to_le32(CMD_PAUSE | ( saa->grab_port[i] == 0 ? MASK_11 : MASK_13)); /* wait for e_fid_a/b */
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG_MASK | (MC1/4)); /* turn off video-dma1 and dma2 (clipping)*/
saa->rps0[ count++ ] = cpu_to_le32(MASK_06 | MASK_22 | MASK_05 | MASK_21); /* => mask */
saa->rps0[ count++ ] = cpu_to_le32(MASK_22 | MASK_21); /* => values */
saa->rps0[ count++ ] = cpu_to_le32(CMD_WR_REG | (1 << 8) | (MC2/4)); /* Write MC2 */
saa->rps0[ count++ ] = cpu_to_le32((1 << (27+i)));
saa->rps0[ count++ ] = cpu_to_le32(CMD_INTERRUPT); /* generate interrupt */
saa->rps0[ count++ ] = cpu_to_le32(CMD_JUMP); /* jump to the beginning */
saa->rps0[ count++ ] = cpu_to_le32(virt_to_bus(&saa->rps0[2]));
old_height[frame] = saa->grab_height[frame];
old_width[frame] = saa->grab_width[frame];
old_port[frame] = saa->grab_port[frame];
old_format[frame] = saa->grab_format[frame];
}
/* write the address of the rps-program */
saa7146_write(saa->mem, RPS_ADDR0, virt_to_bus(&saa->rps0[ 0]));
/* turn on rps again */
saa7146_write(saa->mem, MC1, (MASK_12 | MASK_28));
return 0;
}
/* this funtion is called whenever a new grab is requested. if possible (that
means: if the rps is not running) it re-programs the rps, otherwise it relys on
the irq-handler to do that */
int set_up_grabbing(struct saa7146* saa, int frame)
{
u32 mc1 = 0;
if( 0 == saa->revision ) {
/* check if the rps is currently in use */
mc1 = saa7146_read(saa->mem, MC1);
/* the rps is not running ... */
if( 0 == ( mc1 & MASK_12) ) {
/* we can completly re-program the rps now */
dprintk("saa7146_v4l.o: ==> set_up_grabbing: start new rps.\n");
init_rps0_rev0(saa,frame,0);
} else {
/* the rps is running. in this case, the irq-handler is responsible for
re-programming the rps and nothing can be done right now */
dprintk("saa7146_v4l.o: ==> set_up_grabbing: no new rps started.\n");
}
} else {
/* check if something has changed, reprogram if necessary */
init_rps0_rev1(saa,frame);
/* set rps-signal-bit to start grabbing */
saa7146_write(saa->mem, MC2, (1 << (27+frame)) | (1 << (11+frame)));
}
return 0;
}
void saa7146_std_grab_irq_callback_rps0(struct saa7146* saa, u32 isr, void* data)
{
u32 mc2 = 0;
int i = 0;
hprintk("saa7146_v4l.o: ==> saa7146_v4l_irq_callback_rps0\n");
/* check for revision: old revision */
if( 0 == saa->revision ) {
/* look what buffer has been grabbed, set the done-flag and clear the signal */
mc2 = saa7146_read(saa->mem, MC2);
for( i = 0; i < saa->buffers; i++ ) {
if ((0 != (mc2 & (1 << (11+i)))) && (GBUFFER_GRABBING == saa->frame_stat[i])) {
saa->frame_stat[i] = GBUFFER_DONE;
saa7146_write(saa->mem, MC2, (1<<(27+i)));
}
}
/* look if there is another buffer we can grab to */
for( i = 0; i < saa->buffers; i++ ) {
if ( GBUFFER_GRABBING == saa->frame_stat[i] )
break;
}
/* yes, then set up the rps again */
if( saa->buffers != i) {
init_rps0_rev0(saa,i,1);
}
} else {
/* new revisions */
/* look what buffer has been grabbed, set the done-flag */
mc2 = saa7146_read(saa->mem, MC2);
for( i = 0; i < saa->buffers; i++ ) {
if ((0 == (mc2 & (1 << (11+i)))) && (GBUFFER_GRABBING == saa->frame_stat[i])) {
saa->frame_stat[i] = GBUFFER_DONE;
}
}
}
/* notify any pending process */
wake_up_interruptible(&saa->rps0_wq);
return;
}
/* ---------------------------------------------*/
/* mask-clipping */
/* ---------------------------------------------*/
int calculate_clipping_registers_mask(struct saa7146* saa, u32 width, u32 height, struct saa7146_video_dma* vdma2, u32* clip_format, u32* arbtr_ctrl)
{
u32 clip_addr = 0, clip_pitch = 0;
dprintk("saa7146: ==> calculate_clipping_registers_mask\n");
/* adjust arbitration control register */
*arbtr_ctrl &= 0xffff00ff;
*arbtr_ctrl |= 0x00001000;
clip_addr = virt_to_bus(saa->clipping);
clip_pitch = ((width+31)/32)*4;
vdma2->base_even = clip_addr;
vdma2->base_page = 0x04; /* enable read - operation */
vdma2->prot_addr = clip_addr + (clip_pitch*height);
/* convention: if scaling is between 1/2 and 1/4 we only use
the even lines, the odd lines get discarded (see vertical scaling) */
if( saa->interlace != 0 && height*4 >= modes_constants[saa->mode].v_calc && height*2 <= modes_constants[saa->mode].v_calc) {
vdma2->base_odd = vdma2->prot_addr;
vdma2->pitch = clip_pitch;
vdma2->num_line_byte = (((height)-1) << 16) | (clip_pitch-1);
} else {
vdma2->base_odd = clip_addr+clip_pitch;
vdma2->pitch = clip_pitch*2;
vdma2->num_line_byte = (((height/2)-1) << 16) | (clip_pitch-1);
}
*clip_format &= 0xfffffff7;
return 0;
}
/* helper functions for emulate rect-clipping via mask-clipping.
note: these are extremely inefficient, but for clipping with less than 16
windows rect-clipping should be done anyway...
*/
/* clear one pixel of the clipping memory at position (x,y) */
void set_pixel(s32 x, s32 y, s32 window_width, u32* mem) {
u32 mem_per_row = 0;
u32 adr = 0;
u32 shift = 0;
u32 bit = 0;
mem_per_row = (window_width + 31 )/ 32 ;
adr = y * mem_per_row + (x / 32);
shift = 31 - (x % 32);
bit = (1 << shift);
mem[adr] |= bit;
}
/* clear a box out of the clipping memory, beginning at (x,y) with "width" and "height" */
void set_box(s32 x, s32 y, s32 width, s32 height, s32 window_width, s32 window_height, u32* mem)
{
s32 ty = 0;
s32 tx = 0;
/* the video_clip-struct may contain negative values to indicate that a window
doesn't lay completly over the video window. Thus, we correct the values */
if( width < 0) {
x += width; width = -width;
}
if( height < 0) {
y += height; height = -height;
}
if( x < 0) {
width += x; x = 0;
}
if( y < 0) {
height += y; y = 0;
}
if( width <= 0 || height <= 0) {
printk("saa7146: ==> set_box: sanity error!\n");
return;
}
if(x + width > window_width)
width -= (x + width) - window_width;
if(y + height > window_height)
height -= (y + height) - window_height;
/* Now, set a '1' in the memory, where no video picture should appear */
for(ty = y; ty < y+height; ty++) {
for(tx = x; tx < x+width; tx++) {
set_pixel(tx, ty, window_width, mem);
}
}
}
int emulate_rect_clipping(struct saa7146 *saa, u16 clipcount, int x[], int y[], int w[], int h[], u32 w_width, u32 w_height)
{
int i = 0;
/* clear out clipping mem */
memset(saa->clipping, 0x0, CLIPPING_MEM_SIZE*sizeof(u32));
/* go through list of clipping-windows, clear out rectangular-regions in the clipping memory */
for(i = 0; i < clipcount; i++) {
set_box(x[i], y[i], w[i], h[i], w_width, w_height, saa->clipping);
}
return 0;
}
/* ---------------------------------------------*/
/* rectangle-clipping */
/* ---------------------------------------------*/
#define MIN(x,y) ( ((x) < (y)) ? (x) : (y) )
#define MAX(x,y) ( ((x) > (y)) ? (x) : (y) )
/* simple double-sort algorithm with duplicate elimination */
int sort_and_eliminate(u32* values, int* count)
{
int low = 0, high = 0, top = 0, temp = 0;
int cur = 0, next = 0;
/* sanity checks */
if( (0 > *count) || (NULL == values) ) {
printk("saa7146: ==> sort_and_eliminate: internal error #1\n");
return -EINVAL;
}
/* bubble sort the first count items of the array values */
for( top = *count; top > 0; top--) {
for( low = 0, high = 1; high < top; low++, high++) {
if( values[low] > values[high] ) {
temp = values[low];
values[low] = values[high];
values[high] = temp;
}
}
}
/* remove duplicate items */
for( cur = 0, next = 1; next < *count; next++) {
if( values[cur] != values[next])
values[++cur] = values[next];
}
*count = cur + 1;
return 0;
}
int calculate_clipping_registers_rect(struct saa7146 *saa, int clipcount, int x[], int y[], int w[], int h[], u32 width, u32 height, struct saa7146_video_dma* vdma2, u32* clip_format, u32* arbtr_ctrl)
{
u32 line_list[32];
u32 pixel_list[32];
u32 numdwords = 0;
int i = 0, j = 0;
int l = 0, r = 0, t = 0, b = 0;
int cnt_line = 0, cnt_pixel = 0;
dprintk("saa7146: ==> calculate_clipping_registers_clip\n");
/* clear out memory */
memset(&line_list[0], 0x00, sizeof(u32)*32);
memset(&pixel_list[0], 0x00, sizeof(u32)*32);
memset(saa->clipping, 0x00, sizeof(u32)*CLIPPING_MEM_SIZE);
/* fill the line and pixel-lists */
for(i = 0; i < clipcount; i++) {
/* calculate values for l(eft), r(ight), t(op), b(ottom) */
l = x[i];
r = x[i]+w[i];
t = y[i];
b = y[i]+h[i];
/* insert left/right coordinates */
pixel_list[ 2*i ] = MIN(l, width);
pixel_list[(2*i)+1] = MIN(r, width);
/* insert top/bottom coordinates */
line_list[ 2*i ] = MIN(t, height);
line_list[(2*i)+1] = MIN(b, height);
}
/* sort and eliminate lists */
cnt_line = cnt_pixel = 2*clipcount;
sort_and_eliminate( &pixel_list[0], &cnt_pixel );
sort_and_eliminate( &line_list[0], &cnt_line );
/* calculate the number of used u32s */
numdwords = MAX( (cnt_line+1), (cnt_pixel+1))*2;
numdwords = MAX(4, numdwords);
numdwords = MIN(64, numdwords);
/* fill up cliptable */
for(i = 0; i < cnt_pixel; i++) {
saa->clipping[2*i] |= (pixel_list[i] << 16);
}
for(i = 0; i < cnt_line; i++) {
saa->clipping[(2*i)+1] |= (line_list[i] << 16);
}
/* fill up cliptable with the display infos */
for(j = 0; j < clipcount; j++) {
for(i = 0; i < cnt_pixel; i++) {
if( x[j] < 0)
x[j] = 0;
if( pixel_list[i] < (x[j] + w[j])) {
if ( pixel_list[i] >= x[j] ) {
saa->clipping[2*i] |= (1 << j);
}
}
}
for(i = 0; i < cnt_line; i++) {
if( y[j] < 0)
y[j] = 0;
if( line_list[i] < (y[j] + h[j]) ) {
if( line_list[i] >= y[j] ) {
saa->clipping[(2*i)+1] |= (1 << j);
}
}
}
}
/* adjust arbitration control register */
*arbtr_ctrl &= 0xffff00ff;
*arbtr_ctrl |= 0x00001c00;
vdma2->base_even = virt_to_bus(saa->clipping);
vdma2->base_odd = virt_to_bus(saa->clipping);
vdma2->prot_addr = virt_to_bus(saa->clipping)+((sizeof(u32))*(numdwords));
vdma2->base_page = 0x04;
vdma2->pitch = 0x00;
vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) );
/* set clipping-mode. please note again, that for sizes below 1/2, we only use the
even-field. because of this, we have to specify recinterl correctly (specs, p. 97)*/
*clip_format &= 0xfffffff7;
if( saa->interlace != 0 && height*4 >= modes_constants[saa->mode].v_calc && height*2 <= modes_constants[saa->mode].v_calc) {
*clip_format |= 0x00000000;
} else {
*clip_format |= 0x00000008;
}
return 0;
}
/* ---------------------------------------------*/
/* main function for clipping */
/* ---------------------------------------------*/
/* arguments:
type = see saa7146.h
width = width of the video-window
height = height of the video-window
*mask = pointer to mask memory (only needed for mask-clipping)
*clips = pointer to clip-window-list (only needed for rect-clipping)
clipcount = # of clip-windows (only needed for rect-clipping)
*/
int clip_windows(struct saa7146* saa, u32 type, u32 width, u32 height, u32* mask, u16 clipcount, int x[], int y[], int w[], int h[])
{
struct saa7146_video_dma vdma2;
u32 clip_format = saa7146_read(saa->mem, CLIP_FORMAT_CTRL);
u32 arbtr_ctrl = saa7146_read(saa->mem, PCI_BT_V1);
hprintk("saa7146: ==> clip_windows\n");
/* some sanity checks first */
if ( width <= 0 || height <= 0 ) {
printk("saa7146: ==> clip_windows: sanity error #1!\n");
return -EINVAL;
}
/* check if anything to do here, disable clipping if == 0 */
if( clipcount == 0 ) {
/* mask out relevant bits (=lower word)*/
clip_format &= MASK_W1;
/* upload clipping-registers*/
saa7146_write(saa->mem, CLIP_FORMAT_CTRL,clip_format);
saa7146_write(saa->mem, MC2, (MASK_05 | MASK_21));
/* disable video dma2 */
saa7146_write(saa->mem, MC1, (MASK_21));
return 0;
}
switch(type) {
case SAA7146_CLIPPING_MASK_INVERTED:
case SAA7146_CLIPPING_MASK:
{
printk("mask\n");
/* sanity check */
if( NULL == mask ) {
printk("saa7146: ==> clip_windows: sanity error #1!\n");
return -EINVAL;
}
/* copy the clipping mask to structure */
memmove(saa->clipping, mask, CLIPPING_MEM_SIZE*sizeof(u32));
/* set clipping registers */
calculate_clipping_registers_mask(saa,width,height,&vdma2,&clip_format,&arbtr_ctrl);
break;
}
case SAA7146_CLIPPING_RECT_INVERTED:
case SAA7146_CLIPPING_RECT:
{
/* see if we have anything to do */
if ( 0 == clipcount ) {
return 0;
}
/* sanity check */
if( NULL == x || NULL == y || NULL == w || NULL == h ) {
printk("saa7146: ==> clip_windows: sanity error #2!\n");
return -EINVAL;
}
/* rectangle clipping can only handle 16 overlay windows; if we
have more, we have do emulate the whole thing with mask-clipping */
if (1) { //clipcount > > 16 ) {
//printk("emulate\n");
emulate_rect_clipping(saa, clipcount, x,y,w,h, width, height);
calculate_clipping_registers_mask(saa,width,height,&vdma2,&clip_format,&arbtr_ctrl);
if( SAA7146_CLIPPING_RECT == type )
type = SAA7146_CLIPPING_MASK;
else
type = SAA7146_CLIPPING_MASK_INVERTED;
}
else {
calculate_clipping_registers_rect(saa,clipcount,x,y,w,h,width,height,&vdma2,&clip_format,&arbtr_ctrl);
}
break;
}
default:
{
printk("saa7146: ==> clip_windows: internal error #1!\n");
return -EINVAL;
}
}
/* set clipping format */
clip_format &= 0xffff0008;
clip_format |= (type << 4);
saa7146_write(saa->mem, BASE_EVEN2, vdma2.base_even);
saa7146_write(saa->mem, BASE_ODD2, vdma2.base_odd);
saa7146_write(saa->mem, PROT_ADDR2, vdma2.prot_addr);
saa7146_write(saa->mem, BASE_PAGE2, vdma2.base_page);
saa7146_write(saa->mem, PITCH2, vdma2.pitch);
saa7146_write(saa->mem, NUM_LINE_BYTE2, vdma2.num_line_byte);
saa7146_write(saa->mem, CLIP_FORMAT_CTRL,clip_format);
saa7146_write(saa->mem, PCI_BT_V1, arbtr_ctrl);
/* upload clip_control-register, clipping-registers, enable video dma2 */
saa7146_write(saa->mem, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19));
saa7146_write(saa->mem, MC1, (MASK_05 | MASK_21));
/*
printk("ARBTR_CTRL: 0x%08x\n",saa7146_read(saa->mem, PCI_BT_V1));
printk("CLIP_FORMAT: 0x%08x\n",saa7146_read(saa->mem, CLIP_FORMAT_CTRL));
printk("BASE_ODD1: 0x%08x\n",saa7146_read(saa->mem, BASE_ODD1));
printk("BASE_EVEN1: 0x%08x\n",saa7146_read(saa->mem, BASE_EVEN1));
printk("PROT_ADDR1: 0x%08x\n",saa7146_read(saa->mem, PROT_ADDR1));
printk("PITCH1: 0x%08x\n",saa7146_read(saa->mem, PITCH1));
printk("BASE_PAGE1: 0x%08x\n",saa7146_read(saa->mem, BASE_PAGE1));
printk("NUM_LINE_BYTE1: 0x%08x\n",saa7146_read(saa->mem, NUM_LINE_BYTE1));
printk("BASE_ODD2: 0x%08x\n",saa7146_read(saa->mem, BASE_ODD2));
printk("BASE_EVEN2: 0x%08x\n",saa7146_read(saa->mem, BASE_EVEN2));
printk("PROT_ADDR2: 0x%08x\n",saa7146_read(saa->mem, PROT_ADDR2));
printk("PITCH2: 0x%08x\n",saa7146_read(saa->mem, PITCH2));
printk("BASE_PAGE2: 0x%08x\n",saa7146_read(saa->mem, BASE_PAGE2));
printk("NUM_LINE_BYTE2: 0x%08x\n",saa7146_read(saa->mem, NUM_LINE_BYTE2));
*/
return 0;
}
#endif
#ifdef __COMPILE_SAA7146_I2C__
/* ---------------------------------------------*/
/* i2c-helper functions */
/* ---------------------------------------------*/
/* this functions gets the status from the saa7146 at address 'addr'
and returns it */
u32 i2c_status_check(struct saa7146* saa)
{
u32 iicsta = 0;
iicsta = saa7146_read(saa->mem, I2C_STATUS );
hprintk("saa7146: ==> i2c_status_check:0x%08x\n",iicsta);
return iicsta;
}
/* this function should be called after an i2c-command has been written.
if we are debugging, it checks, if the busy flags rises and falls correctly
and reports a timeout (-1) or the error-bits set like in described in the specs,
p.123, table 110 */
int i2c_busy_rise_and_fall(struct saa7146* saa, int timeout)
{
int i = 0;
u32 status = 0;
hprintk("saa7146: ==> i2c_busy_rise_and_fall\n");
/* wait until busy-flag rises */
for (i = 5; i > 0; i--) {
hprintk("saa7146: i2c_busy_rise_and_fall; rise wait %d\n",i);
status = i2c_status_check(saa);
/* check busy flag */
if ( 0 != (status & SAA7146_I2C_BUSY))
break;
/* see if anything can be done while we're waiting */
cond_resched ();
mdelay(1);
}
/* we don't check the i-value, since it does not matter
if we missed the rise of the busy flag or the fall or
whatever. we just have to wait some undefined time
after an i2c-command has been written out */
/* wait until busy-flag is inactive or error is reported */
for (i = timeout; i > 0; i--) {
hprintk("saa7146: i2c_busy_rise_and_fall; fall wait %d\n",i);
status = i2c_status_check(saa);
/* check busy flag */
if ( 0 == (status & SAA7146_I2C_BUSY))
break;
/* check error flag */
if ( 0 != (status & SAA7146_I2C_ERR))
break;
/* see if anything can be done while we're waiting */
cond_resched ();
mdelay(1);
}
/* did a timeout occur ? */
if ( 0 == i ) {
hprintk("saa7146: i2c_busy_rise_and_fall: timeout #2\n");
return -1;
}
/* report every error pending */
switch( status & 0xfc ) {
case SAA7146_I2C_SPERR:
hprintk("saa7146: i2c_busy_rise_and_fall: error due to invalid start/stop condition\n");
break;
case SAA7146_I2C_APERR:
hprintk("saa7146: i2c_busy_rise_and_fall: error in address phase\n");
break;
case SAA7146_I2C_DTERR:
hprintk("saa7146: i2c_busy_rise_and_fall: error in data transmission\n");
break;
case SAA7146_I2C_DRERR:
hprintk("saa7146: i2c_busy_rise_and_fall: error when receiving data\n");
break;
case SAA7146_I2C_AL:
hprintk("saa7146: i2c_busy_rise_and_fall: error because arbitration lost\n");
break;
}
return status;
}
/* this functions resets the saa7146 at address 'addr'
and returns 0 if everything was fine, otherwise -1 */
int i2c_reset(struct saa7146* saa)
{
u32 status = 0;
hprintk("saa7146: ==> i2c_reset\n");
status = i2c_status_check(saa);
/* clear data-byte for sure */
saa7146_write(saa->mem, I2C_TRANSFER, 0x00);
/* check if any operation is still in progress */
if ( 0 != ( status & SAA7146_I2C_BUSY) ) {
/* Yes, kill ongoing operation */
hprintk("saa7146: i2c_reset: busy_state detected\n");
/* set ABORT-OPERATION-bit */
saa7146_write(saa->mem, I2C_STATUS, ( SAA7146_I2C_BBR | MASK_07));
saa7146_write(saa->mem, MC2, (MASK_00 | MASK_16));
mdelay( SAA7146_I2C_DELAY );
/* clear all error-bits pending; this is needed because p.123, note 1 */
saa7146_write(saa->mem, I2C_STATUS, SAA7146_I2C_BBR );
saa7146_write(saa->mem, MC2, (MASK_00 | MASK_16));
mdelay( SAA7146_I2C_DELAY );
}
/* check if any other error is still present */
if ( SAA7146_I2C_BBR != (status = i2c_status_check(saa)) ) {
/* yes, try to kick it */
hprintk("saa7146: i2c_reset: error_state detected, status:0x%08x\n",status);
/* clear all error-bits pending */
saa7146_write(saa->mem, I2C_STATUS, SAA7146_I2C_BBR );
saa7146_write(saa->mem, MC2, (MASK_00 | MASK_16));
mdelay( SAA7146_I2C_DELAY );
/* the data sheet says it might be necessary to clear the status
twice after an abort */
saa7146_write(saa->mem, I2C_STATUS, SAA7146_I2C_BBR );
saa7146_write(saa->mem, MC2, (MASK_00 | MASK_16));
}
/* if any error is still present, a fatal error has occured ... */
if ( SAA7146_I2C_BBR != (status = i2c_status_check(saa)) ) {
hprintk("saa7146: i2c_reset: fatal error, status:0x%08x\n",status);
return -1;
}
return 0;
}
/* this functions writes out the data-bytes at 'data' to the saa7146
at address 'addr' regarding the 'timeout' and 'retries' values;
it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
failed badly (e.g. address error) */
int i2c_write_out(struct saa7146* saa, u32* data, int timeout)
{
int status = 0;
hprintk("saa7146: ==> writeout: 0x%08x (before) (to:%d)\n",*data,timeout);
/* write out i2c-command */
saa7146_write(saa->mem, I2C_TRANSFER, *data);
saa7146_write(saa->mem, I2C_STATUS, SAA7146_I2C_BBR);
saa7146_write(saa->mem, MC2, (MASK_00 | MASK_16));
/* after writing out an i2c-command we have to wait for a while;
because we do not know, how long we have to wait, we simply look
what the busy-flag is doing, before doing something else */
/* reason: while fiddling around with the i2c-routines, I noticed
that after writing out an i2c-command, one may not read out the
status immediately after that. you *must* wait some time, before
even the busy-flag gets set */
status = i2c_busy_rise_and_fall(saa,timeout);
if ( -1 == status ) {
hprintk("saa7146: i2c_write_out; timeout\n");
return -ETIMEDOUT;
}
/* we only handle address-errors here */
if ( 0 != (status & SAA7146_I2C_APERR)) {
hprintk("saa7146: i2c_write_out; error in address phase\n");
return -EREMOTEIO;
}
/* check for some other mysterious error; we don't handle this here */
if ( 0 != ( status & 0xff)) {
hprintk("saa7146: i2c_write_out: some error has occured\n");
return -EIO;
}
/* read back data, just in case we were reading ... */
*data = saa7146_read(saa->mem, I2C_TRANSFER);
hprintk("saa7146: writeout: 0x%08x (after)\n",*data);
return 0;
}
int clean_up(struct i2c_msg m[], int num, u32 *op)
{
u16 i, j;
u16 op_count = 0;
/* loop through all messages */
for(i = 0; i < num; i++) {
op_count++;
/* loop throgh all bytes of message i */
for(j = 0; j < m[i].len; j++) {
/* write back all bytes that could have been read */
m[i].buf[j] = (op[op_count/3] >> ((3-(op_count%3))*8));
op_count++;
}
}
return 0;
}
int prepare(struct i2c_msg m[], int num, u32 *op)
{
u16 h1, h2;
u16 i, j, addr;
u16 mem = 0, op_count = 0;
//for (i=0; i<num; i++) { printk ("\n%02x (%s): ", m[i].addr, m[i].flags & I2C_M_RD ? "R" : "W"); for (j=0; j<m[i].len; j++) { m[i].buf[j] &= 0xff; printk (" %02x ", (u8) m[i].buf[j]); } } printk ("\n");
/* determine size of needed memory */
for(i = 0; i < num; i++)
mem += m[i].len + 1;
/* we need one u32 for three bytes to be send plus
one byte to address the device */
mem = 1 + ((mem-1) / 3);
if ( mem > I2C_MEM_SIZE ) {
hprintk("saa7146: prepare: i2c-message to big\n");
return -1;
}
/* be careful: clear out the i2c-mem first */
memset(op,0,sizeof(u32)*mem);
for(i = 0; i < num; i++) {
/* insert the address of the i2c-slave.
* note: we get 7-bit-i2c-addresses,
* so we have to perform a translation
*/
addr = (m[i].addr << 1) | ((m[i].flags & I2C_M_RD) ? 1 : 0);
h1 = op_count/3; h2 = op_count%3;
op[h1] |= ((u8)addr << ((3-h2)*8));
op[h1] |= (SAA7146_I2C_START << ((3-h2)*2));
op_count++;
/* loop through all bytes of message i */
for(j = 0; j < m[i].len; j++) {
/* insert the data bytes */
h1 = op_count/3; h2 = op_count%3;
op[h1] |= ((u8)m[i].buf[j] << ((3-h2)*8));
op[h1] |= (SAA7146_I2C_CONT << ((3-h2)*2));
op_count++;
}
}
/* have a look at the last byte inserted:
* if it was: ...CONT change it to ...STOP
*/
h1 = (op_count-1)/3; h2 = (op_count-1)%3;
if ( SAA7146_I2C_CONT == (0x3 & ((op[h1]) >> ((3-h2)*2))) ) {
op[h1] &= ~(0x2 << ((3-h2)*2));
op[h1] |= (SAA7146_I2C_STOP << ((3-h2)*2));
}
return mem;
}
#endif
#ifdef __COMPILE_SAA7146_DEBI__
/* functions for accessing the debi-port. note: we currently don't support
* page-table-transfers.
*/
#define MY_DEBI_TIMEOUT_MS 5
int debi_transfer(struct saa7146* saa, struct saa7146_debi_transfer* dt)
{
u32 debi_config = 0, debi_command = 0, debi_page = 0, debi_ad = 0;
u32 timeout = MY_DEBI_TIMEOUT_MS;
/* sanity checks */
if(dt->direction > 1 || dt->timeout > 15 || dt->swap > 3 || dt->slave16 > 2 || dt->intel > 1 || dt->increment > 1 || dt->tien > 1 )
return -EINVAL;
debi_page = 0;
/* keep bits 31,30,28 clear */
debi_config = (dt->timeout << 22) | (dt->swap << 20) | (dt->slave16 << 19) | (dt->increment << 18) | (dt->intel << 17) | (dt->tien << 16);
debi_command = (dt->num_bytes << 17) | (dt->direction << 16) | (dt->address << 0);
debi_ad = dt->mem;
saa7146_write(saa->mem, DEBI_PAGE, debi_page);
saa7146_write(saa->mem, DEBI_CONFIG, debi_config);
saa7146_write(saa->mem, DEBI_COMMAND, debi_command);
saa7146_write(saa->mem, DEBI_AD, debi_ad);
/* upload debi-registers */
saa7146_write(saa->mem, MC2, (MASK_01|MASK_17));
/* wait for DEBI upload to complete */
while (! (saa7146_read(saa->mem, MC2) & 0x2));
while( --timeout ) {
/* check, if DEBI still active */
u32 psr = saa7146_read(saa->mem, PSR);
if (0 != (psr & SPCI_DEBI_S)) {
/* check, if error occured */
/* if ( 0 != (saa7146_read(saa->mem, SSR) & (MASK_23|MASK_22))) { */
if ( 0 != (saa7146_read(saa->mem, SSR) & (MASK_22))) {
/* clear error status and indicate error */
saa7146_write(saa->mem, ISR, SPCI_DEBI_E);
return -1;
}
}
else {
/* Clear status bit */
saa7146_write(saa->mem, ISR, SPCI_DEBI_S);
break;
}
/* I dont know how we should actually wait for the debi to have finished.
we simply wait 1ms here and then check in a loop for max. MY_DEBI_TIMEOUT_MS */
mdelay(1);
}
/* check for timeout */
if( 0 == timeout ) {
return -1;
}
/* read back data if we did immediate read-transfer */
if(dt->num_bytes <= 4 && dt->direction == 1) {
dt->mem = saa7146_read(saa->mem, DEBI_AD);
switch(dt->num_bytes) {
case 1:
dt->mem &= 0x000000ff;
break;
case 2:
dt->mem &= 0x0000ffff;
break;
case 3:
dt->mem &= 0x00ffffff;
break;
}
}
return 0;
}
#endif
#ifdef __COMPILE_SAA7146_STUFF__
/* ---------------------------------------------*/
/* helper-function: set gpio-pins */
/* ---------------------------------------------*/
void gpio_set(struct saa7146* saa, u8 pin, u8 data)
{
u32 value = 0;
/* sanity check */
if(pin > 3)
return;
/* read old register contents */
value = saa7146_read(saa->mem, GPIO_CTRL );
value &= ~(0xff << (8*pin));
value |= (data << (8*pin));
saa7146_write(saa->mem, GPIO_CTRL, value);
}
void select_input(struct saa7146* saa, int p)
{
u32 hps_ctrl = 0;
/* sanity check */
if( p < 0 || p > 1 )
return;
/* read old state */
hps_ctrl = saa7146_read(saa->mem, HPS_CTRL);
/* mask out relevant bits */
hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 );
/* set bits for input b */
if( 1 == p ) {
hps_ctrl |= ( (1 << 30) | (1 << 28) );
}
/* write back & upload register */
saa7146_write(saa->mem, HPS_CTRL, hps_ctrl);
saa7146_write(saa->mem, MC2, (MASK_05 | MASK_21));
}
#endif
/*
saa7146_core.c - core-functions + i2c driver for the saa7146 by
Philips Semiconductors.
Copyright (C) 1998,1999 Michael Hunold <michael@mihu.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h> /* for module-version */
#include <linux/delay.h> /* for delay-stuff */
#include <linux/slab.h> /* for kmalloc/kfree */
#include <linux/pci.h> /* for pci-config-stuff, vendor ids etc. */
#include <linux/wrapper.h> /* for mem_map_reserve */
#include <linux/init.h>
#include <asm/io.h> /* for accessing the pci-device */
#include <linux/vmalloc.h> /* for module-version */
#include "saa7146_defs.h"
#include "saa7146_core.h"
#include "saa7146_v4l.h"
#include "av7110.h"
#include "../dvb-core/compat.h"
#include "../dvb-core/dvb_i2c.h"
/* insmod parameter: here you can specify the number of video-buffers
to be allocated. for simple capturing 2 buffers (double-buffering)
should suffice. but if you plan to do 25fps grabbing, you should
set this to 4(=maximum), in order to be able to catch up from
temporarily delays */
static int buffers = 2;
/* insmod parameter: some programs (e.g. vic) do not allow to
specify the used video-mode, so you have to tell this to the
modules by hand, 0 = PAL, 1 = NTSC */
static int mode = 0;
/* debug levels: 0 -- no debugging outputs
1 -- prints out entering (and exiting if useful) of functions
2 -- prints out very, very detailed informations of what is going on
3 -- both of the above */
int saa7146_debug = 0; /* insmod parameter */
#define dprintk if (saa7146_debug & 1) printk
#define hprintk if (saa7146_debug & 2) printk
/* ---------------------------------------------*/
/* memory functions - taken from bttv.c */
/* ---------------------------------------------*/
static inline unsigned long kvirt_to_pa(unsigned long adr)
{
unsigned long kva;
kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
kva |= adr & (PAGE_SIZE-1); /* restore the offset */
return __pa(kva);
}
static LIST_HEAD(saa7146_list);
static int saa7146_extension_count = 0;
static struct saa7146_extension* saa7146_ext[SAA7146_MAX_EXTENSIONS];
#define SAA7146_I2C_TIMEOUT 100 /* in ms */
#define SAA7146_I2C_RETRIES 6
static u32 SAA7146_I2C_BBR = SAA7146_I2C_BUS_BIT_RATE_3200;
#define __COMPILE_SAA7146_I2C__
#define __COMPILE_SAA7146_DEBI__
#include "saa7146.c"
#undef __COMPILE_SAA7146_I2C__
/* ---------------------------------------------*/
/* memory functions designed for saa7146 */
/* ---------------------------------------------*/
/* rvmalloc allocates the memory and builds up
the page-tables for quant-number of buffers */
static void* rvmalloc(int quant, u32* pt[])
{
void* mem;
unsigned long adr = 0;
unsigned long count = 0;
u32* ptp = 0;
int i = 0, j = 0;
dprintk(KERN_ERR "saa7146: rvmalloc called, quant:%d\n",quant);
if(!quant)
return NULL;
/* get grabbing memory */
mem = vmalloc_32(quant*GRABBING_MEM_SIZE);
if(!mem)
return NULL;
dprintk(KERN_ERR "saa7146: alloc page tables\n");
/* alloc one page for a page-table for quant buffers */
for(i = 0; i < quant; i++) {
pt[i] = (u32*)kmalloc(PAGE_SIZE,GFP_KERNEL);
/* error: memory could not be allocated */
if(!pt[i]) {
dprintk(KERN_ERR "saa7146: failed, free tables\n");
for(j = (i-1); j >= 0; j--)
kfree(pt[j]);
dprintk(KERN_ERR "saa7146: free buffer memory\n");
vfree(mem);
dprintk(KERN_ERR "saa7146: return 0 address for buffer\n");
return NULL;
}
memset(pt[i], 0x00, PAGE_SIZE);
}
dprintk(KERN_ERR "saa7146: clear RAM\n");
/* clear the ram out, no junk to the user
note: 0x7f gives a nice grey field
in RGB and YUV as well */
memset(mem, 0x7f, quant*GRABBING_MEM_SIZE);
dprintk(KERN_ERR "saa7146: build page tables\n");
adr = (unsigned long)mem;
/* walk through the grabbing-memory and build up the page-tables */
for(i = 0; i < quant; i++) {
for (count=0; count<GRABBING_MEM_SIZE; count+=PAGE_SIZE)
mem_map_reserve(virt_to_page(__va(kvirt_to_pa(adr+count))));
/* separate loop for SAA MMU, PAGE_SIZE can be !=4096 */
ptp = pt[i];
for (count=0; count<GRABBING_MEM_SIZE; count+=4096, adr+=4096)
*(ptp++) = cpu_to_le32(kvirt_to_pa(adr));
}
dprintk(KERN_ERR "saa7146: page tables built\n");
return mem;
}
static void rvfree(void* mem, int quant, u32* pt[])
{
unsigned long adr, page;
unsigned long size = 0;
int i = 0;
dprintk(KERN_ERR "saa7146: rvfree called\n");
if (!quant)
return;
if (mem) {
adr = (unsigned long)mem;
size = quant * GRABBING_MEM_SIZE;
while (size > 0) {
page = kvirt_to_pa(adr);
mem_map_unreserve(virt_to_page(__va(page)));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
/* release the grabbing memory */
vfree(mem);
}
/* free the page tables */
for(i = 0; i < quant; i++) {
kfree(pt[i]);
}
}
/* ---------------------------------------------*/
/* i2c-functions */
/* ---------------------------------------------*/
static
int do_master_xfer (struct dvb_i2c_bus *i2c, struct i2c_msg msgs[], int num)
{
struct saa7146 *a = i2c->data;
int result, count;
int i = 0;
dprintk(KERN_ERR "saa7146_core.o: master_xfer called, num:%d\n",num);
/* prepare the message(s), get number of u32s to transfer */
count = prepare(msgs, num, a->i2c);
if ( 0 > count ) {
hprintk(KERN_ERR "saa7146_core.o: master_xfer: could not prepare i2c-message\n");
return -EIO;
}
/* reset the i2c-device if necessary */
result = i2c_reset( a );
if ( 0 > result ) {
hprintk(KERN_ERR "saa7146_core.o: master_xfer: could not reset i2c-bus\n");
return result;
}
for(i = 0; i < count; i++) {
/* see how many u32 have to be transferred;
* if there is only 1,
* we do not start the whole rps1-engine...
*/
result = i2c_write_out( a, &a->i2c[i],
SAA7146_I2C_TIMEOUT );
if ( 0 != result) {
/* if address-error occured, don't retry */
if ( result == -EREMOTEIO )
{
hprintk(KERN_ERR "saa7146_core.o: master_xfer: error in address phase\n");
return result;
}
hprintk(KERN_ERR "saa7146_core.o: master_xfer: error transferring, trying again\n");
break;
}
}
/* see if an error occured & the last retry failed */
if (0 != result) {
hprintk(KERN_ERR "saa7146_core.o: master_xfer: could not transfer i2c-message\n");
return -EIO;
}
/* if any things had to be read, get the results */
result = clean_up(msgs, num, a->i2c);
if ( 0 > result ) {
hprintk(KERN_ERR "saa7146_core.o: master_xfer: could not cleanup\n");
return -EIO;
}
/* return the number of delivered messages */
return num;
}
static
int master_xfer (struct dvb_i2c_bus *i2c, struct i2c_msg msgs[], int num)
{
int retries = SAA7146_I2C_RETRIES;
int ret;
do {
ret = do_master_xfer (i2c, msgs, num);
} while (ret != num && retries--);
return ret;
}
/* registering functions to load algorithms at runtime */
int i2c_saa7146_add_bus (struct saa7146 *saa)
{
/* enable i2c-port pins */
saa7146_write (saa->mem, MC1, (MASK_08 | MASK_24));
sprintf(saa->name, "saa7146(%d)", saa->dvb_adapter->num);
saa->i2c_bus = dvb_register_i2c_bus (master_xfer, saa,
saa->dvb_adapter, 0);
if (!saa->i2c_bus)
return -ENOMEM;
return 0;
}
void i2c_saa7146_del_bus (struct saa7146 *saa)
{
dvb_unregister_i2c_bus (master_xfer,
saa->i2c_bus->adapter, saa->i2c_bus->id);
dvb_unregister_adapter (saa->dvb_adapter);
}
/* ---------------------------------------------*/
/* debug-helper function: dump-registers */
/* ---------------------------------------------*/
void dump_registers(unsigned char* mem) {
u16 j = 0;
for( j = 0x0; j < 0x1fe; j+=0x4 ) {
printk("0x%03x: 0x%08x\n",j,saa7146_read(mem,j));
}
}
/* -----------------------------------------------------*/
/* dispatcher-function for handling external commands */
/* -----------------------------------------------------*/
static int saa7146_core_command (struct dvb_i2c_bus *i2c, unsigned int cmd, void *arg)
{
int i = 0, result = -ENOIOCTLCMD;
struct saa7146* saa = i2c->data;
dprintk("saa7146_core.o: ==> saa7146_core_command\n");
if( NULL == saa)
return -EINVAL;
/* first let the extensions handle the command */
for (i = 0; i < SAA7146_MAX_EXTENSIONS; i++) {
if (NULL != saa7146_ext[i]) {
if( -ENOIOCTLCMD != (result = saa7146_ext[i]->command(saa, saa->data[i], cmd, arg))) {
break;
}
}
}
/* if command has not been handled by an extension, handle it now */
if( result == -ENOIOCTLCMD ) {
switch(cmd) {
case SAA7146_DUMP_REGISTERS:
{
dump_registers(saa->mem);
break;
}
case SAA7146_SET_DD1:
{
u32 *i = arg;
dprintk(KERN_ERR "saa7146_core.o: SAA7146_SET_DD1 to 0x%08x\n",*i);
/* set dd1 port register */
saa7146_write(saa->mem, DD1_INIT, *i);
/* write out init-values */
saa7146_write(saa->mem,MC2, (MASK_09 | MASK_10 | MASK_26 | MASK_26));
break;
}
case SAA7146_DO_MMAP:
{
struct vm_area_struct *vma = arg;
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long start = vma->vm_start;
unsigned long page,pos;
dprintk(KERN_ERR "saa7146_core.o: SAA7146_DO_MMAP.\n");
if (size > saa->buffers * GRABBING_MEM_SIZE)
return -EINVAL;
if ( NULL == saa->grabbing )
return -EINVAL;
pos=(unsigned long)saa->grabbing;
while (size > 0)
{
page = kvirt_to_pa(pos);
if (remap_page_range(vma, start, page,
PAGE_SIZE, PAGE_SHARED))
return -EAGAIN;
start += PAGE_SIZE;
pos += PAGE_SIZE;
size -= PAGE_SIZE;
}
break;
}
case SAA7146_DEBI_TRANSFER: {
struct saa7146_debi_transfer *dt = arg;
printk("saa7146_core.o: SAA7146_DEBI_TRANSFER\n");
printk("saa7146_core.o: timeout:%d, swap:%d, slave16:%d, increment:%d, intel:%d, tien:%d\n", dt->timeout, dt->swap, dt->slave16, dt->increment, dt->intel, dt->tien);
printk("saa7146_core.o: address:0x%04x, num_bytes:%d, direction:%d, mem:0x%08x\n",dt->address,dt->address,dt->direction,dt->mem);
debi_transfer(saa, dt);
break;
}
default: {
return -ENOIOCTLCMD;
}
}
}
return 0;
}
/* -----------------------------------------------------*/
/* dispatcher-function for handling irq-events */
/* -----------------------------------------------------*/
/* irq-handler function */
static void saa7146_irq(int irq, void *dev_id, struct pt_regs * regs)
{
struct saa7146 *saa = (struct saa7146 *)dev_id;
u32 isr = 0;
int i;
int count = 0;
/* process all interrupts */
while (1) {
/* read out the primary status register */
isr = saa7146_read(saa->mem, ISR);
/* clear all IRQs */
saa7146_write(saa->mem, ISR, isr);
/* is anything to do? */
if ( 0 == isr )
return;
dprintk("%s: irq-call: isr:0x%08x\n",saa->name,isr);
/* first let the extensions handle the interrupt */
for (i = 0; i < SAA7146_MAX_EXTENSIONS; i++)
if (saa7146_ext[i] &&
(isr&saa7146_ext[i]->handles_irqs)) {
saa7146_ext[i]->irq_handler(saa, isr, saa->data[i]);
//saa7146_write(saa->mem, ISR, saa7146_ext[i]->handles_irqs);
}
//printk(KERN_ERR "%s: unhandled interrupt: 0x%08x\n", saa->name, isr);
/* see if we are in a hard interrupt loop */
++count;
if (count > 10)
printk (KERN_WARNING "%s: irq loop %d\n", saa->name, count);
if (count > 20) {
saa7146_write(saa->mem, IER, 0x00000000);
printk(KERN_ERR "%s: IRQ lockup, cleared int mask\n", saa->name);
break;
}
}
}
/* -----------------------------------------------------
functions for finding any saa7146s in the system,
inserting/removing module for kernel, etc.
-----------------------------------------------------*/
int configure_saa7146 (struct saa7146 *saa)
{
u32 rev = 0;
int result = 0;
hprintk("saa7146_core.o: ==> configure_saa7146\n");
/* check module-parameters for sanity */
/* check if wanted number of video-buffers is valid, otherwise fix it */
//if (buffers < 2)
// buffers = 2;
if ( buffers > SAA7146_MAX_BUF )
buffers = SAA7146_MAX_BUF;
/* check if mode is supported */
switch( mode ) {
/* 0 = pal, 1 = ntsc */
case 0:
case 1:
{
break;
}
/* default to pal */
default:
{
mode = 0;
break;
}
}
/* get chip-revision; this is needed to enable bug-fixes */
if( 0 > pci_read_config_dword(saa->device, 0x08, &rev)) {
printk (KERN_ERR
"saa7146_core.o: cannot read from pci-device!\n");
return -1;
}
saa->revision = (rev & 0xf);
/* remap the memory from virtual to physical adress */
saa->mem = ioremap ((saa->device->resource[0].start)
&PCI_BASE_ADDRESS_MEM_MASK, 0x1000);
if ( !saa->mem ) {
printk(KERN_ERR "saa7146_core.o: cannot map pci-address!\n");
return -EFAULT;
}
/* get clipping memory */
saa->clipping = (u32*) kmalloc (CLIPPING_MEM_SIZE*sizeof(u32),GFP_KERNEL);
if ( !saa->clipping ) {
printk(KERN_ERR "saa7146_core.o: not enough kernel-memory for clipping!\n");
return -ENOMEM;
}
memset(saa->clipping, 0x0, CLIPPING_MEM_SIZE*sizeof(u32));
/* get i2c memory */
saa->i2c = (u32*) kmalloc (I2C_MEM_SIZE*sizeof(u32),GFP_KERNEL); /*64*/
if ( !saa->i2c ) {
printk(KERN_ERR "saa7146_core.o: not enough kernel-memory for i2c!\n");
kfree(saa->clipping);
return -ENOMEM;
}
memset(saa->i2c, 0x0, I2C_MEM_SIZE*sizeof(u32));
/* get grabbing memory */
saa->grabbing = (u32*) rvmalloc (buffers, &saa->page_table[0]);
if ( !saa->grabbing ) {
printk(KERN_ERR "saa7146_core.o: not enough kernel-memory for grabbing_mem!\n");
kfree(saa->i2c);
kfree(saa->clipping);
return -ENOMEM;
}
/* get rps0 memory */
saa->rps0 = (u32*) kmalloc (RPS_MEM_SIZE*sizeof(u32),GFP_KERNEL);
if ( !saa->rps0 ) {
printk(KERN_ERR "saa7146_core.o: not enough kernel-memory for rps0_mem!\n");
kfree(saa->i2c);
kfree(saa->clipping);
rvfree(saa->grabbing, buffers, &saa->page_table[0]);
return -ENOMEM;
}
memset(saa->rps0, 0x0, RPS_MEM_SIZE*sizeof(u32));
/* get rps1 memory */
saa->rps1 = (u32*) kmalloc (RPS_MEM_SIZE*sizeof(u32),GFP_KERNEL);
if ( !saa->rps1 ) {
printk(KERN_ERR "saa7146_core.o: not enough kernel-memory for rps1_mem!\n");
kfree(saa->rps0);
kfree(saa->i2c);
kfree(saa->clipping);
rvfree(saa->grabbing, buffers, &saa->page_table[0]);
return -1;
}
memset(saa->rps1, 0x0, RPS_MEM_SIZE*sizeof(u32));
/* get debi memory (32kB) */
saa->debi = (u32*) kmalloc (8192*sizeof(u32),GFP_KERNEL);
if ( !saa->debi ) {
printk(KERN_ERR "saa7146_core.o: not enough kernel-memory for debi_mem!\n");
kfree(saa->rps1);
kfree(saa->rps0);
kfree(saa->i2c);
kfree(saa->clipping);
rvfree(saa->grabbing, buffers, &saa->page_table[0]);
return -1;
}
memset(saa->debi, 0x0, 8192*sizeof(u32));
/* clear out memory for grabbing information */
memset(&saa->grab_width[0], 0x0, sizeof(int)*SAA7146_MAX_BUF);
memset(&saa->grab_height[0], 0x0, sizeof(int)*SAA7146_MAX_BUF);
memset(&saa->grab_format[0], 0x0, sizeof(int)*SAA7146_MAX_BUF);
memset(&saa->grab_port[0], 0x0, sizeof(int)*SAA7146_MAX_BUF);
/* init the frame-status array */
memset(&saa->frame_stat[0], GBUFFER_UNUSED, sizeof(int)*SAA7146_MAX_BUF);
/* clear out all wait queues */
init_waitqueue_head(&saa->rps0_wq);
init_waitqueue_head(&saa->rps1_wq);
/* request an interrupt for the saa7146 */
result = request_irq (saa->device->irq, saa7146_irq,
SA_SHIRQ | SA_INTERRUPT, saa->name, (void *) saa);
switch(result) {
case -EINVAL:
{
printk(KERN_ERR "saa7146_core.o: Bad irq number or handler\n");
return -EINVAL;
}
case -EBUSY:
{
printk(KERN_ERR "saa7146_core.o: IRQ %d busy, change your PnP config in BIOS\n", saa->device->irq);
return -EBUSY;
}
case 0:
{
break;
}
default:
{
return result;
}
}
/* print status message */
printk(KERN_ERR "saa7146_core.o: %s: bus:%d, rev:%d, mem:0x%08x.\n", saa->name, saa->device->bus->number, saa->revision, (unsigned int) saa->mem);
/* enable bus-mastering */
pci_set_master( saa->device );
/* disable everything on the saa7146, perform a software-reset */
saa7146_write(saa->mem, MC1, 0xbfff0000);
mdelay(2);
#if 0
{
int j;
/* clear all registers */
for( j = 0x0; j < 0xfc; j+=0x4 ) {
saa7146_write(saa->mem,j, 0x0000000);
}
for( j = 0x104; j < 0x1fc; j+=0x4 ) {
saa7146_write(saa->mem,j, 0x0000000);
}
}
#endif
/* clear out any rps-signals pending */
saa7146_write(saa->mem, MC2, 0xf8000000);
/* enable video-port-pins*/
saa7146_write(saa->mem,MC1, (MASK_10 | MASK_26));
/* disable all interrupt-conditions, only enable RPS interrupts */
saa7146_write(saa->mem, ISR, 0xffffffff);
saa7146_write(saa->mem, IER, (MASK_27 | MASK_28));
/*
printk("main: 0x114: 0x%08x\n",saa7146_read(saa->mem, 0x114));
printk("main: 0x0e4: 0x%08x\n",saa7146_read(saa->mem, 0x0e4));
printk("PSR: 0x%08x\n",saa7146_read(saa->mem, PSR));
printk("SSR: 0x%08x\n",saa7146_read(saa->mem, SSR));
printk("IER: 0x%08x\n",saa7146_read(saa->mem, IER));
printk("ISR: 0x%08x\n",saa7146_read(saa->mem, ISR));
*/
saa7146_write(saa->mem,PCI_BT_V1, 0x1c00101f);
saa7146_write(saa->mem,BCS_CTRL, 0x80400040);
/* set dd1 stream a & b */
saa7146_write(saa->mem, DD1_STREAM_B, 0x00000000);
saa7146_write(saa->mem, DD1_INIT, 0x02000000);
saa7146_write(saa->mem, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
saa7146_write(saa->mem, MC2, 0x077c077c);
/* the Siemens DVB needs this if you want to have the i2c chips
get recognized before the main driver is loaded
*/
saa7146_write(saa->mem, GPIO_CTRL, 0x500000);
saa->command = &saa7146_core_command;
saa->buffers = buffers;
saa->mode = mode;
saa->interlace = 1;
i2c_saa7146_add_bus (saa);
saa7146_write(saa->mem, GPIO_CTRL, 0x000000);
return 0;
}
void saa7146_foreach (void (*callback) (struct saa7146* saa, void *data),
void *data)
{
struct list_head *entry;
list_for_each (entry, &saa7146_list) {
struct saa7146* saa;
saa = list_entry (entry, struct saa7146, list_head);
callback (saa, data);
}
}
static
void saa7146_attach_extension (struct saa7146* saa, void *data)
{
int ext_id = (int) data;
saa7146_ext[ext_id]->attach (saa, &saa->data[ext_id]);
}
static
void saa7146_detach_extension (struct saa7146* saa, void *data)
{
int ext_id = (int) data;
saa7146_ext[ext_id]->detach (saa, &saa->data[ext_id]);
}
int saa7146_add_extension(struct saa7146_extension* ext)
{
int ext_id = 0;
for (ext_id = 0; ext_id < SAA7146_MAX_EXTENSIONS; ext_id++) {
if (NULL == saa7146_ext[ext_id])
break;
if (SAA7146_MAX_EXTENSIONS == ext_id) {
printk(KERN_WARNING "saa7146.o: attach_extension(%s) - "
"enlarge SAA7146_MAX_EXTENSIONS.\n",ext->name);
return -ENOMEM;
}
}
saa7146_ext[ext_id] = ext;
saa7146_extension_count++;
if (ext->attach)
saa7146_foreach (saa7146_attach_extension, (void*) ext_id);
return 0;
}
int saa7146_del_extension(struct saa7146_extension* ext)
{
int ext_id = 0;
for (ext_id = 0; ext_id < SAA7146_MAX_EXTENSIONS; ext_id++)
if (ext == saa7146_ext[ext_id])
break;
if (SAA7146_MAX_EXTENSIONS == ext_id) {
printk("%s: detach_extension extension [%s] not found.\n",
__FUNCTION__, ext->name);
return -ENODEV;
}
if (ext->detach)
saa7146_foreach (saa7146_detach_extension, (void*) ext_id);
saa7146_ext[ext_id] = NULL;
saa7146_extension_count--;
return 0;
}
static
void remove_saa7146(struct saa7146 *saa)
{
i2c_saa7146_del_bus (saa);
/* shut down all dma transfers */
saa7146_write(saa->mem, MC1, 0xbfff0000);
dprintk("free irqs\n");
/* disable alle irqs, release irq-routine */
saa7146_write(saa->mem, IER, 0x00);
saa7146_write(saa->mem, ISR, 0xffffffff);
free_irq(saa->device->irq, (void *)saa);
dprintk("unmap memory\n");
/* unmap the memory, if necessary */
if (saa->mem)
iounmap((unsigned char *)((unsigned int)saa->mem));
dprintk("release grabbing memory\n");
/* release grabbing memory */
if(saa->grabbing)
rvfree(saa->grabbing, buffers, &saa->page_table[0]);
dprintk("release other memory\n");
/* release clipping, i2c, rps0 memory */
kfree(saa->clipping);
kfree(saa->i2c);
kfree(saa->rps0);
kfree(saa->rps1);
kfree(saa->debi);
}
static int saa7146_suspend(struct pci_dev *pdev, u32 state)
{
printk("saa7146_suspend()\n");
saa7146_core_command(((struct saa7146 *) pdev->driver_data)->i2c_bus,
SAA7146_SUSPEND, 0);
return 0;
}
static int
saa7146_resume(struct pci_dev *pdev)
{
printk("saa7146_resume()\n");
saa7146_core_command(((struct saa7146 *) pdev->driver_data)->i2c_bus,
SAA7146_RESUME, 0);
return 0;
}
struct card_info {
int type;
char *name;
};
static
int __devinit saa7146_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct dvb_adapter_s *adap;
struct saa7146 *saa;
int card_type;
struct card_info *cinfo= (struct card_info *) ent->driver_data;
dprintk("saa7146_init_one()\n");
card_type = cinfo->type;
dvb_register_adapter(&adap, cinfo->name);
if (!(saa = kmalloc (sizeof (struct saa7146), GFP_KERNEL))) {
printk ("%s: out of memory!\n", __FUNCTION__);
return -ENOMEM;
}
memset (saa, 0, sizeof (struct saa7146));
saa->device = pdev;
saa->device->driver_data = saa;
saa->card_type = card_type;
saa->dvb_adapter = adap;
pci_enable_device (saa->device);
configure_saa7146 (saa);
list_add_tail (&saa->list_head, &saa7146_list);
return 0;
}
static
void __devexit saa7146_remove_one (struct pci_dev *pdev)
{
struct saa7146 *saa = pdev->driver_data;
dprintk("saa7146_remove_one()\n");
list_del (&saa->list_head);
pci_disable_device(pdev);
remove_saa7146 (saa);
}
static struct card_info fs_1_5 = { DVB_CARD_TT_SIEMENS, "Siemens cable card PCI rev1.5" };
static struct card_info fs_1_3 = { DVB_CARD_TT_SIEMENS, "Siemens/Technotrend/Hauppauge PCI rev1.3" };
static struct card_info ttbs = { DVB_CARD_TT_BUDGET, "TT-Budget/WinTV-NOVA-S PCI" };
static struct card_info ttbc = { DVB_CARD_TT_BUDGET, "TT-Budget/WinTV-NOVA-C PCI" };
static struct card_info ttbt = { DVB_CARD_TT_BUDGET, "TT-Budget/WinTV-NOVA-T PCI" };
static struct card_info ttbci = { DVB_CARD_TT_BUDGET_CI, "TT-Budget/WinTV-NOVA-CI PCI" };
static struct card_info satel = { DVB_CARD_TT_BUDGET, "SATELCO Multimedia PCI"};
static struct card_info unkwn = { DVB_CARD_TT_SIEMENS, "Technotrend/Hauppauge PCI rev?(unknown0)?"};
static struct card_info tt_1_6 = { DVB_CARD_TT_SIEMENS, "Technotrend/Hauppauge PCI rev1.3 or 1.6" };
static struct card_info tt_2_1 = { DVB_CARD_TT_SIEMENS, "Technotrend/Hauppauge PCI rev2.1" };
static struct card_info tt_t = { DVB_CARD_TT_SIEMENS, "Technotrend/Hauppauge PCI DVB-T" };
static struct card_info knc1 = { DVB_CARD_KNC1, "KNC1 DVB-S" };
#define PHILIPS_SAA7146 PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146
#define CARD_INFO driver_data: (unsigned long) &
static struct pci_device_id saa7146_pci_tbl[] __devinitdata = {
{ PHILIPS_SAA7146, 0x110a, 0xffff, CARD_INFO fs_1_5 },
{ PHILIPS_SAA7146, 0x110a, 0x0000, CARD_INFO fs_1_5 },
{ PHILIPS_SAA7146, 0x13c2, 0x1003, CARD_INFO ttbs },
{ PHILIPS_SAA7146, 0x13c2, 0x1004, CARD_INFO ttbc },
{ PHILIPS_SAA7146, 0x13c2, 0x1005, CARD_INFO ttbt },
{ PHILIPS_SAA7146, 0x13c2, 0x100c, CARD_INFO ttbci },
{ PHILIPS_SAA7146, 0x13c2, 0x1013, CARD_INFO satel },
{ PHILIPS_SAA7146, 0x13c2, 0x0000, CARD_INFO fs_1_3 },
{ PHILIPS_SAA7146, 0x13c2, 0x1002, CARD_INFO unkwn },
{ PHILIPS_SAA7146, 0x13c2, 0x0001, CARD_INFO tt_1_6 },
{ PHILIPS_SAA7146, 0x13c2, 0x0002, CARD_INFO tt_2_1 },
{ PHILIPS_SAA7146, 0x13c2, 0x0003, CARD_INFO tt_2_1 },
{ PHILIPS_SAA7146, 0x13c2, 0x0004, CARD_INFO tt_2_1 },
{ PHILIPS_SAA7146, 0x13c2, 0x0006, CARD_INFO tt_1_6 },
{ PHILIPS_SAA7146, 0x13c2, 0x0008, CARD_INFO tt_t },
{ PHILIPS_SAA7146, 0xffc2, 0x0000, CARD_INFO unkwn },
{ PHILIPS_SAA7146, 0x1131, 0x4f56, CARD_INFO knc1 },
{ 0,},
};
MODULE_DEVICE_TABLE(pci, saa7146_pci_tbl);
static struct pci_driver saa7146_driver = {
name: "saa7146",
id_table: saa7146_pci_tbl,
probe: saa7146_init_one,
remove: saa7146_remove_one,
suspend: saa7146_suspend,
resume: saa7146_resume,
};
static
int __init saa7146_init_module(void)
{
int err;
dprintk("saa7146_init_module\n");
if ((err = pci_module_init(&saa7146_driver)))
return err;
if ((err = saa7146_v4l_init ()))
return err;
if ((err = av7110_init ()))
return err;
if ((err = av7110_ir_init ()))
return err;
return 0;
}
static
void __exit saa7146_cleanup_module(void)
{
av7110_ir_exit ();
av7110_exit ();
saa7146_v4l_exit ();
pci_unregister_driver(&saa7146_driver);
}
module_init(saa7146_init_module);
module_exit(saa7146_cleanup_module);
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>, "
"Christian Theiss <mistert@rz.fh-augsburg.de>, "
"Ralph Metzler <rjkm@convergence.de>, "
"Marcus Metzler <mocm@convergence.de>, "
"Holger Waechtler <holger@convergence.de> and others");
MODULE_DESCRIPTION("driver for saa7146/av7110 based DVB PCI cards");
MODULE_LICENSE("GPL");
MODULE_PARM(mode,"i");
MODULE_PARM(saa7146_debug,"i");
MODULE_PARM(buffers,"i");
#ifndef __SAA7146_CORE__
#define __SAA7146_CORE__
#include <asm/io.h> /* definitions of u32 etc. */
#include "../dvb-core/dvbdev.h"
#if LINUX_VERSION_CODE < 0x020300
#define DECLARE_MUTEX(foo) struct semaphore foo = MUTEX
#define DECLARE_MUTEX_LOCKED(foo) struct semaphore foo = MUTEX_LOCKED
#define WAIT_QUEUE struct wait_queue*
#define init_waitqueue_head(wq) *(wq) = NULL;
#else
#define WAIT_QUEUE wait_queue_head_t
#endif
/* maximum number of capture frames we support */
#define SAA7146_MAX_BUF 5
/* maximum number of extensions we support */
#define SAA7146_MAX_EXTENSIONS 4
/* stuff for writing to saa7146 */
#define saa7146_write(mem,adr,dat) writel((dat),(mem+(adr)))
#define saa7146_read(mem,adr) readl(mem+(adr))
#define DVB_CARD_TT_SIEMENS 0
#define DVB_CARD_TT_BUDGET 1
#define DVB_CARD_TT_BUDGET_CI 2
#define DVB_CARD_KNC1 3
/* this struct contains some constants needed for horizontal and vertical scaling.
currently we only support PAL (mode=0)and NTSC (mode=1). */
struct saa7146 {
char name[32]; /* give it a nice name */
struct list_head list_head;
dvb_adapter_t *dvb_adapter;
struct dvb_i2c_bus *i2c_bus;
struct pci_dev *device;
int card_type;
void* data[SAA7146_MAX_EXTENSIONS]; /* data hooks for extensions */
int (*command) (struct dvb_i2c_bus *i, unsigned int cmd, void *arg);
unsigned char* mem; /* pointer to mapped IO memory */
int revision; /* chip revision; needed for bug-workarounds*/
int interlace;
int mode;
u32* i2c; /* i2c memory */
u32* grabbing; /* grabbing memory */
u32* clipping; /* clipping memory for mask or rectangle clipping*/
u32* rps0; /* memory for rps0-program */
u32* rps1; /* memory for rps1-program */
u32* debi; /* memory for debi-transfers */
int buffers; /* number of grabbing-buffers */
u32* page_table[SAA7146_MAX_BUF]; /* page_tables for buffers*/
int frame_stat[SAA7146_MAX_BUF]; /* status of grabbing buffers */
int grab_width[SAA7146_MAX_BUF]; /* pixel width of grabs */
int grab_height[SAA7146_MAX_BUF]; /* pixel height of grabs */
int grab_format[SAA7146_MAX_BUF]; /* video format of grabs */
int grab_port[SAA7146_MAX_BUF]; /* video port for grab */
WAIT_QUEUE rps0_wq; /* rps0 interrupt queue (=> capture) */
WAIT_QUEUE rps1_wq; /* rps1 interrupt queue (=> i2c, ...) */
};
#define SAA7146_IRQ_RPS0
#define SAA7146_IRQ_RPS1
struct saa7146_extension {
char name[32];
u32 handles_irqs;
void (*irq_handler)(struct saa7146*, u32, void*);
int (*command)(struct saa7146*, void*, unsigned int cmd, void *arg);
int (*attach)(struct saa7146*, void**);
int (*detach)(struct saa7146*, void**);
void (*inc_use)(struct saa7146*);
void (*dec_use)(struct saa7146*);
};
extern int saa7146_add_extension(struct saa7146_extension* ext);
extern int saa7146_del_extension(struct saa7146_extension* ext);
/* external grabbing states */
#define GBUFFER_UNUSED 0x000
#define GBUFFER_GRABBING 0x001
#define GBUFFER_DONE 0x002
#define SAA7146_CORE_BASE 200
#define SAA7146_DO_MMAP _IOW('d', (SAA7146_CORE_BASE+11), struct vm_area_struct *)
#define SAA7146_SET_DD1 _IOW('d', (SAA7146_CORE_BASE+12), u32)
#define SAA7146_DUMP_REGISTERS _IOW('d', (SAA7146_CORE_BASE+13), u32)
#define SAA7146_DEBI_TRANSFER _IOW('d', (SAA7146_CORE_BASE+14), struct saa7146_debi_transfer)
#define SAA7146_SUSPEND _IOW('d', (SAA7146_CORE_BASE+32), u32)
#define SAA7146_RESUME _IOW('d', (SAA7146_CORE_BASE+33), u32)
#endif
#ifndef __INCLUDED_SAA7146__
#define __INCLUDED_SAA7146__
struct saa7146_video_dma {
u32 base_odd;
u32 base_even;
u32 prot_addr;
u32 pitch;
u32 base_page;
u32 num_line_byte;
};
struct saa7146_debi_transfer {
u8 timeout; /* have a look at the specs for reasonable values, p.110 ff */
u8 swap;
u8 slave16;
u8 increment; /* only for block transfers */
u8 intel;
u8 tien;
u16 address;
u16 num_bytes;
u8 direction;
u32 mem; /* either a "pointer" (actually the physical address) of the debi-memory (block-transfer, use virt_to_bus to supply it) or 4 bytes (as one u32-value) for immediate transfer */
};
struct saa7146_modes_constants {
u8 v_offset;
u16 v_field;
u16 v_calc;
u8 h_offset;
u16 h_pixels;
u16 h_calc;
u16 v_max_out;
u16 h_max_out;
};
struct saa7146_mmap_struct
{
const char *adr;
unsigned long size;
};
#define SAA7146_PAL 0
#define SAA7146_NTSC 1
#define SAA7146_SECAM 2
#define SAA7146_HPS_SOURCE_PORT_A 0x00
#define SAA7146_HPS_SOURCE_PORT_B 0x01
#define SAA7146_HPS_SOURCE_YPB_CPA 0x02
#define SAA7146_HPS_SOURCE_YPA_CPB 0x03
#define SAA7146_HPS_SYNC_PORT_A 0x00
#define SAA7146_HPS_SYNC_PORT_B 0x01
/* Number of vertical active lines */
#define V_ACTIVE_LINES_PAL 576
#define V_ACTIVE_LINES_NTSC 480
#define V_ACTIVE_LINES_SECAM 576
/* Number of lines in a field for HPS to process */
#define V_FIELD_PAL 288
#define V_FIELD_NTSC 240
#define V_FIELD_SECAM 288
/* Number of lines of vertical offset before processing */
#define V_OFFSET_NTSC 0x10 /* PLI */
#define V_OFFSET_PAL 0x15
#define V_OFFSET_SECAM 0x14
/* Number of horizontal pixels to process */
#define H_PIXELS_NTSC 708
#define H_PIXELS_PAL 720
#define H_PIXELS_SECAM 720
/* Horizontal offset of processing window */
#define H_OFFSET_NTSC 0x40 /* PLI Try 0x3f and find all red colors turning into blue !!?? */
#define H_OFFSET_PAL 0x3a
#define H_OFFSET_SECAM 0x14
/* some memory-sizes */
#define GRABBING_MEM_SIZE 0x240000 /* 1024 * 576 * 4*/
#define CLIPPING_MEM_SIZE 20000 /* 1024 * 625 / 32 */
#define I2C_MEM_SIZE 0x000800 /* 2048 */
#define RPS_MEM_SIZE 0x000800 /* 2048 */
/************************************************************************/
/* UNSORTED */
/************************************************************************/
#define ME1 0x0000000800
#define PV1 0x0000000008
/************************************************************************/
/* CLIPPING */
/************************************************************************/
/* some defines for the various clipping-modes */
#define SAA7146_CLIPPING_RECT 0x4
#define SAA7146_CLIPPING_RECT_INVERTED 0x5
#define SAA7146_CLIPPING_MASK 0x6
#define SAA7146_CLIPPING_MASK_INVERTED 0x7
/************************************************************************/
/* RPS */
/************************************************************************/
#define CMD_NOP 0x00000000 /* No operation */
#define CMD_CLR_EVENT 0x00000000 /* Clear event */
#define CMD_SET_EVENT 0x10000000 /* Set signal event */
#define CMD_PAUSE 0x20000000 /* Pause */
#define CMD_CHECK_LATE 0x30000000 /* Check late */
#define CMD_UPLOAD 0x40000000 /* Upload */
#define CMD_STOP 0x50000000 /* Stop */
#define CMD_INTERRUPT 0x60000000 /* Interrupt */
#define CMD_JUMP 0x80000000 /* Jump */
#define CMD_WR_REG 0x90000000 /* Write (load) register */
#define CMD_RD_REG 0xa0000000 /* Read (store) register */
#define CMD_WR_REG_MASK 0xc0000000 /* Write register with mask */
/************************************************************************/
/* OUTPUT FORMATS */
/************************************************************************/
/* output formats; each entry holds three types of information */
/* composed is used in the sense of "not-planar" */
#define RGB15_COMPOSED 0x213
/* this means: yuv2rgb-conversation-mode=2, dither=yes(=1), format-mode = 3 */
#define RGB16_COMPOSED 0x210
#define RGB24_COMPOSED 0x201
#define RGB32_COMPOSED 0x202
#define YUV411_COMPOSED 0x003
/* this means: yuv2rgb-conversation-mode=0, dither=no(=0), format-mode = 3 */
#define YUV422_COMPOSED 0x000
#define YUV411_DECOMPOSED 0x00b
#define YUV422_DECOMPOSED 0x009
#define YUV420_DECOMPOSED 0x00a
/************************************************************************/
/* MISC */
/************************************************************************/
/* Bit mask constants */
#define MASK_00 0x00000001 /* Mask value for bit 0 */
#define MASK_01 0x00000002 /* Mask value for bit 1 */
#define MASK_02 0x00000004 /* Mask value for bit 2 */
#define MASK_03 0x00000008 /* Mask value for bit 3 */
#define MASK_04 0x00000010 /* Mask value for bit 4 */
#define MASK_05 0x00000020 /* Mask value for bit 5 */
#define MASK_06 0x00000040 /* Mask value for bit 6 */
#define MASK_07 0x00000080 /* Mask value for bit 7 */
#define MASK_08 0x00000100 /* Mask value for bit 8 */
#define MASK_09 0x00000200 /* Mask value for bit 9 */
#define MASK_10 0x00000400 /* Mask value for bit 10 */
#define MASK_11 0x00000800 /* Mask value for bit 11 */
#define MASK_12 0x00001000 /* Mask value for bit 12 */
#define MASK_13 0x00002000 /* Mask value for bit 13 */
#define MASK_14 0x00004000 /* Mask value for bit 14 */
#define MASK_15 0x00008000 /* Mask value for bit 15 */
#define MASK_16 0x00010000 /* Mask value for bit 16 */
#define MASK_17 0x00020000 /* Mask value for bit 17 */
#define MASK_18 0x00040000 /* Mask value for bit 18 */
#define MASK_19 0x00080000 /* Mask value for bit 19 */
#define MASK_20 0x00100000 /* Mask value for bit 20 */
#define MASK_21 0x00200000 /* Mask value for bit 21 */
#define MASK_22 0x00400000 /* Mask value for bit 22 */
#define MASK_23 0x00800000 /* Mask value for bit 23 */
#define MASK_24 0x01000000 /* Mask value for bit 24 */
#define MASK_25 0x02000000 /* Mask value for bit 25 */
#define MASK_26 0x04000000 /* Mask value for bit 26 */
#define MASK_27 0x08000000 /* Mask value for bit 27 */
#define MASK_28 0x10000000 /* Mask value for bit 28 */
#define MASK_29 0x20000000 /* Mask value for bit 29 */
#define MASK_30 0x40000000 /* Mask value for bit 30 */
#define MASK_31 0x80000000 /* Mask value for bit 31 */
#define MASK_B0 0x000000ff /* Mask value for byte 0 */
#define MASK_B1 0x0000ff00 /* Mask value for byte 1 */
#define MASK_B2 0x00ff0000 /* Mask value for byte 2 */
#define MASK_B3 0xff000000 /* Mask value for byte 3 */
#define MASK_W0 0x0000ffff /* Mask value for word 0 */
#define MASK_W1 0xffff0000 /* Mask value for word 1 */
#define MASK_PA 0xfffffffc /* Mask value for physical address */
#define MASK_PR 0xfffffffe /* Mask value for protection register */
#define MASK_ER 0xffffffff /* Mask value for the entire register */
#define MASK_NONE 0x00000000 /* No mask */
/************************************************************************/
/* REGISTERS */
/************************************************************************/
#define BASE_ODD1 0x00 /* Video DMA 1 registers */
#define BASE_EVEN1 0x04
#define PROT_ADDR1 0x08
#define PITCH1 0x0C
#define BASE_PAGE1 0x10 /* Video DMA 1 base page */
#define NUM_LINE_BYTE1 0x14
#define BASE_ODD2 0x18 /* Video DMA 2 registers */
#define BASE_EVEN2 0x1C
#define PROT_ADDR2 0x20
#define PITCH2 0x24
#define BASE_PAGE2 0x28 /* Video DMA 2 base page */
#define NUM_LINE_BYTE2 0x2C
#define BASE_ODD3 0x30 /* Video DMA 3 registers */
#define BASE_EVEN3 0x34
#define PROT_ADDR3 0x38
#define PITCH3 0x3C
#define BASE_PAGE3 0x40 /* Video DMA 3 base page */
#define NUM_LINE_BYTE3 0x44
#define PCI_BT_V1 0x48 /* Video/FIFO 1 */
#define PCI_BT_V2 0x49 /* Video/FIFO 2 */
#define PCI_BT_V3 0x4A /* Video/FIFO 3 */
#define PCI_BT_DEBI 0x4B /* DEBI */
#define PCI_BT_A 0x4C /* Audio */
#define DD1_INIT 0x50 /* Init setting of DD1 interface */
#define DD1_STREAM_B 0x54 /* DD1 B video data stream handling */
#define DD1_STREAM_A 0x56 /* DD1 A video data stream handling */
#define BRS_CTRL 0x58 /* BRS control register */
#define HPS_CTRL 0x5C /* HPS control register */
#define HPS_V_SCALE 0x60 /* HPS vertical scale */
#define HPS_V_GAIN 0x64 /* HPS vertical ACL and gain */
#define HPS_H_PRESCALE 0x68 /* HPS horizontal prescale */
#define HPS_H_SCALE 0x6C /* HPS horizontal scale */
#define BCS_CTRL 0x70 /* BCS control */
#define CHROMA_KEY_RANGE 0x74
#define CLIP_FORMAT_CTRL 0x78 /* HPS outputs formats & clipping */
#define DEBI_CONFIG 0x7C
#define DEBI_COMMAND 0x80
#define DEBI_PAGE 0x84
#define DEBI_AD 0x88
#define I2C_TRANSFER 0x8C
#define I2C_STATUS 0x90
#define BASE_A1_IN 0x94 /* Audio 1 input DMA */
#define PROT_A1_IN 0x98
#define PAGE_A1_IN 0x9C
#define BASE_A1_OUT 0xA0 /* Audio 1 output DMA */
#define PROT_A1_OUT 0xA4
#define PAGE_A1_OUT 0xA8
#define BASE_A2_IN 0xAC /* Audio 2 input DMA */
#define PROT_A2_IN 0xB0
#define PAGE_A2_IN 0xB4
#define BASE_A2_OUT 0xB8 /* Audio 2 output DMA */
#define PROT_A2_OUT 0xBC
#define PAGE_A2_OUT 0xC0
#define RPS_PAGE0 0xC4 /* RPS task 0 page register */
#define RPS_PAGE1 0xC8 /* RPS task 1 page register */
#define RPS_THRESH0 0xCC /* HBI threshold for task 0 */
#define RPS_THRESH1 0xD0 /* HBI threshold for task 1 */
#define RPS_TOV0 0xD4 /* RPS timeout for task 0 */
#define RPS_TOV1 0xD8 /* RPS timeout for task 1 */
#define IER 0xDC /* Interrupt enable register */
#define GPIO_CTRL 0xE0 /* GPIO 0-3 register */
#define EC1SSR 0xE4 /* Event cnt set 1 source select */
#define EC2SSR 0xE8 /* Event cnt set 2 source select */
#define ECT1R 0xEC /* Event cnt set 1 thresholds */
#define ECT2R 0xF0 /* Event cnt set 2 thresholds */
#define ACON1 0xF4
#define ACON2 0xF8
#define MC1 0xFC /* Main control register 1 */
#define MC2 0x100 /* Main control register 2 */
#define RPS_ADDR0 0x104 /* RPS task 0 address register */
#define RPS_ADDR1 0x108 /* RPS task 1 address register */
#define ISR 0x10C /* Interrupt status register */
#define PSR 0x110 /* Primary status register */
#define SSR 0x114 /* Secondary status register */
#define EC1R 0x118 /* Event counter set 1 register */
#define EC2R 0x11C /* Event counter set 2 register */
#define PCI_VDP1 0x120 /* Video DMA pointer of FIFO 1 */
#define PCI_VDP2 0x124 /* Video DMA pointer of FIFO 2 */
#define PCI_VDP3 0x128 /* Video DMA pointer of FIFO 3 */
#define PCI_ADP1 0x12C /* Audio DMA pointer of audio out 1 */
#define PCI_ADP2 0x130 /* Audio DMA pointer of audio in 1 */
#define PCI_ADP3 0x134 /* Audio DMA pointer of audio out 2 */
#define PCI_ADP4 0x138 /* Audio DMA pointer of audio in 2 */
#define PCI_DMA_DDP 0x13C /* DEBI DMA pointer */
#define LEVEL_REP 0x140,
#define A_TIME_SLOT1 0x180, /* from 180 - 1BC */
#define A_TIME_SLOT2 0x1C0, /* from 1C0 - 1FC */
/************************************************************************/
/* ISR-MASKS */
/************************************************************************/
#define SPCI_PPEF 0x80000000 /* PCI parity error */
#define SPCI_PABO 0x40000000 /* PCI access error (target or master abort) */
#define SPCI_PPED 0x20000000 /* PCI parity error on 'real time data' */
#define SPCI_RPS_I1 0x10000000 /* Interrupt issued by RPS1 */
#define SPCI_RPS_I0 0x08000000 /* Interrupt issued by RPS0 */
#define SPCI_RPS_LATE1 0x04000000 /* RPS task 1 is late */
#define SPCI_RPS_LATE0 0x02000000 /* RPS task 0 is late */
#define SPCI_RPS_E1 0x01000000 /* RPS error from task 1 */
#define SPCI_RPS_E0 0x00800000 /* RPS error from task 0 */
#define SPCI_RPS_TO1 0x00400000 /* RPS timeout task 1 */
#define SPCI_RPS_TO0 0x00200000 /* RPS timeout task 0 */
#define SPCI_UPLD 0x00100000 /* RPS in upload */
#define SPCI_DEBI_S 0x00080000 /* DEBI status */
#define SPCI_DEBI_E 0x00040000 /* DEBI error */
#define SPCI_IIC_S 0x00020000 /* I2C status */
#define SPCI_IIC_E 0x00010000 /* I2C error */
#define SPCI_A2_IN 0x00008000 /* Audio 2 input DMA protection / limit */
#define SPCI_A2_OUT 0x00004000 /* Audio 2 output DMA protection / limit */
#define SPCI_A1_IN 0x00002000 /* Audio 1 input DMA protection / limit */
#define SPCI_A1_OUT 0x00001000 /* Audio 1 output DMA protection / limit */
#define SPCI_AFOU 0x00000800 /* Audio FIFO over- / underflow */
#define SPCI_V_PE 0x00000400 /* Video protection address */
#define SPCI_VFOU 0x00000200 /* Video FIFO over- / underflow */
#define SPCI_FIDA 0x00000100 /* Field ID video port A */
#define SPCI_FIDB 0x00000080 /* Field ID video port B */
#define SPCI_PIN3 0x00000040 /* GPIO pin 3 */
#define SPCI_PIN2 0x00000020 /* GPIO pin 2 */
#define SPCI_PIN1 0x00000010 /* GPIO pin 1 */
#define SPCI_PIN0 0x00000008 /* GPIO pin 0 */
#define SPCI_ECS 0x00000004 /* Event counter 1, 2, 4, 5 */
#define SPCI_EC3S 0x00000002 /* Event counter 3 */
#define SPCI_EC0S 0x00000001 /* Event counter 0 */
/************************************************************************/
/* I2C */
/************************************************************************/
/* time we wait after certain i2c-operations */
#define SAA7146_I2C_DELAY 10
#define SAA7146_I2C_ABORT (1<<7)
#define SAA7146_I2C_SPERR (1<<6)
#define SAA7146_I2C_APERR (1<<5)
#define SAA7146_I2C_DTERR (1<<4)
#define SAA7146_I2C_DRERR (1<<3)
#define SAA7146_I2C_AL (1<<2)
#define SAA7146_I2C_ERR (1<<1)
#define SAA7146_I2C_BUSY (1<<0)
#define SAA7146_I2C_START (0x3)
#define SAA7146_I2C_CONT (0x2)
#define SAA7146_I2C_STOP (0x1)
#define SAA7146_I2C_NOP (0x0)
#define SAA7146_I2C_BUS_BIT_RATE_6400 (0x500)
#define SAA7146_I2C_BUS_BIT_RATE_3200 (0x100)
#define SAA7146_I2C_BUS_BIT_RATE_480 (0x400)
#define SAA7146_I2C_BUS_BIT_RATE_320 (0x600)
#define SAA7146_I2C_BUS_BIT_RATE_240 (0x700)
#define SAA7146_I2C_BUS_BIT_RATE_120 (0x000)
#define SAA7146_I2C_BUS_BIT_RATE_80 (0x200)
#define SAA7146_I2C_BUS_BIT_RATE_60 (0x300)
#endif
/*
video4linux-parts of the saa7146 device driver
Copyright (C) 1998,1999 Michael Hunold <michael@mihu.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h> /* for module-version */
#include <linux/string.h>
#include <linux/slab.h> /* for kmalloc/kfree */
#include <linux/delay.h> /* for delay-stuff */
#include <asm/uaccess.h> /* for copy_to/from_user */
#include <linux/wrapper.h> /* for mem_map_reserve */
#include <linux/vmalloc.h>
#include <linux/videodev.h>
#include "saa7146_defs.h"
#include "saa7146_core.h"
#include "saa7146_v4l.h"
static int saa7146_v4l_debug = 0;
#define dprintk if (saa7146_v4l_debug) printk
#define hprintk if (saa7146_v4l_debug >= 2) printk
#define gprintk if (saa7146_v4l_debug >= 3) printk
#define __COMPILE_SAA7146__
#include "saa7146.c"
/* transform video4linux-cliplist to plain arrays -- we assume that the arrays
are big enough -- if not: your fault! */
int saa7146_v4lclip2plain(struct video_clip *clips, u16 clipcount, int x[], int y[], int width[], int height[])
{
int i = 0;
struct video_clip* vc = NULL;
dprintk("saa7146_v4l.o: ==> saa7146_v4lclip2plain, cc:%d\n",clipcount);
/* anything to do here? */
if( 0 == clipcount )
return 0;
/* copy to kernel-space */
vc = vmalloc(sizeof(struct video_clip)*(clipcount));
if( NULL == vc ) {
printk("saa7146_v4l.o: ==> v4lclip2saa7146_v4l.o: no memory #2!\n");
return -ENOMEM;
}
if(copy_from_user(vc,clips,sizeof(struct video_clip)*clipcount)) {
printk("saa7146_v4l.o: ==> v4lclip2saa7146_v4l.o: could not copy from user-space!\n");
return -EFAULT;
}
/* copy the clip-list to the arrays
note: the video_clip-struct may contain negative values to indicate that a window
doesn't lay completly over the video window. Thus, we correct the values right here */
for(i = 0; i < clipcount; i++) {
if( vc[i].width < 0) {
vc[i].x += vc[i].width; vc[i].width = -vc[i].width;
}
if( vc[i].height < 0) {
vc[i].y += vc[i].height; vc[i].height = -vc[i].height;
}
if( vc[i].x < 0) {
vc[i].width += vc[i].x; vc[i].x = 0;
}
if( vc[i].y < 0) {
vc[i].height += vc[i].y; vc[i].y = 0;
}
if(vc[i].width <= 0 || vc[i].height <= 0) {
vfree(vc);
return -EINVAL;
}
x[i] = vc[i].x;
y[i] = vc[i].y;
width[i] = vc[i].width;
height[i] = vc[i].height;
}
/* free memory used for temporary clips */
vfree(vc);
return 0;
}
struct saa7146_v4l_struct {
struct video_buffer buffer;
struct video_mbuf mbuf;
struct video_window window;
struct video_picture picture;
};
static int saa7146_v4l_command(struct saa7146* saa, void *p, unsigned int cmd, void *arg)
{
struct saa7146_v4l_struct* data = (struct saa7146_v4l_struct*)p;
hprintk("saa7146_v4l.o: ==> saa7146_v4l_command\n");
if( NULL == saa)
return -EINVAL;
switch(cmd) {
case SAA7146_V4L_GPICT:
{
struct video_picture *p = arg;
hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_GPICT\n");
memcpy(p, &data->picture, sizeof(struct video_picture));
}
break;
case SAA7146_V4L_SPICT:
{
struct video_picture *p = arg;
hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_SPICT\n");
memcpy(&data->picture, p, sizeof(struct video_picture));
set_picture_prop(saa, (u32)(data->picture.brightness>>8),(u32)(data->picture.contrast>>9),(u32)(data->picture.colour>>9));
}
break;
case SAA7146_V4L_SWIN:
{
struct video_window *vw = arg;
int *x = NULL, *y = NULL, *w = NULL, *h = NULL;
u32 palette = 0;
hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_SWIN\n");
video_setmode(saa, 0);
saa7146_write(saa->mem, MC1, (MASK_21));
set_window(saa, vw->width, vw->height,0,0,0);
//saa->port, saa->sync);
if (move_to(saa, vw->x, vw->y, vw->height, data->buffer.width,
data->buffer.depth, data->buffer.bytesperline,
(u32)data->buffer.base, 0)<0)
return -1;
switch( data->picture.palette ) {
case VIDEO_PALETTE_RGB555:
palette = RGB15_COMPOSED;
break;
case VIDEO_PALETTE_RGB24:
palette = RGB24_COMPOSED;
break;
case VIDEO_PALETTE_RGB32:
palette = RGB32_COMPOSED;
break;
case VIDEO_PALETTE_UYVY:
palette = YUV422_COMPOSED;
break;
case VIDEO_PALETTE_YUV422P:
palette = YUV422_DECOMPOSED;
break;
case VIDEO_PALETTE_YUV420P:
palette = YUV420_DECOMPOSED;
break;
case VIDEO_PALETTE_YUV411P:
palette = YUV411_DECOMPOSED;
break;
default:
/*case VIDEO_PALETTE_RGB565:*/
palette = RGB16_COMPOSED;
break;
}
set_output_format(saa, palette);
if (vw->flags==VIDEO_CLIP_BITMAP) {
clip_windows(saa, SAA7146_CLIPPING_MASK, vw->width, vw->height,
(u32 *) vw->clips, 1, 0, 0, 0, 0);
} else {
/* this is tricky, but helps us saving kmalloc/kfree-calls
and boring if/else-constructs ... */
x = (int*)kmalloc(sizeof(int)*vw->clipcount*4,GFP_KERNEL);
if( NULL == x ) {
hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_SWIN: out of kernel-memory.\n");
return -ENOMEM;
}
y = x+(1*vw->clipcount);
w = x+(2*vw->clipcount);
h = x+(3*vw->clipcount);
/* transform clipping-windows */
if (0 != saa7146_v4lclip2plain(vw->clips, vw->clipcount,x,y,w,h))
break;
clip_windows(saa, SAA7146_CLIPPING_RECT, vw->width, vw->height,
NULL, vw->clipcount, x, y, w, h);
kfree(x);
memcpy(&data->window, arg, sizeof(struct video_window));
}
video_setmode(saa, 1);
break;
}
case SAA7146_V4L_CCAPTURE:
{
int* i = arg;
hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_CCAPTURE\n");
if ( 0 == *i ) {
video_setmode(saa, 0);
}
else {
video_setmode(saa, 1);
}
break;
}
case SAA7146_V4L_GFBUF:
{
struct video_buffer *b = arg;
hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_GFBUF\n");
memcpy(b, &data->buffer, sizeof(struct video_buffer));
break;
}
case SAA7146_V4L_SFBUF:
{
struct video_buffer *b = arg;
memcpy(&data->buffer, b, sizeof(struct video_buffer));
hprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_SFBUF: b:0x%08x, h:%d, w:%d, d:%d\n", (u32)data->buffer.base, data->buffer.height, data->buffer.width, data->buffer.depth);
break;
}
case SAA7146_V4L_CSYNC:
{
int i = *((int*)arg);
int count = 0, k = 0;
unsigned char* grabbfr;
unsigned char y, uv;
/* sanity checks */
if ( i >= saa->buffers || i < 0) {
gprintk("saa7146_v4l.o: SAA7146_V4L_CSYNC, invalid buffer %d\n",i);
return -EINVAL;
}
/* get the state */
switch ( saa->frame_stat[i] ) {
case GBUFFER_UNUSED:
{
/* there was no grab to this buffer */
gprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_CSYNC, invalid frame (fr:%d)\n",i);
return -EINVAL;
}
case GBUFFER_GRABBING:
{
/* wait to be woken up by the irq-handler */
interruptible_sleep_on(&saa->rps0_wq);
break;
}
case GBUFFER_DONE:
{
gprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_CSYNC, frame done! (fr:%d)\n",i);
break;
}
}
/* all saa7146s below chip-revision 3 are not capable of
doing byte-swaps with video-dma1. for rgb-grabbing this
does not matter, but yuv422-grabbing has the wrong
byte-order, so we have to swap in software */
if ( ( saa->revision<3) &&
(saa->grab_format[i] == YUV422_COMPOSED)) {
/* swap UYVY to YUYV */
count = saa->grab_height[i]*saa->grab_width[i]*2;
grabbfr = ((unsigned char*)(saa->grabbing))+i*GRABBING_MEM_SIZE;
for (k=0; k<count; k=k+2) {
y = grabbfr[k+1];
uv = grabbfr[k];
grabbfr[k] = y;
grabbfr[k+1] = uv;
}
}
/* set corresponding buffer to unused */
saa->frame_stat[i] = GBUFFER_UNUSED;
printk ("saa7146_v4l.o: SAA7146_V4L_CSYNC, frame %i done.\n", i);
break;
}
case SAA7146_V4L_CMCAPTURE:
{
struct video_mmap *vm = arg;
gprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_CMCAPTURE, trying buffer:%d\n", vm->frame);
/* check status for wanted frame */
if ( GBUFFER_GRABBING == saa->frame_stat[vm->frame] ) {
gprintk("saa7146_v4l.o: frame #%d still grabbing!\n",vm->frame);
return -EBUSY;
}
/* do necessary transformations from the videodev-structure to our own format. */
/* sanity checks */
if ( vm->width <= 0 || vm->height <= 0 ) {
gprintk("saa7146_v4l.o: set_up_grabbing, invalid dimension for wanted buffer %d\n",vm->frame);
return -EINVAL;
}
/* set corresponding buffer to grabbing */
saa->frame_stat[vm->frame] = GBUFFER_GRABBING;
/* copy grabbing informtaion for the buffer */
saa->grab_height[vm->frame] = vm->height;
saa->grab_width[vm->frame] = vm->width;
/* fixme: setting of grabbing port ?!*/
saa->grab_port[vm->frame] = 0;
switch( vm->format ) {
case VIDEO_PALETTE_RGB555:
saa->grab_format[vm->frame] = RGB15_COMPOSED;
break;
case VIDEO_PALETTE_RGB24:
saa->grab_format[vm->frame] = RGB24_COMPOSED;
break;
case VIDEO_PALETTE_RGB32:
saa->grab_format[vm->frame] = RGB32_COMPOSED;
break;
case VIDEO_PALETTE_YUV420P:
return -EINVAL;
case VIDEO_PALETTE_YUV422:
saa->grab_format[vm->frame] = YUV422_COMPOSED;
break;
case VIDEO_PALETTE_YUV422P:
saa->grab_format[vm->frame] = YUV422_DECOMPOSED;
break;
case VIDEO_PALETTE_YUV411P:
saa->grab_format[vm->frame] = YUV411_DECOMPOSED;
break;
default:
/*case VIDEO_PALETTE_RGB565:*/
saa->grab_format[vm->frame] = RGB16_COMPOSED;
break;
}
set_up_grabbing(saa,vm->frame);
break;
}
case SAA7146_V4L_GMBUF:
{
struct video_mbuf *m = arg;
int i = 0;
m->size = saa->buffers * GRABBING_MEM_SIZE;
m->frames = saa->buffers;
for(i = 0; i < saa->buffers; i++)
m->offsets[i]=(i*GRABBING_MEM_SIZE);
gprintk(KERN_ERR "saa7146_v4l.o: SAA7146_V4L_GMBUF, providing %d buffers.\n", saa->buffers);
break;
}
default:
return -ENOIOCTLCMD;
}
return 0;
}
int saa7146_v4l_attach(struct saa7146* adap, void** p)
{
struct saa7146_v4l_struct* data;
hprintk("saa7146_v4l.o: ==> saa7146_v4l_inc_use_attach\n");
if (!(data = kmalloc(sizeof(struct saa7146_v4l_struct), GFP_KERNEL))) {
printk (KERN_ERR "%s: out of memory!\n", __FUNCTION__);
return -ENOMEM;
}
*(struct saa7146_v4l_struct**)p = data;
memset(&data->buffer, 0x0, sizeof(struct video_buffer));
memset(&data->mbuf, 0x0, sizeof(struct video_mbuf));
memset(&data->window, 0x0, sizeof(struct video_window));
memset(&data->picture,0x0, sizeof(struct video_picture));
data->picture.brightness = 32768;
data->picture.contrast = 32768;
data->picture.colour = 32768; /* saturation */
data->picture.depth = 16;
data->picture.palette = VIDEO_PALETTE_RGB565;
return 0;
}
void saa7146_v4l_inc_use(struct saa7146* adap)
{
MOD_INC_USE_COUNT;
}
int saa7146_v4l_detach(struct saa7146* adap, void** p)
{
struct saa7146_v4l_struct** data = (struct saa7146_v4l_struct**)p;
kfree(*data);
*data = NULL;
return 0;
}
void saa7146_v4l_dec_use(struct saa7146* adap)
{
MOD_DEC_USE_COUNT;
}
static struct saa7146_extension saa7146_v4l_extension = {
"v4l extension\0",
MASK_27, /* handles rps0 irqs */
saa7146_std_grab_irq_callback_rps0,
saa7146_v4l_command,
saa7146_v4l_attach,
saa7146_v4l_detach,
saa7146_v4l_inc_use,
saa7146_v4l_dec_use
};
int saa7146_v4l_init (void)
{
int res = 0;
if((res = saa7146_add_extension(&saa7146_v4l_extension))) {
printk("saa7146_v4l.o: extension registration failed, module not inserted.\n");
return res;
}
return 0;
}
void saa7146_v4l_exit (void)
{
int res = 0;
if ((res = saa7146_del_extension(&saa7146_v4l_extension))) {
printk("saa7146_v4l.o: extension deregistration failed, module not removed.\n");
}
}
MODULE_PARM(saa7146_v4l_debug, "i");
MODULE_PARM_DESC(saa7146_v4l_debug, "set saa7146_v4l.c in debug mode");
#ifndef __INCLUDED_SAA7146_V4L_V4L__
#define __INCLUDED_SAA7146_V4L_V4L__
/************************************************************************/
/* ADDRESSING */
/************************************************************************/
#define SAA7146_V4L_BASE 100
#define SAA7146_V4L_GPICT _IOW('d', (SAA7146_V4L_BASE+ 1), struct video_picture)
#define SAA7146_V4L_SPICT _IOW('d', (SAA7146_V4L_BASE+ 2), struct video_picture)
#define SAA7146_V4L_GFBUF _IOW('d', (SAA7146_V4L_BASE+ 3), struct video_buffer)
#define SAA7146_V4L_SFBUF _IOW('d', (SAA7146_V4L_BASE+ 4), struct video_buffer)
#define SAA7146_V4L_GMBUF _IOW('d', (SAA7146_V4L_BASE+ 5), struct video_mbuf)
#define SAA7146_V4L_SWIN _IOW('d', (SAA7146_V4L_BASE+ 6), struct video_window)
#define SAA7146_V4L_CCAPTURE _IOW('d', (SAA7146_V4L_BASE+ 7), int)
#define SAA7146_V4L_CMCAPTURE _IOW('d', (SAA7146_V4L_BASE+ 8), struct video_mmap)
#define SAA7146_V4L_CSYNC _IOW('d', (SAA7146_V4L_BASE+ 9), int)
#define SAA7146_V4L_CGSTATUS _IOW('d', (SAA7146_V4L_BASE+10), int)
#define SAA7146_V4L_TSCAPTURE _IOW('d', (SAA7146_V4L_BASE+11), int)
extern int saa7146_v4l_init (void);
extern void saa7146_v4l_exit (void);
#endif
CONFIG_DVB_ALPS_BSRU6
A DVB-S tuner module.
Say Y when you want to support this frontend.
If you don't know what tuner module is soldered on your
DVB adapter simply enable all supported frontends, the
right one will get autodetected.
CONFIG_DVB_ALPS_BSRV2
A DVB-S tuner module. Say Y when you want to support this frontend.
If you don't know what tuner module is soldered on your
DVB adapter simply enable all supported frontends, the
right one will get autodetected.
CONFIG_DVB_GRUNDIG_29504_491
A DVB-S tuner module. Say Y when you want to support this frontend.
If you don't know what tuner module is soldered on your
DVB adapter simply enable all supported frontends, the
right one will get autodetected.
CONFIG_DVB_GRUNDIG_29504_401
A DVB-T tuner module. Say Y when you want to support this frontend.
If you don't know what tuner module is soldered on your
DVB adapter simply enable all supported frontends, the
right one will get autodetected.
CONFIG_DVB_VES1820
The VES1820 Demodulator is used on many DVB-C PCI cards and in some
DVB-C SetTopBoxes. Say Y when you see this demodulator chip near your
tuner module.
If you don't know what tuner module is soldered on your
DVB adapter simply enable all supported frontends, the
right one will get autodetected.
comment 'Supported Frontend Modules'
dep_tristate ' Alps BSRU6 (QPSK)' CONFIG_DVB_ALPS_BSRU6 $CONFIG_DVB_CORE
dep_tristate ' Alps BSRV2 (QPSK)' CONFIG_DVB_ALPS_BSRV2 $CONFIG_DVB_CORE
dep_tristate ' Grundig 29504-491 (QPSK)' CONFIG_DVB_GRUNDIG_29504_491 $CONFIG_DVB_CORE
dep_tristate ' Grundig 29504-401 (OFDM)' CONFIG_DVB_GRUNDIG_29504_401 $CONFIG_DVB_CORE
dep_tristate ' Frontends with external VES1820 demodulator (QAM)' CONFIG_DVB_VES1820 $CONFIG_DVB_CORE
#
# Makefile for the kernel DVB frontend device drivers.
#
EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/
obj-$(CONFIG_DVB_ALPS_BSRU6) += alps_bsru6.o
obj-$(CONFIG_DVB_ALPS_BSRV2) += alps_bsrv2.o
obj-$(CONFIG_DVB_GRUNDIG_29504_491) += grundig_29504-491.o
obj-$(CONFIG_DVB_GRUNDIG_29504_401) += grundig_29504-401.o
obj-$(CONFIG_DVB_VES1820) += ves1820.o
include $(TOPDIR)/Rules.make
/*
Alps BSRU6 DVB QPSK frontend driver
Copyright (C) 2001-2002 Convergence Integrated Media GmbH
<ralph@convergence.de>, <holger@convergence.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
#define dprintk if (debug) printk
#define M_CLK (88000000UL)
/* M=21, K=0, P=0, f_VCO = 4MHz*4*(M+1)/(K+1) = 352 MHz */
static
struct dvb_frontend_info bsru6_info = {
name: "Alps BSRU6",
type: FE_QPSK,
frequency_min: 950000,
frequency_max: 2150000,
frequency_stepsize: 125, /* kHz for QPSK frontends */
frequency_tolerance: M_CLK/2000,
symbol_rate_min: 1000000,
symbol_rate_max: 45000000,
symbol_rate_tolerance: 500, /* ppm */
notifier_delay: 0,
caps: FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK
};
static
u8 init_tab [] = {
0x01, 0x15, // M: 0x15 DIRCLK: 0 K:0
0x02, 0x30, // P: 0 SERCLK: 0 VCO:ON STDBY:0
0x03, 0x00,
0x04, 0x7d, // F22FR, F22=22kHz
0x05, 0x35, // SDAT:0 SCLT:0 I2CT:1
0x06, 0x00, // DAC mode and MSB
0x07, 0x00, // DAC LSB
0x08, 0x43, // DiSEqC
0x09, 0x00,
0x0a, 0x42,
0x0c, 0x51, // QPSK reverse:1 Nyquist:0 OP0 val:1 OP0 con:1 OP1 val:1 OP1 con:1
0x0d, 0x82,
0x0e, 0x23,
0x0f, 0x52,
0x10, 0x3d, // AGC2
0x11, 0x84,
0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on
0x13, 0xb6, // alpha_car b:4 a:0 noise est:256ks derot:on
0x14, 0x93, // beat carc:0 d:0 e:0xf phase detect algo: 1
0x15, 0xc9, // lock detector threshold
0x16, 0x1d,
0x17, 0x0,
0x18, 0x14,
0x19, 0xf2,
0x1a, 0x11,
0x1b, 0x9c,
0x1c, 0x0,
0x1d, 0x0,
0x1e, 0xb,
0x22, 0x00,
0x23, 0x00,
0x24, 0xff,
0x25, 0xff,
0x26, 0xff,
0x28, 0x00, // out imp: normal out type: parallel FEC mode:0
0x29, 0x1e, // 1/2 threshold
0x2a, 0x14, // 2/3 threshold
0x2b, 0x0f, // 3/4 threshold
0x2c, 0x09, // 5/6 threshold
0x2d, 0x05, // 7/8 threshold
0x2e, 0x01,
0x31, 0x1f, // test all FECs
0x32, 0x19, // viterbi and synchro search
0x33, 0xfc, // rs control
0x34, 0x93, // error control
0x0b, 0x00,
0x27, 0x00, 0x2f, 0x00, 0x30, 0x00,
0x35, 0x00, 0x36, 0x00, 0x37, 0x00,
0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00,
0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00,
0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00
};
static
int stv0299_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
int ret;
u8 buf [] = { reg, data };
struct i2c_msg msg = { addr: 0x68, flags: 0, buf: buf, len: 2 };
dprintk ("%s\n", __FUNCTION__);
ret = i2c->xfer (i2c, &msg, 1);
if (ret != 1)
dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
__FUNCTION__, reg, data, ret);
return (ret != 1) ? -1 : 0;
}
static
u8 stv0299_readreg (struct dvb_i2c_bus *i2c, u8 reg)
{
int ret;
u8 b0 [] = { reg };
u8 b1 [] = { 0 };
struct i2c_msg msg [] = { { addr: 0x68, flags: 0, buf: b0, len: 1 },
{ addr: 0x68, flags: I2C_M_RD, buf: b1, len: 1 } };
dprintk ("%s\n", __FUNCTION__);
ret = i2c->xfer (i2c, msg, 2);
if (ret != 2)
dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
return b1[0];
}
static
int stv0299_readregs (struct dvb_i2c_bus *i2c, u8 reg1, u8 *b, u8 len)
{
int ret;
struct i2c_msg msg [] = { { addr: 0x68, flags: 0, buf: &reg1, len: 1 },
{ addr: 0x68, flags: I2C_M_RD, buf: b, len: len } };
dprintk ("%s\n", __FUNCTION__);
ret = i2c->xfer (i2c, msg, 2);
if (ret != 2)
dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
return ret == 2 ? 0 : -1;
}
static
int tsa5059_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
int ret;
u8 rpt1 [] = { 0x05, 0xb5 }; /* enable i2c repeater on stv0299 */
struct i2c_msg msg [] = {{ addr: 0x68, flags: 0, buf: rpt1, len: 2 },
{ addr: 0x61, flags: 0, buf: data, len: 4 }};
dprintk ("%s\n", __FUNCTION__);
ret = i2c->xfer (i2c, msg, 2);
if (ret != 2)
dprintk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
return (ret != 2) ? -1 : 0;
}
/**
* set up the downconverter frequency divisor for a
* reference clock comparision frequency of 125 kHz.
*/
static
int tsa5059_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, u8 pwr)
{
u32 div = freq / 125;
u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, 0x84, (pwr << 5) | 0x20 };
dprintk ("%s\n", __FUNCTION__);
return tsa5059_write (i2c, buf);
}
static
int stv0299_init (struct dvb_i2c_bus *i2c)
{
int i;
dprintk("stv0299: init chip\n");
for (i=0; i<sizeof(init_tab); i+=2)
stv0299_writereg (i2c, init_tab[i], init_tab[i+1]);
return 0;
}
static
int stv0299_set_inversion (struct dvb_i2c_bus *i2c, int inversion)
{
u8 val;
dprintk ("%s\n", __FUNCTION__);
switch (inversion) {
case INVERSION_AUTO:
return -EOPNOTSUPP;
case INVERSION_OFF:
val = stv0299_readreg (i2c, 0x0c);
return stv0299_writereg (i2c, 0x0c, val | 0x01);
case INVERSION_ON:
val = stv0299_readreg (i2c, 0x0c);
return stv0299_writereg (i2c, 0x0c, val & 0xfe);
default:
return -EINVAL;
}
}
static
int stv0299_set_FEC (struct dvb_i2c_bus *i2c, fe_code_rate_t fec)
{
dprintk ("%s\n", __FUNCTION__);
switch (fec) {
case FEC_AUTO:
return stv0299_writereg (i2c, 0x31, 0x1f);
case FEC_1_2:
return stv0299_writereg (i2c, 0x31, 0x01);
case FEC_2_3:
return stv0299_writereg (i2c, 0x31, 0x02);
case FEC_3_4:
return stv0299_writereg (i2c, 0x31, 0x04);
case FEC_5_6:
return stv0299_writereg (i2c, 0x31, 0x08);
case FEC_7_8:
return stv0299_writereg (i2c, 0x31, 0x10);
default:
return -EINVAL;
}
}
static
fe_code_rate_t stv0299_get_FEC (struct dvb_i2c_bus *i2c)
{
static fe_code_rate_t fec_tab [] = { FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_1_2 };
u8 index;
dprintk ("%s\n", __FUNCTION__);
index = stv0299_readreg (i2c, 0x1b);
index &= 0x7;
if (index > 4)
return FEC_AUTO;
return fec_tab [index];
}
static
int stv0299_wait_diseqc_fifo (struct dvb_i2c_bus *i2c, int timeout)
{
unsigned long start = jiffies;
dprintk ("%s\n", __FUNCTION__);
while (stv0299_readreg(i2c, 0x0a) & 1) {
if (jiffies - start > timeout) {
dprintk ("%s: timeout!!\n", __FUNCTION__);
return -ETIMEDOUT;
}
current->state = TASK_INTERRUPTIBLE;
schedule_timeout (1);
};
return 0;
}
static
int stv0299_wait_diseqc_idle (struct dvb_i2c_bus *i2c, int timeout)
{
unsigned long start = jiffies;
dprintk ("%s\n", __FUNCTION__);
while ((stv0299_readreg(i2c, 0x0a) & 3) != 2 ) {
if (jiffies - start > timeout) {
dprintk ("%s: timeout!!\n", __FUNCTION__);
return -ETIMEDOUT;
}
current->state = TASK_INTERRUPTIBLE;
schedule_timeout (1);
};
return 0;
}
static
int stv0299_send_diseqc_msg (struct dvb_i2c_bus *i2c,
struct dvb_diseqc_master_cmd *m)
{
u8 val;
int i;
dprintk ("%s\n", __FUNCTION__);
val = stv0299_readreg (i2c, 0x08);
if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */
return -EREMOTEIO;
for (i=0; i<m->msg_len; i++) {
if (stv0299_wait_diseqc_fifo (i2c, 100) < 0)
return -ETIMEDOUT;
if (stv0299_writereg (i2c, 0x09, m->msg[i]))
return -EREMOTEIO;
}
/* Shouldn't we wait for idle state (FE=1, FF=0) here to
make certain all bytes have been sent ?
Hmm, actually we should do that before all mode changes too ...
if (stv0299_wait_diseqc_idle (i2c, 100) < 0) */
if (stv0299_wait_diseqc_fifo (i2c, 100) < 0)
return -ETIMEDOUT;
return 0;
}
static
int stv0299_send_diseqc_burst (struct dvb_i2c_bus *i2c, fe_sec_mini_cmd_t burst)
{
u8 val;
dprintk ("%s\n", __FUNCTION__);
val = stv0299_readreg (i2c, 0x08);
if (stv0299_wait_diseqc_fifo (i2c, 100) < 0)
return -ETIMEDOUT;
if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x2)) /* burst mode */
return -EREMOTEIO;
if (stv0299_writereg (i2c, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff))
return -EREMOTEIO;
if (stv0299_wait_diseqc_fifo (i2c, 100) < 0)
return -ETIMEDOUT;
if (stv0299_writereg (i2c, 0x08, val))
return -EREMOTEIO;
return 0;
}
static
int stv0299_set_tone (struct dvb_i2c_bus *i2c, fe_sec_tone_mode_t tone)
{
u8 val;
dprintk ("%s\n", __FUNCTION__);
val = stv0299_readreg (i2c, 0x08);
switch (tone) {
case SEC_TONE_ON:
return stv0299_writereg (i2c, 0x08, val | 0x3);
case SEC_TONE_OFF:
return stv0299_writereg (i2c, 0x08, (val & ~0x3) | 0x02);
default:
return -EINVAL;
};
}
static
int stv0299_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
{
u8 val;
dprintk ("%s\n", __FUNCTION__);
val = stv0299_readreg (i2c, 0x0c);
val &= 0x0f;
val |= 0x40;
switch (voltage) {
case SEC_VOLTAGE_13:
return stv0299_writereg (i2c, 0x0c, val);
case SEC_VOLTAGE_18:
return stv0299_writereg (i2c, 0x0c, val | 0x10);
default:
return -EINVAL;
};
}
static
int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate)
{
u32 ratio;
u32 tmp;
u8 aclk = 0xb4, bclk = 0x51;
dprintk ("%s\n", __FUNCTION__);
if (srate > M_CLK)
srate = M_CLK;
if (srate < 500000)
srate = 500000;
if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
#define FIN (M_CLK >> 4)
tmp = srate << 4;
ratio = tmp / FIN;
tmp = (tmp % FIN) << 8;
ratio = (ratio << 8) + tmp / FIN;
tmp = (tmp % FIN) << 8;
ratio = (ratio << 8) + tmp / FIN;
stv0299_writereg (i2c, 0x13, aclk);
stv0299_writereg (i2c, 0x14, bclk);
stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff);
stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff);
stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0);
return 0;
}
static
int stv0299_get_symbolrate (struct dvb_i2c_bus *i2c)
{
u32 Mclk = M_CLK / 4096L;
u32 srate;
s32 offset;
u8 sfr[3];
s8 rtf;
dprintk ("%s\n", __FUNCTION__);
stv0299_readregs (i2c, 0x1f, sfr, 3);
stv0299_readregs (i2c, 0x1a, &rtf, 1);
srate = (sfr[0] << 8) | sfr[1];
srate *= Mclk;
srate /= 16;
srate += (sfr[2] >> 4) * Mclk / 256;
offset = (s32) rtf * (srate / 4096L);
offset /= 128;
srate += offset;
srate += 1000;
srate /= 2000;
srate *= 2000;
return srate;
}
static
int bsru6_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
struct dvb_i2c_bus *i2c = fe->i2c;
dprintk ("%s\n", __FUNCTION__);
switch (cmd) {
case FE_GET_INFO:
memcpy (arg, &bsru6_info, sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS:
{
fe_status_t *status = (fe_status_t *) arg;
u8 signal = 0xff - stv0299_readreg (i2c, 0x18);
u8 sync = stv0299_readreg (i2c, 0x1b);
*status = 0;
if (signal > 10)
*status |= FE_HAS_SIGNAL;
if (sync & 0x80)
*status |= FE_HAS_CARRIER;
if (sync & 0x10)
*status |= FE_HAS_VITERBI;
if (sync & 0x08)
*status |= FE_HAS_SYNC;
if ((sync & 0x98) == 0x98)
*status |= FE_HAS_LOCK;
break;
}
case FE_READ_BER:
*((u32*) arg) = (stv0299_readreg (i2c, 0x1d) << 8)
| stv0299_readreg (i2c, 0x1e);
break;
case FE_READ_SIGNAL_STRENGTH:
{
s32 signal = 0xffff - ((stv0299_readreg (i2c, 0x18) << 8)
| stv0299_readreg (i2c, 0x19));
signal = signal * 5 / 4;
*((u16*) arg) = (signal > 0xffff) ? 0xffff :
(signal < 0) ? 0 : signal;
break;
}
case FE_READ_SNR:
{
s32 snr = 0xffff - ((stv0299_readreg (i2c, 0x24) << 8)
| stv0299_readreg (i2c, 0x25));
snr = 3 * (snr - 0xa100);
*((u16*) arg) = (snr > 0xffff) ? 0xffff :
(snr < 0) ? 0 : snr;
break;
}
case FE_READ_UNCORRECTED_BLOCKS:
*((u32*) arg) = 0; /* the stv0299 can't measure BER and */
return -EOPNOTSUPP; /* errors at the same time.... */
case FE_SET_FRONTEND:
{
struct dvb_frontend_parameters *p = arg;
tsa5059_set_tv_freq (i2c, p->frequency, 3);
stv0299_set_inversion (i2c, p->inversion);
stv0299_set_FEC (i2c, p->u.qpsk.fec_inner);
stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate);
tsa5059_set_tv_freq (i2c, p->frequency, 0);
stv0299_writereg (i2c, 0x22, 0x00);
stv0299_writereg (i2c, 0x23, 0x00);
stv0299_readreg (i2c, 0x23);
stv0299_writereg (i2c, 0x12, 0xb9);
break;
}
case FE_GET_FRONTEND:
{
struct dvb_frontend_parameters *p = arg;
s32 derot_freq;
derot_freq = (s32)(s16) ((stv0299_readreg (i2c, 0x22) << 8)
| stv0299_readreg (i2c, 0x23));
derot_freq *= (M_CLK >> 16);
derot_freq += 500;
derot_freq /= 1000;
p->frequency += derot_freq;
p->inversion = (stv0299_readreg (i2c, 0x0c) & 1) ?
INVERSION_OFF : INVERSION_ON;
p->u.qpsk.fec_inner = stv0299_get_FEC (i2c);
p->u.qpsk.symbol_rate = stv0299_get_symbolrate (i2c);
break;
}
case FE_SLEEP:
stv0299_writereg (i2c, 0x0c, 0x00); /* LNB power off! */
stv0299_writereg (i2c, 0x02, 0x80);
break;
case FE_INIT:
return stv0299_init (i2c);
case FE_RESET:
stv0299_writereg (i2c, 0x22, 0x00);
stv0299_writereg (i2c, 0x23, 0x00);
stv0299_readreg (i2c, 0x23);
stv0299_writereg (i2c, 0x12, 0xb9);
break;
case FE_DISEQC_SEND_MASTER_CMD:
return stv0299_send_diseqc_msg (i2c, arg);
case FE_DISEQC_SEND_BURST:
return stv0299_send_diseqc_burst (i2c, (fe_sec_mini_cmd_t) arg);
case FE_SET_TONE:
return stv0299_set_tone (i2c, (fe_sec_tone_mode_t) arg);
case FE_SET_VOLTAGE:
return stv0299_set_voltage (i2c, (fe_sec_voltage_t) arg);
default:
return -EOPNOTSUPP;
};
return 0;
}
static
int bsru6_attach (struct dvb_i2c_bus *i2c)
{
dprintk ("%s\n", __FUNCTION__);
if ((stv0299_readreg (i2c, 0x00)) != 0xa1)
return -ENODEV;
dvb_register_frontend (bsru6_ioctl, i2c, NULL, &bsru6_info);
return 0;
}
static
void bsru6_detach (struct dvb_i2c_bus *i2c)
{
dprintk ("%s\n", __FUNCTION__);
dvb_unregister_frontend (bsru6_ioctl, i2c);
}
static
int __init init_bsru6 (void)
{
dprintk ("%s\n", __FUNCTION__);
return dvb_register_i2c_device (THIS_MODULE, bsru6_attach, bsru6_detach);
}
static
void __exit exit_bsru6 (void)
{
dprintk ("%s\n", __FUNCTION__);
dvb_unregister_i2c_device (bsru6_attach);
}
module_init (init_bsru6);
module_exit (exit_bsru6);
MODULE_PARM(debug,"i");
MODULE_PARM_DESC(debug, "enable verbose debug messages");
MODULE_DESCRIPTION("BSRU6 DVB Frontend driver");
MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
MODULE_LICENSE("GPL");
/*
Driver for Alps BSRV2 QPSK Frontend
Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
#define dprintk if (debug) printk
static
struct dvb_frontend_info bsrv2_info = {
name: "Alps BSRV2",
type: FE_QPSK,
frequency_min: 950000,
frequency_max: 2150000,
frequency_stepsize: 250, /* kHz for QPSK frontends */
frequency_tolerance: 29500,
symbol_rate_min: 1000000,
symbol_rate_max: 45000000,
/* symbol_rate_tolerance: ???,*/
notifier_delay: 50, /* 1/20 s */
caps: FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK
};
static
u8 init_1893_tab [] = {
0x01, 0xA4, 0x35, 0x81, 0x2A, 0x0d, 0x55, 0xC4,
0x09, 0x69, 0x00, 0x86, 0x4c, 0x28, 0x7F, 0x00,
0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0x31, 0xb0, 0x14, 0x00, 0xDC, 0x20,
0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x55, 0x00, 0x00, 0x7f, 0x00
};
static
u8 init_1893_wtab[] =
{
1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0,
0,1,0,0,0,0,0,0, 1,0,1,1,0,0,0,1,
1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0,
1,1,1,0,1,1
};
static
int ves1893_writereg (struct dvb_i2c_bus *i2c, int reg, int data)
{
u8 buf [] = { 0x00, reg, data };
struct i2c_msg msg = { addr: 0x08, flags: 0, buf: buf, len: 3 };
int err;
if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);
return -EREMOTEIO;
}
return 0;
}
static
u8 ves1893_readreg (struct dvb_i2c_bus *i2c, u8 reg)
{
int ret;
u8 b0 [] = { 0x00, reg };
u8 b1 [] = { 0 };
struct i2c_msg msg [] = { { addr: 0x08, flags: 0, buf: b0, len: 2 },
{ addr: 0x08, flags: I2C_M_RD, buf: b1, len: 1 } };
ret = i2c->xfer (i2c, msg, 2);
if (ret != 2)
dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
return b1[0];
}
static
int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
int ret;
struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
ret = i2c->xfer (i2c, &msg, 1);
if (ret != 1)
printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
return (ret != 1) ? -1 : 0;
}
/**
* set up the downconverter frequency divisor for a
* reference clock comparision frequency of 125 kHz.
*/
static
int sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, u8 pwr)
{
u32 div = (freq + 479500) / 125;
u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, 0x95, (pwr << 5) | 0x30 };
return sp5659_write (i2c, buf);
}
static
int ves1893_init (struct dvb_i2c_bus *i2c)
{
int i;
dprintk("%s: init chip\n", __FUNCTION__);
for (i=0; i<54; i++)
if (init_1893_wtab[i])
ves1893_writereg (i2c, i, init_1893_tab[i]);
return 0;
}
static
int ves1893_clr_bit (struct dvb_i2c_bus *i2c)
{
ves1893_writereg (i2c, 0, init_1893_tab[0] & 0xfe);
ves1893_writereg (i2c, 0, init_1893_tab[0]);
ves1893_writereg (i2c, 3, 0x00);
return ves1893_writereg (i2c, 3, init_1893_tab[3]);
}
static
int ves1893_set_inversion (struct dvb_i2c_bus *i2c, fe_spectral_inversion_t inversion)
{
u8 val;
switch (inversion) {
case INVERSION_OFF:
val = 0xc0;
break;
case INVERSION_ON:
val = 0x80;
break;
case INVERSION_AUTO:
val = 0x40;
break;
default:
return -EINVAL;
}
return ves1893_writereg (i2c, 0x0c, (init_1893_tab[0x0c] & 0x3f) | val);
}
static
int ves1893_set_fec (struct dvb_i2c_bus *i2c, fe_code_rate_t fec)
{
if (fec == FEC_AUTO)
return ves1893_writereg (i2c, 0x0d, 0x08);
else if (fec < FEC_1_2 || fec > FEC_8_9)
return -EINVAL;
else
return ves1893_writereg (i2c, 0x0d, fec - FEC_1_2);
}
static
fe_code_rate_t ves1893_get_fec (struct dvb_i2c_bus *i2c)
{
return FEC_1_2 + ((ves1893_readreg (i2c, 0x0d) >> 4) & 0x7);
}
static
int ves1893_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate)
{
u32 BDR;
u32 ratio;
u8 ADCONF, FCONF, FNR;
u32 BDRI;
u32 tmp;
dprintk("%s: srate == %d\n", __FUNCTION__, srate);
if (srate > 90100000UL/2)
srate = 90100000UL/2;
if (srate < 500000)
srate = 500000;
#define MUL (1UL<<26)
#define FIN (90106000UL>>4)
tmp = srate << 6;
ratio = tmp / FIN;
tmp = (tmp % FIN) << 8;
ratio = (ratio << 8) + tmp / FIN;
tmp = (tmp % FIN) << 8;
ratio = (ratio << 8) + tmp / FIN;
FNR = 0xff;
if (ratio < MUL/3) FNR = 0;
if (ratio < (MUL*11)/50) FNR = 1;
if (ratio < MUL/6) FNR = 2;
if (ratio < MUL/9) FNR = 3;
if (ratio < MUL/12) FNR = 4;
if (ratio < (MUL*11)/200) FNR = 5;
if (ratio < MUL/24) FNR = 6;
if (ratio < (MUL*27)/1000) FNR = 7;
if (ratio < MUL/48) FNR = 8;
if (ratio < (MUL*137)/10000) FNR = 9;
if (FNR == 0xff) {
ADCONF = 0x89;
FCONF = 0x80;
FNR = 0;
} else {
ADCONF = 0x81;
FCONF = 0x88 | (FNR >> 1) | ((FNR & 0x01) << 5);
}
BDR = (( (ratio << (FNR >> 1)) >> 4) + 1) >> 1;
BDRI = ( ((FIN << 8) / ((srate << (FNR >> 1)) >> 2)) + 1) >> 1;
dprintk("FNR= %d\n", FNR);
dprintk("ratio= %08x\n", ratio);
dprintk("BDR= %08x\n", BDR);
dprintk("BDRI= %02x\n", BDRI);
if (BDRI > 0xff)
BDRI = 0xff;
ves1893_writereg (i2c, 0x06, 0xff & BDR);
ves1893_writereg (i2c, 0x07, 0xff & (BDR >> 8));
ves1893_writereg (i2c, 0x08, 0x0f & (BDR >> 16));
ves1893_writereg (i2c, 0x09, BDRI);
ves1893_writereg (i2c, 0x20, ADCONF);
ves1893_writereg (i2c, 0x21, FCONF);
if (srate < 6000000)
ves1893_writereg (i2c, 0x05, init_1893_tab[0x05] | 0x80);
else
ves1893_writereg (i2c, 0x05, init_1893_tab[0x05] & 0x7f);
ves1893_writereg (i2c, 0x00, 0x00);
ves1893_writereg (i2c, 0x00, 0x01);
ves1893_clr_bit (i2c);
return 0;
}
static
int ves1893_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
{
switch (voltage) {
case SEC_VOLTAGE_13:
return ves1893_writereg (i2c, 0x1f, 0x20);
case SEC_VOLTAGE_18:
return ves1893_writereg (i2c, 0x1f, 0x30);
default:
return -EINVAL;
};
}
static
int bsrv2_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
struct dvb_i2c_bus *i2c = fe->i2c;
switch (cmd) {
case FE_GET_INFO:
memcpy (arg, &bsrv2_info, sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS:
{
fe_status_t *status = arg;
int sync = ves1893_readreg (i2c, 0x0e);
*status = 0;
if (sync & 1)
*status |= FE_HAS_SIGNAL;
if (sync & 2)
*status |= FE_HAS_CARRIER;
if (sync & 4)
*status |= FE_HAS_VITERBI;
if (sync & 8)
*status |= FE_HAS_SYNC;
if ((sync & 0x1f) == 0x1f)
*status |= FE_HAS_LOCK;
break;
}
case FE_READ_BER:
{
u32 *ber = (u32 *) arg;
*ber = ves1893_readreg (i2c, 0x15);
*ber |= (ves1893_readreg (i2c, 0x16) << 8);
*ber |= (ves1893_readreg (i2c, 0x17) << 16);
*ber *= 10;
break;
}
case FE_READ_SIGNAL_STRENGTH:
{
u8 signal = ~ves1893_readreg (i2c, 0x0b);
*((u16*) arg) = (signal << 8) | signal;
break;
}
case FE_READ_SNR:
{
u8 snr = ~ves1893_readreg (i2c, 0x1c);
*(u16*) arg = (snr << 8) | snr;
break;
}
case FE_READ_UNCORRECTED_BLOCKS:
{
*(u32*) arg = ves1893_readreg (i2c, 0x18) & 0x7f;
if (*(u32*) arg == 0x7f)
*(u32*) arg = 0xffffffff; /* counter overflow... */
ves1893_writereg (i2c, 0x18, 0x00); /* reset the counter */
ves1893_writereg (i2c, 0x18, 0x80); /* dto. */
break;
}
case FE_SET_FRONTEND:
{
struct dvb_frontend_parameters *p = arg;
sp5659_set_tv_freq (i2c, p->frequency, 0);
ves1893_set_inversion (i2c, p->inversion);
ves1893_set_fec (i2c, p->u.qpsk.fec_inner);
// sp5659_set_tv_freq (i2c, p->frequency, 0);
ves1893_set_symbolrate (i2c, p->u.qpsk.symbol_rate);
break;
}
case FE_GET_FRONTEND:
{
struct dvb_frontend_parameters *p = arg;
s32 afc;
afc = ((int)((char)(ves1893_readreg (i2c, 0x0a) << 1)))/2;
afc = (afc * (int)(p->u.qpsk.symbol_rate/8))/16;
p->frequency += afc;
p->inversion = (ves1893_readreg (i2c, 0x0f) & 2) ?
INVERSION_ON : INVERSION_OFF;
p->u.qpsk.fec_inner = ves1893_get_fec (i2c);
/* XXX FIXME: timing offset !! */
break;
}
case FE_SLEEP:
ves1893_writereg (i2c, 0x1f, 0x00); /* LNB power off */
return ves1893_writereg (i2c, 0x00, 0x08);
case FE_INIT:
return ves1893_init (i2c);
case FE_RESET:
return ves1893_clr_bit (i2c);
case FE_SET_TONE:
return -EOPNOTSUPP; /* the ves1893 can generate the 22k */
/* let's implement this when we have */
/* a box that uses the 22K_0 pin... */
case FE_SET_VOLTAGE:
return ves1893_set_voltage (i2c, (fe_sec_voltage_t) arg);
default:
return -EOPNOTSUPP;
};
return 0;
}
static
int bsrv2_attach (struct dvb_i2c_bus *i2c)
{
if ((ves1893_readreg (i2c, 0x1e) & 0xf0) != 0xd0)
return -ENODEV;
dvb_register_frontend (bsrv2_ioctl, i2c, NULL, &bsrv2_info);
return 0;
}
static
void bsrv2_detach (struct dvb_i2c_bus *i2c)
{
dvb_unregister_frontend (bsrv2_ioctl, i2c);
}
static
int __init init_bsrv2 (void)
{
return dvb_register_i2c_device (THIS_MODULE, bsrv2_attach, bsrv2_detach);
}
static
void __exit exit_bsrv2 (void)
{
dvb_unregister_i2c_device (bsrv2_attach);
}
module_init(init_bsrv2);
module_exit(exit_bsrv2);
MODULE_DESCRIPTION("BSRV2 DVB-S Frontend");
MODULE_AUTHOR("Ralph Metzler");
MODULE_LICENSE("GPL");
MODULE_PARM(debug,"i");
/*
driver for Grundig 29504-401 DVB-T Frontends based on
LSI L64781 COFDM demodulator as used in Technotrend DVB-T cards
Copyright (C) 2001 Holger Waechtler <holger@convergence.de>
for Convergence Integrated Media GmbH
Marko Kohtala <marko.kohtala@nokia.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
#define dprintk if (debug) printk
struct dvb_frontend_info grundig_29504_401_info = {
name: "Grundig 29504-401",
type: FE_OFDM,
/* frequency_min: ???,*/
/* frequency_max: ???,*/
frequency_stepsize: 166666,
/* frequency_tolerance: ???,*/
/* symbol_rate_tolerance: ???,*/
notifier_delay: 0,
caps: FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
FE_CAN_MUTE_TS
};
static
int l64781_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
int ret;
u8 buf [] = { reg, data };
struct i2c_msg msg = { addr: 0x55, flags: 0, buf: buf, len: 2 };
if ((ret = i2c->xfer (i2c, &msg, 1)) != 1)
dprintk ("%s: write_reg error (reg == %02x) = %02x!\n",
__FUNCTION__, reg, ret);
return (ret != 1) ? -1 : 0;
}
static
u8 l64781_readreg (struct dvb_i2c_bus *i2c, u8 reg)
{
int ret;
u8 b0 [] = { reg };
u8 b1 [] = { 0 };
struct i2c_msg msg [] = { { addr: 0x55, flags: 0, buf: b0, len: 1 },
{ addr: 0x55, flags: I2C_M_RD, buf: b1, len: 1 } };
ret = i2c->xfer (i2c, msg, 2);
if (ret != 2)
dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
return b1[0];
}
static
int tsa5060_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
int ret;
struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
if ((ret = i2c->xfer (i2c, &msg, 1)) != 1)
dprintk ("%s: write_reg error == %02x!\n", __FUNCTION__, ret);
return (ret != 1) ? -1 : 0;
}
/**
* set up the downconverter frequency divisor for a
* reference clock comparision frequency of 166666 Hz.
* frequency offset is 36000000 Hz.
*/
static
int tsa5060_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, u8 pwr)
{
u32 div;
u8 buf [4];
u8 cfg;
if (freq < 700000000) {
div = (36000000 + freq) / 31250;
cfg = 0x86;
} else {
div = (36000000 + freq) / 166666;
cfg = 0x88;
}
buf [0] = (div >> 8) & 0x7f;
buf [1] = div & 0xff;
buf [2] = ((div >> 10) & 0x60) | cfg;
buf [3] = pwr << 6;
return tsa5060_write (i2c, buf);
}
static
void apply_tps (struct dvb_i2c_bus *i2c)
{
l64781_writereg (i2c, 0x2a, 0x00);
l64781_writereg (i2c, 0x2a, 0x01);
/* This here is a little bit questionable because it enables
the automatic update of TPS registers. I think we'd need to
handle the IRQ from FE to update some other registers as
well, or at least implement some magic to tuning to correct
to the TPS received from transmission. */
l64781_writereg (i2c, 0x2a, 0x02);
}
static
void reset_afc (struct dvb_i2c_bus *i2c)
{
/* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for
timing offset */
l64781_writereg (i2c, 0x07, 0x9e); /* stall AFC */
l64781_writereg (i2c, 0x08, 0); /* AFC INIT FREQ */
l64781_writereg (i2c, 0x09, 0);
l64781_writereg (i2c, 0x0a, 0);
l64781_writereg (i2c, 0x07, 0x8e);
l64781_writereg (i2c, 0x0e, 0); /* AGC gain to zero in beginning */
l64781_writereg (i2c, 0x11, 0x80); /* stall TIM */
l64781_writereg (i2c, 0x10, 0); /* TIM_OFFSET_LSB */
l64781_writereg (i2c, 0x12, 0);
l64781_writereg (i2c, 0x13, 0);
l64781_writereg (i2c, 0x11, 0x00);
}
static
int apply_frontend_param (struct dvb_i2c_bus *i2c,
struct dvb_frontend_parameters *param)
{
/* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */
static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 };
/* QPSK, QAM_16, QAM_64 */
static const u8 qam_tab [] = { 2, 4, 0, 6 };
static const u8 bw_tab [] = { 8, 7, 6 }; /* 8Mhz, 7MHz, 6MHz */
static const u8 guard_tab [] = { 1, 2, 4, 8 };
/* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */
static const u32 ppm = 8000;
struct dvb_ofdm_parameters *p = &param->u.ofdm;
u32 ddfs_offset_fixed;
/* u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */
/* bw_tab[p->bandWidth]<<10)/15625; */
u32 init_freq;
u32 spi_bias;
u8 val0x04;
u8 val0x05;
u8 val0x06;
if (param->inversion != INVERSION_ON &&
param->inversion != INVERSION_OFF)
return -EINVAL;
if (p->bandwidth < BANDWIDTH_8_MHZ || p->bandwidth > BANDWIDTH_6_MHZ)
return -EINVAL;
if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 &&
p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 &&
p->code_rate_HP != FEC_7_8)
return -EINVAL;
if (p->hierarchy_information != HIERARCHY_NONE &&
(p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 &&
p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 &&
p->code_rate_LP != FEC_7_8))
return -EINVAL;
if (p->constellation != QPSK && p->constellation != QAM_16 &&
p->constellation != QAM_64)
return -EINVAL;
if (p->transmission_mode != TRANSMISSION_MODE_2K &&
p->transmission_mode != TRANSMISSION_MODE_8K)
return -EINVAL;
if (p->guard_interval < GUARD_INTERVAL_1_32 ||
p->guard_interval > GUARD_INTERVAL_1_4)
return -EINVAL;
if (p->hierarchy_information < HIERARCHY_NONE ||
p->hierarchy_information > HIERARCHY_4)
return -EINVAL;
ddfs_offset_fixed = 0x4000-(ppm<<16)/bw_tab[p->bandwidth]/1000000;
/* This works up to 20000 ppm, it overflows if too large ppm! */
init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) /
bw_tab[p->bandwidth] & 0xFFFFFF);
/* SPI bias calculation is slightly modified to fit in 32bit */
/* will work for high ppm only... */
spi_bias = 378 * (1 << 10);
spi_bias *= 16;
spi_bias *= bw_tab[p->bandwidth];
spi_bias *= qam_tab[p->constellation];
spi_bias /= p->code_rate_HP + 1;
spi_bias /= (guard_tab[p->guard_interval] + 32);
spi_bias *= 1000ULL;
spi_bias /= 1000ULL + ppm/1000;
spi_bias *= p->code_rate_HP;
val0x04 = (p->transmission_mode << 2) | p->guard_interval;
val0x05 = fec_tab[p->code_rate_HP];
if (p->hierarchy_information != HIERARCHY_NONE)
val0x05 |= (p->code_rate_LP - FEC_1_2) << 3;
val0x06 = (p->hierarchy_information << 2) | p->constellation;
l64781_writereg (i2c, 0x04, val0x04);
l64781_writereg (i2c, 0x05, val0x05);
l64781_writereg (i2c, 0x06, val0x06);
reset_afc (i2c);
/* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */
l64781_writereg (i2c, 0x15,
p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3);
l64781_writereg (i2c, 0x16, init_freq & 0xff);
l64781_writereg (i2c, 0x17, (init_freq >> 8) & 0xff);
l64781_writereg (i2c, 0x18, (init_freq >> 16) & 0xff);
l64781_writereg (i2c, 0x1b, spi_bias & 0xff);
l64781_writereg (i2c, 0x1c, (spi_bias >> 8) & 0xff);
l64781_writereg (i2c, 0x1d, ((spi_bias >> 16) & 0x7f) |
(param->inversion == INVERSION_ON ? 0x80 : 0x00));
l64781_writereg (i2c, 0x22, ddfs_offset_fixed & 0xff);
l64781_writereg (i2c, 0x23, (ddfs_offset_fixed >> 8) & 0x3f);
l64781_readreg (i2c, 0x00); /* clear interrupt registers... */
l64781_readreg (i2c, 0x01); /* dto. */
apply_tps (i2c);
return 0;
}
static
void reset_and_configure (struct dvb_i2c_bus *i2c)
{
u8 buf [] = { 0x06 };
struct i2c_msg msg = { addr: 0x00, flags: 0, buf: buf, len: 1 };
i2c->xfer (i2c, &msg, 1);
}
static
int init (struct dvb_i2c_bus *i2c)
{
reset_and_configure (i2c);
/* Power up */
l64781_writereg (i2c, 0x3e, 0xa5);
/* Reset hard */
l64781_writereg (i2c, 0x2a, 0x04);
l64781_writereg (i2c, 0x2a, 0x00);
/* Set tuner specific things */
/* AFC_POL, set also in reset_afc */
l64781_writereg (i2c, 0x07, 0x8e);
/* Use internal ADC */
l64781_writereg (i2c, 0x0b, 0x81);
/* AGC loop gain, and polarity is positive */
l64781_writereg (i2c, 0x0c, 0x84);
/* Internal ADC outputs two's complement */
l64781_writereg (i2c, 0x0d, 0x8c);
/* With ppm=8000, it seems the DTR_SENSITIVITY will result in
value of 2 with all possible bandwidths and guard
intervals, which is the initial value anyway. */
/*l64781_writereg (i2c, 0x19, 0x92);*/
/* Everything is two's complement, soft bit and CSI_OUT too */
l64781_writereg (i2c, 0x1e, 0x49);
return 0;
}
static
int grundig_29504_401_ioctl (struct dvb_frontend *fe,
unsigned int cmd, void *arg)
{
struct dvb_i2c_bus *i2c = fe->i2c;
switch (cmd) {
case FE_GET_INFO:
memcpy (arg, &grundig_29504_401_info,
sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS:
{
fe_status_t *status = (fe_status_t *) arg;
int sync = l64781_readreg (i2c, 0x32);
int gain = l64781_readreg (i2c, 0x0e);
l64781_readreg (i2c, 0x00); /* clear interrupt registers... */
l64781_readreg (i2c, 0x01); /* dto. */
*status = 0;
if (gain > 5)
*status |= FE_HAS_SIGNAL;
if (sync & 0x02) /* VCXO locked, this criteria should be ok */
*status |= FE_HAS_CARRIER;
if (sync & 0x20)
*status |= FE_HAS_VITERBI;
if (sync & 0x40)
*status |= FE_HAS_SYNC;
if (sync == 0x7f)
*status |= FE_HAS_LOCK;
break;
}
case FE_READ_BER:
{
/* XXX FIXME: set up counting period (reg 0x26...0x28)
*/
u32 *ber = (u32 *) arg;
*ber = l64781_readreg (i2c, 0x39)
| (l64781_readreg (i2c, 0x3a) << 8);
break;
}
case FE_READ_SIGNAL_STRENGTH:
{
u8 gain = l64781_readreg (i2c, 0x0e);
*(u16 *) arg = (gain << 8) | gain;
break;
}
case FE_READ_SNR:
{
u16 *snr = (u16 *) arg;
u8 avg_quality = 0xff - l64781_readreg (i2c, 0x33);
*snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/
break;
}
case FE_READ_UNCORRECTED_BLOCKS:
{
u32 *ub = (u32 *) arg;
*ub = l64781_readreg (i2c, 0x37)
| (l64781_readreg (i2c, 0x38) << 8);
break;
}
case FE_SET_FRONTEND:
{
struct dvb_frontend_parameters *p = arg;
tsa5060_set_tv_freq (i2c, p->frequency, 3);
apply_frontend_param (i2c, p);
// tsa5060_set_tv_freq (i2c, p->frequency, 0);
}
case FE_GET_FRONTEND:
/* we could correct the frequency here, but...
* (...do you want to implement this?;)
*/
return 0;
case FE_SLEEP:
/* Power down */
return l64781_writereg (i2c, 0x3e, 0x5a);
case FE_INIT:
return init (i2c);
case FE_RESET:
//reset_afc (i2c);
apply_tps (i2c);
l64781_readreg (i2c, 0x00); /* clear interrupt registers... */
l64781_readreg (i2c, 0x01); /* dto. */
break;
default:
dprintk ("%s: unknown command !!!\n", __FUNCTION__);
return -EINVAL;
};
return 0;
}
static
int l64781_attach (struct dvb_i2c_bus *i2c)
{
u8 b0 [] = { 0x1a };
u8 b1 [] = { 0x00 };
struct i2c_msg msg [] = { { addr: 0x55, flags: 0, buf: b0, len: 1 },
{ addr: 0x55, flags: I2C_M_RD, buf: b1, len: 1 } };
if (i2c->xfer (i2c, msg, 2) == 2) /* probably an EEPROM... */
return -ENODEV;
reset_and_configure (i2c);
if (i2c->xfer (i2c, msg, 2) != 2) /* nothing... */
return -ENODEV;
if (b1[0] != 0xa1)
return -ENODEV;
dvb_register_frontend (grundig_29504_401_ioctl, i2c, NULL,
&grundig_29504_401_info);
return 0;
}
static
void l64781_detach (struct dvb_i2c_bus *i2c)
{
dvb_unregister_frontend (grundig_29504_401_ioctl, i2c);
}
static
int __init init_grundig_29504_401 (void)
{
return dvb_register_i2c_device (THIS_MODULE,
l64781_attach, l64781_detach);
}
static
void __exit exit_grundig_29504_401 (void)
{
dvb_unregister_i2c_device (l64781_attach);
}
module_init(init_grundig_29504_401);
module_exit(exit_grundig_29504_401);
MODULE_PARM(debug,"i");
MODULE_PARM_DESC(debug, "enable verbose debug messages");
MODULE_DESCRIPTION("Grundig 29504-401 DVB-T Frontend");
MODULE_AUTHOR("Holger Waechtler, Marko Kohtala");
MODULE_LICENSE("GPL");
/*
Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend
Copyright (C) 2001 Convergence Integrated Media GmbH
written by Ralph Metzler <ralph@convergence.de>
adoption to the new DVB frontend API and diagnostic ioctl's
by Holger Waechtler <holger@convergence.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
#define dprintk if (debug) printk
static
struct dvb_frontend_info grundig_29504_491_info = {
name: "Grundig 29504-491, (TDA8083 based)",
type: FE_QPSK,
frequency_min: 950000, /* FIXME: guessed! */
frequency_max: 1400000, /* FIXME: guessed! */
frequency_stepsize: 125, /* kHz for QPSK frontends */
/* frequency_tolerance: ???,*/
symbol_rate_min: 1000000, /* FIXME: guessed! */
symbol_rate_max: 45000000, /* FIXME: guessed! */
/* symbol_rate_tolerance: ???,*/
notifier_delay: 0,
caps: FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK |
FE_CAN_MUTE_TS
};
static
u8 tda8083_init_tab [] = {
0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea,
0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10,
0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8,
0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00,
0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
static
int tda8083_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
int ret;
u8 buf [] = { reg, data };
struct i2c_msg msg = { addr: 0x68, flags: 0, buf: buf, len: 2 };
ret = i2c->xfer (i2c, &msg, 1);
if (ret != 1)
dprintk ("%s: writereg error (reg %02x, ret == %i)\n",
__FUNCTION__, reg, ret);
return (ret != 1) ? -1 : 0;
}
static
int tda8083_readregs (struct dvb_i2c_bus *i2c, u8 reg1, u8 *b, u8 len)
{
int ret;
struct i2c_msg msg [] = { { addr: 0x68, flags: 0, buf: &reg1, len: 1 },
{ addr: 0x68, flags: I2C_M_RD, buf: b, len: len } };
ret = i2c->xfer (i2c, msg, 2);
if (ret != 2)
dprintk ("%s: readreg error (reg %02x, ret == %i)\n",
__FUNCTION__, reg1, ret);
return ret == 2 ? 0 : -1;
}
static inline
u8 tda8083_readreg (struct dvb_i2c_bus *i2c, u8 reg)
{
u8 val;
tda8083_readregs (i2c, reg, &val, 1);
return val;
}
static
int tsa5522_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
int ret;
struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
ret = i2c->xfer (i2c, &msg, 1);
if (ret != 1)
dprintk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
return (ret != 1) ? -1 : 0;
}
/**
* set up the downconverter frequency divisor for a
* reference clock comparision frequency of 125 kHz.
*/
static
int tsa5522_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq)
{
u32 div = freq / 125;
u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, 0x8e, 0x00 };
return tsa5522_write (i2c, buf);
}
static int
tda8083_init (struct dvb_i2c_bus *i2c)
{
int i;
dprintk("%s: init TDA8083\n", __FILE__);
for (i=0; i<44; i++)
tda8083_writereg (i2c, i, tda8083_init_tab[i]);
return 0;
}
static int
tda8083_set_inversion (struct dvb_i2c_bus *i2c, fe_spectral_inversion_t inversion)
{
/* XXX FIXME: implement other modes than FEC_AUTO */
if (inversion == INVERSION_AUTO)
return 0;
return -EINVAL;
}
static int
tda8083_set_fec (struct dvb_i2c_bus *i2c, fe_code_rate_t fec)
{
if (fec == FEC_AUTO)
return tda8083_writereg (i2c, 0x07, 0xff);
if (fec >= FEC_1_2 && fec <= FEC_8_9)
return tda8083_writereg (i2c, 0x07, 1 << (FEC_8_9 - fec));
return -EINVAL;
}
static
fe_code_rate_t tda8083_get_fec (struct dvb_i2c_bus *i2c)
{
u8 index;
static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4,
FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 };
index = tda8083_readreg (i2c, 0x0e) & 0x3;
if (index > 7)
return FEC_NONE;
return fec_tab [index];
}
static
int tda8083_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate)
{
u32 ratio;
u32 tmp;
u8 filter;
if (srate > 32000000)
srate = 32000000;
if (srate < 500000)
srate = 500000;
filter = 0;
if (srate < 24000000)
filter = 2;
if (srate < 16000000)
filter = 3;
tmp = 31250 << 16;
ratio = tmp / srate;
tmp = (tmp % srate) << 8;
ratio = (ratio << 8) + tmp / srate;
tmp = (tmp % srate) << 8;
ratio = (ratio << 8) + tmp / srate;
dprintk("tda8083: ratio == %08x\n", ratio);
tda8083_writereg (i2c, 0x05, filter);
tda8083_writereg (i2c, 0x02, (ratio >> 16) & 0xff);
tda8083_writereg (i2c, 0x03, (ratio >> 8) & 0xff);
tda8083_writereg (i2c, 0x04, (ratio ) & 0xff);
tda8083_writereg (i2c, 0x00, 0x3c);
tda8083_writereg (i2c, 0x00, 0x04);
return 1;
}
static
void tda8083_wait_diseqc_fifo (struct dvb_i2c_bus *i2c, int timeout)
{
unsigned long start = jiffies;
while (jiffies - start < timeout &&
!(tda8083_readreg(i2c, 0x02) & 0x80))
{
current->state = TASK_INTERRUPTIBLE;
schedule_timeout (5);
};
}
static
int tda8083_send_diseqc_msg (struct dvb_i2c_bus *i2c,
struct dvb_diseqc_master_cmd *m)
{
int i;
tda8083_writereg (i2c, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */
for (i=0; i<m->msg_len; i++)
tda8083_writereg (i2c, 0x23 + i, m->msg[i]);
tda8083_writereg (i2c, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */
tda8083_wait_diseqc_fifo (i2c, 100);
return 0;
}
static
int tda8083_send_diseqc_burst (struct dvb_i2c_bus *i2c, fe_sec_mini_cmd_t burst)
{
switch (burst) {
case SEC_MINI_A:
tda8083_writereg (i2c, 0x29, (5 << 2)); /* send burst A */
break;
case SEC_MINI_B:
tda8083_writereg (i2c, 0x29, (7 << 2)); /* send B */
break;
default:
return -EINVAL;
};
tda8083_wait_diseqc_fifo (i2c, 100);
return 0;
}
static
int tda8083_set_tone (struct dvb_i2c_bus *i2c, fe_sec_tone_mode_t tone)
{
tda8083_writereg (i2c, 0x26, 0xf1);
switch (tone) {
case SEC_TONE_OFF:
return tda8083_writereg (i2c, 0x29, 0x00);
case SEC_TONE_ON:
return tda8083_writereg (i2c, 0x29, 0x80);
default:
return -EINVAL;
};
}
static
int tda8083_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
{
switch (voltage) {
case SEC_VOLTAGE_13:
return tda8083_writereg (i2c, 0x20, 0x00);
case SEC_VOLTAGE_18:
return tda8083_writereg (i2c, 0x20, 0x11);
default:
return -EINVAL;
};
}
static
int grundig_29504_491_ioctl (struct dvb_frontend *fe, unsigned int cmd,
void *arg)
{
struct dvb_i2c_bus *i2c = fe->i2c;
switch (cmd) {
case FE_GET_INFO:
memcpy (arg, &grundig_29504_491_info,
sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS:
{
fe_status_t *status=(fe_status_t *) arg;
u8 signal = ~tda8083_readreg (i2c, 0x01);
u8 sync = tda8083_readreg (i2c, 0x02);
*status = 0;
if (signal > 10)
*status |= FE_HAS_SIGNAL;
if (sync & 0x01)
*status |= FE_HAS_CARRIER;
if (sync & 0x02)
*status |= FE_HAS_VITERBI;
if (sync & 0x10)
*status |= FE_HAS_SYNC;
if ((sync & 0x1f) == 0x1f)
*status |= FE_HAS_LOCK;
break;
}
case FE_READ_BER:
*((u32*) arg) = 0; /* XXX FIXME: implement me!!! */
return -EOPNOTSUPP;
case FE_READ_SIGNAL_STRENGTH:
{
u8 signal = ~tda8083_readreg (i2c, 0x01);
*((u16*) arg) = (signal << 8) | signal;
break;
}
case FE_READ_SNR:
{
u8 snr = tda8083_readreg (i2c, 0x08);
*((u16*) arg) = (snr << 8) | snr;
break;
}
case FE_READ_UNCORRECTED_BLOCKS:
*((u32*) arg) = 0; /* XXX FIXME: implement me!!! */
return -EOPNOTSUPP;
case FE_SET_FRONTEND:
{
struct dvb_frontend_parameters *p = arg;
tsa5522_set_tv_freq (i2c, p->frequency);
tda8083_set_inversion (i2c, p->inversion);
tda8083_set_fec (i2c, p->u.qpsk.fec_inner);
tda8083_set_symbolrate (i2c, p->u.qpsk.symbol_rate);
tda8083_writereg (i2c, 0x00, 0x3c);
tda8083_writereg (i2c, 0x00, 0x04);
break;
}
case FE_GET_FRONTEND:
{
struct dvb_frontend_parameters *p = arg;
/* FIXME: get symbolrate & frequency offset...*/
/*p->frequency = ???;*/
p->inversion = (tda8083_readreg (i2c, 0x0e) & 0x80) ?
INVERSION_ON : INVERSION_OFF;
p->u.qpsk.fec_inner = tda8083_get_fec (i2c);
/*p->u.qpsk.symbol_rate = tda8083_get_symbolrate (i2c);*/
break;
}
case FE_SLEEP:
tda8083_writereg (i2c, 0x00, 0x02);
break;
case FE_INIT:
tda8083_init (i2c);
tda8083_writereg (i2c, 0x00, 0x3c);
tda8083_writereg (i2c, 0x00, 0x04);
break;
case FE_RESET:
tda8083_writereg (i2c, 0x00, 0x3c);
tda8083_writereg (i2c, 0x00, 0x04);
break;
case FE_DISEQC_SEND_MASTER_CMD:
return tda8083_send_diseqc_msg (i2c, arg);
case FE_DISEQC_SEND_BURST:
tda8083_send_diseqc_burst (i2c, (fe_sec_mini_cmd_t) arg);
tda8083_writereg (i2c, 0x00, 0x3c);
tda8083_writereg (i2c, 0x00, 0x04);
break;
case FE_SET_TONE:
tda8083_set_tone (i2c, (fe_sec_tone_mode_t) arg);
tda8083_writereg (i2c, 0x00, 0x3c);
tda8083_writereg (i2c, 0x00, 0x04);
break;
case FE_SET_VOLTAGE:
tda8083_set_voltage (i2c, (fe_sec_voltage_t) arg);
tda8083_writereg (i2c, 0x00, 0x3c);
tda8083_writereg (i2c, 0x00, 0x04);
break;
default:
return -EOPNOTSUPP;
};
return 0;
}
static
int tda8083_attach (struct dvb_i2c_bus *i2c)
{
if ((tda8083_readreg (i2c, 0x00)) != 0x05)
return -ENODEV;
dvb_register_frontend (grundig_29504_491_ioctl, i2c, NULL,
&grundig_29504_491_info);
return 0;
}
static
void tda8083_detach (struct dvb_i2c_bus *i2c)
{
dvb_unregister_frontend (grundig_29504_491_ioctl, i2c);
}
static
int __init init_tda8083 (void)
{
return dvb_register_i2c_device (THIS_MODULE,
tda8083_attach, tda8083_detach);
}
static
void __exit exit_tda8083 (void)
{
dvb_unregister_i2c_device (tda8083_attach);
}
module_init(init_tda8083);
module_exit(exit_tda8083);
MODULE_PARM(debug,"i");
MODULE_DESCRIPTION("Grundig 29504-491 DVB frontend driver");
MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
MODULE_LICENSE("GPL");
/*
VES1820 - Single Chip Cable Channel Receiver driver module
used on the the Siemens DVB-C cards
Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
#define dprintk if (debug) printk
/**
* since we need only a few bits to store internal state we don't allocate
* extra memory but use frontend->data as bitfield
*/
#define SET_PWM(frontend,pwm) do { \
(int) frontend->data &= ~0xff; \
(int) frontend->data |= pwm; \
} while (0)
#define SET_REG0(frontend,reg0) do { \
(int) frontend->data &= ~(0xff << 8); \
(int) frontend->data |= reg0 << 8; \
} while (0)
#define SET_TUNER(frontend,type) do { \
(int) frontend->data &= ~(0xff << 16); \
(int) frontend->data |= type << 16; \
} while (0)
#define GET_PWM(frontend) ((u8) ((int) frontend->data & 0xff))
#define GET_REG0(frontend) ((u8) (((int) frontend->data >> 8) & 0xff))
#define GET_TUNER(frontend) ((u8) (((int) frontend->data >> 16) & 0xff))
static
struct dvb_frontend_info ves1820_info = {
name: "VES1820/Grundig tuner as used on the Siemens DVB-C card",
type: FE_QAM,
frequency_stepsize: 62500,
frequency_min: 51000000,
frequency_max: 858000000,
#if 0
frequency_tolerance: ???,
symbol_rate_min: ???,
symbol_rate_max: ???,
symbol_rate_tolerance: ???, /* ppm */ /* == 8% (spec p. 5) */
notifier_delay: ?,
#endif
caps: FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
FE_CAN_QAM_128 | FE_CAN_QAM_256
};
static
u8 ves1820_inittab [] =
{
0x69, 0x6A, 0x9B, 0x0A, 0x52, 0x46, 0x26, 0x1A,
0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x28,
0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40
};
static
int ves1820_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
int ret;
u8 buf[] = { 0x00, reg, data };
struct i2c_msg msg = { addr: 0x09, flags: 0, buf: buf, len: 3 };
ret = i2c->xfer (i2c, &msg, 1);
if (ret != 1)
dprintk("%s: writereg error "
"(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
__FUNCTION__, reg, data, ret);
mdelay(10);
return (ret != 1) ? -EREMOTEIO : 0;
}
static
u8 ves1820_readreg (struct dvb_i2c_bus *i2c, u8 reg)
{
int ret;
u8 b0 [] = { 0x00, reg };
u8 b1 [] = { 0 };
struct i2c_msg msg [] = { { addr: 0x09, flags: 0, buf: b0, len: 2 },
{ addr: 0x09, flags: I2C_M_RD, buf: b1, len: 1 } };
ret = i2c->xfer (i2c, msg, 2);
if (ret != 2)
dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
return b1[0];
}
static
int tuner_write (struct dvb_i2c_bus *i2c, u8 addr, u8 data [4])
{
int ret;
struct i2c_msg msg = { addr: addr, flags: 0, buf: data, len: 4 };
ret = i2c->xfer (i2c, &msg, 1);
if (ret != 1)
printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
return (ret != 1) ? -EREMOTEIO : 0;
}
/**
* set up the downconverter frequency divisor for a
* reference clock comparision frequency of 62.5 kHz.
*/
static
int tuner_set_tv_freq (struct dvb_frontend *frontend, u32 freq)
{
u32 div;
static u8 addr [] = { 0x61, 0x62 };
static u8 byte3 [] = { 0x8e, 0x85 };
int tuner_type = GET_TUNER(frontend);
u8 buf [4];
div = (freq + 36250000 + 31250) / 62500;
buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
buf[2] = byte3[tuner_type];
if (tuner_type == 1) {
buf[2] |= (div >> 10) & 0x60;
buf[3] = (freq < 174000000 ? 0x88 :
freq < 470000000 ? 0x84 : 0x81);
} else {
buf[3] = (freq < 174000000 ? 0xa1 :
freq < 454000000 ? 0x92 : 0x34);
}
return tuner_write (frontend->i2c, addr[tuner_type], buf);
}
static
int probe_tuner (struct dvb_frontend *frontend)
{
struct dvb_i2c_bus *i2c = frontend->i2c;
struct i2c_msg msg = { addr: 0x61, flags: 0, buf: NULL, len: 0 };
if (i2c->xfer(i2c, &msg, 1) == 1) {
SET_TUNER(frontend,0);
printk ("%s: setup for tuner spXXXX\n", __FILE__);
} else {
SET_TUNER(frontend,1);
printk ("%s: setup for tuner sp5659c\n", __FILE__);
}
return 0;
}
static
int ves1820_init (struct dvb_frontend *frontend)
{
struct dvb_i2c_bus *i2c = frontend->i2c;
u8 b0 [] = { 0xff };
u8 pwm;
int i;
struct i2c_msg msg [] = { { addr: 0x28, flags: 0, buf: b0, len: 1 },
{ addr: 0x28, flags: I2C_M_RD, buf: &pwm, len: 1 } };
dprintk("VES1820: init chip\n");
i2c->xfer (i2c, msg, 2);
dprintk("VES1820: pwm=%02x\n", pwm);
if (pwm == 0xff)
pwm=0x48;
ves1820_writereg (i2c, 0, 0);
for (i=0; i<53; i++)
ves1820_writereg (i2c, i, ves1820_inittab[i]);
ves1820_writereg (i2c, 0x34, pwm);
(int) frontend->data = 0;
SET_PWM(frontend,pwm);
probe_tuner (frontend);
return 0;
}
static
int ves1820_setup_reg0 (struct dvb_frontend *frontend,
u8 real_qam, fe_spectral_inversion_t inversion)
{
struct dvb_i2c_bus *i2c = frontend->i2c;
u8 reg0 = (ves1820_inittab[0] & 0xe3) | (real_qam << 2);
switch (inversion) {
case INVERSION_OFF: /* XXX FIXME: reversed?? p. 25 */
reg0 |= 0x20;
break;
case INVERSION_ON:
reg0 &= 0xdf;
break;
default:
return -EINVAL;
}
SET_REG0(frontend, reg0);
ves1820_writereg (i2c, 0x00, reg0 & 0xfe);
ves1820_writereg (i2c, 0x00, reg0);
return 0;
}
static
int ves1820_reset (struct dvb_frontend *frontend)
{
struct dvb_i2c_bus *i2c = frontend->i2c;
u8 reg0 = GET_REG0(frontend);
ves1820_writereg (i2c, 0x00, reg0 & 0xfe);
ves1820_writereg (i2c, 0x00, reg0);
return 0;
}
static
void ves1820_reset_uncorrected_block_counter (struct dvb_i2c_bus *i2c)
{
ves1820_writereg (i2c, 0x10, ves1820_inittab[0x10] & 0xdf);
ves1820_writereg (i2c, 0x10, ves1820_inittab[0x10]);
}
static
int ves1820_set_symbolrate (struct dvb_i2c_bus *i2c, u32 symbolrate)
{
s32 BDR;
s32 BDRI;
s16 SFIL=0;
u16 NDEC = 0;
u32 tmp, ratio;
#define XIN 57840000UL
#define FIN (57840000UL>>4)
if (symbolrate > XIN/2)
symbolrate = XIN/2;
if (symbolrate < 500000)
symbolrate = 500000;
if (symbolrate < XIN/16) NDEC = 1;
if (symbolrate < XIN/32) NDEC = 2;
if (symbolrate < XIN/64) NDEC = 3;
if (symbolrate < (u32)(XIN/12.3)) SFIL = 1;
if (symbolrate < (u32)(XIN/16)) SFIL = 0;
if (symbolrate < (u32)(XIN/24.6)) SFIL = 1;
if (symbolrate < (u32)(XIN/32)) SFIL = 0;
if (symbolrate < (u32)(XIN/49.2)) SFIL = 1;
if (symbolrate < (u32)(XIN/64)) SFIL = 0;
if (symbolrate < (u32)(XIN/98.4)) SFIL = 1;
symbolrate <<= NDEC;
ratio = (symbolrate << 4) / FIN;
tmp = ((symbolrate << 4) % FIN) << 8;
ratio = (ratio << 8) + tmp / FIN;
tmp = (tmp % FIN) << 8;
ratio = (ratio << 8) + (tmp + FIN/2) / FIN;
BDR = ratio;
BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
if (BDRI > 0xFF)
BDRI = 0xFF;
SFIL = (SFIL << 4) | ves1820_inittab[0x0E];
NDEC = (NDEC << 6) | ves1820_inittab[0x03];
ves1820_writereg (i2c, 0x03, NDEC);
ves1820_writereg (i2c, 0x0a, BDR&0xff);
ves1820_writereg (i2c, 0x0b, (BDR>> 8)&0xff);
ves1820_writereg (i2c, 0x0c, (BDR>>16)&0x3f);
ves1820_writereg (i2c, 0x0d, BDRI);
ves1820_writereg (i2c, 0x0e, SFIL);
return 0;
}
static
void ves1820_reset_pwm (struct dvb_frontend *frontend)
{
u8 pwm = GET_PWM(frontend);
ves1820_writereg (frontend->i2c, 0x34, pwm);
}
typedef struct {
fe_modulation_t QAM_Mode;
int NoOfSym;
u8 Reg1;
u8 Reg5;
u8 Reg8;
u8 Reg9;
} QAM_SETTING;
QAM_SETTING QAM_Values[] = {
{ QAM_16, 16, 140, 164, 162, 145 },
{ QAM_32, 32, 140, 120, 116, 150 },
{ QAM_64, 64, 106, 70, 67, 106 },
{ QAM_128, 128, 120, 54, 52, 126 },
{ QAM_256, 256, 92, 38, 35, 107 }
};
static
int ves1820_set_parameters (struct dvb_frontend *frontend,
struct dvb_frontend_parameters *p)
{
struct dvb_i2c_bus* i2c = frontend->i2c;
int real_qam;
switch (p->u.qam.modulation) {
case QAM_16 : real_qam = 0; break;
case QAM_32 : real_qam = 1; break;
case QAM_64 : real_qam = 2; break;
case QAM_128: real_qam = 3; break;
case QAM_256: real_qam = 4; break;
default:
return -EINVAL;
}
tuner_set_tv_freq (frontend, p->frequency);
ves1820_set_symbolrate (i2c, p->u.qam.symbol_rate);
ves1820_reset_pwm (frontend);
ves1820_writereg (i2c, 0x01, QAM_Values[real_qam].Reg1);
ves1820_writereg (i2c, 0x05, QAM_Values[real_qam].Reg5);
ves1820_writereg (i2c, 0x08, QAM_Values[real_qam].Reg8);
ves1820_writereg (i2c, 0x09, QAM_Values[real_qam].Reg9);
ves1820_setup_reg0 (frontend, real_qam, p->inversion);
return 0;
}
static
int ves1820_ioctl (struct dvb_frontend *frontend, unsigned int cmd, void *arg)
{
switch (cmd) {
case FE_GET_INFO:
memcpy (arg, &ves1820_info, sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS:
{
fe_status_t *status = (fe_status_t *) arg;
int sync;
*status = 0;
sync = ves1820_readreg (frontend->i2c, 0x11);
if (sync & 2)
*status |= FE_HAS_SIGNAL;
if (sync & 2)
*status |= FE_HAS_CARRIER;
if (sync & 2) /* XXX FIXME! */
*status |= FE_HAS_VITERBI;
if (sync & 4)
*status |= FE_HAS_SYNC;
if (sync & 8)
*status |= FE_HAS_LOCK;
break;
}
case FE_READ_BER:
*((u32*) arg) = ves1820_readreg(frontend->i2c, 0x14) |
(ves1820_readreg(frontend->i2c, 0x15) << 8) |
(ves1820_readreg(frontend->i2c, 0x16) << 16);
/* XXX FIXME: scale!!*/
break;
case FE_READ_SIGNAL_STRENGTH:
{
u8 gain = ves1820_readreg(frontend->i2c, 0x17);
*((u16*) arg) = (gain << 8) | gain;
break;
}
case FE_READ_SNR:
{
u8 quality = ~ves1820_readreg(frontend->i2c, 0x18);
*((u16*) arg) = (quality << 8) | quality;
break;
}
case FE_READ_UNCORRECTED_BLOCKS:
*((u32*) arg) = ves1820_readreg (frontend->i2c, 0x13) & 0x7f;
if (*((u32*) arg) == 0x7f)
*((u32*) arg) = 0xffffffff;
ves1820_reset_uncorrected_block_counter (frontend->i2c);
break;
case FE_SET_FRONTEND:
return ves1820_set_parameters (frontend, arg);
case FE_GET_FRONTEND:
/* XXX FIXME: implement! */
/*
struct frontend *front = (struct frontend *)arg;
front->afc=(int)((char)(readreg(client,0x19)));
front->afc=(front->afc*(int)(front->param.u.qam.SymbolRate/8))/128;
*/
break;
case FE_SLEEP:
ves1820_writereg (frontend->i2c, 0x1b, 0x02); /* pdown ADC */
ves1820_writereg (frontend->i2c, 0x00, 0x80); /* standby */
break;
case FE_INIT:
return ves1820_init (frontend);
case FE_RESET:
ves1820_reset (frontend);
break;
default:
return -EINVAL;
}
return 0;
}
static
int ves1820_attach (struct dvb_i2c_bus *i2c)
{
if ((ves1820_readreg (i2c, 0x1a) & 0xf0) != 0x70)
return -ENODEV;
dvb_register_frontend (ves1820_ioctl, i2c, NULL, &ves1820_info);
return 0;
}
static
void ves1820_detach (struct dvb_i2c_bus *i2c)
{
dvb_unregister_frontend (ves1820_ioctl, i2c);
}
static
int __init init_ves1820 (void)
{
return dvb_register_i2c_device (THIS_MODULE,
ves1820_attach, ves1820_detach);
}
static
void __exit exit_ves1820 (void)
{
dvb_unregister_i2c_device (ves1820_attach);
}
module_init(init_ves1820);
module_exit(exit_ves1820);
MODULE_DESCRIPTION("");
MODULE_AUTHOR("Ralph Metzler");
MODULE_LICENSE("GPL");
MODULE_PARM(debug,"i");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment