[media] v4l: Rename vb2_queue.timestamp_type as timestamp_flags
[linux-2.6-block.git] / drivers / media / usb / em28xx / em28xx-video.c
index dd19c9ff76e0f9a159c6630320814c89e9e83d5a..13466c47023c16efe6c3a63c3b6ab0efff6118fc 100644 (file)
 #include <linux/slab.h>
 
 #include "em28xx.h"
+#include "em28xx-v4l.h"
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-event.h>
+#include <media/v4l2-clk.h>
 #include <media/msp3400.h>
 #include <media/tuner.h>
 
                      "Mauro Carvalho Chehab <mchehab@infradead.org>, " \
                      "Sascha Sommer <saschasommer@freenet.de>"
 
-#define DRIVER_DESC         "Empia em28xx based USB video device driver"
+static unsigned int isoc_debug;
+module_param(isoc_debug, int, 0644);
+MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
+
+static unsigned int disable_vbi;
+module_param(disable_vbi, int, 0644);
+MODULE_PARM_DESC(disable_vbi, "disable vbi support");
 
-#define EM28XX_VERSION "0.2.0"
+static int alt;
+module_param(alt, int, 0644);
+MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
 
 #define em28xx_videodbg(fmt, arg...) do {\
        if (video_debug) \
                printk(KERN_INFO "%s %s :"fmt, \
                         dev->name, __func__ , ##arg); } while (0)
 
-static unsigned int isoc_debug;
-module_param(isoc_debug, int, 0644);
-MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
-
 #define em28xx_isocdbg(fmt, arg...) \
 do {\
        if (isoc_debug) { \
@@ -71,7 +77,7 @@ do {\
   } while (0)
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_DESCRIPTION(DRIVER_DESC " - v4l2 interface");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(EM28XX_VERSION);
 
@@ -135,6 +141,257 @@ static struct em28xx_fmt format[] = {
        },
 };
 
+static int em28xx_vbi_supported(struct em28xx *dev)
+{
+       /* Modprobe option to manually disable */
+       if (disable_vbi == 1)
+               return 0;
+
+       if (dev->board.is_webcam)
+               return 0;
+
+       /* FIXME: check subdevices for VBI support */
+
+       if (dev->chip_id == CHIP_ID_EM2860 ||
+           dev->chip_id == CHIP_ID_EM2883)
+               return 1;
+
+       /* Version of em28xx that does not support VBI */
+       return 0;
+}
+
+/*
+ * em28xx_wake_i2c()
+ * configure i2c attached devices
+ */
+static void em28xx_wake_i2c(struct em28xx *dev)
+{
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core,  reset, 0);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+                       INPUT(dev->ctl_input)->vmux, 0, 0);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+}
+
+static int em28xx_colorlevels_set_default(struct em28xx *dev)
+{
+       em28xx_write_reg(dev, EM28XX_R20_YGAIN, CONTRAST_DEFAULT);
+       em28xx_write_reg(dev, EM28XX_R21_YOFFSET, BRIGHTNESS_DEFAULT);
+       em28xx_write_reg(dev, EM28XX_R22_UVGAIN, SATURATION_DEFAULT);
+       em28xx_write_reg(dev, EM28XX_R23_UOFFSET, BLUE_BALANCE_DEFAULT);
+       em28xx_write_reg(dev, EM28XX_R24_VOFFSET, RED_BALANCE_DEFAULT);
+       em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, SHARPNESS_DEFAULT);
+
+       em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20);
+       em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20);
+       em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20);
+       em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20);
+       em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00);
+       em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00);
+       return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00);
+}
+
+static int em28xx_set_outfmt(struct em28xx *dev)
+{
+       int ret;
+       u8 fmt, vinctrl;
+
+       fmt = dev->format->reg;
+       if (!dev->is_em25xx)
+               fmt |= 0x20;
+       /*
+        * NOTE: it's not clear if this is really needed !
+        * The datasheets say bit 5 is a reserved bit and devices seem to work
+        * fine without it. But the Windows driver sets it for em2710/50+em28xx
+        * devices and we've always been setting it, too.
+        *
+        * em2765 (em25xx, em276x/7x/8x) devices do NOT work with this bit set,
+        * it's likely used for an additional (compressed ?) format there.
+        */
+       ret = em28xx_write_reg(dev, EM28XX_R27_OUTFMT, fmt);
+       if (ret < 0)
+               return ret;
+
+       ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode);
+       if (ret < 0)
+               return ret;
+
+       vinctrl = dev->vinctl;
+       if (em28xx_vbi_supported(dev) == 1) {
+               vinctrl |= EM28XX_VINCTRL_VBI_RAW;
+               em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
+               em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4);
+               em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height);
+               if (dev->norm & V4L2_STD_525_60) {
+                       /* NTSC */
+                       em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
+               } else if (dev->norm & V4L2_STD_625_50) {
+                       /* PAL */
+                       em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07);
+               }
+       }
+
+       return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl);
+}
+
+static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
+                                 u8 ymin, u8 ymax)
+{
+       em28xx_videodbg("em28xx Scale: (%d,%d)-(%d,%d)\n",
+                       xmin, ymin, xmax, ymax);
+
+       em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1);
+       em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1);
+       em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1);
+       return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1);
+}
+
+static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart,
+                                  u16 width, u16 height)
+{
+       u8 cwidth = width >> 2;
+       u8 cheight = height >> 2;
+       u8 overflow = (height >> 9 & 0x02) | (width >> 10 & 0x01);
+       /* NOTE: size limit: 2047x1023 = 2MPix */
+
+       em28xx_videodbg("capture area set to (%d,%d): %dx%d\n",
+                      hstart, vstart,
+                      ((overflow & 2) << 9 | cwidth << 2),
+                      ((overflow & 1) << 10 | cheight << 2));
+
+       em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1);
+       em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1);
+       em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1);
+       em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1);
+       em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1);
+
+       /* FIXME: function/meaning of these registers ? */
+       /* FIXME: align width+height to multiples of 4 ?! */
+       if (dev->is_em25xx) {
+               em28xx_write_reg(dev, 0x34, width >> 4);
+               em28xx_write_reg(dev, 0x35, height >> 4);
+       }
+}
+
+static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
+{
+       u8 mode;
+       /* the em2800 scaler only supports scaling down to 50% */
+
+       if (dev->board.is_em2800) {
+               mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00);
+       } else {
+               u8 buf[2];
+
+               buf[0] = h;
+               buf[1] = h >> 8;
+               em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2);
+
+               buf[0] = v;
+               buf[1] = v >> 8;
+               em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2);
+               /* it seems that both H and V scalers must be active
+                  to work correctly */
+               mode = (h || v) ? 0x30 : 0x00;
+       }
+       return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30);
+}
+
+/* FIXME: this only function read values from dev */
+static int em28xx_resolution_set(struct em28xx *dev)
+{
+       int width, height;
+       width = norm_maxw(dev);
+       height = norm_maxh(dev);
+
+       /* Properly setup VBI */
+       dev->vbi_width = 720;
+       if (dev->norm & V4L2_STD_525_60)
+               dev->vbi_height = 12;
+       else
+               dev->vbi_height = 18;
+
+       em28xx_set_outfmt(dev);
+
+       em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
+
+       /* If we don't set the start position to 2 in VBI mode, we end up
+          with line 20/21 being YUYV encoded instead of being in 8-bit
+          greyscale.  The core of the issue is that line 21 (and line 23 for
+          PAL WSS) are inside of active video region, and as a result they
+          get the pixelformatting associated with that area.  So by cropping
+          it out, we end up with the same format as the rest of the VBI
+          region */
+       if (em28xx_vbi_supported(dev) == 1)
+               em28xx_capture_area_set(dev, 0, 2, width, height);
+       else
+               em28xx_capture_area_set(dev, 0, 0, width, height);
+
+       return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
+}
+
+/* Set USB alternate setting for analog video */
+static int em28xx_set_alternate(struct em28xx *dev)
+{
+       int errCode;
+       int i;
+       unsigned int min_pkt_size = dev->width * 2 + 4;
+
+       /* NOTE: for isoc transfers, only alt settings > 0 are allowed
+                bulk transfers seem to work only with alt=0 ! */
+       dev->alt = 0;
+       if ((alt > 0) && (alt < dev->num_alt)) {
+               em28xx_videodbg("alternate forced to %d\n", dev->alt);
+               dev->alt = alt;
+               goto set_alt;
+       }
+       if (dev->analog_xfer_bulk)
+               goto set_alt;
+
+       /* When image size is bigger than a certain value,
+          the frame size should be increased, otherwise, only
+          green screen will be received.
+        */
+       if (dev->width * 2 * dev->height > 720 * 240 * 2)
+               min_pkt_size *= 2;
+
+       for (i = 0; i < dev->num_alt; i++) {
+               /* stop when the selected alt setting offers enough bandwidth */
+               if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) {
+                       dev->alt = i;
+                       break;
+               /* otherwise make sure that we end up with the maximum bandwidth
+                  because the min_pkt_size equation might be wrong...
+               */
+               } else if (dev->alt_max_pkt_size_isoc[i] >
+                          dev->alt_max_pkt_size_isoc[dev->alt])
+                       dev->alt = i;
+       }
+
+set_alt:
+       /* NOTE: for bulk transfers, we need to call usb_set_interface()
+        * even if the previous settings were the same. Otherwise streaming
+        * fails with all urbs having status = -EOVERFLOW ! */
+       if (dev->analog_xfer_bulk) {
+               dev->max_pkt_size = 512; /* USB 2.0 spec */
+               dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER;
+       } else { /* isoc */
+               em28xx_videodbg("minimum isoc packet size: %u (alt=%d)\n",
+                              min_pkt_size, dev->alt);
+               dev->max_pkt_size =
+                                 dev->alt_max_pkt_size_isoc[dev->alt];
+               dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS;
+       }
+       em28xx_videodbg("setting alternate %d with wMaxPacketSize=%u\n",
+                      dev->alt, dev->max_pkt_size);
+       errCode = usb_set_interface(dev->udev, dev->ifnum, dev->alt);
+       if (errCode < 0) {
+               em28xx_errdev("cannot change alternate number to %d (error=%i)\n",
+                             dev->alt, errCode);
+               return errCode;
+       }
+       return 0;
+}
+
 /* ------------------------------------------------------------------
        DMA and thread functions
    ------------------------------------------------------------------*/
@@ -763,7 +1020,7 @@ static struct vb2_ops em28xx_video_qops = {
        .wait_finish    = vb2_ops_wait_finish,
 };
 
-int em28xx_vb2_setup(struct em28xx *dev)
+static int em28xx_vb2_setup(struct em28xx *dev)
 {
        int rc;
        struct vb2_queue *q;
@@ -772,7 +1029,7 @@ int em28xx_vb2_setup(struct em28xx *dev)
        q = &dev->vb_vidq;
        q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
-       q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->drv_priv = dev;
        q->buf_struct_size = sizeof(struct em28xx_buffer);
        q->ops = &em28xx_video_qops;
@@ -786,7 +1043,7 @@ int em28xx_vb2_setup(struct em28xx *dev)
        q = &dev->vb_vbiq;
        q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
        q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
-       q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->drv_priv = dev;
        q->buf_struct_size = sizeof(struct em28xx_buffer);
        q->ops = &em28xx_vbi_qops;
@@ -831,7 +1088,7 @@ static void video_mux(struct em28xx *dev, int index)
        em28xx_audio_analog_set(dev);
 }
 
-void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv)
+static void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv)
 {
        struct em28xx *dev = priv;
 
@@ -890,7 +1147,7 @@ static int em28xx_s_ctrl(struct v4l2_ctrl *ctrl)
        return (ret < 0) ? ret : 0;
 }
 
-const struct v4l2_ctrl_ops em28xx_ctrl_ops = {
+static const struct v4l2_ctrl_ops em28xx_ctrl_ops = {
        .s_ctrl = em28xx_s_ctrl,
 };
 
@@ -1368,7 +1625,7 @@ static int vidioc_g_register(struct file *file, void *priv,
                reg->val = ret;
        } else {
                __le16 val = 0;
-               ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
+               ret = dev->em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
                                                   reg->reg, (char *)&val, 2);
                if (ret < 0)
                        return ret;
@@ -1570,6 +1827,10 @@ static int em28xx_v4l2_open(struct file *filp)
        case VFL_TYPE_VBI:
                fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
                break;
+       case VFL_TYPE_RADIO:
+               break;
+       default:
+               return -EINVAL;
        }
 
        em28xx_videodbg("open dev=%s type=%s users=%d\n",
@@ -1590,15 +1851,17 @@ static int em28xx_v4l2_open(struct file *filp)
        fh->type = fh_type;
        filp->private_data = fh;
 
-       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
+       if (dev->users == 0) {
                em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
-               em28xx_resolution_set(dev);
 
-               /* Needed, since GPIO might have disabled power of
-                  some i2c device
+               if (vdev->vfl_type != VFL_TYPE_RADIO)
+                       em28xx_resolution_set(dev);
+
+               /*
+                * Needed, since GPIO might have disabled power
+                * of some i2c devices
                 */
                em28xx_wake_i2c(dev);
-
        }
 
        if (vdev->vfl_type == VFL_TYPE_RADIO) {
@@ -1615,40 +1878,85 @@ static int em28xx_v4l2_open(struct file *filp)
 }
 
 /*
- * em28xx_realease_resources()
+ * em28xx_v4l2_fini()
  * unregisters the v4l2,i2c and usb devices
  * called when the device gets disconected or at module unload
 */
-void em28xx_release_analog_resources(struct em28xx *dev)
+static int em28xx_v4l2_fini(struct em28xx *dev)
 {
+       if (dev->is_audio_only) {
+               /* Shouldn't initialize IR for this interface */
+               return 0;
+       }
 
-       /*FIXME: I2C IR should be disconnected */
+       if (!dev->has_video) {
+               /* This device does not support the v4l2 extension */
+               return 0;
+       }
+
+       em28xx_info("Closing video extension");
+
+       mutex_lock(&dev->lock);
+
+       v4l2_device_disconnect(&dev->v4l2_dev);
+
+       em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
 
        if (dev->radio_dev) {
-               if (video_is_registered(dev->radio_dev))
-                       video_unregister_device(dev->radio_dev);
-               else
-                       video_device_release(dev->radio_dev);
-               dev->radio_dev = NULL;
+               em28xx_info("V4L2 device %s deregistered\n",
+                           video_device_node_name(dev->radio_dev));
+               video_unregister_device(dev->radio_dev);
        }
        if (dev->vbi_dev) {
                em28xx_info("V4L2 device %s deregistered\n",
                            video_device_node_name(dev->vbi_dev));
-               if (video_is_registered(dev->vbi_dev))
-                       video_unregister_device(dev->vbi_dev);
-               else
-                       video_device_release(dev->vbi_dev);
-               dev->vbi_dev = NULL;
+               video_unregister_device(dev->vbi_dev);
        }
        if (dev->vdev) {
                em28xx_info("V4L2 device %s deregistered\n",
                            video_device_node_name(dev->vdev));
-               if (video_is_registered(dev->vdev))
-                       video_unregister_device(dev->vdev);
-               else
-                       video_device_release(dev->vdev);
-               dev->vdev = NULL;
+               video_unregister_device(dev->vdev);
+       }
+
+       v4l2_ctrl_handler_free(&dev->ctrl_handler);
+       v4l2_device_unregister(&dev->v4l2_dev);
+
+       if (dev->clk) {
+               v4l2_clk_unregister_fixed(dev->clk);
+               dev->clk = NULL;
        }
+
+       if (dev->users)
+               em28xx_warn("Device is open ! Memory deallocation is deferred on last close.\n");
+       mutex_unlock(&dev->lock);
+
+       return 0;
+}
+
+static int em28xx_v4l2_suspend(struct em28xx *dev)
+{
+       if (dev->is_audio_only)
+               return 0;
+
+       if (!dev->has_video)
+               return 0;
+
+       em28xx_info("Suspending video extension");
+       em28xx_stop_urbs(dev);
+       return 0;
+}
+
+static int em28xx_v4l2_resume(struct em28xx *dev)
+{
+       if (dev->is_audio_only)
+               return 0;
+
+       if (!dev->has_video)
+               return 0;
+
+       em28xx_info("Resuming video extension");
+       /* what do we do here */
+       return 0;
 }
 
 /*
@@ -1668,14 +1976,10 @@ static int em28xx_v4l2_close(struct file *filp)
        mutex_lock(&dev->lock);
 
        if (dev->users == 1) {
-               /* the device is already disconnect,
-                  free the remaining resources */
+               /* free the remaining resources if device is disconnected */
                if (dev->disconnected) {
-                       em28xx_release_resources(dev);
                        kfree(dev->alt_max_pkt_size_isoc);
-                       mutex_unlock(&dev->lock);
-                       kfree(dev);
-                       return 0;
+                       goto exit;
                }
 
                /* Save some power by putting tuner to sleep */
@@ -1694,11 +1998,29 @@ static int em28xx_v4l2_close(struct file *filp)
                }
        }
 
+exit:
        dev->users--;
        mutex_unlock(&dev->lock);
        return 0;
 }
 
+/*
+ * em28xx_videodevice_release()
+ * called when the last user of the video device exits and frees the memeory
+ */
+static void em28xx_videodevice_release(struct video_device *vdev)
+{
+       struct em28xx *dev = video_get_drvdata(vdev);
+
+       video_device_release(vdev);
+       if (vdev == dev->vdev)
+               dev->vdev = NULL;
+       else if (vdev == dev->vbi_dev)
+               dev->vbi_dev = NULL;
+       else if (vdev == dev->radio_dev)
+               dev->radio_dev = NULL;
+}
+
 static const struct v4l2_file_operations em28xx_v4l_fops = {
        .owner         = THIS_MODULE,
        .open          = em28xx_v4l2_open,
@@ -1753,11 +2075,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
 };
 
 static const struct video_device em28xx_video_template = {
-       .fops                       = &em28xx_v4l_fops,
-       .release                    = video_device_release_empty,
-       .ioctl_ops                  = &video_ioctl_ops,
-
-       .tvnorms                    = V4L2_STD_ALL,
+       .fops           = &em28xx_v4l_fops,
+       .ioctl_ops      = &video_ioctl_ops,
+       .release        = em28xx_videodevice_release,
+       .tvnorms        = V4L2_STD_ALL,
 };
 
 static const struct v4l2_file_operations radio_fops = {
@@ -1783,14 +2104,30 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = {
 };
 
 static struct video_device em28xx_radio_template = {
-       .name                 = "em28xx-radio",
-       .fops                 = &radio_fops,
-       .ioctl_ops            = &radio_ioctl_ops,
+       .fops           = &radio_fops,
+       .ioctl_ops      = &radio_ioctl_ops,
+       .release        = em28xx_videodevice_release,
 };
 
-/******************************** usb interface ******************************/
+/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
+static unsigned short saa711x_addrs[] = {
+       0x4a >> 1, 0x48 >> 1,   /* SAA7111, SAA7111A and SAA7113 */
+       0x42 >> 1, 0x40 >> 1,   /* SAA7114, SAA7115 and SAA7118 */
+       I2C_CLIENT_END };
 
+static unsigned short tvp5150_addrs[] = {
+       0xb8 >> 1,
+       0xba >> 1,
+       I2C_CLIENT_END
+};
+
+static unsigned short msp3400_addrs[] = {
+       0x80 >> 1,
+       0x88 >> 1,
+       I2C_CLIENT_END
+};
 
+/******************************** usb interface ******************************/
 
 static struct video_device *em28xx_vdev_init(struct em28xx *dev,
                                        const struct video_device *template,
@@ -1817,14 +2154,199 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
        return vfd;
 }
 
-int em28xx_register_analog_devices(struct em28xx *dev)
+static void em28xx_tuner_setup(struct em28xx *dev)
+{
+       struct tuner_setup           tun_setup;
+       struct v4l2_frequency        f;
+
+       if (dev->tuner_type == TUNER_ABSENT)
+               return;
+
+       memset(&tun_setup, 0, sizeof(tun_setup));
+
+       tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+       tun_setup.tuner_callback = em28xx_tuner_callback;
+
+       if (dev->board.radio.type) {
+               tun_setup.type = dev->board.radio.type;
+               tun_setup.addr = dev->board.radio_addr;
+
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+       }
+
+       if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) {
+               tun_setup.type   = dev->tuner_type;
+               tun_setup.addr   = dev->tuner_addr;
+
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+       }
+
+       if (dev->tda9887_conf) {
+               struct v4l2_priv_tun_config tda9887_cfg;
+
+               tda9887_cfg.tuner = TUNER_TDA9887;
+               tda9887_cfg.priv = &dev->tda9887_conf;
+
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg);
+       }
+
+       if (dev->tuner_type == TUNER_XC2028) {
+               struct v4l2_priv_tun_config  xc2028_cfg;
+               struct xc2028_ctrl           ctl;
+
+               memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+               memset(&ctl, 0, sizeof(ctl));
+
+               em28xx_setup_xc3028(dev, &ctl);
+
+               xc2028_cfg.tuner = TUNER_XC2028;
+               xc2028_cfg.priv  = &ctl;
+
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
+       }
+
+       /* configure tuner */
+       f.tuner = 0;
+       f.type = V4L2_TUNER_ANALOG_TV;
+       f.frequency = 9076;     /* just a magic number */
+       dev->ctl_freq = f.frequency;
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+}
+
+static int em28xx_v4l2_init(struct em28xx *dev)
 {
        u8 val;
        int ret;
        unsigned int maxw;
+       struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+
+       if (dev->is_audio_only) {
+               /* Shouldn't initialize IR for this interface */
+               return 0;
+       }
+
+       if (!dev->has_video) {
+               /* This device does not support the v4l2 extension */
+               return 0;
+       }
+
+       em28xx_info("Registering V4L2 extension\n");
+
+       mutex_lock(&dev->lock);
+
+       ret = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
+       if (ret < 0) {
+               em28xx_errdev("Call to v4l2_device_register() failed!\n");
+               goto err;
+       }
+
+       v4l2_ctrl_handler_init(hdl, 8);
+       dev->v4l2_dev.ctrl_handler = hdl;
+
+       /*
+        * Default format, used for tvp5150 or saa711x output formats
+        */
+       dev->vinmode = 0x10;
+       dev->vinctl  = EM28XX_VINCTRL_INTERLACED |
+                      EM28XX_VINCTRL_CCIR656_ENABLE;
+
+       /* request some modules */
+
+       if (dev->board.has_msp34xx)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
+                       "msp3400", 0, msp3400_addrs);
+
+       if (dev->board.decoder == EM28XX_SAA711X)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
+                       "saa7115_auto", 0, saa711x_addrs);
+
+       if (dev->board.decoder == EM28XX_TVP5150)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
+                       "tvp5150", 0, tvp5150_addrs);
+
+       if (dev->board.adecoder == EM28XX_TVAUDIO)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
+                       "tvaudio", dev->board.tvaudio_addr, NULL);
+
+       /* Initialize tuner and camera */
+
+       if (dev->board.tuner_type != TUNER_ABSENT) {
+               int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
+
+               if (dev->board.radio.type)
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
+                               "tuner", dev->board.radio_addr, NULL);
+
+               if (has_demod)
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                               &dev->i2c_adap[dev->def_i2c_bus], "tuner",
+                               0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+               if (dev->tuner_addr == 0) {
+                       enum v4l2_i2c_tuner_type type =
+                               has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
+                       struct v4l2_subdev *sd;
+
+                       sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                               &dev->i2c_adap[dev->def_i2c_bus], "tuner",
+                               0, v4l2_i2c_tuner_addrs(type));
+
+                       if (sd)
+                               dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
+               } else {
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
+                               "tuner", dev->tuner_addr, NULL);
+               }
+       }
+
+       em28xx_tuner_setup(dev);
+       if (dev->em28xx_sensor != EM28XX_NOSENSOR)
+               em28xx_init_camera(dev);
+
+       /* Configure audio */
+       ret = em28xx_audio_setup(dev);
+       if (ret < 0) {
+               em28xx_errdev("%s: Error while setting audio - error [%d]!\n",
+                       __func__, ret);
+               goto unregister_dev;
+       }
+       if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+               v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+               v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
+                       V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f);
+       } else {
+               /* install the em28xx notify callback */
+               v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE),
+                               em28xx_ctrl_notify, dev);
+               v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME),
+                               em28xx_ctrl_notify, dev);
+       }
 
