msm:camera add kernel driver for front camera sensor (sp1628)

Add sp1628 camera sensor driver for 8x10

Change-Id: I4c23c55a25636b2c4ab70e9b5d1a82dbb95e0f62
Signed-off-by: Ju He <juhe@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-8610.c b/arch/arm/mach-msm/clock-8610.c
index 2f4677f..18a81d0 100644
--- a/arch/arm/mach-msm/clock-8610.c
+++ b/arch/arm/mach-msm/clock-8610.c
@@ -3001,8 +3001,10 @@
 	/* MM sensor clocks */
 	CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006f"),
 	CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006d"),
+	CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "6-0078"),
 	CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006f"),
 	CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006d"),
+	CLK_LOOKUP("cam_clk", mclk1_clk.c, "6-0078"),
 
 
 	/* CSIPHY clocks */
diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile
index 5104bcb..b315959 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile
@@ -11,3 +11,4 @@
 obj-$(CONFIG_OV2720) += ov2720.o
 obj-$(CONFIG_OV9724) += ov9724.o
 obj-$(CONFIG_MT9M114) += mt9m114.o
+obj-$(CONFIG_SP1628) += sp1628.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
index b1331ab..7af04ba 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
@@ -121,4 +121,9 @@
 	struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
 	enum msm_camera_i2c_data_type data_type);
 
+int32_t msm_camera_qup_i2c_write_conf_tbl(
+	struct msm_camera_i2c_client *client,
+	struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+	enum msm_camera_i2c_data_type data_type);
+
 #endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
index 9222bb5..ac9cdbf 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
@@ -22,6 +22,10 @@
 #define S_I2C_DBG(fmt, args...) do { } while (0)
 #endif
 
+#define I2C_COMPARE_MATCH 0
+#define I2C_COMPARE_MISMATCH 1
+#define I2C_POLL_MAX_ITERATION 20
+
 static int32_t msm_camera_qup_i2c_rxdata(
 	struct msm_camera_i2c_client *dev_client, unsigned char *rxdata,
 	int data_length)
@@ -330,3 +334,210 @@
 	}
 	return rc;
 }
