Update from Jon Corbet:

Andres Salomon dilinger at debian.org
Tue Nov 7 22:38:27 EST 2006


Commit:     12553c53dc5a454cae694219c43e94a90c359f0d
Parent:     655c540e8ec8404d2112c6b670bd657ad2651d8c
commit 12553c53dc5a454cae694219c43e94a90c359f0d
Author:     Andres Salomon <dilinger at debian.org>
AuthorDate: Fri Oct 27 23:52:31 2006 -0400
Commit:     Andres Salomon <dilinger at debian.org>
CommitDate: Fri Oct 27 23:52:31 2006 -0400

    Update from Jon Corbet:
    
     - The OV7670 layer has been redone and no longer uses the ovcamchip
       layer.  So configurations will need to change; ovcamchip can be
       configured out, and ov7670 needs to be added - something which "make
       oldconfig" should do automatically.
    
     - There's support for CIF and QVGA window sizes.  CIF gives
       hallucinogenic colors; I need some guidance from OmniVision on that.
       QVGA works, though I notice I get a bit of noise on the left side
       on the b-test board; my test system didn't have that.  Will tweak
       accordingly.
    
     - A new load-time option controls image flipping.  Load with "flip=1"
       to invert the image.  Note that I had flip wired on in previous
       versions, so you probably want it off.
    
    There's another option, min_buffers, which is currently set to one.
    Increasing it (to six, say) makes mplayer a bit more resistant to image
    jitter; it should probably be configured that way in build images for
    test systems.
---
 drivers/media/video/Kconfig                    |   49 +
 drivers/media/video/Makefile                   |    1 
 drivers/media/video/m88alp01-ccic.c            |  312 ++-----
 drivers/media/video/m88alp01-regs.h            |    7 
 drivers/media/video/ov7670.c                   | 1032 ++++++++++++++++++++++++
 drivers/media/video/ovcamchip/Makefile         |    2 
 drivers/media/video/ovcamchip/ov7670.c         |  794 ------------------
 drivers/media/video/ovcamchip/ovcamchip_core.c |   24 -
 include/linux/i2c-id.h                         |    1 
 include/media/ovcamchip.h                      |    5 
 include/media/v4l2-common.h                    |    7 
 11 files changed, 1177 insertions(+), 1057 deletions(-)

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 7a53c42..72f7070 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -29,25 +29,6 @@ config VIDEO_HELPER_CHIPS_AUTO
 
 	  In doubt, say Y.
 
-config VIDEO_OVCAMCHIP
-	tristate "OmniVision Camera Chip support"
-	depends on I2C 
-	---help---
-	  Support for the OmniVision OV6xxx and OV7xxx series of camera chips.
-	  This driver is intended to be used with the ov511 and w9968cf USB
-	  camera drivers.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called ovcamchip.
-
-config VIDEO_M88ALP01_CCIC
-	tristate "Marvell 88ALP01 CMOS Camera Controller support"
-	depends on I2C && VIDEO_V4L2
-	---help---
-	  This is a video4linux2 driver for the Marvell 88ALP01 integrated
-	  CMOS camera controller.  It currently requires the "ovcamchip"
-	  module.
-
 #
 # Encoder / Decoder module configuration
 #
@@ -203,6 +184,14 @@ config VIDEO_KS0127
 	  To compile this driver as a module, choose M here: the
 	  module will be called ks0127.
 
+config VIDEO_OV7670
+	tristate "OmniVision OV7670 sensor support"
+	depends on I2C && VIDEO_V4L2
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV7670 VGA camera.  It currently only works with the M88ALP01
+	  controller.
+
 config VIDEO_SAA7110
 	tristate "Philips SAA7110 video decoder"
 	depends on VIDEO_V4L1
@@ -689,6 +678,17 @@ config VIDEO_M32R_AR_M64278
 	  To compile this driver as a module, choose M here: the
 	  module will be called arv.
 
+config VIDEO_M88ALP01_CCIC
+	tristate "Marvell 88ALP01 CMOS Camera Controller support"
+	depends on I2C && VIDEO_V4L2
+	select VIDEO_OV7670
+	---help---
+	  This is a video4linux2 driver for the Marvell 88ALP01 integrated
+	  CMOS camera controller.
+
+
+
+
 #
 # USB Multimedia device configuration
 #
@@ -702,6 +702,17 @@ source "drivers/media/video/usbvideo/Kco
 
 source "drivers/media/video/et61x251/Kconfig"
 
+config VIDEO_OVCAMCHIP
+	tristate "OmniVision Camera Chip support"
+	depends on I2C && VIDEO_V4L1
+	---help---
+	  Support for the OmniVision OV6xxx and OV7xxx series of camera chips.
+	  This driver is intended to be used with the ov511 and w9968cf USB
+	  camera drivers.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ovcamchip.
+
 config USB_W9968CF
 	tristate "USB W996[87]CF JPEG Dual Mode Camera support"
 	depends on USB && VIDEO_V4L1 && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index e582ca8..6dfe9c8 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083
 obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
 
 obj-$(CONFIG_VIDEO_M88ALP01_CCIC) += m88alp01-ccic.o
+obj-$(CONFIG_VIDEO_OV7670) 	+= ov7670.o
 
 obj-$(CONFIG_USB_DABUSB)        += dabusb.o
 obj-$(CONFIG_USB_OV511)         += ov511.o
diff --git a/drivers/media/video/m88alp01-ccic.c b/drivers/media/video/m88alp01-ccic.c
index 22f505f..6a989ec 100644
--- a/drivers/media/video/m88alp01-ccic.c
+++ b/drivers/media/video/m88alp01-ccic.c
@@ -21,6 +21,7 @@ #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
 #include <linux/videodev2.h>
+#include <media/v4l2-common.h>
 #include <linux/device.h>
 #include <linux/wait.h>
 #include <linux/list.h>
@@ -29,7 +30,6 @@ #include <linux/delay.h>
 #include <linux/debugfs.h>
 #include <linux/jiffies.h>
 #include <linux/vmalloc.h>
-#include <media/ovcamchip.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -63,7 +63,7 @@ MODULE_SUPPORTED_DEVICE("Video");
  */
 
 #define MAX_DMA_BUFS 3
