media: video: tegra: Driver for 2MP SOC2030 imager for use with tegra isp
authorRebecca Schultz Zavin <rebecca@android.com>
Tue, 10 Aug 2010 23:49:45 +0000 (16:49 -0700)
committerColin Cross <ccross@android.com>
Wed, 6 Oct 2010 23:33:34 +0000 (16:33 -0700)
Change-Id: I5ec72fd2a06c97c8cbf496d69fe2b03c1ef0941e
Signed-off-by: Rebecca Schultz Zavin <rebecca@android.com>
drivers/media/video/tegra/Kconfig
drivers/media/video/tegra/Makefile
drivers/media/video/tegra/soc2030.c [new file with mode: 0644]
include/media/soc2030.h [new file with mode: 0755]

index 7d59186a8c40e20126f7087c8c16a9796e79c867..b2926322b7f85bed6dbc2e5fbaec813124536f03 100644 (file)
@@ -5,3 +5,10 @@ config VIDEO_OV5650
           This is a driver for the Omnivision OV5650 5MP camera sensor
          for use with the tegra isp.
 
+config VIDEO_SOC2030
+        tristate "SOC2030 camera sensor support"
+        depends on I2C && ARCH_TEGRA
+        ---help---
+          This is a driver for the SOC2030 2MP camera sensor
+         for use with the tegra isp.
+
index 0092374433c05eaf475a34c013e15f46e9623349..ad068a0ad31756d7d80f1a136e94a1481cf6b866 100644 (file)
@@ -3,3 +3,4 @@
 #
 obj-y                          += tegra_isp.o
 obj-$(CONFIG_VIDEO_OV5650)     += ov5650.o
