Initial commit
This commit is contained in:
commit
169c65d57e
51358 changed files with 23120455 additions and 0 deletions
154
drivers/media/pci/ttpci/Kconfig
Normal file
154
drivers/media/pci/ttpci/Kconfig
Normal file
|
@ -0,0 +1,154 @@
|
|||
config DVB_AV7110
|
||||
tristate "AV7110 cards"
|
||||
depends on DVB_CORE && PCI && I2C
|
||||
select TTPCI_EEPROM
|
||||
select VIDEO_SAA7146_VV
|
||||
depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
|
||||
select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_SP8870 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
|
||||
help
|
||||
Support for SAA7146 and AV7110 based DVB cards as produced
|
||||
by Fujitsu-Siemens, Technotrend, Hauppauge and others.
|
||||
|
||||
This driver only supports the fullfeatured cards with
|
||||
onboard MPEG2 decoder.
|
||||
|
||||
This driver needs an external firmware. Please use the script
|
||||
"<kerneldir>/Documentation/dvb/get_dvb_firmware av7110" to
|
||||
download/extract it, and then copy it to /usr/lib/hotplug/firmware
|
||||
or /lib/firmware (depending on configuration of firmware hotplug).
|
||||
|
||||
Alternatively, you can download the file and use the kernel's
|
||||
EXTRA_FIRMWARE configuration option to build it into your
|
||||
kernel image by adding the filename to the EXTRA_FIRMWARE
|
||||
configuration option string.
|
||||
|
||||
Say Y if you own such a card and want to use it.
|
||||
|
||||
config DVB_AV7110_OSD
|
||||
bool "AV7110 OSD support"
|
||||
depends on DVB_AV7110
|
||||
default y if DVB_AV7110=y || DVB_AV7110=m
|
||||
help
|
||||
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.
|
||||
|
||||
config DVB_BUDGET_CORE
|
||||
tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)"
|
||||
depends on DVB_CORE && PCI && I2C
|
||||
select VIDEO_SAA7146
|
||||
select TTPCI_EEPROM
|
||||
help
|
||||
Support for simple SAA7146 based DVB cards
|
||||
(so called Budget- or Nova-PCI cards) without onboard
|
||||
MPEG2 decoder.
|
||||
|
||||
config DVB_BUDGET
|
||||
tristate "Budget cards"
|
||||
depends on DVB_BUDGET_CORE && I2C
|
||||
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
|
||||
help
|
||||
Support for simple SAA7146 based DVB cards (so called Budget-
|
||||
or Nova-PCI cards) without onboard MPEG2 decoder, and without
|
||||
analog inputs or an onboard Common Interface connector.
|
||||
|
||||
Say Y if you own such a card and want to use it.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called budget.
|
||||
|
||||
config DVB_BUDGET_CI
|
||||
tristate "Budget cards with onboard CI connector"
|
||||
depends on DVB_BUDGET_CORE && I2C
|
||||
select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT
|
||||
depends on RC_CORE
|
||||
help
|
||||
Support for simple SAA7146 based DVB cards
|
||||
(so called Budget- or Nova-PCI cards) without onboard
|
||||
MPEG2 decoder, but with onboard Common Interface connector.
|
||||
|
||||
Note: The Common Interface is not yet supported by this driver
|
||||
due to lack of information from the vendor.
|
||||
|
||||
Say Y if you own such a card and want to use it.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called budget-ci.
|
||||
|
||||
config DVB_BUDGET_AV
|
||||
tristate "Budget cards with analog video inputs"
|
||||
depends on DVB_BUDGET_CORE && I2C
|
||||
select VIDEO_SAA7146_VV
|
||||
depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
|
||||
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT
|
||||
help
|
||||
Support for simple SAA7146 based DVB cards
|
||||
(so called Budget- or Nova-PCI cards) without onboard
|
||||
MPEG2 decoder, but with one or more analog video inputs.
|
||||
|
||||
Say Y if you own such a card and want to use it.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called budget-av.
|
||||
|
||||
config DVB_BUDGET_PATCH
|
||||
tristate "AV7110 cards with Budget Patch"
|
||||
depends on DVB_BUDGET_CORE && I2C
|
||||
depends on DVB_AV7110
|
||||
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
|
||||
select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
|
||||
help
|
||||
Support for Budget Patch (full TS) modification on
|
||||
SAA7146+AV7110 based cards (DVB-S cards). This
|
||||
driver doesn't use onboard MPEG2 decoder. The
|
||||
card is driven in Budget-only mode. Card is
|
||||
required to have loaded firmware to tune properly.
|
||||
Firmware can be loaded by insertion and removal of
|
||||
standard AV7110 driver prior to loading this
|
||||
driver.
|
||||
|
||||
Say Y if you own such a card and want to use it.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called budget-patch.
|
21
drivers/media/pci/ttpci/Makefile
Normal file
21
drivers/media/pci/ttpci/Makefile
Normal file
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# Makefile for the kernel SAA7146 FULL TS DVB device driver
|
||||
# and the AV7110 DVB device driver
|
||||
#
|
||||
|
||||
dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o
|
||||
|
||||
ifdef CONFIG_INPUT_EVDEV
|
||||
dvb-ttpci-objs += av7110_ir.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
|
||||
obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o
|
||||
obj-$(CONFIG_DVB_BUDGET) += budget.o
|
||||
obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o
|
||||
obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o
|
||||
obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o
|
||||
obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
|
||||
|
||||
ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
|
||||
ccflags-y += -Idrivers/media/tuners
|
2941
drivers/media/pci/ttpci/av7110.c
Normal file
2941
drivers/media/pci/ttpci/av7110.c
Normal file
File diff suppressed because it is too large
Load diff
317
drivers/media/pci/ttpci/av7110.h
Normal file
317
drivers/media/pci/ttpci/av7110.h
Normal file
|
@ -0,0 +1,317 @@
|
|||
#ifndef _AV7110_H_
|
||||
#define _AV7110_H_
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
#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 <linux/mutex.h>
|
||||
|
||||
#include "dvbdev.h"
|
||||
#include "demux.h"
|
||||
#include "dvb_demux.h"
|
||||
#include "dmxdev.h"
|
||||
#include "dvb_filter.h"
|
||||
#include "dvb_net.h"
|
||||
#include "dvb_ringbuffer.h"
|
||||
#include "dvb_frontend.h"
|
||||
#include "ves1820.h"
|
||||
#include "ves1x93.h"
|
||||
#include "stv0299.h"
|
||||
#include "tda8083.h"
|
||||
#include "sp8870.h"
|
||||
#include "stv0297.h"
|
||||
#include "l64781.h"
|
||||
|
||||
#include <media/saa7146_vv.h>
|
||||
|
||||
|
||||
#define ANALOG_TUNER_VES1820 1
|
||||
#define ANALOG_TUNER_STV0297 2
|
||||
|
||||
extern int av7110_debug;
|
||||
|
||||
#define dprintk(level,args...) \
|
||||
do { if ((av7110_debug & level)) { printk("dvb-ttpci: %s(): ", __func__); printk(args); } } while (0)
|
||||
|
||||
#define MAXFILT 32
|
||||
|
||||
enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM};
|
||||
|
||||
enum av7110_video_mode {
|
||||
AV7110_VIDEO_MODE_PAL = 0,
|
||||
AV7110_VIDEO_MODE_NTSC = 1
|
||||
};
|
||||
|
||||
struct av7110_p2t {
|
||||
u8 pes[TS_SIZE];
|
||||
u8 counter;
|
||||
long int pos;
|
||||
int frags;
|
||||
struct dvb_demux_feed *feed;
|
||||
};
|
||||
|
||||
/* video MPEG decoder events: */
|
||||
/* (code copied from dvb_frontend.c, should maybe be factored out...) */
|
||||
#define MAX_VIDEO_EVENT 8
|
||||
struct dvb_video_events {
|
||||
struct video_event events[MAX_VIDEO_EVENT];
|
||||
int eventw;
|
||||
int eventr;
|
||||
int overflow;
|
||||
wait_queue_head_t wait_queue;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
|
||||
struct av7110;
|
||||
|
||||
/* infrared remote control */
|
||||
struct infrared {
|
||||
u16 key_map[256];
|
||||
struct input_dev *input_dev;
|
||||
char input_phys[32];
|
||||
struct timer_list keyup_timer;
|
||||
struct tasklet_struct ir_tasklet;
|
||||
void (*ir_handler)(struct av7110 *av7110, u32 ircom);
|
||||
u32 ir_command;
|
||||
u32 ir_config;
|
||||
u32 device_mask;
|
||||
u8 protocol;
|
||||
u8 inversion;
|
||||
u16 last_key;
|
||||
u16 last_toggle;
|
||||
u8 delay_timer_finished;
|
||||
};
|
||||
|
||||
|
||||
/* place to store all the necessary device information */
|
||||
struct av7110 {
|
||||
|
||||
/* devices */
|
||||
|
||||
struct dvb_device dvb_dev;
|
||||
struct dvb_net dvb_net;
|
||||
|
||||
struct video_device *v4l_dev;
|
||||
struct video_device *vbi_dev;
|
||||
|
||||
struct saa7146_dev *dev;
|
||||
|
||||
struct i2c_adapter i2c_adap;
|
||||
|
||||
char *card_name;
|
||||
|
||||
/* support for analog module of dvb-c */
|
||||
int analog_tuner_flags;
|
||||
int current_input;
|
||||
u32 current_freq;
|
||||
|
||||
struct tasklet_struct debi_tasklet;
|
||||
struct tasklet_struct gpio_tasklet;
|
||||
|
||||
int adac_type; /* audio DAC type */
|
||||
#define DVB_ADAC_TI 0
|
||||
#define DVB_ADAC_CRYSTAL 1
|
||||
#define DVB_ADAC_MSP34x0 2
|
||||
#define DVB_ADAC_MSP34x5 3
|
||||
#define DVB_ADAC_NONE -1
|
||||
|
||||
|
||||
/* buffers */
|
||||
|
||||
void *iobuf; /* memory for all buffers */
|
||||
struct dvb_ringbuffer avout; /* buffer for video or A/V mux */
|
||||
#define AVOUTLEN (128*1024)
|
||||
struct dvb_ringbuffer aout; /* buffer for audio */
|
||||
#define AOUTLEN (64*1024)
|
||||
void *bmpbuf;
|
||||
#define BMPLEN (8*32768+1024)
|
||||
|
||||
/* bitmap buffers and states */
|
||||
|
||||
int bmpp;
|
||||
int bmplen;
|
||||
volatile int bmp_state;
|
||||
#define BMP_NONE 0
|
||||
#define BMP_LOADING 1
|
||||
#define BMP_LOADED 2
|
||||
wait_queue_head_t bmpq;
|
||||
|
||||
|
||||
/* DEBI and polled command interface */
|
||||
|
||||
spinlock_t debilock;
|
||||
struct mutex dcomlock;
|
||||
volatile int debitype;
|
||||
volatile int debilen;
|
||||
|
||||
|
||||
/* 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];
|
||||
struct mutex osd_mutex;
|
||||
|
||||
/* CA */
|
||||
|
||||
ca_slot_info_t ci_slot[2];
|
||||
|
||||
enum av7110_video_mode vidmode;
|
||||
struct dmxdev dmxdev;
|
||||
struct dvb_demux demux;
|
||||
|
||||
struct dmx_frontend hw_frontend;
|
||||
struct dmx_frontend mem_frontend;
|
||||
|
||||
/* for budget mode demux1 */
|
||||
struct dmxdev dmxdev1;
|
||||
struct dvb_demux demux1;
|
||||
struct dvb_net dvb_net1;
|
||||
spinlock_t feedlock1;
|
||||
int feeding1;
|
||||
u32 ttbp;
|
||||
unsigned char *grabbing;
|
||||
struct saa7146_pgtable pt;
|
||||
struct tasklet_struct vpe_tasklet;
|
||||
bool full_ts;
|
||||
|
||||
int fe_synced;
|
||||
struct mutex pid_mutex;
|
||||
|
||||
int video_blank;
|
||||
struct video_status videostate;
|
||||
u16 display_panscan;
|
||||
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;
|
||||
|
||||
struct dvb_demux_filter *handle2filter[32];
|
||||
struct av7110_p2t p2t_filter[MAXFILT];
|
||||
struct dvb_filter_pes2ts p2t[2];
|
||||
struct ipack ipack[2];
|
||||
u8 *kbuf[2];
|
||||
|
||||
int sinfo;
|
||||
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_head_t arm_wait;
|
||||
u16 arm_loops;
|
||||
|
||||
void *debi_virt;
|
||||
dma_addr_t debi_bus;
|
||||
|
||||
u16 pids[DMX_PES_OTHER];
|
||||
|
||||
struct dvb_ringbuffer ci_rbuffer;
|
||||
struct dvb_ringbuffer ci_wbuffer;
|
||||
|
||||
struct audio_mixer mixer;
|
||||
|
||||
struct dvb_adapter dvb_adapter;
|
||||
struct dvb_device *video_dev;
|
||||
struct dvb_device *audio_dev;
|
||||
struct dvb_device *ca_dev;
|
||||
struct dvb_device *osd_dev;
|
||||
|
||||
struct dvb_video_events video_events;
|
||||
video_size_t video_size;
|
||||
|
||||
u16 wssMode;
|
||||
u16 wssData;
|
||||
|
||||
struct infrared ir;
|
||||
|
||||
/* firmware stuff */
|
||||
unsigned char *bin_fw;
|
||||
unsigned long size_fw;
|
||||
|
||||
unsigned char *bin_dpram;
|
||||
unsigned long size_dpram;
|
||||
|
||||
unsigned char *bin_root;
|
||||
unsigned long size_root;
|
||||
|
||||
struct dvb_frontend* fe;
|
||||
fe_status_t fe_status;
|
||||
|
||||
struct mutex ioctl_mutex;
|
||||
|
||||
/* crash recovery */
|
||||
void (*recover)(struct av7110* av7110);
|
||||
fe_sec_voltage_t saved_voltage;
|
||||
fe_sec_tone_mode_t saved_tone;
|
||||
struct dvb_diseqc_master_cmd saved_master_cmd;
|
||||
fe_sec_mini_cmd_t saved_minicmd;
|
||||
|
||||
int (*fe_init)(struct dvb_frontend* fe);
|
||||
int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status);
|
||||
int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe);
|
||||
int (*fe_diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd);
|
||||
int (*fe_diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd);
|
||||
int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone);
|
||||
int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
|
||||
int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd);
|
||||
int (*fe_set_frontend)(struct dvb_frontend *fe);
|
||||
};
|
||||
|
||||
|
||||
extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
|
||||
u16 subpid, u16 pcrpid);
|
||||
|
||||
extern int av7110_check_ir_config(struct av7110 *av7110, int force);
|
||||
extern int av7110_ir_init(struct av7110 *av7110);
|
||||
extern void av7110_ir_exit(struct av7110 *av7110);
|
||||
|
||||
/* msp3400 i2c subaddresses */
|
||||
#define MSP_WR_DEM 0x10
|
||||
#define MSP_RD_DEM 0x11
|
||||
#define MSP_WR_DSP 0x12
|
||||
#define MSP_RD_DSP 0x13
|
||||
|
||||
extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val);
|
||||
extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg);
|
||||
extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val);
|
||||
|
||||
|
||||
extern int av7110_init_analog_module(struct av7110 *av7110);
|
||||
extern int av7110_init_v4l(struct av7110 *av7110);
|
||||
extern int av7110_exit_v4l(struct av7110 *av7110);
|
||||
|
||||
#endif /* _AV7110_H_ */
|
1634
drivers/media/pci/ttpci/av7110_av.c
Normal file
1634
drivers/media/pci/ttpci/av7110_av.c
Normal file
File diff suppressed because it is too large
Load diff
30
drivers/media/pci/ttpci/av7110_av.h
Normal file
30
drivers/media/pci/ttpci/av7110_av.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef _AV7110_AV_H_
|
||||
#define _AV7110_AV_H_
|
||||
|
||||
struct av7110;
|
||||
|
||||
extern int av7110_set_vidmode(struct av7110 *av7110,
|
||||
enum av7110_video_mode mode);
|
||||
|
||||
extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len);
|
||||
extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen);
|
||||
extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len);
|
||||
|
||||
extern int av7110_set_volume(struct av7110 *av7110, int volleft, int volright);
|
||||
extern int av7110_av_stop(struct av7110 *av7110, int av);
|
||||
extern int av7110_av_start_record(struct av7110 *av7110, int av,
|
||||
struct dvb_demux_feed *dvbdmxfeed);
|
||||
extern int av7110_av_start_play(struct av7110 *av7110, int av);
|
||||
|
||||
extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event);
|
||||
|
||||
extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed);
|
||||
extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p);
|
||||
|
||||
extern int av7110_av_register(struct av7110 *av7110);
|
||||
extern void av7110_av_unregister(struct av7110 *av7110);
|
||||
extern int av7110_av_init(struct av7110 *av7110);
|
||||
extern void av7110_av_exit(struct av7110 *av7110);
|
||||
|
||||
|
||||
#endif /* _AV7110_AV_H_ */
|
397
drivers/media/pci/ttpci/av7110_ca.c
Normal file
397
drivers/media/pci/ttpci/av7110_ca.c
Normal file
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* av7110_ca.c: CA and CI stuff
|
||||
*
|
||||
* Copyright (C) 1999-2002 Ralph Metzler
|
||||
* & Marcus Metzler for convergence integrated media GmbH
|
||||
*
|
||||
* originally based on code by:
|
||||
* Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
*
|
||||
* the project's page is at http://www.linuxtv.org/
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include "av7110.h"
|
||||
#include "av7110_hw.h"
|
||||
#include "av7110_ca.h"
|
||||
|
||||
|
||||
void CI_handle(struct av7110 *av7110, u8 *data, u16 len)
|
||||
{
|
||||
dprintk(8, "av7110:%p\n",av7110);
|
||||
|
||||
if (len < 3)
|
||||
return;
|
||||
switch (data[0]) {
|
||||
case CI_MSG_CI_INFO:
|
||||
if (data[2] != 1 && data[2] != 2)
|
||||
break;
|
||||
switch (data[1]) {
|
||||
case 0:
|
||||
av7110->ci_slot[data[2] - 1].flags = 0;
|
||||
break;
|
||||
case 1:
|
||||
av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT;
|
||||
break;
|
||||
case 2:
|
||||
av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CI_SWITCH_PRG_REPLY:
|
||||
//av7110->ci_stat=data[1];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len)
|
||||
{
|
||||
if (dvb_ringbuffer_free(cibuf) < len + 2)
|
||||
return;
|
||||
|
||||
DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8);
|
||||
DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff);
|
||||
dvb_ringbuffer_write(cibuf, data, len);
|
||||
wake_up_interruptible(&cibuf->queue);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* CI link layer file ops
|
||||
******************************************************************************/
|
||||
|
||||
static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size)
|
||||
{
|
||||
struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p;
|
||||
void *data;
|
||||
|
||||
for (p = tab; *p; p++) {
|
||||
data = vmalloc(size);
|
||||
if (!data) {
|
||||
while (p-- != tab) {
|
||||
vfree(p[0]->data);
|
||||
p[0]->data = NULL;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
dvb_ringbuffer_init(*p, data, size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf)
|
||||
{
|
||||
dvb_ringbuffer_flush_spinlock_wakeup(cirbuf);
|
||||
dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf);
|
||||
}
|
||||
|
||||
static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf)
|
||||
{
|
||||
vfree(cirbuf->data);
|
||||
cirbuf->data = NULL;
|
||||
vfree(ciwbuf->data);
|
||||
ciwbuf->data = NULL;
|
||||
}
|
||||
|
||||
static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file,
|
||||
int slots, ca_slot_info_t *slot)
|
||||
{
|
||||
int i;
|
||||
int len = 0;
|
||||
u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 };
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (slots & (1 << i))
|
||||
len += 8;
|
||||
}
|
||||
|
||||
if (dvb_ringbuffer_free(cibuf) < len)
|
||||
return -EBUSY;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (slots & (1 << i)) {
|
||||
msg[2] = i;
|
||||
dvb_ringbuffer_write(cibuf, msg, 8);
|
||||
slot[i].flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file,
|
||||
const char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
int free;
|
||||
int non_blocking = file->f_flags & O_NONBLOCK;
|
||||
u8 *page = (u8 *)__get_free_page(GFP_USER);
|
||||
int res;
|
||||
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
res = -EINVAL;
|
||||
if (count > 2048)
|
||||
goto out;
|
||||
|
||||
res = -EFAULT;
|
||||
if (copy_from_user(page, buf, count))
|
||||
goto out;
|
||||
|
||||
free = dvb_ringbuffer_free(cibuf);
|
||||
if (count + 2 > free) {
|
||||
res = -EWOULDBLOCK;
|
||||
if (non_blocking)
|
||||
goto out;
|
||||
res = -ERESTARTSYS;
|
||||
if (wait_event_interruptible(cibuf->queue,
|
||||
(dvb_ringbuffer_free(cibuf) >= count + 2)))
|
||||
goto out;
|
||||
}
|
||||
|
||||
DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8);
|
||||
DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff);
|
||||
|
||||
res = dvb_ringbuffer_write(cibuf, page, count);
|
||||
out:
|
||||
free_page((unsigned long)page);
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file,
|
||||
char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
int avail;
|
||||
int non_blocking = file->f_flags & O_NONBLOCK;
|
||||
ssize_t len;
|
||||
|
||||
if (!cibuf->data || !count)
|
||||
return 0;
|
||||
if (non_blocking && (dvb_ringbuffer_empty(cibuf)))
|
||||
return -EWOULDBLOCK;
|
||||
if (wait_event_interruptible(cibuf->queue,
|
||||
!dvb_ringbuffer_empty(cibuf)))
|
||||
return -ERESTARTSYS;
|
||||
avail = dvb_ringbuffer_avail(cibuf);
|
||||
if (avail < 4)
|
||||
return 0;
|
||||
len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8;
|
||||
len |= DVB_RINGBUFFER_PEEK(cibuf, 1);
|
||||
if (avail < len + 2 || count < len)
|
||||
return -EINVAL;
|
||||
DVB_RINGBUFFER_SKIP(cibuf, 2);
|
||||
|
||||
return dvb_ringbuffer_read_user(cibuf, buf, len);
|
||||
}
|
||||
|
||||
static int dvb_ca_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dvb_device *dvbdev = file->private_data;
|
||||
struct av7110 *av7110 = dvbdev->priv;
|
||||
int err = dvb_generic_open(inode, file);
|
||||
|
||||
dprintk(8, "av7110:%p\n",av7110);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int dvb_ca_poll (struct file *file, poll_table *wait)
|
||||
{
|
||||
struct dvb_device *dvbdev = file->private_data;
|
||||
struct av7110 *av7110 = dvbdev->priv;
|
||||
struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer;
|
||||
struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer;
|
||||
unsigned int mask = 0;
|
||||
|
||||
dprintk(8, "av7110:%p\n",av7110);
|
||||
|
||||
poll_wait(file, &rbuf->queue, wait);
|
||||
poll_wait(file, &wbuf->queue, wait);
|
||||
|
||||
if (!dvb_ringbuffer_empty(rbuf))
|
||||
mask |= (POLLIN | POLLRDNORM);
|
||||
|
||||
if (dvb_ringbuffer_free(wbuf) > 1024)
|
||||
mask |= (POLLOUT | POLLWRNORM);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg)
|
||||
{
|
||||
struct dvb_device *dvbdev = file->private_data;
|
||||
struct av7110 *av7110 = dvbdev->priv;
|
||||
unsigned long arg = (unsigned long) parg;
|
||||
int ret = 0;
|
||||
|
||||
dprintk(8, "av7110:%p\n",av7110);
|
||||
|
||||
if (mutex_lock_interruptible(&av7110->ioctl_mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
switch (cmd) {
|
||||
case CA_RESET:
|
||||
ret = ci_ll_reset(&av7110->ci_wbuffer, file, arg,
|
||||
&av7110->ci_slot[0]);
|
||||
break;
|
||||
case CA_GET_CAP:
|
||||
{
|
||||
ca_caps_t cap;
|
||||
|
||||
cap.slot_num = 2;
|
||||
cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ?
|
||||
CA_CI_LINK : CA_CI) | CA_DESCR;
|
||||
cap.descr_num = 16;
|
||||
cap.descr_type = CA_ECD;
|
||||
memcpy(parg, &cap, sizeof(cap));
|
||||
break;
|
||||
}
|
||||
|
||||
case CA_GET_SLOT_INFO:
|
||||
{
|
||||
ca_slot_info_t *info=(ca_slot_info_t *)parg;
|
||||
|
||||
if (info->num < 0 || info->num > 1) {
|
||||
mutex_unlock(&av7110->ioctl_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
av7110->ci_slot[info->num].num = info->num;
|
||||
av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ?
|
||||
CA_CI_LINK : CA_CI;
|
||||
memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t));
|
||||
break;
|
||||
}
|
||||
|
||||
case CA_GET_MSG:
|
||||
break;
|
||||
|
||||
case CA_SEND_MSG:
|
||||
break;
|
||||
|
||||
case CA_GET_DESCR_INFO:
|
||||
{
|
||||
ca_descr_info_t info;
|
||||
|
||||
info.num = 16;
|
||||
info.type = CA_ECD;
|
||||
memcpy(parg, &info, sizeof (info));
|
||||
break;
|
||||
}
|
||||
|
||||
case CA_SET_DESCR:
|
||||
{
|
||||
ca_descr_t *descr = (ca_descr_t*) parg;
|
||||
|
||||
if (descr->index >= 16 || descr->parity > 1) {
|
||||
mutex_unlock(&av7110->ioctl_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5,
|
||||
(descr->index<<8)|descr->parity,
|
||||
(descr->cw[0]<<8)|descr->cw[1],
|
||||
(descr->cw[2]<<8)|descr->cw[3],
|
||||
(descr->cw[4]<<8)|descr->cw[5],
|
||||
(descr->cw[6]<<8)|descr->cw[7]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&av7110->ioctl_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dvb_ca_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct dvb_device *dvbdev = file->private_data;
|
||||
struct av7110 *av7110 = dvbdev->priv;
|
||||
|
||||
dprintk(8, "av7110:%p\n",av7110);
|
||||
return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos);
|
||||
}
|
||||
|
||||
static ssize_t dvb_ca_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct dvb_device *dvbdev = file->private_data;
|
||||
struct av7110 *av7110 = dvbdev->priv;
|
||||
|
||||
dprintk(8, "av7110:%p\n",av7110);
|
||||
return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos);
|
||||
}
|
||||
|
||||
static const struct file_operations dvb_ca_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = dvb_ca_read,
|
||||
.write = dvb_ca_write,
|
||||
.unlocked_ioctl = dvb_generic_ioctl,
|
||||
.open = dvb_ca_open,
|
||||
.release = dvb_generic_release,
|
||||
.poll = dvb_ca_poll,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static struct dvb_device dvbdev_ca = {
|
||||
.priv = NULL,
|
||||
.users = 1,
|
||||
.writers = 1,
|
||||
.fops = &dvb_ca_fops,
|
||||
.kernel_ioctl = dvb_ca_ioctl,
|
||||
};
|
||||
|
||||
|
||||
int av7110_ca_register(struct av7110 *av7110)
|
||||
{
|
||||
return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev,
|
||||
&dvbdev_ca, av7110, DVB_DEVICE_CA);
|
||||
}
|
||||
|
||||
void av7110_ca_unregister(struct av7110 *av7110)
|
||||
{
|
||||
dvb_unregister_device(av7110->ca_dev);
|
||||
}
|
||||
|
||||
int av7110_ca_init(struct av7110* av7110)
|
||||
{
|
||||
return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192);
|
||||
}
|
||||
|
||||
void av7110_ca_exit(struct av7110* av7110)
|
||||
{
|
||||
ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer);
|
||||
}
|
14
drivers/media/pci/ttpci/av7110_ca.h
Normal file
14
drivers/media/pci/ttpci/av7110_ca.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef _AV7110_CA_H_
|
||||
#define _AV7110_CA_H_
|
||||
|
||||
struct av7110;
|
||||
|
||||
extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len);
|
||||
extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len);
|
||||
|
||||
extern int av7110_ca_register(struct av7110 *av7110);
|
||||
extern void av7110_ca_unregister(struct av7110 *av7110);
|
||||
extern int av7110_ca_init(struct av7110* av7110);
|
||||
extern void av7110_ca_exit(struct av7110* av7110);
|
||||
|
||||
#endif /* _AV7110_CA_H_ */
|
1223
drivers/media/pci/ttpci/av7110_hw.c
Normal file
1223
drivers/media/pci/ttpci/av7110_hw.c
Normal file
File diff suppressed because it is too large
Load diff
495
drivers/media/pci/ttpci/av7110_hw.h
Normal file
495
drivers/media/pci/ttpci/av7110_hw.h
Normal file
|
@ -0,0 +1,495 @@
|
|||
#ifndef _AV7110_HW_H_
|
||||
#define _AV7110_HW_H_
|
||||
|
||||
#include "av7110.h"
|
||||
|
||||
/* 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)
|
||||
|
||||
|
||||
enum av7110_bootstate
|
||||
{
|
||||
BOOTSTATE_BUFFER_EMPTY = 0,
|
||||
BOOTSTATE_BUFFER_FULL = 1,
|
||||
BOOTSTATE_AV7110_BOOT_COMPLETE = 2
|
||||
};
|
||||
|
||||
enum av7110_type_rec_play_format
|
||||
{ RP_None,
|
||||
AudioPES,
|
||||
AudioMp2,
|
||||
AudioPCM,
|
||||
VideoPES,
|
||||
AV_PES
|
||||
};
|
||||
|
||||
enum av7110_osd_palette_type
|
||||
{
|
||||
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 */
|
||||
};
|
||||
|
||||
/* switch defines */
|
||||
#define SB_GPIO 3
|
||||
#define SB_OFF SAA7146_GPIO_OUTLO /* SlowBlank off (TV-Mode) */
|
||||
#define SB_ON SAA7146_GPIO_INPUT /* SlowBlank on (AV-Mode) */
|
||||
#define SB_WIDE SAA7146_GPIO_OUTHI /* SlowBlank 6V (16/9-Mode) (not implemented) */
|
||||
|
||||
#define FB_GPIO 1
|
||||
#define FB_OFF SAA7146_GPIO_LO /* FastBlank off (CVBS-Mode) */
|
||||
#define FB_ON SAA7146_GPIO_OUTHI /* FastBlank on (RGB-Mode) */
|
||||
#define FB_LOOP SAA7146_GPIO_INPUT /* FastBlank loop-through (PC graphics ???) */
|
||||
|
||||
enum av7110_video_output_mode
|
||||
{
|
||||
NO_OUT = 0, /* disable analog output */
|
||||
CVBS_RGB_OUT = 1,
|
||||
CVBS_YC_OUT = 2,
|
||||
YC_OUT = 3
|
||||
};
|
||||
|
||||
/* firmware internal msg q status: */
|
||||
#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 GPMQBusy 0x0040 /* Queue not empty, FW >= 261d */
|
||||
#define HPQBusy 0x0080
|
||||
#define OSDQBusy 0x0100
|
||||
|
||||
/* hw section filter flags */
|
||||
#define SECTION_EIT 0x01
|
||||
#define SECTION_SINGLE 0x00
|
||||
#define SECTION_CYCLE 0x02
|
||||
#define SECTION_CONTINUOS 0x04
|
||||
#define SECTION_MODE 0x06
|
||||
#define SECTION_IPMPE 0x0C /* size up to 4k */
|
||||
#define SECTION_HIGH_SPEED 0x1C /* larger buffer */
|
||||
#define DATA_PIPING_FLAG 0x20 /* for 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
|
||||
|
||||
|
||||
/* firmware command codes */
|
||||
enum av7110_osd_command {
|
||||
WCreate,
|
||||
WDestroy,
|
||||
WMoveD,
|
||||
WMoveA,
|
||||
WHide,
|
||||
WTop,
|
||||
DBox,
|
||||
DLine,
|
||||
DText,
|
||||
Set_Font,
|
||||
SetColor,
|
||||
SetBlend,
|
||||
SetWBlend,
|
||||
SetCBlend,
|
||||
SetNonBlend,
|
||||
LoadBmp,
|
||||
BlitBmp,
|
||||
ReleaseBmp,
|
||||
SetWTrans,
|
||||
SetWNoTrans,
|
||||
Set_Palette
|
||||
};
|
||||
|
||||
enum av7110_pid_command {
|
||||
MultiPID,
|
||||
VideoPID,
|
||||
AudioPID,
|
||||
InitFilt,
|
||||
FiltError,
|
||||
NewVersion,
|
||||
CacheError,
|
||||
AddPIDFilter,
|
||||
DelPIDFilter,
|
||||
Scan,
|
||||
SetDescr,
|
||||
SetIR,
|
||||
FlushTSQueue
|
||||
};
|
||||
|
||||
enum av7110_mpeg_command {
|
||||
SelAudChannels
|
||||
};
|
||||
|
||||
enum av7110_audio_command {
|
||||
AudioDAC,
|
||||
CabADAC,
|
||||
ON22K,
|
||||
OFF22K,
|
||||
MainSwitch,
|
||||
ADSwitch,
|
||||
SendDiSEqC,
|
||||
SetRegister,
|
||||
SpdifSwitch
|
||||
};
|
||||
|
||||
enum av7110_request_command {
|
||||
AudioState,
|
||||
AudioBuffState,
|
||||
VideoState1,
|
||||
VideoState2,
|
||||
VideoState3,
|
||||
CrashCounter,
|
||||
ReqVersion,
|
||||
ReqVCXO,
|
||||
ReqRegister,
|
||||
ReqSecFilterError,
|
||||
ReqSTC
|
||||
};
|
||||
|
||||
enum av7110_encoder_command {
|
||||
SetVidMode,
|
||||
SetTestMode,
|
||||
LoadVidCode,
|
||||
SetMonitorType,
|
||||
SetPanScanType,
|
||||
SetFreezeMode,
|
||||
SetWSSConfig
|
||||
};
|
||||
|
||||
enum av7110_rec_play_state {
|
||||
__Record,
|
||||
__Stop,
|
||||
__Play,
|
||||
__Pause,
|
||||
__Slow,
|
||||
__FF_IP,
|
||||
__Scan_I,
|
||||
__Continue
|
||||
};
|
||||
|
||||
enum av7110_fw_cmd_misc {
|
||||
AV7110_FW_VIDEO_ZOOM = 1,
|
||||
AV7110_FW_VIDEO_COMMAND,
|
||||
AV7110_FW_AUDIO_COMMAND
|
||||
};
|
||||
|
||||
enum av7110_command_type {
|
||||
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_MISC = 0x80
|
||||
};
|
||||
|
||||
#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 */
|
||||
|
||||
/* MPEG video decoder commands */
|
||||
#define AV_VIDEO_CMD_STOP 0x000e
|
||||
#define AV_VIDEO_CMD_PLAY 0x000d
|
||||
#define AV_VIDEO_CMD_FREEZE 0x0102
|
||||
#define AV_VIDEO_CMD_FFWD 0x0016
|
||||
#define AV_VIDEO_CMD_SLOW 0x0022
|
||||
|
||||
/* MPEG audio decoder commands */
|
||||
#define AUDIO_CMD_MUTE 0x0001
|
||||
#define AUDIO_CMD_UNMUTE 0x0002
|
||||
#define AUDIO_CMD_PCM16 0x0010
|
||||
#define AUDIO_CMD_STEREO 0x0080
|
||||
#define AUDIO_CMD_MONO_L 0x0100
|
||||
#define AUDIO_CMD_MONO_R 0x0200
|
||||
#define AUDIO_CMD_SYNC_OFF 0x000e
|
||||
#define AUDIO_CMD_SYNC_ON 0x000f
|
||||
|
||||
/* firmware data interface codes */
|
||||
#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_MPEG_VIDEO_EVENT 0x0d
|
||||
|
||||
#define DATA_PES_RECORD 0x10
|
||||
#define DATA_PES_PLAY 0x11
|
||||
#define DATA_TS_RECORD 0x12
|
||||
#define DATA_TS_PLAY 0x13
|
||||
|
||||
/* ancient CI command codes, only two are actually still used
|
||||
* by the link level CI firmware */
|
||||
#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
|
||||
|
||||
|
||||
/* base address of the dual ported RAM which serves as communication
|
||||
* area between PCI bus and av7110,
|
||||
* as seen by the DEBI bus of the saa7146 */
|
||||
#define DPRAM_BASE 0x4000
|
||||
|
||||
/* boot protocol area */
|
||||
#define AV7110_BOOT_STATE (DPRAM_BASE + 0x3F8)
|
||||
#define AV7110_BOOT_SIZE (DPRAM_BASE + 0x3FA)
|
||||
#define AV7110_BOOT_BASE (DPRAM_BASE + 0x3FC)
|
||||
#define AV7110_BOOT_BLOCK (DPRAM_BASE + 0x400)
|
||||
#define AV7110_BOOT_MAX_SIZE 0xc00
|
||||
|
||||
/* firmware command protocol area */
|
||||
#define IRQ_STATE (DPRAM_BASE + 0x0F4)
|
||||
#define IRQ_STATE_EXT (DPRAM_BASE + 0x0F6)
|
||||
#define MSGSTATE (DPRAM_BASE + 0x0F8)
|
||||
#define COMMAND (DPRAM_BASE + 0x0FC)
|
||||
#define COM_BUFF (DPRAM_BASE + 0x100)
|
||||
#define COM_BUFF_SIZE 0x20
|
||||
|
||||
/* various data buffers */
|
||||
#define BUFF1_BASE (DPRAM_BASE + 0x120)
|
||||
#define BUFF1_SIZE 0xE0
|
||||
|
||||
#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 DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE)
|
||||
#define DATA_BUFF3_SIZE 0x0400
|
||||
|
||||
#define Reserved (DPRAM_BASE + 0x1E00)
|
||||
#define Reserved_SIZE 0x1C0
|
||||
|
||||
|
||||
/* firmware status area */
|
||||
#define STATUS_BASE (DPRAM_BASE + 0x1FC0)
|
||||
#define STATUS_LOOPS (STATUS_BASE + 0x08)
|
||||
|
||||
#define STATUS_MPEG_WIDTH (STATUS_BASE + 0x0C)
|
||||
/* ((aspect_ratio & 0xf) << 12) | (height & 0xfff) */
|
||||
#define STATUS_MPEG_HEIGHT_AR (STATUS_BASE + 0x0E)
|
||||
|
||||
/* firmware data protocol area */
|
||||
#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)
|
||||
|
||||
/* used by boot protocol to load firmware into av7110 DRAM */
|
||||
#define DRAM_START_CODE 0x2e000404
|
||||
#define DRAM_MAX_CODE_SIZE 0x00100000
|
||||
|
||||
/* saa7146 gpio lines */
|
||||
#define RESET_LINE 2
|
||||
#define DEBI_DONE_LINE 1
|
||||
#define ARM_IRQ_LINE 0
|
||||
|
||||
|
||||
|
||||
extern int av7110_bootarm(struct av7110 *av7110);
|
||||
extern int av7110_firmversion(struct av7110 *av7110);
|
||||
#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000)
|
||||
#define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000)
|
||||
#define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF)
|
||||
|
||||
extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags);
|
||||
extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...);
|
||||
extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
|
||||
int request_buf_len, u16 *reply_buf, int reply_buf_len);
|
||||
|
||||
|
||||
/* DEBI (saa7146 data extension bus interface) access */
|
||||
extern int av7110_debiwrite(struct av7110 *av7110, u32 config,
|
||||
int addr, u32 val, int count);
|
||||
extern u32 av7110_debiread(struct av7110 *av7110, u32 config,
|
||||
int addr, int count);
|
||||
|
||||
|
||||
/* DEBI during interrupt */
|
||||
/* single word writes */
|
||||
static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
|
||||
{
|
||||
av7110_debiwrite(av7110, config, addr, val, count);
|
||||
}
|
||||
|
||||
/* buffer writes */
|
||||
static inline void mwdebi(struct av7110 *av7110, u32 config, int addr,
|
||||
const u8 *val, int count)
|
||||
{
|
||||
memcpy(av7110->debi_virt, val, count);
|
||||
av7110_debiwrite(av7110, config, addr, 0, count);
|
||||
}
|
||||
|
||||
static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
|
||||
{
|
||||
u32 res;
|
||||
|
||||
res=av7110_debiread(av7110, config, addr, count);
|
||||
if (count<=4)
|
||||
memcpy(av7110->debi_virt, (char *) &res, count);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* DEBI outside interrupts, only for count <= 4! */
|
||||
static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&av7110->debilock, flags);
|
||||
av7110_debiwrite(av7110, config, addr, val, count);
|
||||
spin_unlock_irqrestore(&av7110->debilock, flags);
|
||||
}
|
||||
|
||||
static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 res;
|
||||
|
||||
spin_lock_irqsave(&av7110->debilock, flags);
|
||||
res=av7110_debiread(av7110, config, addr, count);
|
||||
spin_unlock_irqrestore(&av7110->debilock, flags);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* handle mailbox registers of the dual ported RAM */
|
||||
static inline void ARM_ResetMailBox(struct av7110 *av7110)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&av7110->debilock, flags);
|
||||
av7110_debiread(av7110, DEBINOSWAP, IRQ_RX, 2);
|
||||
av7110_debiwrite(av7110, DEBINOSWAP, IRQ_RX, 0, 2);
|
||||
spin_unlock_irqrestore(&av7110->debilock, flags);
|
||||
}
|
||||
|
||||
static inline void ARM_ClearMailBox(struct av7110 *av7110)
|
||||
{
|
||||
iwdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2);
|
||||
}
|
||||
|
||||
static inline void ARM_ClearIrq(struct av7110 *av7110)
|
||||
{
|
||||
irdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Firmware commands
|
||||
****************************************************************************/
|
||||
|
||||
static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data)
|
||||
{
|
||||
return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data);
|
||||
}
|
||||
|
||||
static inline int av7710_set_video_mode(struct av7110 *av7110, int mode)
|
||||
{
|
||||
return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode);
|
||||
}
|
||||
|
||||
static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg)
|
||||
{
|
||||
return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4,
|
||||
(com>>16), (com&0xffff),
|
||||
(arg>>16), (arg&0xffff));
|
||||
}
|
||||
|
||||
static inline int audcom(struct av7110 *av7110, u32 com)
|
||||
{
|
||||
return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2,
|
||||
(com>>16), (com&0xffff));
|
||||
}
|
||||
|
||||
static inline int Set22K(struct av7110 *av7110, int state)
|
||||
{
|
||||
return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0);
|
||||
}
|
||||
|
||||
|
||||
extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst);
|
||||
|
||||
|
||||
#ifdef CONFIG_DVB_AV7110_OSD
|
||||
extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc);
|
||||
extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap);
|
||||
#endif /* CONFIG_DVB_AV7110_OSD */
|
||||
|
||||
|
||||
|
||||
#endif /* _AV7110_HW_H_ */
|
403
drivers/media/pci/ttpci/av7110_ipack.c
Normal file
403
drivers/media/pci/ttpci/av7110_ipack.c
Normal file
|
@ -0,0 +1,403 @@
|
|||
#include "dvb_filter.h"
|
||||
#include "av7110_ipack.h"
|
||||
#include <linux/string.h> /* for memcpy() */
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
|
||||
void av7110_ipack_reset(struct ipack *p)
|
||||
{
|
||||
p->found = 0;
|
||||
p->cid = 0;
|
||||
p->plength = 0;
|
||||
p->flag1 = 0;
|
||||
p->flag2 = 0;
|
||||
p->hlength = 0;
|
||||
p->mpeg = 0;
|
||||
p->check = 0;
|
||||
p->which = 0;
|
||||
p->done = 0;
|
||||
p->count = 0;
|
||||
}
|
||||
|
||||
|
||||
int av7110_ipack_init(struct ipack *p, int size,
|
||||
void (*func)(u8 *buf, int size, void *priv))
|
||||
{
|
||||
if (!(p->buf = vmalloc(size*sizeof(u8)))) {
|
||||
printk(KERN_WARNING "Couldn't allocate memory for ipack\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
p->size = size;
|
||||
p->func = func;
|
||||
p->repack_subids = 0;
|
||||
av7110_ipack_reset(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void av7110_ipack_free(struct ipack *p)
|
||||
{
|
||||
vfree(p->buf);
|
||||
}
|
||||
|
||||
|
||||
static void send_ipack(struct ipack *p)
|
||||
{
|
||||
int off;
|
||||
struct dvb_audio_info ai;
|
||||
int ac3_off = 0;
|
||||
int streamid = 0;
|
||||
int nframes = 0;
|
||||
int f = 0;
|
||||
|
||||
switch (p->mpeg) {
|
||||
case 2:
|
||||
if (p->count < 10)
|
||||
return;
|
||||
p->buf[3] = p->cid;
|
||||
p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8);
|
||||
p->buf[5] = (u8)((p->count - 6) & 0x00ff);
|
||||
if (p->repack_subids && p->cid == PRIVATE_STREAM1) {
|
||||
off = 9 + p->buf[8];
|
||||
streamid = p->buf[off];
|
||||
if ((streamid & 0xf8) == 0x80) {
|
||||
ai.off = 0;
|
||||
ac3_off = ((p->buf[off + 2] << 8)|
|
||||
p->buf[off + 3]);
|
||||
if (ac3_off < p->count)
|
||||
f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off,
|
||||
p->count - ac3_off, &ai, 0);
|
||||
if (!f) {
|
||||
nframes = (p->count - off - 3 - ac3_off) /
|
||||
ai.framesize + 1;
|
||||
p->buf[off + 2] = (ac3_off >> 8) & 0xff;
|
||||
p->buf[off + 3] = (ac3_off) & 0xff;
|
||||
p->buf[off + 1] = nframes;
|
||||
ac3_off += nframes * ai.framesize - p->count;
|
||||
}
|
||||
}
|
||||
}
|
||||
p->func(p->buf, p->count, p->data);
|
||||
|
||||
p->buf[6] = 0x80;
|
||||
p->buf[7] = 0x00;
|
||||
p->buf[8] = 0x00;
|
||||
p->count = 9;
|
||||
if (p->repack_subids && p->cid == PRIVATE_STREAM1
|
||||
&& (streamid & 0xf8) == 0x80) {
|
||||
p->count += 4;
|
||||
p->buf[9] = streamid;
|
||||
p->buf[10] = (ac3_off >> 8) & 0xff;
|
||||
p->buf[11] = (ac3_off) & 0xff;
|
||||
p->buf[12] = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (p->count < 8)
|
||||
return;
|
||||
p->buf[3] = p->cid;
|
||||
p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8);
|
||||
p->buf[5] = (u8)((p->count - 6) & 0x00ff);
|
||||
p->func(p->buf, p->count, p->data);
|
||||
|
||||
p->buf[6] = 0x0f;
|
||||
p->count = 7;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void av7110_ipack_flush(struct ipack *p)
|
||||
{
|
||||
if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6)
|
||||
return;
|
||||
p->plength = p->found - 6;
|
||||
p->found = 0;
|
||||
send_ipack(p);
|
||||
av7110_ipack_reset(p);
|
||||
}
|
||||
|
||||
|
||||
static void write_ipack(struct ipack *p, const u8 *data, int count)
|
||||
{
|
||||
u8 headr[3] = { 0x00, 0x00, 0x01 };
|
||||
|
||||
if (p->count < 6) {
|
||||
memcpy(p->buf, headr, 3);
|
||||
p->count = 6;
|
||||
}
|
||||
|
||||
if (p->count + count < p->size){
|
||||
memcpy(p->buf+p->count, data, count);
|
||||
p->count += count;
|
||||
} else {
|
||||
int rest = p->size - p->count;
|
||||
memcpy(p->buf+p->count, data, rest);
|
||||
p->count += rest;
|
||||
send_ipack(p);
|
||||
if (count - rest > 0)
|
||||
write_ipack(p, data + rest, count - rest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p)
|
||||
{
|
||||
int l;
|
||||
int c = 0;
|
||||
|
||||
while (c < count && (p->mpeg == 0 ||
|
||||
(p->mpeg == 1 && p->found < 7) ||
|
||||
(p->mpeg == 2 && p->found < 9))
|
||||
&& (p->found < 5 || !p->done)) {
|
||||
switch (p->found) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (buf[c] == 0x00)
|
||||
p->found++;
|
||||
else
|
||||
p->found = 0;
|
||||
c++;
|
||||
break;
|
||||
case 2:
|
||||
if (buf[c] == 0x01)
|
||||
p->found++;
|
||||
else if (buf[c] == 0)
|
||||
p->found = 2;
|
||||
else
|
||||
p->found = 0;
|
||||
c++;
|
||||
break;
|
||||
case 3:
|
||||
p->cid = 0;
|
||||
switch (buf[c]) {
|
||||
case PROG_STREAM_MAP:
|
||||
case PRIVATE_STREAM2:
|
||||
case PROG_STREAM_DIR:
|
||||
case ECM_STREAM :
|
||||
case EMM_STREAM :
|
||||
case PADDING_STREAM :
|
||||
case DSM_CC_STREAM :
|
||||
case ISO13522_STREAM:
|
||||
p->done = 1;
|
||||
/* fall through */
|
||||
case PRIVATE_STREAM1:
|
||||
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
||||
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
||||
p->found++;
|
||||
p->cid = buf[c];
|
||||
c++;
|
||||
break;
|
||||
default:
|
||||
p->found = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (count-c > 1) {
|
||||
p->plen[0] = buf[c];
|
||||
c++;
|
||||
p->plen[1] = buf[c];
|
||||
c++;
|
||||
p->found += 2;
|
||||
p->plength = (p->plen[0] << 8) | p->plen[1];
|
||||
} else {
|
||||
p->plen[0] = buf[c];
|
||||
p->found++;
|
||||
return count;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
p->plen[1] = buf[c];
|
||||
c++;
|
||||
p->found++;
|
||||
p->plength = (p->plen[0] << 8) | p->plen[1];
|
||||
break;
|
||||
case 6:
|
||||
if (!p->done) {
|
||||
p->flag1 = buf[c];
|
||||
c++;
|
||||
p->found++;
|
||||
if ((p->flag1 & 0xc0) == 0x80)
|
||||
p->mpeg = 2;
|
||||
else {
|
||||
p->hlength = 0;
|
||||
p->which = 0;
|
||||
p->mpeg = 1;
|
||||
p->flag2 = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 7:
|
||||
if (!p->done && p->mpeg == 2) {
|
||||
p->flag2 = buf[c];
|
||||
c++;
|
||||
p->found++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
if (!p->done && p->mpeg == 2) {
|
||||
p->hlength = buf[c];
|
||||
c++;
|
||||
p->found++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == count)
|
||||
return count;
|
||||
|
||||
if (!p->plength)
|
||||
p->plength = MMAX_PLENGTH - 6;
|
||||
|
||||
if (p->done || ((p->mpeg == 2 && p->found >= 9) ||
|
||||
(p->mpeg == 1 && p->found >= 7))) {
|
||||
switch (p->cid) {
|
||||
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
||||
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
||||
case PRIVATE_STREAM1:
|
||||
if (p->mpeg == 2 && p->found == 9) {
|
||||
write_ipack(p, &p->flag1, 1);
|
||||
write_ipack(p, &p->flag2, 1);
|
||||
write_ipack(p, &p->hlength, 1);
|
||||
}
|
||||
|
||||
if (p->mpeg == 1 && p->found == 7)
|
||||
write_ipack(p, &p->flag1, 1);
|
||||
|
||||
if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
|
||||
p->found < 14) {
|
||||
while (c < count && p->found < 14) {
|
||||
p->pts[p->found - 9] = buf[c];
|
||||
write_ipack(p, buf + c, 1);
|
||||
c++;
|
||||
p->found++;
|
||||
}
|
||||
if (c == count)
|
||||
return count;
|
||||
}
|
||||
|
||||
if (p->mpeg == 1 && p->which < 2000) {
|
||||
|
||||
if (p->found == 7) {
|
||||
p->check = p->flag1;
|
||||
p->hlength = 1;
|
||||
}
|
||||
|
||||
while (!p->which && c < count &&
|
||||
p->check == 0xff){
|
||||
p->check = buf[c];
|
||||
write_ipack(p, buf + c, 1);
|
||||
c++;
|
||||
p->found++;
|
||||
p->hlength++;
|
||||
}
|
||||
|
||||
if (c == count)
|
||||
return count;
|
||||
|
||||
if ((p->check & 0xc0) == 0x40 && !p->which) {
|
||||
p->check = buf[c];
|
||||
write_ipack(p, buf + c, 1);
|
||||
c++;
|
||||
p->found++;
|
||||
p->hlength++;
|
||||
|
||||
p->which = 1;
|
||||
if (c == count)
|
||||
return count;
|
||||
p->check = buf[c];
|
||||
write_ipack(p, buf + c, 1);
|
||||
c++;
|
||||
p->found++;
|
||||
p->hlength++;
|
||||
p->which = 2;
|
||||
if (c == count)
|
||||
return count;
|
||||
}
|
||||
|
||||
if (p->which == 1) {
|
||||
p->check = buf[c];
|
||||
write_ipack(p, buf + c, 1);
|
||||
c++;
|
||||
p->found++;
|
||||
p->hlength++;
|
||||
p->which = 2;
|
||||
if (c == count)
|
||||
return count;
|
||||
}
|
||||
|
||||
if ((p->check & 0x30) && p->check != 0xff) {
|
||||
p->flag2 = (p->check & 0xf0) << 2;
|
||||
p->pts[0] = p->check;
|
||||
p->which = 3;
|
||||
}
|
||||
|
||||
if (c == count)
|
||||
return count;
|
||||
if (p->which > 2){
|
||||
if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) {
|
||||
while (c < count && p->which < 7) {
|
||||
p->pts[p->which - 2] = buf[c];
|
||||
write_ipack(p, buf + c, 1);
|
||||
c++;
|
||||
p->found++;
|
||||
p->which++;
|
||||
p->hlength++;
|
||||
}
|
||||
if (c == count)
|
||||
return count;
|
||||
} else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) {
|
||||
while (c < count && p->which < 12) {
|
||||
if (p->which < 7)
|
||||
p->pts[p->which - 2] = buf[c];
|
||||
write_ipack(p, buf + c, 1);
|
||||
c++;
|
||||
p->found++;
|
||||
p->which++;
|
||||
p->hlength++;
|
||||
}
|
||||
if (c == count)
|
||||
return count;
|
||||
}
|
||||
p->which = 2000;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (c < count && p->found < p->plength + 6) {
|
||||
l = count - c;
|
||||
if (l + p->found > p->plength + 6)
|
||||
l = p->plength + 6 - p->found;
|
||||
write_ipack(p, buf + c, l);
|
||||
p->found += l;
|
||||
c += l;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (p->done) {
|
||||
if (p->found + count - c < p->plength + 6) {
|
||||
p->found += count - c;
|
||||
c = count;
|
||||
} else {
|
||||
c += p->plength + 6 - p->found;
|
||||
p->found = p->plength + 6;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->plength && p->found == p->plength + 6) {
|
||||
send_ipack(p);
|
||||
av7110_ipack_reset(p);
|
||||
if (c < count)
|
||||
av7110_ipack_instant_repack(buf + c, count - c, p);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
12
drivers/media/pci/ttpci/av7110_ipack.h
Normal file
12
drivers/media/pci/ttpci/av7110_ipack.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef _AV7110_IPACK_H_
|
||||
#define _AV7110_IPACK_H_
|
||||
|
||||
extern int av7110_ipack_init(struct ipack *p, int size,
|
||||
void (*func)(u8 *buf, int size, void *priv));
|
||||
|
||||
extern void av7110_ipack_reset(struct ipack *p);
|
||||
extern int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p);
|
||||
extern void av7110_ipack_free(struct ipack * p);
|
||||
extern void av7110_ipack_flush(struct ipack *p);
|
||||
|
||||
#endif
|
415
drivers/media/pci/ttpci/av7110_ir.c
Normal file
415
drivers/media/pci/ttpci/av7110_ir.c
Normal file
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* Driver for the remote control of SAA7146 based AV7110 cards
|
||||
*
|
||||
* Copyright (C) 1999-2003 Holger Waechtler <holger@convergence.de>
|
||||
* Copyright (C) 2003-2007 Oliver Endriss <o.endriss@gmx.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include "av7110.h"
|
||||
#include "av7110_hw.h"
|
||||
|
||||
|
||||
#define AV_CNT 4
|
||||
|
||||
#define IR_RC5 0
|
||||
#define IR_RCMM 1
|
||||
#define IR_RC5_EXT 2 /* internal only */
|
||||
|
||||
#define IR_ALL 0xffffffff
|
||||
|
||||
#define UP_TIMEOUT (HZ*7/25)
|
||||
|
||||
|
||||
/* Note: enable ir debugging by or'ing debug with 16 */
|
||||
|
||||
static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM};
|
||||
module_param_array(ir_protocol, int, NULL, 0644);
|
||||
MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)");
|
||||
|
||||
static int ir_inversion[AV_CNT];
|
||||
module_param_array(ir_inversion, int, NULL, 0644);
|
||||
MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted");
|
||||
|
||||
static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL };
|
||||
module_param_array(ir_device_mask, uint, NULL, 0644);
|
||||
MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)");
|
||||
|
||||
|
||||
static int av_cnt;
|
||||
static struct av7110 *av_list[AV_CNT];
|
||||
|
||||
static u16 default_key_map [256] = {
|
||||
KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7,
|
||||
KEY_8, KEY_9, KEY_BACK, 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_VCR
|
||||
};
|
||||
|
||||
|
||||
/* key-up timer */
|
||||
static void av7110_emit_keyup(unsigned long parm)
|
||||
{
|
||||
struct infrared *ir = (struct infrared *) parm;
|
||||
|
||||
if (!ir || !test_bit(ir->last_key, ir->input_dev->key))
|
||||
return;
|
||||
|
||||
input_report_key(ir->input_dev, ir->last_key, 0);
|
||||
input_sync(ir->input_dev);
|
||||
}
|
||||
|
||||
|
||||
/* tasklet */
|
||||
static void av7110_emit_key(unsigned long parm)
|
||||
{
|
||||
struct infrared *ir = (struct infrared *) parm;
|
||||
u32 ircom = ir->ir_command;
|
||||
u8 data;
|
||||
u8 addr;
|
||||
u16 toggle;
|
||||
u16 keycode;
|
||||
|
||||
/* extract device address and data */
|
||||
switch (ir->protocol) {
|
||||
case IR_RC5: /* RC5: 5 bits device address, 6 bits data */
|
||||
data = ircom & 0x3f;
|
||||
addr = (ircom >> 6) & 0x1f;
|
||||
toggle = ircom & 0x0800;
|
||||
break;
|
||||
|
||||
case IR_RCMM: /* RCMM: ? bits device address, ? bits data */
|
||||
data = ircom & 0xff;
|
||||
addr = (ircom >> 8) & 0x1f;
|
||||
toggle = ircom & 0x8000;
|
||||
break;
|
||||
|
||||
case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */
|
||||
data = ircom & 0x3f;
|
||||
addr = (ircom >> 6) & 0x1f;
|
||||
/* invert 7th data bit for backward compatibility with RC5 keymaps */
|
||||
if (!(ircom & 0x1000))
|
||||
data |= 0x40;
|
||||
toggle = ircom & 0x0800;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("%s invalid protocol %x\n", __func__, ir->protocol);
|
||||
return;
|
||||
}
|
||||
|
||||
input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data);
|
||||
input_event(ir->input_dev, EV_MSC, MSC_SCAN, data);
|
||||
|
||||
keycode = ir->key_map[data];
|
||||
|
||||
dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n",
|
||||
__func__, ircom, addr, data, keycode);
|
||||
|
||||
/* check device address */
|
||||
if (!(ir->device_mask & (1 << addr)))
|
||||
return;
|
||||
|
||||
if (!keycode) {
|
||||
printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n",
|
||||
__func__, ircom, addr, data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (timer_pending(&ir->keyup_timer)) {
|
||||
del_timer(&ir->keyup_timer);
|
||||
if (ir->last_key != keycode || toggle != ir->last_toggle) {
|
||||
ir->delay_timer_finished = 0;
|
||||
input_event(ir->input_dev, EV_KEY, ir->last_key, 0);
|
||||
input_event(ir->input_dev, EV_KEY, keycode, 1);
|
||||
input_sync(ir->input_dev);
|
||||
} else if (ir->delay_timer_finished) {
|
||||
input_event(ir->input_dev, EV_KEY, keycode, 2);
|
||||
input_sync(ir->input_dev);
|
||||
}
|
||||
} else {
|
||||
ir->delay_timer_finished = 0;
|
||||
input_event(ir->input_dev, EV_KEY, keycode, 1);
|
||||
input_sync(ir->input_dev);
|
||||
}
|
||||
|
||||
ir->last_key = keycode;
|
||||
ir->last_toggle = toggle;
|
||||
|
||||
ir->keyup_timer.expires = jiffies + UP_TIMEOUT;
|
||||
add_timer(&ir->keyup_timer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* register with input layer */
|
||||
static void input_register_keys(struct infrared *ir)
|
||||
{
|
||||
int i;
|
||||
|
||||
set_bit(EV_KEY, ir->input_dev->evbit);
|
||||
set_bit(EV_REP, ir->input_dev->evbit);
|
||||
set_bit(EV_MSC, ir->input_dev->evbit);
|
||||
|
||||
set_bit(MSC_RAW, ir->input_dev->mscbit);
|
||||
set_bit(MSC_SCAN, ir->input_dev->mscbit);
|
||||
|
||||
memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) {
|
||||
if (ir->key_map[i] > KEY_MAX)
|
||||
ir->key_map[i] = 0;
|
||||
else if (ir->key_map[i] > KEY_RESERVED)
|
||||
set_bit(ir->key_map[i], ir->input_dev->keybit);
|
||||
}
|
||||
|
||||
ir->input_dev->keycode = ir->key_map;
|
||||
ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
|
||||
ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map);
|
||||
}
|
||||
|
||||
|
||||
/* called by the input driver after rep[REP_DELAY] ms */
|
||||
static void input_repeat_key(unsigned long parm)
|
||||
{
|
||||
struct infrared *ir = (struct infrared *) parm;
|
||||
|
||||
ir->delay_timer_finished = 1;
|
||||
}
|
||||
|
||||
|
||||
/* check for configuration changes */
|
||||
int av7110_check_ir_config(struct av7110 *av7110, int force)
|
||||
{
|
||||
int i;
|
||||
int modified = force;
|
||||
int ret = -ENODEV;
|
||||
|
||||
for (i = 0; i < av_cnt; i++)
|
||||
if (av7110 == av_list[i])
|
||||
break;
|
||||
|
||||
if (i < av_cnt && av7110) {
|
||||
if ((av7110->ir.protocol & 1) != ir_protocol[i] ||
|
||||
av7110->ir.inversion != ir_inversion[i])
|
||||
modified = true;
|
||||
|
||||
if (modified) {
|
||||
/* protocol */
|
||||
if (ir_protocol[i]) {
|
||||
ir_protocol[i] = 1;
|
||||
av7110->ir.protocol = IR_RCMM;
|
||||
av7110->ir.ir_config = 0x0001;
|
||||
} else if (FW_VERSION(av7110->arm_app) >= 0x2620) {
|
||||
av7110->ir.protocol = IR_RC5_EXT;
|
||||
av7110->ir.ir_config = 0x0002;
|
||||
} else {
|
||||
av7110->ir.protocol = IR_RC5;
|
||||
av7110->ir.ir_config = 0x0000;
|
||||
}
|
||||
/* inversion */
|
||||
if (ir_inversion[i]) {
|
||||
ir_inversion[i] = 1;
|
||||
av7110->ir.ir_config |= 0x8000;
|
||||
}
|
||||
av7110->ir.inversion = ir_inversion[i];
|
||||
/* update ARM */
|
||||
ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
|
||||
av7110->ir.ir_config);
|
||||
} else
|
||||
ret = 0;
|
||||
|
||||
/* address */
|
||||
if (av7110->ir.device_mask != ir_device_mask[i])
|
||||
av7110->ir.device_mask = ir_device_mask[i];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* /proc/av7110_ir interface */
|
||||
static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
char *page;
|
||||
u32 ir_config;
|
||||
int size = sizeof ir_config + sizeof av_list[0]->ir.key_map;
|
||||
int i;
|
||||
|
||||
if (count < size)
|
||||
return -EINVAL;
|
||||
|
||||
page = vmalloc(size);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(page, buffer, size)) {
|
||||
vfree(page);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
memcpy(&ir_config, page, sizeof ir_config);
|
||||
|
||||
for (i = 0; i < av_cnt; i++) {
|
||||
/* keymap */
|
||||
memcpy(av_list[i]->ir.key_map, page + sizeof ir_config,
|
||||
sizeof(av_list[i]->ir.key_map));
|
||||
/* protocol, inversion, address */
|
||||
ir_protocol[i] = ir_config & 0x0001;
|
||||
ir_inversion[i] = ir_config & 0x8000 ? 1 : 0;
|
||||
if (ir_config & 0x4000)
|
||||
ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f);
|
||||
else
|
||||
ir_device_mask[i] = IR_ALL;
|
||||
/* update configuration */
|
||||
av7110_check_ir_config(av_list[i], false);
|
||||
input_register_keys(&av_list[i]->ir);
|
||||
}
|
||||
vfree(page);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations av7110_ir_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = av7110_ir_proc_write,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
/* interrupt handler */
|
||||
static void ir_handler(struct av7110 *av7110, u32 ircom)
|
||||
{
|
||||
dprintk(4, "ir command = %08x\n", ircom);
|
||||
av7110->ir.ir_command = ircom;
|
||||
tasklet_schedule(&av7110->ir.ir_tasklet);
|
||||
}
|
||||
|
||||
|
||||
int av7110_ir_init(struct av7110 *av7110)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
static struct proc_dir_entry *e;
|
||||
int err;
|
||||
|
||||
if (av_cnt >= ARRAY_SIZE(av_list))
|
||||
return -ENOSPC;
|
||||
|
||||
av_list[av_cnt++] = av7110;
|
||||
av7110_check_ir_config(av7110, true);
|
||||
|
||||
init_timer(&av7110->ir.keyup_timer);
|
||||
av7110->ir.keyup_timer.function = av7110_emit_keyup;
|
||||
av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir;
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
av7110->ir.input_dev = input_dev;
|
||||
snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys),
|
||||
"pci-%s/ir0", pci_name(av7110->dev->pci));
|
||||
|
||||
input_dev->name = "DVB on-card IR receiver";
|
||||
|
||||
input_dev->phys = av7110->ir.input_phys;
|
||||
input_dev->id.bustype = BUS_PCI;
|
||||
input_dev->id.version = 2;
|
||||
if (av7110->dev->pci->subsystem_vendor) {
|
||||
input_dev->id.vendor = av7110->dev->pci->subsystem_vendor;
|
||||
input_dev->id.product = av7110->dev->pci->subsystem_device;
|
||||
} else {
|
||||
input_dev->id.vendor = av7110->dev->pci->vendor;
|
||||
input_dev->id.product = av7110->dev->pci->device;
|
||||
}
|
||||
input_dev->dev.parent = &av7110->dev->pci->dev;
|
||||
/* initial keymap */
|
||||
memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map);
|
||||
input_register_keys(&av7110->ir);
|
||||
err = input_register_device(input_dev);
|
||||
if (err) {
|
||||
input_free_device(input_dev);
|
||||
return err;
|
||||
}
|
||||
input_dev->timer.function = input_repeat_key;
|
||||
input_dev->timer.data = (unsigned long) &av7110->ir;
|
||||
|
||||
if (av_cnt == 1) {
|
||||
e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops);
|
||||
if (e)
|
||||
proc_set_size(e, 4 + 256 * sizeof(u16));
|
||||
}
|
||||
|
||||
tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir);
|
||||
av7110->ir.ir_handler = ir_handler;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void av7110_ir_exit(struct av7110 *av7110)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (av_cnt == 0)
|
||||
return;
|
||||
|
||||
del_timer_sync(&av7110->ir.keyup_timer);
|
||||
av7110->ir.ir_handler = NULL;
|
||||
tasklet_kill(&av7110->ir.ir_tasklet);
|
||||
|
||||
for (i = 0; i < av_cnt; i++)
|
||||
if (av_list[i] == av7110) {
|
||||
av_list[i] = av_list[av_cnt-1];
|
||||
av_list[av_cnt-1] = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (av_cnt == 1)
|
||||
remove_proc_entry("av7110_ir", NULL);
|
||||
|
||||
input_unregister_device(av7110->ir.input_dev);
|
||||
|
||||
av_cnt--;
|
||||
}
|
||||
|
||||
//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
|
||||
//MODULE_LICENSE("GPL");
|
966
drivers/media/pci/ttpci/av7110_v4l.c
Normal file
966
drivers/media/pci/ttpci/av7110_v4l.c
Normal file
|
@ -0,0 +1,966 @@
|
|||
/*
|
||||
* av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module
|
||||
*
|
||||
* Copyright (C) 1999-2002 Ralph Metzler
|
||||
* & Marcus Metzler for convergence integrated media GmbH
|
||||
*
|
||||
* originally based on code by:
|
||||
* Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* the project's page is at http://www.linuxtv.org/
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#include "av7110.h"
|
||||
#include "av7110_hw.h"
|
||||
#include "av7110_av.h"
|
||||
|
||||
int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val)
|
||||
{
|
||||
u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff };
|
||||
struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg };
|
||||
|
||||
switch (av7110->adac_type) {
|
||||
case DVB_ADAC_MSP34x0:
|
||||
msgs.addr = 0x40;
|
||||
break;
|
||||
case DVB_ADAC_MSP34x5:
|
||||
msgs.addr = 0x42;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) {
|
||||
dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n",
|
||||
av7110->dvb_adapter.num, reg, val);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val)
|
||||
{
|
||||
u8 msg1[3] = { dev, reg >> 8, reg & 0xff };
|
||||
u8 msg2[2];
|
||||
struct i2c_msg msgs[2] = {
|
||||
{ .flags = 0 , .len = 3, .buf = msg1 },
|
||||
{ .flags = I2C_M_RD, .len = 2, .buf = msg2 }
|
||||
};
|
||||
|
||||
switch (av7110->adac_type) {
|
||||
case DVB_ADAC_MSP34x0:
|
||||
msgs[0].addr = 0x40;
|
||||
msgs[1].addr = 0x40;
|
||||
break;
|
||||
case DVB_ADAC_MSP34x5:
|
||||
msgs[0].addr = 0x42;
|
||||
msgs[1].addr = 0x42;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) {
|
||||
dprintk(1, "dvb-ttpci: failed @ card %d, %u\n",
|
||||
av7110->dvb_adapter.num, reg);
|
||||
return -EIO;
|
||||
}
|
||||
*val = (msg2[0] << 8) | msg2[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_input inputs[4] = {
|
||||
{
|
||||
.index = 0,
|
||||
.name = "DVB",
|
||||
.type = V4L2_INPUT_TYPE_CAMERA,
|
||||
.audioset = 1,
|
||||
.tuner = 0, /* ignored */
|
||||
.std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
|
||||
.status = 0,
|
||||
.capabilities = V4L2_IN_CAP_STD,
|
||||
}, {
|
||||
.index = 1,
|
||||
.name = "Television",
|
||||
.type = V4L2_INPUT_TYPE_TUNER,
|
||||
.audioset = 1,
|
||||
.tuner = 0,
|
||||
.std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
|
||||
.status = 0,
|
||||
.capabilities = V4L2_IN_CAP_STD,
|
||||
}, {
|
||||
.index = 2,
|
||||
.name = "Video",
|
||||
.type = V4L2_INPUT_TYPE_CAMERA,
|
||||
.audioset = 0,
|
||||
.tuner = 0,
|
||||
.std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
|
||||
.status = 0,
|
||||
.capabilities = V4L2_IN_CAP_STD,
|
||||
}, {
|
||||
.index = 3,
|
||||
.name = "Y/C",
|
||||
.type = V4L2_INPUT_TYPE_CAMERA,
|
||||
.audioset = 0,
|
||||
.tuner = 0,
|
||||
.std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
|
||||
.status = 0,
|
||||
.capabilities = V4L2_IN_CAP_STD,
|
||||
}
|
||||
};
|
||||
|
||||
static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data)
|
||||
{
|
||||
struct av7110 *av7110 = dev->ext_priv;
|
||||
u8 buf[] = { 0x00, reg, data };
|
||||
struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 };
|
||||
|
||||
dprintk(4, "dev: %p\n", dev);
|
||||
|
||||
if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4])
|
||||
{
|
||||
struct av7110 *av7110 = dev->ext_priv;
|
||||
struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 };
|
||||
|
||||
dprintk(4, "dev: %p\n", dev);
|
||||
|
||||
if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq)
|
||||
{
|
||||
u32 div;
|
||||
u8 config;
|
||||
u8 buf[4];
|
||||
|
||||
dprintk(4, "freq: 0x%08x\n", freq);
|
||||
|
||||
/* magic number: 614. tuning with the frequency given by v4l2
|
||||
is always off by 614*62.5 = 38375 kHz...*/
|
||||
div = freq + 614;
|
||||
|
||||
buf[0] = (div >> 8) & 0x7f;
|
||||
buf[1] = div & 0xff;
|
||||
buf[2] = 0x8e;
|
||||
|
||||
if (freq < (u32) (16 * 168.25))
|
||||
config = 0xa0;
|
||||
else if (freq < (u32) (16 * 447.25))
|
||||
config = 0x90;
|
||||
else
|
||||
config = 0x30;
|
||||
config &= ~0x02;
|
||||
|
||||
buf[3] = config;
|
||||
|
||||
return tuner_write(dev, 0x61, buf);
|
||||
}
|
||||
|
||||
static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq)
|
||||
{
|
||||
struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
|
||||
u32 div;
|
||||
u8 data[4];
|
||||
|
||||
div = (freq + 38900000 + 31250) / 62500;
|
||||
|
||||
data[0] = (div >> 8) & 0x7f;
|
||||
data[1] = div & 0xff;
|
||||
data[2] = 0xce;
|
||||
|
||||
if (freq < 45000000)
|
||||
return -EINVAL;
|
||||
else if (freq < 137000000)
|
||||
data[3] = 0x01;
|
||||
else if (freq < 403000000)
|
||||
data[3] = 0x02;
|
||||
else if (freq < 860000000)
|
||||
data[3] = 0x04;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (av7110->fe->ops.i2c_gate_ctrl)
|
||||
av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1);
|
||||
return tuner_write(dev, 0x63, data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct saa7146_standard analog_standard[];
|
||||
static struct saa7146_standard dvb_standard[];
|
||||
static struct saa7146_standard standard[];
|
||||
|
||||
static struct v4l2_audio msp3400_v4l2_audio = {
|
||||
.index = 0,
|
||||
.name = "Television",
|
||||
.capability = V4L2_AUDCAP_STEREO
|
||||
};
|
||||
|
||||
static int av7110_dvb_c_switch(struct saa7146_fh *fh)
|
||||
{
|
||||
struct saa7146_dev *dev = fh->dev;
|
||||
struct saa7146_vv *vv = dev->vv_data;
|
||||
struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
|
||||
u16 adswitch;
|
||||
int source, sync, err;
|
||||
|
||||
dprintk(4, "%p\n", av7110);
|
||||
|
||||
if ((vv->video_status & STATUS_OVERLAY) != 0) {
|
||||
vv->ov_suspend = vv->video_fh;
|
||||
err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
|
||||
if (err != 0) {
|
||||
dprintk(2, "suspending video failed\n");
|
||||
vv->ov_suspend = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != av7110->current_input) {
|
||||
dprintk(1, "switching to analog TV:\n");
|
||||
adswitch = 1;
|
||||
source = SAA7146_HPS_SOURCE_PORT_B;
|
||||
sync = SAA7146_HPS_SYNC_PORT_B;
|
||||
memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2);
|
||||
|
||||
switch (av7110->current_input) {
|
||||
case 1:
|
||||
dprintk(1, "switching SAA7113 to Analog Tuner Input\n");
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume
|
||||
|
||||
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
|
||||
if (ves1820_writereg(dev, 0x09, 0x0f, 0x60))
|
||||
dprintk(1, "setting band in demodulator failed\n");
|
||||
} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
|
||||
saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD)
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF)
|
||||
}
|
||||
if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1)
|
||||
dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
|
||||
break;
|
||||
case 2:
|
||||
dprintk(1, "switching SAA7113 to Video AV CVBS Input\n");
|
||||
if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1)
|
||||
dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
|
||||
break;
|
||||
case 3:
|
||||
dprintk(1, "switching SAA7113 to Video AV Y/C Input\n");
|
||||
if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1)
|
||||
dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
|
||||
break;
|
||||
default:
|
||||
dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n");
|
||||
}
|
||||
} else {
|
||||
adswitch = 0;
|
||||
source = SAA7146_HPS_SOURCE_PORT_A;
|
||||
sync = SAA7146_HPS_SYNC_PORT_A;
|
||||
memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
|
||||
dprintk(1, "switching DVB mode\n");
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
|
||||
|
||||
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
|
||||
if (ves1820_writereg(dev, 0x09, 0x0f, 0x20))
|
||||
dprintk(1, "setting band in demodulator failed\n");
|
||||
} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
|
||||
saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
|
||||
}
|
||||
}
|
||||
|
||||
/* hmm, this does not do anything!? */
|
||||
if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch))
|
||||
dprintk(1, "ADSwitch error\n");
|
||||
|
||||
saa7146_set_hps_source_and_sync(dev, source, sync);
|
||||
|
||||
if (vv->ov_suspend != NULL) {
|
||||
saa7146_start_preview(vv->ov_suspend);
|
||||
vv->ov_suspend = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
u16 stereo_det;
|
||||
s8 stereo;
|
||||
|
||||
dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index);
|
||||
|
||||
if (!av7110->analog_tuner_flags || t->index != 0)
|
||||
return -EINVAL;
|
||||
|
||||
memset(t, 0, sizeof(*t));
|
||||
strcpy((char *)t->name, "Television");
|
||||
|
||||
t->type = V4L2_TUNER_ANALOG_TV;
|
||||
t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
|
||||
V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
|
||||
t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
|
||||
t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */
|
||||
/* FIXME: add the real signal strength here */
|
||||
t->signal = 0xffff;
|
||||
t->afc = 0;
|
||||
|
||||
/* FIXME: standard / stereo detection is still broken */
|
||||
msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
|
||||
dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
|
||||
msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
|
||||
dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
|
||||
stereo = (s8)(stereo_det >> 8);
|
||||
if (stereo > 0x10) {
|
||||
/* stereo */
|
||||
t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
|
||||
t->audmode = V4L2_TUNER_MODE_STEREO;
|
||||
} else if (stereo < -0x10) {
|
||||
/* bilingual */
|
||||
t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
|
||||
t->audmode = V4L2_TUNER_MODE_LANG1;
|
||||
} else /* mono */
|
||||
t->rxsubchans = V4L2_TUNER_SUB_MONO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
u16 fm_matrix, src;
|
||||
dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index);
|
||||
|
||||
if (!av7110->analog_tuner_flags || av7110->current_input != 1)
|
||||
return -EINVAL;
|
||||
|
||||
switch (t->audmode) {
|
||||
case V4L2_TUNER_MODE_STEREO:
|
||||
dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n");
|
||||
fm_matrix = 0x3001; /* stereo */
|
||||
src = 0x0020;
|
||||
break;
|
||||
case V4L2_TUNER_MODE_LANG1_LANG2:
|
||||
dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n");
|
||||
fm_matrix = 0x3000; /* bilingual */
|
||||
src = 0x0020;
|
||||
break;
|
||||
case V4L2_TUNER_MODE_LANG1:
|
||||
dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
|
||||
fm_matrix = 0x3000; /* mono */
|
||||
src = 0x0000;
|
||||
break;
|
||||
case V4L2_TUNER_MODE_LANG2:
|
||||
dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n");
|
||||
fm_matrix = 0x3000; /* mono */
|
||||
src = 0x0010;
|
||||
break;
|
||||
default: /* case V4L2_TUNER_MODE_MONO: */
|
||||
dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n");
|
||||
fm_matrix = 0x3000; /* mono */
|
||||
src = 0x0030;
|
||||
break;
|
||||
}
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix);
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0008, src);
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0009, src);
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x000a, src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency);
|
||||
|
||||
if (!av7110->analog_tuner_flags || av7110->current_input != 1)
|
||||
return -EINVAL;
|
||||
|
||||
memset(f, 0, sizeof(*f));
|
||||
f->type = V4L2_TUNER_ANALOG_TV;
|
||||
f->frequency = av7110->current_freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency);
|
||||
|
||||
if (!av7110->analog_tuner_flags || av7110->current_input != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (V4L2_TUNER_ANALOG_TV != f->type)
|
||||
return -EINVAL;
|
||||
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0);
|
||||
|
||||
/* tune in desired frequency */
|
||||
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820)
|
||||
ves1820_set_tv_freq(dev, f->frequency);
|
||||
else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297)
|
||||
stv0297_set_tv_freq(dev, f->frequency);
|
||||
av7110->current_freq = f->frequency;
|
||||
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000);
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index);
|
||||
|
||||
if (av7110->analog_tuner_flags) {
|
||||
if (i->index >= 4)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (i->index != 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(i, &inputs[i->index], sizeof(struct v4l2_input));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
*input = av7110->current_input;
|
||||
dprintk(2, "VIDIOC_G_INPUT: %d\n", *input);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
|
||||
|
||||
if (!av7110->analog_tuner_flags)
|
||||
return input ? -EINVAL : 0;
|
||||
|
||||
if (input >= 4)
|
||||
return -EINVAL;
|
||||
|
||||
av7110->current_input = input;
|
||||
return av7110_dvb_c_switch(fh);
|
||||
}
|
||||
|
||||
static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
|
||||
{
|
||||
dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
|
||||
if (a->index != 0)
|
||||
return -EINVAL;
|
||||
*a = msp3400_v4l2_audio;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
|
||||
if (a->index != 0)
|
||||
return -EINVAL;
|
||||
if (av7110->current_input >= 2)
|
||||
return -EINVAL;
|
||||
*a = msp3400_v4l2_audio;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
|
||||
if (av7110->current_input >= 2)
|
||||
return -EINVAL;
|
||||
return a->index ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh,
|
||||
struct v4l2_sliced_vbi_cap *cap)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n");
|
||||
if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
|
||||
return -EINVAL;
|
||||
if (FW_VERSION(av7110->arm_app) >= 0x2623) {
|
||||
cap->service_set = V4L2_SLICED_WSS_625;
|
||||
cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh,
|
||||
struct v4l2_format *f)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
dprintk(2, "VIDIOC_G_FMT:\n");
|
||||
if (FW_VERSION(av7110->arm_app) < 0x2623)
|
||||
return -EINVAL;
|
||||
memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
|
||||
if (av7110->wssMode) {
|
||||
f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
|
||||
f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
|
||||
f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh,
|
||||
struct v4l2_format *f)
|
||||
{
|
||||
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
|
||||
struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
|
||||
|
||||
dprintk(2, "VIDIOC_S_FMT\n");
|
||||
if (FW_VERSION(av7110->arm_app) < 0x2623)
|
||||
return -EINVAL;
|
||||
if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 &&
|
||||
f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) {
|
||||
memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
|
||||
/* WSS controlled by firmware */
|
||||
av7110->wssMode = 0;
|
||||
av7110->wssData = 0;
|
||||
return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
|
||||
SetWSSConfig, 1, 0);
|
||||
} else {
|
||||
memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
|
||||
f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
|
||||
f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
|
||||
f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
|
||||
/* WSS controlled by userspace */
|
||||
av7110->wssMode = 1;
|
||||
av7110->wssData = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int av7110_vbi_reset(struct file *file)
|
||||
{
|
||||
struct saa7146_fh *fh = file->private_data;
|
||||
struct saa7146_dev *dev = fh->dev;
|
||||
struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
|
||||
|
||||
dprintk(2, "%s\n", __func__);
|
||||
av7110->wssMode = 0;
|
||||
av7110->wssData = 0;
|
||||
if (FW_VERSION(av7110->arm_app) < 0x2623)
|
||||
return 0;
|
||||
else
|
||||
return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0);
|
||||
}
|
||||
|
||||
static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct saa7146_fh *fh = file->private_data;
|
||||
struct saa7146_dev *dev = fh->dev;
|
||||
struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
|
||||
struct v4l2_sliced_vbi_data d;
|
||||
int rc;
|
||||
|
||||
dprintk(2, "%s\n", __func__);
|
||||
if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&d, data, count))
|
||||
return -EFAULT;
|
||||
if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23)
|
||||
return -EINVAL;
|
||||
if (d.id)
|
||||
av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0];
|
||||
else
|
||||
av7110->wssData = 0x8000;
|
||||
rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData);
|
||||
return (rc < 0) ? rc : count;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* INITIALIZATION
|
||||
****************************************************************************/
|
||||
|
||||
static u8 saa7113_init_regs[] = {
|
||||
0x02, 0xd0,
|
||||
0x03, 0x23,
|
||||
0x04, 0x00,
|
||||
0x05, 0x00,
|
||||
0x06, 0xe9,
|
||||
0x07, 0x0d,
|
||||
0x08, 0x98,
|
||||
0x09, 0x02,
|
||||
0x0a, 0x80,
|
||||
0x0b, 0x40,
|
||||
0x0c, 0x40,
|
||||
0x0d, 0x00,
|
||||
0x0e, 0x01,
|
||||
0x0f, 0x7c,
|
||||
0x10, 0x48,
|
||||
0x11, 0x0c,
|
||||
0x12, 0x8b,
|
||||
0x13, 0x1a,
|
||||
0x14, 0x00,
|
||||
0x15, 0x00,
|
||||
0x16, 0x00,
|
||||
0x17, 0x00,
|
||||
0x18, 0x00,
|
||||
0x19, 0x00,
|
||||
0x1a, 0x00,
|
||||
0x1b, 0x00,
|
||||
0x1c, 0x00,
|
||||
0x1d, 0x00,
|
||||
0x1e, 0x00,
|
||||
|
||||
0x41, 0x77,
|
||||
0x42, 0x77,
|
||||
0x43, 0x77,
|
||||
0x44, 0x77,
|
||||
0x45, 0x77,
|
||||
0x46, 0x77,
|
||||
0x47, 0x77,
|
||||
0x48, 0x77,
|
||||
0x49, 0x77,
|
||||
0x4a, 0x77,
|
||||
0x4b, 0x77,
|
||||
0x4c, 0x77,
|
||||
0x4d, 0x77,
|
||||
0x4e, 0x77,
|
||||
0x4f, 0x77,
|
||||
0x50, 0x77,
|
||||
0x51, 0x77,
|
||||
0x52, 0x77,
|
||||
0x53, 0x77,
|
||||
0x54, 0x77,
|
||||
0x55, 0x77,
|
||||
0x56, 0x77,
|
||||
0x57, 0xff,
|
||||
|
||||
0xff
|
||||
};
|
||||
|
||||
|
||||
static struct saa7146_ext_vv av7110_vv_data_st;
|
||||
static struct saa7146_ext_vv av7110_vv_data_c;
|
||||
|
||||
int av7110_init_analog_module(struct av7110 *av7110)
|
||||
{
|
||||
u16 version1, version2;
|
||||
|
||||
if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 &&
|
||||
i2c_writereg(av7110, 0x80, 0x0, 0) == 1) {
|
||||
pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n",
|
||||
av7110->dvb_adapter.num);
|
||||
av7110->adac_type = DVB_ADAC_MSP34x0;
|
||||
} else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 &&
|
||||
i2c_writereg(av7110, 0x84, 0x0, 0) == 1) {
|
||||
pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n",
|
||||
av7110->dvb_adapter.num);
|
||||
av7110->adac_type = DVB_ADAC_MSP34x5;
|
||||
} else
|
||||
return -ENODEV;
|
||||
|
||||
msleep(100); // the probing above resets the msp...
|
||||
msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1);
|
||||
msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2);
|
||||
dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n",
|
||||
av7110->dvb_adapter.num, version1, version2);
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00);
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
|
||||
msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART
|
||||
|
||||
if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) {
|
||||
pr_info("saa7113 not accessible\n");
|
||||
} else {
|
||||
u8 *i = saa7113_init_regs;
|
||||
|
||||
if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) {
|
||||
/* Fujitsu/Siemens DVB-Cable */
|
||||
av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
|
||||
} else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) {
|
||||
/* Hauppauge/TT DVB-C premium */
|
||||
av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
|
||||
} else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) {
|
||||
/* Hauppauge/TT DVB-C premium */
|
||||
av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297;
|
||||
}
|
||||
|
||||
/* setup for DVB by default */
|
||||
if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
|
||||
if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20))
|
||||
dprintk(1, "setting band in demodulator failed\n");
|
||||
} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
|
||||
saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
|
||||
saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
|
||||
}
|
||||
|
||||
/* init the saa7113 */
|
||||
while (*i != 0xff) {
|
||||
if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) {
|
||||
dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num);
|
||||
break;
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
/* setup msp for analog sound: B/G Dual-FM */
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI
|
||||
msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2
|
||||
}
|
||||
|
||||
memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
|
||||
/* set dd1 stream a & b */
|
||||
saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
|
||||
saa7146_write(av7110->dev, DD1_INIT, 0x03000700);
|
||||
saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int av7110_init_v4l(struct av7110 *av7110)
|
||||
{
|
||||
struct saa7146_dev* dev = av7110->dev;
|
||||
struct saa7146_ext_vv *vv_data;
|
||||
int ret;
|
||||
|
||||
/* special case DVB-C: these cards have an analog tuner
|
||||
plus need some special handling, so we have separate
|
||||
saa7146_ext_vv data for these... */
|
||||
if (av7110->analog_tuner_flags)
|
||||
vv_data = &av7110_vv_data_c;
|
||||
else
|
||||
vv_data = &av7110_vv_data_st;
|
||||
ret = saa7146_vv_init(dev, vv_data);
|
||||
|
||||
if (ret) {
|
||||
ERR("cannot init capture device. skipping\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input;
|
||||
vv_data->vid_ops.vidioc_g_input = vidioc_g_input;
|
||||
vv_data->vid_ops.vidioc_s_input = vidioc_s_input;
|
||||
vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner;
|
||||
vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner;
|
||||
vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency;
|
||||
vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency;
|
||||
vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio;
|
||||
vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio;
|
||||
vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio;
|
||||
vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL;
|
||||
|
||||
vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner;
|
||||
vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner;
|
||||
vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency;
|
||||
vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency;
|
||||
vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL;
|
||||
vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap;
|
||||
vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out;
|
||||
vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
|
||||
|
||||
if (FW_VERSION(av7110->arm_app) < 0x2623)
|
||||
vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT;
|
||||
|
||||
if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
|
||||
ERR("cannot register capture device. skipping\n");
|
||||
saa7146_vv_release(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (FW_VERSION(av7110->arm_app) >= 0x2623) {
|
||||
if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI))
|
||||
ERR("cannot register vbi v4l2 device. skipping\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int av7110_exit_v4l(struct av7110 *av7110)
|
||||
{
|
||||
struct saa7146_dev* dev = av7110->dev;
|
||||
|
||||
saa7146_unregister_device(&av7110->v4l_dev, av7110->dev);
|
||||
saa7146_unregister_device(&av7110->vbi_dev, av7110->dev);
|
||||
|
||||
saa7146_vv_release(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* FIXME: these values are experimental values that look better than the
|
||||
values from the latest "official" driver -- at least for me... (MiHu) */
|
||||
static struct saa7146_standard standard[] = {
|
||||
{
|
||||
.name = "PAL", .id = V4L2_STD_PAL_BG,
|
||||
.v_offset = 0x15, .v_field = 288,
|
||||
.h_offset = 0x48, .h_pixels = 708,
|
||||
.v_max_out = 576, .h_max_out = 768,
|
||||
}, {
|
||||
.name = "NTSC", .id = V4L2_STD_NTSC,
|
||||
.v_offset = 0x10, .v_field = 244,
|
||||
.h_offset = 0x40, .h_pixels = 708,
|
||||
.v_max_out = 480, .h_max_out = 640,
|
||||
}
|
||||
};
|
||||
|
||||
static struct saa7146_standard analog_standard[] = {
|
||||
{
|
||||
.name = "PAL", .id = V4L2_STD_PAL_BG,
|
||||
.v_offset = 0x1b, .v_field = 288,
|
||||
.h_offset = 0x08, .h_pixels = 708,
|
||||
.v_max_out = 576, .h_max_out = 768,
|
||||
}, {
|
||||
.name = "NTSC", .id = V4L2_STD_NTSC,
|
||||
.v_offset = 0x10, .v_field = 244,
|
||||
.h_offset = 0x40, .h_pixels = 708,
|
||||
.v_max_out = 480, .h_max_out = 640,
|
||||
}
|
||||
};
|
||||
|
||||
static struct saa7146_standard dvb_standard[] = {
|
||||
{
|
||||
.name = "PAL", .id = V4L2_STD_PAL_BG,
|
||||
.v_offset = 0x14, .v_field = 288,
|
||||
.h_offset = 0x48, .h_pixels = 708,
|
||||
.v_max_out = 576, .h_max_out = 768,
|
||||
}, {
|
||||
.name = "NTSC", .id = V4L2_STD_NTSC,
|
||||
.v_offset = 0x10, .v_field = 244,
|
||||
.h_offset = 0x40, .h_pixels = 708,
|
||||
.v_max_out = 480, .h_max_out = 640,
|
||||
}
|
||||
};
|
||||
|
||||
static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
|
||||
{
|
||||
struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
|
||||
|
||||
if (std->id & V4L2_STD_PAL) {
|
||||
av7110->vidmode = AV7110_VIDEO_MODE_PAL;
|
||||
av7110_set_vidmode(av7110, av7110->vidmode);
|
||||
}
|
||||
else if (std->id & V4L2_STD_NTSC) {
|
||||
av7110->vidmode = AV7110_VIDEO_MODE_NTSC;
|
||||
av7110_set_vidmode(av7110, av7110->vidmode);
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct saa7146_ext_vv av7110_vv_data_st = {
|
||||
.inputs = 1,
|
||||
.audios = 1,
|
||||
.capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
|
||||
.flags = 0,
|
||||
|
||||
.stds = &standard[0],
|
||||
.num_stds = ARRAY_SIZE(standard),
|
||||
.std_callback = &std_callback,
|
||||
|
||||
.vbi_fops.open = av7110_vbi_reset,
|
||||
.vbi_fops.release = av7110_vbi_reset,
|
||||
.vbi_fops.write = av7110_vbi_write,
|
||||
};
|
||||
|
||||
static struct saa7146_ext_vv av7110_vv_data_c = {
|
||||
.inputs = 1,
|
||||
.audios = 1,
|
||||
.capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
|
||||
.flags = SAA7146_USE_PORT_B_FOR_VBI,
|
||||
|
||||
.stds = &standard[0],
|
||||
.num_stds = ARRAY_SIZE(standard),
|
||||
.std_callback = &std_callback,
|
||||
|
||||
.vbi_fops.open = av7110_vbi_reset,
|
||||
.vbi_fops.release = av7110_vbi_reset,
|
||||
.vbi_fops.write = av7110_vbi_write,
|
||||
};
|
||||
|
1640
drivers/media/pci/ttpci/budget-av.c
Normal file
1640
drivers/media/pci/ttpci/budget-av.c
Normal file
File diff suppressed because it is too large
Load diff
1591
drivers/media/pci/ttpci/budget-ci.c
Normal file
1591
drivers/media/pci/ttpci/budget-ci.c
Normal file
File diff suppressed because it is too large
Load diff
602
drivers/media/pci/ttpci/budget-core.c
Normal file
602
drivers/media/pci/ttpci/budget-core.c
Normal file
|
@ -0,0 +1,602 @@
|
|||
/*
|
||||
* budget-core.c: driver for the SAA7146 based Budget DVB cards
|
||||
*
|
||||
* Compiled from various sources by Michael Hunold <michael@mihu.de>
|
||||
*
|
||||
* Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
|
||||
*
|
||||
* Copyright (C) 1999-2002 Ralph Metzler
|
||||
* & Marcus Metzler for convergence integrated media GmbH
|
||||
*
|
||||
* 26feb2004 Support for FS Activy Card (Grundig tuner) by
|
||||
* Michael Dreher <michael@5dot1.de>,
|
||||
* Oliver Endriss <o.endriss@gmx.de>,
|
||||
* Andreas 'randy' Weinberger
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
*
|
||||
* the project's page is at http://www.linuxtv.org/
|
||||
*/
|
||||
|
||||
|
||||
#include "budget.h"
|
||||
#include "ttpci-eeprom.h"
|
||||
|
||||
#define TS_WIDTH (2 * TS_SIZE)
|
||||
#define TS_WIDTH_ACTIVY TS_SIZE
|
||||
#define TS_WIDTH_DVBC TS_SIZE
|
||||
#define TS_HEIGHT_MASK 0xf00
|
||||
#define TS_HEIGHT_MASK_ACTIVY 0xc00
|
||||
#define TS_HEIGHT_MASK_DVBC 0xe00
|
||||
#define TS_MIN_BUFSIZE_K 188
|
||||
#define TS_MAX_BUFSIZE_K 1410
|
||||
#define TS_MAX_BUFSIZE_K_ACTIVY 564
|
||||
#define TS_MAX_BUFSIZE_K_DVBC 1316
|
||||
#define BUFFER_WARNING_WAIT (30*HZ)
|
||||
|
||||
int budget_debug;
|
||||
static int dma_buffer_size = TS_MIN_BUFSIZE_K;
|
||||
module_param_named(debug, budget_debug, int, 0644);
|
||||
module_param_named(bufsize, dma_buffer_size, int, 0444);
|
||||
MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off).");
|
||||
MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)");
|
||||
|
||||
/****************************************************************************
|
||||
* TT budget / WinTV Nova
|
||||
****************************************************************************/
|
||||
|
||||
static int stop_ts_capture(struct budget *budget)
|
||||
{
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off
|
||||
SAA7146_IER_DISABLE(budget->dev, MASK_10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_ts_capture(struct budget *budget)
|
||||
{
|
||||
struct saa7146_dev *dev = budget->dev;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
if (!budget->feeding || !budget->fe_synced)
|
||||
return 0;
|
||||
|
||||
saa7146_write(dev, MC1, MASK_20); // DMA3 off
|
||||
|
||||
memset(budget->grabbing, 0x00, budget->buffer_size);
|
||||
|
||||
saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
|
||||
|
||||
budget->ttbp = 0;
|
||||
|
||||
/*
|
||||
* Signal path on the Activy:
|
||||
*
|
||||
* tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory
|
||||
*
|
||||
* Since the tuner feeds 204 bytes packets into the SAA7146,
|
||||
* DMA3 is configured to strip the trailing 16 FEC bytes:
|
||||
* Pitch: 188, NumBytes3: 188, NumLines3: 1024
|
||||
*/
|
||||
|
||||
switch(budget->card->type) {
|
||||
case BUDGET_FS_ACTIVY:
|
||||
saa7146_write(dev, DD1_INIT, 0x04000000);
|
||||
saa7146_write(dev, MC2, (MASK_09 | MASK_25));
|
||||
saa7146_write(dev, BRS_CTRL, 0x00000000);
|
||||
break;
|
||||
case BUDGET_PATCH:
|
||||
saa7146_write(dev, DD1_INIT, 0x00000200);
|
||||
saa7146_write(dev, MC2, (MASK_10 | MASK_26));
|
||||
saa7146_write(dev, BRS_CTRL, 0x60000000);
|
||||
break;
|
||||
case BUDGET_CIN1200C_MK3:
|
||||
case BUDGET_KNC1C_MK3:
|
||||
case BUDGET_KNC1C_TDA10024:
|
||||
case BUDGET_KNC1CP_MK3:
|
||||
if (budget->video_port == BUDGET_VIDEO_PORTA) {
|
||||
saa7146_write(dev, DD1_INIT, 0x06000200);
|
||||
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
|
||||
saa7146_write(dev, BRS_CTRL, 0x00000000);
|
||||
} else {
|
||||
saa7146_write(dev, DD1_INIT, 0x00000600);
|
||||
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
|
||||
saa7146_write(dev, BRS_CTRL, 0x60000000);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (budget->video_port == BUDGET_VIDEO_PORTA) {
|
||||
saa7146_write(dev, DD1_INIT, 0x06000200);
|
||||
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
|
||||
saa7146_write(dev, BRS_CTRL, 0x00000000);
|
||||
} else {
|
||||
saa7146_write(dev, DD1_INIT, 0x02000600);
|
||||
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
|
||||
saa7146_write(dev, BRS_CTRL, 0x60000000);
|
||||
}
|
||||
}
|
||||
|
||||
saa7146_write(dev, MC2, (MASK_08 | MASK_24));
|
||||
mdelay(10);
|
||||
|
||||
saa7146_write(dev, BASE_ODD3, 0);
|
||||
if (budget->buffer_size > budget->buffer_height * budget->buffer_width) {
|
||||
// using odd/even buffers
|
||||
saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width);
|
||||
} else {
|
||||
// using a single buffer
|
||||
saa7146_write(dev, BASE_EVEN3, 0);
|
||||
}
|
||||
saa7146_write(dev, PROT_ADDR3, budget->buffer_size);
|
||||
saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90);
|
||||
|
||||
saa7146_write(dev, PITCH3, budget->buffer_width);
|
||||
saa7146_write(dev, NUM_LINE_BYTE3,
|
||||
(budget->buffer_height << 16) | budget->buffer_width);
|
||||
|
||||
saa7146_write(dev, MC2, (MASK_04 | MASK_20));
|
||||
|
||||
SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */
|
||||
SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
|
||||
saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_read_fe_status(struct dvb_frontend *fe, fe_status_t *status)
|
||||
{
|
||||
struct budget *budget = (struct budget *) fe->dvb->priv;
|
||||
int synced;
|
||||
int ret;
|
||||
|
||||
if (budget->read_fe_status)
|
||||
ret = budget->read_fe_status(fe, status);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
if (!ret) {
|
||||
synced = (*status & FE_HAS_LOCK);
|
||||
if (synced != budget->fe_synced) {
|
||||
budget->fe_synced = synced;
|
||||
spin_lock(&budget->feedlock);
|
||||
if (synced)
|
||||
start_ts_capture(budget);
|
||||
else
|
||||
stop_ts_capture(budget);
|
||||
spin_unlock(&budget->feedlock);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vpeirq(unsigned long data)
|
||||
{
|
||||
struct budget *budget = (struct budget *) data;
|
||||
u8 *mem = (u8 *) (budget->grabbing);
|
||||
u32 olddma = budget->ttbp;
|
||||
u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
|
||||
u32 count;
|
||||
|
||||
/* Ensure streamed PCI data is synced to CPU */
|
||||
pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE);
|
||||
|
||||
/* nearest lower position divisible by 188 */
|
||||
newdma -= newdma % 188;
|
||||
|
||||
if (newdma >= budget->buffer_size)
|
||||
return;
|
||||
|
||||
budget->ttbp = newdma;
|
||||
|
||||
if (budget->feeding == 0 || newdma == olddma)
|
||||
return;
|
||||
|
||||
if (newdma > olddma) { /* no wraparound, dump olddma..newdma */
|
||||
count = newdma - olddma;
|
||||
dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188);
|
||||
} else { /* wraparound, dump olddma..buflen and 0..newdma */
|
||||
count = budget->buffer_size - olddma;
|
||||
dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188);
|
||||
count += newdma;
|
||||
dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188);
|
||||
}
|
||||
|
||||
if (count > budget->buffer_warning_threshold)
|
||||
budget->buffer_warnings++;
|
||||
|
||||
if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) {
|
||||
printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n",
|
||||
budget->dev->name, __func__, budget->buffer_warnings, count);
|
||||
budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT;
|
||||
budget->buffer_warnings = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
|
||||
int uselocks, int nobusyloop)
|
||||
{
|
||||
struct saa7146_dev *saa = budget->dev;
|
||||
int result = 0;
|
||||
unsigned long flags = 0;
|
||||
|
||||
if (count > 4 || count <= 0)
|
||||
return 0;
|
||||
|
||||
if (uselocks)
|
||||
spin_lock_irqsave(&budget->debilock, flags);
|
||||
|
||||
if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
|
||||
if (uselocks)
|
||||
spin_unlock_irqrestore(&budget->debilock, flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
|
||||
saa7146_write(saa, DEBI_CONFIG, config);
|
||||
saa7146_write(saa, DEBI_PAGE, 0);
|
||||
saa7146_write(saa, MC2, (2 << 16) | 2);
|
||||
|
||||
if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
|
||||
if (uselocks)
|
||||
spin_unlock_irqrestore(&budget->debilock, flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = saa7146_read(saa, DEBI_AD);
|
||||
result &= (0xffffffffUL >> ((4 - count) * 8));
|
||||
|
||||
if (uselocks)
|
||||
spin_unlock_irqrestore(&budget->debilock, flags);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr,
|
||||
int count, u32 value, int uselocks, int nobusyloop)
|
||||
{
|
||||
struct saa7146_dev *saa = budget->dev;
|
||||
unsigned long flags = 0;
|
||||
int result;
|
||||
|
||||
if (count > 4 || count <= 0)
|
||||
return 0;
|
||||
|
||||
if (uselocks)
|
||||
spin_lock_irqsave(&budget->debilock, flags);
|
||||
|
||||
if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
|
||||
if (uselocks)
|
||||
spin_unlock_irqrestore(&budget->debilock, flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff));
|
||||
saa7146_write(saa, DEBI_CONFIG, config);
|
||||
saa7146_write(saa, DEBI_PAGE, 0);
|
||||
saa7146_write(saa, DEBI_AD, value);
|
||||
saa7146_write(saa, MC2, (2 << 16) | 2);
|
||||
|
||||
if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
|
||||
if (uselocks)
|
||||
spin_unlock_irqrestore(&budget->debilock, flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (uselocks)
|
||||
spin_unlock_irqrestore(&budget->debilock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* DVB API SECTION
|
||||
****************************************************************************/
|
||||
|
||||
static int budget_start_feed(struct dvb_demux_feed *feed)
|
||||
{
|
||||
struct dvb_demux *demux = feed->demux;
|
||||
struct budget *budget = (struct budget *) demux->priv;
|
||||
int status = 0;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
if (!demux->dmx.frontend)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&budget->feedlock);
|
||||
feed->pusi_seen = 0; /* have a clean section start */
|
||||
if (budget->feeding++ == 0)
|
||||
status = start_ts_capture(budget);
|
||||
spin_unlock(&budget->feedlock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int budget_stop_feed(struct dvb_demux_feed *feed)
|
||||
{
|
||||
struct dvb_demux *demux = feed->demux;
|
||||
struct budget *budget = (struct budget *) demux->priv;
|
||||
int status = 0;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
spin_lock(&budget->feedlock);
|
||||
if (--budget->feeding == 0)
|
||||
status = stop_ts_capture(budget);
|
||||
spin_unlock(&budget->feedlock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int budget_register(struct budget *budget)
|
||||
{
|
||||
struct dvb_demux *dvbdemux = &budget->demux;
|
||||
int ret;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
dvbdemux->priv = (void *) budget;
|
||||
|
||||
dvbdemux->filternum = 256;
|
||||
dvbdemux->feednum = 256;
|
||||
dvbdemux->start_feed = budget_start_feed;
|
||||
dvbdemux->stop_feed = budget_stop_feed;
|
||||
dvbdemux->write_to_decoder = NULL;
|
||||
|
||||
dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
|
||||
DMX_MEMORY_BASED_FILTERING);
|
||||
|
||||
dvb_dmx_init(&budget->demux);
|
||||
|
||||
budget->dmxdev.filternum = 256;
|
||||
budget->dmxdev.demux = &dvbdemux->dmx;
|
||||
budget->dmxdev.capabilities = 0;
|
||||
|
||||
dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter);
|
||||
|
||||
budget->hw_frontend.source = DMX_FRONTEND_0;
|
||||
|
||||
ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
budget->mem_frontend.source = DMX_MEMORY_FE;
|
||||
ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void budget_unregister(struct budget *budget)
|
||||
{
|
||||
struct dvb_demux *dvbdemux = &budget->demux;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
dvb_net_release(&budget->dvb_net);
|
||||
|
||||
dvbdemux->dmx.close(&dvbdemux->dmx);
|
||||
dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend);
|
||||
dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend);
|
||||
|
||||
dvb_dmxdev_release(&budget->dmxdev);
|
||||
dvb_dmx_release(&budget->demux);
|
||||
}
|
||||
|
||||
int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
|
||||
struct saa7146_pci_extension_data *info,
|
||||
struct module *owner, short *adapter_nums)
|
||||
{
|
||||
int ret = 0;
|
||||
struct budget_info *bi = info->ext_priv;
|
||||
int max_bufsize;
|
||||
int height_mask;
|
||||
|
||||
memset(budget, 0, sizeof(struct budget));
|
||||
|
||||
dprintk(2, "dev: %p, budget: %p\n", dev, budget);
|
||||
|
||||
budget->card = bi;
|
||||
budget->dev = (struct saa7146_dev *) dev;
|
||||
|
||||
switch(budget->card->type) {
|
||||
case BUDGET_FS_ACTIVY:
|
||||
budget->buffer_width = TS_WIDTH_ACTIVY;
|
||||
max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY;
|
||||
height_mask = TS_HEIGHT_MASK_ACTIVY;
|
||||
break;
|
||||
|
||||
case BUDGET_KNC1C:
|
||||
case BUDGET_KNC1CP:
|
||||
case BUDGET_CIN1200C:
|
||||
case BUDGET_KNC1C_MK3:
|
||||
case BUDGET_KNC1C_TDA10024:
|
||||
case BUDGET_KNC1CP_MK3:
|
||||
case BUDGET_CIN1200C_MK3:
|
||||
budget->buffer_width = TS_WIDTH_DVBC;
|
||||
max_bufsize = TS_MAX_BUFSIZE_K_DVBC;
|
||||
height_mask = TS_HEIGHT_MASK_DVBC;
|
||||
break;
|
||||
|
||||
default:
|
||||
budget->buffer_width = TS_WIDTH;
|
||||
max_bufsize = TS_MAX_BUFSIZE_K;
|
||||
height_mask = TS_HEIGHT_MASK;
|
||||
}
|
||||
|
||||
if (dma_buffer_size < TS_MIN_BUFSIZE_K)
|
||||
dma_buffer_size = TS_MIN_BUFSIZE_K;
|
||||
else if (dma_buffer_size > max_bufsize)
|
||||
dma_buffer_size = max_bufsize;
|
||||
|
||||
budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width;
|
||||
if (budget->buffer_height > 0xfff) {
|
||||
budget->buffer_height /= 2;
|
||||
budget->buffer_height &= height_mask;
|
||||
budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width;
|
||||
} else {
|
||||
budget->buffer_height &= height_mask;
|
||||
budget->buffer_size = budget->buffer_height * budget->buffer_width;
|
||||
}
|
||||
budget->buffer_warning_threshold = budget->buffer_size * 80/100;
|
||||
budget->buffer_warnings = 0;
|
||||
budget->buffer_warning_time = jiffies;
|
||||
|
||||
dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n",
|
||||
budget->dev->name,
|
||||
budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single",
|
||||
budget->buffer_width, budget->buffer_height);
|
||||
printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
|
||||
|
||||
ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name,
|
||||
owner, &budget->dev->pci->dev, adapter_nums);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set dd1 stream a & b */
|
||||
saa7146_write(dev, DD1_STREAM_B, 0x00000000);
|
||||
saa7146_write(dev, MC2, (MASK_09 | MASK_25));
|
||||
saa7146_write(dev, MC2, (MASK_10 | MASK_26));
|
||||
saa7146_write(dev, DD1_INIT, 0x02000000);
|
||||
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
|
||||
|
||||
if (bi->type != BUDGET_FS_ACTIVY)
|
||||
budget->video_port = BUDGET_VIDEO_PORTB;
|
||||
else
|
||||
budget->video_port = BUDGET_VIDEO_PORTA;
|
||||
spin_lock_init(&budget->feedlock);
|
||||
spin_lock_init(&budget->debilock);
|
||||
|
||||
/* the Siemens DVB needs this if you want to have the i2c chips
|
||||
get recognized before the main driver is loaded */
|
||||
if (bi->type != BUDGET_FS_ACTIVY)
|
||||
saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */
|
||||
|
||||
strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name));
|
||||
|
||||
saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120);
|
||||
strcpy(budget->i2c_adap.name, budget->card->name);
|
||||
|
||||
if (i2c_add_adapter(&budget->i2c_adap) < 0) {
|
||||
ret = -ENOMEM;
|
||||
goto err_dvb_unregister;
|
||||
}
|
||||
|
||||
ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac);
|
||||
|
||||
budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt);
|
||||
if (NULL == budget->grabbing) {
|
||||
ret = -ENOMEM;
|
||||
goto err_del_i2c;
|
||||
}
|
||||
|
||||
saa7146_write(dev, PCI_BT_V1, 0x001c0000);
|
||||
/* upload all */
|
||||
saa7146_write(dev, GPIO_CTRL, 0x000000);
|
||||
|
||||
tasklet_init(&budget->vpe_tasklet, vpeirq, (unsigned long) budget);
|
||||
|
||||
/* frontend power on */
|
||||
if (bi->type != BUDGET_FS_ACTIVY)
|
||||
saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
|
||||
|
||||
if ((ret = budget_register(budget)) == 0)
|
||||
return 0; /* Everything OK */
|
||||
|
||||
/* An error occurred, cleanup resources */
|
||||
saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt);
|
||||
|
||||
err_del_i2c:
|
||||
i2c_del_adapter(&budget->i2c_adap);
|
||||
|
||||
err_dvb_unregister:
|
||||
dvb_unregister_adapter(&budget->dvb_adapter);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ttpci_budget_init_hooks(struct budget *budget)
|
||||
{
|
||||
if (budget->dvb_frontend && !budget->read_fe_status) {
|
||||
budget->read_fe_status = budget->dvb_frontend->ops.read_status;
|
||||
budget->dvb_frontend->ops.read_status = budget_read_fe_status;
|
||||
}
|
||||
}
|
||||
|
||||
int ttpci_budget_deinit(struct budget *budget)
|
||||
{
|
||||
struct saa7146_dev *dev = budget->dev;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
budget_unregister(budget);
|
||||
|
||||
tasklet_kill(&budget->vpe_tasklet);
|
||||
|
||||
saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt);
|
||||
|
||||
i2c_del_adapter(&budget->i2c_adap);
|
||||
|
||||
dvb_unregister_adapter(&budget->dvb_adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr)
|
||||
{
|
||||
struct budget *budget = (struct budget *) dev->ext_priv;
|
||||
|
||||
dprintk(8, "dev: %p, budget: %p\n", dev, budget);
|
||||
|
||||
if (*isr & MASK_10)
|
||||
tasklet_schedule(&budget->vpe_tasklet);
|
||||
}
|
||||
|
||||
void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port)
|
||||
{
|
||||
struct budget *budget = (struct budget *) dev->ext_priv;
|
||||
|
||||
spin_lock(&budget->feedlock);
|
||||
budget->video_port = video_port;
|
||||
if (budget->feeding) {
|
||||
stop_ts_capture(budget);
|
||||
start_ts_capture(budget);
|
||||
}
|
||||
spin_unlock(&budget->feedlock);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ttpci_budget_debiread);
|
||||
EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite);
|
||||
EXPORT_SYMBOL_GPL(ttpci_budget_init);
|
||||
EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks);
|
||||
EXPORT_SYMBOL_GPL(ttpci_budget_deinit);
|
||||
EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler);
|
||||
EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port);
|
||||
EXPORT_SYMBOL_GPL(budget_debug);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
680
drivers/media/pci/ttpci/budget-patch.c
Normal file
680
drivers/media/pci/ttpci/budget-patch.c
Normal file
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
* budget-patch.c: driver for Budget Patch,
|
||||
* hardware modification of DVB-S cards enabling full TS
|
||||
*
|
||||
* Written by Emard <emard@softhome.net>
|
||||
*
|
||||
* Original idea by Roberto Deza <rdeza@unav.es>
|
||||
*
|
||||
* Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic
|
||||
* and Metzlerbros
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
*
|
||||
* the project's page is at http://www.linuxtv.org/
|
||||
*/
|
||||
|
||||
#include "av7110.h"
|
||||
#include "av7110_hw.h"
|
||||
#include "budget.h"
|
||||
#include "stv0299.h"
|
||||
#include "ves1x93.h"
|
||||
#include "tda8083.h"
|
||||
|
||||
#include "bsru6.h"
|
||||
|
||||
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
||||
|
||||
#define budget_patch budget
|
||||
|
||||
static struct saa7146_extension budget_extension;
|
||||
|
||||
MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH);
|
||||
//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC);
|
||||
|
||||
static struct pci_device_id pci_tbl[] = {
|
||||
MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000),
|
||||
// MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
|
||||
{
|
||||
.vendor = 0,
|
||||
}
|
||||
};
|
||||
|
||||
/* those lines are for budget-patch to be tried
|
||||
** on a true budget card and observe the
|
||||
** behaviour of VSYNC generated by rps1.
|
||||
** this code was shamelessly copy/pasted from budget.c
|
||||
*/
|
||||
static void gpio_Set22K (struct budget *budget, int state)
|
||||
{
|
||||
struct saa7146_dev *dev=budget->dev;
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
|
||||
}
|
||||
|
||||
/* Diseqc functions only for TT Budget card */
|
||||
/* taken from the Skyvision DVB driver by
|
||||
Ralph Metzler <rjkm@metzlerbros.de> */
|
||||
|
||||
static void DiseqcSendBit (struct budget *budget, int data)
|
||||
{
|
||||
struct saa7146_dev *dev=budget->dev;
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
|
||||
udelay(data ? 500 : 1000);
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
|
||||
udelay(data ? 1000 : 500);
|
||||
}
|
||||
|
||||
static void DiseqcSendByte (struct budget *budget, int data)
|
||||
{
|
||||
int i, par=1, d;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
for (i=7; i>=0; i--) {
|
||||
d = (data>>i)&1;
|
||||
par ^= d;
|
||||
DiseqcSendBit(budget, d);
|
||||
}
|
||||
|
||||
DiseqcSendBit(budget, par);
|
||||
}
|
||||
|
||||
static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
|
||||
{
|
||||
struct saa7146_dev *dev=budget->dev;
|
||||
int i;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
|
||||
mdelay(16);
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
DiseqcSendByte(budget, msg[i]);
|
||||
|
||||
mdelay(16);
|
||||
|
||||
if (burst!=-1) {
|
||||
if (burst)
|
||||
DiseqcSendByte(budget, 0xff);
|
||||
else {
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
|
||||
mdelay(12);
|
||||
udelay(500);
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
|
||||
}
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* shamelessly copy/pasted from budget.c
|
||||
*/
|
||||
static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
|
||||
{
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
|
||||
switch (tone) {
|
||||
case SEC_TONE_ON:
|
||||
gpio_Set22K (budget, 1);
|
||||
break;
|
||||
|
||||
case SEC_TONE_OFF:
|
||||
gpio_Set22K (budget, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
|
||||
{
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
|
||||
SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
|
||||
{
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
|
||||
SendDiSEqCMsg (budget, 0, NULL, minicmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
for (i = 2; i < length; i++)
|
||||
{
|
||||
ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0);
|
||||
msleep(5);
|
||||
}
|
||||
if (length)
|
||||
ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0);
|
||||
else
|
||||
ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0);
|
||||
msleep(5);
|
||||
ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0);
|
||||
msleep(5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void av7110_set22k(struct budget_patch *budget, int state)
|
||||
{
|
||||
u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0};
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
budget_av7110_send_fw_cmd(budget, buf, 2);
|
||||
}
|
||||
|
||||
static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst)
|
||||
{
|
||||
int i;
|
||||
u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC),
|
||||
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
if (len>10)
|
||||
len=10;
|
||||
|
||||
buf[1] = len+2;
|
||||
buf[2] = len;
|
||||
|
||||
if (burst != -1)
|
||||
buf[3]=burst ? 0x01 : 0x00;
|
||||
else
|
||||
buf[3]=0xffff;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
buf[i+4]=msg[i];
|
||||
|
||||
budget_av7110_send_fw_cmd(budget, buf, 18);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_patch_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
|
||||
{
|
||||
struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
|
||||
|
||||
switch (tone) {
|
||||
case SEC_TONE_ON:
|
||||
av7110_set22k (budget, 1);
|
||||
break;
|
||||
|
||||
case SEC_TONE_OFF:
|
||||
av7110_set22k (budget, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
|
||||
{
|
||||
struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
|
||||
|
||||
av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
|
||||
{
|
||||
struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
|
||||
|
||||
av7110_send_diseqc_msg (budget, 0, NULL, minicmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
|
||||
{
|
||||
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
|
||||
struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
|
||||
u8 pwr = 0;
|
||||
u8 buf[4];
|
||||
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
|
||||
u32 div = (p->frequency + 479500) / 125;
|
||||
|
||||
if (p->frequency > 2000000)
|
||||
pwr = 3;
|
||||
else if (p->frequency > 1800000)
|
||||
pwr = 2;
|
||||
else if (p->frequency > 1600000)
|
||||
pwr = 1;
|
||||
else if (p->frequency > 1200000)
|
||||
pwr = 0;
|
||||
else if (p->frequency >= 1100000)
|
||||
pwr = 1;
|
||||
else pwr = 2;
|
||||
|
||||
buf[0] = (div >> 8) & 0x7f;
|
||||
buf[1] = div & 0xff;
|
||||
buf[2] = ((div & 0x18000) >> 10) | 0x95;
|
||||
buf[3] = (pwr << 6) | 0x30;
|
||||
|
||||
// NOTE: since we're using a prescaler of 2, we set the
|
||||
// divisor frequency to 62.5kHz and divide by 125 above
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ves1x93_config alps_bsrv2_config = {
|
||||
.demod_address = 0x08,
|
||||
.xin = 90100000UL,
|
||||
.invert_pwm = 0,
|
||||
};
|
||||
|
||||
static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe)
|
||||
{
|
||||
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
|
||||
struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
|
||||
u32 div;
|
||||
u8 data[4];
|
||||
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
|
||||
|
||||
div = p->frequency / 125;
|
||||
data[0] = (div >> 8) & 0x7f;
|
||||
data[1] = div & 0xff;
|
||||
data[2] = 0x8e;
|
||||
data[3] = 0x00;
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tda8083_config grundig_29504_451_config = {
|
||||
.demod_address = 0x68,
|
||||
};
|
||||
|
||||
static void frontend_init(struct budget_patch* budget)
|
||||
{
|
||||
switch(budget->dev->pci->subsystem_device) {
|
||||
case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X
|
||||
case 0x1013: // SATELCO Multimedia PCI
|
||||
|
||||
// try the ALPS BSRV2 first of all
|
||||
budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
|
||||
budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd;
|
||||
budget->dvb_frontend->ops.diseqc_send_burst = budget_patch_diseqc_send_burst;
|
||||
budget->dvb_frontend->ops.set_tone = budget_patch_set_tone;
|
||||
break;
|
||||
}
|
||||
|
||||
// try the ALPS BSRU6 now
|
||||
budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
|
||||
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
|
||||
|
||||
budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
|
||||
budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
|
||||
budget->dvb_frontend->ops.set_tone = budget_set_tone;
|
||||
break;
|
||||
}
|
||||
|
||||
// Try the grundig 29504-451
|
||||
budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
|
||||
budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
|
||||
budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
|
||||
budget->dvb_frontend->ops.set_tone = budget_set_tone;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (budget->dvb_frontend == NULL) {
|
||||
printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
|
||||
budget->dev->pci->vendor,
|
||||
budget->dev->pci->device,
|
||||
budget->dev->pci->subsystem_vendor,
|
||||
budget->dev->pci->subsystem_device);
|
||||
} else {
|
||||
if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) {
|
||||
printk("budget-av: Frontend registration failed!\n");
|
||||
dvb_frontend_detach(budget->dvb_frontend);
|
||||
budget->dvb_frontend = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* written by Emard */
|
||||
static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
|
||||
{
|
||||
struct budget_patch *budget;
|
||||
int err;
|
||||
int count = 0;
|
||||
int detected = 0;
|
||||
|
||||
#define PATCH_RESET 0
|
||||
#define RPS_IRQ 0
|
||||
#define HPS_SETUP 0
|
||||
#if PATCH_RESET
|
||||
saa7146_write(dev, MC1, MASK_31);
|
||||
msleep(40);
|
||||
#endif
|
||||
#if HPS_SETUP
|
||||
// initialize registers. Better to have it like this
|
||||
// than leaving something unconfigured
|
||||
saa7146_write(dev, DD1_STREAM_B, 0);
|
||||
// port B VSYNC at rising edge
|
||||
saa7146_write(dev, DD1_INIT, 0x00000200); // have this in budget-core too!
|
||||
saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI
|
||||
|
||||
// debi config
|
||||
// saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18);
|
||||
|
||||
// zero all HPS registers
|
||||
saa7146_write(dev, HPS_H_PRESCALE, 0); // r68
|
||||
saa7146_write(dev, HPS_H_SCALE, 0); // r6c
|
||||
saa7146_write(dev, BCS_CTRL, 0); // r70
|
||||
saa7146_write(dev, HPS_V_SCALE, 0); // r60
|
||||
saa7146_write(dev, HPS_V_GAIN, 0); // r64
|
||||
saa7146_write(dev, CHROMA_KEY_RANGE, 0); // r74
|
||||
saa7146_write(dev, CLIP_FORMAT_CTRL, 0); // r78
|
||||
// Set HPS prescaler for port B input
|
||||
saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) );
|
||||
saa7146_write(dev, MC2,
|
||||
0 * (MASK_08 | MASK_24) | // BRS control
|
||||
0 * (MASK_09 | MASK_25) | // a
|
||||
0 * (MASK_10 | MASK_26) | // b
|
||||
1 * (MASK_06 | MASK_22) | // HPS_CTRL1
|
||||
1 * (MASK_05 | MASK_21) | // HPS_CTRL2
|
||||
0 * (MASK_01 | MASK_15) // DEBI
|
||||
);
|
||||
#endif
|
||||
// Disable RPS1 and RPS0
|
||||
saa7146_write(dev, MC1, ( MASK_29 | MASK_28));
|
||||
// RPS1 timeout disable
|
||||
saa7146_write(dev, RPS_TOV1, 0);
|
||||
|
||||
// code for autodetection
|
||||
// will wait for VBI_B event (vertical blank at port B)
|
||||
// and will reset GPIO3 after VBI_B is detected.
|
||||
// (GPIO3 should be raised high by CPU to
|
||||
// test if GPIO3 will generate vertical blank signal
|
||||
// in budget patch GPIO3 is connected to VSYNC_B
|
||||
count = 0;
|
||||
#if 0
|
||||
WRITE_RPS1(CMD_UPLOAD |
|
||||
MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 );
|
||||
#endif
|
||||
WRITE_RPS1(CMD_PAUSE | EVT_VBI_B);
|
||||
WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
|
||||
WRITE_RPS1(GPIO3_MSK);
|
||||
WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
|
||||
#if RPS_IRQ
|
||||
// issue RPS1 interrupt to increment counter
|
||||
WRITE_RPS1(CMD_INTERRUPT);
|
||||
// at least a NOP is neede between two interrupts
|
||||
WRITE_RPS1(CMD_NOP);
|
||||
// interrupt again
|
||||
WRITE_RPS1(CMD_INTERRUPT);
|
||||
#endif
|
||||
WRITE_RPS1(CMD_STOP);
|
||||
|
||||
#if RPS_IRQ
|
||||
// set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53)
|
||||
// use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled
|
||||
// use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called
|
||||
saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
|
||||
// set event counter 1 threshold to maximum allowed value (rEC p55)
|
||||
saa7146_write(dev, ECT1R, 0x3fff );
|
||||
#endif
|
||||
// Fix VSYNC level
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
|
||||
// Set RPS1 Address register to point to RPS code (r108 p42)
|
||||
saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
|
||||
// Enable RPS1, (rFC p33)
|
||||
saa7146_write(dev, MC1, (MASK_13 | MASK_29 ));
|
||||
|
||||
|
||||
mdelay(50);
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
|
||||
mdelay(150);
|
||||
|
||||
|
||||
if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0)
|
||||
detected = 1;
|
||||
|
||||
#if RPS_IRQ
|
||||
printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff );
|
||||
#endif
|
||||
// Disable RPS1
|
||||
saa7146_write(dev, MC1, ( MASK_29 ));
|
||||
|
||||
if(detected == 0)
|
||||
printk("budget-patch not detected or saa7146 in non-default state.\n"
|
||||
"try enabling ressetting of 7146 with MASK_31 in MC1 register\n");
|
||||
|
||||
else
|
||||
printk("BUDGET-PATCH DETECTED.\n");
|
||||
|
||||
|
||||
/* OLD (Original design by Roberto Deza):
|
||||
** This code will setup the SAA7146_RPS1 to generate a square
|
||||
** wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of
|
||||
** TS_WIDTH packets) has been acquired on SAA7146_D1B video port;
|
||||
** then, this GPIO3 output which is connected to the D1B_VSYNC
|
||||
** input, will trigger the acquisition of the alternate field
|
||||
** and so on.
|
||||
** Currently, the TT_budget / WinTV_Nova cards have two ICs
|
||||
** (74HCT4040, LVC74) for the generation of this VSYNC signal,
|
||||
** which seems that can be done perfectly without this :-)).
|
||||
*/
|
||||
|
||||
/* New design (By Emard)
|
||||
** this rps1 code will copy internal HS event to GPIO3 pin.
|
||||
** GPIO3 is in budget-patch hardware connected to port B VSYNC
|
||||
|
||||
** HS is an internal event of 7146, accessible with RPS
|
||||
** and temporarily raised high every n lines
|
||||
** (n in defined in the RPS_THRESH1 counter threshold)
|
||||
** I think HS is raised high on the beginning of the n-th line
|
||||
** and remains high until this n-th line that triggered
|
||||
** it is completely received. When the reception of n-th line
|
||||
** ends, HS is lowered.
|
||||
|
||||
** To transmit data over DMA, 7146 needs changing state at
|
||||
** port B VSYNC pin. Any changing of port B VSYNC will
|
||||
** cause some DMA data transfer, with more or less packets loss.
|
||||
** It depends on the phase and frequency of VSYNC and
|
||||
** the way of 7146 is instructed to trigger on port B (defined
|
||||
** in DD1_INIT register, 3rd nibble from the right valid
|
||||
** numbers are 0-7, see datasheet)
|
||||
**
|
||||
** The correct triggering can minimize packet loss,
|
||||
** dvbtraffic should give this stable bandwidths:
|
||||
** 22k transponder = 33814 kbit/s
|
||||
** 27.5k transponder = 38045 kbit/s
|
||||
** by experiment it is found that the best results
|
||||
** (stable bandwidths and almost no packet loss)
|
||||
** are obtained using DD1_INIT triggering number 2
|
||||
** (Va at rising edge of VS Fa = HS x VS-failing forced toggle)
|
||||
** and a VSYNC phase that occurs in the middle of DMA transfer
|
||||
** (about byte 188*512=96256 in the DMA window).
|
||||
**
|
||||
** Phase of HS is still not clear to me how to control,
|
||||
** It just happens to be so. It can be seen if one enables
|
||||
** RPS_IRQ and print Event Counter 1 in vpeirq(). Every
|
||||
** time RPS_INTERRUPT is called, the Event Counter 1 will
|
||||
** increment. That's how the 7146 is programmed to do event
|
||||
** counting in this budget-patch.c
|
||||
** I *think* HPS setting has something to do with the phase
|
||||
** of HS but I can't be 100% sure in that.
|
||||
|
||||
** hardware debug note: a working budget card (including budget patch)
|
||||
** with vpeirq() interrupt setup in mode "0x90" (every 64K) will
|
||||
** generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes
|
||||
** and that means 3*25=75 Hz of interrupt frequency, as seen by
|
||||
** watch cat /proc/interrupts
|
||||
**
|
||||
** If this frequency is 3x lower (and data received in the DMA
|
||||
** buffer don't start with 0x47, but in the middle of packets,
|
||||
** whose lengths appear to be like 188 292 188 104 etc.
|
||||
** this means VSYNC line is not connected in the hardware.
|
||||
** (check soldering pcb and pins)
|
||||
** The same behaviour of missing VSYNC can be duplicated on budget
|
||||
** cards, by setting DD1_INIT trigger mode 7 in 3rd nibble.
|
||||
*/
|
||||
|
||||
// Setup RPS1 "program" (p35)
|
||||
count = 0;
|
||||
|
||||
|
||||
// Wait Source Line Counter Threshold (p36)
|
||||
WRITE_RPS1(CMD_PAUSE | EVT_HS);
|
||||
// Set GPIO3=1 (p42)
|
||||
WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
|
||||
WRITE_RPS1(GPIO3_MSK);
|
||||
WRITE_RPS1(SAA7146_GPIO_OUTHI<<24);
|
||||
#if RPS_IRQ
|
||||
// issue RPS1 interrupt
|
||||
WRITE_RPS1(CMD_INTERRUPT);
|
||||
#endif
|
||||
// Wait reset Source Line Counter Threshold (p36)
|
||||
WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS);
|
||||
// Set GPIO3=0 (p42)
|
||||
WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
|
||||
WRITE_RPS1(GPIO3_MSK);
|
||||
WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
|
||||
#if RPS_IRQ
|
||||
// issue RPS1 interrupt
|
||||
WRITE_RPS1(CMD_INTERRUPT);
|
||||
#endif
|
||||
// Jump to begin of RPS program (p37)
|
||||
WRITE_RPS1(CMD_JUMP);
|
||||
WRITE_RPS1(dev->d_rps1.dma_handle);
|
||||
|
||||
// Fix VSYNC level
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
|
||||
// Set RPS1 Address register to point to RPS code (r108 p42)
|
||||
saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
|
||||
|
||||
if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
|
||||
if (err) {
|
||||
kfree(budget);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Set Source Line Counter Threshold, using BRS (rCC p43)
|
||||
// It generates HS event every TS_HEIGHT lines
|
||||
// this is related to TS_WIDTH set in register
|
||||
// NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE
|
||||
// low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188
|
||||
//,then RPS_THRESH1
|
||||
// should be set to trigger every TS_HEIGHT (512) lines.
|
||||
//
|
||||
saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 );
|
||||
|
||||
// saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 );
|
||||
// Enable RPS1 (rFC p33)
|
||||
saa7146_write(dev, MC1, (MASK_13 | MASK_29));
|
||||
|
||||
|
||||
dev->ext_priv = budget;
|
||||
|
||||
budget->dvb_adapter.priv = budget;
|
||||
frontend_init(budget);
|
||||
|
||||
ttpci_budget_init_hooks(budget);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_patch_detach (struct saa7146_dev* dev)
|
||||
{
|
||||
struct budget_patch *budget = (struct budget_patch*) dev->ext_priv;
|
||||
int err;
|
||||
|
||||
if (budget->dvb_frontend) {
|
||||
dvb_unregister_frontend(budget->dvb_frontend);
|
||||
dvb_frontend_detach(budget->dvb_frontend);
|
||||
}
|
||||
err = ttpci_budget_deinit (budget);
|
||||
|
||||
kfree (budget);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __init budget_patch_init(void)
|
||||
{
|
||||
return saa7146_register_extension(&budget_extension);
|
||||
}
|
||||
|
||||
static void __exit budget_patch_exit(void)
|
||||
{
|
||||
saa7146_unregister_extension(&budget_extension);
|
||||
}
|
||||
|
||||
static struct saa7146_extension budget_extension = {
|
||||
.name = "budget_patch dvb",
|
||||
.flags = 0,
|
||||
|
||||
.module = THIS_MODULE,
|
||||
.pci_tbl = pci_tbl,
|
||||
.attach = budget_patch_attach,
|
||||
.detach = budget_patch_detach,
|
||||
|
||||
.irq_mask = MASK_10,
|
||||
.irq_func = ttpci_budget_irq10_handler,
|
||||
};
|
||||
|
||||
module_init(budget_patch_init);
|
||||
module_exit(budget_patch_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others");
|
||||
MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 "
|
||||
"based so-called Budget Patch cards");
|
883
drivers/media/pci/ttpci/budget.c
Normal file
883
drivers/media/pci/ttpci/budget.c
Normal file
|
@ -0,0 +1,883 @@
|
|||
/*
|
||||
* budget.c: driver for the SAA7146 based Budget DVB cards
|
||||
*
|
||||
* Compiled from various sources by Michael Hunold <michael@mihu.de>
|
||||
*
|
||||
* Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
|
||||
*
|
||||
* Copyright (C) 1999-2002 Ralph Metzler
|
||||
* & Marcus Metzler for convergence integrated media GmbH
|
||||
*
|
||||
* 26feb2004 Support for FS Activy Card (Grundig tuner) by
|
||||
* Michael Dreher <michael@5dot1.de>,
|
||||
* Oliver Endriss <o.endriss@gmx.de> and
|
||||
* Andreas 'randy' Weinberger
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
*
|
||||
* the project's page is at http://www.linuxtv.org/
|
||||
*/
|
||||
|
||||
#include "budget.h"
|
||||
#include "stv0299.h"
|
||||
#include "ves1x93.h"
|
||||
#include "ves1820.h"
|
||||
#include "l64781.h"
|
||||
#include "tda8083.h"
|
||||
#include "s5h1420.h"
|
||||
#include "tda10086.h"
|
||||
#include "tda826x.h"
|
||||
#include "lnbp21.h"
|
||||
#include "bsru6.h"
|
||||
#include "bsbe1.h"
|
||||
#include "tdhd1.h"
|
||||
#include "stv6110x.h"
|
||||
#include "stv090x.h"
|
||||
#include "isl6423.h"
|
||||
#include "lnbh24.h"
|
||||
|
||||
|
||||
static int diseqc_method;
|
||||
module_param(diseqc_method, int, 0444);
|
||||
MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)");
|
||||
|
||||
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
||||
|
||||
static void Set22K (struct budget *budget, int state)
|
||||
{
|
||||
struct saa7146_dev *dev=budget->dev;
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
|
||||
}
|
||||
|
||||
/* Diseqc functions only for TT Budget card */
|
||||
/* taken from the Skyvision DVB driver by
|
||||
Ralph Metzler <rjkm@metzlerbros.de> */
|
||||
|
||||
static void DiseqcSendBit (struct budget *budget, int data)
|
||||
{
|
||||
struct saa7146_dev *dev=budget->dev;
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
|
||||
udelay(data ? 500 : 1000);
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
|
||||
udelay(data ? 1000 : 500);
|
||||
}
|
||||
|
||||
static void DiseqcSendByte (struct budget *budget, int data)
|
||||
{
|
||||
int i, par=1, d;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
for (i=7; i>=0; i--) {
|
||||
d = (data>>i)&1;
|
||||
par ^= d;
|
||||
DiseqcSendBit(budget, d);
|
||||
}
|
||||
|
||||
DiseqcSendBit(budget, par);
|
||||
}
|
||||
|
||||
static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
|
||||
{
|
||||
struct saa7146_dev *dev=budget->dev;
|
||||
int i;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
|
||||
mdelay(16);
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
DiseqcSendByte(budget, msg[i]);
|
||||
|
||||
mdelay(16);
|
||||
|
||||
if (burst!=-1) {
|
||||
if (burst)
|
||||
DiseqcSendByte(budget, 0xff);
|
||||
else {
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
|
||||
mdelay(12);
|
||||
udelay(500);
|
||||
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
|
||||
}
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines for the Fujitsu Siemens Activy budget card
|
||||
* 22 kHz tone and DiSEqC are handled by the frontend.
|
||||
* Voltage must be set here.
|
||||
* GPIO 1: LNBP EN, GPIO 2: LNBP VSEL
|
||||
*/
|
||||
static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage)
|
||||
{
|
||||
struct saa7146_dev *dev=budget->dev;
|
||||
|
||||
dprintk(2, "budget: %p\n", budget);
|
||||
|
||||
switch (voltage) {
|
||||
case SEC_VOLTAGE_13:
|
||||
saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
|
||||
saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
|
||||
break;
|
||||
case SEC_VOLTAGE_18:
|
||||
saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
|
||||
saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
|
||||
break;
|
||||
case SEC_VOLTAGE_OFF:
|
||||
saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
|
||||
{
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
|
||||
return SetVoltage_Activy (budget, voltage);
|
||||
}
|
||||
|
||||
static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
|
||||
{
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
|
||||
switch (tone) {
|
||||
case SEC_TONE_ON:
|
||||
Set22K (budget, 1);
|
||||
break;
|
||||
|
||||
case SEC_TONE_OFF:
|
||||
Set22K (budget, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
|
||||
{
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
|
||||
SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
|
||||
{
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
|
||||
SendDiSEqCMsg (budget, 0, NULL, minicmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
|
||||
{
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
u8 pwr = 0;
|
||||
u8 buf[4];
|
||||
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
|
||||
u32 div = (c->frequency + 479500) / 125;
|
||||
|
||||
if (c->frequency > 2000000)
|
||||
pwr = 3;
|
||||
else if (c->frequency > 1800000)
|
||||
pwr = 2;
|
||||
else if (c->frequency > 1600000)
|
||||
pwr = 1;
|
||||
else if (c->frequency > 1200000)
|
||||
pwr = 0;
|
||||
else if (c->frequency >= 1100000)
|
||||
pwr = 1;
|
||||
else pwr = 2;
|
||||
|
||||
buf[0] = (div >> 8) & 0x7f;
|
||||
buf[1] = div & 0xff;
|
||||
buf[2] = ((div & 0x18000) >> 10) | 0x95;
|
||||
buf[3] = (pwr << 6) | 0x30;
|
||||
|
||||
// NOTE: since we're using a prescaler of 2, we set the
|
||||
// divisor frequency to 62.5kHz and divide by 125 above
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ves1x93_config alps_bsrv2_config =
|
||||
{
|
||||
.demod_address = 0x08,
|
||||
.xin = 90100000UL,
|
||||
.invert_pwm = 0,
|
||||
};
|
||||
|
||||
static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe)
|
||||
{
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
u32 div;
|
||||
u8 data[4];
|
||||
struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) };
|
||||
|
||||
div = (c->frequency + 35937500 + 31250) / 62500;
|
||||
|
||||
data[0] = (div >> 8) & 0x7f;
|
||||
data[1] = div & 0xff;
|
||||
data[2] = 0x85 | ((div >> 10) & 0x60);
|
||||
data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81);
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ves1820_config alps_tdbe2_config = {
|
||||
.demod_address = 0x09,
|
||||
.xin = 57840000UL,
|
||||
.invert = 1,
|
||||
.selagc = VES1820_SELAGC_SIGNAMPERR,
|
||||
};
|
||||
|
||||
static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe)
|
||||
{
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
struct budget *budget = fe->dvb->priv;
|
||||
u8 *tuner_addr = fe->tuner_priv;
|
||||
u32 div;
|
||||
u8 cfg, cpump, band_select;
|
||||
u8 data[4];
|
||||
struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) };
|
||||
|
||||
if (tuner_addr)
|
||||
msg.addr = *tuner_addr;
|
||||
else
|
||||
msg.addr = 0x61;
|
||||
|
||||
div = (36125000 + c->frequency) / 166666;
|
||||
|
||||
cfg = 0x88;
|
||||
|
||||
if (c->frequency < 175000000)
|
||||
cpump = 2;
|
||||
else if (c->frequency < 390000000)
|
||||
cpump = 1;
|
||||
else if (c->frequency < 470000000)
|
||||
cpump = 2;
|
||||
else if (c->frequency < 750000000)
|
||||
cpump = 1;
|
||||
else
|
||||
cpump = 3;
|
||||
|
||||
if (c->frequency < 175000000)
|
||||
band_select = 0x0e;
|
||||
else if (c->frequency < 470000000)
|
||||
band_select = 0x05;
|
||||
else
|
||||
band_select = 0x03;
|
||||
|
||||
data[0] = (div >> 8) & 0x7f;
|
||||
data[1] = div & 0xff;
|
||||
data[2] = ((div >> 10) & 0x60) | cfg;
|
||||
data[3] = (cpump << 6) | band_select;
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct l64781_config grundig_29504_401_config = {
|
||||
.demod_address = 0x55,
|
||||
};
|
||||
|
||||
static struct l64781_config grundig_29504_401_config_activy = {
|
||||
.demod_address = 0x54,
|
||||
};
|
||||
|
||||
static u8 tuner_address_grundig_29504_401_activy = 0x60;
|
||||
|
||||
static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe)
|
||||
{
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
u32 div;
|
||||
u8 data[4];
|
||||
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
|
||||
|
||||
div = c->frequency / 125;
|
||||
data[0] = (div >> 8) & 0x7f;
|
||||
data[1] = div & 0xff;
|
||||
data[2] = 0x8e;
|
||||
data[3] = 0x00;
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tda8083_config grundig_29504_451_config = {
|
||||
.demod_address = 0x68,
|
||||
};
|
||||
|
||||
static int s5h1420_tuner_set_params(struct dvb_frontend *fe)
|
||||
{
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
struct budget* budget = (struct budget*) fe->dvb->priv;
|
||||
u32 div;
|
||||
u8 data[4];
|
||||
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
|
||||
|
||||
div = c->frequency / 1000;
|
||||
data[0] = (div >> 8) & 0x7f;
|
||||
data[1] = div & 0xff;
|
||||
data[2] = 0xc2;
|
||||
|
||||
if (div < 1450)
|
||||
data[3] = 0x00;
|
||||
else if (div < 1850)
|
||||
data[3] = 0x40;
|
||||
else if (div < 2000)
|
||||
data[3] = 0x80;
|
||||
else
|
||||
data[3] = 0xc0;
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct s5h1420_config s5h1420_config = {
|
||||
.demod_address = 0x53,
|
||||
.invert = 1,
|
||||
.cdclk_polarity = 1,
|
||||
};
|
||||
|
||||
static struct tda10086_config tda10086_config = {
|
||||
.demod_address = 0x0e,
|
||||
.invert = 0,
|
||||
.diseqc_tone = 1,
|
||||
.xtal_freq = TDA10086_XTAL_16M,
|
||||
};
|
||||
|
||||
static struct stv0299_config alps_bsru6_config_activy = {
|
||||
.demod_address = 0x68,
|
||||
.inittab = alps_bsru6_inittab,
|
||||
.mclk = 88000000UL,
|
||||
.invert = 1,
|
||||
.op0_off = 1,
|
||||
.min_delay_ms = 100,
|
||||
.set_symbol_rate = alps_bsru6_set_symbol_rate,
|
||||
};
|
||||
|
||||
static struct stv0299_config alps_bsbe1_config_activy = {
|
||||
.demod_address = 0x68,
|
||||
.inittab = alps_bsbe1_inittab,
|
||||
.mclk = 88000000UL,
|
||||
.invert = 1,
|
||||
.op0_off = 1,
|
||||
.min_delay_ms = 100,
|
||||
.set_symbol_rate = alps_bsbe1_set_symbol_rate,
|
||||
};
|
||||
|
||||
static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name)
|
||||
{
|
||||
struct budget *budget = (struct budget *)fe->dvb->priv;
|
||||
|
||||
return request_firmware(fw, name, &budget->dev->pci->dev);
|
||||
}
|
||||
|
||||
|
||||
static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg)
|
||||
{
|
||||
u8 val;
|
||||
struct i2c_msg msg[] = {
|
||||
{ .addr = adr, .flags = 0, .buf = ®, .len = 1 },
|
||||
{ .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
|
||||
};
|
||||
|
||||
return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val;
|
||||
}
|
||||
|
||||
static u8 read_pwm(struct budget* budget)
|
||||
{
|
||||
u8 b = 0xff;
|
||||
u8 pwm;
|
||||
struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 },
|
||||
{ .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} };
|
||||
|
||||
if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff))
|
||||
pwm = 0x48;
|
||||
|
||||
return pwm;
|
||||
}
|
||||
|
||||
static struct stv090x_config tt1600_stv090x_config = {
|
||||
.device = STV0903,
|
||||
.demod_mode = STV090x_SINGLE,
|
||||
.clk_mode = STV090x_CLK_EXT,
|
||||
|
||||
.xtal = 13500000,
|
||||
.address = 0x68,
|
||||
|
||||
.ts1_mode = STV090x_TSMODE_DVBCI,
|
||||
.ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
|
||||
|
||||
.repeater_level = STV090x_RPTLEVEL_16,
|
||||
|
||||
.tuner_init = NULL,
|
||||
.tuner_sleep = NULL,
|
||||
.tuner_set_mode = NULL,
|
||||
.tuner_set_frequency = NULL,
|
||||
.tuner_get_frequency = NULL,
|
||||
.tuner_set_bandwidth = NULL,
|
||||
.tuner_get_bandwidth = NULL,
|
||||
.tuner_set_bbgain = NULL,
|
||||
.tuner_get_bbgain = NULL,
|
||||
.tuner_set_refclk = NULL,
|
||||
.tuner_get_status = NULL,
|
||||
};
|
||||
|
||||
static struct stv6110x_config tt1600_stv6110x_config = {
|
||||
.addr = 0x60,
|
||||
.refclk = 27000000,
|
||||
.clk_div = 2,
|
||||
};
|
||||
|
||||
static struct isl6423_config tt1600_isl6423_config = {
|
||||
.current_max = SEC_CURRENT_515m,
|
||||
.curlim = SEC_CURRENT_LIM_ON,
|
||||
.mod_extern = 1,
|
||||
.addr = 0x08,
|
||||
};
|
||||
|
||||
static void frontend_init(struct budget *budget)
|
||||
{
|
||||
(void)alps_bsbe1_config; /* avoid warning */
|
||||
|
||||
switch(budget->dev->pci->subsystem_device) {
|
||||
case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659))
|
||||
case 0x1013:
|
||||
// try the ALPS BSRV2 first of all
|
||||
budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
|
||||
budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
|
||||
budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
|
||||
budget->dvb_frontend->ops.set_tone = budget_set_tone;
|
||||
break;
|
||||
}
|
||||
|
||||
// try the ALPS BSRU6 now
|
||||
budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
|
||||
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
|
||||
if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) {
|
||||
budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
|
||||
budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
|
||||
budget->dvb_frontend->ops.set_tone = budget_set_tone;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659))
|
||||
|
||||
budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget));
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060))
|
||||
|
||||
budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
|
||||
budget->dvb_frontend->tuner_priv = NULL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4f52: /* Cards based on Philips Semi Sylt PCI ref. design */
|
||||
budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
printk(KERN_INFO "budget: tuner ALPS BSRU6 in Philips Semi. Sylt detected\n");
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
|
||||
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */
|
||||
{
|
||||
int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67);
|
||||
|
||||
if (subtype < 0)
|
||||
break;
|
||||
/* fixme: find a better way to identify the card */
|
||||
if (subtype < 0x36) {
|
||||
/* assume ALPS BSRU6 */
|
||||
budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n");
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
|
||||
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
|
||||
budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
|
||||
budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* assume ALPS BSBE1 */
|
||||
/* reset tuner */
|
||||
saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO);
|
||||
msleep(50);
|
||||
saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI);
|
||||
msleep(250);
|
||||
budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n");
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
|
||||
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
|
||||
budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
|
||||
budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522))
|
||||
budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
|
||||
budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
|
||||
budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */
|
||||
budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params;
|
||||
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */
|
||||
budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy;
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260))
|
||||
budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params;
|
||||
if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
|
||||
printk("%s: No LNBP21 found!\n", __func__);
|
||||
goto error_out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262)
|
||||
// gpio2 is connected to CLB - reset it + leave it high
|
||||
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
|
||||
msleep(1);
|
||||
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
|
||||
msleep(1);
|
||||
|
||||
budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap);
|
||||
if (budget->dvb_frontend) {
|
||||
if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL)
|
||||
printk("%s: No tda826x found!\n", __func__);
|
||||
if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
|
||||
printk("%s: No LNBP21 found!\n", __func__);
|
||||
goto error_out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x101c: { /* TT S2-1600 */
|
||||
struct stv6110x_devctl *ctl;
|
||||
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
|
||||
msleep(50);
|
||||
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
|
||||
msleep(250);
|
||||
|
||||
budget->dvb_frontend = dvb_attach(stv090x_attach,
|
||||
&tt1600_stv090x_config,
|
||||
&budget->i2c_adap,
|
||||
STV090x_DEMODULATOR_0);
|
||||
|
||||
if (budget->dvb_frontend) {
|
||||
|
||||
ctl = dvb_attach(stv6110x_attach,
|
||||
budget->dvb_frontend,
|
||||
&tt1600_stv6110x_config,
|
||||
&budget->i2c_adap);
|
||||
|
||||
if (ctl) {
|
||||
tt1600_stv090x_config.tuner_init = ctl->tuner_init;
|
||||
tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep;
|
||||
tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode;
|
||||
tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
|
||||
tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
|
||||
tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
|
||||
tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
|
||||
tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain;
|
||||
tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain;
|
||||
tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
|
||||
tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status;
|
||||
|
||||
/* call the init function once to initialize
|
||||
tuner's clock output divider and demod's
|
||||
master clock */
|
||||
if (budget->dvb_frontend->ops.init)
|
||||
budget->dvb_frontend->ops.init(budget->dvb_frontend);
|
||||
|
||||
if (dvb_attach(isl6423_attach,
|
||||
budget->dvb_frontend,
|
||||
&budget->i2c_adap,
|
||||
&tt1600_isl6423_config) == NULL) {
|
||||
printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__);
|
||||
goto error_out;
|
||||
}
|
||||
} else {
|
||||
printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x1020: { /* Omicom S2 */
|
||||
struct stv6110x_devctl *ctl;
|
||||
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
|
||||
msleep(50);
|
||||
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
|
||||
msleep(250);
|
||||
|
||||
budget->dvb_frontend = dvb_attach(stv090x_attach,
|
||||
&tt1600_stv090x_config,
|
||||
&budget->i2c_adap,
|
||||
STV090x_DEMODULATOR_0);
|
||||
|
||||
if (budget->dvb_frontend) {
|
||||
printk(KERN_INFO "budget: Omicom S2 detected\n");
|
||||
|
||||
ctl = dvb_attach(stv6110x_attach,
|
||||
budget->dvb_frontend,
|
||||
&tt1600_stv6110x_config,
|
||||
&budget->i2c_adap);
|
||||
|
||||
if (ctl) {
|
||||
tt1600_stv090x_config.tuner_init = ctl->tuner_init;
|
||||
tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep;
|
||||
tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode;
|
||||
tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
|
||||
tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
|
||||
tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
|
||||
tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
|
||||
tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain;
|
||||
tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain;
|
||||
tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
|
||||
tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status;
|
||||
|
||||
/* call the init function once to initialize
|
||||
tuner's clock output divider and demod's
|
||||
master clock */
|
||||
if (budget->dvb_frontend->ops.init)
|
||||
budget->dvb_frontend->ops.init(budget->dvb_frontend);
|
||||
|
||||
if (dvb_attach(lnbh24_attach,
|
||||
budget->dvb_frontend,
|
||||
&budget->i2c_adap,
|
||||
LNBH24_PCL | LNBH24_TTX,
|
||||
LNBH24_TEN, 0x14>>1) == NULL) {
|
||||
printk(KERN_ERR
|
||||
"No LNBH24 found!\n");
|
||||
goto error_out;
|
||||
}
|
||||
} else {
|
||||
printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (budget->dvb_frontend == NULL) {
|
||||
printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
|
||||
budget->dev->pci->vendor,
|
||||
budget->dev->pci->device,
|
||||
budget->dev->pci->subsystem_vendor,
|
||||
budget->dev->pci->subsystem_device);
|
||||
} else {
|
||||
if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend))
|
||||
goto error_out;
|
||||
}
|
||||
return;
|
||||
|
||||
error_out:
|
||||
printk("budget: Frontend registration failed!\n");
|
||||
dvb_frontend_detach(budget->dvb_frontend);
|
||||
budget->dvb_frontend = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
|
||||
{
|
||||
struct budget *budget = NULL;
|
||||
int err;
|
||||
|
||||
budget = kmalloc(sizeof(struct budget), GFP_KERNEL);
|
||||
if( NULL == budget ) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget);
|
||||
|
||||
dev->ext_priv = budget;
|
||||
|
||||
err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
|
||||
if (err) {
|
||||
printk("==> failed\n");
|
||||
kfree (budget);
|
||||
return err;
|
||||
}
|
||||
|
||||
budget->dvb_adapter.priv = budget;
|
||||
frontend_init(budget);
|
||||
|
||||
ttpci_budget_init_hooks(budget);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int budget_detach (struct saa7146_dev* dev)
|
||||
{
|
||||
struct budget *budget = (struct budget*) dev->ext_priv;
|
||||
int err;
|
||||
|
||||
if (budget->dvb_frontend) {
|
||||
dvb_unregister_frontend(budget->dvb_frontend);
|
||||
dvb_frontend_detach(budget->dvb_frontend);
|
||||
}
|
||||
|
||||
err = ttpci_budget_deinit (budget);
|
||||
|
||||
kfree (budget);
|
||||
dev->ext_priv = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct saa7146_extension budget_extension;
|
||||
|
||||
MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT);
|
||||
MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT);
|
||||
MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
|
||||
MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC);
|
||||
MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT);
|
||||
MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT);
|
||||
MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY);
|
||||
MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY);
|
||||
MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY);
|
||||
MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY);
|
||||
MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT);
|
||||
MAKE_BUDGET_INFO(sylt, "Philips Semi Sylt PCI", BUDGET_TT_HW_DISEQC);
|
||||
|
||||
static struct pci_device_id pci_tbl[] = {
|
||||
MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003),
|
||||
MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004),
|
||||
MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005),
|
||||
MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
|
||||
MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016),
|
||||
MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018),
|
||||
MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c),
|
||||
MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60),
|
||||
MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61),
|
||||
MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60),
|
||||
MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61),
|
||||
MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020),
|
||||
MAKE_EXTENSION_PCI(sylt, 0x1131, 0x4f52),
|
||||
{
|
||||
.vendor = 0,
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, pci_tbl);
|
||||
|
||||
static struct saa7146_extension budget_extension = {
|
||||
.name = "budget dvb",
|
||||
.flags = SAA7146_USE_I2C_IRQ,
|
||||
|
||||
.module = THIS_MODULE,
|
||||
.pci_tbl = pci_tbl,
|
||||
.attach = budget_attach,
|
||||
.detach = budget_detach,
|
||||
|
||||
.irq_mask = MASK_10,
|
||||
.irq_func = ttpci_budget_irq10_handler,
|
||||
};
|
||||
|
||||
static int __init budget_init(void)
|
||||
{
|
||||
return saa7146_register_extension(&budget_extension);
|
||||
}
|
||||
|
||||
static void __exit budget_exit(void)
|
||||
{
|
||||
saa7146_unregister_extension(&budget_extension);
|
||||
}
|
||||
|
||||
module_init(budget_init);
|
||||
module_exit(budget_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
|
||||
MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
|
||||
"budget PCI DVB cards by Siemens, Technotrend, Hauppauge");
|
124
drivers/media/pci/ttpci/budget.h
Normal file
124
drivers/media/pci/ttpci/budget.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
|
||||
#ifndef __BUDGET_DVB__
|
||||
#define __BUDGET_DVB__
|
||||
|
||||
#include "dvb_frontend.h"
|
||||
#include "dvbdev.h"
|
||||
#include "demux.h"
|
||||
#include "dvb_demux.h"
|
||||
#include "dmxdev.h"
|
||||
#include "dvb_filter.h"
|
||||
#include "dvb_net.h"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <media/saa7146.h>
|
||||
|
||||
extern int budget_debug;
|
||||
|
||||
#ifdef dprintk
|
||||
#undef dprintk
|
||||
#endif
|
||||
|
||||
#define dprintk(level,args...) \
|
||||
do { if ((budget_debug & level)) { printk("%s: %s(): ", KBUILD_MODNAME, __func__); printk(args); } } while (0)
|
||||
|
||||
struct budget_info {
|
||||
char *name;
|
||||
int type;
|
||||
};
|
||||
|
||||
/* place to store all the necessary device information */
|
||||
struct budget {
|
||||
|
||||
/* devices */
|
||||
struct dvb_device dvb_dev;
|
||||
struct dvb_net dvb_net;
|
||||
|
||||
struct saa7146_dev *dev;
|
||||
|
||||
struct i2c_adapter i2c_adap;
|
||||
struct budget_info *card;
|
||||
|
||||
unsigned char *grabbing;
|
||||
struct saa7146_pgtable pt;
|
||||
|
||||
struct tasklet_struct fidb_tasklet;
|
||||
struct tasklet_struct vpe_tasklet;
|
||||
|
||||
struct dmxdev dmxdev;
|
||||
struct dvb_demux demux;
|
||||
|
||||
struct dmx_frontend hw_frontend;
|
||||
struct dmx_frontend mem_frontend;
|
||||
|
||||
int ci_present;
|
||||
int video_port;
|
||||
|
||||
u32 buffer_width;
|
||||
u32 buffer_height;
|
||||
u32 buffer_size;
|
||||
u32 buffer_warning_threshold;
|
||||
u32 buffer_warnings;
|
||||
unsigned long buffer_warning_time;
|
||||
|
||||
u32 ttbp;
|
||||
int feeding;
|
||||
|
||||
spinlock_t feedlock;
|
||||
|
||||
spinlock_t debilock;
|
||||
|
||||
struct dvb_adapter dvb_adapter;
|
||||
struct dvb_frontend *dvb_frontend;
|
||||
int (*read_fe_status)(struct dvb_frontend *fe, fe_status_t *status);
|
||||
int fe_synced;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \
|
||||
static struct budget_info x_var ## _info = { \
|
||||
.name=x_name, \
|
||||
.type=x_type }; \
|
||||
static struct saa7146_pci_extension_data x_var = { \
|
||||
.ext_priv = &x_var ## _info, \
|
||||
.ext = &budget_extension };
|
||||
|
||||
#define BUDGET_TT 0
|
||||
#define BUDGET_TT_HW_DISEQC 1
|
||||
#define BUDGET_PATCH 3
|
||||
#define BUDGET_FS_ACTIVY 4
|
||||
#define BUDGET_CIN1200S 5
|
||||
#define BUDGET_CIN1200C 6
|
||||
#define BUDGET_CIN1200T 7
|
||||
#define BUDGET_KNC1S 8
|
||||
#define BUDGET_KNC1C 9
|
||||
#define BUDGET_KNC1T 10
|
||||
#define BUDGET_KNC1SP 11
|
||||
#define BUDGET_KNC1CP 12
|
||||
#define BUDGET_KNC1TP 13
|
||||
#define BUDGET_TVSTAR 14
|
||||
#define BUDGET_CIN1200C_MK3 15
|
||||
#define BUDGET_KNC1C_MK3 16
|
||||
#define BUDGET_KNC1CP_MK3 17
|
||||
#define BUDGET_KNC1S2 18
|
||||
#define BUDGET_KNC1C_TDA10024 19
|
||||
|
||||
#define BUDGET_VIDEO_PORTA 0
|
||||
#define BUDGET_VIDEO_PORTB 1
|
||||
|
||||
extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
|
||||
struct saa7146_pci_extension_data *info,
|
||||
struct module *owner, short *adapter_nums);
|
||||
extern void ttpci_budget_init_hooks(struct budget *budget);
|
||||
extern int ttpci_budget_deinit(struct budget *budget);
|
||||
extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr);
|
||||
extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port);
|
||||
extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
|
||||
int uselocks, int nobusyloop);
|
||||
extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value,
|
||||
int uselocks, int nobusyloop);
|
||||
|
||||
#endif
|
176
drivers/media/pci/ttpci/ttpci-eeprom.c
Normal file
176
drivers/media/pci/ttpci/ttpci-eeprom.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
Retrieve encoded MAC address from 24C16 serial 2-wire EEPROM,
|
||||
decode it and store it in the associated adapter struct for
|
||||
use by dvb_net.c
|
||||
|
||||
This card appear to have the 24C16 write protect held to ground,
|
||||
thus permitting normal read/write operation. Theoretically it
|
||||
would be possible to write routines to burn a different (encoded)
|
||||
MAC address into the EEPROM.
|
||||
|
||||
Robert Schlabbach GMX
|
||||
Michael Glaum KVH Industries
|
||||
Holger Waechtler Convergence
|
||||
|
||||
Copyright (C) 2002-2003 Ralph Metzler <rjkm@metzlerbros.de>
|
||||
Metzler Brothers Systementwicklung GbR
|
||||
|
||||
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 <asm/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include "ttpci-eeprom.h"
|
||||
|
||||
#if 1
|
||||
#define dprintk(x...) do { printk(x); } while (0)
|
||||
#else
|
||||
#define dprintk(x...) do { } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
static int check_mac_tt(u8 *buf)
|
||||
{
|
||||
int i;
|
||||
u16 tmp = 0xffff;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
tmp = (tmp << 8) | ((tmp >> 8) ^ buf[i]);
|
||||
tmp ^= (tmp >> 4) & 0x0f;
|
||||
tmp ^= (tmp << 12) ^ ((tmp & 0xff) << 5);
|
||||
}
|
||||
tmp ^= 0xffff;
|
||||
return (((tmp >> 8) ^ buf[8]) | ((tmp & 0xff) ^ buf[9]));
|
||||
}
|
||||
|
||||
static int getmac_tt(u8 * decodedMAC, u8 * encodedMAC)
|
||||
{
|
||||
u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c,
|
||||
0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6,
|
||||
0x1d, 0x36, 0x64, 0x78};
|
||||
u8 data[20];
|
||||
int i;
|
||||
|
||||
/* In case there is a sig check failure have the orig contents available */
|
||||
memcpy(data, encodedMAC, 20);
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
data[i] ^= xor[i];
|
||||
for (i = 0; i < 10; i++)
|
||||
data[i] = ((data[2 * i + 1] << 8) | data[2 * i])
|
||||
>> ((data[2 * i + 1] >> 6) & 3);
|
||||
|
||||
if (check_mac_tt(data))
|
||||
return -ENODEV;
|
||||
|
||||
decodedMAC[0] = data[2]; decodedMAC[1] = data[1]; decodedMAC[2] = data[0];
|
||||
decodedMAC[3] = data[6]; decodedMAC[4] = data[5]; decodedMAC[5] = data[4];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC)
|
||||
{
|
||||
u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c,
|
||||
0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6,
|
||||
0x1d, 0x36, 0x64, 0x78};
|
||||
u8 data[20];
|
||||
int i;
|
||||
|
||||
memcpy(data, encodedMAC, 20);
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
data[i] ^= xor[i];
|
||||
for (i = 0; i < 10; i++)
|
||||
data[i] = ((data[2 * i + 1] << 8) | data[2 * i])
|
||||
>> ((data[2 * i + 1] >> 6) & 3);
|
||||
|
||||
if (check_mac_tt(data))
|
||||
return -ENODEV;
|
||||
|
||||
decodedMAC[0] = data[2];
|
||||
decodedMAC[1] = data[1];
|
||||
decodedMAC[2] = data[0];
|
||||
decodedMAC[3] = data[6];
|
||||
decodedMAC[4] = data[5];
|
||||
decodedMAC[5] = data[4];
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ttpci_eeprom_decode_mac);
|
||||
|
||||
static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encodedMAC)
|
||||
{
|
||||
int ret;
|
||||
u8 b0[] = { 0xcc };
|
||||
|
||||
struct i2c_msg msg[] = {
|
||||
{ .addr = 0x50, .flags = 0, .buf = b0, .len = 1 },
|
||||
{ .addr = 0x50, .flags = I2C_M_RD, .buf = encodedMAC, .len = 20 }
|
||||
};
|
||||
|
||||
/* dprintk("%s\n", __func__); */
|
||||
|
||||
ret = i2c_transfer(adapter, msg, 2);
|
||||
|
||||
if (ret != 2) /* Assume EEPROM isn't there */
|
||||
return (-ENODEV);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac)
|
||||
{
|
||||
int ret, i;
|
||||
u8 encodedMAC[20];
|
||||
u8 decodedMAC[6];
|
||||
|
||||
ret = ttpci_eeprom_read_encodedMAC(adapter, encodedMAC);
|
||||
|
||||
if (ret != 0) { /* Will only be -ENODEV */
|
||||
dprintk("Couldn't read from EEPROM: not there?\n");
|
||||
memset(proposed_mac, 0, 6);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = getmac_tt(decodedMAC, encodedMAC);
|
||||
if( ret != 0 ) {
|
||||
dprintk("adapter failed MAC signature check\n");
|
||||
dprintk("encoded MAC from EEPROM was " );
|
||||
for(i=0; i<19; i++) {
|
||||
dprintk( "%.2x:", encodedMAC[i]);
|
||||
}
|
||||
dprintk("%.2x\n", encodedMAC[19]);
|
||||
memset(proposed_mac, 0, 6);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy(proposed_mac, decodedMAC, 6);
|
||||
dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
|
||||
decodedMAC[0], decodedMAC[1], decodedMAC[2],
|
||||
decodedMAC[3], decodedMAC[4], decodedMAC[5]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ttpci_eeprom_parse_mac);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others");
|
||||
MODULE_DESCRIPTION("Decode dvb_net MAC address from EEPROM of PCI DVB cards "
|
||||
"made by Siemens, Technotrend, Hauppauge");
|
34
drivers/media/pci/ttpci/ttpci-eeprom.h
Normal file
34
drivers/media/pci/ttpci/ttpci-eeprom.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Retrieve encoded MAC address from ATMEL ttpci_eeprom serial 2-wire EEPROM,
|
||||
decode it and store it in associated adapter net device
|
||||
|
||||
Robert Schlabbach GMX
|
||||
Michael Glaum KVH Industries
|
||||
Holger Waechtler Convergence
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __TTPCI_EEPROM_H__
|
||||
#define __TTPCI_EEPROM_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
extern int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC);
|
||||
extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac);
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue