Re: [PATCH] Add Omnivision OV9640 sensor support.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Hans,

On Mon, Dec 1, 2008 at 6:21 PM, Trilok Soni <soni.trilok@xxxxxxxxx> 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
From 5c1b3e8dbc6c896eb60c8852e1f4dc3094d73ce1 Mon Sep 17 00:00:00 2001
From: Trilok Soni <soni.trilok@xxxxxxxxx>
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@xxxxxxxxx>
---
 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@xxxxxxxxxx)
+ * Contact: Trilok Soni <soni.trilok@xxxxxxxxx>
+ *
+ * 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@xxxxxxxxxx)
+ *
+ * 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


[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux