diff options
author | Gerd Knorr <devnull@localhost> | 2004-08-25 14:47:53 +0000 |
---|---|---|
committer | Gerd Knorr <devnull@localhost> | 2004-08-25 14:47:53 +0000 |
commit | 6550ed72a57b714bfb1a718538d1fb5c542aa470 (patch) | |
tree | 7e7eb707f80203ecdb680cfd8b3d7ac4f2d3af11 /linux/drivers/media/video/cx88/cx88-dvb.c | |
parent | f680c1f3f262eae1802d080db3eccee4f9da3f43 (diff) | |
download | mediapointer-dvb-s2-6550ed72a57b714bfb1a718538d1fb5c542aa470.tar.gz mediapointer-dvb-s2-6550ed72a57b714bfb1a718538d1fb5c542aa470.tar.bz2 |
- cx88: plenty of mpeg work. blackbird might even work with
some luck. dvb needs more work.
Diffstat (limited to 'linux/drivers/media/video/cx88/cx88-dvb.c')
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-dvb.c | 521 |
1 files changed, 264 insertions, 257 deletions
diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 08cc56d27..398fbbd16 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -23,342 +23,349 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/fs.h> +#include <linux/kthread.h> +#include <linux/file.h> #include "cx88.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); +MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); -/* ------------------------------------------------------------------ */ +static unsigned int debug = 0; +MODULE_PARM(debug,"i"); +MODULE_PARM_DESC(debug,"enable debug messages [dvb]"); -#if 0 -static void cx88_dvb_tasklet(unsigned long data) -{ - struct cx8800_dvb *dvb = (struct cx8800_dvb *)data; - struct cx8800_dev *dev = dvb->dev; - struct cx88_dmaqueue *q = &dvb->tsq; - struct cx88_buffer *buf; - unsigned long flags; +#define dprintk(level,fmt, arg...) if (debug >= level) \ + printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->core->name , ## arg) - /* Process any pending buffers */ - spin_lock_irqsave(&dev->slock, flags); - while (! list_empty(&q->queued)) { - /* get items */ - buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue); - spin_unlock_irqrestore(&dev->slock, flags); - - /* Hand the received frames to the software filter */ - (dvb->ts_includes_fec ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter) - (&dvb->demux, (char *)buf->vb.dma.vmalloc, - buf->vb.field_count * buf->bpl); - - /* Reactivate the buffer */ - spin_lock_irqsave(&dev->slock, flags); - list_del(&buf->vb.queue); - activate_ts_buffer(dvb, buf, 1); - } - spin_unlock_irqrestore(&dev->slock, flags); -} +/* ------------------------------------------------------------------ */ -static void cx8800_wakeup_dvb_tasklet(struct cx8800_dvb *dvb, - unsigned int maxframe, int timeout_case) +static int dvb_buf_setup(struct file *file, unsigned int *count, unsigned int *size) { - struct cx88_dmaqueue *q = &dvb->tsq; - struct cx88_buffer *buf; - int processed_buffers = 0; - - /* Called with dev->slock held */ - - dprintk(2, "cx8800_wakeup_dvb_tasklet called, maxframe: %d, " - "timeout_case: %d\n", maxframe, timeout_case); - - while (! list_empty(&q->active)) { - buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); - - /* Calculate how many frames that are waiting for processing */ - maxframe &= 0xffff; - buf->count &= 0xffff; - buf->vb.field_count = (maxframe - buf->count) & 0xffff; - - dprintk(2, "[%p/%d] wakeup reg=%d buf=%d, field_count=%d, " - "processed=%d\n", buf, buf->vb.i, maxframe, buf->count, - buf->vb.field_count, processed_buffers); - - /* Reject any incomplete buffer if we aren't handling a timeout, - * as data transfer into it is still occurring. */ - if (buf->vb.field_count < buf->vb.height && !timeout_case) { - if (! processed_buffers) { - dprintk(2, "incomplete buffer: mf: %d, count: " - "%d, fc: %d height: %d\n", maxframe, - buf->count, buf->vb.field_count, - buf->vb.height); - return; /* Don't re-timeout... */ - } - break; - } + struct cx8802_dev *dev = file->private_data; - /* We've got no frames to process, so just reschedule - * the timeout. */ - if (buf->vb.field_count == 0) { - dprintk(2, "no frames... %d %d\n", maxframe, - buf->count); - break; - } - - /* We can't possibly have more frames than we have size for - * in the buffer, so trim back how many we think we have. */ - if (buf->vb.field_count > buf->vb.height) { - if (buf->vb.field_count > buf->vb.height * 2 && - ++too_many_frames_in_one_interrupt == 10) { - printk(KERN_WARNING "%s: you should consider" - "increasing ts_frames_per_irq (%d " - "frame/irq overload)\n", dvb->dev->name, - (buf->vb.field_count - buf->vb.height)); - } - buf->vb.field_count = buf->vb.height; - } - - /* Mark the buffer as done. */ - buf->vb.state = timeout_case ? STATE_ERROR : STATE_DONE; - - /* Move the buffer from the active queue to the - * queued-for-processing queue */ - list_move_tail(&buf->vb.queue, &q->queued); - - /* Schedule the tasklet to process them */ - tasklet_schedule(&dvb->dvb_tasklet); - - /* Don't queue more than one buffer if we timed out. */ - if (timeout_case) - break; + dev->ts_packet_size = 188 * 4; + dev->ts_packet_count = 32; - /* Record that we processed a buffer. */ - processed_buffers++; - } + *size = dev->ts_packet_size * dev->ts_packet_count; + *count = 32; + return 0; +} - if (list_empty(&q->active)) { - del_timer(&q->timeout); - } else { - mod_timer(&q->timeout, jiffies + TS_BUFFER_TIMEOUT); - } +static int dvb_buf_prepare(struct file *file, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx8802_dev *dev = file->private_data; + return cx8802_buf_prepare(dev, (struct cx88_buffer*)vb); } -/* ---------------------------------------------------------------------------- */ +static void dvb_buf_queue(struct file *file, struct videobuf_buffer *vb) +{ + struct cx8802_dev *dev = file->private_data; + cx8802_buf_queue(dev, (struct cx88_buffer*)vb); +} -static int master_xfer(struct dvb_i2c_bus *i2c, const struct i2c_msg msgs[], - int num) +static void dvb_buf_release(struct file *file, struct videobuf_buffer *vb) { - struct cx8800_dvb *dvb = (struct cx8800_dvb *)i2c->data; - struct cx8800_dev *dev = dvb->dev; - int retval; + struct cx8802_dev *dev = file->private_data; + cx88_free_buffer(dev->pci, (struct cx88_buffer*)vb); +} - if (down_interruptible(&dvb->dvb_i2c_lock)) - return -ERESTARTSYS; +struct videobuf_queue_ops dvb_qops = { + .buf_setup = dvb_buf_setup, + .buf_prepare = dvb_buf_prepare, + .buf_queue = dvb_buf_queue, + .buf_release = dvb_buf_release, +}; - retval = i2c_transfer(&dev->i2c_adap, (struct i2c_msg *)msgs, num); +static int dvb_thread(void *data) +{ + struct cx8802_dev *dev = data; + struct videobuf_buffer *buf; + struct file *file; + unsigned long flags; + int err; + + dprintk(1,"cx88: dvb thread started\n"); + file = get_empty_filp(); + file->private_data = dev; + videobuf_read_start(file, &dev->dvbq); + + for (;;) { + if (kthread_should_stop()) + break; - up(&dvb->dvb_i2c_lock); + /* fetch next buffer */ + buf = list_entry(dev->dvbq.stream.next, + struct videobuf_buffer, stream); + list_del(&buf->stream); + err = videobuf_waiton(buf,0,0); + + /* feed buffer data to demux */ + if (buf->state == STATE_DONE) + dvb_dmx_swfilter(&dev->demux, buf->dma.vmalloc, + buf->size); + + /* requeue buffer */ + list_add_tail(&buf->stream,&dev->dvbq.stream); + spin_lock_irqsave(dev->dvbq.irqlock,flags); + dev->dvbq.ops->buf_queue(file,buf); + spin_unlock_irqrestore(dev->dvbq.irqlock,flags); + } - return retval; + videobuf_read_stop(file, &dev->dvbq); + put_filp(file); + dprintk(1,"cx88: dvb thread stopped\n"); + return 0; } -static int dvb_cx8800_start_feed(struct dvb_demux_feed *feed) +/* ---------------------------------------------------------------------------- */ + +static int dvb_start_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; - struct cx8800_dvb *dvb = demux->priv; + struct cx8802_dev *dev = demux->priv; + int rc = 0; dprintk(2, "dvb_cx8800_start_feed\n"); if (!demux->dmx.frontend) return -EINVAL; + if (dev->dvb_thread) + return -EINVAL; - return start_ts_capture(dvb); + dev->dvb_thread = kthread_run(dvb_thread, dev, "%s dvb", dev->core->name); + if (IS_ERR(dev->dvb_thread)) { + rc = PTR_ERR(dev->dvb_thread); + dev->dvb_thread = NULL; + } + return rc; } -static int dvb_cx8800_stop_feed(struct dvb_demux_feed *feed) +static int dvb_stop_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; - struct cx8800_dvb *dvb = demux->priv; + struct cx8802_dev *dev = demux->priv; + int err; dprintk(2, "dvb_cx8800_stop_feed\n"); - if (!demux->dmx.frontend) + if (NULL == dev->dvb_thread) return -EINVAL; - - return stop_ts_capture(dvb); + err = kthread_stop(dev->dvb_thread); + dev->dvb_thread = NULL; + return err; } -static void cx8800_ts_release_dev(struct cx8800_dev *dev) +static void dvb_unregister(struct cx8802_dev *dev) { - struct cx8800_dvb *dvb = dev->dvb; - - /* Force us to stop any feed */ - spin_lock(&dvb->feedlock); - if (dvb->ts_feeding != 0) { - spin_unlock(&dvb->feedlock); - printk("Error: releasing a card while feed in progress\n"); - stop_ts_capture(dvb); - } else - spin_unlock(&dvb->feedlock); - - - /* release everything we registered */ - dvb_net_release(&dvb->dvbnet); - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); - dvb_dmxdev_release(&dvb->dmxdev); - dvb_dmx_release(&dvb->demux); - dvb_unregister_i2c_bus(master_xfer, dvb->dvb_adapter, 0); - dvb_unregister_adapter(dvb->dvb_adapter); - btcx_riscmem_free(dev->pci, &dvb->tsq.stopper); - - /* Free ourselves */ - kfree(dvb); - dev->dvb = NULL; - return; -} +#if 1 /* really needed? */ + if (NULL != dev->dvb_thread) { + kthread_stop(dev->dvb_thread); + BUG(); + } +#endif -static void cx8800_ts_resume_dev(struct cx8800_dev *dev) -{ - /* Called with dev->slock held */ - cx8800_restart_ts_queue(dev->dvb); + dvb_net_release(&dev->dvbnet); + dev->demux.dmx.remove_frontend(&dev->demux.dmx, &dev->fe_mem); + dev->demux.dmx.remove_frontend(&dev->demux.dmx, &dev->fe_hw); + dvb_dmxdev_release(&dev->dmxdev); + dvb_dmx_release(&dev->demux); + dvb_unregister_adapter(dev->dvb_adapter); + return; } -static int cx8800_ts_attach_dev(struct cx8800_dev *dev) +static int dvb_register(struct cx8802_dev *dev) { - struct cx8800_dvb *dvb; int result; +#if 0 /* hmm, this is board specific I guess? move to cx88-cards.c? */ /* Take DVB hardware out of reset */ cx_set(MO_GP0_IO, cx88_boards[dev->board].ts.gpio0); cx_clear(MO_GP0_IO, cx88_boards[dev->board].ts.gpio0 & 0xff); - udelay(500); + msleep(1); cx_set(MO_GP0_IO, cx88_boards[dev->board].ts.gpio0); +#endif - /* Allocate a DVB structure */ - dvb = kmalloc(sizeof(struct cx8800_dvb), GFP_KERNEL); - if (dvb == NULL) { - result = -ENOMEM; - goto fail; - } - memset(dvb, 0, sizeof(struct cx8800_dvb)); - - /* Record our device and details */ - dvb->dev = dev; - dvb->irq_handler = cx8800_ts_irq; - dvb->release_dev = cx8800_ts_release_dev; - dvb->resume_dev = cx8800_ts_resume_dev; - - /* init TS dma queues */ - INIT_LIST_HEAD(&dvb->tsq.active); - INIT_LIST_HEAD(&dvb->tsq.queued); - dvb->tsq.timeout.function = cx8800_ts_timeout; - dvb->tsq.timeout.data = (unsigned long) dvb; - init_timer(&dvb->tsq.timeout); - cx88_risc_stopper(dev->pci, &dvb->tsq.stopper, - MO_TS_DMACNTRL, 0x11, 0x00); - - /* init TS feed spinlock */ - dvb->feedlock = SPIN_LOCK_UNLOCKED; - - /* Clear any pending interrupts */ - cx8800_stop_ts_dma(dvb); - - if ((result = - dvb_register_adapter(&dvb->dvb_adapter, dev->name, - THIS_MODULE)) < 0) { - printk(KERN_WARNING - "%s: dvb_register_adapter failed (errno = %d)\n", - dev->name, result); - goto fail; + result = dvb_register_adapter(&dev->dvb_adapter, dev->core->name, + THIS_MODULE); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", + dev->core->name, result); + goto fail1; } - init_MUTEX(&dvb->dvb_i2c_lock); - - if (! dvb_register_i2c_bus(master_xfer, dvb, dvb->dvb_adapter, 0)) { - printk(KERN_WARNING "%s: dvb_register_i2c_bus failed\n", - dev->name); - result = -EFAULT; + dev->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dev->demux.priv = dev; + dev->demux.filternum = 256; + dev->demux.feednum = 256; + dev->demux.start_feed = dvb_start_feed; + dev->demux.stop_feed = dvb_stop_feed; + result = dvb_dmx_init(&dev->demux); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", + dev->core->name, result); goto fail2; } - memset(&dvb->demux, 0, sizeof(struct dvb_demux)); - - dvb->demux.dmx.capabilities = - DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - - dvb->demux.priv = dvb; - dvb->demux.filternum = 256; - dvb->demux.feednum = 256; - dvb->demux.start_feed = dvb_cx8800_start_feed; - dvb->demux.stop_feed = dvb_cx8800_stop_feed; - dvb->demux.write_to_decoder = NULL; - - if ((result = dvb_dmx_init(&dvb->demux)) < 0) { - printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", - dev->name, result); + dev->dmxdev.filternum = 256; + dev->dmxdev.demux = &dev->demux.dmx; + dev->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dev->dmxdev, dev->dvb_adapter); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", + dev->core->name, result); goto fail3; } - dvb->dmxdev.filternum = 256; - dvb->dmxdev.demux = &dvb->demux.dmx; - dvb->dmxdev.capabilities = 0; - - if ((result = dvb_dmxdev_init(&dvb->dmxdev, dvb->dvb_adapter)) < 0) { - printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", - dev->name, result); + dev->fe_hw.source = DMX_FRONTEND_0; + result = dev->demux.dmx.add_frontend(&dev->demux.dmx, &dev->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dev->core->name, result); goto fail4; } - dvb->fe_hw.source = DMX_FRONTEND_0; - - if ((result = - dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw)) < 0) { - printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", - dev->name, result); + dev->fe_mem.source = DMX_MEMORY_FE; + result = dev->demux.dmx.add_frontend(&dev->demux.dmx, &dev->fe_mem); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontent failed (DMX_MEMORY_FE, errno = %d)\n", + dev->core->name, result); goto fail5; } - dvb->fe_mem.source = DMX_MEMORY_FE; - - if ((result = - dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem)) < 0) { - printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", - dev->name, result); + result = dev->demux.dmx.connect_frontend(&dev->demux.dmx, &dev->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: connect_frontent failed (errno = %d)\n", + dev->core->name, result); goto fail6; } - if ((result = - dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, - &dvb->fe_hw)) < 0) { - printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", - dev->name, result); - goto fail7; - } - - dvb_net_init(dvb->dvb_adapter, &dvb->dvbnet, &dvb->demux.dmx); - - dev->dvb = dvb; - printk(KERN_INFO "DVB attached to dev %p, dvb: %p\n", dev, dvb); - + dvb_net_init(dev->dvb_adapter, &dev->dvbnet, &dev->demux.dmx); return 0; -fail7: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); fail6: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dev->demux.dmx.remove_frontend(&dev->demux.dmx, &dev->fe_mem); fail5: - dvb_dmxdev_release(&dvb->dmxdev); + dev->demux.dmx.remove_frontend(&dev->demux.dmx, &dev->fe_hw); fail4: - dvb_dmx_release(&dvb->demux); + dvb_dmxdev_release(&dev->dmxdev); fail3: - dvb_unregister_i2c_bus(master_xfer, dvb->dvb_adapter, 0); + dvb_dmx_release(&dev->demux); fail2: - dvb_unregister_adapter(dvb->dvb_adapter); -fail: + dvb_unregister_adapter(dev->dvb_adapter); +fail1: return result; } +/* ----------------------------------------------------------- */ + +static int __devinit dvb_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct cx8802_dev *dev; + struct cx88_core *core; + int err; + + /* general setup */ + core = cx88_core_get(pci_dev); + if (NULL == core) + return -EINVAL; + + err = -ENODEV; + if (!cx88_boards[core->board].dvb) + goto fail_core; + + err = -ENOMEM; + dev = kmalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) + goto fail_core; + memset(dev,0,sizeof(*dev)); + dev->pci = pci_dev; + dev->core = core; + + err = cx8802_init_common(dev); + if (0 != err) + goto fail_free; + + /* dvb stuff */ + printk("%s/2: cx23416 based dvb card\n", core->name); + videobuf_queue_init(&dev->dvbq, &dvb_qops, + dev->pci, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_TOP, + sizeof(struct cx88_buffer)); + init_MUTEX(&dev->dvbq.lock); + dvb_register(dev); + + return 0; + + fail_free: + kfree(dev); + fail_core: + cx88_core_put(core,pci_dev); + return err; +} + +static void __devexit dvb_remove(struct pci_dev *pci_dev) +{ + struct cx8802_dev *dev = pci_get_drvdata(pci_dev); + + /* dvb */ + dvb_unregister(dev); + + /* common */ + cx8802_fini_common(dev); + cx88_core_put(dev->core,dev->pci); + kfree(dev); +} + +static struct pci_device_id cx8802_pci_tbl[] = { + { + .vendor = 0x14f1, + .device = 0x8802, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + },{ + /* --- end of list --- */ + } +}; +MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); + +static struct pci_driver dvb_pci_driver = { + .name = "cx8802", + .id_table = cx8802_pci_tbl, + .probe = dvb_probe, + .remove = dvb_remove, +}; + +static int dvb_init(void) +{ + printk(KERN_INFO "cx2388x dvb driver version %d.%d.%d loaded\n", + (CX88_VERSION_CODE >> 16) & 0xff, + (CX88_VERSION_CODE >> 8) & 0xff, + CX88_VERSION_CODE & 0xff); +#ifdef SNAPSHOT + printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", + SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif + return pci_module_init(&dvb_pci_driver); +} + +static void dvb_fini(void) +{ + pci_unregister_driver(&dvb_pci_driver); +} + +module_init(dvb_init); +module_exit(dvb_fini); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ |