V4L/DVB: TVP7002 driver for DM365
[safe/jmp/linux-2.6] / drivers / media / video / ov772x.c
index 3ea650d..7f8ece3 100644 (file)
@@ -22,8 +22,9 @@
 #include <linux/delay.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
 #include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
 #include <media/ov772x.h>
 
 /*
 
 /* COM5 */
 #define AFR_ON_OFF      0x80   /* Auto frame rate control ON/OFF selection */
-#define AFR_SPPED       0x40   /* Auto frame rate control speed slection */
+#define AFR_SPPED       0x40   /* Auto frame rate control speed selection */
                                /* Auto frame rate max rate control */
 #define AFR_NO_RATE     0x00   /*     No  reduction of frame rate */
 #define AFR_1p2         0x10   /*     Max reduction to 1/2 frame rate */
@@ -382,11 +383,11 @@ struct regval_list {
 };
 
 struct ov772x_color_format {
-       char                     *name;
-       __u32                     fourcc;
-       u8                        dsp3;
-       u8                        com3;
-       u8                        com7;
+       enum v4l2_mbus_pixelcode code;
+       enum v4l2_colorspace colorspace;
+       u8 dsp3;
+       u8 com3;
+       u8 com7;
 };
 
 struct ov772x_win_size {
@@ -398,12 +399,15 @@ struct ov772x_win_size {
 };
 
 struct ov772x_priv {
+       struct v4l2_subdev                subdev;
        struct ov772x_camera_info        *info;
-       const struct ov772x_color_format *fmt;
+       const struct ov772x_color_format *cfmt;
        const struct ov772x_win_size     *win;
        int                               model;
-       unsigned int                      flag_vflip:1;
-       unsigned int                      flag_hflip:1;
+       unsigned short                    flag_vflip:1;
+       unsigned short                    flag_hflip:1;
+       /* band_filter = COM8[5] ? 256 - BDBASE : 0 */
+       unsigned short                    band_filter;
 };
 
 #define ENDMARKER { 0xff, 0xff }
@@ -432,93 +436,57 @@ static const struct regval_list ov772x_vga_regs[] = {
 };
 
 /*
- * supported format list
- */
-
-#define SETFOURCC(type) .name = (#type), .fourcc = (V4L2_PIX_FMT_ ## type)
-static const struct soc_camera_data_format ov772x_fmt_lists[] = {
-       {
-               SETFOURCC(YUYV),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_JPEG,
-       },
-       {
-               SETFOURCC(YVYU),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_JPEG,
-       },
-       {
-               SETFOURCC(UYVY),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_JPEG,
-       },
-       {
-               SETFOURCC(RGB555),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_SRGB,
-       },
-       {
-               SETFOURCC(RGB555X),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_SRGB,
-       },
-       {
-               SETFOURCC(RGB565),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_SRGB,
-       },
-       {
-               SETFOURCC(RGB565X),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_SRGB,
-       },
-};
-
-/*
- * color format list
+ * supported color format list
  */
 static const struct ov772x_color_format ov772x_cfmts[] = {
        {
-               SETFOURCC(YUYV),
-               .dsp3   = 0x0,
-               .com3   = SWAP_YUV,
-               .com7   = OFMT_YUV,
+               .code           = V4L2_MBUS_FMT_YUYV8_2X8_LE,
+               .colorspace     = V4L2_COLORSPACE_JPEG,
+               .dsp3           = 0x0,
+               .com3           = SWAP_YUV,
+               .com7           = OFMT_YUV,
        },
        {
-               SETFOURCC(YVYU),
-               .dsp3   = UV_ON,
-               .com3   = SWAP_YUV,
-               .com7   = OFMT_YUV,
+               .code           = V4L2_MBUS_FMT_YVYU8_2X8_LE,
+               .colorspace     = V4L2_COLORSPACE_JPEG,
+               .dsp3           = UV_ON,
+               .com3           = SWAP_YUV,
+               .com7           = OFMT_YUV,
        },
        {
-               SETFOURCC(UYVY),
-               .dsp3   = 0x0,
-               .com3   = 0x0,
-               .com7   = OFMT_YUV,
+               .code           = V4L2_MBUS_FMT_YUYV8_2X8_BE,
+               .colorspace     = V4L2_COLORSPACE_JPEG,
+               .dsp3           = 0x0,
+               .com3           = 0x0,
+               .com7           = OFMT_YUV,
        },
        {
-               SETFOURCC(RGB555),
-               .dsp3   = 0x0,
-               .com3   = SWAP_RGB,
-               .com7   = FMT_RGB555 | OFMT_RGB,
+               .code           = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+               .dsp3           = 0x0,
+               .com3           = SWAP_RGB,
+               .com7           = FMT_RGB555 | OFMT_RGB,
        },
        {
-               SETFOURCC(RGB555X),
-               .dsp3   = 0x0,
-               .com3   = 0x0,
-               .com7   = FMT_RGB555 | OFMT_RGB,
+               .code           = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+               .dsp3           = 0x0,
+               .com3           = 0x0,
+               .com7           = FMT_RGB555 | OFMT_RGB,
        },
        {
-               SETFOURCC(RGB565),
-               .dsp3   = 0x0,
-               .com3   = SWAP_RGB,
-               .com7   = FMT_RGB565 | OFMT_RGB,
+               .code           = V4L2_MBUS_FMT_RGB565_2X8_LE,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+               .dsp3           = 0x0,
+               .com3           = SWAP_RGB,
+               .com7           = FMT_RGB565 | OFMT_RGB,
        },
        {
-               SETFOURCC(RGB565X),
-               .dsp3   = 0x0,
-               .com3   = 0x0,
-               .com7   = FMT_RGB565 | OFMT_RGB,
+               .code           = V4L2_MBUS_FMT_RGB565_2X8_BE,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+               .dsp3           = 0x0,
+               .com3           = 0x0,
+               .com7           = FMT_RGB565 | OFMT_RGB,
        },
 };
 
@@ -568,13 +536,27 @@ static const struct v4l2_queryctrl ov772x_controls[] = {
                .step           = 1,
                .default_value  = 0,
        },
+       {
+               .id             = V4L2_CID_BAND_STOP_FILTER,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "Band-stop filter",
+               .minimum        = 0,
+               .maximum        = 256,
+               .step           = 1,
+               .default_value  = 0,
+       },
 };
 
-
 /*
  * general function
  */
 
+static struct ov772x_priv *to_ov772x(const struct i2c_client *client)
+{
+       return container_of(i2c_get_clientdata(client), struct ov772x_priv,
+                           subdev);
+}
+
 static int ov772x_write_array(struct i2c_client        *client,
                              const struct regval_list *vals)
 {
@@ -615,61 +597,29 @@ static int ov772x_reset(struct i2c_client *client)
  * soc_camera_ops function
  */
 
-static int ov772x_init(struct soc_camera_device *icd)
+static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct soc_camera_link *icl = to_soc_camera_link(icd);
-       int ret = 0;
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
 
-       if (icl->power) {
-               ret = icl->power(&client->dev, 1);
-               if (ret < 0)
-                       return ret;
+       if (!enable) {
+               ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
+               return 0;
        }
 
-       if (icl->reset)
-               ret = icl->reset(&client->dev);
-
-       return ret;
-}
-
-static int ov772x_release(struct soc_camera_device *icd)
-{
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct soc_camera_link *icl = to_soc_camera_link(icd);
-       int ret = 0;
-
-       if (icl->power)
-               ret = icl->power(&client->dev, 0);
-
-       return ret;
-}
-
-static int ov772x_start_capture(struct soc_camera_device *icd)
-{
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct ov772x_priv *priv = i2c_get_clientdata(client);
-
-       if (!priv->win || !priv->fmt) {
-               dev_err(&icd->dev, "norm or win select error\n");
+       if (!priv->win || !priv->cfmt) {
+               dev_err(&client->dev, "norm or win select error\n");
                return -EPERM;
        }
 
        ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
 
-       dev_dbg(&icd->dev,
-               "format %s, win %s\n", priv->fmt->name, priv->win->name);
+       dev_dbg(&client->dev, "format %d, win %s\n",
+               priv->cfmt->code, priv->win->name);
 
        return 0;
 }
 
-static int ov772x_stop_capture(struct soc_camera_device *icd)
-{
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
-       return 0;
-}
-
 static int ov772x_set_bus_param(struct soc_camera_device *icd,
                                unsigned long             flags)
 {
@@ -683,16 +633,20 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
        struct soc_camera_link *icl = to_soc_camera_link(icd);
        unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
                SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
-               SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
+               SOCAM_DATA_ACTIVE_HIGH;
+
+       if (priv->info->flags & OV772X_FLAG_8BIT)
+               flags |= SOCAM_DATAWIDTH_8;
+       else
+               flags |= SOCAM_DATAWIDTH_10;
 
        return soc_camera_apply_sensor_flags(icl, flags);
 }
 
-static int ov772x_get_control(struct soc_camera_device *icd,
-                             struct v4l2_control *ctrl)
+static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct ov772x_priv *priv = i2c_get_clientdata(client);
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
 
        switch (ctrl->id) {
        case V4L2_CID_VFLIP:
@@ -701,15 +655,17 @@ static int ov772x_get_control(struct soc_camera_device *icd,
        case V4L2_CID_HFLIP:
                ctrl->value = priv->flag_hflip;
                break;
+       case V4L2_CID_BAND_STOP_FILTER:
+               ctrl->value = priv->band_filter;
+               break;
        }
        return 0;
 }
 
-static int ov772x_set_control(struct soc_camera_device *icd,
-                             struct v4l2_control *ctrl)
+static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct ov772x_priv *priv = i2c_get_clientdata(client);
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
        int ret = 0;
        u8 val;
 
@@ -728,16 +684,39 @@ static int ov772x_set_control(struct soc_camera_device *icd,
                        val ^= HFLIP_IMG;
                ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val);
                break;
+       case V4L2_CID_BAND_STOP_FILTER:
+               if ((unsigned)ctrl->value > 256)
+                       ctrl->value = 256;
+               if (ctrl->value == priv->band_filter)
+                       break;
+               if (!ctrl->value) {
+                       /* Switch the filter off, it is on now */
+                       ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
+                       if (!ret)
+                               ret = ov772x_mask_set(client, COM8,
+                                                     BNDF_ON_OFF, 0);
+               } else {
+                       /* Switch the filter on, set AEC low limit */
+                       val = 256 - ctrl->value;
+                       ret = ov772x_mask_set(client, COM8,
+                                             BNDF_ON_OFF, BNDF_ON_OFF);
+                       if (!ret)
+                               ret = ov772x_mask_set(client, BDBASE,
+                                                     0xff, val);
+               }
+               if (!ret)
+                       priv->band_filter = ctrl->value;
+               break;
        }
 
        return ret;
 }
 
-static int ov772x_get_chip_id(struct soc_camera_device *icd,
-                             struct v4l2_dbg_chip_ident *id)
+static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
+                              struct v4l2_dbg_chip_ident *id)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct ov772x_priv *priv = i2c_get_clientdata(client);
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
 
        id->ident    = priv->model;
        id->revision = 0;
@@ -746,10 +725,10 @@ static int ov772x_get_chip_id(struct soc_camera_device *icd,
 }
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov772x_get_register(struct soc_camera_device *icd,
-                              struct v4l2_dbg_register *reg)
+static int ov772x_g_register(struct v4l2_subdev *sd,
+                            struct v4l2_dbg_register *reg)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+       struct i2c_client *client = sd->priv;
        int ret;
 
        reg->size = 1;
@@ -765,10 +744,10 @@ static int ov772x_get_register(struct soc_camera_device *icd,
        return 0;
 }
 
-static int ov772x_set_register(struct soc_camera_device *icd,
-                              struct v4l2_dbg_register *reg)
+static int ov772x_s_register(struct v4l2_subdev *sd,
+                            struct v4l2_dbg_register *reg)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+       struct i2c_client *client = sd->priv;
 
        if (reg->reg > 0xff ||
            reg->val > 0xff)
@@ -778,8 +757,7 @@ static int ov772x_set_register(struct soc_camera_device *icd,
 }
 #endif
 
-static const struct ov772x_win_size*
-ov772x_select_win(u32 width, u32 height)
+static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
 {
        __u32 diff;
        const struct ov772x_win_size *win;
@@ -798,11 +776,10 @@ ov772x_select_win(u32 width, u32 height)
        return win;
 }
 
-static int ov772x_set_params(struct soc_camera_device *icd,
-                            u32 width, u32 height, u32 pixfmt)
+static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
+                            enum v4l2_mbus_pixelcode code)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct ov772x_priv *priv = i2c_get_clientdata(client);
+       struct ov772x_priv *priv = to_ov772x(client);
        int ret = -EINVAL;
        u8  val;
        int i;
@@ -810,21 +787,20 @@ static int ov772x_set_params(struct soc_camera_device *icd,
        /*
         * select format
         */
-       priv->fmt = NULL;
+       priv->cfmt = NULL;
        for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
-               if (pixfmt == ov772x_cfmts[i].fourcc) {
-                       priv->fmt = ov772x_cfmts + i;
+               if (code == ov772x_cfmts[i].code) {
+                       priv->cfmt = ov772x_cfmts + i;
                        break;
                }
        }
-       dev_dbg(&icd->dev, "Using fmt %x #%d\n", pixfmt, i);
-       if (!priv->fmt)
+       if (!priv->cfmt)
                goto ov772x_set_fmt_error;
 
        /*
         * select win
         */
-       priv->win = ov772x_select_win(width, height);
+       priv->win = ov772x_select_win(*width, *height);
 
        /*
         * reset hardware
@@ -888,7 +864,7 @@ static int ov772x_set_params(struct soc_camera_device *icd,
        /*
         * set DSP_CTRL3
         */
-       val = priv->fmt->dsp3;
+       val = priv->cfmt->dsp3;
        if (val) {
                ret = ov772x_mask_set(client,
                                      DSP_CTRL3, UV_MASK, val);
@@ -899,7 +875,7 @@ static int ov772x_set_params(struct soc_camera_device *icd,
        /*
         * set COM3
         */
-       val = priv->fmt->com3;
+       val = priv->cfmt->com3;
        if (priv->info->flags & OV772X_FLAG_VFLIP)
                val |= VFLIP_IMG;
        if (priv->info->flags & OV772X_FLAG_HFLIP)
@@ -917,60 +893,137 @@ static int ov772x_set_params(struct soc_camera_device *icd,
        /*
         * set COM7
         */
-       val = priv->win->com7_bit | priv->fmt->com7;
+       val = priv->win->com7_bit | priv->cfmt->com7;
        ret = ov772x_mask_set(client,
-                             COM7, (SLCT_MASK | FMT_MASK | OFMT_MASK),
+                             COM7, SLCT_MASK | FMT_MASK | OFMT_MASK,
                              val);
        if (ret < 0)
                goto ov772x_set_fmt_error;
 
+       /*
+        * set COM8
+        */
+       if (priv->band_filter) {
+               ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+               if (!ret)
+                       ret = ov772x_mask_set(client, BDBASE,
+                                             0xff, 256 - priv->band_filter);
+               if (ret < 0)
+                       goto ov772x_set_fmt_error;
+       }
+
+       *width = priv->win->width;
+       *height = priv->win->height;
+
        return ret;
 
 ov772x_set_fmt_error:
 
        ov772x_reset(client);
        priv->win = NULL;
-       priv->fmt = NULL;
+       priv->cfmt = NULL;
 
        return ret;
 }
 
-static int ov772x_set_crop(struct soc_camera_device *icd,
-                          struct v4l2_rect *rect)
+static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct ov772x_priv *priv = i2c_get_clientdata(client);
+       a->c.left       = 0;
+       a->c.top        = 0;
+       a->c.width      = VGA_WIDTH;
+       a->c.height     = VGA_HEIGHT;
+       a->type         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
-       if (!priv->fmt)
-               return -EINVAL;
+       return 0;
+}
 
-       return ov772x_set_params(icd, rect->width, rect->height,
-                                priv->fmt->fourcc);
+static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+       a->bounds.left                  = 0;
+       a->bounds.top                   = 0;
+       a->bounds.width                 = VGA_WIDTH;
+       a->bounds.height                = VGA_HEIGHT;
+       a->defrect                      = a->bounds;
+       a->type                         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       a->pixelaspect.numerator        = 1;
+       a->pixelaspect.denominator      = 1;
+
+       return 0;
+}
+
+static int ov772x_g_fmt(struct v4l2_subdev *sd,
+                       struct v4l2_mbus_framefmt *mf)
+{
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
+
+       if (!priv->win || !priv->cfmt) {
+               u32 width = VGA_WIDTH, height = VGA_HEIGHT;
+               int ret = ov772x_set_params(client, &width, &height,
+                                           V4L2_MBUS_FMT_YUYV8_2X8_LE);
+               if (ret < 0)
+                       return ret;
+       }
+
+       mf->width       = priv->win->width;
+       mf->height      = priv->win->height;
+       mf->code        = priv->cfmt->code;
+       mf->colorspace  = priv->cfmt->colorspace;
+       mf->field       = V4L2_FIELD_NONE;
+
+       return 0;
 }
 
-static int ov772x_set_fmt(struct soc_camera_device *icd,
-                         struct v4l2_format *f)
+static int ov772x_s_fmt(struct v4l2_subdev *sd,
+                       struct v4l2_mbus_framefmt *mf)
 {
-       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
+       int ret = ov772x_set_params(client, &mf->width, &mf->height,
+                                   mf->code);
 
-       return ov772x_set_params(icd, pix->width, pix->height,
-                                pix->pixelformat);
+       if (!ret)
+               mf->colorspace = priv->cfmt->colorspace;
+
+       return ret;
 }
 
-static int ov772x_try_fmt(struct soc_camera_device *icd,
-                         struct v4l2_format       *f)
+static int ov772x_try_fmt(struct v4l2_subdev *sd,
+                         struct v4l2_mbus_framefmt *mf)
 {
-       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
        const struct ov772x_win_size *win;
+       int i;
 
        /*
         * select suitable win
         */
-       win = ov772x_select_win(pix->width, pix->height);
+       win = ov772x_select_win(mf->width, mf->height);
+
+       mf->width       = win->width;
+       mf->height      = win->height;
+       mf->field       = V4L2_FIELD_NONE;
 
-       pix->width  = win->width;
-       pix->height = win->height;
-       pix->field  = V4L2_FIELD_NONE;
+       for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++)
+               if (mf->code == ov772x_cfmts[i].code)
+                       break;
+
+       if (i == ARRAY_SIZE(ov772x_cfmts)) {
+               /* Unsupported format requested. Propose either */
+               if (priv->cfmt) {
+                       /* the current one or */
+                       mf->colorspace = priv->cfmt->colorspace;
+                       mf->code = priv->cfmt->code;
+               } else {
+                       /* the default one */
+                       mf->colorspace = ov772x_cfmts[0].colorspace;
+                       mf->code = ov772x_cfmts[0].code;
+               }
+       } else {
+               /* Also return the colorspace */
+               mf->colorspace  = ov772x_cfmts[i].colorspace;
+       }
 
        return 0;
 }
