V4L/DVB (6956): Add Radio support for em28xx
authorMauro Carvalho Chehab <mchehab@infradead.org>
Sat, 5 Jan 2008 20:22:01 +0000 (17:22 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Fri, 25 Jan 2008 21:04:34 +0000 (19:04 -0200)
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/em28xx/em28xx.h

index 0f075f532eae1d24eea97222b80352e4bd201606..5a90462d82e552bd88f8294b9662328bf75d36a0 100644 (file)
@@ -61,13 +61,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);
@@ -791,7 +795,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;
@@ -811,7 +815,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);
@@ -1148,6 +1154,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
@@ -1155,7 +1257,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;
 
@@ -1168,6 +1270,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;
@@ -1183,6 +1290,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) {
@@ -1207,6 +1315,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++;
 
@@ -1229,12 +1341,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);
 }
@@ -1555,6 +1685,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,
@@ -1595,6 +1734,25 @@ static const struct video_device em28xx_video_template = {
        .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,
+};
+
 /******************************** usb interface *****************************************/
 
 
@@ -1637,6 +1795,29 @@ void em28xx_unregister_extension(struct em28xx_ops *ops)
 }
 EXPORT_SYMBOL(em28xx_unregister_extension);
 
+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
@@ -1710,40 +1891,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)
                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");
+
+       /* 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");
 
-       list_add_tail(&dev->devlist,&em28xx_devlist);
 
        if (dev->has_msp34xx) {
                /* Send a reset to other chips via gpio */
@@ -1755,32 +1951,6 @@ 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);
@@ -1795,6 +1965,12 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
        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)
index 2d57330c453756af8981f23c91d4f7cda7e37f48..2ba34e5b4cc25f47e9de65d87635dfebe928dcf1 100644 (file)
@@ -191,6 +191,7 @@ struct em28xx_board {
        enum em28xx_decoder decoder;
 
        struct em28xx_input       input[MAX_EM28XX_INPUT];
+       struct em28xx_input       radio;
 };
 
 struct em28xx_eeprom {
@@ -307,6 +308,7 @@ struct em28xx {
        struct list_head inqueue, outqueue;
        wait_queue_head_t open, wait_frame, wait_stream;
        struct video_device *vbi_dev;
+       struct video_device *radio_dev;
 
        unsigned char eedata[256];