From 6c5be74c86f102c2d4e123bc51d2fa93155fd794 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 2 Dec 2006 21:15:51 -0200 Subject: [PATCH] V4L/DVB (4676): Dynamic cx88 mpeg port management for HVR1300 MPEG2/DVB-T support. A series of patches to change the cx88 framework to allow the PCI mpeg port to be shared dynamically between different types of drivers or applications. This patch changes the cx88-dvb and cx88-blackbird drivers to become 'sub drivers' of a higher single cx88-mpeg driver. The cx88-mpeg driver is a superset of the previous cx88-mpeg/blackbird drivers and now owns the IRQ. cx88-dvb/blackbird now become mini drivers, registering themselves with cx88-mpeg through a standard interface with callbacks. Sub drivers request access to hardware via the cx88-mpeg driver. In turn the cx88-mpeg driver determines whether the hardware is busy and accepts or refuses the request, grant access using callbacks into the sub drivers. The net effect is that you are no longer able to tamper with the mpeg port from multiple different applications at the same time, potentially breaking a live mpeg2 hardware encoding or dvb stream. The mechanism extends to enable multiple dvb frontends to be registered and share the single resource. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx88/cx88-blackbird.c | 178 ++++++----- drivers/media/video/cx88/cx88-cards.c | 2 +- drivers/media/video/cx88/cx88-dvb.c | 197 +++++++------ drivers/media/video/cx88/cx88-mpeg.c | 344 ++++++++++++++++++++-- drivers/media/video/cx88/cx88.h | 46 +++ 5 files changed, 591 insertions(+), 176 deletions(-) diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 46738321adaf..0037188d77d4 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -50,7 +50,6 @@ MODULE_PARM_DESC(debug,"enable debug messages [blackbird]"); #define dprintk(level,fmt, arg...) if (debug >= level) \ printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg) -static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ @@ -882,7 +881,7 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file, BLACKBIRD_MPEG_CAPTURE, BLACKBIRD_RAW_BITS_NONE); - cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); + cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); blackbird_initialize_codec(dev); cx88_set_scale(dev->core, dev->width, dev->height, @@ -914,11 +913,15 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file, } default: - return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); + return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); } return 0; } +int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); +unsigned int (*cx88_ioctl_translator)(unsigned int cmd); + static unsigned int mpeg_translate_ioctl(unsigned int cmd) { return cmd; @@ -927,33 +930,48 @@ static unsigned int mpeg_translate_ioctl(unsigned int cmd) static int mpeg_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - cmd = mpeg_translate_ioctl( cmd ); - return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl); + cmd = cx88_ioctl_translator( cmd ); + return video_usercopy(inode, file, cmd, arg, cx88_ioctl_hook); } static int mpeg_open(struct inode *inode, struct file *file) { int minor = iminor(inode); - struct cx8802_dev *h,*dev = NULL; + struct cx8802_dev *dev = NULL; struct cx8802_fh *fh; - struct list_head *list; + struct cx8802_driver *drv = NULL; + int err; - list_for_each(list,&cx8802_devlist) { - h = list_entry(list, struct cx8802_dev, devlist); - if (h->mpeg_dev->minor == minor) - dev = h; - } - if (NULL == dev) + dprintk( 1, "%s\n", __FUNCTION__); + + dev = cx8802_get_device(inode); + if (dev == NULL) return -ENODEV; - if (blackbird_initialize_codec(dev) < 0) + /* Make sure we can acquire the hardware */ + drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); + if (drv) { + err = drv->request_acquire(drv); + if(err != 0) { + dprintk(1,"%s: Unable to acquire hardware, %d\n", __FUNCTION__, err); + return err; + } + } + + if (blackbird_initialize_codec(dev) < 0) { + if (drv) + drv->request_release(drv); return -EINVAL; + } dprintk(1,"open minor=%d\n",minor); /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh),GFP_KERNEL); - if (NULL == fh) + if (NULL == fh) { + if (drv) + drv->request_release(drv); return -ENOMEM; + } file->private_data = fh; fh->dev = dev; @@ -974,6 +992,8 @@ static int mpeg_open(struct inode *inode, struct file *file) static int mpeg_release(struct inode *inode, struct file *file) { struct cx8802_fh *fh = file->private_data; + struct cx8802_dev *dev = NULL; + struct cx8802_driver *drv = NULL; /* blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */ blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, @@ -992,6 +1012,16 @@ static int mpeg_release(struct inode *inode, struct file *file) videobuf_mmap_free(&fh->mpegq); file->private_data = NULL; kfree(fh); + + /* Make sure we release the hardware */ + dev = cx8802_get_device(inode); + if (dev == NULL) + return -ENODEV; + + drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); + if (drv) + drv->request_release(drv); + return 0; } @@ -1043,6 +1073,44 @@ static struct video_device cx8802_mpeg_template = /* ------------------------------------------------------------------ */ +/* The CX8802 MPEG API will call this when we can use the hardware */ +static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* By default, core setup will leave the cx22702 out of reset, on the bus. + * We left the hardware on power up with the cx22702 active. + * We're being given access to re-arrange the GPIOs. + * Take the bus off the cx22702 and put the cx23416 on it. + */ + cx_clear(MO_GP0_IO, 0x00000080); /* cx22702 in reset */ + cx_set(MO_GP0_IO, 0x00000004); /* Disable the cx22702 */ + break; + default: + err = -ENODEV; + } + return err; +} + +/* The CX8802 MPEG API will call this when we need to release the hardware */ +static int cx8802_blackbird_advise_release(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Exit leaving the cx23416 on the bus */ + break; + default: + err = -ENODEV; + } + return err; +} + static void blackbird_unregister_video(struct cx8802_dev *dev) { if (dev->mpeg_dev) { @@ -1073,28 +1141,23 @@ static int blackbird_register_video(struct cx8802_dev *dev) /* ----------------------------------------------------------- */ -static int __devinit blackbird_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) +static int cx8802_blackbird_probe(struct cx8802_driver *drv) { - struct cx8802_dev *dev; - struct cx88_core *core; + struct cx88_core *core = drv->core; + struct cx8802_dev *dev = core->dvbdev; int err; - /* general setup */ - core = cx88_core_get(pci_dev); - if (NULL == core) - return -EINVAL; + dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", + core->board, + core->name, + core->pci_bus, + core->pci_slot); err = -ENODEV; if (!(cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD)) goto fail_core; - err = -ENOMEM; - dev = kzalloc(sizeof(*dev),GFP_KERNEL); - if (NULL == dev) - goto fail_core; - dev->pci = pci_dev; - dev->core = core; dev->width = 720; dev->height = 576; cx2341x_fill_defaults(&dev->params); @@ -1106,64 +1169,36 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev, dev->height = 576; } - err = cx8802_init_common(dev); - if (0 != err) - goto fail_free; - /* blackbird stuff */ printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n", core->name); host_setup(dev->core); - list_add_tail(&dev->devlist,&cx8802_devlist); blackbird_register_video(dev); /* initial device configuration: needed ? */ return 0; - fail_free: - kfree(dev); fail_core: - cx88_core_put(core,pci_dev); return err; } -static void __devexit blackbird_remove(struct pci_dev *pci_dev) +static int cx8802_blackbird_remove(struct cx8802_driver *drv) { - struct cx8802_dev *dev = pci_get_drvdata(pci_dev); - /* blackbird */ - blackbird_unregister_video(dev); - list_del(&dev->devlist); + blackbird_unregister_video(drv->core->dvbdev); - /* common */ - cx8802_fini_common(dev); - cx88_core_put(dev->core,dev->pci); - kfree(dev); + return 0; } -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 blackbird_pci_driver = { - .name = "cx88-blackbird", - .id_table = cx8802_pci_tbl, - .probe = blackbird_probe, - .remove = __devexit_p(blackbird_remove), -#ifdef CONFIG_PM - .suspend = cx8802_suspend_common, - .resume = cx8802_resume_common, -#endif +static struct cx8802_driver cx8802_blackbird_driver = { + .type_id = CX88_MPEG_BLACKBIRD, + .hw_access = CX8802_DRVCTL_SHARED, + .probe = cx8802_blackbird_probe, + .remove = cx8802_blackbird_remove, + .advise_acquire = cx8802_blackbird_advise_acquire, + .advise_release = cx8802_blackbird_advise_release, }; static int blackbird_init(void) @@ -1176,17 +1211,22 @@ static int blackbird_init(void) printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif - return pci_register_driver(&blackbird_pci_driver); + cx88_ioctl_hook = mpeg_do_ioctl; + cx88_ioctl_translator = mpeg_translate_ioctl; + return cx8802_register_driver(&cx8802_blackbird_driver); } static void blackbird_fini(void) { - pci_unregister_driver(&blackbird_pci_driver); + cx8802_unregister_driver(&cx8802_blackbird_driver); } module_init(blackbird_init); module_exit(blackbird_fini); +EXPORT_SYMBOL(cx88_ioctl_hook); +EXPORT_SYMBOL(cx88_ioctl_translator); + /* ----------------------------------------------------------- */ /* * Local variables: diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index f764a57c56be..1c5db2070a6e 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1303,7 +1303,7 @@ struct cx88_board cx88_boards[] = { .gpio0 = 0xe780, }}, /* fixme: Add radio support */ - .mpeg = CX88_MPEG_DVB, + .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, }, }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 0ef13e7efa2e..8150c09cd2c0 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -57,7 +57,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"enable debug messages [dvb]"); #define dprintk(level,fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->core->name , ## arg) + printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg) /* ------------------------------------------------------------------ */ @@ -315,20 +315,36 @@ static struct cx22702_config hauppauge_novat_config = { .demod_address = 0x43, .output_mode = CX22702_SERIAL_OUTPUT, }; - static struct cx22702_config hauppauge_hvr1100_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, }; - -static struct cx22702_config hauppauge_hvr1300_config = { +static struct cx22702_config hauppauge_hvr3000_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, }; -static struct cx22702_config hauppauge_hvr3000_config = { +static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, + int acquire) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct cx8802_driver *drv = NULL; + int ret = 0; + + drv = cx8802_get_driver(dev, CX88_MPEG_DVB); + if (drv) { + if(acquire) + ret = drv->request_acquire(drv); + else + ret = drv->request_release(drv); + } + + return ret; +} + +static struct cx22702_config hauppauge_hvr1300_config = { .demod_address = 0x63, - .output_mode = CX22702_SERIAL_OUTPUT, + .output_mode = CX22702_SERIAL_OUTPUT, }; static int or51132_set_ts_param(struct dvb_frontend* fe, @@ -555,26 +571,6 @@ static int dvb_register(struct cx8802_dev *dev) &dvb_pll_fmd1216me); } break; - case CX88_BOARD_HAUPPAUGE_HVR1300: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr1300_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); - } - break; - case CX88_BOARD_HAUPPAUGE_HVR3000: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr3000_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); - } - break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: dev->dvb.frontend = dvb_attach(mt352_attach, &dvico_fusionhdtv, @@ -782,6 +778,26 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; } break; + case CX88_BOARD_HAUPPAUGE_HVR1300: + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr1300_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_fmd1216me); + } + break; + case CX88_BOARD_HAUPPAUGE_HVR3000: + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr3000_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_fmd1216me); + } + break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->core->name); @@ -796,6 +812,8 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend->ops.info.frequency_min = dev->core->pll_desc->min; dev->dvb.frontend->ops.info.frequency_max = dev->core->pll_desc->max; } + /* Ensure all frontends negotiate bus access */ + dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; /* Put the analog decoder in standby to keep it quiet */ cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); @@ -806,37 +824,67 @@ static int dvb_register(struct cx8802_dev *dev) /* ----------------------------------------------------------- */ -static int __devinit dvb_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) +/* CX8802 MPEG -> mini driver - We have been given the hardware */ +static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv) { - struct cx8802_dev *dev; - struct cx88_core *core; + struct cx88_core *core = drv->core; + int err = 0; + dprintk( 1, "%s\n", __FUNCTION__); + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* We arrive here with either the cx23416 or the cx22702 + * on the bus. Take the bus from the cx23416 and enable the + * cx22702 demod + */ + cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset and enable */ + cx_clear(MO_GP0_IO, 0x00000004); + udelay(1000); + break; + default: + err = -ENODEV; + } + return err; +} + +/* CX8802 MPEG -> mini driver - We no longer have the hardware */ +static int cx8802_dvb_advise_release(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + dprintk( 1, "%s\n", __FUNCTION__); + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Do Nothing, leave the cx22702 on the bus. */ + break; + default: + err = -ENODEV; + } + return err; +} + +static int cx8802_dvb_probe(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + struct cx8802_dev *dev = drv->core->dvbdev; int err; - /* general setup */ - core = cx88_core_get(pci_dev); - if (NULL == core) - return -EINVAL; + dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", + core->board, + core->name, + core->pci_bus, + core->pci_slot); err = -ENODEV; if (!(cx88_boards[core->board].mpeg & CX88_MPEG_DVB)) 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 (0 != err) - goto fail_free; - #ifdef HAVE_VP3054_I2C err = vp3054_i2c_probe(dev); if (0 != err) - goto fail_free; + goto fail_core; #endif /* dvb stuff */ @@ -848,28 +896,16 @@ static int __devinit dvb_probe(struct pci_dev *pci_dev, sizeof(struct cx88_buffer), dev); err = dvb_register(dev); - if (0 != err) - goto fail_fini; - - /* Maintain a reference to cx88-video can query the 8802 device. */ - core->dvbdev = dev; - return 0; + if (err != 0) + printk("%s dvb_register failed err = %d\n", __FUNCTION__, err); - fail_fini: - cx8802_fini_common(dev); - fail_free: - kfree(dev); fail_core: - cx88_core_put(core,pci_dev); return err; } -static void __devexit dvb_remove(struct pci_dev *pci_dev) +static int cx8802_dvb_remove(struct cx8802_driver *drv) { - struct cx8802_dev *dev = pci_get_drvdata(pci_dev); - - /* Destroy any 8802 reference. */ - dev->core->dvbdev = NULL; + struct cx8802_dev *dev = drv->core->dvbdev; /* dvb */ videobuf_dvb_unregister(&dev->dvb); @@ -878,33 +914,16 @@ static void __devexit dvb_remove(struct pci_dev *pci_dev) vp3054_i2c_remove(dev); #endif - /* common */ - cx8802_fini_common(dev); - cx88_core_put(dev->core,dev->pci); - kfree(dev); + return 0; } -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 = "cx88-dvb", - .id_table = cx8802_pci_tbl, - .probe = dvb_probe, - .remove = __devexit_p(dvb_remove), -#ifdef CONFIG_PM - .suspend = cx8802_suspend_common, - .resume = cx8802_resume_common, -#endif +static struct cx8802_driver cx8802_dvb_driver = { + .type_id = CX88_MPEG_DVB, + .hw_access = CX8802_DRVCTL_SHARED, + .probe = cx8802_dvb_probe, + .remove = cx8802_dvb_remove, + .advise_acquire = cx8802_dvb_advise_acquire, + .advise_release = cx8802_dvb_advise_release, }; static int dvb_init(void) @@ -917,12 +936,12 @@ static int dvb_init(void) printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif - return pci_register_driver(&dvb_pci_driver); + return cx8802_register_driver(&cx8802_dvb_driver); } static void dvb_fini(void) { - pci_unregister_driver(&dvb_pci_driver); + cx8802_unregister_driver(&cx8802_dvb_driver); } module_init(dvb_init); diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 6b23a4e6f66d..6f155713ee02 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -44,8 +44,12 @@ 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) + +static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ static int cx8802_start_dma(struct cx8802_dev *dev, @@ -65,17 +69,13 @@ static int cx8802_start_dma(struct cx8802_dev *dev, /* FIXME: this needs a review. * also: move to cx88-blackbird + cx88-dvb source files? */ - if (cx88_boards[core->board].mpeg == (CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) ) { - /* Report a warning until the mini driver patch is applied, - * else the following conditions will set the dma registers incorrectly. - * This will be removed in the next major patch and changes to the conditions - * will be made. - */ - printk(KERN_INFO "%s() board->(CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) is invalid\n", __FUNCTION__); - return -EINVAL; - } - if (cx88_boards[core->board].mpeg & CX88_MPEG_DVB) { + dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id); + + 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); @@ -93,15 +93,17 @@ static int cx8802_start_dma(struct cx8802_dev *dev, 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); - } - - if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) { + } 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_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */ @@ -112,6 +114,10 @@ static int cx8802_start_dma(struct cx8802_dev *dev, 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; } /* reset counter */ @@ -542,8 +548,311 @@ 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); + + 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) { + 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); + + /* 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) { + 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; + + 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; + 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); @@ -551,9 +860,10 @@ 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: diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 3bc91aad4fe5..5980e47aee13 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -74,6 +74,11 @@ enum cx88_board_type { CX88_MPEG_BLACKBIRD }; +enum cx8802_board_access { + CX8802_DRVCTL_SHARED = 1, + CX8802_DRVCTL_EXCLUSIVE = 2, +}; + /* ----------------------------------------------------------- */ /* tv norms */ @@ -330,6 +335,7 @@ struct cx88_core { /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ struct cx8802_dev *dvbdev; + enum cx88_board_type active_type_id; }; struct cx8800_dev; @@ -405,6 +411,31 @@ struct cx8802_suspend_state { int disabled; }; +struct cx8802_driver { + struct cx88_core *core; + struct list_head devlist; + + /* Type of driver and access required */ + enum cx88_board_type type_id; + enum cx8802_board_access hw_access; + + /* MPEG 8802 internal only */ + int (*suspend)(struct pci_dev *pci_dev, pm_message_t state); + int (*resume)(struct pci_dev *pci_dev); + + /* MPEG 8802 -> mini driver - Driver probe and configuration */ + int (*probe)(struct cx8802_driver *drv); + int (*remove)(struct cx8802_driver *drv); + + /* MPEG 8802 -> mini driver - Access for hardware control */ + int (*advise_acquire)(struct cx8802_driver *drv); + int (*advise_release)(struct cx8802_driver *drv); + + /* MPEG 8802 <- mini driver - Access for hardware control */ + int (*request_acquire)(struct cx8802_driver *drv); + int (*request_release)(struct cx8802_driver *drv); +}; + struct cx8802_dev { struct cx88_core *core; spinlock_t slock; @@ -439,6 +470,9 @@ struct cx8802_dev { /* mpeg params */ struct cx2341x_mpeg_params params; + + /* List of attached drivers */ + struct cx8802_driver drvlist; }; /* ----------------------------------------------------------- */ @@ -571,6 +605,11 @@ void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t); void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual); int cx88_audio_thread(void *data); +int cx8802_register_driver(struct cx8802_driver *drv); +int cx8802_unregister_driver(struct cx8802_driver *drv); +struct cx8802_dev * cx8802_get_device(struct inode *inode); +struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); + /* ----------------------------------------------------------- */ /* cx88-input.c */ @@ -600,6 +639,13 @@ extern int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, extern const u32 cx88_user_ctrls[]; extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl); +/* ----------------------------------------------------------- */ +/* cx88-blackbird.c */ +/* used by cx88-ivtv ioctl emulation layer */ +extern int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); +extern unsigned int (*cx88_ioctl_translator)(unsigned int cmd); + /* * Local variables: * c-basic-offset: 8 -- 2.30.2