-       printk(KERN_INFO "%s: v4l2 driver version %s\n",
-               dev->name, EM28XX_VERSION);
+       /* wake i2c devices */
+       em28xx_wake_i2c(dev);
+
+       /* init video dma queues */
+       INIT_LIST_HEAD(&dev->vidq.active);
+       INIT_LIST_HEAD(&dev->vbiq.active);
+
+       if (dev->board.has_msp34xx) {
+               /* Send a reset to other chips via gpio */
+               ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7);
+               if (ret < 0) {
+                       em28xx_errdev("%s: em28xx_write_reg - msp34xx(1) failed! error [%d]\n",
+                                     __func__, ret);
+                       goto unregister_dev;
+               }
+               msleep(3);
+
+               ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff);
+               if (ret < 0) {
+                       em28xx_errdev("%s: em28xx_write_reg - msp34xx(2) failed! error [%d]\n",
+                                     __func__, ret);
+                       goto unregister_dev;
+               }
+               msleep(3);
+       }
 
        /* set default norm */
        dev->norm = V4L2_STD_PAL;
@@ -1888,14 +2410,16 @@ int em28xx_register_analog_devices(struct em28xx *dev)
        /* Reset image controls */
        em28xx_colorlevels_set_default(dev);
        v4l2_ctrl_handler_setup(&dev->ctrl_handler);
-       if (dev->ctrl_handler.error)
-               return dev->ctrl_handler.error;
+       ret = dev->ctrl_handler.error;
+       if (ret)
+               goto unregister_dev;
 
        /* allocate and fill video video_device struct */
        dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
        if (!dev->vdev) {
                em28xx_errdev("cannot allocate video_device.\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto unregister_dev;
        }
        dev->vdev->queue = &dev->vb_vidq;
        dev->vdev->queue->lock = &dev->vb_queue_lock;
@@ -1925,7 +2449,7 @@ int em28xx_register_analog_devices(struct em28xx *dev)
        if (ret) {
                em28xx_errdev("unable to register video device (error=%i).\n",
                              ret);
-               return ret;
+               goto unregister_dev;
        }
 
        /* Allocate and fill vbi video_device struct */
@@ -1954,7 +2478,7 @@ int em28xx_register_analog_devices(struct em28xx *dev)
                                            vbi_nr[dev->devno]);
                if (ret < 0) {
                        em28xx_errdev("unable to register vbi device\n");
-                       return ret;
+                       goto unregister_dev;
                }
        }
 
@@ -1963,13 +2487,14 @@ int em28xx_register_analog_devices(struct em28xx *dev)
                                                  "radio");
                if (!dev->radio_dev) {
                        em28xx_errdev("cannot allocate video_device.\n");
-                       return -ENODEV;
+                       ret = -ENODEV;
+                       goto unregister_dev;
                }
                ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
                                            radio_nr[dev->devno]);
                if (ret < 0) {
                        em28xx_errdev("can't register radio device\n");
-                       return ret;
+                       goto unregister_dev;
                }
                em28xx_info("Registered radio device as %s\n",
                            video_device_node_name(dev->radio_dev));
@@ -1982,5 +2507,43 @@ int em28xx_register_analog_devices(struct em28xx *dev)
                em28xx_info("V4L2 VBI device registered as %s\n",
                            video_device_node_name(dev->vbi_dev));
 
+       /* Save some power by putting tuner to sleep */
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+
+       /* initialize videobuf2 stuff */
+       em28xx_vb2_setup(dev);
+
+       em28xx_info("V4L2 extension successfully initialized\n");
+
+       mutex_unlock(&dev->lock);
        return 0;
+
+unregister_dev:
+       v4l2_ctrl_handler_free(&dev->ctrl_handler);
+       v4l2_device_unregister(&dev->v4l2_dev);
+err:
+       mutex_unlock(&dev->lock);
+       return ret;
 }
+
+static struct em28xx_ops v4l2_ops = {
+       .id   = EM28XX_V4L2,
+       .name = "Em28xx v4l2 Extension",
+       .init = em28xx_v4l2_init,
+       .fini = em28xx_v4l2_fini,
+       .suspend = em28xx_v4l2_suspend,
+       .resume = em28xx_v4l2_resume,
+};
+
+static int __init em28xx_video_register(void)
+{
+       return em28xx_register_extension(&v4l2_ops);
+}
+
+static void __exit em28xx_video_unregister(void)
+{
+       em28xx_unregister_extension(&v4l2_ops);
+}
+
+module_init(em28xx_video_register);
+module_exit(em28xx_video_unregister);