+obj-$(CONFIG_VIDEO_SOC2030)    += soc2030.o
diff --git a/drivers/media/video/tegra/soc2030.c b/drivers/media/video/tegra/soc2030.c
new file mode 100644 (file)
index 0000000..67fdc3a
--- /dev/null
@@ -0,0 +1,918 @@
+/*
+ * soc2030.c - soc2030 sensor driver
+ *
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Contributors:
+ *      Rebecca Schultz Zavin <rebecca@android.com>
+ *
+ * Leverage OV9640.c
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <media/soc2030.h>
+
+struct soc2030_reg {
+       u16 addr;
+       u16 val;
+};
+
+struct soc2030_info {
+       int mode;
+       struct i2c_client *i2c_client;
+       struct soc2030_platform_data *pdata;
+};
+
+#define SOC2030_TABLE_END 1
+#define SOC2030_MAX_RETRIES 3
+#define SOC2030_POLL_RETRIES 5
+
+static struct soc2030_reg mode_1280x720[] = {
+       {0x321C, 0x0000},       /* By Pass TxFIFO = 0 */
+       {0x98C, 0x2703},        /* Output Width (A) */
+       {0x990, 0x0500},        /*       = 1280 */
+       {0x98C, 0x2705},        /* Output Height (A) */
+       {0x990, 0x02D0},        /*       = 720 */
+       {0x98C, 0x2707},        /* Output Width (B) */
+       {0x990, 0x0500},        /*       = 1280 */
+       {0x98C, 0x2709},        /* Output Height (B) */
+       {0x990, 0x02D0},        /*       = 720 */
+       {0x98C, 0x270D},        /* Row Start (A) */
+       {0x990, 0x0F6},         /*  = 246 */
+       {0x98C, 0x270F},        /* Column Start (A) */
+       {0x990, 0x0A6},         /*  = 166 */
+       {0x98C, 0x2711},        /* Row End (A) */
+       {0x990, 0x3CD},         /* = 973 */
+       {0x98C, 0x2713},        /* Column End (A) */
+       {0x990, 0x5AD},         /*  = 1453 */
+       {0x98C, 0x2715},        /* Row Speed (A) */
+       {0x990, 0x0111},        /*       = 273 */
+       {0x98C, 0x2717},        /* Read Mode (A) */
+       {0x990, 0x0024},        /*       = 36 */
+       {0x98C, 0x2719},        /* sensor_fine_correction (A) */
+       {0x990, 0x003A},        /*       = 58 */
+       {0x98C, 0x271B},        /* sensor_fine_IT_min (A) */
+       {0x990, 0x00F6},        /*       = 246 */
+       {0x98C, 0x271D},        /* sensor_fine_IT_max_margin (A) */
+       {0x990, 0x008B},        /*       = 139 */
+       {0x98C, 0x271F},        /* Frame Lines (A) */
+       {0x990, 0x032D},        /*       = 813 */
+       {0x98C, 0x2721},        /* Line Length (A) */
+       {0x990, 0x067C},        /*       = 1660 */
+       {0x98C, 0x2723},        /* Row Start (B) */
+       {0x990, 0x0F6},         /*  = 246 */
+       {0x98C, 0x2725},        /* Column Start (B) */
+       {0x990, 0x0A6},         /*  = 166 */
+       {0x98C, 0x2727},        /* Row End (B) */
+       {0x990, 0x3CD},         /*  = 973 */
+       {0x98C, 0x2729},        /* Column End (B) */
+       {0x990, 0x5AD},         /*  = 1453 */
+       {0x98C, 0x272B},        /* Row Speed (B) */
+       {0x990, 0x0111},        /*       = 273 */
+       {0x98C, 0x272D},        /* Read Mode (B) */
+       {0x990, 0x0024},        /*       = 36 */
+       {0x98C, 0x272F},        /* sensor_fine_correction (B) */
+       {0x990, 0x003A},        /*       = 58 */
+       {0x98C, 0x2731},        /* sensor_fine_IT_min (B) */
+       {0x990, 0x00F6},        /*       = 246 */
+       {0x98C, 0x2733},        /* sensor_fine_IT_max_margin (B) */
+       {0x990, 0x008B},        /*       = 139 */
+       {0x98C, 0x2735},        /* Frame Lines (B) */
+       {0x990, 0x032D},        /*       = 813 */
+       {0x98C, 0x2737},        /* Line Length (B) */
+       {0x990, 0x067C},        /*       = 1660 */
+       {0x98C, 0x2739},        /* Crop_X0 (A) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x273B},        /* Crop_X1 (A) */
+       {0x990, 0x04FF},        /*       = 1279 */
+       {0x98C, 0x273D},        /* Crop_Y0 (A) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x273F},        /* Crop_Y1 (A) */
+       {0x990, 0x02CF},        /*       = 719 */
+       {0x98C, 0x2747},        /* Crop_X0 (B) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x2749},        /* Crop_X1 (B) */
+       {0x990, 0x04FF},        /*       = 1279 */
+       {0x98C, 0x274B},        /* Crop_Y0 (B) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x274D},        /* Crop_Y1 (B) */
+       {0x990, 0x02CF},        /*       = 719 */
+       {0x98C, 0x222D},        /* R9 Step */
+       {0x990, 0x00CB},        /*       = 203 */
+       {0x98C, 0xA408},        /* search_f1_50 */
+       {0x990, 0x31},          /*  = 49 */
+       {0x98C, 0xA409},        /* search_f2_50 */
+       {0x990, 0x33},          /*  = 51 */
+       {0x98C, 0xA40A},        /* search_f1_60 */
+       {0x990, 0x3C},          /*  = 60 */
+       {0x98C, 0xA40B},        /* search_f2_60 */
+       {0x990, 0x3E},          /*  = 62 */
+       {0x98C, 0x2411},        /* R9_Step_60 (A) */
+       {0x990, 0x00CB},        /*       = 203 */
+       {0x98C, 0x2413},        /* R9_Step_50 (A) */
+       {0x990, 0x00F4},        /*       = 244 */
+       {0x98C, 0x2415},        /* R9_Step_60 (B) */
+       {0x990, 0x00CB},        /*       = 203 */
+       {0x98C, 0x2417},        /* R9_Step_50 (B) */
+       {0x990, 0x00F4},        /*       = 244 */
+       {0x98C, 0xA404},        /* FD Mode */
+       {0x990, 0x10},          /*  = 16 */
+       {0x98C, 0xA40D},        /* Stat_min */
+       {0x990, 0x02},          /*  = 2 */
+       {0x98C, 0xA40E},        /* Stat_max */
+       {0x990, 0x03},          /*  = 3 */
+       {0x98C, 0xA410},        /* Min_amplitude */
+       {0x990, 0x0A},          /*  = 10 */
+
+       /* fix at 30 fps */
+       {0x098C, 0xA215},       /*  MCU_ADDRESS [RESERVED_AE_15] */
+       {0x0990, 0x0001},       /*  MCU_DATA_0 */
+       {0x098C, 0xA20C},       /*  MCU_ADDRESS [AE_MAX_INDEX] */
+       {0x0990, 0x0001},       /*  MCU_DATA_0 */
+
+       {SOC2030_TABLE_END, 0x0000}
+
+};
+
+/**
+ * SetMode Sequence for 1280X720.Phase 0. Sensor Dependent.
+ * This sequence should put sensor in streaming mode for 2592x1944
+ * This is usually given by the FAE or the sensor vendor.
+ */
+static struct soc2030_reg mode_1600x1200[] = {
+       {0x321C, 0x0000},       /* By Pass TxFIFO = 0 */
+       {0x98C, 0x2703},        /* Output Width (A) */
+       {0x990, 0x0640},        /*       = 1600 */
+       {0x98C, 0x2705},        /* Output Height (A) */
+       {0x990, 0x04B0},        /*       = 1200 */
+       {0x98C, 0x2707},        /* Output Width (B) */
+       {0x990, 0x0640},        /*       = 1600 */
+       {0x98C, 0x2709},        /* Output Height (B) */
+       {0x990, 0x04B0},        /*       = 1200 */
+       {0x98C, 0x270D},        /* Row Start (A) */
+       {0x990, 0x004},         /*  = 4 */
+       {0x98C, 0x270F},        /* Column Start (A) */
+       {0x990, 0x004},         /*  = 4 */
+       {0x98C, 0x2711},        /* Row End (A) */
+       {0x990, 0x4BB},         /*  = 1211 */
+       {0x98C, 0x2713},        /* Column End (A) */
+       {0x990, 0x64B},         /*  = 1611 */
+       {0x98C, 0x2715},        /* Row Speed (A) */
+       {0x990, 0x0111},        /*       = 273 */
+       {0x98C, 0x2717},        /* Read Mode (A) */
+       {0x990, 0x0024},        /*       = 36 */
+       {0x98C, 0x2719},        /* sensor_fine_correction (A) */
+       {0x990, 0x003A},        /*       = 58 */
+       {0x98C, 0x271B},        /* sensor_fine_IT_min (A) */
+       {0x990, 0x00F6},        /*       = 246 */
+       {0x98C, 0x271D},        /* sensor_fine_IT_max_margin (A) */
+       {0x990, 0x008B},        /*       = 139 */
+       {0x98C, 0x271F},        /* Frame Lines (A) */
+       {0x990, 0x050D},        /*       = 1293 */
+       {0x98C, 0x2721},        /* Line Length (A) */
+       {0x990, 0x085A},        /*       = 2138 */
+       {0x98C, 0x2723},        /* Row Start (B) */
+       {0x990, 0x004},         /*  = 4 */
+       {0x98C, 0x2725},        /* Column Start (B) */
+       {0x990, 0x004},         /*  = 4 */
+       {0x98C, 0x2727},        /* Row End (B) */
+       {0x990, 0x4BB},         /*  = 1211 */
+       {0x98C, 0x2729},        /* Column End (B) */
+       {0x990, 0x64B},         /*  = 1611 */
+       {0x98C, 0x272B},        /* Row Speed (B) */
+       {0x990, 0x0111},        /*       = 273 */
+       {0x98C, 0x272D},        /* Read Mode (B) */
+       {0x990, 0x0024},        /*       = 36 */
+       {0x98C, 0x272F},        /* sensor_fine_correction (B) */
+       {0x990, 0x003A},        /*       = 58 */
+       {0x98C, 0x2731},        /* sensor_fine_IT_min (B) */
+       {0x990, 0x00F6},        /*       = 246 */
+       {0x98C, 0x2733},        /* sensor_fine_IT_max_margin (B) */
+       {0x990, 0x008B},        /*       = 139 */
+       {0x98C, 0x2735},        /* Frame Lines (B) */
+       {0x990, 0x050D},        /*       = 1293 */
+       {0x98C, 0x2737},        /* Line Length (B) */
+       {0x990, 0x085A},        /*       = 2138 */
+       {0x98C, 0x2739},        /* Crop_X0 (A) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x273B},        /* Crop_X1 (A) */
+       {0x990, 0x063F},        /*       = 1599 */
+       {0x98C, 0x273D},        /* Crop_Y0 (A) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x273F},        /* Crop_Y1 (A) */
+       {0x990, 0x04AF},        /*       = 1199 */
+       {0x98C, 0x2747},        /* Crop_X0 (B) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x2749},        /* Crop_X1 (B) */
+       {0x990, 0x063F},        /*       = 1599 */
+       {0x98C, 0x274B},        /* Crop_Y0 (B) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x274D},        /* Crop_Y1 (B) */
+       {0x990, 0x04AF},        /*       = 1199 */
+       {0x98C, 0x222D},        /* R9 Step */
+       {0x990, 0x009E},        /*       = 158 */
+       {0x98C, 0xA408},        /* search_f1_50 */
+       {0x990, 0x26},          /*  = 38 */
+       {0x98C, 0xA409},        /* search_f2_50 */
+       {0x990, 0x28},          /*  = 40 */
+       {0x98C, 0xA40A},        /* search_f1_60 */
+       {0x990, 0x2E},          /*  = 46 */
+       {0x98C, 0xA40B},        /* search_f2_60 */
+       {0x990, 0x30},          /*  = 48 */
+       {0x98C, 0x2411},        /* R9_Step_60 (A) */
+       {0x990, 0x009E},        /*       = 158 */
+       {0x98C, 0x2413},        /* R9_Step_50 (A) */
+       {0x990, 0x00BD},        /*       = 189 */
+       {0x98C, 0x2415},        /* R9_Step_60 (B) */
+       {0x990, 0x009E},        /*       = 158 */
+       {0x98C, 0x2417},        /* R9_Step_50 (B) */
+       {0x990, 0x00BD},        /*       = 189 */
+       {0x98C, 0xA404},        /* FD Mode */
+       {0x990, 0x10},          /*  = 16 */
+       {0x98C, 0xA40D},        /* Stat_min */
+       {0x990, 0x02},          /*  = 2 */
+       {0x98C, 0xA40E},        /* Stat_max */
+       {0x990, 0x03},          /*  = 3 */
+       {0x98C, 0xA410},        /* Min_amplitude */
+       {0x990, 0x0A},          /*  = 10 */
+
+       /* fix at 30 fps */
+       {0x098C, 0xA215},       /*  MCU_ADDRESS [RESERVED_AE_15] */
+       {0x0990, 0x0001},       /*  MCU_DATA_0 */
+       {0x098C, 0xA20C},       /*  MCU_ADDRESS [AE_MAX_INDEX] */
+       {0x0990, 0x0001},       /*  MCU_DATA_0 */
+
+       {SOC2030_TABLE_END, 0x0000}
+
+};
+
+/**
+ * SetMode Sequence for 1280X720.Phase 0. Sensor Dependent.
+ * This sequence should put sensor in streaming mode for 2592x1944
+ * This is usually given by the FAE or the sensor vendor.
+ */
+static struct soc2030_reg mode_800x600[] = {
+       {0x321C, 0x0000},       /* By Pass TxFIFO = 0 */
+       {0x98C, 0x2703},        /* Output Width (A) */
+       {0x990, 0x0320},        /*       = 800 */
+       {0x98C, 0x2705},        /* Output Height (A) */
+       {0x990, 0x0258},        /*       = 600 */
+       {0x98C, 0x2707},        /* Output Width (B) */
+       {0x990, 0x0640},        /*       = 1600 */
+       {0x98C, 0x2709},        /* Output Height (B) */
+       {0x990, 0x04B0},        /*       = 1200 */
+       {0x98C, 0x270D},        /* Row Start (A) */
+       {0x990, 0x000},         /*  = 0 */
+       {0x98C, 0x270F},        /* Column Start (A) */
+       {0x990, 0x000},         /*  = 0 */
+       {0x98C, 0x2711},        /* Row End (A) */
+       {0x990, 0x4BD},         /*  = 1213 */
+       {0x98C, 0x2713},        /* Column End (A) */
+       {0x990, 0x64D},         /*  = 1613 */
+       {0x98C, 0x2715},        /* Row Speed (A) */
+       {0x990, 0x0111},        /*       = 273 */
+       {0x98C, 0x2717},        /* Read Mode (A) */
+       {0x990, 0x046C},        /*       = 1132 */
+       {0x98C, 0x2719},        /* sensor_fine_correction (A) */
+       {0x990, 0x005A},        /*       = 90 */
+       {0x98C, 0x271B},        /* sensor_fine_IT_min (A) */
+       {0x990, 0x01BE},        /*       = 446 */
+       {0x98C, 0x271D},        /* sensor_fine_IT_max_margin (A) */
+       {0x990, 0x0131},        /*       = 305 */
+       {0x98C, 0x271F},        /* Frame Lines (A) */
+       {0x990, 0x02B3},        /*       = 691 */
+       {0x98C, 0x2721},        /* Line Length (A) */
+       {0x990, 0x0853},        /*       = 2131 */
+       {0x98C, 0x2723},        /* Row Start (B) */
+       {0x990, 0x004},         /*  = 4 */
+       {0x98C, 0x2725},        /* Column Start (B) */
+       {0x990, 0x004},         /*  = 4 */
+       {0x98C, 0x2727},        /* Row End (B) */
+       {0x990, 0x4BB},         /*  = 1211 */
+       {0x98C, 0x2729},        /* Column End (B) */
+       {0x990, 0x64B},         /*  = 1611 */
+       {0x98C, 0x272B},        /* Row Speed (B) */
+       {0x990, 0x0111},        /*       = 273 */
+       {0x98C, 0x272D},        /* Read Mode (B) */
+       {0x990, 0x0024},        /*       = 36 */
+       {0x98C, 0x272F},        /* sensor_fine_correction (B) */
+       {0x990, 0x003A},        /*       = 58 */
+       {0x98C, 0x2731},        /* sensor_fine_IT_min (B) */
+       {0x990, 0x00F6},        /*       = 246 */
+       {0x98C, 0x2733},        /* sensor_fine_IT_max_margin (B) */
+       {0x990, 0x008B},        /*       = 139 */
+       {0x98C, 0x2735},        /* Frame Lines (B) */
+       {0x990, 0x050D},        /*       = 1293 */
+       {0x98C, 0x2737},        /* Line Length (B) */
+       {0x990, 0x085A},        /*       = 2138 */
+       {0x98C, 0x2739},        /* Crop_X0 (A) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x273B},        /* Crop_X1 (A) */
+       {0x990, 0x031F},        /*       = 799 */
+       {0x98C, 0x273D},        /* Crop_Y0 (A) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x273F},        /* Crop_Y1 (A) */
+       {0x990, 0x0257},        /*       = 599 */
+       {0x98C, 0x2747},        /* Crop_X0 (B) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x2749},        /* Crop_X1 (B) */
+       {0x990, 0x063F},        /*       = 1599 */
+       {0x98C, 0x274B},        /* Crop_Y0 (B) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x274D},        /* Crop_Y1 (B) */
+       {0x990, 0x04AF},        /*       = 1199 */
+       {0x98C, 0x222D},        /* R9 Step */
+       {0x990, 0x009E},        /*       = 158 */
+       {0x98C, 0xA408},        /* search_f1_50 */
+       {0x990, 0x26},          /*  = 38 */
+       {0x98C, 0xA409},        /* search_f2_50 */
+       {0x990, 0x28},          /*  = 40 */
+       {0x98C, 0xA40A},        /* search_f1_60 */
+       {0x990, 0x2E},          /*  = 46 */
+       {0x98C, 0xA40B},        /* search_f2_60 */
+       {0x990, 0x30},          /*  = 48 */
+       {0x98C, 0x2411},        /* R9_Step_60 (A) */
+       {0x990, 0x009E},        /*       = 158 */
+       {0x98C, 0x2413},        /* R9_Step_50 (A) */
+       {0x990, 0x00BE},        /*       = 190 */
+       {0x98C, 0x2415},        /* R9_Step_60 (B) */
+       {0x990, 0x009E},        /*       = 158 */
+       {0x98C, 0x2417},        /* R9_Step_50 (B) */
+       {0x990, 0x00BD},        /*       = 189 */
+       {0x98C, 0xA404},        /* FD Mode */
+       {0x990, 0x10},          /*  = 16 */
+       {0x98C, 0xA40D},        /* Stat_min */
+       {0x990, 0x02},          /*  = 2 */
+       {0x98C, 0xA40E},        /* Stat_max */
+       {0x990, 0x03},          /*  = 3 */
+       {0x98C, 0xA410},        /* Min_amplitude */
+       {0x990, 0x0A},          /*  = 10 */
+#if 0
+       /* fix at 30 fps */
+       {0x098C, 0xA215},       /*  MCU_ADDRESS [RESERVED_AE_15] */
+       {0x0990, 0x0001},       /*  MCU_DATA_0 */
+       {0x098C, 0xA20C},       /*  MCU_ADDRESS [AE_MAX_INDEX] */
+       {0x0990, 0x0001},       /*  MCU_DATA_0 */
+#endif
+
+       {SOC2030_TABLE_END, 0x0000}
+
+};
+
+/**
+ * SetMode Sequence for 1280X720.Phase 0. Sensor Dependent.
+ * This sequence should put sensor in streaming mode for 2592x1944
+ * This is usually given by the FAE or the sensor vendor.
+ */
+static struct soc2030_reg mode_816x616[] = {
+       {0x321C, 0x0000},       /* By Pass TxFIFO = 0 */
+       {0x98C, 0x2703},        /* Output Width (A) */
+       {0x990, 0x0330},        /*       = 816 */
+       {0x98C, 0x2705},        /* Output Height (A) */
+       {0x990, 0x0268},        /*       = 616 */
+       {0x98C, 0x2707},        /* Output Width (B) */
+       {0x990, 0x0640},        /*       = 1600 */
+       {0x98C, 0x2709},        /* Output Height (B) */
+       {0x990, 0x04B0},        /*       = 1200 */
+       {0x98C, 0x270D},        /* Row Start (A) */
+       {0x990, 0x12A},         /*  = 298 */
+       {0x98C, 0x270F},        /* Column Start (A) */
+       {0x990, 0x18E},         /*  = 398 */
+       {0x98C, 0x2711},        /* Row End (A) */
+       {0x990, 0x399},         /*  = 921 */
+       {0x98C, 0x2713},        /* Column End (A) */
+       {0x990, 0x4C5},         /*  = 1221 */
+       {0x98C, 0x2715},        /* Row Speed (A) */
+       {0x990, 0x0111},        /*       = 273 */
+       {0x98C, 0x2717},        /* Read Mode (A) */
+       {0x990, 0x0024},        /*       = 36 */
+       {0x98C, 0x2719},        /* sensor_fine_correction (A) */
+       {0x990, 0x003A},        /*       = 58 */
+       {0x98C, 0x271B},        /* sensor_fine_IT_min (A) */
+       {0x990, 0x00F6},        /*       = 246 */
+       {0x98C, 0x271D},        /* sensor_fine_IT_max_margin (A) */
+       {0x990, 0x008B},        /*       = 139 */
+       {0x98C, 0x271F},        /* Frame Lines (A) */
+       {0x990, 0x02C5},        /*       = 709 */
+       {0x98C, 0x2721},        /* Line Length (A) */
+       {0x990, 0x081D},        /*       = 2077 */
+       {0x98C, 0x2723},        /* Row Start (B) */
+       {0x990, 0x004},         /*  = 4 */
+       {0x98C, 0x2725},        /* Column Start (B) */
+       {0x990, 0x004},         /*  = 4 */
+       {0x98C, 0x2727},        /* Row End (B) */
+       {0x990, 0x4BB},         /*  = 1211 */
+       {0x98C, 0x2729},        /* Column End (B) */
+       {0x990, 0x64B},         /*  = 1611 */
+       {0x98C, 0x272B},        /* Row Speed (B) */
+       {0x990, 0x0111},        /*       = 273 */
+       {0x98C, 0x272D},        /* Read Mode (B) */
+       {0x990, 0x0024},        /*       = 36 */
+       {0x98C, 0x272F},        /* sensor_fine_correction (B) */
+       {0x990, 0x003A},        /*       = 58 */
+       {0x98C, 0x2731},        /* sensor_fine_IT_min (B) */
+       {0x990, 0x00F6},        /*       = 246 */
+       {0x98C, 0x2733},        /* sensor_fine_IT_max_margin (B) */
+       {0x990, 0x008B},        /*       = 139 */
+       {0x98C, 0x2735},        /* Frame Lines (B) */
+       {0x990, 0x050D},        /*       = 1293 */
+       {0x98C, 0x2737},        /* Line Length (B) */
+       {0x990, 0x085A},        /*       = 2138 */
+       {0x98C, 0x2739},        /* Crop_X0 (A) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x273B},        /* Crop_X1 (A) */
+       {0x990, 0x032F},        /*       = 815 */
+       {0x98C, 0x273D},        /* Crop_Y0 (A) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x273F},        /* Crop_Y1 (A) */
+       {0x990, 0x0267},        /*       = 615 */
+       {0x98C, 0x2747},        /* Crop_X0 (B) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x2749},        /* Crop_X1 (B) */
+       {0x990, 0x063F},        /*       = 1599 */
+       {0x98C, 0x274B},        /* Crop_Y0 (B) */
+       {0x990, 0x0000},        /*       = 0 */
+       {0x98C, 0x274D},        /* Crop_Y1 (B) */
+       {0x990, 0x04AF},        /*       = 1199 */
+       {0x98C, 0x222D},        /* R9 Step */
+       {0x990, 0x00A2},        /*       = 162 */
+       {0x98C, 0xA408},        /* search_f1_50 */
+       {0x990, 0x27},          /*  = 39 */
+       {0x98C, 0xA409},        /* search_f2_50 */
+       {0x990, 0x29},          /*  = 41 */
+       {0x98C, 0xA40A},        /* search_f1_60 */
+       {0x990, 0x2F},          /*  = 47 */
+       {0x98C, 0xA40B},        /* search_f2_60 */
+       {0x990, 0x31},          /*  = 49 */
+       {0x98C, 0x2411},        /* R9_Step_60 (A) */
+       {0x990, 0x00A2},        /*       = 162 */
+       {0x98C, 0x2413},        /* R9_Step_50 (A) */
+       {0x990, 0x00C3},        /*       = 195 */
+       {0x98C, 0x2415},        /* R9_Step_60 (B) */
+       {0x990, 0x009E},        /*       = 158 */
+       {0x98C, 0x2417},        /* R9_Step_50 (B) */
+       {0x990, 0x00BD},        /*       = 189 */
+       {0x98C, 0xA404},        /* FD Mode */
+       {0x990, 0x10},          /*  = 16 */
+       {0x98C, 0xA40D},        /* Stat_min */
+       {0x990, 0x02},          /*  = 2 */
+       {0x98C, 0xA40E},        /* Stat_max */
+       {0x990, 0x03},          /*  = 3 */
+       {0x98C, 0xA410},        /* Min_amplitude */
+       {0x990, 0x0A},          /*  = 10 */
+
+       /* fix at 30 fps */
+       {0x098C, 0xA215},       /*  MCU_ADDRESS [RESERVED_AE_15] */
+       {0x0990, 0x0001},       /*  MCU_DATA_0 */
+       {0x098C, 0xA20C},       /*  MCU_ADDRESS [AE_MAX_INDEX] */
+       {0x0990, 0x0001},       /*  MCU_DATA_0 */
+
+       {SOC2030_TABLE_END, 0x0000}
+
+};
+
+static struct soc2030_reg pll_table[] = {
+       {0x001e, 0x0707},       /*  Pad Slew rate */
+       {0x0014, 0x21F9},       /*  PLL_CONTROL */
+       {0x0010, 0x011B},       /*  PLL_DIVIDERS */
+       {0x0012, 0x10F7},       /*  PLL_P_DIVIDERS */
+       {0x0014, 0x21FB},       /*  PLL_CONTROL */
+       {0x0014, 0x20FB},       /*  PLL_CONTROL */
+       {SOC2030_TABLE_END, 0x0000}
+};
+
+enum {
+       SOC2030_MODE_800x600,
+       SOC2030_MODE_1600x1200,
+};
+
+static struct soc2030_reg *mode_table[] = {
+       [SOC2030_MODE_800x600] = mode_800x600,
+       [SOC2030_MODE_1600x1200] = mode_1600x1200,
+};
+
+static int soc2030_read_reg(struct i2c_client *client, u16 addr, u16 *val)
+{
+       int err;
+       struct i2c_msg msg[2];
+       unsigned char data[4];
+
+       if (!client->adapter)
+               return -ENODEV;
+
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].len = 2;
+       msg[0].buf = data;
+       data[0] = (u8) (addr >> 8);
+       data[1] = (u8) (addr & 0xff);
+
+       msg[1].addr = client->addr;
+       msg[1].flags = I2C_M_RD;
+       msg[1].len = 2;
+       msg[1].buf = data + 2;
+       err = i2c_transfer(client->adapter, msg, 2);
+
+       if (err != 2)
+               return -EIO;
+
+       *val = ((u16)(data[2] << 8)) | data[3];
+
+       return 0;
+}
+
+static int soc2030_write_reg(struct i2c_client *client, u16 addr, u16 val)
+{
+       int err;
+       struct i2c_msg msg;
+       unsigned char data[4];
+       int retry = 0;
+
+       if (!client->adapter)
+               return -ENODEV;
+
+       data[0] = (u8) (addr >> 8);
+       data[1] = (u8) (addr & 0xff);
+       data[2] = (u8) (val >> 8);
+       data[3] = (u8) (val & 0xff);
+
+       msg.addr = client->addr;
+       msg.flags = 0;
+       msg.len = 4;
+       msg.buf = data;
+
+       do {
+               err = i2c_transfer(client->adapter, &msg, 1);
+               if (err == 1)
+                       return 0;
+               retry++;
+               pr_err("soc2030: i2c transfer failed, retrying %x %x\n",
+                      addr, val);
+               msleep(3);
+       } while (retry <= SOC2030_MAX_RETRIES);
+
+       return retry > SOC2030_MAX_RETRIES ? -EIO : err;
+}
+
+static int soc2030_write_table(struct i2c_client *client,
+                              const struct soc2030_reg table[])
+{
+       int err;
+       const struct soc2030_reg *next;
+
+       for (next = table; next->addr != SOC2030_TABLE_END; next++) {
+               err = soc2030_write_reg(client, next->addr, next->val);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static int soc2030_write_bits(struct i2c_client *client, u16 addr, u16 val,
+                             u16 mask)
+{
+       u16 rval, wval;
+       int err;
+
+       err = soc2030_read_reg(client, addr, &rval);
+       if (err) {
+               pr_err("soc2030: error reading from %x\n", addr);
+               return err;
+       }
+       wval = ((~mask) & rval) | val;
+       err = soc2030_write_reg(client, addr, wval);
+       return err;
+}
+
+static int soc2030_clear_bits(struct i2c_client *client, u16 addr, u16 bits)
+{
+       return soc2030_write_bits(client, addr, 0, bits);
+}
+
+static int soc2030_set_bits(struct i2c_client *client, u16 addr, u16 bits)
+{
+       return soc2030_write_bits(client, addr, bits, bits);
+}
+
+static int soc2030_poll(struct i2c_client *client, u16 addr, u16 expected,
+                       u16 mask)
+{
+       u16 val;
+       int try, err;
+
+       for (try = 0; try < SOC2030_POLL_RETRIES; try++) {
+               err = soc2030_read_reg(client, addr, &val);
+               if (err)
+                       return err;
+               if (expected == (val & mask)) {
+                       pr_info("poll success %x: %x == %x & %x\n", addr,
+                               expected, val, mask);
+                       return 0;
+               }
+               msleep(50);
+       }
+       pr_err("soc2030: poll for %x == ([%x]=%x) & %x failed\n", expected,
+              addr, val, mask);
+       return -EIO;
+
+}
+
+static int soc2030_poll_bit_set(struct i2c_client *client, u16 addr, u16 bit)
+{
+       return soc2030_poll(client, addr, bit, bit);
+}
+
+static int soc2030_poll_bit_clear(struct i2c_client *client, u16 addr, u16 bit)
+{
+       return soc2030_poll(client, addr, 0, bit);
+}
+
+static int soc2030_write_xdma_reg(struct i2c_client *client, u16 addr, u16 val)
+{
+       int err;
+
+       err = soc2030_write_reg(client, 0x098c, addr);
+       if (err)
+               return err;
+       err = soc2030_write_reg(client, 0x0990, val);
+       if (err)
+               return err;
+       return 0;
+}
+
+static int soc2030_read_xdma_reg(struct i2c_client *client, u16 addr, u16 *val)
+{
+       int err;
+
+       err = soc2030_write_reg(client, 0x098c, addr);
+       if (err)
+               return err;
+       err = soc2030_read_reg(client, 0x0990, val);
+       if (err)
+               return err;
+       return 0;
+}
+
+static int soc2030_poll_xdma_reg(struct i2c_client *client, u16 addr,
+                                u16 expected)
+{
+       int try, err;
+       u16 val;
+
+       for (try = 0; try < SOC2030_POLL_RETRIES; try++) {
+               err = soc2030_read_xdma_reg(client, addr, &val);
+               if (err)
+                       return err;
+               if (expected == val)
+                       return 0;
+               msleep(50);
+       }
+       pr_err("soc2030: xdma poll for %x == ([%x]=%x) failed\n", expected,
+              addr, val);
+       return -EINVAL;
+}
+
+static int soc2030_set_mode(struct soc2030_info *info,
+                           struct soc2030_mode *mode)
+{
+       int sensor_mode, err;
+
+       if (mode->xres == 800 && mode->yres == 600)
+               sensor_mode = SOC2030_MODE_800x600;
+       else if (mode->xres == 1600 && mode->yres == 1200)
+               sensor_mode = SOC2030_MODE_1600x1200;
+       else {
+               pr_err("%s: invalid resolution supplied to set mode %d %d\n",
+                      __func__, mode->xres, mode->yres);
+               return -EINVAL;
+       }
+
+       /* reset */
+       soc2030_set_bits(info->i2c_client, 0x001a, 0x3);
+       msleep(10);
+       soc2030_clear_bits(info->i2c_client, 0x001a, 0x1);
+
+       /* write the pll table */
+       soc2030_write_table(info->i2c_client, pll_table);
+
+       /* wait for pll lock */
+       err = soc2030_poll_bit_set(info->i2c_client, 0x0014, 0x8000);
+       if (err)
+               return err;
+
+       /* enable the pll */
+       soc2030_clear_bits(info->i2c_client, 0x0014, 0x0001);
+       /* enable parallel output */
+       soc2030_set_bits(info->i2c_client, 0x001a, 0x200);
+       /* disable mipi */
+       soc2030_clear_bits(info->i2c_client, 0x001a, 0x4);
+       /* disable mcu */
+       soc2030_set_bits(info->i2c_client, 0x0018, 0x4);
+       /* leave standby */
+       soc2030_clear_bits(info->i2c_client, 0x0018, 0x1);
+       /* wait to complete leave standby */
+       soc2030_poll_bit_clear(info->i2c_client, 0x0018, 0x4000);
+
+       err = soc2030_write_table(info->i2c_client, mode_table[sensor_mode]);
+       if (err)
+               return err;
+
+       /* invert the pixel clock */
+       soc2030_write_xdma_reg(info->i2c_client, 0x2755, 0x0200);
+       soc2030_write_xdma_reg(info->i2c_client, 0x2757, 0x0200);
+
+       /* enable mcu */
+       soc2030_clear_bits(info->i2c_client, 0x0018, 0x4);
+
+       /* wait for preview state */
+       soc2030_poll_xdma_reg(info->i2c_client, 0xa104, 0x3);
+
+       /* refresh the sequencer mode */
+       soc2030_write_xdma_reg(info->i2c_client, 0xa103, 0x0006);
+
+       /* wait for cmd complete */
+       soc2030_poll_xdma_reg(info->i2c_client, 0xa103, 0x0);
+
+       /* refresh sequencer state */
+       soc2030_write_xdma_reg(info->i2c_client, 0xa103, 0x0005);
+
+       /* wait for cmd complete */
+       soc2030_poll_xdma_reg(info->i2c_client, 0xa103, 0x0);
+
+       info->mode = sensor_mode;
+       return 0;
+}
+
+static int soc2030_get_status(struct soc2030_info *info, u16 *status)
+{
+       int err;
+
+       *status = 0;
+       err = soc2030_read_xdma_reg(info->i2c_client, 0x0, status);
+       if (err)
+               return err;
+       err = soc2030_read_xdma_reg(info->i2c_client, 0x2104, status + 1);
+       if (err)
+               return err;
+       err = soc2030_read_xdma_reg(info->i2c_client, 0x2703, status + 2);
+       if (err)
+               return err;
+       err = soc2030_read_xdma_reg(info->i2c_client, 0x2705, status + 3);
+       if (err)
+               return err;
+       err = soc2030_read_xdma_reg(info->i2c_client, 0x2737, status + 4);
+       if (err)
+               return err;
+       pr_info("%s: [0]=%x [2104]=%x [2703]=%x [2705]=%x [2737]=%x\n"
+               , __func__, status[0], status[1], status[2], status[3],
+               status[4]);
+       return 0;
+}
+
+
+static int soc2030_ioctl(struct inode *inode, struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       struct soc2030_info *info = file->private_data;
+
+       switch (cmd) {
+       case SOC2030_IOCTL_SET_MODE:
+       {
+               struct soc2030_mode mode;
+               if (copy_from_user(&mode,
+                                  (const void __user *)arg,
+                                  sizeof(struct soc2030_mode))) {
+                       pr_info("%s: Error copying from user\n", __func__);
+                       return -EFAULT;
+               }
+
+               return soc2030_set_mode(info, &mode);
+       }
+       case SOC2030_IOCTL_GET_STATUS:
+       {
+               int err;
+               u16 status[5];
+
+               err = soc2030_get_status(info, status);
+               if (err)
+                       return err;
+               if (copy_to_user((void __user *)arg, &status,
+                                10)) {
+                       pr_info("%s: Error copying to user\n", __func__);
+                       return -EFAULT;
+               }
+               return 0;
+       }
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static struct soc2030_info *info;
+
+static int soc2030_open(struct inode *inode, struct file *file)
+{
+       file->private_data = info;
+       if (info->pdata && info->pdata->power_on)
+               info->pdata->power_on();
+       return 0;
+}
+
+int soc2030_release(struct inode *inode, struct file *file)
+{
+       if (info->pdata && info->pdata->power_off)
+               info->pdata->power_off();
+       file->private_data = NULL;
+       return 0;
+}
+
+
+static const struct file_operations soc2030_fileops = {
+       .owner = THIS_MODULE,
+       .open = soc2030_open,
+       .ioctl = soc2030_ioctl,
+       .release = soc2030_release,
+};
+
+static struct miscdevice soc2030_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "soc2030",
+       .fops = &soc2030_fileops,
+};
+
+static int soc2030_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       int err;
+
+       pr_info("soc2030: probing sensor.\n");
+
+       info = kzalloc(sizeof(struct soc2030_info), GFP_KERNEL);
+       if (!info) {
+               pr_err("soc2030: Unable to allocate memory!\n");
+               return -ENOMEM;
+       }
+
+       err = misc_register(&soc2030_device);
+       if (err) {
+               pr_err("soc2030: Unable to register misc device!\n");
+               kfree(info);
+               return err;
+       }
+
+       info->pdata = client->dev.platform_data;
+       info->i2c_client = client;
+
+       i2c_set_clientdata(client, info);
+       return 0;
+}
+
+static int soc2030_remove(struct i2c_client *client)
+{
+       struct soc2030_info *info;
+       info = i2c_get_clientdata(client);
+       misc_deregister(&soc2030_device);
+       kfree(info);
+       return 0;
+}
+
+static const struct i2c_device_id soc2030_id[] = {
+       { "soc2030", 0 },
+       { },
+};
+
+MODULE_DEVICE_TABLE(i2c, soc2030_id);
+
+static struct i2c_driver soc2030_i2c_driver = {
+       .driver = {
+               .name = "soc2030",
+               .owner = THIS_MODULE,
+       },
+       .probe = soc2030_probe,
+       .remove = soc2030_remove,
+       .id_table = soc2030_id,
+};
+
+static int __init soc2030_init(void)
+{
+       return i2c_add_driver(&soc2030_i2c_driver);
+}
+
+static void __exit soc2030_exit(void)
+{
+       i2c_del_driver(&soc2030_i2c_driver);
+}
+
+module_init(soc2030_init);
+module_exit(soc2030_exit);
+
diff --git a/include/media/soc2030.h b/include/media/soc2030.h
new file mode 100755 (executable)
index 0000000..d3ca24b
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef __SOC2030_H__
+#define __SOC2030_H__
+
+#include <linux/ioctl.h>  /* For IOCTL macros */
+
+#define SOC2030_IOCTL_SET_MODE         _IOWR('o', 1, struct soc2030_mode)
+#define SOC2030_IOCTL_GET_STATUS        _IOC(_IOC_READ, 'o', 2, 10)
+
+
+struct soc2030_mode {
+       int xres;
+       int yres;
+};
+
+#ifdef __KERNEL__
+struct soc2030_platform_data {
+       int (*power_on)(void);
+       int (*power_off)(void);
+
+};
+#endif /* __KERNEL__ */
+
+#endif  /* __SOC2030_H__ */
+