-static int alloc_bufs_at_load = 1;
+static int alloc_bufs_at_load = 0;
 module_param(alloc_bufs_at_load, bool, 0444);
 MODULE_PARM_DESC(alloc_bufs_at_load,
 		"Non-zero value causes DMA buffers to be allocated at module "
@@ -77,7 +77,7 @@ MODULE_PARM_DESC(n_dma_bufs,
 		"The number of DMA buffers to allocate.  Can be either two "
 		"(saves memory, makes timing tighter) or three.");
 
-static int dma_buf_size = 640 * 480 * 2;  /* Worst case */
+static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2;  /* Worst case */
 module_param(dma_buf_size, uint, 0444);
 MODULE_PARM_DESC(dma_buf_size,
 		"The size of the allocated DMA buffers.  If actual operating "
@@ -97,6 +97,12 @@ MODULE_PARM_DESC(max_buffers,
 		"will be allowed to allocate.  These buffers are big and live "
 		"in vmalloc space.");
 
+static int flip = 0;
+module_param(flip, bool, 0444);
+MODULE_PARM_DESC(flip,
+		"If set, the sensor will be instructed to flip the image "
+		"vertically.");
+
 
 enum m88_state {
 	S_NOTREADY,	/* Not yet initialized */
@@ -129,7 +135,7 @@ struct m88_camera
 	enum m88_state state;  
 	unsigned long flags;   		/* Buffer status, mainly (dev_lock) */
 	int users;			/* How many open FDs */
-	struct m88_user *owner; 	/* Who has data access (v4l2) */
+	struct file *owner;	 	/* Who has data access (v4l2) */
 
 	/*
 	 * Subsystem structures.
@@ -160,7 +166,7 @@ struct m88_camera
 	struct tasklet_struct s_tasklet; 
 
 	/* Current operating parameters */
-	int sensor_type;		/* Currently ov7670 only */
+	enum v4l2_chip_ident sensor_type;		/* Currently ov7670 only */
 	struct v4l2_pix_format pix_format;
 	
 	/* Locks */
@@ -213,15 +219,6 @@ static void m88_set_config_needed(struct
 		clear_bit(CF_CONFIG_NEEDED, &cam->flags);
 }
 
-/*
- * V4L2 requires us to keep track of each file handle - at least, to
- * the point of knowing which one is the "owner" which is allowed to
- * actually map buffers and such.
- */
-struct m88_user {
-	struct m88_camera *cam;
-/* mapped buffers ... */
-};
 
 
 
@@ -360,11 +357,6 @@ static int m88_smbus_write_data(struct m
 	unsigned int rval;
 	unsigned long flags;
 
-	/*
-	 * For reasons unknown, ovcamchip.h defines the sensor slave IDs
-	 * shifted by one bit.  Shift them back here.
-	 */
-	addr <<= 1;
 	spin_lock_irqsave(&cam->dev_lock, flags);
 	rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
 	rval |= TWSIC0_OVMAGIC;  /* Make OV sensors work */
@@ -377,7 +369,7 @@ static int m88_smbus_write_data(struct m
 	rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
 	m88_reg_write(cam, REG_TWSIC1, rval);
 	spin_unlock_irqrestore(&cam->dev_lock, flags);
-	mdelay(2); /* Required or things flake */
+	msleep(2); /* Required or things flake */
 
 	wait_event_timeout(cam->smbus_wait, m88_smbus_write_done(cam),
 			M88_SMBUS_TIMEOUT);
@@ -425,11 +417,6 @@ static int m88_smbus_read_data(struct m8
 	unsigned int rval;
 	unsigned long flags;
 
-	/*
-	 * For reasons unknown, ovcamchip.h defines the sensor slave IDs
-	 * shifted by one bit.  Shift them back here.
-	 */
-	addr <<= 1;
 	spin_lock_irqsave(&cam->dev_lock, flags);
 	rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
 	rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
@@ -477,7 +464,13 @@ static int m88_smbus_xfer(struct i2c_ada
 	 * Refuse to talk to anything but OV cam chips.  We should
 	 * never even see an attempt to do so, but one never knows.
 	 */
-	if (addr != OV7xx0_SID) {
+#if 0  /* client needs to talk during attach process */
+	if (! cam->sensor) {
+		cam_err(cam, "SMBUS xfer with no client\n");
+		return -EINVAL;
+	}
+#endif
+	if (cam->sensor && addr != cam->sensor->addr) {
 		cam_err(cam, "funky smbus addr %d\n", addr);
 		return -EINVAL;
 	}
@@ -528,7 +521,8 @@ static int m88_smbus_attach(struct i2c_c
 	/*
 	 * Don't talk to chips we don't recognize.
 	 */
-	if (client->driver->id == I2C_DRIVERID_OVCAMCHIP) {
+	cam_err(cam, "smbus_attach id = %d\n", client->driver->id);
+	if (client->driver->id == I2C_DRIVERID_OV7670) {
 		cam->sensor = client;
 		return m88_cam_init(cam);
 	}
@@ -826,7 +820,7 @@ static int __m88_cam_cmd(struct m88_came
 static int __m88_cam_reset(struct m88_camera *cam)
 {
 	int zero = 0;
-	return __m88_cam_cmd(cam, OVCAMCHIP_CMD_INITIALIZE, &zero);
+	return __m88_cam_cmd(cam, VIDIOC_INT_RESET, &zero);
 }
 
 /*
@@ -844,16 +838,16 @@ static int m88_cam_init(struct m88_camer
 	ret = __m88_cam_reset(cam);
 	if (ret)
 		goto out;
-	ret = __m88_cam_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE, &cam->sensor_type);
+	ret = __m88_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type);
 	if (ret)
 		goto out;
-	if (cam->sensor->addr != OV7xx0_SID) {
-		cam_err(cam, "Unsupported sensor addr %d", cam->sensor->addr);
+//	if (cam->sensor->addr != OV7xx0_SID) {
+	if (cam->sensor_type != V4L2_IDENT_OV7670) {
+		cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr);
 		ret = -EINVAL;
 		goto out;
 	}
 /* Get/set parameters? */
-
 	ret = 0;
 	cam->state = S_IDLE;
   out:
@@ -865,26 +859,33 @@ static int m88_cam_init(struct m88_camer
  * Configure the sensor to match the parameters we have.  Caller should
  * hold s_mutex
  */
-static int m88_cam_configure(struct m88_camera *cam)
+static int m88_cam_set_flip(struct m88_camera *cam)
 {
-#if 0
-	int ret;
-	struct ovcamchip_window win;
+	struct v4l2_control ctrl;
 
-	if (cam->state != S_IDLE)
-		return -EINVAL;
-	win.x = win.y = 0;
-	win.width = cam->pix_format.width;
-	win.height = cam->pix_format.height;
-	ret = __m88_cam_cmd(cam, OVCAMCHIP_CMD_S_MODE, &win);
-	return ret;
-#endif
+	memset(&ctrl, 0, sizeof(ctrl));
+	ctrl.id = V4L2_CID_VFLIP;
+	ctrl.value = flip;
+	return __m88_cam_cmd(cam, VIDIOC_S_CTRL, &ctrl);
+}
+
+
+static int m88_cam_configure(struct m88_camera *cam)
+{
 	struct v4l2_format fmt;
+	int ret, zero = 0;
 	
 	if (cam->state != S_IDLE)
 		return -EINVAL;
 	fmt.fmt.pix = cam->pix_format;
-	return __m88_cam_cmd(cam, VIDIOC_S_FMT, &fmt);
+	ret = __m88_cam_cmd(cam, VIDIOC_INT_INIT, &zero);
+	if (ret == 0) 
+		ret = __m88_cam_cmd(cam, VIDIOC_S_FMT, &fmt);
+	/*
+	 * OV7670 does weird things if flip is set *before* format...
+	 */
+	ret += m88_cam_set_flip(cam);
+	return ret;
 }
 
 /* -------------------------------------------------------------------- */
@@ -1034,8 +1035,7 @@ static int m88_read_setup(struct m88_cam
 static ssize_t m88_v4l_read(struct file *filp,
 		char __user *buffer, size_t len, loff_t *pos)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 	int ret;
 	
 	/*
@@ -1061,11 +1061,11 @@ static ssize_t m88_v4l_read(struct file 
 	 * v4l2: multiple processes can open the device, but only
 	 * one gets to grab data from it.
 	 */
-	if (cam->owner && cam->owner != user) {
+	if (cam->owner && cam->owner != filp) {
 		ret = -EBUSY;
 		goto out_unlock;
 	}
-	cam->owner = user;
+	cam->owner = filp;
 
 	/*
 	 * Do setup if need be.
@@ -1112,8 +1112,7 @@ static ssize_t m88_v4l_read(struct file 
 static int m88_vidioc_streamon(struct file *filp, void *priv,
 		enum v4l2_buf_type type)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 	int ret = -EINVAL;
 
 	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1135,8 +1134,7 @@ static int m88_vidioc_streamon(struct fi
 static int m88_vidioc_streamoff(struct file *filp, void *priv,
 		enum v4l2_buf_type type)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 	int ret = -EINVAL;
 
 	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1197,8 +1195,9 @@ static int m88_free_sio_buffers(struct m
 	 */
 	for (i = 0; i < cam->n_sbufs; i++)
 		vfree(cam->sb_bufs[i].buffer);
-	kfree(cam->sb_bufs);
 	cam->n_sbufs = 0;
+	kfree(cam->sb_bufs);
+	cam->sb_bufs = NULL;
 	INIT_LIST_HEAD(&cam->sb_avail);
 	INIT_LIST_HEAD(&cam->sb_full);
 	return 0;
@@ -1209,8 +1208,7 @@ static int m88_free_sio_buffers(struct m
 static int m88_vidioc_reqbufs(struct file *filp, void *priv,
 		struct v4l2_requestbuffers *req)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 	int ret;
 
 	/*
@@ -1237,11 +1235,11 @@ static int m88_vidioc_reqbufs(struct fil
 	 * right thing in S_SPECREAD by shutting things down, but it
 	 * probably doesn't matter.
 	 */
-	if (cam->state != S_IDLE || (cam->owner && cam->owner != user)) {
+	if (cam->state != S_IDLE || (cam->owner && cam->owner != filp)) {
 		ret = -EBUSY;
 		goto out;
 	}
-	cam->owner = user;
+	cam->owner = filp;
 
 	if (req->count < min_buffers)
 		req->count = min_buffers;
@@ -1280,8 +1278,7 @@ static int m88_vidioc_reqbufs(struct fil
 static int m88_vidioc_querybuf(struct file *filp, void *priv,
 		struct v4l2_buffer *buf)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 	int ret = -EINVAL;
 
 	mutex_lock(&cam->s_mutex);
@@ -1299,8 +1296,7 @@ static int m88_vidioc_querybuf(struct fi
 static int m88_vidioc_qbuf(struct file *filp, void *priv,
 		struct v4l2_buffer *buf)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 	struct m88_sio_buffer *sbuf;
 	int ret = -EINVAL;
 	unsigned long flags;
@@ -1333,8 +1329,7 @@ static int m88_vidioc_qbuf(struct file *
 static int m88_vidioc_dqbuf(struct file *filp, void *priv,
 		struct v4l2_buffer *buf)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 	struct m88_sio_buffer *sbuf;
 	int ret = -EINVAL;
 	unsigned long flags;
@@ -1412,8 +1407,7 @@ static struct vm_operations_struct m88_v
 
 static int m88_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 	int ret = -EINVAL;
 	int i;
@@ -1452,16 +1446,11 @@ static int m88_v4l_mmap(struct file *fil
 static int m88_v4l_open(struct inode *inode, struct file *filp)
 {
 	struct m88_camera *cam;
-	struct m88_user *user;
 
 	cam = m88_find_dev(iminor(inode));
 	if (cam == NULL)
 		return -ENODEV;
-	user = kzalloc(sizeof(struct m88_user), GFP_KERNEL);
-	if (! user)
-		return -ENOMEM;
-	user->cam = cam;
-	filp->private_data = user;
+	filp->private_data = cam;
 
 	mutex_lock(&cam->s_mutex);
 	if (cam->users == 0) {
@@ -1478,12 +1467,11 @@ static int m88_v4l_open(struct inode *in
 
 static int m88_v4l_release(struct inode *inode, struct file *filp)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 	
 	mutex_lock(&cam->s_mutex);
 	(cam->users)--;
-	if (user == cam->owner) {
+	if (filp == cam->owner) {
 		m88_ctlr_stop_dma(cam);
 		m88_free_sio_buffers(cam);
 		cam->owner = NULL;
@@ -1491,7 +1479,6 @@ static int m88_v4l_release(struct inode 
 	if (cam->users == 0)
 		m88_ctlr_power_down(cam);
 	mutex_unlock(&cam->s_mutex);
-	kfree(user);
 	return 0;
 }
 
@@ -1500,8 +1487,7 @@ static int m88_v4l_release(struct inode 
 static unsigned int m88_v4l_poll(struct file *filp,
 		struct poll_table_struct *pt)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = filp->private_data;
 
 	poll_wait(filp, &cam->iowait, pt);
 	if (cam->next_buf >= 0)
@@ -1510,90 +1496,28 @@ static unsigned int m88_v4l_poll(struct 
 }
 
 
-/*
- * Controls.
- */
-static struct m88_v4l_control {
-	__u32 	c_v4l_id;	/* V4L2 control ID */
-	int	c_ov_id;	/* ovcamchip ID */
-	struct v4l2_queryctrl qc;
-} m88_controls[] =
-{
-	{
-		.c_v4l_id = V4L2_CID_BRIGHTNESS,
-		.c_ov_id = OVCAMCHIP_CID_BRIGHT,
-		.qc = {
-			.id = V4L2_CID_BRIGHTNESS,
-			.type = V4L2_CTRL_TYPE_INTEGER,
-			.name = "Brightness",
-			.minimum = 0,
-			.maximum = 255,
-			.step = 1,
-			.default_value = 0x60,   /* XXX from ovcamchip */
-			.flags = V4L2_CTRL_FLAG_SLIDER
-		}
-	},
-	{
-		.c_v4l_id = V4L2_CID_CONTRAST,
-		.c_ov_id = OVCAMCHIP_CID_CONT,
-		.qc = {
-			.id = V4L2_CID_CONTRAST,
-			.type = V4L2_CTRL_TYPE_INTEGER,
-			.name = "Contrast",
-			.minimum = 0,
-			.maximum = 255,
-			.step = 1,
-			.default_value = 0x40,   /* XXX ov7670 spec */
-			.flags = V4L2_CTRL_FLAG_SLIDER
-		}
-	},
-	{
-		.c_v4l_id = 0,
-	}
-};
-	  
-
-static struct m88_v4l_control *m88_v4l_find_ctrl(__u32 id)
-{
-	int i;
-	
-	for (i = 0; m88_controls[i].c_v4l_id != 0; i++)
-		if (m88_controls[i].c_v4l_id == id)
-			return m88_controls + i;
-	return NULL;
-}
 
 static int m88_vidioc_queryctrl(struct file *filp, void *priv,
 		struct v4l2_queryctrl *qc)
 {
-	struct m88_v4l_control *ctrl = m88_v4l_find_ctrl(qc->id);
+	struct m88_camera *cam = filp->private_data;
+	int ret;
 	
-	if (ctrl == NULL)
-		return -EINVAL;
-	*qc = ctrl->qc;
-	return 0;
+	mutex_lock(&cam->s_mutex);
+	ret = __m88_cam_cmd(cam, VIDIOC_QUERYCTRL, qc);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
 }
 
 
 static int m88_vidioc_g_ctrl(struct file *filp, void *priv,
 		struct v4l2_control *ctrl)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
-	struct m88_v4l_control *mctrl;
-	int ret = -EINVAL;
-	struct ovcamchip_control ovctrl;
-	
-	mctrl = m88_v4l_find_ctrl(ctrl->id);
-	if (mctrl == NULL)
-		return -EINVAL;
+	struct m88_camera *cam = filp->private_data;
+	int ret;
 	
 	mutex_lock(&cam->s_mutex);
-	/* FIXME for non-integer values */
-	ovctrl.id = mctrl->c_ov_id;
-	ret = __m88_cam_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ovctrl);
-	if (ret == 0) 
-		ctrl->value = (ovctrl.value >> 8) & 0xff;
+	ret = __m88_cam_cmd(cam, VIDIOC_G_CTRL, ctrl);
 	mutex_unlock(&cam->s_mutex);
 	return ret;
 }
@@ -1602,27 +1526,19 @@ static int m88_vidioc_g_ctrl(struct file
 static int m88_vidioc_s_ctrl(struct file *filp, void *priv,
 		struct v4l2_control *ctrl)
 {
-	struct m88_user *user = filp->private_data;
-	struct m88_camera *cam = user->cam;
-	struct m88_v4l_control *mctrl;
-	int ret = -EINVAL;
-	struct ovcamchip_control ovctrl;
-	
-	mctrl = m88_v4l_find_ctrl(ctrl->id);
-	if (mctrl == NULL)
-		return -EINVAL;
+	struct m88_camera *cam = filp->private_data;
+	int ret;
 	
 	mutex_lock(&cam->s_mutex);
-	/* FIXME for non-integer values */
-	ovctrl.id = mctrl->c_ov_id;
-	ovctrl.value = ctrl->value << 8;
-	ret = __m88_cam_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ovctrl);
+	ret = __m88_cam_cmd(cam, VIDIOC_S_CTRL, ctrl);
 	mutex_unlock(&cam->s_mutex);
 	return ret;
 }
 
 
 
+
+
 static int m88_vidioc_querycap(struct file *file, void *priv,
 		struct v4l2_capability *cap)
 {
@@ -1639,19 +1555,18 @@ static int m88_vidioc_querycap(struct fi
  * The default format we use until somebody says otherwise.
  */
 static struct v4l2_pix_format m88_def_pix_format = {
-	.width		= 640,
-	.height		= 480,
+	.width		= VGA_WIDTH,
+	.height		= VGA_HEIGHT,
 	.pixelformat	= V4L2_PIX_FMT_YUYV,
 	.field		= V4L2_FIELD_NONE,
-	.bytesperline	= 640*2,
-	.sizeimage	= 640*480*2,
+	.bytesperline	= VGA_WIDTH*2,
+	.sizeimage	= VGA_WIDTH*VGA_HEIGHT*2,
 };
 
 static int m88_vidioc_enum_fmt_cap(struct file *filp,
 		void *priv, struct v4l2_fmtdesc *fmt)
 {
-	struct m88_user *user = priv;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = priv;
 	int ret;
 
 	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1666,47 +1581,7 @@ static int m88_vidioc_enum_fmt_cap(struc
 static int m88_vidioc_try_fmt_cap (struct file *filp, void *priv,
 		struct v4l2_format *fmt)
 {
-	struct m88_user *user = priv;
-	struct m88_camera *cam = user->cam;
-
-#if 0
-	int index, sizeindex;
-	struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-	for (index = 0; index < M88_N_FORMATS; index++)
-		if (m88_format_list[index].pixelformat == pix->pixelformat)
-			break;
-	if (index >= M88_N_FORMATS)
-		return -EINVAL;
-	/*
-	 * 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) {
-		cam_warn(cam, "Unsupported field requested");
-		return -EINVAL;
-	}
-	/*
-	 * Round requested image size down to the nearest
-	 * we support, but not below the smallest.
-	 */
-	if (pix->height < m88_height_options[0] ||
-			pix->width < m88_width_options[0])
-		sizeindex = 0;
-	else {
-		for (sizeindex = N_WINDOW_SIZES-1; sizeindex > 0; sizeindex--)
-			if (pix->height >= m88_height_options[sizeindex] &&
-			    pix->width >= m88_width_options[sizeindex])
-				break;
-	}
-	pix->width = m88_width_options[sizeindex];
-	pix->height = m88_height_options[sizeindex];
-	/* Must be a multiple of 4 - will be with std widths */
-	pix->bytesperline = pix->width*m88_format_list[index].bytes_per_pixel;
-	pix->sizeimage = pix->height*pix->bytesperline;
-	return 0;
-#endif
+	struct m88_camera *cam = priv;
 	int ret;
 
 	mutex_lock(&cam->s_mutex);
@@ -1718,8 +1593,7 @@ #endif
 static int m88_vidioc_s_fmt_cap(struct file *filp, void *priv,
 		struct v4l2_format *fmt)
 {
-	struct m88_user *user = priv;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = priv;
 	int ret;
 
 	/*
@@ -1769,8 +1643,7 @@ static int m88_vidioc_s_fmt_cap(struct f
 static int m88_vidioc_g_fmt_cap(struct file *filp, void *priv,
 		struct v4l2_format *f)
 {
-	struct m88_user *user = priv;
-	struct m88_camera *cam = user->cam;
+	struct m88_camera *cam = priv;
 	
 	f->fmt.pix = cam->pix_format;
 	return 0;
@@ -1971,6 +1844,11 @@ static void m88_frame_tasklet(unsigned l
 
 static void m88_frame_complete(struct m88_camera *cam, int frame)
 {
+	/*
+	 * Basic frame housekeeping.
+	 */
+	if (test_bit(frame, &cam->flags) && printk_ratelimit())
+		cam_err(cam, "Frame overrun on %d, frames lost\n", frame);		
 	set_bit(frame, &cam->flags);
 	clear_bit(CF_DMA_ACTIVE, &cam->flags);
 	if (cam->next_buf < 0)
@@ -2132,11 +2010,13 @@ static ssize_t m88_dfs_read_cam(struct f
 	char *s = m88_debug_buf;
 	int offset;
 
+	if (! cam->sensor)
+		return -EINVAL;
 	for (offset = 0x0; offset < 0x8a; offset++)
 	{
 		u8 v;
 
-		m88_smbus_read_data(cam, OV7xx0_SID, offset, &v);
+		m88_smbus_read_data(cam, cam->sensor->addr, offset, &v);
 		s += sprintf(s, "%02x: %02x\n", offset, v);
 	}
 	return simple_read_from_buffer(buf, count, ppos, m88_debug_buf,
@@ -2304,8 +2184,6 @@ static void m88_shutdown(struct m88_came
 	m88_free_dma_bufs(cam);
 	free_irq(cam->pdev->irq, cam);
 	pci_iounmap(cam->pdev, cam->regs);
-	release_mem_region(pci_resource_start(cam->pdev, 0),
-			pci_resource_len(cam->pdev, 0));
 	video_unregister_device(&cam->v4ldev);
 	/* kfree(cam); done in v4l_release () */
 }
@@ -2360,7 +2238,7 @@ static int __init m88_init(void)
 		printk(KERN_ERR "Unable to register m881000-ccic driver\n");
 		goto out;
 	}
-	request_module("ovcamchip");
+	request_module("ov7670");  /* FIXME want something more general */
 	ret = 0;
 
   out:
diff --git a/drivers/media/video/m88alp01-regs.h b/drivers/media/video/m88alp01-regs.h
index aaf6b25..b2c22a0 100644
--- a/drivers/media/video/m88alp01-regs.h
+++ b/drivers/media/video/m88alp01-regs.h
@@ -151,3 +151,10 @@ #define REG_GL_IMASK   0x300c  /* Interr
 #define   GIMSK_CCIC_EN          0x00000004    /* CCIC Interrupt enable */
 
 #define REG_LEN                REG_GL_IMASK + 4
+
+
+/*
+ * Useful stuff that probably belongs somewhere global.
+ */
+#define VGA_WIDTH	640
+#define VGA_HEIGHT	480
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
new file mode 100644
index 0000000..fefa812
--- /dev/null
+++ b/drivers/media/video/ov7670.c
@@ -0,0 +1,1032 @@
+/*
+ * A newly reworked OmniVision 7670 sensor driver.
+ *
+ * Copyright 2006 One Laptop Per Child Association, Inc.
+ *
+ * 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>
+
+
+MODULE_AUTHOR("Jonathan Corbet <corbet at lwn.net.");
+MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
+MODULE_LICENSE("GPL");
+
+/*
+ * Basic window sizes.  These probably belong somewhere more globally
+ * useful.
+ */
+#define VGA_WIDTH	640
+#define VGA_HEIGHT	480
+#define QVGA_WIDTH	320
+#define QVGA_HEIGHT	240
+#define CIF_WIDTH	352
+#define CIF_HEIGHT	288
+#define QCIF_WIDTH	176
+#define	QCIF_HEIGHT	144
+
+/*
+ * The 7670 sits on i2c with ID 0x42
+ */
+#define OV7670_I2C_ADDR 0x42
+
+/* Registers */
+#define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
+#define REG_BLUE	0x01	/* blue gain */
+#define REG_RED		0x02	/* red gain */
+#define REG_VREF	0x03	/* Pieces of GAIN, VSTART, VSTOP */
+#define REG_COM1	0x04	/* Control 1 */
+#define  COM1_CCIR656	  0x40  /* CCIR656 enable */
+#define REG_BAVE	0x05	/* U/B Average level */
+#define REG_GbAVE	0x06	/* Y/Gb Average level */
+#define REG_AECHH	0x07	/* AEC MS 5 bits */
+#define REG_RAVE	0x08	/* V/R Average level */
+#define REG_COM2	0x09	/* Control 2 */
+#define  COM2_SSLEEP	  0x10	/* Soft sleep mode */
+#define REG_PID		0x0a	/* Product ID MSB */
+#define REG_VER		0x0b	/* Product ID LSB */
+#define REG_COM3	0x0c	/* Control 3 */
+#define  COM3_SWAP	  0x40	  /* Byte swap */
+#define  COM3_SCALEEN	  0x08	  /* Enable scaling */
+#define  COM3_DCWEN	  0x04	  /* Enable downsamp/crop/window */
+#define REG_COM4	0x0d	/* Control 4 */
+#define REG_COM5	0x0e	/* All "reserved" */
+#define REG_COM6	0x0f	/* Control 6 */
+#define REG_AECH	0x10	/* More bits of AEC value */
+#define REG_CLKRC	0x11	/* Clocl control */
+#define   CLK_EXT	  0x40	  /* Use external clock directly */
+#define   CLK_SCALE	  0x3f	  /* Mask for internal clock scale */
+#define REG_COM7	0x12	/* Control 7 */
+#define   COM7_RESET	  0x80	  /* Register reset */
+#define   COM7_FMT_MASK	  0x38
+#define   COM7_FMT_VGA	  0x00
+#define	  COM7_FMT_CIF	  0x20	  /* CIF format */
+#define   COM7_FMT_QVGA	  0x10	  /* QVGA format */
+#define   COM7_FMT_QCIF	  0x08	  /* QCIF format */
+#define	  COM7_RGB	  0x04	  /* bits 0 and 2 - RGB format */
+#define	  COM7_YUV	  0x00	  /* YUV */
+#define	  COM7_BAYER	  0x01	  /* Bayer format */
+#define	  COM7_PBAYER	  0x05	  /* "Processed bayer" */
+#define REG_COM8	0x13	/* Control 8 */
+#define   COM8_FASTAEC	  0x80	  /* Enable fast AGC/AEC */
+#define   COM8_AECSTEP	  0x40	  /* Unlimited AEC step size */
+#define   COM8_BFILT	  0x20	  /* Band filter enable */
+#define   COM8_AGC	  0x04	  /* Auto gain enable */
+#define   COM8_AWB	  0x02	  /* White balance enable */
+#define   COM8_AEC	  0x01	  /* Auto exposure enable */
+#define REG_COM9	0x14	/* Control 9  - gain ceiling */
+#define REG_COM10	0x15	/* Control 10 */
+#define   COM10_HSYNC	  0x40	  /* HSYNC instead of HREF */
+#define   COM10_PCLK_HB	  0x20	  /* Suppress PCLK on horiz blank */
+#define   COM10_HREF_REV  0x08	  /* Reverse HREF */
+#define   COM10_VS_LEAD	  0x04	  /* VSYNC on clock leading edge */
+#define   COM10_VS_NEG	  0x02	  /* VSYNC negative */
+#define   COM10_HS_NEG	  0x01	  /* HSYNC negative */
+#define REG_HSTART	0x17	/* Horiz start high bits */
+#define REG_HSTOP	0x18	/* Horiz stop high bits */
+#define REG_VSTART	0x19	/* Vert start high bits */
+#define REG_VSTOP	0x1a	/* Vert stop high bits */
+#define REG_PSHFT	0x1b	/* Pixel delay after HREF */
+#define REG_MIDH	0x1c	/* Manuf. ID high */
+#define REG_MIDL	0x1d	/* Manuf. ID low */
+#define REG_MVFP	0x1e	/* Mirror / vflip */
+#define   MVFP_MIRROR	  0x20	  /* Mirror image */
+#define   MVFP_FLIP	  0x10	  /* Vertical flip */
+
+#define REG_AEW		0x24	/* AGC upper limit */
+#define REG_AEB		0x25	/* AGC lower limit */
+#define REG_VPT		0x26	/* AGC/AEC fast mode op region */
+#define REG_HSYST	0x30	/* HSYNC rising edge delay */
+#define REG_HSYEN	0x31	/* HSYNC falling edge delay */
+#define REG_HREF	0x32	/* HREF pieces */
+#define REG_TSLB	0x3a	/* lots of stuff */
+#define   TSLB_YLAST	  0x04	  /* UYVY or VYUY - see com13 */
+#define REG_COM11	0x3b	/* Control 11 */
+#define   COM11_NIGHT	  0x80	  /* NIght mode enable */
+#define   COM11_NMFR	  0x60	  /* Two bit NM frame rate */
+#define   COM11_HZAUTO	  0x10	  /* Auto detect 50/60 Hz */
+#define	  COM11_50HZ	  0x08	  /* Manual 50Hz select */
+#define   COM11_EXP	  0x02
+#define REG_COM12	0x3c	/* Control 12 */
+#define   COM12_HREF	  0x80	  /* HREF always */
+#define REG_COM13	0x3d	/* Control 13 */
+#define   COM13_GAMMA	  0x80	  /* Gamma enable */
+#define	  COM13_UVSAT	  0x40	  /* UV saturation auto adjustment */
+#define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
+#define REG_COM14	0x3e	/* Control 14 */
+#define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
+#define REG_EDGE	0x3f	/* Edge enhancement factor */
+#define REG_COM15	0x40	/* Control 15 */
+#define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
+#define	  COM15_R01FE	  0x80	  /*            01 to FE */
+#define   COM15_R00FF	  0xc0	  /*            00 to FF */
+#define   COM15_RGB565	  0x10	  /* RGB565 output */
+#define   COM15_RGB555	  0x30	  /* RGB555 output */
+#define REG_COM16	0x41	/* Control 16 */
+#define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
+#define REG_COM17	0x42	/* Control 17 */
+#define   COM17_AECWIN	  0xc0	  /* AEC window - must match COM4 */
+#define   COM17_CBAR	  0x08	  /* DSP Color bar */
+
+#define REG_BRIGHT	0x55	/* Brightness */
+#define REG_CONTRAS	0x56	/* Contrast control */
+
+#define REG_GFIX	0x69	/* Fix gain control */
+
+#define REG_RGB444	0x8c	/* RGB 444 control */
+#define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
+#define   R444_RGBX	  0x01	  /* Empty nibble at end */
+
+#define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
+#define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
+
+#define REG_BD50MAX	0xa5	/* 50hz banding step limit */
+#define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
+#define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
+#define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
+#define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
+#define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
+#define REG_BD60MAX	0xab	/* 60hz banding step limit */
+
+
+/*
+ * The default register settings, as obtained from OmniVision.  There
+ * is really no making sense of most of these - lots of "reserved" values
+ * and such.
+ *
+ * These settings give VGA YUYV.
+ */
+
+struct regval_list {
+	unsigned char reg_num;
+	unsigned char value;
+};
+
+static struct regval_list ov7670_default_regs[] = {
+	{ REG_COM7, COM7_RESET },
+/*
+ * Clock scale: 3 = 15fps
+ *              2 = 20fps
+ *              1 = 30fps
+ */
+	{ REG_CLKRC, 0x1 },	/* OV: clock scale (15 fps) */
+	{ REG_TSLB,  0x04 },	/* OV */
+	{ REG_COM7, 0 },	/* VGA */
+	/*
+	 * Set the hardware window.  These values from OV don't entirely
+	 * make sense - hstop is less than hstart.  But they work...
+	 */
+	{ REG_HSTART, 0x13 },	{ REG_HSTOP, 0x01 },
+	{ REG_HREF, 0xb6 },	{ REG_VSTART, 0x02 },
+	{ REG_VSTOP, 0x7a },	{ REG_VREF, 0x0a },
+
+	{ REG_COM3, 0 },	{ REG_COM14, 0 },
+	/* Mystery scaling numbers */
+	{ 0x70, 0x3a },		{ 0x71, 0x35 },
+	{ 0x72, 0x11 },		{ 0x73, 0xf0 },
+	{ 0xa2, 0x02 },		{ REG_COM10, 0x0 },
+	
+	/* Gamma curve values */
+	{ 0x7a, 0x20 },		{ 0x7b, 0x10 },
+	{ 0x7c, 0x1e },		{ 0x7d, 0x35 },
+	{ 0x7e, 0x5a },		{ 0x7f, 0x69 },
+	{ 0x80, 0x76 },		{ 0x81, 0x80 },
+	{ 0x82, 0x88 },		{ 0x83, 0x8f },
+	{ 0x84, 0x96 },		{ 0x85, 0xa3 },
+	{ 0x86, 0xaf },		{ 0x87, 0xc4 },
+	{ 0x88, 0xd7 },		{ 0x89, 0xe8 },
+
+	/* AGC and AEC parameters.  Note we start by disabling those features,
+	   then turn them only after tweaking the values. */
+	{ REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
+	{ REG_GAIN, 0 },	{ REG_AECH, 0 },
+	{ REG_COM4, 0x40 }, /* magic reserved bit */
+	{ REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+	{ REG_BD50MAX, 0x05 },	{ REG_BD60MAX, 0x07 },
+	{ REG_AEW, 0x95 },	{ REG_AEB, 0x33 },
+	{ REG_VPT, 0xe3 },	{ REG_HAECC1, 0x78 },
+	{ REG_HAECC2, 0x68 },	{ 0xa1, 0x03 }, /* magic */
+	{ REG_HAECC3, 0xd8 },	{ REG_HAECC4, 0xd8 },
+	{ REG_HAECC5, 0xf0 },	{ REG_HAECC6, 0x90 },
+	{ REG_HAECC7, 0x94 },
+	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
+	
+	/* Almost all of these are magic "reserved" values.  */
+	{ REG_COM5, 0x61 },	{ REG_COM6, 0x4b },
+	{ 0x16, 0x02 },		{ REG_MVFP, 0x07|MVFP_MIRROR },
+	{ 0x21, 0x02 },		{ 0x22, 0x91 },
+	{ 0x29, 0x07 },		{ 0x33, 0x0b },
+	{ 0x35, 0x0b },		{ 0x37, 0x1d },
+	{ 0x38, 0x71 },		{ 0x39, 0x2a },
+	{ REG_COM12, 0x78 },	{ 0x4d, 0x40 },
+	{ 0x4e, 0x20 },		{ REG_GFIX, 0 },
+	{ 0x6b, 0x4a },		{ 0x74, 0x10 },
+	{ 0x8d, 0x4f },		{ 0x8e, 0 },
+	{ 0x8f, 0 },		{ 0x90, 0 },
+	{ 0x91, 0 },		{ 0x96, 0 },
+	{ 0x9a, 0 },		{ 0xb0, 0x84 },
+	{ 0xb1, 0x0c },		{ 0xb2, 0x0e },
+	{ 0xb3, 0x82 },		{ 0xb8, 0x0a },
+
+	/* More reserved magic, some of which tweaks white balance */
+	{ 0x43, 0x0a },		{ 0x44, 0xf0 },
+	{ 0x45, 0x34 },		{ 0x46, 0x58 },
+	{ 0x47, 0x28 },		{ 0x48, 0x3a },
+	{ 0x59, 0x88 },		{ 0x5a, 0x88 },
+	{ 0x5b, 0x44 },		{ 0x5c, 0x67 },
+	{ 0x5d, 0x49 },		{ 0x5e, 0x0e },
+	{ 0x6c, 0x0a },		{ 0x6d, 0x55 },
+	{ 0x6e, 0x11 },		{ 0x6f, 0x9f }, /* "9e for advance AWB" */
+	{ 0x6a, 0x40 },		{ REG_BLUE, 0x40 },
+	{ REG_RED, 0x60 },
+	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
+
+	/* Matrix coefficients */
+	{ 0x4f, 0x80 },		{ 0x50, 0x80 },
+	{ 0x51, 0 },		{ 0x52, 0x22 },
+	{ 0x53, 0x5e },		{ 0x54, 0x80 },
+	{ 0x58, 0x9e },
+	
+	{ REG_COM16, COM16_AWBGAIN },	{ REG_EDGE, 0 },
+	{ 0x75, 0x05 },		{ 0x76, 0xe1 },
+	{ 0x4c, 0 },		{ 0x77, 0x01 },
+	{ REG_COM13, 0xc3 },	{ 0x4b, 0x09 },
+	{ 0xc9, 0x60 },		{ REG_COM16, 0x38 },
+	{ 0x56, 0x40 },
+
+	{ 0x34, 0x11 },		{ REG_COM11, COM11_EXP },
+	{ 0xa4, 0x88 },		{ 0x96, 0 },
+	{ 0x97, 0x30 },		{ 0x98, 0x20 },
+	{ 0x99, 0x30 },		{ 0x9a, 0x84 },
+	{ 0x9b, 0x29 },		{ 0x9c, 0x03 },
+	{ 0x9d, 0x4c },		{ 0x9e, 0x3f },
+	{ 0x78, 0x04 },
+
+	/* Extra-weird stuff.  Some sort of multiplexor register */
+	{ 0x79, 0x01 },		{ 0xc8, 0xf0 },
+	{ 0x79, 0x0f },		{ 0xc8, 0x00 },
+	{ 0x79, 0x10 },		{ 0xc8, 0x7e },
+	{ 0x79, 0x0a },		{ 0xc8, 0x80 },
+	{ 0x79, 0x0b },		{ 0xc8, 0x01 },
+	{ 0x79, 0x0c },		{ 0xc8, 0x0f },
+	{ 0x79, 0x0d },		{ 0xc8, 0x20 },
+	{ 0x79, 0x09 },		{ 0xc8, 0x80 },
+	{ 0x79, 0x02 },		{ 0xc8, 0xc0 },
+	{ 0x79, 0x03 },		{ 0xc8, 0x40 },
+	{ 0x79, 0x05 },		{ 0xc8, 0x30 },
+	{ 0x79, 0x26 },
+
+	/* Not sure if these should be here */
+	{ 0xf1, 0x10 },		{ 0x0f, 0x1d },
+	{ 0x0f, 0x1f },
+
+	{ 0xff, 0xff },	/* END MARKER */
+};
+
+
+/*
+ * Here we'll try to encapsulate the changes for just the output
+ * video format.
+ *
+ * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
+ *
+ * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
+ */
+
+
+static struct regval_list ov7670_fmt_yuv422[] = {
+	{ REG_COM7, 0x0 },  /* Selects YUV mode */
+	{ REG_RGB444, 0 },	/* No RGB444 please */
+	{ REG_COM1, 0 },
+	{ REG_COM15, COM15_R00FF },
+	{ REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0x80 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0x80 }, 	/* "matrix coefficient 2" */
+	{ 0x52, 0x22 }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0x5e }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0x80 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
+	{ 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb565[] = {
+	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
+	{ REG_RGB444, 0 },	/* No RGB444 please */
+	{ REG_COM1, 0x0 },
+	{ REG_COM15, COM15_RGB565 },
+	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
+	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
+	{ 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb444[] = {
+	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
+	{ REG_RGB444, R444_ENABLE },	/* Enable xxxxrrrr ggggbbbb */
+	{ REG_COM1, 0x40 },	/* Magic reserved bit */
+	{ REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
+	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
+	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 },  /* Magic rsvd bit */
+	{ 0xff, 0xff },
+};
+
+
+
+
+/*
+ * Low-level register I/O.
+ */
+
+static int ov7670_read(struct i2c_client *c, unsigned char reg,
+		unsigned char *value)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(c, reg);
+	if (ret > 0)
+		*value = (unsigned char) ret;
+	return ret;
+}
+
+
+static int ov7670_write(struct i2c_client *c, unsigned char reg,
+		unsigned char value)
+{
+	return i2c_smbus_write_byte_data(c, reg, value);
+}
+
+static int ov7670_write_mask(struct i2c_client *c, unsigned char reg,
+		unsigned char value, unsigned char mask)
+{
+	unsigned char v;
+	int ret, tries = 0;
+
+	ret = ov7670_read(c, reg, &v);
+	printk(KERN_ERR "ovwm read %x %d\n", v, ret);
+	if (ret < 0)
+		return ret;
+	v &= ~mask;
+	v |= (value & mask);
+	msleep(10); /* FIXME experiment */
+	printk(KERN_ERR "To write %x\n", v);
+	do {
+		ret = ov7670_write(c, reg, v);
+		printk(KERN_ERR "write status %d\n", ret);
+	} while (ret < 0 && ++tries < 3);
+	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)
+{
+	while (vals->reg_num != 0xff || vals->value != 0xff) {
+		int ret = ov7670_write(c, vals->reg_num, vals->value);
+		if (ret < 0)
+			return ret;
+		vals++;
+	}
+	return 0;
+}
+
+
+/*
+ * Stuff that knows about the sensor.
+ */
+static void ov7670_reset(struct i2c_client *client)
+{
+	ov7670_write(client, REG_COM7, COM7_RESET);
+	msleep(1);
+}
+
+
+static int ov7670_init(struct i2c_client *client)
+{
+	return ov7670_write_array(client, ov7670_default_regs);
+}
+
+
+
+static int ov7670_detect(struct i2c_client *client)
+{
+	unsigned char v;
+	int ret;
+
+	ret = ov7670_init(client);
+	if (ret < 0)
+		return ret;
+	ret = ov7670_read(client, REG_MIDH, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0x7f) /* OV manuf. id. */
+		return -ENODEV;
+	ret = ov7670_read(client, REG_MIDL, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0xa2)
+		return -ENODEV;
+	/*
+	 * OK, we know we have an OmniVision chip...but which one?
+	 */
+	ret = ov7670_read(client, REG_PID, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0x76)  /* PID + VER = 0x76 / 0x73 */
+		return -ENODEV;
+	ret = ov7670_read(client, REG_VER, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0x73)  /* PID + VER = 0x76 / 0x73 */
+		return -ENODEV;
+	return 0;
+}
+
+
+
+
+
+static struct ov7670_format_struct {
+	__u8 *desc;
+	__u32 pixelformat;
+	struct regval_list *regs;
+} ov7670_formats[] = {
+	{
+		.desc		= "YUYV 4:2:2",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+		.regs 		= ov7670_fmt_yuv422,
+	},
+	{
+		.desc		= "RGB 444",
+		.pixelformat	= V4L2_PIX_FMT_RGB444,
+		.regs		= ov7670_fmt_rgb444,
+	},
+	{
+		.desc		= "RGB 565",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+		.regs		= ov7670_fmt_rgb565,
+	},
+	/*
+	 * Pretend we do RGB32.  This is here on the assumption that the
+	 * upper layer will reformat RGB444 appropriately.
+	 *
+	 * The entire purpose for this thing's existence is to enable easy
+	 * display of RGB444 for debugging purposes.  It will come out soon.
+	 */
+	{
+		.desc		= "RGB32 (faked)",
+		.pixelformat	= V4L2_PIX_FMT_RGB32,
+		.regs		= ov7670_fmt_rgb444,
+	},
+};
+#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0]))
+
+/*
+ * 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.
+ */
+static struct ov7670_win_size {
+	int	width;
+	int	height;
+	unsigned char com7_bit;
+	int	hstart;		/* Start/stop values for the camera.  Note */
+	int	hstop;		/* that they do not always make complete */
+	int	vstart;		/* sense to humans, but evidently the sensor */
+	int	vstop;		/* will do the right thing... */
+/* h/vref stuff */
+} ov7670_win_sizes[] = {
+	/* VGA */
+	{
+		.width		= VGA_WIDTH,
+		.height		= VGA_HEIGHT,
+		.com7_bit	= COM7_FMT_VGA,
+		.hstart		= 158,		/* These values from */
+		.hstop		=  14,		/* Omnivision */
+		.vstart		=  10,
+		.vstop		= 490,
+	},
+	/* CIF */
+	{
+		.width		= CIF_WIDTH,
+		.height		= CIF_HEIGHT,
+		.com7_bit	= COM7_FMT_CIF,
+		.hstart		= 170,		/* Empirically determined */
+		.hstop		=  90,
+		.vstart		=  14,
+		.vstop		= 494,
+	},
+	/* QVGA */
+	{
+		.width		= QVGA_WIDTH,
+		.height		= QVGA_HEIGHT,
+		.com7_bit	= COM7_FMT_QVGA,
+		.hstart		= 164,		/* Empirically determined */
+		.hstop		=  20,
+		.vstart		=  14,
+		.vstop		= 494,
+	},
+#if 0 /* Does not work at all yet */
+	/* QCIF */
+	{
+		.width		= QCIF_WIDTH,
+		.height		= QCIF_HEIGHT,
+		.com7_bit	= COM7_FMT_QCIF,
+		.hstart		= 28,		/* Empirically determined */
+		.hstop		=  24,
+		.vstart		=  14,
+		.vstop		= 494,
+	},
+#endif
+};
+
+#define N_WIN_SIZES (sizeof(ov7670_win_sizes)/sizeof(ov7670_win_sizes[0]))
+
+
+/*
+ * Store a set of start/stop values into the camera.
+ */
+static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop,
+		int vstart, int vstop)
+{
+	int ret;
+	unsigned char v;
+/*
+ * Horizontal: 11 bits, top 8 live in hstart and hstop.  Bottom 3 of
+ * 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);
+	v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
+	msleep(10);
+	ret += ov7670_write(client, 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);
+	v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
+	msleep(10);
+	ret += ov7670_write(client, REG_VREF, v);
+	return ret;
+}
+
+
+static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt)
+{
+	struct ov7670_format_struct *ofmt;
+	
+	if (fmt->index >= N_OV7670_FMTS)
+		return -EINVAL;
+
+	ofmt = ov7670_formats + fmt->index;
+	fmt->flags = 0;
+	strcpy(fmt->description, ofmt->desc);
+	fmt->pixelformat = ofmt->pixelformat;
+	return 0;
+}
+
+
+static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
+		struct ov7670_format_struct **ret_fmt,
+		struct ov7670_win_size **ret_wsize)
+{
+	int index;
+	struct ov7670_win_size *wsize;
+	struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+	for (index = 0; index < N_OV7670_FMTS; index++)
+		if (ov7670_formats[index].pixelformat == pix->pixelformat)
+			break;
+	if (index >= N_OV7670_FMTS)
+		return -EINVAL;
+	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;
+	/*
+	 * Round requested image size down to the nearest
+	 * we support, but not below the smallest.
+	 */
+	for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES;
+	     wsize++) 
+		if (pix->width >= wsize->width && pix->height >= wsize->height)
+			break;
+	if (wsize > ov7670_win_sizes + N_WIN_SIZES)
+		wsize--;   /* Take the smallest one */
+	if (ret_wsize != NULL)
+		*ret_wsize = wsize;
+	/*
+	 * Note the size we'll actually handle.
+	 */
+	pix->width = wsize->width;
+	pix->height = wsize->height;
+	pix->bytesperline = pix->width*BYTES_PER_PIXEL;
+	if (pix->pixelformat == V4L2_PIX_FMT_RGB32)
+		pix->bytesperline *= 2;
+	pix->sizeimage = pix->height*pix->bytesperline;
+	return 0;
+
+}
+
+/*
+ * Set a format.
+ */
+static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
+{
+	int ret;
+	struct ov7670_format_struct *ovfmt;
+	struct ov7670_win_size *wsize;
+	unsigned char com7;
+
+	ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize);
+	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
+	 * register sets list it first.
+	 */
+	com7 = ovfmt->regs[0].value;
+	com7 |= wsize->com7_bit;
+	ov7670_write(c, 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,
+			wsize->vstop);
+	return 0;
+}
+
+/*
+ * Code for dealing with controls.
+ */
+
+/*
+ * Some weird registers seem to store values in a sign/magnitude format!
+ */
+static unsigned char ov7670_sm_to_abs(unsigned char v)
+{
+	if ((v & 0x80) == 0)
+		return v + 128;
+	else
+		return 128 - (v & 0x7f);
+}
+
+
+static unsigned char ov7670_abs_to_sm(unsigned char v)
+{
+	if (v > 127)
+		return v & 0x7f;
+	else
+		return (128 - v) | 0x80;
+}
+
+static int ov7670_t_brightness(struct i2c_client *client, unsigned char value)
+{
+	unsigned char com8;
+	int ret;
+	
+	ov7670_read(client, REG_COM8, &com8);
+	com8 &= ~COM8_AEC;
+	ov7670_write(client, REG_COM8, com8);
+	value = ov7670_abs_to_sm(value);
+	ret = ov7670_write(client, REG_BRIGHT, value);
+	return ret;
+}
+
+static int ov7670_q_brightness(struct i2c_client *client, unsigned char *value)
+{
+	int ret;
+	ret = ov7670_read(client, REG_BRIGHT, value);
+	*value = ov7670_sm_to_abs(*value);
+	return ret;
+}
+
+static int ov7670_t_contrast(struct i2c_client *client, unsigned char value)
+{
+	return ov7670_write(client, REG_CONTRAS, value);
+}
+
+static int ov7670_q_contrast(struct i2c_client *client, unsigned char *value)
+{
+	return ov7670_read(client, REG_CONTRAS, value);
+}
+
+static int ov7670_q_hflip(struct i2c_client *client, unsigned char *value)
+{
+	int ret;
+	unsigned char v;
+
+	ret = ov7670_read(client, REG_MVFP, &v);
+	*value = (v & MVFP_MIRROR) == MVFP_MIRROR;
+	return ret;
+}
+
+
+static int ov7670_t_hflip(struct i2c_client *client, unsigned char value)
+{
+	unsigned char v;
+	int ret;
+
+	ret = ov7670_read(client, REG_MVFP, &v);
+	if (value)
+		v |= MVFP_MIRROR;
+	else
+		v &= ~MVFP_MIRROR;
+	msleep(10);  /* FIXME */
+	ret += ov7670_write(client, REG_MVFP, v);
+	return ret;
+}
+
+
+
+static int ov7670_q_vflip(struct i2c_client *client, unsigned char *value)
+{
+	int ret;
+	unsigned char v;
+
+	ret = ov7670_read(client, REG_MVFP, &v);
+	*value = (v & MVFP_FLIP) == MVFP_FLIP;
+	return ret;
+}
+
+
+static int ov7670_t_vflip(struct i2c_client *client, unsigned char value)
+{
+	unsigned char v;
+	int ret;
+
+	ret = ov7670_read(client, REG_MVFP, &v);
+	if (value)
+		v |= MVFP_FLIP;
+	else
+		v &= ~MVFP_FLIP;
+	msleep(10);  /* FIXME */
+	ret += ov7670_write(client, REG_MVFP, v);
+	return ret;
+}
+
+
+static struct ov7670_control {
+	struct v4l2_queryctrl qc;
+	int (*query)(struct i2c_client *c, unsigned char *value);
+	int (*tweak)(struct i2c_client *c, unsigned char value);
+} ov7670_controls[] =
+{
+	{
+		.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_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]))
+
+static struct ov7670_control *ov7670_find_control(__u32 id)
+{
+	int i;
+
+	for (i = 0; i < N_CONTROLS; i++)
+		if (ov7670_controls[i].qc.id == id)
+			return ov7670_controls + i;
+	return NULL;
+}
+
+
+static int ov7670_queryctrl(struct i2c_client *client,
+		struct v4l2_queryctrl *qc)
+{
+	struct ov7670_control *ctrl = ov7670_find_control(qc->id);
+
+	if (ctrl == NULL)
+		return -EINVAL;
+	*qc = ctrl->qc;
+	return 0;
+}
+
+static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+	struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+	int ret;
+	unsigned char v;
+	
+	if (octrl == NULL)
+		return -EINVAL;
+	ret = octrl->query(client, &v);
+	if (ret >= 0) {
+		ctrl->value = v;
+		return 0;
+	}
+	return ret;
+}
+
+static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+	struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+	
+	if (octrl == NULL)
+		return -EINVAL;
+	return octrl->tweak(client, ctrl->value);
+}
+
+
+
+
+
+
+
+/*
+ * Basic i2c stuff.
+ */
+static struct i2c_driver ov7670_driver;
+
+static int ov7670_attach(struct i2c_adapter *adapter)
+{
+	int ret;
+	struct i2c_client *client;
+	
+	printk(KERN_ERR "ov7670 attach, id = %d\n", adapter->id);
+	/*
+	 * For now: only deal with adapters we recognize.
+	 */
+	if (adapter->id != I2C_HW_SMBUS_M88ALP01)
+		return -ENODEV;
+
+	printk(KERN_ERR "ov7670 accepting\n");
+	client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL);
+	if (! client)
+		return -ENOMEM;
+	client->adapter = adapter;
+	client->addr = OV7670_I2C_ADDR;
+	client->driver = &ov7670_driver,
+	strcpy(client->name, "OV7670");
+	/* Do we need clientdata? */
+
+	/*
+	 * Make sure it's an ov7670
+	 */
+	ret = ov7670_detect(client);
+	printk(KERN_ERR "detect result is %d\n", ret);
+	if (ret)
+		goto out_free;
+	i2c_attach_client(client);
+	return 0;
+
+  out_free:
+	kfree(client);
+	return ret;
+}
+
+
+static int ov7670_detach(struct i2c_client *client)
+{
+	i2c_detach_client(client);
+	kfree(client);
+	return 0;
+}
+
+
+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);
+	/* Todo:
+	   g/s_parm
+	   initialization
+	*/
+	}
+	return -EINVAL;
+}
+
+
+
+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,
+};
+
+
+/*
+ * 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);
diff --git a/drivers/media/video/ovcamchip/Makefile b/drivers/media/video/ovcamchip/Makefile
index 2db0dfa..cba4cdf 100644
--- a/drivers/media/video/ovcamchip/Makefile
+++ b/drivers/media/video/ovcamchip/Makefile
@@ -1,4 +1,4 @@
 ovcamchip-objs     := ovcamchip_core.o ov6x20.o ov6x30.o ov7x10.o ov7x20.o \
-		      ov76be.o ov7670.o
+		      ov76be.o
 
 obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip.o
diff --git a/drivers/media/video/ovcamchip/ov7670.c b/drivers/media/video/ovcamchip/ov7670.c
deleted file mode 100644
index c89d4dc..0000000
--- a/drivers/media/video/ovcamchip/ov7670.c
+++ /dev/null
@@ -1,794 +0,0 @@
-/* OmniVision OV7670 Camera chip support code
- *
- * Liberally stolen from earlier drivers by Jonathan Corbet, thus:
- *
- * Copyright (c) 1999-2004 Mark McClelland <mark at alpha.dyndns.org>
- * http://alpha.dyndns.org/ov511/
- *
- * Copyright 2006 One Laptop Per Child Association, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied.
- */
-
-#define DEBUG
-
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include "ovcamchip_priv.h"
-
-/* Registers */
-#define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
-#define REG_BLUE	0x01	/* blue gain */
-#define REG_RED		0x02	/* red gain */
-#define REG_VREF	0x03	/* Pieces of GAIN, VSTART, VSTOP */
-#define REG_COM1	0x04	/* Control 1 */
-#define  COM1_CCIR656	  0x40  /* CCIR656 enable */
-#define REG_BAVE	0x05	/* U/B Average level */
-#define REG_GbAVE	0x06	/* Y/Gb Average level */
-#define REG_AECHH	0x07	/* AEC MS 5 bits */
-#define REG_RAVE	0x08	/* V/R Average level */
-#define REG_COM2	0x09	/* Control 2 */
-#define  COM2_SSLEEP	  0x10	/* Soft sleep mode */
-#define REG_PID		0x0a	/* Product ID MSB */
-#define REG_VER		0x0b	/* Product ID LSB */
-#define REG_COM3	0x0c	/* Control 3 */
-#define  COM3_SWAP	  0x40	  /* Byte swap */
-#define  COM3_SCALEEN	  0x08	  /* Enable scaling */
-#define  COM3_DCWEN	  0x04	  /* Enable downsamp/crop/window */
-#define REG_COM4	0x0d	/* Control 4 */
-#define REG_COM5	0x0e	/* All "reserved" */
-#define REG_COM6	0x0f	/* Control 6 */
-#define REG_AECH	0x10	/* More bits of AEC value */
-#define REG_CLKRC	0x11	/* Clocl control */
-#define   CLK_EXT	  0x40	  /* Use external clock directly */
-#define   CLK_SCALE	  0x3f	  /* Mask for internal clock scale */
-#define REG_COM7	0x12	/* Control 7 */
-#define   COM7_RESET	  0x80	  /* Register reset */
-#define   COM7_FMT_MASK	  0x38
-#define   COM7_FMT_VGA	  0x00
-#define	  COM7_FMT_CIF	  0x20	  /* CIF format */
-#define   COM7_FMT_QVGA	  0x10	  /* QVGA format */
-#define   COM7_FMT_QCIF	  0x08	  /* QCIF format */
-#define	  COM7_RGB	  0x04	  /* bits 0 and 2 - RGB format */
-#define	  COM7_YUV	  0x00	  /* YUV */
-#define	  COM7_BAYER	  0x01	  /* Bayer format */
-#define	  COM7_PBAYER	  0x05	  /* "Processed bayer" */
-#define REG_COM8	0x13	/* Control 8 */
-#define   COM8_FASTAEC	  0x80	  /* Enable fast AGC/AEC */
-#define   COM8_AECSTEP	  0x40	  /* Unlimited AEC step size */
-#define   COM8_BFILT	  0x20	  /* Band filter enable */
-#define   COM8_AGC	  0x04	  /* Auto gain enable */
-#define   COM8_AWB	  0x02	  /* White balance enable */
-#define   COM8_AEC	  0x01	  /* Auto exposure enable */
-#define REG_COM9	0x14	/* Control 9  - gain ceiling */
-#define REG_COM10	0x15	/* Control 10 */
-#define   COM10_HSYNC	  0x40	  /* HSYNC instead of HREF */
-#define   COM10_PCLK_HB	  0x20	  /* Suppress PCLK on horiz blank */
-#define   COM10_HREF_REV  0x08	  /* Reverse HREF */
-#define   COM10_VS_LEAD	  0x04	  /* VSYNC on clock leading edge */
-#define   COM10_VS_NEG	  0x02	  /* VSYNC negative */
-#define   COM10_HS_NEG	  0x01	  /* HSYNC negative */
-#define REG_HSTART	0x17	/* Horiz start high bits */
-#define REG_HSTOP	0x18	/* Horiz stop high bits */
-#define REG_VSTART	0x19	/* Vert start high bits */
-#define REG_VSTOP	0x1a	/* Vert stop high bits */
-#define REG_PSHFT	0x1b	/* Pixel delay after HREF */
-#define REG_MIDH	0x1c	/* Manuf. ID high */
-#define REG_MIDL	0x1d	/* Manuf. ID low */
-#define REG_MVFP	0x1e	/* Mirror / vflip */
-#define   MVFP_MIRROR	  0x20	  /* Mirror image */
-#define   MVFP_FLIP	  0x10	  /* Vertical flip */
-
-#define REG_AEW		0x24	/* AGC upper limit */
-#define REG_AEB		0x25	/* AGC lower limit */
-#define REG_VPT		0x26	/* AGC/AEC fast mode op region */
-#define REG_HSYST	0x30	/* HSYNC rising edge delay */
-#define REG_HSYEN	0x31	/* HSYNC falling edge delay */
-#define REG_HREF	0x32	/* HREF pieces */
-#define REG_TSLB	0x3a	/* lots of stuff */
-#define   TSLB_YLAST	  0x04	  /* UYVY or VYUY - see com13 */
-#define REG_COM11	0x3b	/* Control 11 */
-#define   COM11_NIGHT	  0x80	  /* NIght mode enable */
-#define   COM11_NMFR	  0x60	  /* Two bit NM frame rate */
-#define   COM11_HZAUTO	  0x10	  /* Auto detect 50/60 Hz */
-#define	  COM11_50HZ	  0x08	  /* Manual 50Hz select */
-#define   COM11_EXP	  0x02
-#define REG_COM12	0x3c	/* Control 12 */
-#define   COM12_HREF	  0x80	  /* HREF always */
-#define REG_COM13	0x3d	/* Control 13 */
-#define   COM13_GAMMA	  0x80	  /* Gamma enable */
-#define	  COM13_UVSAT	  0x40	  /* UV saturation auto adjustment */
-#define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
-#define REG_COM14	0x3e	/* Control 14 */
-#define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
-#define REG_EDGE	0x3f	/* Edge enhancement factor */
-#define REG_COM15	0x40	/* Control 15 */
-#define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
-#define	  COM15_R01FE	  0x80	  /*            01 to FE */
-#define   COM15_R00FF	  0xc0	  /*            00 to FF */
-#define   COM15_RGB565	  0x10	  /* RGB565 output */
-#define   COM15_RGB555	  0x30	  /* RGB555 output */
-#define REG_COM16	0x41	/* Control 16 */
-#define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
-#define REG_COM17	0x42	/* Control 17 */
-#define   COM17_AECWIN	  0xc0	  /* AEC window - must match COM4 */
-#define   COM17_CBAR	  0x08	  /* DSP Color bar */
-
-#define REG_BRIGHT	0x55	/* Brightness */
-#define REG_CONTRAS	0x56	/* Contrast control */
-
-#define REG_GFIX	0x69	/* Fix gain control */
-
-#define REG_RGB444	0x8c	/* RGB 444 control */
-#define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
-#define   R444_RGBX	  0x01	  /* Empty nibble at end */
-
-#define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
-#define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
-
-#define REG_BD50MAX	0xa5	/* 50hz banding step limit */
-#define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
-#define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
-#define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
-#define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
-#define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
-#define REG_BD60MAX	0xab	/* 60hz banding step limit */
-
-
-/* Default control settings. Values are in terms of V4L2 controls. */
-#define OV7120_DFL_BRIGHT     0x60
-#define OV7620_DFL_BRIGHT     0x60
-#define OV7120_DFL_SAT        0xb0
-#define OV7620_DFL_SAT        0xc0
-#define DFL_AUTO_EXP             1
-#define DFL_AUTO_GAIN            1
-#define OV7120_DFL_GAIN       0x00
-#define OV7620_DFL_GAIN       0x00
-/* NOTE: Since autoexposure is the default, these aren't programmed into the
- * OV7x20 chip. They are just here because V4L2 expects a default */
-#define OV7120_DFL_EXP        0x7f
-#define OV7620_DFL_EXP        0x7f
-
-/*
- * Window params, from the spec.
- */
-#define HWSBASE 0x44	/* 7670 spec default - 11 bits */
-#define HWEBASE 0x308	/* spec default */
-#define VWSBASE 0x0c	/* spec default - 10 bits */
-#define VWEBASE 0x1ec	/* spec default */
-
-struct ov7670 {
-	int auto_brt;
-	int auto_exp;
-	int auto_gain;
-	int backlight;
-	int bandfilt;
-	int mirror;
-};
-
-
-/*
- * The default register settings, as obtained from OmniVision.  There
- * is really no making sense of most of these - lots of "reserved" values
- * and such.
- *
- * These settings give VGA YUYV.
- */
-static struct ovcamchip_regvals regvals_init_7670[] = {
-	{ REG_COM7, COM7_RESET },
-//	{ 0xff, 0xff },	/* REMOVE ME DAMMIT */
-/*
- * Clock scale: 3 = 15fps
- *              2 = 20fps
- *              1 = 30fps
- */
-	{ REG_CLKRC, 0x1 },	/* OV: clock scale (15 fps) */
-	{ REG_TSLB,  0x04 },	/* OV */
-	{ REG_COM7, 0 },	/* VGA */
-/* For now: trying hardcoding OV's H/VSTART numbers, even though they
- * don't make a lot of sense. */
-	{ REG_HSTART, 0x13 },
-	{ REG_HSTOP, 0x01 },
-	{ REG_HREF, 0xb6 },
-	{ REG_VSTART, 0x02 },
-	{ REG_VSTOP, 0x7a },
-	{ REG_VREF, 0x0a },
-
-	{ REG_COM3, 0 },
-	{ REG_COM14, 0 },
-	/* Mystery scaling numbers */
-	{ 0x70, 0x3a },
-	{ 0x71, 0x35 },
-	{ 0x72, 0x11 },
-	{ 0x73, 0xf0 },  /* "reserved" */
-	{ 0xa2, 0x02 },
-
-/* EXPERIMENT */
-	{ REG_COM10, 0x0 },
-	
-	/* Gamma curve values */
-	{ 0x7a, 0x20 },
-	{ 0x7b, 0x10 },
-	{ 0x7c, 0x1e },
-	{ 0x7d, 0x35 },
-	{ 0x7e, 0x5a },
-	{ 0x7f, 0x69 },
-	{ 0x80, 0x76 },
-	{ 0x81, 0x80 },
-	{ 0x82, 0x88 },
-	{ 0x83, 0x8f },
-	{ 0x84, 0x96 },
-	{ 0x85, 0xa3 },
-	{ 0x86, 0xaf },
-	{ 0x87, 0xc4 },
-	{ 0x88, 0xd7 },
-	{ 0x89, 0xe8 },
-
-	/* AGC and AEC parameters.  Note we start by disabling those features,
-	   then turn them only after tweaking the values. */
-	{ REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
-	{ REG_GAIN, 0 },
-	{ REG_AECH, 0 },
-	{ REG_COM4, 0x40 }, /* magic reserved bit */
-	{ REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
-	{ REG_BD50MAX, 0x05 },
-	{ REG_BD60MAX, 0x07 },
-	{ REG_AEW, 0x95 },
-	{ REG_AEB, 0x33 },
-	{ REG_VPT, 0xe3 },
-	{ REG_HAECC1, 0x78 },
-	{ REG_HAECC2, 0x68 },
-	{ 0xa1, 0x03 }, /* magic */
-	{ REG_HAECC3, 0xd8 },
-	{ REG_HAECC4, 0xd8 },
-	{ REG_HAECC5, 0xf0 },
-	{ REG_HAECC6, 0x90 },
-	{ REG_HAECC7, 0x94 },
-	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
-	
-	/* Almost all of these are magic "reserved" values.  */
-	{ REG_COM5, 0x61 },
-	{ REG_COM6, 0x4b },
-	{ 0x16, 0x02 },
-	{ REG_MVFP, 0x07 },
-//	{ REG_MVFP, 0x07 | MVFP_FLIP }, // Flip needed on dev system
-	{ 0x21, 0x02 },
-	{ 0x22, 0x91 },
-	{ 0x29, 0x07 },
-	{ 0x33, 0x0b },
-	{ 0x35, 0x0b },
-	{ 0x37, 0x1d },
-	{ 0x38, 0x71 },
-	{ 0x39, 0x2a },
-	{ REG_COM12, 0x78 },
-	{ 0x4d, 0x40 },
-	{ 0x4e, 0x20 },
-	{ REG_GFIX, 0 },
-	{ 0x6b, 0x4a },
-	{ 0x74, 0x10 },
-	{ 0x8d, 0x4f },
-	{ 0x8e, 0 },
-	{ 0x8f, 0 },
-	{ 0x90, 0 },
-	{ 0x91, 0 },
-	{ 0x96, 0 },
-	{ 0x9a, 0 },
-	{ 0xb0, 0x84 },
-	{ 0xb1, 0x0c },
-	{ 0xb2, 0x0e },
-	{ 0xb3, 0x82 },
-	{ 0xb8, 0x0a },
-
-	/* More reserved magic, some of which tweaks white balance */
-	{ 0x43, 0x0a },
-	{ 0x44, 0xf0 },
-	{ 0x45, 0x34 },
-	{ 0x46, 0x58 },
-	{ 0x47, 0x28 },
-	{ 0x48, 0x3a },
-	{ 0x59, 0x88 },
-	{ 0x5a, 0x88 },
-	{ 0x5b, 0x44 },
-	{ 0x5c, 0x67 },
-	{ 0x5d, 0x49 },
-	{ 0x5e, 0x0e },
-	{ 0x6c, 0x0a },
-	{ 0x6d, 0x55 },
-	{ 0x6e, 0x11 },
-	{ 0x6f, 0x9f }, /* "9e for advance AWB" */
-	{ 0x6a, 0x40 },
-	{ REG_BLUE, 0x40 },
-	{ REG_RED, 0x60 },
-	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
-
-	/* Matrix coefficients */
-	{ 0x4f, 0x80 },
-	{ 0x50, 0x80 },
-	{ 0x51, 0 },
-	{ 0x52, 0x22 },
-	{ 0x53, 0x5e },
-	{ 0x54, 0x80 },
-	{ 0x58, 0x9e },
-
-	{ REG_COM16, COM16_AWBGAIN },
-	{ REG_EDGE, 0 },
-	{ 0x75, 0x05 },
-	{ 0x76, 0xe1 },
-	{ 0x4c, 0 },
-	{ 0x77, 0x01 },
-	{ REG_COM13, 0xc3 }, /* rsvd bits */
-	{ 0x4b, 0x09 },
-	{ 0xc9, 0x60 },
-	{ REG_COM16, 0x38 },
-	{ 0x56, 0x40 },
-
-	{ 0x34, 0x11 },
-	{ REG_COM11, COM11_EXP },
-	{ 0xa4, 0x88 },
-	{ 0x96, 0 },
-	{ 0x97, 0x30 },
-	{ 0x98, 0x20 },
-	{ 0x99, 0x30 },
-	{ 0x9a, 0x84 },
-	{ 0x9b, 0x29 },
-	{ 0x9c, 0x03 },
-	{ 0x9d, 0x4c },
-	{ 0x9e, 0x3f },
-	{ 0x78, 0x04 },
-
-	/* Now for the really weird shit. */
-	{ 0x79, 0x01 },
-	{ 0xc8, 0xf0 },
-	{ 0x79, 0x0f },
-	{ 0xc8, 0x00 },
-	{ 0x79, 0x10 },
-	{ 0xc8, 0x7e },
-	{ 0x79, 0x0a },
-	{ 0xc8, 0x80 },
-	{ 0x79, 0x0b },
-	{ 0xc8, 0x01 },
-	{ 0x79, 0x0c },
-	{ 0xc8, 0x0f },
-	{ 0x79, 0x0d },
-	{ 0xc8, 0x20 },
-	{ 0x79, 0x09 },
-	{ 0xc8, 0x80 },
-	{ 0x79, 0x02 },
-	{ 0xc8, 0xc0 },
-	{ 0x79, 0x03 },
-	{ 0xc8, 0x40 },
-	{ 0x79, 0x05 },
-	{ 0xc8, 0x30 },
-	{ 0x79, 0x26 },
-
-	/* Not sure if these should be here */
-	{ 0xf1, 0x10 },
-	{ 0x0f, 0x1d },
-	{ 0x0f, 0x1f },
-
-
-//	{ REG_COM11, COM11_HZAUTO }, /* Auto freq detect */
-//	{ REG_COM13, COM13_GAMMA },
-
-	{ 0xff, 0xff },	/* END MARKER */
-};
-
-/*
- * Here we'll try to encapsulate the changes for just the output
- * video format.
- *
- * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
- */
-
-static struct ovcamchip_regvals reg_fmt_rgb565[] = {
-	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
-	{ REG_RGB444, 0 },	/* No RGB444 please */
-	{ REG_COM1, 0x0 },
-	{ REG_COM15, COM15_RGB565 },
-	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
-	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
-	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
-	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
-	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
-	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
-	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
-	{ 0xff, 0xff },
-};
-
-static struct ovcamchip_regvals reg_fmt_yuv422[] = {
-	{ REG_COM7, 0x0 },  /* Selects YUV mode */
-	{ REG_RGB444, 0 },	/* No RGB444 please */
-	{ REG_COM1, 0 },
-	{ REG_COM15, COM15_R00FF },
-	{ REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */
-	{ 0x4f, 0x80 }, 	/* "matrix coefficient 1" */
-	{ 0x50, 0x80 }, 	/* "matrix coefficient 2" */
-	{ 0x52, 0x22 }, 	/* "matrix coefficient 4" */
-	{ 0x53, 0x5e }, 	/* "matrix coefficient 5" */
-	{ 0x54, 0x80 }, 	/* "matrix coefficient 6" */
-	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
-	{ 0xff, 0xff },
-};
-
-static struct ovcamchip_regvals reg_fmt_rgb444[] = {
-	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
-	{ REG_RGB444, R444_ENABLE },	/* Enable xxxxrrrr ggggbbbb */
-	{ REG_COM1, 0x40 },	/* Magic reserved bit */
-	{ REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
-	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
-	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
-	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
-	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
-	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
-	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
-	{ REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 },  /* Magic rsvd bit */
-	{ 0xff, 0xff },
-};
-
-
-
-/* This initializes the OV7670 camera chip and relevant variables. */
-static int ov7670_init(struct i2c_client *c)
-{
-	struct ovcamchip *ov = i2c_get_clientdata(c);
-	struct ov7670 *s;
-	int rc;
-
-	DDEBUG(4, &c->dev, "entered");
-
-	rc = ov_write_regvals(c, regvals_init_7670);
-	if (rc < 0)
-		return rc;
-
-	ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL);
-	if (!s)
-		return -ENOMEM;
-
-	s->auto_brt = 1;
-	s->auto_exp = DFL_AUTO_EXP;
-	s->auto_gain = DFL_AUTO_GAIN;
-
-	return 0;
-}
-
-static int ov7670_free(struct i2c_client *c)
-{
-	struct ovcamchip *ov = i2c_get_clientdata(c);
-
-	kfree(ov->spriv);
-	return 0;
-}
-
-static int ov7670_set_v4l1_control(struct i2c_client *c,
-				   struct ovcamchip_control *ctl)
-{
-	struct ovcamchip *ov = i2c_get_clientdata(c);
-	struct ov7670 *s = ov->spriv;
-	int rc;
-	int v = ctl->value;
-
-	switch (ctl->id) {
-	case OVCAMCHIP_CID_CONT:
-		/* This is a total guess */
-		rc = ov_write(c, REG_CONTRAS, v >> 8);
-		break;
-	case OVCAMCHIP_CID_BRIGHT:
-		/* 7620 doesn't like manual changes when in auto mode */
-    		/* For 7670, guessing again */
-		if (!s->auto_brt)
-			rc = ov_write(c, REG_BRIGHT, v >> 8);
-		else
-			rc = 0;
-		break;
-#if 0 /* Not sure how to do this */
-	case OVCAMCHIP_CID_SAT:
-		rc = ov_write(c, REG_SAT, v >> 8);
-		break;
-#endif
-	case OVCAMCHIP_CID_EXP:
-	    	/*
-		 * Exposure is split among three registers.
-		 */
-	    	if (!s->auto_exp) {
-			rc = ov_write_mask(c, REG_COM1, v, 0x3);
-			rc += ov_write(c, REG_AECH, (v >> 2) & 0xff);
-			rc += ov_write_mask(c, REG_AECHH, v >> 10, 0x3f);
-		}
-		else
-			rc = -EBUSY;
-		break;
-	case OVCAMCHIP_CID_FREQ:
-	    	/*
-		 * Turn off autodetect in the process.
-		 */
-		if (v == 60)
-			rc = ov_write_mask(c, REG_COM11, 0, 0x18);
-		else
-			rc = ov_write_mask(c, REG_COM11, COM11_50HZ, COM11_50HZ);
-		break;
-	case OVCAMCHIP_CID_BANDFILT:
-		rc = ov_write_mask(c, REG_COM8, v ? COM8_BFILT : 0x00, COM8_BFILT);
-		s->bandfilt = v;
-		break;
-	case OVCAMCHIP_CID_AUTOBRIGHT:
-	    	rc = ov_write_mask(c, REG_COM8, v ? COM8_AGC : 0x00, COM8_AGC);
-		s->auto_brt = v;
-		break;
-	case OVCAMCHIP_CID_AUTOEXP:
-	    	rc = ov_write_mask(c, REG_COM8, v ? COM8_AEC : 0x00, COM8_AEC);
-		s->auto_exp = v;
-		break;
-	case OVCAMCHIP_CID_MIRROR:
-		rc = ov_write_mask(c, REG_MVFP, v ? MVFP_MIRROR : 0x00,
-				MVFP_MIRROR);
-		s->mirror = v;
-		break;
-	default:
-		DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
-		return -EPERM;
-	}
-
-	DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc);
-	return rc;
-}
-
-static int ov7670_get_v4l1_control(struct i2c_client *c,
-				   struct ovcamchip_control *ctl)
-{
-	struct ovcamchip *ov = i2c_get_clientdata(c);
-	struct ov7670 *s = ov->spriv;
-	int rc = 0;
-	unsigned char val = 0, v2, v3;
-
-	switch (ctl->id) {
-	case OVCAMCHIP_CID_CONT:
-		rc = ov_read(c, REG_CONTRAS, &val);
-		ctl->value = val << 8;
-		break;
-	case OVCAMCHIP_CID_BRIGHT:
-		rc = ov_read(c, REG_BRIGHT, &val);
-		ctl->value = val << 8;
-		break;
-#if 0
-	case OVCAMCHIP_CID_SAT:
-		rc = ov_read(c, REG_SAT, &val);
-		ctl->value = val << 8;
-		break;
-#endif
-	case OVCAMCHIP_CID_EXP:
-		rc = ov_read(c, REG_COM1, &val);
-		rc += ov_read(c, REG_AECH, &v2);
-		rc += ov_read(c, REG_AECHH, &v3);
-		ctl->value = (val & 0x3) | (v2 << 2) | ((v3 & 0x3f) << 10);
-		break;
-	/*
-	 * Why doesn't it just read these like the rest?
-	 */
-	case OVCAMCHIP_CID_BANDFILT:
-		ctl->value = s->bandfilt;
-		break;
-	case OVCAMCHIP_CID_AUTOBRIGHT:
-		ctl->value = s->auto_brt;
-		break;
-	case OVCAMCHIP_CID_AUTOEXP:
-		ctl->value = s->auto_exp;
-		break;
-	case OVCAMCHIP_CID_BACKLIGHT:
-		ctl->value = s->backlight;
-		break;
-	case OVCAMCHIP_CID_MIRROR:
-		ctl->value = s->mirror;
-		break;
-	default:
-		DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
-		return -EPERM;
-	}
-
-	DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc);
-	return rc;
-}
-
-
-static int ov7670_set_window(struct i2c_client *c, struct ovcamchip_window *win)
-{
-/* int lsb; */
-
-	if (win->width == 176) 
-		ov_write_mask(c, REG_COM7, COM7_FMT_QCIF, COM7_FMT_MASK);
-	else if (win->width == 320)
-		ov_write_mask(c, REG_COM7, COM7_FMT_QVGA, COM7_FMT_MASK);
-	else if (win->width == 352)
-		ov_write_mask(c, REG_COM7, COM7_FMT_CIF, COM7_FMT_MASK);
-	else
-		ov_write_mask(c, REG_COM7, COM7_FMT_VGA, COM7_FMT_MASK);
-	/*
-	 * 7670 seems to be more rational than some with how the
-	 * sizing parameters are set - no scaling.  The down side is
-	 * that they get split across multiple registers.
-	 */
-#ifdef notdef
-	ov_write(c, REG_HSTART, HWSBASE >> 3); /* deal with x/y later */
-	ov_write(c, REG_HSTOP, (HWSBASE + win->width) >> 3);
-	lsb = (HWSBASE&0x7) | (((HWSBASE + win->width)&0x7) << 3);
-	ov_write_mask(c, REG_HREF, lsb, 0x3f);
-
-	ov_write(c, REG_VSTART, VWSBASE >> 2);
-	ov_write(c, REG_VSTOP, (VWSBASE+win->height) >> 2);
-	lsb = (VWSBASE&0x3) | (((VWSBASE + win->height)&0x3) << 2);
-	ov_write_mask(c, REG_VREF, lsb, 0xf);
-#endif
-/* Try the OV-supplied numbers for now, regardless of whether they make
-   any sense at all */
-
-	return 0;
-}
-
-/*
- * The beginnings of V4L2 awareness.
- */
-
-static struct ov7670_format_struct {
-	__u8 *desc;
-	__u32 pixelformat;
-	struct ovcamchip_regvals *regs;
-} ov7670_formats[] = {
-	{
-		.desc		= "YUYV 4:2:2",
-		.pixelformat	= V4L2_PIX_FMT_YUYV,
-		.regs 		= reg_fmt_yuv422,
-	},
-	{
-		.desc		= "RGB 444",
-		.pixelformat	= V4L2_PIX_FMT_RGB444,
-		.regs		= reg_fmt_rgb444,
-	},
-	{
-		.desc		= "RGB 565",
-		.pixelformat	= V4L2_PIX_FMT_RGB565,
-		.regs		= reg_fmt_rgb565,
-	},
-	/*
-	 * Pretend we do RGB32.  This is here on the assumption that the
-	 * upper layer will reformat RGB444 appropriately.  It's a test
-	 * function (no easy way to display RGB444 otherwise) and can
-	 * eventually come out.
-	 */
-	{
-		.desc		= "RGB32 (faked)",
-		.pixelformat	= V4L2_PIX_FMT_RGB32,
-		.regs		= reg_fmt_rgb444,
-	},
-};
-#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0]))
-
-/*
- * All formats we support are 2 bytes/pixel.
- */
-#define BYTES_PER_PIXEL 2
-
-
-static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt)
-{
-	struct ov7670_format_struct *ofmt;
-	
-	if (fmt->index >= N_OV7670_FMTS)
-		return -EINVAL;
-
-	ofmt = ov7670_formats + fmt->index;
-	fmt->flags = 0;
-	strcpy(fmt->description, ofmt->desc);
-	fmt->pixelformat = ofmt->pixelformat;
-	return 0;
-}
-
-
-static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt)
-{
-	int index;
-	struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-	for (index = 0; index < N_OV7670_FMTS; index++)
-		if (ov7670_formats[index].pixelformat == pix->pixelformat)
-			break;
-	if (index >= N_OV7670_FMTS) {
-		printk(KERN_ERR "OV try fmt fail, fmt = %x\n", pix->pixelformat);
-		return -EINVAL;
-	}
-	/*
-	 * 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;
-	/*
-	 * Round requested image size down to the nearest
-	 * we support, but not below the smallest.
-	 */
-#if 0
-	if (pix->height < m88_height_options[0] ||
-			pix->width < m88_width_options[0])
-		sizeindex = 0;
-	else {
-		for (sizeindex = N_WINDOW_SIZES-1; sizeindex > 0; sizeindex--)
-			if (pix->height >= m88_height_options[sizeindex] &&
-			    pix->width >= m88_width_options[sizeindex])
-				break;
-	}
-	pix->width = m88_width_options[sizeindex];
-	pix->height = m88_height_options[sizeindex];
-#endif
-	/*
-	 * Wire VGA for now; we'll add flexibility later
-	 */
-	pix->width = 640;
-	pix->height = 480;
-	pix->bytesperline = pix->width*BYTES_PER_PIXEL;
-	if (pix->pixelformat == V4L2_PIX_FMT_RGB32)
-		pix->bytesperline *= 2;
-	pix->sizeimage = pix->height*pix->bytesperline;
-	return 0;
-
-}
-
-/*
- * Set a format.
- */
-static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
-{
-	int ret, i;
-	struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-	ret = ov7670_try_fmt(c, fmt);
-	if (ret)
-		return ret;
-	/*
-	 * Resolution wired to VGA for the moment.
-	 */
-	ov_write_mask(c, REG_COM7, COM7_FMT_VGA, COM7_FMT_MASK);
-	/*
-	 * Find our format again.  Kind of silly that we have to
-	 * search twice.  We know it exists, though, or try_fmt()
-	 * would have barfed.
-	 */
-	for (i = 0; i < N_OV7670_FMTS; i++)
-		if (ov7670_formats[i].pixelformat == pix->pixelformat)
-			break;
-	ov_write_regvals(c, ov7670_formats[i].regs);
-	return 0;
-}
-
-
-static int ov7670_command(struct i2c_client *c, unsigned int cmd, void *arg)
-{
-	switch (cmd) {
-	case OVCAMCHIP_CMD_S_CTRL:
-		return ov7670_set_v4l1_control(c, arg);
-	case OVCAMCHIP_CMD_G_CTRL:
-		return ov7670_get_v4l1_control(c, arg);
-	case OVCAMCHIP_CMD_S_MODE:
-		return ov7670_set_window(c, arg);
-	/*
-	 * Accept a few V4L2 ioctls directly.
-	 */
-	case VIDIOC_ENUM_FMT:
-		return ov7670_enum_fmt(c, (struct v4l2_fmtdesc *) arg);
-	case VIDIOC_TRY_FMT:
-		return ov7670_try_fmt(c, (struct v4l2_format *) arg);
-	case VIDIOC_S_FMT:
-		return ov7670_s_fmt(c, (struct v4l2_format *) arg);
-		
-	default:
-		DDEBUG(2, &c->dev, "command not supported: %d", cmd);
-		return -ENOIOCTLCMD;
-	}
-}
-
-struct ovcamchip_ops ov7670_ops = {
-	.init    =	ov7670_init,
-	.free    =	ov7670_free,
-	.command =	ov7670_command,
-};
diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c
index 7598100..3fe9fa0 100644
--- a/drivers/media/video/ovcamchip/ovcamchip_core.c
+++ b/drivers/media/video/ovcamchip/ovcamchip_core.c
@@ -46,8 +46,6 @@ MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
 /* Registers common to all chips, that are needed for detection */
-#define GENERIC_REG_PID_HIGH	  0x0a	/* Product ID */
-#define GENERIC_REG_PID_LOW	  0x0b
 #define GENERIC_REG_ID_HIGH       0x1C	/* manufacturer ID MSB */
 #define GENERIC_REG_ID_LOW        0x1D	/* manufacturer ID LSB */
 #define GENERIC_REG_COM_I         0x29	/* misc ID bits */
@@ -57,7 +55,6 @@ extern struct ovcamchip_ops ov6x30_ops;
 extern struct ovcamchip_ops ov7x10_ops;
 extern struct ovcamchip_ops ov7x20_ops;
 extern struct ovcamchip_ops ov76be_ops;
-extern struct ovcamchip_ops ov7670_ops;
 
 static char *chip_names[NUM_CC_TYPES] = {
 	[CC_UNKNOWN]	= "Unknown chip",
@@ -69,7 +66,6 @@ static char *chip_names[NUM_CC_TYPES] = 
 	[CC_OV6630]	= "OV6630",
 	[CC_OV6630AE]	= "OV6630AE",
 	[CC_OV6630AF]	= "OV6630AF",
-	[CC_OV7670]	= "OV7670"
 };
 
 /* Forward declarations */
@@ -168,27 +164,10 @@ static int ov7xx0_detect(struct i2c_clie
 {
 	struct ovcamchip *ov = i2c_get_clientdata(c);
 	int rc;
-	unsigned char val, val2;
+	unsigned char val;
 
 	PDEBUG(4, "");
 
-	/*
-	 * Detect the 7670 via the product ID registers.  Not sure why
-	 * they aren't all done this way.
-	 */
-	rc = ov_read(c, GENERIC_REG_PID_HIGH, &val) +
-		ov_read(c, GENERIC_REG_PID_LOW, &val2);
-	if (rc < 0) {
-		PERROR("Error reading 0v7xx0 PID regs");
-		return -1;
-	}
-	if (val == 0x76 && val2 == 0x73) {
-		ov->subtype = CC_OV7670;
-		ov->sops = &ov7670_ops;	
-		PINFO("Camera chip is an OV7670");
-		return 0;
-	}
-
 	/* Detect chip (sub)type */
 	rc = ov_read(c, GENERIC_REG_COM_I, &val);
 	if (rc < 0) {
@@ -321,7 +300,6 @@ static int ovcamchip_attach(struct i2c_a
 	case I2C_HW_SMBUS_OV518:
 	case I2C_HW_SMBUS_OVFX2:
 	case I2C_HW_SMBUS_W9968CF:
-	case I2C_HW_SMBUS_M88ALP01:
 		PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id);
 		break;
 	default:
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index b946d6b..af9dece 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -159,6 +159,7 @@ #define I2C_DRIVERID_LM90 1042
 #define I2C_DRIVERID_ASB100 1043
 #define I2C_DRIVERID_FSCHER 1046
 #define I2C_DRIVERID_W83L785TS 1047
+#define I2C_DRIVERID_OV7670 1048	/* Omnivision 7670 camera */
 
 /*
  * ---- Adapter types ----------------------------------------------------
diff --git a/include/media/ovcamchip.h b/include/media/ovcamchip.h
index 6940a01..718474e 100644
--- a/include/media/ovcamchip.h
+++ b/include/media/ovcamchip.h
@@ -38,7 +38,7 @@ enum {
 };
 
 /* Chip types */
-#define NUM_CC_TYPES	10
+#define NUM_CC_TYPES	9
 enum {
 	CC_UNKNOWN,
 	CC_OV76BE,
@@ -48,8 +48,7 @@ enum {
 	CC_OV6620,
 	CC_OV6630,
 	CC_OV6630AE,
-	CC_OV6630AF,
-	CC_OV7670
+	CC_OV6630AF
 };
 
 /* --------------------------------- */
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index aecc946..91b1992 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -144,6 +144,9 @@ enum v4l2_chip_ident {
 	V4L2_IDENT_CX25841 = 241,
 	V4L2_IDENT_CX25842 = 242,
 	V4L2_IDENT_CX25843 = 243,
+
+	/* OmniVision sensors - range 250-299 */
+	V4L2_IDENT_OV7670 = 250,
 };
 
 /* audio ioctls */
@@ -251,4 +254,8 @@ struct v4l2_crystal_freq {
    If the frequency is not supported, then -EINVAL is returned. */
 #define VIDIOC_INT_S_CRYSTAL_FREQ 	_IOW ('d', 113, struct v4l2_crystal_freq)
 
+/* Initialize the sensor registors to some sort of reasonable
+   default values. */
+#define VIDIOC_INT_INIT			_IOW ('d', 114, u32)
+
 #endif /* V4L2_COMMON_H_ */


More information about the Commits-kernel mailing list