public inbox for linux-media@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH][RFC] Add support for the ALi m5602 usb bridge webcam
@ 2008-09-18  7:02 Erik Andrén
  2008-09-19  4:54 ` Hans de Goede
  0 siblings, 1 reply; 3+ messages in thread
From: Erik Andrén @ 2008-09-18  7:02 UTC (permalink / raw)
  To: video4linux-list; +Cc: m560x-driver-devel

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

Hi,
I'm proud to announce the following patch adding support for the ALi m5602
usb bridge connected to several different sensors.
This driver has been brewing for a long time in the m560x.sf.net project and
exists due to the hard work of many persons.

libv4l is needed in order to gain support for multiple pixelformats.

The patch should apply against the latest v4l-dvb hg tree.

Thanks for any feedback,
Erik

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: add_ali_m5602.patch --]
[-- Type: text/x-diff; name=add_ali_m5602.patch, Size: 201199 bytes --]

diff -r e5ca4534b543 linux/drivers/media/video/Kconfig
--- a/linux/drivers/media/video/Kconfig	Tue Sep 09 08:29:56 2008 -0700
+++ b/linux/drivers/media/video/Kconfig	Wed Sep 17 07:53:53 2008 +0200
@@ -852,6 +852,8 @@
 
 source "drivers/media/video/et61x251/Kconfig"
 
+source "drivers/media/video/m5602/Kconfig"
+
 config VIDEO_OVCAMCHIP
 	tristate "OmniVision Camera Chip support"
 	depends on I2C && VIDEO_V4L1
diff -r e5ca4534b543 linux/drivers/media/video/Makefile
--- a/linux/drivers/media/video/Makefile	Tue Sep 09 08:29:56 2008 -0700
+++ b/linux/drivers/media/video/Makefile	Wed Sep 17 07:53:53 2008 +0200
@@ -119,6 +119,8 @@
 obj-$(CONFIG_USB_PWC)           += pwc/
 obj-$(CONFIG_USB_ZC0301)        += zc0301/
 obj-$(CONFIG_USB_GSPCA)         += gspca/
+obj-$(CONFIG_USB_M5602) 	+= m5602/
+
 
 obj-$(CONFIG_USB_IBMCAM)        += usbvideo/
 obj-$(CONFIG_USB_KONICAWC)      += usbvideo/
diff -r e5ca4534b543 linux/drivers/media/video/m5602/Kconfig
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/Kconfig	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,9 @@
+config USB_M5602
+	tristate "USB ALi m5602 Webcam support"
+	depends on VIDEO_V4L2
+	help
+	  Say Y here if you want support for cameras based on the ALi m5602 connected
+	  to various image sensors
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called m5602.
diff -r e5ca4534b543 linux/drivers/media/video/m5602/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/Makefile	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,11 @@
+m5602-objs := m5602_core.o \
+	      m5602_v4l2.o \
+	      m5602_ov9650.o \
+	      m5602_s5k83a.o \
+	      m5602_mt9m111.o \
+	      m5602_s5k4aa.o \
+	      m5602_po1030.o
+
+obj-$(CONFIG_USB_M5602) += m5602.o
+
+EXTRA_CFLAGS = -Idrivers/media/video
diff -r e5ca4534b543 linux/drivers/media/video/m5602/README
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/README	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,11 @@
+GENERAL STUFF:
+This branch of the m560x project supports the ALi m5602 bridge connected
+to the following supported sensors: OmniVision OV9650, Samsung s5k83a, s5k4aa, Micron mt9m111 or Pixel plus PO1030.
+
+This driver mimics the windows equivalent drivers, which have a braindead implementation sending bayer-encoded frames at VGA resolution.
+
+In a perfect world we should be able to reprogram the m5602 and the connected sensor in hardware instead.
+
+Anyway, have fun and report any bugs to m560x-driver-devel@lists.sourceforge.net
+
+/Erik
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_bridge.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_bridge.h	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,246 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_BRIDGE_H_
+#define M5602_BRIDGE_H_
+
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/mm.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+/*****************************************************************************/
+
+/* Debug parameters */
+#define DBG_INIT 0x1
+#define DBG_PROBE 0x2
+#define DBG_V4L2 0x4
+#define DBG_TRACE 0x8
+#define DBG_DATA 0x10
+#define DBG_V4L2_CID 0x20
+
+#define PDEBUG(level, fmt, args...) \
+	do { \
+		if (debug & level)     \
+			info("[%s:%d] " fmt, __func__, __LINE__ , \
+			## args); \
+	} while (0)
+
+/*****************************************************************************/
+
+#define M5602_XB_SENSOR_TYPE 0x00
+#define M5602_XB_SENSOR_CTRL 0x01
+#define M5602_XB_LINE_OF_FRAME_H 0x02
+#define M5602_XB_LINE_OF_FRAME_L 0x03
+#define M5602_XB_PIX_OF_LINE_H 0x04
+#define M5602_XB_PIX_OF_LINE_L 0x05
+#define M5602_XB_VSYNC_PARA 0x06
+#define M5602_XB_HSYNC_PARA 0x07
+#define M5602_XB_TEST_MODE_1 0x08
+#define M5602_XB_TEST_MODE_2 0x09
+#define M5602_XB_SIG_INI 0x0a
+#define M5602_XB_DS_PARA 0x0e
+#define M5602_XB_TRIG_PARA 0x0f
+#define M5602_XB_CLK_PD 0x10
+#define M5602_XB_MCU_CLK_CTRL 0x12
+#define M5602_XB_MCU_CLK_DIV 0x13
+#define M5602_XB_SEN_CLK_CTRL 0x14
+#define M5602_XB_SEN_CLK_DIV 0x15
+#define M5602_XB_AUD_CLK_CTRL 0x16
+#define M5602_XB_AUD_CLK_DIV 0x17
+#define M5602_XB_DEVCTR1 0x41
+#define M5602_XB_EPSETR0 0x42
+#define M5602_XB_EPAFCTR 0x47
+#define M5602_XB_EPBFCTR 0x49
+#define M5602_XB_EPEFCTR 0x4f
+#define M5602_XB_TEST_REG 0x53
+#define M5602_XB_ALT2SIZE 0x54
+#define M5602_XB_ALT3SIZE 0x55
+#define M5602_XB_OBSFRAME 0x56
+#define M5602_XB_PWR_CTL 0x59
+#define M5602_XB_ADC_CTRL 0x60
+#define M5602_XB_ADC_DATA 0x61
+#define M5602_XB_MISC_CTRL 0x62
+#define M5602_XB_SNAPSHOT 0x63
+#define M5602_XB_SCRATCH_1 0x64
+#define M5602_XB_SCRATCH_2 0x65
+#define M5602_XB_SCRATCH_3 0x66
+#define M5602_XB_SCRATCH_4 0x67
+#define M5602_XB_I2C_CTRL 0x68
+#define M5602_XB_I2C_CLK_DIV 0x69
+#define M5602_XB_I2C_DEV_ADDR 0x6a
+#define M5602_XB_I2C_REG_ADDR 0x6b
+#define M5602_XB_I2C_DATA 0x6c
+#define M5602_XB_I2C_STATUS 0x6d
+#define M5602_XB_GPIO_DAT_H 0x70
+#define M5602_XB_GPIO_DAT_L 0x71
+#define M5602_XB_GPIO_DIR_H 0x72
+#define M5602_XB_GPIO_DIR_L 0x73
+#define M5602_XB_GPIO_EN_H 0x74
+#define M5602_XB_GPIO_EN_L 0x75
+#define M5602_XB_GPIO_DAT 0x76
+#define M5602_XB_GPIO_DIR 0x77
+#define M5602_XB_MISC_CTL 0x70
+
+#define I2C_BUSY 0x80
+
+/*****************************************************************************/
+
+/* Driver info */
+#define DRIVER_AUTHOR "ALi m5602 Linux Driver Project"
+#define DRIVER_DESC "ALi m5602 webcam driver"
+
+#define M5602_MAX_FRAMES	32
+#define M5602_URBS		2
+#define M5602_ISOC_PACKETS	14
+
+#define M5602_URB_TIMEOUT	msecs_to_jiffies(2 * M5602_ISOC_PACKETS)
+#define M5602_URB_MSG_TIMEOUT   5000
+#define M5602_FRAME_TIMEOUT	2
+
+/*****************************************************************************/
+
+static DECLARE_RWSEM(m5602_disconnect);
+
+/* A skeleton used for sending messages to the m5602 bridge */
+static const unsigned char bridge_urb_skeleton[] = {
+	0x13, 0x00, 0x81, 0x00
+};
+
+/* A skeleton used for sending messages to the sensor */
+static const unsigned char sensor_urb_skeleton[] = {
+	0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06,
+	0x23, M5602_XB_MISC_CTRL, 0x81, 0x80,
+	0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00,
+	0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00,
+	0x13, M5602_XB_I2C_DATA, 0x81, 0x00,
+	0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
+};
+
+/* Enumerates all the frame states */
+enum m5602_frame_state {
+	FRAME_UNUSED,   /* Unused (no MCAPTURE) */
+	FRAME_QUEUED,   /* Ready to start grabbing */
+	FRAME_GRABBING, /* In the process of being grabbed into */
+	FRAME_DONE,     /* Finished grabbing, but not been synced yet */
+	FRAME_ERROR     /* Something bad happened while processing */
+};
+
+/* Enumerates all the different stream states */
+enum m5602_stream_state {
+	STREAM_OFF,
+	STREAM_INTERRUPT,
+	STREAM_ON
+};
+
+enum m5602_dev_state {
+	DEV_INITIALIZED = 0x1,
+	DEV_DISCONNECTED = 0x2,
+	DEV_MISCONFIGURED = 0x4
+};
+
+/* Frame structure */
+struct m5602_frame_t {
+	struct list_head frame;
+
+	unsigned char *rawdata;
+	int framenum;
+	struct v4l2_buffer buf;
+	enum m5602_frame_state state;
+	unsigned long vma_use_count;
+};
+
+struct m5602_camera {
+	/* The name of the m5602 camera */
+	char *name;
+
+	/* A pointer to the currently connected sensor */
+	struct m5602_sensor *sensor;
+
+	struct device dev;
+
+	/* the v4l video device for this device */
+	struct video_device *vdev;
+
+	/* the usb device for this camera */
+	struct usb_device *udev;
+
+	/* the interface for this usb device */
+	struct usb_interface *interface;
+
+	/* A buffer used to store usb messages */
+	u8 *control_buffer;
+
+	/* the size of the INT receive buffer */
+	size_t int_in_size;
+
+	/* the address of the INT in endpoint */
+	__u8 int_in_endpointAddr;
+
+	/* A pointer array of URBs */
+	struct urb *urb[M5602_URBS];
+
+	/* the buffer to receive ISOC data */
+	unsigned char *isoc_in_buffer[M5602_URBS];
+
+	/* the size of the ISOC receive buffer */
+	size_t isoc_in_size;
+
+	/* the address of the ISOC in endpoint */
+	__u8 isoc_in_endpointAddr;
+
+	/* Pointer to the current frame and an array of frames */
+	struct m5602_frame_t *frame_current, frame[M5602_MAX_FRAMES];
+
+	/* The current frame count and the number of allocated frame buffers */
+	u32 frame_count, nbuffers;
+
+	/* Two linked lists, one for free frames,
+	one for finished frames and one for frames awaiting post processing */
+	struct list_head inqueue, outqueue;
+
+	enum v4l2_memory io;
+	enum m5602_stream_state stream;
+	enum m5602_dev_state state;
+
+	/* The number of current users */
+	u8 users;
+
+	/* Various mutexes, used throughout the driver */
+	struct mutex dev_mutex, fileop_mutex;
+
+	/* A lock to protect the various linked lists */
+	spinlock_t queue_lock;
+
+	struct kref kref;
+
+	/* Various waitqueues used throughout the driver */
+	wait_queue_head_t open, wait_frame, wait_stream;
+};
+
+int m5602_read_bridge(
+	struct m5602_camera *cam, u8 address, u8 *i2c_data);
+
+int m5602_write_bridge(
+	struct m5602_camera *cam, u8 address, u8 i2c_data);
+
+#endif
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_core.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_core.c	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,468 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include "m5602_v4l2.h"
+
+#include "m5602_ov9650.h"
+#include "m5602_s5k83a.h"
+#include "m5602_s5k4aa.h"
+#include "m5602_mt9m111.h"
+#include "m5602_po1030.h"
+
+/* Kernel module parameters */
+int force_sensor = 0;
+int dump_bridge;
+int dump_sensor;
+unsigned int debug;
+
+static struct usb_device_id m5602_table[] = {
+	{USB_DEVICE(0x0402, 0x5602)},
+	{}
+};
+
+static char *m5602_name[2] = {
+	"BisonCam",
+	NULL
+};
+
+MODULE_DEVICE_TABLE(usb, m5602_table);
+
+/* Reads a byte from the m5602 */
+int m5602_read_bridge(struct m5602_camera *cam, u8 address, u8 *i2c_data)
+{
+	int err;
+	err = usb_control_msg(cam->udev, usb_rcvctrlpipe(cam->udev, 0),
+			      0x04, 0xc0, 0x14,
+			      0x8100 + address, cam->control_buffer,
+			      1, M5602_URB_MSG_TIMEOUT);
+	*i2c_data = cam->control_buffer[0];
+
+	PDEBUG(DBG_TRACE, "Reading bridge register 0x%x containing 0x%x",
+	       address, *i2c_data);
+
+	/* usb_control_msg(...) returns the number of bytes sent upon success,
+	mask that and return zero upon success instead*/
+	return (err < 0) ? err : 0;
+}
+
+/* Writes a byte to to the m5602 */
+int m5602_write_bridge(struct m5602_camera *cam, u8 address, u8 i2c_data)
+{
+	int err;
+	PDEBUG(DBG_TRACE, "Writing bridge register 0x%x with 0x%x",
+	       address, i2c_data);
+
+	memcpy(cam->control_buffer, bridge_urb_skeleton,
+	       sizeof(bridge_urb_skeleton));
+	cam->control_buffer[1] = address;
+	cam->control_buffer[3] = i2c_data;
+
+	err = usb_control_msg(cam->udev, usb_sndctrlpipe(cam->udev, 0),
+				0x04, 0x40, 0x19,
+				0x0000, cam->control_buffer,
+				4, M5602_URB_MSG_TIMEOUT);
+
+	/* usb_control_msg(...) returns the number of bytes sent upon success,
+	   mask that and return zero upon success instead */
+	return (err < 0) ? err : 0;
+}
+
+/* Dump all the registers of the m5602 bridge,
+   unfortunately this breaks the camera until it's power cycled */
+static void m5602_dump_bridge(struct m5602_camera *cam)
+{
+	int i;
+	for (i = 0; i < 0x80; i++) {
+		unsigned char val = 0;
+		m5602_read_bridge(cam, i, &val);
+		info("ALi m5602 address 0x%x contains 0x%x", i, val);
+	}
+	info("Warning: The camera probably won't work until it's power cycled");
+}
+
+int m5602_initialize_camera(struct m5602_camera *cam)
+{
+	PDEBUG(DBG_INIT, "m5602_initialize_camera");
+
+	if (!(cam->state & DEV_INITIALIZED)) {
+		init_waitqueue_head(&cam->open);
+
+		mutex_init(&cam->dev_mutex);
+		mutex_init(&cam->fileop_mutex);
+
+		spin_lock_init(&cam->queue_lock);
+
+		init_waitqueue_head(&cam->wait_frame);
+		init_waitqueue_head(&cam->wait_stream);
+
+		cam->state |= DEV_INITIALIZED;
+	}
+
+	if (dump_bridge)
+		m5602_dump_bridge(cam);
+
+	/* Now we should be able to probe for the sensor */
+	if (m5602_probe_sensor(cam))
+		goto init_fail;
+
+	/* Ok, sensor is probed and attached, now init it */
+	if (cam->sensor->init(cam))
+		goto init_fail;
+
+	return 0;
+
+init_fail:
+	info("Initialization of the ALi m5602 webcam failed");
+	return -ENODEV;
+}
+
+static void m5602_release_resources(struct m5602_camera *cam)
+{
+	info("Video device %d releases its resources", cam->vdev->minor);
+	video_set_drvdata(cam->vdev, NULL);
+	video_unregister_device(cam->vdev);
+	kfree(cam->control_buffer);
+}
+
+static struct file_operations v4l_m5602_fops = {
+	.owner		= THIS_MODULE,
+	.open		= m5602_v4l_open,
+	.release  	= m5602_v4l_release,
+	.read		= NULL,
+	.poll		= NULL,
+	.mmap		= m5602_v4l_mmap,
+	.ioctl		= m5602_v4l_ioctl,
+	.compat_ioctl 	= v4l_compat_ioctl32,
+	.llseek   	= no_llseek
+};
+
+int m5602_probe_sensor(struct m5602_camera *cam)
+{
+	/* Try the mt9m111 sensor */
+	cam->sensor = &mt9m111;
+	if (!cam->sensor->probe(cam))
+		return 0;
+
+	/* Try the s5k4aa */
+	cam->sensor = &s5k4aa;
+	if (!cam->sensor->probe(cam))
+		return 0;
+
+	/* Try the ov9650 */
+	cam->sensor = &ov9650;
+	if (!cam->sensor->probe(cam))
+		return 0;
+
+	/* Try the s5k83a */
+	cam->sensor = &s5k83a;
+	if (!cam->sensor->probe(cam))
+		return 0;
+
+	/* Try the po1030 */
+	cam->sensor = &po1030;
+	if (!cam->sensor->probe(cam))
+		return 0;
+
+	/* More sensor probe function goes here */
+	info("Failed to find a sensor");
+	cam->sensor = NULL;
+	return -ENODEV;
+}
+
+/* We only have one interface in our webcam. */
+int m5602_usb_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct m5602_camera *cam;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	char *camera_name = NULL;
+	size_t buffer_size;
+	int i, error = 0;
+
+	for (i = 0; i < ARRAY_SIZE(m5602_table); i++) {
+		if ((le16_to_cpu(udev->descriptor.idVendor)
+				   == m5602_table[i].idVendor) &&
+		    (le16_to_cpu(udev->descriptor.idProduct)
+				   == m5602_table[i].idProduct)) {
+			camera_name = m5602_name[i];
+			info("%s webcam found", m5602_name[i]);
+		}
+	}
+
+	if (!camera_name) {
+		err("Vendor/Product ID doesn't match");
+		return -ENODEV;
+	}
+
+	cam = kzalloc(sizeof(struct m5602_camera), GFP_KERNEL);
+	if (!cam)
+		return -ENOMEM;
+
+	PDEBUG(DBG_TRACE, "cam 0x%p", cam);
+
+	cam->udev = udev;
+	memcpy(&cam->dev, &udev->dev, sizeof(struct device));
+
+	cam->name = camera_name;
+	cam->interface = intf;
+
+	cam->control_buffer = kmalloc(256, GFP_KERNEL);
+
+	if (!cam->control_buffer) {
+		err("kmalloc() failed");
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	PDEBUG(DBG_PROBE, "Num altsettings: %d", intf->num_altsetting);
+
+	usb_set_interface(cam->udev, 0, 2);
+
+	iface_desc = intf->cur_altsetting;
+
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (!cam->int_in_endpointAddr &&
+			((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+				   == USB_DIR_IN) &&
+			((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				   == USB_ENDPOINT_XFER_INT)) {
+
+			/* we found an int in endpoint */
+			buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+			cam->int_in_size = buffer_size;
+			cam->int_in_endpointAddr = endpoint->bEndpointAddress;
+			PDEBUG(DBG_V4L2, "INTR Endpoint: 0x%x, "
+					 "wMaxPacketSize: 0x%zx",
+					 cam->int_in_endpointAddr,
+					 cam->int_in_size);
+		}
+
+		if (!cam->isoc_in_endpointAddr &&
+			((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+				   == USB_DIR_IN) &&
+			((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				   == USB_ENDPOINT_XFER_ISOC)) {
+
+			/* we found an isoc in endpoint */
+			buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+			cam->isoc_in_size = (buffer_size & 0x7ff) * 3;
+			cam->isoc_in_endpointAddr = endpoint->bEndpointAddress;
+
+			PDEBUG(DBG_V4L2, "ISOC Endpoint: 0x%x, "
+					 "wMaxPacketSize: 0x%zx",
+					 cam->isoc_in_endpointAddr,
+					 cam->isoc_in_size);
+		}
+	}
+
+	if (!(cam->int_in_endpointAddr && cam->isoc_in_endpointAddr)) {
+		err("Could not find both int-in and isoc-in endpoints");
+		error = -ENODEV;
+		goto fail;
+	}
+
+	cam->vdev = video_device_alloc();
+	if (!cam->vdev) {
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	error = m5602_initialize_camera(cam);
+	if (error)
+		goto fail;
+
+	strcpy(cam->vdev->name, "BisonCam");
+	cam->vdev->parent   = &udev->dev;
+	cam->vdev->fops     = &v4l_m5602_fops;
+	cam->vdev->release  = video_device_release;
+	cam->vdev->minor    = -1;
+
+	video_set_drvdata(cam->vdev, cam);
+
+	mutex_lock(&cam->dev_mutex);
+
+	error = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1);
+	if (error) {
+		err("video_register_device failed");
+		mutex_unlock(&cam->dev_mutex);
+		goto fail;
+	}
+
+	info("ALi m5602 webcam driver is now controlling video device %d",
+	     cam->vdev->minor);
+
+	usb_set_intfdata(intf, cam);
+
+	mutex_unlock(&cam->dev_mutex);
+	return 0;
+
+fail:
+	if (cam) {
+		kfree(cam->control_buffer);
+		cam->control_buffer = NULL;
+		if (cam->vdev)
+			video_device_release(cam->vdev);
+
+		kfree(cam);
+	}
+	return error;
+}
+
+static int m5602_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct m5602_camera *cam = usb_get_intfdata(intf);
+	int err = 0;
+
+	PDEBUG(DBG_TRACE, "Suspending ALi m5602 webcam");
+
+	if (cam->stream == STREAM_ON) {
+		PDEBUG(DBG_TRACE, "Halting stream");
+		err = m5602_stop_transfer(cam);
+	}
+
+	return err;
+}
+
+static int m5602_resume(struct usb_interface *intf)
+{
+	struct m5602_camera *cam = usb_get_intfdata(intf);
+	int err = 0;
+
+	PDEBUG(DBG_TRACE, "Resuming ALi m5602 webcam");
+
+	/* Re-run the init sequence */
+	cam->sensor->init(cam);
+
+	if (cam->stream == STREAM_ON) {
+		PDEBUG(DBG_TRACE, "Restarting stream");
+		err = m5602_start_transfer(cam);
+	}
+
+	return err;
+}
+
+static void m5602_usb_disconnect(struct usb_interface *intf)
+{
+	struct m5602_camera *cam = usb_get_intfdata(intf);
+
+	if (!cam)
+		return;
+
+	down_write(&m5602_disconnect);
+	mutex_lock(&cam->dev_mutex);
+
+	cam->state |= DEV_DISCONNECTED;
+
+	if (cam->users) {
+		PDEBUG(DBG_TRACE, "Device /dev/video%d is still open",
+		       cam->vdev->minor);
+
+		cam->state |= DEV_MISCONFIGURED;
+		m5602_stop_transfer(cam);
+		cam->state |= DEV_DISCONNECTED;
+
+		wake_up_interruptible(&cam->wait_frame);
+		wake_up_interruptible(&cam->wait_stream);
+	}
+
+	mutex_unlock(&cam->dev_mutex);
+
+	wake_up_interruptible_all(&cam->open);
+
+	PDEBUG(DBG_TRACE, "Waiting for all applications to "
+	       "release device /dev/video%d",
+	       cam->vdev->minor);
+
+	wait_event_interruptible_exclusive(cam->open, (cam->users == 0));
+
+	if (!cam->users) {
+		PDEBUG(DBG_TRACE, "All applications have "
+		       "released device /dev/video%d",
+		       cam->vdev->minor);
+
+		cam->sensor->power_down(cam);
+		m5602_release_resources(cam);
+		cam->state |= DEV_DISCONNECTED;
+
+		PDEBUG(DBG_TRACE, "Freeing cam struct");
+		kfree(cam);
+
+		info("ALi m5602 webcam disconnected");
+	}
+	up_write(&m5602_disconnect);
+}
+
+static struct usb_driver m5602_usb_driver = {
+	.name 	    = "m5602",
+	.probe	    = m5602_usb_probe,
+	.disconnect = m5602_usb_disconnect,
+	.id_table   = m5602_table,
+	.suspend    = m5602_suspend,
+	.resume     = m5602_resume
+};
+
+static int __init usb_m5602_init(void)
+{
+	int result;
+
+	info("ALi m5602 webcam driver startup");
+
+	if (debug)
+		info("debug level defined to 0x%x", debug);
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&m5602_usb_driver);
+
+	if (result)
+		err("usb_register failed with error %d", result);
+
+	return result;
+}
+
+static void __exit usb_m5602_exit(void)
+{
+	info("ALi m5602 webcam driver shutdown");
+
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&m5602_usb_driver);
+}
+
+module_init(usb_m5602_init);
+module_exit(usb_m5602_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "toggles debug on/off");
+
+module_param(force_sensor, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(force_sensor,
+		 "force detection of sensor, "
+		 "1 = OV9650, 2 = S5K83A, 3 = S5K4AA, 4 = MT9M111, 5 = PO1030");
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all usb sensor registers "
+		"at startup providing a sensor is found");
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_mt9m111.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_mt9m111.c	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,304 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include "m5602_mt9m111.h"
+
+int mt9m111_probe(struct m5602_camera *cam)
+{
+	u8 data[2] = {0x00, 0x00};
+	int i;
+
+	if (force_sensor) {
+		if (force_sensor == MT9M111_SENSOR) {
+			info("Forcing a %s sensor", mt9m111.name);
+			return 0;
+		}
+		/* If we want to force another sensor, don't try to probe this
+		 * one */
+		return -ENODEV;
+	}
+
+	info("Probing for a mt9m111 sensor");
+
+	/* Do the preinit */
+	for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
+		if (preinit_mt9m111[i][0] == BRIDGE) {
+			m5602_write_bridge(cam,
+				preinit_mt9m111[i][1],
+				preinit_mt9m111[i][2]);
+		} else {
+			data[0] = preinit_mt9m111[i][2];
+			data[1] = preinit_mt9m111[i][3];
+			mt9m111_write_sensor(cam,
+				preinit_mt9m111[i][1], data, 2);
+		}
+	}
+
+	if (mt9m111_read_sensor(cam, MT9M111_SC_CHIPVER, data, 2))
+		return -ENODEV;
+
+	if ((data[0] == 0x14) && (data[1] == 0x3a)) {
+		info("Detected a mt9m111 sensor");
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+int mt9m111_init(struct m5602_camera *cam)
+{
+	int i, err = 0;
+
+	/* Init the sensor */
+	for (i = 0; i < ARRAY_SIZE(init_mt9m111); i++) {
+		u8 data[2];
+
+		if (init_mt9m111[i][0] == BRIDGE) {
+			err = m5602_write_bridge(cam,
+				init_mt9m111[i][1],
+				init_mt9m111[i][2]);
+		} else {
+			data[0] = init_mt9m111[i][2];
+			data[1] = init_mt9m111[i][3];
+			err = mt9m111_write_sensor(cam,
+				init_mt9m111[i][1], data, 2);
+		}
+	}
+
+	if (dump_sensor)
+		mt9m111_dump_registers(cam);
+
+	return (err < 0) ? err : 0;
+}
+
+int mt9m111_power_down(struct m5602_camera *cam)
+{
+	return 0;
+}
+
+int mt9m111_get_ctrl(struct m5602_camera *cam, struct v4l2_control *ctrl)
+{
+	int tmp, err = 0;
+	u8 data[2] = {0x00, 0x00};
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		err = mt9m111_read_sensor(cam, MT9M111_SC_GLOBAL_GAIN, data, 2);
+		tmp = ((data[1] << 8) | data[0]);
+
+		ctrl->value = ((tmp & (1 << 10)) * 2) |
+			      ((tmp & (1 <<  9)) * 2) |
+			      ((tmp & (1 <<  8)) * 2) |
+			       (tmp & 0x7f);
+
+		PDEBUG(DBG_V4L2_CID, "Read gain %d", ctrl->value);
+		break;
+
+	case V4L2_CID_VFLIP:
+		err = mt9m111_read_sensor(cam,
+					  MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+		ctrl->value = data[0] & MT9M111_RMB_MIRROR_ROWS;
+		PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", ctrl->value);
+		break;
+
+	case V4L2_CID_HFLIP:
+		err = mt9m111_read_sensor(cam,
+					  MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+		ctrl->value = (data[0] & MT9M111_RMB_MIRROR_COLS) >> 1;
+		PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", ctrl->value);
+		break;
+
+	default:
+		PDEBUG(DBG_V4L2_CID, "Unknown V4L2 CID, id 0x%x", ctrl->value);
+		err = -EINVAL;
+	}
+
+	return (err < 0) ? err : 0;
+}
+
+int mt9m111_set_ctrl(struct m5602_camera *cam,
+		     const struct v4l2_control *ctrl)
+{
+	int tmp, err = 0;
+	u8 data[2] = {0x00, 0x00};
+
+	switch (ctrl->id) {
+	case V4L2_CID_VFLIP:
+		PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", ctrl->value);
+
+		/* Set the correct page map */
+		mt9m111_write_sensor(cam, MT9M111_PAGE_MAP, data, 2);
+
+		mt9m111_read_sensor(cam, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+		data[0] = (data[0] & 0xfe) | ctrl->value;
+		err = mt9m111_write_sensor(cam, MT9M111_SC_R_MODE_CONTEXT_B,
+					   data, 2);
+		break;
+
+	case V4L2_CID_HFLIP:
+		PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d", ctrl->value);
+
+		/* Set the correct page map */
+		mt9m111_write_sensor(cam, MT9M111_PAGE_MAP, data, 2);
+
+		mt9m111_read_sensor(cam,
+				    MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+
+		data[0] = (data[0] & 0xfd) | ((ctrl->value << 1) & 0x02);
+		err = mt9m111_write_sensor(cam, MT9M111_SC_R_MODE_CONTEXT_B,
+					   data, 2);
+		break;
+
+	case V4L2_CID_GAIN:
+		PDEBUG(DBG_V4L2_CID, "Set gain to %d", ctrl->value);
+
+		/* Set the correct page map */
+		mt9m111_write_sensor(cam, MT9M111_PAGE_MAP, data, 2);
+
+		if (ctrl->value >= INITIAL_MAX_GAIN * 2 * 2 * 2)
+			return -EINVAL;
+
+		if ((ctrl->value >= INITIAL_MAX_GAIN * 2 * 2) &&
+		    (ctrl->value < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2))
+			tmp = (1 << 10) | (ctrl->value << 9) |
+			      (ctrl->value << 8) | (ctrl->value / 8);
+		else if ((ctrl->value >= INITIAL_MAX_GAIN * 2) &&
+			 (ctrl->value < INITIAL_MAX_GAIN * 2 * 2))
+			tmp = (1 << 9) | (1 << 8) | (ctrl->value / 4);
+		else if ((ctrl->value >= INITIAL_MAX_GAIN) &&
+			 (ctrl->value < INITIAL_MAX_GAIN * 2))
+			tmp = (1 << 8) | (ctrl->value / 2);
+		else
+			tmp = ctrl->value;
+
+		data[1] = (tmp & 0xff00) >> 8;
+		data[0] = (tmp & 0xff);
+		PDEBUG(DBG_V4L2_CID, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
+		       data[1], data[0]);
+
+		err = mt9m111_write_sensor(cam,
+					   MT9M111_SC_GLOBAL_GAIN, data, 2);
+		break;
+
+	default:
+		PDEBUG(DBG_V4L2_CID, "Illegal V4L2 CID %d, value %d",
+			ctrl->id, ctrl->value);
+		err = -EINVAL;
+	}
+	return (err < 0) ? err : 0;
+}
+
+int mt9m111_read_sensor(struct m5602_camera *cam, const u8 address,
+			       u8 *i2c_data, const u8 len) {
+	int err, i;
+
+	do {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_STATUS, i2c_data);
+	} while ((*i2c_data & I2C_BUSY) && !err);
+
+	m5602_write_bridge(cam, M5602_XB_I2C_DEV_ADDR,
+			   cam->sensor->i2c_slave_id);
+	m5602_write_bridge(cam, M5602_XB_I2C_REG_ADDR, address);
+	m5602_write_bridge(cam, M5602_XB_I2C_CTRL, 0x1a);
+
+	for (i = 0; i < len; i++) {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+		PDEBUG(DBG_TRACE, "Reading sensor register "
+				"0x%x contains 0x%x ", address, *i2c_data);
+	}
+	return (err < 0) ? err : 0;
+}
+
+int mt9m111_write_sensor(struct m5602_camera *cam, const u8 address,
+				u8 *i2c_data, const u8 len)
+{
+	int err, i;
+	u8 *p;
+
+	/* No sensor with a data width larger
+	   than 16 bits has yet been seen, nor with 0 :p*/
+	if (len > 2 || !len)
+		return -EINVAL;
+
+	memcpy(cam->control_buffer, sensor_urb_skeleton,
+	       sizeof(sensor_urb_skeleton));
+
+	cam->control_buffer[11] = cam->sensor->i2c_slave_id;
+	cam->control_buffer[15] = address;
+
+	p = cam->control_buffer + 16;
+
+	/* Copy a four byte write sequence for each byte to be written to */
+	for (i = 0; i < len; i++) {
+		memcpy(p, sensor_urb_skeleton + 16, 4);
+		p[3] = i2c_data[i];
+		p += 4;
+		PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+		       address, i2c_data[i]);
+	}
+
+	/* Copy the tailer */
+	memcpy(p, sensor_urb_skeleton + 20, 4);
+
+	/* Set the total length */
+	p[3] = 0x10 + len;
+
+	err = usb_control_msg(cam->udev, usb_sndctrlpipe(cam->udev, 0),
+			      0x04, 0x40, 0x19,
+			      0x0000, cam->control_buffer,
+			      20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+	return (err < 0) ? err : 0;
+}
+
+void mt9m111_dump_registers(struct m5602_camera *cam)
+{
+	u8 address, value[2] = {0x00, 0x00};
+
+	info("Dumping the mt9m111 register state");
+
+	info("Dumping the mt9m111 sensor core registers");
+	value[1] = MT9M111_SENSOR_CORE;
+	mt9m111_write_sensor(cam, MT9M111_PAGE_MAP, value, 2);
+	for (address = 0; address < 0xff; address++) {
+		mt9m111_read_sensor(cam, address, value, 2);
+		info("register 0x%x contains 0x%x%x",
+		     address, value[0], value[1]);
+	}
+
+	info("Dumping the mt9m111 color pipeline registers");
+	value[1] = MT9M111_COLORPIPE;
+	mt9m111_write_sensor(cam, MT9M111_PAGE_MAP, value, 2);
+	for (address = 0; address < 0xff; address++) {
+		mt9m111_read_sensor(cam, address, value, 2);
+		info("register 0x%x contains 0x%x%x",
+		     address, value[0], value[1]);
+	}
+
+	info("Dumping the mt9m111 camera control registers");
+	value[1] = MT9M111_CAMERA_CONTROL;
+	mt9m111_write_sensor(cam, MT9M111_PAGE_MAP, value, 2);
+	for (address = 0; address < 0xff; address++) {
+		mt9m111_read_sensor(cam, address, value, 2);
+		info("register 0x%x contains 0x%x%x",
+		     address, value[0], value[1]);
+	}
+
+	info("mt9m111 register state dump complete");
+}
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_mt9m111.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_mt9m111.h	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,1013 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Some defines taken from the mt9m111 sensor driver
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_MT9M111_H_
+#define M5602_MT9M111_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define MT9M111_SC_CHIPVER			0x00
+#define MT9M111_SC_ROWSTART			0x01
+#define MT9M111_SC_COLSTART			0x02
+#define MT9M111_SC_WINDOW_HEIGHT		0x03
+#define MT9M111_SC_WINDOW_WIDTH			0x04
+#define MT9M111_SC_HBLANK_CONTEXT_B		0x05
+#define MT9M111_SC_VBLANK_CONTEXT_B		0x06
+#define MT9M111_SC_HBLANK_CONTEXT_A		0x07
+#define MT9M111_SC_VBLANK_CONTEXT_A		0x08
+#define MT9M111_SC_SHUTTER_WIDTH		0x09
+#define MT9M111_SC_ROW_SPEED			0x0a
+
+#define MT9M111_SC_EXTRA_DELAY			0x0b
+#define MT9M111_SC_SHUTTER_DELAY		0x0c
+#define MT9M111_SC_RESET			0x0d
+#define MT9M111_SC_R_MODE_CONTEXT_B		0x20
+#define MT9M111_SC_R_MODE_CONTEXT_A		0x21
+#define MT9M111_SC_FLASH_CONTROL		0x23
+#define MT9M111_SC_GREEN_1_GAIN			0x2b
+#define MT9M111_SC_BLUE_GAIN			0x2c
+#define MT9M111_SC_RED_GAIN			0x2d
+#define MT9M111_SC_GREEN_2_GAIN			0x2e
+#define MT9M111_SC_GLOBAL_GAIN			0x2f
+
+#define MT9M111_RMB_MIRROR_ROWS			(1 << 0)
+#define MT9M111_RMB_MIRROR_COLS			(1 << 1)
+
+#define MT9M111_CONTEXT_CONTROL			0xc8
+#define MT9M111_PAGE_MAP			0xf0
+#define MT9M111_BYTEWISE_ADDRESS		0xf1
+
+#define MT9M111_CP_OPERATING_MODE_CTL		0x06
+#define MT9M111_CP_LUMA_OFFSET			0x34
+#define MT9M111_CP_LUMA_CLIP			0x35
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A 0x3a
+#define MT9M111_CP_LENS_CORRECTION_1		0x3b
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_A	0x4c
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_B	0x4d
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B 0x9b
+#define MT9M111_CP_GLOBAL_CLK_CONTROL		0xb3
+
+#define MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18   0x65
+#define MT9M111_CC_AWB_PARAMETER_7		0x28
+
+#define MT9M111_SENSOR_CORE			0x00
+#define MT9M111_COLORPIPE			0x01
+#define MT9M111_CAMERA_CONTROL			0x02
+
+#define INITIAL_MAX_GAIN			64
+#define DEFAULT_GAIN 				190
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+int mt9m111_get_ctrl(struct m5602_camera *cam,
+		     struct v4l2_control *ctrl);
+int mt9m111_set_ctrl(struct m5602_camera *cam,
+		     const struct v4l2_control *ctrl);
+int mt9m111_probe(struct m5602_camera *cam);
+int mt9m111_init(struct m5602_camera *cam);
+int mt9m111_power_down(struct m5602_camera *cam);
+
+int mt9m111_read_sensor(struct m5602_camera *cam, const u8 address,
+			u8 *i2c_data, const u8 len);
+
+int mt9m111_write_sensor(struct m5602_camera *cam, const u8 address,
+			 u8 *i2c_data, const u8 len);
+
+void mt9m111_dump_registers(struct m5602_camera *cam);
+
+static struct m5602_sensor mt9m111 = {
+	.name = "MT9M111",
+
+	.i2c_slave_id = 0xba,
+
+	.probe = mt9m111_probe,
+	.init = mt9m111_init,
+	.power_down = mt9m111_power_down,
+	.get_ctrl = mt9m111_get_ctrl,
+	.set_ctrl = mt9m111_set_ctrl,
+
+	.read_sensor = mt9m111_read_sensor,
+	.write_sensor = mt9m111_write_sensor,
+
+	.qctrl = {
+	{
+		.id             = V4L2_CID_VFLIP,
+		.type           = V4L2_CTRL_TYPE_BOOLEAN,
+		.name           = "vertical flip",
+		.minimum        = 0,
+		.maximum        = 1,
+		.step           = 1,
+		.default_value  = 0
+	}, {
+		.id             = V4L2_CID_HFLIP,
+		.type           = V4L2_CTRL_TYPE_BOOLEAN,
+		.name           = "horizontal flip",
+		.minimum        = 0,
+		.maximum        = 1,
+		.step           = 1,
+		.default_value  = 0
+	}, {
+		.id             = V4L2_CID_GAIN,
+		.type           = V4L2_CTRL_TYPE_INTEGER,
+		.name           = "gain",
+		.minimum        = 0,
+		.maximum        = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2,
+		.step           = 1,
+		.default_value  = DEFAULT_GAIN,
+		.flags          = V4L2_CTRL_FLAG_SLIDER
+	}
+	},
+
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		}
+	},
+	.current_fmt = {
+		.width = M5602_DEFAULT_FRAME_WIDTH,
+		.height = M5602_DEFAULT_FRAME_HEIGHT,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 1,
+		.colorspace = V4L2_COLORSPACE_SRGB
+	}
+};
+
+static const unsigned char preinit_mt9m111[][4] =
+{
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0xff, 0xf7},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}
+};
+
+static const unsigned char init_mt9m111[][4] =
+{
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0xff, 0xff},
+	{SENSOR, MT9M111_SC_RESET, 0xff, 0xff},
+	{SENSOR, MT9M111_SC_RESET, 0xff, 0xde},
+	{SENSOR, MT9M111_SC_RESET, 0xff, 0xff},
+	{SENSOR, MT9M111_SC_RESET, 0xff, 0xf7},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+
+	{SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0xff, 0xff},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+	{SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+	{SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+
+	{SENSOR, 0xcd, 0x00, 0x0e},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+	{SENSOR, 0xd0, 0x00, 0x40},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+	{SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+	{SENSOR, 0x33, 0x03, 0x49},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+
+	{SENSOR, 0x33, 0x03, 0x49},
+	{SENSOR, 0x34, 0xc0, 0x19},
+	{SENSOR, 0x3f, 0x20, 0x20},
+	{SENSOR, 0x40, 0x20, 0x20},
+	{SENSOR, 0x5a, 0xc0, 0x0a},
+	{SENSOR, 0x70, 0x7b, 0x0a},
+	{SENSOR, 0x71, 0xff, 0x00},
+	{SENSOR, 0x72, 0x19, 0x0e},
+	{SENSOR, 0x73, 0x18, 0x0f},
+	{SENSOR, 0x74, 0x57, 0x32},
+	{SENSOR, 0x75, 0x56, 0x34},
+	{SENSOR, 0x76, 0x73, 0x35},
+	{SENSOR, 0x77, 0x30, 0x12},
+	{SENSOR, 0x78, 0x79, 0x02},
+	{SENSOR, 0x79, 0x75, 0x06},
+	{SENSOR, 0x7a, 0x77, 0x0a},
+	{SENSOR, 0x7b, 0x78, 0x09},
+	{SENSOR, 0x7c, 0x7d, 0x06},
+	{SENSOR, 0x7d, 0x31, 0x10},
+	{SENSOR, 0x7e, 0x00, 0x7e},
+	{SENSOR, 0x80, 0x59, 0x04},
+	{SENSOR, 0x81, 0x59, 0x04},
+	{SENSOR, 0x82, 0x57, 0x0a},
+	{SENSOR, 0x83, 0x58, 0x0b},
+	{SENSOR, 0x84, 0x47, 0x0c},
+	{SENSOR, 0x85, 0x48, 0x0e},
+	{SENSOR, 0x86, 0x5b, 0x02},
+	{SENSOR, 0x87, 0x00, 0x5c},
+	{SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+	{SENSOR, 0x60, 0x00, 0x80},
+	{SENSOR, 0x61, 0x00, 0x00},
+	{SENSOR, 0x62, 0x00, 0x00},
+	{SENSOR, 0x63, 0x00, 0x00},
+	{SENSOR, 0x64, 0x00, 0x00},
+
+	{SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+	{SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18},
+	{SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04},
+	{SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03},
+	{SENSOR, 0x30, 0x04, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4},
+	{SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+	{SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+	{SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+	{SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+	{SENSOR, 0xcd, 0x00, 0x0e},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+	{SENSOR, 0xd0, 0x00, 0x40},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+	{SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+	{SENSOR, 0x33, 0x03, 0x49},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+
+	{SENSOR, 0x33, 0x03, 0x49},
+	{SENSOR, 0x34, 0xc0, 0x19},
+	{SENSOR, 0x3f, 0x20, 0x20},
+	{SENSOR, 0x40, 0x20, 0x20},
+	{SENSOR, 0x5a, 0xc0, 0x0a},
+	{SENSOR, 0x70, 0x7b, 0x0a},
+	{SENSOR, 0x71, 0xff, 0x00},
+	{SENSOR, 0x72, 0x19, 0x0e},
+	{SENSOR, 0x73, 0x18, 0x0f},
+	{SENSOR, 0x74, 0x57, 0x32},
+	{SENSOR, 0x75, 0x56, 0x34},
+	{SENSOR, 0x76, 0x73, 0x35},
+	{SENSOR, 0x77, 0x30, 0x12},
+	{SENSOR, 0x78, 0x79, 0x02},
+	{SENSOR, 0x79, 0x75, 0x06},
+	{SENSOR, 0x7a, 0x77, 0x0a},
+	{SENSOR, 0x7b, 0x78, 0x09},
+	{SENSOR, 0x7c, 0x7d, 0x06},
+	{SENSOR, 0x7d, 0x31, 0x10},
+	{SENSOR, 0x7e, 0x00, 0x7e},
+	{SENSOR, 0x80, 0x59, 0x04},
+	{SENSOR, 0x81, 0x59, 0x04},
+	{SENSOR, 0x82, 0x57, 0x0a},
+	{SENSOR, 0x83, 0x58, 0x0b},
+	{SENSOR, 0x84, 0x47, 0x0c},
+	{SENSOR, 0x85, 0x48, 0x0e},
+	{SENSOR, 0x86, 0x5b, 0x02},
+	{SENSOR, 0x87, 0x00, 0x5c},
+	{SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+	{SENSOR, 0x60, 0x00, 0x80},
+	{SENSOR, 0x61, 0x00, 0x00},
+	{SENSOR, 0x62, 0x00, 0x00},
+	{SENSOR, 0x63, 0x00, 0x00},
+	{SENSOR, 0x64, 0x00, 0x00},
+
+	{SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+	{SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18},
+	{SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04},
+	{SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03},
+	{SENSOR, 0x30, 0x04, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4},
+	{SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+	{SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+	{SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+	{SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+	{SENSOR, 0xcd, 0x00, 0x0e},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+	{SENSOR, 0xd0, 0x00, 0x40},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+	{SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+	{SENSOR, 0x33, 0x03, 0x49},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+
+	{SENSOR, 0x33, 0x03, 0x49},
+	{SENSOR, 0x34, 0xc0, 0x19},
+	{SENSOR, 0x3f, 0x20, 0x20},
+	{SENSOR, 0x40, 0x20, 0x20},
+	{SENSOR, 0x5a, 0xc0, 0x0a},
+	{SENSOR, 0x70, 0x7b, 0x0a},
+	{SENSOR, 0x71, 0xff, 0x00},
+	{SENSOR, 0x72, 0x19, 0x0e},
+	{SENSOR, 0x73, 0x18, 0x0f},
+	{SENSOR, 0x74, 0x57, 0x32},
+	{SENSOR, 0x75, 0x56, 0x34},
+	{SENSOR, 0x76, 0x73, 0x35},
+	{SENSOR, 0x77, 0x30, 0x12},
+	{SENSOR, 0x78, 0x79, 0x02},
+	{SENSOR, 0x79, 0x75, 0x06},
+	{SENSOR, 0x7a, 0x77, 0x0a},
+	{SENSOR, 0x7b, 0x78, 0x09},
+	{SENSOR, 0x7c, 0x7d, 0x06},
+	{SENSOR, 0x7d, 0x31, 0x10},
+	{SENSOR, 0x7e, 0x00, 0x7e},
+	{SENSOR, 0x80, 0x59, 0x04},
+	{SENSOR, 0x81, 0x59, 0x04},
+	{SENSOR, 0x82, 0x57, 0x0a},
+	{SENSOR, 0x83, 0x58, 0x0b},
+	{SENSOR, 0x84, 0x47, 0x0c},
+	{SENSOR, 0x85, 0x48, 0x0e},
+	{SENSOR, 0x86, 0x5b, 0x02},
+	{SENSOR, 0x87, 0x00, 0x5c},
+	{SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+	{SENSOR, 0x60, 0x00, 0x80},
+	{SENSOR, 0x61, 0x00, 0x00},
+	{SENSOR, 0x62, 0x00, 0x00},
+	{SENSOR, 0x63, 0x00, 0x00},
+	{SENSOR, 0x64, 0x00, 0x00},
+
+	{SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+	{SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18},
+	{SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04},
+	{SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03},
+	{SENSOR, 0x30, 0x04, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4},
+	{SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+	{SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+	{SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, 0xcd, 0x00, 0x0e},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, 0xd0, 0x00, 0x40},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+	{SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, 0x33, 0x03, 0x49},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+
+	{SENSOR, 0x33, 0x03, 0x49},
+	{SENSOR, 0x34, 0xc0, 0x19},
+	{SENSOR, 0x3f, 0x20, 0x20},
+	{SENSOR, 0x40, 0x20, 0x20},
+	{SENSOR, 0x5a, 0xc0, 0x0a},
+	{SENSOR, 0x70, 0x7b, 0x0a},
+	{SENSOR, 0x71, 0xff, 0x00},
+	{SENSOR, 0x72, 0x19, 0x0e},
+	{SENSOR, 0x73, 0x18, 0x0f},
+	{SENSOR, 0x74, 0x57, 0x32},
+	{SENSOR, 0x75, 0x56, 0x34},
+	{SENSOR, 0x76, 0x73, 0x35},
+	{SENSOR, 0x77, 0x30, 0x12},
+	{SENSOR, 0x78, 0x79, 0x02},
+	{SENSOR, 0x79, 0x75, 0x06},
+	{SENSOR, 0x7a, 0x77, 0x0a},
+	{SENSOR, 0x7b, 0x78, 0x09},
+	{SENSOR, 0x7c, 0x7d, 0x06},
+	{SENSOR, 0x7d, 0x31, 0x10},
+	{SENSOR, 0x7e, 0x00, 0x7e},
+	{SENSOR, 0x80, 0x59, 0x04},
+	{SENSOR, 0x81, 0x59, 0x04},
+	{SENSOR, 0x82, 0x57, 0x0a},
+	{SENSOR, 0x83, 0x58, 0x0b},
+	{SENSOR, 0x84, 0x47, 0x0c},
+	{SENSOR, 0x85, 0x48, 0x0e},
+	{SENSOR, 0x86, 0x5b, 0x02},
+	{SENSOR, 0x87, 0x00, 0x5c},
+	{SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+	{SENSOR, 0x60, 0x00, 0x80},
+	{SENSOR, 0x61, 0x00, 0x00},
+	{SENSOR, 0x62, 0x00, 0x00},
+	{SENSOR, 0x63, 0x00, 0x00},
+	{SENSOR, 0x64, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+	{SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12},
+	{SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00},
+	{SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f},
+	{SENSOR, 0x30, 0x04, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe3, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
+	{SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xe6},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+	{SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+	{SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, 0xcd, 0x00, 0x0e},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, 0xd0, 0x00, 0x40},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+	{SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+	{SENSOR, 0x33, 0x03, 0x49},
+	{BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+	{BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+	{BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+
+	{SENSOR, 0x33, 0x03, 0x49},
+	{SENSOR, 0x34, 0xc0, 0x19},
+	{SENSOR, 0x3f, 0x20, 0x20},
+	{SENSOR, 0x40, 0x20, 0x20},
+	{SENSOR, 0x5a, 0xc0, 0x0a},
+	{SENSOR, 0x70, 0x7b, 0x0a},
+	{SENSOR, 0x71, 0xff, 0x00},
+	{SENSOR, 0x72, 0x19, 0x0e},
+	{SENSOR, 0x73, 0x18, 0x0f},
+	{SENSOR, 0x74, 0x57, 0x32},
+	{SENSOR, 0x75, 0x56, 0x34},
+	{SENSOR, 0x76, 0x73, 0x35},
+	{SENSOR, 0x77, 0x30, 0x12},
+	{SENSOR, 0x78, 0x79, 0x02},
+	{SENSOR, 0x79, 0x75, 0x06},
+	{SENSOR, 0x7a, 0x77, 0x0a},
+	{SENSOR, 0x7b, 0x78, 0x09},
+	{SENSOR, 0x7c, 0x7d, 0x06},
+	{SENSOR, 0x7d, 0x31, 0x10},
+	{SENSOR, 0x7e, 0x00, 0x7e},
+	{SENSOR, 0x80, 0x59, 0x04},
+	{SENSOR, 0x81, 0x59, 0x04},
+	{SENSOR, 0x82, 0x57, 0x0a},
+	{SENSOR, 0x83, 0x58, 0x0b},
+	{SENSOR, 0x84, 0x47, 0x0c},
+	{SENSOR, 0x85, 0x48, 0x0e},
+	{SENSOR, 0x86, 0x5b, 0x02},
+	{SENSOR, 0x87, 0x00, 0x5c},
+	{SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+	{SENSOR, 0x60, 0x00, 0x80},
+	{SENSOR, 0x61, 0x00, 0x00},
+	{SENSOR, 0x62, 0x00, 0x00},
+	{SENSOR, 0x63, 0x00, 0x00},
+	{SENSOR, 0x64, 0x00, 0x00},
+
+	{SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+	{SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12},
+	{SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00},
+	{SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60},
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f},
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f},
+	{SENSOR, 0x30, 0x04, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	/* Set number of blank rows chosen to 400 */
+	{SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
+	/* Set the global gain to 190 (of 256) */
+	{SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x03, 0x47}
+};
+
+#endif
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_ov9650.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_ov9650.c	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,408 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include "m5602_ov9650.h"
+
+int ov9650_read_sensor(struct m5602_camera *cam, const u8 address,
+		      u8 *i2c_data, const u8 len)
+{
+	int err, i;
+
+	/* The ov9650 registers have a max depth of one byte */
+	if (len > 1 || !len)
+		return -EINVAL;
+
+	do {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_STATUS, i2c_data);
+	} while ((*i2c_data & I2C_BUSY) && !err);
+
+	m5602_write_bridge(cam, M5602_XB_I2C_DEV_ADDR,
+			   ov9650.i2c_slave_id);
+	m5602_write_bridge(cam, M5602_XB_I2C_REG_ADDR, address);
+	m5602_write_bridge(cam, M5602_XB_I2C_CTRL, 0x10 + len);
+	m5602_write_bridge(cam, M5602_XB_I2C_CTRL, 0x08);
+
+	for (i = 0; i < len; i++) {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+		PDEBUG(DBG_TRACE, "Reading sensor register "
+				"0x%x containing 0x%x ", address, *i2c_data);
+	}
+	return (err < 0) ? err : 0;
+}
+
+int ov9650_write_sensor(struct m5602_camera *cam, const u8 address,
+			u8 *i2c_data, const u8 len)
+{
+	int err, i;
+	u8 *p;
+
+	/* The ov9650 only supports one byte writes */
+	if (len > 1 || !len)
+		return -EINVAL;
+
+	memcpy(cam->control_buffer, sensor_urb_skeleton,
+	       sizeof(sensor_urb_skeleton));
+
+	cam->control_buffer[11] = cam->sensor->i2c_slave_id;
+	cam->control_buffer[15] = address;
+
+	/* Special case larger sensor writes */
+	p = cam->control_buffer + 16;
+
+	/* Copy a four byte write sequence for each byte to be written to */
+	for (i = 0; i < len; i++) {
+		memcpy(p, sensor_urb_skeleton + 16, 4);
+		p[3] = i2c_data[i];
+		p += 4;
+		PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+		       address, i2c_data[i]);
+	}
+
+	/* Copy the tailer */
+	memcpy(p, sensor_urb_skeleton + 20, 4);
+
+	/* Set the total length */
+	p[3] = 0x10 + len;
+
+	err = usb_control_msg(cam->udev, usb_sndctrlpipe(cam->udev, 0),
+			      0x04, 0x40, 0x19,
+			      0x0000, cam->control_buffer,
+			      20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+	return (err < 0) ? err : 0;
+}
+
+int ov9650_probe(struct m5602_camera *cam)
+{
+	u8 prod_id = 0, ver_id = 0, i;
+
+	if (force_sensor) {
+		if (force_sensor == OV9650_SENSOR) {
+			info("Forcing an %s sensor", ov9650.name);
+			return 0;
+		}
+		/* If we want to force another sensor,
+		   don't try to probe this one */
+		return -ENODEV;
+	}
+
+	info("Probing for an ov9650 sensor");
+
+	/* Run the pre-init to actually probe the unit */
+	for (i = 0; i < ARRAY_SIZE(preinit_ov9650); i++) {
+		u8 data = preinit_ov9650[i][2];
+		if (preinit_ov9650[i][0] == SENSOR)
+			ov9650_write_sensor(cam,
+					    preinit_ov9650[i][1], &data, 1);
+		else
+			m5602_write_bridge(cam, preinit_ov9650[i][1], data);
+	}
+
+	if (ov9650_read_sensor(cam, OV9650_PID, &prod_id, 1))
+		return -ENODEV;
+
+	if (ov9650_read_sensor(cam, OV9650_VER, &ver_id, 1))
+		return -ENODEV;
+
+	if ((prod_id == 0x96) && (ver_id == 0x52)) {
+		info("Detected an ov9650 sensor");
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+int ov9650_init(struct m5602_camera *cam)
+{
+	int i, err = 0;
+	u8 data;
+
+	if (dump_sensor)
+		ov9650_dump_registers(cam);
+
+	for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
+		data = init_ov9650[i][2];
+		if (init_ov9650[i][0] == SENSOR)
+			err = ov9650_write_sensor(cam, init_ov9650[i][1],
+						  &data, 1);
+		else
+			err = m5602_write_bridge(cam, init_ov9650[i][1], data);
+	}
+
+	if (!err && dmi_check_system(ov9650_flip_dmi_table)) {
+		info("flip quirk active");
+		data = 0x30;
+		err = ov9650_write_sensor(cam, OV9650_MVFP, &data, 1);
+	}
+
+	return (err < 0) ? err : 0;
+}
+
+int ov9650_power_down(struct m5602_camera *cam)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(power_down_ov9650); i++) {
+		u8 data = power_down_ov9650[i][2];
+		if (power_down_ov9650[i][0] == SENSOR)
+			ov9650_write_sensor(cam,
+					    power_down_ov9650[i][1], &data, 1);
+		else
+			m5602_write_bridge(cam, power_down_ov9650[i][1], data);
+	}
+
+	return 0;
+}
+
+int ov9650_get_ctrl(struct m5602_camera *cam, struct v4l2_control *ctrl)
+{
+	u8 i2c_data = 0;
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		err = ov9650_read_sensor(cam, OV9650_COM1, &i2c_data, 1);
+		ctrl->value = i2c_data & 0x03;
+
+		err = ov9650_read_sensor(cam, OV9650_AECH, &i2c_data, 1);
+		ctrl->value |= (i2c_data << 2);
+
+		err = ov9650_read_sensor(cam, OV9650_AECHM, &i2c_data, 1);
+		ctrl->value |= (i2c_data & 0x3f) << 10;
+
+		PDEBUG(DBG_V4L2_CID, "Read exposure %d", ctrl->value);
+		break;
+
+	case V4L2_CID_GAIN:
+		ov9650_read_sensor(cam, OV9650_VREF, &i2c_data, 1);
+		ctrl->value = (i2c_data & 0x03) << 8;
+
+		err = ov9650_read_sensor(cam, OV9650_GAIN, &i2c_data, 1);
+		ctrl->value |= i2c_data;
+		PDEBUG(DBG_V4L2_CID, "Read gain %d", ctrl->value);
+		break;
+
+	case V4L2_CID_RED_BALANCE:
+		err = ov9650_read_sensor(cam, OV9650_RED, &i2c_data, 1);
+		ctrl->value = i2c_data;
+		PDEBUG(DBG_V4L2_CID, "Read red balance %d", ctrl->value);
+		break;
+
+	case V4L2_CID_BLUE_BALANCE:
+		err = ov9650_read_sensor(cam, OV9650_BLUE, &i2c_data, 1);
+		ctrl->value = i2c_data;
+		PDEBUG(DBG_V4L2_CID, "Read blue balance %d", ctrl->value);
+		break;
+
+	case V4L2_CID_HFLIP:
+		err = ov9650_read_sensor(cam, OV9650_MVFP, &i2c_data, 1);
+		if (dmi_check_system(ov9650_flip_dmi_table))
+			ctrl->value = ((i2c_data  & 0x20) >> 5) ? 0 : 1;
+		else
+			ctrl->value = (i2c_data & 0x20) >> 5;
+		PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", ctrl->value);
+		break;
+
+	case V4L2_CID_VFLIP:
+		err = ov9650_read_sensor(cam, OV9650_MVFP, &i2c_data, 1);
+		if (dmi_check_system(ov9650_flip_dmi_table))
+			ctrl->value = ((i2c_data & 0x10) >> 4) ? 0 : 1;
+		else
+			ctrl->value = (i2c_data & 0x10) >> 4;
+		PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", ctrl->value);
+		break;
+
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		err = ov9650_read_sensor(cam, OV9650_COM8, &i2c_data, 1);
+		ctrl->value = (i2c_data & 0x02) >> 1;
+		PDEBUG(DBG_V4L2_CID, "Read auto white balance %d", ctrl->value);
+		break;
+
+	case V4L2_CID_AUTOGAIN:
+		err = ov9650_read_sensor(cam, OV9650_COM8, &i2c_data, 1);
+		ctrl->value = (i2c_data & 0x04) >> 2;
+		PDEBUG(DBG_V4L2_CID, "Read auto gain control %d", ctrl->value);
+		break;
+
+	default:
+		PDEBUG(DBG_V4L2_CID, "Unknown V4L2 CID 0x%x", ctrl->value);
+		return -EINVAL;
+	}
+	return (err < 0) ? err : 0;
+}
+
+int ov9650_set_ctrl(struct m5602_camera *cam,
+			   const struct v4l2_control *ctrl)
+{
+	u8 register_data = 0;
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		PDEBUG(DBG_V4L2_CID, "Set exposure to %d",
+			ctrl->value & 0xffff);
+
+		/* The 6 MSBs */
+		register_data = (ctrl->value >> 10) & 0x3f;
+		err = ov9650_write_sensor(cam, OV9650_AECHM,
+					  &register_data, 1);
+
+		/* The 8 middle bits */
+		register_data = (ctrl->value >> 2) & 0xff;
+		err = ov9650_write_sensor(cam, OV9650_AECH,
+					  &register_data, 1);
+
+		/* The 2 LSBs */
+		register_data = ctrl->value & 0x03;
+		err = ov9650_write_sensor(cam, OV9650_COM1, &register_data, 1);
+		break;
+
+	case V4L2_CID_GAIN:
+		PDEBUG(DBG_V4L2_CID, "Set gain to %d", ctrl->value & 0x3ff);
+
+		/* FIXME: The gain register is 10 bits wide, but touching
+		(even reading) the OV9650_VREF register causes instant
+		hangs of the camera stream */
+
+		/* The 2 MSB */
+		/* Read the OV9650_VREF register first to avoid
+		corrupting the VREF high and low bits */
+		ov9650_read_sensor(cam, OV9650_VREF, &register_data, 1);
+		/* Mask away all uninteresting bits */
+		register_data = ((ctrl->value & 0x0300) >> 2) |
+				 (register_data & 0x3F);
+		err = ov9650_write_sensor(cam, OV9650_VREF, &register_data, 1);
+
+		/* The 8 LSBs */
+		register_data = ctrl->value & 0xff;
+		err = ov9650_write_sensor(cam, OV9650_GAIN, &register_data, 1);
+		break;
+
+	case V4L2_CID_BLUE_BALANCE:
+		PDEBUG(DBG_V4L2_CID, "Set blue balance to %d",
+			ctrl->value & 0xff);
+
+		register_data = ctrl->value & 0xff;
+		err = ov9650_write_sensor(cam, OV9650_BLUE, &register_data, 1);
+		break;
+
+	case V4L2_CID_RED_BALANCE:
+		PDEBUG(DBG_V4L2_CID, "Set red balance to %d",
+			ctrl->value & 0xff);
+
+		register_data = ctrl->value & 0xff;
+		err = ov9650_write_sensor(cam, OV9650_RED, &register_data, 1);
+		break;
+
+	case V4L2_CID_HFLIP:
+		PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d",
+			ctrl->value);
+		err = ov9650_read_sensor(cam, OV9650_MVFP, &register_data, 1);
+		if (err < 0)
+			break;
+
+		if (dmi_check_system(ov9650_flip_dmi_table))
+			register_data = ((register_data & 0xdf) |
+					(((ctrl->value ? 0 : 1) & 0x01) << 5));
+		else
+			register_data = ((register_data & 0xdf) |
+					((ctrl->value & 0x01) << 5));
+
+		err = ov9650_write_sensor(cam, OV9650_MVFP, &register_data, 1);
+		break;
+
+	case V4L2_CID_VFLIP:
+		PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d",
+			ctrl->value);
+		err = ov9650_read_sensor(cam, OV9650_MVFP, &register_data, 1);
+		if (err < 0)
+			break;
+
+		if (dmi_check_system(ov9650_flip_dmi_table))
+			register_data = ((register_data & 0xef) |
+					(((ctrl->value ? 0 : 1) & 0x01) << 4));
+		else
+			register_data = ((register_data & 0xef) |
+					((ctrl->value & 0x01) << 4));
+
+		err = ov9650_write_sensor(cam, OV9650_MVFP, &register_data, 1);
+		break;
+
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		PDEBUG(DBG_V4L2_CID, "Set auto white balance to %d",
+			ctrl->value);
+		err = ov9650_read_sensor(cam, OV9650_COM8, &register_data, 1);
+		if (err < 0)
+			break;
+
+		register_data = ((register_data & 0xfd)
+				| ((ctrl->value & 0x01) << 1));
+		err = ov9650_write_sensor(cam, OV9650_COM8, &register_data, 1);
+		break;
+
+	case V4L2_CID_AUTOGAIN:
+		PDEBUG(DBG_V4L2_CID, "Set auto gain control to %d",
+			ctrl->value);
+		err = ov9650_read_sensor(cam, OV9650_COM8, &register_data, 1);
+		if (err < 0)
+			break;
+
+		register_data = ((register_data & 0xfb)
+				| ((ctrl->value & 0x01) << 2));
+		err = ov9650_write_sensor(cam, OV9650_COM8, &register_data, 1);
+		break;
+
+	default:
+		PDEBUG(DBG_V4L2_CID, "Illegal V4L CID %d, value %d",
+			ctrl->id, ctrl->value);
+		return -EINVAL;
+	}
+	return (err < 0) ? err : 0;
+}
+
+void ov9650_dump_registers(struct m5602_camera *cam)
+{
+	int address;
+	info("Dumping the ov9650 register state");
+	for (address = 0; address < 0xa9; address++) {
+		u8 value;
+		ov9650_read_sensor(cam, address, &value, 1);
+		info("register 0x%x contains 0x%x",
+		     address, value);
+	}
+
+	info("ov9650 register state dump complete");
+
+	info("Probing for which registers that are read/write");
+	for (address = 0; address < 0xff; address++) {
+		u8 old_value, ctrl_value;
+		u8 test_value[2] = {0xff, 0xff};
+
+		ov9650_read_sensor(cam, address, &old_value, 1);
+		ov9650_write_sensor(cam, address, test_value, 1);
+		ov9650_read_sensor(cam, address, &ctrl_value, 1);
+
+		if (ctrl_value == test_value[0])
+			info("register 0x%x is writeable", address);
+		else
+			info("register 0x%x is read only", address);
+
+		/* Restore original value */
+		ov9650_write_sensor(cam, address, &old_value, 1);
+	}
+}
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_ov9650.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_ov9650.h	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,447 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_OV9650_H_
+#define M5602_OV9650_H_
+
+#include <linux/dmi.h>
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define OV9650_GAIN		0x00
+#define OV9650_BLUE		0x01
+#define OV9650_RED		0x02
+#define OV9650_VREF		0x03
+#define OV9650_COM1		0x04
+#define OV9650_BAVE		0x05
+#define OV9650_GEAVE		0x06
+#define OV9650_RSVD7		0x07
+#define OV9650_PID		0x0a
+#define OV9650_VER		0x0b
+#define OV9650_COM5		0x0e
+#define OV9650_COM6		0x0f
+#define OV9650_AECH		0x10
+#define OV9650_CLKRC		0x11
+#define OV9650_COM7		0x12
+#define OV9650_COM8		0x13
+#define OV9650_COM9		0x14
+#define OV9650_COM10		0x15
+#define OV9650_RSVD16		0x16
+#define OV9650_HSTART		0x17
+#define OV9650_HSTOP		0x18
+#define OV9650_VSTRT		0x19
+#define OV9650_VSTOP		0x1a
+#define OV9650_PSHFT		0x1b
+#define OV9650_MVFP		0x1e
+#define OV9650_AEW		0x24
+#define OV9650_AEB		0x25
+#define OV9650_VPT		0x26
+#define OV9650_BBIAS		0x27
+#define OV9650_GbBIAS		0x28
+#define OV9650_Gr_COM		0x29
+#define OV9650_RBIAS		0x2c
+#define OV9650_HREF		0x32
+#define OV9650_CHLF		0x33
+#define OV9650_ARBLM		0x34
+#define OV9650_RSVD35		0x35
+#define OV9650_RSVD36		0x36
+#define OV9650_ADC		0x37
+#define OV9650_ACOM38		0x38
+#define OV9650_OFON		0x39
+#define OV9650_TSLB		0x3a
+#define OV9650_COM12		0x3c
+#define OV9650_COM13		0x3d
+#define OV9650_COM15		0x40
+#define OV9650_COM16		0x41
+#define OV9650_LCC1		0x62
+#define OV9650_LCC2		0x63
+#define OV9650_LCC3		0x64
+#define OV9650_LCC4		0x65
+#define OV9650_LCC5		0x66
+#define OV9650_HV		0x69
+#define OV9650_DBLV		0x6b
+#define OV9650_COM21		0x8b
+#define OV9650_COM22		0x8c
+#define OV9650_COM24		0x8e
+#define OV9650_DBLC1		0x8f
+#define OV9650_RSVD94		0x94
+#define OV9650_RSVD95		0x95
+#define OV9650_RSVD96		0x96
+#define OV9650_LCCFB		0x9d
+#define OV9650_LCCFR		0x9e
+#define OV9650_AECHM		0xa1
+#define OV9650_COM26		0xa5
+#define OV9650_ACOMA8		0xa8
+#define OV9650_ACOMA9		0xa9
+
+#define GAIN_DEFAULT		0x14
+#define RED_GAIN_DEFAULT	0x70
+#define BLUE_GAIN_DEFAULT	0x20
+#define EXPOSURE_DEFAULT	0x5003
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+int ov9650_get_ctrl(struct m5602_camera *cam,
+		    struct v4l2_control *ctrl);
+int ov9650_set_ctrl(struct m5602_camera *cam,
+		    const struct v4l2_control *ctrl);
+int ov9650_probe(struct m5602_camera *cam);
+int ov9650_init(struct m5602_camera *cam);
+int ov9650_power_down(struct m5602_camera *cam);
+
+int ov9650_read_sensor(struct m5602_camera *cam, const u8 address,
+		       u8 *i2c_data, const u8 len);
+int ov9650_write_sensor(struct m5602_camera *cam, const u8 address,
+		       u8 *i2c_data, const u8 len);
+
+void ov9650_dump_registers(struct m5602_camera *cam);
+
+static struct m5602_sensor ov9650 = {
+	.name = "OV9650",
+	.probe = ov9650_probe,
+	.init = ov9650_init,
+	.power_down = ov9650_power_down,
+	.get_ctrl = ov9650_get_ctrl,
+	.set_ctrl = ov9650_set_ctrl,
+	.i2c_slave_id = 0x60,
+	.read_sensor = ov9650_read_sensor,
+	.write_sensor = &ov9650_write_sensor,
+
+	.qctrl = {
+	{
+		.id 		= V4L2_CID_EXPOSURE,
+		.type 		= V4L2_CTRL_TYPE_INTEGER,
+		.name 		= "exposure",
+		.minimum 	= 0x00,
+		.maximum 	= 0xffff,
+		.step 		= 0x1,
+		.default_value 	= EXPOSURE_DEFAULT,
+		.flags         	= V4L2_CTRL_FLAG_SLIDER
+	},
+	{
+		.id 		= V4L2_CID_GAIN,
+		.type 		= V4L2_CTRL_TYPE_INTEGER,
+		.name 		= "gain",
+		.minimum 	= 0x00,
+		/* FIXME: Real gain is 10 bits (0x3ff),
+			we currently limit this to 8 bits
+			due to hard crashes when touching the register
+			containing the higher two bits */
+		.maximum 	= 0x3ff,
+		.step 		= 0x1,
+		.default_value 	= GAIN_DEFAULT,
+		.flags         	= V4L2_CTRL_FLAG_SLIDER
+	},
+	{
+		.id 		= V4L2_CID_RED_BALANCE,
+		.type 		= V4L2_CTRL_TYPE_INTEGER,
+		.name 		= "red balance",
+		.minimum 	= 0x00,
+		.maximum 	= 0xff,
+		.step 		= 0x1,
+		.default_value 	= RED_GAIN_DEFAULT,
+		.flags         	= V4L2_CTRL_FLAG_SLIDER
+	},
+	{
+		.id 		= V4L2_CID_BLUE_BALANCE,
+		.type 		= V4L2_CTRL_TYPE_INTEGER,
+		.name 		= "blue balance",
+		.minimum 	= 0x00,
+		.maximum 	= 0xff,
+		.step 		= 0x1,
+		.default_value 	= BLUE_GAIN_DEFAULT,
+		.flags         	= V4L2_CTRL_FLAG_SLIDER
+	},
+	{
+		.id 		= V4L2_CID_HFLIP,
+		.type 		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name 		= "horizontal flip",
+		.minimum 	= 0,
+		.maximum 	= 1,
+		.step 		= 1,
+		.default_value 	= 0
+	},
+	{
+		.id 		= V4L2_CID_VFLIP,
+		.type 		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name 		= "vertical flip",
+		.minimum 	= 0,
+		.maximum 	= 1,
+		.step 		= 1,
+		.default_value 	= 0
+	},
+	{
+		.id 		= V4L2_CID_AUTO_WHITE_BALANCE,
+		.type 		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name 		= "auto white balance",
+		.minimum 	= 0,
+		.maximum 	= 1,
+		.step 		= 1,
+		.default_value 	= 0
+	},
+	{
+		.id 		= V4L2_CID_AUTOGAIN,
+		.type 		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name 		= "auto gain control",
+		.minimum 	= 0,
+		.maximum 	= 1,
+		.step 		= 1,
+		.default_value 	= 0
+	}
+	},
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		}
+	},
+	.current_fmt = {
+		.width = M5602_DEFAULT_FRAME_WIDTH,
+		.height = M5602_DEFAULT_FRAME_HEIGHT,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 1,
+		.colorspace = V4L2_COLORSPACE_SRGB
+	}
+};
+
+static const unsigned char preinit_ov9650[][3] =
+{
+	/* [INITCAM] */
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+	/* Reset chip */
+	{SENSOR, OV9650_COM7, 0x80},
+	/* Enable double clock */
+	{SENSOR, OV9650_CLKRC, 0x80},
+	/* Do something out of spec with the power */
+	{SENSOR, OV9650_OFON, 0x40}
+};
+
+static const unsigned char init_ov9650[][3] =
+{
+	/* [INITCAM] */
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+	/* Reset chip */
+	{SENSOR, OV9650_COM7, 0x80},
+	/* Enable double clock */
+	{SENSOR, OV9650_CLKRC, 0x80},
+	/* Do something out of spec with the power */
+	{SENSOR, OV9650_OFON, 0x40},
+
+	/* Set QQVGA */
+	{SENSOR, OV9650_COM1, 0x20},
+	/* Set fast AGC/AEC algorithm with unlimited step size */
+	{SENSOR, OV9650_COM8, 0xc0},
+
+	{SENSOR, OV9650_CHLF, 0x10},
+	{SENSOR, OV9650_ARBLM, 0xbf},
+	{SENSOR, OV9650_ACOM38, 0x81},
+	/* Turn off color matrix coefficient double option */
+	{SENSOR, OV9650_COM16, 0x00},
+	/* Enable color matrix for RGB/YUV, Delay Y channel,
+	set output Y/UV delay to 1 */
+	{SENSOR, OV9650_COM13, 0x19},
+	/* Enable digital BLC, Set output mode to U Y V Y */
+	{SENSOR, OV9650_TSLB, 0x0c},
+	/* Limit the AGC/AEC stable upper region */
+	{SENSOR, OV9650_COM24, 0x00},
+	/* Enable HREF and some out of spec things */
+	{SENSOR, OV9650_COM12, 0x73},
+	/* Set all DBLC offset signs to positive and
+	do some out of spec stuff */
+	{SENSOR, OV9650_DBLC1, 0xdf},
+	{SENSOR, OV9650_COM21, 0x06},
+	{SENSOR, OV9650_RSVD35, 0x91},
+	/* Necessary, no camera stream without it */
+	{SENSOR, OV9650_RSVD16, 0x06},
+	{SENSOR, OV9650_RSVD94, 0x99},
+	{SENSOR, OV9650_RSVD95, 0x99},
+	{SENSOR, OV9650_RSVD96, 0x04},
+	/* Enable full range output */
+	{SENSOR, OV9650_COM15, 0x0},
+	/* Enable HREF at optical black, enable ADBLC bias,
+	enable ADBLC, reset timings at format change */
+	{SENSOR, OV9650_COM6, 0x4b},
+	/* Subtract 32 from the B channel bias */
+	{SENSOR, OV9650_BBIAS, 0xa0},
+	/* Subtract 32 from the Gb channel bias */
+	{SENSOR, OV9650_GbBIAS, 0xa0},
+	/* Do not bypass the analog BLC and to some out of spec stuff */
+	{SENSOR, OV9650_Gr_COM, 0x00},
+	/* Subtract 32 from the R channel bias */
+	{SENSOR, OV9650_RBIAS, 0xa0},
+	/* Subtract 32 from the R channel bias */
+	{SENSOR, OV9650_RBIAS, 0x0},
+	{SENSOR, OV9650_COM26, 0x80},
+	{SENSOR, OV9650_ACOMA9, 0x98},
+	/* Set the AGC/AEC stable region upper limit */
+	{SENSOR, OV9650_AEW, 0x68},
+	/* Set the AGC/AEC stable region lower limit */
+	{SENSOR, OV9650_AEB, 0x5c},
+	/* Set the high and low limit nibbles to 3 */
+	{SENSOR, OV9650_VPT, 0xc3},
+	/* Set the Automatic Gain Ceiling (AGC) to 128x,
+	drop VSYNC at frame drop,
+	limit exposure timing,
+	drop frame when the AEC step is larger than the exposure gap */
+	{SENSOR, OV9650_COM9, 0x6e},
+	/* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync)
+	and set PWDN to SLVS (slave mode vertical sync) */
+	{SENSOR, OV9650_COM10, 0x42},
+	/* Set horizontal column start high to default value */
+	{SENSOR, OV9650_HSTART, 0x1a},
+	/* Set horizontal column end */
+	{SENSOR, OV9650_HSTOP, 0xbf},
+	/* Complementing register to the two writes above */
+	{SENSOR, OV9650_HREF, 0xb2},
+	/* Set vertical row start high bits */
+	{SENSOR, OV9650_VSTRT, 0x02},
+	/* Set vertical row end low bits */
+	{SENSOR, OV9650_VSTOP, 0x7e},
+	/* Set complementing vertical frame control */
+	{SENSOR, OV9650_VREF, 0x10},
+	/* Set raw RGB output format with VGA resolution */
+	{SENSOR, OV9650_COM7, 0x45},
+	{SENSOR, OV9650_ADC, 0x04},
+	{SENSOR, OV9650_HV, 0x40},
+	/* Enable denoise, and white-pixel erase */
+	{SENSOR, OV9650_COM22, 0x23},
+
+	/* Set the high bits of the exposure value */
+	{SENSOR, OV9650_AECH, ((EXPOSURE_DEFAULT & 0xff00) >> 8)},
+	/* Set the low bits of the exposure value */
+	{SENSOR, OV9650_COM1, (EXPOSURE_DEFAULT & 0xff)},
+
+	{SENSOR, OV9650_GAIN, GAIN_DEFAULT},
+	{SENSOR, OV9650_BLUE, BLUE_GAIN_DEFAULT},
+	{SENSOR, OV9650_RED, RED_GAIN_DEFAULT},
+
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe0},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x5e},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0xde}
+};
+
+static const unsigned char power_down_ov9650[][3] =
+{
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{SENSOR, OV9650_COM7, 0x80},
+	{SENSOR, OV9650_OFON, 0xf4},
+	{SENSOR, OV9650_MVFP, 0x80},
+	{SENSOR, OV9650_DBLV, 0x3f},
+	{SENSOR, OV9650_RSVD36, 0x49},
+	{SENSOR, OV9650_COM7, 0x05},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}
+};
+
+/* Vertically and horizontally flips the image if matched, needed for machines
+   where the sensor is mounted upside down */
+static const struct dmi_system_id ov9650_flip_dmi_table[] = {
+	{
+		.ident = "ASUS A6VC",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
+		}
+	},
+	{
+		.ident = "ASUS A6VM",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
+		}
+	},
+	{
+		.ident = "ASUS A6JC",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
+		}
+	},
+	{
+		.ident = "ASUS A6Kt",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
+		}
+	},
+	{ }
+};
+
+#endif
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_po1030.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_po1030.c	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,304 @@
+/*
+ * Driver for the po1030 sensor
+ *
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include "m5602_po1030.h"
+
+int po1030_probe(struct m5602_camera *cam)
+{
+	u8 prod_id = 0, ver_id = 0, i;
+
+	if (force_sensor) {
+		if (force_sensor == PO1030_SENSOR) {
+			info("Forcing a %s sensor", po1030.name);
+			return 0;
+		}
+		/* If we want to force another sensor, don't try to probe this
+		 * one */
+		return -ENODEV;
+	}
+
+	info("Probing for a po1030 sensor");
+
+	/* Run the pre-init to actually probe the unit */
+	for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
+		u8 data = preinit_po1030[i][2];
+		if (preinit_po1030[i][0] == SENSOR)
+			po1030_write_sensor(cam,
+				preinit_po1030[i][1], &data, 1);
+		else
+			m5602_write_bridge(cam, preinit_po1030[i][1], data);
+	}
+
+	if (po1030_read_sensor(cam, 0x3, &prod_id, 1))
+		return -ENODEV;
+
+	if (po1030_read_sensor(cam, 0x4, &ver_id, 1))
+		return -ENODEV;
+
+	if ((prod_id == 0x02) && (ver_id == 0xef)) {
+		info("Detected a po1030 sensor");
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+int po1030_read_sensor(struct m5602_camera *cam, const u8 address,
+				u8 *i2c_data, const u8 len)
+{
+	int err, i;
+
+	do {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_STATUS, i2c_data);
+	} while ((*i2c_data & I2C_BUSY) && !err);
+
+	m5602_write_bridge(cam, M5602_XB_I2C_DEV_ADDR,
+			   cam->sensor->i2c_slave_id);
+	m5602_write_bridge(cam, M5602_XB_I2C_REG_ADDR, address);
+	m5602_write_bridge(cam, M5602_XB_I2C_CTRL, 0x10 + len);
+	m5602_write_bridge(cam, M5602_XB_I2C_CTRL, 0x08);
+
+	for (i = 0; i < len; i++) {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+		PDEBUG(DBG_TRACE, "Reading sensor register "
+				"0x%x containing 0x%x ", address, *i2c_data);
+	}
+	return (err < 0) ? err : 0;
+}
+
+int po1030_write_sensor(struct m5602_camera *cam, const u8 address,
+				u8 *i2c_data, const u8 len)
+{
+	int err, i;
+	u8 *p;
+
+	/* The po1030 only supports one byte writes */
+	if (len > 1 || !len)
+		return -EINVAL;
+
+	memcpy(cam->control_buffer, sensor_urb_skeleton,
+				sizeof(sensor_urb_skeleton));
+
+	cam->control_buffer[11] = cam->sensor->i2c_slave_id;
+	cam->control_buffer[15] = address;
+
+	p = cam->control_buffer + 16;
+
+	/* Copy a four byte write sequence for each byte to be written to */
+	for (i = 0; i < len; i++) {
+		memcpy(p, sensor_urb_skeleton + 16, 4);
+		p[3] = i2c_data[i];
+		p += 4;
+		PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+		       address, i2c_data[i]);
+	}
+
+	/* Copy the footer */
+	memcpy(p, sensor_urb_skeleton + 20, 4);
+
+	/* Set the total length */
+	p[3] = 0x10 + len;
+
+	err = usb_control_msg(cam->udev, usb_sndctrlpipe(cam->udev, 0),
+			      0x04, 0x40, 0x19,
+			      0x0000, cam->control_buffer,
+			      20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+	return (err < 0) ? err : 0;
+}
+
+int po1030_init(struct m5602_camera *cam)
+{
+	int i, err = 0;
+
+	/* Init the sensor */
+	for (i = 0; i < ARRAY_SIZE(init_po1030); i++) {
+		u8 data[2] = {0x00, 0x00};
+
+		switch (init_po1030[i][0]) {
+		case BRIDGE:
+			err = m5602_write_bridge(cam,
+				init_po1030[i][1],
+				init_po1030[i][2]);
+			break;
+
+		case SENSOR:
+			data[0] = init_po1030[i][2];
+			err = po1030_write_sensor(cam,
+				init_po1030[i][1], data, 1);
+			break;
+
+		case SENSOR_LONG:
+			data[0] = init_po1030[i][2];
+			data[1] = init_po1030[i][3];
+			err = po1030_write_sensor(cam,
+				init_po1030[i][1], data, 2);
+			break;
+		default:
+			info("Invalid stream command, exiting init");
+			return -EINVAL;
+		}
+	}
+
+	if (dump_sensor)
+		po1030_dump_registers(cam);
+
+	return (err < 0) ? err : 0;
+}
+
+int po1030_power_down(struct m5602_camera *cam)
+{
+	return 0;
+}
+
+int po1030_get_ctrl(struct m5602_camera *cam,
+				 struct v4l2_control *ctrl)
+{
+	u8 i2c_data;
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		err = po1030_read_sensor(cam, PO1030_REG_GLOBALGAIN,
+					       &i2c_data, 1);
+		ctrl->value = i2c_data;
+		PDEBUG(DBG_V4L2_CID, "Read global gain %d", ctrl->value);
+		break;
+
+	case V4L2_CID_EXPOSURE:
+		err = po1030_read_sensor(cam, PO1030_REG_INTEGLINES_H,
+					       &i2c_data, 1);
+		ctrl->value = (i2c_data << 8);
+
+		err = po1030_read_sensor(cam, PO1030_REG_INTEGLINES_M,
+					       &i2c_data, 1);
+		ctrl->value |= i2c_data;
+
+		PDEBUG(DBG_V4L2_CID, "Exposure read as %d", ctrl->value);
+		break;
+
+	case V4L2_CID_RED_BALANCE:
+		err = po1030_read_sensor(cam, PO1030_REG_RED_GAIN,
+					 &i2c_data, 1);
+		ctrl->value = i2c_data;
+		PDEBUG(DBG_V4L2_CID, "Read red gain %d", ctrl->value);
+		break;
+
+	case V4L2_CID_BLUE_BALANCE:
+		err = po1030_read_sensor(cam, PO1030_REG_BLUE_GAIN,
+				&i2c_data, 1);
+		ctrl->value = i2c_data;
+		PDEBUG(DBG_V4L2_CID, "Read blue gain %d", ctrl->value);
+		break;
+
+	default:
+		PDEBUG(DBG_V4L2, "Unknown V4L2 CID, 0x%x", ctrl->value);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int po1030_set_ctrl(struct m5602_camera *cam,
+			   const struct v4l2_control *ctrl)
+{
+	u8 reg_data;
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		reg_data = ctrl->value & 0xff;
+		PDEBUG(DBG_V4L2, "Set global gain to %d", reg_data);
+		err = po1030_write_sensor(cam, PO1030_REG_GLOBALGAIN,
+						&reg_data, 1);
+		break;
+
+	case V4L2_CID_EXPOSURE:
+		PDEBUG(DBG_V4L2, "Set exposure to %d", ctrl->value & 0xffff);
+
+		reg_data = ((ctrl->value & 0xff00) >> 8);
+		PDEBUG(DBG_V4L2, "Set exposure to high byte to 0x%x",
+		       reg_data);
+
+		err = po1030_write_sensor(cam, PO1030_REG_INTEGLINES_H,
+			&reg_data, 1);
+
+		reg_data = (ctrl->value & 0xff);
+		PDEBUG(DBG_V4L2, "Set exposure to low byte to 0x%x",
+		       reg_data);
+		err = po1030_write_sensor(cam, PO1030_REG_INTEGLINES_M,
+								&reg_data, 1);
+		break;
+
+	case V4L2_CID_RED_BALANCE:
+		reg_data = ctrl->value & 0xff;
+		PDEBUG(DBG_V4L2, "Set red gain to %d", reg_data);
+		err = po1030_write_sensor(cam, PO1030_REG_RED_GAIN,
+					  &reg_data, 1);
+		break;
+
+	case V4L2_CID_BLUE_BALANCE:
+		reg_data = ctrl->value & 0xff;
+		PDEBUG(DBG_V4L2, "Set blue gain to %d", reg_data);
+		err = po1030_write_sensor(cam, PO1030_REG_BLUE_GAIN,
+					  &reg_data, 1);
+		break;
+
+
+	default:
+		PDEBUG(DBG_V4L2, "Illegal V4L id %d, value %d",
+		       ctrl->id, ctrl->value);
+		return -EINVAL;
+	}
+
+	return (err < 0) ? err : 0;;
+}
+
+void po1030_dump_registers(struct m5602_camera *cam)
+{
+	int address;
+	u8 value = 0;
+
+	info("Dumping the po1030 sensor core registers");
+	for (address = 0; address < 0x7f; address++) {
+		po1030_read_sensor(cam, address, &value, 1);
+		info("register 0x%x contains 0x%x",
+		     address, value);
+	}
+
+	info("po1030 register state dump complete");
+
+	info("Probing for which registers that are read/write");
+	for (address = 0; address < 0xff; address++) {
+		u8 old_value, ctrl_value;
+		u8 test_value[2] = {0xff, 0xff};
+
+		po1030_read_sensor(cam, address, &old_value, 1);
+		po1030_write_sensor(cam, address, test_value, 1);
+		po1030_read_sensor(cam, address, &ctrl_value, 1);
+
+		if (ctrl_value == test_value[0])
+			info("register 0x%x is writeable", address);
+		else
+			info("register 0x%x is read only", address);
+
+		/* Restore original value */
+		po1030_write_sensor(cam, address, &old_value, 1);
+	}
+}
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_po1030.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_po1030.h	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,469 @@
+/*
+ * Driver for the po1030 sensor.
+ * This is probably a pixel plus sensor but we haven't identified it yet
+ *
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Register defines taken from Pascal Stangs Proxycon Armlib
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_PO1030_H_
+#define M5602_PO1030_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define PO1030_REG_DEVID_H		0x00
+#define PO1030_REG_DEVID_L		0x01
+#define PO1030_REG_FRAMEWIDTH_H		0x04
+#define PO1030_REG_FRAMEWIDTH_L		0x05
+#define PO1030_REG_FRAMEHEIGHT_H	0x06
+#define PO1030_REG_FRAMEHEIGHT_L	0x07
+#define PO1030_REG_WINDOWX_H		0x08
+#define PO1030_REG_WINDOWX_L		0x09
+#define PO1030_REG_WINDOWY_H		0x0a
+#define PO1030_REG_WINDOWY_L		0x0b
+#define PO1030_REG_WINDOWWIDTH_H	0x0c
+#define PO1030_REG_WINDOWWIDTH_L	0x0d
+#define PO1030_REG_WINDOWHEIGHT_H	0x0e
+#define PO1030_REG_WINDOWHEIGHT_L	0x0f
+
+#define PO1030_REG_GLOBALIBIAS		0x12
+#define PO1030_REG_PIXELIBIAS		0x13
+
+#define PO1030_REG_GLOBALGAIN		0x15
+#define PO1030_REG_RED_GAIN		0x16
+#define PO1030_REG_GREEN_1_GAIN		0x17
+#define PO1030_REG_BLUE_GAIN		0x18
+#define PO1030_REG_GREEN_2_GAIN		0x19
+
+#define PO1030_REG_INTEGLINES_H		0x1a
+#define PO1030_REG_INTEGLINES_M		0x1b
+#define PO1030_REG_INTEGLINES_L		0x1c
+
+#define PO1030_REG_CONTROL1		0x1d
+#define PO1030_REG_CONTROL2		0x1e
+#define PO1030_REG_CONTROL3		0x1f
+#define PO1030_REG_CONTROL4		0x20
+
+#define PO1030_REG_PERIOD50_H		0x23
+#define PO1030_REG_PERIOD50_L		0x24
+#define PO1030_REG_PERIOD60_H		0x25
+#define PO1030_REG_PERIOD60_L		0x26
+#define PO1030_REG_REGCLK167		0x27
+#define PO1030_REG_DELTA50		0x28
+#define PO1030_REG_DELTA60		0x29
+
+#define PO1030_REG_ADCOFFSET		0x2c
+
+/* Gamma Correction Coeffs */
+#define PO1030_REG_GC0			0x2d
+#define PO1030_REG_GC1			0x2e
+#define PO1030_REG_GC2			0x2f
+#define PO1030_REG_GC3			0x30
+#define PO1030_REG_GC4			0x31
+#define PO1030_REG_GC5			0x32
+#define PO1030_REG_GC6			0x33
+#define PO1030_REG_GC7			0x34
+
+/* Color Transform Matrix */
+#define PO1030_REG_CT0			0x35
+#define PO1030_REG_CT1			0x36
+#define PO1030_REG_CT2			0x37
+#define PO1030_REG_CT3			0x38
+#define PO1030_REG_CT4			0x39
+#define PO1030_REG_CT5			0x3a
+#define PO1030_REG_CT6			0x3b
+#define PO1030_REG_CT7			0x3c
+#define PO1030_REG_CT8			0x3d
+
+#define PO1030_REG_AUTOCTRL1		0x3e
+#define PO1030_REG_AUTOCTRL2		0x3f
+
+#define PO1030_REG_YTARGET		0x40
+#define PO1030_REG_GLOBALGAINMIN	0x41
+#define PO1030_REG_GLOBALGAINMAX	0x42
+
+/* Output format control */
+#define PO1030_REG_OUTFORMCTRL1		0x5a
+#define PO1030_REG_OUTFORMCTRL2		0x5b
+#define PO1030_REG_OUTFORMCTRL3		0x5c
+#define PO1030_REG_OUTFORMCTRL4		0x5d
+#define PO1030_REG_OUTFORMCTRL5		0x5e
+
+/* Imaging coefficients */
+#define PO1030_REG_YBRIGHT		0x73
+#define PO1030_REG_YCONTRAST		0x74
+#define PO1030_REG_YSATURATION		0x75
+
+/*****************************************************************************/
+
+#define PO1030_GLOBAL_GAIN_DEFAULT	0x12
+#define PO1030_EXPOSURE_DEFAULT		0xf0ff
+#define PO1030_BLUE_GAIN_DEFAULT 	0x40
+#define PO1030_RED_GAIN_DEFAULT 	0x40
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+int po1030_get_ctrl(struct m5602_camera *cam,
+			   struct v4l2_control *ctrl);
+int po1030_set_ctrl(struct m5602_camera *cam,
+			   const struct v4l2_control *ctrl);
+int po1030_probe(struct m5602_camera *cam);
+int po1030_init(struct m5602_camera *cam);
+int po1030_power_down(struct m5602_camera *cam);
+
+void po1030_dump_registers(struct m5602_camera *cam);
+
+int po1030_read_sensor(struct m5602_camera *cam, const u8 address,
+			      u8 *i2c_data, const u8 len);
+int po1030_write_sensor(struct m5602_camera *cam, const u8 address,
+			       u8 *i2c_data, const u8 len);
+
+static struct m5602_sensor po1030 = {
+	.name = "PO1030",
+
+	.i2c_slave_id = 0xdc,
+
+	.probe = po1030_probe,
+	.init = po1030_init,
+	.power_down = po1030_power_down,
+	.get_ctrl = po1030_get_ctrl,
+	.set_ctrl = po1030_set_ctrl,
+	.qctrl = {
+		{
+			.id 		= V4L2_CID_GAIN,
+			.type 		= V4L2_CTRL_TYPE_INTEGER,
+			.name 		= "gain",
+			.minimum 	= 0x00,
+			.maximum 	= 0xff,
+			.step 		= 0x1,
+			.default_value 	= PO1030_GLOBAL_GAIN_DEFAULT,
+			.flags         	= V4L2_CTRL_FLAG_SLIDER
+		},
+
+		{
+			.id 		= V4L2_CID_EXPOSURE,
+			.type 		= V4L2_CTRL_TYPE_INTEGER,
+			.name 		= "exposure",
+			.minimum 	= 0x00,
+			.maximum 	= 0xffff,
+			.step 		= 0x1,
+			.default_value 	= PO1030_EXPOSURE_DEFAULT,
+			.flags         	= V4L2_CTRL_FLAG_SLIDER
+		},
+		{
+			.id 		= V4L2_CID_RED_BALANCE,
+			.type 		= V4L2_CTRL_TYPE_INTEGER,
+			.name 		= "red balance",
+			.minimum 	= 0x00,
+			.maximum 	= 0xff,
+			.step 		= 0x1,
+			.default_value 	= PO1030_RED_GAIN_DEFAULT,
+			.flags         	= V4L2_CTRL_FLAG_SLIDER
+		},
+		{
+			.id 		= V4L2_CID_BLUE_BALANCE,
+			.type 		= V4L2_CTRL_TYPE_INTEGER,
+			.name 		= "blue balance",
+			.minimum 	= 0x00,
+			.maximum 	= 0xff,
+			.step 		= 0x1,
+			.default_value 	= PO1030_BLUE_GAIN_DEFAULT,
+			.flags         	= V4L2_CTRL_FLAG_SLIDER
+		}
+	},
+
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		}
+	},
+	.current_fmt = {
+		.width = M5602_DEFAULT_FRAME_WIDTH,
+		.height = M5602_DEFAULT_FRAME_HEIGHT,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 1,
+		.colorspace = V4L2_COLORSPACE_SRGB
+	}
+};
+
+static const unsigned char preinit_po1030[][3] =
+{
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+
+	{SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x87},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00},
+
+	{SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00}
+};
+
+static const unsigned char init_po1030[][4] =
+{
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	/*sequence 1*/
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	/*end of sequence 1*/
+
+	/*sequence 2 (same as stop sequence)*/
+	{SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	/*end of sequence 2*/
+
+	/*sequence 5*/
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x87},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00},
+	/*end of sequence 5*/
+
+	/*sequence 2 stop */
+	{SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	/*end of sequence 2 stop */
+
+/* ---------------------------------
+ * end of init - begin of start
+ * --------------------------------- */
+
+	/*sequence 3*/
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	/*end of sequence 3*/
+	/*sequence 4*/
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+
+	{SENSOR, PO1030_REG_AUTOCTRL2, 0x04},
+
+	/* Set the width to 751 */
+	{SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02},
+	{SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef},
+
+	/* Set the height to 540 */
+	{SENSOR, PO1030_REG_FRAMEHEIGHT_H, 0x02},
+	{SENSOR, PO1030_REG_FRAMEHEIGHT_L, 0x1c},
+
+	/* Set the x window to 1 */
+	{SENSOR, PO1030_REG_WINDOWX_H, 0x00},
+	{SENSOR, PO1030_REG_WINDOWX_L, 0x01},
+
+	/* Set the y window to 1 */
+	{SENSOR, PO1030_REG_WINDOWY_H, 0x00},
+	{SENSOR, PO1030_REG_WINDOWX_L, 0x01},
+
+	{SENSOR, PO1030_REG_WINDOWWIDTH_H, 0x02},
+	{SENSOR, PO1030_REG_WINDOWWIDTH_L, 0x87},
+	{SENSOR, PO1030_REG_WINDOWHEIGHT_H, 0x01},
+	{SENSOR, PO1030_REG_WINDOWHEIGHT_L, 0xe3},
+
+	{SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04},
+	{SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04},
+	{SENSOR, PO1030_REG_AUTOCTRL1, 0x08},
+	{SENSOR, PO1030_REG_CONTROL2, 0x03},
+	{SENSOR, 0x21, 0x90},
+	{SENSOR, PO1030_REG_YTARGET, 0x60},
+	{SENSOR, 0x59, 0x13},
+	{SENSOR, PO1030_REG_OUTFORMCTRL1, 0x40},
+	{SENSOR, 0x5f, 0x00},
+	{SENSOR, 0x60, 0x80},
+	{SENSOR, 0x78, 0x14},
+	{SENSOR, 0x6f, 0x01},
+	{SENSOR, PO1030_REG_CONTROL1, 0x18},
+	{SENSOR, PO1030_REG_GLOBALGAINMAX, 0x14},
+	{SENSOR, 0x63, 0x38},
+	{SENSOR, 0x64, 0x38},
+	{SENSOR, PO1030_REG_CONTROL1, 0x58},
+	{SENSOR, PO1030_REG_RED_GAIN, 0x30},
+	{SENSOR, PO1030_REG_GREEN_1_GAIN, 0x30},
+	{SENSOR, PO1030_REG_BLUE_GAIN, 0x30},
+	{SENSOR, PO1030_REG_GREEN_2_GAIN, 0x30},
+	{SENSOR, PO1030_REG_GC0, 0x10},
+	{SENSOR, PO1030_REG_GC1, 0x20},
+	{SENSOR, PO1030_REG_GC2, 0x40},
+	{SENSOR, PO1030_REG_GC3, 0x60},
+	{SENSOR, PO1030_REG_GC4, 0x80},
+	{SENSOR, PO1030_REG_GC5, 0xa0},
+	{SENSOR, PO1030_REG_GC6, 0xc0},
+	{SENSOR, PO1030_REG_GC7, 0xff},
+	/*end of sequence 4*/
+	/*sequence 5*/
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x7e},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00},
+	/*end of sequence 5*/
+
+	/*sequence 6*/
+	/* Changing 40 in f0 the image becomes green in bayer mode and red in
+	 * rgb mode */
+	{SENSOR, PO1030_REG_RED_GAIN, PO1030_RED_GAIN_DEFAULT},
+	/* in changing 40 in f0 the image becomes green in bayer mode and red in
+	 * rgb mode */
+	{SENSOR, PO1030_REG_BLUE_GAIN, PO1030_BLUE_GAIN_DEFAULT},
+
+	/* with a very low lighted environment increase the exposure but
+	 * decrease the FPS (Frame Per Second) */
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+
+	/* Controls high exposure more than SENSOR_LOW_EXPOSURE, use only in
+	 * low lighted environment (f0 is more than ff ?)*/
+	{SENSOR, PO1030_REG_INTEGLINES_H, ((PO1030_EXPOSURE_DEFAULT >> 2)
+		& 0xff)},
+
+	/* Controls middle exposure, use only in high lighted environment */
+	{SENSOR, PO1030_REG_INTEGLINES_M, PO1030_EXPOSURE_DEFAULT & 0xff},
+
+	/* Controls clarity (not sure) */
+	{SENSOR, PO1030_REG_INTEGLINES_L, 0x00},
+	/* Controls gain (the image is more lighted) */
+	{SENSOR, PO1030_REG_GLOBALGAIN, PO1030_GLOBAL_GAIN_DEFAULT},
+
+	/* Sets the width */
+	{SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02},
+	{SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef}
+	/*end of sequence 6*/
+};
+
+#endif
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_s5k4aa.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_s5k4aa.c	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,362 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include "m5602_s5k4aa.h"
+
+int s5k4aa_probe(struct m5602_camera *cam)
+{
+	u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};
+	int i, err = 0;
+
+	if (force_sensor) {
+		if (force_sensor == S5K4AA_SENSOR) {
+			info("Forcing a %s sensor", s5k4aa.name);
+			return 0;
+		}
+		/* If we want to force another sensor, don't try to probe this
+		 * one */
+		return -ENODEV;
+	}
+
+	info("Probing for a s5k4aa sensor");
+
+	/* Preinit the sensor */
+	for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
+		u8 data[2] = {0x00, 0x00};
+
+		switch (preinit_s5k4aa[i][0]) {
+		case BRIDGE:
+			err = m5602_write_bridge(cam,
+						preinit_s5k4aa[i][1],
+						preinit_s5k4aa[i][2]);
+			break;
+
+		case SENSOR:
+			data[0] = preinit_s5k4aa[i][2];
+			err = s5k4aa_write_sensor(cam,
+					preinit_s5k4aa[i][1], data, 1);
+			break;
+
+		case SENSOR_LONG:
+			data[0] = preinit_s5k4aa[i][2];
+			data[1] = preinit_s5k4aa[i][3];
+			err = s5k4aa_write_sensor(cam,
+					preinit_s5k4aa[i][1], data, 2);
+			break;
+		default:
+			info("Invalid stream command, exiting init");
+			return -EINVAL;
+		}
+	}
+
+	/* Test some registers, but we don't know their exact meaning yet */
+	if (s5k4aa_read_sensor(cam, 0x00, prod_id, sizeof(prod_id)))
+		return -ENODEV;
+
+	if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
+		return -ENODEV;
+	else
+		info("Detected a s5k4aa sensor");
+
+	return 0;
+}
+
+int s5k4aa_read_sensor(struct m5602_camera *cam, const u8 address,
+		       u8 *i2c_data, const u8 len)
+{
+	int err, i;
+
+	do {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_STATUS, i2c_data);
+	} while ((*i2c_data & I2C_BUSY) && !err);
+
+	m5602_write_bridge(cam, M5602_XB_I2C_DEV_ADDR,
+			   cam->sensor->i2c_slave_id);
+	m5602_write_bridge(cam, M5602_XB_I2C_REG_ADDR, address);
+	m5602_write_bridge(cam, M5602_XB_I2C_CTRL, 0x18 + len);
+
+	do {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_STATUS, i2c_data);
+	} while ((*i2c_data & I2C_BUSY) && !err);
+
+	for (i = 0; i < len; i++) {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+		PDEBUG(DBG_TRACE, "Reading sensor register "
+				  "0x%x containing 0x%x ", address, *i2c_data);
+	}
+	return (err < 0) ? err : 0;
+}
+
+int s5k4aa_write_sensor(struct m5602_camera *cam, const u8 address,
+			u8 *i2c_data, const u8 len)
+{
+	int err, i;
+	u8 *p;
+
+	/* No sensor with a data width larger than 16 bits has yet been seen */
+	if (len > 2 || !len)
+		return -EINVAL;
+
+	memcpy(cam->control_buffer, sensor_urb_skeleton,
+	       sizeof(sensor_urb_skeleton));
+
+	cam->control_buffer[11] = cam->sensor->i2c_slave_id;
+	cam->control_buffer[15] = address;
+
+	/* Special case larger sensor writes */
+	p = cam->control_buffer + 16;
+
+	/* Copy a four byte write sequence for each byte to be written to */
+	for (i = 0; i < len; i++) {
+		memcpy(p, sensor_urb_skeleton + 16, 4);
+		p[3] = i2c_data[i];
+		p += 4;
+		PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+		       address, i2c_data[i]);
+	}
+
+	/* Copy the tailer */
+	memcpy(p, sensor_urb_skeleton + 20, 4);
+
+	/* Set the total length */
+	p[3] = 0x10 + len;
+
+	err = usb_control_msg(cam->udev, usb_sndctrlpipe(cam->udev, 0),
+			      0x04, 0x40, 0x19,
+			      0x0000, cam->control_buffer,
+			      20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+	return (err < 0) ? err : 0;
+}
+
+int s5k4aa_init(struct m5602_camera *cam)
+{
+	int i, err = 0;
+
+	for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {
+		u8 data[2] = {0x00, 0x00};
+
+		switch (init_s5k4aa[i][0]) {
+		case BRIDGE:
+			err = m5602_write_bridge(cam,
+				init_s5k4aa[i][1],
+				init_s5k4aa[i][2]);
+			break;
+
+		case SENSOR:
+			data[0] = init_s5k4aa[i][2];
+			err = s5k4aa_write_sensor(cam,
+				init_s5k4aa[i][1], data, 1);
+			break;
+
+		case SENSOR_LONG:
+			data[0] = init_s5k4aa[i][2];
+			data[1] = init_s5k4aa[i][3];
+			err = s5k4aa_write_sensor(cam,
+				init_s5k4aa[i][1], data, 2);
+			break;
+		default:
+			info("Invalid stream command, exiting init");
+			return -EINVAL;
+		}
+	}
+
+	if (dump_sensor)
+		s5k4aa_dump_registers(cam);
+
+	if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) {
+		u8 data = 0x02;
+		info("vertical flip quirk active");
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		s5k4aa_read_sensor(cam, S5K4AA_READ_MODE, &data, 1);
+		data |= S5K4AA_RM_V_FLIP;
+		data &= ~S5K4AA_RM_H_FLIP;
+		s5k4aa_write_sensor(cam, S5K4AA_READ_MODE, &data, 1);
+
+		/* Decrement COLSTART to preserve color order (BGGR) */
+		s5k4aa_read_sensor(cam, S5K4AA_COLSTART_LO, &data, 1);
+		data--;
+		s5k4aa_write_sensor(cam, S5K4AA_COLSTART_LO, &data, 1);
+
+		/* Increment ROWSTART to preserve color order (BGGR) */
+		s5k4aa_read_sensor(cam, S5K4AA_ROWSTART_LO, &data, 1);
+		data++;
+		s5k4aa_write_sensor(cam, S5K4AA_ROWSTART_LO, &data, 1);
+	}
+
+	return (err < 0) ? err : 0;
+}
+
+int s5k4aa_power_down(struct m5602_camera *cam)
+{
+	return 0;
+}
+
+int s5k4aa_get_ctrl(struct m5602_camera *cam, struct v4l2_control *ctrl)
+{
+	u8 data = 0x02;
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_VFLIP:
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		s5k4aa_read_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		ctrl->value = (data & S5K4AA_RM_V_FLIP) >> 7;
+		PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", ctrl->value);
+		break;
+
+	case V4L2_CID_HFLIP:
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		s5k4aa_read_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		ctrl->value = (data & S5K4AA_RM_H_FLIP) >> 6;
+		PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", ctrl->value);
+		break;
+
+	case V4L2_CID_GAIN:
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		s5k4aa_read_sensor(cam, S5K4AA_GAIN_2, &data, 1);
+		ctrl->value = data;
+		PDEBUG(DBG_V4L2_CID, "Read gain %d", ctrl->value);
+		break;
+
+	case V4L2_CID_EXPOSURE:
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		s5k4aa_read_sensor(cam, S5K4AA_EXPOSURE_HI, &data, 1);
+		ctrl->value = data << 8;
+		s5k4aa_read_sensor(cam, S5K4AA_EXPOSURE_LO, &data, 1);
+		ctrl->value |= data;
+		PDEBUG(DBG_V4L2_CID, "Read exposure %d", ctrl->value);
+		break;
+
+	default:
+		PDEBUG(DBG_V4L2_CID, "Unknown V4L2 CID 0x%x", ctrl->value);
+		err = -EINVAL;
+	}
+	return (err < 0) ? err : 0;
+}
+
+int s5k4aa_set_ctrl(struct m5602_camera *cam,
+			   const struct v4l2_control *ctrl)
+{
+	u8 data = 0x02;
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_VFLIP:
+		PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d",
+			ctrl->value);
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		s5k4aa_write_sensor(cam, S5K4AA_READ_MODE, &data, 1);
+		data = ((data & ~S5K4AA_RM_V_FLIP)
+			| ((ctrl->value & 0x01) << 7));
+		s5k4aa_write_sensor(cam, S5K4AA_READ_MODE, &data, 1);
+		if (ctrl->value) {
+			s5k4aa_read_sensor(cam, S5K4AA_ROWSTART_LO, &data, 1);
+			data++;
+			s5k4aa_write_sensor(cam, S5K4AA_ROWSTART_LO, &data, 1);
+		} else {
+			s5k4aa_read_sensor(cam, S5K4AA_ROWSTART_LO, &data, 1);
+			data--;
+			s5k4aa_write_sensor(cam, S5K4AA_ROWSTART_LO, &data, 1);
+		}
+		break;
+
+	case V4L2_CID_HFLIP:
+		PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d",
+			ctrl->value);
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		s5k4aa_write_sensor(cam, S5K4AA_READ_MODE, &data, 1);
+		data = ((data & ~S5K4AA_RM_H_FLIP)
+			| ((ctrl->value & 0x01) << 6));
+		s5k4aa_write_sensor(cam, S5K4AA_READ_MODE, &data, 1);
+		if (ctrl->value) {
+			s5k4aa_read_sensor(cam, S5K4AA_COLSTART_LO, &data, 1);
+			data++;
+			s5k4aa_write_sensor(cam, S5K4AA_COLSTART_LO, &data, 1);
+		} else {
+			s5k4aa_read_sensor(cam, S5K4AA_COLSTART_LO, &data, 1);
+			data--;
+			s5k4aa_write_sensor(cam, S5K4AA_COLSTART_LO, &data, 1);
+		}
+		break;
+
+	case V4L2_CID_GAIN:
+		PDEBUG(DBG_V4L2_CID, "Set gain to %d", ctrl->value);
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		data = ctrl->value & 0xff;
+		s5k4aa_write_sensor(cam, S5K4AA_GAIN_2, &data, 1);
+		break;
+
+	case V4L2_CID_EXPOSURE:
+		PDEBUG(DBG_V4L2_CID, "Set exposure to %d", ctrl->value);
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &data, 1);
+		data = (ctrl->value >> 8) & 0xff;
+		s5k4aa_write_sensor(cam, S5K4AA_EXPOSURE_HI, &data, 1);
+		data = ctrl->value & 0xff;
+		s5k4aa_write_sensor(cam, S5K4AA_EXPOSURE_LO, &data, 1);
+		break;
+	default:
+		PDEBUG(DBG_V4L2_CID, "Illegal V4L2 CID %d, value %d",
+		       ctrl->id, ctrl->value);
+		err = -EINVAL;
+	}
+	return (err < 0) ? err : 0;
+}
+
+void s5k4aa_dump_registers(struct m5602_camera *cam)
+{
+	int address;
+	u8 page, old_page;
+	s5k4aa_read_sensor(cam, S5K4AA_PAGE_MAP, &old_page, 1);
+	for (page = 0; page < 16; page++) {
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &page, 1);
+		info("Dumping the s5k4aa register state for page 0x%x", page);
+		for (address = 0; address <= 0xff; address++) {
+			u8 value = 0;
+			s5k4aa_read_sensor(cam, address, &value, 1);
+			info("register 0x%x contains 0x%x",
+			     address, value);
+		}
+	}
+	info("s5k4aa register state dump complete");
+
+	for (page = 0; page < 16; page++) {
+		s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &page, 1);
+		info("Probing for which registers that are "
+		     "read/write for page 0x%x", page);
+		for (address = 0; address <= 0xff; address++) {
+			u8 old_value, ctrl_value, test_value = 0xff;
+
+			s5k4aa_read_sensor(cam, address, &old_value, 1);
+			s5k4aa_write_sensor(cam, address, &test_value, 1);
+			s5k4aa_read_sensor(cam, address, &ctrl_value, 1);
+
+			if (ctrl_value == test_value)
+				info("register 0x%x is writeable", address);
+			else
+				info("register 0x%x is read only", address);
+
+			/* Restore original value */
+			s5k4aa_write_sensor(cam, address, &old_value, 1);
+		}
+	}
+	info("Read/write register probing complete");
+	s5k4aa_write_sensor(cam, S5K4AA_PAGE_MAP, &old_page, 1);
+}
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_s5k4aa.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_s5k4aa.h	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,351 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_S5K4AA_H_
+#define M5602_S5K4AA_H_
+
+#include <linux/dmi.h>
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define S5K4AA_PAGE_MAP			0xec
+
+/* Sensor register definitions for page 0x02 */
+#define S5K4AA_READ_MODE		0x03
+#define S5K4AA_ROWSTART_HI		0x04
+#define S5K4AA_ROWSTART_LO		0x05
+#define S5K4AA_COLSTART_HI		0x06
+#define S5K4AA_COLSTART_LO		0x07
+#define S5K4AA_WINDOW_HEIGHT_HI		0x08
+#define S5K4AA_WINDOW_HEIGHT_LO		0x09
+#define S5K4AA_WINDOW_WIDTH_HI		0x0a
+#define S5K4AA_WINDOW_WIDTH_LO		0x0b
+#define S5K4AA_GLOBAL_GAIN__		0x0f /* Only a guess ATM !!! */
+#define S5K4AA_H_BLANK_HI__		0x1d /* Only a guess ATM !!! sync lost
+						if too low, reduces frame rate
+						if too high */
+#define S5K4AA_H_BLANK_LO__		0x1e /* Only a guess ATM !!! */
+#define S5K4AA_EXPOSURE_HI		0x17
+#define S5K4AA_EXPOSURE_LO		0x18
+#define S5K4AA_GAIN_1			0x1f /* (digital?) gain : 5 bits */
+#define S5K4AA_GAIN_2			0x20 /* (analogue?) gain : 7 bits */
+
+#define S5K4AA_RM_ROW_SKIP_4X		0x08
+#define S5K4AA_RM_ROW_SKIP_2X		0x04
+#define S5K4AA_RM_COL_SKIP_4X		0x02
+#define S5K4AA_RM_COL_SKIP_2X		0x01
+#define S5K4AA_RM_H_FLIP		0x40
+#define S5K4AA_RM_V_FLIP		0x80
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+int s5k4aa_get_ctrl(struct m5602_camera *cam,
+			   struct v4l2_control *ctrl);
+int s5k4aa_set_ctrl(struct m5602_camera *cam,
+			   const struct v4l2_control *ctrl);
+int s5k4aa_probe(struct m5602_camera *cam);
+int s5k4aa_init(struct m5602_camera *cam);
+int s5k4aa_power_down(struct m5602_camera *cam);
+
+void s5k4aa_dump_registers(struct m5602_camera *cam);
+
+int s5k4aa_read_sensor(struct m5602_camera *cam, const u8 address,
+		       u8 *i2c_data, const u8 len);
+int s5k4aa_write_sensor(struct m5602_camera *cam, const u8 address,
+			u8 *i2c_data, const u8 len);
+
+static struct m5602_sensor s5k4aa = {
+	.name = "S5K4AA",
+	.probe = s5k4aa_probe,
+	.init = s5k4aa_init,
+	.power_down = s5k4aa_power_down,
+	.get_ctrl = s5k4aa_get_ctrl,
+	.set_ctrl = s5k4aa_set_ctrl,
+	.read_sensor = s5k4aa_read_sensor,
+	.write_sensor = s5k4aa_write_sensor,
+	.i2c_slave_id = 0x5a,
+	.qctrl = {
+	{
+		.id 		= V4L2_CID_VFLIP,
+		.type 		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name 		= "vertical flip",
+		.minimum 	= 0,
+		.maximum 	= 1,
+		.step 		= 1,
+		.default_value 	= 0
+	},
+	{
+		.id 		= V4L2_CID_HFLIP,
+		.type 		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name 		= "horizontal flip",
+		.minimum 	= 0,
+		.maximum 	= 1,
+		.step 		= 1,
+		.default_value 	= 0
+	},
+	{
+		.id		= V4L2_CID_GAIN,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Gain",
+		.minimum	= 0,
+		.maximum	= 127,
+		.step		= 1,
+		.default_value	= 0xa0,
+		.flags		= V4L2_CTRL_FLAG_SLIDER
+	},
+	{
+		.id		= V4L2_CID_EXPOSURE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Exposure",
+		.minimum	= 13,
+		.maximum	= 0xfff,
+		.step		= 1,
+		.default_value	= 0x100,
+		.flags		= V4L2_CTRL_FLAG_SLIDER
+	}
+	},
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		}
+	},
+	.current_fmt = {
+		.width = M5602_DEFAULT_FRAME_WIDTH,
+		.height = M5602_DEFAULT_FRAME_HEIGHT,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 1,
+		.colorspace = V4L2_COLORSPACE_SRGB
+	}
+};
+
+static const unsigned char preinit_s5k4aa[][4] =
+{
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+	{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
+};
+
+static const unsigned char init_s5k4aa[][4] =
+{
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+	{SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
+	{SENSOR, 0x36, 0x01, 0x00},
+	{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, 0x7b, 0xff, 0x00},
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+	{SENSOR, 0x0c, 0x05, 0x00},
+	{SENSOR, 0x02, 0x0e, 0x00},
+	{SENSOR, S5K4AA_GAIN_1, 0x0f, 0x00},
+	{SENSOR, S5K4AA_GAIN_2, 0x00, 0x00},
+	{SENSOR, S5K4AA_GLOBAL_GAIN__, 0x01, 0x00},
+	{SENSOR, 0x11, 0x00, 0x00},
+	{SENSOR, 0x12, 0x00, 0x00},
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+	{SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
+	{SENSOR, 0x37, 0x00, 0x00},
+	{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+	{SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00},
+	{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+	{SENSOR, S5K4AA_COLSTART_LO, 0x0b, 0x00},
+	{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
+	{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc4, 0x00},
+	{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+	{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x08, 0x00},
+	{SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
+	{SENSOR, S5K4AA_H_BLANK_LO__, 0x48, 0x00},
+	{SENSOR, S5K4AA_EXPOSURE_HI, 0x00, 0x00},
+	{SENSOR, S5K4AA_EXPOSURE_LO, 0x43, 0x00},
+	{SENSOR, 0x11, 0x04, 0x00},
+	{SENSOR, 0x12, 0xc3, 0x00},
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	/* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	/* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
+
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+	{SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
+		| S5K4AA_RM_COL_SKIP_2X, 0x00},
+	/* 0x37 : Fix image stability when light is too bright and improves
+	 * image quality in 640x480, but worsens it in 1280x1024 */
+	{SENSOR, 0x37, 0x01, 0x00},
+	/* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
+	{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+	{SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00},
+	{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+	{SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
+	/* window_height_hi, window_height_lo : 960 = 0x03c0 */
+	{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
+	{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
+	/* window_width_hi, window_width_lo : 1280 = 0x0500 */
+	{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+	{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
+	{SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
+	{SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
+	{SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
+	{SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
+	{SENSOR, 0x11, 0x04, 0x00},
+	{SENSOR, 0x12, 0xc3, 0x00},
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+	{SENSOR, 0x02, 0x0e, 0x00},
+	{SENSOR_LONG, S5K4AA_GLOBAL_GAIN__, 0x0f, 0x00},
+	{SENSOR, S5K4AA_GAIN_1, 0x0b, 0x00},
+	{SENSOR, S5K4AA_GAIN_2, 0xa0, 0x00}
+};
+
+static const struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
+	{
+		.ident = "Fujitsu-Siemens Amilo Xa 2528",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
+		}
+	},
+	{
+		.ident = "Fujitsu-Siemens Amilo Xi 2550",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
+		}
+	},
+		{
+		.ident = "MSI GX700",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+			DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
+		}
+	},
+	{ }
+};
+
+#endif
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_s5k83a.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_s5k83a.c	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,365 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include "m5602_s5k83a.h"
+
+int s5k83a_probe(struct m5602_camera *cam)
+{
+	u8 prod_id = 0, ver_id = 0;
+	int i, err = 0;
+
+	if (force_sensor) {
+		if (force_sensor == S5K83A_SENSOR) {
+			info("Forcing a %s sensor", s5k83a.name);
+			return 0;
+		}
+		/* If we want to force another sensor, don't try to probe this
+		 * one */
+		return -ENODEV;
+	}
+
+	info("Probing for a s5k83a sensor");
+
+	/* Preinit the sensor */
+	for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
+		u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
+		if (preinit_s5k83a[i][0] == SENSOR)
+			err = s5k83a_write_sensor(cam, preinit_s5k83a[i][1],
+				data, 2);
+		else
+			err = m5602_write_bridge(cam, preinit_s5k83a[i][1],
+				data[0]);
+	}
+
+	/* We don't know what register (if any) that contain the product id
+	 * Just pick the first addresses that seem to produce the same results
+	 * on multiple machines */
+	if (s5k83a_read_sensor(cam, 0x00, &prod_id, 1))
+		return -ENODEV;
+
+	if (s5k83a_read_sensor(cam, 0x01, &ver_id, 1))
+		return -ENODEV;
+
+	if ((prod_id == 0xff) || (ver_id == 0xff))
+		return -ENODEV;
+	else
+		info("Detected a s5k83a sensor");
+
+	return 0;
+}
+
+int s5k83a_read_sensor(struct m5602_camera *cam, const u8 address,
+			      u8 *i2c_data, const u8 len)
+{
+	int err, i;
+
+	do {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_STATUS, i2c_data);
+	} while ((*i2c_data & I2C_BUSY) && !err);
+
+	m5602_write_bridge(cam, M5602_XB_I2C_DEV_ADDR,
+			   cam->sensor->i2c_slave_id);
+	m5602_write_bridge(cam, M5602_XB_I2C_REG_ADDR, address);
+	m5602_write_bridge(cam, M5602_XB_I2C_CTRL, 0x18 + len);
+
+	do {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_STATUS, i2c_data);
+	} while ((*i2c_data & I2C_BUSY) && !err);
+
+	for (i = 0; i < len; i++) {
+		err = m5602_read_bridge(cam, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+		PDEBUG(DBG_TRACE, "Reading sensor register "
+				  "0x%x containing 0x%x ", address, *i2c_data);
+	}
+	return (err < 0) ? err : 0;
+}
+
+int s5k83a_write_sensor(struct m5602_camera *cam, const u8 address,
+			       u8 *i2c_data, const u8 len)
+{
+	int err, i;
+	u8 *p;
+
+	/* No sensor with a data width larger than 16 bits has yet been seen */
+	if (len > 2 || !len)
+		return -EINVAL;
+
+	memcpy(cam->control_buffer, sensor_urb_skeleton,
+	       sizeof(sensor_urb_skeleton));
+
+	cam->control_buffer[11] = cam->sensor->i2c_slave_id;
+	cam->control_buffer[15] = address;
+
+	/* Special case larger sensor writes */
+	p = cam->control_buffer + 16;
+
+	/* Copy a four byte write sequence for each byte to be written to */
+	for (i = 0; i < len; i++) {
+		memcpy(p, sensor_urb_skeleton + 16, 4);
+		p[3] = i2c_data[i];
+		p += 4;
+		PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+		       address, i2c_data[i]);
+	}
+
+	/* Copy the tailer */
+	memcpy(p, sensor_urb_skeleton + 20, 4);
+
+	/* Set the total length */
+	p[3] = 0x10 + len;
+
+	err = usb_control_msg(cam->udev, usb_sndctrlpipe(cam->udev, 0),
+			      0x04, 0x40, 0x19,
+			      0x0000, cam->control_buffer,
+			      20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+	return (err < 0) ? err : 0;
+}
+
+int s5k83a_init(struct m5602_camera *cam)
+{
+	int i, err = 0;
+
+	for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) {
+		u8 data[2] = {0x00, 0x00};
+
+		switch (init_s5k83a[i][0]) {
+		case BRIDGE:
+			err = m5602_write_bridge(cam,
+					init_s5k83a[i][1],
+					init_s5k83a[i][2]);
+			break;
+
+		case SENSOR:
+			data[0] = init_s5k83a[i][2];
+			err = s5k83a_write_sensor(cam,
+				init_s5k83a[i][1], data, 1);
+			break;
+
+		case SENSOR_LONG:
+			data[0] = init_s5k83a[i][2];
+			data[1] = init_s5k83a[i][3];
+			err = s5k83a_write_sensor(cam,
+				init_s5k83a[i][1], data, 2);
+			break;
+		default:
+			info("Invalid stream command, exiting init");
+			return -EINVAL;
+		}
+	}
+
+	if (dump_sensor)
+		s5k83a_dump_registers(cam);
+
+	return (err < 0) ? err : 0;
+}
+
+int s5k83a_power_down(struct m5602_camera *cam)
+{
+	return 0;
+}
+
+int s5k83a_get_ctrl(struct m5602_camera *cam, struct v4l2_control *ctrl)
+{
+	int err = 0;
+	u8 reg_data;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		err = s5k83a_get_brightness(cam, &reg_data);
+		ctrl->value = reg_data;
+		PDEBUG(DBG_V4L2_CID, "get brightness 0x%x", ctrl->value);
+		break;
+
+	case V4L2_CID_WHITENESS:
+		err = s5k83a_get_whiteness(cam, &reg_data);
+		ctrl->value = reg_data;
+		PDEBUG(DBG_V4L2_CID, "get whiteness 0x%x", ctrl->value);
+		break;
+
+	case V4L2_CID_GAIN:
+		err = s5k83a_get_gain(cam, &reg_data);
+		ctrl->value = reg_data;
+		PDEBUG(DBG_V4L2_CID, "get gain 0x%x", ctrl->value);
+		break;
+
+	default:
+		PDEBUG(DBG_V4L2_CID, "Unknown V4L2 CID 0x%x", ctrl->id);
+		err = -EINVAL;
+	}
+	return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_ctrl(struct m5602_camera *cam,
+			   const struct v4l2_control *ctrl)
+{
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		err = s5k83a_set_brightness(cam, ctrl->value);
+		PDEBUG(DBG_V4L2_CID, "Set brightness to 0x%x", ctrl->value);
+		break;
+
+	case V4L2_CID_WHITENESS:
+		err = s5k83a_set_whiteness(cam, ctrl->value);
+		PDEBUG(DBG_V4L2_CID, "Set whiteness to 0x%x", ctrl->value);
+		break;
+
+	case V4L2_CID_GAIN:
+		err = s5k83a_set_gain(cam, ctrl->value);
+		PDEBUG(DBG_V4L2_CID, "Set gain to 0x%x", ctrl->value);
+		break;
+
+	default:
+		PDEBUG(DBG_V4L2_CID,
+		       "Illegal V4L2 CID %d, value %d", ctrl->id, ctrl->value);
+		err = -EINVAL;
+	}
+	return (err < 0) ? err : 0;
+}
+
+void s5k83a_dump_registers(struct m5602_camera *cam)
+{
+	int address;
+	u8 page, old_page;
+	s5k83a_read_sensor(cam, S5K83A_PAGE_MAP, &old_page, 1);
+	for (page = 0; page < 16; page++) {
+		s5k83a_write_sensor(cam, S5K83A_PAGE_MAP, &page, 1);
+		info("Dumping the s5k83a register state for page 0x%x", page);
+		for (address = 0; address <= 0xff; address++) {
+			u8 value = 0;
+			s5k83a_read_sensor(cam, address, &value, 1);
+			info("register 0x%x contains 0x%x",
+			     address, value);
+		}
+	}
+	info("s5k83a register state dump complete");
+
+	for (page = 0; page < 16; page++) {
+		s5k83a_write_sensor(cam, S5K83A_PAGE_MAP, &page, 1);
+		info("Probing for which registers that are read/write "
+		      "for page 0x%x", page);
+		for (address = 0; address <= 0xff; address++) {
+			u8 old_value, ctrl_value, test_value = 0xff;
+
+			s5k83a_read_sensor(cam, address, &old_value, 1);
+			s5k83a_write_sensor(cam, address, &test_value, 1);
+			s5k83a_read_sensor(cam, address, &ctrl_value, 1);
+
+			if (ctrl_value == test_value)
+				info("register 0x%x is writeable", address);
+			else
+				info("register 0x%x is read only", address);
+
+			/* Restore original value */
+			s5k83a_write_sensor(cam, address, &old_value, 1);
+		}
+	}
+	info("Read/write register probing complete");
+	s5k83a_write_sensor(cam, S5K83A_PAGE_MAP, &old_page, 1);
+}
+
+int s5k83a_get_brightness(struct m5602_camera *cam, u8 *reg_data)
+{
+	int err = 0;
+	u8 data[2];
+
+	err = s5k83a_read_sensor(cam, S5K83A_BRIGHTNESS, data, 2);
+	data[1] = data[1] << 1;
+	*reg_data = data[1];
+
+	return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_brightness(struct m5602_camera *cam, u8 value)
+{
+	int err = 0;
+	u8 data[2];
+
+	data[0] = 0x00;
+	data[1] = 0x20;
+	err = s5k83a_write_sensor(cam, 0x14, data, 2);
+	if (err < 0)
+		return err;
+
+	data[0] = 0x01;
+	data[1] = 0x00;
+	err = s5k83a_write_sensor(cam, 0x0d, data, 2);
+	if (err < 0)
+		return err;
+
+	/* FIXME: This is not sane, we need to figure out the composition
+		  of these registers */
+	data[0] = value >> 3; /* brightness, high 5 bits */
+	data[1] = value >> 1; /* brightness, high 7 bits */
+	err = s5k83a_write_sensor(cam, S5K83A_BRIGHTNESS, data, 2);
+
+	return (err < 0) ? err : 0;
+}
+
+int s5k83a_get_whiteness(struct m5602_camera *cam, u8 *reg_data)
+{
+	int err = 0;
+	u8 data;
+
+	err = s5k83a_read_sensor(cam, S5K83A_WHITENESS, &data, 1);
+
+	*reg_data = data;
+	return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_whiteness(struct m5602_camera *cam, u8 value)
+{
+	int err = 0;
+	u8 data[1];
+
+	data[0] = value;
+	err = s5k83a_write_sensor(cam, S5K83A_WHITENESS, data, 1);
+
+	return (err < 0) ? err : 0;
+}
+
+int s5k83a_get_gain(struct m5602_camera *cam, u8 *reg_data)
+{
+	int err = 0;
+	u8 data[2];
+
+	err = s5k83a_read_sensor(cam, S5K83A_GAIN, data, 2);
+
+	data[1] = data[1] & 0x3f;
+	if (data[1] > S5K83A_MAXIMUM_GAIN)
+		data[1] = S5K83A_MAXIMUM_GAIN;
+
+	*reg_data = data[1];
+
+	return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_gain(struct m5602_camera *cam, u8 value)
+{
+	int err = 0;
+	u8 data[2];
+
+	data[0] = 0;
+	data[1] = value;
+	err = s5k83a_write_sensor(cam, S5K83A_GAIN, data, 2);
+
+	return (err < 0) ? err : 0;
+}
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_s5k83a.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_s5k83a.h	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,443 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_S5K83A_H_
+#define M5602_S5K83A_H_
+
+#include "m5602_sensor.h"
+
+#define S5K83A_PAGE_MAP			0xec
+#define S5K83A_GAIN			0x18
+#define S5K83A_WHITENESS		0x0a
+#define S5K83A_BRIGHTNESS 		0x1b
+
+#define S5K83A_DEFAULT_BRIGHTNESS	0x71
+#define S5K83A_DEFAULT_WHITENESS	0x7e
+#define S5K83A_DEFAULT_GAIN		0x00
+#define S5K83A_MAXIMUM_GAIN		0x3c
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+int s5k83a_get_ctrl(struct m5602_camera *cam,
+			   struct v4l2_control *ctrl);
+int s5k83a_set_ctrl(struct m5602_camera *cam,
+			   const struct v4l2_control *ctrl);
+int s5k83a_probe(struct m5602_camera *cam);
+int s5k83a_init(struct m5602_camera *cam);
+int s5k83a_power_down(struct m5602_camera *cam);
+
+void s5k83a_dump_registers(struct m5602_camera *cam);
+
+int s5k83a_read_sensor(struct m5602_camera *cam, const u8 address,
+		       u8 *i2c_data, const u8 len);
+int s5k83a_write_sensor(struct m5602_camera *cam, const u8 address,
+			u8 *i2c_data, const u8 len);
+
+int s5k83a_set_brightness(struct m5602_camera *cam, u8 value);
+int s5k83a_get_brightness(struct m5602_camera *cam, u8 *reg_data);
+int s5k83a_set_whiteness(struct m5602_camera *cam, u8 value);
+int s5k83a_get_whiteness(struct m5602_camera *cam, u8 *reg_data);
+int s5k83a_set_gain(struct m5602_camera *cam, u8 value);
+int s5k83a_get_gain(struct m5602_camera *cam, u8 *reg_data);
+
+static struct m5602_sensor s5k83a = {
+	.name = "S5K83A",
+	.probe = s5k83a_probe,
+	.init = s5k83a_init,
+	.power_down = s5k83a_power_down,
+	.get_ctrl = s5k83a_get_ctrl,
+	.set_ctrl = s5k83a_set_ctrl,
+	.read_sensor = s5k83a_read_sensor,
+	.write_sensor = s5k83a_write_sensor,
+	.i2c_slave_id = 0x5a,
+	.qctrl = {
+		{
+			.id = V4L2_CID_BRIGHTNESS,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "brightness",
+			.minimum = 0x00,
+			.maximum = 0xff,
+			.step = 0x01,
+			.default_value = S5K83A_DEFAULT_BRIGHTNESS,
+			.flags = V4L2_CTRL_FLAG_SLIDER
+		},
+		{
+			.id = V4L2_CID_WHITENESS,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "whiteness",
+			.minimum = 0x00,
+			.maximum = 0xff,
+			.step = 0x01,
+			.default_value = S5K83A_DEFAULT_WHITENESS,
+			.flags = V4L2_CTRL_FLAG_SLIDER
+		},
+		{
+			.id = V4L2_CID_GAIN,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "gain",
+			.minimum = 0x00,
+			.maximum = S5K83A_MAXIMUM_GAIN,
+			.step = 0x01,
+			.default_value = S5K83A_DEFAULT_GAIN,
+			.flags = V4L2_CTRL_FLAG_SLIDER
+		}
+	},
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = M5602_DEFAULT_FRAME_WIDTH,
+			.height = M5602_DEFAULT_FRAME_HEIGHT
+		}
+	},
+	.current_fmt = {
+		.width = M5602_DEFAULT_FRAME_WIDTH,
+		.height = M5602_DEFAULT_FRAME_HEIGHT,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 1,
+		.colorspace = V4L2_COLORSPACE_SRGB
+	}
+};
+
+static const unsigned char preinit_s5k83a[][4] =
+{
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+	{SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}
+};
+
+/* This could probably be considerably shortened.
+   I don't have the hardware to experiment with it, patches welcome
+*/
+static const unsigned char init_s5k83a[][4] =
+{
+	{SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+	{SENSOR, 0xaf, 0x01, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, 0x7b, 0xff, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR, 0x01, 0x50, 0x00},
+	{SENSOR, 0x12, 0x20, 0x00},
+	{SENSOR, 0x17, 0x40, 0x00},
+	{SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+	{SENSOR, 0x1c, 0x00, 0x00},
+	{SENSOR, 0x02, 0x70, 0x00},
+	{SENSOR, 0x03, 0x0b, 0x00},
+	{SENSOR, 0x04, 0xf0, 0x00},
+	{SENSOR, 0x05, 0x0b, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR, 0x06, 0x71, 0x00},
+	{SENSOR, 0x07, 0xe8, 0x00},
+	{SENSOR, 0x08, 0x02, 0x00},
+	{SENSOR, 0x09, 0x88, 0x00},
+	{SENSOR, 0x14, 0x00, 0x00},
+	{SENSOR, 0x15, 0x20, 0x00},
+	{SENSOR, 0x19, 0x00, 0x00},
+	{SENSOR, 0x1a, 0x98, 0x00},
+	{SENSOR, 0x0f, 0x02, 0x00},
+	{SENSOR, 0x10, 0xe5, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR_LONG, 0x14, 0x00, 0x20},
+	{SENSOR_LONG, 0x0d, 0x00, 0x7d},
+	{SENSOR_LONG, 0x1b, 0x0d, 0x05},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00},
+
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+	{SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+	{SENSOR, 0xaf, 0x01, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	/* ff ( init value )is very dark) || 71 and f0 better */
+	{SENSOR, 0x7b, 0xff, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR, 0x01, 0x50, 0x00},
+	{SENSOR, 0x12, 0x20, 0x00},
+	{SENSOR, 0x17, 0x40, 0x00},
+	{SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+	{SENSOR, 0x1c, 0x00, 0x00},
+	{SENSOR, 0x02, 0x70, 0x00},
+	/* some values like 0x10 give a blue-purple image */
+	{SENSOR, 0x03, 0x0b, 0x00},
+	{SENSOR, 0x04, 0xf0, 0x00},
+	{SENSOR, 0x05, 0x0b, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	/* under 80 don't work, highter depend on value */
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
+
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR, 0x06, 0x71, 0x00},
+	{SENSOR, 0x07, 0xe8, 0x00},
+	{SENSOR, 0x08, 0x02, 0x00},
+	{SENSOR, 0x09, 0x88, 0x00},
+	{SENSOR, 0x14, 0x00, 0x00},
+	{SENSOR, 0x15, 0x20, 0x00},
+	{SENSOR, 0x19, 0x00, 0x00},
+	{SENSOR, 0x1a, 0x98, 0x00},
+	{SENSOR, 0x0f, 0x02, 0x00},
+	{SENSOR, 0x10, 0xe5, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR_LONG, 0x14, 0x00, 0x20},
+	{SENSOR_LONG, 0x0d, 0x00, 0x7d},
+	{SENSOR_LONG, 0x1b, 0x0d, 0x05},
+
+	/* The following sequence is useless after a clean boot
+	   but is necessary after resume from suspend */
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+	{SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+	{SENSOR, 0xaf, 0x01, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, 0x7b, 0xff, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR, 0x01, 0x50, 0x00},
+	{SENSOR, 0x12, 0x20, 0x00},
+	{SENSOR, 0x17, 0x40, 0x00},
+	{SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+	{SENSOR, 0x1c, 0x00, 0x00},
+	{SENSOR, 0x02, 0x70, 0x00},
+	{SENSOR, 0x03, 0x0b, 0x00},
+	{SENSOR, 0x04, 0xf0, 0x00},
+	{SENSOR, 0x05, 0x0b, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR, 0x06, 0x71, 0x00},
+	{SENSOR, 0x07, 0xe8, 0x00},
+	{SENSOR, 0x08, 0x02, 0x00},
+	{SENSOR, 0x09, 0x88, 0x00},
+	{SENSOR, 0x14, 0x00, 0x00},
+	{SENSOR, 0x15, 0x20, 0x00},
+	{SENSOR, 0x19, 0x00, 0x00},
+	{SENSOR, 0x1a, 0x98, 0x00},
+	{SENSOR, 0x0f, 0x02, 0x00},
+
+	{SENSOR, 0x10, 0xe5, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR_LONG, 0x14, 0x00, 0x20},
+	{SENSOR_LONG, 0x0d, 0x00, 0x7d},
+	{SENSOR_LONG, 0x1b, 0x0d, 0x05},
+
+	/* normal colors
+	   (this is value after boot, but after tries can be different) */
+	{SENSOR, 0x00, 0x06, 0x00},
+
+	/* set default brightness */
+	{SENSOR_LONG, 0x14, 0x00, 0x20},
+	{SENSOR_LONG, 0x0d, 0x01, 0x00},
+	{SENSOR_LONG, 0x1b, S5K83A_DEFAULT_BRIGHTNESS >> 3,
+			    S5K83A_DEFAULT_BRIGHTNESS >> 1},
+
+	/* set default whiteness */
+	{SENSOR, S5K83A_WHITENESS, S5K83A_DEFAULT_WHITENESS, 0x00},
+
+	/* set default gain */
+	{SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_GAIN}
+};
+
+#endif
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_sensor.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_sensor.h	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,89 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_SENSOR_H_
+#define M5602_SENSOR_H_
+
+#include "m5602_bridge.h"
+
+#define M5602_DEFAULT_FRAME_WIDTH  640
+#define M5602_DEFAULT_FRAME_HEIGHT 480
+
+#define M5602_MAX_CTRLS		(V4L2_CID_LASTP1 - V4L2_CID_BASE + 10)
+
+/* Enumerates all supported sensors */
+enum sensors {
+	OV9650_SENSOR	= 1,
+	S5K83A_SENSOR	= 2,
+	S5K4AA_SENSOR	= 3,
+	MT9M111_SENSOR	= 4,
+	PO1030_SENSOR	= 5
+};
+
+/* Enumerates all possible instruction types */
+enum instruction {
+	BRIDGE,
+	SENSOR,
+	SENSOR_LONG
+};
+
+struct m5602_sensor {
+	/* Defines the name of a sensor */
+	char name[32];
+
+	struct v4l2_rect _rect;
+	struct v4l2_cropcap cropcap;
+	struct v4l2_queryctrl qctrl[M5602_MAX_CTRLS];
+
+	/* What i2c address the sensor is connected to */
+	u8 i2c_slave_id;
+
+	/* The currently assigned format */
+	struct v4l2_pix_format current_fmt;
+
+	/* Probes if the sensor is connected */
+	int (*probe)(struct m5602_camera *cam);
+
+	/* Performs a initialization sequence */
+	int (*init)(struct m5602_camera *cam);
+
+	/* Performs a power down sequence */
+	int (*power_down)(struct m5602_camera *cam);
+
+	/* Returns an exposed v4l control */
+	int (*get_ctrl)(struct m5602_camera *cam,
+	      struct v4l2_control *ctrl);
+
+	/* Sets an exposed v4l2 control */
+	int (*set_ctrl)(struct m5602_camera *cam,
+	      const struct v4l2_control *ctrl);
+
+	/* Sets a cropping range */
+	int (*set_crop)(struct m5602_camera *cam,
+	      const struct v4l2_rect *rect);
+
+	/* Reads a sensor register */
+	int (*read_sensor)(struct m5602_camera *cam, const u8 address,
+	      u8 *i2c_data, const u8 len);
+
+	/* Writes to a sensor register */
+	int (*write_sensor)(struct m5602_camera *cam, const u8 address,
+	      u8 *i2c_data, const u8 len);
+};
+
+#endif
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_v4l2.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_v4l2.c	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,1238 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include "m5602_v4l2.h"
+
+#define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control)
+
+/* Tracks all available capture formats */
+struct m5602_capture_format {
+	__u32 pixelformat;
+	char description[32];
+} m5602_capture_format;
+
+struct m5602_capture_format available_fmts[] =
+{
+	{V4L2_PIX_FMT_SBGGR8, "BAYER"}
+};
+
+u32 m5602_request_buffers(struct m5602_camera *cam, u32 count)
+{
+	u32 i;
+	u8 *raw = NULL;
+
+	PDEBUG(DBG_V4L2, "m5602_request_buffers, count %d", count);
+
+	if (!count) {
+		PDEBUG(DBG_V4L2, "No buffers requested");
+		return 0;
+	}
+
+	if (count > M5602_MAX_FRAMES)
+		count = M5602_MAX_FRAMES;
+
+	if (cam->nbuffers) {
+		if (cam->stream == STREAM_ON)
+			m5602_stream_interrupt(cam);
+
+		m5602_stop_transfer(cam);
+		m5602_empty_framequeues(cam);
+		m5602_release_buffers(cam);
+	}
+
+	cam->nbuffers = count;
+
+	/* Allocate buffers where the raw bayer encoded frames will reside */
+	while (cam->nbuffers) {
+		raw = vmalloc_32_user(cam->nbuffers *
+				M5602_DEFAULT_FRAME_WIDTH *
+				M5602_DEFAULT_FRAME_HEIGHT);
+		if (raw)
+			break;
+
+		/* Buffer allocation failed, try with one buffer less */
+		cam->nbuffers--;
+	}
+
+	for (i = 0; i < cam->nbuffers; i++) {
+		cam->frame[i].rawdata = raw + (i *
+			PAGE_ALIGN(M5602_DEFAULT_FRAME_WIDTH *
+				   M5602_DEFAULT_FRAME_HEIGHT));
+		memset(&cam->frame[i].buf, 0, sizeof(struct v4l2_buffer));
+
+		cam->frame[i].buf.m.offset = i * cam->sensor->current_fmt.width
+			* cam->sensor->current_fmt.height;
+		cam->frame[i].buf.length = cam->sensor->current_fmt.width
+			* cam->sensor->current_fmt.height;
+
+		cam->frame[i].buf.index = i;
+		cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		cam->frame[i].buf.field = V4L2_FIELD_NONE;
+		cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+	}
+
+	PDEBUG(DBG_V4L2, "Allocated %d buffers", cam->nbuffers);
+	return cam->nbuffers;
+}
+
+void m5602_release_buffers(struct m5602_camera *cam)
+{
+	if (!cam)
+		return;
+
+	if (!cam->nbuffers)
+		return;
+
+	PDEBUG(DBG_INIT, "Releasing %d allocated buffers", cam->nbuffers);
+	if (cam->io == V4L2_MEMORY_MMAP) {
+		if (cam->frame[0].rawdata)
+			vfree(cam->frame[0].rawdata);
+		cam->nbuffers = 0;
+	}
+	cam->frame_current = NULL;
+}
+
+/* Empties the frame queue and re-initializes the linked lists */
+void m5602_empty_framequeues(struct m5602_camera *cam)
+{
+	u32 i;
+
+	INIT_LIST_HEAD(&cam->inqueue);
+	INIT_LIST_HEAD(&cam->outqueue);
+
+	for (i = 0; i < M5602_MAX_FRAMES; i++) {
+		cam->frame[i].state = FRAME_UNUSED;
+		cam->frame[i].buf.bytesused = 0;
+	}
+}
+
+/* Executes when fresh URBs arrive from the bridge */
+void m5602_urb_complete(struct urb *urb)
+{
+	struct m5602_camera *cam = urb->context;
+	struct m5602_frame_t *f;
+	unsigned long lock_flags;
+	int i, err = 0;
+
+	if (urb->status == -ENOENT)
+		return;
+
+	f = cam->frame_current;
+
+	if (cam->stream == STREAM_INTERRUPT) {
+		PDEBUG(DBG_V4L2, "Stream interrupted");
+		cam->stream = STREAM_OFF;
+		if (f)
+			f->state = FRAME_QUEUED;
+
+		wake_up_interruptible(&cam->wait_stream);
+	}
+
+	if (cam->state & DEV_DISCONNECTED) {
+		PDEBUG(DBG_V4L2, "Device disconnect");
+		return;
+	}
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		PDEBUG(DBG_V4L2, "Device misconfigured");
+		wake_up_interruptible(&cam->wait_frame);
+		return;
+	}
+
+	if (list_empty(&cam->inqueue) || (cam->stream == STREAM_OFF)) {
+		if (list_empty(&cam->inqueue))
+			PDEBUG(DBG_V4L2, "Inqueue empty, resubmitting");
+		else
+			PDEBUG(DBG_V4L2, "Stream is off, resubmitting");
+		goto resubmit;
+	}
+
+	if (!f) {
+		spin_lock_irqsave(&cam->queue_lock, lock_flags);
+		/* Pull the next frame from the inqueue */
+		f = list_entry(cam->inqueue.next, struct m5602_frame_t, frame);
+		spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+	}
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		int len;
+		u8 *pos;
+
+		if (urb->iso_frame_desc[i].status) {
+			PDEBUG(DBG_V4L2, "Error in isochronous frame %d",
+			       urb->iso_frame_desc[i].status);
+			f->state = FRAME_ERROR;
+			f->buf.bytesused = 0;
+			continue;
+		}
+
+		len = urb->iso_frame_desc[i].actual_length;
+		pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+		/* If the packet is smaller than 12 bytes in size, skip it */
+		if (len < 12)
+			continue;
+
+		/* Frame delimiter: ff xx xx xx ff ff */
+		if (pos[0] == 0xff && pos[4] == 0xff && pos[5] == 0xff) {
+			PDEBUG(DBG_DATA, "Frame delimiter detected");
+
+			/* Remove the extra fluff appended on each header */
+			pos += 6;
+			len -= 6;
+
+			switch (f->state) {
+			case FRAME_GRABBING:
+				f->buf.bytesused =
+					cam->sensor->current_fmt.sizeimage;
+				do_gettimeofday(&f->buf.timestamp);
+				f->state = FRAME_DONE;
+
+				PDEBUG(DBG_V4L2, "Frame %d captured, contains "
+						 "%d bytes", f->framenum,
+						 f->buf.bytesused);
+
+				spin_lock_irqsave(&cam->queue_lock, lock_flags);
+
+				/* Add the current frame to the outqueue */
+				list_move_tail(&f->frame, &cam->outqueue);
+
+				if (list_empty(&cam->inqueue)) {
+					cam->frame_current = NULL;
+					spin_unlock_irqrestore(&cam->queue_lock,
+						lock_flags);
+					break;
+				}
+
+				/* Pull the next frame */
+				f = list_entry(cam->inqueue.next,
+					struct m5602_frame_t, frame);
+
+				spin_unlock_irqrestore(&cam->queue_lock,
+					lock_flags);
+
+				wake_up_interruptible(&cam->wait_frame);
+
+				/* Start with the new frame */
+				PDEBUG(DBG_DATA, "Starting frame %d, "
+					"copying %d bytes",
+					f->framenum, len);
+
+				memcpy(f->rawdata, pos, len);
+				f->buf.sequence = ++cam->frame_count;
+				f->framenum = f->buf.sequence;
+				f->buf.bytesused = len;
+				f->state = FRAME_GRABBING;
+
+				cam->frame_current = f;
+				break;
+
+			case FRAME_QUEUED:
+			case FRAME_ERROR:
+				PDEBUG(DBG_DATA, "Starting frame %d, "
+				       "copying %d bytes", f->framenum, len);
+
+				memcpy(f->rawdata, pos, len);
+				f->buf.sequence  = ++cam->frame_count;
+				f->framenum = f->buf.sequence;
+				f->buf.bytesused = len;
+				f->state = FRAME_GRABBING;
+
+				cam->frame_current = f;
+				break;
+
+			default:
+				break;
+			}
+		} else {
+			if (f->state == FRAME_GRABBING) {
+				/* Remove urb header */
+				len -= 4;
+				pos += 4;
+
+				PDEBUG(DBG_DATA, "Continuing frame %d, "
+					"copying %d bytes", f->framenum, len);
+				if ((f->buf.bytesused + len) <=
+					(M5602_DEFAULT_FRAME_WIDTH *
+					 M5602_DEFAULT_FRAME_HEIGHT)) {
+					memcpy(f->rawdata + f->buf.bytesused,
+					       pos, len);
+					f->buf.bytesused += len;
+				}
+			}
+		}
+	}
+
+resubmit:
+	urb->dev = cam->udev;
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+int m5602_start_transfer(struct m5602_camera *cam)
+{
+	int i, j, retval = 0;
+	struct urb *urb;
+	struct usb_device *udev = cam->udev;
+
+	PDEBUG(DBG_V4L2, "Initialize isochronous buffers and urbs, "
+		"ISOC IN: 0x%x", cam->isoc_in_endpointAddr);
+	PDEBUG(DBG_V4L2, "Number of urbs: %d, packet size: %zi, "
+		"number of packets: %d", M5602_URBS,
+		cam->isoc_in_size, M5602_ISOC_PACKETS);
+
+	/* Allocate isoc buffers */
+	for (i = 0; i < M5602_URBS; i++) {
+		cam->isoc_in_buffer[i] =
+			kzalloc(cam->isoc_in_size *
+			M5602_ISOC_PACKETS, GFP_KERNEL);
+		if (!cam->isoc_in_buffer[i]) {
+			PDEBUG(DBG_DATA, "Not enough memory for ISOC buffer %d",
+			       i);
+			retval = -ENOMEM;
+			goto free_buffers;
+		}
+	}
+
+	/* Allocate urbs */
+	for (i = 0; i < M5602_URBS; i++) {
+		urb = usb_alloc_urb(M5602_ISOC_PACKETS, GFP_KERNEL);
+
+		if (!urb) {
+			PDEBUG(DBG_DATA, "Not enough memory for URB");
+			retval = -ENOMEM;
+			goto free_urbs;
+		}
+
+		cam->urb[i] = urb;
+		urb->dev = udev;
+		urb->context = cam;
+		urb->pipe = usb_rcvisocpipe(udev, cam->isoc_in_endpointAddr);
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->number_of_packets = M5602_ISOC_PACKETS;
+		urb->complete = m5602_urb_complete;
+		urb->transfer_buffer = cam->isoc_in_buffer[i];
+		urb->transfer_buffer_length =
+			cam->isoc_in_size * M5602_ISOC_PACKETS;
+		urb->interval = 1;
+
+		for (j = 0; j < M5602_ISOC_PACKETS; j++) {
+			urb->iso_frame_desc[j].offset = cam->isoc_in_size * j;
+			urb->iso_frame_desc[j].length = cam->isoc_in_size;
+		}
+	}
+
+	cam->frame_current = NULL;
+	retval = usb_set_interface(cam->udev, 0, 2);
+	if (retval)
+		goto free_urbs;
+
+	for (i = 0; i < M5602_URBS; i++) {
+		if (cam->urb[i]) {
+			retval = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+			if (retval) {
+				PDEBUG(DBG_V4L2, "usb_submit_urb failed! %d",
+				       retval);
+				goto free_urbs;
+			}
+		}
+	}
+
+	/* Send start command to the camera */
+	for (i = 0; i < M5602_URBS; i++) {
+		u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01};
+		memcpy(cam->control_buffer, buffer, sizeof(buffer));
+		usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			0x04, 0x40, 0x19, 0x0000, cam->control_buffer,
+			4, M5602_URB_MSG_TIMEOUT);
+	}
+
+	PDEBUG(DBG_V4L2, "Transfer started");
+
+	return retval;
+
+free_urbs:
+	for (i = 0; (i < M5602_URBS) && cam->urb[i]; i++)
+		usb_free_urb(cam->urb[i]);
+
+free_buffers:
+	for (i = 0; (i < M5602_URBS) && cam->isoc_in_buffer[i]; i++) {
+		kfree(cam->isoc_in_buffer[i]);
+		cam->isoc_in_buffer[i] = NULL;
+	}
+
+	PDEBUG(DBG_V4L2, "Failed to start transfer");
+	return retval;
+}
+
+int m5602_stop_transfer(struct m5602_camera *cam)
+{
+	int i, retval = 0;
+
+	PDEBUG(DBG_V4L2, "Freeing isochronous buffers and urbs");
+
+	if (cam->state & DEV_DISCONNECTED)
+		return -ENODEV;
+
+	for (i = M5602_URBS - 1; i >= 0; i--) {
+		PDEBUG(DBG_V4L2, "Freeing urb %d", i);
+		if (cam->urb[i]) {
+			usb_kill_urb(cam->urb[i]);
+			usb_free_urb(cam->urb[i]);
+			cam->urb[i] = NULL;
+		}
+
+		if (cam->isoc_in_buffer[i]) {
+			kfree(cam->isoc_in_buffer[i]);
+			cam->isoc_in_buffer[i] = NULL;
+		}
+	}
+
+	PDEBUG(DBG_V4L2, "Isochronous buffers and urbs freed");
+
+	usb_set_interface(cam->udev, 0, 0); /* 0 Mb/s */
+
+	PDEBUG(DBG_V4L2, "Transfer stopped");
+	return retval;
+}
+
+int m5602_stream_interrupt(struct m5602_camera *cam)
+{
+	int timeout;
+
+	cam->stream = STREAM_INTERRUPT;
+
+	timeout = wait_event_timeout(cam->wait_stream,
+		(cam->stream == STREAM_OFF) ||
+		(cam->state & DEV_DISCONNECTED), M5602_URB_TIMEOUT);
+
+	if (cam->state & DEV_DISCONNECTED)
+		return -ENODEV;
+
+	if (cam->stream != STREAM_OFF) {
+		cam->state |= DEV_MISCONFIGURED;
+		PDEBUG(DBG_V4L2, "The camera is misconfigured. To use it, "
+				"close and open /dev/video%d again.",
+				cam->vdev->minor);
+		return -EIO;
+	}
+	return 0;
+}
+
+void m5602_vm_open(struct vm_area_struct *vma)
+{
+	struct m5602_frame_t *f = vma->vm_private_data;
+	f->vma_use_count++;
+}
+
+void m5602_vm_close(struct vm_area_struct *vma)
+{
+	struct m5602_frame_t *f = vma->vm_private_data;
+	f->vma_use_count--;
+}
+
+struct vm_operations_struct m5602_vm_ops = {
+	.open = m5602_vm_open,
+	.close = m5602_vm_close
+};
+
+int m5602_v4l_open(struct inode *inode, struct file *filp)
+{
+	struct m5602_camera *cam;
+	int err = 0;
+
+	if (!down_read_trylock(&m5602_disconnect))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(video_devdata(filp));
+
+	info("%s on /dev/video%d opened", cam->name, cam->vdev->minor);
+
+	if (mutex_lock_interruptible(&cam->dev_mutex)) {
+		PDEBUG(DBG_INIT, "Could not grab cam mutex");
+		up_read(&m5602_disconnect);
+		return -ERESTARTSYS;
+	}
+
+	if (cam->state & DEV_DISCONNECTED) {
+		PDEBUG(DBG_INIT, "Device disconnected");
+		err = -ENODEV;
+		goto out;
+	}
+
+	if (cam->users) {
+		PDEBUG(DBG_V4L2, "%s on /dev/video%d has "
+			"currently %d users", cam->name, cam->vdev->minor,
+			cam->users);
+
+		if ((filp->f_flags & O_NONBLOCK) ||
+			(filp->f_flags & O_NDELAY)) {
+			err = -EWOULDBLOCK;
+			goto out;
+		}
+
+		PDEBUG(DBG_INIT, "A blocking open() has been requested. "
+				"Waiting for the device to be released");
+
+		mutex_unlock(&cam->dev_mutex);
+
+		err = wait_event_interruptible_exclusive(cam->open,
+			(cam->state & DEV_DISCONNECTED) || (cam->users == 0));
+		if (err) {
+			up_read(&m5602_disconnect);
+			return err;
+		}
+
+		if (cam->state & DEV_DISCONNECTED) {
+			up_read(&m5602_disconnect);
+			return -ENODEV;
+		}
+
+		mutex_lock(&cam->dev_mutex);
+	}
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		PDEBUG(DBG_INIT, "Camera is misconfigured, re-initializing");
+		err = m5602_initialize_camera(cam);
+		if (err) {
+			PDEBUG(DBG_INIT, "Initialization failed again");
+			err = -EBUSY;
+			goto out;
+		}
+		cam->state &= ~DEV_MISCONFIGURED;
+	}
+
+	filp->private_data = cam;
+	cam->users++;
+	cam->io = 0;
+	cam->stream = STREAM_OFF;
+	cam->nbuffers = 0;
+	cam->frame_count = 0;
+
+	m5602_empty_framequeues(cam);
+
+	PDEBUG(DBG_V4L2, "Video device /dev/video%d is open", cam->vdev->minor);
+
+out:
+	mutex_unlock(&cam->dev_mutex);
+	up_read(&m5602_disconnect);
+	return err;
+}
+
+int m5602_v4l_release(struct inode *inode, struct file *filp)
+{
+	struct m5602_camera *cam = video_get_drvdata(video_devdata(filp));
+
+	mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
+
+	if (cam->stream == STREAM_ON)
+		m5602_stream_interrupt(cam);
+
+	m5602_stop_transfer(cam);
+	m5602_empty_framequeues(cam);
+	m5602_release_buffers(cam);
+
+	cam->users--;
+	wake_up_interruptible_nr(&cam->open, 1);
+
+	info("%s on /dev/video%d closed", cam->name, cam->vdev->minor);
+	mutex_unlock(&cam->dev_mutex);
+
+	return 0;
+}
+
+int m5602_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct m5602_camera *cam = video_get_drvdata(video_devdata(filp));
+	unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start;
+	void *pos;
+	u32 i;
+
+	PDEBUG(DBG_V4L2, "Mmap call, size: %ld, buffer length %d",
+	       size, PAGE_ALIGN(cam->frame[0].buf.length));
+
+	if (mutex_lock_interruptible(&cam->fileop_mutex))
+		return -ERESTARTSYS;
+
+	if (cam->state & DEV_DISCONNECTED) {
+		PDEBUG(DBG_V4L2, "Device not present");
+		mutex_unlock(&cam->fileop_mutex);
+		return -ENODEV;
+	}
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		PDEBUG(DBG_V4L2, "The camera is misconfigured");
+		mutex_unlock(&cam->fileop_mutex);
+		return -EIO;
+	}
+
+	if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
+		PDEBUG(DBG_V4L2, "Invalid mmap flags");
+		mutex_unlock(&cam->fileop_mutex);
+		return -EACCES;
+	}
+
+	if (cam->io != V4L2_MEMORY_MMAP) {
+		PDEBUG(DBG_V4L2, "Camera not configured for mmap");
+		mutex_unlock(&cam->fileop_mutex);
+		return -EINVAL;
+	}
+
+	if (size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+		PDEBUG(DBG_V4L2, "Size %ld differs from buffer length %d",
+		       size, PAGE_ALIGN(cam->frame[0].buf.length));
+		mutex_unlock(&cam->fileop_mutex);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < cam->nbuffers; i++) {
+		if ((cam->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
+			break;
+	}
+
+	if (i == cam->nbuffers) {
+		mutex_unlock(&cam->fileop_mutex);
+		return -EINVAL;
+	}
+
+	/* VM_IO is eventually going to replace PageReserved altogether */
+	vma->vm_flags |= VM_IO | VM_RESERVED; /* avoid to swap out this VMA */
+	pos = cam->frame[i].rawdata;
+
+	while (size > 0) { /* size is page-aligned */
+		if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+			PDEBUG(DBG_V4L2, "Failed to insert a vm page");
+			mutex_unlock(&cam->fileop_mutex);
+			return -EAGAIN;
+		}
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+
+	vma->vm_ops = &m5602_vm_ops;
+	vma->vm_private_data = &cam->frame[i];
+	m5602_vm_open(vma);
+
+	mutex_unlock(&cam->fileop_mutex);
+
+	PDEBUG(DBG_V4L2, "Mmap call succeeded");
+
+	return 0;
+}
+
+/* Checks supplied v4l2_pix_formats returns 1 on ok, 0 on fail */
+int check_format(struct v4l2_format *fmt)
+{
+	if (!fmt)
+		return 0;
+
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return 0;
+
+	PDEBUG(DBG_V4L2, "Trying to set pixel format 0x%08x [%c%c%c%c], "
+			"width %d, height %d",
+			fmt->fmt.pix.pixelformat,
+			fmt->fmt.pix.pixelformat & 0xff,
+			(fmt->fmt.pix.pixelformat >> 8) & 0xff,
+			(fmt->fmt.pix.pixelformat >> 24) & 0xff,
+			(fmt->fmt.pix.pixelformat >> 16) & 0xff,
+			fmt->fmt.pix.width,
+			fmt->fmt.pix.height);
+
+	if ((fmt->fmt.pix.width != M5602_DEFAULT_FRAME_WIDTH) ||
+	    (fmt->fmt.pix.height != M5602_DEFAULT_FRAME_HEIGHT)) {
+		fmt->fmt.pix.width = M5602_DEFAULT_FRAME_WIDTH;
+		fmt->fmt.pix.height = M5602_DEFAULT_FRAME_HEIGHT;
+	}
+
+	switch (fmt->fmt.pix.pixelformat) {
+	case V4L2_PIX_FMT_SBGGR8:
+		fmt->fmt.pix.priv = 1;
+		break;
+
+	default:
+		PDEBUG(DBG_V4L2, "Unsupported pixel format");
+		return 0;
+	}
+
+	fmt->fmt.pix.sizeimage = fmt->fmt.pix.width *
+		fmt->fmt.pix.height *
+		fmt->fmt.pix.priv;
+	fmt->fmt.pix.field = V4L2_FIELD_NONE;
+	fmt->fmt.pix.bytesperline =
+		fmt->fmt.pix.sizeimage / fmt->fmt.pix.height;
+	fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+
+	PDEBUG(DBG_V4L2, "Pixel format ok");
+	return 1;
+}
+
+int m5602_v4l_ioctl(struct inode *inode,
+			   struct file *file,
+			   unsigned int cmd,
+			   unsigned long arg)
+{
+	struct m5602_camera *cam = video_get_drvdata(video_devdata(file));
+	int err;
+
+	if (mutex_lock_interruptible(&cam->fileop_mutex))
+		return -ERESTARTSYS;
+
+	if (cam->state & DEV_DISCONNECTED) {
+		PDEBUG(DBG_V4L2, "Device not present");
+		mutex_unlock(&cam->fileop_mutex);
+		return -ENODEV;
+	}
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		PDEBUG(DBG_V4L2, "The camera is misconfigured. "
+				"Close and open it again");
+		mutex_unlock(&cam->fileop_mutex);
+		return -EIO;
+	}
+
+	err = m5602_v4l_do_ioctl(inode, file, cmd, (void __user *) arg);
+
+	mutex_unlock(&cam->fileop_mutex);
+	return err;
+}
+
+int m5602_v4l_do_ioctl(struct inode *inode,
+			      struct file *file,
+			      unsigned int ioctlnr,
+			      void *arg)
+{
+	struct m5602_camera *cam = (struct m5602_camera *) file->private_data;
+	struct m5602_frame_t *f;
+	struct m5602_sensor *s;
+	int retval = 0, err;
+
+	if (!cam) {
+		PDEBUG(DBG_V4L2, "%s improperly initialized", cam->name);
+		return -ENODEV;
+	}
+
+	switch (ioctlnr) {
+	case VIDIOC_QUERYBUF: { /* only useable with V4L2_MEMORY_MMAP */
+		struct v4l2_buffer buf;
+
+		if (copy_from_user(&buf, arg, sizeof(buf)))
+			return -EFAULT;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_QUERYBUF, index %d", buf.index);
+
+		if ((buf.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+			(buf.index >= cam->nbuffers) ||
+			(cam->io != V4L2_MEMORY_MMAP))
+			return -EINVAL;
+
+		memcpy(&buf, &cam->frame[buf.index].buf,
+			sizeof(struct v4l2_buffer));
+
+		if (cam->frame[buf.index].vma_use_count)
+			buf.flags |= V4L2_BUF_FLAG_MAPPED;
+
+		if (cam->frame[buf.index].state == FRAME_DONE)
+			buf.flags |= V4L2_BUF_FLAG_DONE;
+		else if (cam->frame[buf.index].state != FRAME_UNUSED)
+			buf.flags |= V4L2_BUF_FLAG_QUEUED;
+
+		if (copy_to_user(arg, &buf, sizeof(struct v4l2_buffer)))
+			return -EFAULT;
+		break;
+	}
+
+	case VIDIOC_QBUF: {
+		struct v4l2_buffer buf;
+		unsigned long lock_flags;
+
+		if (copy_from_user(&buf, arg, sizeof(buf)))
+			return -EFAULT;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_QBUF, buffer %d", buf.index);
+
+		if ((buf.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+			(cam->io != V4L2_MEMORY_MMAP) ||
+			(buf.index >= cam->nbuffers))
+			return -EINVAL;
+
+		if (cam->frame[buf.index].state != FRAME_UNUSED)
+			return -EINVAL;
+
+		cam->frame[buf.index].state = FRAME_QUEUED;
+		cam->frame[buf.index].buf.flags |= V4L2_BUF_FLAG_QUEUED;
+		cam->frame[buf.index].buf.flags &= ~V4L2_BUF_FLAG_DONE;
+
+		spin_lock_irqsave(&cam->queue_lock, lock_flags);
+		list_add_tail(&cam->frame[buf.index].frame, &cam->inqueue);
+		spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+		break;
+	}
+
+	case VIDIOC_DQBUF: {
+		struct v4l2_buffer buf;
+		unsigned long lock_flags;
+
+		if (copy_from_user(&buf, arg, sizeof(buf)))
+			return -EFAULT;
+
+		if ((buf.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+			(cam->io != V4L2_MEMORY_MMAP))
+			return -EINVAL;
+
+		if (list_empty(&cam->outqueue)) {
+			if (cam->stream == STREAM_OFF)
+				return -EINVAL;
+
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			PDEBUG(DBG_V4L2, "Waiting for a frame");
+			wait_event_interruptible(cam->wait_frame,
+					(!list_empty(&cam->outqueue)) ||
+					(cam->state & DEV_DISCONNECTED) ||
+					(cam->state & DEV_MISCONFIGURED));
+
+			if (cam->state & DEV_DISCONNECTED)
+				return -ENODEV;
+
+			if (cam->state & DEV_MISCONFIGURED)
+				return -EIO;
+
+			if (list_empty(&cam->outqueue))
+				return -EINVAL;
+		}
+
+		spin_lock_irqsave(&cam->queue_lock, lock_flags);
+		f = list_entry(cam->outqueue.next, struct m5602_frame_t, frame);
+		list_del(cam->outqueue.next);
+		spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+		PDEBUG(DBG_V4L2, "VIDIOC_DQBUF, frame %d", f->framenum);
+
+		memcpy(&buf, &f->buf, sizeof(struct v4l2_buffer));
+
+		f->state = FRAME_UNUSED;
+
+		if (f->vma_use_count)
+			buf.flags |= V4L2_BUF_FLAG_MAPPED;
+
+		if (copy_to_user(arg, &buf, sizeof(buf)))
+			return -EFAULT;
+		break;
+	}
+
+	case VIDIOC_QUERYCAP: {
+		struct v4l2_capability cap;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_QUERYCAP");
+		memset(&cap, 0, sizeof(struct v4l2_capability));
+		strlcpy(cap.driver, "m5602", sizeof(cap.driver));
+
+		cap.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+		strlcpy(cap.card, cam->vdev->name, sizeof(cap.card));
+
+		if (usb_make_path(cam->udev, cap.bus_info,
+		    sizeof(cap.bus_info)) < 0)
+			strlcpy(cap.bus_info,
+				cam->vdev->name, sizeof(cap.bus_info));
+
+		if (copy_to_user(arg, &cap, sizeof(cap)))
+			return -EFAULT;
+		break;
+	}
+
+	/* CAPTURE IOCTL */
+	case VIDIOC_ENUM_FMT: {
+		struct v4l2_fmtdesc fmtd;
+		PDEBUG(DBG_V4L2, "VIDIOC_ENUM_FMT");
+
+		if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+			return -EFAULT;
+
+		if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+
+		if (fmtd.index >= (sizeof(available_fmts)
+				  / sizeof(m5602_capture_format)))
+			return -EINVAL;
+
+		fmtd.pixelformat = available_fmts[fmtd.index].pixelformat;
+		strlcpy(fmtd.description,
+			available_fmts[fmtd.index].description,
+			sizeof(fmtd.description));
+		memset(fmtd.reserved, 0, sizeof(fmtd.reserved));
+		fmtd.flags = 0;
+
+		if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+			return -EFAULT;
+		break;
+	}
+
+	case VIDIOC_G_FMT: {
+		struct v4l2_format fmt;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_G_FMT");
+		if (copy_from_user(&fmt, arg, sizeof(fmt)))
+			return -EFAULT;
+
+		fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		memcpy(&(fmt.fmt.pix), &cam->sensor->current_fmt,
+			 sizeof(struct v4l2_pix_format));
+
+		if (copy_to_user(arg, &fmt, sizeof(fmt)))
+			return -EFAULT;
+		break;
+	}
+
+	case VIDIOC_TRY_FMT:
+	case VIDIOC_S_FMT: {
+		struct v4l2_format fmt;
+
+		if (ioctlnr == VIDIOC_TRY_FMT)
+			PDEBUG(DBG_V4L2, "VIDIOC_TRY_FMT");
+		else
+			PDEBUG(DBG_V4L2, "VIDIOC_S_FMT");
+
+		if (copy_from_user(&fmt, arg, sizeof(fmt)))
+			return -EFAULT;
+
+		if (!check_format(&fmt))
+			return -EINVAL;
+
+		if (ioctlnr == VIDIOC_S_FMT) {
+			if (cam->stream == STREAM_ON) {
+				err = m5602_stream_interrupt(cam);
+				if (err)
+					return err;
+			}
+
+			memcpy(&cam->sensor->current_fmt, &fmt.fmt.pix,
+				sizeof(struct v4l2_pix_format));
+			m5602_empty_framequeues(cam);
+		}
+
+		if (copy_to_user(arg, &fmt, sizeof(fmt)))
+			return -EFAULT;
+		break;
+	}
+
+	/* INPUT SWITCHING */
+	case VIDIOC_ENUMINPUT: {
+		struct v4l2_input i;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_ENUMINPUT");
+		if (copy_from_user(&i, arg, sizeof(i)))
+			return -EFAULT;
+
+		/* FIXME: Add support for a non-zero index */
+		if (i.index)
+			return -EINVAL;
+
+		memset(&i, 0, sizeof(i));
+		strlcpy(i.name, "USB", sizeof(i.name));
+		i.type = V4L2_INPUT_TYPE_CAMERA;
+		i.std = 0;
+
+		if (copy_to_user(arg, &i, sizeof(i)))
+			return -EFAULT;
+		break;
+	}
+
+	case VIDIOC_G_INPUT: {
+		int index = 0;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_G_INPUT");
+
+		if (copy_to_user(arg, &index, sizeof(index)))
+			return -EFAULT;
+		break;
+	}
+
+	case VIDIOC_S_INPUT: {
+		struct v4l2_input i;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_S_INPUT");
+		if (copy_from_user(&i, arg, sizeof(i)))
+			return -EFAULT;
+
+		if (i.index != 0)
+			return -EINVAL;
+
+		break;
+	}
+
+	case VIDIOC_REQBUFS: {
+		struct v4l2_requestbuffers rb;
+		int i;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_REQBUFS");
+
+		if (copy_from_user(&rb, arg, sizeof(rb)))
+			return -EFAULT;
+
+		if ((rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+			((rb.memory != V4L2_MEMORY_MMAP)))
+			return -EINVAL;
+
+		for (i = 0; i < cam->nbuffers; i++) {
+			if (cam->frame[i].vma_use_count) {
+				PDEBUG(DBG_V4L2, "VIDIOC_REQBUFS failed. "
+					"Previous buffers are still mapped");
+				return -EBUSY;
+			}
+		}
+
+		if (cam->stream == STREAM_ON) {
+			err = m5602_stream_interrupt(cam);
+			if (err)
+				return err;
+		}
+
+		m5602_empty_framequeues(cam);
+		m5602_release_buffers(cam);
+
+		if (rb.count)
+			rb.count = m5602_request_buffers(cam, rb.count);
+
+		if (rb.count == 0)
+			return -EFAULT;
+
+		if (copy_to_user(arg, &rb, sizeof(rb))) {
+			m5602_release_buffers(cam);
+			cam->io = 0;
+			return -EFAULT;
+		}
+
+		cam->io = V4L2_MEMORY_MMAP;
+		break;
+	}
+
+	case VIDIOC_STREAMON: {
+		int type;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_STREAMON");
+
+		if (copy_from_user(&type, arg, sizeof(type)))
+			return -EFAULT;
+
+		if ((type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+			(cam->io != V4L2_MEMORY_MMAP))
+			return -EINVAL;
+
+		err = m5602_start_transfer(cam);
+		if (err)
+			break;
+
+		cam->stream = STREAM_ON;
+		break;
+	}
+
+	case VIDIOC_STREAMOFF: {
+		int type;
+		PDEBUG(DBG_V4L2, "VIDIOC_STREAMOFF");
+
+		if (copy_from_user(&type, arg, sizeof(type)))
+			return -EFAULT;
+
+		if ((type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+			(cam->io != V4L2_MEMORY_MMAP))
+			return -EINVAL;
+
+		if (cam->stream == STREAM_ON) {
+			err = m5602_stream_interrupt(cam);
+			if (err)
+				return err;
+		}
+
+		m5602_empty_framequeues(cam);
+		break;
+	}
+
+	/* STREAMING PARAMS */
+	case VIDIOC_G_PARM: {
+		struct v4l2_streamparm sp;
+
+		if (copy_from_user(&sp, arg, sizeof(sp)))
+			return -EFAULT;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_G_PARM %d", sp.type);
+
+		if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+
+		sp.parm.capture.capability = 0;
+		sp.parm.capture.capturemode = 0;
+		sp.parm.capture.timeperframe.numerator = 0;
+		sp.parm.capture.timeperframe.denominator = 0;
+		sp.parm.capture.readbuffers = 1;
+		sp.parm.capture.extendedmode = 0;
+		memset(&sp.parm.capture.reserved, 0,
+			sizeof(sp.parm.capture.reserved));
+
+		if (copy_to_user(arg, &sp, sizeof(sp)))
+			return -EFAULT;
+		break;
+	}
+
+	/* CONTROLS */
+	case VIDIOC_QUERYCTRL: {
+		struct v4l2_queryctrl qc;
+		int i, n;
+
+		if (copy_from_user(&qc, arg, sizeof(qc)))
+			return -EFAULT;
+
+		s = cam->sensor;
+		PDEBUG(DBG_V4L2, "VIDIOC_QUERYCTRL");
+
+		n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
+
+		retval = -EINVAL;
+		for (i = 0; i < n; i++) {
+			if (qc.id && qc.id == s->qctrl[i].id) {
+				memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+				if (copy_to_user(arg, &qc, sizeof(qc)))
+					return -EFAULT;
+				retval = 0;
+				break;
+			}
+		}
+		break;
+	}
+
+	case VIDIOC_G_CTRL: {
+		struct v4l2_control ctrl;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_G_CTRL");
+		s = cam->sensor;
+
+		if (!s->get_ctrl)
+			return -EINVAL;
+
+		if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+			return -EFAULT;
+
+		err = s->get_ctrl(cam, &ctrl);
+		if (err)
+			return err;
+
+		if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+			return -EFAULT;
+		break;
+	}
+
+	case VIDIOC_S_CTRL_OLD:
+	case VIDIOC_S_CTRL: {
+		struct v4l2_control ctrl;
+		int i, n;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_S_CTRL");
+		s = cam->sensor;
+
+		if (!s->set_ctrl)
+			return -EINVAL;
+
+		if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+			return -EFAULT;
+
+		n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
+
+		for (i = 0; i < n; i++) {
+			if (ctrl.id == s->qctrl[i].id) {
+				if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+					return -EINVAL;
+
+				if ((ctrl.value < s->qctrl[i].minimum) ||
+					(ctrl.value > s->qctrl[i].maximum))
+					return -ERANGE;
+
+				ctrl.value -= ctrl.value % s->qctrl[i].step;
+				break;
+			}
+		}
+
+		err = s->set_ctrl(cam, &ctrl);
+		if (err)
+			return err;
+		break;
+	}
+
+	case VIDIOC_CROPCAP: {
+		struct v4l2_cropcap cc;
+
+		PDEBUG(DBG_V4L2, "VIDIOC_CROPCAP");
+
+		if (copy_from_user(&cc, arg, sizeof(cc)))
+			return -EFAULT;
+
+		if (cc.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+
+		cc.pixelaspect.numerator = 1;
+		cc.pixelaspect.denominator = 1;
+		cc.bounds.top = 0;
+		cc.bounds.left = 0;
+		cc.bounds.width = M5602_DEFAULT_FRAME_WIDTH;
+		cc.bounds.height = M5602_DEFAULT_FRAME_HEIGHT;
+		cc.defrect.top = 0;
+		cc.defrect.left = 0;
+		cc.defrect.width = M5602_DEFAULT_FRAME_WIDTH;
+		cc.defrect.height = M5602_DEFAULT_FRAME_HEIGHT;
+
+		if (copy_to_user(arg, &cc, sizeof(cc)))
+			return -EFAULT;
+		break;
+	}
+
+	case VIDIOC_G_CROP:
+	case VIDIOC_S_CROP:
+		return -EINVAL;
+
+	case VIDIOC_G_STD:
+		PDEBUG(DBG_V4L2, "VIDIOC_G_STD");
+		return -EINVAL;
+
+	case VIDIOC_S_STD:
+		PDEBUG(DBG_V4L2, "VIDIOC_S_STD");
+		return -EINVAL;
+
+	case VIDIOC_QUERYSTD:
+		PDEBUG(DBG_V4L2, "VIDIOC_S_STD");
+		return -EINVAL;
+
+	case VIDIOC_ENUMSTD:
+		PDEBUG(DBG_V4L2, "VIDIOC_ENUMSTD");
+		return -EINVAL;
+
+	case VIDIOC_QUERYMENU:
+		PDEBUG(DBG_V4L2, "VIDIOC_QUERYMENU");
+		return -EINVAL;
+
+	case VIDIOC_ENUM_FRAMEINTERVALS:
+		PDEBUG(DBG_V4L2, "VIDIOC_ENUM_FRAMEINTERVALS");
+		return -EINVAL;
+
+	default:
+		PDEBUG(DBG_V4L2, "Unhandled IOCTL: 0x%x!", ioctlnr);
+		retval = -EINVAL;
+		break;
+	}
+	return retval;
+}
diff -r e5ca4534b543 linux/drivers/media/video/m5602/m5602_v4l2.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/video/m5602/m5602_v4l2.h	Wed Sep 17 07:53:53 2008 +0200
@@ -0,0 +1,53 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_V4L2_H_
+#define M5602_V4L2_H_
+
+#include "m5602_sensor.h"
+
+#define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control)
+
+extern unsigned int debug;
+
+int check_format(struct v4l2_format *fmt);
+
+u32 m5602_request_buffers(struct m5602_camera *cam, u32 count);
+void m5602_release_buffers(struct m5602_camera *cam);
+void m5602_empty_framequeues(struct m5602_camera *cam);
+int m5602_start_transfer(struct m5602_camera *cam);
+int m5602_stop_transfer(struct m5602_camera *cam);
+int m5602_stream_interrupt(struct m5602_camera *cam);
+void m5602_vm_open(struct vm_area_struct *vma);
+void m5602_vm_close(struct vm_area_struct *vma);
+int m5602_v4l_open(struct inode *inode, struct file *filp);
+int m5602_v4l_release(struct inode *inode, struct file *filp);
+int m5602_v4l_mmap(struct file *filp, struct vm_area_struct *vma);
+int m5602_v4l_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg);
+int m5602_v4l_do_ioctl(struct inode *inode,
+		       struct file *file,
+		       unsigned int ioctlnr,
+		       void *arg);
+
+void m5602_urb_complete(struct urb *urb);
+
+int m5602_probe_sensor(struct m5602_camera *cam);
+int m5602_initialize_camera(struct m5602_camera *cam);
+
+#endif

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

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

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

end of thread, other threads:[~2008-09-19 18:56 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-09-18  7:02 [PATCH][RFC] Add support for the ALi m5602 usb bridge webcam Erik Andrén
2008-09-19  4:54 ` Hans de Goede
2008-09-19 18:56   ` Erik Andrén

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox