Subject: Re: [E3-hacking] [PATCH] Camera Driver

From: Matt Callow <[email-masked]>
Date: Sat, 09 Sep 2006 18:25:59 +0100

Matt Callow wrote:
> Finally I've (hopefully) got the camera driver ready for release. I've
> been short of time recently, so I've not had much time to work on it.
> 
> The driver basically works, but there are a few known issues:
> 
> - colours are wrong for CIF images
> - clock calculation is broken (probably causes the wrong colours)
> - pixel formats are not configurable
> - lots of controls aren't implemented
> 
> that said, it does work (for me) and I thought people may be able to
> make use of what I've done so far.
> 
> It's a patch against 2.6.16-omap2. I've not had time to update to 2.6.17
> yet but I expect it will apply to 2.6.17 as well - let me know how you
> get on.
> 
> You'll need to enable 'Device Driver/Multimedia Devices/Video For Linux'
> Then 'OMAP Camera Support' and 'OV6650 sensor support'
> 
> I've also attached a test program which may be of use. Compile it, copy
> the binary to the e3 and then run:
> 
> v4ltest --size=1 display
> 
> to get the camera image displayed on the e3 screen.
> 
> Try:
> 
> v4ltest -h
> 
> for other options
> 
> Matt
> 
Original email is awaiting moderator approval (too big), so in the
meantime I've put the files here:

http://mcallow.pwp.blueyonder.co.uk/e3/09-ams-delta-camera.diff
http://mcallow.pwp.blueyonder.co.uk/e3/v4ltest.c

Matt

---

From: Matt Callow <[email-masked]>
Date: Sat, 09 Sep 2006 16:08:49 +0100

v4ltest --size=1 display

Try:

v4ltest -h

for other options

Matt

Index: linux-2.6/drivers/media/video/omap/sensor_ov6650.c
===================================================================
--- linux-2.6/drivers/media/video/omap/sensor_ov6650.c	(revision 0)
+++ linux-2.6/drivers/media/video/omap/sensor_ov6650.c	(revision 477)
@@ -0,0 +1,893 @@
+
+/*
+ * drivers/media/video/omap/sensor_ov6650.c
+ *
+ * Ov6650 Sensor driver for OMAP camera sensor interface
+ * Based on sensor_ov9640.c, by Andy Lowe
+ *
+ * Author: Matt Callow ([email-masked])
+ * Author: Andy Lowe ([email-masked])
+ *
+ * Copyright (c) 2006 Matt Callow
+ * 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/errno.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <media/video-buf.h>
+#include <linux/delay.h>
+#include <asm/mach-types.h>
+
+#include "sensor_if.h"
+#include "ov6650.h"
+
+#define CAMERA_OV6650
+#ifdef CAMERA_OV6650
+
+struct ov6650_sensor {
+	/* I2C parameters */
+	struct i2c_client client;
+};
+
+static struct ov6650_sensor ov6650;
+
+/* list of image formats supported by OV9640 sensor */
+const static struct v4l2_fmtdesc ov6650_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 (sizeof(ov6650_formats)/sizeof(ov6650_formats[0]))
+#define NUM_OVERLAY_FORMATS 2
+
+/* register initialization tables for OV9640 */
+
+#define OV6650_REG_TERM 0xFF	/* terminating list entry for reg */
+#define OV6650_VAL_TERM 0xFF	/* terminating list entry for val */
+
+/* Updated to new values for V2 */
+static struct ov6650_reg init_regtbl_6650[] = {
+	{ OV6650_COMA, 0x80}, // reset
+	{ OV6650_BLUE, 0xC0},
+	{ OV6650_RED, 0x98},
+	{ OV6650_SAT, 0x00},//0ld Value{ 0x03, 0x50} //Old Value{ 0x03, 0x48},
+	{ OV6650_HUE, 0x32}, //New Register
+	{ OV6650_BRT, 0x96}, //Old Value{ 0x06, 0x70},
+	{ OV6650_CLKRC, 0xC1},
+	{ OV6650_COMA, 0x21},
+	{ OV6650_COMB, 0x5F},
+	{ OV6650_COMC, 0xE3}, //Old Value{ 0x14, 0xc3},
+	{ OV6650_COMD, 0x42}, //Old Value{ 0x15, 0x00},
+	{ OV6650_COML, 0x82},
+	{ OV6650_AEW, 0x91}, //Old Value{ 0x24, 0x80},
+	{ OV6650_AEB, 0x81}, //Old Value{ 0x25, 0x70},
+	{ 0x26, 0x01},
+	{ 0x27, 0x5C},
+	{ 0x29, 0x80},
+	{ 0x22, 0x80},
+	{ 0x23, 0x80},
+	{ 0x28, 0x18},
+	{ 0x2C, 0x09},
+	{ 0x2D, 0x08},
+	{ 0x34, 0xE4},
+	{ 0x30, 0x40},
+	{ 0x32, 0x00},
+	{ 0x33, 0x00},
+	{ 0x60, 0xC0}, //Old Value{ 0x60, 0xC1},
+	{ 0x61, 0x60},
+	{ 0x62, 0x88},
+	{ 0x63, 0x92},
+	{ 0x68, 0x04},
+	{ 0x6C, 0x11},
+	{ 0x6D, 0x01},
+	{ 0x6E, 0x06},
+	{ OV6650_REG_TERM, OV6650_VAL_TERM }
+};
+
+
+ 
+#define DEF_GAIN          0
+#define DEF_AUTOGAIN      1
+#define DEF_EXPOSURE    0x4d
+#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+0
+#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;
+} control[] = {
+        { { V4L2_CID_GAIN, V4L2_CTRL_TYPE_INTEGER, "Gain", 0, 63, 1,
+            DEF_GAIN },
+          0, OV6650_GAIN, 0x3f, 0 },
+	/*
+        { { V4L2_CID_AUTOGAIN, V4L2_CTRL_TYPE_BOOLEAN, "Auto Gain", 0, 1, 0,
+            DEF_AUTOGAIN },
+          0, OV9640_COM8, 0x04, 2 },
+	  */
+        { { V4L2_CID_EXPOSURE, V4L2_CTRL_TYPE_INTEGER, "Exposure", 0, 255, 1,
+            DEF_EXPOSURE },
+          0, OV6650_AECH, 0xff, 0 },
+	/*
+        { { V4L2_CID_AUTOEXPOSURE, V4L2_CTRL_TYPE_BOOLEAN, "Auto Exposure", 0, 1, 0,
+            DEF_AEC },
+          0, OV9640_COM8, 0x01, 0 },
+        { { V4L2_CID_FREEZE_AGCAEC, V4L2_CTRL_TYPE_BOOLEAN, "Freeze AGC/AEC", 0,1,0,
+            DEF_FREEZE_AGCAEC },
+          0, OV9640_COM9, 0x01, 0 },
+        { { V4L2_CID_RED_BALANCE, V4L2_CTRL_TYPE_INTEGER, "Red Balance", 0, 255, 1,
+            DEF_RED },
+          0, OV9640_RED, 0xff, 0 },
+        { { V4L2_CID_BLUE_BALANCE, V4L2_CTRL_TYPE_INTEGER, "Blue Balance", 0, 255, 1,
+            DEF_BLUE },
+          0, OV9640_BLUE, 0xff, 0 },
+        { { V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CTRL_TYPE_BOOLEAN, "Auto White Balance", 0,1,0,
+            DEF_AWB },
+          0, OV9640_COM8, 0x02, 1 },
+	  */
+        { { V4L2_CID_HFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Mirror Image", 0, 1, 0,
+            DEF_HFLIP },
+          0, OV6650_COMB, 0x20, 5 },
+        { { V4L2_CID_VFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Vertical Flip", 0, 1, 0,
+            DEF_VFLIP },
+          0, OV6650_COMB, 0x80, 7 },
+};
+
+#define NUM_CONTROLS (sizeof(control)/sizeof(control[0]))
+
+
+/* 
+ * returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int 
+ov6650_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 OV6650 sensor device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int 
+ov6650_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 
+ov6650_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 */
+		if ((rc = ov6650_read_reg(client, reg, &oldval)))
+			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 */
+	if ((rc = ov6650_write_reg(client, reg, newval)))
+		return rc;
+
+	if ((rc = ov6650_read_reg(client, reg, &newval)))
+		return rc;
+
+	*val = newval & mask;
+	return 0;
+}
+
+static int 
+ov6650_read_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+	int rc;
+
+	if ((rc = ov6650_read_reg(client, reg, val)))
+		return rc;
+	(*val) &= mask;
+
+	return 0;
+}
+
+/* Initialize a list of OV6650 registers.
+ * The list of registers is terminated by the pair of values 
+ * { OV6650_REG_TERM, OV6650_VAL_TERM }.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int 
+ov6650_write_regs(struct i2c_client *client, const struct ov6650_reg reglist[])
+{
+	int err;
+	const struct ov6650_reg *next = reglist;
+	
+	while (!((next->reg == OV6650_REG_TERM) 
+		&& (next->val == OV6650_VAL_TERM)))
+	{
+		err = ov6650_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 = NUM_CONTROLS - 1; i >= 0; i--)
+		if (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 
+ * OV6650 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 OV6650 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
+ov6650_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;
+	const static unsigned long clks_per_frame[] = 
+		{ 200000, 200000, 200000, 200000, 400000, 800000, 3200000 };
+	unsigned char clkrc = 0;
+
+	printk(KERN_DEBUG "ov6650_clkrc(isize=%d xclk=%lu fper=%d/%d)\r\n", isize, xclk, fper->numerator, fper->denominator);
+	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;
+		}
+	}
+
+	clkrc = divisor - 1;
+	clkrc &= 0x3f;
+	switch (xclk) {
+		case 8000000:
+			clkrc |= 0x00;
+			break;
+		case 12000000:
+			clkrc |= 0x40;
+			break;
+		case 16000000:
+			clkrc |= 0x80;
+			break;
+		case 24000000:
+			clkrc |= 0xC0;
+			break;
+	}
+	/*
+	 * TODO - fix this. For now, just return a known, working  value
+	 * Getting the value wrong seems to affect the AGC
+	 */
+	clkrc = 0xc1;
+	printk(KERN_DEBUG "ov6650_clkrc(xclk=%ld fper=%d/%d) divisor=%ld clkrc=0x%x\r\n", xclk, fper->numerator, fper->denominator, divisor, clkrc);
+	return clkrc;
+}
+
+/* Configure the OV6650 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
+ov6650_configure(struct i2c_client *client, 
+	enum image_size isize, 
+	enum pixel_format pfmt,
+	unsigned long xclk,
+	struct v4l2_fract *fper)
+{
+	int err;
+	u8 format_val = 0x00; // CIF 0x20; // QCIF
+	u8 format_mask = 0x20;
+	unsigned char clkrc;
+
+	printk(KERN_DEBUG "ov6650_configure(isize=%d pfmt=%d xclk=%lu)\r\n", isize, pfmt, xclk);
+
+	/* common register initialization */
+	err = ov6650_write_regs(client, init_regtbl_6650);
+	if (err)
+		return err;
+
+	/* configure image size */
+	if (isize == QCIF) format_val = 0x20;
+	else format_val == 0x00;
+	err = ov6650_write_reg_mask(client, 0x12, &format_val, format_mask);
+	if (err)
+		return err;
+
+	// TODO - set pixel format
+	/* configure frame rate */
+	clkrc = ov6650_clkrc(isize, xclk, fper);
+	err = ov6650_write_reg(client, OV6650_CLKRC, clkrc);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/* Detect if an OV6650 is present
+ * A device is considered to be detected if the manufacturer ID (MIDH and MIDL) 
+ * and the product ID (PIDH and PIDL) registers match the expected values.  
+ * 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 0
+ * if a device is detected.
+ */
+static int
+ov6650_detect(struct i2c_client *client)
+{
+	u8 midh, midl, pidh, pidl;
+
+	if (!client)
+		return -ENODEV;
+ 
+	if (ov6650_read_reg(client, OV6650_MIDH, &midh))
+		return -ENODEV;
+	if (ov6650_read_reg(client, OV6650_MIDL, &midl))
+		return -ENODEV;
+	if (ov6650_read_reg(client, OV6650_PIDH, &pidh))
+		return -ENODEV;
+	if (ov6650_read_reg(client, OV6650_PIDL, &pidl))
+		return -ENODEV;
+
+	if ((midh != OV6650_MIDH_MAGIC) 
+		|| (midl != OV6650_MIDL_MAGIC)
+		|| (pidh != OV6650_PIDH_MAGIC)
+		|| (pidl != OV6650_PIDL_MAGIC))
+	{
+		/* We didn't read the values we expected, so 
+		 * this must not be an OV6650.
+		 */
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static struct i2c_driver ov6650sensor_i2c_driver;
+
+/* This function registers an I2C client via i2c_attach_client() for an OV6650 
+ * sensor device.  If 'probe' is non-zero, then the I2C client is only 
+ * registered if the device can be detected.  If 'probe' is zero, then no 
+ * device detection is attempted and the I2C client is always registered.
+ * Returns zero if an I2C client is successfully registered, or non-zero 
+ * otherwise.
+ */
+static int 
+ov6650_i2c_attach_client(struct i2c_adapter *adap, int addr, int probe)
+{
+	struct ov6650_sensor *sensor = &ov6650;
+	struct i2c_client *client = &sensor->client;
+	int err;
+
+	if (client->adapter)
+		return -EBUSY;	/* our client is already attached */
+
+	client->addr = addr;
+	client->driver = &ov6650sensor_i2c_driver;
+	client->adapter = adap;
+
+	err = i2c_attach_client(client);
+	if (err) {
+		client->adapter = NULL;
+		return err;
+	}
+
+	if (probe) {
+		err = ov6650_detect(client);
+		if (err < 0) {
+			i2c_detach_client(client);
+			client->adapter = NULL;
+			return err;
+		}
+	}
+	return 0;
+}
+
+/* This function is called by i2c_del_adapter() and i2c_del_driver() 
+ * if the adapter or driver with which this I2C client is associated is 
+ * removed.  This function unregisters the client via i2c_detach_client().
+ * Returns zero if the client is successfully detached, or non-zero 
+ * otherwise.
+ */
+static int 
+ov6650_i2c_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	if (!client->adapter)
+		return -ENODEV;	/* our client isn't attached */
+
+	err = i2c_detach_client(client);
+	client->adapter = NULL;
+
+	return err;
+}
+
+/* This function will be called for each registered I2C bus adapter when our 
+ * I2C driver is registered via i2c_add_driver().  It will also be called 
+ * whenever a new I2C adapter is registered after our I2C driver is registered.
+ * This function probes the specified I2C bus adapter to determine if an 
+ * OV6650 sensor device is present.  If a device is detected, an I2C client 
+ * is registered for it via ov6650_i2c_attach_client().  Note that we can't use 
+ * the standard i2c_probe() function to look for the sensor because the OMAP 
+ * I2C controller doesn't support probing.
+ * Returns zero if an OV6650 device is detected and an I2C client successfully 
+ * registered for it, or non-zero otherwise.
+ */
+static int 
+ov6650_i2c_probe_adapter(struct i2c_adapter *adap)
+{
+	return ov6650_i2c_attach_client(adap, OV6650_I2C_ADDR, 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
+ov6650_find_size(unsigned int width, unsigned int height)
+{
+	unsigned long pixels = width*height;
+
+	if (ov6650_sizes[CIF].height*
+		ov6650_sizes[CIF].width > pixels)
+	{
+		return QCIF;
+	}
+	return CIF;
+}
+
+/* following are sensor interface functions implemented by 
+ * OV9640 sensor driver.
+ */
+static int
+ov6650sensor_query_control(struct v4l2_queryctrl *qc, void *priv)
+{
+	int i;
+
+	i = find_vctrl (qc->id);
+	if (i == -EINVAL) {
+		qc->flags = V4L2_CTRL_FLAG_DISABLED;
+		return 0;
+	}
+	if (i < 0)
+		return -EINVAL;
+
+	*qc = control[i].qc;
+	return 0;
+}
+
+static int
+ov6650sensor_get_control(struct v4l2_control *vc, void *priv)
+{
+	struct ov6650_sensor *sensor = (struct ov6650_sensor *) priv;
+	struct i2c_client *client = &sensor->client;
+	int i, val;
+	struct vcontrol * lvc;
+	
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &control[i];	
+	if (ov6650_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
+ov6650sensor_set_control(struct v4l2_control *vc, void *priv)
+{
+	struct ov6650_sensor *sensor = (struct ov6650_sensor *) priv;
+	struct i2c_client *client = &sensor->client;
+	struct vcontrol *lvc;
+	int val = vc->value;
+	int i;
+
+	i = find_vctrl(vc->id);
+	if (i < 0)
+		return -EINVAL;
+
+	lvc = &control[i];
+	val = val << lvc->start_bit;
+	if (ov6650_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
+ov6650sensor_enum_pixformat(struct v4l2_fmtdesc *fmt, void *priv)
+{
+	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;
+
+		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+		if (index >= NUM_OVERLAY_FORMATS)
+			return -EINVAL;
+		break;
+
+		default:
+			return -EINVAL;
+	}
+
+	fmt->flags = ov6650_formats[index].flags;
+	strlcpy(fmt->description, ov6650_formats[index].description, sizeof(fmt->description));
+	fmt->pixelformat = ov6650_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
+ov6650sensor_try_format(struct v4l2_pix_format *pix, void *priv)
+{
+	enum image_size isize;
+	int ifmt;
+
+	isize = ov6650_find_size(pix->width, pix->height);
+	pix->width = ov6650_sizes[isize].width;
+	pix->height = ov6650_sizes[isize].height;
+	for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
+		if (pix->pixelformat == ov6650_formats[ifmt].pixelformat)
+			break;
+	}
+	if (ifmt == NUM_CAPTURE_FORMATS)
+		ifmt = 0;
+	pix->pixelformat = ov6650_formats[ifmt].pixelformat;
+	pix->field = V4L2_FIELD_NONE;
+	pix->bytesperline = pix->width*2;
+	pix->sizeimage = pix->bytesperline*pix->height;
+	printk(KERN_DEBUG "6650sensor_try_format sizeimage=%d\n", pix->sizeimage);
+	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;
+}
+
+/* Given the image capture format in pix, the nominal frame period in 
+ * timeperframe, calculate the required xclk frequency 
+ */
+static unsigned long
+ov6650sensor_calc_xclk(struct v4l2_pix_format *pix,
+			struct v4l2_fract *timeperframe, void *priv)
+{
+	unsigned long tgt_xclk;			/* target xclk */
+
+	printk(KERN_DEBUG "ov6650sensor_calc_xclk\r\n");
+	/*
+	 * For now, just return a fixed value
+	 * Typical frequency for the 6650 is 24MHz, min is 8MHz and max is 27MHz
+	 */
+	tgt_xclk = 24000000;
+	printk(KERN_DEBUG "ov6650sensor_calc_xclk returning %ld\r\n", tgt_xclk);
+	return tgt_xclk;
+}
+
+/* Given a capture format in pix, the frame period in timeperframe, and
+ * the xclk frequency, set the capture format of the OV9640 sensor.
+ * The actual frame period will be returned in timeperframe.
+ */
+static int
+ov6650sensor_configure(struct v4l2_pix_format *pix, unsigned long xclk,
+			struct v4l2_fract *timeperframe, void *priv)
+{
+	struct ov6650_sensor *sensor = (struct ov6650_sensor *) priv;
+  	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;
+ 	}
+
+	return ov6650_configure(&sensor->client,
+				ov6650_find_size(pix->width, pix->height),
+				pfmt, xclk, timeperframe);
+}
+
+/* Prepare for the driver to exit.
+ * Balances ov6650sensor_init().
+ * This function must de-initialize the sensor and its associated data 
+ * structures.
+ */
+static int
+ov6650sensor_cleanup(void *priv)
+{
+	struct ov6650_sensor *sensor = (struct ov6650_sensor *) priv;
+
+	if (sensor) {
+		i2c_del_driver(&ov6650sensor_i2c_driver);
+ 	}
+	return 0;
+}
+
+
+static struct i2c_driver ov6650sensor_i2c_driver = {
+	.driver {
+		.name		= "ov6650",
+	},
+	.id		= I2C_DRIVERID_MISC, /*FIXME:accroding to i2c-ids.h */
+	.attach_adapter	= ov6650_i2c_probe_adapter,
+	.detach_client	= ov6650_i2c_detach_client,
+};
+
+/* Initialize the OV6650 sensor.
+ * This routine allocates and initializes the data structure for the sensor, 
+ * registers the I2C driver, and sets a default image 
+ * capture format in pix.  The capture format is not actually programmed 
+ * into the OV6650 sensor by this routine.
+ * This function must return a non-NULL value to indicate that 
+ * initialization is successful.
+ */
+static void *
+ov6650sensor_init(struct v4l2_pix_format *pix)
+{
+	struct ov6650_sensor *sensor = &ov6650;
+ 	int err;
+
+	memset(sensor, 0, sizeof(*sensor));
+ 
+	err = i2c_add_driver(&ov6650sensor_i2c_driver);
+	if (err) {
+		printk(KERN_ERR "Failed to register OV6650 I2C client.\n");
+		return NULL;
+	}
+	if (!sensor->client.adapter) {
+		printk(KERN_WARNING 
+			"Failed to detect OV6650 sensor chip.\n");
+		return NULL;
+	}
+	else {
+		printk(KERN_INFO 
+			"OV6650 sensor chip\n");
+	}
+
+	/* set initial image size and pixel format */
+	pix->width = ov6650_sizes[CIF].width;
+	pix->height = ov6650_sizes[CIF].height;
+	pix->pixelformat = V4L2_PIX_FMT_YUYV;
+	ov6650sensor_try_format(pix, NULL);
+
+	return (void *)sensor;
+}
+
+struct camera_sensor camera_sensor_if = {
+	.version	= 0x01,
+	.name		= "OV6650",
+	.init		= ov6650sensor_init,
+	.cleanup	= ov6650sensor_cleanup,
+	.enum_pixformat = ov6650sensor_enum_pixformat,
+	.try_format	= ov6650sensor_try_format,
+	.calc_xclk	= ov6650sensor_calc_xclk,
+	.configure	= ov6650sensor_configure,
+	.query_control	= ov6650sensor_query_control,
+	.get_control	= ov6650sensor_get_control,
+	.set_control	= ov6650sensor_set_control,
+};
+
+
+EXPORT_SYMBOL(camera_sensor_if);
+#endif	/* ifdef CAMERA_OV6650 */
Index: linux-2.6/drivers/media/video/omap/Kconfig
===================================================================
--- linux-2.6/drivers/media/video/omap/Kconfig	(revision 412)
+++ linux-2.6/drivers/media/video/omap/Kconfig	(working copy)
@@ -1,7 +1,7 @@
 config VIDEO_OMAP_CAMERA
 	tristate "OMAP Camera support (EXPERIMENTAL)"
 	select VIDEO_BUF
-	depends on VIDEO_DEV && (ARCH_OMAP16XX || ARCH_OMAP24XX)
+	depends on VIDEO_DEV && (ARCH_OMAP16XX || ARCH_OMAP24XX || MACH_AMS_DELTA)
 	help
 	  V4L2 camera driver support for OMAP1/2 based boards.
 	
@@ -10,3 +10,9 @@
 	depends on VIDEO_OMAP_CAMERA
 	help
 	  OmniVision 9640 camera sensor support
+
+config VIDEO_CAMERA_SENSOR_OV6650
+	bool "OV6650  sensor support"
+	depends on VIDEO_OMAP_CAMERA 
+	help
+	  E3 Camera
Index: linux-2.6/drivers/media/video/omap/ov6650.h
===================================================================
--- linux-2.6/drivers/media/video/omap/ov6650.h	(revision 0)
+++ linux-2.6/drivers/media/video/omap/ov6650.h	(revision 476)
@@ -0,0 +1,113 @@
+/*
+ * drivers/media/video/omap/ov6650.h
+ *
+ * Register definitions for the OmniVision OV6650 CameraChip.
+ *
+ * Copyright (c) 2006 Matt Callow
+ *
+ * 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 OV6650_H
+#define OV6650_H
+
+#define OV6650_I2C_ADDR	0x60
+
+#define OV6650_PIDH_MAGIC	0x66	/* high byte of product ID number */
+#define OV6650_PIDL_MAGIC	0x50	/* low byte of product ID number */
+#define OV6650_MIDH_MAGIC	0x7F	/* high byte of mfg ID */
+#define OV6650_MIDL_MAGIC	0xA2	/* low byte of mfg ID */
+
+/* define register offsets for the OV6650 sensor chip */
+#define OV6650_GAIN	0x00	/* range 00 - 3F */
+#define OV6650_BLUE	0x01
+#define OV6650_RED	0x02
+#define OV6650_SAT 	0x03	/* [7:4] saturation [0:3] reserved */
+#define OV6650_HUE 	0x04	/* [7:6] rsrvd [5] hue en [4:0] hue */
+#define OV6650_BRT  	0x06
+#define OV6650_PIDH	0x0A
+#define OV6650_PIDL 	0x0B
+#define OV6650_AECH 	0x10	/* Exposure Value */
+#define OV6650_CLKRC 	0x11	/* Data Format and Internal Clock */
+				/* [7:6] Input system clock (MHz)*/
+				/*   00=8, 01=12, 10=16, 11=24 */
+				/* [5:0]: Internal Clock Pre-Scaler */
+#define OV6650_COMA 	0x12	/* [7] Reset */
+#define OV6650_COMB 	0x13 
+#define OV6650_COMC 	0x14 
+#define OV6650_COMD 	0x15 
+#define OV6650_COML	0x16 
+#define OV6650_HSTART 	0x17 
+#define OV6650_HSTOP 	0x18 
+#define OV6650_VSTRT	0x19 
+#define OV6650_VSTOP	0x1A 
+#define OV6650_PSHFT	0x1B 
+#define OV6650_MIDH 	0x1C 
+#define OV6650_MIDL 	0x1D 
+#define OV6650_HSYNCS 	0x1E 
+#define OV6650_HSYNCE 	0x1F 
+#define OV6650_COME 	0x20 
+#define OV6650_YOFF	0x21 
+#define OV6650_UOFF	0x22 
+#define OV6650_VOFF	0x23 
+#define OV6650_AEW 	0x24 
+#define OV6650_AEB 	0x25 
+#define OV6650_COMF	0x26 
+#define OV6650_COMG 	0x27 
+#define OV6650_COMH 	0x28 
+#define OV6650_COMI 	0x29 
+#define OV6650_RSVD 	0x2A 
+#define OV6650_FRARL 	0x2B 
+#define OV6650_COMJ 	0x2C 
+#define OV6650_COMK 	0x2D 
+#define OV6650_AVGY 	0x2E 
+#define OV6650_REF0 	0x2F 
+#define OV6650_REF1 	0x30 
+#define OV6650_REF2 	0x31 
+#define OV6650_FRAJH 	0x32 
+#define OV6650_FRAJL 	0x33 
+#define OV6650_FACT 	0x34 
+#define OV6650_L1AEC 	0x35 
+#define OV6650_AVGU 	0x36 
+#define OV6650_AVGV 	0x37 
+#define OV6650_SPCB 	0x60 
+#define OV6650_SPCC 	0x61 
+#define OV6650_GAM1 	0x62 
+#define OV6650_GAM2 	0x63 
+#define OV6650_GAM3 	0x64 
+#define OV6650_SPCD 	0x65 
+#define OV6650_SPCE 	0x68 
+#define OV6650_ADCL 	0x69 
+#define OV6650_RMCO 	0x6C 
+#define OV6650_GMCO 	0x6D 
+#define OV6650_BMCO 	0x6E 
+#define OV6650_NUM_REGS		(OV6650_BMCO + 1)
+
+/* define a structure for register initialization values */
+struct ov6650_reg {
+	unsigned char reg;
+	unsigned char val;
+};
+
+enum image_size { QCIF, CIF };
+enum pixel_format { YUV, RGB565, RGB555 };
+#define NUM_IMAGE_SIZES 2
+#define NUM_PIXEL_FORMATS 3
+
+struct capture_size {
+	unsigned long width;
+	unsigned long height;
+};
+
+/* Array of image sizes supported by OV6650.  These must be ordered from 
+ * smallest image size to largest.
+ */
+const static struct capture_size ov6650_sizes[] = {
+	{  176, 144 },	/* QCIF */
+	{  352, 288 },	/* CIF */
+};
+
+#endif /* ifndef OV6650_H */
+
Index: linux-2.6/drivers/media/video/omap/omap1510cam.c
===================================================================
--- linux-2.6/drivers/media/video/omap/omap1510cam.c	(revision 0)
+++ linux-2.6/drivers/media/video/omap/omap1510cam.c	(revision 476)
@@ -0,0 +1,508 @@
+/*
+ * drivers/media/video/omap/omap1510cam.c
+ *
+ * Based on
+ * drivers/media/video/omap/omap16xxcam.c
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc. 
+ * Copyright (C) 2006 Matt Callow
+ * 
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP 1510 camera controller 
+ *
+ * leverage some code from CEE distribution 
+ * Copyright (C) 2003-2004 MontaVista Software, Inc.
+ * 
+ * This package is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. 
+ * 
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
+ */
+ 
+#include <linux/config.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+
+#include <asm/arch/irqs.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/hardware.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <asm/mach-types.h>
+#include <asm/arch/board-ams-delta.h>
+
+
+#include "omap1510cam.h"
+#include "camera_hw_if.h"
+#include "camera_core.h"
+  
+#define CONF_CAMERAIF_RESET_R 5
+#define EN_PER	  0
+
+typedef struct {
+        unsigned int ctrlclock;     /* 00 */
+        unsigned int it_status;     /* 04 */
+        unsigned int mode;          /* 08 */
+        unsigned int status;        /* 0C */
+        unsigned int camdata;       /* 10 */
+        unsigned int gpio;   	    /* 14 */
+        unsigned int peak_counter;  /* 18 */
+} camera_regs_t;
+
+struct camdma_state {
+	dma_callback_t callback;
+	void *arg1;
+	void *arg2;
+};
+
+struct omap1510cam {
+	camera_regs_t *camera_regs;
+	unsigned long iobase_phys;
+
+	/* dma related stuff */
+	spinlock_t dma_lock;
+	int free_dmach;
+	struct camdma_state camdma;
+	int dma_channel;
+
+	wait_queue_head_t vsync_wait;
+	
+	int new;
+};
+static struct omap1510cam hardware_data;
+   
+static int omap1510cam_set_xclk(int, void *);
+static void omap1510_cam_dma_link_callback(int, unsigned short, void *);
+
+/* Clears the camera data FIFO by setting RAZ_FIFO bit in MODE configuration
+   register. */
+static void
+omap1510_cam_clear_fifo(struct omap1510cam *data)
+{
+	//data->camera_regs->ctrlclock &= ~LCLK_EN; 
+	data->camera_regs->mode |= RAZ_FIFO;
+	udelay(10);
+	data->camera_regs->mode &= ~RAZ_FIFO;
+	//data->camera_regs->ctrlclock |= LCLK_EN; 
+}
+  
+static void
+omap1510_cam_reset(struct omap1510cam *data, int yes)
+{
+	data->camera_regs->gpio = yes ? 1 : 0;
+}
+
+static void 
+omap1510_cam_init(void)
+{
+	printk(KERN_DEBUG "omap1510_cam_init\r\n");
+	/*
+	 * FIXME - Use mux API's instead of directly writing in to MUX registers
+	 */
+	omap_writel(omap_readl(FUNC_MUX_CTRL_4) & ~(0x1ff << 21), FUNC_MUX_CTRL_4);
+	omap_writel(0, FUNC_MUX_CTRL_5);
+	omap_writel(omap_readl(PULL_DWN_CTRL_0) & ~(0x1FFF << 17), PULL_DWN_CTRL_0);
+	omap_writel(omap_readl(PU_PD_SEL_0) & ~(0x1FFF << 17), PU_PD_SEL_0);
+
+	omap_writel(0xeaef, COMP_MODE_CTRL_0);
+	omap_writel(omap_readl(OMAP1610_RESET_CONTROL) & ~(1 << CONF_CAMERAIF_RESET_R),
+			OMAP1610_RESET_CONTROL);
+	omap_writel(omap_readl(OMAP1610_RESET_CONTROL) | (1 << CONF_CAMERAIF_RESET_R),
+			OMAP1610_RESET_CONTROL);
+    
+	/* Enable peripheral reset */
+	omap_writew(omap_readw(ARM_RSTCT2) | (1 << EN_PER), ARM_RSTCT2); 
+
+	/* enable peripheral clock */
+	clk_enable(clk_get(0, "armper_ck"));
+}
+
+static void
+omap1510_cam_waitfor_syncedge(struct omap1510cam *data, u32 edge_mask)
+{
+	data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT) | edge_mask;
+	do {
+		interruptible_sleep_on(&data->vsync_wait);
+	} while (signal_pending(current));
+}
+
+static void
+omap1510_cam_configure_dma(struct omap1510cam *data)
+{
+
+	data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT)
+			 | EN_DMA | EN_FIFO_FULL;
+	data->camera_regs->ctrlclock |= LCLK_EN; 
+}
+
+/* acquire h/w resources DMA */
+static int
+omap1510_cam_link_open(struct omap1510cam *data)
+{
+	int ret;
+
+	/* Acquire dma channel */
+	if ((ret = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, 
+				"camera dma 1", omap1510_cam_dma_link_callback,
+				(void *)data, &data->dma_channel))) {
+                return ret;
+	}
+	printk(KERN_DEBUG "Got DMA channel %d\n", data->dma_channel);
+	// set up sync
+	//OMAP_DMA_CCR_REG(data->dma_channel) |= OMAP_DMA_CAMERA_IF_RX;
+	return 0;
+}
+
+/* free h/w resources, stop i/f */
+static int
+omap1510_cam_link_close(struct omap1510cam *data)
+{
+	/* free dma */
+	omap_stop_dma(data->dma_channel);
+	omap_free_dma (data->dma_channel);
+
+	return 0;
+}
+
+/* dma callback routine. */
+static void
+omap1510_cam_dma_link_callback(int lch, unsigned short ch_status, void *data)
+{
+	void *arg1, *arg2;
+	struct sgdma_state *sgdma = sgdma;
+	struct omap1510cam *cam = (struct omap1510cam *)data;
+	dma_callback_t callback;
+
+	//printk("omap1510_cam_dma_link_callback ch=%d status=%d\n", lch, ch_status);
+	spin_lock(&cam->dma_lock);
+	if (cam->free_dmach == 1)
+	{
+		printk(KERN_WARNING "callback all CHANNELS WERE IDLE \n");
+		spin_unlock(&cam->dma_lock);
+		return;
+	}
+
+	if (OMAP_DMA_CCR_REG(cam->dma_channel) & (1 << 7)) {
+		printk(KERN_WARNING "transfer still in progress\n");
+	} else {
+
+		callback = cam->camdma.callback;
+ 		arg1 = cam->camdma.arg1;
+		arg2 = cam->camdma.arg2;
+		cam->free_dmach++;
+
+		spin_unlock(&cam->dma_lock);		
+ 		callback(arg1, arg2);
+		spin_lock(&cam->dma_lock);
+	}
+
+	spin_unlock(&cam->dma_lock);
+	
+}
+
+static irqreturn_t
+omap1510_cam_isr(int irq, void *client_data, struct pt_regs *regs)
+{
+	struct omap1510cam *data = (struct omap1510cam *)client_data;
+	unsigned int itstat = data->camera_regs->it_status;
+
+	/* VSYNC UP interrupt, start filling FIFO and enabling DMA */
+	if (itstat & V_UP) {		
+		data->camera_regs->mode &= ~EN_V_UP;
+	 	omap1510_cam_clear_fifo(data);	
+		omap1510_cam_configure_dma(data);
+		omap_start_dma(data->dma_channel);
+		wake_up_interruptible(&data->vsync_wait);
+	}
+
+	if (itstat & V_DOWN) {
+		data->camera_regs->mode &= ~EN_V_DOWN;
+		wake_up_interruptible(&data->vsync_wait);
+	}
+
+	if (itstat & H_UP)
+		printk(KERN_WARNING "H_UP\n");
+	
+	if (itstat & H_DOWN)
+		printk(KERN_WARNING "H_DOWN\n");
+	
+	if (itstat & FIFO_FULL) {
+		omap1510_cam_clear_fifo(data);	
+		printk(KERN_WARNING "FIFO_FULL\n");
+	}
+	
+	if (itstat & DATA_XFER)
+		printk(KERN_WARNING "DATA_TRANS\n");
+	
+	return IRQ_HANDLED;
+}
+ 
+/* ------------- below are interface functions ----------------- */
+/* ------------- these functions are named omap1510cam_<name> -- */
+static int
+omap1510cam_init_dma(void *priv)
+{
+	struct omap1510cam *data = (struct omap1510cam *) priv;
+
+	printk(KERN_DEBUG "omap1510cam_init_dma\r\n");
+
+	data->free_dmach = 1;
+	data->camdma.callback = NULL;
+	data->camdma.arg1 = NULL;
+	data->camdma.arg2 = NULL;
+
+	return 0;
+}
+
+/* start the dma of chains */
+static int 
+omap1510cam_start_dma(struct sgdma_state *sgdma,
+		dma_callback_t callback, void *arg1, void *arg2, void *priv)
+{
+	struct omap1510cam *data = (struct omap1510cam *) priv;
+	struct scatterlist *sglist;
+	unsigned long irqflags;
+	int dmach;
+
+	spin_lock_irqsave(&data->dma_lock, irqflags);
+	sglist = (struct scatterlist *)(sgdma->sglist + sgdma->next_sglist);
+
+	if (!data->free_dmach) {
+		spin_unlock_irqrestore(&data->dma_lock, irqflags);
+		return -EBUSY;
+	} 
+	dmach = data->dma_channel;
+	data->camdma.callback = callback;
+	data->camdma.arg1 = arg1;
+	data->camdma.arg2 = arg2;
+	//printk("omap1510cam_start_dma dst=%u\n", sg_dma_address(sglist) );
+	omap_set_dma_dest_params(dmach, OMAP_DMA_PORT_EMIFF,
+	                     OMAP_DMA_AMODE_POST_INC, sg_dma_address(sglist),
+			     0, 0);
+
+	omap_set_dma_transfer_params(dmach, OMAP_DMA_DATA_TYPE_S32,
+			FIFO_TRIGGER_LVL, 
+			sg_dma_len(sglist)/(DMA_ELEM_SIZE * FIFO_TRIGGER_LVL), 
+ 			OMAP_DMA_SYNC_FRAME,
+			0, 0);
+
+	// set up sync device
+	//OMAP_DMA_CCR_REG(dmach) &= ~0x1f;
+	//OMAP_DMA_CCR_REG(dmach) |= OMAP_DMA_CAMERA_IF_RX;
+
+	if (data->new) {
+		data->new = 0;
+		omap_set_dma_src_params(dmach, OMAP_DMA_PORT_TIPB,
+			OMAP_DMA_AMODE_CONSTANT, CAM_CAMDATA_REG,
+			0, 0);
+		omap1510_cam_waitfor_syncedge(data, EN_V_UP);
+	} else {
+		omap_start_dma(dmach);
+	}
+ 	
+	data->free_dmach--;
+	spin_unlock_irqrestore(&data->dma_lock, irqflags);
+	return 0;
+}
+int static
+omap1510cam_finish_dma(void *priv)
+{
+	struct omap1510cam *data = (struct omap1510cam *) priv;
+
+	printk(KERN_DEBUG "omap1510cam_finish_dma\n");
+	while (data->free_dmach < 1)
+		mdelay(1);
+
+	return 0;
+}
+
+
+/* Enables the camera. Takes camera out of reset. Enables the clocks. */ 
+static int
+omap1510cam_enable(void *priv)
+{
+	struct omap1510cam *data = (struct omap1510cam *) priv;
+
+	printk(KERN_DEBUG "omap1510cam_enable\r\n");
+
+	omap1510_cam_reset(data, 1);
+	
+	/* give clock to camera_module */
+	data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT);
+	data->camera_regs->ctrlclock = MCLK_EN | CAMEXCLK_EN;
+
+	omap1510_cam_clear_fifo(data);
+
+	/* wait for camera to settle down */
+	mdelay(5);
+
+	printk(KERN_DEBUG "exit omap1510cam_enable\r\n");
+	return 0;
+}
+ 
+/* Disables all the camera clocks. Put the camera interface in reset. */
+static int
+omap1510cam_disable(void *priv)
+{ 	
+	struct omap1510cam *data = (struct omap1510cam *) priv;
+
+	omap1510_cam_clear_fifo(data);
+
+	data->camera_regs->ctrlclock = 0x00000000;     
+	data->camera_regs->mode = 0x00000000;
+	
+	omap1510_cam_reset(data, 0);
+
+	return 0;
+}
+
+/* Abort the data transfer */
+static int
+omap1510cam_abort(void *priv)
+{
+	return omap1510cam_disable(priv);
+}
+
+/*
+ * Set camera xclk
+ * Set to the requested frequency, or the next highest available
+ */
+static int
+omap1510cam_set_xclk(int requested_xclk, void *priv)
+{ 	
+ 	int xclk_val;
+	int actual_xclk;
+	struct omap1510cam *data = (struct omap1510cam *) priv;
+
+	if (requested_xclk <= 6000000) {
+		actual_xclk = 6000000;
+		xclk_val = FOSCMOD_6MHz;
+	} else if (requested_xclk <= 8000000) {
+		actual_xclk = 8000000;
+		xclk_val = FOSCMOD_8MHz | DPLL_EN;
+	} else if (requested_xclk <= 9600000) {
+		actual_xclk = 9600000;
+		xclk_val = FOSCMOD_9_6MHz | DPLL_EN;
+	} else if (requested_xclk <= 12000000) {
+		actual_xclk = 12000000;
+		xclk_val = FOSCMOD_12MHz;
+	} else {
+		actual_xclk = 24000000;
+		xclk_val = FOSCMOD_24MHz | DPLL_EN;
+	}
+
+	/* follow the protocol to change the XCLK clock */
+	data->camera_regs->ctrlclock=data->camera_regs->ctrlclock&(~(MCLK_EN|DPLL_EN)); 
+        data->camera_regs->ctrlclock = data->camera_regs->ctrlclock | (CAMEXCLK_EN | xclk_val);
+        data->camera_regs->ctrlclock = data->camera_regs->ctrlclock | (MCLK_EN | LCLK_EN );
+
+	printk(KERN_DEBUG "omap1510cam_set_xclk requested=%d actual=%d\r\n", requested_xclk, actual_xclk);
+	return (actual_xclk);
+}
+
+static int
+omap1510cam_open(void *priv)
+{
+	struct omap1510cam *data = (struct omap1510cam *) priv;
+	int ret;
+
+	if ((ret = request_irq(INT_CAMERA, omap1510_cam_isr, SA_INTERRUPT, 
+					"camera", data))) {
+		printk(KERN_ERR "FAILED to aquire irq\n");
+		return ret;
+	}
+
+	data->new = 1;
+	omap1510cam_enable(data);
+	omap1510cam_init_dma(data);
+
+	ams_delta_latch1_write(0x01, 0x01);
+	return omap1510_cam_link_open(data);
+}
+
+static int
+omap1510cam_close(void *priv)
+{
+	struct omap1510cam *data = (struct omap1510cam *) priv;
+
+	omap1510cam_disable(priv);
+	 
+	free_irq(INT_CAMERA, data);
+	
+	ams_delta_latch1_write(0x01, 0x00);
+	return omap1510_cam_link_close(data);
+}
+
+static int
+omap1510cam_cleanup(void *priv)
+{
+	struct omap1510cam *data = (struct omap1510cam *) priv;
+
+	omap1510cam_disable(data);
+	if (data->iobase_phys) {
+		release_mem_region(data->iobase_phys, CAMERA_IOSIZE);
+		data->iobase_phys = 0;
+	}
+
+	return 0;
+}
+
+/* Initialise the OMAP camera interface */
+static void *
+omap1510cam_init(void)
+{
+	unsigned long cam_iobase;
+
+	printk(KERN_DEBUG "omap1510cam_init\r\n");
+
+	if (!request_region(CAMERA_BASE, CAMERA_IOSIZE, "OAMP1510 Camera")) {
+		printk (KERN_ERR "OMAP1510 Parallel Camera Interface is already in use\n");
+		return NULL;
+	}
+
+	cam_iobase = io_p2v(CAMERA_BASE);
+
+	printk(KERN_DEBUG "cam_iobase %lX\r\n", cam_iobase);
+
+	/* Set the base address of the camera registers */
+	hardware_data.camera_regs = (camera_regs_t *)cam_iobase;
+	hardware_data.iobase_phys = (unsigned long) CAMERA_BASE;
+
+	/* Init the camera IF */
+	omap1510_cam_init();
+	/* enable it. This is needed for sensor detection */
+	omap1510cam_enable((void*)&hardware_data);
+	/* Init dma data */
+	spin_lock_init(&hardware_data.dma_lock);
+
+	init_waitqueue_head(&hardware_data.vsync_wait);
+	printk(KERN_DEBUG "exit omap1510cam_init\r\n");
+	return (void*)&hardware_data;
+}
+
+struct camera_hardware camera_hardware_if = {
+	.version	= 0x01,
+	.name		= "OMAP1510 Camera Parallel",
+	.init		= omap1510cam_init,
+	.cleanup	= omap1510cam_cleanup,
+	.open		= omap1510cam_open,
+	.close		= omap1510cam_close,
+	.enable		= omap1510cam_enable,
+	.disable	= omap1510cam_disable,
+	.abort		= omap1510cam_abort,
+	.set_xclk	= omap1510cam_set_xclk,
+	.init_dma	= omap1510cam_init_dma,
+	.start_dma	= omap1510cam_start_dma,
+	.finish_dma	= omap1510cam_finish_dma,
+};
+
Index: linux-2.6/drivers/media/video/omap/omap1510cam.h
===================================================================
--- linux-2.6/drivers/media/video/omap/omap1510cam.h	(revision 0)
+++ linux-2.6/drivers/media/video/omap/omap1510cam.h	(revision 476)
@@ -0,0 +1,104 @@
+/*
+ *  drivers/media/video/omap/omap1510cam.h
+ *  based on omap16xxcam.h
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc. 
+ * Copyright (C) 2006 Matt Callow
+ * 
+ * This package is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. 
+ * 
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
+ */
+ 
+#ifndef OMAP_1510_CAM_H
+#define OMAP_1510_CAM_H
+
+#define DMA_ELEM_SIZE   4
+#define FIFO_TRIGGER_LVL (32)
+
+/*
+ * ---------------------------------------------------------------------------
+ *  OMAP1510 Camera Interface
+ * ---------------------------------------------------------------------------
+ */
+
+#define CAMERA_BASE          (IO_PHYS + 0x6800)
+
+#define CAM_CTRLCLOCK_REG    (CAMERA_BASE + 0x00)
+#define CAM_IT_STATUS_REG    (CAMERA_BASE + 0x04)
+#define CAM_MODE_REG         (CAMERA_BASE + 0x08)
+#define CAM_STATUS_REG       (CAMERA_BASE + 0x0C)
+#define CAM_CAMDATA_REG      (CAMERA_BASE + 0x10)
+#define CAM_GPIO_REG         (CAMERA_BASE + 0x14)
+#define CAM_PEAK_CTR_REG     (CAMERA_BASE + 0x18)
+#define CAMERA_IOSIZE        0x1C
+
+/* CTRLCLOCK bit shifts */
+#define FOSCMOD_BIT    		0
+#define FOSCMOD_MASK   		(0x7 << FOSCMOD_BIT)
+#define FOSCMOD_12MHz		0x0
+#define	FOSCMOD_6MHz		0x2
+#define	FOSCMOD_9_6MHz		0x4
+#define	FOSCMOD_24MHz		0x5
+#define	FOSCMOD_8MHz		0x6
+#define	FOSCMOD_TC2_CK2		0x3
+#define	FOSCMOD_TC2_CK3    	0x1
+#define	FOSCMOD_TC2_CK4     	0x5
+#define	FOSCMOD_TC2_CK8     	0x0
+#define	FOSCMOD_TC2_CK10     	0x4
+#define	FOSCMOD_TC2_CK12     	0x6
+#define	FOSCMOD_TC2_CK16     	0x2
+#define	POLCLK         		(1<<3)
+#define	CAMEXCLK_EN    		(1<<4)
+#define	MCLK_EN        		(1<<5)
+#define	DPLL_EN        		(1<<6)
+#define	LCLK_EN        		(1<<7)
+
+/* IT_STATUS bit shifts */
+#define V_UP           (1<<0)
+#define V_DOWN         (1<<1)
+#define H_UP           (1<<2)
+#define H_DOWN         (1<<3)
+#define FIFO_FULL      (1<<4)
+#define DATA_XFER      (1<<5)
+
+/* MODE bit shifts */
+#define CAMOSC         (1<<0)
+#define IMGSIZE_BIT    1
+#define IMGSIZE_MASK   (0x3 << IMGSIZE_BIT)
+#define	IMGSIZE_CIF      (0x0 << IMGSIZE_BIT)    /* 352x288 */
+#define	IMGSIZE_QCIF     (0x1 << IMGSIZE_BIT)    /* 176x144 */
+#define	IMGSIZE_VGA      (0x2 << IMGSIZE_BIT)    /* 640x480 */
+#define	IMGSIZE_QVGA     (0x3 << IMGSIZE_BIT)    /* 320x240 */
+#define ORDERCAMD      (1<<3)
+#define EN_V_UP        (1<<4)
+#define EN_V_DOWN      (1<<5)
+#define EN_H_UP        (1<<6)
+#define EN_H_DOWN      (1<<7)
+#define EN_DMA         (1<<8)
+#define THRESHOLD      (1<<9)
+#define THRESHOLD_BIT  9
+#define THRESHOLD_MASK (0x7f<<9)
+#define EN_NIRQ        (1<<16)
+#define EN_FIFO_FULL   (1<<17)
+#define RAZ_FIFO       (1<<18)
+
+/* STATUS bit shifts */
+#define VSTATUS        (1<<0)
+#define HSTATUS        (1<<1)
+
+/* GPIO bit shifts */
+#define CAM_RST        (1<<0)
+
+
+#define XCLK_6MHZ     6000000
+#define XCLK_8MHZ     8000000
+#define XCLK_9_6MHZ   9000000
+#define XCLK_12MHZ   12000000
+#define XCLK_24MHZ   24000000
+
+#endif /* OMAP_1510_CAM_H */
Index: linux-2.6/drivers/media/video/omap/Makefile
===================================================================
--- linux-2.6/drivers/media/video/omap/Makefile	(revision 412)
+++ linux-2.6/drivers/media/video/omap/Makefile	(working copy)
@@ -2,9 +2,11 @@
 
 obj-$(CONFIG_VIDEO_OMAP_CAMERA) += omapcamera.o
 obj-$(CONFIG_VIDEO_CAMERA_SENSOR_OV9640) += sensor_ov9640.o
+obj-$(CONFIG_VIDEO_CAMERA_SENSOR_OV6650) += sensor_ov6650.o
 
 objs-yy := camera_core.o
 
+objs-y$(CONFIG_MACH_AMS_DELTA) += omap1510cam.o  
 objs-y$(CONFIG_ARCH_OMAP16XX) += omap16xxcam.o
 objs-y$(CONFIG_MACH_OMAP_H3) += h3_sensor_power.o
 objs-y$(CONFIG_MACH_OMAP_H4) += h4_sensor_power.o
/*
 * v4ltest.c
 *
 * Copyright (C) 2006 Matt Callow
 *
 * Video-for-Linux test program for the E3 camera
 * 
 * This package is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation. 
 * 
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
 *
 */
/*
 * TODO:
 * - not sure that mirror works properly. Maybe a driver problem
 * - streaming?
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev.h>

static struct option options_long[] = {
	{ "flip", 1, NULL, 'f' },
	{ "mirror", 1, NULL, 'm' },
	{ "help", 0, NULL, 'h' },
	{ "output-file", 1, NULL, 'o' },
	{ "size", 1, NULL, 's' },
	{ 0,0,0,0 }
};

static const char options[] = "f:m:ho:s:";

static void setsize(int );
static void setorientation(int , int);

static int video_fd;
static int quit = 0;
#define CONV_TYPE_REAL 0
#define CONV_TYPE_INT 1
#define CONV_TYPE_LUT 2
#define CONV_TYPE CONV_TYPE_LUT

#if CONV_TYPE == CONV_TYPE_LUT
long lut_v1[256];
long lut_v2[256];
long lut_u1[256];
long lut_u2[256];
#endif

struct type_rgb {
	long r;
	long g;
	long b;
};

static void fatal(char *msg)
{
	perror(msg);
	exit(EXIT_FAILURE);
}

static inline long clip(long v)
{
	if (v<0)  return 0;
	if (v>255) return 255;
	return v;
}
#if CONV_TYPE == CONV_TYPE_INT 
static inline struct type_rgb YUV_to_rgb(unsigned char y,unsigned char u,unsigned char v)
{
	struct type_rgb rgb;
	long c = y - 16;
	long d = u - 128;
	long e = v - 128;

	rgb.r = clip(( 298 * c           + 409 * e + 128) >> 8);
	rgb.g = clip(( 298 * c - 100 * d - 208 * e + 128) >> 8);
	rgb.b = clip(( 298 * c + 516 * d           + 128) >> 8);

	return rgb;
}
#endif

#if CONV_TYPE == CONV_TYPE_REAL
struct type_rgb YUV_to_rgb(int y,int u,int v)
{
   struct type_rgb rgb;
   
   // u and v are +-0.5
   u -= 128;
   v -= 128;

   // Conversion
   rgb.r = y + 1.370705 * v;
   rgb.g = y - 0.698001 * v - 0.337633 * u;
   rgb.b = y + 1.732446 * u;

/*
   r = y + 1.402 * v;
   g = y - 0.344 * u - 0.714 * v;
   b = y + 1.772 * u;
*/
/*
   y -= 16;
   r = 1.164 * y + 1.596 * v;
   g = 1.164 * y - 0.392 * u - 0.813 * v;
   b = 1.164 * y + 2.017 * u;
*/

   // Clamp to 0..1
   if (rgb.r < 0) rgb.r = 0;
   if (rgb.g < 0) rgb.g = 0;
   if (rgb.b < 0) rgb.b = 0;
   if (rgb.r > 255) rgb.r = 255;
   if (rgb.g > 255) rgb.g = 255;
   if (rgb.b > 255) rgb.b = 255;

   return(rgb);
}
#endif

static void setup_lut()
{
#if CONV_TYPE == CONV_TYPE_LUT
	int u,v;
	fprintf(stderr,"Creating lookup tables...\n");
	for (v=0;v<256;v++) {
		lut_v1[v] = (v-128) * 1.370705;
		lut_v2[v] = (v-128) * 0.698001;
	}
	for (u=0;u<256;u++) {
		lut_u1[u] = (u-128) * 0.337633;
		lut_u2[u] = (u-128) * 1.732446;
	}
#endif
}

#if CONV_TYPE == CONV_TYPE_LUT
static inline struct type_rgb YUV_to_rgb(__u8 y, __u8 u, __u8 v)
{
   struct type_rgb rgb;

   rgb.r = clip(y + lut_v1[v]);
   rgb.g = clip(y - lut_v2[v] - lut_u1[u]);
   rgb.b = clip(y + lut_u2[u]);

   return rgb;
}
#endif

void capture(const char *filename)
{
	struct v4l2_format format;
	unsigned char *ibuf;
	unsigned char *obuf;
	int i,j;
	struct type_rgb  rgb;
	int output_fd = 1;

	setup_lut();
	if (filename != NULL) {
		output_fd = open(filename, O_TRUNC | O_CREAT | O_WRONLY);
	}
	if (-1 == ioctl (video_fd, VIDIOC_G_FMT, &format)) {
		fatal ("VIDIOC_G_FMT");
	}
	ibuf=malloc(format.fmt.pix.sizeimage);
	obuf=malloc(3 * format.fmt.pix.sizeimage / 2);
	read(video_fd, ibuf, format.fmt.pix.sizeimage);
	for (i=0,j=0;i<format.fmt.pix.sizeimage;i+=4,j+=6) {
		rgb = YUV_to_rgb(ibuf[i+3], ibuf[i+0], ibuf[i+2]);
		obuf[j+0] = rgb.r;
		obuf[j+1] = rgb.g;
		obuf[j+2] = rgb.b;

		rgb = YUV_to_rgb(ibuf[i+1], ibuf[i+0], ibuf[i+2]);
		obuf[j+3] = rgb.r;
		obuf[j+4] = rgb.g;
		obuf[j+5] = rgb.b;
	}
	write(output_fd, obuf, 3 * format.fmt.pix.sizeimage / 2);
	if (output_fd != 1) close(output_fd);
	free(ibuf);
	free(obuf);
}

static inline short rgb_to_screen(struct type_rgb rgb)
{
	short ret = ((rgb.r & 0xF0) << 4);         // upper 4 bits of red at bit 8
	ret |= (rgb.g & 0xF0);                      // upper 4 bits of green at bit 4
	ret |= ((rgb.b & 0xF0)>>4);          // upper 4 bits of blue at bit 0
	return ret;
}

static void setorientation(int mirror , int flip)
{
	struct v4l2_control control;

	fprintf(stderr, "mirror=%d, flip=%d\n", mirror, flip);
	control.id = V4L2_CID_HFLIP;
	control.value=mirror;
	if (-1 == ioctl (video_fd, VIDIOC_S_CTRL, &control)) {
		fatal ("V4L2_CID_HFLIP,");
	}
	control.id = V4L2_CID_VFLIP;
	control.value=flip;
	if (-1 == ioctl (video_fd, VIDIOC_S_CTRL, &control)) {
		fatal ("V4L2_CID_VFLIP,");
	}
}
/*
 * size 0=QCIF, 1=CIF 
 */
static void setsize(int size)
{
	struct v4l2_format format;
	format.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (size == 0) {
		format.fmt.pix.width=10;
		format.fmt.pix.height=10;
	} else {
		format.fmt.pix.width=352;
		format.fmt.pix.height=288;
	}
	if (-1 == ioctl (video_fd, VIDIOC_S_FMT, &format)) {
		fatal ("VIDIOC_S_FMT");
	}
}

static void sig_handler(int sig)
{
	fprintf(stderr, "\nSignal %d\n", sig);
	exit(EXIT_SUCCESS);
}

void display()
{
	struct v4l2_format format;
	unsigned long i;
	unsigned int x,y;
	unsigned char *ibuf;
	unsigned short *screen;
	int counter;
	struct v4l2_control control;
	int framebuffer_fd = open("/dev/fb0", O_RDWR);

	setup_lut();
	if (framebuffer_fd == -1) {
		fatal ("open framebuffer");
	}

	screen = mmap(0, 480 * 320 * 2, PROT_READ|PROT_WRITE, MAP_SHARED, framebuffer_fd, 0);

	fprintf(stderr, "Screen mapped at %p\n", screen);

	control.id = V4L2_CID_HFLIP;
	control.value = 1;
	if (-1 == ioctl (video_fd, VIDIOC_S_CTRL, &control)) {
		fatal ("VIDIOC_S_CTRL");
	}
	if (-1 == ioctl (video_fd, VIDIOC_G_FMT, &format)) {
		fatal ("VIDIOC_G_FMT");
	}

	fprintf(stderr, "size %d height %d width %d\n", format.fmt.pix.sizeimage, format.fmt.pix.height, format.fmt.pix.width);
	ibuf=malloc(format.fmt.pix.sizeimage);
	signal(SIGINT, sig_handler);
	fprintf(stderr, "CTRL-C to quit\n");

	while (!quit) {
		time_t start =  time(0);
		for (counter=0;(quit == 0) && (counter<100);counter++) {
			read(video_fd, ibuf, format.fmt.pix.sizeimage);
			i=0;
			for (y=0;y<format.fmt.pix.height;y++) {
				for (x=0;x<format.fmt.pix.width;x+=2) {
					*(volatile unsigned short *)(screen + x + (y * 480 )) = rgb_to_screen(YUV_to_rgb(ibuf[i+3], ibuf[i+0], ibuf[i+2]));
					*(volatile unsigned short *)(screen + 1 + x + (y * 480 )) = rgb_to_screen(YUV_to_rgb(ibuf[i+1], ibuf[i+0], ibuf[i+2]));
					i+=4;
				}
			}
		}
		time_t end =  time(0);
		fprintf(stderr, "%f fps\r\n", 1.0* counter / (end - start));
	}
	free(ibuf);

	munmap(screen, 480 * 320 * 2);
	close(framebuffer_fd);
}

void info() 
{
	struct v4l2_input input;
	struct v4l2_capability caps;
	struct v4l2_fmtdesc formatdesc;
	struct v4l2_format format;
	int index=0;

	memset (&input, 0, sizeof (input));
	input.index = index;

	if (-1 == ioctl (video_fd, VIDIOC_ENUMINPUT, &input)) {
	        fatal ("VIDIOC_ENUMINPUT");
	}

	printf ("Current input: %s\n", input.name);

	memset (&input, 0, sizeof (input));

	if (-1 == ioctl (video_fd, VIDIOC_G_INPUT, &input.index)) {
		fatal ("VIDIOC_G_INPUT");
	}

	if (-1 == ioctl (video_fd, VIDIOC_ENUMINPUT, &input)) {
		fatal ("VIDIOC_ENUM_INPUT");
	}

	printf ("Current input %s supports:\n", input.name);

	// Query capabilities
	if (-1 == ioctl (video_fd, VIDIOC_QUERYCAP, &caps)) {
		fatal ("VIDIOC_QUERYCAP");
	}
	printf ("capabilities = 0x%04x:\n", caps.capabilities);
	if (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) printf("\tV4L2_CAP_VIDEO_CAPTURE\n");
	if (caps.capabilities & V4L2_CAP_VIDEO_OUTPUT) printf("\tV4L2_CAP_VIDEO_OUTPUT\n");
	if (caps.capabilities & V4L2_CAP_VIDEO_OVERLAY) printf("\tV4L2_CAP_VIDEO_OVERLAY\n");

	if (caps.capabilities & V4L2_CAP_VBI_CAPTURE) printf("\tV4L2_CAP_VBI_CAPTURE\n");
	if (caps.capabilities & V4L2_CAP_VBI_OUTPUT) printf("\tV4L2_CAP_VBI_OUTPUT\n");
	if (caps.capabilities & V4L2_CAP_RDS_CAPTURE) printf("\tV4L2_CAP_RDS_CAPTURE\n");
	if (caps.capabilities & V4L2_CAP_TUNER) printf("\tV4L2_CAP_TUNER\n");

	if (caps.capabilities & V4L2_CAP_AUDIO) printf("\tV4L2_CAP_AUDIO\n");

	if (caps.capabilities & V4L2_CAP_READWRITE) printf("\tV4L2_CAP_READWRITE\n");
	if (caps.capabilities & V4L2_CAP_ASYNCIO) printf("\tV4L2_CAP_ASYNCIO\n");
	if (caps.capabilities & V4L2_CAP_STREAMING) printf("\tV4L2_CAP_STREAMING\n");

	formatdesc.index=0;
	formatdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	printf("Capture formats:\n");
	while (-1 != ioctl (video_fd, VIDIOC_ENUM_FMT, &formatdesc)) {
		printf("\t%s\n", formatdesc.description);
		formatdesc.index++;
	}

	if (-1 == ioctl (video_fd, VIDIOC_G_FMT, &format)) {
		fatal ("VIDIOC_G_FMT");
	}
	printf("current format(type=%d)\n", format.type);
	printf("width=%d height=%d image size=%d\n", format.fmt.pix.width, format.fmt.pix.height, format.fmt.pix.sizeimage);
}

void usage(int argc, char **argv)
{
	fprintf(stderr, "Usage %s [OPTIONS] COMMAND\n", argv[0]);
	fprintf(stderr, "Valid commands:\n\tcapture\n\tinfo\n\tdisplay\n");
	fprintf(stderr, "options:\n\t-o output-file\n\t-s size (0=QCIF, 1=CIF)\n\t-m mirror\n\t-f flip\n");
	exit(-1);
}

int main(int argc, char **argv) 
{

	int opt;
	int size = 0;
	int mirror=0;
	int flip=0;
	char *command=NULL;
	char *output=NULL;

	do {
		opt = getopt_long(argc, argv, options, options_long, NULL);
		switch (opt) {
			case '?': 
				usage(argc, argv);
				break;
			case 's':
				size = atoi(optarg);
				if ((size > 1) || (size < 0)) usage(argc, argv);
				break;
			case 'f':
				flip = atoi(optarg);
				if ((flip > 1) || (flip < 0)) usage(argc, argv);
				break;
			case 'm':
				mirror = atoi(optarg);
				if ((mirror > 1) || (mirror < 0)) usage(argc, argv);
				break;
			case 'o':
				output = malloc(strlen(optarg) + 1);
				strcpy(output, optarg);
				break;
			case -1:
				break;
			default:
				usage(argc, argv);
				break;

		}
	} while (opt != -1);

	if (optind < argc) {
		command = argv[optind];
	}
	if (command == NULL) usage(argc, argv);

	video_fd = open("/dev/video", O_RDWR);
	if (video_fd == -1) {
		fatal("open video dev");
	}

	setsize(size);
	setorientation(mirror, flip);
	if (!strcmp(command, "capture")) {
		capture(output);
	} else if (!strcmp(command, "info")) {
		info();
	} else if (!strcmp(command, "display")) {
		display();
	} else {
		usage(argc, argv);
	}
	close(video_fd);
	return 0;
}

---

From: "Piotr Michniewski" <[email-masked]>
Date: Sun, 17 Sep 2006 20:06:11 +0200

Well, I've modified your driver to compile using 2.6.17 (one liner)
but it doesn't do anything. It loads fine, but there's no device or
anything. Would your driver print anything if perhaps I broke my
camera by plugging it wrong ? Or debug messages aren't implememted yet
?

-- 
Piotr Michniewski

---

From: Matt Callow <[email-masked]>
Date: Sun, 17 Sep 2006 20:58:30 +0100

> Well, I've modified your driver to compile using 2.6.17 (one liner)
OK. What did you have to change?
> camera by plugging it wrong ? Or debug messages aren't implememted yet
I'm not using udev, so I created the device myself.
crw-r--r--    1 root     root      81,   0 Apr 17  2006 /dev/video

I see the following dmesg output:

Linux video capture interface: v1.00
OMAP Camera driver initialzing
omap1510cam_init
cam_iobase FEFB6800
omap1510_cam_init
omap1510cam_enable
exit omap1510cam_enable
exit omap1510cam_init
omap1510cam_set_xclk requested=21000000 actual=24000000
OV6650 sensor chip
6650sensor_try_format sizeimage=202752
omap-camera: OMAP1510 Camera Parallel interface with OV6650 sensor
ov6650sensor_calc_xclk
ov6650sensor_calc_xclk returning 24000000
omap1510cam_init_dma
omap-camera: registered device video0 [v4l2]

Are you sure you enabled the camera in the kernel config? The patch
doesn't do it for you?

Matt

---

From: "Piotr Michniewski" <[email-masked]>
Date: Sun, 17 Sep 2006 22:43:03 +0200

> OK. What did you have to change?

--- sensor_ov6650.c     2006-09-17 12:45:10.000000000 +0200
+++ sensor_ov6650.c.mod 2006-09-17 12:53:35.000000000 +0200
@@ -874,7 +874,7 @@
        return (void *)sensor;
 }

-struct camera_sensor camera_sensor_if = {
+struct omap_camera_sensor camera_sensor_if = {
        .version        = 0x01,
        .name           = "OV6650",
        .init           = ov6650sensor_init,

-----------------------

Also the Makefile needed some tuning, but I think it's ok:

# Makefile for OMAP1/2 camera driver

obj-$(CONFIG_VIDEO_OMAP_CAMERA) += omapcamera.o
obj-$(CONFIG_VIDEO_CAMERA_SENSOR_OV9640) += sensor_ov9640.o
obj-$(CONFIG_VIDEO_CAMERA_SENSOR_OV6650) += sensor_ov6650.o

objs-y$(CONFIG_MACH_AMS_DELTA) += omap1510cam.o
objs-y$(CONFIG_ARCH_OMAP16XX) += omap16xxcam.o camera_core.o
objs-y$(CONFIG_MACH_OMAP_H3) += h3_sensor_power.o
objs-y$(CONFIG_MACH_OMAP_H4) += h4_sensor_power.o

omapcamera-objs := $(objs-yy)

EXTRA_CFLAGS = -I$(src)/..

-----------------------

> crw-r--r--    1 root     root      81,   0 Apr 17  2006 /dev/video

When I load the Virtual Video Driver, the device is created
automatically, so that's not the problem.

> omap-camera: registered device video0 [v4l2]

I don't get anything...

> Are you sure you enabled the camera in the kernel config? The patch

I'm not using defconfig, but:

CONFIG_VIDEO_DEV=m
CONFIG_VIDEO_V4L1=y
CONFIG_VIDEO_V4L1_COMPAT=y
CONFIG_VIDEO_V4L2=y

CONFIG_VIDEO_OMAP_CAMERA=m
# CONFIG_VIDEO_CAMERA_SENSOR_OV9640 is not set
CONFIG_VIDEO_CAMERA_SENSOR_OV6650=y

-- 
Piotr Michniewski

---

From: Matt Callow <[email-masked]>
Date: Sun, 17 Sep 2006 22:38:36 +0100

> +++ sensor_ov6650.c.mod 2006-09-17 12:53:35.000000000 +0200
Ah, the driver won't work as a module (without some other hacks). I have:
#
# Multimedia devices
#
CONFIG_VIDEO_DEV=y

#
# Video For Linux
#

#
# Video Adapters
#
# CONFIG_VIDEO_ADV_DEBUG is not set
# CONFIG_VIDEO_CPIA is not set
# CONFIG_VIDEO_SAA5246A is not set
# CONFIG_VIDEO_SAA5249 is not set
# CONFIG_TUNER_3036 is not set
# CONFIG_VIDEO_EM28XX is not set
# CONFIG_VIDEO_OVCAMCHIP is not set
# CONFIG_VIDEO_AUDIO_DECODER is not set
# CONFIG_VIDEO_DECODER is not set
CONFIG_VIDEO_OMAP_CAMERA=y
# CONFIG_VIDEO_CAMERA_SENSOR_OV9640 is not set
CONFIG_VIDEO_CAMERA_SENSOR_OV6650=y

Matt

---

From: "Piotr Michniewski" <[email-masked]>
Date: Mon, 18 Sep 2006 00:03:16 +0200

> Ah, the driver won't work as a module (without some other hacks).

Oh, and I see that it requires that i2c also isn't a module. (good to
see that the E3 actually has I2C connected to the board).

-- 
Piotr Michniewski

---

From: "Piotr Michniewski" <[email-masked]>
Date: Mon, 18 Sep 2006 02:30:37 +0200

> see that the E3 actually has I2C connected to the board).

I'm so dumb... The Makefile should go like this:

objs-y$(CONFIG_MACH_AMS_DELTA) += omap1510cam.o camera_core.o
objs-y$(CONFIG_ARCH_OMAP16XX) += omap16xxcam.o camera_core.o
objs-y$(CONFIG_MACH_OMAP_H3) += h3_sensor_power.o
objs-y$(CONFIG_MACH_OMAP_H4) += h4_sensor_power.o

-------------

I can now confirm that the driver works almost flawlessly (not
counting color problems :)

-- 
Piotr Michniewski

---

From: Matt Callow <[email-masked]>
Date: Mon, 18 Sep 2006 11:52:59 +0100

> counting color problems :)
Excellent - thanks for trying it out

Matt

---

From: "Piotr Michniewski" <[email-masked]>
Date: Mon, 18 Sep 2006 13:32:49 +0200

> Excellent - thanks for trying it out

The coloring issue is a bit strange. The colors are wrong in both CIF
and QCIF. In CIF they are too orange, and in QCIF - greenish/blueish.
Also the brightness is a bit too high, but I'm sure you'll be able to
sort it out soon :)

-- 
Piotr Michniewski