X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmedia%2Fvideo%2Fcx88%2Fcx88-mpeg.c;h=e1182247f28f023d84676d0fafc2eca2a83b47a2;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=b538c8385af0c6629e3ed92e2a833b73e2eb943f;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index b538c8385..e1182247f 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-mpeg.c,v 1.14 2004/10/25 11:26:36 kraxel Exp $ * * Support for the mpeg transport stream transfers * PCI function #2 of the cx2388x. @@ -24,6 +23,7 @@ */ #include +#include #include #include #include @@ -44,8 +44,46 @@ module_param(debug,int,0644); MODULE_PARM_DESC(debug,"enable debug messages [mpeg]"); #define dprintk(level,fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "%s/2: " fmt, dev->core->name , ## arg) + printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg) +#define mpeg_dbg(level,fmt, arg...) if (debug >= level) \ + printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg) + +#if defined(CONFIG_MODULES) && defined(MODULE) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +static void request_module_async(void *ptr) +{ + struct cx8802_dev *dev=(struct cx8802_dev*)ptr; +#else +static void request_module_async(struct work_struct *work) +{ + struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk); +#endif + + if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_DVB) + request_module("cx88-dvb"); + if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_BLACKBIRD) + request_module("cx88-blackbird"); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#define request_modules(dev) +#else +static void request_modules(struct cx8802_dev *dev) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + INIT_WORK(&dev->request_module_wk, request_module_async, (void*)dev); +#else + INIT_WORK(&dev->request_module_wk, request_module_async); +#endif + schedule_work(&dev->request_module_wk); +} +#endif +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + +static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ static int cx8802_start_dma(struct cx8802_dev *dev, @@ -54,7 +92,7 @@ static int cx8802_start_dma(struct cx8802_dev *dev, { struct cx88_core *core = dev->core; - dprintk(1, "cx8802_start_mpegport_dma %d\n", buf->vb.width); + dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n", dev->width, dev->height, buf->vb.field); /* setup fifo + format */ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], @@ -63,48 +101,78 @@ static int cx8802_start_dma(struct cx8802_dev *dev, /* write TS length to chip */ cx_write(MO_TS_LNGTH, buf->vb.width); -#if 1 /* FIXME: this needs a review. * also: move to cx88-blackbird + cx88-dvb source files? */ - if (cx88_boards[core->board].dvb) { - /* Setup TS portion of chip */ - cx_write(TS_GEN_CNTRL, 0x0c); - } + dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id); - if (cx88_boards[core->board].blackbird) { + if ( (core->active_type_id == CX88_MPEG_DVB) && + (cx88_boards[core->board].mpeg & CX88_MPEG_DVB) ) { + + dprintk( 1, "cx8802_start_dma doing .dvb\n"); + /* negedge driven & software reset */ + cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl); + udelay(100); + cx_write(MO_PINMUX_IO, 0x00); + cx_write(TS_HW_SOP_CNTRL,0x47<<16|188<<4|0x01); + switch (core->board) { + case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: + case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: + case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: + case CX88_BOARD_PCHDTV_HD5500: + cx_write(TS_SOP_STAT, 1<<13); + break; + case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: + case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: + cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */ + udelay(100); + break; + case CX88_BOARD_HAUPPAUGE_HVR1300: + break; + default: + cx_write(TS_SOP_STAT, 0x00); + break; + } + cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl); + udelay(100); + } else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) && + (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) ) { + dprintk( 1, "cx8802_start_dma doing .blackbird\n"); cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */ - // cx_write(TS_F2_CMD_STAT_MM, 0x2900106); /* F2_CMD_STAT_MM defaults + master + memory space */ cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */ udelay(100); cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */ - //cx_write(TS_HW_SOP_CNTRL, 0x2F0BC0); /* mpeg start byte ts: 0x2F0BC0 ? */ cx_write(TS_VALERR_CNTRL, 0x2000); cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */ udelay(100); + } else { + printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __FUNCTION__, + cx88_boards[core->board].mpeg ); + return -EINVAL; } -#endif /* reset counter */ cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET); q->count = 1; /* enable irqs */ - cx_set(MO_PCI_INTMSK, 0x00fc04); - cx_write(MO_TS_INTMSK, 0x1f0011); + dprintk( 1, "setting the interrupt mask\n" ); + cx_set(MO_PCI_INTMSK, core->pci_irqmask | 0x04); + cx_set(MO_TS_INTMSK, 0x1f0011); /* start dma */ - cx_write(MO_DEV_CNTRL2, (1<<5)); /* FIXME: s/write/set/ ??? */ - cx_write(MO_TS_DMACNTRL, 0x11); + cx_set(MO_DEV_CNTRL2, (1<<5)); + cx_set(MO_TS_DMACNTRL, 0x11); return 0; } static int cx8802_stop_dma(struct cx8802_dev *dev) { struct cx88_core *core = dev->core; + dprintk( 1, "cx8802_stop_dma\n" ); /* stop dma */ cx_clear(MO_TS_DMACNTRL, 0x11); @@ -124,8 +192,45 @@ static int cx8802_restart_queue(struct cx8802_dev *dev, struct cx88_buffer *buf; struct list_head *item; + dprintk( 1, "cx8802_restart_queue\n" ); if (list_empty(&q->active)) + { + struct cx88_buffer *prev; + prev = NULL; + + dprintk(1, "cx8802_restart_queue: queue is empty\n" ); + + for (;;) { + if (list_empty(&q->queued)) + return 0; + buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue); + if (NULL == prev) { + list_del(&buf->vb.queue); + list_add_tail(&buf->vb.queue,&q->active); + cx8802_start_dma(dev, q, buf); + buf->vb.state = STATE_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(1,"[%p/%d] restart_queue - first active\n", + buf,buf->vb.i); + + } else if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_del(&buf->vb.queue); + list_add_tail(&buf->vb.queue,&q->active); + buf->vb.state = STATE_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + dprintk(1,"[%p/%d] restart_queue - move to active\n", + buf,buf->vb.i); + } else { + return 0; + } + prev = buf; + } return 0; + } buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); dprintk(2,"restart_queue [%p/%d]: restart dma\n", @@ -141,7 +246,8 @@ static int cx8802_restart_queue(struct cx8802_dev *dev, /* ------------------------------------------------------------------ */ -int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf) +int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev, + struct cx88_buffer *buf, enum v4l2_field field) { int size = dev->ts_packet_size * dev->ts_packet_count; int rc; @@ -154,9 +260,9 @@ int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf) buf->vb.width = dev->ts_packet_size; buf->vb.height = dev->ts_packet_count; buf->vb.size = size; - buf->vb.field = V4L2_FIELD_TOP; + buf->vb.field = field /*V4L2_FIELD_TOP*/; - if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL))) + if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) goto fail; cx88_risc_databuffer(dev->pci, &buf->risc, buf->vb.dma.sglist, @@ -166,35 +272,38 @@ int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf) return 0; fail: - cx88_free_buffer(dev->pci,buf); + cx88_free_buffer(q,buf); return rc; } void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) { struct cx88_buffer *prev; - struct cx88_dmaqueue *q = &dev->mpegq; + struct cx88_dmaqueue *cx88q = &dev->mpegq; + dprintk( 1, "cx8802_buf_queue\n" ); /* add jump to stopper */ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma); - if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue,&q->active); - cx8802_start_dma(dev, q, buf); + if (list_empty(&cx88q->active)) { + dprintk( 1, "queue is empty - first active\n" ); + list_add_tail(&buf->vb.queue,&cx88q->active); + cx8802_start_dma(dev, cx88q, buf); buf->vb.state = STATE_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(2,"[%p/%d] %s - first active\n", + buf->count = cx88q->count++; + mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(1,"[%p/%d] %s - first active\n", buf, buf->vb.i, __FUNCTION__); } else { - prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); - list_add_tail(&buf->vb.queue,&q->active); + dprintk( 1, "queue is not empty - append to active\n" ); + prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue); + list_add_tail(&buf->vb.queue,&cx88q->active); buf->vb.state = STATE_ACTIVE; - buf->count = q->count++; + buf->count = cx88q->count++; prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - dprintk(2,"[%p/%d] %s - append to active\n", + dprintk( 1, "[%p/%d] %s - append to active\n", buf, buf->vb.i, __FUNCTION__); } } @@ -217,7 +326,10 @@ static void do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart) buf, buf->vb.i, reason, (unsigned long)buf->risc.dma); } if (restart) + { + dprintk(1, "restarting queue\n" ); cx8802_restart_queue(dev,q); + } spin_unlock_irqrestore(&dev->slock,flags); } @@ -225,6 +337,7 @@ void cx8802_cancel_buffers(struct cx8802_dev *dev) { struct cx88_dmaqueue *q = &dev->mpegq; + dprintk( 1, "cx8802_cancel_buffers" ); del_timer_sync(&q->timeout); cx8802_stop_dma(dev); do_cancel_buffers(dev,"cancel",0); @@ -234,7 +347,7 @@ static void cx8802_timeout(unsigned long data) { struct cx8802_dev *dev = (struct cx8802_dev*)data; - dprintk(1, "%s\n",__FUNCTION__); + dprintk(0, "%s\n",__FUNCTION__); if (debug) cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); @@ -242,17 +355,28 @@ static void cx8802_timeout(unsigned long data) do_cancel_buffers(dev,"timeout",1); } +static char *cx88_mpeg_irqs[32] = { + "ts_risci1", NULL, NULL, NULL, + "ts_risci2", NULL, NULL, NULL, + "ts_oflow", NULL, NULL, NULL, + "ts_sync", NULL, NULL, NULL, + "opc_err", "par_err", "rip_err", "pci_abort", + "ts_err?", +}; + static void cx8802_mpeg_irq(struct cx8802_dev *dev) { struct cx88_core *core = dev->core; u32 status, mask, count; + dprintk( 1, "cx8802_mpeg_irq\n" ); status = cx_read(MO_TS_INTSTAT); mask = cx_read(MO_TS_INTMSK); if (0 == (status & mask)) return; cx_write(MO_TS_INTSTAT, status); + if (debug || (status & mask & ~0xff)) cx88_print_irqbits(core->name, "irq mpeg ", cx88_mpeg_irqs, status, mask); @@ -266,6 +390,7 @@ static void cx8802_mpeg_irq(struct cx8802_dev *dev) /* risc1 y */ if (status & 0x01) { + dprintk( 1, "wake up\n" ); spin_lock(&dev->slock); count = cx_read(MO_TS_GPCNT); cx88_wakeup(dev->core, &dev->mpegq, count); @@ -279,36 +404,42 @@ static void cx8802_mpeg_irq(struct cx8802_dev *dev) spin_unlock(&dev->slock); } - /* other general errors */ - if (status & 0x1f0100) { - spin_lock(&dev->slock); + /* other general errors */ + if (status & 0x1f0100) { + dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 ); + spin_lock(&dev->slock); cx8802_stop_dma(dev); - cx8802_restart_queue(dev,&dev->mpegq); - spin_unlock(&dev->slock); - } + cx8802_restart_queue(dev,&dev->mpegq); + spin_unlock(&dev->slock); + } } -static irqreturn_t cx8802_irq(int irq, void *dev_id, struct pt_regs *regs) +#define MAX_IRQ_LOOP 10 + +static irqreturn_t cx8802_irq(int irq, void *dev_id) { struct cx8802_dev *dev = dev_id; struct cx88_core *core = dev->core; - u32 status, mask; + u32 status; int loop, handled = 0; - for (loop = 0; loop < 10; loop++) { - status = cx_read(MO_PCI_INTSTAT) & (~0x1f | 0x04); - mask = cx_read(MO_PCI_INTMSK); - if (0 == (status & mask)) + for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { + status = cx_read(MO_PCI_INTSTAT) & (core->pci_irqmask | 0x04); + if (0 == status) goto out; + dprintk( 1, "cx8802_irq\n" ); + dprintk( 1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP ); + dprintk( 1, " status: %d\n", status ); handled = 1; cx_write(MO_PCI_INTSTAT, status); - if (status & mask & ~0x1f) - cx88_irq(core,status,mask); + if (status & core->pci_irqmask) + cx88_core_irq(core,status); if (status & 0x04) cx8802_mpeg_irq(dev); }; - if (10 == loop) { + if (MAX_IRQ_LOOP == loop) { + dprintk( 0, "clearing mask\n" ); printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", core->name); cx_write(MO_PCI_INTMSK,0); @@ -323,6 +454,7 @@ static irqreturn_t cx8802_irq(int irq, void *dev_id, struct pt_regs *regs) int cx8802_init_common(struct cx8802_dev *dev) { + struct cx88_core *core = dev->core; int err; /* pci init */ @@ -335,15 +467,14 @@ int cx8802_init_common(struct cx8802_dev *dev) } pci_read_config_byte(dev->pci, PCI_CLASS_REVISION, &dev->pci_rev); - pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat); - printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, " - "latency: %d, mmio: 0x%lx\n", dev->core->name, + pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat); + printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, " + "latency: %d, mmio: 0x%llx\n", dev->core->name, pci_name(dev->pci), dev->pci_rev, dev->pci->irq, - dev->pci_lat,pci_resource_start(dev->pci,0)); + dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0)); /* initialize driver struct */ - init_MUTEX(&dev->lock); - dev->slock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&dev->slock); /* init dma queue */ INIT_LIST_HEAD(&dev->mpegq.active); @@ -354,24 +485,15 @@ int cx8802_init_common(struct cx8802_dev *dev) cx88_risc_stopper(dev->pci,&dev->mpegq.stopper, MO_TS_DMACNTRL,0x11,0x00); -#if 0 /* FIXME */ - /* initialize hardware */ - cx8802_reset(dev); -#endif - /* get irq */ err = request_irq(dev->pci->irq, cx8802_irq, - SA_SHIRQ | SA_INTERRUPT, dev->core->name, dev); + IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev); if (err < 0) { printk(KERN_ERR "%s: can't get IRQ %d\n", dev->core->name, dev->pci->irq); return err; } - -#if 0 /* FIXME */ - /* register i2c bus + load i2c helpers */ - cx88_card_setup(dev); -#endif + cx_set(MO_PCI_INTMSK, core->pci_irqmask); /* everything worked */ pci_set_drvdata(dev->pci,dev); @@ -380,6 +502,7 @@ int cx8802_init_common(struct cx8802_dev *dev) void cx8802_fini_common(struct cx8802_dev *dev) { + dprintk( 2, "cx8802_fini_common\n" ); cx8802_stop_dma(dev); pci_disable_device(dev->pci); @@ -393,27 +516,26 @@ void cx8802_fini_common(struct cx8802_dev *dev) /* ----------------------------------------------------------- */ -int cx8802_suspend_common(struct pci_dev *pci_dev, u32 state) +int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state) { - struct cx8802_dev *dev = pci_get_drvdata(pci_dev); + struct cx8802_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; /* stop mpeg dma */ spin_lock(&dev->slock); if (!list_empty(&dev->mpegq.active)) { + dprintk( 2, "suspend\n" ); printk("%s: suspend mpeg\n", core->name); cx8802_stop_dma(dev); del_timer(&dev->mpegq.timeout); } spin_unlock(&dev->slock); -#if 1 /* FIXME -- shutdown device */ cx88_shutdown(dev->core); -#endif pci_save_state(pci_dev); - if (0 != pci_set_power_state(pci_dev, state)) { + if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { pci_disable_device(pci_dev); dev->state.disabled = 1; } @@ -422,20 +544,32 @@ int cx8802_suspend_common(struct pci_dev *pci_dev, u32 state) int cx8802_resume_common(struct pci_dev *pci_dev) { - struct cx8802_dev *dev = pci_get_drvdata(pci_dev); + struct cx8802_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; + int err; if (dev->state.disabled) { - pci_enable_device(pci_dev); + err=pci_enable_device(pci_dev); + if (err) { + printk(KERN_ERR "%s: can't enable device\n", + dev->core->name); + return err; + } dev->state.disabled = 0; } - pci_set_power_state(pci_dev, 0); + err=pci_set_power_state(pci_dev, PCI_D0); + if (err) { + printk(KERN_ERR "%s: can't enable device\n", + dev->core->name); + pci_disable_device(pci_dev); + dev->state.disabled = 1; + + return err; + } pci_restore_state(pci_dev); -#if 1 /* FIXME: re-initialize hardware */ cx88_reset(dev->core); -#endif /* restart video+vbi capture */ spin_lock(&dev->slock); @@ -448,8 +582,318 @@ int cx8802_resume_common(struct pci_dev *pci_dev) return 0; } +struct cx8802_dev * cx8802_get_device(struct inode *inode) +{ + int minor = iminor(inode); + struct cx8802_dev *h = NULL; + struct list_head *list; + + list_for_each(list,&cx8802_devlist) { + h = list_entry(list, struct cx8802_dev, devlist); + if (h->mpeg_dev->minor == minor) + return h; + } + + return NULL; +} + +struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype) +{ + struct cx8802_dev *h = NULL; + struct cx8802_driver *d = NULL; + struct list_head *list; + struct list_head *list2; + + list_for_each(list,&cx8802_devlist) { + h = list_entry(list, struct cx8802_dev, devlist); + if (h != dev) + continue; + + list_for_each(list2, &h->drvlist.devlist) { + d = list_entry(list2, struct cx8802_driver, devlist); + + /* only unregister the correct driver type */ + if (d->type_id == btype) { + return d; + } + } + } + + return NULL; +} + +/* Driver asked for hardware access. */ +int cx8802_request_acquire(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + + /* Fail a request for hardware if the device is busy. */ + if (core->active_type_id != CX88_BOARD_NONE) + return -EBUSY; + + if (drv->advise_acquire) + { + core->active_type_id = drv->type_id; + drv->advise_acquire(drv); + + mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO)); + } + + return 0; +} + +/* Driver asked to release hardware. */ +int cx8802_request_release(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + + if (drv->advise_release) + { + drv->advise_release(drv); + core->active_type_id = CX88_BOARD_NONE; + mpeg_dbg(1,"%s() Post release GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO)); + } + + return 0; +} + +static int cx8802_check_driver(struct cx8802_driver *drv) +{ + if (drv == NULL) + return -ENODEV; + + if ((drv->type_id != CX88_MPEG_DVB) && + (drv->type_id != CX88_MPEG_BLACKBIRD)) + return -EINVAL; + + if ((drv->hw_access != CX8802_DRVCTL_SHARED) && + (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE)) + return -EINVAL; + + if ((drv->probe == NULL) || + (drv->remove == NULL) || + (drv->advise_acquire == NULL) || + (drv->advise_release == NULL)) + return -EINVAL; + + return 0; +} + +int cx8802_register_driver(struct cx8802_driver *drv) +{ + struct cx8802_dev *h; + struct cx8802_driver *driver; + struct list_head *list; + int err = 0, i = 0; + + printk(KERN_INFO "%s() ->registering driver type=%s access=%s\n", __FUNCTION__ , + drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", + drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); + + if ((err = cx8802_check_driver(drv)) != 0) { + printk(KERN_INFO "%s() cx8802_driver is invalid\n", __FUNCTION__ ); + return err; + } + + list_for_each(list,&cx8802_devlist) { + h = list_entry(list, struct cx8802_dev, devlist); + + printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n", + h->core->name,h->pci->subsystem_vendor, + h->pci->subsystem_device,cx88_boards[h->core->board].name, + h->core->board); + + /* Bring up a new struct for each driver instance */ + driver = kzalloc(sizeof(*drv),GFP_KERNEL); + if (driver == NULL) + return -ENOMEM; + + /* Snapshot of the driver registration data */ + drv->core = h->core; + drv->suspend = cx8802_suspend_common; + drv->resume = cx8802_resume_common; + drv->request_acquire = cx8802_request_acquire; + drv->request_release = cx8802_request_release; + memcpy(driver, drv, sizeof(*driver)); + + err = drv->probe(driver); + if (err == 0) { + i++; + mutex_lock(&drv->core->lock); + list_add_tail(&driver->devlist,&h->drvlist.devlist); + mutex_unlock(&drv->core->lock); + } else { + printk(KERN_ERR "%s() ->probe failed err = %d\n", __FUNCTION__, err); + } + + } + if (i == 0) + err = -ENODEV; + else + err = 0; + + return err; +} + +int cx8802_unregister_driver(struct cx8802_driver *drv) +{ + struct cx8802_dev *h; + struct cx8802_driver *d; + struct list_head *list; + struct list_head *list2, *q; + int err = 0, i = 0; + + printk(KERN_INFO "%s() ->unregistering driver type=%s\n", __FUNCTION__ , + drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird"); + + list_for_each(list,&cx8802_devlist) { + i++; + h = list_entry(list, struct cx8802_dev, devlist); + + printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n", + h->core->name,h->pci->subsystem_vendor, + h->pci->subsystem_device,cx88_boards[h->core->board].name, + h->core->board); + + list_for_each_safe(list2, q, &h->drvlist.devlist) { + d = list_entry(list2, struct cx8802_driver, devlist); + + /* only unregister the correct driver type */ + if (d->type_id != drv->type_id) + continue; + + err = d->remove(d); + if (err == 0) { + mutex_lock(&drv->core->lock); + list_del(list2); + mutex_unlock(&drv->core->lock); + } else + printk(KERN_ERR "%s() ->remove failed err = %d\n", __FUNCTION__, err); + + } + + } + + return err; +} + /* ----------------------------------------------------------- */ +static int __devinit cx8802_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; + + printk("%s/2: cx2388x 8802 Driver Manager\n", core->name); + + err = -ENODEV; + if (!cx88_boards[core->board].mpeg) + goto fail_core; + + err = -ENOMEM; + dev = kzalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) + goto fail_core; + dev->pci = pci_dev; + dev->core = core; + err = cx8802_init_common(dev); + if (err != 0) + goto fail_free; + + INIT_LIST_HEAD(&dev->drvlist.devlist); + list_add_tail(&dev->devlist,&cx8802_devlist); + + /* Maintain a reference so cx88-video can query the 8802 device. */ + core->dvbdev = dev; + + /* now autoload cx88-dvb or cx88-blackbird */ + request_modules(dev); + return 0; + + fail_free: + kfree(dev); + fail_core: + cx88_core_put(core,pci_dev); + return err; +} + +static void __devexit cx8802_remove(struct pci_dev *pci_dev) +{ + struct cx8802_dev *dev; + struct cx8802_driver *h; + struct list_head *list; + + dev = pci_get_drvdata(pci_dev); + + dprintk( 1, "%s\n", __FUNCTION__); + + list_for_each(list,&dev->drvlist.devlist) { + h = list_entry(list, struct cx8802_driver, devlist); + dprintk( 1, " ->driver\n"); + if (h->remove == NULL) { + printk(KERN_ERR "%s .. skipping driver, no probe function\n", __FUNCTION__); + continue; + } + printk(KERN_INFO "%s .. Removing driver type %d\n", __FUNCTION__, h->type_id); + cx8802_unregister_driver(h); + list_del(&dev->drvlist.devlist); + } + + /* Destroy any 8802 reference. */ + dev->core->dvbdev = NULL; + + /* 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 cx8802_pci_driver = { + .name = "cx88-mpeg driver manager", + .id_table = cx8802_pci_tbl, + .probe = cx8802_probe, + .remove = __devexit_p(cx8802_remove), +}; + +static int cx8802_init(void) +{ + printk(KERN_INFO "cx2388x cx88-mpeg Driver Manager 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_register_driver(&cx8802_pci_driver); +} + +static void cx8802_fini(void) +{ + pci_unregister_driver(&cx8802_pci_driver); +} + +module_init(cx8802_init); +module_exit(cx8802_fini); EXPORT_SYMBOL(cx8802_buf_prepare); EXPORT_SYMBOL(cx8802_buf_queue); EXPORT_SYMBOL(cx8802_cancel_buffers); @@ -457,12 +901,14 @@ EXPORT_SYMBOL(cx8802_cancel_buffers); EXPORT_SYMBOL(cx8802_init_common); EXPORT_SYMBOL(cx8802_fini_common); -EXPORT_SYMBOL(cx8802_suspend_common); -EXPORT_SYMBOL(cx8802_resume_common); - +EXPORT_SYMBOL(cx8802_register_driver); +EXPORT_SYMBOL(cx8802_unregister_driver); +EXPORT_SYMBOL(cx8802_get_device); +EXPORT_SYMBOL(cx8802_get_driver); /* ----------------------------------------------------------- */ /* * Local variables: * c-basic-offset: 8 * End: + * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off */