📄 av7110.c
字号:
/* * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB) * av7110.c: initialization and demux 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/dvb/ */#include <linux/config.h>#include <linux/module.h>#include <linux/kmod.h>#include <linux/delay.h>#include <linux/fs.h>#include <linux/timer.h>#include <linux/poll.h>#include <linux/byteorder/swabb.h>#include <linux/smp_lock.h>#include <linux/kernel.h>#include <linux/moduleparam.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/string.h>#include <linux/pci.h>#include <linux/vmalloc.h>#include <linux/firmware.h>#include <linux/crc32.h>#include <linux/i2c.h>#include <asm/system.h>#include <asm/semaphore.h>#include <linux/dvb/frontend.h>#include "dvb_frontend.h"#include "ttpci-eeprom.h"#include "av7110.h"#include "av7110_hw.h"#include "av7110_av.h"#include "av7110_ca.h"#include "av7110_ipack.h"#define TS_WIDTH 376#define TS_HEIGHT 512#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT)#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE)int av7110_debug;static int vidmode=CVBS_RGB_OUT;static int pids_off;static int adac=DVB_ADAC_TI;static int hw_sections;static int rgb_on;static int volume = 255;static int budgetpatch = 0;module_param_named(debug, av7110_debug, int, 0644);MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)");module_param(vidmode, int, 0444);MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC");module_param(pids_off, int, 0444);MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed");module_param(adac, int, 0444);MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)");module_param(hw_sections, int, 0444);MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware");module_param(rgb_on, int, 0444);MODULE_PARM_DESC(rgb_on, "For Siemens DVB-C cards only: Enable RGB control" " signal on SCART pin 16 to switch SCART video mode from CVBS to RGB");module_param(volume, int, 0444);MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)");module_param(budgetpatch, int, 0444);MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)");static void restart_feeds(struct av7110 *av7110);static int av7110_num = 0;#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \{\ if (fe_func != NULL) { \ av7110_copy = fe_func; \ fe_func = av7110_func; \ } \}static void init_av7110_av(struct av7110 *av7110){ struct saa7146_dev *dev=av7110->dev; /* set internal volume control to maximum */ av7110->adac_type = DVB_ADAC_TI; av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); av7710_set_video_mode(av7110, vidmode); /* handle different card types */ /* remaining inits according to card and frontend type */ av7110->analog_tuner_flags = 0; av7110->current_input = 0; if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", av7110->dvb_adapter->num); av7110->adac_type = DVB_ADAC_CRYSTAL; i2c_writereg(av7110, 0x20, 0x01, 0xd2); i2c_writereg(av7110, 0x20, 0x02, 0x49); i2c_writereg(av7110, 0x20, 0x03, 0x00); i2c_writereg(av7110, 0x20, 0x04, 0x00); /** * some special handling for the Siemens DVB-C cards... */ } else if (0 == av7110_init_analog_module(av7110)) { /* done. */ } else if (dev->pci->subsystem_vendor == 0x110a) { printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", av7110->dvb_adapter->num); av7110->adac_type = DVB_ADAC_NONE; } else { av7110->adac_type = adac; printk("dvb-ttpci: adac type set to %d @ card %d\n", av7110->dvb_adapter->num, av7110->adac_type); } if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP) { // switch DVB SCART on av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); if (rgb_on && (av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16 //saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8 } } av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); av7110_setup_irc_config(av7110, 0);}static void recover_arm(struct av7110 *av7110){ dprintk(4, "%p\n",av7110); av7110_bootarm(av7110); msleep(100); restart_feeds(av7110); av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, av7110->ir_config);}static void arm_error(struct av7110 *av7110){ dprintk(4, "%p\n",av7110); av7110->arm_errors++; av7110->arm_ready=0; recover_arm(av7110);}static int arm_thread(void *data){ struct av7110 *av7110 = data; u16 newloops = 0; int timeout; dprintk(4, "%p\n",av7110); lock_kernel (); daemonize ("arm_mon"); sigfillset (¤t->blocked); unlock_kernel (); av7110->arm_thread = current; for (;;) { timeout = wait_event_interruptible_timeout(av7110->arm_wait, av7110->arm_rmmod, 5 * HZ); if (-ERESTARTSYS == timeout || av7110->arm_rmmod) { /* got signal or told to quit*/ break; } if (!av7110->arm_ready) continue; if (down_interruptible(&av7110->dcomlock)) break; newloops=rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); up(&av7110->dcomlock); if (newloops==av7110->arm_loops) { printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n", av7110->dvb_adapter->num); arm_error(av7110); av7710_set_video_mode(av7110, vidmode); init_av7110_av(av7110); if (down_interruptible(&av7110->dcomlock)) break; newloops=rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2)-1; up(&av7110->dcomlock); } av7110->arm_loops=newloops; } av7110->arm_thread = NULL; return 0;}/** * Hack! we save the last av7110 ptr. This should be ok, since * you rarely will use more then one IR control. * * If we want to support multiple controls we would have to do much more... */void av7110_setup_irc_config (struct av7110 *av7110, u32 ir_config){ static struct av7110 *last; dprintk(4, "%p\n",av7110); if (!av7110) av7110 = last; else last = av7110; if (av7110) { av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, ir_config); av7110->ir_config = ir_config; }}static void (*irc_handler)(u32);void av7110_register_irc_handler(void (*func)(u32)) { dprintk(4, "registering %p\n", func); irc_handler = func;}void av7110_unregister_irc_handler(void (*func)(u32)) { dprintk(4, "unregistering %p\n", func); irc_handler = NULL;}static void run_handlers(unsigned long ircom){ if (irc_handler != NULL) (*irc_handler)((u32) ircom);}static DECLARE_TASKLET(irtask, run_handlers, 0);static void IR_handle(struct av7110 *av7110, u32 ircom){ dprintk(4, "ircommand = %08x\n", ircom); irtask.data = (unsigned long) ircom; tasklet_schedule(&irtask);}/**************************************************************************** * IRQ handling ****************************************************************************/static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, u8 * buffer2, size_t buffer2_len, struct dvb_demux_filter *dvbdmxfilter, enum dmx_success success, struct av7110 *av7110){ if (!dvbdmxfilter->feed->demux->dmx.frontend) return 0; if (dvbdmxfilter->feed->demux->dmx.frontend->source==DMX_MEMORY_FE) return 0; switch(dvbdmxfilter->type) { case DMX_TYPE_SEC: if ((((buffer1[1]<<8)|buffer1[2])&0xfff)+3!=buffer1_len) return 0; if (dvbdmxfilter->doneq) { struct dmx_section_filter *filter=&dvbdmxfilter->filter; int i; u8 xor, neq=0; for (i=0; i<DVB_DEMUX_MASK_MAX; i++) { xor=filter->filter_value[i]^buffer1[i]; neq|=dvbdmxfilter->maskandnotmode[i]&xor; } if (!neq) return 0; } return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len, buffer2, buffer2_len, &dvbdmxfilter->filter, DMX_OK); case DMX_TYPE_TS: if (!(dvbdmxfilter->feed->ts_type & TS_PACKET)) return 0; if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY) return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len, buffer2, buffer2_len, &dvbdmxfilter->feed->feed.ts, DMX_OK); else av7110_p2t_write(buffer1, buffer1_len, dvbdmxfilter->feed->pid, &av7110->p2t_filter[dvbdmxfilter->index]); default: return 0; }}//#define DEBUG_TIMINGstatic inline void print_time(char *s){#ifdef DEBUG_TIMING struct timeval tv; do_gettimeofday(&tv); printk("%s: %d.%d\n", s, (int)tv.tv_sec, (int)tv.tv_usec);#endif}#define DEBI_READ 0#define DEBI_WRITE 1static inline void start_debi_dma(struct av7110 *av7110, int dir, unsigned long addr, unsigned int len){ dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len); if (saa7146_wait_for_debi_done(av7110->dev, 0)) { printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __FUNCTION__); return; } SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */ SAA7146_IER_ENABLE(av7110->dev, MASK_19); if (len < 5) len = 5; /* we want a real DEBI DMA */ if (dir == DEBI_WRITE) iwdebi(av7110, DEBISWAB, addr, 0, (len + 3) & ~3); else irdebi(av7110, DEBISWAB, addr, 0, len);}static void debiirq (unsigned long data){ struct av7110 *av7110 = (struct av7110*) data; int type=av7110->debitype; int handle=(type>>8)&0x1f; unsigned int xfer = 0; print_time("debi"); dprintk(4, "type 0x%04x\n", type); if (type==-1) { printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", jiffies, saa7146_read(av7110->dev, PSR), saa7146_read(av7110->dev, SSR)); goto debi_done; } av7110->debitype=-1; switch (type&0xff) { case DATA_TS_RECORD: dvb_dmx_swfilter_packets(&av7110->demux, (const u8 *)av7110->debi_virt, av7110->debilen/188); xfer = RX_BUFF; break; case DATA_PES_RECORD: if (av7110->demux.recording) av7110_record_cb(&av7110->p2t[handle], (u8 *)av7110->debi_virt, av7110->debilen); xfer = RX_BUFF; break; case DATA_IPMPE: case DATA_FSECTION: case DATA_PIPING: if (av7110->handle2filter[handle]) DvbDmxFilterCallback((u8 *)av7110->debi_virt, av7110->debilen, NULL, 0, av7110->handle2filter[handle], DMX_OK, av7110); xfer = RX_BUFF; break; case DATA_CI_GET: { u8 *data=av7110->debi_virt; if ((data[0]<2) && data[2]==0xff) { int flags=0; if (data[5]>0) flags|=CA_CI_MODULE_PRESENT; if (data[5]>5) flags|=CA_CI_MODULE_READY; av7110->ci_slot[data[0]].flags=flags; } else ci_get_data(&av7110->ci_rbuffer, av7110->debi_virt, av7110->debilen); xfer = RX_BUFF;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -