From 1a23f81b7dc3115b29cff0e4f58b5dd04a6242ad Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 28 Dec 2008 22:18:14 -0300 Subject: [PATCH] V4L/DVB (9979): em28xx: move usb probe code to a proper place em28xx-video were holding several code that are not specific to V4L2 interface. This patch moves the core code for em28xx-core, and usb probing code into em28xx-cards. This opens the possibility of breaking em28xx into a core module and a V4L2 module, loaded only on devices that have analog interfaces. Some cleanup may be done at em28xx-cards to optimize the config code. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 430 +++++++++++++++ drivers/media/video/em28xx/em28xx-core.c | 143 +++++ drivers/media/video/em28xx/em28xx-video.c | 619 ++-------------------- drivers/media/video/em28xx/em28xx.h | 16 +- 4 files changed, 633 insertions(+), 575 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index e5d9424d03f9..42978f9592b7 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -37,6 +37,8 @@ #include "em28xx.h" +#define DRIVER_NAME "em28xx" + static int tuner = -1; module_param(tuner, int, 0444); MODULE_PARM_DESC(tuner, "tuner type"); @@ -45,6 +47,13 @@ static unsigned int disable_ir; module_param(disable_ir, int, 0444); MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); +static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + +/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ +static unsigned long em28xx_devused; + struct em28xx_hash_table { unsigned long hash; unsigned int model; @@ -1801,3 +1810,424 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_ir_init(dev); } + +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct em28xx *dev = container_of(work, + struct em28xx, request_module_wk); + + if (dev->has_audio_class) + request_module("snd-usb-audio"); + else if (dev->has_alsa_audio) + request_module("em28xx-alsa"); + + if (dev->board.has_dvb) + request_module("em28xx-dvb"); +} + +static void request_modules(struct em28xx *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + +/* + * em28xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void em28xx_release_resources(struct em28xx *dev) +{ + if (dev->sbutton_input_dev) + em28xx_deregister_snapshot_button(dev); + + if (dev->ir) + em28xx_ir_fini(dev); + + /*FIXME: I2C IR should be disconnected */ + + em28xx_release_analog_resources(dev); + + em28xx_remove_from_devlist(dev); + + em28xx_i2c_unregister(dev); + usb_put_dev(dev->udev); + + /* Mark device as unused */ + em28xx_devused &= ~(1 << dev->devno); +}; + +/* + * em28xx_init_dev() + * allocates and inits the device structs, registers i2c bus and v4l device + */ +int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, + int minor) +{ + struct em28xx *dev = *devhandle; + int retval = -ENOMEM; + int errCode; + + dev->udev = udev; + mutex_init(&dev->ctrl_urb_lock); + spin_lock_init(&dev->slock); + init_waitqueue_head(&dev->open); + init_waitqueue_head(&dev->wait_frame); + init_waitqueue_head(&dev->wait_stream); + + dev->em28xx_write_regs = em28xx_write_regs; + dev->em28xx_read_reg = em28xx_read_reg; + dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len; + dev->em28xx_write_regs_req = em28xx_write_regs_req; + dev->em28xx_read_reg_req = em28xx_read_reg_req; + dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; + + em28xx_pre_card_setup(dev); + + if (!dev->board.is_em2800) { + /* Sets I2C speed to 100 KHz */ + retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); + if (retval < 0) { + em28xx_errdev("%s: em28xx_write_regs_req failed!" + " retval [%d]\n", + __func__, retval); + return retval; + } + } + + /* register i2c bus */ + errCode = em28xx_i2c_register(dev); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* Do board specific init and eeprom reading */ + em28xx_card_setup(dev); + + /* Configure audio */ + errCode = em28xx_audio_setup(dev); + if (errCode < 0) { + em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n", + __func__, errCode); + } + + /* wake i2c devices */ + em28xx_wake_i2c(dev); + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + + if (dev->board.has_msp34xx) { + /* Send a reset to other chips via gpio */ + errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_write_regs_req - " + "msp34xx(1) failed! errCode [%d]\n", + __func__, errCode); + return errCode; + } + msleep(3); + + errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_write_regs_req - " + "msp34xx(2) failed! errCode [%d]\n", + __func__, errCode); + return errCode; + } + msleep(3); + } + + em28xx_add_into_devlist(dev); + + errCode = em28xx_analog_config(dev); + if (errCode) { + em28xx_errdev("error configuring device\n"); + return -ENOMEM; + } + + retval = em28xx_register_analog_devices(dev); + if (retval < 0) { + em28xx_release_resources(dev); + goto fail_reg_devices; + } + + em28xx_init_extension(dev); + + /* Save some power by putting tuner to sleep */ + em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + + return 0; + +fail_reg_devices: + return retval; +} + +/* + * em28xx_usb_probe() + * checks for supported devices + */ +static int em28xx_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + const struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev; + struct usb_interface *uif; + struct em28xx *dev = NULL; + int retval = -ENODEV; + int i, nr, ifnum, isoc_pipe; + char *speed; + char descr[255] = ""; + + udev = usb_get_dev(interface_to_usbdev(interface)); + ifnum = interface->altsetting[0].desc.bInterfaceNumber; + + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); + em28xx_devused |= 1<altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { + em28xx_err(DRIVER_NAME " audio device (%04x:%04x): " + "interface %i, class %i\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting[0].desc.bInterfaceClass); + + em28xx_devused &= ~(1<cur_altsetting->endpoint[0].desc; + + /* check if the device has the iso in endpoint at the correct place */ + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_ISOC && + (interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940)) { + /* It's a newer em2874/em2875 device */ + isoc_pipe = 0; + } else { + int check_interface = 1; + isoc_pipe = 1; + endpoint = &interface->cur_altsetting->endpoint[1].desc; + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + USB_ENDPOINT_XFER_ISOC) + check_interface = 0; + + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) + check_interface = 0; + + if (!check_interface) { + em28xx_err(DRIVER_NAME " video device (%04x:%04x): " + "interface %i, class %i found.\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting[0].desc.bInterfaceClass); + + em28xx_err(DRIVER_NAME " This is an anciliary " + "interface not used by the driver\n"); + + em28xx_devused &= ~(1<speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + if (udev->manufacturer) + strlcpy(descr, udev->manufacturer, sizeof(descr)); + + if (udev->product) { + if (*descr) + strlcat(descr, " ", sizeof(descr)); + strlcat(descr, udev->product, sizeof(descr)); + } + if (*descr) + strlcat(descr, " ", sizeof(descr)); + + printk(DRIVER_NAME ": New device %s@ %s Mbps " + "(%04x:%04x, interface %d, class %d)\n", + descr, + speed, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting->desc.bInterfaceNumber); + + if (nr >= EM28XX_MAXBOARDS) { + printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", + EM28XX_MAXBOARDS); + em28xx_devused &= ~(1<name, 29, "em28xx #%d", nr); + dev->devno = nr; + dev->model = id->driver_info; + dev->alt = -1; + + /* Checks if audio is provided by some interface */ + for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { + uif = udev->config->interface[i]; + if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { + dev->has_audio_class = 1; + break; + } + } + + /* compute alternate max packet sizes */ + uif = udev->actconfig->interface[0]; + + dev->num_alt = uif->num_altsetting; + dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL); + + if (dev->alt_max_pkt_size == NULL) { + em28xx_errdev("out of memory!\n"); + em28xx_devused &= ~(1<num_alt ; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize); + dev->alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + } + + if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) + dev->model = card[nr]; + + /* allocate device struct */ + mutex_init(&dev->lock); + mutex_lock(&dev->lock); + retval = em28xx_init_dev(&dev, udev, nr); + if (retval) { + em28xx_devused &= ~(1<devno); + kfree(dev); + + return retval; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + request_modules(dev); + + /* Should be the last thing to do, to avoid newer udev's to + open the device before fully initializing it + */ + mutex_unlock(&dev->lock); + + return 0; +} + +/* + * em28xx_usb_disconnect() + * called when the device gets diconencted + * video device will be unregistered on v4l2_close in case it is still open + */ +static void em28xx_usb_disconnect(struct usb_interface *interface) +{ + struct em28xx *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + em28xx_info("disconnecting %s\n", dev->vdev->name); + + /* wait until all current v4l2 io is finished then deallocate + resources */ + mutex_lock(&dev->lock); + + wake_up_interruptible_all(&dev->open); + + if (dev->users) { + em28xx_warn + ("device /dev/video%d is open! Deregistration and memory " + "deallocation are deferred on close.\n", + dev->vdev->num); + + dev->state |= DEV_MISCONFIGURED; + em28xx_uninit_isoc(dev); + dev->state |= DEV_DISCONNECTED; + wake_up_interruptible(&dev->wait_frame); + wake_up_interruptible(&dev->wait_stream); + } else { + dev->state |= DEV_DISCONNECTED; + em28xx_release_resources(dev); + } + + em28xx_close_extension(dev); + + mutex_unlock(&dev->lock); + + if (!dev->users) { + kfree(dev->alt_max_pkt_size); + kfree(dev); + } +} + +static struct usb_driver em28xx_usb_driver = { + .name = "em28xx", + .probe = em28xx_usb_probe, + .disconnect = em28xx_usb_disconnect, + .id_table = em28xx_id_table, +}; + +static int __init em28xx_module_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&em28xx_usb_driver); + if (result) + em28xx_err(DRIVER_NAME + " usb_register failed. Error number %d.\n", result); + + printk(KERN_INFO DRIVER_NAME " driver loaded\n"); + + return result; +} + +static void __exit em28xx_module_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&em28xx_usb_driver); +} + +module_init(em28xx_module_init); +module_exit(em28xx_module_exit); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 5964998daf2e..ac735f25283c 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "em28xx.h" @@ -974,3 +975,145 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, return 0; } EXPORT_SYMBOL_GPL(em28xx_init_isoc); + +/* + * em28xx_wake_i2c() + * configure i2c attached devices + */ +void em28xx_wake_i2c(struct em28xx *dev) +{ + struct v4l2_routing route; + int zero = 0; + + route.input = INPUT(dev->ctl_input)->vmux; + route.output = 0; + em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero); + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); + em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL); +} + +/* + * Device control list + */ + +static LIST_HEAD(em28xx_devlist); +static DEFINE_MUTEX(em28xx_devlist_mutex); + +struct em28xx *em28xx_get_device(struct inode *inode, + enum v4l2_buf_type *fh_type, + int *has_radio) +{ + struct em28xx *h, *dev = NULL; + int minor = iminor(inode); + + *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + *has_radio = 0; + + mutex_lock(&em28xx_devlist_mutex); + list_for_each_entry(h, &em28xx_devlist, devlist) { + if (h->vdev->minor == minor) + dev = h; + if (h->vbi_dev->minor == minor) { + dev = h; + *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; + } + if (h->radio_dev && + h->radio_dev->minor == minor) { + dev = h; + *has_radio = 1; + } + } + mutex_unlock(&em28xx_devlist_mutex); + + return dev; +} + +/* + * em28xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void em28xx_remove_from_devlist(struct em28xx *dev) +{ + mutex_lock(&em28xx_devlist_mutex); + list_del(&dev->devlist); + mutex_unlock(&em28xx_devlist_mutex); +}; + +void em28xx_add_into_devlist(struct em28xx *dev) +{ + mutex_lock(&em28xx_devlist_mutex); + list_add_tail(&dev->devlist, &em28xx_devlist); + mutex_unlock(&em28xx_devlist_mutex); +}; + +/* + * Extension interface + */ + +static LIST_HEAD(em28xx_extension_devlist); +static DEFINE_MUTEX(em28xx_extension_devlist_lock); + +int em28xx_register_extension(struct em28xx_ops *ops) +{ + struct em28xx *dev = NULL; + + mutex_lock(&em28xx_devlist_mutex); + mutex_lock(&em28xx_extension_devlist_lock); + list_add_tail(&ops->next, &em28xx_extension_devlist); + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->init(dev); + } + printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); + mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); + return 0; +} +EXPORT_SYMBOL(em28xx_register_extension); + +void em28xx_unregister_extension(struct em28xx_ops *ops) +{ + struct em28xx *dev = NULL; + + mutex_lock(&em28xx_devlist_mutex); + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->fini(dev); + } + + mutex_lock(&em28xx_extension_devlist_lock); + printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); +} +EXPORT_SYMBOL(em28xx_unregister_extension); + +void em28xx_init_extension(struct em28xx *dev) +{ + struct em28xx_ops *ops = NULL; + + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); +} + +void em28xx_close_extension(struct em28xx *dev) +{ + struct em28xx_ops *ops = NULL; + + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); +} diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index b5ea18e12290..4c0986920970 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -48,9 +48,8 @@ "Mauro Carvalho Chehab , " \ "Sascha Sommer " -#define DRIVER_NAME "em28xx" #define DRIVER_DESC "Empia em28xx based USB video device driver" -#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0) +#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 1) #define em28xx_videodbg(fmt, arg...) do {\ if (video_debug) \ @@ -73,19 +72,13 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static LIST_HEAD(em28xx_devlist); -static DEFINE_MUTEX(em28xx_devlist_mutex); - -static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -module_param_array(card, int, NULL, 0444); module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); module_param_array(radio_nr, int, NULL, 0444); -MODULE_PARM_DESC(card, "card type"); MODULE_PARM_DESC(video_nr, "video device numbers"); MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); MODULE_PARM_DESC(radio_nr, "radio device numbers"); @@ -94,9 +87,6 @@ static unsigned int video_debug; module_param(video_debug, int, 0644); MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); -/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ -static unsigned long em28xx_devused; - /* supported video standards */ static struct em28xx_fmt format[] = { { @@ -131,8 +121,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { } }; -static struct usb_driver em28xx_usb_driver; - /* ------------------------------------------------------------------ DMA and thread functions ------------------------------------------------------------------*/ @@ -519,56 +507,6 @@ static struct videobuf_queue_ops em28xx_video_qops = { /********************* v4l2 interface **************************************/ -/* - * em28xx_config() - * inits registers with sane defaults - */ -static int em28xx_config(struct em28xx *dev) -{ - int retval; - - /* Sets I2C speed to 100 KHz */ - if (!dev->board.is_em2800) { - retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); - if (retval < 0) { - em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n", - __func__, retval); - return retval; - } - } - - /* enable vbi capturing */ - -/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ -/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */ - em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51); - - dev->mute = 1; /* maybe not the right place... */ - dev->volume = 0x1f; - - em28xx_set_outfmt(dev); - em28xx_colorlevels_set_default(dev); - em28xx_compression_disable(dev); - - return 0; -} - -/* - * em28xx_config_i2c() - * configure i2c attached devices - */ -static void em28xx_config_i2c(struct em28xx *dev) -{ - struct v4l2_routing route; - int zero = 0; - - route.input = INPUT(dev->ctl_input)->vmux; - route.output = 0; - em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero); - em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); - em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL); -} - static void video_mux(struct em28xx *dev, int index) { struct v4l2_routing route; @@ -1647,28 +1585,12 @@ static int radio_queryctrl(struct file *file, void *priv, static int em28xx_v4l2_open(struct inode *inode, struct file *filp) { int minor = iminor(inode); - int errCode = 0, radio = 0; - struct em28xx *h, *dev = NULL; + int errCode = 0, radio; + struct em28xx *dev; + enum v4l2_buf_type fh_type; struct em28xx_fh *fh; - enum v4l2_buf_type fh_type = 0; - mutex_lock(&em28xx_devlist_mutex); - list_for_each_entry(h, &em28xx_devlist, devlist) { - if (h->vdev->minor == minor) { - dev = h; - fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - } - if (h->vbi_dev->minor == minor) { - dev = h; - fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; - } - if (h->radio_dev && - h->radio_dev->minor == minor) { - radio = 1; - dev = h; - } - } - mutex_unlock(&em28xx_devlist_mutex); + dev = em28xx_get_device(inode, &fh_type, &radio); if (NULL == dev) return -ENODEV; @@ -1703,7 +1625,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) /* Needed, since GPIO might have disabled power of some i2c device */ - em28xx_config_i2c(dev); + em28xx_wake_i2c(dev); } if (fh->radio) { @@ -1727,18 +1649,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) * unregisters the v4l2,i2c and usb devices * called when the device gets disconected or at module unload */ -static void em28xx_release_resources(struct em28xx *dev) +void em28xx_release_analog_resources(struct em28xx *dev) { /*FIXME: I2C IR should be disconnected */ - list_del(&dev->devlist); - if (dev->sbutton_input_dev) - em28xx_deregister_snapshot_button(dev); - - if (dev->ir) - em28xx_ir_fini(dev); - if (dev->radio_dev) { if (-1 != dev->radio_dev->minor) video_unregister_device(dev->radio_dev); @@ -1764,11 +1679,6 @@ static void em28xx_release_resources(struct em28xx *dev) video_device_release(dev->vdev); dev->vdev = NULL; } - em28xx_i2c_unregister(dev); - usb_put_dev(dev->udev); - - /* Mark device as unused */ - em28xx_devused &= ~(1<devno); } /* @@ -2018,44 +1928,6 @@ static struct video_device em28xx_radio_template = { /******************************** usb interface ******************************/ -static LIST_HEAD(em28xx_extension_devlist); -static DEFINE_MUTEX(em28xx_extension_devlist_lock); - -int em28xx_register_extension(struct em28xx_ops *ops) -{ - struct em28xx *dev = NULL; - - mutex_lock(&em28xx_devlist_mutex); - mutex_lock(&em28xx_extension_devlist_lock); - list_add_tail(&ops->next, &em28xx_extension_devlist); - list_for_each_entry(dev, &em28xx_devlist, devlist) { - if (dev) - ops->init(dev); - } - printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); - mutex_unlock(&em28xx_extension_devlist_lock); - mutex_unlock(&em28xx_devlist_mutex); - return 0; -} -EXPORT_SYMBOL(em28xx_register_extension); - -void em28xx_unregister_extension(struct em28xx_ops *ops) -{ - struct em28xx *dev = NULL; - - mutex_lock(&em28xx_devlist_mutex); - list_for_each_entry(dev, &em28xx_devlist, devlist) { - if (dev) - ops->fini(dev); - } - - mutex_lock(&em28xx_extension_devlist_lock); - printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); - list_del(&ops->next); - mutex_unlock(&em28xx_extension_devlist_lock); - mutex_unlock(&em28xx_devlist_mutex); -} -EXPORT_SYMBOL(em28xx_unregister_extension); static struct video_device *em28xx_vdev_init(struct em28xx *dev, const struct video_device *template, @@ -2078,7 +1950,45 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } -static int register_analog_devices(struct em28xx *dev) +int em28xx_analog_config(struct em28xx *dev) +{ + printk(KERN_INFO "%s: v4l2 driver version %d.%d.%d\n", + dev->name, + (EM28XX_VERSION_CODE >> 16) & 0xff, + (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff); + + /* Analog specific initialization */ + dev->format = &format[0]; + video_mux(dev, 0); + + /* enable vbi capturing */ + +/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ +/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */ + em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51); + + dev->mute = 1; /* maybe not the right place... */ + dev->volume = 0x1f; + + em28xx_set_outfmt(dev); + em28xx_colorlevels_set_default(dev); + em28xx_compression_disable(dev); + + /* set default norm */ + dev->norm = em28xx_video_template.current_norm; + dev->width = norm_maxw(dev); + dev->height = norm_maxh(dev); + dev->interlaced = EM28XX_INTERLACED_DEFAULT; + dev->hscale = 0; + dev->vscale = 0; + + /* FIXME: This is a very bad hack! Not all devices have TV on input 2 */ + dev->ctl_input = 2; + + return 0; +} + +int em28xx_register_analog_devices(struct em28xx *dev) { int ret; @@ -2130,440 +2040,3 @@ static int register_analog_devices(struct em28xx *dev) return 0; } - - -/* - * em28xx_init_dev() - * allocates and inits the device structs, registers i2c bus and v4l device - */ -static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, - int minor) -{ - struct em28xx_ops *ops = NULL; - struct em28xx *dev = *devhandle; - int retval = -ENOMEM; - int errCode; - unsigned int maxh, maxw; - - dev->udev = udev; - mutex_init(&dev->ctrl_urb_lock); - spin_lock_init(&dev->slock); - init_waitqueue_head(&dev->open); - init_waitqueue_head(&dev->wait_frame); - init_waitqueue_head(&dev->wait_stream); - - dev->em28xx_write_regs = em28xx_write_regs; - dev->em28xx_read_reg = em28xx_read_reg; - dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len; - dev->em28xx_write_regs_req = em28xx_write_regs_req; - dev->em28xx_read_reg_req = em28xx_read_reg_req; - dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; - dev->format = &format[0]; - - em28xx_pre_card_setup(dev); - - errCode = em28xx_config(dev); - if (errCode) { - em28xx_errdev("error configuring device\n"); - return -ENOMEM; - } - - /* register i2c bus */ - errCode = em28xx_i2c_register(dev); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* Do board specific init and eeprom reading */ - em28xx_card_setup(dev); - - /* Configure audio */ - errCode = em28xx_audio_setup(dev); - if (errCode < 0) { - em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n", - __func__, errCode); - } - - /* configure the device */ - em28xx_config_i2c(dev); - - /* set default norm */ - dev->norm = em28xx_video_template.current_norm; - - maxw = norm_maxw(dev); - maxh = norm_maxh(dev); - - /* set default image size */ - dev->width = maxw; - dev->height = maxh; - dev->interlaced = EM28XX_INTERLACED_DEFAULT; - dev->hscale = 0; - dev->vscale = 0; - dev->ctl_input = 2; - - errCode = em28xx_config(dev); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_config - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* init video dma queues */ - INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); - - - if (dev->board.has_msp34xx) { - /* Send a reset to other chips via gpio */ - errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n", - __func__, errCode); - return errCode; - } - msleep(3); - - errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n", - __func__, errCode); - return errCode; - } - msleep(3); - } - - video_mux(dev, 0); - - mutex_lock(&em28xx_devlist_mutex); - list_add_tail(&dev->devlist, &em28xx_devlist); - retval = register_analog_devices(dev); - if (retval < 0) { - em28xx_release_resources(dev); - mutex_unlock(&em28xx_devlist_mutex); - goto fail_reg_devices; - } - - mutex_lock(&em28xx_extension_devlist_lock); - if (!list_empty(&em28xx_extension_devlist)) { - list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->id) - ops->init(dev); - } - } - mutex_unlock(&em28xx_extension_devlist_lock); - mutex_unlock(&em28xx_devlist_mutex); - - /* Save some power by putting tuner to sleep */ - em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); - - return 0; - -fail_reg_devices: - return retval; -} - -#if defined(CONFIG_MODULES) && defined(MODULE) -static void request_module_async(struct work_struct *work) -{ - struct em28xx *dev = container_of(work, - struct em28xx, request_module_wk); - - if (dev->has_audio_class) - request_module("snd-usb-audio"); - else if (dev->has_alsa_audio) - request_module("em28xx-alsa"); - - if (dev->board.has_dvb) - request_module("em28xx-dvb"); -} - -static void request_modules(struct em28xx *dev) -{ - INIT_WORK(&dev->request_module_wk, request_module_async); - schedule_work(&dev->request_module_wk); -} -#else -#define request_modules(dev) -#endif /* CONFIG_MODULES */ - -/* - * em28xx_usb_probe() - * checks for supported devices - */ -static int em28xx_usb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - const struct usb_endpoint_descriptor *endpoint; - struct usb_device *udev; - struct usb_interface *uif; - struct em28xx *dev = NULL; - int retval = -ENODEV; - int i, nr, ifnum, isoc_pipe; - char *speed; - char descr[255] = ""; - - udev = usb_get_dev(interface_to_usbdev(interface)); - ifnum = interface->altsetting[0].desc.bInterfaceNumber; - - /* Check to see next free device and mark as used */ - nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); - em28xx_devused |= 1<altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { - em28xx_err(DRIVER_NAME " audio device (%04x:%04x): " - "interface %i, class %i\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - ifnum, - interface->altsetting[0].desc.bInterfaceClass); - - em28xx_devused &= ~(1<cur_altsetting->endpoint[0].desc; - - /* check if the device has the iso in endpoint at the correct place */ - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == - USB_ENDPOINT_XFER_ISOC && - (interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940)) - { - /* It's a newer em2874/em2875 device */ - isoc_pipe = 0; - } else { - int check_interface = 1; - isoc_pipe = 1; - endpoint = &interface->cur_altsetting->endpoint[1].desc; - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != - USB_ENDPOINT_XFER_ISOC) - check_interface = 0; - - if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) - check_interface = 0; - - if (!check_interface) { - em28xx_err(DRIVER_NAME " video device (%04x:%04x): " - "interface %i, class %i found.\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - ifnum, - interface->altsetting[0].desc.bInterfaceClass); - - em28xx_err(DRIVER_NAME " This is an anciliary " - "interface not used by the driver\n"); - - em28xx_devused &= ~(1<speed) { - case USB_SPEED_LOW: - speed = "1.5"; - break; - case USB_SPEED_UNKNOWN: - case USB_SPEED_FULL: - speed = "12"; - break; - case USB_SPEED_HIGH: - speed = "480"; - break; - default: - speed = "unknown"; - } - - if (udev->manufacturer) - strlcpy(descr, udev->manufacturer, sizeof(descr)); - - if (udev->product) { - if (*descr) - strlcat(descr, " ", sizeof(descr)); - strlcat(descr, udev->product, sizeof(descr)); - } - if (*descr) - strlcat(descr, " ", sizeof(descr)); - - printk(DRIVER_NAME ": New device %s@ %s Mbps " - "(%04x:%04x, interface %d, class %d)\n", - descr, - speed, - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - ifnum, - interface->altsetting->desc.bInterfaceNumber); - - if (nr >= EM28XX_MAXBOARDS) { - printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", - EM28XX_MAXBOARDS); - em28xx_devused &= ~(1<name, 29, "em28xx #%d", nr); - dev->devno = nr; - dev->model = id->driver_info; - dev->alt = -1; - - /* Checks if audio is provided by some interface */ - for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { - uif = udev->config->interface[i]; - if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { - dev->has_audio_class = 1; - break; - } - } - - /* compute alternate max packet sizes */ - uif = udev->actconfig->interface[0]; - - dev->num_alt = uif->num_altsetting; - em28xx_videodbg("Alternate settings: %i\n", dev->num_alt); -/* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */ - dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL); - - if (dev->alt_max_pkt_size == NULL) { - em28xx_errdev("out of memory!\n"); - em28xx_devused &= ~(1<num_alt ; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. - wMaxPacketSize); - dev->alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - em28xx_videodbg("Alternate setting %i, max size= %i\n", i, - dev->alt_max_pkt_size[i]); - } - - if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) - dev->model = card[nr]; - - /* allocate device struct */ - mutex_init(&dev->lock); - mutex_lock(&dev->lock); - retval = em28xx_init_dev(&dev, udev, nr); - if (retval) { - em28xx_devused &= ~(1<devno); - kfree(dev); - - return retval; - } - - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - - request_modules(dev); - - /* Should be the last thing to do, to avoid newer udev's to - open the device before fully initializing it - */ - mutex_unlock(&dev->lock); - - return 0; -} - -/* - * em28xx_usb_disconnect() - * called when the device gets diconencted - * video device will be unregistered on v4l2_close in case it is still open - */ -static void em28xx_usb_disconnect(struct usb_interface *interface) -{ - struct em28xx *dev; - struct em28xx_ops *ops = NULL; - - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - if (!dev) - return; - - em28xx_info("disconnecting %s\n", dev->vdev->name); - - /* wait until all current v4l2 io is finished then deallocate - resources */ - mutex_lock(&dev->lock); - - wake_up_interruptible_all(&dev->open); - - if (dev->users) { - em28xx_warn - ("device /dev/video%d is open! Deregistration and memory " - "deallocation are deferred on close.\n", - dev->vdev->num); - - dev->state |= DEV_MISCONFIGURED; - em28xx_uninit_isoc(dev); - dev->state |= DEV_DISCONNECTED; - wake_up_interruptible(&dev->wait_frame); - wake_up_interruptible(&dev->wait_stream); - } else { - dev->state |= DEV_DISCONNECTED; - em28xx_release_resources(dev); - } - mutex_unlock(&dev->lock); - - mutex_lock(&em28xx_extension_devlist_lock); - if (!list_empty(&em28xx_extension_devlist)) { - list_for_each_entry(ops, &em28xx_extension_devlist, next) { - ops->fini(dev); - } - } - mutex_unlock(&em28xx_extension_devlist_lock); - - if (!dev->users) { - kfree(dev->alt_max_pkt_size); - kfree(dev); - } -} - -static struct usb_driver em28xx_usb_driver = { - .name = "em28xx", - .probe = em28xx_usb_probe, - .disconnect = em28xx_usb_disconnect, - .id_table = em28xx_id_table, -}; - -static int __init em28xx_module_init(void) -{ - int result; - - printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n", - (EM28XX_VERSION_CODE >> 16) & 0xff, - (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff); -#ifdef SNAPSHOT - printk(KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d\n", - SNAPSHOT / 10000, (SNAPSHOT / 100) % 100, SNAPSHOT % 100); -#endif - - /* register this driver with the USB subsystem */ - result = usb_register(&em28xx_usb_driver); - if (result) - em28xx_err(DRIVER_NAME - " usb_register failed. Error number %d.\n", result); - - return result; -} - -static void __exit em28xx_module_exit(void) -{ - /* deregister this driver with the USB subsystem */ - usb_deregister(&em28xx_usb_driver); -} - -module_init(em28xx_module_init); -module_exit(em28xx_module_exit); - diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 100f27819cdc..c8e71edbfea7 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -581,10 +581,21 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, void em28xx_uninit_isoc(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); - -/* Provided by em28xx-video.c */ +void em28xx_wake_i2c(struct em28xx *dev); +void em28xx_remove_from_devlist(struct em28xx *dev); +void em28xx_add_into_devlist(struct em28xx *dev); +struct em28xx *em28xx_get_device(struct inode *inode, + enum v4l2_buf_type *fh_type, + int *has_radio); int em28xx_register_extension(struct em28xx_ops *dev); void em28xx_unregister_extension(struct em28xx_ops *dev); +void em28xx_init_extension(struct em28xx *dev); +void em28xx_close_extension(struct em28xx *dev); + +/* Provided by em28xx-video.c */ +int em28xx_analog_config(struct em28xx *dev); +int em28xx_register_analog_devices(struct em28xx *dev); +void em28xx_release_analog_resources(struct em28xx *dev); /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device *udev, int model); @@ -595,6 +606,7 @@ extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir); int em28xx_tuner_callback(void *ptr, int component, int command, int arg); +void em28xx_release_resources(struct em28xx *dev); /* Provided by em28xx-input.c */ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -- 2.30.2