V4L/DVB (11303): tda7432: remove legacy code for old-style i2c API
[safe/jmp/linux-2.6] / drivers / media / video / ov7670.c
index 5ed0adc..0e2184e 100644 (file)
@@ -5,23 +5,29 @@
  * by Jonathan Corbet with substantial inspiration from Mark
  * McClelland's ovcamchip code.
  *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
  * This file may be distributed under the terms of the GNU General
  * Public License, version 2.
  */
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/videodev.h>
-#include <media/v4l2-common.h>
 #include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
 
 
 MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
 MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
 MODULE_LICENSE("GPL");
 
+static int debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
 /*
  * Basic window sizes.  These probably belong somewhere more globally
  * useful.
@@ -162,6 +168,10 @@ MODULE_LICENSE("GPL");
 
 #define REG_GFIX       0x69    /* Fix gain control */
 
+#define REG_REG76      0x76    /* OV's name */
+#define   R76_BLKPCOR    0x80    /* Black pixel correction enable */
+#define   R76_WHTPCOR    0x40    /* White pixel correction enable */
+
 #define REG_RGB444     0x8c    /* RGB 444 control */
 #define   R444_ENABLE    0x02    /* Turn on RGB444, overrides 5x5 */
 #define   R444_RGBX      0x01    /* Empty nibble at end */
@@ -183,11 +193,16 @@ MODULE_LICENSE("GPL");
  */
 struct ov7670_format_struct;  /* coming later */
 struct ov7670_info {
+       struct v4l2_subdev sd;
        struct ov7670_format_struct *fmt;  /* Current format */
        unsigned char sat;              /* Saturation value */
        int hue;                        /* Hue value */
 };
 
+static inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct ov7670_info, sd);
+}
 
 
 
@@ -255,7 +270,7 @@ static struct regval_list ov7670_default_regs[] = {
 
        /* Almost all of these are magic "reserved" values.  */
        { REG_COM5, 0x61 },     { REG_COM6, 0x4b },
-       { 0x16, 0x02 },         { REG_MVFP, 0x07|MVFP_MIRROR },
+       { 0x16, 0x02 },         { REG_MVFP, 0x07 },
        { 0x21, 0x02 },         { 0x22, 0x91 },
        { 0x29, 0x07 },         { 0x33, 0x0b },
        { 0x35, 0x0b },         { 0x37, 0x1d },
@@ -380,6 +395,13 @@ static struct regval_list ov7670_fmt_rgb444[] = {
        { 0xff, 0xff },
 };
 
+static struct regval_list ov7670_fmt_raw[] = {
+       { REG_COM7, COM7_BAYER },
+       { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */
+       { REG_COM16, 0x3d }, /* Edge enhancement, denoise */
+       { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */
+       { 0xff, 0xff },
+};
 
 
 
@@ -387,32 +409,40 @@ static struct regval_list ov7670_fmt_rgb444[] = {
  * Low-level register I/O.
  */
 
-static int ov7670_read(struct i2c_client *c, unsigned char reg,
+static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
                unsigned char *value)
 {
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
        int ret;
 
-       ret = i2c_smbus_read_byte_data(c, reg);
-       if (ret >= 0)
-               *value = (unsigned char) ret;
+       ret = i2c_smbus_read_byte_data(client, reg);
+       if (ret >= 0) {
+               *value = (unsigned char)ret;
+               ret = 0;
+       }
        return ret;
 }
 
 
-static int ov7670_write(struct i2c_client *c, unsigned char reg,
+static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
                unsigned char value)
 {
-       return i2c_smbus_write_byte_data(c, reg, value);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = i2c_smbus_write_byte_data(client, reg, value);
+
+       if (reg == REG_COM7 && (value & COM7_RESET))
+               msleep(2);  /* Wait for reset to run */
+       return ret;
 }
 
 
 /*
  * Write a list of register settings; ff/ff stops the process.
  */
-static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals)
+static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals)
 {
        while (vals->reg_num != 0xff || vals->value != 0xff) {
-               int ret = ov7670_write(c, vals->reg_num, vals->value);
+               int ret = ov7670_write(sd, vals->reg_num, vals->value);
                if (ret < 0)
                        return ret;
                vals++;
@@ -424,34 +454,35 @@ static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals)
 /*
  * Stuff that knows about the sensor.
  */
-static void ov7670_reset(struct i2c_client *client)
+static int ov7670_reset(struct v4l2_subdev *sd, u32 val)
 {
-       ov7670_write(client, REG_COM7, COM7_RESET);
+       ov7670_write(sd, REG_COM7, COM7_RESET);
        msleep(1);
+       return 0;
 }
 
 
-static int ov7670_init(struct i2c_client *client)
+static int ov7670_init(struct v4l2_subdev *sd, u32 val)
 {
-       return ov7670_write_array(client, ov7670_default_regs);
+       return ov7670_write_array(sd, ov7670_default_regs);
 }
 
 
 
-static int ov7670_detect(struct i2c_client *client)
+static int ov7670_detect(struct v4l2_subdev *sd)
 {
        unsigned char v;
        int ret;
 
-       ret = ov7670_init(client);
+       ret = ov7670_init(sd, 0);
        if (ret < 0)
                return ret;
-       ret = ov7670_read(client, REG_MIDH, &v);
+       ret = ov7670_read(sd, REG_MIDH, &v);
        if (ret < 0)
                return ret;
        if (v != 0x7f) /* OV manuf. id. */
                return -ENODEV;
-       ret = ov7670_read(client, REG_MIDL, &v);
+       ret = ov7670_read(sd, REG_MIDL, &v);
        if (ret < 0)
                return ret;
        if (v != 0xa2)
@@ -459,12 +490,12 @@ static int ov7670_detect(struct i2c_client *client)
        /*
         * OK, we know we have an OmniVision chip...but which one?
         */
-       ret = ov7670_read(client, REG_PID, &v);
+       ret = ov7670_read(sd, REG_PID, &v);
        if (ret < 0)
                return ret;
        if (v != 0x76)  /* PID + VER = 0x76 / 0x73 */
                return -ENODEV;
-       ret = ov7670_read(client, REG_VER, &v);
+       ret = ov7670_read(sd, REG_VER, &v);
        if (ret < 0)
                return ret;
        if (v != 0x73)  /* PID + VER = 0x76 / 0x73 */
@@ -483,32 +514,39 @@ static struct ov7670_format_struct {
        __u32 pixelformat;
        struct regval_list *regs;
        int cmatrix[CMATRIX_LEN];
+       int bpp;   /* Bytes per pixel */
 } ov7670_formats[] = {
        {
                .desc           = "YUYV 4:2:2",
                .pixelformat    = V4L2_PIX_FMT_YUYV,
                .regs           = ov7670_fmt_yuv422,
                .cmatrix        = { 128, -128, 0, -34, -94, 128 },
+               .bpp            = 2,
        },
        {
                .desc           = "RGB 444",
                .pixelformat    = V4L2_PIX_FMT_RGB444,
                .regs           = ov7670_fmt_rgb444,
                .cmatrix        = { 179, -179, 0, -61, -176, 228 },
+               .bpp            = 2,
        },
        {
                .desc           = "RGB 565",
                .pixelformat    = V4L2_PIX_FMT_RGB565,
                .regs           = ov7670_fmt_rgb565,
                .cmatrix        = { 179, -179, 0, -61, -176, 228 },
+               .bpp            = 2,
+       },
+       {
+               .desc           = "Raw RGB Bayer",
+               .pixelformat    = V4L2_PIX_FMT_SBGGR8,
+               .regs           = ov7670_fmt_raw,
+               .cmatrix        = { 0, 0, 0, 0, 0, 0 },
+               .bpp            = 1
        },
 };
-#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0]))
+#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)
 
-/*
- * All formats we support are 2 bytes/pixel.
- */
-#define BYTES_PER_PIXEL 2
 
 /*
  * Then there is the issue of window sizes.  Try to capture the info here.
@@ -596,13 +634,13 @@ static struct ov7670_win_size {
        },
 };
 
-#define N_WIN_SIZES (sizeof(ov7670_win_sizes)/sizeof(ov7670_win_sizes[0]))
+#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes))
 
 
 /*
  * Store a set of start/stop values into the camera.
  */
-static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop,
+static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop,
                int vstart, int vstop)
 {
        int ret;
@@ -612,26 +650,26 @@ static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop,
  * hstart are in href[2:0], bottom 3 of hstop in href[5:3].  There is
  * a mystery "edge offset" value in the top two bits of href.
  */
-       ret =  ov7670_write(client, REG_HSTART, (hstart >> 3) & 0xff);
-       ret += ov7670_write(client, REG_HSTOP, (hstop >> 3) & 0xff);
-       ret += ov7670_read(client, REG_HREF, &v);
+       ret =  ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff);
+       ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff);
+       ret += ov7670_read(sd, REG_HREF, &v);
        v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
        msleep(10);
-       ret += ov7670_write(client, REG_HREF, v);
+       ret += ov7670_write(sd, REG_HREF, v);
 /*
  * Vertical: similar arrangement, but only 10 bits.
  */
-       ret += ov7670_write(client, REG_VSTART, (vstart >> 2) & 0xff);
-       ret += ov7670_write(client, REG_VSTOP, (vstop >> 2) & 0xff);
-       ret += ov7670_read(client, REG_VREF, &v);
+       ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff);
+       ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff);
+       ret += ov7670_read(sd, REG_VREF, &v);
        v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
        msleep(10);
-       ret += ov7670_write(client, REG_VREF, v);
+       ret += ov7670_write(sd, REG_VREF, v);
        return ret;
 }
 
 
-static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt)
+static int ov7670_enum_fmt(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt)
 {
        struct ov7670_format_struct *ofmt;
 
@@ -646,7 +684,8 @@ static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt)
 }
 
 
-static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
+static int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
+               struct v4l2_format *fmt,
                struct ov7670_format_struct **ret_fmt,
                struct ov7670_win_size **ret_wsize)
 {
@@ -657,17 +696,17 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
        for (index = 0; index < N_OV7670_FMTS; index++)
                if (ov7670_formats[index].pixelformat == pix->pixelformat)
                        break;
-       if (index >= N_OV7670_FMTS)
-               return -EINVAL;
+       if (index >= N_OV7670_FMTS) {
+               /* default to first format */
+               index = 0;
+               pix->pixelformat = ov7670_formats[0].pixelformat;
+       }
        if (ret_fmt != NULL)
                *ret_fmt = ov7670_formats + index;
        /*
         * Fields: the OV devices claim to be progressive.
         */
-       if (pix->field == V4L2_FIELD_ANY)
-               pix->field = V4L2_FIELD_NONE;
-       else if (pix->field != V4L2_FIELD_NONE)
-               return -EINVAL;
+       pix->field = V4L2_FIELD_NONE;
        /*
         * Round requested image size down to the nearest
         * we support, but not below the smallest.
@@ -685,26 +724,41 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
         */
        pix->width = wsize->width;
        pix->height = wsize->height;
-       pix->bytesperline = pix->width*BYTES_PER_PIXEL;
+       pix->bytesperline = pix->width*ov7670_formats[index].bpp;
        pix->sizeimage = pix->height*pix->bytesperline;
        return 0;
 }
 
+static int ov7670_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+       return ov7670_try_fmt_internal(sd, fmt, NULL, NULL);
+}
+
 /*
  * Set a format.
  */
-static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
+static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
 {
        int ret;
        struct ov7670_format_struct *ovfmt;
        struct ov7670_win_size *wsize;
-       struct ov7670_info *info = i2c_get_clientdata(c);
-       unsigned char com7;
+       struct ov7670_info *info = to_state(sd);
+       unsigned char com7, clkrc = 0;
 
-       ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize);
+       ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize);
        if (ret)
                return ret;
        /*
+        * HACK: if we're running rgb565 we need to grab then rewrite
+        * CLKRC.  If we're *not*, however, then rewriting clkrc hoses
+        * the colors.
+        */
+       if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) {
+               ret = ov7670_read(sd, REG_CLKRC, &clkrc);
+               if (ret)
+                       return ret;
+       }
+       /*
         * COM7 is a pain in the ass, it doesn't like to be read then
         * quickly written afterward.  But we have everything we need
         * to set it absolutely here, as long as the format-specific
@@ -712,25 +766,28 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
         */
        com7 = ovfmt->regs[0].value;
        com7 |= wsize->com7_bit;
-       ov7670_write(c, REG_COM7, com7);
+       ov7670_write(sd, REG_COM7, com7);
        /*
         * Now write the rest of the array.  Also store start/stops
         */
-       ov7670_write_array(c, ovfmt->regs + 1);
-       ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart,
+       ov7670_write_array(sd, ovfmt->regs + 1);
+       ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart,
                        wsize->vstop);
        ret = 0;
        if (wsize->regs)
-               ret = ov7670_write_array(c, wsize->regs);
+               ret = ov7670_write_array(sd, wsize->regs);
        info->fmt = ovfmt;
-       return 0;
+
+       if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 && ret == 0)
+               ret = ov7670_write(sd, REG_CLKRC, clkrc);
+       return ret;
 }
 
 /*
  * Implement G/S_PARM.  There is a "high quality" mode we could try
  * to do someday; for now, we just do the frame rate tweak.
  */
-static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
 {
        struct v4l2_captureparm *cp = &parms->parm.capture;
        unsigned char clkrc;
@@ -738,7 +795,7 @@ static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
 
        if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
-       ret = ov7670_read(c, REG_CLKRC, &clkrc);
+       ret = ov7670_read(sd, REG_CLKRC, &clkrc);
        if (ret < 0)
                return ret;
        memset(cp, 0, sizeof(struct v4l2_captureparm));
@@ -750,7 +807,7 @@ static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
        return 0;
 }
 
-static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
 {
        struct v4l2_captureparm *cp = &parms->parm.capture;
        struct v4l2_fract *tpf = &cp->timeperframe;
@@ -764,7 +821,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
        /*
         * CLKRC has a reserved bit, so let's preserve it.
         */
-       ret = ov7670_read(c, REG_CLKRC, &clkrc);
+       ret = ov7670_read(sd, REG_CLKRC, &clkrc);
        if (ret < 0)
                return ret;
        if (tpf->numerator == 0 || tpf->denominator == 0)
@@ -778,7 +835,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
        clkrc = (clkrc & 0x80) | div;
        tpf->numerator = 1;
        tpf->denominator = OV7670_FRAME_RATE/div;
-       return ov7670_write(c, REG_CLKRC, clkrc);
+       return ov7670_write(sd, REG_CLKRC, clkrc);
 }
 
 
@@ -791,17 +848,17 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
 
 
 
-static int ov7670_store_cmatrix(struct i2c_client *client,
+static int ov7670_store_cmatrix(struct v4l2_subdev *sd,
                int matrix[CMATRIX_LEN])
 {
        int i, ret;
-       unsigned char signbits;
+       unsigned char signbits = 0;
 
        /*
         * Weird crap seems to exist in the upper part of
         * the sign bits register, so let's preserve it.
         */
-       ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits);
+       ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits);
        signbits &= 0xc0;
 
        for (i = 0; i < CMATRIX_LEN; i++) {
@@ -820,9 +877,9 @@ static int ov7670_store_cmatrix(struct i2c_client *client,
                        else
                                raw = matrix[i] & 0xff;
                }
-               ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw);
+               ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw);
        }
-       ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits);
+       ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits);
        return ret;
 }
 
@@ -905,29 +962,29 @@ static void ov7670_calc_cmatrix(struct ov7670_info *info,
 
 
 
-static int ov7670_t_sat(struct i2c_client *client, int value)
+static int ov7670_s_sat(struct v4l2_subdev *sd, int value)
 {
-       struct ov7670_info *info = i2c_get_clientdata(client);
+       struct ov7670_info *info = to_state(sd);
        int matrix[CMATRIX_LEN];
        int ret;
 
        info->sat = value;
        ov7670_calc_cmatrix(info, matrix);
-       ret = ov7670_store_cmatrix(client, matrix);
+       ret = ov7670_store_cmatrix(sd, matrix);
        return ret;
 }
 
-static int ov7670_q_sat(struct i2c_client *client, __s32 *value)
+static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value)
 {
-       struct ov7670_info *info = i2c_get_clientdata(client);
+       struct ov7670_info *info = to_state(sd);
 
        *value = info->sat;
        return 0;
 }
 
-static int ov7670_t_hue(struct i2c_client *client, int value)
+static int ov7670_s_hue(struct v4l2_subdev *sd, int value)
 {
-       struct ov7670_info *info = i2c_get_clientdata(client);
+       struct ov7670_info *info = to_state(sd);
        int matrix[CMATRIX_LEN];
        int ret;
 
@@ -935,14 +992,14 @@ static int ov7670_t_hue(struct i2c_client *client, int value)
                return -EINVAL;
        info->hue = value;
        ov7670_calc_cmatrix(info, matrix);
-       ret = ov7670_store_cmatrix(client, matrix);
+       ret = ov7670_store_cmatrix(sd, matrix);
        return ret;
 }
 
 
-static int ov7670_q_hue(struct i2c_client *client, __s32 *value)
+static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value)
 {
-       struct ov7670_info *info = i2c_get_clientdata(client);
+       struct ov7670_info *info = to_state(sd);
 
        *value = info->hue;
        return 0;
@@ -956,8 +1013,7 @@ static unsigned char ov7670_sm_to_abs(unsigned char v)
 {
        if ((v & 0x80) == 0)
                return v + 128;
-       else
-               return 128 - (v & 0x7f);
+       return 128 - (v & 0x7f);
 }
 
 
@@ -965,369 +1021,275 @@ static unsigned char ov7670_abs_to_sm(unsigned char v)
 {
        if (v > 127)
                return v & 0x7f;
-       else
-               return (128 - v) | 0x80;
+       return (128 - v) | 0x80;
 }
 
-static int ov7670_t_brightness(struct i2c_client *client, int value)
+static int ov7670_s_brightness(struct v4l2_subdev *sd, int value)
 {
-       unsigned char com8, v;
+       unsigned char com8 = 0, v;
        int ret;
 
-       ov7670_read(client, REG_COM8, &com8);
+       ov7670_read(sd, REG_COM8, &com8);
        com8 &= ~COM8_AEC;
-       ov7670_write(client, REG_COM8, com8);
+       ov7670_write(sd, REG_COM8, com8);
        v = ov7670_abs_to_sm(value);
-       ret = ov7670_write(client, REG_BRIGHT, v);
+       ret = ov7670_write(sd, REG_BRIGHT, v);
        return ret;
 }
 
-static int ov7670_q_brightness(struct i2c_client *client, __s32 *value)
+static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value)
 {
-       unsigned char v;
-       int ret = ov7670_read(client, REG_BRIGHT, &v);
+       unsigned char v = 0;
+       int ret = ov7670_read(sd, REG_BRIGHT, &v);
 
        *value = ov7670_sm_to_abs(v);
        return ret;
 }
 
-static int ov7670_t_contrast(struct i2c_client *client, int value)
+static int ov7670_s_contrast(struct v4l2_subdev *sd, int value)
 {
-       return ov7670_write(client, REG_CONTRAS, (unsigned char) value);
+       return ov7670_write(sd, REG_CONTRAS, (unsigned char) value);
 }
 
-static int ov7670_q_contrast(struct i2c_client *client, __s32 *value)
+static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value)
 {
-       unsigned char v;
-       int ret = ov7670_read(client, REG_CONTRAS, &v);
+       unsigned char v = 0;
+       int ret = ov7670_read(sd, REG_CONTRAS, &v);
 
        *value = v;
        return ret;
 }
 
-static int ov7670_q_hflip(struct i2c_client *client, __s32 *value)
+static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value)
 {
        int ret;
-       unsigned char v;
+       unsigned char v = 0;
 
-       ret = ov7670_read(client, REG_MVFP, &v);
+       ret = ov7670_read(sd, REG_MVFP, &v);
        *value = (v & MVFP_MIRROR) == MVFP_MIRROR;
        return ret;
 }
 
 
-static int ov7670_t_hflip(struct i2c_client *client, int value)
+static int ov7670_s_hflip(struct v4l2_subdev *sd, int value)
 {
-       unsigned char v;
+       unsigned char v = 0;
        int ret;
 
-       ret = ov7670_read(client, REG_MVFP, &v);
+       ret = ov7670_read(sd, REG_MVFP, &v);
        if (value)
                v |= MVFP_MIRROR;
        else
                v &= ~MVFP_MIRROR;
        msleep(10);  /* FIXME */
-       ret += ov7670_write(client, REG_MVFP, v);
+       ret += ov7670_write(sd, REG_MVFP, v);
        return ret;
 }
 
 
 