+
+static int32_t msm_camera_qup_i2c_compare(struct msm_camera_i2c_client *client,
+	uint32_t addr, uint16_t data,
+	enum msm_camera_i2c_data_type data_type)
+{
+	int32_t rc;
+	uint16_t reg_data = 0;
+	int data_len = 0;
+	switch (data_type) {
+	case MSM_CAMERA_I2C_BYTE_DATA:
+	case MSM_CAMERA_I2C_WORD_DATA:
+		data_len = data_type;
+		break;
+	case MSM_CAMERA_I2C_SET_BYTE_MASK:
+	case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+		data_len = MSM_CAMERA_I2C_BYTE_DATA;
+		break;
+	case MSM_CAMERA_I2C_SET_WORD_MASK:
+	case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+		data_len = MSM_CAMERA_I2C_WORD_DATA;
+		break;
+	default:
+		pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+		break;
+	}
+
+	rc = msm_camera_qup_i2c_read(client, addr, &reg_data, data_len);
+	if (rc < 0)
+		return rc;
+
+	rc = I2C_COMPARE_MISMATCH;
+	switch (data_type) {
+	case MSM_CAMERA_I2C_BYTE_DATA:
+	case MSM_CAMERA_I2C_WORD_DATA:
+		if (data == reg_data)
+			rc = I2C_COMPARE_MATCH;
+		break;
+	case MSM_CAMERA_I2C_SET_BYTE_MASK:
+	case MSM_CAMERA_I2C_SET_WORD_MASK:
+		if ((reg_data & data) == data)
+			rc = I2C_COMPARE_MATCH;
+		break;
+	case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+	case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+		if (!(reg_data & data))
+			rc = I2C_COMPARE_MATCH;
+		break;
+	default:
+		pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+		break;
+	}
+
+	S_I2C_DBG("%s: Register and data match result %d\n", __func__,
+		rc);
+	return rc;
+}
+
+int32_t msm_camera_qup_i2c_poll(struct msm_camera_i2c_client *client,
+	uint32_t addr, uint16_t data,
+	enum msm_camera_i2c_data_type data_type)
+{
+	int32_t rc;
+	int i;
+	S_I2C_DBG("%s: addr: 0x%x data: 0x%x dt: %d\n",
+		__func__, addr, data, data_type);
+
+	for (i = 0; i < I2C_POLL_MAX_ITERATION; i++) {
+		rc = msm_camera_qup_i2c_compare(client,
+			addr, data, data_type);
+		if (rc == 0 || rc < 0)
+			break;
+		usleep_range(10000, 11000);
+	}
+	return rc;
+}
+
+static int32_t msm_camera_qup_i2c_set_mask(struct msm_camera_i2c_client *client,
+	uint32_t addr, uint16_t mask,
+	enum msm_camera_i2c_data_type data_type, uint16_t set_mask)
+{
+	int32_t rc;
+	uint16_t reg_data;
+
+	rc = msm_camera_qup_i2c_read(client, addr, &reg_data, data_type);
+	if (rc < 0) {
+		S_I2C_DBG("%s read fail\n", __func__);
+		return rc;
+	}
+	S_I2C_DBG("%s addr: 0x%x data: 0x%x setmask: 0x%x\n",
+			__func__, addr, reg_data, mask);
+
+	if (set_mask)
+		reg_data |= mask;
+	else
+		reg_data &= ~mask;
+	S_I2C_DBG("%s write: 0x%x\n", __func__, reg_data);
+
+	rc = msm_camera_qup_i2c_write(client, addr, reg_data, data_type);
+	if (rc < 0)
+		S_I2C_DBG("%s write fail\n", __func__);
+
+	return rc;
+}
+
+static int32_t msm_camera_qup_i2c_set_write_mask_data(
+	struct msm_camera_i2c_client *client,
+	uint32_t addr, uint16_t data, int16_t mask,
+	enum msm_camera_i2c_data_type data_type)
+{
+	int32_t rc;
+	uint16_t reg_data;
+	CDBG("%s\n", __func__);
+	if (mask == -1)
+		return 0;
+	if (mask == 0) {
+		rc = msm_camera_qup_i2c_write(client, addr, data, data_type);
+	} else {
+		rc = msm_camera_qup_i2c_read(client, addr, &reg_data,
+			data_type);
+		if (rc < 0) {
+			CDBG("%s read fail\n", __func__);
+			return rc;
+		}
+		reg_data &= ~mask;
+		reg_data |= (data & mask);
+		rc = msm_camera_qup_i2c_write(client, addr, reg_data,
+			data_type);
+		if (rc < 0)
+			CDBG("%s write fail\n", __func__);
+	}
+	return rc;
+}
+
+
+int32_t msm_camera_qup_i2c_write_conf_tbl(
+	struct msm_camera_i2c_client *client,
+	struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+	enum msm_camera_i2c_data_type data_type)
+{
+	int i;
+	int32_t rc = -EFAULT;
+	pr_err("%s, E. ", __func__);
+	for (i = 0; i < size; i++) {
+		enum msm_camera_i2c_data_type dt;
+		if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) {
+			rc = msm_camera_qup_i2c_poll(client,
+				reg_conf_tbl->reg_addr,
+				reg_conf_tbl->reg_data,
+				reg_conf_tbl->dt);
+		} else {
+			if (reg_conf_tbl->dt == 0)
+				dt = data_type;
+			else
+				dt = reg_conf_tbl->dt;
+			switch (dt) {
+			case MSM_CAMERA_I2C_BYTE_DATA:
+			case MSM_CAMERA_I2C_WORD_DATA:
+				rc = msm_camera_qup_i2c_write(
+					client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data, dt);
+				break;
+			case MSM_CAMERA_I2C_SET_BYTE_MASK:
+				rc = msm_camera_qup_i2c_set_mask(client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data,
+					MSM_CAMERA_I2C_BYTE_DATA, 1);
+				break;
+			case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+				rc = msm_camera_qup_i2c_set_mask(client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data,
+					MSM_CAMERA_I2C_BYTE_DATA, 0);
+				break;
+			case MSM_CAMERA_I2C_SET_WORD_MASK:
+				rc = msm_camera_qup_i2c_set_mask(client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data,
+					MSM_CAMERA_I2C_WORD_DATA, 1);
+				break;
+			case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+				rc = msm_camera_qup_i2c_set_mask(client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data,
+					MSM_CAMERA_I2C_WORD_DATA, 0);
+				break;
+			case MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA:
+				rc = msm_camera_qup_i2c_set_write_mask_data(
+					client,
+					reg_conf_tbl->reg_addr,
+					reg_conf_tbl->reg_data,
+					reg_conf_tbl->mask,
+					MSM_CAMERA_I2C_BYTE_DATA);
+				break;
+			default:
+				pr_err("%s: Unsupport data type: %d\n",
+					__func__, dt);
+				break;
+			}
+		}
+		if (rc < 0)
+			break;
+		reg_conf_tbl++;
+	}
+	return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
index c834925..34f4428 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -1660,6 +1660,7 @@
 	.i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table,
 	.i2c_write_table_w_microdelay =
 		msm_camera_qup_i2c_write_table_w_microdelay,
+	.i2c_write_conf_tbl = msm_camera_qup_i2c_write_conf_tbl,
 };
 
 int32_t msm_sensor_platform_probe(struct platform_device *pdev, void *data)
