V4L/DVB (11113): ov7670: convert to v4l2_subdev

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Acked-by: Jonathan Corbet <corbet@lwn.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index 003120c..78bd430 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -12,18 +12,22 @@
  */
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/slab.h>
+#include <linux/i2c.h>
 #include <linux/delay.h>
 #include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <linux/i2c.h>
+#include <media/v4l2-i2c-drv-legacy.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.
@@ -189,11 +193,16 @@
  */
 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);
+}
 
 
 
@@ -400,24 +409,27 @@
  * 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);
+	ret = i2c_smbus_read_byte_data(client, reg);
 	if (ret >= 0) {
-		*value = (unsigned char) ret;
+		*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)
 {
-	int ret = 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;
@@ -427,10 +439,10 @@
 /*
  * 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++;
@@ -442,34 +454,35 @@
 /*
  * 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)
@@ -477,12 +490,12 @@
 	/*
 	 * 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 */
@@ -627,7 +640,7 @@
 /*
  * 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;
@@ -637,26 +650,26 @@
  * 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;
 
@@ -671,7 +684,8 @@
 }
 
 
-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)
 {
@@ -715,18 +729,23 @@
 	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, clkrc;
+	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;
 	/*
@@ -735,7 +754,7 @@
 	 * the colors.
 	 */
 	if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) {
-		ret = ov7670_read(c, REG_CLKRC, &clkrc);
+		ret = ov7670_read(sd, REG_CLKRC, &clkrc);
 		if (ret)
 			return ret;
 	}
@@ -747,20 +766,20 @@
 	 */
 	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;
 
 	if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 && ret == 0)
-		ret = ov7670_write(c, REG_CLKRC, clkrc);
+		ret = ov7670_write(sd, REG_CLKRC, clkrc);
 	return ret;
 }
 
@@ -768,7 +787,7 @@
  * 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;
@@ -776,7 +795,7 @@
 
 	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));
@@ -788,7 +807,7 @@
 	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;
@@ -802,7 +821,7 @@
 	/*
 	 * 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)
@@ -816,7 +835,7 @@
 	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);
 }
 
 
@@ -829,7 +848,7 @@
 
 
 
-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;
@@ -839,7 +858,7 @@
 	 * 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++) {
@@ -858,9 +877,9 @@
 			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;
 }
 
@@ -943,29 +962,29 @@
 
 
 
-static int ov7670_t_sat(struct i2c_client *client, int value)
+static int ov7670_t_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_q_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_t_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;
 
@@ -973,14 +992,14 @@
 		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_q_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;
@@ -994,8 +1013,7 @@
 {
 	if ((v & 0x80) == 0)
 		return v + 128;
-	else
-		return 128 - (v & 0x7f);
+	return 128 - (v & 0x7f);
 }
 
 
@@ -1003,105 +1021,104 @@
 {
 	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_t_brightness(struct v4l2_subdev *sd, int value)
 {
 	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_q_brightness(struct v4l2_subdev *sd, __s32 *value)
 {
 	unsigned char v = 0;
-	int ret = ov7670_read(client, REG_BRIGHT, &v);
+	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_t_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_q_contrast(struct v4l2_subdev *sd, __s32 *value)
 {
 	unsigned char v = 0;
-	int ret = ov7670_read(client, REG_CONTRAS, &v);
+	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_q_hflip(struct v4l2_subdev *sd, __s32 *value)
 {
 	int ret;
 	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_t_hflip(struct v4l2_subdev *sd, int value)
 {
 	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_q_vflip(struct v4l2_subdev *sd, __s32 *value)
 {
 	int ret;
 	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_t_vflip(struct v4l2_subdev *sd, int value)
 {
 	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);
+	int (*query)(struct v4l2_subdev *sd, __s32 *value);
+	int (*tweak)(struct v4l2_subdev *sd, int value);
 } ov7670_controls[] =
 {
 	{
@@ -1200,7 +1217,7 @@
 }
 
 
-static int ov7670_queryctrl(struct i2c_client *client,
+static int ov7670_queryctrl(struct v4l2_subdev *sd,
 		struct v4l2_queryctrl *qc)
 {
 	struct ov7670_control *ctrl = ov7670_find_control(qc->id);
@@ -1211,161 +1228,128 @@
 	return 0;
 }
 
-static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
 	struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
 	int ret;
 
 	if (octrl == NULL)
 		return -EINVAL;
-	ret = octrl->query(client, &ctrl->value);
+	ret = octrl->query(sd, &ctrl->value);
 	if (ret >= 0)
 		return 0;
 	return ret;
 }
 
-static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
 	struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
 	int ret;
 
 	if (octrl == NULL)
 		return -EINVAL;
-	ret =  octrl->tweak(client, ctrl->value);
+	ret = octrl->tweak(sd, ctrl->value);
 	if (ret >= 0)
 		return 0;
 	return ret;
 }
 
-
-
-
-
-
-/*
- * Basic i2c stuff.
- */
-static struct i2c_driver ov7670_driver;
-
-static int ov7670_attach(struct i2c_adapter *adapter)
+static int ov7670_g_chip_ident(struct v4l2_subdev *sd,
+		struct v4l2_dbg_chip_ident *chip)
 {
-	int ret;
-	struct i2c_client *client;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0);
+}
+
+static int ov7670_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+	return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+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,
+};
+
+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,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int ov7670_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	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)
+	if (client->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;
-	ret = i2c_attach_client(client);
-	if (ret)
-		goto out_free_info;
-	return 0;
-
-  out_free_info:
-	kfree(info);
-  out_free:
-	kfree(client);
-	return ret;
-}
-
-
-static int ov7670_detach(struct i2c_client *client)
-{
-	i2c_detach_client(client);
-	kfree(i2c_get_clientdata(client));
-	kfree(client);
 	return 0;
 }
 
 
-static int ov7670_command(struct i2c_client *client, unsigned int cmd,
-		void *arg)
+static int ov7670_remove(struct i2c_client *client)
 {
-	switch (cmd) {
-	case VIDIOC_DBG_G_CHIP_IDENT:
-		return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
-	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,
-	.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",
+	.command = ov7670_command,
+	.probe = ov7670_probe,
+	.remove = ov7670_remove,
+	.legacy_class = I2C_CLASS_TV_ANALOG,
+	.id_table = ov7670_id,
+};