All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Add Omnivision OV9640 sensor support.
@ 2008-11-28 10:16 ` Trilok Soni
  0 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-11-28 10:16 UTC (permalink / raw)
  To: v4l; +Cc: Sakari Ailus, linux-omap@vger.kernel.org Mailing List

[-- Attachment #1: Type: text/plain, Size: 89 bytes --]

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-Omnivision-OV9640-sensor-support.patch --]
[-- Type: text/x-patch; name=0001-Add-Omnivision-OV9640-sensor-support.patch, Size: 44280 bytes --]

From 2186c84b08aef9c1af124b5d770eabbf735618a7 Mon Sep 17 00:00:00 2001
From: Trilok Soni <soni.trilok@gmail.com>
Date: Fri, 28 Nov 2008 14:20:18 +0530
Subject: [PATCH] Add Omnivision OV9640 sensor support.

- Add driver for Omnivision OV9640 camera sensor. It has been
used with Texas Instruments OMAP1/2 based evaluation boards.

Signed-off-by: Trilok Soni <soni.trilok@gmail.com>
---
 drivers/media/video/Kconfig  |    7 +
 drivers/media/video/Makefile |    1 +
 drivers/media/video/ov9640.c | 1267 ++++++++++++++++++++++++++++++++++++++++++
 include/media/ov9640.h       |  192 +++++++
 4 files changed, 1467 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/ov9640.c
 create mode 100644 include/media/ov9640.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d980f23..b58f75b 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -305,6 +305,13 @@ config VIDEO_TCM825X
 	  This is a driver for the Toshiba TCM825x VGA camera sensor.
 	  It is used for example in Nokia N800.
 
+config VIDEO_OV9640
+	tristate "OmniVision OV9640 sensor support"
+	depends on I2C && VIDEO_V4L2
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV9640.
+
 config VIDEO_SAA7110
 	tristate "Philips SAA7110 video decoder"
 	depends on VIDEO_V4L1 && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 0797c3c..b2fe305 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
 obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
 obj-$(CONFIG_VIDEO_OMAP2)		+= omap2cam.o
+obj-$(CONFIG_VIDEO_OV9640)		+= ov9640.o
 obj-$(CONFIG_SOC_CAMERA)	+= soc_camera.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
 obj-$(CONFIG_SOC_CAMERA_MT9M111)	+= mt9m111.o
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
new file mode 100644
index 0000000..2b38926
--- /dev/null
+++ b/drivers/media/video/ov9640.c
@@ -0,0 +1,1267 @@
+/*
+ * drivers/media/video/ov9640.c
+ *
+ * OV9640 sensor driver
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ * Contact: Trilok Soni <soni.trilok@gmail.com>
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <media/v4l2-int-device.h>
+
+#include <media/ov9640.h>
+
+#define DRIVER_NAME  "ov9640"
+
+struct ov9640_sensor {
+	const struct ov9640_platform_data *pdata;
+	struct v4l2_int_device *v4l2_int_device;
+	struct i2c_client *i2c_client;
+	struct v4l2_pix_format pix;
+	struct v4l2_fract timeperframe;
+	int ver;				/*ov9640 chip version*/
+};
+
+static struct ov9640_sensor ov9640;
+static struct i2c_driver ov9640sensor_i2c_driver;
+
+/* list of image formats supported by OV9640 sensor */
+const static struct v4l2_fmtdesc ov9640_formats[] = {
+	{
+		/* Note:  V4L2 defines RGB565 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 r4 r3 r2 r1 r0	  b4 b3 b2 b1 b0 g5 g4 g3
+		 *
+		 * We interpret RGB565 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 b4 b3 b2 b1 b0	  r4 r3 r2 r1 r0 g5 g4 g3
+		 */
+		.description	= "RGB565, le",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+	}, {
+		/* Note:  V4L2 defines RGB565X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	b4 b3 b2 b1 b0 g5 g4 g3	  g2 g1 g0 r4 r3 r2 r1 r0
+		 *
+		 * We interpret RGB565X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	r4 r3 r2 r1 r0 g5 g4 g3	  g2 g1 g0 b4 b3 b2 b1 b0
+		 */
+		.description	= "RGB565, be",
+		.pixelformat	= V4L2_PIX_FMT_RGB565X,
+	}, {
+		.description	= "YUYV (YUV 4:2:2), packed",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+	}, {
+		.description	= "UYVY, packed",
+		.pixelformat	= V4L2_PIX_FMT_UYVY,
+	}, {
+		/* Note:  V4L2 defines RGB555 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 r4 r3 r2 r1 r0	  x  b4 b3 b2 b1 b0 g4 g3
+		 *
+		 * We interpret RGB555 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 b4 b3 b2 b1 b0	  x  r4 r3 r2 r1 r0 g4 g3
+		 */
+		.description	= "RGB555, le",
+		.pixelformat	= V4L2_PIX_FMT_RGB555,
+	}, {
+		/* Note:  V4L2 defines RGB555X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	x  b4 b3 b2 b1 b0 g4 g3	  g2 g1 g0 r4 r3 r2 r1 r0
+		 *
+		 * We interpret RGB555X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	x  r4 r3 r2 r1 r0 g4 g3	  g2 g1 g0 b4 b3 b2 b1 b0
+		 */
+		.description	= "RGB555, be",
+		.pixelformat	= V4L2_PIX_FMT_RGB555X,
+	},
+};
+
+#define NUM_CAPTURE_FORMATS ARRAY_SIZE(ov9640_formats)
+
+/*
+ * OV9640 register configuration for all combinations of pixel format and
+ * image size
+ */
+	/* YUV (YCbCr) QQCIF */
+const static struct ov9640_reg qqcif_yuv[] = {
+	{ 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QQVGA */
+const static struct ov9640_reg qqvga_yuv[] = {
+	{ 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QCIF */
+const static struct ov9640_reg qcif_yuv[] = {
+	{ 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QVGA */
+const static struct ov9640_reg qvga_yuv[] = {
+	{ 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) CIF */
+const static struct ov9640_reg cif_yuv[] = {
+	{ 0x12, 0x20 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) VGA */
+const static struct ov9640_reg vga_yuv[] = {
+	{ 0x12, 0x40 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) SXGA */
+const static struct ov9640_reg sxga_yuv[] = {
+	{ 0x12, 0x00 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QQCIF */
+const static struct ov9640_reg qqcif_565[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QQVGA */
+const static struct ov9640_reg qqvga_565[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QCIF */
+const static struct ov9640_reg qcif_565[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QVGA */
+const static struct ov9640_reg qvga_565[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 CIF */
+const static struct ov9640_reg cif_565[] = {
+	{ 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 VGA */
+const static struct ov9640_reg vga_565[] = {
+	{ 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 SXGA */
+const static struct ov9640_reg sxga_565[] = {
+	{ 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QQCIF */
+const static struct ov9640_reg qqcif_555[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QQVGA */
+const static struct ov9640_reg qqvga_555[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QCIF */
+const static struct ov9640_reg qcif_555[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QVGA */
+const static struct ov9640_reg qvga_555[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 CIF */
+const static struct ov9640_reg cif_555[] = {
+	{ 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 VGA */
+const static struct ov9640_reg vga_555[] = {
+	{ 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 SXGA */
+const static struct ov9640_reg sxga_555[] = {
+	{ 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+
+
+#define DEF_GAIN         31
+#define DEF_AUTOGAIN      1
+#define DEF_EXPOSURE    154
+#define DEF_AEC           1
+#define DEF_FREEZE_AGCAEC 0
+#define DEF_BLUE        153
+#define DEF_RED         (255 - DEF_BLUE)
+#define DEF_AWB           1
+#define DEF_HFLIP         0
+#define DEF_VFLIP         0
+
+/* Our own specific controls */
+#define V4L2_CID_FREEZE_AGCAEC		V4L2_CID_PRIVATE_BASE
+#define V4L2_CID_AUTOEXPOSURE		(V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_LAST_PRIV		V4L2_CID_AUTOEXPOSURE
+
+/*  Video controls  */
+static struct vcontrol {
+	struct v4l2_queryctrl qc;
+	int current_value;
+	u8 reg;
+	u8 mask;
+	u8 start_bit;
+} video_control[] = {
+	{
+		{
+			.id = V4L2_CID_GAIN,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Gain",
+			.minimum = 0,
+			.maximum = 63,
+			.step = 1,
+			.default_value = DEF_GAIN,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_GAIN,
+		.mask		= 0x3f,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTOGAIN,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto Gain",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AUTOGAIN,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x04,
+		.start_bit	= 2,
+	}, {
+		{
+			.id = V4L2_CID_EXPOSURE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Exposure",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_EXPOSURE,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_AECH,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTOEXPOSURE,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto Exposure",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AEC,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x01,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_FREEZE_AGCAEC,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Freeze AGC/AEC",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_FREEZE_AGCAEC,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM9,
+		.mask		= 0x01,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_RED_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Red Balance",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_RED,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_RED,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_BLUE_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Blue Balance",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_BLUE,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_BLUE,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTO_WHITE_BALANCE,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto White Balance",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AWB,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x02,
+		.start_bit	= 1,
+	}, {
+		{
+			.id = V4L2_CID_HFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Mirror Image",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_HFLIP,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_MVFP,
+		.mask		= 0x20,
+		.start_bit	= 5,
+	}, {
+		{
+			.id = V4L2_CID_VFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Vertical Flip",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_VFLIP,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_MVFP,
+		.mask		= 0x10,
+		.start_bit	= 4,
+	},
+};
+
+const static struct ov9640_reg *
+	ov9640_reg_init[NUM_PIXEL_FORMATS][NUM_IMAGE_SIZES] =
+{
+ { qqcif_yuv, qqvga_yuv, qcif_yuv, qvga_yuv, cif_yuv, vga_yuv, sxga_yuv },
+ { qqcif_565, qqvga_565, qcif_565, qvga_565, cif_565, vga_565, sxga_565 },
+ { qqcif_555, qqvga_555, qcif_555, qvga_555, cif_555, vga_555, sxga_555 },
+};
+
+
+/*
+ * Read a value from a register in an OV9640 sensor device.
+ * The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_read_reg(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int err;
+	struct i2c_msg msg[1];
+	unsigned char data[1];
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 1;
+	msg->buf = data;
+	*data = reg;
+	err = i2c_transfer(client->adapter, msg, 1);
+	if (err >= 0) {
+		msg->flags = I2C_M_RD;
+		err = i2c_transfer(client->adapter, msg, 1);
+	}
+	if (err >= 0) {
+		*val = *data;
+		return 0;
+	}
+	return err;
+}
+
+/*
+ * Write a value to a register in an OV9640 sensor device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int err;
+	struct i2c_msg msg[1];
+	unsigned char data[2];
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 2;
+	msg->buf = data;
+	data[0] = reg;
+	data[1] = val;
+	err = i2c_transfer(client->adapter, msg, 1);
+	if (err >= 0)
+		return 0;
+	return err;
+}
+
+static int
+ov9640_write_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+	u8 oldval, newval;
+	int rc;
+
+	if (mask == 0xff)
+		newval = *val;
+	else {
+		/* need to do read - modify - write */
+		rc = ov9640_read_reg(client, reg, &oldval);
+		if (rc)
+			return rc;
+		oldval &= (~mask);              /* Clear the masked bits */
+		*val &= mask;                  /* Enforce mask on value */
+		newval = oldval | *val;        /* Set the desired bits */
+	}
+
+	/* write the new value to the register */
+	rc = ov9640_write_reg(client, reg, newval);
+	if (rc)
+		return rc;
+
+	rc = ov9640_read_reg(client, reg, &newval);
+	if (rc)
+		return rc;
+
+	*val = newval & mask;
+	return 0;
+}
+
+static int
+ov9640_read_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+	int rc;
+
+	rc = ov9640_read_reg(client, reg, val);
+	if (rc)
+		return rc;
+	(*val) &= mask;
+
+	return 0;
+}
+
+/*
+ * Initialize a list of OV9640 registers.
+ * The list of registers is terminated by the pair of values
+ * { OV9640_REG_TERM, OV9640_VAL_TERM }.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_write_regs(struct i2c_client *client, const struct ov9640_reg reglist[])
+{
+	int err;
+	const struct ov9640_reg *next = reglist;
+
+	while (!((next->reg == OV9640_REG_TERM)
+		&& (next->val == OV9640_VAL_TERM))) {
+		err = ov9640_write_reg(client, next->reg, next->val);
+		udelay(100);
+		if (err)
+			return err;
+		next++;
+	}
+	return 0;
+}
+
+/* Returns the index of the requested ID from the control structure array */
+static int
+find_vctrl(int id)
+{
+	int i;
+
+	if (id < V4L2_CID_BASE)
+		return -EDOM;
+
+	for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--)
+		if (video_control[i].qc.id == id)
+			break;
+	if (i < 0)
+		i = -EINVAL;
+	return i;
+}
+
+/*
+ * Calculate the internal clock divisor (value of the CLKRC register) of the
+ * OV9640 given the image size, the frequency (in Hz) of its XCLK input and a
+ * desired frame period (in seconds).  The frame period 'fper' is expressed as
+ * a fraction.  The frame period is an input/output parameter.
+ * Returns the value of the OV9640 CLKRC register that will yield the frame
+ * period returned in 'fper' at the specified xclk frequency.  The
+ * returned period will be as close to the requested period as possible.
+ */
+static unsigned char
+ov9640_clkrc(enum image_size isize, unsigned long xclk, struct v4l2_fract *fper)
+{
+	unsigned long fpm, fpm_max;	/* frames per minute */
+	unsigned long divisor;
+	const unsigned long divisor_max = 64;
+	/* FIXME
+	 * clks_per_frame should come from platform data
+	 */
+	const static unsigned long clks_per_frame[] =
+		{ 200000, 400000, 200000, 400000, 400000, 800000, 3200000 };
+      /*         QQCIF   QQVGA    QCIF    QVGA  CIF     VGA	SXGA
+       *         199680,400000, 199680, 400000, 399360, 800000, 3200000
+       */
+
+	if (fper->numerator > 0)
+		fpm = (fper->denominator*60)/fper->numerator;
+	else
+		fpm = 0xffffffff;
+	fpm_max = (xclk*60)/clks_per_frame[isize];
+	if (fpm_max == 0)
+		fpm_max = 1;
+	if (fpm > fpm_max)
+		fpm = fpm_max;
+	if (fpm == 0)
+		fpm = 1;
+	divisor = fpm_max/fpm;
+	if (divisor > divisor_max)
+		divisor = divisor_max;
+	fper->numerator = divisor*60;
+	fper->denominator = fpm_max;
+
+	/* try to reduce the fraction */
+	while (!(fper->denominator % 5) && !(fper->numerator % 5)) {
+		fper->numerator /= 5;
+		fper->denominator /= 5;
+	}
+	while (!(fper->denominator % 3) && !(fper->numerator % 3)) {
+		fper->numerator /= 3;
+		fper->denominator /= 3;
+	}
+	while (!(fper->denominator % 2) && !(fper->numerator % 2)) {
+		fper->numerator /= 2;
+		fper->denominator /= 2;
+	}
+	if (fper->numerator < fper->denominator) {
+		if (!(fper->denominator % fper->numerator)) {
+			fper->denominator /= fper->numerator;
+			fper->numerator = 1;
+		}
+	} else {
+		if (!(fper->numerator % fper->denominator)) {
+			fper->numerator /= fper->denominator;
+			fper->denominator = 1;
+		}
+	}
+
+	/* we set bit 7 in CLKRC to enable the digital PLL */
+	return 0x80 | (divisor - 1);
+}
+
+/*
+ * Find the best match for a requested image capture size.  The best match
+ * is chosen as the nearest match that has the same number or fewer pixels
+ * as the requested size, or the smallest image size if the requested size
+ * has fewer pixels than the smallest image.
+ */
+static enum image_size
+ov9640_find_size(unsigned int width, unsigned int height)
+{
+	enum image_size isize;
+	unsigned long pixels = width*height;
+
+	for (isize = QQCIF; isize < SXGA; isize++) {
+		if (ov9640_sizes[isize + 1].height *
+			ov9640_sizes[isize + 1].width > pixels)
+			return isize;
+	}
+	return SXGA;
+}
+
+/*
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency
+ */
+static unsigned long
+ov9640sensor_calc_xclk(struct i2c_client *c)
+{
+	struct ov9640_sensor *sensor = i2c_get_clientdata(c);
+	struct v4l2_fract *timeperframe = &sensor->timeperframe;
+	struct v4l2_pix_format *pix = &sensor->pix;
+
+	unsigned long tgt_xclk;			/* target xclk */
+	unsigned long tgt_fpm;			/* target frames per minute */
+	enum image_size isize;
+
+	/*
+	 * We use arbitrary rules to select the xclk frequency.  If the
+	 * capture size is VGA and the frame rate is greater than 900
+	 * frames per minute, or if the capture size is SXGA and the
+	 * frame rate is greater than 450 frames per minutes, then the
+	 * xclk frequency will be set to 48MHz.  Otherwise, the xclk
+	 * frequency will be set to 24MHz.  If the mclk frequency is such that
+	 * the target xclk frequency is not achievable, then xclk will be set
+	 * as close as to the target as possible.
+	 */
+	tgt_fpm = (timeperframe->denominator*60)
+		/ timeperframe->numerator;
+	tgt_xclk = OV9640_XCLK_NOM;
+	isize = ov9640_find_size(pix->width, pix->height);
+	switch (isize) {
+	case SXGA:
+		if (tgt_fpm > 450)
+			tgt_xclk = OV9640_XCLK_MAX;
+		break;
+	case VGA:
+		if (tgt_fpm > 900)
+			tgt_xclk = OV9640_XCLK_MAX;
+		break;
+	default:
+		break;
+	}
+	return tgt_xclk;
+}
+
+/*
+ * Configure the OV9640 for a specified image size, pixel format, and frame
+ * period.  xclk is the frequency (in Hz) of the xclk input to the OV9640.
+ * fper is the frame period (in seconds) expressed as a fraction.
+ * Returns zero if successful, or non-zero otherwise.
+ * The actual frame period is returned in fper.
+ */
+static int ov9640_configure(struct v4l2_int_device *s)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_pix_format *pix = &sensor->pix;
+	struct v4l2_fract *fper = &sensor->timeperframe;
+	struct i2c_client *client = sensor->i2c_client;
+	enum image_size isize;
+	unsigned long xclk;
+
+	int err;
+	unsigned char clkrc;
+	enum pixel_format pfmt = YUV;
+
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_RGB565X:
+		pfmt = RGB565;
+		break;
+	case V4L2_PIX_FMT_RGB555:
+	case V4L2_PIX_FMT_RGB555X:
+		pfmt = RGB555;
+		break;
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_UYVY:
+	default:
+		pfmt = YUV;
+	}
+
+	xclk = ov9640sensor_calc_xclk(client);
+
+	isize = ov9640_find_size(pix->width, pix->height);
+
+	/* common register initialization */
+	err = ov9640_write_regs(client, sensor->pdata->default_regs);
+	if (err)
+		return err;
+
+	/* configure image size and pixel format */
+	err = ov9640_write_regs(client, ov9640_reg_init[pfmt][isize]);
+	if (err)
+		return err;
+
+	/* configure frame rate */
+	clkrc = ov9640_clkrc(isize, xclk, fper);
+	err = ov9640_write_reg(client, OV9640_CLKRC, clkrc);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/*
+ * Detect if an OV9640 is present, and if so which revision.
+ * A device is considered to be detected if the manufacturer ID (MIDH and MIDL)
+ * and the product ID (PID) registers match the expected values.
+ * Any value of the version ID (VER) register is accepted.
+ * Here are the version numbers we know about:
+ *	0x48 --> OV9640 Revision 1 or OV9640 Revision 2
+ *	0x49 --> OV9640 Revision 3
+ * Returns a negative error number if no device is detected, or the
+ * non-negative value of the version ID register if a device is detected.
+ */
+static int
+ov9640_detect(struct i2c_client *client)
+{
+	u8 midh, midl, pid, ver;
+
+	if (!client)
+		return -ENODEV;
+
+	if (ov9640_read_reg(client, OV9640_MIDH, &midh))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_MIDL, &midl))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_PID, &pid))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_VER, &ver))
+		return -ENODEV;
+
+	if ((midh != OV9640_MIDH_MAGIC)
+		|| (midl != OV9640_MIDL_MAGIC)
+		|| (pid != OV9640_PID_MAGIC))
+		/*
+		 * We didn't read the values we expected, so
+		 * this must not be an OV9640.
+		 */
+		return -ENODEV;
+
+	return ver;
+}
+
+/*
+ * following are sensor interface functions implemented by
+ * OV9640 sensor driver.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+				struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	i = find_vctrl(qc->id);
+	if (i == -EINVAL) {
+		qc->flags = V4L2_CTRL_FLAG_DISABLED;
+		return 0;
+	}
+	if (i < 0)
+		return -EINVAL;
+
+	*qc = video_control[i].qc;
+	return 0;
+}
+
+static int ioctl_g_ctrl(struct v4l2_int_device *s,
+			     struct v4l2_control *vc)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	int i, val;
+	struct vcontrol *lvc;
+
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &video_control[i];
+	if (ov9640_read_reg_mask(client, lvc->reg, (u8 *)&val, lvc->mask))
+		return -EIO;
+
+	val = val >> lvc->start_bit;
+	if (val >= 0) {
+		vc->value = lvc->current_value = val;
+		return 0;
+	} else
+		return val;
+}
+
+static int ioctl_s_ctrl(struct v4l2_int_device *s,
+			     struct v4l2_control *vc)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	struct vcontrol *lvc;
+	int val = vc->value;
+	int i;
+
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &video_control[i];
+	val = val << lvc->start_bit;
+	if (ov9640_write_reg_mask(client, lvc->reg, (u8 *)&val, (u8)lvc->mask))
+		return -EIO;
+
+	val = val >> lvc->start_bit;
+	if (val >= 0) {
+		lvc->current_value = val;
+		return 0;
+	} else
+		return val;
+}
+
+/*
+ * Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+				   struct v4l2_fmtdesc *fmt)
+{
+	int index = fmt->index;
+	enum v4l2_buf_type type = fmt->type;
+
+	memset(fmt, 0, sizeof(*fmt));
+	fmt->index = index;
+	fmt->type = type;
+
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (index >= NUM_CAPTURE_FORMATS)
+			return -EINVAL;
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	fmt->flags = ov9640_formats[index].flags;
+	strlcpy(fmt->description, ov9640_formats[index].description,
+					sizeof(fmt->description));
+	fmt->pixelformat = ov9640_formats[index].pixelformat;
+
+	return 0;
+}
+
+/*
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.  This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int ioctl_try_fmt_cap(struct v4l2_int_device *s,
+			     struct v4l2_format *f)
+{
+	enum image_size isize;
+	int ifmt;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+
+	isize = ov9640_find_size(pix->width, pix->height);
+	pix->width = ov9640_sizes[isize].width;
+	pix->height = ov9640_sizes[isize].height;
+	for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
+		if (pix->pixelformat == ov9640_formats[ifmt].pixelformat)
+			break;
+	}
+	if (ifmt == NUM_CAPTURE_FORMATS)
+		ifmt = 0;
+	pix->pixelformat = ov9640_formats[ifmt].pixelformat;
+	pix->field = V4L2_FIELD_NONE;
+	pix->bytesperline = pix->width*2;
+	pix->sizeimage = pix->bytesperline*pix->height;
+	pix->priv = 0;
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_UYVY:
+	default:
+		pix->colorspace = V4L2_COLORSPACE_JPEG;
+		break;
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_RGB565X:
+	case V4L2_PIX_FMT_RGB555:
+	case V4L2_PIX_FMT_RGB555X:
+		pix->colorspace = V4L2_COLORSPACE_SRGB;
+		break;
+	}
+	return 0;
+}
+
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s,
+				struct v4l2_format *f)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	int rval;
+
+	rval = ioctl_try_fmt_cap(s, f);
+	if (rval)
+		return rval;
+
+	rval = ov9640_configure(s);
+
+	if (!rval)
+		sensor->pix = *pix;
+
+	return rval;
+}
+
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s,
+				struct v4l2_format *f)
+{
+	struct ov9640_sensor *sensor = s->priv;
+
+	f->fmt.pix = sensor->pix;
+
+	return 0;
+}
+
+static int ioctl_g_parm(struct v4l2_int_device *s,
+			     struct v4l2_streamparm *a)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	cparm->capability = V4L2_CAP_TIMEPERFRAME;
+	cparm->timeperframe = sensor->timeperframe;
+
+	return 0;
+}
+
+static int ioctl_s_parm(struct v4l2_int_device *s,
+			     struct v4l2_streamparm *a)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+	struct v4l2_fract timeperframe_old;
+	int rval;
+
+	timeperframe_old = sensor->timeperframe;
+	sensor->timeperframe = *timeperframe;
+
+	rval = ov9640_configure(s);
+
+	if (rval)
+		sensor->timeperframe = timeperframe_old;
+	else
+		*timeperframe = sensor->timeperframe;
+
+	return rval;
+}
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	u32 xclk;	/* target xclk */
+	int rval;
+
+	rval = sensor->pdata->ifparm(p);
+	if (rval)
+		return rval;
+
+	xclk = ov9640sensor_calc_xclk(client);
+
+	p->u.bt656.clock_curr = xclk;
+
+	return 0;
+}
+
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+	struct ov9640_sensor *sensor = s->priv;
+
+	return sensor->pdata->power_set(on);
+}
+
+static int ioctl_init(struct v4l2_int_device *s)
+{
+	return ov9640_configure(s);
+}
+
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+	return 0;
+}
+
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *c = sensor->i2c_client;
+	int err;
+
+	err = ov9640_detect(c);
+	if (err < 0) {
+		dev_err(&c->dev, "Unable to detect " DRIVER_NAME " sensor\n");
+		return err;
+	}
+
+	sensor->ver = err;
+	pr_info(DRIVER_NAME " chip version 0x%02x detected\n", sensor->ver);
+
+	return 0;
+}
+
+static struct v4l2_int_ioctl_desc ov9640_ioctl_desc[] = {
+	{ vidioc_int_dev_init_num,
+	  (v4l2_int_ioctl_func *)ioctl_dev_init },
+	{ vidioc_int_dev_exit_num,
+	  (v4l2_int_ioctl_func *)ioctl_dev_exit },
+	{ vidioc_int_s_power_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_power },
+	{ vidioc_int_g_ifparm_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_ifparm },
+	{ vidioc_int_init_num,
+	  (v4l2_int_ioctl_func *)ioctl_init },
+	{ vidioc_int_enum_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
+	{ vidioc_int_try_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
+	{ vidioc_int_g_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
+	{ vidioc_int_s_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
+	{ vidioc_int_g_parm_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_parm },
+	{ vidioc_int_s_parm_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_parm },
+	{ vidioc_int_queryctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_queryctrl },
+	{ vidioc_int_g_ctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_ctrl },
+	{ vidioc_int_s_ctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_ctrl },
+};
+
+static struct v4l2_int_slave ov9640_slave = {
+	.ioctls		= ov9640_ioctl_desc,
+	.num_ioctls	= ARRAY_SIZE(ov9640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov9640_int_device = {
+	.module	= THIS_MODULE,
+	.name	= DRIVER_NAME,
+	.priv	= &ov9640,
+	.type	= v4l2_int_type_slave,
+	.u	= {
+		.slave = &ov9640_slave,
+	},
+};
+
+static int
+ov9640_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct ov9640_sensor *sensor = &ov9640;
+	int err;
+
+	if (i2c_get_clientdata(client))
+		return -EBUSY;
+
+	sensor->pdata = client->dev.platform_data;
+
+	if (!sensor->pdata || !sensor->pdata->default_regs) {
+		dev_err(&client->dev, "no platform data?\n");
+		return -ENODEV;
+	}
+
+	sensor->v4l2_int_device = &ov9640_int_device;
+	sensor->i2c_client = client;
+
+	i2c_set_clientdata(client, sensor);
+
+	/* Make the default capture format QCIF RGB565 */
+	sensor->pix.width = ov9640_sizes[QCIF].width;
+	sensor->pix.height = ov9640_sizes[QCIF].height;
+	sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+
+	err = v4l2_int_device_register(sensor->v4l2_int_device);
+	if (err)
+		i2c_set_clientdata(client, NULL);
+
+	return 0;
+}
+
+static int __exit
+ov9640_remove(struct i2c_client *client)
+{
+	struct ov9640_sensor *sensor = i2c_get_clientdata(client);
+
+	if (!client->adapter)
+		return -ENODEV;	/* our client isn't attached */
+
+	v4l2_int_device_unregister(sensor->v4l2_int_device);
+	i2c_set_clientdata(client, NULL);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov9640_id[] = {
+	{ "ov9640", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov9640_id);
+
+static struct i2c_driver ov9640sensor_i2c_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe	= ov9640_probe,
+	.remove	= __exit_p(ov9640_remove),
+};
+
+static struct ov9640_sensor ov9640 = {
+	.timeperframe = {
+		.numerator = 1,
+		.denominator = 15,
+	},
+};
+
+static int __init ov9640sensor_init(void)
+{
+	int err;
+
+	err = i2c_add_driver(&ov9640sensor_i2c_driver);
+	if (err) {
+		printk(KERN_ERR "Failed to register" DRIVER_NAME ".\n");
+		return err;
+	}
+	return 0;
+}
+module_init(ov9640sensor_init);
+
+static void __exit ov9640sensor_cleanup(void)
+{
+	i2c_del_driver(&ov9640sensor_i2c_driver);
+}
+module_exit(ov9640sensor_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OV9640 camera sensor driver");
diff --git a/include/media/ov9640.h b/include/media/ov9640.h
new file mode 100644
index 0000000..fcee4d3
--- /dev/null
+++ b/include/media/ov9640.h
@@ -0,0 +1,192 @@
+/*
+ * Register definitions for the OmniVision OV9640 CameraChip.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef OV9640_H
+#define OV9640_H
+
+#define OV9640_I2C_ADDR		0x30
+
+/* define register offsets for the OV9640 sensor chip */
+#define OV9640_GAIN		0x00
+#define OV9640_BLUE		0x01
+#define OV9640_RED		0x02
+#define OV9640_VREF		0x03
+#define OV9640_COM1		0x04
+#define OV9640_BAVE		0x05
+#define OV9640_GEAVE		0x06
+#define OV9640_RAVE		0x08
+#define OV9640_COM2		0x09
+#define OV9640_PID		0x0A
+#define OV9640_VER		0x0B
+#define OV9640_COM3		0x0C
+#define OV9640_COM4		0x0D
+#define OV9640_COM5		0x0E
+#define OV9640_COM6		0x0F
+#define OV9640_AECH		0x10
+#define OV9640_CLKRC		0x11
+#define OV9640_COM7		0x12
+#define OV9640_COM8		0x13
+#define OV9640_COM9		0x14
+#define OV9640_COM10		0x15
+#define OV9640_HSTRT		0x17
+#define OV9640_HSTOP		0x18
+#define OV9640_VSTRT		0x19
+#define OV9640_VSTOP		0x1A
+#define OV9640_PSHFT		0x1B
+#define OV9640_MIDH		0x1C
+#define OV9640_MIDL		0x1D
+#define OV9640_MVFP		0x1E
+#define OV9640_LAEC		0x1F
+#define OV9640_BOS		0x20
+#define OV9640_GBOS		0x21
+#define OV9640_GROS		0x22
+#define OV9640_ROS		0x23
+#define OV9640_AEW		0x24
+#define OV9640_AEB		0x25
+#define OV9640_VPT		0x26
+#define OV9640_BBIAS		0x27
+#define OV9640_GBBIAS		0x28
+#define OV9640_EXHCH		0x2A
+#define OV9640_EXHCL		0x2B
+#define OV9640_RBIAS		0x2C
+#define OV9640_ADVFL		0x2D
+#define OV9640_ADVFH		0x2E
+#define OV9640_YAVE		0x2F
+#define OV9640_HSYST		0x30
+#define OV9640_HSYEN		0x31
+#define OV9640_HREF		0x32
+#define OV9640_CHLF		0x33
+#define OV9640_ARBLM		0x34
+#define OV9640_ADC		0x37
+#define OV9640_ACOM		0x38
+#define OV9640_OFON		0x39
+#define OV9640_TSLB		0x3A
+#define OV9640_COM11		0x3B
+#define OV9640_COM12		0x3C
+#define OV9640_COM13		0x3D
+#define OV9640_COM14		0x3E
+#define OV9640_EDGE		0x3F
+#define OV9640_COM15		0x40
+#define OV9640_COM16		0x41
+#define OV9640_COM17		0x42
+#define OV9640_MTX1		0x4F
+#define OV9640_MTX2		0x50
+#define OV9640_MTX3		0x51
+#define OV9640_MTX4		0x52
+#define OV9640_MTX5		0x53
+#define OV9640_MTX6		0x54
+#define OV9640_MTX7		0x55
+#define OV9640_MTX8		0x56
+#define OV9640_MTX9		0x57
+#define OV9640_MTXS		0x58
+#define OV9640_LCC1		0x62
+#define OV9640_LCC2		0x63
+#define OV9640_LCC3		0x64
+#define OV9640_LCC4		0x65
+#define OV9640_LCC5		0x66
+#define OV9640_MANU		0x67
+#define OV9640_MANV		0x68
+#define OV9640_HV		0x69
+#define OV9640_MBD		0x6A
+#define OV9640_DBLV		0x6B
+#define OV9640_GSP1		0x6C
+#define OV9640_GSP2		0x6D
+#define OV9640_GSP3		0x6E
+#define OV9640_GSP4		0x6F
+#define OV9640_GSP5		0x70
+#define OV9640_GSP6		0x71
+#define OV9640_GSP7		0x72
+#define OV9640_GSP8		0x73
+#define OV9640_GSP9		0x74
+#define OV9640_GSP10		0x75
+#define OV9640_GSP11		0x76
+#define OV9640_GSP12		0x77
+#define OV9640_GSP13		0x78
+#define OV9640_GSP14		0x79
+#define OV9640_GSP15		0x7A
+#define OV9640_GSP16		0x7B
+#define OV9640_GST1		0x7C
+#define OV9640_GST2		0x7D
+#define OV9640_GST3		0x7E
+#define OV9640_GST4		0x7F
+#define OV9640_GST5		0x80
+#define OV9640_GST6		0x81
+#define OV9640_GST7		0x82
+#define OV9640_GST8		0x83
+#define OV9640_GST9		0x84
+#define OV9640_GST10		0x85
+#define OV9640_GST11		0x86
+#define OV9640_GST12		0x87
+#define OV9640_GST13		0x88
+#define OV9640_GST14		0x89
+#define OV9640_GST15		0x8A
+
+#define OV9640_NUM_REGS		(OV9640_GST15 + 1)
+
+#define OV9640_PID_MAGIC	0x96	/* high byte of product ID number */
+#define OV9640_VER_REV2		0x48	/* low byte of product ID number */
+#define OV9640_VER_REV3		0x49	/* low byte of product ID number */
+#define OV9640_MIDH_MAGIC	0x7F	/* high byte of mfg ID */
+#define OV9640_MIDL_MAGIC	0xA2	/* low byte of mfg ID */
+
+#define OV9640_REG_TERM 0xFF	/* terminating list entry for reg */
+#define OV9640_VAL_TERM 0xFF	/* terminating list entry for val */
+
+/*
+ * The nominal xclk input frequency of the OV9640 is 24MHz, maximum
+ * frequency is 48MHz, and minimum frequency is 10MHz.
+ */
+#define OV9640_XCLK_MIN 10000000
+#define OV9640_XCLK_MAX 48000000
+#define OV9640_XCLK_NOM 24000000
+
+/* define a structure for ov9640 register initialization values */
+struct ov9640_reg {
+	unsigned char reg;
+	unsigned char val;
+};
+
+enum image_size { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
+enum pixel_format { YUV, RGB565, RGB555 };
+
+#define NUM_IMAGE_SIZES 7
+#define NUM_PIXEL_FORMATS 3
+
+struct capture_size {
+	unsigned long width;
+	unsigned long height;
+};
+
+struct ov9640_platform_data {
+	/* Set power state, zero is off, non-zero is on. */
+	int (*power_set)(int power);
+	/* Default registers written after power-on or reset. */
+	const struct ov9640_reg *default_regs;
+	int (*ifparm)(struct v4l2_ifparm *p);
+};
+
+/*
+ * Array of image sizes supported by OV9640.  These must be ordered from
+ * smallest image size to largest.
+ */
+const static struct capture_size ov9640_sizes[] = {
+	{   88,  72 },	/* QQCIF */
+	{  160, 120 },	/* QQVGA */
+	{  176, 144 },	/* QCIF */
+	{  320, 240 },	/* QVGA */
+	{  352, 288 },	/* CIF */
+	{  640, 480 },	/* VGA */
+	{ 1280, 960 },	/* SXGA */
+};
+
+#endif /* ifndef OV9640_H */
-- 
1.6.0.3


[-- Attachment #3: Type: text/plain, Size: 164 bytes --]

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH] Add Omnivision OV9640 sensor support.
@ 2008-11-28 10:16 ` Trilok Soni
  0 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-11-28 10:16 UTC (permalink / raw)
  To: v4l; +Cc: Hans Verkuil, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

