V4L/DVB (7163): em28xx: makes audio settings more stable
[deliverable/linux.git] / drivers / media / video / em28xx / em28xx-video.c
index c4db1aebdcd725b71d47f4f738564d8114f159d5..eeda3b2faec8a723f93f09c490b6bd124bb7c964 100644 (file)
 #include <linux/i2c.h>
 #include <linux/version.h>
 #include <linux/mm.h>
-#include <linux/video_decoder.h>
 #include <linux/mutex.h>
 
 #include "em28xx.h"
 #include <media/v4l2-common.h>
 #include <media/msp3400.h>
+#include <media/tuner.h>
 
 #define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
                      "Markus Rechberger <mrechberger@gmail.com>, " \
@@ -62,13 +62,17 @@ static LIST_HEAD(em28xx_devlist);
 
 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 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_PARM_DESC(card,"card type");
-MODULE_PARM_DESC(video_nr,"video device numbers");
-MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
+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");
 
 static unsigned int video_debug = 0;
 module_param(video_debug,int,0644);
@@ -77,29 +81,6 @@ MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
 /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
 static unsigned long em28xx_devused;
 
-/* supported tv norms */
-static struct em28xx_tvnorm tvnorms[] = {
-       {
-               .name = "PAL",
-               .id = V4L2_STD_PAL,
-               .mode = VIDEO_MODE_PAL,
-        }, {
-               .name = "NTSC",
-               .id = V4L2_STD_NTSC,
-               .mode = VIDEO_MODE_NTSC,
-       }, {
-                .name = "SECAM",
-                .id = V4L2_STD_SECAM,
-                .mode = VIDEO_MODE_SECAM,
-       }, {
-               .name = "PAL-M",
-               .id = V4L2_STD_PAL_M,
-               .mode = VIDEO_MODE_PAL,
-       }
-};
-
-#define TVNORMS ARRAY_SIZE(tvnorms)
-
 /* supported controls */
 /* Common to all boards */
 static struct v4l2_queryctrl em28xx_qctrl[] = {
@@ -146,11 +127,9 @@ static int em28xx_config(struct em28xx *dev)
 /*     em28xx_write_regs_req(dev,0x00,0x0f,"\x80",1); clk register */
        em28xx_write_regs_req(dev,0x00,0x11,"\x51",1);
 
-       em28xx_audio_usb_mute(dev, 1);
        dev->mute = 1;          /* maybe not the right place... */
        dev->volume = 0x1f;
-       em28xx_audio_analog_set(dev);
-       em28xx_audio_analog_setup(dev);
+
        em28xx_outfmt_set_yuv422(dev);
        em28xx_colorlevels_set_default(dev);
        em28xx_compression_disable(dev);
@@ -192,7 +171,6 @@ static void em28xx_empty_framequeues(struct em28xx *dev)
 
 static void video_mux(struct em28xx *dev, int index)
 {
-       int ainput;
        struct v4l2_routing route;
 
        route.input = INPUT(index)->vmux;
@@ -209,18 +187,9 @@ static void video_mux(struct em28xx *dev, int index)
                route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
                /* Note: this is msp3400 specific */
                em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route);
-               ainput = EM28XX_AUDIO_SRC_TUNER;
-               em28xx_audio_source(dev, ainput);
-       } else {
-               switch (dev->ctl_ainput) {
-                       case 0:
-                               ainput = EM28XX_AUDIO_SRC_TUNER;
-                               break;
-                       default:
-                               ainput = EM28XX_AUDIO_SRC_LINE;
-               }
-               em28xx_audio_source(dev, ainput);
        }
+
+       em28xx_audio_analog_set(dev);
 }
 
 /* Usage lock check functions */
@@ -316,7 +285,6 @@ static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
        case V4L2_CID_AUDIO_MUTE:
                if (ctrl->value != dev->mute) {
                        dev->mute = ctrl->value;
-                       em28xx_audio_usb_mute(dev, ctrl->value);
                        return em28xx_audio_analog_set(dev);
                }
                return 0;
@@ -536,27 +504,14 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
        struct em28xx_fh   *fh  = priv;
        struct em28xx      *dev = fh->dev;
        struct v4l2_format f;
-       unsigned int       i;
        int                rc;
 
        rc = check_dev(dev);
        if (rc < 0)
                return rc;
 
-       for (i = 0; i < TVNORMS; i++)
-               if (*norm == tvnorms[i].id)
-                       break;
-       if (i == TVNORMS)
-               for (i = 0; i < TVNORMS; i++)
-                       if (*norm & tvnorms[i].id)
-                               break;
-       if (i == TVNORMS)
-               return -EINVAL;
-
-       *norm = tvnorms[i].id;
-
        mutex_lock(&dev->lock);
-       dev->tvnorm = &tvnorms[i];
+       dev->norm = *norm;
        mutex_unlock(&dev->lock);
 
        /* Adjusts width/height, if needed */
@@ -575,7 +530,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
        get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
 
        em28xx_resolution_set(dev);
-       em28xx_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id);
+       em28xx_i2c_call_clients(dev, VIDIOC_S_STD, &dev->norm);
 
        mutex_unlock(&dev->lock);
        return 0;
@@ -615,8 +570,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
                (EM28XX_VMUX_CABLE == INPUT(n)->type))
                i->type = V4L2_INPUT_TYPE_TUNER;
 
-       for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
-               i->std |= tvnorms[n].id;
+       i->std = dev->vdev->tvnorms;
 
        return 0;
 }
@@ -842,7 +796,7 @@ static int vidioc_g_frequency(struct file *file, void *priv,
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
 
-       f->type = V4L2_TUNER_ANALOG_TV;
+       f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
        f->frequency = dev->ctl_freq;
 
        return 0;
@@ -862,7 +816,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
        if (0 != f->tuner)
                return -EINVAL;
 
-       if (V4L2_TUNER_ANALOG_TV != f->type)
+       if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+               return -EINVAL;
+       if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
                return -EINVAL;
 
        mutex_lock(&dev->lock);
@@ -874,6 +830,63 @@ static int vidioc_s_frequency(struct file *file, void *priv,
        return 0;
 }
 
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int em28xx_reg_len(int reg)
+{
+       switch (reg) {
+       case AC97LSB_REG:
+       case HSCALELOW_REG:
+       case VSCALELOW_REG:
+               return 2;
+       default:
+               return 1;
+       }
+}
+
+static int vidioc_g_register(struct file *file, void *priv,
+                            struct v4l2_register *reg)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int ret;
+
+       if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+               return -EINVAL;
+
+       if (em28xx_reg_len(reg->reg) == 1) {
+               ret = em28xx_read_reg(dev, reg->reg);
+               if (ret < 0)
+                       return ret;
+
+               reg->val = ret;
+       } else {
+               u64 val = 0;
+               ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
+                                                  reg->reg, (char *)&val, 2);
+               if (ret < 0)
+                       return ret;
+
+               reg->val = cpu_to_le64((__u64)val);
+       }
+
+       return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+                            struct v4l2_register *reg)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       u64 buf;
+
+       buf = le64_to_cpu((__u64)reg->val);
+
+       return em28xx_write_regs(dev, reg->reg, (char *)&buf,
+                                em28xx_reg_len(reg->reg));
+}
+#endif
+
+
 static int vidioc_cropcap(struct file *file, void *priv,
                                        struct v4l2_cropcap *cc)
 {
@@ -973,7 +986,7 @@ static int vidioc_querycap(struct file *file, void  *priv,
                        V4L2_CAP_AUDIO |
                        V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
 
-       if (dev->has_tuner)
+       if (dev->tuner_type != TUNER_ABSENT)
                cap->capabilities |= V4L2_CAP_TUNER;
 
        return 0;
@@ -1199,6 +1212,102 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
        return 0;
 }
 
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS                                      */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap(struct file *file, void  *priv,
+                         struct v4l2_capability *cap)
+{
+       struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+       strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+       strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+       strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+
+       cap->version = EM28XX_VERSION_CODE;
+       cap->capabilities = V4L2_CAP_TUNER;
+       return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+                        struct v4l2_tuner *t)
+{
+       struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+       if (unlikely(t->index > 0))
+               return -EINVAL;
+
+       strcpy(t->name, "Radio");
+       t->type = V4L2_TUNER_RADIO;
+
+       em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+       return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+                           struct v4l2_input *i)
+{
+       if (i->index != 0)
+               return -EINVAL;
+       strcpy(i->name, "Radio");
+       i->type = V4L2_INPUT_TYPE_TUNER;
+
+       return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+       if (unlikely(a->index))
+               return -EINVAL;
+
+       strcpy(a->name, "Radio");
+       return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+                        struct v4l2_tuner *t)
+{
+       struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+       if (0 != t->index)
+               return -EINVAL;
+
+       em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+
+       return 0;
+}
+
+static int radio_s_audio(struct file *file, void *fh,
+                        struct v4l2_audio *a)
+{
+       return 0;
+}
+
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
+{
+       return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+                          struct v4l2_queryctrl *qc)
+{
+       int i;
+
+       if (qc->id <  V4L2_CID_BASE ||
+               qc->id >= V4L2_CID_LASTP1)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+               if (qc->id && qc->id == em28xx_qctrl[i].id) {
+                       memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
 /*
  * em28xx_v4l2_open()
  * inits the device and starts isoc transfer
@@ -1206,7 +1315,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
 static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
 {
        int minor = iminor(inode);
-       int errCode = 0;
+       int errCode = 0, radio = 0;
        struct em28xx *h,*dev = NULL;
        struct em28xx_fh *fh;
 
@@ -1219,6 +1328,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
                        dev  = h;
                        dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
                }
+               if (h->radio_dev &&
+                   h->radio_dev->minor == minor) {
+                       radio = 1;
+                       dev   = h;
+               }
        }
        if (NULL == dev)
                return -ENODEV;
@@ -1234,6 +1348,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
        }
        mutex_lock(&dev->lock);
        fh->dev = dev;
+       fh->radio = radio;
        filp->private_data = fh;
 
        if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
@@ -1258,6 +1373,10 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
 
                em28xx_empty_framequeues(dev);
        }
+       if (fh->radio) {
+               em28xx_videodbg("video_open: setting radio device\n");
+               em28xx_i2c_call_clients(dev, AUDC_SET_RADIO, NULL);
+       }
 
        dev->users++;
 
@@ -1280,12 +1399,30 @@ static void em28xx_release_resources(struct em28xx *dev)
                                dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
                                dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
        list_del(&dev->devlist);
-       video_unregister_device(dev->vdev);
-       video_unregister_device(dev->vbi_dev);
+       if (dev->radio_dev) {
+               if (-1 != dev->radio_dev->minor)
+                       video_unregister_device(dev->radio_dev);
+               else
+                       video_device_release(dev->radio_dev);
+               dev->radio_dev = NULL;
+       }
+       if (dev->vbi_dev) {
+               if (-1 != dev->vbi_dev->minor)
+                       video_unregister_device(dev->vbi_dev);
+               else
+                       video_device_release(dev->vbi_dev);
+               dev->vbi_dev = NULL;
+       }
+       if (dev->vdev) {
+               if (-1 != dev->vdev->minor)
+                       video_unregister_device(dev->vdev);
+               else
+                       video_device_release(dev->vdev);
+               dev->vdev = NULL;
+       }
        em28xx_i2c_unregister(dev);
        usb_put_dev(dev->udev);
 
-
        /* Mark device as unused */
        em28xx_devused&=~(1<<dev->devno);
 }
@@ -1606,6 +1743,15 @@ static const struct file_operations em28xx_v4l_fops = {
        .compat_ioctl  = v4l_compat_ioctl32,
 };
 
+static const struct file_operations radio_fops = {
+       .owner         = THIS_MODULE,
+       .open          = em28xx_v4l2_open,
+       .release       = em28xx_v4l2_close,
+       .ioctl         = video_ioctl2,
+       .compat_ioctl  = v4l_compat_ioctl32,
+       .llseek        = no_llseek,
+};
+
 static const struct video_device em28xx_video_template = {
        .fops                       = &em28xx_v4l_fops,
        .release                    = video_device_release,
@@ -1641,14 +1787,103 @@ static const struct video_device em28xx_video_template = {
        .vidioc_s_tuner             = vidioc_s_tuner,
        .vidioc_g_frequency         = vidioc_g_frequency,
        .vidioc_s_frequency         = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register          = vidioc_g_register,
+       .vidioc_s_register          = vidioc_s_register,
+#endif
 
        .tvnorms                    = V4L2_STD_ALL,
-       .current_norm               = V4L2_STD_NTSC_M,
+       .current_norm               = V4L2_STD_PAL,
 };
 
+static struct video_device em28xx_radio_template = {
+       .name                 = "em28xx-radio",
+       .type                 = VID_TYPE_TUNER,
+       .fops                 = &radio_fops,
+       .minor                = -1,
+       .vidioc_querycap      = radio_querycap,
+       .vidioc_g_tuner       = radio_g_tuner,
+       .vidioc_enum_input    = radio_enum_input,
+       .vidioc_g_audio       = radio_g_audio,
+       .vidioc_s_tuner       = radio_s_tuner,
+       .vidioc_s_audio       = radio_s_audio,
+       .vidioc_s_input       = radio_s_input,
+       .vidioc_queryctrl     = radio_queryctrl,
+       .vidioc_g_ctrl        = vidioc_g_ctrl,
+       .vidioc_s_ctrl        = vidioc_s_ctrl,
+       .vidioc_g_frequency   = vidioc_g_frequency,
+       .vidioc_s_frequency   = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register    = vidioc_g_register,
+       .vidioc_s_register    = vidioc_s_register,
+#endif
+};
 
 /******************************** 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 *h, *dev = NULL;
+
+       list_for_each_entry(h, &em28xx_devlist, devlist)
+               dev = h;
+
+       mutex_lock(&em28xx_extension_devlist_lock);
+       list_add_tail(&ops->next, &em28xx_extension_devlist);
+       if (dev)
+               ops->init(dev);
+
+       printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
+       mutex_unlock(&em28xx_extension_devlist_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(em28xx_register_extension);
+
+void em28xx_unregister_extension(struct em28xx_ops *ops)
+{
+       struct em28xx *h, *dev = NULL;
+
+       list_for_each_entry(h, &em28xx_devlist, devlist)
+               dev = h;
+
+       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);
+}
+EXPORT_SYMBOL(em28xx_unregister_extension);
+
+static struct video_device *em28xx_vdev_init(struct em28xx *dev,
+                                            const struct video_device *template,
+                                            const int type,
+                                            const char *type_name)
+{
+       struct video_device *vfd;
+
+       vfd = video_device_alloc();
+       if (NULL == vfd)
+               return NULL;
+       *vfd = *template;
+       vfd->minor   = -1;
+       vfd->dev = &dev->udev->dev;
+       vfd->release = video_device_release;
+       vfd->type = type;
+
+       snprintf(vfd->name, sizeof(vfd->name), "%s %s",
+                dev->name, type_name);
+
+       return vfd;
+}
+
+
 /*
  * em28xx_init_dev()
  * allocates and inits the device structs, registers i2c bus and v4l device
@@ -1656,9 +1891,10 @@ static const struct video_device em28xx_video_template = {
 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, i;
+       int errCode;
        unsigned int maxh, maxw;
 
        dev->udev = udev;
@@ -1675,6 +1911,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
        dev->em28xx_read_reg_req = em28xx_read_reg_req;
        dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
 
+       errCode = em28xx_read_reg(dev, CHIPID_REG);
+       if (errCode >= 0)
+               em28xx_info("em28xx chip ID = %d\n", errCode);
+
        em28xx_pre_card_setup(dev);
 
        errCode = em28xx_config(dev);
@@ -1691,18 +1931,14 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
        /* Do board specific init and eeprom reading */
        em28xx_card_setup(dev);
 
+       /* Configure audio */
+       em28xx_audio_analog_set(dev);
+
        /* configure the device */
        em28xx_config_i2c(dev);
 
-       for (i = 0; i < TVNORMS; i++)
-               if (em28xx_boards[dev->model].norm == tvnorms[i].mode)
-                       break;
-       if (i == TVNORMS)
-               i = 0;
-
-       dev->tvnorm = &tvnorms[i];      /* set default norm */
-
-       em28xx_videodbg("tvnorm=%s\n", dev->tvnorm->name);
+       /* set default norm */
+       dev->norm = em28xx_video_template.current_norm;
 
        maxw = norm_maxw(dev);
        maxh = norm_maxh(dev);
@@ -1721,42 +1957,55 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
 
        errCode = em28xx_config(dev);
 
+       list_add_tail(&dev->devlist, &em28xx_devlist);
+
        /* allocate and fill video video_device struct */
-       dev->vdev = video_device_alloc();
+       dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template,
+                                         VID_TYPE_CAPTURE, "video");
        if (NULL == dev->vdev) {
                em28xx_errdev("cannot allocate video_device.\n");
-               em28xx_devused&=~(1<<dev->devno);
-               kfree(dev);
-               return -ENOMEM;
+               goto fail_unreg;
        }
-       memcpy(dev->vdev, &em28xx_video_template,
-                                       sizeof(em28xx_video_template));
-       dev->vdev->type = VID_TYPE_CAPTURE;
-       if (dev->has_tuner)
+       if (dev->tuner_type != TUNER_ABSENT)
                dev->vdev->type |= VID_TYPE_TUNER;
-       dev->vdev->dev = &dev->udev->dev;
-       snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name),
-                "%s#%d %s", "em28xx", dev->devno, "video");
-       dev->vdev->current_norm = dev->tvnorm->id;
+
+       /* register v4l2 video video_device */
+       retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
+                                      video_nr[dev->devno]);
+       if (retval) {
+               em28xx_errdev("unable to register video device (error=%i).\n",
+                             retval);
+               goto fail_unreg;
+       }
 
        /* Allocate and fill vbi video_device struct */
-       dev->vbi_dev = video_device_alloc();
-       if (NULL == dev->vbi_dev) {
-               em28xx_errdev("cannot allocate video_device.\n");
-               kfree(dev->vdev);
-               em28xx_devused&=~(1<<dev->devno);
-               kfree(dev);
-               return -ENOMEM;
+       dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
+                                         VFL_TYPE_VBI, "vbi");
+       /* register v4l2 vbi video_device */
+       if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+                                       vbi_nr[dev->devno]) < 0) {
+               em28xx_errdev("unable to register vbi device\n");
+               retval = -ENODEV;
+               goto fail_unreg;
+       }
+
+       if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
+               dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
+                                       VFL_TYPE_RADIO, "radio");
+               if (NULL == dev->radio_dev) {
+                       em28xx_errdev("cannot allocate video_device.\n");
+                       goto fail_unreg;
+               }
+               retval = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+                                           radio_nr[dev->devno]);
+               if (retval < 0) {
+                       em28xx_errdev("can't register radio device\n");
+                       goto fail_unreg;
+               }
+               em28xx_info("Registered radio device as /dev/radio%d\n",
+                           dev->radio_dev->minor & 0x1f);
        }
-       memcpy(dev->vbi_dev, &em28xx_video_template,
-                                       sizeof(em28xx_video_template));
-       dev->vbi_dev->type = VFL_TYPE_VBI;
-       dev->vbi_dev->dev = &dev->udev->dev;
-       snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name),
-                               "%s#%d %s", "em28xx", dev->devno, "vbi");
-       dev->vbi_dev->current_norm = dev->tvnorm->id;
 
-       list_add_tail(&dev->devlist,&em28xx_devlist);
 
        if (dev->has_msp34xx) {
                /* Send a reset to other chips via gpio */
@@ -1768,38 +2017,48 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
 
        video_mux(dev, 0);
 
-       /* register v4l2 video video_device */
-       if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
-                                        video_nr[dev->devno]))) {
-               em28xx_errdev("unable to register video device (error=%i).\n",
-                             retval);
-               mutex_unlock(&dev->lock);
-               list_del(&dev->devlist);
-               video_device_release(dev->vdev);
-               em28xx_devused&=~(1<<dev->devno);
-               kfree(dev);
-               return -ENODEV;
-       }
-
-       /* register v4l2 vbi video_device */
-       if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
-                                       vbi_nr[dev->devno]) < 0) {
-               printk("unable to register vbi device\n");
-               mutex_unlock(&dev->lock);
-               list_del(&dev->devlist);
-               video_device_release(dev->vbi_dev);
-               video_device_release(dev->vdev);
-               em28xx_devused&=~(1<<dev->devno);
-               kfree(dev);
-               return -ENODEV;
-       }
-
        em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
                                dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
                                dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
 
+       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);
+
        return 0;
+
+fail_unreg:
+       em28xx_release_resources(dev);
+       mutex_unlock(&dev->lock);
+       kfree(dev);
+       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
+               request_module("em28xx-alsa");
+}
+
+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()
@@ -1871,6 +2130,18 @@ static int em28xx_usb_probe(struct usb_interface *interface,
        dev->devno = nr;
        dev->model = id->driver_info;
 
+       /* 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;
+               }
+       }
+
+       printk(KERN_INFO DRIVER_NAME " %s usb audio class\n",
+                  dev->has_audio_class ? "Has" : "Doesn't have");
+
        /* compute alternate max packet sizes */
        uif = udev->actconfig->interface[0];
 
@@ -1907,6 +2178,9 @@ static int em28xx_usb_probe(struct usb_interface *interface,
 
        /* save our data pointer in this interface device */
        usb_set_intfdata(interface, dev);
+
+       request_modules(dev);
+
        return 0;
 }
 
@@ -1918,6 +2192,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
 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);
@@ -1947,15 +2222,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
                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 = {
This page took 0.033397 seconds and 5 git commands to generate.