diff --git a/drivers/media/platform/msm/camera_v2/sensor/sp1628.c b/drivers/media/platform/msm/camera_v2/sensor/sp1628.c
new file mode 100644
index 0000000..82e4b7c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/sp1628.c
@@ -0,0 +1,946 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#include "msm_sensor.h"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+#define SP1628_SENSOR_NAME "sp1628"
+DEFINE_MSM_MUTEX(sp1628_mut);
+
+static struct msm_sensor_ctrl_t sp1628_s_ctrl;
+
+static struct msm_sensor_power_setting sp1628_power_setting[] = {
+	{
+		.seq_type = SENSOR_VREG,
+		.seq_val = CAM_VDIG,
+		.config_val = 0,
+		.delay = 0,
+	},
+	{
+		.seq_type = SENSOR_VREG,
+		.seq_val = CAM_VIO,
+		.config_val = 0,
+		.delay = 0,
+	},
+	{
+		.seq_type = SENSOR_VREG,
+		.seq_val = CAM_VANA,
+		.config_val = 0,
+		.delay = 5,
+	},
+	{
+		.seq_type = SENSOR_GPIO,
+		.seq_val = SENSOR_GPIO_STANDBY,
+		.config_val = GPIO_OUT_HIGH,
+		.delay = 50,
+	},
+
+	{
+		.seq_type = SENSOR_GPIO,
+		.seq_val = SENSOR_GPIO_RESET,
+		.config_val = GPIO_OUT_HIGH,
+		.delay = 50,
+	},
+	{
+		.seq_type = SENSOR_GPIO,
+		.seq_val = SENSOR_GPIO_RESET,
+		.config_val = GPIO_OUT_LOW,
+		.delay = 50,
+	},
+	{
+		.seq_type = SENSOR_GPIO,
+		.seq_val = SENSOR_GPIO_RESET,
+		.config_val = GPIO_OUT_HIGH,
+		.delay = 50,
+	},
+	{
+		.seq_type = SENSOR_GPIO,
+		.seq_val = SENSOR_GPIO_STANDBY,
+		.config_val = GPIO_OUT_LOW,
+		.delay = 50,
+	},
+	{
+		.seq_type = SENSOR_CLK,
+		.seq_val = SENSOR_CAM_MCLK,
+		.config_val = 0,
+		.delay = 1,
+	},
+
+	{
+		.seq_type = SENSOR_I2C_MUX,
+		.seq_val = 0,
+		.config_val = 0,
+		.delay = 5,
+	},
+};
+
+static struct msm_camera_i2c_reg_conf sp1628_start_settings[] = {
+	{0x92, 0x81},
+};
+
+static struct msm_camera_i2c_reg_conf sp1628_stop_settings[] = {
+	{0x92, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf sp1628_recommend_settings[] = {
+	{0xfd, 0x00,},
+	{0x91, 0x00,},
+	{0x92, 0x81,},
+	{0x98, 0x2a,},
+	{0x96, 0xd0,}, /* c0*/
+	{0x97, 0x02,}, /* 03*/
+	{0x2f, 0x20,},	/* 24M*3=72M*/
+	{0x0b, 0x48,},	/* analog*/
+	{0x30, 0x80,},	/* 00*/
+	{0x0c, 0x66,},	/* analog*/
+	{0x0d, 0x12,},
+	{0x13, 0x0f,},	/* 10*/
+	{0x14, 0x00,},
+	{0x12, 0x00,},
+	{0x6b, 0x10,},	/* 11*/
+	{0x6c, 0x00,},
+	{0x6d, 0x00,},
+	{0x6e, 0x00,},
+	{0x6f, 0x10,},	/* 11*/
+	{0x73, 0x11,},	/* 12*/
+	{0x7a, 0x10,},	/* 11*/
+	{0x15, 0x17,},	/* 18*/
+	{0x71, 0x18,},	/* 19*/
+	{0x76, 0x18,},	/* 19*/
+	{0x29, 0x08,},
+	{0x18, 0x01,},
+	{0x19, 0x10,},
+	{0x1a, 0xc3,},	/* c1*/
+	{0x1b, 0x6f,},
+	{0x1d, 0x11,},	/* 01*/
+	{0x1e, 0x00,},	/* 1e*/
+	{0x1f, 0x80,},
+	{0x20, 0x7f,},
+	{0x22, 0x3c,},	/* 1b*/
+	{0x25, 0xff,},
+	{0x2b, 0x88,},
+	{0x2c, 0x85,},
+	{0x2d, 0x00,},
+	{0x2e, 0x80,},
+	{0x27, 0x38,},
+	{0x28, 0x03,},
+	{0x70, 0x1a,},
+	{0x72, 0x18,},	/* 1a*/
+	{0x74, 0x18,},
+	{0x75, 0x18,},
+	{0x77, 0x16,},	/* 18*/
+	{0x7f, 0x19,},
+	{0x31, 0x71,},	/*70 mirror/flip 720P*/
+	{0xfd, 0x01,},
+	{0x5d, 0x11,},	/* position*/
+	{0x5f, 0x00,},
+	{0x36, 0x08,},
+	{0x2f, 0xff,},
+	{0xfb, 0x25,},	/* blacklevl*/
+	{0x48, 0x00,},	/* dp*/
+	{0x49, 0x99,},
+	{0xf2, 0x0A,},
+	{0xfd, 0x02,},	/* AE*/
+	{0x52, 0x34,},
+	{0x53, 0x02,},
+	{0x54, 0x0c,},
+	{0x55, 0x08,},
+	{0x86, 0x0c,},
+	{0x87, 0x10,},
+	{0x8b, 0x10,},
+
+	/* 12-30 50Hz*/
+	{0xfd, 0x00,},	/* ae setting*/
+	{0x03, 0x05,},
+	{0x04, 0x64,},
+	{0x05, 0x00,},
+	{0x06, 0x00,},
+	{0x09, 0x00,},
+	{0x0a, 0x02,},
+	{0xfd, 0x01,},
+	{0xf0, 0x00,},
+	{0xf7, 0xe6,},
+	{0xf8, 0xc1,},
+	{0x02, 0x08,},
+	{0x03, 0x01,},
+	{0x06, 0xe6,},
+	{0x07, 0x00,},
+	{0x08, 0x01,},
+	{0x09, 0x00,},
+	{0xfd, 0x02,},
+	{0x40, 0x0a,},
+	{0x41, 0xc1,},
+	{0x42, 0x00,},
+	{0x88, 0x37,},
+	{0x89, 0xa7,},
+	{0x8a, 0x22,},
+	{0xfd, 0x02,},	/* Status*/
+	{0xbe, 0x30,},
+	{0xbf, 0x07,},
+	{0xd0, 0x30,},
+	{0xd1, 0x07,},
+	{0xfd, 0x01,},
+	{0x5b, 0x07,},
+	{0x5c, 0x30,},
+	{0xfd, 0x00,},
+
+	/* 12-30	60Hz*/
+	{0xfd, 0x00,},	/* ae setting*/
+	{0x03, 0x04,},
+	{0x04, 0x80,},
+	{0x05, 0x00,},
+	{0x06, 0x00,},
+	{0x09, 0x00,},
+	{0x0a, 0x01,},
+	{0xfd, 0x01,},
+	{0xf0, 0x00,},
+	{0xf7, 0xc0,},
+	{0xf8, 0xc1,},
+	{0x02, 0x0a,},
+	{0x03, 0x01,},
+	{0x06, 0xc0,},
+	{0x07, 0x00,},
+	{0x08, 0x01,},
+	{0x09, 0x00,},
+	{0xfd, 0x02,},
+	{0x40, 0x0a,},
+	{0x41, 0xc1,},
+	{0x42, 0x00,},
+	{0x88, 0x37,},
+	{0x89, 0xa7,},
+	{0x8a, 0x22,},
+	{0xfd, 0x02,},	/* Status*/
+	{0xbe, 0x80,},
+	{0xbf, 0x07,},
+	{0xd0, 0x80,},
+	{0xd1, 0x07,},
+	{0xfd, 0x01,},
+	{0x5b, 0x07,},
+	{0x5c, 0x80,},
+	{0xfd, 0x00,},
+
+	{0xfd, 0x01,},	/* fix status*/
+	{0x5a, 0x38,},	/* DP_gain*/
+	{0xfd, 0x02,},
+	{0xba, 0x30,},	/* mean_dummy_low*/
+	{0xbb, 0x50,},	/* mean_low_dummy*/
+	{0xbc, 0xc0,},	/* rpc_heq_low*/
+	{0xbd, 0xa0,},	/* rpc_heq_dummy*/
+	{0xb8, 0x80,},	/* mean_nr_dummy*/
+	{0xb9, 0x90,},	/* mean_dummy_nr*/
+	{0xfd, 0x01,},	/* rpc*/
+	{0xe0, 0x54,},
+	{0xe1, 0x40,},
+	{0xe2, 0x38,},
+	{0xe3, 0x34,},
+	{0xe4, 0x34,},
+	{0xe5, 0x30,},
+	{0xe6, 0x30,},
+	{0xe7, 0x2e,},
+	{0xe8, 0x2e,},
+	{0xe9, 0x2e,},
+	{0xea, 0x2c,},
+	{0xf3, 0x2c,},
+	{0xf4, 0x2c,},
+	{0xfd, 0x01,},	/* min gain*/
+	{0x04, 0xc0,},	/* rpc_max_indr*/
+	{0x05, 0x2c,},	/* rpc_min_indr*/
+	{0x0a, 0xc0,},	/* rpc_max_outdr*/
+	{0x0b, 0x2c,},	/* rpc_min_outdr*/
+	{0xfd, 0x01,},	/* ae target*/
+	{0xeb, 0x78,},
+	{0xec, 0x78,},
+	{0xed, 0x05,},
+	{0xee, 0x0a,},
+	{0xfd, 0x01,},	/* lsc*/
+	{0x26, 0x30,},
+	{0x27, 0xdc,},
+	{0x28, 0x05,},
+	{0x29, 0x08,},
+	{0x2a, 0x00,},
+	{0x2b, 0x03,},
+	{0x2c, 0x00,},
+	{0x2d, 0x2f,},
+	{0xfd, 0x01,},	/* RGainf*/
+	{0xa1, 0x37,},	/* left*/
+	{0xa2, 0x26,},	/* right*/
+	{0xa3, 0x32,},	/* up*/
+	{0xa4, 0x2b,},	/* down*/
+	{0xad, 0x0f,},	/* lu*/
+	{0xae, 0x0a,},	/* ru*/
+	{0xaf, 0x0a,},	/* ld*/
+	{0xb0, 0x0a,},	/* rd*/
+	{0x18, 0x2f,},	/* left*/
+	{0x19, 0x30,},	/* right*/
+	{0x1a, 0x32,},	/* up*/
+	{0x1b, 0x30,},	/* down*/
+	{0xbf, 0xa5,},	/* lu*/
+	{0xc0, 0x12,},	/* ru*/
+	{0xc1, 0x08,},	/* ld*/
+	{0xfa, 0x00,},	/* rd*/
+	{0xa5, 0x35,},	/* GGain*/
+	{0xa6, 0x24,},
+	{0xa7, 0x2e,},
+	{0xa8, 0x25,},
+	{0xb1, 0x00,},
+	{0xb2, 0x04,},
+	{0xb3, 0x00,},
+	{0xb4, 0x00,},
+	{0x1c, 0x24,},
+	{0x1d, 0x23,},
+	{0x1e, 0x2c,},
+	{0xb9, 0x25,},
+	{0x21, 0xa0,},
+	{0x22, 0x13,},
+	{0x23, 0x1c,},
+	{0x24, 0x0d,},
+	{0xa9, 0x2f,},	/* BGain*/
+	{0xaa, 0x24,},
+	{0xab, 0x2d,},
+	{0xac, 0x24,},
+	{0xb5, 0x00,},
+	{0xb6, 0x00,},
+	{0xb7, 0x00,},
+	{0xb8, 0x00,},
+	{0xba, 0x22,},
+	{0xbc, 0x24,},
+	{0xbd, 0x31,},
+	{0xbe, 0x24,},
+	{0x25, 0xa0,},
+	{0x45, 0x08,},
+	{0x46, 0x12,},
+	{0x47, 0x09,},
+	{0xfd, 0x01,},	/* awb*/
+	{0x32, 0x15,},
+	{0xfd, 0x02,},
+	{0x26, 0xc9,},
+	{0x27, 0x8b,},
+	{0x1b, 0x80,},
+	{0x1a, 0x80,},
+	{0x18, 0x27,},
+	{0x19, 0x26,},
+	{0x2a, 0x01,},
+	{0x2b, 0x10,},
+	{0x28, 0xf8,},
+	{0x29, 0x08,},
+
+	/* d65*/
+	{0x66, 0x35,},
+	{0x67, 0x60,},
+	{0x68, 0xb0,},
+	{0x69, 0xe0,},
+	{0x6a, 0xa5,},
+
+	/* indoor*/
+	{0x7c, 0x38,},
+	{0x7d, 0x58,},
+	{0x7e, 0xdb,},
+	{0x7f, 0x13,},
+	{0x80, 0xa6,},
+
+	/* cwftl84*/
+	{0x70, 0x18,},	/* 2f*/
+	{0x71, 0x4a,},
+	{0x72, 0x08,},
+	{0x73, 0x32,},	/* 24*/
+	{0x74, 0xaa,},
+
+	/* tl84--F*/
+	{0x6b, 0x02,},	/* 18*/
+	{0x6c, 0x2a,},	/* 34*/
+	{0x6d, 0x1e,},	/* 17*/
+	{0x6e, 0x49,},	/* 32*/
+	{0x6f, 0xaa,},
+
+	/* f--H*/
+	{0x61, 0xea,},	/* 02*/
+	{0x62, 0xf8,},	/* 2a*/
+	{0x63, 0x4f,},	/* 1e*/
+	{0x64, 0x5f,},	/* 49*/
+	{0x65, 0x5a,},	/* aa*/
+
+	{0x75, 0x80,},
+	{0x76, 0x09,},
+	{0x77, 0x02,},
+	{0x24, 0x25,},
+	{0x0e, 0x16,},
+	{0x3b, 0x09,},
+	{0xfd, 0x02,},	/*	sharp*/
+	{0xde, 0x0f,},
+	{0xd2, 0x0c,},	/* control black-white edge; 0 - bolder, f - thinner*/
+	{0xd3, 0x0a,},
+	{0xd4, 0x08,},
+	{0xd5, 0x08,},
+	{0xd7, 0x10,},	/* outline judgement*/
+	{0xd8, 0x1d,},
+	{0xd9, 0x32,},
+	{0xda, 0x48,},
+	{0xdb, 0x08,},
+	{0xe8, 0x38,},	/* outline strength*/
+	{0xe9, 0x38,},
+	{0xea, 0x38,},	/* 30*/
+	{0xeb, 0x38,},	/* 2*/
+	{0xec, 0x60,},
+	{0xed, 0x40,},
+	{0xee, 0x38,},	/* 30*/
+	{0xef, 0x38,},	/* 20*/
+	{0xf3, 0x00,},	/* sharpness level of flat area*/
+	{0xf4, 0x00,},
+	{0xf5, 0x00,},
+	{0xf6, 0x00,},
+	{0xfd, 0x02,},	/* skin sharpen*/
+	{0xdc, 0x04,},	/* skin de-sharpen*/
+	{0x05, 0x6f,},
+	{0x09, 0x10,},
+	{0xfd, 0x01,},	/* dns*/
+	{0x64, 0x22,},	/* 0 - max, 8 - min*/
+	{0x65, 0x22,},
+	{0x86, 0x20,},	/* threshold, 0 - min*/
+	{0x87, 0x20,},
+	{0x88, 0x20,},
+	{0x89, 0x20,},
+	{0x6d, 0x0f,},
+	{0x6e, 0x0f,},
+	{0x6f, 0x10,},
+	{0x70, 0x10,},
+	{0x71, 0x0d,},
+	{0x72, 0x23,},
+	{0x73, 0x23,},	/* 28*/
+	{0x74, 0x23,},	/* 2a*/
+	{0x75, 0x46,},	/* [7:4] strength of flat area,
+					[3:0]strength of un-flat area;
+					0-max, 8-min*/
+	{0x76, 0x36,},
+	{0x77, 0x36,},	/* 25*/
+	{0x78, 0x36,},	/* 12*/
+	{0x81, 0x1d,},	/* 2x*/
+	{0x82, 0x2b,},	/* 4x*/
+	{0x83, 0x2b,},	/* 50; 8x*/
+	{0x84, 0x2b,},	/* 80; 16x*/
+	{0x85, 0x0a,},	/* 12/8reg0x81*/
+	{0xfd, 0x01,},	/* gamma*/
+	{0x8b, 0x00,},	/* 00; 00; 00;*/
+	{0x8c, 0x0d,},	/* 02; 0b; 0b;*/
+	{0x8d, 0x1f,},	/* 0a; 19; 17;*/
+	{0x8e, 0x2d,},	/* 13; 2a; 27;*/
+	{0x8f, 0x3a,},	/* 1d; 37; 35;*/
+	{0x90, 0x4b,},	/* 30; 4b; 51;*/
+	{0x91, 0x59,},	/* 40; 5e; 64;*/
+	{0x92, 0x64,},	/* 4e; 6c; 74;*/
+	{0x93, 0x70,},	/* 5a; 78; 80;*/
+	{0x94, 0x83,},	/* 71; 92; 92;*/
+	{0x95, 0x92,},	/* 85; a6; a2;*/
+	{0x96, 0xa1,},	/* 96; b5; af;*/
+	{0x97, 0xae,},	/* a6; bf; bb;*/
+	{0x98, 0xba,},	/* b3; ca; c6;*/
+	{0x99, 0xc4,},	/* c0; d2; d0;*/
+	{0x9a, 0xcf,},	/* cb; d9; d9;*/
+	{0x9b, 0xdb,},	/* d5; e1; e0;*/
+	{0x9c, 0xe5,},	/* df; e8; e8;*/
+	{0x9d, 0xec,},	/* e9; ee; ee;*/
+	{0x9e, 0xf3,},	/* f2; f4; f4;*/
+	{0x9f, 0xfa,},	/* fa; fa; fa;*/
+	{0xa0, 0xff,},	/* ff; ff; ff;*/
+	{0xfd, 0x02,},	/* CCM*/
+	{0x15, 0xc8,},	/* b>th ab*/
+	{0x16, 0x95,},	/* r<th 87*/
+
+	{0xa0, 0x8c,},	/* !F*/
+	{0xa1, 0xfa,},
+	{0xa2, 0xfa,},
+	{0xa3, 0xf4,},
+	{0xa4, 0x99,},
+	{0xa5, 0xf4,},
+	{0xa6, 0x00,},
+	{0xa7, 0xb4,},
+	{0xa8, 0xcc,},
+	{0xa9, 0x3c,},
+	{0xaa, 0x33,},
+	{0xab, 0x0c,},
+
+	{0xac, 0x80,}, /* F*/
+	{0xad, 0x00,},
+	{0xae, 0x00,},
+	{0xaf, 0xe7,},
+	{0xb0, 0xc0,},
+	{0xb1, 0xda,},
+	{0xb2, 0xe7,},
+	{0xb3, 0xb4,},
+	{0xb4, 0xe6,},
+	{0xb5, 0x00,},
+	{0xb6, 0x33,},
+	{0xb7, 0x0f,},
+	{0xfd, 0x01,},	/* sat u*/
+	{0xd3, 0x8a,},	/* 90 105%*/
+	{0xd4, 0x8a,},	/* 90*/
+	{0xd5, 0x88,},
+	{0xd6, 0x80,},
+	{0xd7, 0x8a,},	/* 90; sat v*/
+	{0xd8, 0x8a,},	/* 90*/
+	{0xd9, 0x88,},
+	{0xda, 0x80,},
+	{0xfd, 0x01,},	/* auto_sat*/
+	{0xd2, 0x00,},	/* autosa_en*/
+	{0xfd, 0x01,},	/* uv_th*/
+	{0xc2, 0xee,},
+	{0xc3, 0xee,},
+	{0xc4, 0xdd,},
+	{0xc5, 0xbb,},
+	{0xfd, 0x01,},	/* low_lum_offset*/
+	{0xcd, 0x10,},
+	{0xce, 0x1f,},
+	{0xfd, 0x02,},	/* gw*/
+	{0x35, 0x6f,},
+	{0x37, 0x13,},
+	{0xfd, 0x01,},	/* heq*/
+	{0xdb, 0x00,},
+	{0x10, 0x00,},
+	{0x14, 0x25,},
+	{0x11, 0x10,},
+	{0x15, 0x25,},
+	{0x16, 0x15,},
+	{0xfd, 0x02,},	/* cnr*/
+	{0x8e, 0x10,},
+	{0x90, 0x20,},
+	{0x91, 0x20,},
+	{0x92, 0x60,},
+	{0x93, 0x80,},
+	{0xfd, 0x02,},	/* auto*/
+	{0x85, 0x00,},
+	{0xfd, 0x01,},
+	{0x00, 0x00,},	/* fix mode*/
+	{0x32, 0x15,},	/* ae en*/
+	{0x33, 0xef,},	/* lsc\bpc en*/
+	{0x34, 0xc7,},	/* ynr\cnr\gamma\color en*/
+	{0x35, 0x40,},	/* YUYV*/
+	{0xfd, 0x00,},
+
+};
+
+static struct v4l2_subdev_info sp1628_subdev_info[] = {
+	{
+		.code   = V4L2_MBUS_FMT_YUYV8_2X8,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.fmt    = 1,
+		.order  = 0,
+	},
+};
+
+static const struct i2c_device_id sp1628_i2c_id[] = {
+	{SP1628_SENSOR_NAME, (kernel_ulong_t)&sp1628_s_ctrl},
+	{ }
+};
+
+static int32_t msm_sp1628_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	CDBG("%s, E.", __func__);
+
+	return msm_sensor_i2c_probe(client, id, &sp1628_s_ctrl);
+}
+
+static struct i2c_driver sp1628_i2c_driver = {
+	.id_table = sp1628_i2c_id,
+	.probe  = msm_sp1628_i2c_probe,
+	.driver = {
+		.name = SP1628_SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client sp1628_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+static const struct of_device_id sp1628_dt_match[] = {
+	{.compatible = "qcom,sp1628", .data = &sp1628_s_ctrl},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, sp1628_dt_match);
+
+static struct platform_driver sp1628_platform_driver = {
+	.driver = {
+		.name = "qcom,sp1628",
+		.owner = THIS_MODULE,
+		.of_match_table = sp1628_dt_match,
+	},
+};
+
+static int32_t sp1628_platform_probe(struct platform_device *pdev)
+{
+	int32_t rc;
+	const struct of_device_id *match;
+	CDBG("%s, E.", __func__);
+	match = of_match_device(sp1628_dt_match, &pdev->dev);
+	rc = msm_sensor_platform_probe(pdev, match->data);
+	return rc;
+}
+
+static int __init sp1628_init_module(void)
+{
+	int32_t rc;
+	pr_info("%s:%d\n", __func__, __LINE__);
+	rc = platform_driver_probe(&sp1628_platform_driver,
+		sp1628_platform_probe);
+	if (!rc)
+		return rc;
+	pr_err("%s:%d rc %d\n", __func__, __LINE__, rc);
+	return i2c_add_driver(&sp1628_i2c_driver);
+}
+
+static void __exit sp1628_exit_module(void)
+{
+	pr_info("%s:%d\n", __func__, __LINE__);
+	if (sp1628_s_ctrl.pdev) {
+		msm_sensor_free_sensor_data(&sp1628_s_ctrl);
+		platform_driver_unregister(&sp1628_platform_driver);
+	} else
+		i2c_del_driver(&sp1628_i2c_driver);
+	return;
+}
+
+int32_t sp1628_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+	void __user *argp)
+{
+	struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+	long rc = 0;
+	int32_t i = 0;
+	mutex_lock(s_ctrl->msm_sensor_mutex);
+	CDBG("%s:%d %s cfgtype = %d\n", __func__, __LINE__,
+		s_ctrl->sensordata->sensor_name, cdata->cfgtype);
+	switch (cdata->cfgtype) {
+	case CFG_GET_SENSOR_INFO:
+		memcpy(cdata->cfg.sensor_info.sensor_name,
+			s_ctrl->sensordata->sensor_name,
+			sizeof(cdata->cfg.sensor_info.sensor_name));
+		cdata->cfg.sensor_info.session_id =
+			s_ctrl->sensordata->sensor_info->session_id;
+		for (i = 0; i < SUB_MODULE_MAX; i++)
+			cdata->cfg.sensor_info.subdev_id[i] =
+				s_ctrl->sensordata->sensor_info->subdev_id[i];
+		CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
+			cdata->cfg.sensor_info.sensor_name);
+		CDBG("%s:%d session id %d\n", __func__, __LINE__,
+			cdata->cfg.sensor_info.session_id);
+		for (i = 0; i < SUB_MODULE_MAX; i++)
+			CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i,
+				cdata->cfg.sensor_info.subdev_id[i]);
+
+		break;
+	case CFG_SET_INIT_SETTING:
+		/* Write Recommend settings */
+		pr_err("%s, sensor write init setting!!", __func__);
+		rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+			i2c_write_conf_tbl(s_ctrl->sensor_i2c_client,
+			sp1628_recommend_settings,
+			ARRAY_SIZE(sp1628_recommend_settings),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		break;
+	case CFG_SET_RESOLUTION:
+		break;
+	case CFG_SET_STOP_STREAM:
+		pr_err("%s, sensor stop stream!!", __func__);
+		rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+			i2c_write_conf_tbl(s_ctrl->sensor_i2c_client,
+			sp1628_stop_settings,
+			ARRAY_SIZE(sp1628_stop_settings),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		break;
+	case CFG_SET_START_STREAM:
+		pr_err("%s, sensor start stream!!", __func__);
+		rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+			i2c_write_conf_tbl(s_ctrl->sensor_i2c_client,
+			sp1628_start_settings,
+			ARRAY_SIZE(sp1628_start_settings),
+			MSM_CAMERA_I2C_BYTE_DATA);
+		break;
+	case CFG_GET_SENSOR_INIT_PARAMS:
+		cdata->cfg.sensor_init_params =
+			*s_ctrl->sensordata->sensor_init_params;
+		CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__,
+			__LINE__,
+			cdata->cfg.sensor_init_params.modes_supported,
+			cdata->cfg.sensor_init_params.position,
+			cdata->cfg.sensor_init_params.sensor_mount_angle);
+		break;
+	case CFG_SET_SLAVE_INFO: {
+		struct msm_camera_sensor_slave_info sensor_slave_info;
+		struct msm_sensor_power_setting_array *power_setting_array;
+		int slave_index = 0;
+		if (copy_from_user(&sensor_slave_info,
+		    (void *)cdata->cfg.setting,
+		    sizeof(struct msm_camera_sensor_slave_info))) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			rc = -EFAULT;
+			break;
+		}
+		/* Update sensor slave address */
+		if (sensor_slave_info.slave_addr) {
+			s_ctrl->sensor_i2c_client->cci_client->sid =
+				sensor_slave_info.slave_addr >> 1;
+		}
+
+		/* Update sensor address type */
+		s_ctrl->sensor_i2c_client->addr_type =
+			sensor_slave_info.addr_type;
+
+		/* Update power up / down sequence */
+		s_ctrl->power_setting_array =
+			sensor_slave_info.power_setting_array;
+		power_setting_array = &s_ctrl->power_setting_array;
+		power_setting_array->power_setting = kzalloc(
+			power_setting_array->size *
+			sizeof(struct msm_sensor_power_setting), GFP_KERNEL);
+		if (!power_setting_array->power_setting) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			rc = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(power_setting_array->power_setting,
+		    (void *)sensor_slave_info.power_setting_array.power_setting,
+		    power_setting_array->size *
+		    sizeof(struct msm_sensor_power_setting))) {
+			kfree(power_setting_array->power_setting);
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			rc = -EFAULT;
+			break;
+		}
+		s_ctrl->free_power_setting = true;
+		CDBG("%s sensor id %x\n", __func__,
+			sensor_slave_info.slave_addr);
+		CDBG("%s sensor addr type %d\n", __func__,
+			sensor_slave_info.addr_type);
+		CDBG("%s sensor reg %x\n", __func__,
+			sensor_slave_info.sensor_id_info.sensor_id_reg_addr);
+		CDBG("%s sensor id %x\n", __func__,
+			sensor_slave_info.sensor_id_info.sensor_id);
+		for (slave_index = 0; slave_index <
+			power_setting_array->size; slave_index++) {
+			CDBG("%s i %d power setting %d %d %ld %d\n", __func__,
+				slave_index,
+				power_setting_array->power_setting[slave_index].
+				seq_type,
+				power_setting_array->power_setting[slave_index].
+				seq_val,
+				power_setting_array->power_setting[slave_index].
+				config_val,
+				power_setting_array->power_setting[slave_index].
+				delay);
+		}
+		kfree(power_setting_array->power_setting);
+		break;
+	}
+	case CFG_WRITE_I2C_ARRAY: {
+		struct msm_camera_i2c_reg_setting conf_array;
+		struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+		if (copy_from_user(&conf_array,
+			(void *)cdata->cfg.setting,
+			sizeof(struct msm_camera_i2c_reg_setting))) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			rc = -EFAULT;
+			break;
+		}
+
+		reg_setting = kzalloc(conf_array.size *
+			(sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+		if (!reg_setting) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			rc = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+			conf_array.size *
+			sizeof(struct msm_camera_i2c_reg_array))) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			kfree(reg_setting);
+			rc = -EFAULT;
+			break;
+		}
+
+		conf_array.reg_setting = reg_setting;
+		rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+			s_ctrl->sensor_i2c_client, &conf_array);
+		kfree(reg_setting);
+		break;
+	}
+	case CFG_WRITE_I2C_SEQ_ARRAY: {
+		struct msm_camera_i2c_seq_reg_setting conf_array;
+		struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+		if (copy_from_user(&conf_array,
+			(void *)cdata->cfg.setting,
+			sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			rc = -EFAULT;
+			break;
+		}
+
+		reg_setting = kzalloc(conf_array.size *
+			(sizeof(struct msm_camera_i2c_seq_reg_array)),
+			GFP_KERNEL);
+		if (!reg_setting) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			rc = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+			conf_array.size *
+			sizeof(struct msm_camera_i2c_seq_reg_array))) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			kfree(reg_setting);
+			rc = -EFAULT;
+			break;
+		}
+
+		conf_array.reg_setting = reg_setting;
+		rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+			i2c_write_seq_table(s_ctrl->sensor_i2c_client,
+			&conf_array);
+		kfree(reg_setting);
+		break;
+	}
+
+	case CFG_POWER_UP:
+		if (s_ctrl->func_tbl->sensor_power_up)
+			rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+		else
+			rc = -EFAULT;
+		break;
+
+	case CFG_POWER_DOWN:
+		if (s_ctrl->func_tbl->sensor_power_down)
+			rc = s_ctrl->func_tbl->sensor_power_down(
+				s_ctrl);
+		else
+			rc = -EFAULT;
+		break;
+
+	case CFG_SET_STOP_STREAM_SETTING: {
+		struct msm_camera_i2c_reg_setting *stop_setting =
+			&s_ctrl->stop_setting;
+		struct msm_camera_i2c_reg_array *reg_setting = NULL;
+		if (copy_from_user(stop_setting, (void *)cdata->cfg.setting,
+		    sizeof(struct msm_camera_i2c_reg_setting))) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			rc = -EFAULT;
+			break;
+		}
+
+		reg_setting = stop_setting->reg_setting;
+		stop_setting->reg_setting = kzalloc(stop_setting->size *
+			(sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+		if (!stop_setting->reg_setting) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			rc = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(stop_setting->reg_setting,
+		    (void *)reg_setting, stop_setting->size *
+		    sizeof(struct msm_camera_i2c_reg_array))) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			kfree(stop_setting->reg_setting);
+			stop_setting->reg_setting = NULL;
+			stop_setting->size = 0;
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	default:
+		rc = -EFAULT;
+		break;
+	}
+
+	mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+	return rc;
+}
+
+int32_t sp1628_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+	int32_t rc = 0;
+	uint16_t chipid = 0;
+
+	CDBG("%s, E. calling i2c_read:, i2c_addr:%d, id_reg_addr:%d",
+		__func__,
+		s_ctrl->sensordata->slave_info->sensor_slave_addr,
+		s_ctrl->sensordata->slave_info->sensor_id_reg_addr);
+
+	rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+			s_ctrl->sensor_i2c_client,
+			0x02,
+			&chipid, MSM_CAMERA_I2C_BYTE_DATA);
+	if (rc < 0) {
+		pr_err("%s: %s: read id failed\n", __func__,
+			s_ctrl->sensordata->sensor_name);
+		return rc;
+	}
+
+	CDBG("%s: read id: %x expected id 0x16:\n", __func__, chipid);
+	if (chipid != 0x16) {
+		pr_err("msm_sensor_match_id chip id doesnot match\n");
+		return -ENODEV;
+	}
+
+	chipid = 0;
+	rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+			s_ctrl->sensor_i2c_client,
+			0xa0,
+			&chipid, MSM_CAMERA_I2C_BYTE_DATA);
+	if (rc < 0) {
+		pr_err("%s: %s: read id failed\n", __func__,
+			s_ctrl->sensordata->sensor_name);
+		return rc;
+	}
+
+	CDBG("%s: read id: %x expected id 0x28:\n", __func__, chipid);
+	if (chipid != 0x28) {
+		pr_err("msm_sensor_match_id chip id doesnot match\n");
+		return -ENODEV;
+	}
+
+	return rc;
+}
+
+
+static struct msm_sensor_fn_t sp1628_sensor_func_tbl = {
+	.sensor_config = sp1628_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+	.sensor_match_id = sp1628_match_id,
+};
+
+static struct msm_sensor_ctrl_t sp1628_s_ctrl = {
+	.sensor_i2c_client = &sp1628_sensor_i2c_client,
+	.power_setting_array.power_setting = sp1628_power_setting,
+	.power_setting_array.size = ARRAY_SIZE(sp1628_power_setting),
+	.msm_sensor_mutex = &sp1628_mut,
+	.sensor_v4l2_subdev_info = sp1628_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(sp1628_subdev_info),
+	.func_tbl = &sp1628_sensor_func_tbl,
+};
+
+module_init(sp1628_init_module);
+module_exit(sp1628_exit_module);
+MODULE_DESCRIPTION("Aptina 1.26MP YUV sensor driver");
+MODULE_LICENSE("GPL v2");