-static int ov7670_q_vflip(struct i2c_client *client, __s32 *value)
+static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value)
 {
        int ret;
-       unsigned char v;
+       unsigned char v = 0;
 
-       ret = ov7670_read(client, REG_MVFP, &v);
+       ret = ov7670_read(sd, REG_MVFP, &v);
        *value = (v & MVFP_FLIP) == MVFP_FLIP;
        return ret;
 }
 
 
-static int ov7670_t_vflip(struct i2c_client *client, int value)
+static int ov7670_s_vflip(struct v4l2_subdev *sd, int value)
 {
-       unsigned char v;
+       unsigned char v = 0;
        int ret;
 
-       ret = ov7670_read(client, REG_MVFP, &v);
+       ret = ov7670_read(sd, REG_MVFP, &v);
        if (value)
                v |= MVFP_FLIP;
        else
                v &= ~MVFP_FLIP;
        msleep(10);  /* FIXME */
-       ret += ov7670_write(client, REG_MVFP, v);
+       ret += ov7670_write(sd, REG_MVFP, v);
        return ret;
 }
 
-
-static struct ov7670_control {
-       struct v4l2_queryctrl qc;
-       int (*query)(struct i2c_client *c, __s32 *value);
-       int (*tweak)(struct i2c_client *c, int value);
-} ov7670_controls[] =
+static int ov7670_queryctrl(struct v4l2_subdev *sd,
+               struct v4l2_queryctrl *qc)
 {
-       {
-               .qc = {
-                       .id = V4L2_CID_BRIGHTNESS,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Brightness",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step = 1,
-                       .default_value = 0x80,
-                       .flags = V4L2_CTRL_FLAG_SLIDER
-               },
-               .tweak = ov7670_t_brightness,
-               .query = ov7670_q_brightness,
-       },
-       {
-               .qc = {
-                       .id = V4L2_CID_CONTRAST,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Contrast",
-                       .minimum = 0,
-                       .maximum = 127,
-                       .step = 1,
-                       .default_value = 0x40,   /* XXX ov7670 spec */
-                       .flags = V4L2_CTRL_FLAG_SLIDER
-               },
-               .tweak = ov7670_t_contrast,
-               .query = ov7670_q_contrast,
-       },
-       {
-               .qc = {
-                       .id = V4L2_CID_SATURATION,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Saturation",
-                       .minimum = 0,
-                       .maximum = 256,
-                       .step = 1,
-                       .default_value = 0x80,
-                       .flags = V4L2_CTRL_FLAG_SLIDER
-               },
-               .tweak = ov7670_t_sat,
-               .query = ov7670_q_sat,
-       },
-       {
-               .qc = {
-                       .id = V4L2_CID_HUE,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "HUE",
-                       .minimum = -180,
-                       .maximum = 180,
-                       .step = 5,
-                       .default_value = 0,
-                       .flags = V4L2_CTRL_FLAG_SLIDER
-               },
-               .tweak = ov7670_t_hue,
-               .query = ov7670_q_hue,
-       },
-       {
-               .qc = {
-                       .id = V4L2_CID_VFLIP,
-                       .type = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name = "Vertical flip",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step = 1,
-                       .default_value = 0,
-               },
-               .tweak = ov7670_t_vflip,
-               .query = ov7670_q_vflip,
-       },
-       {
-               .qc = {
-                       .id = V4L2_CID_HFLIP,
-                       .type = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name = "Horizontal mirror",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step = 1,
-                       .default_value = 0,
-               },
-               .tweak = ov7670_t_hflip,
-               .query = ov7670_q_hflip,
-       },
-};
-#define N_CONTROLS (sizeof(ov7670_controls)/sizeof(ov7670_controls[0]))
+       /* Fill in min, max, step and default value for these controls. */
+       switch (qc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+       case V4L2_CID_CONTRAST:
+               return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
+       case V4L2_CID_VFLIP:
+       case V4L2_CID_HFLIP:
+               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+       case V4L2_CID_SATURATION:
+               return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128);
+       case V4L2_CID_HUE:
+               return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0);
+       }
+       return -EINVAL;
+}
 