@@ -978,10 +1031,9 @@ static int ov772x_try_fmt(struct soc_camera_device *icd,
 static int ov772x_video_probe(struct soc_camera_device *icd,
                              struct i2c_client *client)
 {
-       struct ov772x_priv *priv = i2c_get_clientdata(client);
+       struct ov772x_priv *priv = to_ov772x(client);
        u8                  pid, ver;
        const char         *devname;
-       int ret;
 
        /*
         * We must have a parent by now. And it cannot be a wrong one.
@@ -992,23 +1044,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd,
                return -ENODEV;
 
        /*
-        * ov772x only use 8 or 10 bit bus width
-        */
-       if (SOCAM_DATAWIDTH_10 != priv->info->buswidth &&
-           SOCAM_DATAWIDTH_8  != priv->info->buswidth) {
-               dev_err(&icd->dev, "bus width error\n");
-               return -ENODEV;
-       }
-
-       icd->formats     = ov772x_fmt_lists;
-       icd->num_formats = ARRAY_SIZE(ov772x_fmt_lists);
-
-       /* Switch master clock on */
-       ret = soc_camera_video_start(icd, &client->dev);
-       if (ret)
-               return ret;
-
-       /*
         * check and show product ID and manufacturer ID
         */
        pid = i2c_smbus_read_byte_data(client, PID);
@@ -1024,13 +1059,12 @@ static int ov772x_video_probe(struct soc_camera_device *icd,
                priv->model = V4L2_IDENT_OV7725;
                break;
        default:
-               dev_err(&icd->dev,
+               dev_err(&client->dev,
                        "Product ID error %x:%x\n", pid, ver);
-               ret = -ENODEV;
-               goto ever;
+               return -ENODEV;
        }
 
-       dev_info(&icd->dev,
+       dev_info(&client->dev,
                 "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
                 devname,
                 pid,
@@ -1038,34 +1072,51 @@ static int ov772x_video_probe(struct soc_camera_device *icd,
                 i2c_smbus_read_byte_data(client, MIDH),
                 i2c_smbus_read_byte_data(client, MIDL));
 
-       soc_camera_video_stop(icd);
-
-ever:
-       return ret;
+       return 0;
 }
 
 static struct soc_camera_ops ov772x_ops = {
-       .owner                  = THIS_MODULE,
-       .init                   = ov772x_init,
-       .release                = ov772x_release,
-       .start_capture          = ov772x_start_capture,
-       .stop_capture           = ov772x_stop_capture,
-       .set_crop               = ov772x_set_crop,
-       .set_fmt                = ov772x_set_fmt,
-       .try_fmt                = ov772x_try_fmt,
        .set_bus_param          = ov772x_set_bus_param,
        .query_bus_param        = ov772x_query_bus_param,
        .controls               = ov772x_controls,
        .num_controls           = ARRAY_SIZE(ov772x_controls),
-       .get_control            = ov772x_get_control,
-       .set_control            = ov772x_set_control,
-       .get_chip_id            = ov772x_get_chip_id,
+};
+
+static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
+       .g_ctrl         = ov772x_g_ctrl,
+       .s_ctrl         = ov772x_s_ctrl,
+       .g_chip_ident   = ov772x_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-       .get_register           = ov772x_get_register,
-       .set_register           = ov772x_set_register,
+       .g_register     = ov772x_g_register,
+       .s_register     = ov772x_s_register,
 #endif
 };
 
+static int ov772x_enum_fmt(struct v4l2_subdev *sd, int index,
+                          enum v4l2_mbus_pixelcode *code)
+{
+       if ((unsigned int)index >= ARRAY_SIZE(ov772x_cfmts))
+               return -EINVAL;
+
+       *code = ov772x_cfmts[index].code;
+       return 0;
+}
+
+static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
+       .s_stream       = ov772x_s_stream,
+       .g_mbus_fmt     = ov772x_g_fmt,
+       .s_mbus_fmt     = ov772x_s_fmt,
+       .try_mbus_fmt   = ov772x_try_fmt,
+       .cropcap        = ov772x_cropcap,
+       .g_crop         = ov772x_g_crop,
+       .enum_mbus_fmt  = ov772x_enum_fmt,
+};
+
+static struct v4l2_subdev_ops ov772x_subdev_ops = {
+       .core   = &ov772x_subdev_core_ops,
+       .video  = &ov772x_subdev_video_ops,
+};
+
 /*
  * i2c_driver function
  */
@@ -1074,23 +1125,20 @@ static int ov772x_probe(struct i2c_client *client,
                        const struct i2c_device_id *did)
 {
        struct ov772x_priv        *priv;
-       struct ov772x_camera_info *info;
        struct soc_camera_device  *icd = client->dev.platform_data;
        struct i2c_adapter        *adapter = to_i2c_adapter(client->dev.parent);
        struct soc_camera_link    *icl;
        int                        ret;
 
        if (!icd) {
-               dev_err(&client->dev, "MT9M001: missing soc-camera data!\n");
+               dev_err(&client->dev, "OV772X: missing soc-camera data!\n");
                return -EINVAL;
        }
 
        icl = to_soc_camera_link(icd);
-       if (!icl)
+       if (!icl || !icl->priv)
                return -EINVAL;
 
-       info = container_of(icl, struct ov772x_camera_info, link);
-
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
                dev_err(&adapter->dev,
                        "I2C-Adapter doesn't support "
@@ -1102,12 +1150,11 @@ static int ov772x_probe(struct i2c_client *client,
        if (!priv)
                return -ENOMEM;
 
-       priv->info   = info;
-       i2c_set_clientdata(client, priv);
+       priv->info = icl->priv;
 
-       icd->ops        = &ov772x_ops;
-       icd->width_max  = MAX_WIDTH;
-       icd->height_max = MAX_HEIGHT;
+       v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
+
+       icd->ops                = &ov772x_ops;
 
        ret = ov772x_video_probe(icd, client);
        if (ret) {
@@ -1121,7 +1168,7 @@ static int ov772x_probe(struct i2c_client *client,
 
 static int ov772x_remove(struct i2c_client *client)
 {
-       struct ov772x_priv *priv = i2c_get_clientdata(client);
+       struct ov772x_priv *priv = to_ov772x(client);
        struct soc_camera_device *icd = client->dev.platform_data;
 
        icd->ops = NULL;