[-- Attachment #1: Type: text/plain, Size: 89 bytes --]

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-Omnivision-OV9640-sensor-support.patch --]
[-- Type: text/x-patch; name=0001-Add-Omnivision-OV9640-sensor-support.patch, Size: 44280 bytes --]

From 2186c84b08aef9c1af124b5d770eabbf735618a7 Mon Sep 17 00:00:00 2001
From: Trilok Soni <soni.trilok@gmail.com>
Date: Fri, 28 Nov 2008 14:20:18 +0530
Subject: [PATCH] Add Omnivision OV9640 sensor support.

- Add driver for Omnivision OV9640 camera sensor. It has been
used with Texas Instruments OMAP1/2 based evaluation boards.

Signed-off-by: Trilok Soni <soni.trilok@gmail.com>
---
 drivers/media/video/Kconfig  |    7 +
 drivers/media/video/Makefile |    1 +
 drivers/media/video/ov9640.c | 1267 ++++++++++++++++++++++++++++++++++++++++++
 include/media/ov9640.h       |  192 +++++++
 4 files changed, 1467 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/ov9640.c
 create mode 100644 include/media/ov9640.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d980f23..b58f75b 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -305,6 +305,13 @@ config VIDEO_TCM825X
 	  This is a driver for the Toshiba TCM825x VGA camera sensor.
 	  It is used for example in Nokia N800.
 
+config VIDEO_OV9640
+	tristate "OmniVision OV9640 sensor support"
+	depends on I2C && VIDEO_V4L2
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV9640.
+
 config VIDEO_SAA7110
 	tristate "Philips SAA7110 video decoder"
 	depends on VIDEO_V4L1 && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 0797c3c..b2fe305 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
 obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
 obj-$(CONFIG_VIDEO_OMAP2)		+= omap2cam.o
+obj-$(CONFIG_VIDEO_OV9640)		+= ov9640.o
 obj-$(CONFIG_SOC_CAMERA)	+= soc_camera.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
 obj-$(CONFIG_SOC_CAMERA_MT9M111)	+= mt9m111.o
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
new file mode 100644
index 0000000..2b38926
--- /dev/null
+++ b/drivers/media/video/ov9640.c
@@ -0,0 +1,1267 @@
+/*
+ * drivers/media/video/ov9640.c
+ *
+ * OV9640 sensor driver
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ * Contact: Trilok Soni <soni.trilok@gmail.com>
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <media/v4l2-int-device.h>
+
+#include <media/ov9640.h>
+
+#define DRIVER_NAME  "ov9640"
+
+struct ov9640_sensor {
+	const struct ov9640_platform_data *pdata;
+	struct v4l2_int_device *v4l2_int_device;
+	struct i2c_client *i2c_client;
+	struct v4l2_pix_format pix;
+	struct v4l2_fract timeperframe;
+	int ver;				/*ov9640 chip version*/
+};
+
+static struct ov9640_sensor ov9640;
+static struct i2c_driver ov9640sensor_i2c_driver;
+
+/* list of image formats supported by OV9640 sensor */
+const static struct v4l2_fmtdesc ov9640_formats[] = {
+	{
+		/* Note:  V4L2 defines RGB565 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 r4 r3 r2 r1 r0	  b4 b3 b2 b1 b0 g5 g4 g3
+		 *
+		 * We interpret RGB565 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 b4 b3 b2 b1 b0	  r4 r3 r2 r1 r0 g5 g4 g3
+		 */
+		.description	= "RGB565, le",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+	}, {
+		/* Note:  V4L2 defines RGB565X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	b4 b3 b2 b1 b0 g5 g4 g3	  g2 g1 g0 r4 r3 r2 r1 r0
+		 *
+		 * We interpret RGB565X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	r4 r3 r2 r1 r0 g5 g4 g3	  g2 g1 g0 b4 b3 b2 b1 b0
+		 */
+		.description	= "RGB565, be",
+		.pixelformat	= V4L2_PIX_FMT_RGB565X,
+	}, {
+		.description	= "YUYV (YUV 4:2:2), packed",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+	}, {
+		.description	= "UYVY, packed",
+		.pixelformat	= V4L2_PIX_FMT_UYVY,
+	}, {
+		/* Note:  V4L2 defines RGB555 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 r4 r3 r2 r1 r0	  x  b4 b3 b2 b1 b0 g4 g3
+		 *
+		 * We interpret RGB555 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 b4 b3 b2 b1 b0	  x  r4 r3 r2 r1 r0 g4 g3
+		 */
+		.description	= "RGB555, le",
+		.pixelformat	= V4L2_PIX_FMT_RGB555,
+	}, {
+		/* Note:  V4L2 defines RGB555X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	x  b4 b3 b2 b1 b0 g4 g3	  g2 g1 g0 r4 r3 r2 r1 r0
+		 *
+		 * We interpret RGB555X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	x  r4 r3 r2 r1 r0 g4 g3	  g2 g1 g0 b4 b3 b2 b1 b0
+		 */
+		.description	= "RGB555, be",
+		.pixelformat	= V4L2_PIX_FMT_RGB555X,
+	},
+};
+
+#define NUM_CAPTURE_FORMATS ARRAY_SIZE(ov9640_formats)
+
+/*
+ * OV9640 register configuration for all combinations of pixel format and
+ * image size
+ */
+	/* YUV (YCbCr) QQCIF */
+const static struct ov9640_reg qqcif_yuv[] = {
+	{ 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QQVGA */
+const static struct ov9640_reg qqvga_yuv[] = {
+	{ 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QCIF */
+const static struct ov9640_reg qcif_yuv[] = {
+	{ 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QVGA */
+const static struct ov9640_reg qvga_yuv[] = {
+	{ 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) CIF */
+const static struct ov9640_reg cif_yuv[] = {
+	{ 0x12, 0x20 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) VGA */
+const static struct ov9640_reg vga_yuv[] = {
+	{ 0x12, 0x40 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) SXGA */
+const static struct ov9640_reg sxga_yuv[] = {
+	{ 0x12, 0x00 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QQCIF */
+const static struct ov9640_reg qqcif_565[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QQVGA */
+const static struct ov9640_reg qqvga_565[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QCIF */
+const static struct ov9640_reg qcif_565[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QVGA */
+const static struct ov9640_reg qvga_565[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 CIF */
+const static struct ov9640_reg cif_565[] = {
+	{ 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 VGA */
+const static struct ov9640_reg vga_565[] = {
+	{ 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 SXGA */
+const static struct ov9640_reg sxga_565[] = {
+	{ 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QQCIF */
+const static struct ov9640_reg qqcif_555[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QQVGA */
+const static struct ov9640_reg qqvga_555[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QCIF */
+const static struct ov9640_reg qcif_555[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QVGA */
+const static struct ov9640_reg qvga_555[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 CIF */
+const static struct ov9640_reg cif_555[] = {
+	{ 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 VGA */
+const static struct ov9640_reg vga_555[] = {
+	{ 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 SXGA */
+const static struct ov9640_reg sxga_555[] = {
+	{ 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+
+
+#define DEF_GAIN         31
+#define DEF_AUTOGAIN      1
+#define DEF_EXPOSURE    154
+#define DEF_AEC           1
+#define DEF_FREEZE_AGCAEC 0
+#define DEF_BLUE        153
+#define DEF_RED         (255 - DEF_BLUE)
+#define DEF_AWB           1
+#define DEF_HFLIP         0
+#define DEF_VFLIP         0
+
+/* Our own specific controls */
+#define V4L2_CID_FREEZE_AGCAEC		V4L2_CID_PRIVATE_BASE
+#define V4L2_CID_AUTOEXPOSURE		(V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_LAST_PRIV		V4L2_CID_AUTOEXPOSURE
+
+/*  Video controls  */
+static struct vcontrol {
+	struct v4l2_queryctrl qc;
+	int current_value;
+	u8 reg;
+	u8 mask;
+	u8 start_bit;
+} video_control[] = {
+	{
+		{
+			.id = V4L2_CID_GAIN,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Gain",
+			.minimum = 0,
+			.maximum = 63,
+			.step = 1,
+			.default_value = DEF_GAIN,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_GAIN,
+		.mask		= 0x3f,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTOGAIN,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto Gain",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AUTOGAIN,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x04,
+		.start_bit	= 2,
+	}, {
+		{
+			.id = V4L2_CID_EXPOSURE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Exposure",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_EXPOSURE,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_AECH,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTOEXPOSURE,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto Exposure",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AEC,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x01,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_FREEZE_AGCAEC,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Freeze AGC/AEC",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_FREEZE_AGCAEC,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM9,
+		.mask		= 0x01,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_RED_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Red Balance",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_RED,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_RED,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_BLUE_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Blue Balance",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_BLUE,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_BLUE,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTO_WHITE_BALANCE,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto White Balance",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AWB,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x02,
+		.start_bit	= 1,
+	}, {
+		{
+			.id = V4L2_CID_HFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Mirror Image",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_HFLIP,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_MVFP,
+		.mask		= 0x20,
+		.start_bit	= 5,
+	}, {
+		{
+			.id = V4L2_CID_VFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Vertical Flip",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_VFLIP,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_MVFP,
+		.mask		= 0x10,
+		.start_bit	= 4,
+	},
+};
+
+const static struct ov9640_reg *
+	ov9640_reg_init[NUM_PIXEL_FORMATS][NUM_IMAGE_SIZES] =
+{
+ { qqcif_yuv, qqvga_yuv, qcif_yuv, qvga_yuv, cif_yuv, vga_yuv, sxga_yuv },
+ { qqcif_565, qqvga_565, qcif_565, qvga_565, cif_565, vga_565, sxga_565 },
+ { qqcif_555, qqvga_555, qcif_555, qvga_555, cif_555, vga_555, sxga_555 },
+};
+
+
+/*
+ * Read a value from a register in an OV9640 sensor device.
+ * The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_read_reg(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int err;
+	struct i2c_msg msg[1];
+	unsigned char data[1];
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 1;
+	msg->buf = data;
+	*data = reg;
+	err = i2c_transfer(client->adapter, msg, 1);
+	if (err >= 0) {
+		msg->flags = I2C_M_RD;
+		err = i2c_transfer(client->adapter, msg, 1);
+	}
+	if (err >= 0) {
+		*val = *data;
+		return 0;
+	}
+	return err;
+}
+
+/*
+ * Write a value to a register in an OV9640 sensor device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int err;
+	struct i2c_msg msg[1];
+	unsigned char data[2];
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 2;
+	msg->buf = data;
+	data[0] = reg;
+	data[1] = val;
+	err = i2c_transfer(client->adapter, msg, 1);
+	if (err >= 0)
+		return 0;
+	return err;
+}
+
+static int
+ov9640_write_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+	u8 oldval, newval;
+	int rc;
+
+	if (mask == 0xff)
+		newval = *val;
+	else {
+		/* need to do read - modify - write */
+		rc = ov9640_read_reg(client, reg, &oldval);
+		if (rc)
+			return rc;
+		oldval &= (~mask);              /* Clear the masked bits */
+		*val &= mask;                  /* Enforce mask on value */
+		newval = oldval | *val;        /* Set the desired bits */
+	}
+
+	/* write the new value to the register */
+	rc = ov9640_write_reg(client, reg, newval);
+	if (rc)
+		return rc;
+
+	rc = ov9640_read_reg(client, reg, &newval);
+	if (rc)
+		return rc;
+
+	*val = newval & mask;
+	return 0;
+}
+
+static int
+ov9640_read_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+	int rc;
+
+	rc = ov9640_read_reg(client, reg, val);
+	if (rc)
+		return rc;
+	(*val) &= mask;
+
+	return 0;
+}
+
+/*
+ * Initialize a list of OV9640 registers.
+ * The list of registers is terminated by the pair of values
+ * { OV9640_REG_TERM, OV9640_VAL_TERM }.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_write_regs(struct i2c_client *client, const struct ov9640_reg reglist[])
+{
+	int err;
+	const struct ov9640_reg *next = reglist;
+
+	while (!((next->reg == OV9640_REG_TERM)
+		&& (next->val == OV9640_VAL_TERM))) {
+		err = ov9640_write_reg(client, next->reg, next->val);
+		udelay(100);
+		if (err)
+			return err;
+		next++;
+	}
+	return 0;
+}
+
+/* Returns the index of the requested ID from the control structure array */
+static int
+find_vctrl(int id)
+{
+	int i;
+
+	if (id < V4L2_CID_BASE)
+		return -EDOM;
+
+	for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--)
+		if (video_control[i].qc.id == id)
+			break;
+	if (i < 0)
+		i = -EINVAL;
+	return i;
+}
+
+/*
+ * Calculate the internal clock divisor (value of the CLKRC register) of the
+ * OV9640 given the image size, the frequency (in Hz) of its XCLK input and a
+ * desired frame period (in seconds).  The frame period 'fper' is expressed as
+ * a fraction.  The frame period is an input/output parameter.
+ * Returns the value of the OV9640 CLKRC register that will yield the frame
+ * period returned in 'fper' at the specified xclk frequency.  The
+ * returned period will be as close to the requested period as possible.
+ */
+static unsigned char
+ov9640_clkrc(enum image_size isize, unsigned long xclk, struct v4l2_fract *fper)
+{
+	unsigned long fpm, fpm_max;	/* frames per minute */
+	unsigned long divisor;
+	const unsigned long divisor_max = 64;
+	/* FIXME
+	 * clks_per_frame should come from platform data
+	 */
+	const static unsigned long clks_per_frame[] =
+		{ 200000, 400000, 200000, 400000, 400000, 800000, 3200000 };
+      /*         QQCIF   QQVGA    QCIF    QVGA  CIF     VGA	SXGA
+       *         199680,400000, 199680, 400000, 399360, 800000, 3200000
+       */
+
+	if (fper->numerator > 0)
+		fpm = (fper->denominator*60)/fper->numerator;
+	else
+		fpm = 0xffffffff;
+	fpm_max = (xclk*60)/clks_per_frame[isize];
+	if (fpm_max == 0)
+		fpm_max = 1;
+	if (fpm > fpm_max)
+		fpm = fpm_max;
+	if (fpm == 0)
+		fpm = 1;
+	divisor = fpm_max/fpm;
+	if (divisor > divisor_max)
+		divisor = divisor_max;
+	fper->numerator = divisor*60;
+	fper->denominator = fpm_max;
+
+	/* try to reduce the fraction */
+	while (!(fper->denominator % 5) && !(fper->numerator % 5)) {
+		fper->numerator /= 5;
+		fper->denominator /= 5;
+	}
+	while (!(fper->denominator % 3) && !(fper->numerator % 3)) {
+		fper->numerator /= 3;
+		fper->denominator /= 3;
+	}
+	while (!(fper->denominator % 2) && !(fper->numerator % 2)) {
+		fper->numerator /= 2;
+		fper->denominator /= 2;
+	}
+	if (fper->numerator < fper->denominator) {
+		if (!(fper->denominator % fper->numerator)) {
+			fper->denominator /= fper->numerator;
+			fper->numerator = 1;
+		}
+	} else {
+		if (!(fper->numerator % fper->denominator)) {
+			fper->numerator /= fper->denominator;
+			fper->denominator = 1;
+		}
+	}
+
+	/* we set bit 7 in CLKRC to enable the digital PLL */
+	return 0x80 | (divisor - 1);
+}
+
+/*
+ * Find the best match for a requested image capture size.  The best match
+ * is chosen as the nearest match that has the same number or fewer pixels
+ * as the requested size, or the smallest image size if the requested size
+ * has fewer pixels than the smallest image.
+ */
+static enum image_size
+ov9640_find_size(unsigned int width, unsigned int height)
+{
+	enum image_size isize;
+	unsigned long pixels = width*height;
+
+	for (isize = QQCIF; isize < SXGA; isize++) {
+		if (ov9640_sizes[isize + 1].height *
+			ov9640_sizes[isize + 1].width > pixels)
+			return isize;
+	}
+	return SXGA;
+}
+
+/*
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency
+ */
+static unsigned long
+ov9640sensor_calc_xclk(struct i2c_client *c)
+{
+	struct ov9640_sensor *sensor = i2c_get_clientdata(c);
+	struct v4l2_fract *timeperframe = &sensor->timeperframe;
+	struct v4l2_pix_format *pix = &sensor->pix;
+
+	unsigned long tgt_xclk;			/* target xclk */
+	unsigned long tgt_fpm;			/* target frames per minute */
+	enum image_size isize;
+
+	/*
+	 * We use arbitrary rules to select the xclk frequency.  If the
+	 * capture size is VGA and the frame rate is greater than 900
+	 * frames per minute, or if the capture size is SXGA and the
+	 * frame rate is greater than 450 frames per minutes, then the
+	 * xclk frequency will be set to 48MHz.  Otherwise, the xclk
+	 * frequency will be set to 24MHz.  If the mclk frequency is such that
+	 * the target xclk frequency is not achievable, then xclk will be set
+	 * as close as to the target as possible.
+	 */
+	tgt_fpm = (timeperframe->denominator*60)
+		/ timeperframe->numerator;
+	tgt_xclk = OV9640_XCLK_NOM;
+	isize = ov9640_find_size(pix->width, pix->height);
+	switch (isize) {
+	case SXGA:
+		if (tgt_fpm > 450)
+			tgt_xclk = OV9640_XCLK_MAX;
+		break;
+	case VGA:
+		if (tgt_fpm > 900)
+			tgt_xclk = OV9640_XCLK_MAX;
+		break;
+	default:
+		break;
+	}
+	return tgt_xclk;
+}
+
+/*
+ * Configure the OV9640 for a specified image size, pixel format, and frame
+ * period.  xclk is the frequency (in Hz) of the xclk input to the OV9640.
+ * fper is the frame period (in seconds) expressed as a fraction.
+ * Returns zero if successful, or non-zero otherwise.
+ * The actual frame period is returned in fper.
+ */
+static int ov9640_configure(struct v4l2_int_device *s)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_pix_format *pix = &sensor->pix;
+	struct v4l2_fract *fper = &sensor->timeperframe;
+	struct i2c_client *client = sensor->i2c_client;
+	enum image_size isize;
+	unsigned long xclk;
+
+	int err;
+	unsigned char clkrc;
+	enum pixel_format pfmt = YUV;
+
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_RGB565X:
+		pfmt = RGB565;
+		break;
+	case V4L2_PIX_FMT_RGB555:
+	case V4L2_PIX_FMT_RGB555X:
+		pfmt = RGB555;
+		break;
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_UYVY:
+	default:
+		pfmt = YUV;
+	}
+
+	xclk = ov9640sensor_calc_xclk(client);
+
+	isize = ov9640_find_size(pix->width, pix->height);
+
+	/* common register initialization */
+	err = ov9640_write_regs(client, sensor->pdata->default_regs);
+	if (err)
+		return err;
+
+	/* configure image size and pixel format */
+	err = ov9640_write_regs(client, ov9640_reg_init[pfmt][isize]);
+	if (err)
+		return err;
+
+	/* configure frame rate */
+	clkrc = ov9640_clkrc(isize, xclk, fper);
+	err = ov9640_write_reg(client, OV9640_CLKRC, clkrc);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/*
+ * Detect if an OV9640 is present, and if so which revision.
+ * A device is considered to be detected if the manufacturer ID (MIDH and MIDL)
+ * and the product ID (PID) registers match the expected values.
+ * Any value of the version ID (VER) register is accepted.
+ * Here are the version numbers we know about:
+ *	0x48 --> OV9640 Revision 1 or OV9640 Revision 2
+ *	0x49 --> OV9640 Revision 3
+ * Returns a negative error number if no device is detected, or the
+ * non-negative value of the version ID register if a device is detected.
+ */
+static int
+ov9640_detect(struct i2c_client *client)
+{
+	u8 midh, midl, pid, ver;
+
+	if (!client)
+		return -ENODEV;
+
+	if (ov9640_read_reg(client, OV9640_MIDH, &midh))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_MIDL, &midl))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_PID, &pid))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_VER, &ver))
+		return -ENODEV;
+
+	if ((midh != OV9640_MIDH_MAGIC)
+		|| (midl != OV9640_MIDL_MAGIC)
+		|| (pid != OV9640_PID_MAGIC))
+		/*
+		 * We didn't read the values we expected, so
+		 * this must not be an OV9640.
+		 */
+		return -ENODEV;
+
+	return ver;
+}
+
+/*
+ * following are sensor interface functions implemented by
+ * OV9640 sensor driver.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+				struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	i = find_vctrl(qc->id);
+	if (i == -EINVAL) {
+		qc->flags = V4L2_CTRL_FLAG_DISABLED;
+		return 0;
+	}
+	if (i < 0)
+		return -EINVAL;
+
+	*qc = video_control[i].qc;
+	return 0;
+}
+
+static int ioctl_g_ctrl(struct v4l2_int_device *s,
+			     struct v4l2_control *vc)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	int i, val;
+	struct vcontrol *lvc;
+
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &video_control[i];
+	if (ov9640_read_reg_mask(client, lvc->reg, (u8 *)&val, lvc->mask))
+		return -EIO;
+
+	val = val >> lvc->start_bit;
+	if (val >= 0) {
+		vc->value = lvc->current_value = val;
+		return 0;
+	} else
+		return val;
+}
+
+static int ioctl_s_ctrl(struct v4l2_int_device *s,
+			     struct v4l2_control *vc)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	struct vcontrol *lvc;
+	int val = vc->value;
+	int i;
+
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &video_control[i];
+	val = val << lvc->start_bit;
+	if (ov9640_write_reg_mask(client, lvc->reg, (u8 *)&val, (u8)lvc->mask))
+		return -EIO;
+
+	val = val >> lvc->start_bit;
+	if (val >= 0) {
+		lvc->current_value = val;
+		return 0;
+	} else
+		return val;
+}
+
+/*
+ * Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+				   struct v4l2_fmtdesc *fmt)
+{
+	int index = fmt->index;
+	enum v4l2_buf_type type = fmt->type;
+
+	memset(fmt, 0, sizeof(*fmt));
+	fmt->index = index;
+	fmt->type = type;
+
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (index >= NUM_CAPTURE_FORMATS)
+			return -EINVAL;
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	fmt->flags = ov9640_formats[index].flags;
+	strlcpy(fmt->description, ov9640_formats[index].description,
+					sizeof(fmt->description));
+	fmt->pixelformat = ov9640_formats[index].pixelformat;
+
+	return 0;
+}
+
+/*
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.  This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int ioctl_try_fmt_cap(struct v4l2_int_device *s,
+			     struct v4l2_format *f)
+{
+	enum image_size isize;
+	int ifmt;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+
+	isize = ov9640_find_size(pix->width, pix->height);
+	pix->width = ov9640_sizes[isize].width;
+	pix->height = ov9640_sizes[isize].height;
+	for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
+		if (pix->pixelformat == ov9640_formats[ifmt].pixelformat)
+			break;
+	}
+	if (ifmt == NUM_CAPTURE_FORMATS)
+		ifmt = 0;
+	pix->pixelformat = ov9640_formats[ifmt].pixelformat;
+	pix->field = V4L2_FIELD_NONE;
+	pix->bytesperline = pix->width*2;
+	pix->sizeimage = pix->bytesperline*pix->height;
+	pix->priv = 0;
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_UYVY:
+	default:
+		pix->colorspace = V4L2_COLORSPACE_JPEG;
+		break;
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_RGB565X:
+	case V4L2_PIX_FMT_RGB555:
+	case V4L2_PIX_FMT_RGB555X:
+		pix->colorspace = V4L2_COLORSPACE_SRGB;
+		break;
+	}
+	return 0;
+}
+
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s,
+				struct v4l2_format *f)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	int rval;
+
+	rval = ioctl_try_fmt_cap(s, f);
+	if (rval)
+		return rval;
+
+	rval = ov9640_configure(s);
+
+	if (!rval)
+		sensor->pix = *pix;
+
+	return rval;
+}
+
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s,
+				struct v4l2_format *f)
+{
+	struct ov9640_sensor *sensor = s->priv;
+
+	f->fmt.pix = sensor->pix;
+
+	return 0;
+}
+
+static int ioctl_g_parm(struct v4l2_int_device *s,
+			     struct v4l2_streamparm *a)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	cparm->capability = V4L2_CAP_TIMEPERFRAME;
+	cparm->timeperframe = sensor->timeperframe;
+
+	return 0;
+}
+
+static int ioctl_s_parm(struct v4l2_int_device *s,
+			     struct v4l2_streamparm *a)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+	struct v4l2_fract timeperframe_old;
+	int rval;
+
+	timeperframe_old = sensor->timeperframe;
+	sensor->timeperframe = *timeperframe;
+
+	rval = ov9640_configure(s);
+
+	if (rval)
+		sensor->timeperframe = timeperframe_old;
+	else
+		*timeperframe = sensor->timeperframe;
+
+	return rval;
+}
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	u32 xclk;	/* target xclk */
+	int rval;
+
+	rval = sensor->pdata->ifparm(p);
+	if (rval)
+		return rval;
+
+	xclk = ov9640sensor_calc_xclk(client);
+
+	p->u.bt656.clock_curr = xclk;
+
+	return 0;
+}
+
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+	struct ov9640_sensor *sensor = s->priv;
+
+	return sensor->pdata->power_set(on);
+}
+
+static int ioctl_init(struct v4l2_int_device *s)
+{
+	return ov9640_configure(s);
+}
+
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+	return 0;
+}
+
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *c = sensor->i2c_client;
+	int err;
+
+	err = ov9640_detect(c);
+	if (err < 0) {
+		dev_err(&c->dev, "Unable to detect " DRIVER_NAME " sensor\n");
+		return err;
+	}
+
+	sensor->ver = err;
+	pr_info(DRIVER_NAME " chip version 0x%02x detected\n", sensor->ver);
+
+	return 0;
+}
+
+static struct v4l2_int_ioctl_desc ov9640_ioctl_desc[] = {
+	{ vidioc_int_dev_init_num,
+	  (v4l2_int_ioctl_func *)ioctl_dev_init },
+	{ vidioc_int_dev_exit_num,
+	  (v4l2_int_ioctl_func *)ioctl_dev_exit },
+	{ vidioc_int_s_power_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_power },
+	{ vidioc_int_g_ifparm_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_ifparm },
+	{ vidioc_int_init_num,
+	  (v4l2_int_ioctl_func *)ioctl_init },
+	{ vidioc_int_enum_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
+	{ vidioc_int_try_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
+	{ vidioc_int_g_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
+	{ vidioc_int_s_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
+	{ vidioc_int_g_parm_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_parm },
+	{ vidioc_int_s_parm_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_parm },
+	{ vidioc_int_queryctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_queryctrl },
+	{ vidioc_int_g_ctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_ctrl },
+	{ vidioc_int_s_ctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_ctrl },
+};
+
+static struct v4l2_int_slave ov9640_slave = {
+	.ioctls		= ov9640_ioctl_desc,
+	.num_ioctls	= ARRAY_SIZE(ov9640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov9640_int_device = {
+	.module	= THIS_MODULE,
+	.name	= DRIVER_NAME,
+	.priv	= &ov9640,
+	.type	= v4l2_int_type_slave,
+	.u	= {
+		.slave = &ov9640_slave,
+	},
+};
+
+static int
+ov9640_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct ov9640_sensor *sensor = &ov9640;
+	int err;
+
+	if (i2c_get_clientdata(client))
+		return -EBUSY;
+
+	sensor->pdata = client->dev.platform_data;
+
+	if (!sensor->pdata || !sensor->pdata->default_regs) {
+		dev_err(&client->dev, "no platform data?\n");
+		return -ENODEV;
+	}
+
+	sensor->v4l2_int_device = &ov9640_int_device;
+	sensor->i2c_client = client;
+
+	i2c_set_clientdata(client, sensor);
+
+	/* Make the default capture format QCIF RGB565 */
+	sensor->pix.width = ov9640_sizes[QCIF].width;
+	sensor->pix.height = ov9640_sizes[QCIF].height;
+	sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+
+	err = v4l2_int_device_register(sensor->v4l2_int_device);
+	if (err)
+		i2c_set_clientdata(client, NULL);
+
+	return 0;
+}
+
+static int __exit
+ov9640_remove(struct i2c_client *client)
+{
+	struct ov9640_sensor *sensor = i2c_get_clientdata(client);
+
+	if (!client->adapter)
+		return -ENODEV;	/* our client isn't attached */
+
+	v4l2_int_device_unregister(sensor->v4l2_int_device);
+	i2c_set_clientdata(client, NULL);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov9640_id[] = {
+	{ "ov9640", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov9640_id);
+
+static struct i2c_driver ov9640sensor_i2c_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe	= ov9640_probe,
+	.remove	= __exit_p(ov9640_remove),
+};
+
+static struct ov9640_sensor ov9640 = {
+	.timeperframe = {
+		.numerator = 1,
+		.denominator = 15,
+	},
+};
+
+static int __init ov9640sensor_init(void)
+{
+	int err;
+
+	err = i2c_add_driver(&ov9640sensor_i2c_driver);
+	if (err) {
+		printk(KERN_ERR "Failed to register" DRIVER_NAME ".\n");
+		return err;
+	}
+	return 0;
+}
+module_init(ov9640sensor_init);
+
+static void __exit ov9640sensor_cleanup(void)
+{
+	i2c_del_driver(&ov9640sensor_i2c_driver);
+}
+module_exit(ov9640sensor_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OV9640 camera sensor driver");
diff --git a/include/media/ov9640.h b/include/media/ov9640.h
new file mode 100644
index 0000000..fcee4d3
--- /dev/null
+++ b/include/media/ov9640.h
@@ -0,0 +1,192 @@
+/*
+ * Register definitions for the OmniVision OV9640 CameraChip.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef OV9640_H
+#define OV9640_H
+
+#define OV9640_I2C_ADDR		0x30
+
+/* define register offsets for the OV9640 sensor chip */
+#define OV9640_GAIN		0x00
+#define OV9640_BLUE		0x01
+#define OV9640_RED		0x02
+#define OV9640_VREF		0x03
+#define OV9640_COM1		0x04
+#define OV9640_BAVE		0x05
+#define OV9640_GEAVE		0x06
+#define OV9640_RAVE		0x08
+#define OV9640_COM2		0x09
+#define OV9640_PID		0x0A
+#define OV9640_VER		0x0B
+#define OV9640_COM3		0x0C
+#define OV9640_COM4		0x0D
+#define OV9640_COM5		0x0E
+#define OV9640_COM6		0x0F
+#define OV9640_AECH		0x10
+#define OV9640_CLKRC		0x11
+#define OV9640_COM7		0x12
+#define OV9640_COM8		0x13
+#define OV9640_COM9		0x14
+#define OV9640_COM10		0x15
+#define OV9640_HSTRT		0x17
+#define OV9640_HSTOP		0x18
+#define OV9640_VSTRT		0x19
+#define OV9640_VSTOP		0x1A
+#define OV9640_PSHFT		0x1B
+#define OV9640_MIDH		0x1C
+#define OV9640_MIDL		0x1D
+#define OV9640_MVFP		0x1E
+#define OV9640_LAEC		0x1F
+#define OV9640_BOS		0x20
+#define OV9640_GBOS		0x21
+#define OV9640_GROS		0x22
+#define OV9640_ROS		0x23
+#define OV9640_AEW		0x24
+#define OV9640_AEB		0x25
+#define OV9640_VPT		0x26
+#define OV9640_BBIAS		0x27
+#define OV9640_GBBIAS		0x28
+#define OV9640_EXHCH		0x2A
+#define OV9640_EXHCL		0x2B
+#define OV9640_RBIAS		0x2C
+#define OV9640_ADVFL		0x2D
+#define OV9640_ADVFH		0x2E
+#define OV9640_YAVE		0x2F
+#define OV9640_HSYST		0x30
+#define OV9640_HSYEN		0x31
+#define OV9640_HREF		0x32
+#define OV9640_CHLF		0x33
+#define OV9640_ARBLM		0x34
+#define OV9640_ADC		0x37
+#define OV9640_ACOM		0x38
+#define OV9640_OFON		0x39
+#define OV9640_TSLB		0x3A
+#define OV9640_COM11		0x3B
+#define OV9640_COM12		0x3C
+#define OV9640_COM13		0x3D
+#define OV9640_COM14		0x3E
+#define OV9640_EDGE		0x3F
+#define OV9640_COM15		0x40
+#define OV9640_COM16		0x41
+#define OV9640_COM17		0x42
+#define OV9640_MTX1		0x4F
+#define OV9640_MTX2		0x50
+#define OV9640_MTX3		0x51
+#define OV9640_MTX4		0x52
+#define OV9640_MTX5		0x53
+#define OV9640_MTX6		0x54
+#define OV9640_MTX7		0x55
+#define OV9640_MTX8		0x56
+#define OV9640_MTX9		0x57
+#define OV9640_MTXS		0x58
+#define OV9640_LCC1		0x62
+#define OV9640_LCC2		0x63
+#define OV9640_LCC3		0x64
+#define OV9640_LCC4		0x65
+#define OV9640_LCC5		0x66
+#define OV9640_MANU		0x67
+#define OV9640_MANV		0x68
+#define OV9640_HV		0x69
+#define OV9640_MBD		0x6A
+#define OV9640_DBLV		0x6B
+#define OV9640_GSP1		0x6C
+#define OV9640_GSP2		0x6D
+#define OV9640_GSP3		0x6E
+#define OV9640_GSP4		0x6F
+#define OV9640_GSP5		0x70
+#define OV9640_GSP6		0x71
+#define OV9640_GSP7		0x72
+#define OV9640_GSP8		0x73
+#define OV9640_GSP9		0x74
+#define OV9640_GSP10		0x75
+#define OV9640_GSP11		0x76
+#define OV9640_GSP12		0x77
+#define OV9640_GSP13		0x78
+#define OV9640_GSP14		0x79
+#define OV9640_GSP15		0x7A
+#define OV9640_GSP16		0x7B
+#define OV9640_GST1		0x7C
+#define OV9640_GST2		0x7D
+#define OV9640_GST3		0x7E
+#define OV9640_GST4		0x7F
+#define OV9640_GST5		0x80
+#define OV9640_GST6		0x81
+#define OV9640_GST7		0x82
+#define OV9640_GST8		0x83
+#define OV9640_GST9		0x84
+#define OV9640_GST10		0x85
+#define OV9640_GST11		0x86
+#define OV9640_GST12		0x87
+#define OV9640_GST13		0x88
+#define OV9640_GST14		0x89
+#define OV9640_GST15		0x8A
+
+#define OV9640_NUM_REGS		(OV9640_GST15 + 1)
+
+#define OV9640_PID_MAGIC	0x96	/* high byte of product ID number */
+#define OV9640_VER_REV2		0x48	/* low byte of product ID number */
+#define OV9640_VER_REV3		0x49	/* low byte of product ID number */
+#define OV9640_MIDH_MAGIC	0x7F	/* high byte of mfg ID */
+#define OV9640_MIDL_MAGIC	0xA2	/* low byte of mfg ID */
+
+#define OV9640_REG_TERM 0xFF	/* terminating list entry for reg */
+#define OV9640_VAL_TERM 0xFF	/* terminating list entry for val */
+
+/*
+ * The nominal xclk input frequency of the OV9640 is 24MHz, maximum
+ * frequency is 48MHz, and minimum frequency is 10MHz.
+ */
+#define OV9640_XCLK_MIN 10000000
+#define OV9640_XCLK_MAX 48000000
+#define OV9640_XCLK_NOM 24000000
+
+/* define a structure for ov9640 register initialization values */
+struct ov9640_reg {
+	unsigned char reg;
+	unsigned char val;
+};
+
+enum image_size { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
+enum pixel_format { YUV, RGB565, RGB555 };
+
+#define NUM_IMAGE_SIZES 7
+#define NUM_PIXEL_FORMATS 3
+
+struct capture_size {
+	unsigned long width;
+	unsigned long height;
+};
+
+struct ov9640_platform_data {
+	/* Set power state, zero is off, non-zero is on. */
+	int (*power_set)(int power);
+	/* Default registers written after power-on or reset. */
+	const struct ov9640_reg *default_regs;
+	int (*ifparm)(struct v4l2_ifparm *p);
+};
+
+/*
+ * Array of image sizes supported by OV9640.  These must be ordered from
+ * smallest image size to largest.
+ */
+const static struct capture_size ov9640_sizes[] = {
+	{   88,  72 },	/* QQCIF */
+	{  160, 120 },	/* QQVGA */
+	{  176, 144 },	/* QCIF */
+	{  320, 240 },	/* QVGA */
+	{  352, 288 },	/* CIF */
+	{  640, 480 },	/* VGA */
+	{ 1280, 960 },	/* SXGA */
+};
+
+#endif /* ifndef OV9640_H */
-- 
1.6.0.3


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
  2008-11-28 10:16 ` Trilok Soni
@ 2008-12-01 12:34   ` Hans Verkuil
  -1 siblings, 0 replies; 17+ messages in thread
From: Hans Verkuil @ 2008-12-01 12:34 UTC (permalink / raw)
  To: Trilok Soni; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

On Friday 28 November 2008 11:16:20 Trilok Soni wrote:
> --
> ---Trilok Soni
> http://triloksoni.wordpress.com
> http://www.linkedin.com/in/triloksoni

Hi Trilok,

I reviewed this sensor driver and it's fine except for one thing: 
setting the default registers from outside the driver. This is a really 
bad idea. I2C drivers should be self-contained. I've made the same 
comment in the tvp514x driver review which I'm copying below (with some 
small edits):

This driver relies on an outside source to do the initialization of the 
registers. This is very much the wrong approach. It would require all 
platforms/master drivers that are going to use this chip to program the 
chip's registers. That's not the way to do it. The ov9640 driver should 
do this and the bridge driver should only pass high-level config data 
(such as with input/output pins are being used, signalling setup, 
whatever). This is how for example the Micron sensor mt* drivers do it. 
This approach means that all the relevant programming is inside the 
chip's driver and that bridge drivers can easily replace one chip for 
another since except for a little bit of initialization (usually 
routing, signalling setup) there is otherwise no difference between 
using one chip or another.

Remember that this is not a driver for use with omap, this is a generic 
i2c driver for this chip. omap just happens to be the only one using it 
right now, but that can easily change.

Yes, it makes it harder to write an i2c driver, but experience has shown 
us that the advantages regarding reuse far outweigh this initial cost.

I noticed that the tcm825x.c driver takes exactly the same wrong 
approach, BTW.

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
@ 2008-12-01 12:34   ` Hans Verkuil
  0 siblings, 0 replies; 17+ messages in thread
From: Hans Verkuil @ 2008-12-01 12:34 UTC (permalink / raw)
  To: Trilok Soni; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

On Friday 28 November 2008 11:16:20 Trilok Soni wrote:
> --
> ---Trilok Soni
> http://triloksoni.wordpress.com
> http://www.linkedin.com/in/triloksoni

Hi Trilok,

I reviewed this sensor driver and it's fine except for one thing: 
setting the default registers from outside the driver. This is a really 
bad idea. I2C drivers should be self-contained. I've made the same 
comment in the tvp514x driver review which I'm copying below (with some 
small edits):

This driver relies on an outside source to do the initialization of the 
registers. This is very much the wrong approach. It would require all 
platforms/master drivers that are going to use this chip to program the 
chip's registers. That's not the way to do it. The ov9640 driver should 
do this and the bridge driver should only pass high-level config data 
(such as with input/output pins are being used, signalling setup, 
whatever). This is how for example the Micron sensor mt* drivers do it. 
This approach means that all the relevant programming is inside the 
chip's driver and that bridge drivers can easily replace one chip for 
another since except for a little bit of initialization (usually 
routing, signalling setup) there is otherwise no difference between 
using one chip or another.

Remember that this is not a driver for use with omap, this is a generic 
i2c driver for this chip. omap just happens to be the only one using it 
right now, but that can easily change.

Yes, it makes it harder to write an i2c driver, but experience has shown 
us that the advantages regarding reuse far outweigh this initial cost.

I noticed that the tcm825x.c driver takes exactly the same wrong 
approach, BTW.

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
  2008-12-01 12:34   ` Hans Verkuil
@ 2008-12-01 12:51     ` Trilok Soni
  -1 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-12-01 12:51 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

Hi Hans,

>
> I reviewed this sensor driver and it's fine except for one thing:
> setting the default registers from outside the driver. This is a really
> bad idea. I2C drivers should be self-contained. I've made the same
> comment in the tvp514x driver review which I'm copying below (with some
> small edits):

I knew that you are going to comment on that, and I agree on those
points. I will pull in that register initialization to the driver.

>
> I noticed that the tcm825x.c driver takes exactly the same wrong
> approach, BTW.

Yes, because ov9640 was the first sensor driver on OMAP2 ;)

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
@ 2008-12-01 12:51     ` Trilok Soni
  0 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-12-01 12:51 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

Hi Hans,

>
> I reviewed this sensor driver and it's fine except for one thing:
> setting the default registers from outside the driver. This is a really
> bad idea. I2C drivers should be self-contained. I've made the same
> comment in the tvp514x driver review which I'm copying below (with some
> small edits):

I knew that you are going to comment on that, and I agree on those
points. I will pull in that register initialization to the driver.

>
> I noticed that the tcm825x.c driver takes exactly the same wrong
> approach, BTW.

Yes, because ov9640 was the first sensor driver on OMAP2 ;)

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
  2008-12-01 12:51     ` Trilok Soni
@ 2008-12-07  9:40       ` Trilok Soni
  -1 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-12-07  9:40 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

[-- Attachment #1: Type: text/plain, Size: 682 bytes --]

Hi Hans,

On Mon, Dec 1, 2008 at 6:21 PM, Trilok Soni <soni.trilok@gmail.com> wrote:
> Hi Hans,
>
>>
>> I reviewed this sensor driver and it's fine except for one thing:
>> setting the default registers from outside the driver. This is a really
>> bad idea. I2C drivers should be self-contained. I've made the same
>> comment in the tvp514x driver review which I'm copying below (with some
>> small edits):
>
> I knew that you are going to comment on that, and I agree on those
> points. I will pull in that register initialization to the driver.
>

Attached the updated ov9640 sensor patch.

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-Omnivision-OV9640-sensor-support.patch --]
[-- Type: text/x-patch; name=0001-Add-Omnivision-OV9640-sensor-support.patch, Size: 46396 bytes --]

From 5c1b3e8dbc6c896eb60c8852e1f4dc3094d73ce1 Mon Sep 17 00:00:00 2001
From: Trilok Soni <soni.trilok@gmail.com>
Date: Fri, 28 Nov 2008 14:20:18 +0530
Subject: [PATCH] Add Omnivision OV9640 sensor support.

- Add driver for Omnivision OV9640 camera sensor. It has been
used with Texas Instruments OMAP1/2 based evaluation boards.

Signed-off-by: Trilok Soni <soni.trilok@gmail.com>
---
 drivers/media/video/Kconfig  |    7 +
 drivers/media/video/Makefile |    1 +
 drivers/media/video/ov9640.c | 1305 ++++++++++++++++++++++++++++++++++++++++++
 include/media/ov9640.h       |  190 ++++++
 4 files changed, 1503 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/ov9640.c
 create mode 100644 include/media/ov9640.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d980f23..b58f75b 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -305,6 +305,13 @@ config VIDEO_TCM825X
 	  This is a driver for the Toshiba TCM825x VGA camera sensor.
 	  It is used for example in Nokia N800.
 
+config VIDEO_OV9640
+	tristate "OmniVision OV9640 sensor support"
+	depends on I2C && VIDEO_V4L2
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV9640.
+
 config VIDEO_SAA7110
 	tristate "Philips SAA7110 video decoder"
 	depends on VIDEO_V4L1 && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 0797c3c..b2fe305 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
 obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
 obj-$(CONFIG_VIDEO_OMAP2)		+= omap2cam.o
+obj-$(CONFIG_VIDEO_OV9640)		+= ov9640.o
 obj-$(CONFIG_SOC_CAMERA)	+= soc_camera.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
 obj-$(CONFIG_SOC_CAMERA_MT9M111)	+= mt9m111.o
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
new file mode 100644
index 0000000..64a63f9
--- /dev/null
+++ b/drivers/media/video/ov9640.c
@@ -0,0 +1,1305 @@
+/*
+ * drivers/media/video/ov9640.c
+ *
+ * OV9640 sensor driver
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ * Contact: Trilok Soni <soni.trilok@gmail.com>
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <media/v4l2-int-device.h>
+
+#include <media/ov9640.h>
+
+#define DRIVER_NAME  "ov9640"
+
+struct ov9640_sensor {
+	const struct ov9640_platform_data *pdata;
+	struct v4l2_int_device *v4l2_int_device;
+	struct i2c_client *i2c_client;
+	struct v4l2_pix_format pix;
+	struct v4l2_fract timeperframe;
+	int ver;				/*ov9640 chip version*/
+};
+
+static struct ov9640_sensor ov9640;
+static struct i2c_driver ov9640sensor_i2c_driver;
+
+/*
+ * Common OV9640 register initialization for all image sizes, pixel formats,
+ * and frame rates
+ */
+const static struct ov9640_reg ov9640_common[] = {
+	{ 0x12, 0x80 }, { 0x11, 0x80 }, { 0x13, 0x8F },	/* COM7, CLKRC, COM8 */
+	{ 0x01, 0x80 }, { 0x02, 0x80 }, { 0x04, 0x00 },	/* BLUE, RED, COM1 */
+	{ 0x0E, 0x81 }, { 0x0F, 0x4F }, { 0x14, 0x4A },	/* COM5, COM6, COM9 */
+	{ 0x16, 0x02 }, { 0x1B, 0x01 }, { 0x24, 0x70 },	/* ?, PSHFT, AEW */
+	{ 0x25, 0x68 }, { 0x26, 0xD3 }, { 0x27, 0x90 },	/* AEB, VPT, BBIAS */
+	{ 0x2A, 0x00 }, { 0x2B, 0x00 }, { 0x32, 0x24 },	/* EXHCH, EXHCL, HREF */
+	{ 0x33, 0x02 }, { 0x37, 0x02 }, { 0x38, 0x13 },	/* CHLF, ADC, ACOM */
+	{ 0x39, 0xF0 }, { 0x3A, 0x00 }, { 0x3B, 0x01 },	/* OFON, TSLB, COM11 */
+	{ 0x3D, 0x90 }, { 0x3E, 0x02 }, { 0x3F, 0xF2 },	/* COM13, COM14, EDGE */
+	{ 0x41, 0x02 }, { 0x42, 0xC8 },		/* COM16, COM17 */
+	{ 0x43, 0xF0 }, { 0x44, 0x10 }, { 0x45, 0x6C },	/* ?, ?, ? */
+	{ 0x46, 0x6C }, { 0x47, 0x44 }, { 0x48, 0x44 },	/* ?, ?, ? */
+	{ 0x49, 0x03 }, { 0x59, 0x49 }, { 0x5A, 0x94 },	/* ?, ?, ? */
+	{ 0x5B, 0x46 }, { 0x5C, 0x84 }, { 0x5D, 0x5C },	/* ?, ?, ? */
+	{ 0x5E, 0x08 }, { 0x5F, 0x00 }, { 0x60, 0x14 },	/* ?, ?, ? */
+	{ 0x61, 0xCE },					/* ? */
+	{ 0x62, 0x70 }, { 0x63, 0x00 }, { 0x64, 0x04 },	/* LCC1, LCC2, LCC3 */
+	{ 0x65, 0x00 }, { 0x66, 0x00 },			/* LCC4, LCC5 */
+	{ 0x69, 0x00 }, { 0x6A, 0x3E }, { 0x6B, 0x3F },	/* HV, MBD, DBLV */
+	{ 0x6C, 0x40 }, { 0x6D, 0x30 }, { 0x6E, 0x4B },	/* GSP1, GSP2, GSP3 */
+	{ 0x6F, 0x60 }, { 0x70, 0x70 }, { 0x71, 0x70 },	/* GSP4, GSP5, GSP6 */
+	{ 0x72, 0x70 }, { 0x73, 0x70 }, { 0x74, 0x60 },	/* GSP7, GSP8, GSP9 */
+	{ 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 },	/* GSP10,GSP11,GSP12 */
+	{ 0x78, 0x3A }, { 0x79, 0x2E }, { 0x7A, 0x28 },	/* GSP13,GSP14,GSP15 */
+	{ 0x7B, 0x22 }, { 0x7C, 0x04 }, { 0x7D, 0x07 },	/* GSP16,GST1, GST2 */
+	{ 0x7E, 0x10 }, { 0x7F, 0x28 }, { 0x80, 0x36 },	/* GST3, GST4, GST5 */
+	{ 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 },	/* GST6, GST7, GST8 */
+	{ 0x84, 0x6C }, { 0x85, 0x78 }, { 0x86, 0x8C },	/* GST9, GST10,GST11 */
+	{ 0x87, 0x9E }, { 0x88, 0xBB }, { 0x89, 0xD2 },	/* GST12,GST13,GST14 */
+	{ 0x8A, 0xE6 }, { 0x13, 0x8F }, { 0x00, 0x7F },	/* GST15, COM8 */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+
+/* list of image formats supported by OV9640 sensor */
+const static struct v4l2_fmtdesc ov9640_formats[] = {
+	{
+		/* Note:  V4L2 defines RGB565 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 r4 r3 r2 r1 r0	  b4 b3 b2 b1 b0 g5 g4 g3
+		 *
+		 * We interpret RGB565 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 b4 b3 b2 b1 b0	  r4 r3 r2 r1 r0 g5 g4 g3
+		 */
+		.description	= "RGB565, le",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+	}, {
+		/* Note:  V4L2 defines RGB565X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	b4 b3 b2 b1 b0 g5 g4 g3	  g2 g1 g0 r4 r3 r2 r1 r0
+		 *
+		 * We interpret RGB565X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	r4 r3 r2 r1 r0 g5 g4 g3	  g2 g1 g0 b4 b3 b2 b1 b0
+		 */
+		.description	= "RGB565, be",
+		.pixelformat	= V4L2_PIX_FMT_RGB565X,
+	}, {
+		.description	= "YUYV (YUV 4:2:2), packed",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+	}, {
+		.description	= "UYVY, packed",
+		.pixelformat	= V4L2_PIX_FMT_UYVY,
+	}, {
+		/* Note:  V4L2 defines RGB555 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 r4 r3 r2 r1 r0	  x  b4 b3 b2 b1 b0 g4 g3
+		 *
+		 * We interpret RGB555 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 b4 b3 b2 b1 b0	  x  r4 r3 r2 r1 r0 g4 g3
+		 */
+		.description	= "RGB555, le",
+		.pixelformat	= V4L2_PIX_FMT_RGB555,
+	}, {
+		/* Note:  V4L2 defines RGB555X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	x  b4 b3 b2 b1 b0 g4 g3	  g2 g1 g0 r4 r3 r2 r1 r0
+		 *
+		 * We interpret RGB555X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	x  r4 r3 r2 r1 r0 g4 g3	  g2 g1 g0 b4 b3 b2 b1 b0
+		 */
+		.description	= "RGB555, be",
+		.pixelformat	= V4L2_PIX_FMT_RGB555X,
+	},
+};
+
+#define NUM_CAPTURE_FORMATS ARRAY_SIZE(ov9640_formats)
+
+/*
+ * OV9640 register configuration for all combinations of pixel format and
+ * image size
+ */
+	/* YUV (YCbCr) QQCIF */
+const static struct ov9640_reg qqcif_yuv[] = {
+	{ 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QQVGA */
+const static struct ov9640_reg qqvga_yuv[] = {
+	{ 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QCIF */
+const static struct ov9640_reg qcif_yuv[] = {
+	{ 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QVGA */
+const static struct ov9640_reg qvga_yuv[] = {
+	{ 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) CIF */
+const static struct ov9640_reg cif_yuv[] = {
+	{ 0x12, 0x20 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) VGA */
+const static struct ov9640_reg vga_yuv[] = {
+	{ 0x12, 0x40 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) SXGA */
+const static struct ov9640_reg sxga_yuv[] = {
+	{ 0x12, 0x00 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QQCIF */
+const static struct ov9640_reg qqcif_565[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QQVGA */
+const static struct ov9640_reg qqvga_565[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QCIF */
+const static struct ov9640_reg qcif_565[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QVGA */
+const static struct ov9640_reg qvga_565[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 CIF */
+const static struct ov9640_reg cif_565[] = {
+	{ 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 VGA */
+const static struct ov9640_reg vga_565[] = {
+	{ 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 SXGA */
+const static struct ov9640_reg sxga_565[] = {
+	{ 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QQCIF */
+const static struct ov9640_reg qqcif_555[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QQVGA */
+const static struct ov9640_reg qqvga_555[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QCIF */
+const static struct ov9640_reg qcif_555[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QVGA */
+const static struct ov9640_reg qvga_555[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 CIF */
+const static struct ov9640_reg cif_555[] = {
+	{ 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 VGA */
+const static struct ov9640_reg vga_555[] = {
+	{ 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 SXGA */
+const static struct ov9640_reg sxga_555[] = {
+	{ 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+
+
+#define DEF_GAIN         31
+#define DEF_AUTOGAIN      1
+#define DEF_EXPOSURE    154
+#define DEF_AEC           1
+#define DEF_FREEZE_AGCAEC 0
+#define DEF_BLUE        153
+#define DEF_RED         (255 - DEF_BLUE)
+#define DEF_AWB           1
+#define DEF_HFLIP         0
+#define DEF_VFLIP         0
+
+/* Our own specific controls */
+#define V4L2_CID_FREEZE_AGCAEC		V4L2_CID_PRIVATE_BASE
+#define V4L2_CID_AUTOEXPOSURE		(V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_LAST_PRIV		V4L2_CID_AUTOEXPOSURE
+
+/*  Video controls  */
+static struct vcontrol {
+	struct v4l2_queryctrl qc;
+	int current_value;
+	u8 reg;
+	u8 mask;
+	u8 start_bit;
+} video_control[] = {
+	{
+		{
+			.id = V4L2_CID_GAIN,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Gain",
+			.minimum = 0,
+			.maximum = 63,
+			.step = 1,
+			.default_value = DEF_GAIN,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_GAIN,
+		.mask		= 0x3f,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTOGAIN,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto Gain",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AUTOGAIN,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x04,
+		.start_bit	= 2,
+	}, {
+		{
+			.id = V4L2_CID_EXPOSURE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Exposure",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_EXPOSURE,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_AECH,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTOEXPOSURE,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto Exposure",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AEC,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x01,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_FREEZE_AGCAEC,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Freeze AGC/AEC",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_FREEZE_AGCAEC,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM9,
+		.mask		= 0x01,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_RED_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Red Balance",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_RED,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_RED,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_BLUE_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Blue Balance",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_BLUE,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_BLUE,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTO_WHITE_BALANCE,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto White Balance",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AWB,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x02,
+		.start_bit	= 1,
+	}, {
+		{
+			.id = V4L2_CID_HFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Mirror Image",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_HFLIP,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_MVFP,
+		.mask		= 0x20,
+		.start_bit	= 5,
+	}, {
+		{
+			.id = V4L2_CID_VFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Vertical Flip",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_VFLIP,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_MVFP,
+		.mask		= 0x10,
+		.start_bit	= 4,
+	},
+};
+
+const static struct ov9640_reg *
+	ov9640_reg_init[NUM_PIXEL_FORMATS][NUM_IMAGE_SIZES] =
+{
+ { qqcif_yuv, qqvga_yuv, qcif_yuv, qvga_yuv, cif_yuv, vga_yuv, sxga_yuv },
+ { qqcif_565, qqvga_565, qcif_565, qvga_565, cif_565, vga_565, sxga_565 },
+ { qqcif_555, qqvga_555, qcif_555, qvga_555, cif_555, vga_555, sxga_555 },
+};
+
+
+/*
+ * Read a value from a register in an OV9640 sensor device.
+ * The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_read_reg(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int err;
+	struct i2c_msg msg[1];
+	unsigned char data[1];
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 1;
+	msg->buf = data;
+	*data = reg;
+	err = i2c_transfer(client->adapter, msg, 1);
+	if (err >= 0) {
+		msg->flags = I2C_M_RD;
+		err = i2c_transfer(client->adapter, msg, 1);
+	}
+	if (err >= 0) {
+		*val = *data;
+		return 0;
+	}
+	return err;
+}
+
+/*
+ * Write a value to a register in an OV9640 sensor device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int err;
+	struct i2c_msg msg[1];
+	unsigned char data[2];
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 2;
+	msg->buf = data;
+	data[0] = reg;
+	data[1] = val;
+	err = i2c_transfer(client->adapter, msg, 1);
+	if (err >= 0)
+		return 0;
+	return err;
+}
+
+static int
+ov9640_write_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+	u8 oldval, newval;
+	int rc;
+
+	if (mask == 0xff)
+		newval = *val;
+	else {
+		/* need to do read - modify - write */
+		rc = ov9640_read_reg(client, reg, &oldval);
+		if (rc)
+			return rc;
+		oldval &= (~mask);              /* Clear the masked bits */
+		*val &= mask;                  /* Enforce mask on value */
+		newval = oldval | *val;        /* Set the desired bits */
+	}
+
+	/* write the new value to the register */
+	rc = ov9640_write_reg(client, reg, newval);
+	if (rc)
+		return rc;
+
+	rc = ov9640_read_reg(client, reg, &newval);
+	if (rc)
+		return rc;
+
+	*val = newval & mask;
+	return 0;
+}
+
+static int
+ov9640_read_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+	int rc;
+
+	rc = ov9640_read_reg(client, reg, val);
+	if (rc)
+		return rc;
+	(*val) &= mask;
+
+	return 0;
+}
+
+/*
+ * Initialize a list of OV9640 registers.
+ * The list of registers is terminated by the pair of values
+ * { OV9640_REG_TERM, OV9640_VAL_TERM }.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_write_regs(struct i2c_client *client, const struct ov9640_reg reglist[])
+{
+	int err;
+	const struct ov9640_reg *next = reglist;
+
+	while (!((next->reg == OV9640_REG_TERM)
+		&& (next->val == OV9640_VAL_TERM))) {
+		err = ov9640_write_reg(client, next->reg, next->val);
+		udelay(100);
+		if (err)
+			return err;
+		next++;
+	}
+	return 0;
+}
+
+/* Returns the index of the requested ID from the control structure array */
+static int
+find_vctrl(int id)
+{
+	int i;
+
+	if (id < V4L2_CID_BASE)
+		return -EDOM;
+
+	for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--)
+		if (video_control[i].qc.id == id)
+			break;
+	if (i < 0)
+		i = -EINVAL;
+	return i;
+}
+
+/*
+ * Calculate the internal clock divisor (value of the CLKRC register) of the
+ * OV9640 given the image size, the frequency (in Hz) of its XCLK input and a
+ * desired frame period (in seconds).  The frame period 'fper' is expressed as
+ * a fraction.  The frame period is an input/output parameter.
+ * Returns the value of the OV9640 CLKRC register that will yield the frame
+ * period returned in 'fper' at the specified xclk frequency.  The
+ * returned period will be as close to the requested period as possible.
+ */
+static unsigned char
+ov9640_clkrc(enum image_size isize, unsigned long xclk, struct v4l2_fract *fper)
+{
+	unsigned long fpm, fpm_max;	/* frames per minute */
+	unsigned long divisor;
+	const unsigned long divisor_max = 64;
+	/* FIXME
+	 * clks_per_frame should come from platform data
+	 */
+	const static unsigned long clks_per_frame[] =
+		{ 200000, 400000, 200000, 400000, 400000, 800000, 3200000 };
+      /*         QQCIF   QQVGA    QCIF    QVGA  CIF     VGA	SXGA
+       *         199680,400000, 199680, 400000, 399360, 800000, 3200000
+       */
+
+	if (fper->numerator > 0)
+		fpm = (fper->denominator*60)/fper->numerator;
+	else
+		fpm = 0xffffffff;
+	fpm_max = (xclk*60)/clks_per_frame[isize];
+	if (fpm_max == 0)
+		fpm_max = 1;
+	if (fpm > fpm_max)
+		fpm = fpm_max;
+	if (fpm == 0)
+		fpm = 1;
+	divisor = fpm_max/fpm;
+	if (divisor > divisor_max)
+		divisor = divisor_max;
+	fper->numerator = divisor*60;
+	fper->denominator = fpm_max;
+
+	/* try to reduce the fraction */
+	while (!(fper->denominator % 5) && !(fper->numerator % 5)) {
+		fper->numerator /= 5;
+		fper->denominator /= 5;
+	}
+	while (!(fper->denominator % 3) && !(fper->numerator % 3)) {
+		fper->numerator /= 3;
+		fper->denominator /= 3;
+	}
+	while (!(fper->denominator % 2) && !(fper->numerator % 2)) {
+		fper->numerator /= 2;
+		fper->denominator /= 2;
+	}
+	if (fper->numerator < fper->denominator) {
+		if (!(fper->denominator % fper->numerator)) {
+			fper->denominator /= fper->numerator;
+			fper->numerator = 1;
+		}
+	} else {
+		if (!(fper->numerator % fper->denominator)) {
+			fper->numerator /= fper->denominator;
+			fper->denominator = 1;
+		}
+	}
+
+	/* we set bit 7 in CLKRC to enable the digital PLL */
+	return 0x80 | (divisor - 1);
+}
+
+/*
+ * Find the best match for a requested image capture size.  The best match
+ * is chosen as the nearest match that has the same number or fewer pixels
+ * as the requested size, or the smallest image size if the requested size
+ * has fewer pixels than the smallest image.
+ */
+static enum image_size
+ov9640_find_size(unsigned int width, unsigned int height)
+{
+	enum image_size isize;
+	unsigned long pixels = width*height;
+
+	for (isize = QQCIF; isize < SXGA; isize++) {
+		if (ov9640_sizes[isize + 1].height *
+			ov9640_sizes[isize + 1].width > pixels)
+			return isize;
+	}
+	return SXGA;
+}
+
+/*
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency
+ */
+static unsigned long
+ov9640sensor_calc_xclk(struct i2c_client *c)
+{
+	struct ov9640_sensor *sensor = i2c_get_clientdata(c);
+	struct v4l2_fract *timeperframe = &sensor->timeperframe;
+	struct v4l2_pix_format *pix = &sensor->pix;
+
+	unsigned long tgt_xclk;			/* target xclk */
+	unsigned long tgt_fpm;			/* target frames per minute */
+	enum image_size isize;
+
+	/*
+	 * We use arbitrary rules to select the xclk frequency.  If the
+	 * capture size is VGA and the frame rate is greater than 900
+	 * frames per minute, or if the capture size is SXGA and the
+	 * frame rate is greater than 450 frames per minutes, then the
+	 * xclk frequency will be set to 48MHz.  Otherwise, the xclk
+	 * frequency will be set to 24MHz.  If the mclk frequency is such that
+	 * the target xclk frequency is not achievable, then xclk will be set
+	 * as close as to the target as possible.
+	 */
+	tgt_fpm = (timeperframe->denominator*60)
+		/ timeperframe->numerator;
+	tgt_xclk = OV9640_XCLK_NOM;
+	isize = ov9640_find_size(pix->width, pix->height);
+	switch (isize) {
+	case SXGA:
+		if (tgt_fpm > 450)
+			tgt_xclk = OV9640_XCLK_MAX;
+		break;
+	case VGA:
+		if (tgt_fpm > 900)
+			tgt_xclk = OV9640_XCLK_MAX;
+		break;
+	default:
+		break;
+	}
+	return tgt_xclk;
+}
+
+/*
+ * Configure the OV9640 for a specified image size, pixel format, and frame
+ * period.  xclk is the frequency (in Hz) of the xclk input to the OV9640.
+ * fper is the frame period (in seconds) expressed as a fraction.
+ * Returns zero if successful, or non-zero otherwise.
+ * The actual frame period is returned in fper.
+ */
+static int ov9640_configure(struct v4l2_int_device *s)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_pix_format *pix = &sensor->pix;
+	struct v4l2_fract *fper = &sensor->timeperframe;
+	struct i2c_client *client = sensor->i2c_client;
+	enum image_size isize;
+	unsigned long xclk;
+
+	int err;
+	unsigned char clkrc;
+	enum pixel_format pfmt = YUV;
+
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_RGB565X:
+		pfmt = RGB565;
+		break;
+	case V4L2_PIX_FMT_RGB555:
+	case V4L2_PIX_FMT_RGB555X:
+		pfmt = RGB555;
+		break;
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_UYVY:
+	default:
+		pfmt = YUV;
+	}
+
+	xclk = ov9640sensor_calc_xclk(client);
+
+	isize = ov9640_find_size(pix->width, pix->height);
+
+	/* common register initialization */
+	err = ov9640_write_regs(client, ov9640_common);
+	if (err)
+		return err;
+
+	/* configure image size and pixel format */
+	err = ov9640_write_regs(client, ov9640_reg_init[pfmt][isize]);
+	if (err)
+		return err;
+
+	/* configure frame rate */
+	clkrc = ov9640_clkrc(isize, xclk, fper);
+	err = ov9640_write_reg(client, OV9640_CLKRC, clkrc);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/*
+ * Detect if an OV9640 is present, and if so which revision.
+ * A device is considered to be detected if the manufacturer ID (MIDH and MIDL)
+ * and the product ID (PID) registers match the expected values.
+ * Any value of the version ID (VER) register is accepted.
+ * Here are the version numbers we know about:
+ *	0x48 --> OV9640 Revision 1 or OV9640 Revision 2
+ *	0x49 --> OV9640 Revision 3
+ * Returns a negative error number if no device is detected, or the
+ * non-negative value of the version ID register if a device is detected.
+ */
+static int
+ov9640_detect(struct i2c_client *client)
+{
+	u8 midh, midl, pid, ver;
+
+	if (!client)
+		return -ENODEV;
+
+	if (ov9640_read_reg(client, OV9640_MIDH, &midh))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_MIDL, &midl))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_PID, &pid))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_VER, &ver))
+		return -ENODEV;
+
+	if ((midh != OV9640_MIDH_MAGIC)
+		|| (midl != OV9640_MIDL_MAGIC)
+		|| (pid != OV9640_PID_MAGIC))
+		/*
+		 * We didn't read the values we expected, so
+		 * this must not be an OV9640.
+		 */
+		return -ENODEV;
+
+	return ver;
+}
+
+/*
+ * following are sensor interface functions implemented by
+ * OV9640 sensor driver.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+				struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	i = find_vctrl(qc->id);
+	if (i == -EINVAL) {
+		qc->flags = V4L2_CTRL_FLAG_DISABLED;
+		return 0;
+	}
+	if (i < 0)
+		return -EINVAL;
+
+	*qc = video_control[i].qc;
+	return 0;
+}
+
+static int ioctl_g_ctrl(struct v4l2_int_device *s,
+			     struct v4l2_control *vc)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	int i, val;
+	struct vcontrol *lvc;
+
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &video_control[i];
+	if (ov9640_read_reg_mask(client, lvc->reg, (u8 *)&val, lvc->mask))
+		return -EIO;
+
+	val = val >> lvc->start_bit;
+	if (val >= 0) {
+		vc->value = lvc->current_value = val;
+		return 0;
+	} else
+		return val;
+}
+
+static int ioctl_s_ctrl(struct v4l2_int_device *s,
+			     struct v4l2_control *vc)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	struct vcontrol *lvc;
+	int val = vc->value;
+	int i;
+
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &video_control[i];
+	val = val << lvc->start_bit;
+	if (ov9640_write_reg_mask(client, lvc->reg, (u8 *)&val, (u8)lvc->mask))
+		return -EIO;
+
+	val = val >> lvc->start_bit;
+	if (val >= 0) {
+		lvc->current_value = val;
+		return 0;
+	} else
+		return val;
+}
+
+/*
+ * Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+				   struct v4l2_fmtdesc *fmt)
+{
+	int index = fmt->index;
+	enum v4l2_buf_type type = fmt->type;
+
+	memset(fmt, 0, sizeof(*fmt));
+	fmt->index = index;
+	fmt->type = type;
+
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (index >= NUM_CAPTURE_FORMATS)
+			return -EINVAL;
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	fmt->flags = ov9640_formats[index].flags;
+	strlcpy(fmt->description, ov9640_formats[index].description,
+					sizeof(fmt->description));
+	fmt->pixelformat = ov9640_formats[index].pixelformat;
+
+	return 0;
+}
+
+/*
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.  This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int ioctl_try_fmt_cap(struct v4l2_int_device *s,
+			     struct v4l2_format *f)
+{
+	enum image_size isize;
+	int ifmt;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+
+	isize = ov9640_find_size(pix->width, pix->height);
+	pix->width = ov9640_sizes[isize].width;
+	pix->height = ov9640_sizes[isize].height;
+	for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
+		if (pix->pixelformat == ov9640_formats[ifmt].pixelformat)
+			break;
+	}
+	if (ifmt == NUM_CAPTURE_FORMATS)
+		ifmt = 0;
+	pix->pixelformat = ov9640_formats[ifmt].pixelformat;
+	pix->field = V4L2_FIELD_NONE;
+	pix->bytesperline = pix->width*2;
+	pix->sizeimage = pix->bytesperline*pix->height;
+	pix->priv = 0;
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_UYVY:
+	default:
+		pix->colorspace = V4L2_COLORSPACE_JPEG;
+		break;
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_RGB565X:
+	case V4L2_PIX_FMT_RGB555:
+	case V4L2_PIX_FMT_RGB555X:
+		pix->colorspace = V4L2_COLORSPACE_SRGB;
+		break;
+	}
+	return 0;
+}
+
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s,
+				struct v4l2_format *f)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	int rval;
+
+	rval = ioctl_try_fmt_cap(s, f);
+	if (rval)
+		return rval;
+
+	rval = ov9640_configure(s);
+
+	if (!rval)
+		sensor->pix = *pix;
+
+	return rval;
+}
+
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s,
+				struct v4l2_format *f)
+{
+	struct ov9640_sensor *sensor = s->priv;
+
+	f->fmt.pix = sensor->pix;
+
+	return 0;
+}
+
+static int ioctl_g_parm(struct v4l2_int_device *s,
+			     struct v4l2_streamparm *a)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	cparm->capability = V4L2_CAP_TIMEPERFRAME;
+	cparm->timeperframe = sensor->timeperframe;
+
+	return 0;
+}
+
+static int ioctl_s_parm(struct v4l2_int_device *s,
+			     struct v4l2_streamparm *a)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+	struct v4l2_fract timeperframe_old;
+	int rval;
+
+	timeperframe_old = sensor->timeperframe;
+	sensor->timeperframe = *timeperframe;
+
+	rval = ov9640_configure(s);
+
+	if (rval)
+		sensor->timeperframe = timeperframe_old;
+	else
+		*timeperframe = sensor->timeperframe;
+
+	return rval;
+}
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	u32 xclk;	/* target xclk */
+	int rval;
+
+	rval = sensor->pdata->ifparm(p);
+	if (rval)
+		return rval;
+
+	xclk = ov9640sensor_calc_xclk(client);
+
+	p->u.bt656.clock_curr = xclk;
+
+	return 0;
+}
+
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+	struct ov9640_sensor *sensor = s->priv;
+
+	return sensor->pdata->power_set(on);
+}
+
+static int ioctl_init(struct v4l2_int_device *s)
+{
+	return ov9640_configure(s);
+}
+
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+	return 0;
+}
+
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *c = sensor->i2c_client;
+	int err;
+
+	err = ov9640_detect(c);
+	if (err < 0) {
+		dev_err(&c->dev, "Unable to detect " DRIVER_NAME " sensor\n");
+		return err;
+	}
+
+	sensor->ver = err;
+	pr_info(DRIVER_NAME " chip version 0x%02x detected\n", sensor->ver);
+
+	return 0;
+}
+
+static struct v4l2_int_ioctl_desc ov9640_ioctl_desc[] = {
+	{ vidioc_int_dev_init_num,
+	  (v4l2_int_ioctl_func *)ioctl_dev_init },
+	{ vidioc_int_dev_exit_num,
+	  (v4l2_int_ioctl_func *)ioctl_dev_exit },
+	{ vidioc_int_s_power_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_power },
+	{ vidioc_int_g_ifparm_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_ifparm },
+	{ vidioc_int_init_num,
+	  (v4l2_int_ioctl_func *)ioctl_init },
+	{ vidioc_int_enum_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
+	{ vidioc_int_try_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
+	{ vidioc_int_g_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
+	{ vidioc_int_s_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
+	{ vidioc_int_g_parm_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_parm },
+	{ vidioc_int_s_parm_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_parm },
+	{ vidioc_int_queryctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_queryctrl },
+	{ vidioc_int_g_ctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_ctrl },
+	{ vidioc_int_s_ctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_ctrl },
+};
+
+static struct v4l2_int_slave ov9640_slave = {
+	.ioctls		= ov9640_ioctl_desc,
+	.num_ioctls	= ARRAY_SIZE(ov9640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov9640_int_device = {
+	.module	= THIS_MODULE,
+	.name	= DRIVER_NAME,
+	.priv	= &ov9640,
+	.type	= v4l2_int_type_slave,
+	.u	= {
+		.slave = &ov9640_slave,
+	},
+};
+
+static int
+ov9640_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct ov9640_sensor *sensor = &ov9640;
+	int err;
+
+	if (i2c_get_clientdata(client))
+		return -EBUSY;
+
+	sensor->pdata = client->dev.platform_data;
+
+	if (!sensor->pdata) {
+		dev_err(&client->dev, "no platform data?\n");
+		return -ENODEV;
+	}
+
+	sensor->v4l2_int_device = &ov9640_int_device;
+	sensor->i2c_client = client;
+
+	i2c_set_clientdata(client, sensor);
+
+	/* Make the default capture format QCIF RGB565 */
+	sensor->pix.width = ov9640_sizes[QCIF].width;
+	sensor->pix.height = ov9640_sizes[QCIF].height;
+	sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+
+	err = v4l2_int_device_register(sensor->v4l2_int_device);
+	if (err)
+		i2c_set_clientdata(client, NULL);
+
+	return 0;
+}
+
+static int __exit
+ov9640_remove(struct i2c_client *client)
+{
+	struct ov9640_sensor *sensor = i2c_get_clientdata(client);
+
+	if (!client->adapter)
+		return -ENODEV;	/* our client isn't attached */
+
+	v4l2_int_device_unregister(sensor->v4l2_int_device);
+	i2c_set_clientdata(client, NULL);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov9640_id[] = {
+	{ "ov9640", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov9640_id);
+
+static struct i2c_driver ov9640sensor_i2c_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe	= ov9640_probe,
+	.remove	= __exit_p(ov9640_remove),
+};
+
+static struct ov9640_sensor ov9640 = {
+	.timeperframe = {
+		.numerator = 1,
+		.denominator = 15,
+	},
+};
+
+static int __init ov9640sensor_init(void)
+{
+	int err;
+
+	err = i2c_add_driver(&ov9640sensor_i2c_driver);
+	if (err) {
+		printk(KERN_ERR "Failed to register" DRIVER_NAME ".\n");
+		return err;
+	}
+	return 0;
+}
+module_init(ov9640sensor_init);
+
+static void __exit ov9640sensor_cleanup(void)
+{
+	i2c_del_driver(&ov9640sensor_i2c_driver);
+}
+module_exit(ov9640sensor_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OV9640 camera sensor driver");
diff --git a/include/media/ov9640.h b/include/media/ov9640.h
new file mode 100644
index 0000000..bcdb927
--- /dev/null
+++ b/include/media/ov9640.h
@@ -0,0 +1,190 @@
+/*
+ * Register definitions for the OmniVision OV9640 CameraChip.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef OV9640_H
+#define OV9640_H
+
+#define OV9640_I2C_ADDR		0x30
+
+/* define register offsets for the OV9640 sensor chip */
+#define OV9640_GAIN		0x00
+#define OV9640_BLUE		0x01
+#define OV9640_RED		0x02
+#define OV9640_VREF		0x03
+#define OV9640_COM1		0x04
+#define OV9640_BAVE		0x05
+#define OV9640_GEAVE		0x06
+#define OV9640_RAVE		0x08
+#define OV9640_COM2		0x09
+#define OV9640_PID		0x0A
+#define OV9640_VER		0x0B
+#define OV9640_COM3		0x0C
+#define OV9640_COM4		0x0D
+#define OV9640_COM5		0x0E
+#define OV9640_COM6		0x0F
+#define OV9640_AECH		0x10
+#define OV9640_CLKRC		0x11
+#define OV9640_COM7		0x12
+#define OV9640_COM8		0x13
+#define OV9640_COM9		0x14
+#define OV9640_COM10		0x15
+#define OV9640_HSTRT		0x17
+#define OV9640_HSTOP		0x18
+#define OV9640_VSTRT		0x19
+#define OV9640_VSTOP		0x1A
+#define OV9640_PSHFT		0x1B
+#define OV9640_MIDH		0x1C
+#define OV9640_MIDL		0x1D
+#define OV9640_MVFP		0x1E
+#define OV9640_LAEC		0x1F
+#define OV9640_BOS		0x20
+#define OV9640_GBOS		0x21
+#define OV9640_GROS		0x22
+#define OV9640_ROS		0x23
+#define OV9640_AEW		0x24
+#define OV9640_AEB		0x25
+#define OV9640_VPT		0x26
+#define OV9640_BBIAS		0x27
+#define OV9640_GBBIAS		0x28
+#define OV9640_EXHCH		0x2A
+#define OV9640_EXHCL		0x2B
+#define OV9640_RBIAS		0x2C
+#define OV9640_ADVFL		0x2D
+#define OV9640_ADVFH		0x2E
+#define OV9640_YAVE		0x2F
+#define OV9640_HSYST		0x30
+#define OV9640_HSYEN		0x31
+#define OV9640_HREF		0x32
+#define OV9640_CHLF		0x33
+#define OV9640_ARBLM		0x34
+#define OV9640_ADC		0x37
+#define OV9640_ACOM		0x38
+#define OV9640_OFON		0x39
+#define OV9640_TSLB		0x3A
+#define OV9640_COM11		0x3B
+#define OV9640_COM12		0x3C
+#define OV9640_COM13		0x3D
+#define OV9640_COM14		0x3E
+#define OV9640_EDGE		0x3F
+#define OV9640_COM15		0x40
+#define OV9640_COM16		0x41
+#define OV9640_COM17		0x42
+#define OV9640_MTX1		0x4F
+#define OV9640_MTX2		0x50
+#define OV9640_MTX3		0x51
+#define OV9640_MTX4		0x52
+#define OV9640_MTX5		0x53
+#define OV9640_MTX6		0x54
+#define OV9640_MTX7		0x55
+#define OV9640_MTX8		0x56
+#define OV9640_MTX9		0x57
+#define OV9640_MTXS		0x58
+#define OV9640_LCC1		0x62
+#define OV9640_LCC2		0x63
+#define OV9640_LCC3		0x64
+#define OV9640_LCC4		0x65
+#define OV9640_LCC5		0x66
+#define OV9640_MANU		0x67
+#define OV9640_MANV		0x68
+#define OV9640_HV		0x69
+#define OV9640_MBD		0x6A
+#define OV9640_DBLV		0x6B
+#define OV9640_GSP1		0x6C
+#define OV9640_GSP2		0x6D
+#define OV9640_GSP3		0x6E
+#define OV9640_GSP4		0x6F
+#define OV9640_GSP5		0x70
+#define OV9640_GSP6		0x71
+#define OV9640_GSP7		0x72
+#define OV9640_GSP8		0x73
+#define OV9640_GSP9		0x74
+#define OV9640_GSP10		0x75
+#define OV9640_GSP11		0x76
+#define OV9640_GSP12		0x77
+#define OV9640_GSP13		0x78
+#define OV9640_GSP14		0x79
+#define OV9640_GSP15		0x7A
+#define OV9640_GSP16		0x7B
+#define OV9640_GST1		0x7C
+#define OV9640_GST2		0x7D
+#define OV9640_GST3		0x7E
+#define OV9640_GST4		0x7F
+#define OV9640_GST5		0x80
+#define OV9640_GST6		0x81
+#define OV9640_GST7		0x82
+#define OV9640_GST8		0x83
+#define OV9640_GST9		0x84
+#define OV9640_GST10		0x85
+#define OV9640_GST11		0x86
+#define OV9640_GST12		0x87
+#define OV9640_GST13		0x88
+#define OV9640_GST14		0x89
+#define OV9640_GST15		0x8A
+
+#define OV9640_NUM_REGS		(OV9640_GST15 + 1)
+
+#define OV9640_PID_MAGIC	0x96	/* high byte of product ID number */
+#define OV9640_VER_REV2		0x48	/* low byte of product ID number */
+#define OV9640_VER_REV3		0x49	/* low byte of product ID number */
+#define OV9640_MIDH_MAGIC	0x7F	/* high byte of mfg ID */
+#define OV9640_MIDL_MAGIC	0xA2	/* low byte of mfg ID */
+
+#define OV9640_REG_TERM 0xFF	/* terminating list entry for reg */
+#define OV9640_VAL_TERM 0xFF	/* terminating list entry for val */
+
+/*
+ * The nominal xclk input frequency of the OV9640 is 24MHz, maximum
+ * frequency is 48MHz, and minimum frequency is 10MHz.
+ */
+#define OV9640_XCLK_MIN 10000000
+#define OV9640_XCLK_MAX 48000000
+#define OV9640_XCLK_NOM 24000000
+
+/* define a structure for ov9640 register initialization values */
+struct ov9640_reg {
+	unsigned char reg;
+	unsigned char val;
+};
+
+enum image_size { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
+enum pixel_format { YUV, RGB565, RGB555 };
+
+#define NUM_IMAGE_SIZES 7
+#define NUM_PIXEL_FORMATS 3
+
+struct capture_size {
+	unsigned long width;
+	unsigned long height;
+};
+
+struct ov9640_platform_data {
+	/* Set power state, zero is off, non-zero is on. */
+	int (*power_set)(int power);
+	int (*ifparm)(struct v4l2_ifparm *p);
+};
+
+/*
+ * Array of image sizes supported by OV9640.  These must be ordered from
+ * smallest image size to largest.
+ */
+const static struct capture_size ov9640_sizes[] = {
+	{   88,  72 },	/* QQCIF */
+	{  160, 120 },	/* QQVGA */
+	{  176, 144 },	/* QCIF */
+	{  320, 240 },	/* QVGA */
+	{  352, 288 },	/* CIF */
+	{  640, 480 },	/* VGA */
+	{ 1280, 960 },	/* SXGA */
+};
+
+#endif /* ifndef OV9640_H */
-- 
1.6.0.3


[-- Attachment #3: Type: text/plain, Size: 164 bytes --]

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
@ 2008-12-07  9:40       ` Trilok Soni
  0 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-12-07  9:40 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

[-- Attachment #1: Type: text/plain, Size: 682 bytes --]

Hi Hans,

On Mon, Dec 1, 2008 at 6:21 PM, Trilok Soni <soni.trilok@gmail.com> wrote:
> Hi Hans,
>
>>
>> I reviewed this sensor driver and it's fine except for one thing:
>> setting the default registers from outside the driver. This is a really
>> bad idea. I2C drivers should be self-contained. I've made the same
>> comment in the tvp514x driver review which I'm copying below (with some
>> small edits):
>
> I knew that you are going to comment on that, and I agree on those
> points. I will pull in that register initialization to the driver.
>

Attached the updated ov9640 sensor patch.

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-Omnivision-OV9640-sensor-support.patch --]
[-- Type: text/x-patch; name=0001-Add-Omnivision-OV9640-sensor-support.patch, Size: 46396 bytes --]

From 5c1b3e8dbc6c896eb60c8852e1f4dc3094d73ce1 Mon Sep 17 00:00:00 2001
From: Trilok Soni <soni.trilok@gmail.com>
Date: Fri, 28 Nov 2008 14:20:18 +0530
Subject: [PATCH] Add Omnivision OV9640 sensor support.

- Add driver for Omnivision OV9640 camera sensor. It has been
used with Texas Instruments OMAP1/2 based evaluation boards.

Signed-off-by: Trilok Soni <soni.trilok@gmail.com>
---
 drivers/media/video/Kconfig  |    7 +
 drivers/media/video/Makefile |    1 +
 drivers/media/video/ov9640.c | 1305 ++++++++++++++++++++++++++++++++++++++++++
 include/media/ov9640.h       |  190 ++++++
 4 files changed, 1503 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/ov9640.c
 create mode 100644 include/media/ov9640.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d980f23..b58f75b 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -305,6 +305,13 @@ config VIDEO_TCM825X
 	  This is a driver for the Toshiba TCM825x VGA camera sensor.
 	  It is used for example in Nokia N800.
 
+config VIDEO_OV9640
+	tristate "OmniVision OV9640 sensor support"
+	depends on I2C && VIDEO_V4L2
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV9640.
+
 config VIDEO_SAA7110
 	tristate "Philips SAA7110 video decoder"
 	depends on VIDEO_V4L1 && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 0797c3c..b2fe305 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
 obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
 obj-$(CONFIG_VIDEO_OMAP2)		+= omap2cam.o
+obj-$(CONFIG_VIDEO_OV9640)		+= ov9640.o
 obj-$(CONFIG_SOC_CAMERA)	+= soc_camera.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
 obj-$(CONFIG_SOC_CAMERA_MT9M111)	+= mt9m111.o
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
new file mode 100644
index 0000000..64a63f9
--- /dev/null
+++ b/drivers/media/video/ov9640.c
@@ -0,0 +1,1305 @@
+/*
+ * drivers/media/video/ov9640.c
+ *
+ * OV9640 sensor driver
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ * Contact: Trilok Soni <soni.trilok@gmail.com>
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <media/v4l2-int-device.h>
+
+#include <media/ov9640.h>
+
+#define DRIVER_NAME  "ov9640"
+
+struct ov9640_sensor {
+	const struct ov9640_platform_data *pdata;
+	struct v4l2_int_device *v4l2_int_device;
+	struct i2c_client *i2c_client;
+	struct v4l2_pix_format pix;
+	struct v4l2_fract timeperframe;
+	int ver;				/*ov9640 chip version*/
+};
+
+static struct ov9640_sensor ov9640;
+static struct i2c_driver ov9640sensor_i2c_driver;
+
+/*
+ * Common OV9640 register initialization for all image sizes, pixel formats,
+ * and frame rates
+ */
+const static struct ov9640_reg ov9640_common[] = {
+	{ 0x12, 0x80 }, { 0x11, 0x80 }, { 0x13, 0x8F },	/* COM7, CLKRC, COM8 */
+	{ 0x01, 0x80 }, { 0x02, 0x80 }, { 0x04, 0x00 },	/* BLUE, RED, COM1 */
+	{ 0x0E, 0x81 }, { 0x0F, 0x4F }, { 0x14, 0x4A },	/* COM5, COM6, COM9 */
+	{ 0x16, 0x02 }, { 0x1B, 0x01 }, { 0x24, 0x70 },	/* ?, PSHFT, AEW */
+	{ 0x25, 0x68 }, { 0x26, 0xD3 }, { 0x27, 0x90 },	/* AEB, VPT, BBIAS */
+	{ 0x2A, 0x00 }, { 0x2B, 0x00 }, { 0x32, 0x24 },	/* EXHCH, EXHCL, HREF */
+	{ 0x33, 0x02 }, { 0x37, 0x02 }, { 0x38, 0x13 },	/* CHLF, ADC, ACOM */
+	{ 0x39, 0xF0 }, { 0x3A, 0x00 }, { 0x3B, 0x01 },	/* OFON, TSLB, COM11 */
+	{ 0x3D, 0x90 }, { 0x3E, 0x02 }, { 0x3F, 0xF2 },	/* COM13, COM14, EDGE */
+	{ 0x41, 0x02 }, { 0x42, 0xC8 },		/* COM16, COM17 */
+	{ 0x43, 0xF0 }, { 0x44, 0x10 }, { 0x45, 0x6C },	/* ?, ?, ? */
+	{ 0x46, 0x6C }, { 0x47, 0x44 }, { 0x48, 0x44 },	/* ?, ?, ? */
+	{ 0x49, 0x03 }, { 0x59, 0x49 }, { 0x5A, 0x94 },	/* ?, ?, ? */
+	{ 0x5B, 0x46 }, { 0x5C, 0x84 }, { 0x5D, 0x5C },	/* ?, ?, ? */
+	{ 0x5E, 0x08 }, { 0x5F, 0x00 }, { 0x60, 0x14 },	/* ?, ?, ? */
+	{ 0x61, 0xCE },					/* ? */
+	{ 0x62, 0x70 }, { 0x63, 0x00 }, { 0x64, 0x04 },	/* LCC1, LCC2, LCC3 */
+	{ 0x65, 0x00 }, { 0x66, 0x00 },			/* LCC4, LCC5 */
+	{ 0x69, 0x00 }, { 0x6A, 0x3E }, { 0x6B, 0x3F },	/* HV, MBD, DBLV */
+	{ 0x6C, 0x40 }, { 0x6D, 0x30 }, { 0x6E, 0x4B },	/* GSP1, GSP2, GSP3 */
+	{ 0x6F, 0x60 }, { 0x70, 0x70 }, { 0x71, 0x70 },	/* GSP4, GSP5, GSP6 */
+	{ 0x72, 0x70 }, { 0x73, 0x70 }, { 0x74, 0x60 },	/* GSP7, GSP8, GSP9 */
+	{ 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 },	/* GSP10,GSP11,GSP12 */
+	{ 0x78, 0x3A }, { 0x79, 0x2E }, { 0x7A, 0x28 },	/* GSP13,GSP14,GSP15 */
+	{ 0x7B, 0x22 }, { 0x7C, 0x04 }, { 0x7D, 0x07 },	/* GSP16,GST1, GST2 */
+	{ 0x7E, 0x10 }, { 0x7F, 0x28 }, { 0x80, 0x36 },	/* GST3, GST4, GST5 */
+	{ 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 },	/* GST6, GST7, GST8 */
+	{ 0x84, 0x6C }, { 0x85, 0x78 }, { 0x86, 0x8C },	/* GST9, GST10,GST11 */
+	{ 0x87, 0x9E }, { 0x88, 0xBB }, { 0x89, 0xD2 },	/* GST12,GST13,GST14 */
+	{ 0x8A, 0xE6 }, { 0x13, 0x8F }, { 0x00, 0x7F },	/* GST15, COM8 */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+
+/* list of image formats supported by OV9640 sensor */
+const static struct v4l2_fmtdesc ov9640_formats[] = {
+	{
+		/* Note:  V4L2 defines RGB565 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 r4 r3 r2 r1 r0	  b4 b3 b2 b1 b0 g5 g4 g3
+		 *
+		 * We interpret RGB565 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 b4 b3 b2 b1 b0	  r4 r3 r2 r1 r0 g5 g4 g3
+		 */
+		.description	= "RGB565, le",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+	}, {
+		/* Note:  V4L2 defines RGB565X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	b4 b3 b2 b1 b0 g5 g4 g3	  g2 g1 g0 r4 r3 r2 r1 r0
+		 *
+		 * We interpret RGB565X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	r4 r3 r2 r1 r0 g5 g4 g3	  g2 g1 g0 b4 b3 b2 b1 b0
+		 */
+		.description	= "RGB565, be",
+		.pixelformat	= V4L2_PIX_FMT_RGB565X,
+	}, {
+		.description	= "YUYV (YUV 4:2:2), packed",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+	}, {
+		.description	= "UYVY, packed",
+		.pixelformat	= V4L2_PIX_FMT_UYVY,
+	}, {
+		/* Note:  V4L2 defines RGB555 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 r4 r3 r2 r1 r0	  x  b4 b3 b2 b1 b0 g4 g3
+		 *
+		 * We interpret RGB555 as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	g2 g1 g0 b4 b3 b2 b1 b0	  x  r4 r3 r2 r1 r0 g4 g3
+		 */
+		.description	= "RGB555, le",
+		.pixelformat	= V4L2_PIX_FMT_RGB555,
+	}, {
+		/* Note:  V4L2 defines RGB555X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	x  b4 b3 b2 b1 b0 g4 g3	  g2 g1 g0 r4 r3 r2 r1 r0
+		 *
+		 * We interpret RGB555X as:
+		 *
+		 *	Byte 0			  Byte 1
+		 *	x  r4 r3 r2 r1 r0 g4 g3	  g2 g1 g0 b4 b3 b2 b1 b0
+		 */
+		.description	= "RGB555, be",
+		.pixelformat	= V4L2_PIX_FMT_RGB555X,
+	},
+};
+
+#define NUM_CAPTURE_FORMATS ARRAY_SIZE(ov9640_formats)
+
+/*
+ * OV9640 register configuration for all combinations of pixel format and
+ * image size
+ */
+	/* YUV (YCbCr) QQCIF */
+const static struct ov9640_reg qqcif_yuv[] = {
+	{ 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QQVGA */
+const static struct ov9640_reg qqvga_yuv[] = {
+	{ 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QCIF */
+const static struct ov9640_reg qcif_yuv[] = {
+	{ 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) QVGA */
+const static struct ov9640_reg qvga_yuv[] = {
+	{ 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) CIF */
+const static struct ov9640_reg cif_yuv[] = {
+	{ 0x12, 0x20 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) VGA */
+const static struct ov9640_reg vga_yuv[] = {
+	{ 0x12, 0x40 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* YUV (YCbCr) SXGA */
+const static struct ov9640_reg sxga_yuv[] = {
+	{ 0x12, 0x00 }, { 0x3C, 0x46 }, { 0x40, 0xC0 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x0F },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QQCIF */
+const static struct ov9640_reg qqcif_565[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QQVGA */
+const static struct ov9640_reg qqvga_565[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QCIF */
+const static struct ov9640_reg qcif_565[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 QVGA */
+const static struct ov9640_reg qvga_565[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 CIF */
+const static struct ov9640_reg cif_565[] = {
+	{ 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 VGA */
+const static struct ov9640_reg vga_565[] = {
+	{ 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB565 SXGA */
+const static struct ov9640_reg sxga_565[] = {
+	{ 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x10 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QQCIF */
+const static struct ov9640_reg qqcif_555[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QQVGA */
+const static struct ov9640_reg qqvga_555[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QCIF */
+const static struct ov9640_reg qcif_555[] = {
+	{ 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 QVGA */
+const static struct ov9640_reg qvga_555[] = {
+	{ 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 CIF */
+const static struct ov9640_reg cif_555[] = {
+	{ 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 VGA */
+const static struct ov9640_reg vga_555[] = {
+	{ 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+	/* RGB555 SXGA */
+const static struct ov9640_reg sxga_555[] = {
+	{ 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x30 },	/* COM7, COM12, COM15 */
+	{ 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 },	/* COM1, COM3, COM4 */
+	{ 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C },	/* MTX1, MTX2, MTX3 */
+	{ 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 },	/* MTX4, MTX5, MTX6 */
+	{ 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 },	/* MTX7, MTX8, MTX9 */
+	{ 0x58, 0x65 },					/* MTXS */
+	{ OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+
+
+#define DEF_GAIN         31
+#define DEF_AUTOGAIN      1
+#define DEF_EXPOSURE    154
+#define DEF_AEC           1
+#define DEF_FREEZE_AGCAEC 0
+#define DEF_BLUE        153
+#define DEF_RED         (255 - DEF_BLUE)
+#define DEF_AWB           1
+#define DEF_HFLIP         0
+#define DEF_VFLIP         0
+
+/* Our own specific controls */
+#define V4L2_CID_FREEZE_AGCAEC		V4L2_CID_PRIVATE_BASE
+#define V4L2_CID_AUTOEXPOSURE		(V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_LAST_PRIV		V4L2_CID_AUTOEXPOSURE
+
+/*  Video controls  */
+static struct vcontrol {
+	struct v4l2_queryctrl qc;
+	int current_value;
+	u8 reg;
+	u8 mask;
+	u8 start_bit;
+} video_control[] = {
+	{
+		{
+			.id = V4L2_CID_GAIN,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Gain",
+			.minimum = 0,
+			.maximum = 63,
+			.step = 1,
+			.default_value = DEF_GAIN,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_GAIN,
+		.mask		= 0x3f,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTOGAIN,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto Gain",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AUTOGAIN,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x04,
+		.start_bit	= 2,
+	}, {
+		{
+			.id = V4L2_CID_EXPOSURE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Exposure",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_EXPOSURE,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_AECH,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTOEXPOSURE,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto Exposure",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AEC,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x01,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_FREEZE_AGCAEC,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Freeze AGC/AEC",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_FREEZE_AGCAEC,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM9,
+		.mask		= 0x01,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_RED_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Red Balance",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_RED,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_RED,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_BLUE_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Blue Balance",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = DEF_BLUE,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_BLUE,
+		.mask		= 0xff,
+		.start_bit	= 0,
+	}, {
+		{
+			.id = V4L2_CID_AUTO_WHITE_BALANCE,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Auto White Balance",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_AWB,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_COM8,
+		.mask		= 0x02,
+		.start_bit	= 1,
+	}, {
+		{
+			.id = V4L2_CID_HFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Mirror Image",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_HFLIP,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_MVFP,
+		.mask		= 0x20,
+		.start_bit	= 5,
+	}, {
+		{
+			.id = V4L2_CID_VFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Vertical Flip",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 0,
+			.default_value = DEF_VFLIP,
+		},
+		.current_value	= 0,
+		.reg		= OV9640_MVFP,
+		.mask		= 0x10,
+		.start_bit	= 4,
+	},
+};
+
+const static struct ov9640_reg *
+	ov9640_reg_init[NUM_PIXEL_FORMATS][NUM_IMAGE_SIZES] =
+{
+ { qqcif_yuv, qqvga_yuv, qcif_yuv, qvga_yuv, cif_yuv, vga_yuv, sxga_yuv },
+ { qqcif_565, qqvga_565, qcif_565, qvga_565, cif_565, vga_565, sxga_565 },
+ { qqcif_555, qqvga_555, qcif_555, qvga_555, cif_555, vga_555, sxga_555 },
+};
+
+
+/*
+ * Read a value from a register in an OV9640 sensor device.
+ * The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_read_reg(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int err;
+	struct i2c_msg msg[1];
+	unsigned char data[1];
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 1;
+	msg->buf = data;
+	*data = reg;
+	err = i2c_transfer(client->adapter, msg, 1);
+	if (err >= 0) {
+		msg->flags = I2C_M_RD;
+		err = i2c_transfer(client->adapter, msg, 1);
+	}
+	if (err >= 0) {
+		*val = *data;
+		return 0;
+	}
+	return err;
+}
+
+/*
+ * Write a value to a register in an OV9640 sensor device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int err;
+	struct i2c_msg msg[1];
+	unsigned char data[2];
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 2;
+	msg->buf = data;
+	data[0] = reg;
+	data[1] = val;
+	err = i2c_transfer(client->adapter, msg, 1);
+	if (err >= 0)
+		return 0;
+	return err;
+}
+
+static int
+ov9640_write_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+	u8 oldval, newval;
+	int rc;
+
+	if (mask == 0xff)
+		newval = *val;
+	else {
+		/* need to do read - modify - write */
+		rc = ov9640_read_reg(client, reg, &oldval);
+		if (rc)
+			return rc;
+		oldval &= (~mask);              /* Clear the masked bits */
+		*val &= mask;                  /* Enforce mask on value */
+		newval = oldval | *val;        /* Set the desired bits */
+	}
+
+	/* write the new value to the register */
+	rc = ov9640_write_reg(client, reg, newval);
+	if (rc)
+		return rc;
+
+	rc = ov9640_read_reg(client, reg, &newval);
+	if (rc)
+		return rc;
+
+	*val = newval & mask;
+	return 0;
+}
+
+static int
+ov9640_read_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+	int rc;
+
+	rc = ov9640_read_reg(client, reg, val);
+	if (rc)
+		return rc;
+	(*val) &= mask;
+
+	return 0;
+}
+
+/*
+ * Initialize a list of OV9640 registers.
+ * The list of registers is terminated by the pair of values
+ * { OV9640_REG_TERM, OV9640_VAL_TERM }.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+ov9640_write_regs(struct i2c_client *client, const struct ov9640_reg reglist[])
+{
+	int err;
+	const struct ov9640_reg *next = reglist;
+
+	while (!((next->reg == OV9640_REG_TERM)
+		&& (next->val == OV9640_VAL_TERM))) {
+		err = ov9640_write_reg(client, next->reg, next->val);
+		udelay(100);
+		if (err)
+			return err;
+		next++;
+	}
+	return 0;
+}
+
+/* Returns the index of the requested ID from the control structure array */
+static int
+find_vctrl(int id)
+{
+	int i;
+
+	if (id < V4L2_CID_BASE)
+		return -EDOM;
+
+	for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--)
+		if (video_control[i].qc.id == id)
+			break;
+	if (i < 0)
+		i = -EINVAL;
+	return i;
+}
+
+/*
+ * Calculate the internal clock divisor (value of the CLKRC register) of the
+ * OV9640 given the image size, the frequency (in Hz) of its XCLK input and a
+ * desired frame period (in seconds).  The frame period 'fper' is expressed as
+ * a fraction.  The frame period is an input/output parameter.
+ * Returns the value of the OV9640 CLKRC register that will yield the frame
+ * period returned in 'fper' at the specified xclk frequency.  The
+ * returned period will be as close to the requested period as possible.
+ */
+static unsigned char
+ov9640_clkrc(enum image_size isize, unsigned long xclk, struct v4l2_fract *fper)
+{
+	unsigned long fpm, fpm_max;	/* frames per minute */
+	unsigned long divisor;
+	const unsigned long divisor_max = 64;
+	/* FIXME
+	 * clks_per_frame should come from platform data
+	 */
+	const static unsigned long clks_per_frame[] =
+		{ 200000, 400000, 200000, 400000, 400000, 800000, 3200000 };
+      /*         QQCIF   QQVGA    QCIF    QVGA  CIF     VGA	SXGA
+       *         199680,400000, 199680, 400000, 399360, 800000, 3200000
+       */
+
+	if (fper->numerator > 0)
+		fpm = (fper->denominator*60)/fper->numerator;
+	else
+		fpm = 0xffffffff;
+	fpm_max = (xclk*60)/clks_per_frame[isize];
+	if (fpm_max == 0)
+		fpm_max = 1;
+	if (fpm > fpm_max)
+		fpm = fpm_max;
+	if (fpm == 0)
+		fpm = 1;
+	divisor = fpm_max/fpm;
+	if (divisor > divisor_max)
+		divisor = divisor_max;
+	fper->numerator = divisor*60;
+	fper->denominator = fpm_max;
+
+	/* try to reduce the fraction */
+	while (!(fper->denominator % 5) && !(fper->numerator % 5)) {
+		fper->numerator /= 5;
+		fper->denominator /= 5;
+	}
+	while (!(fper->denominator % 3) && !(fper->numerator % 3)) {
+		fper->numerator /= 3;
+		fper->denominator /= 3;
+	}
+	while (!(fper->denominator % 2) && !(fper->numerator % 2)) {
+		fper->numerator /= 2;
+		fper->denominator /= 2;
+	}
+	if (fper->numerator < fper->denominator) {
+		if (!(fper->denominator % fper->numerator)) {
+			fper->denominator /= fper->numerator;
+			fper->numerator = 1;
+		}
+	} else {
+		if (!(fper->numerator % fper->denominator)) {
+			fper->numerator /= fper->denominator;
+			fper->denominator = 1;
+		}
+	}
+
+	/* we set bit 7 in CLKRC to enable the digital PLL */
+	return 0x80 | (divisor - 1);
+}
+
+/*
+ * Find the best match for a requested image capture size.  The best match
+ * is chosen as the nearest match that has the same number or fewer pixels
+ * as the requested size, or the smallest image size if the requested size
+ * has fewer pixels than the smallest image.
+ */
+static enum image_size
+ov9640_find_size(unsigned int width, unsigned int height)
+{
+	enum image_size isize;
+	unsigned long pixels = width*height;
+
+	for (isize = QQCIF; isize < SXGA; isize++) {
+		if (ov9640_sizes[isize + 1].height *
+			ov9640_sizes[isize + 1].width > pixels)
+			return isize;
+	}
+	return SXGA;
+}
+
+/*
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency
+ */
+static unsigned long
+ov9640sensor_calc_xclk(struct i2c_client *c)
+{
+	struct ov9640_sensor *sensor = i2c_get_clientdata(c);
+	struct v4l2_fract *timeperframe = &sensor->timeperframe;
+	struct v4l2_pix_format *pix = &sensor->pix;
+
+	unsigned long tgt_xclk;			/* target xclk */
+	unsigned long tgt_fpm;			/* target frames per minute */
+	enum image_size isize;
+
+	/*
+	 * We use arbitrary rules to select the xclk frequency.  If the
+	 * capture size is VGA and the frame rate is greater than 900
+	 * frames per minute, or if the capture size is SXGA and the
+	 * frame rate is greater than 450 frames per minutes, then the
+	 * xclk frequency will be set to 48MHz.  Otherwise, the xclk
+	 * frequency will be set to 24MHz.  If the mclk frequency is such that
+	 * the target xclk frequency is not achievable, then xclk will be set
+	 * as close as to the target as possible.
+	 */
+	tgt_fpm = (timeperframe->denominator*60)
+		/ timeperframe->numerator;
+	tgt_xclk = OV9640_XCLK_NOM;
+	isize = ov9640_find_size(pix->width, pix->height);
+	switch (isize) {
+	case SXGA:
+		if (tgt_fpm > 450)
+			tgt_xclk = OV9640_XCLK_MAX;
+		break;
+	case VGA:
+		if (tgt_fpm > 900)
+			tgt_xclk = OV9640_XCLK_MAX;
+		break;
+	default:
+		break;
+	}
+	return tgt_xclk;
+}
+
+/*
+ * Configure the OV9640 for a specified image size, pixel format, and frame
+ * period.  xclk is the frequency (in Hz) of the xclk input to the OV9640.
+ * fper is the frame period (in seconds) expressed as a fraction.
+ * Returns zero if successful, or non-zero otherwise.
+ * The actual frame period is returned in fper.
+ */
+static int ov9640_configure(struct v4l2_int_device *s)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_pix_format *pix = &sensor->pix;
+	struct v4l2_fract *fper = &sensor->timeperframe;
+	struct i2c_client *client = sensor->i2c_client;
+	enum image_size isize;
+	unsigned long xclk;
+
+	int err;
+	unsigned char clkrc;
+	enum pixel_format pfmt = YUV;
+
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_RGB565X:
+		pfmt = RGB565;
+		break;
+	case V4L2_PIX_FMT_RGB555:
+	case V4L2_PIX_FMT_RGB555X:
+		pfmt = RGB555;
+		break;
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_UYVY:
+	default:
+		pfmt = YUV;
+	}
+
+	xclk = ov9640sensor_calc_xclk(client);
+
+	isize = ov9640_find_size(pix->width, pix->height);
+
+	/* common register initialization */
+	err = ov9640_write_regs(client, ov9640_common);
+	if (err)
+		return err;
+
+	/* configure image size and pixel format */
+	err = ov9640_write_regs(client, ov9640_reg_init[pfmt][isize]);
+	if (err)
+		return err;
+
+	/* configure frame rate */
+	clkrc = ov9640_clkrc(isize, xclk, fper);
+	err = ov9640_write_reg(client, OV9640_CLKRC, clkrc);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/*
+ * Detect if an OV9640 is present, and if so which revision.
+ * A device is considered to be detected if the manufacturer ID (MIDH and MIDL)
+ * and the product ID (PID) registers match the expected values.
+ * Any value of the version ID (VER) register is accepted.
+ * Here are the version numbers we know about:
+ *	0x48 --> OV9640 Revision 1 or OV9640 Revision 2
+ *	0x49 --> OV9640 Revision 3
+ * Returns a negative error number if no device is detected, or the
+ * non-negative value of the version ID register if a device is detected.
+ */
+static int
+ov9640_detect(struct i2c_client *client)
+{
+	u8 midh, midl, pid, ver;
+
+	if (!client)
+		return -ENODEV;
+
+	if (ov9640_read_reg(client, OV9640_MIDH, &midh))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_MIDL, &midl))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_PID, &pid))
+		return -ENODEV;
+	if (ov9640_read_reg(client, OV9640_VER, &ver))
+		return -ENODEV;
+
+	if ((midh != OV9640_MIDH_MAGIC)
+		|| (midl != OV9640_MIDL_MAGIC)
+		|| (pid != OV9640_PID_MAGIC))
+		/*
+		 * We didn't read the values we expected, so
+		 * this must not be an OV9640.
+		 */
+		return -ENODEV;
+
+	return ver;
+}
+
+/*
+ * following are sensor interface functions implemented by
+ * OV9640 sensor driver.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+				struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	i = find_vctrl(qc->id);
+	if (i == -EINVAL) {
+		qc->flags = V4L2_CTRL_FLAG_DISABLED;
+		return 0;
+	}
+	if (i < 0)
+		return -EINVAL;
+
+	*qc = video_control[i].qc;
+	return 0;
+}
+
+static int ioctl_g_ctrl(struct v4l2_int_device *s,
+			     struct v4l2_control *vc)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	int i, val;
+	struct vcontrol *lvc;
+
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &video_control[i];
+	if (ov9640_read_reg_mask(client, lvc->reg, (u8 *)&val, lvc->mask))
+		return -EIO;
+
+	val = val >> lvc->start_bit;
+	if (val >= 0) {
+		vc->value = lvc->current_value = val;
+		return 0;
+	} else
+		return val;
+}
+
+static int ioctl_s_ctrl(struct v4l2_int_device *s,
+			     struct v4l2_control *vc)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	struct vcontrol *lvc;
+	int val = vc->value;
+	int i;
+
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &video_control[i];
+	val = val << lvc->start_bit;
+	if (ov9640_write_reg_mask(client, lvc->reg, (u8 *)&val, (u8)lvc->mask))
+		return -EIO;
+
+	val = val >> lvc->start_bit;
+	if (val >= 0) {
+		lvc->current_value = val;
+		return 0;
+	} else
+		return val;
+}
+
+/*
+ * Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+				   struct v4l2_fmtdesc *fmt)
+{
+	int index = fmt->index;
+	enum v4l2_buf_type type = fmt->type;
+
+	memset(fmt, 0, sizeof(*fmt));
+	fmt->index = index;
+	fmt->type = type;
+
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (index >= NUM_CAPTURE_FORMATS)
+			return -EINVAL;
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	fmt->flags = ov9640_formats[index].flags;
+	strlcpy(fmt->description, ov9640_formats[index].description,
+					sizeof(fmt->description));
+	fmt->pixelformat = ov9640_formats[index].pixelformat;
+
+	return 0;
+}
+
+/*
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.  This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int ioctl_try_fmt_cap(struct v4l2_int_device *s,
+			     struct v4l2_format *f)
+{
+	enum image_size isize;
+	int ifmt;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+
+	isize = ov9640_find_size(pix->width, pix->height);
+	pix->width = ov9640_sizes[isize].width;
+	pix->height = ov9640_sizes[isize].height;
+	for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
+		if (pix->pixelformat == ov9640_formats[ifmt].pixelformat)
+			break;
+	}
+	if (ifmt == NUM_CAPTURE_FORMATS)
+		ifmt = 0;
+	pix->pixelformat = ov9640_formats[ifmt].pixelformat;
+	pix->field = V4L2_FIELD_NONE;
+	pix->bytesperline = pix->width*2;
+	pix->sizeimage = pix->bytesperline*pix->height;
+	pix->priv = 0;
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_UYVY:
+	default:
+		pix->colorspace = V4L2_COLORSPACE_JPEG;
+		break;
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_RGB565X:
+	case V4L2_PIX_FMT_RGB555:
+	case V4L2_PIX_FMT_RGB555X:
+		pix->colorspace = V4L2_COLORSPACE_SRGB;
+		break;
+	}
+	return 0;
+}
+
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s,
+				struct v4l2_format *f)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	int rval;
+
+	rval = ioctl_try_fmt_cap(s, f);
+	if (rval)
+		return rval;
+
+	rval = ov9640_configure(s);
+
+	if (!rval)
+		sensor->pix = *pix;
+
+	return rval;
+}
+
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s,
+				struct v4l2_format *f)
+{
+	struct ov9640_sensor *sensor = s->priv;
+
+	f->fmt.pix = sensor->pix;
+
+	return 0;
+}
+
+static int ioctl_g_parm(struct v4l2_int_device *s,
+			     struct v4l2_streamparm *a)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	cparm->capability = V4L2_CAP_TIMEPERFRAME;
+	cparm->timeperframe = sensor->timeperframe;
+
+	return 0;
+}
+
+static int ioctl_s_parm(struct v4l2_int_device *s,
+			     struct v4l2_streamparm *a)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+	struct v4l2_fract timeperframe_old;
+	int rval;
+
+	timeperframe_old = sensor->timeperframe;
+	sensor->timeperframe = *timeperframe;
+
+	rval = ov9640_configure(s);
+
+	if (rval)
+		sensor->timeperframe = timeperframe_old;
+	else
+		*timeperframe = sensor->timeperframe;
+
+	return rval;
+}
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *client = sensor->i2c_client;
+	u32 xclk;	/* target xclk */
+	int rval;
+
+	rval = sensor->pdata->ifparm(p);
+	if (rval)
+		return rval;
+
+	xclk = ov9640sensor_calc_xclk(client);
+
+	p->u.bt656.clock_curr = xclk;
+
+	return 0;
+}
+
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+	struct ov9640_sensor *sensor = s->priv;
+
+	return sensor->pdata->power_set(on);
+}
+
+static int ioctl_init(struct v4l2_int_device *s)
+{
+	return ov9640_configure(s);
+}
+
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+	return 0;
+}
+
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+	struct ov9640_sensor *sensor = s->priv;
+	struct i2c_client *c = sensor->i2c_client;
+	int err;
+
+	err = ov9640_detect(c);
+	if (err < 0) {
+		dev_err(&c->dev, "Unable to detect " DRIVER_NAME " sensor\n");
+		return err;
+	}
+
+	sensor->ver = err;
+	pr_info(DRIVER_NAME " chip version 0x%02x detected\n", sensor->ver);
+
+	return 0;
+}
+
+static struct v4l2_int_ioctl_desc ov9640_ioctl_desc[] = {
+	{ vidioc_int_dev_init_num,
+	  (v4l2_int_ioctl_func *)ioctl_dev_init },
+	{ vidioc_int_dev_exit_num,
+	  (v4l2_int_ioctl_func *)ioctl_dev_exit },
+	{ vidioc_int_s_power_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_power },
+	{ vidioc_int_g_ifparm_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_ifparm },
+	{ vidioc_int_init_num,
+	  (v4l2_int_ioctl_func *)ioctl_init },
+	{ vidioc_int_enum_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
+	{ vidioc_int_try_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
+	{ vidioc_int_g_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
+	{ vidioc_int_s_fmt_cap_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
+	{ vidioc_int_g_parm_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_parm },
+	{ vidioc_int_s_parm_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_parm },
+	{ vidioc_int_queryctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_queryctrl },
+	{ vidioc_int_g_ctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_g_ctrl },
+	{ vidioc_int_s_ctrl_num,
+	  (v4l2_int_ioctl_func *)ioctl_s_ctrl },
+};
+
+static struct v4l2_int_slave ov9640_slave = {
+	.ioctls		= ov9640_ioctl_desc,
+	.num_ioctls	= ARRAY_SIZE(ov9640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov9640_int_device = {
+	.module	= THIS_MODULE,
+	.name	= DRIVER_NAME,
+	.priv	= &ov9640,
+	.type	= v4l2_int_type_slave,
+	.u	= {
+		.slave = &ov9640_slave,
+	},
+};
+
+static int
+ov9640_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct ov9640_sensor *sensor = &ov9640;
+	int err;
+
+	if (i2c_get_clientdata(client))
+		return -EBUSY;
+
+	sensor->pdata = client->dev.platform_data;
+
+	if (!sensor->pdata) {
+		dev_err(&client->dev, "no platform data?\n");
+		return -ENODEV;
+	}
+
+	sensor->v4l2_int_device = &ov9640_int_device;
+	sensor->i2c_client = client;
+
+	i2c_set_clientdata(client, sensor);
+
+	/* Make the default capture format QCIF RGB565 */
+	sensor->pix.width = ov9640_sizes[QCIF].width;
+	sensor->pix.height = ov9640_sizes[QCIF].height;
+	sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+
+	err = v4l2_int_device_register(sensor->v4l2_int_device);
+	if (err)
+		i2c_set_clientdata(client, NULL);
+
+	return 0;
+}
+
+static int __exit
+ov9640_remove(struct i2c_client *client)
+{
+	struct ov9640_sensor *sensor = i2c_get_clientdata(client);
+
+	if (!client->adapter)
+		return -ENODEV;	/* our client isn't attached */
+
+	v4l2_int_device_unregister(sensor->v4l2_int_device);
+	i2c_set_clientdata(client, NULL);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov9640_id[] = {
+	{ "ov9640", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov9640_id);
+
+static struct i2c_driver ov9640sensor_i2c_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe	= ov9640_probe,
+	.remove	= __exit_p(ov9640_remove),
+};
+
+static struct ov9640_sensor ov9640 = {
+	.timeperframe = {
+		.numerator = 1,
+		.denominator = 15,
+	},
+};
+
+static int __init ov9640sensor_init(void)
+{
+	int err;
+
+	err = i2c_add_driver(&ov9640sensor_i2c_driver);
+	if (err) {
+		printk(KERN_ERR "Failed to register" DRIVER_NAME ".\n");
+		return err;
+	}
+	return 0;
+}
+module_init(ov9640sensor_init);
+
+static void __exit ov9640sensor_cleanup(void)
+{
+	i2c_del_driver(&ov9640sensor_i2c_driver);
+}
+module_exit(ov9640sensor_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OV9640 camera sensor driver");
diff --git a/include/media/ov9640.h b/include/media/ov9640.h
new file mode 100644
index 0000000..bcdb927
--- /dev/null
+++ b/include/media/ov9640.h
@@ -0,0 +1,190 @@
+/*
+ * Register definitions for the OmniVision OV9640 CameraChip.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef OV9640_H
+#define OV9640_H
+
+#define OV9640_I2C_ADDR		0x30
+
+/* define register offsets for the OV9640 sensor chip */
+#define OV9640_GAIN		0x00
+#define OV9640_BLUE		0x01
+#define OV9640_RED		0x02
+#define OV9640_VREF		0x03
+#define OV9640_COM1		0x04
+#define OV9640_BAVE		0x05
+#define OV9640_GEAVE		0x06
+#define OV9640_RAVE		0x08
+#define OV9640_COM2		0x09
+#define OV9640_PID		0x0A
+#define OV9640_VER		0x0B
+#define OV9640_COM3		0x0C
+#define OV9640_COM4		0x0D
+#define OV9640_COM5		0x0E
+#define OV9640_COM6		0x0F
+#define OV9640_AECH		0x10
+#define OV9640_CLKRC		0x11
+#define OV9640_COM7		0x12
+#define OV9640_COM8		0x13
+#define OV9640_COM9		0x14
+#define OV9640_COM10		0x15
+#define OV9640_HSTRT		0x17
+#define OV9640_HSTOP		0x18
+#define OV9640_VSTRT		0x19
+#define OV9640_VSTOP		0x1A
+#define OV9640_PSHFT		0x1B
+#define OV9640_MIDH		0x1C
+#define OV9640_MIDL		0x1D
+#define OV9640_MVFP		0x1E
+#define OV9640_LAEC		0x1F
+#define OV9640_BOS		0x20
+#define OV9640_GBOS		0x21
+#define OV9640_GROS		0x22
+#define OV9640_ROS		0x23
+#define OV9640_AEW		0x24
+#define OV9640_AEB		0x25
+#define OV9640_VPT		0x26
+#define OV9640_BBIAS		0x27
+#define OV9640_GBBIAS		0x28
+#define OV9640_EXHCH		0x2A
+#define OV9640_EXHCL		0x2B
+#define OV9640_RBIAS		0x2C
+#define OV9640_ADVFL		0x2D
+#define OV9640_ADVFH		0x2E
+#define OV9640_YAVE		0x2F
+#define OV9640_HSYST		0x30
+#define OV9640_HSYEN		0x31
+#define OV9640_HREF		0x32
+#define OV9640_CHLF		0x33
+#define OV9640_ARBLM		0x34
+#define OV9640_ADC		0x37
+#define OV9640_ACOM		0x38
+#define OV9640_OFON		0x39
+#define OV9640_TSLB		0x3A
+#define OV9640_COM11		0x3B
+#define OV9640_COM12		0x3C
+#define OV9640_COM13		0x3D
+#define OV9640_COM14		0x3E
+#define OV9640_EDGE		0x3F
+#define OV9640_COM15		0x40
+#define OV9640_COM16		0x41
+#define OV9640_COM17		0x42
+#define OV9640_MTX1		0x4F
+#define OV9640_MTX2		0x50
+#define OV9640_MTX3		0x51
+#define OV9640_MTX4		0x52
+#define OV9640_MTX5		0x53
+#define OV9640_MTX6		0x54
+#define OV9640_MTX7		0x55
+#define OV9640_MTX8		0x56
+#define OV9640_MTX9		0x57
+#define OV9640_MTXS		0x58
+#define OV9640_LCC1		0x62
+#define OV9640_LCC2		0x63
+#define OV9640_LCC3		0x64
+#define OV9640_LCC4		0x65
+#define OV9640_LCC5		0x66
+#define OV9640_MANU		0x67
+#define OV9640_MANV		0x68
+#define OV9640_HV		0x69
+#define OV9640_MBD		0x6A
+#define OV9640_DBLV		0x6B
+#define OV9640_GSP1		0x6C
+#define OV9640_GSP2		0x6D
+#define OV9640_GSP3		0x6E
+#define OV9640_GSP4		0x6F
+#define OV9640_GSP5		0x70
+#define OV9640_GSP6		0x71
+#define OV9640_GSP7		0x72
+#define OV9640_GSP8		0x73
+#define OV9640_GSP9		0x74
+#define OV9640_GSP10		0x75
+#define OV9640_GSP11		0x76
+#define OV9640_GSP12		0x77
+#define OV9640_GSP13		0x78
+#define OV9640_GSP14		0x79
+#define OV9640_GSP15		0x7A
+#define OV9640_GSP16		0x7B
+#define OV9640_GST1		0x7C
+#define OV9640_GST2		0x7D
+#define OV9640_GST3		0x7E
+#define OV9640_GST4		0x7F
+#define OV9640_GST5		0x80
+#define OV9640_GST6		0x81
+#define OV9640_GST7		0x82
+#define OV9640_GST8		0x83
+#define OV9640_GST9		0x84
+#define OV9640_GST10		0x85
+#define OV9640_GST11		0x86
+#define OV9640_GST12		0x87
+#define OV9640_GST13		0x88
+#define OV9640_GST14		0x89
+#define OV9640_GST15		0x8A
+
+#define OV9640_NUM_REGS		(OV9640_GST15 + 1)
+
+#define OV9640_PID_MAGIC	0x96	/* high byte of product ID number */
+#define OV9640_VER_REV2		0x48	/* low byte of product ID number */
+#define OV9640_VER_REV3		0x49	/* low byte of product ID number */
+#define OV9640_MIDH_MAGIC	0x7F	/* high byte of mfg ID */
+#define OV9640_MIDL_MAGIC	0xA2	/* low byte of mfg ID */
+
+#define OV9640_REG_TERM 0xFF	/* terminating list entry for reg */
+#define OV9640_VAL_TERM 0xFF	/* terminating list entry for val */
+
+/*
+ * The nominal xclk input frequency of the OV9640 is 24MHz, maximum
+ * frequency is 48MHz, and minimum frequency is 10MHz.
+ */
+#define OV9640_XCLK_MIN 10000000
+#define OV9640_XCLK_MAX 48000000
+#define OV9640_XCLK_NOM 24000000
+
+/* define a structure for ov9640 register initialization values */
+struct ov9640_reg {
+	unsigned char reg;
+	unsigned char val;
+};
+
+enum image_size { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
+enum pixel_format { YUV, RGB565, RGB555 };
+
+#define NUM_IMAGE_SIZES 7
+#define NUM_PIXEL_FORMATS 3
+
+struct capture_size {
+	unsigned long width;
+	unsigned long height;
+};
+
+struct ov9640_platform_data {
+	/* Set power state, zero is off, non-zero is on. */
+	int (*power_set)(int power);
+	int (*ifparm)(struct v4l2_ifparm *p);
+};
+
+/*
+ * Array of image sizes supported by OV9640.  These must be ordered from
+ * smallest image size to largest.
+ */
+const static struct capture_size ov9640_sizes[] = {
+	{   88,  72 },	/* QQCIF */
+	{  160, 120 },	/* QQVGA */
+	{  176, 144 },	/* QCIF */
+	{  320, 240 },	/* QVGA */
+	{  352, 288 },	/* CIF */
+	{  640, 480 },	/* VGA */
+	{ 1280, 960 },	/* SXGA */
+};
+
+#endif /* ifndef OV9640_H */
-- 
1.6.0.3


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
  2008-12-07  9:40       ` Trilok Soni
  (?)
@ 2008-12-07 10:39       ` Hans Verkuil
  2008-12-22  7:49           ` Trilok Soni
  -1 siblings, 1 reply; 17+ messages in thread
From: Hans Verkuil @ 2008-12-07 10:39 UTC (permalink / raw)
  To: Trilok Soni; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

Hi Trilok,

On Sunday 07 December 2008 10:40:51 Trilok Soni wrote:
> Hi Hans,
>
> On Mon, Dec 1, 2008 at 6:21 PM, Trilok Soni <soni.trilok@gmail.com> 
wrote:
> > Hi Hans,
> >
> >> I reviewed this sensor driver and it's fine except for one thing:
> >> setting the default registers from outside the driver. This is a
> >> really bad idea. I2C drivers should be self-contained. I've made
> >> the same comment in the tvp514x driver review which I'm copying
> >> below (with some small edits):
> >
> > I knew that you are going to comment on that, and I agree on those
> > points. I will pull in that register initialization to the driver.
>
> Attached the updated ov9640 sensor patch.

Thanks. Here is my review:

1) Don't use this: static struct ov9640_sensor ov9640;

This allows only one sensor instance. This should be dynamic. Remember 
that it should be possible (once the new v4l2_device/v4l2_subdev 
framework is merged) to reuse this driver in other products as well. So 
it should be possible to use two webcams with this sensor at the same 
time. It's only a small amount of work to make this struct dynamic. 
Ditto for the 'current_value' field of the static struct vcontrol: this 
too is per-instance.

2) Looking at all the YUV and RGB register settings I notice that they 
seem to fall into two parts: all MTX regs and some COM regs are 
identical for either RGB or YUV. It's a good idea to have only two 
arrays for these registers rather than duplicating them for each 
format. You might want to consider setting the remaining COM regs 
directly in a switch (fmt) statement. Try it and see what is more 
readable.

3) There already exists a standard autoexposure control: 
V4L2_CID_EXPOSURE_AUTO.

4) What does V4L2_CID_FREEZE_AGCAEC do? 

5) We have standardized the camera control names. A patch for this is 
still pending, but I recommend that in the meantime you use these 
names:

AUTOGAIN: "Gain, Automatic"
AUTO_WHITE_BALANCE: "White Balance, Automatic"
HFLIP: "Horizontal Flip"
VFLIP: "Vertical Flip"

AUTO_EXPOSURE will probably change to "Exposure, Automatic", but this is 
still under discussion.

6) include/media/ov9640.h: what part of this header needs to be visible 
to other parts of the kernel? A lot seems to be internal to the driver 
and so should be moved to the driver source (or a ov9640_regs.h headers 
next to the driver source).

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH] Add Omnivision OV9640 sensor support.
  2008-12-07  9:40       ` Trilok Soni
@ 2008-12-08  4:24         ` Hiremath, Vaibhav
  -1 siblings, 0 replies; 17+ messages in thread
From: Hiremath, Vaibhav @ 2008-12-08  4:24 UTC (permalink / raw)
  To: Trilok Soni, Hans Verkuil
  Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

Hi Soni,

Thanks,
Vaibhav Hiremath

> -----Original Message-----
> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
> owner@vger.kernel.org] On Behalf Of Trilok Soni
> Sent: Sunday, December 07, 2008 3:11 PM
> To: Hans Verkuil
> Cc: v4l; linux-omap@vger.kernel.org Mailing List; Sakari Ailus
> Subject: Re: [PATCH] Add Omnivision OV9640 sensor support.
> 
> Hi Hans,
> 
> On Mon, Dec 1, 2008 at 6:21 PM, Trilok Soni <soni.trilok@gmail.com>
> wrote:
> > Hi Hans,
> >
> >>
> >> I reviewed this sensor driver and it's fine except for one thing:
> >> setting the default registers from outside the driver. This is a
> really
> >> bad idea. I2C drivers should be self-contained. I've made the
> same
> >> comment in the tvp514x driver review which I'm copying below
> (with some
> >> small edits):
> >
> > I knew that you are going to comment on that, and I agree on those
> > points. I will pull in that register initialization to the driver.
> >
> 
> Attached the updated ov9640 sensor patch.
> 


[Hiremath, Vaibhav] I just had quick walk through of code and I think you may want to take look at the review comments received for TVP514x driver (Especially for I2C).

> --
> ---Trilok Soni
> http://triloksoni.wordpress.com
> http://www.linkedin.com/in/triloksoni

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH] Add Omnivision OV9640 sensor support.
@ 2008-12-08  4:24         ` Hiremath, Vaibhav
  0 siblings, 0 replies; 17+ messages in thread
From: Hiremath, Vaibhav @ 2008-12-08  4:24 UTC (permalink / raw)
  To: Trilok Soni, Hans Verkuil
  Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

Hi Soni,

Thanks,
Vaibhav Hiremath

> -----Original Message-----
> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
> owner@vger.kernel.org] On Behalf Of Trilok Soni
> Sent: Sunday, December 07, 2008 3:11 PM
> To: Hans Verkuil
> Cc: v4l; linux-omap@vger.kernel.org Mailing List; Sakari Ailus
> Subject: Re: [PATCH] Add Omnivision OV9640 sensor support.
> 
> Hi Hans,
> 
> On Mon, Dec 1, 2008 at 6:21 PM, Trilok Soni <soni.trilok@gmail.com>
> wrote:
> > Hi Hans,
> >
> >>
> >> I reviewed this sensor driver and it's fine except for one thing:
> >> setting the default registers from outside the driver. This is a
> really
> >> bad idea. I2C drivers should be self-contained. I've made the
> same
> >> comment in the tvp514x driver review which I'm copying below
> (with some
> >> small edits):
> >
> > I knew that you are going to comment on that, and I agree on those
> > points. I will pull in that register initialization to the driver.
> >
> 
> Attached the updated ov9640 sensor patch.
> 


[Hiremath, Vaibhav] I just had quick walk through of code and I think you may want to take look at the review comments received for TVP514x driver (Especially for I2C).

> --
> ---Trilok Soni
> http://triloksoni.wordpress.com
> http://www.linkedin.com/in/triloksoni

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
  2008-12-07 10:39       ` Hans Verkuil
@ 2008-12-22  7:49           ` Trilok Soni
  0 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-12-22  7:49 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

Hi Hans,

Sorry for the delay in reply.

>>
>> Attached the updated ov9640 sensor patch.
>
> Thanks. Here is my review:
>
> 1) Don't use this: static struct ov9640_sensor ov9640;
>
> This allows only one sensor instance. This should be dynamic. Remember
> that it should be possible (once the new v4l2_device/v4l2_subdev
> framework is merged) to reuse this driver in other products as well. So
> it should be possible to use two webcams with this sensor at the same
> time. It's only a small amount of work to make this struct dynamic.
> Ditto for the 'current_value' field of the static struct vcontrol: this
> too is per-instance.

Hopefully kzallocing in the probe ?

>
> 2) Looking at all the YUV and RGB register settings I notice that they
> seem to fall into two parts: all MTX regs and some COM regs are
> identical for either RGB or YUV. It's a good idea to have only two
> arrays for these registers rather than duplicating them for each
> format. You might want to consider setting the remaining COM regs
> directly in a switch (fmt) statement. Try it and see what is more
> readable.

I will try this.

>
> 3) There already exists a standard autoexposure control:
> V4L2_CID_EXPOSURE_AUTO.

Ok.

>
> 4) What does V4L2_CID_FREEZE_AGCAEC do?
>

I need to pull out some docs on this, but not looked at them from long time.

> 5) We have standardized the camera control names. A patch for this is
> still pending, but I recommend that in the meantime you use these
> names:
>
> AUTOGAIN: "Gain, Automatic"
> AUTO_WHITE_BALANCE: "White Balance, Automatic"
> HFLIP: "Horizontal Flip"
> VFLIP: "Vertical Flip"
>
> AUTO_EXPOSURE will probably change to "Exposure, Automatic", but this is
> still under discussion.

Ok.

>
> 6) include/media/ov9640.h: what part of this header needs to be visible
> to other parts of the kernel? A lot seems to be internal to the driver
> and so should be moved to the driver source (or a ov9640_regs.h headers
> next to the driver source).

I will bring regs inside the source file and remove ov9640.h from
include/media if there is no need of platform data dependency from
board-xxx.c files.

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
@ 2008-12-22  7:49           ` Trilok Soni
  0 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-12-22  7:49 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

Hi Hans,

Sorry for the delay in reply.

>>
>> Attached the updated ov9640 sensor patch.
>
> Thanks. Here is my review:
>
> 1) Don't use this: static struct ov9640_sensor ov9640;
>
> This allows only one sensor instance. This should be dynamic. Remember
> that it should be possible (once the new v4l2_device/v4l2_subdev
> framework is merged) to reuse this driver in other products as well. So
> it should be possible to use two webcams with this sensor at the same
> time. It's only a small amount of work to make this struct dynamic.
> Ditto for the 'current_value' field of the static struct vcontrol: this
> too is per-instance.

Hopefully kzallocing in the probe ?

>
> 2) Looking at all the YUV and RGB register settings I notice that they
> seem to fall into two parts: all MTX regs and some COM regs are
> identical for either RGB or YUV. It's a good idea to have only two
> arrays for these registers rather than duplicating them for each
> format. You might want to consider setting the remaining COM regs
> directly in a switch (fmt) statement. Try it and see what is more
> readable.

I will try this.

>
> 3) There already exists a standard autoexposure control:
> V4L2_CID_EXPOSURE_AUTO.

Ok.

>
> 4) What does V4L2_CID_FREEZE_AGCAEC do?
>

I need to pull out some docs on this, but not looked at them from long time.

> 5) We have standardized the camera control names. A patch for this is
> still pending, but I recommend that in the meantime you use these
> names:
>
> AUTOGAIN: "Gain, Automatic"
> AUTO_WHITE_BALANCE: "White Balance, Automatic"
> HFLIP: "Horizontal Flip"
> VFLIP: "Vertical Flip"
>
> AUTO_EXPOSURE will probably change to "Exposure, Automatic", but this is
> still under discussion.

Ok.

>
> 6) include/media/ov9640.h: what part of this header needs to be visible
> to other parts of the kernel? A lot seems to be internal to the driver
> and so should be moved to the driver source (or a ov9640_regs.h headers
> next to the driver source).

I will bring regs inside the source file and remove ov9640.h from
include/media if there is no need of platform data dependency from
board-xxx.c files.

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
  2008-12-22  7:49           ` Trilok Soni
@ 2008-12-22 12:07             ` Hans Verkuil
  -1 siblings, 0 replies; 17+ messages in thread
From: Hans Verkuil @ 2008-12-22 12:07 UTC (permalink / raw)
  To: Trilok Soni; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

On Monday 22 December 2008 08:49:34 Trilok Soni wrote:
> Hi Hans,
>
> Sorry for the delay in reply.
>
> >> Attached the updated ov9640 sensor patch.
> >
> > Thanks. Here is my review:
> >
> > 1) Don't use this: static struct ov9640_sensor ov9640;
> >
> > This allows only one sensor instance. This should be dynamic. Remember
> > that it should be possible (once the new v4l2_device/v4l2_subdev
> > framework is merged) to reuse this driver in other products as well. So
> > it should be possible to use two webcams with this sensor at the same
> > time. It's only a small amount of work to make this struct dynamic.
> > Ditto for the 'current_value' field of the static struct vcontrol: this
> > too is per-instance.
>
> Hopefully kzallocing in the probe ?

Yes. Typically all state information is pulled into one struct which is 
kzalloc'ed in the probe and freed at the end.

>
> > 2) Looking at all the YUV and RGB register settings I notice that they
> > seem to fall into two parts: all MTX regs and some COM regs are
> > identical for either RGB or YUV. It's a good idea to have only two
> > arrays for these registers rather than duplicating them for each
> > format. You might want to consider setting the remaining COM regs
> > directly in a switch (fmt) statement. Try it and see what is more
> > readable.
>
> I will try this.
>
> > 3) There already exists a standard autoexposure control:
> > V4L2_CID_EXPOSURE_AUTO.
>
> Ok.
>
> > 4) What does V4L2_CID_FREEZE_AGCAEC do?
>
> I need to pull out some docs on this, but not looked at them from long
> time.

I recommend that you read my posting "Re: Extended driver private controls?" 
on the linux-omap list as well (for some reason it was refused on the v4l 
list, weird).

> > 5) We have standardized the camera control names. A patch for this is
> > still pending, but I recommend that in the meantime you use these
> > names:
> >
> > AUTOGAIN: "Gain, Automatic"
> > AUTO_WHITE_BALANCE: "White Balance, Automatic"
> > HFLIP: "Horizontal Flip"
> > VFLIP: "Vertical Flip"
> >
> > AUTO_EXPOSURE will probably change to "Exposure, Automatic", but this
> > is still under discussion.
>
> Ok.

It's been decided that it will be called "Auto Exposure" instead.

>
> > 6) include/media/ov9640.h: what part of this header needs to be visible
> > to other parts of the kernel? A lot seems to be internal to the driver
> > and so should be moved to the driver source (or a ov9640_regs.h headers
> > next to the driver source).
>
> I will bring regs inside the source file and remove ov9640.h from
> include/media if there is no need of platform data dependency from
> board-xxx.c files.

Thanks,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
@ 2008-12-22 12:07             ` Hans Verkuil
  0 siblings, 0 replies; 17+ messages in thread
From: Hans Verkuil @ 2008-12-22 12:07 UTC (permalink / raw)
  To: Trilok Soni; +Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

On Monday 22 December 2008 08:49:34 Trilok Soni wrote:
> Hi Hans,
>
> Sorry for the delay in reply.
>
> >> Attached the updated ov9640 sensor patch.
> >
> > Thanks. Here is my review:
> >
> > 1) Don't use this: static struct ov9640_sensor ov9640;
> >
> > This allows only one sensor instance. This should be dynamic. Remember
> > that it should be possible (once the new v4l2_device/v4l2_subdev
> > framework is merged) to reuse this driver in other products as well. So
> > it should be possible to use two webcams with this sensor at the same
> > time. It's only a small amount of work to make this struct dynamic.
> > Ditto for the 'current_value' field of the static struct vcontrol: this
> > too is per-instance.
>
> Hopefully kzallocing in the probe ?

Yes. Typically all state information is pulled into one struct which is 
kzalloc'ed in the probe and freed at the end.

>
> > 2) Looking at all the YUV and RGB register settings I notice that they
> > seem to fall into two parts: all MTX regs and some COM regs are
> > identical for either RGB or YUV. It's a good idea to have only two
> > arrays for these registers rather than duplicating them for each
> > format. You might want to consider setting the remaining COM regs
> > directly in a switch (fmt) statement. Try it and see what is more
> > readable.
>
> I will try this.
>
> > 3) There already exists a standard autoexposure control:
> > V4L2_CID_EXPOSURE_AUTO.
>
> Ok.
>
> > 4) What does V4L2_CID_FREEZE_AGCAEC do?
>
> I need to pull out some docs on this, but not looked at them from long
> time.

I recommend that you read my posting "Re: Extended driver private controls?" 
on the linux-omap list as well (for some reason it was refused on the v4l 
list, weird).

> > 5) We have standardized the camera control names. A patch for this is
> > still pending, but I recommend that in the meantime you use these
> > names:
> >
> > AUTOGAIN: "Gain, Automatic"
> > AUTO_WHITE_BALANCE: "White Balance, Automatic"
> > HFLIP: "Horizontal Flip"
> > VFLIP: "Vertical Flip"
> >
> > AUTO_EXPOSURE will probably change to "Exposure, Automatic", but this
> > is still under discussion.
>
> Ok.

It's been decided that it will be called "Auto Exposure" instead.

>
> > 6) include/media/ov9640.h: what part of this header needs to be visible
> > to other parts of the kernel? A lot seems to be internal to the driver
> > and so should be moved to the driver source (or a ov9640_regs.h headers
> > next to the driver source).
>
> I will bring regs inside the source file and remove ov9640.h from
> include/media if there is no need of platform data dependency from
> board-xxx.c files.

Thanks,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
  2008-12-08  4:24         ` Hiremath, Vaibhav
@ 2008-12-23  8:58           ` Trilok Soni
  -1 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-12-23  8:58 UTC (permalink / raw)
  To: Hiremath, Vaibhav
  Cc: v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

Hi Vaibhav,

>
>
> [Hiremath, Vaibhav] I just had quick walk through of code and I think you may want to take look at the review comments received for TVP514x driver (Especially for I2C).
>

I will not go for smbus_read/write APIs as of now. I will look at rest
of the comments.

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Add Omnivision OV9640 sensor support.
@ 2008-12-23  8:58           ` Trilok Soni
  0 siblings, 0 replies; 17+ messages in thread
From: Trilok Soni @ 2008-12-23  8:58 UTC (permalink / raw)
  To: Hiremath, Vaibhav
  Cc: Hans Verkuil, v4l, linux-omap@vger.kernel.org Mailing List, Sakari Ailus

Hi Vaibhav,

>
>
> [Hiremath, Vaibhav] I just had quick walk through of code and I think you may want to take look at the review comments received for TVP514x driver (Especially for I2C).
>

I will not go for smbus_read/write APIs as of now. I will look at rest
of the comments.

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2008-12-23  9:00 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-11-28 10:16 [PATCH] Add Omnivision OV9640 sensor support Trilok Soni
2008-11-28 10:16 ` Trilok Soni
2008-12-01 12:34 ` Hans Verkuil
2008-12-01 12:34   ` Hans Verkuil
2008-12-01 12:51   ` Trilok Soni
2008-12-01 12:51     ` Trilok Soni
2008-12-07  9:40     ` Trilok Soni
2008-12-07  9:40       ` Trilok Soni
2008-12-07 10:39       ` Hans Verkuil
2008-12-22  7:49         ` Trilok Soni
2008-12-22  7:49           ` Trilok Soni
2008-12-22 12:07           ` Hans Verkuil
2008-12-22 12:07             ` Hans Verkuil
2008-12-08  4:24       ` Hiremath, Vaibhav
2008-12-08  4:24         ` Hiremath, Vaibhav
2008-12-23  8:58         ` Trilok Soni
2008-12-23  8:58           ` Trilok Soni

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.