-static struct ov7670_control *ov7670_find_control(__u32 id)
+static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-       int i;
-
-       for (i = 0; i < N_CONTROLS; i++)
-               if (ov7670_controls[i].qc.id == id)
-                       return ov7670_controls + i;
-       return NULL;
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               return ov7670_g_brightness(sd, &ctrl->value);
+       case V4L2_CID_CONTRAST:
+               return ov7670_g_contrast(sd, &ctrl->value);
+       case V4L2_CID_SATURATION:
+               return ov7670_g_sat(sd, &ctrl->value);
+       case V4L2_CID_HUE:
+               return ov7670_g_hue(sd, &ctrl->value);
+       case V4L2_CID_VFLIP:
+               return ov7670_g_vflip(sd, &ctrl->value);
+       case V4L2_CID_HFLIP:
+               return ov7670_g_hflip(sd, &ctrl->value);
+       }
+       return -EINVAL;
 }
 
+static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               return ov7670_s_brightness(sd, ctrl->value);
+       case V4L2_CID_CONTRAST:
+               return ov7670_s_contrast(sd, ctrl->value);
+       case V4L2_CID_SATURATION:
+               return ov7670_s_sat(sd, ctrl->value);
+       case V4L2_CID_HUE:
+               return ov7670_s_hue(sd, ctrl->value);
+       case V4L2_CID_VFLIP:
+               return ov7670_s_vflip(sd, ctrl->value);
+       case V4L2_CID_HFLIP:
+               return ov7670_s_hflip(sd, ctrl->value);
+       }
+       return -EINVAL;
+}
 
-static int ov7670_queryctrl(struct i2c_client *client,
-               struct v4l2_queryctrl *qc)
+static int ov7670_g_chip_ident(struct v4l2_subdev *sd,
+               struct v4l2_dbg_chip_ident *chip)
 {
-       struct ov7670_control *ctrl = ov7670_find_control(qc->id);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-       if (ctrl == NULL)
-               return -EINVAL;
-       *qc = ctrl->qc;
-       return 0;
+       return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0);
 }
 
-static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 {
-       struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       unsigned char val = 0;
        int ret;
 
-       if (octrl == NULL)
+       if (!v4l2_chip_match_i2c_client(client, &reg->match))
                return -EINVAL;
-       ret = octrl->query(client, &ctrl->value);
-       if (ret >= 0)
-               return 0;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       ret = ov7670_read(sd, reg->reg & 0xff, &val);
+       reg->val = val;
+       reg->size = 1;
        return ret;
 }
 
-static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 {
-       struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
-       int ret;
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-       if (octrl == NULL)
+       if (!v4l2_chip_match_i2c_client(client, &reg->match))
                return -EINVAL;
-       ret =  octrl->tweak(client, ctrl->value);
-       if (ret >= 0)
-               return 0;
-       return ret;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff);
+       return 0;
 }
+#endif
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops ov7670_core_ops = {
+       .g_chip_ident = ov7670_g_chip_ident,
+       .g_ctrl = ov7670_g_ctrl,
+       .s_ctrl = ov7670_s_ctrl,
+       .queryctrl = ov7670_queryctrl,
+       .reset = ov7670_reset,
+       .init = ov7670_init,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .g_register = ov7670_g_register,
+       .s_register = ov7670_s_register,
+#endif
+};
 
+static const struct v4l2_subdev_video_ops ov7670_video_ops = {
+       .enum_fmt = ov7670_enum_fmt,
+       .try_fmt = ov7670_try_fmt,
+       .s_fmt = ov7670_s_fmt,
+       .s_parm = ov7670_s_parm,
+       .g_parm = ov7670_g_parm,
+};
 
+static const struct v4l2_subdev_ops ov7670_ops = {
+       .core = &ov7670_core_ops,
+       .video = &ov7670_video_ops,
+};
 
+/* ----------------------------------------------------------------------- */
 
-
-
-/*
- * Basic i2c stuff.
- */
-static struct i2c_driver ov7670_driver;
-
-static int ov7670_attach(struct i2c_adapter *adapter)
+static int ov7670_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
 {
-       int ret;
-       struct i2c_client *client;
+       struct v4l2_subdev *sd;
        struct ov7670_info *info;
+       int ret;
 
-       /*
-        * For now: only deal with adapters we recognize.
-        */
-       if (adapter->id != I2C_HW_SMBUS_CAFE)
-               return -ENODEV;
-
-       client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL);
-       if (! client)
+       info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL);
+       if (info == NULL)
                return -ENOMEM;
-       client->adapter = adapter;
-       client->addr = OV7670_I2C_ADDR;
-       client->driver = &ov7670_driver,
-       strcpy(client->name, "OV7670");
-       /*
-        * Set up our info structure.
-        */
-       info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL);
-       if (! info) {
-               ret = -ENOMEM;
-               goto out_free;
+       sd = &info->sd;
+       v4l2_i2c_subdev_init(sd, client, &ov7670_ops);
+
+       /* Make sure it's an ov7670 */
+       ret = ov7670_detect(sd);
+       if (ret) {
+               v4l_dbg(1, debug, client,
+                       "chip found @ 0x%x (%s) is not an ov7670 chip.\n",
+                       client->addr << 1, client->adapter->name);
+               kfree(info);
+               return ret;
        }
+       v4l_info(client, "chip found @ 0x%02x (%s)\n",
+                       client->addr << 1, client->adapter->name);
+
        info->fmt = &ov7670_formats[0];
        info->sat = 128;        /* Review this */
-       i2c_set_clientdata(client, info);
 
-       /*
-        * Make sure it's an ov7670
-        */
-       ret = ov7670_detect(client);
-       if (ret)
-               goto out_free_info;
-       i2c_attach_client(client);
        return 0;
-
-  out_free_info:
-       kfree(info);
-  out_free:
-       kfree(client);
-       return ret;
 }
 
 
-static int ov7670_detach(struct i2c_client *client)
+static int ov7670_remove(struct i2c_client *client)
 {
-       i2c_detach_client(client);
-       kfree(i2c_get_clientdata(client));
-       kfree(client);
-       return 0;
-}
-
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
-static int ov7670_command(struct i2c_client *client, unsigned int cmd,
-               void *arg)
-{
-       switch (cmd) {
-       case VIDIOC_INT_G_CHIP_IDENT:
-               * (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670;
-               return 0;
-
-       case VIDIOC_INT_RESET:
-               ov7670_reset(client);
-               return 0;
-
-       case VIDIOC_INT_INIT:
-               return ov7670_init(client);
-
-       case VIDIOC_ENUM_FMT:
-               return ov7670_enum_fmt(client, (struct v4l2_fmtdesc *) arg);
-       case VIDIOC_TRY_FMT:
-               return ov7670_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL);
-       case VIDIOC_S_FMT:
-               return ov7670_s_fmt(client, (struct v4l2_format *) arg);
-       case VIDIOC_QUERYCTRL:
-               return ov7670_queryctrl(client, (struct v4l2_queryctrl *) arg);
-       case VIDIOC_S_CTRL:
-               return ov7670_s_ctrl(client, (struct v4l2_control *) arg);
-       case VIDIOC_G_CTRL:
-               return ov7670_g_ctrl(client, (struct v4l2_control *) arg);
-       case VIDIOC_S_PARM:
-               return ov7670_s_parm(client, (struct v4l2_streamparm *) arg);
-       case VIDIOC_G_PARM:
-               return ov7670_g_parm(client, (struct v4l2_streamparm *) arg);
-       }
-       return -EINVAL;
+       v4l2_device_unregister_subdev(sd);
+       kfree(to_state(sd));
+       return 0;
 }
 
-
-
-static struct i2c_driver ov7670_driver = {
-       .driver = {
-               .name = "ov7670",
-       },
-       .id             = I2C_DRIVERID_OV7670,
-       .class          = I2C_CLASS_CAM_DIGITAL,
-       .attach_adapter = ov7670_attach,
-       .detach_client  = ov7670_detach,
-       .command        = ov7670_command,
+static const struct i2c_device_id ov7670_id[] = {
+       { "ov7670", 0 },
+       { }
 };
+MODULE_DEVICE_TABLE(i2c, ov7670_id);
 
-
-/*
- * Module initialization
- */
-static int __init ov7670_mod_init(void)
-{
-       printk(KERN_NOTICE "OmniVision ov7670 sensor driver, at your service\n");
-       return i2c_add_driver(&ov7670_driver);
-}
-
-static void __exit ov7670_mod_exit(void)
-{
-       i2c_del_driver(&ov7670_driver);
-}
-
-module_init(ov7670_mod_init);
-module_exit(ov7670_mod_exit);
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+       .name = "ov7670",
+       .probe = ov7670_probe,
+       .remove = ov7670_remove,
+       .id_table = ov7670_id,
+};