#include "budget.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51) #define KBUILD_MODNAME budget #endif int budget_debug = 0; /**************************************************************************** * General helper functions ****************************************************************************/ /* this is videobuf_vmalloc_to_sg() from video-buf.c */ struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) { struct scatterlist *sglist; struct page *pg; int i; sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL); if (NULL == sglist) return NULL; memset(sglist,0,sizeof(struct scatterlist)*nr_pages); for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { pg = vmalloc_to_page(virt); if (NULL == pg) goto err; if (PageHighMem(pg)) BUG(); sglist[i].page = pg; sglist[i].length = PAGE_SIZE; } return sglist; err: kfree(sglist); return NULL; } static inline void ddelay(int i) { current->state=TASK_INTERRUPTIBLE; schedule_timeout((HZ*i)/100); } /**************************************************************************** * TT budget / WinTV Nova ****************************************************************************/ static int TTBStop(struct budget_s *budget) { DEB_EE(("budget: %p\n",budget)); if (--budget->feeding) return budget->feeding; saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off IER_DISABLE(budget->dev, MASK_07); return 0; } static int TTBStart(struct budget_s *budget) { struct saa7146_dev *dev=budget->dev; DEB_EE(("budget: %p\n",budget)); if (budget->feeding) return ++budget->feeding; saa7146_write(dev, MC1, MASK_20); // DMA3 off memset(budget->grabbing, 0x00, TS_HEIGHT*TS_WIDTH); saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); budget->tsf=0xff; budget->ttbp=0; saa7146_write(dev, DD1_INIT, 0x020006c0); 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); saa7146_write(dev, BASE_EVEN3, TS_WIDTH*TS_HEIGHT/2); saa7146_write(dev, PROT_ADDR3, TS_WIDTH*TS_HEIGHT); saa7146_write(dev, BASE_PAGE3, budget->pt.dma |ME1|0xb0); saa7146_write(dev, PITCH3, TS_WIDTH); saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT/2)<<16)|TS_WIDTH); saa7146_write(dev, MC2, (MASK_04 | MASK_20)); saa7146_write(dev, MC1, (MASK_04 | MASK_20)); // DMA3 on // FIDB IER_ENABLE(budget->dev, MASK_07); return ++budget->feeding; } static void fidbirq (unsigned long data) { struct budget_s *budget = (struct budget_s*) data; u8 *mem=(u8 *)(budget->grabbing); int num=512; int field=0x80 & saa7146_read(budget->dev, PSR); DEB_EE(("budget: %p\n",budget)); if (field) { if (field==budget->tsf) num=1024; else mem+=TS_BUFLEN/2; } else { if (field==budget->tsf) { if (budget->feeding && mem[TS_BUFLEN/2]==0x47) dvb_dmx_swfilter_packets(&budget->demux, mem+TS_BUFLEN/2, 512 ); } } budget->tsf=field; if (budget->feeding && mem[0]==0x47) dvb_dmx_swfilter_packets(&budget->demux, mem, num); } inline static void Set22K(struct budget_s *budget, int state) { struct saa7146_dev *dev=budget->dev; DEB_EE(("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 */ inline static void DiseqcSendBit(struct budget_s *budget, int data) { struct saa7146_dev *dev=budget->dev; DEB_EE(("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_s *budget, int data) { int i, par=1, d; DEB_EE(("budget: %p\n",budget)); for (i=7; i>=0; i--) { d=(data>>i)&1; par^=d; DiseqcSendBit(budget, d); } DiseqcSendBit(budget, par); } inline static int SendDiSEqCMsg(struct budget_s *budget, int len, u8 *msg, int burst) { struct saa7146_dev *dev=budget->dev; int i; DEB_EE(("budget: %p\n",budget)); saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); mdelay(16); for (i=0; idemux; struct budget_s *budget = (struct budget_s *) demux->priv; DEB_EE(("budget: %p\n",budget)); if (!demux->dmx.frontend) return -EINVAL; return TTBStart(budget); } static int budget_stop_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; struct budget_s *budget = (struct budget_s *) demux->priv; DEB_EE(("budget: %p\n",budget)); return TTBStop(budget); } /****************************************************************************** * SEC device file operations ******************************************************************************/ static int budget_diseqc_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) { struct budget_s *budget = fe->before_after_data; DEB_EE(("budget: %p\n",budget)); switch (cmd) { case FE_SET_TONE: switch ((fe_sec_tone_mode_t) arg) { case SEC_TONE_ON: Set22K (budget, 1); break; case SEC_TONE_OFF: Set22K (budget, 0); break; default: return -EINVAL; }; break; case FE_DISEQC_SEND_MASTER_CMD: { struct dvb_diseqc_master_cmd *cmd = arg; SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); break; } case FE_DISEQC_SEND_BURST: SendDiSEqCMsg (budget, 0, NULL, (int) arg); break; default: return -EOPNOTSUPP; }; return 0; } int budget_register(struct budget_s *budget) { int ret; dmx_frontend_t *dvbfront=&budget->hw_frontend; struct dvb_demux *dvbdemux=&budget->demux; DEB_EE(("budget: %p\n",budget)); if (budget->registered) return -1; budget->registered=1; /* init DiSEqC stuff if necessary */ if(budget->card->type == BUDGET_TT) dvb_add_frontend_ioctls (budget->dvb_adapter, budget_diseqc_ioctl, NULL, budget); memcpy(budget->demux_id, "demux0_0", 9); budget->demux_id[5] = budget->dvb_adapter->num + '0'; 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.vendor="CIM"; dvbdemux->dmx.model="sw"; dvbdemux->dmx.id=budget->demux_id; dvbdemux->dmx.capabilities=(DMX_TS_FILTERING| DMX_SECTION_FILTERING| DMX_MEMORY_BASED_FILTERING); dvb_dmx_init(&budget->demux); dvbfront->id="hw_frontend"; dvbfront->vendor="VLSI"; dvbfront->model="DVB Frontend"; dvbfront->source=DMX_FRONTEND_0; budget->dmxdev.filternum=256; budget->dmxdev.demux=&dvbdemux->dmx; budget->dmxdev.capabilities=0; dvb_dmxdev_init(&budget->dmxdev, budget->dvb_adapter); ret=dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); if (ret<0) return ret; budget->mem_frontend.id="mem_frontend"; budget->mem_frontend.vendor="memory"; budget->mem_frontend.model="sw"; 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; budget->dvb_net.card_num=budget->dvb_adapter->num; dvb_net_init(budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); return 0; } static void dvb_unregister(struct budget_s *budget) { struct dvb_demux *dvbdemux=&budget->demux; DEB_EE(("budget: %p\n",budget)); if (!budget->registered) return; 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); if(budget->card->type == BUDGET_TT) dvb_remove_frontend_ioctls (budget->dvb_adapter, budget_diseqc_ioctl, NULL); } static int master_xfer (struct dvb_i2c_bus *i2c, const struct i2c_msg msgs[], int num) { struct saa7146_dev *dev = i2c->data; return saa7146_i2c_transfer(dev, msgs, num, 6); } int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) { struct budget_s *budget = NULL; struct scatterlist *slist = NULL; int slen = 0; int length = TS_WIDTH*TS_HEIGHT; int pages = (length+PAGE_SIZE-1)/PAGE_SIZE; int ret = 0; struct budget_info *bi = info->ext_priv; if (!(budget = kmalloc (sizeof (struct budget_s), GFP_KERNEL))) { printk ("%s: out of memory!\n", __FUNCTION__); return -ENOMEM; } memset(budget, 0, sizeof(struct budget_s)); DEB_EE(("dev: %p, budget: %p\n",dev,budget)); budget->card = bi; budget->dev=(struct saa7146_dev *)dev; (struct budget_s*)dev->ext_priv = budget; dvb_register_adapter(&budget->dvb_adapter, budget->card->name); /* set dd1 stream a & b */ saa7146_write(dev, DD1_STREAM_B, 0x00000000); saa7146_write(dev, DD1_INIT, 0x02000000); saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); /* the Siemens DVB needs this if you want to have the i2c chips get recognized before the main driver is loaded */ saa7146_write(dev, GPIO_CTRL, 0x500000); saa7146_i2c_adapter_prepare(dev, NULL, SAA7146_I2C_BUS_BIT_RATE_3200); budget->i2c_bus = dvb_register_i2c_bus (master_xfer, dev, budget->dvb_adapter, 0); if (!budget->i2c_bus) { dvb_unregister_adapter (budget->dvb_adapter); kfree(budget); return -ENOMEM; } budget->grabbing = vmalloc(length); if (!budget->grabbing) { printk(KERN_ERR "dvb: vmalloc() failed.\n"); ret = -ENOMEM; goto err; } if (!(slist = vmalloc_to_sg(budget->grabbing, pages))) { printk(KERN_ERR "dvb: vmalloc_to_sg() failed.\n"); ret = -ENOMEM; goto err; } if (saa7146_pgtable_alloc(dev->pci, &budget->pt)) { printk(KERN_ERR "dvb: saa7146_pgtable_alloc() failed.\n"); ret = -ENOMEM; goto err; } slen = pci_map_sg(dev->pci,slist,pages,PCI_DMA_FROMDEVICE); saa7146_pgtable_build_single(dev->pci, &budget->pt, slist, slen); saa7146_write(dev, PCI_BT_V1, 0x1c00101f); /* upload all */ saa7146_write(dev, MC2, 0x077c077c); saa7146_write(dev, GPIO_CTRL, 0x000000); tasklet_init (&budget->fidb_tasklet, fidbirq, (unsigned long) budget); saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); /* frontend power on */ return 0; err: if( NULL != budget->grabbing ) { vfree(budget->grabbing); } if( NULL != slist ) { kfree(slist); } dvb_unregister_i2c_bus (master_xfer,budget->i2c_bus->adapter, budget->i2c_bus->id); dvb_unregister_adapter (budget->dvb_adapter); if( NULL != budget ) { kfree(budget); } return ret; } int budget_detach (struct saa7146_dev* saa) { struct budget_s *budget = (struct budget_s*)saa->ext_priv; DEB_EE(("budget: %p\n",budget)); dvb_unregister(budget); dvb_unregister_i2c_bus (master_xfer,budget->i2c_bus->adapter, budget->i2c_bus->id); dvb_unregister_adapter (budget->dvb_adapter); saa7146_pgtable_free(saa->pci, &budget->pt); vfree(budget->grabbing); kfree (budget); saa->ext_priv = NULL; return 0; } void budget_irq(struct saa7146_dev* dev, u32 *isr) { struct budget_s *budget = (struct budget_s*)dev->ext_priv; DEB_EE(("dev: %p, budget: %p\n",dev,budget)); if (*isr & MASK_07) tasklet_schedule (&budget->fidb_tasklet); } EXPORT_SYMBOL_GPL(budget_register); EXPORT_SYMBOL_GPL(budget_irq); EXPORT_SYMBOL_GPL(budget_attach); EXPORT_SYMBOL_GPL(budget_detach); EXPORT_SYMBOL_GPL(budget_debug); MODULE_PARM(budget_debug,"i"); MODULE_LICENSE("GPL");