📄 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/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/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/version.h>#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))#include <linux/firmware.h>#endif#include <linux/crc32.h>#include <asm/system.h>#include <asm/semaphore.h>#include <linux/dvb/frontend.h>#include "dvb_i2c.h"#include "dvb_frontend.h"#include "dvb_functions.h"#define DEBUG_VARIABLE av7110_debug#include "ttpci-eeprom.h"#include "av7110.h"#include "av7110_hw.h"#include "av7110_av.h"#include "av7110_ca.h"#include "av7110_ipack.h"static void restart_feeds(struct av7110 *av7110);int av7110_debug = 0;static int vidmode = CVBS_RGB_OUT;static int pids_off;static int adac = DVB_ADAC_TI;static int hw_sections = 1;static int rgb_on = 0;int av7110_num = 0;static void recover_arm(struct av7110 *av7110){ DEB_EE(("av7110: %p\n",av7110)); av7110_bootarm(av7110); dvb_delay(100); restart_feeds(av7110); av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, av7110->ir_config);}static void arm_error(struct av7110 *av7110){ DEB_EE(("av7110: %p\n",av7110)); av7110->arm_errors++; av7110->arm_ready = 0; recover_arm(av7110);}static int arm_thread(void *data){ struct av7110 *av7110 = data; unsigned long timeout; u16 newloops = 0; DEB_EE(("av7110: %p\n",av7110)); dvb_kernel_thread_setup("arm_mon"); av7110->arm_thread = current; while (1) { timeout = wait_event_interruptible_timeout(av7110->arm_wait,0 != av7110->arm_rmmod, 5*HZ); if (-ERESTARTSYS == timeout || 0 != 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 "av7110%d: ARM crashed!\n", av7110->dvb_adapter->num); arm_error(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; DEB_EE(("av7110: %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)){ DEB_EE(("registering %p\n", func)); irc_handler = func;}void av7110_unregister_irc_handler(void (*func)(u32)){ DEB_EE(("unregistering %p\n", func)); irc_handler = NULL;}void run_handlers(unsigned long ircom){ if (irc_handler != NULL) (*irc_handler)((u32) ircom);}DECLARE_TASKLET(irtask, run_handlers, 0);void IR_handle(struct av7110 *av7110, u32 ircom){ DEB_S(("av7110: ircommand = %08x\n", ircom)); irtask.data = (unsigned long) ircom; tasklet_schedule(&irtask);}/**************************************************************************** * IRQ handling ****************************************************************************/static inline 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){ DEB_INT(("av7110: %p\n", 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}static void debiirq (unsigned long data){ struct av7110 *av7110 = (struct av7110*) data; int type = av7110->debitype; int handle = (type >> 8) & 0x1f;// DEB_EE(("av7110: %p\n",av7110)); print_time("debi"); saa7146_write(av7110->dev, IER, saa7146_read(av7110->dev, IER) & ~MASK_19); saa7146_write(av7110->dev, ISR, MASK_19); 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)); spin_lock(&av7110->debilock); ARM_ClearMailBox(av7110); ARM_ClearIrq(av7110); spin_unlock(&av7110->debilock); return; } av7110->debitype = -1; switch (type & 0xff) { case DATA_TS_RECORD: dvb_dmx_swfilter_packets(&av7110->demux, (const u8 *) av7110->debi_virt, av7110->debilen / 188); spin_lock(&av7110->debilock); iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ARM_ClearMailBox(av7110); spin_unlock(&av7110->debilock); return; case DATA_PES_RECORD: if (av7110->demux.recording) av7110_record_cb(&av7110->p2t[handle], (u8 *) av7110->debi_virt, av7110->debilen); spin_lock(&av7110->debilock); iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ARM_ClearMailBox(av7110); spin_unlock(&av7110->debilock); return; case DATA_IPMPE: case DATA_FSECTION: case DATA_PIPING: if (av7110->handle2filter[handle]) DvbDmxFilterCallback((u8 *)av7110->debi_virt, av7110->debilen, 0, 0, av7110->handle2filter[handle], DMX_OK, av7110); spin_lock(&av7110->debilock); iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ARM_ClearMailBox(av7110); spin_unlock(&av7110->debilock); return; 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); spin_lock(&av7110->debilock); iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ARM_ClearMailBox(av7110); spin_unlock(&av7110->debilock); return; } case DATA_COMMON_INTERFACE: CI_handle(av7110, (u8 *)av7110->debi_virt, av7110->debilen);#if 0 { int i; printk("av7110%d: ", av7110->num); printk("%02x ", *(u8 *)av7110->debi_virt); printk("%02x ", *(1+(u8 *)av7110->debi_virt)); for (i = 2; i < av7110->debilen; i++) printk("%02x ", (*(i+(unsigned char *)av7110->debi_virt))); for (i = 2; i < av7110->debilen; i++) printk("%c", chtrans(*(i+(unsigned char *)av7110->debi_virt))); printk("\n"); }#endif spin_lock(&av7110->debilock); iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ARM_ClearMailBox(av7110); spin_unlock(&av7110->debilock); return; case DATA_DEBUG_MESSAGE: ((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0; printk("%s\n", (s8 *) av7110->debi_virt); spin_lock(&av7110->debilock); iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ARM_ClearMailBox(av7110); spin_unlock(&av7110->debilock); return; case DATA_CI_PUT: case DATA_MPEG_PLAY: case DATA_BMP_LOAD: spin_lock(&av7110->debilock); iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); ARM_ClearMailBox(av7110); spin_unlock(&av7110->debilock); return; default: break; } spin_lock(&av7110->debilock); ARM_ClearMailBox(av7110); spin_unlock(&av7110->debilock);}static void gpioirq (unsigned long data){ struct av7110 *av7110 = (struct av7110*) data; u32 rxbuf, txbuf; int len; //printk("GPIO0 irq\n"); if (av7110->debitype !=-1) printk("GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", jiffies, saa7146_read(av7110->dev, PSR), saa7146_read(av7110->dev, SSR)); spin_lock(&av7110->debilock); ARM_ClearIrq(av7110); saa7146_write(av7110->dev, IER, saa7146_read(av7110->dev, IER) & ~MASK_19); saa7146_write(av7110->dev, ISR, MASK_19); av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2); av7110->debilen = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); rxbuf = irdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); len = (av7110->debilen + 3) & ~3;// DEB_D(("GPIO0 irq %d %d\n", av7110->debitype, av7110->debilen)); print_time("gpio");// DEB_D(("GPIO0 irq %02x\n", av7110->debitype&0xff)); switch (av7110->debitype & 0xff) { case DATA_TS_PLAY: case DATA_PES_PLAY: break; case DATA_MPEG_VIDEO_EVENT: { u32 h_ar; struct video_event event; av7110->video_size.w = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_WIDTH, 0, 2); h_ar = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_HEIGHT_AR, 0, 2); iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); av7110->video_size.h = h_ar & 0xfff; DEB_D(("GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n", av7110->video_size.w, av7110->video_size.h, av7110->video_size.aspect_ratio)); event.type = VIDEO_EVENT_SIZE_CHANGED; event.u.size.w = av7110->video_size.w; event.u.size.h = av7110->video_size.h; switch ((h_ar >> 12) & 0xf) { case 3: av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9; event.u.size.aspect_ratio = VIDEO_FORMAT_16_9; av7110->videostate.video_format = VIDEO_FORMAT_16_9; break; case 4: av7110->video_size.aspect_ratio = VIDEO_FORMAT_221_1; event.u.size.aspect_ratio = VIDEO_FORMAT_221_1; av7110->videostate.video_format = VIDEO_FORMAT_221_1; break; default: av7110->video_size.aspect_ratio = VIDEO_FORMAT_4_3; event.u.size.aspect_ratio = VIDEO_FORMAT_4_3; av7110->videostate.video_format = VIDEO_FORMAT_4_3; } dvb_video_add_event(av7110, &event); break; } case DATA_CI_PUT: { int avail; struct dvb_ringbuffer *cibuf = &av7110->ci_wbuffer; avail = dvb_ringbuffer_avail(cibuf); if (avail <= 2) { iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); break; } len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; len |= DVB_RINGBUFFER_PEEK(cibuf, 1); if (avail < len + 2) { iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); break; } DVB_RINGBUFFER_SKIP(cibuf, 2); dvb_ringbuffer_read(cibuf, av7110->debi_virt,len, 0); wake_up(&cibuf->queue); iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); saa7146_wait_for_debi_done(av7110->dev); saa7146_write(av7110->dev, IER, saa7146_read(av7110->dev, IER) | MASK_19); if (len < 5) len = 5; /* we want a real DEBI DMA */ iwdebi(av7110, DEBISWAB, DPRAM_BASE + txbuf, 0, (len + 3) & ~3); spin_unlock(&av7110->debilock); return; } case DATA_MPEG_PLAY: if (!av7110->playing) { iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); break; } len = 0; if (av7110->debitype & 0x100) { spin_lock(&av7110->aout.lock); len=av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048); spin_unlock(&av7110->aout.lock); } if (len <=0 && (av7110->debitype & 0x200) &&av7110->videostate.play_state != VIDEO_FREEZED) { spin_lock(&av7110->avout.lock); len=av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048); spin_unlock(&av7110->avout.lock); } if (len <= 0) { iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); break; } DEB_D(("GPIO0 PES_PLAY len=%04x\n", len)); iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); saa7146_wait_for_debi_done(av7110->dev); saa7146_write(av7110->dev, IER, saa7146_read(av7110->dev, IER) | MASK_19); iwdebi(av7110, DEBISWAB, DPRAM_BASE + txbuf, 0, (len + 3) & ~3); spin_unlock(&av7110->debilock); return; case DATA_BMP_LOAD: len = av7110->debilen; if (!len) { av7110->bmp_state = BMP_LOADED; iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); wake_up(&av7110->bmpq); break; } if (len > av7110->bmplen) len = av7110->bmplen; if (len > 2 * 1024)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -