camera: rockchip: porting ov8858&ov4689&ov2710 driver
authordalong.zhang <dalon.zhang@rock-chips.com>
Sun, 9 Oct 2016 08:38:35 +0000 (16:38 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Wed, 30 Nov 2016 03:55:35 +0000 (11:55 +0800)
Change-Id: Ia9a0a39d347a9fd506f493c6639785267233e0ac
Signed-off-by: dalong.zhang <dalon.zhang@rock-chips.com>
16 files changed:
drivers/media/i2c/soc_camera/Kconfig
drivers/media/i2c/soc_camera/Makefile
drivers/media/i2c/soc_camera/rockchip/Kconfig [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/Makefile [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.h [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/imx323_v4l2-i2c-subdev.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/imx_camera_module.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/imx_camera_module.h [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/ov2710_v4l2-i2c-subdev.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/ov4689_v4l2-i2c-subdev.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/ov8858_v4l2-i2c-subdev.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/ov_camera_module.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/ov_camera_module.h [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/rk_camera_module.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/rk_camera_module_version.h [new file with mode: 0644]

index 23d352f0adf0af58290e342aa4ed90f12b5df2e6..de785db82a74344e10b3872fe38a24eb137d3a6f 100644 (file)
@@ -85,3 +85,5 @@ config SOC_CAMERA_TW9910
        depends on SOC_CAMERA && I2C
        help
          This is a tw9910 video driver
+
+source "drivers/media/i2c/soc_camera/rockchip/Kconfig"
\ No newline at end of file
index d0421feaa796115be0987dcad61f32b19312532f..b1394116d2aa09f72767aab21589f4b8e08b2920 100644 (file)
@@ -12,3 +12,4 @@ obj-$(CONFIG_SOC_CAMERA_OV9640)               += ov9640.o
 obj-$(CONFIG_SOC_CAMERA_OV9740)                += ov9740.o
 obj-$(CONFIG_SOC_CAMERA_RJ54N1)                += rj54n1cb0c.o
 obj-$(CONFIG_SOC_CAMERA_TW9910)                += tw9910.o
+obj-y += rockchip/
diff --git a/drivers/media/i2c/soc_camera/rockchip/Kconfig b/drivers/media/i2c/soc_camera/rockchip/Kconfig
new file mode 100644 (file)
index 0000000..1683289
--- /dev/null
@@ -0,0 +1,27 @@
+config VIDEO_ov8858
+       tristate "ov8858 driver adapt to rockchip cif isp platform"
+       depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C
+       default n
+       ---help---
+         This is ov8858 camera driver adapt to rockchip cif isp platform.
+
+config VIDEO_ov2710
+       tristate "ov2710 driver adapt to rockchip cif isp platform"
+       depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C
+       default n
+       ---help---
+         This is ov2710 camera driver adapt to rockchip cif isp platform.
+
+config VIDEO_ov4689
+       tristate "ov4689 driver adapt to rockchip cif isp platform"
+       depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C
+       default n
+       ---help---
+         This is ov8858 camera driver adapt to rockchip cif isp platform.
+
+config VIDEO_imx323
+       tristate "imx323 driver adapt to rockchip cif isp platform"
+       depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C
+       default n
+       ---help---
+         This is imx323 camera driver adapt to rockchip cif isp platform.
diff --git a/drivers/media/i2c/soc_camera/rockchip/Makefile b/drivers/media/i2c/soc_camera/rockchip/Makefile
new file mode 100644 (file)
index 0000000..3c89fb6
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_VIDEO_ov8858) += ov_camera_module.o rk_camera_module.o ov8858_v4l2-i2c-subdev.o
+obj-$(CONFIG_VIDEO_ov2710) += ov_camera_module.o rk_camera_module.o ov2710_v4l2-i2c-subdev.o
+obj-$(CONFIG_VIDEO_ov4689) += ov_camera_module.o rk_camera_module.o ov4689_v4l2-i2c-subdev.o
+obj-$(CONFIG_VIDEO_imx323) += imx_camera_module.o rk_camera_module.o imx323_v4l2-i2c-subdev.o
diff --git a/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.c
new file mode 100644 (file)
index 0000000..9782de3
--- /dev/null
@@ -0,0 +1,1428 @@
+/*
+ * aptina_camera_module.c
+ *
+ * Generic galaxycore sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-core.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+#include <linux/i2c.h>
+#include <media/v4l2-controls_rockchip.h>
+#include "aptina_camera_module.h"
+
+#define I2C_M_WR 0
+#define I2C_MSG_MAX 300
+#define I2C_DATA_MAX (I2C_MSG_MAX * 3)
+
+/* ======================================================================== */
+
+struct aptina_camera_module *to_aptina_camera_module(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct aptina_camera_module, sd);
+}
+
+/* ======================================================================== */
+
+void aptina_camera_module_reset(
+       struct aptina_camera_module *cam_mod)
+{
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       cam_mod->inited = false;
+       cam_mod->active_config = NULL;
+       cam_mod->update_config = true;
+       cam_mod->frm_fmt_valid = false;
+       cam_mod->frm_intrvl_valid = false;
+       cam_mod->exp_config.auto_exp = false;
+       cam_mod->exp_config.auto_gain = false;
+       cam_mod->wb_config.auto_wb = false;
+       cam_mod->hflip = false;
+       cam_mod->vflip = false;
+       cam_mod->auto_adjust_fps = true;
+       cam_mod->rotation = 0;
+       cam_mod->ctrl_updt = 0;
+       cam_mod->state = APTINA_CAMERA_MODULE_POWER_OFF;
+       cam_mod->state_before_suspend = APTINA_CAMERA_MODULE_POWER_OFF;
+}
+
+/* ======================================================================== */
+
+int aptina_read_i2c_reg(
+       struct v4l2_subdev *sd,
+       u16 data_length,
+       u16 reg,
+       u32 *val)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = 0;
+       struct i2c_msg msg[1];
+       unsigned char data[2] = { 0, 0};
+
+       if (!client->adapter) {
+               pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n");
+               return -ENODEV;
+       }
+
+       msg->addr = client->addr;
+       msg->flags = I2C_M_WR;
+       msg->len = 2;
+       msg->buf = data;
+
+       /* High byte goes out first */
+       data[0] = (u8)((reg >> 8) & 0xff);
+       data[1] = (u8)(reg & 0xff);
+
+       ret = i2c_transfer(client->adapter, msg, 1);
+       if (ret >= 0) {
+               mdelay(3);
+               msg->flags = I2C_M_RD;
+               msg->len = data_length;
+               i2c_transfer(client->adapter, msg, 1);
+       }
+       if (ret >= 0) {
+               *val = 0;
+               /* High byte comes first */
+               if (data_length == 1)
+                       *val = data[0];
+               else if (data_length == 2)
+                       *val = data[1] + (data[0] << 8);
+               else
+               ;
+
+               return 0;
+       }
+       pltfrm_camera_module_pr_err(sd,
+               "i2c read from offset 0x%08x failed with error %d\n", reg, ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int aptina_write_i2c_reg(
+       struct v4l2_subdev *sd,
+       u16 reg, u16 val)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = 0;
+       struct i2c_msg msg[1];
+       unsigned char data[4] = {0, 0, 0, 0};
+       int retries;
+
+       if (!client->adapter) {
+               pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n");
+               return -ENODEV;
+       }
+
+       for (retries = 0; retries < 5; retries++) {
+               msg->addr = client->addr;
+               msg->flags = I2C_M_WR;
+               msg->len = 4;
+               msg->buf = data;
+
+               /* high byte goes out first */
+               data[0] = (u8)((reg >> 8) & 0xff);
+               data[1] = (u8)(reg & 0xff);
+               data[2] = (u8)((val >> 8) & 0xff);
+               data[3] = (u8)(val & 0xff);
+
+               ret = i2c_transfer(client->adapter, msg, 1);
+               usleep_range(20, 50);
+
+               if (ret == 1)
+                       return 0;
+
+               pltfrm_camera_module_pr_debug(sd,
+                       "retrying I2C... %d\n", retries);
+               retries++;
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(msecs_to_jiffies(20));
+       }
+
+       pltfrm_camera_module_pr_err(sd,
+               "i2c write to offset 0x%08x failed with error %d\n", reg, ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int aptina_write_reglist(
+       struct v4l2_subdev *sd,
+       const struct pltfrm_camera_module_reg reglist[],
+       int len)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = 0;
+       unsigned int k = 0, j = 0;
+       int i = 0;
+       struct i2c_msg *msg;
+       unsigned char *data;
+       unsigned int max_entries = len;
+
+       msg = kmalloc((sizeof(struct i2c_msg) * I2C_MSG_MAX), GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+       data = kmalloc((sizeof(unsigned char) * I2C_DATA_MAX), GFP_KERNEL);
+       if (!data) {
+               kfree(msg);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < max_entries; i++) {
+               switch (reglist[i].flag) {
+               case PLTFRM_CAMERA_MODULE_REG_TYPE_DATA:
+                       (msg + j)->addr = client->addr;
+                       (msg + j)->flags = I2C_M_WR;
+                       (msg + j)->len = 4;
+                       (msg + j)->buf = (data + k);
+
+                       data[k + 0] = (u8)((reglist[i].reg >> 8) & 0xFF);
+                       data[k + 1] = (u8)(reglist[i].reg & 0xFF);
+                       data[k + 2] = (u8)((reglist[i].val >> 8) & 0xFF);
+                       data[k + 3] = (u8)(reglist[i].val & 0xFF);
+
+                       k = k + 4;
+                       j++;
+                       if (j == (I2C_MSG_MAX - 1)) {
+                               /* Bulk I2C transfer */
+                               pltfrm_camera_module_pr_err(sd,
+                                       "messages transfers 1 0x%p msg %d bytes %d\n",
+                                       msg, j, k);
+                               ret = i2c_transfer(client->adapter, msg, j);
+                               if (ret < 0) {
+                                       pltfrm_camera_module_pr_err(sd,
+                                               "i2c transfer returned with err %d\n",
+                                               ret);
+                                       kfree(msg);
+                                       kfree(data);
+                                       return ret;
+                               }
+                               j = 0;
+                               k = 0;
+                               pltfrm_camera_module_pr_debug(sd,
+                                       "i2c_transfer return %d\n", ret);
+                       }
+                       break;
+               case PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT:
+                       if (j > 0) {
+                               /* Bulk I2C transfer */
+                               pltfrm_camera_module_pr_debug(sd,
+                                       "messages transfers 1 0x%p msg %d bytes %d\n",
+                                       msg, j, k);
+                               ret = i2c_transfer(client->adapter, msg, j);
+                               if (ret < 0) {
+                                       pltfrm_camera_module_pr_debug(sd,
+                                               "i2c transfer returned with err %d\n",
+                                               ret);
+                                       kfree(msg);
+                                       kfree(data);
+                                       return ret;
+                               }
+                               pltfrm_camera_module_pr_debug(sd,
+                                       "i2c_transfer return %d\n", ret);
+                       }
+                       mdelay(reglist[i].val);
+                       j = 0;
+                       k = 0;
+                       break;
+               default:
+                       pltfrm_camera_module_pr_debug(sd, "unknown command\n");
+                       kfree(msg);
+                       kfree(data);
+                       return -1;
+               }
+       }
+
+       if (j != 0) {           /*Remaining I2C message*/
+               pltfrm_camera_module_pr_debug(sd,
+                       "messages transfers 1 0x%p msg %d bytes %d\n",
+                       msg, j, k);
+               ret = i2c_transfer(client->adapter, msg, j);
+               if (ret < 0) {
+                       pltfrm_camera_module_pr_err(sd,
+                               "i2c transfer returned with err %d\n", ret);
+                       kfree(msg);
+                       kfree(data);
+                       return ret;
+               }
+               pltfrm_camera_module_pr_debug(sd,
+                       "i2c_transfer return %d\n", ret);
+       }
+
+       kfree(msg);
+       kfree(data);
+       return 0;
+}
+
+static void aptina_camera_module_set_active_config(
+       struct aptina_camera_module *cam_mod,
+       struct aptina_camera_module_config *new_config)
+{
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (IS_ERR_OR_NULL(new_config)) {
+               cam_mod->active_config = new_config;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "no active config\n");
+       } else {
+               cam_mod->ctrl_updt &= APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP |
+                       APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN |
+                       APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+               if (new_config->auto_exp_enabled !=
+                       cam_mod->exp_config.auto_exp) {
+                       cam_mod->ctrl_updt |=
+                               APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP;
+                       cam_mod->exp_config.auto_exp =
+                               new_config->auto_exp_enabled;
+               }
+               if (new_config->auto_gain_enabled !=
+                       cam_mod->exp_config.auto_gain) {
+                       cam_mod->ctrl_updt |=
+                               APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN;
+                       cam_mod->exp_config.auto_gain =
+                               new_config->auto_gain_enabled;
+               }
+               if (new_config->auto_wb_enabled !=
+                       cam_mod->wb_config.auto_wb) {
+                       cam_mod->ctrl_updt |=
+                               APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+                       cam_mod->wb_config.auto_wb =
+                               new_config->auto_wb_enabled;
+               }
+               if (new_config != cam_mod->active_config) {
+                       cam_mod->update_config = true;
+                       cam_mod->active_config = new_config;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "activating config '%s'\n",
+                               new_config->name);
+               }
+       }
+}
+
+/* ======================================================================== */
+
+static struct aptina_camera_module_config *aptina_camera_module_find_config(
+       struct aptina_camera_module *cam_mod,
+       struct v4l2_mbus_framefmt *fmt,
+       struct v4l2_subdev_frame_interval *frm_intrvl)
+{
+       u32 i;
+       unsigned long gcdiv;
+       struct v4l2_subdev_frame_interval norm_interval;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+       if (!IS_ERR_OR_NULL(fmt))
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "%dx%d, fmt code 0x%04x\n",
+                       fmt->width, fmt->height, fmt->code);
+
+       if (!IS_ERR_OR_NULL(frm_intrvl))
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "frame interval %d/%d\n",
+                       frm_intrvl->interval.numerator,
+                       frm_intrvl->interval.denominator);
+
+       for (i = 0; i < cam_mod->custom.num_configs; i++) {
+               if (!IS_ERR_OR_NULL(frm_intrvl)) {
+                       gcdiv = gcd(cam_mod->custom.configs[i].
+                               frm_intrvl.interval.numerator,
+                               cam_mod->custom.configs[i].
+                                       frm_intrvl.interval.denominator);
+                       norm_interval.interval.numerator =
+                               cam_mod->custom.configs[i].
+                                       frm_intrvl.interval.numerator / gcdiv;
+                       norm_interval.interval.denominator =
+                               cam_mod->custom.configs[i].
+                               frm_intrvl.interval.denominator / gcdiv;
+                       if ((frm_intrvl->interval.numerator !=
+                               norm_interval.interval.numerator) ||
+                               (frm_intrvl->interval.denominator !=
+                               norm_interval.interval.denominator))
+                               continue;
+               }
+               if (!IS_ERR_OR_NULL(fmt)) {
+                       if ((cam_mod->custom.configs[i].frm_fmt.width !=
+                               fmt->width) ||
+                               (cam_mod->custom.configs[i].frm_fmt.height !=
+                               fmt->height) ||
+                               (cam_mod->custom.configs[i].frm_fmt.code !=
+                               fmt->code)) {
+                               continue;
+                       }
+               }
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "found matching config %s\n",
+                       cam_mod->custom.configs[i].name);
+               return &cam_mod->custom.configs[i];
+       }
+       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+               "no matching config found\n");
+
+       return ERR_PTR(-EINVAL);
+}
+
+/* ======================================================================== */
+
+static int aptina_camera_module_write_config(
+       struct aptina_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "no active sensor configuration");
+               ret = -EFAULT;
+               goto err;
+       }
+
+       ret = aptina_write_reglist(&cam_mod->sd,
+               cam_mod->active_config->reg_table,
+               cam_mod->active_config->reg_table_num_entries);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+       ret = pltfrm_camera_module_patch_config(&cam_mod->sd,
+               &cam_mod->frm_fmt,
+               &cam_mod->frm_intrvl);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+static int aptina_camera_module_attach(
+       struct aptina_camera_module *cam_mod)
+{
+       int ret = 0;
+       struct aptina_camera_module_custom_config *custom;
+
+       custom = &cam_mod->custom;
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+       if (custom->check_camera_id) {
+               aptina_camera_module_s_power(&cam_mod->sd, 1);
+               ret = custom->check_camera_id(cam_mod);
+               aptina_camera_module_s_power(&cam_mod->sd, 0);
+               if (ret != 0)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "failed with error %d\n", ret);
+       aptina_camera_module_release(cam_mod);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_try_fmt(struct v4l2_subdev *sd,
+       struct v4l2_mbus_framefmt *fmt)
+{
+       struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n",
+               fmt->width, fmt->height, fmt->code);
+
+       if (IS_ERR_OR_NULL(
+       aptina_camera_module_find_config(
+       cam_mod, fmt, NULL))) {
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "format not supported\n");
+               return -EINVAL;
+       }
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "format supported\n");
+
+       return 0;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_s_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format)
+{
+       struct aptina_camera_module *cam_mod =  to_aptina_camera_module(sd);
+       struct v4l2_mbus_framefmt *fmt = &format->format;
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n",
+               fmt->width, fmt->height, fmt->code);
+
+       if (IS_ERR_OR_NULL(
+       aptina_camera_module_find_config(
+       cam_mod, fmt, NULL))) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "format %dx%d, code 0x%04x, not supported\n",
+                       fmt->width, fmt->height, fmt->code);
+               ret = -EINVAL;
+               goto err;
+       }
+       cam_mod->frm_fmt_valid = true;
+       cam_mod->frm_fmt = *fmt;
+       if (cam_mod->frm_intrvl_valid) {
+               aptina_camera_module_set_active_config(cam_mod,
+                       aptina_camera_module_find_config(cam_mod,
+                               fmt, &cam_mod->frm_intrvl));
+       }
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_g_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format)
+{
+       struct aptina_camera_module *cam_mod =  to_aptina_camera_module(sd);
+       struct v4l2_mbus_framefmt *fmt = &format->format;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (cam_mod->active_config) {
+               fmt->code = cam_mod->active_config->frm_fmt.code;
+               fmt->width = cam_mod->active_config->frm_fmt.width;
+               fmt->height = cam_mod->active_config->frm_fmt.height;
+               return 0;
+       }
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "no active config\n");
+
+       return -1;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_s_frame_interval(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_frame_interval *interval)
+{
+       struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd);
+       unsigned long gcdiv;
+       struct v4l2_subdev_frame_interval norm_interval;
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+       if ((interval->interval.denominator == 0) ||
+               (interval->interval.numerator == 0)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "invalid frame interval %d/%d\n",
+                       interval->interval.numerator,
+                       interval->interval.denominator);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d/%d (%dfps)\n",
+               interval->interval.numerator, interval->interval.denominator,
+               (interval->interval.denominator +
+               (interval->interval.numerator >> 1)) /
+               interval->interval.numerator);
+
+       /* normalize interval */
+       gcdiv = gcd(interval->interval.numerator,
+               interval->interval.denominator);
+       norm_interval.interval.numerator =
+               interval->interval.numerator / gcdiv;
+       norm_interval.interval.denominator =
+               interval->interval.denominator / gcdiv;
+
+       if (IS_ERR_OR_NULL(aptina_camera_module_find_config(cam_mod,
+                       NULL, &norm_interval))) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "frame interval %d/%d not supported\n",
+                       interval->interval.numerator,
+                       interval->interval.denominator);
+               ret = -EINVAL;
+               goto err;
+       }
+       cam_mod->frm_intrvl_valid = true;
+       cam_mod->frm_intrvl = norm_interval;
+       if (cam_mod->frm_fmt_valid) {
+               aptina_camera_module_set_active_config(cam_mod,
+                       aptina_camera_module_find_config(cam_mod,
+                               &cam_mod->frm_fmt, interval));
+       }
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       int ret = 0;
+       struct aptina_camera_module *cam_mod =  to_aptina_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", enable);
+
+       if (enable) {
+               if (cam_mod->state == APTINA_CAMERA_MODULE_STREAMING)
+                       return 0;
+               if (IS_ERR_OR_NULL(cam_mod->active_config)) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "no active sensor configuration, cannot start streaming\n");
+                       ret = -EFAULT;
+                       goto err;
+               }
+               if (cam_mod->state != APTINA_CAMERA_MODULE_SW_STANDBY) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "sensor is not powered on (in state %d), cannot start streaming\n",
+                               cam_mod->state);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               if (!IS_ERR_OR_NULL(cam_mod->custom.set_flip))
+                       cam_mod->custom.set_flip(cam_mod);
+
+               if (cam_mod->update_config)
+                       ret = aptina_camera_module_write_config(cam_mod);
+                       if (IS_ERR_VALUE(ret))
+                               goto err;
+
+               ret = cam_mod->custom.start_streaming(cam_mod);
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+               cam_mod->update_config = false;
+               cam_mod->ctrl_updt = 0;
+               mdelay(cam_mod->custom.power_up_delays_ms[2]);
+               cam_mod->state = APTINA_CAMERA_MODULE_STREAMING;
+       } else {
+               int pclk;
+               int wait_ms;
+               struct isp_supplemental_sensor_mode_data timings;
+
+               if (cam_mod->state != APTINA_CAMERA_MODULE_STREAMING)
+                       return 0;
+               ret = cam_mod->custom.stop_streaming(cam_mod);
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+
+               cam_mod->state = APTINA_CAMERA_MODULE_SW_STANDBY;
+
+               ret = aptina_camera_module_ioctl(sd, RK_VIDIOC_SENSOR_MODE_DATA,
+                                            &timings);
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+               pclk = timings.vt_pix_clk_freq_hz / 1000;
+               if (!pclk)
+                       goto err;
+               wait_ms = (timings.line_length_pck
+                       * timings.frame_length_lines) / pclk;
+               /* wait for a frame period to make sure that there is
+                *no pending frame left.
+                */
+               msleep(wait_ms + 1);
+       }
+
+       cam_mod->state_before_suspend = cam_mod->state;
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_s_power(struct v4l2_subdev *sd, int on)
+{
+       int ret = 0;
+       struct aptina_camera_module *cam_mod =  to_aptina_camera_module(sd);
+       struct v4l2_subdev *af_ctrl;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", on);
+
+       if (on) {
+               if (cam_mod->state == APTINA_CAMERA_MODULE_POWER_OFF) {
+                       ret = pltfrm_camera_module_s_power(&cam_mod->sd, 1);
+                       if (!IS_ERR_VALUE(ret)) {
+                               mdelay(cam_mod->custom.power_up_delays_ms[0]);
+                               cam_mod->state =
+                               APTINA_CAMERA_MODULE_HW_STANDBY;
+                       }
+               }
+               if (cam_mod->state == APTINA_CAMERA_MODULE_HW_STANDBY) {
+                       ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_PWR,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+                       msleep(20);
+
+                       ret = pltfrm_camera_module_set_pin_state(
+                           &cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_PD,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+                       msleep(20);
+
+                       ret = pltfrm_camera_module_set_pin_state(
+                           &cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_RESET,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+                       msleep(20);
+
+                       ret = pltfrm_camera_module_set_pin_state(
+                           &cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_RESET,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+                       msleep(20);
+
+                       ret = pltfrm_camera_module_set_pin_state(
+                           &cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_RESET,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+                       msleep(20);
+
+                       ret = pltfrm_camera_module_set_pin_state(
+                           &cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_PD,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+                       msleep(20);
+                       ret = pltfrm_camera_module_set_pin_state(
+                           &cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_PD,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+                       msleep(20);
+
+                       if (!IS_ERR_VALUE(ret)) {
+                               mdelay(cam_mod->custom.power_up_delays_ms[1]);
+                               cam_mod->state =
+                               APTINA_CAMERA_MODULE_SW_STANDBY;
+
+                               #if 1
+                               if (!IS_ERR_OR_NULL(
+                                   cam_mod->custom.init_common) &&
+                                   cam_mod->custom.init_common(
+                                   cam_mod))
+                               usleep_range(1000, 1500);
+                               #endif
+
+                               af_ctrl = pltfrm_camera_module_get_af_ctrl(sd);
+                               if (!IS_ERR_OR_NULL(af_ctrl)) {
+                                       v4l2_subdev_call(af_ctrl,
+                                                        core, init, 0);
+                               }
+                       }
+               }
+       } else {
+               if (cam_mod->state == APTINA_CAMERA_MODULE_STREAMING) {
+                       ret = aptina_camera_module_s_stream(sd, 0);
+                       if (!IS_ERR_VALUE(ret))
+                               cam_mod->state =
+                               APTINA_CAMERA_MODULE_SW_STANDBY;
+               }
+               if (cam_mod->state == APTINA_CAMERA_MODULE_SW_STANDBY) {
+                       ret = pltfrm_camera_module_set_pin_state(
+                           &cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_PD,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+
+                       ret = pltfrm_camera_module_set_pin_state(
+                           &cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_RESET,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+
+                       ret = pltfrm_camera_module_set_pin_state(
+                           &cam_mod->sd,
+                           PLTFRM_CAMERA_MODULE_PIN_PWR,
+                           PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+
+                       if (!IS_ERR_VALUE(ret))
+                               cam_mod->state =
+                               APTINA_CAMERA_MODULE_HW_STANDBY;
+               }
+               if (cam_mod->state == APTINA_CAMERA_MODULE_HW_STANDBY) {
+                       ret = pltfrm_camera_module_s_power(&cam_mod->sd, 0);
+                       if (!IS_ERR_VALUE(ret)) {
+                               cam_mod->state = APTINA_CAMERA_MODULE_POWER_OFF;
+                               aptina_camera_module_reset(cam_mod);
+                       }
+               }
+       }
+
+       cam_mod->state_before_suspend = cam_mod->state;
+
+       if (IS_ERR_VALUE(ret)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "%s failed, camera left in state %d\n",
+                       on ? "on" : "off", cam_mod->state);
+               goto err;
+       } else
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "camera powered %s\n", on ? "on" : "off");
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_g_ctrl(struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd);
+       int ret;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, " id 0x%x\n", ctrl->id);
+
+       if (ctrl->id == V4L2_CID_FLASH_LED_MODE) {
+               ctrl->value = cam_mod->exp_config.flash_mode;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_FLASH_LED_MODE %d\n",
+                       ctrl->value);
+               return 0;
+       }
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "no active configuration\n");
+               return -EFAULT;
+       }
+
+       if (ctrl->id == RK_V4L2_CID_VBLANKING) {
+               ctrl->value = cam_mod->active_config->v_blanking_time_us;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "RK_V4L2_CID_VBLANKING %d\n",
+                       ctrl->value);
+               return 0;
+       }
+
+/*
+ *if (ctrl->id == RK_V4L2_CID_IGNORE_MEASUREMENT_CHECK) {
+ *     ctrl->value = cam_mod->active_config->ignore_measurement_check;
+ *             pltfrm_camera_module_pr_debug(
+ *                     &cam_mod->sd,
+ *                     "CIF_ISP20_CID_IGNORE_MEASUREMENT_CHECK %d\n",
+ *                     ctrl->value);
+ *             return 0;
+ *     }
+ */
+
+       if ((cam_mod->state != APTINA_CAMERA_MODULE_SW_STANDBY) &&
+               (cam_mod->state != APTINA_CAMERA_MODULE_STREAMING)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "cannot get controls when camera is off\n");
+               return -EFAULT;
+       }
+
+       if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
+               struct v4l2_subdev *af_ctrl;
+
+               af_ctrl =
+                       pltfrm_camera_module_get_af_ctrl(sd);
+               if (!IS_ERR_OR_NULL(af_ctrl)) {
+                       ret = v4l2_subdev_call(af_ctrl, core, g_ctrl, ctrl);
+                       return ret;
+               }
+       }
+
+       if (!IS_ERR_OR_NULL(cam_mod->custom.g_ctrl)) {
+               ret = cam_mod->custom.g_ctrl(cam_mod, ctrl->id);
+               if (IS_ERR_VALUE(ret))
+                       return ret;
+       }
+
+       switch (ctrl->id) {
+       case V4L2_CID_GAIN:
+               ctrl->value = cam_mod->exp_config.gain;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                            "V4L2_CID_GAIN %d\n",
+                            ctrl->value);
+               break;
+       case V4L2_CID_EXPOSURE:
+               ctrl->value = cam_mod->exp_config.exp_time;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                            "V4L2_CID_EXPOSURE %d\n",
+                            ctrl->value);
+               break;
+       case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+               ctrl->value = cam_mod->wb_config.temperature;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               ctrl->value = cam_mod->exp_config.auto_gain;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTOGAIN %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_EXPOSURE_AUTO:
+               ctrl->value = cam_mod->exp_config.auto_exp;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_EXPOSURE_AUTO %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               ctrl->value = cam_mod->wb_config.auto_wb;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_WHITE_BALANCE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_FOCUS_ABSOLUTE:
+               ctrl->value = cam_mod->af_config.abs_pos;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_FOCUS_ABSOLUTE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_HFLIP:
+       case V4L2_CID_VFLIP:
+               /* TBD */
+               /* fallthrough */
+       default:
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "failed, unknown ctrl %d\n", ctrl->id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* ======================================================================== */
+
+static int flash_light_ctrl(
+               struct v4l2_subdev *sd,
+               struct aptina_camera_module *cam_mod,
+               int value)
+{
+       return 0;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls)
+{
+       int i;
+       int ctrl_cnt = 0;
+       struct aptina_camera_module *cam_mod =  to_aptina_camera_module(sd);
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (ctrls->count == 0)
+               return -EINVAL;
+
+       for (i = 0; i < ctrls->count; i++) {
+               struct v4l2_ext_control *ctrl;
+               u32 ctrl_updt = 0;
+
+               ctrl = &ctrls->controls[i];
+
+               switch (ctrl->id) {
+               case V4L2_CID_GAIN:
+                       ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_GAIN;
+                       cam_mod->exp_config.gain = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_GAIN %d\n",
+                       ctrl->value);
+                       break;
+               case RK_V4L2_CID_GAIN_PERCENT:
+                       ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_GAIN;
+                       cam_mod->exp_config.gain_percent = ctrl->value;
+                       break;
+               case V4L2_CID_FLASH_LED_MODE:
+                       ret = flash_light_ctrl(sd, cam_mod, ctrl->value);
+                       if (ret == 0) {
+                               cam_mod->exp_config.flash_mode = ctrl->value;
+                               pltfrm_camera_module_pr_debug(
+                                       &cam_mod->sd,
+                                       "V4L2_CID_FLASH_LED_MODE %d\n",
+                                       ctrl->value);
+                       }
+                       break;
+               case V4L2_CID_EXPOSURE:
+                       ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_EXP_TIME;
+                       cam_mod->exp_config.exp_time = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_EXPOSURE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+                       ctrl_updt =
+                       APTINA_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE;
+                       cam_mod->wb_config.temperature = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_AUTOGAIN:
+                       ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN;
+                       cam_mod->exp_config.auto_gain = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTOGAIN %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_EXPOSURE_AUTO:
+                       ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP;
+                       cam_mod->exp_config.auto_exp = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_EXPOSURE_AUTO %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_AUTO_WHITE_BALANCE:
+                       ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+                       cam_mod->wb_config.auto_wb = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_WHITE_BALANCE %d\n",
+                       ctrl->value);
+                       break;
+               case RK_V4L2_CID_AUTO_FPS:
+                       cam_mod->auto_adjust_fps = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "RK_V4L2_CID_AUTO_FPS %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_FOCUS_ABSOLUTE:
+                       {
+                               struct v4l2_subdev *af_ctrl;
+
+                               af_ctrl = pltfrm_camera_module_get_af_ctrl(sd);
+                               if (!IS_ERR_OR_NULL(af_ctrl)) {
+                                       struct v4l2_control single_ctrl;
+
+                                       single_ctrl.id =
+                                               V4L2_CID_FOCUS_ABSOLUTE;
+                                       single_ctrl.value = ctrl->value;
+                                       ret = v4l2_subdev_call(af_ctrl,
+                                               core, s_ctrl, &single_ctrl);
+                                       return ret;
+                               }
+                       }
+                       ctrl_updt =
+                               APTINA_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE;
+                       cam_mod->af_config.abs_pos = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_FOCUS_ABSOLUTE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_HFLIP:
+                       if (ctrl->value)
+                               cam_mod->hflip = true;
+                       else
+                               cam_mod->hflip = false;
+                       break;
+               case V4L2_CID_VFLIP:
+                       if (ctrl->value)
+                               cam_mod->vflip = true;
+                       else
+                               cam_mod->vflip = false;
+                       break;
+               default:
+                       pltfrm_camera_module_pr_warn(&cam_mod->sd,
+                       "ignoring unknown ctrl 0x%x\n", ctrl->id);
+                       break;
+               }
+
+               if (cam_mod->state != APTINA_CAMERA_MODULE_SW_STANDBY &&
+               cam_mod->state != APTINA_CAMERA_MODULE_STREAMING)
+                       cam_mod->ctrl_updt |= ctrl_updt;
+               else if (ctrl_updt)
+                       ctrl_cnt++;
+       }
+
+       /* if camera module is already streaming, write through */
+       if (ctrl_cnt &&
+               (cam_mod->state == APTINA_CAMERA_MODULE_STREAMING ||
+               cam_mod->state == APTINA_CAMERA_MODULE_SW_STANDBY)) {
+               struct aptina_camera_module_ext_ctrls aptina_ctrls;
+
+               aptina_ctrls.ctrls =
+               (struct aptina_camera_module_ext_ctrl *)
+               kmalloc(ctrl_cnt * sizeof(struct aptina_camera_module_ext_ctrl),
+                       GFP_KERNEL);
+
+               if (aptina_ctrls.ctrls) {
+                       for (i = 0; i < ctrl_cnt; i++) {
+                               aptina_ctrls.ctrls[i].id =
+                                       ctrls->controls[i].id;
+                               aptina_ctrls.ctrls[i].value =
+                                       ctrls->controls[i].value;
+                       }
+
+                       aptina_ctrls.count = ctrl_cnt;
+
+                       ret = cam_mod->custom.s_ext_ctrls(cam_mod,
+                                               &aptina_ctrls);
+
+                       kfree(aptina_ctrls.ctrls);
+               } else {
+                       ret = -ENOMEM;
+               }
+
+               if (IS_ERR_VALUE(ret))
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "failed with error %d\n", ret);
+       }
+
+       return ret;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       struct aptina_camera_module *cam_mod =  to_aptina_camera_module(sd);
+       struct v4l2_ext_control ext_ctrl[1];
+       struct v4l2_ext_controls ext_ctrls;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+               "0x%x 0x%x\n", ctrl->id, ctrl->value);
+
+       ext_ctrl[0].id = ctrl->id;
+       ext_ctrl[0].value = ctrl->value;
+
+       ext_ctrls.count = 1;
+       ext_ctrls.controls = ext_ctrl;
+
+       return aptina_camera_module_s_ext_ctrls(sd, &ext_ctrls);
+}
+
+/* ======================================================================== */
+
+long aptina_camera_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg)
+{
+       struct aptina_camera_module *cam_mod =  to_aptina_camera_module(sd);
+       int ret;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (cmd == RK_VIDIOC_SENSOR_MODE_DATA) {
+               struct aptina_camera_module_timings aptina_timings;
+               struct isp_supplemental_sensor_mode_data *timings =
+               (struct isp_supplemental_sensor_mode_data *)arg;
+
+               if (cam_mod->custom.g_timings)
+                       ret = cam_mod->custom.g_timings(cam_mod,
+                                               &aptina_timings);
+               else
+                       ret = -EPERM;
+
+               if (IS_ERR_VALUE(ret)) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "failed with error %d\n", ret);
+                       return ret;
+               }
+
+               timings->sensor_output_width =
+                       aptina_timings.sensor_output_width;
+               timings->sensor_output_height =
+                       aptina_timings.sensor_output_height;
+               timings->crop_horizontal_start =
+                       aptina_timings.crop_horizontal_start;
+               timings->crop_vertical_start =
+                       aptina_timings.crop_vertical_start;
+               timings->crop_horizontal_end =
+                       aptina_timings.crop_horizontal_end;
+               timings->crop_vertical_end =
+                       aptina_timings.crop_vertical_end;
+               timings->line_length_pck =
+                       aptina_timings.line_length_pck;
+               timings->frame_length_lines =
+                       aptina_timings.frame_length_lines;
+               timings->vt_pix_clk_freq_hz =
+                       aptina_timings.vt_pix_clk_freq_hz;
+               timings->binning_factor_x =
+                       aptina_timings.binning_factor_x;
+               timings->binning_factor_y =
+                       aptina_timings.binning_factor_y;
+               timings->coarse_integration_time_max_margin =
+                       aptina_timings.coarse_integration_time_max_margin;
+               timings->coarse_integration_time_min =
+                       aptina_timings.coarse_integration_time_min;
+               timings->fine_integration_time_max_margin =
+                       aptina_timings.fine_integration_time_max_margin;
+               timings->fine_integration_time_min =
+                       aptina_timings.fine_integration_time_min;
+
+               if (cam_mod->custom.g_exposure_valid_frame)
+                       timings->exposure_valid_frame =
+                               cam_mod->custom.g_exposure_valid_frame(cam_mod);
+
+               /*
+                *timings->exp_time = cam_mod->exp_config.exp_time;
+                *timings->gain = cam_mod->exp_config.gain;
+                */
+
+               return ret;
+       } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) {
+               struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg;
+               struct aptina_camera_module_config *config;
+
+               if (cam_mod->custom.num_configs <= 0) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "cam_mod->custom.num_configs is NULL, Get interface config failed!\n");
+                       return -EINVAL;
+               }
+
+               if (IS_ERR_OR_NULL(cam_mod->active_config))
+                       config = &cam_mod->custom.configs[0];
+               else
+                       config = cam_mod->active_config;
+
+               *itf_cfg = config->itf_cfg;
+               pltfrm_camera_module_ioctl(sd, PLTFRM_CIFCAM_G_ITF_CFG, arg);
+               return 0;
+       } else if (cmd == PLTFRM_CIFCAM_ATTACH) {
+               aptina_camera_module_init(cam_mod, &cam_mod->custom);
+               pltfrm_camera_module_ioctl(sd, cmd, arg);
+               return aptina_camera_module_attach(cam_mod);
+       }
+
+       ret = pltfrm_camera_module_ioctl(sd, cmd, arg);
+       return ret;
+}
+
+/* Reconfigure bayer order according to xml's definition */
+static int aptina_camera_module_s_flip(struct aptina_camera_module *cam_mod)
+{
+       int i, mode = 0;
+       static int flip_mode;
+
+       if (!cam_mod->custom.configs->bayer_order_alter_enble) {
+               aptina_camera_module_pr_info(cam_mod,
+                                        "not need to change bayer order!");
+               return 0;
+       }
+
+       mode = pltfrm_camera_module_get_flip_mirror(&cam_mod->sd);
+
+       aptina_camera_module_pr_debug(cam_mod,
+                       "%s(%d): flip_mode is %d, mode is %d.\n",
+                       __func__,
+                       __LINE__,
+                       flip_mode,
+                       mode);
+
+       if (mode != flip_mode) {
+               /* Reconfigure the compatible bayer order */
+               switch (mode) {
+               case 0:
+                       /* Change all the bayter order of registered configs */
+                       for (i = 0; i < cam_mod->custom.num_configs; i++)
+                               cam_mod->custom.configs[i].frm_fmt.code =
+                                       MEDIA_BUS_FMT_SGRBG10_1X10;
+                       break;
+
+               case 1:
+                       for (i = 0; i < cam_mod->custom.num_configs; i++)
+                               cam_mod->custom.configs[i].frm_fmt.code =
+                                       MEDIA_BUS_FMT_SRGGB10_1X10;
+                       break;
+
+               case 2:
+                       for (i = 0; i < cam_mod->custom.num_configs; i++)
+                               cam_mod->custom.configs[i].frm_fmt.code =
+                                       MEDIA_BUS_FMT_SBGGR10_1X10;
+                       break;
+
+               case 3:
+                       for (i = 0; i < cam_mod->custom.num_configs; i++)
+                               cam_mod->custom.configs[i].frm_fmt.code =
+                                       MEDIA_BUS_FMT_SGBRG10_1X10;
+                       break;
+
+               default:
+                       break;
+               };
+       }
+
+       flip_mode = mode;
+
+       return 0;
+}
+
+int aptina_camera_module_get_flip_mirror(
+       struct aptina_camera_module *cam_mod)
+{
+       return pltfrm_camera_module_get_flip_mirror(&cam_mod->sd);
+}
+
+int aptina_camera_module_enum_frameintervals(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_frame_interval_enum *file)
+{
+       struct aptina_camera_module *cam_mod =  to_aptina_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", file->index);
+
+       if (file->index >= cam_mod->custom.num_configs)
+               return -EINVAL;
+
+       aptina_camera_module_s_flip(cam_mod);
+       file->code =
+               cam_mod->custom.configs[file->index].frm_fmt.code;
+       file->width = cam_mod->custom.configs[file->index].frm_fmt.width;
+       file->height = cam_mod->custom.configs[file->index].frm_fmt.height;
+       file->interval.numerator = cam_mod->custom.
+               configs[file->index].frm_intrvl.interval.numerator;
+       file->interval.denominator = cam_mod->custom.
+               configs[file->index].frm_intrvl.interval.denominator;
+       return 0;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_write_reglist(
+       struct aptina_camera_module *cam_mod,
+       const struct aptina_camera_module_reg reglist[],
+       int len)
+{
+       return aptina_write_reglist(&cam_mod->sd, reglist, len);
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_write_reg(
+       struct aptina_camera_module *cam_mod,
+       u16 reg,
+       u16 val)
+{
+       return aptina_write_i2c_reg(&cam_mod->sd, reg, val);
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_read_reg(
+       struct aptina_camera_module *cam_mod,
+       u16 data_length,
+       u16 reg,
+       u32 *val)
+{
+       return aptina_read_i2c_reg(&cam_mod->sd,
+               data_length, reg, val);
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_read_reg_table(
+       struct aptina_camera_module *cam_mod,
+       u16 reg,
+       u32 *val)
+{
+       int i;
+
+       if (cam_mod->state == APTINA_CAMERA_MODULE_STREAMING)
+               return aptina_read_i2c_reg(&cam_mod->sd,
+                       1, reg, val);
+
+       if (!IS_ERR_OR_NULL(cam_mod->active_config)) {
+               for (
+                       i = cam_mod->active_config->reg_table_num_entries - 1;
+                       i > 0;
+                       i--) {
+                       if (cam_mod->active_config->reg_table[i].reg == reg) {
+                               *val = cam_mod->active_config->reg_table[i].val;
+                               return 0;
+                       }
+               }
+       }
+
+       if (cam_mod->state == APTINA_CAMERA_MODULE_SW_STANDBY)
+               return aptina_read_i2c_reg(&cam_mod->sd,
+                       1, reg, val);
+
+       return -EFAULT;
+}
+
+/* ======================================================================== */
+
+int aptina_camera_module_init(struct aptina_camera_module *cam_mod,
+       struct aptina_camera_module_custom_config *custom)
+{
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       aptina_camera_module_reset(cam_mod);
+
+       if (IS_ERR_OR_NULL(custom->start_streaming) ||
+               IS_ERR_OR_NULL(custom->stop_streaming) ||
+               IS_ERR_OR_NULL(custom->s_ctrl) ||
+               IS_ERR_OR_NULL(custom->g_ctrl)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "mandatory callback function is missing\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = pltfrm_camera_module_init(&cam_mod->sd, &cam_mod->pltfm_data);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_PD,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+       ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_RESET,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+       if (IS_ERR_VALUE(ret)) {
+               aptina_camera_module_release(cam_mod);
+               goto err;
+       }
+/*
+ *if (custom->check_camera_id) {
+ *     aptina_camera_module_s_power(&cam_mod->sd, 1);
+ *     ret = (custom->check_camera_id)(cam_mod);
+ *     aptina_camera_module_s_power(&cam_mod->sd, 0);
+ *}
+ *
+ *if (IS_ERR_VALUE(ret)) {
+ *     aptina_camera_module_release(cam_mod);
+ *     goto err;
+ *}
+ */
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+void aptina_camera_module_release(struct aptina_camera_module *cam_mod)
+{
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       cam_mod->custom.configs = NULL;
+
+       pltfrm_camera_module_release(&cam_mod->sd);
+       v4l2_device_unregister_subdev(&cam_mod->sd);
+}
diff --git a/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.h b/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.h
new file mode 100644 (file)
index 0000000..5bd1b92
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * aptina_camera_module.h
+ *
+ * Generic galaxycore sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#ifndef APTINA_CAMERA_MODULE_H
+#define APTINA_CAMERA_MODULE_H
+#include <linux/workqueue.h>
+#include <linux/platform_data/rk_isp10_platform_camera_module.h>
+#include <linux/platform_data/rk_isp10_platform.h>
+/*
+ * TODO: references to v4l2 should be reomved from here and go into a
+ * platform dependent wrapper
+ */
+
+#define APTINA_CAMERA_MODULE_REG_TYPE_DATA \
+                               PLTFRM_CAMERA_MODULE_REG_TYPE_DATA
+#define APTINA_CAMERA_MODULE_REG_TYPE_TIMEOUT \
+                               PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT
+#define aptina_camera_module_csi_config
+#define aptina_camera_module_reg pltfrm_camera_module_reg
+#define APTINA_FLIP_BIT_MASK 0x2
+#define APTINA_MIRROR_BIT_MASK 0x1
+
+#define APTINA_CAMERA_MODULE_CTRL_UPDT_GAIN 0x01
+#define APTINA_CAMERA_MODULE_CTRL_UPDT_EXP_TIME 0x02
+#define APTINA_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE 0x04
+#define APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_WB 0x08
+#define APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN 0x10
+#define APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP 0x20
+#define APTINA_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE 0x40
+#define APTINA_CAMERA_MODULE_CTRL_UPDT_PRESET_WB 0x80
+
+enum aptina_camera_module_state {
+       APTINA_CAMERA_MODULE_POWER_OFF = 0,
+       APTINA_CAMERA_MODULE_HW_STANDBY = 1,
+       APTINA_CAMERA_MODULE_SW_STANDBY = 2,
+       APTINA_CAMERA_MODULE_STREAMING = 3
+};
+
+struct aptina_camera_module;
+
+struct aptina_camera_module_timings {
+       /* public */
+       u32 coarse_integration_time_min;
+       u32 coarse_integration_time_max_margin;
+       u32 fine_integration_time_min;
+       u32 fine_integration_time_max_margin;
+       u32 frame_length_lines;
+       u32 line_length_pck;
+       u32 vt_pix_clk_freq_hz;
+       u32 sensor_output_width;
+       u32 sensor_output_height;
+       u32 crop_horizontal_start; /* Sensor crop start cord. (x0,y0) */
+       u32 crop_vertical_start;
+       u32 crop_horizontal_end; /* Sensor crop end cord. (x1,y1) */
+       u32 crop_vertical_end;
+       u8 binning_factor_x;
+       u8 binning_factor_y;
+};
+
+struct aptina_camera_module_config {
+       const char *name;
+       struct v4l2_mbus_framefmt frm_fmt;
+       struct v4l2_subdev_frame_interval frm_intrvl;
+       bool auto_exp_enabled;
+       bool auto_gain_enabled;
+       bool auto_wb_enabled;
+       struct aptina_camera_module_reg *reg_table;
+       u32 reg_table_num_entries;
+       u32 bayer_order_alter_enble;
+       u32 v_blanking_time_us;
+       u32 line_length_pck;
+       u32 frame_length_lines;
+       struct aptina_camera_module_timings timings;
+       bool soft_reset;
+       bool ignore_measurement_check;
+       struct pltfrm_cam_itf itf_cfg;
+};
+
+struct aptina_camera_module_exp_config {
+       u32 exp_time;
+       bool auto_exp;
+       u16 gain;
+       u16 gain_percent;
+       bool auto_gain;
+       enum v4l2_flash_led_mode flash_mode;
+};
+
+struct aptina_camera_module_wb_config {
+       u32 temperature;
+       u32 preset_id;
+       bool auto_wb;
+};
+
+struct aptina_camera_module_af_config {
+       u32 abs_pos;
+       u32 rel_pos;
+};
+
+struct aptina_camera_module_ext_ctrl {
+       /* public */
+       u32 id;
+       u32 value;
+       __u32 reserved2[1];
+};
+
+struct aptina_camera_module_ext_ctrls {
+       /* public */
+       u32 count;
+       struct aptina_camera_module_ext_ctrl *ctrls;
+};
+
+/*
+ * start_streaming: (mandatory) will be called when sensor should be
+ * put into streaming mode right after the base config has been
+ * written to the sensor. After a successful call of this function
+ * the sensor should start delivering frame data.
+ *
+ * stop_streaming: (mandatory) will be called when sensor should stop
+ * delivering data. After a successful call of this function the
+ * sensor should not deliver any more frame data.
+ *
+ * check_camera_id: (optional) will be called when the sensor is
+ * powered on. If provided should check the sensor ID/version
+ * required by the custom driver. Register access should be
+ * possible when this function is invoked.
+ *
+ * s_ctrl: (mandatory) will be called at the successful end of
+ * aptina_camera_module_s_ctrl with the ctrl_id as argument.
+ *
+ * priv: (optional) for private data used by the custom driver.
+ */
+struct aptina_camera_module_custom_config {
+       int (*start_streaming)(struct aptina_camera_module *cam_mod);
+       int (*stop_streaming)(struct aptina_camera_module *cam_mod);
+       int (*check_camera_id)(struct aptina_camera_module *cam_mod);
+       int (*s_ctrl)(struct aptina_camera_module *cam_mod, u32 ctrl_id);
+       int (*g_ctrl)(struct aptina_camera_module *cam_mod, u32 ctrl_id);
+       int (*g_timings)(struct aptina_camera_module *cam_mod,
+               struct aptina_camera_module_timings *timings);
+       int (*s_ext_ctrls)(struct aptina_camera_module *cam_mod,
+               struct aptina_camera_module_ext_ctrls *ctrls);
+       int (*set_flip)(struct aptina_camera_module *cam_mod);
+       struct aptina_camera_module_config *configs;
+       int (*init_common)(struct aptina_camera_module *cam_mod);
+       int (*g_exposure_valid_frame)(struct aptina_camera_module *cam_mod);
+       u32 num_configs;
+       u32 power_up_delays_ms[3];
+       void *priv;
+       struct aptina_camera_module_timings timings;
+};
+
+struct aptina_camera_module {
+       /* public */
+       struct v4l2_subdev sd;
+       struct v4l2_mbus_framefmt frm_fmt;
+       struct v4l2_subdev_frame_interval frm_intrvl;
+       struct aptina_camera_module_exp_config exp_config;
+       struct aptina_camera_module_wb_config wb_config;
+       struct aptina_camera_module_af_config af_config;
+       struct aptina_camera_module_custom_config custom;
+       enum aptina_camera_module_state state;
+       enum aptina_camera_module_state state_before_suspend;
+       struct aptina_camera_module_config *active_config;
+       u32 ctrl_updt;
+       u32 vts_min;
+       bool auto_adjust_fps;
+       bool update_config;
+       bool frm_fmt_valid;
+       bool frm_intrvl_valid;
+       bool hflip;
+       bool vflip;
+       u32 rotation;
+       void *pltfm_data;
+       bool inited;
+};
+
+#define aptina_camera_module_pr_info(cam_mod, fmt, arg...) \
+       pltfrm_camera_module_pr_info(&cam_mod->sd, fmt, ## arg)
+#define aptina_camera_module_pr_debug(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_debug(&cam_mod->sd, fmt, ## arg)
+#define aptina_camera_module_pr_warn(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_warn(&cam_mod->sd, fmt, ## arg)
+#define aptina_camera_module_pr_err(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_err(&cam_mod->sd, fmt, ## arg)
+
+int aptina_camera_module_write_reglist(
+       struct aptina_camera_module *cam_mod,
+       const struct aptina_camera_module_reg reglist[],
+       int len);
+
+int aptina_camera_module_write_reg(
+       struct aptina_camera_module *cam_mod,
+       u16 reg,
+       u16 val);
+
+int aptina_camera_module_read_reg(
+       struct aptina_camera_module *cam_mod,
+       u16 data_length,
+       u16 reg,
+       u32 *val);
+
+int aptina_camera_module_read_reg_table(
+       struct aptina_camera_module *cam_mod,
+       u16 reg,
+       u32 *val);
+
+int aptina_camera_module_try_fmt(
+       struct v4l2_subdev *sd,
+       struct v4l2_mbus_framefmt *fmt);
+
+int aptina_camera_module_s_fmt(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format);
+
+int aptina_camera_module_g_fmt(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format);
+
+int aptina_camera_module_s_frame_interval(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_frame_interval *interval);
+
+int aptina_camera_module_s_stream(
+       struct v4l2_subdev *sd,
+       int enable);
+
+int aptina_camera_module_s_power(
+       struct v4l2_subdev *sd,
+       int on);
+
+int aptina_camera_module_g_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl);
+
+int aptina_camera_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl);
+
+int aptina_camera_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls);
+
+int aptina_camera_module_enum_frameintervals(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_frame_interval_enum *file);
+
+int aptina_camera_module_init(
+       struct aptina_camera_module *cam_mod,
+       struct aptina_camera_module_custom_config *custom);
+
+void aptina_camera_module_release(
+       struct aptina_camera_module *cam_mod);
+
+long aptina_camera_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg);
+
+int aptina_camera_module_get_flip_mirror(
+       struct aptina_camera_module *cam_mod);
+#endif
+
diff --git a/drivers/media/i2c/soc_camera/rockchip/imx323_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/imx323_v4l2-i2c-subdev.c
new file mode 100644 (file)
index 0000000..4df5f00
--- /dev/null
@@ -0,0 +1,613 @@
+/*
+ * IMX323 sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf-core.h>
+#include <linux/slab.h>
+#include "imx_camera_module.h"
+
+#define IMX323_DRIVER_NAME "imx323"
+
+#define IMX323_AEC_PK_GAIN_REG 0x301e
+
+#define IMX323_AEC_PK_EXPO_HIGH_REG 0x0202
+#define IMX323_AEC_PK_EXPO_LOW_REG 0x0203
+
+#define IMX323_FETCH_HIGH_BYTE_EXP(VAL) ((VAL >> 8) & 0xFF)
+#define IMX323_FETCH_LOW_BYTE_EXP(VAL) (VAL & 0xFF)
+
+#define IMX323_PID_ADDR 0x0112
+#define IMX323_PID_MAGIC 0xa
+
+#define IMX323_TIMING_VTS_HIGH_REG 0x0340
+#define IMX323_TIMING_VTS_LOW_REG 0x0341
+#define IMX323_TIMING_HTS_HIGH_REG 0x0342
+#define IMX323_TIMING_HTS_LOW_REG 0x0343
+
+#define IMX323_INTEGRATION_TIME_MARGIN 8
+#define IMX323_FINE_INTG_TIME_MIN 0
+#define IMX323_FINE_INTG_TIME_MAX_MARGIN 0
+#define IMX323_COARSE_INTG_TIME_MIN 16
+#define IMX323_COARSE_INTG_TIME_MAX_MARGIN 4
+
+#define IMX323_ORIENTATION_REG 0x0101
+#define IMX323_ORIENTATION_H 0x1
+#define IMX323_ORIENTATION_V 0x2
+
+#define IMX323_EXT_CLK 37125000
+
+static struct imx_camera_module imx323;
+static struct imx_camera_module_custom_config imx323_custom_config;
+
+/* ======================================================================== */
+/* Base sensor configs */
+/* ======================================================================== */
+
+/* MCLK:37.125MHz  1920x1080  30fps   DVP_12_DATA   PCLK:74.25MHz */
+static struct imx_camera_module_reg imx323_init_tab_1920_1080_30fps[] = {
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {IMX_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 0x01},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0009, 0xf0},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0112, 0x0c},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0113, 0x0c},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0340, 0x04},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0341, 0x65},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0342, 0x04},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0343, 0x4c},
+       {IMX_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 0x01},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3000, 0x31},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3002, 0x0f},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3011, 0x00},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3012, 0x82},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3013, 0x40},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3016, 0x3c},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x301a, 0xc9},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x301c, 0x50},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x301f, 0x73},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3021, 0x00},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x40},
+       {IMX_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 0x01},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3027, 0x20},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x302c, 0x00},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x303f, 0x0a},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x304f, 0x47},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3054, 0x11},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x307a, 0x00},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x307b, 0x00},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3098, 0x26},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3099, 0x02},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x309a, 0x26},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x309b, 0x02},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x30ce, 0x16},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x30cf, 0x82},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x30d0, 0x00},
+       {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3117, 0x0d}
+};
+
+/* ======================================================================== */
+
+static struct imx_camera_module_config imx323_configs[] = {
+       {
+               .name = "1920x1080_30fps",
+               .frm_fmt = {
+                       .width = 2200,
+                       .height = 1125,
+                       .code = MEDIA_BUS_FMT_SGBRG12_1X12
+               },
+               .frm_intrvl = {
+                       .interval = {
+                               .numerator = 1,
+                               .denominator = 30
+                       }
+               },
+               .auto_exp_enabled = false,
+               .auto_gain_enabled = false,
+               .auto_wb_enabled = false,
+               .reg_table = (void *)imx323_init_tab_1920_1080_30fps,
+               .reg_table_num_entries =
+                       sizeof(imx323_init_tab_1920_1080_30fps) /
+                       sizeof(imx323_init_tab_1920_1080_30fps[0]),
+               .v_blanking_time_us = 5000,
+               PLTFRM_CAM_ITF_DVP_CFG(
+                       PLTFRM_CAM_ITF_BT601_12,
+                       PLTFRM_CAM_SIGNAL_HIGH_LEVEL,
+                       PLTFRM_CAM_SIGNAL_HIGH_LEVEL,
+                       PLTFRM_CAM_SDR_NEG_EDG,
+                       IMX323_EXT_CLK)
+       }
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_g_VTS(struct imx_camera_module *cam_mod, u32 *vts)
+{
+       u32 msb, lsb;
+       int ret;
+
+       ret = imx_camera_module_read_reg_table(
+               cam_mod,
+               IMX323_TIMING_VTS_HIGH_REG,
+               &msb);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       ret = imx_camera_module_read_reg_table(
+               cam_mod,
+               IMX323_TIMING_VTS_LOW_REG,
+               &lsb);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       *vts = (msb << 8) | lsb;
+
+       return 0;
+err:
+       imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_auto_adjust_fps(struct imx_camera_module *cam_mod,
+       u32 exp_time)
+{
+       int ret;
+       u32 vts;
+
+       if ((cam_mod->exp_config.exp_time + IMX323_COARSE_INTG_TIME_MAX_MARGIN)
+               > cam_mod->vts_min)
+               vts = cam_mod->exp_config.exp_time +
+                                       IMX323_COARSE_INTG_TIME_MAX_MARGIN;
+       else
+               vts = cam_mod->vts_min;
+       ret = imx_camera_module_write_reg(cam_mod,
+                               IMX323_TIMING_VTS_LOW_REG, vts & 0xFF);
+       ret |= imx_camera_module_write_reg(cam_mod,
+                               IMX323_TIMING_VTS_HIGH_REG, (vts >> 8) & 0xFF);
+
+       if (IS_ERR_VALUE(ret))
+               imx_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       else
+               imx_camera_module_pr_debug(cam_mod,
+                       "updated vts = %d,vts_min=%d\n", vts, cam_mod->vts_min);
+
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_write_aec(struct imx_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       imx_camera_module_pr_debug(cam_mod,
+                                 "exp_time = %d, gain = %d, flash_mode = %d\n",
+                                 cam_mod->exp_config.exp_time,
+                                 cam_mod->exp_config.gain,
+                                 cam_mod->exp_config.flash_mode);
+
+       /*
+        * if the sensor is already streaming, write to shadow registers,
+        * if the sensor is in SW standby, write to active registers,
+        * if the sensor is off/registers are not writeable, do nothing
+        */
+       if ((cam_mod->state == IMX_CAMERA_MODULE_SW_STANDBY) ||
+       (cam_mod->state == IMX_CAMERA_MODULE_STREAMING)) {
+               u32 a_gain = cam_mod->exp_config.gain;
+               u32 exp_time = cam_mod->exp_config.exp_time;
+
+               a_gain = a_gain * cam_mod->exp_config.gain_percent / 100;
+
+               if (!IS_ERR_VALUE(ret) && cam_mod->auto_adjust_fps)
+                       ret = imx323_auto_adjust_fps(cam_mod,
+                                       cam_mod->exp_config.exp_time);
+
+               /* Gain */
+               ret = imx_camera_module_write_reg(cam_mod,
+                               IMX323_AEC_PK_GAIN_REG, a_gain);
+
+               /* Integration Time */
+               ret = imx_camera_module_write_reg(cam_mod,
+                               IMX323_AEC_PK_EXPO_HIGH_REG,
+                               IMX323_FETCH_HIGH_BYTE_EXP(exp_time));
+               ret |= imx_camera_module_write_reg(cam_mod,
+                               IMX323_AEC_PK_EXPO_LOW_REG,
+                               IMX323_FETCH_LOW_BYTE_EXP(exp_time));
+       }
+
+       if (IS_ERR_VALUE(ret))
+               imx_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_g_ctrl(struct imx_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       imx_camera_module_pr_debug(cam_mod, "\n");
+
+       switch (ctrl_id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_EXPOSURE:
+       case V4L2_CID_FLASH_LED_MODE:
+               /* nothing to be done here */
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               imx_camera_module_pr_debug(cam_mod,
+                               "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_filltimings(struct imx_camera_module_custom_config *custom)
+{
+       int i, j;
+       struct imx_camera_module_config *config;
+       struct imx_camera_module_timings *timings;
+       struct imx_camera_module_reg *reg_table;
+       int reg_table_num_entries;
+
+       for (i = 0; i < custom->num_configs; i++) {
+               config = &custom->configs[i];
+               reg_table = config->reg_table;
+               reg_table_num_entries = config->reg_table_num_entries;
+               timings = &config->timings;
+
+               for (j = 0; j < reg_table_num_entries; j++) {
+                       switch (reg_table[j].reg) {
+                       case IMX323_TIMING_VTS_HIGH_REG:
+                               timings->frame_length_lines =
+                                       reg_table[j].val << 8;
+                               break;
+                       case IMX323_TIMING_VTS_LOW_REG:
+                               timings->frame_length_lines |= reg_table[j].val;
+                               break;
+                       case IMX323_TIMING_HTS_HIGH_REG:
+                               timings->line_length_pck =
+                                       (reg_table[j].val << 8);
+                               break;
+                       case IMX323_TIMING_HTS_LOW_REG:
+                               timings->line_length_pck |= reg_table[j].val;
+                               break;
+                       }
+               }
+
+               timings->exp_time >>= 4;
+               timings->line_length_pck = timings->line_length_pck * 2;
+               timings->vt_pix_clk_freq_hz =
+                                       config->frm_intrvl.interval.denominator
+                                       * timings->frame_length_lines
+                                       * timings->line_length_pck;
+
+               timings->coarse_integration_time_min =
+                       IMX323_COARSE_INTG_TIME_MIN;
+               timings->coarse_integration_time_max_margin =
+                       IMX323_COARSE_INTG_TIME_MAX_MARGIN;
+
+               /* IMX Sensor do not use fine integration time. */
+               timings->fine_integration_time_min = IMX323_FINE_INTG_TIME_MIN;
+               timings->fine_integration_time_max_margin =
+                       IMX323_FINE_INTG_TIME_MAX_MARGIN;
+       }
+
+       return 0;
+}
+
+static int imx323_g_timings(struct imx_camera_module *cam_mod,
+       struct imx_camera_module_timings *timings)
+{
+       int ret = 0;
+       unsigned int vts;
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config))
+               goto err;
+
+       *timings = cam_mod->active_config->timings;
+
+       vts = (!cam_mod->vts_cur) ?
+               timings->frame_length_lines :
+               cam_mod->vts_cur;
+
+       if (cam_mod->frm_intrvl_valid)
+               timings->vt_pix_clk_freq_hz =
+                       cam_mod->frm_intrvl.interval.denominator
+                       * vts
+                       * timings->line_length_pck;
+       else
+               timings->vt_pix_clk_freq_hz =
+                       cam_mod->active_config->frm_intrvl.interval.denominator
+                       * vts
+                       * timings->line_length_pck;
+
+       return ret;
+err:
+       imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_set_flip(struct imx_camera_module *cam_mod)
+{
+       int mode = 0;
+       u16 orientation = 0;
+
+       mode = imx_camera_module_get_flip_mirror(cam_mod);
+       if (mode == -1) {
+               imx_camera_module_pr_info(cam_mod,
+                       "dts don't set flip, return!\n");
+               return 0;
+       }
+
+       if (!IS_ERR_OR_NULL(cam_mod->active_config)) {
+               if (mode == IMX_FLIP_BIT_MASK)
+                       orientation = IMX323_ORIENTATION_V;
+               else if (mode == IMX_MIRROR_BIT_MASK)
+                       orientation = IMX323_ORIENTATION_H;
+               else if (mode == (IMX_MIRROR_BIT_MASK | IMX_FLIP_BIT_MASK))
+                       orientation = IMX323_ORIENTATION_H |
+                                       IMX323_ORIENTATION_V;
+               else
+                       orientation = 0;
+       }
+
+       return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_s_ctrl(struct imx_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       imx_camera_module_pr_debug(cam_mod, "\n");
+
+       switch (ctrl_id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_EXPOSURE:
+               ret = imx323_write_aec(cam_mod);
+               break;
+       case V4L2_CID_FLASH_LED_MODE:
+               /* nothing to be done here */
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               imx_camera_module_pr_debug(cam_mod,
+                               "failed with error (%d) 0x%x\n",
+                               ret, ctrl_id);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_s_ext_ctrls(struct imx_camera_module *cam_mod,
+       struct imx_camera_module_ext_ctrls *ctrls)
+{
+       int ret = 0;
+
+       /* Handles only exposure and gain together special case. */
+       if (ctrls->count == 1)
+               ret = imx323_s_ctrl(cam_mod, ctrls->ctrls[0].id);
+       else if ((ctrls->count == 3) &&
+                ((ctrls->ctrls[0].id == V4L2_CID_GAIN &&
+                  ctrls->ctrls[1].id == V4L2_CID_EXPOSURE) ||
+                 (ctrls->ctrls[1].id == V4L2_CID_GAIN &&
+                  ctrls->ctrls[0].id == V4L2_CID_EXPOSURE)))
+               ret = imx323_write_aec(cam_mod);
+       else
+               ret = -EINVAL;
+
+       if (IS_ERR_VALUE(ret))
+               imx_camera_module_pr_debug(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_start_streaming(struct imx_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       imx_camera_module_pr_debug(cam_mod,
+               "active config=%s\n", cam_mod->active_config->name);
+
+       ret = imx323_g_VTS(cam_mod, &cam_mod->vts_min);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       if (IS_ERR_VALUE(imx_camera_module_write_reg(cam_mod, 0x0100, 1)))
+               goto err;
+
+       msleep(25);
+
+       return 0;
+err:
+       imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n",
+               ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int imx323_stop_streaming(struct imx_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       imx_camera_module_pr_debug(cam_mod, "\n");
+
+       ret = imx_camera_module_write_reg(cam_mod, 0x0100, 0);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       msleep(25);
+
+       return 0;
+err:
+       imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+static int imx323_check_camera_id(struct imx_camera_module *cam_mod)
+{
+       u32 pid;
+       int ret = 0;
+
+       imx_camera_module_pr_debug(cam_mod, "\n");
+
+       ret |= imx_camera_module_read_reg(cam_mod, 1, IMX323_PID_ADDR, &pid);
+       if (IS_ERR_VALUE(ret)) {
+               imx_camera_module_pr_err(cam_mod,
+               "register read failed, camera module powered off?\n");
+               goto err;
+       }
+
+       if (pid == IMX323_PID_MAGIC) {
+               imx_camera_module_pr_debug(cam_mod,
+               "successfully detected camera ID 0x%02x\n",
+               pid);
+       } else {
+               imx_camera_module_pr_err(cam_mod,
+               "wrong camera ID, expected 0x%02x, detected 0x%02x\n",
+               IMX323_PID_MAGIC, pid);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       return 0;
+err:
+       imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+/* This part is platform dependent */
+/* ======================================================================== */
+
+static struct v4l2_subdev_core_ops imx323_camera_module_core_ops = {
+       .g_ctrl = imx_camera_module_g_ctrl,
+       .s_ctrl = imx_camera_module_s_ctrl,
+       .s_ext_ctrls = imx_camera_module_s_ext_ctrls,
+       .s_power = imx_camera_module_s_power,
+       .ioctl = imx_camera_module_ioctl
+};
+
+static struct v4l2_subdev_video_ops imx323_camera_module_video_ops = {
+       .s_frame_interval = imx_camera_module_s_frame_interval,
+       .s_stream = imx_camera_module_s_stream
+};
+
+static struct v4l2_subdev_pad_ops imx323_camera_module_pad_ops = {
+       .enum_frame_interval = imx_camera_module_enum_frameintervals,
+       .get_fmt = imx_camera_module_g_fmt,
+       .set_fmt = imx_camera_module_s_fmt,
+};
+
+static struct v4l2_subdev_ops imx323_camera_module_ops = {
+       .core = &imx323_camera_module_core_ops,
+       .video = &imx323_camera_module_video_ops,
+       .pad = &imx323_camera_module_pad_ops
+};
+
+static struct imx_camera_module_custom_config imx323_custom_config = {
+       .start_streaming = imx323_start_streaming,
+       .stop_streaming = imx323_stop_streaming,
+       .s_ctrl = imx323_s_ctrl,
+       .s_ext_ctrls = imx323_s_ext_ctrls,
+       .g_ctrl = imx323_g_ctrl,
+       .g_timings = imx323_g_timings,
+       .check_camera_id = imx323_check_camera_id,
+       .set_flip = imx323_set_flip,
+       .configs = imx323_configs,
+       .num_configs =  ARRAY_SIZE(imx323_configs),
+       .power_up_delays_ms = {5, 20, 0}
+};
+
+static int imx323_probe(
+       struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       dev_info(&client->dev, "probing...\n");
+
+       imx323_filltimings(&imx323_custom_config);
+       v4l2_i2c_subdev_init(&imx323.sd, client, &imx323_camera_module_ops);
+       imx323.custom = imx323_custom_config;
+
+       dev_info(&client->dev, "probing successful\n");
+       return 0;
+}
+
+static int imx323_remove(struct i2c_client *client)
+{
+       struct imx_camera_module *cam_mod = i2c_get_clientdata(client);
+
+       dev_info(&client->dev, "removing device...\n");
+
+       if (!client->adapter)
+               return -ENODEV; /* our client isn't attached */
+
+       imx_camera_module_release(cam_mod);
+
+       dev_info(&client->dev, "removed\n");
+       return 0;
+}
+
+static const struct i2c_device_id imx323_id[] = {
+       { IMX323_DRIVER_NAME, 0 },
+       { }
+};
+
+static const struct of_device_id imx323_of_match[] = {
+       {.compatible = "sony,imx323-v4l2-i2c-subdev"},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, imx323_id);
+
+static struct i2c_driver imx323_i2c_driver = {
+       .driver = {
+               .name = IMX323_DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = imx323_of_match
+       },
+       .probe = imx323_probe,
+       .remove = imx323_remove,
+       .id_table = imx323_id,
+};
+
+module_i2c_driver(imx323_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for IMX323");
+MODULE_AUTHOR("George");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.c
new file mode 100644 (file)
index 0000000..34317d5
--- /dev/null
@@ -0,0 +1,1108 @@
+/*
+ * imx_camera_module.c
+ *
+ * Generic sony sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-core.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+#include <media/v4l2-controls_rockchip.h>
+
+#include "imx_camera_module.h"
+
+static struct imx_camera_module *to_imx_camera_module(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct imx_camera_module, sd);
+}
+
+/* ======================================================================== */
+
+static void imx_camera_module_reset(
+       struct imx_camera_module *cam_mod)
+{
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       cam_mod->inited = false;
+       cam_mod->active_config = NULL;
+       cam_mod->update_config = true;
+       cam_mod->frm_fmt_valid = false;
+       cam_mod->frm_intrvl_valid = false;
+       cam_mod->exp_config.auto_exp = false;
+       cam_mod->exp_config.auto_gain = false;
+       cam_mod->wb_config.auto_wb = false;
+       cam_mod->hflip = false;
+       cam_mod->vflip = false;
+       cam_mod->auto_adjust_fps = true;
+       cam_mod->rotation = 0;
+       cam_mod->ctrl_updt = 0;
+       cam_mod->state = IMX_CAMERA_MODULE_POWER_OFF;
+       cam_mod->state_before_suspend = IMX_CAMERA_MODULE_POWER_OFF;
+       cam_mod->exp_config.exp_time = 0;
+       cam_mod->exp_config.gain = 0;
+       cam_mod->vts_cur = 0;
+}
+
+/* ======================================================================== */
+
+static void imx_camera_module_set_active_config(
+       struct imx_camera_module *cam_mod,
+       struct imx_camera_module_config *new_config)
+{
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (IS_ERR_OR_NULL(new_config)) {
+               cam_mod->active_config = new_config;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "no active config\n");
+       } else {
+               cam_mod->ctrl_updt &= IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP |
+                       IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN |
+                       IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+               if (new_config->auto_exp_enabled !=
+                       cam_mod->exp_config.auto_exp) {
+                       cam_mod->ctrl_updt |=
+                               IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP;
+                       cam_mod->exp_config.auto_exp =
+                               new_config->auto_exp_enabled;
+               }
+               if (new_config->auto_gain_enabled !=
+                       cam_mod->exp_config.auto_gain) {
+                       cam_mod->ctrl_updt |=
+                               IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN;
+                       cam_mod->exp_config.auto_gain =
+                               new_config->auto_gain_enabled;
+               }
+               if (new_config->auto_wb_enabled !=
+                       cam_mod->wb_config.auto_wb) {
+                       cam_mod->ctrl_updt |=
+                               IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+                       cam_mod->wb_config.auto_wb =
+                               new_config->auto_wb_enabled;
+               }
+               if (new_config != cam_mod->active_config) {
+                       cam_mod->update_config = true;
+                       cam_mod->active_config = new_config;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "activating config '%s'\n",
+                               cam_mod->active_config->name);
+               }
+       }
+}
+
+/* ======================================================================== */
+
+static struct imx_camera_module_config *imx_camera_module_find_config(
+       struct imx_camera_module *cam_mod,
+       struct v4l2_mbus_framefmt *fmt,
+       struct v4l2_subdev_frame_interval *frm_intrvl)
+{
+       u32 i;
+       unsigned long gcdiv;
+       struct v4l2_subdev_frame_interval norm_interval;
+
+       if (!IS_ERR_OR_NULL(fmt))
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "%dx%d, fmt code 0x%04x\n",
+                       fmt->width, fmt->height, fmt->code);
+
+       if (!IS_ERR_OR_NULL(frm_intrvl))
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "frame interval %d/%d\n",
+                       frm_intrvl->interval.numerator,
+                       frm_intrvl->interval.denominator);
+
+       for (i = 0; i < cam_mod->custom.num_configs; i++) {
+               if (!IS_ERR_OR_NULL(frm_intrvl)) {
+                       gcdiv = gcd(cam_mod->custom.configs[i].
+                               frm_intrvl.interval.numerator,
+                               cam_mod->custom.configs[i].
+                                       frm_intrvl.interval.denominator);
+                       norm_interval.interval.numerator =
+                               cam_mod->custom.configs[i].
+                                       frm_intrvl.interval.numerator / gcdiv;
+                       norm_interval.interval.denominator =
+                               cam_mod->custom.configs[i].
+                               frm_intrvl.interval.denominator / gcdiv;
+                       if ((frm_intrvl->interval.numerator !=
+                               norm_interval.interval.numerator) ||
+                               (frm_intrvl->interval.denominator !=
+                               norm_interval.interval.denominator))
+                               continue;
+               }
+               if (!IS_ERR_OR_NULL(fmt)) {
+                       if ((cam_mod->custom.configs[i].frm_fmt.width !=
+                               fmt->width) ||
+                               (cam_mod->custom.configs[i].frm_fmt.height !=
+                               fmt->height) ||
+                               (cam_mod->custom.configs[i].frm_fmt.code !=
+                               fmt->code)) {
+                               continue;
+                       }
+               }
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "found matching config %s\n",
+                       cam_mod->custom.configs[i].name);
+               return &cam_mod->custom.configs[i];
+       }
+       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+               "no matching config found\n");
+
+       return ERR_PTR(-EINVAL);
+}
+
+/* ======================================================================== */
+
+static int imx_camera_module_write_config(
+       struct imx_camera_module *cam_mod)
+{
+       int ret = 0;
+       struct imx_camera_module_reg *reg_table;
+       u32 reg_table_num_entries;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "no active sensor configuration");
+               ret = -EFAULT;
+               goto err;
+       }
+
+       if (cam_mod->inited == false) {
+               cam_mod->active_config->soft_reset = true;
+               reg_table = cam_mod->active_config->reg_table;
+               reg_table_num_entries =
+               cam_mod->active_config->reg_table_num_entries;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "write config %s\n",
+                               cam_mod->active_config->name);
+       }
+
+       if (!IS_ERR_OR_NULL(cam_mod->custom.set_flip))
+               cam_mod->custom.set_flip(cam_mod);
+
+       ret = pltfrm_camera_module_write_reglist(&cam_mod->sd,
+               reg_table, reg_table_num_entries);
+
+       if (IS_ERR_VALUE(ret))
+               goto err;
+       ret = pltfrm_camera_module_patch_config(&cam_mod->sd,
+               &cam_mod->frm_fmt,
+               &cam_mod->frm_intrvl);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+static int imx_camera_module_attach(
+       struct imx_camera_module *cam_mod)
+{
+       int ret = 0;
+       struct imx_camera_module_custom_config *custom;
+
+       custom = &cam_mod->custom;
+
+       if (custom->check_camera_id) {
+               imx_camera_module_s_power(&cam_mod->sd, 1);
+               ret = custom->check_camera_id(cam_mod);
+               imx_camera_module_s_power(&cam_mod->sd, 0);
+               if (ret != 0)
+                       goto err;
+       }
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       imx_camera_module_release(cam_mod);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_try_fmt(struct v4l2_subdev *sd,
+       struct v4l2_mbus_framefmt *fmt)
+{
+       struct imx_camera_module *cam_mod = to_imx_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n",
+               fmt->width, fmt->height, fmt->code);
+
+       if (IS_ERR_OR_NULL(imx_camera_module_find_config(cam_mod, fmt, NULL))) {
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "format not supported\n");
+               return -EINVAL;
+       }
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "format supported\n");
+
+       return 0;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_s_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format)
+{
+       struct imx_camera_module *cam_mod =  to_imx_camera_module(sd);
+       struct v4l2_mbus_framefmt *fmt = &format->format;
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n",
+               fmt->width, fmt->height, fmt->code);
+
+       if (IS_ERR_OR_NULL(imx_camera_module_find_config(cam_mod, fmt, NULL))) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "format %dx%d, code 0x%04x, not supported\n",
+                       fmt->width, fmt->height, fmt->code);
+               ret = -EINVAL;
+               goto err;
+       }
+       cam_mod->frm_fmt_valid = true;
+       cam_mod->frm_fmt = *fmt;
+       if (cam_mod->frm_intrvl_valid) {
+               imx_camera_module_set_active_config(cam_mod,
+                       imx_camera_module_find_config(cam_mod,
+                               fmt, &cam_mod->frm_intrvl));
+       }
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_g_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format)
+{
+       struct imx_camera_module *cam_mod =  to_imx_camera_module(sd);
+       struct v4l2_mbus_framefmt *fmt = &format->format;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (cam_mod->active_config) {
+               fmt->code = cam_mod->active_config->frm_fmt.code;
+               fmt->width = cam_mod->active_config->frm_fmt.width;
+               fmt->height = cam_mod->active_config->frm_fmt.height;
+               return 0;
+       }
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "no active config\n");
+
+       return -1;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_s_frame_interval(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_frame_interval *interval)
+{
+       struct imx_camera_module *cam_mod = to_imx_camera_module(sd);
+       unsigned long gcdiv;
+       struct v4l2_subdev_frame_interval norm_interval;
+       int ret = 0;
+
+       if ((interval->interval.denominator == 0) ||
+               (interval->interval.numerator == 0)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "invalid frame interval %d/%d\n",
+                       interval->interval.numerator,
+                       interval->interval.denominator);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d/%d (%dfps)\n",
+               interval->interval.numerator, interval->interval.denominator,
+               (interval->interval.denominator +
+               (interval->interval.numerator >> 1)) /
+               interval->interval.numerator);
+
+       /* normalize interval */
+       gcdiv = gcd(interval->interval.numerator,
+               interval->interval.denominator);
+       norm_interval.interval.numerator =
+               interval->interval.numerator / gcdiv;
+       norm_interval.interval.denominator =
+               interval->interval.denominator / gcdiv;
+
+       if (IS_ERR_OR_NULL(imx_camera_module_find_config(cam_mod,
+                       NULL, &norm_interval))) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "frame interval %d/%d not supported\n",
+                       interval->interval.numerator,
+                       interval->interval.denominator);
+               ret = -EINVAL;
+               goto err;
+       }
+       cam_mod->frm_intrvl_valid = true;
+       cam_mod->frm_intrvl = norm_interval;
+       if (cam_mod->frm_fmt_valid) {
+               imx_camera_module_set_active_config(cam_mod,
+                       imx_camera_module_find_config(cam_mod,
+                               &cam_mod->frm_fmt, interval));
+       }
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       int ret = 0;
+       struct imx_camera_module *cam_mod =  to_imx_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", enable);
+
+       if (enable) {
+               if (cam_mod->state == IMX_CAMERA_MODULE_STREAMING)
+                       return 0;
+               if (IS_ERR_OR_NULL(cam_mod->active_config)) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "no active sensor configuration, cannot start streaming\n");
+                       ret = -EFAULT;
+                       goto err;
+               }
+               if (cam_mod->state != IMX_CAMERA_MODULE_SW_STANDBY) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "sensor is not powered on (in state %d), cannot start streaming\n",
+                               cam_mod->state);
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if (cam_mod->update_config)
+                       ret = imx_camera_module_write_config(cam_mod);
+                       if (IS_ERR_VALUE(ret))
+                               goto err;
+
+               ret = cam_mod->custom.start_streaming(cam_mod);
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+               if (!cam_mod->inited && cam_mod->update_config)
+                       cam_mod->inited = true;
+               cam_mod->update_config = false;
+               cam_mod->ctrl_updt = 0;
+               mdelay(cam_mod->custom.power_up_delays_ms[2]);
+               cam_mod->state = IMX_CAMERA_MODULE_STREAMING;
+       } else {
+               int pclk;
+               int wait_ms;
+               struct isp_supplemental_sensor_mode_data timings;
+
+               if (cam_mod->state != IMX_CAMERA_MODULE_STREAMING)
+                       return 0;
+               ret = cam_mod->custom.stop_streaming(cam_mod);
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+
+               ret = imx_camera_module_ioctl(sd,
+                                       RK_VIDIOC_SENSOR_MODE_DATA,
+                                       &timings);
+
+               cam_mod->state = IMX_CAMERA_MODULE_SW_STANDBY;
+
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+
+               pclk = timings.vt_pix_clk_freq_hz / 1000;
+
+               if (!pclk)
+                       goto err;
+
+               wait_ms =
+                       (timings.line_length_pck *
+                       timings.frame_length_lines) /
+                       pclk;
+
+               /*
+                * wait for a frame period to make sure that there is
+                * no pending frame left.
+                */
+
+               msleep(wait_ms + 1);
+       }
+
+       cam_mod->state_before_suspend = cam_mod->state;
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_s_power(struct v4l2_subdev *sd, int on)
+{
+       int ret = 0;
+       struct imx_camera_module *cam_mod =  to_imx_camera_module(sd);
+       struct v4l2_subdev *af_ctrl;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "%d state:%d\n", on, cam_mod->state);
+
+       if (on) {
+               if (cam_mod->state == IMX_CAMERA_MODULE_POWER_OFF) {
+                       ret = pltfrm_camera_module_s_power(&cam_mod->sd, 1);
+                       if (!IS_ERR_VALUE(ret)) {
+                               mdelay(cam_mod->custom.power_up_delays_ms[0]);
+                               cam_mod->state = IMX_CAMERA_MODULE_HW_STANDBY;
+                       }
+               }
+               if (cam_mod->state == IMX_CAMERA_MODULE_HW_STANDBY) {
+                       ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_PD,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+                       if (!IS_ERR_VALUE(ret)) {
+                               mdelay(cam_mod->custom.power_up_delays_ms[1]);
+                               cam_mod->state = IMX_CAMERA_MODULE_SW_STANDBY;
+                               if (!IS_ERR_OR_NULL(
+                                   cam_mod->custom.init_common) &&
+                                   cam_mod->custom.init_common(
+                                   cam_mod))
+                               usleep_range(1000, 1500);
+
+                               af_ctrl = pltfrm_camera_module_get_af_ctrl(sd);
+                               if (!IS_ERR_OR_NULL(af_ctrl)) {
+                                       v4l2_subdev_call(af_ctrl,
+                                                        core, init, 0);
+                               }
+                       }
+               }
+       } else {
+               if (cam_mod->state == IMX_CAMERA_MODULE_STREAMING) {
+                       ret = imx_camera_module_s_stream(sd, 0);
+                       if (!IS_ERR_VALUE(ret))
+                               cam_mod->state = IMX_CAMERA_MODULE_SW_STANDBY;
+               }
+               if (cam_mod->state == IMX_CAMERA_MODULE_SW_STANDBY) {
+                       ret = pltfrm_camera_module_set_pin_state(
+                               &cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_PD,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+                       if (!IS_ERR_VALUE(ret))
+                               cam_mod->state = IMX_CAMERA_MODULE_HW_STANDBY;
+               }
+               if (cam_mod->state == IMX_CAMERA_MODULE_HW_STANDBY) {
+                       ret = pltfrm_camera_module_s_power(&cam_mod->sd, 0);
+                       if (!IS_ERR_VALUE(ret)) {
+                               cam_mod->state = IMX_CAMERA_MODULE_POWER_OFF;
+                               imx_camera_module_reset(cam_mod);
+                       }
+               }
+       }
+
+       cam_mod->state_before_suspend = cam_mod->state;
+
+       if (IS_ERR_VALUE(ret)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "%s failed, camera left in state %d\n",
+                       on ? "on" : "off", cam_mod->state);
+               goto err;
+       } else
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "camera powered %s\n", on ? "on" : "off");
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_g_ctrl(struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       struct imx_camera_module *cam_mod = to_imx_camera_module(sd);
+       int ret;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, " id 0x%x\n", ctrl->id);
+
+       if (ctrl->id == V4L2_CID_FLASH_LED_MODE) {
+               ctrl->value = cam_mod->exp_config.flash_mode;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_FLASH_LED_MODE %d\n",
+                       ctrl->value);
+               return 0;
+       }
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "no active configuration\n");
+               return -EFAULT;
+       }
+
+       if (ctrl->id == RK_V4L2_CID_VBLANKING) {
+               ctrl->value = cam_mod->active_config->v_blanking_time_us;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "RK_V4L2_CID_VBLANKING %d\n",
+                       ctrl->value);
+               return 0;
+       }
+
+       if ((cam_mod->state != IMX_CAMERA_MODULE_SW_STANDBY) &&
+               (cam_mod->state != IMX_CAMERA_MODULE_STREAMING)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "cannot get controls when camera is off\n");
+               return -EFAULT;
+       }
+
+       if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
+               struct v4l2_subdev *af_ctrl;
+
+               af_ctrl = pltfrm_camera_module_get_af_ctrl(sd);
+               if (!IS_ERR_OR_NULL(af_ctrl)) {
+                       ret = v4l2_subdev_call(af_ctrl, core, g_ctrl, ctrl);
+                       return ret;
+               }
+       }
+
+       if (!IS_ERR_OR_NULL(cam_mod->custom.g_ctrl)) {
+               ret = cam_mod->custom.g_ctrl(cam_mod, ctrl->id);
+               if (IS_ERR_VALUE(ret))
+                       return ret;
+       }
+
+       switch (ctrl->id) {
+       case V4L2_CID_GAIN:
+               ctrl->value = cam_mod->exp_config.gain;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                            "V4L2_CID_GAIN %d\n",
+                            ctrl->value);
+               break;
+       case V4L2_CID_EXPOSURE:
+               ctrl->value = cam_mod->exp_config.exp_time;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                            "V4L2_CID_EXPOSURE %d\n",
+                            ctrl->value);
+               break;
+       case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+               ctrl->value = cam_mod->wb_config.temperature;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+               ctrl->value = cam_mod->wb_config.preset_id;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               ctrl->value = cam_mod->exp_config.auto_gain;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTOGAIN %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_EXPOSURE_AUTO:
+               ctrl->value = cam_mod->exp_config.auto_exp;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_EXPOSURE_AUTO %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               ctrl->value = cam_mod->wb_config.auto_wb;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_WHITE_BALANCE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_FOCUS_ABSOLUTE:
+               ctrl->value = cam_mod->af_config.abs_pos;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_FOCUS_ABSOLUTE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_HFLIP:
+       case V4L2_CID_VFLIP:
+               /* TBD */
+               /* fallthrough */
+       default:
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "failed, unknown ctrl %d\n", ctrl->id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int flash_light_ctrl(
+               struct v4l2_subdev *sd,
+               struct imx_camera_module *cam_mod,
+               int value)
+{
+       return 0;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls)
+{
+       int i;
+       int ctrl_cnt = 0;
+       struct imx_camera_module *cam_mod =  to_imx_camera_module(sd);
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+       if (ctrls->count == 0)
+               return -EINVAL;
+
+       for (i = 0; i < ctrls->count; i++) {
+               struct v4l2_ext_control *ctrl;
+               u32 ctrl_updt = 0;
+
+               ctrl = &ctrls->controls[i];
+
+               switch (ctrl->id) {
+               case V4L2_CID_GAIN:
+                       ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_GAIN;
+                       cam_mod->exp_config.gain = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_GAIN %d\n",
+                       ctrl->value);
+                       break;
+               case RK_V4L2_CID_GAIN_PERCENT:
+                       ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_GAIN;
+                       cam_mod->exp_config.gain_percent = ctrl->value;
+                       break;
+               case V4L2_CID_FLASH_LED_MODE:
+                       ret = flash_light_ctrl(sd, cam_mod, ctrl->value);
+                       if (ret == 0) {
+                               cam_mod->exp_config.flash_mode = ctrl->value;
+                               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                                       "V4L2_CID_FLASH_LED_MODE %d\n",
+                                       ctrl->value);
+                       }
+                       break;
+               case V4L2_CID_EXPOSURE:
+                       ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_EXP_TIME;
+                       cam_mod->exp_config.exp_time = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_EXPOSURE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+                       ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE;
+                       cam_mod->wb_config.temperature = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+                       ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_PRESET_WB;
+                       cam_mod->wb_config.preset_id = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_AUTOGAIN:
+                       ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN;
+                       cam_mod->exp_config.auto_gain = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTOGAIN %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_EXPOSURE_AUTO:
+                       ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP;
+                       cam_mod->exp_config.auto_exp = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_EXPOSURE_AUTO %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_AUTO_WHITE_BALANCE:
+                       ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+                       cam_mod->wb_config.auto_wb = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_WHITE_BALANCE %d\n",
+                       ctrl->value);
+                       break;
+               case RK_V4L2_CID_AUTO_FPS:
+                       cam_mod->auto_adjust_fps = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "RK_V4L2_CID_AUTO_FPS %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_FOCUS_ABSOLUTE:
+                       {
+                               struct v4l2_subdev *af_ctrl;
+
+                               af_ctrl = pltfrm_camera_module_get_af_ctrl(sd);
+                               if (!IS_ERR_OR_NULL(af_ctrl)) {
+                                       struct v4l2_control single_ctrl;
+
+                                       single_ctrl.id =
+                                               V4L2_CID_FOCUS_ABSOLUTE;
+                                       single_ctrl.value = ctrl->value;
+                                       ret = v4l2_subdev_call(af_ctrl,
+                                               core, s_ctrl, &single_ctrl);
+                                       return ret;
+                               }
+                       }
+                       ctrl_updt =
+                               IMX_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE;
+                       cam_mod->af_config.abs_pos = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_FOCUS_ABSOLUTE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_HFLIP:
+                       if (ctrl->value)
+                               cam_mod->hflip = true;
+                       else
+                               cam_mod->hflip = false;
+                       break;
+               case V4L2_CID_VFLIP:
+                       if (ctrl->value)
+                               cam_mod->vflip = true;
+                       else
+                               cam_mod->vflip = false;
+                       break;
+               default:
+                       pltfrm_camera_module_pr_warn(&cam_mod->sd,
+                       "ignoring unknown ctrl 0x%x\n", ctrl->id);
+                       break;
+               }
+
+               if (cam_mod->state != IMX_CAMERA_MODULE_SW_STANDBY &&
+               cam_mod->state != IMX_CAMERA_MODULE_STREAMING)
+                       cam_mod->ctrl_updt |= ctrl_updt;
+               else if (ctrl_updt)
+                       ctrl_cnt++;
+       }
+
+       /* if camera module is already streaming, write through */
+       if (ctrl_cnt &&
+               (cam_mod->state == IMX_CAMERA_MODULE_STREAMING ||
+               cam_mod->state == IMX_CAMERA_MODULE_SW_STANDBY)) {
+               struct imx_camera_module_ext_ctrls imx_ctrls;
+
+               imx_ctrls.ctrls =
+               (struct imx_camera_module_ext_ctrl *)
+               kmalloc(ctrl_cnt * sizeof(struct imx_camera_module_ext_ctrl),
+                       GFP_KERNEL);
+
+               if (imx_ctrls.ctrls) {
+                       for (i = 0; i < ctrl_cnt; i++) {
+                               imx_ctrls.ctrls[i].id = ctrls->controls[i].id;
+                               imx_ctrls.ctrls[i].value =
+                                       ctrls->controls[i].value;
+                       }
+
+                       imx_ctrls.count = ctrl_cnt;
+
+                       ret = cam_mod->custom.s_ext_ctrls(cam_mod, &imx_ctrls);
+
+                       kfree(imx_ctrls.ctrls);
+               } else {
+                       ret = -ENOMEM;
+               }
+
+               if (IS_ERR_VALUE(ret))
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "failed with error %d\n", ret);
+       }
+
+       return ret;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       struct imx_camera_module *cam_mod =  to_imx_camera_module(sd);
+       struct v4l2_ext_control ext_ctrl[1];
+       struct v4l2_ext_controls ext_ctrls;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+               "0x%x 0x%x\n", ctrl->id, ctrl->value);
+
+       ext_ctrl[0].id = ctrl->id;
+       ext_ctrl[0].value = ctrl->value;
+
+       ext_ctrls.count = 1;
+       ext_ctrls.controls = ext_ctrl;
+
+       return imx_camera_module_s_ext_ctrls(sd, &ext_ctrls);
+}
+
+/* ======================================================================== */
+
+long imx_camera_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg)
+{
+       struct imx_camera_module *cam_mod =  to_imx_camera_module(sd);
+       int ret;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "cmd: 0x%x\n", cmd);
+
+       if (cmd == RK_VIDIOC_SENSOR_MODE_DATA) {
+               struct imx_camera_module_timings imx_timings;
+               struct isp_supplemental_sensor_mode_data *timings =
+               (struct isp_supplemental_sensor_mode_data *)arg;
+
+               if (cam_mod->custom.g_timings)
+                       ret = cam_mod->custom.g_timings(cam_mod, &imx_timings);
+               else
+                       ret = -EPERM;
+
+               if (IS_ERR_VALUE(ret)) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "failed with error %d\n", ret);
+                       return ret;
+               }
+
+               timings->sensor_output_width =
+               imx_timings.sensor_output_width;
+               timings->sensor_output_height =
+               imx_timings.sensor_output_height;
+               timings->crop_horizontal_start =
+                       imx_timings.crop_horizontal_start;
+               timings->crop_vertical_start =
+               imx_timings.crop_vertical_start;
+               timings->crop_horizontal_end =
+               imx_timings.crop_horizontal_end;
+               timings->crop_vertical_end = imx_timings.crop_vertical_end;
+               timings->line_length_pck = imx_timings.line_length_pck;
+               timings->frame_length_lines = imx_timings.frame_length_lines;
+               timings->vt_pix_clk_freq_hz = imx_timings.vt_pix_clk_freq_hz;
+               timings->binning_factor_x = imx_timings.binning_factor_x;
+               timings->binning_factor_y = imx_timings.binning_factor_y;
+               timings->coarse_integration_time_max_margin =
+                       imx_timings.coarse_integration_time_max_margin;
+               timings->coarse_integration_time_min =
+                       imx_timings.coarse_integration_time_min;
+               timings->fine_integration_time_max_margin =
+                       imx_timings.fine_integration_time_max_margin;
+               timings->fine_integration_time_min =
+                       imx_timings.fine_integration_time_min;
+
+               if (cam_mod->custom.g_exposure_valid_frame)
+                       timings->exposure_valid_frame =
+                               cam_mod->custom.g_exposure_valid_frame(cam_mod);
+               timings->exp_time = cam_mod->exp_config.exp_time;
+               timings->gain = cam_mod->exp_config.gain;
+
+               if (cam_mod->exp_config.exp_time)
+                       timings->exp_time = cam_mod->exp_config.exp_time;
+               else
+                       timings->exp_time = imx_timings.exp_time;
+
+               if (cam_mod->exp_config.gain)
+                       timings->gain = cam_mod->exp_config.gain;
+               else
+                       timings->gain = imx_timings.gain;
+               return ret;
+       } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) {
+               struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg;
+               struct imx_camera_module_config *config;
+
+               if (cam_mod->custom.num_configs <= 0) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "cam_mod->custom.num_configs is NULL, Get interface config failed!\n");
+                       return -EINVAL;
+               }
+
+               if (IS_ERR_OR_NULL(cam_mod->active_config))
+                       config = &cam_mod->custom.configs[0];
+               else
+                       config = cam_mod->active_config;
+
+               *itf_cfg = config->itf_cfg;
+
+               pltfrm_camera_module_ioctl(sd, PLTFRM_CIFCAM_G_ITF_CFG, arg);
+               return 0;
+       } else if (cmd == PLTFRM_CIFCAM_ATTACH) {
+               imx_camera_module_init(cam_mod, &cam_mod->custom);
+               pltfrm_camera_module_ioctl(sd, cmd, arg);
+               return imx_camera_module_attach(cam_mod);
+       }
+
+       ret = pltfrm_camera_module_ioctl(sd, cmd, arg);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_get_flip_mirror(
+       struct imx_camera_module *cam_mod)
+{
+       return pltfrm_camera_module_get_flip_mirror(&cam_mod->sd);
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_enum_frameintervals(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_frame_interval_enum *fie)
+{
+       struct imx_camera_module *cam_mod =  to_imx_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", fie->index);
+
+       if (fie->index >= cam_mod->custom.num_configs)
+               return -EINVAL;
+       fie->code =
+               cam_mod->custom.configs[fie->index].frm_fmt.code;
+       fie->width =
+               cam_mod->custom.configs[fie->index].frm_fmt.width;
+       fie->height =
+               cam_mod->custom.configs[fie->index].frm_fmt.height;
+       fie->interval.numerator = cam_mod->custom.
+               configs[fie->index].frm_intrvl.interval.numerator;
+       fie->interval.denominator = cam_mod->custom.
+               configs[fie->index].frm_intrvl.interval.denominator;
+       return 0;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_write_reglist(
+       struct imx_camera_module *cam_mod,
+       const struct imx_camera_module_reg reglist[],
+       int len)
+{
+       return pltfrm_camera_module_write_reglist(&cam_mod->sd, reglist, len);
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_write_reg(
+       struct imx_camera_module *cam_mod,
+       u16 reg,
+       u8 val)
+{
+       return pltfrm_camera_module_write_reg(&cam_mod->sd, reg, val);
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_read_reg(
+       struct imx_camera_module *cam_mod,
+       u16 data_length,
+       u16 reg,
+       u32 *val)
+{
+       return pltfrm_camera_module_read_reg(&cam_mod->sd,
+               data_length, reg, val);
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_read_reg_table(
+       struct imx_camera_module *cam_mod,
+       u16 reg,
+       u32 *val)
+{
+       int i;
+
+       if (cam_mod->state == IMX_CAMERA_MODULE_STREAMING)
+               return pltfrm_camera_module_read_reg(&cam_mod->sd,
+                       1, reg, val);
+
+       if (!IS_ERR_OR_NULL(cam_mod->active_config)) {
+               for (
+                       i = cam_mod->active_config->reg_table_num_entries - 1;
+                       i > 0;
+                       i--) {
+                       if (cam_mod->active_config->reg_table[i].reg == reg) {
+                               *val = cam_mod->active_config->reg_table[i].val;
+                               return 0;
+                       }
+               }
+       }
+
+       if (cam_mod->state == IMX_CAMERA_MODULE_SW_STANDBY)
+               return pltfrm_camera_module_read_reg(&cam_mod->sd,
+                       1, reg, val);
+
+       return -EFAULT;
+}
+
+/* ======================================================================== */
+
+int imx_camera_module_init(struct imx_camera_module *cam_mod,
+       struct imx_camera_module_custom_config *custom)
+{
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       imx_camera_module_reset(cam_mod);
+
+       if (IS_ERR_OR_NULL(custom->start_streaming) ||
+               IS_ERR_OR_NULL(custom->stop_streaming) ||
+               IS_ERR_OR_NULL(custom->s_ctrl) ||
+               IS_ERR_OR_NULL(custom->g_ctrl)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "mandatory callback function is missing\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = pltfrm_camera_module_init(&cam_mod->sd, &cam_mod->pltfm_data);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_PD,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+       ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_RESET,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+       if (IS_ERR_VALUE(ret)) {
+               imx_camera_module_release(cam_mod);
+               goto err;
+       }
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+void imx_camera_module_release(struct imx_camera_module *cam_mod)
+{
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       cam_mod->custom.configs = NULL;
+
+       pltfrm_camera_module_release(&cam_mod->sd);
+       v4l2_device_unregister_subdev(&cam_mod->sd);
+}
diff --git a/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.h b/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.h
new file mode 100644 (file)
index 0000000..787beec
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * imx_camera_module.h
+ *
+ * Generic sony sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#ifndef IMX_CAMERA_MODULE_H
+#define IMX_CAMERA_MODULE_H
+#include <linux/workqueue.h>
+#include <linux/platform_data/rk_isp10_platform_camera_module.h>
+#include <linux/platform_data/rk_isp10_platform.h>
+/*
+ * TODO: references to v4l2 should be reomved from here and go into a
+ * platform dependent wrapper
+ */
+
+#define IMX_CAMERA_MODULE_REG_TYPE_DATA PLTFRM_CAMERA_MODULE_REG_TYPE_DATA
+#define IMX_CAMERA_MODULE_REG_TYPE_TIMEOUT PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT
+#define imx_camera_module_csi_config
+#define imx_camera_module_reg pltfrm_camera_module_reg
+
+#define IMX_FLIP_BIT_MASK 0x2
+#define IMX_MIRROR_BIT_MASK 0x1
+
+#define IMX_CAMERA_MODULE_CTRL_UPDT_GAIN 0x01
+#define IMX_CAMERA_MODULE_CTRL_UPDT_EXP_TIME 0x02
+#define IMX_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE 0x04
+#define IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_WB 0x08
+#define IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN 0x10
+#define IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP 0x20
+#define IMX_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE 0x40
+#define IMX_CAMERA_MODULE_CTRL_UPDT_PRESET_WB 0x80
+
+enum imx_camera_module_state {
+       IMX_CAMERA_MODULE_POWER_OFF = 0,
+       IMX_CAMERA_MODULE_HW_STANDBY = 1,
+       IMX_CAMERA_MODULE_SW_STANDBY = 2,
+       IMX_CAMERA_MODULE_STREAMING = 3
+};
+
+struct imx_camera_module;
+
+struct imx_camera_module_timings {
+       u32 coarse_integration_time_min;
+       u32 coarse_integration_time_max_margin;
+       u32 fine_integration_time_min;
+       u32 fine_integration_time_max_margin;
+       u32 frame_length_lines;
+       u32 line_length_pck;
+       u32 vt_pix_clk_freq_hz;
+       u32 sensor_output_width;
+       u32 sensor_output_height;
+       u32 crop_horizontal_start;
+       u32 crop_vertical_start;
+       u32 crop_horizontal_end;
+       u32 crop_vertical_end;
+       u8 binning_factor_x;
+       u8 binning_factor_y;
+       u32 exp_time;
+       u32 gain;
+};
+
+struct imx_camera_module_config {
+       const char *name;
+       struct v4l2_mbus_framefmt frm_fmt;
+       struct v4l2_subdev_frame_interval frm_intrvl;
+       bool auto_exp_enabled;
+       bool auto_gain_enabled;
+       bool auto_wb_enabled;
+       struct imx_camera_module_reg *reg_table;
+       u32 reg_table_num_entries;
+       u32 reg_diff_table_num_entries;
+       u32 v_blanking_time_us;
+       u32 line_length_pck;
+       u32 frame_length_lines;
+       struct imx_camera_module_timings timings;
+       bool soft_reset;
+       bool ignore_measurement_check;
+       struct pltfrm_cam_itf itf_cfg;
+};
+
+struct imx_camera_module_exp_config {
+       u32 exp_time;
+       bool auto_exp;
+       u16 gain;
+       u16 gain_percent;
+       bool auto_gain;
+       enum v4l2_flash_led_mode flash_mode;
+};
+
+struct imx_camera_module_wb_config {
+       u32 temperature;
+       u32 preset_id;
+       bool auto_wb;
+};
+
+struct imx_camera_module_af_config {
+       u32 abs_pos;
+       u32 rel_pos;
+};
+
+struct imx_camera_module_ext_ctrl {
+       /* public */
+       u32 id;
+       u32 value;
+       __u32 reserved2[1];
+};
+
+struct imx_camera_module_ext_ctrls {
+       /* public */
+       u32 count;
+       struct imx_camera_module_ext_ctrl *ctrls;
+};
+
+/*
+ * start_streaming: (mandatory) will be called when sensor should be
+ * put into streaming mode right after the base config has been
+ * written to the sensor. After a successful call of this function
+ * the sensor should start delivering frame data.
+ *
+ * stop_streaming: (mandatory) will be called when sensor should stop
+ * delivering data. After a successful call of this function the
+ * sensor should not deliver any more frame data.
+ *
+ * check_camera_id: (optional) will be called when the sensor is
+ * powered on. If provided should check the sensor ID/version
+ * required by the custom driver. Register access should be
+ * possible when this function is invoked.
+ *
+ * s_ctrl: (mandatory) will be called at the successful end of
+ * imx_camera_module_s_ctrl with the ctrl_id as argument.
+ *
+ * priv: (optional) for private data used by the custom driver.
+ */
+struct imx_camera_module_custom_config {
+       int (*start_streaming)(struct imx_camera_module *cam_mod);
+       int (*stop_streaming)(struct imx_camera_module *cam_mod);
+       int (*check_camera_id)(struct imx_camera_module *cam_mod);
+       int (*s_ctrl)(struct imx_camera_module *cam_mod, u32 ctrl_id);
+       int (*g_ctrl)(struct imx_camera_module *cam_mod, u32 ctrl_id);
+       int (*g_timings)(struct imx_camera_module *cam_mod,
+               struct imx_camera_module_timings *timings);
+       int (*g_exposure_valid_frame)(struct imx_camera_module *cam_mod);
+       int (*s_ext_ctrls)(struct imx_camera_module *cam_mod,
+               struct imx_camera_module_ext_ctrls *ctrls);
+       int (*set_flip)(struct imx_camera_module *cam_mod);
+       int (*init_common)(struct imx_camera_module *cam_mod);
+       struct imx_camera_module_config *configs;
+       u32 num_configs;
+       u32 power_up_delays_ms[3];
+       void *priv;
+};
+
+struct imx_camera_module_otp_work {
+       struct work_struct work;
+       struct workqueue_struct *wq;
+       void *cam_mod;
+};
+
+struct imx_camera_module {
+       /* public */
+       struct v4l2_subdev sd;
+       struct v4l2_mbus_framefmt frm_fmt;
+       struct v4l2_subdev_frame_interval frm_intrvl;
+       struct imx_camera_module_exp_config exp_config;
+       struct imx_camera_module_wb_config wb_config;
+       struct imx_camera_module_af_config af_config;
+       struct imx_camera_module_custom_config custom;
+       enum imx_camera_module_state state;
+       enum imx_camera_module_state state_before_suspend;
+       struct imx_camera_module_config *active_config;
+       struct imx_camera_module_otp_work otp_work;
+       u32 ctrl_updt;
+       u32 vts_cur;
+       u32 vts_min;
+       bool auto_adjust_fps;
+       bool update_config;
+       bool frm_fmt_valid;
+       bool frm_intrvl_valid;
+       bool hflip;
+       bool vflip;
+       u32 rotation;
+       void *pltfm_data;
+       bool inited;
+};
+
+#define imx_camera_module_pr_info(cam_mod, fmt, arg...) \
+       pltfrm_camera_module_pr_info(&cam_mod->sd, fmt, ## arg)
+#define imx_camera_module_pr_debug(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_debug(&cam_mod->sd, fmt, ## arg)
+#define imx_camera_module_pr_warn(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_warn(&cam_mod->sd, fmt, ## arg)
+#define imx_camera_module_pr_err(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_err(&cam_mod->sd, fmt, ## arg)
+
+int imx_camera_module_write_reglist(
+       struct imx_camera_module *cam_mod,
+       const struct imx_camera_module_reg reglist[],
+       int len);
+
+int imx_camera_module_write_reg(
+       struct imx_camera_module *cam_mod,
+       u16 reg,
+       u8 val);
+
+int imx_camera_module_read_reg(
+       struct imx_camera_module *cam_mod,
+       u16 data_length,
+       u16 reg,
+       u32 *val);
+
+int imx_camera_module_read_reg_table(
+       struct imx_camera_module *cam_mod,
+       u16 reg,
+       u32 *val);
+
+int imx_camera_module_s_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format);
+
+int imx_camera_module_g_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format);
+
+int imx_camera_module_s_frame_interval(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_frame_interval *interval);
+
+int imx_camera_module_s_stream(
+       struct v4l2_subdev *sd,
+       int enable);
+
+int imx_camera_module_s_power(
+       struct v4l2_subdev *sd,
+       int on);
+
+int imx_camera_module_g_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl);
+
+int imx_camera_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl);
+
+int imx_camera_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls);
+
+int imx_camera_module_enum_frameintervals(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_frame_interval_enum *fie);
+
+int imx_camera_module_init(
+       struct imx_camera_module *cam_mod,
+       struct imx_camera_module_custom_config *custom);
+
+void imx_camera_module_release(
+       struct imx_camera_module *cam_mod);
+
+long imx_camera_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg);
+
+int imx_camera_module_get_flip_mirror(
+       struct imx_camera_module *cam_mod);
+#endif
diff --git a/drivers/media/i2c/soc_camera/rockchip/ov2710_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/ov2710_v4l2-i2c-subdev.c
new file mode 100644 (file)
index 0000000..c749fce
--- /dev/null
@@ -0,0 +1,915 @@
+/*
+ * drivers/media/i2c/soc_camera/xgold/ov2710.c
+ *
+ * ov2710 sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * Note:
+ *
+ * v0.1.0:
+ *     1. Initialize version;
+ *     2. Stream on sensor in configuration,
+ *     and stream off sensor after 1frame;
+ *     3. Stream delay time is define in power_up_delays_ms[2];
+ */
+
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf-core.h>
+#include <linux/slab.h>
+#include <media/v4l2-controls_rockchip.h>
+#include "ov_camera_module.h"
+
+#define OV2710_DRIVER_NAME "ov2710"
+
+#define OV2710_FETCH_LSB_GAIN(VAL) (VAL & 0x00FF)       /* gain[7:0] */
+#define OV2710_FETCH_MSB_GAIN(VAL) ((VAL >> 8) & 0x1)  /* gain[10:8] */
+#define OV2710_AEC_PK_LONG_GAIN_HIGH_REG 0x350a        /* Bit 8 */
+#define OV2710_AEC_PK_LONG_GAIN_LOW_REG         0x350b /* Bits 0 -7 */
+
+#define OV2710_AEC_PK_LONG_EXPO_3RD_REG 0x3500 /* Exposure Bits 16-19 */
+#define OV2710_AEC_PK_LONG_EXPO_2ND_REG 0x3501 /* Exposure Bits 8-15 */
+#define OV2710_AEC_PK_LONG_EXPO_1ST_REG 0x3502 /* Exposure Bits 0-7 */
+
+#define OV2710_AEC_GROUP_UPDATE_ADDRESS 0x3212
+#define OV2710_AEC_GROUP_UPDATE_START_DATA 0x00
+#define OV2710_AEC_GROUP_UPDATE_END_DATA 0x10
+#define OV2710_AEC_GROUP_UPDATE_END_LAUNCH 0xA0
+
+#define OV2710_FETCH_3RD_BYTE_EXP(VAL) (((VAL) >> 12) & 0xF)   /* 4 Bits */
+#define OV2710_FETCH_2ND_BYTE_EXP(VAL) (((VAL) >> 4) & 0xFF)   /* 8 Bits */
+#define OV2710_FETCH_1ST_BYTE_EXP(VAL) (((VAL) & 0x0F) << 4)   /* 4 Bits */
+
+#define OV2710_PIDH_ADDR     0x300A
+#define OV2710_PIDL_ADDR     0x300B
+
+/* High byte of product ID */
+#define OV2710_PIDH_MAGIC 0x27
+/* Low byte of product ID  */
+#define OV2710_PIDL_MAGIC 0x10
+
+#define OV2710_EXT_CLK 24000000
+#define OV2710_PLL_PREDIV0_REG 0x3088
+#define OV2710_PLL_PREDIV_REG  0x3080
+#define OV2710_PLL_MUL_HIGH_REG 0x3081
+#define OV2710_PLL_MUL_LOW_REG 0x3082
+#define OV2710_PLL_SPDIV_REG 0x3086
+#define OV2710_PLL_DIVSYS_REG 0x3084
+#define OV2710_TIMING_VTS_HIGH_REG 0x380e
+#define OV2710_TIMING_VTS_LOW_REG 0x380f
+#define OV2710_TIMING_HTS_HIGH_REG 0x380c
+#define OV2710_TIMING_HTS_LOW_REG 0x380d
+#define OV2710_FINE_INTG_TIME_MIN 0
+#define OV2710_FINE_INTG_TIME_MAX_MARGIN 0
+#define OV2710_COARSE_INTG_TIME_MIN 1
+#define OV2710_COARSE_INTG_TIME_MAX_MARGIN 4
+#define OV2710_TIMING_X_INC            0x3814
+#define OV2710_TIMING_Y_INC            0x3815
+#define OV2710_HORIZONTAL_START_HIGH_REG 0x3800
+#define OV2710_HORIZONTAL_START_LOW_REG 0x3801
+#define OV2710_VERTICAL_START_HIGH_REG 0x3802
+#define OV2710_VERTICAL_START_LOW_REG 0x3803
+#define OV2710_HORIZONTAL_END_HIGH_REG 0x3804
+#define OV2710_HORIZONTAL_END_LOW_REG 0x3805
+#define OV2710_VERTICAL_END_HIGH_REG 0x3806
+#define OV2710_VERTICAL_END_LOW_REG 0x3807
+#define OV2710_HORIZONTAL_OUTPUT_SIZE_HIGH_REG 0x3808
+#define OV2710_HORIZONTAL_OUTPUT_SIZE_LOW_REG 0x3809
+#define OV2710_VERTICAL_OUTPUT_SIZE_HIGH_REG 0x380a
+#define OV2710_VERTICAL_OUTPUT_SIZE_LOW_REG 0x380b
+#define OV2710_H_WIN_OFF_HIGH_REG 0x3810
+#define OV2710_H_WIN_OFF_LOW_REG 0x3811
+#define OV2710_V_WIN_OFF_HIGH_REG 0x3812
+#define OV2710_V_WIN_OFF_LOW_REG 0x3813
+
+#define OV2710_ANA_ARRAR01 0x3621
+#define OV2710_TIMING_CONCTROL_VH 0x3803
+#define OV2710_TIMING_CONCTROL18 0x3818
+
+/* ======================================================================== */
+/* Base sensor configs */
+/* ======================================================================== */
+/* MCLK:24MHz  1920x1080  30fps   mipi 1lane   800Mbps/lane */
+static struct ov_camera_module_reg ov2710_init_tab_1920_1080_30fps[] = {
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3008, 0x82},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3008, 0x42},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4201, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4202, 0x0f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3103, 0x93},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3017, 0x7f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0xfc},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x61},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3712, 0x0c},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3630, 0x6d},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0xb4},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x0a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3818, 0x80},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x07},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0x80},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0x38},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x07},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x80},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0x38},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x06},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3812, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3621, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x60},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0xa7},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3631, 0x26},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3600, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3620, 0x37},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3623, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3702, 0x9e},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x5c},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3704, 0x40},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370d, 0x0f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3713, 0x9f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3714, 0x4c},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3710, 0x9e},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0xc4},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x05},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x3f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x302d, 0x90},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0x40},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3716, 0x31},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3707, 0x52},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x74},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5181, 0x20},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x518f, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4301, 0xff},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4303, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a00, 0x78},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x300f, 0x88},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3011, 0x28},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a1a, 0x06},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a18, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a19, 0x7a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a13, 0x54},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382e, 0x0f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x381a, 0x1a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x02},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5688, 0x03},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5684, 0x07},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5685, 0xa0},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5686, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5687, 0x43},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3011, 0x0a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x300f, 0x8a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3017, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x300e, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4801, 0x0f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x300f, 0xc3},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a0f, 0x40},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a10, 0x38},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a1b, 0x48},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a1e, 0x30},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a11, 0x90},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a1f, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x09},/* HTS H */
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x74},/* HTS L */
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04},/* VTS H */
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0x50},/* VTS L */
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x28},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x90},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x07},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350a, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350b, 0x1f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0x5f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x4e},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3406, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3400, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3401, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3402, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3403, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3404, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3405, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4800, 0x24},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4201, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4202, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3008, 0x02},
+{OV_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 40},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3008, 0x42},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4201, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4202, 0x0f}
+};
+
+/* ======================================================================== */
+static struct ov_camera_module_config ov2710_configs[] = {
+       {
+               .name = "1920x1080_30fps",
+               .frm_fmt = {
+                       .width = 1920,
+                       .height = 1080,
+                       .code = MEDIA_BUS_FMT_SBGGR10_1X10
+               },
+               .frm_intrvl = {
+                       .interval = {
+                               .numerator = 1,
+                               .denominator = 30
+                       }
+               },
+               .auto_exp_enabled = false,
+               .auto_gain_enabled = false,
+               .auto_wb_enabled = false,
+               .reg_table = (void *)ov2710_init_tab_1920_1080_30fps,
+               .reg_table_num_entries =
+                       sizeof(ov2710_init_tab_1920_1080_30fps) /
+                       sizeof(ov2710_init_tab_1920_1080_30fps[0]),
+               .v_blanking_time_us = 3078,
+               .ignore_measurement_check = 1,
+               PLTFRM_CAM_ITF_MIPI_CFG(0, 1, 800, 24000000)
+       }
+};
+
+/*--------------------------------------------------------------------------*/
+static int ov2710_set_flip(
+       struct ov_camera_module *cam_mod,
+       struct pltfrm_camera_module_reg reglist[],
+       int len)
+{
+       int i, mode = 0;
+       u16 match_reg[3];
+
+       mode = ov_camera_module_get_flip_mirror(cam_mod);
+
+       if (mode == -1) {
+               ov_camera_module_pr_debug(
+                       cam_mod,
+                       "dts don't set flip, return!\n");
+               return 0;
+       }
+
+       if (mode == OV_FLIP_BIT_MASK) {
+               match_reg[0] = 0x04;
+               match_reg[1] = 0x09;
+               match_reg[2] = 0xa0;
+       } else if (mode == OV_MIRROR_BIT_MASK) {
+               match_reg[0] = 0x14;
+               match_reg[1] = 0x0a;
+               match_reg[2] = 0xc0;
+       } else if (mode == (OV_MIRROR_BIT_MASK |
+               OV_FLIP_BIT_MASK)) {
+               match_reg[0] = 0x14;
+               match_reg[1] = 0x09;
+               match_reg[2] = 0xe0;
+       } else {
+               match_reg[0] = 0x04;
+               match_reg[1] = 0x0a;
+               match_reg[2] = 0x80;
+       }
+
+       for (i = len; i > 0; i--) {
+               if (reglist[i].reg == OV2710_ANA_ARRAR01)
+                       reglist[i].val = match_reg[0];
+               else if (reglist[i].reg == OV2710_TIMING_CONCTROL_VH)
+                       reglist[i].val = match_reg[1];
+               else if (reglist[i].reg == OV2710_TIMING_CONCTROL18)
+                       reglist[i].val = match_reg[2];
+       }
+
+       return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+static int OV2710_g_VTS(struct ov_camera_module *cam_mod, u32 *vts)
+{
+       u32 msb, lsb;
+       int ret;
+
+       ret = ov_camera_module_read_reg_table(
+               cam_mod,
+               OV2710_TIMING_VTS_HIGH_REG,
+               &msb);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       ret = ov_camera_module_read_reg_table(
+               cam_mod,
+               OV2710_TIMING_VTS_LOW_REG,
+               &lsb);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       *vts = (msb << 8) | lsb;
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+static int OV2710_auto_adjust_fps(struct ov_camera_module *cam_mod,
+       u32 exp_time)
+{
+       int ret;
+       u32 vts;
+
+       if ((cam_mod->exp_config.exp_time + OV2710_COARSE_INTG_TIME_MAX_MARGIN)
+               > cam_mod->vts_min)
+               vts = cam_mod->exp_config.exp_time +
+                       OV2710_COARSE_INTG_TIME_MAX_MARGIN;
+       else
+               vts = cam_mod->vts_min;
+
+       /*
+        * if (cam_mod->fps_ctrl > 0 && cam_mod->fps_ctrl < 100)
+        * vts = vts * 100 / cam_mod->fps_ctrl;
+        */
+
+       if (vts > 0xfff)
+               vts = 0xfff;
+       else
+               vts = vts;/* VTS value is 0x380e[3:0]/380f[7:0] */
+
+       ret = ov_camera_module_write_reg(cam_mod,
+               OV2710_TIMING_VTS_LOW_REG,
+               vts & 0xFF);
+       ret |= ov_camera_module_write_reg(cam_mod,
+               OV2710_TIMING_VTS_HIGH_REG,
+               (vts >> 8) & 0x0F);
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_err(cam_mod,
+                               "failed with error (%d)\n", ret);
+       else
+               ov_camera_module_pr_debug(cam_mod,
+                       "updated vts = 0x%x,vts_min=0x%x\n",
+                       vts, cam_mod->vts_min);
+
+       return ret;
+}
+
+static int ov2710_write_aec(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod,
+               "exp_time = %d lines, gain = %d, flash_mode = %d\n",
+               cam_mod->exp_config.exp_time,
+               cam_mod->exp_config.gain,
+               cam_mod->exp_config.flash_mode);
+
+       /*
+        * if the sensor is already streaming, write to shadow registers,
+        * if the sensor is in SW standby, write to active registers,
+        * if the sensor is off/registers are not writeable, do nothing
+        */
+       if ((cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY) ||
+               (cam_mod->state == OV_CAMERA_MODULE_STREAMING)) {
+               u32 a_gain = cam_mod->exp_config.gain;
+               u32 exp_time = cam_mod->exp_config.exp_time;
+
+               a_gain = a_gain * cam_mod->exp_config.gain_percent / 100;
+
+               if (cam_mod->state == OV_CAMERA_MODULE_STREAMING)
+                       ret = ov_camera_module_write_reg(cam_mod,
+                               OV2710_AEC_GROUP_UPDATE_ADDRESS,
+                               OV2710_AEC_GROUP_UPDATE_START_DATA);
+               if (!IS_ERR_VALUE(ret) && cam_mod->auto_adjust_fps)
+                       ret = OV2710_auto_adjust_fps(cam_mod,
+                               cam_mod->exp_config.exp_time);
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       OV2710_AEC_PK_LONG_GAIN_HIGH_REG,
+                       OV2710_FETCH_MSB_GAIN(a_gain));
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       OV2710_AEC_PK_LONG_GAIN_LOW_REG,
+                       OV2710_FETCH_LSB_GAIN(a_gain));
+               ret = ov_camera_module_write_reg(cam_mod,
+                       OV2710_AEC_PK_LONG_EXPO_3RD_REG,
+                       OV2710_FETCH_3RD_BYTE_EXP(exp_time));
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       OV2710_AEC_PK_LONG_EXPO_2ND_REG,
+                       OV2710_FETCH_2ND_BYTE_EXP(exp_time));
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       OV2710_AEC_PK_LONG_EXPO_1ST_REG,
+                       OV2710_FETCH_1ST_BYTE_EXP(exp_time));
+               if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) {
+                       ret = ov_camera_module_write_reg(cam_mod,
+                               OV2710_AEC_GROUP_UPDATE_ADDRESS,
+                               OV2710_AEC_GROUP_UPDATE_END_DATA);
+                       ret = ov_camera_module_write_reg(cam_mod,
+                               OV2710_AEC_GROUP_UPDATE_ADDRESS,
+                               OV2710_AEC_GROUP_UPDATE_END_LAUNCH);
+               }
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+static int ov2710_g_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       switch (ctrl_id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_EXPOSURE:
+       case V4L2_CID_FLASH_LED_MODE:
+               /* nothing to be done here */
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_debug(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov2710_filltimings(struct ov_camera_module_custom_config *custom)
+{
+       int i, j;
+       u32 win_h_off = 0, win_v_off = 0;
+       struct ov_camera_module_config *config;
+       struct ov_camera_module_timings *timings;
+       struct ov_camera_module_reg *reg_table;
+       int reg_table_num_entries;
+
+       for (i = 0; i < custom->num_configs; i++) {
+               config = &custom->configs[i];
+               reg_table = config->reg_table;
+               reg_table_num_entries = config->reg_table_num_entries;
+               timings = &config->timings;
+
+               memset(timings, 0x00, sizeof(*timings));
+               for (j = 0; j < reg_table_num_entries; j++) {
+                       switch (reg_table[j].reg) {
+                       case OV2710_TIMING_VTS_HIGH_REG:
+                               timings->frame_length_lines =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->frame_length_lines & 0xff));
+                               break;
+                       case OV2710_TIMING_VTS_LOW_REG:
+                               timings->frame_length_lines =
+                                       (reg_table[j].val |
+                                       (timings->frame_length_lines & 0xff00));
+                               break;
+                       case OV2710_TIMING_HTS_HIGH_REG:
+                               timings->line_length_pck =
+                                       ((reg_table[j].val << 8) |
+                                       timings->line_length_pck);
+                               break;
+                       case OV2710_TIMING_HTS_LOW_REG:
+                               timings->line_length_pck =
+                                       (reg_table[j].val |
+                                       (timings->line_length_pck & 0xff00));
+                               break;
+                       case OV2710_TIMING_X_INC:
+                               timings->binning_factor_x =
+                                       ((reg_table[j].val >> 4) + 1) / 2;
+                               if (timings->binning_factor_x == 0)
+                                       timings->binning_factor_x = 1;
+                               break;
+                       case OV2710_TIMING_Y_INC:
+                               timings->binning_factor_y =
+                                       ((reg_table[j].val >> 4) + 1) / 2;
+                               if (timings->binning_factor_y == 0)
+                                       timings->binning_factor_y = 1;
+                               break;
+                       case OV2710_HORIZONTAL_START_HIGH_REG:
+                               timings->crop_horizontal_start =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->crop_horizontal_start &
+                                       0xff));
+                               break;
+                       case OV2710_HORIZONTAL_START_LOW_REG:
+                               timings->crop_horizontal_start =
+                                       (reg_table[j].val |
+                                       (timings->crop_horizontal_start &
+                                       0xff00));
+                               break;
+                       case OV2710_VERTICAL_START_HIGH_REG:
+                               timings->crop_vertical_start =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->crop_vertical_start & 0xff));
+                               break;
+                       case OV2710_VERTICAL_START_LOW_REG:
+                               timings->crop_vertical_start =
+                                       ((reg_table[j].val) |
+                                       (timings->crop_vertical_start &
+                                       0xff00));
+                               break;
+                       case OV2710_HORIZONTAL_END_HIGH_REG:
+                               timings->crop_horizontal_end =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->crop_horizontal_end & 0xff));
+                               break;
+                       case OV2710_HORIZONTAL_END_LOW_REG:
+                               timings->crop_horizontal_end =
+                                       (reg_table[j].val |
+                                       (timings->crop_horizontal_end &
+                                       0xff00));
+                               break;
+                       case OV2710_VERTICAL_END_HIGH_REG:
+                               timings->crop_vertical_end =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->crop_vertical_end & 0xff));
+                               break;
+                       case OV2710_VERTICAL_END_LOW_REG:
+                               timings->crop_vertical_end =
+                                       (reg_table[j].val |
+                                       (timings->crop_vertical_end & 0xff00));
+                               break;
+                       case OV2710_H_WIN_OFF_HIGH_REG:
+                               win_h_off = (reg_table[j].val & 0xf) << 8;
+                               break;
+                       case OV2710_H_WIN_OFF_LOW_REG:
+                               win_h_off |= (reg_table[j].val & 0xff);
+                               break;
+                       case OV2710_V_WIN_OFF_HIGH_REG:
+                               win_v_off = (reg_table[j].val & 0xf) << 8;
+                               break;
+                       case OV2710_V_WIN_OFF_LOW_REG:
+                               win_v_off |= (reg_table[j].val & 0xff);
+                               break;
+                       case OV2710_HORIZONTAL_OUTPUT_SIZE_HIGH_REG:
+                               timings->sensor_output_width =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->sensor_output_width & 0xff));
+                               break;
+                       case OV2710_HORIZONTAL_OUTPUT_SIZE_LOW_REG:
+                               timings->sensor_output_width =
+                                       (reg_table[j].val |
+                                       (timings->sensor_output_width &
+                                       0xff00));
+                               break;
+                       case OV2710_VERTICAL_OUTPUT_SIZE_HIGH_REG:
+                               timings->sensor_output_height =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->sensor_output_height & 0xff));
+                               break;
+                       case OV2710_VERTICAL_OUTPUT_SIZE_LOW_REG:
+                               timings->sensor_output_height =
+                                       (reg_table[j].val |
+                                       (timings->sensor_output_height &
+                                       0xff00));
+                               break;
+                       }
+               }
+
+               timings->crop_horizontal_start += win_h_off;
+               timings->crop_horizontal_end -= win_h_off;
+               timings->crop_vertical_start += win_v_off;
+               timings->crop_vertical_end -= win_v_off;
+
+               timings->exp_time >>= 4;
+               timings->vt_pix_clk_freq_hz =
+                       config->frm_intrvl.interval.denominator *
+                       timings->frame_length_lines *
+                       timings->line_length_pck;
+
+               timings->coarse_integration_time_min =
+                       OV2710_COARSE_INTG_TIME_MIN;
+               timings->coarse_integration_time_max_margin =
+                       OV2710_COARSE_INTG_TIME_MAX_MARGIN;
+
+               /* OV Sensor do not use fine integration time. */
+               timings->fine_integration_time_min =
+                       OV2710_FINE_INTG_TIME_MIN;
+               timings->fine_integration_time_max_margin =
+                       OV2710_FINE_INTG_TIME_MAX_MARGIN;
+       }
+
+       return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov2710_g_timings(struct ov_camera_module *cam_mod,
+                           struct ov_camera_module_timings *timings)
+{
+       int ret = 0;
+       unsigned int vts;
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config))
+               goto err;
+
+       *timings = cam_mod->active_config->timings;
+
+       vts = (!cam_mod->vts_cur) ?
+               timings->frame_length_lines :
+               cam_mod->vts_cur;
+       if (cam_mod->frm_intrvl_valid)
+               timings->vt_pix_clk_freq_hz =
+                       cam_mod->frm_intrvl.interval.denominator
+                       * vts
+                       * timings->line_length_pck;
+       else
+               timings->vt_pix_clk_freq_hz =
+               cam_mod->active_config->frm_intrvl.interval.denominator *
+               vts * timings->line_length_pck;
+
+       return ret;
+err:
+       ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov2710_s_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       switch (ctrl_id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_EXPOSURE:
+               ret = ov2710_write_aec(cam_mod);
+               break;
+       case V4L2_CID_FLASH_LED_MODE:
+               /* nothing to be done here */
+               break;
+       case V4L2_CID_FOCUS_ABSOLUTE:
+               /* todo*/
+               break;
+       /*
+        * case RK_V4L2_CID_FPS_CTRL:
+        * if (cam_mod->auto_adjust_fps)
+        * ret = OV2710_auto_adjust_fps(
+        * cam_mod,
+        * cam_mod->exp_config.exp_time);
+        * break;
+        */
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov2710_s_ext_ctrls(struct ov_camera_module *cam_mod,
+                                struct ov_camera_module_ext_ctrls *ctrls)
+{
+       int ret = 0;
+
+       /* Handles only exposure and gain together special case. */
+       if (ctrls->count == 1)
+               ret = ov2710_s_ctrl(cam_mod, ctrls->ctrls[0].id);
+       else if ((ctrls->count == 3) &&
+               (ctrls->ctrls[0].id == V4L2_CID_GAIN ||
+               ctrls->ctrls[0].id == V4L2_CID_EXPOSURE ||
+               ctrls->ctrls[1].id == V4L2_CID_GAIN ||
+               ctrls->ctrls[1].id == V4L2_CID_EXPOSURE))
+               ret = ov2710_write_aec(cam_mod);
+       else
+               ret = -EINVAL;
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_debug(cam_mod,
+                       "failed with error (%d)\n", ret);
+
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov2710_start_streaming(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod,
+               "active config=%s\n", cam_mod->active_config->name);
+
+       ret = OV2710_g_VTS(cam_mod, &cam_mod->vts_min);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+       ov_camera_module_pr_debug(cam_mod, "=====streaming on ===\n");
+       ret = ov_camera_module_write_reg(cam_mod, 0x3008, 0x02);
+       ret |= ov_camera_module_write_reg(cam_mod, 0x4201, 0x00);
+       ret |= ov_camera_module_write_reg(cam_mod, 0x4202, 0x00);
+
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n",
+               ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov2710_stop_streaming(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+       ret = ov_camera_module_write_reg(cam_mod, 0x3008, 0x42);
+       ret |= ov_camera_module_write_reg(cam_mod, 0x4201, 0x00);
+       ret |= ov_camera_module_write_reg(cam_mod, 0x4202, 0x0f);
+
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov2710_check_camera_id(struct ov_camera_module *cam_mod)
+{
+       u32 pidh, pidl;
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       ret |= ov_camera_module_read_reg(cam_mod, 1, OV2710_PIDH_ADDR, &pidh);
+       ret |= ov_camera_module_read_reg(cam_mod, 1, OV2710_PIDL_ADDR, &pidl);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "register read failed, camera module powered off?\n");
+               goto err;
+       }
+
+       if ((pidh == OV2710_PIDH_MAGIC) && (pidl == OV2710_PIDL_MAGIC))
+               ov_camera_module_pr_debug(cam_mod,
+                       "successfully detected camera ID 0x%02x%02x\n",
+                       pidh, pidl);
+       else {
+               ov_camera_module_pr_err(cam_mod,
+                       "wrong camera ID, expected 0x%02x%02x, detected 0x%02x%02x\n",
+                       OV2710_PIDH_MAGIC, OV2710_PIDL_MAGIC, pidh, pidl);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+int ov_camera_2710_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       return 0;
+}
+
+/* ======================================================================== */
+
+int ov_camera_2710_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls)
+{
+       return 0;
+}
+
+long ov_camera_2710_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg)
+{
+       return 0;
+}
+
+/* ======================================================================== */
+/* This part is platform dependent */
+/* ======================================================================== */
+
+static struct v4l2_subdev_core_ops ov2710_camera_module_core_ops = {
+       .g_ctrl = ov_camera_module_g_ctrl,
+       .s_ctrl = ov_camera_module_s_ctrl,
+       .s_ext_ctrls = ov_camera_module_s_ext_ctrls,
+       .s_power = ov_camera_module_s_power,
+       .ioctl = ov_camera_module_ioctl
+};
+
+static struct v4l2_subdev_video_ops ov2710_camera_module_video_ops = {
+       .s_frame_interval = ov_camera_module_s_frame_interval,
+       .s_stream = ov_camera_module_s_stream
+};
+
+static struct v4l2_subdev_pad_ops ov2710_camera_module_pad_ops = {
+       .enum_frame_interval = ov_camera_module_enum_frameintervals,
+       .get_fmt = ov_camera_module_g_fmt,
+       .set_fmt = ov_camera_module_s_fmt,
+};
+
+static struct v4l2_subdev_ops ov2710_camera_module_ops = {
+       .core = &ov2710_camera_module_core_ops,
+       .video = &ov2710_camera_module_video_ops,
+       .pad = &ov2710_camera_module_pad_ops
+};
+
+static struct ov_camera_module ov2710;
+
+static struct ov_camera_module_custom_config ov2710_custom_config = {
+       .start_streaming = ov2710_start_streaming,
+       .stop_streaming = ov2710_stop_streaming,
+       .s_ctrl = ov2710_s_ctrl,
+       .g_ctrl = ov2710_g_ctrl,
+       .s_ext_ctrls = ov2710_s_ext_ctrls,
+       .g_timings = ov2710_g_timings,
+       .set_flip = ov2710_set_flip,
+       .check_camera_id = ov2710_check_camera_id,
+       .configs = ov2710_configs,
+       .num_configs = ARRAY_SIZE(ov2710_configs),
+       .power_up_delays_ms = {5, 30, 30}
+};
+
+static int ov2710_probe(
+       struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       dev_info(&client->dev, "probing...\n");
+
+       ov2710_filltimings(&ov2710_custom_config);
+       v4l2_i2c_subdev_init(&ov2710.sd, client,
+                               &ov2710_camera_module_ops);
+
+       ov2710.custom = ov2710_custom_config;
+
+       dev_info(&client->dev, "probing successful\n");
+       return 0;
+}
+
+/* ======================================================================== */
+
+static int ov2710_remove(
+       struct i2c_client *client)
+{
+       struct ov_camera_module *cam_mod = i2c_get_clientdata(client);
+
+       dev_info(&client->dev, "removing device...\n");
+
+       if (!client->adapter)
+               return -ENODEV; /* our client isn't attached */
+
+       ov_camera_module_release(cam_mod);
+
+       dev_info(&client->dev, "removed\n");
+       return 0;
+}
+
+static const struct i2c_device_id ov2710_id[] = {
+       { OV2710_DRIVER_NAME, 0 },
+       { }
+};
+
+static const struct of_device_id ov2710_of_match[] = {
+       {.compatible = "omnivision,ov2710-v4l2-i2c-subdev"},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov2710_id);
+
+static struct i2c_driver ov2710_i2c_driver = {
+       .driver = {
+               .name = OV2710_DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = ov2710_of_match
+       },
+       .probe = ov2710_probe,
+       .remove = ov2710_remove,
+       .id_table = ov2710_id,
+};
+
+module_i2c_driver(ov2710_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for ov2710");
+MODULE_AUTHOR("Eike Grimpe");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/i2c/soc_camera/rockchip/ov4689_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/ov4689_v4l2-i2c-subdev.c
new file mode 100644 (file)
index 0000000..ad22b04
--- /dev/null
@@ -0,0 +1,949 @@
+/*
+ * ov4689 sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf-core.h>
+#include <linux/slab.h>
+#include "ov_camera_module.h"
+
+#define ov4689_DRIVER_NAME "ov4689"
+
+#define ov4689_AEC_PK_LONG_GAIN_HIGH_REG 0x3508        /* Bit 6-13 */
+#define ov4689_AEC_PK_LONG_GAIN_LOW_REG         0x3509 /* Bits 0 -5 */
+#define ov4689_FETCH_LSB_GAIN(VAL) ((VAL) & 0x00ff)
+#define ov4689_FETCH_MSB_GAIN(VAL) (((VAL) >> 8) & 0xff)
+
+#define ov4689_AEC_PK_LONG_EXPO_3RD_REG 0x3500 /* Exposure Bits 16-19 */
+#define ov4689_AEC_PK_LONG_EXPO_2ND_REG 0x3501 /* Exposure Bits 8-15 */
+#define ov4689_AEC_PK_LONG_EXPO_1ST_REG 0x3502 /* Exposure Bits 0-7 */
+#define ov4689_FETCH_3RD_BYTE_EXP(VAL) (((VAL) >> 12) & 0xF)   /* 4 Bits */
+#define ov4689_FETCH_2ND_BYTE_EXP(VAL) (((VAL) >> 4) & 0xFF)   /* 8 Bits */
+#define ov4689_FETCH_1ST_BYTE_EXP(VAL) (((VAL) & 0x0F) << 4)   /* 4 Bits */
+
+#define ov4689_AEC_GROUP_UPDATE_ADDRESS                0x3208
+#define ov4689_AEC_GROUP_UPDATE_START_DATA     0x00
+#define ov4689_AEC_GROUP_UPDATE_END_DATA       0x10
+#define ov4689_AEC_GROUP_UPDATE_END_LAUNCH     0xA0
+
+#define ov4689_PIDH_ADDR    0x300A
+#define ov4689_PIDL_ADDR       0x300B
+
+#define ov4689_PIDH_MAGIC      0x46
+#define ov4689_PIDL_MAGIC      0x88
+
+#define ov4689_TIMING_VTS_HIGH_REG 0x380e
+#define ov4689_TIMING_VTS_LOW_REG 0x380f
+#define ov4689_TIMING_HTS_HIGH_REG 0x380c
+#define ov4689_TIMING_HTS_LOW_REG 0x380d
+#define ov4689_INTEGRATION_TIME_MARGIN 8
+#define ov4689_FINE_INTG_TIME_MIN 0
+#define ov4689_FINE_INTG_TIME_MAX_MARGIN 0
+#define ov4689_COARSE_INTG_TIME_MIN 16
+#define ov4689_COARSE_INTG_TIME_MAX_MARGIN 4
+#define ov4689_TIMING_X_INC            0x3814
+#define ov4689_TIMING_Y_INC            0x3815
+#define ov4689_HORIZONTAL_START_HIGH_REG 0x3800
+#define ov4689_HORIZONTAL_START_LOW_REG 0x3801
+#define ov4689_VERTICAL_START_HIGH_REG 0x3802
+#define ov4689_VERTICAL_START_LOW_REG 0x3803
+#define ov4689_HORIZONTAL_END_HIGH_REG 0x3804
+#define ov4689_HORIZONTAL_END_LOW_REG 0x3805
+#define ov4689_VERTICAL_END_HIGH_REG 0x3806
+#define ov4689_VERTICAL_END_LOW_REG 0x3807
+#define ov4689_HORIZONTAL_OUTPUT_SIZE_HIGH_REG 0x3808
+#define ov4689_HORIZONTAL_OUTPUT_SIZE_LOW_REG 0x3809
+#define ov4689_VERTICAL_OUTPUT_SIZE_HIGH_REG 0x380a
+#define ov4689_VERTICAL_OUTPUT_SIZE_LOW_REG 0x380b
+#define ov4689_FLIP_REG                     0x3820
+#define ov4689_MIRROR_REG                                      0x3821
+
+#define ov4689_EXT_CLK 24000000
+
+#define ov4689_FULL_SIZE_RESOLUTION_WIDTH 2688
+#define ov4689_BINING_SIZE_RESOLUTION_WIDTH 1280
+
+static struct ov_camera_module ov4689;
+static struct ov_camera_module_custom_config ov4689_custom_config;
+
+/* ======================================================================== */
+/* Base sensor configs */
+/* ======================================================================== */
+
+/* MCLK:24MHz  2688x1520  30fps   mipi 4lane   1008Mbps/lane */
+static struct ov_camera_module_reg ov4689_init_tab_2688_1520_30fps[] = {
+/* global setting */
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0103, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0300, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0302, 0x2a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0303, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0304, 0x03},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030b, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030d, 0x1e},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x031e, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3000, 0x20},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3002, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x32},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3020, 0x93},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3021, 0x03},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3031, 0x0a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x303f, 0x0c},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3305, 0xf1},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3307, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3309, 0x29},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x60},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3504, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3505, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3506, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3507, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3509, 0x80},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350a, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350b, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350c, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350d, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350e, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350f, 0x80},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3510, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3511, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3512, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3513, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3514, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3515, 0x80},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3516, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3517, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3518, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3519, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351a, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351b, 0x80},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351c, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351d, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351e, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351f, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3520, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3521, 0x80},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3522, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3524, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3526, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3528, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x352a, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3602, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0x40},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x02},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3607, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3609, 0x12},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360a, 0x40},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360c, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360f, 0xe5},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3608, 0x8f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3611, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3613, 0xf7},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3616, 0x58},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3619, 0x99},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361b, 0x60},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361c, 0x7a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361e, 0x79},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361f, 0x02},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3632, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3633, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3634, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3635, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3636, 0x15},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3646, 0x86},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x364a, 0x0b},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3700, 0x17},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3701, 0x22},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x37},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3705, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x63},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3709, 0x3c},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370c, 0x30},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3710, 0x24},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3711, 0x0c},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3716, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3720, 0x28},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3729, 0x7b},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372a, 0x84},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372b, 0xbd},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372c, 0xbc},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372e, 0x52},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373c, 0x0e},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373e, 0x33},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3743, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3744, 0x88},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3745, 0xc0},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x374a, 0x43},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x374c, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x374e, 0x23},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3751, 0x7b},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3752, 0x84},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3753, 0xbd},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3754, 0xbc},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3756, 0x52},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375c, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3760, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3761, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3762, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3764, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3767, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3769, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376a, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376b, 0x20},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376c, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376d, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376e, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3773, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3774, 0x51},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3776, 0xbd},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3777, 0xbd }
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3781, 0x18},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3783, 0x25},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3798, 0x1b},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x0a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0x97},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x05},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0xfb},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x0a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x80},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x05},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xf0},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x0a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x18},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x06},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0x12},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3812, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3815, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3819, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3820, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x06},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3829, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382b, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3837, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3841, 0x02},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3846, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3847, 0x07},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d85, 0x36},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d8c, 0x71},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d8d, 0xcb},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4000, 0xf1},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x40},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4002, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4003, 0x14},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400e, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4011, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401a, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401b, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401c, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401f, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4020, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4021, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x07},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xcf},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x09},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x60},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x09},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x6f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4028, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4029, 0x02},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402a, 0x06},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402c, 0x02},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402d, 0x02},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x0e},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4302, 0xff},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4303, 0xff},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4304, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4305, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4306, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4308, 0x02},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x6c},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4501, 0xc4},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4502, 0x40},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4503, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xa7},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4800, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4813, 0x08},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x481f, 0x40},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4829, 0x78},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4837, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b00, 0x2a},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b0d, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d00, 0x04},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d01, 0x42},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d02, 0xd1},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d03, 0x93},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d04, 0xf5},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d05, 0xc1},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0xf3},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x11},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5004, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x500a, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x500b, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5032, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5040, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5050, 0x0c},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5500, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5501, 0x10},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5502, 0x01},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5503, 0x0f},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8000, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8001, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8002, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8003, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8004, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8005, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8006, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8007, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8008, 0x00},
+{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0x00},
+/* {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x01} */
+};
+
+static struct ov_camera_module_config ov4689_configs[] = {
+       {
+               .name = "2688x1520_30fps",
+               .frm_fmt = {
+                       .width = 2688,
+                       .height = 1520,
+                       .code = MEDIA_BUS_FMT_SBGGR10_1X10
+               },
+               .frm_intrvl = {
+                       .interval = {
+                               .numerator = 1,
+                               .denominator = 30
+                       }
+               },
+               .auto_exp_enabled = false,
+               .auto_gain_enabled = false,
+               .auto_wb_enabled = false,
+               .reg_table = (void *)ov4689_init_tab_2688_1520_30fps,
+               .reg_table_num_entries =
+                       sizeof(ov4689_init_tab_2688_1520_30fps) /
+                       sizeof(ov4689_init_tab_2688_1520_30fps[0]),
+               .v_blanking_time_us = 5000,
+               PLTFRM_CAM_ITF_MIPI_CFG(0, 2, 999, ov4689_EXT_CLK)
+       }
+};
+
+static int ov4689_g_VTS(struct ov_camera_module *cam_mod, u32 *vts)
+{
+       u32 msb, lsb;
+       int ret;
+
+       ret = ov_camera_module_read_reg_table(
+               cam_mod,
+               ov4689_TIMING_VTS_HIGH_REG,
+               &msb);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       ret = ov_camera_module_read_reg_table(
+               cam_mod,
+               ov4689_TIMING_VTS_LOW_REG,
+               &lsb);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       *vts = (msb << 8) | lsb;
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+static int ov4689_auto_adjust_fps(struct ov_camera_module *cam_mod,
+       u32 exp_time)
+{
+       int ret;
+       u32 vts;
+
+       if ((cam_mod->exp_config.exp_time +
+       ov4689_COARSE_INTG_TIME_MAX_MARGIN)
+       > cam_mod->vts_min)
+               vts = cam_mod->exp_config.exp_time +
+                       ov4689_COARSE_INTG_TIME_MAX_MARGIN;
+       else
+               vts = cam_mod->vts_min;
+       ret = ov_camera_module_write_reg(cam_mod,
+               ov4689_TIMING_VTS_LOW_REG, vts & 0xFF);
+       ret |= ov_camera_module_write_reg(cam_mod,
+               ov4689_TIMING_VTS_HIGH_REG,
+               (vts >> 8) & 0xFF);
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       else
+               ov_camera_module_pr_debug(cam_mod,
+                       "updated vts = %d,vts_min=%d\n",
+                       vts, cam_mod->vts_min);
+
+       return ret;
+}
+
+static int ov4689_write_aec(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod,
+                                 "exp_time = %d, gain = %d, flash_mode = %d\n",
+                                 cam_mod->exp_config.exp_time,
+                                 cam_mod->exp_config.gain,
+                                 cam_mod->exp_config.flash_mode);
+
+       /*
+        * if the sensor is already streaming, write to shadow registers,
+        * if the sensor is in SW standby, write to active registers,
+        * if the sensor is off/registers are not writeable, do nothing
+        */
+       if ((cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY) ||
+               (cam_mod->state == OV_CAMERA_MODULE_STREAMING)) {
+               u32 a_gain = cam_mod->exp_config.gain;
+               u32 exp_time = cam_mod->exp_config.exp_time;
+
+               a_gain = a_gain * cam_mod->exp_config.gain_percent / 100;
+
+               if (cam_mod->state == OV_CAMERA_MODULE_STREAMING)
+                       ret = ov_camera_module_write_reg(cam_mod,
+                       ov4689_AEC_GROUP_UPDATE_ADDRESS,
+                       ov4689_AEC_GROUP_UPDATE_START_DATA);
+               if (!IS_ERR_VALUE(ret) && cam_mod->auto_adjust_fps)
+                       ret = ov4689_auto_adjust_fps(cam_mod,
+                                               cam_mod->exp_config.exp_time);
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       ov4689_AEC_PK_LONG_GAIN_HIGH_REG,
+                       ov4689_FETCH_MSB_GAIN(a_gain));
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       ov4689_AEC_PK_LONG_GAIN_LOW_REG,
+                       ov4689_FETCH_LSB_GAIN(a_gain));
+               ret = ov_camera_module_write_reg(cam_mod,
+                       ov4689_AEC_PK_LONG_EXPO_3RD_REG,
+                       ov4689_FETCH_3RD_BYTE_EXP(exp_time));
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       ov4689_AEC_PK_LONG_EXPO_2ND_REG,
+                       ov4689_FETCH_2ND_BYTE_EXP(exp_time));
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       ov4689_AEC_PK_LONG_EXPO_1ST_REG,
+                       ov4689_FETCH_1ST_BYTE_EXP(exp_time));
+               if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) {
+                       ret = ov_camera_module_write_reg(cam_mod,
+                               ov4689_AEC_GROUP_UPDATE_ADDRESS,
+                               ov4689_AEC_GROUP_UPDATE_END_DATA);
+                       ret = ov_camera_module_write_reg(cam_mod,
+                               ov4689_AEC_GROUP_UPDATE_ADDRESS,
+                               ov4689_AEC_GROUP_UPDATE_END_LAUNCH);
+               }
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov4689_g_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       switch (ctrl_id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_EXPOSURE:
+       case V4L2_CID_FLASH_LED_MODE:
+               /* nothing to be done here */
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_debug(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+static int ov4689_filltimings(struct ov_camera_module_custom_config *custom)
+{
+       int i, j;
+       struct ov_camera_module_config *config;
+       struct ov_camera_module_timings *timings;
+       struct ov_camera_module_reg *reg_table;
+       int reg_table_num_entries;
+
+       for (i = 0; i < custom->num_configs; i++) {
+               config = &custom->configs[i];
+               reg_table = config->reg_table;
+               reg_table_num_entries = config->reg_table_num_entries;
+               timings = &config->timings;
+
+               for (j = 0; j < reg_table_num_entries; j++) {
+                       switch (reg_table[j].reg) {
+                       case ov4689_TIMING_VTS_HIGH_REG:
+                               timings->frame_length_lines =
+                                       reg_table[j].val << 8;
+                               break;
+                       case ov4689_TIMING_VTS_LOW_REG:
+                               timings->frame_length_lines |= reg_table[j].val;
+                               break;
+                       case ov4689_TIMING_HTS_HIGH_REG:
+                               timings->line_length_pck =
+                                       (reg_table[j].val << 8);
+                               break;
+                       case ov4689_TIMING_HTS_LOW_REG:
+                               timings->line_length_pck |= reg_table[j].val;
+                               break;
+                       case ov4689_TIMING_X_INC:
+                               timings->binning_factor_x =
+                                       ((reg_table[j].val >> 4) + 1) / 2;
+                               if (timings->binning_factor_x == 0)
+                                       timings->binning_factor_x = 1;
+                               break;
+                       case ov4689_TIMING_Y_INC:
+                               timings->binning_factor_y =
+                                       ((reg_table[j].val >> 4) + 1) / 2;
+                               if (timings->binning_factor_y == 0)
+                                       timings->binning_factor_y = 1;
+                               break;
+                       case ov4689_HORIZONTAL_START_HIGH_REG:
+                               timings->crop_horizontal_start =
+                                       reg_table[j].val << 8;
+                               break;
+                       case ov4689_HORIZONTAL_START_LOW_REG:
+                               timings->crop_horizontal_start |=
+                                       reg_table[j].val;
+                               break;
+                       case ov4689_VERTICAL_START_HIGH_REG:
+                               timings->crop_vertical_start =
+                                       reg_table[j].val << 8;
+                               break;
+                       case ov4689_VERTICAL_START_LOW_REG:
+                               timings->crop_vertical_start |=
+                                       reg_table[j].val;
+                               break;
+                       case ov4689_HORIZONTAL_END_HIGH_REG:
+                               timings->crop_horizontal_end =
+                                       reg_table[j].val << 8;
+                               break;
+                       case ov4689_HORIZONTAL_END_LOW_REG:
+                               timings->crop_horizontal_end |=
+                                       reg_table[j].val;
+                               break;
+                       case ov4689_VERTICAL_END_HIGH_REG:
+                               timings->crop_vertical_end =
+                                       reg_table[j].val << 8;
+                               break;
+                       case ov4689_VERTICAL_END_LOW_REG:
+                               timings->crop_vertical_end |= reg_table[j].val;
+                               break;
+                       case ov4689_HORIZONTAL_OUTPUT_SIZE_HIGH_REG:
+                               timings->sensor_output_width =
+                                       reg_table[j].val << 8;
+                               break;
+                       case ov4689_HORIZONTAL_OUTPUT_SIZE_LOW_REG:
+                               timings->sensor_output_width |=
+                                       reg_table[j].val;
+                               break;
+                       case ov4689_VERTICAL_OUTPUT_SIZE_HIGH_REG:
+                               timings->sensor_output_height =
+                                       reg_table[j].val << 8;
+                               break;
+                       case ov4689_VERTICAL_OUTPUT_SIZE_LOW_REG:
+                               timings->sensor_output_height |=
+                                       reg_table[j].val;
+                               break;
+                       }
+               }
+
+               timings->vt_pix_clk_freq_hz =
+                       config->frm_intrvl.interval.denominator *
+                               timings->frame_length_lines *
+                               timings->line_length_pck;
+
+               timings->coarse_integration_time_min =
+                       ov4689_COARSE_INTG_TIME_MIN;
+               timings->coarse_integration_time_max_margin =
+                       ov4689_COARSE_INTG_TIME_MAX_MARGIN;
+
+               /* OV Sensor do not use fine integration time. */
+               timings->fine_integration_time_min = ov4689_FINE_INTG_TIME_MIN;
+               timings->fine_integration_time_max_margin =
+                               ov4689_FINE_INTG_TIME_MAX_MARGIN;
+       }
+
+       return 0;
+}
+
+static int ov4689_g_timings(struct ov_camera_module *cam_mod,
+       struct ov_camera_module_timings *timings)
+{
+       int ret = 0;
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config))
+               goto err;
+
+       *timings = cam_mod->active_config->timings;
+
+       if (cam_mod->frm_intrvl_valid)
+               timings->vt_pix_clk_freq_hz =
+                       cam_mod->frm_intrvl.interval.denominator
+                       * timings->frame_length_lines
+                       * timings->line_length_pck;
+       else
+               timings->vt_pix_clk_freq_hz =
+                       cam_mod->active_config->frm_intrvl.interval.denominator
+                       * timings->frame_length_lines
+                       * timings->line_length_pck;
+
+       return ret;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov4689_set_flip(
+       struct ov_camera_module *cam_mod,
+       struct pltfrm_camera_module_reg reglist[],
+       int len)
+{
+       int i, mode = 0;
+       u16 match_reg[2];
+
+       mode = ov_camera_module_get_flip_mirror(cam_mod);
+       if (mode == -1) {
+               ov_camera_module_pr_info(cam_mod,
+                       "dts don't set flip, return!\n");
+               return 0;
+       }
+
+       if (!IS_ERR_OR_NULL(cam_mod->active_config)) {
+               switch (cam_mod->active_config->frm_fmt.width) {
+               case ov4689_FULL_SIZE_RESOLUTION_WIDTH:
+                       if (mode == OV_FLIP_BIT_MASK) {
+                               match_reg[0] = 0x06;
+                               match_reg[1] = 0x00;
+                       } else if (mode == OV_MIRROR_BIT_MASK) {
+                               match_reg[0] = 0x00;
+                               match_reg[1] = 0x06;
+                       } else if (mode == (OV_MIRROR_BIT_MASK |
+                               OV_FLIP_BIT_MASK)) {
+                               match_reg[0] = 0x06;
+                               match_reg[1] = 0x06;
+                       } else {
+                               match_reg[0] = 0x00;
+                               match_reg[1] = 0x00;
+                       }
+                       break;
+               case ov4689_BINING_SIZE_RESOLUTION_WIDTH:
+                       if (mode == OV_FLIP_BIT_MASK) {
+                               match_reg[0] = 0x16;
+                               match_reg[1] = 0x01;
+                       } else if (mode == OV_MIRROR_BIT_MASK) {
+                               match_reg[0] = 0x16;
+                               match_reg[1] = 0x07;
+                       } else if (mode == (OV_MIRROR_BIT_MASK |
+                               OV_FLIP_BIT_MASK)) {
+                               match_reg[0] = 0x16;
+                               match_reg[1] = 0x07;
+                       } else {
+                               match_reg[0] = 0x10;
+                               match_reg[1] = 0x01;
+                       }
+                       break;
+               default:
+                               return 0;
+               }
+
+               for (i = len; i > 0; i--) {
+                       if (reglist[i].reg == ov4689_FLIP_REG)
+                               reglist[i].val = match_reg[0];
+                       else if (reglist[i].reg == ov4689_MIRROR_REG)
+                               reglist[i].val = match_reg[1];
+               }
+       }
+
+       return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov4689_s_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       switch (ctrl_id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_EXPOSURE:
+               ret = ov4689_write_aec(cam_mod);
+               break;
+       case V4L2_CID_FLASH_LED_MODE:
+               /* nothing to be done here */
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_debug(cam_mod,
+                       "failed with error (%d) 0x%x\n", ret, ctrl_id);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov4689_s_ext_ctrls(struct ov_camera_module *cam_mod,
+       struct ov_camera_module_ext_ctrls *ctrls)
+{
+       int ret = 0;
+
+       /* Handles only exposure and gain together special case. */
+       if (ctrls->count == 1)
+               ret = ov4689_s_ctrl(cam_mod, ctrls->ctrls[0].id);
+       else if ((ctrls->count == 3) &&
+                ((ctrls->ctrls[0].id == V4L2_CID_GAIN &&
+                  ctrls->ctrls[1].id == V4L2_CID_EXPOSURE) ||
+                 (ctrls->ctrls[1].id == V4L2_CID_GAIN &&
+                  ctrls->ctrls[0].id == V4L2_CID_EXPOSURE)))
+               ret = ov4689_write_aec(cam_mod);
+       else
+               ret = -EINVAL;
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_debug(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov4689_start_streaming(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod,
+               "active config=%s\n", cam_mod->active_config->name);
+
+       ret = ov4689_g_VTS(cam_mod, &cam_mod->vts_min);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       if (IS_ERR_VALUE(ov_camera_module_write_reg(cam_mod, 0x0100, 1)))
+               goto err;
+
+       msleep(25);
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n",
+               ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov4689_stop_streaming(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       ret = ov_camera_module_write_reg(cam_mod, 0x0100, 0);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       msleep(25);
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+static int ov4689_check_camera_id(struct ov_camera_module *cam_mod)
+{
+       u32 pidh, pidl;
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       ret |= ov_camera_module_read_reg(cam_mod, 1, ov4689_PIDH_ADDR, &pidh);
+       ret |= ov_camera_module_read_reg(cam_mod, 1, ov4689_PIDL_ADDR, &pidl);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "register read failed, camera module powered off?\n");
+               goto err;
+       }
+
+       if ((pidh == ov4689_PIDH_MAGIC) && (pidl == ov4689_PIDL_MAGIC))
+               ov_camera_module_pr_debug(cam_mod,
+                       "successfully detected camera ID 0x%02x%02x\n",
+                       pidh, pidl);
+       else {
+               ov_camera_module_pr_err(cam_mod,
+                       "wrong camera ID, expected 0x%02x%02x, detected 0x%02x%02x\n",
+                       ov4689_PIDH_MAGIC, ov4689_PIDL_MAGIC, pidh, pidl);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+/* This part is platform dependent */
+/* ======================================================================== */
+
+static struct v4l2_subdev_core_ops ov4689_camera_module_core_ops = {
+       .g_ctrl = ov_camera_module_g_ctrl,
+       .s_ctrl = ov_camera_module_s_ctrl,
+       .s_ext_ctrls = ov_camera_module_s_ext_ctrls,
+       .s_power = ov_camera_module_s_power,
+       .ioctl = ov_camera_module_ioctl
+};
+
+static struct v4l2_subdev_video_ops ov4689_camera_module_video_ops = {
+       .s_frame_interval = ov_camera_module_s_frame_interval,
+       .s_stream = ov_camera_module_s_stream
+};
+
+static struct v4l2_subdev_pad_ops ov4689_camera_module_pad_ops = {
+       .enum_frame_interval = ov_camera_module_enum_frameintervals,
+       .get_fmt = ov_camera_module_g_fmt,
+       .set_fmt = ov_camera_module_s_fmt,
+};
+
+static struct v4l2_subdev_ops ov4689_camera_module_ops = {
+       .core = &ov4689_camera_module_core_ops,
+       .video = &ov4689_camera_module_video_ops,
+       .pad = &ov4689_camera_module_pad_ops
+};
+
+static struct ov_camera_module_custom_config ov4689_custom_config = {
+       .start_streaming = ov4689_start_streaming,
+       .stop_streaming = ov4689_stop_streaming,
+       .s_ctrl = ov4689_s_ctrl,
+       .s_ext_ctrls = ov4689_s_ext_ctrls,
+       .g_ctrl = ov4689_g_ctrl,
+       .g_timings = ov4689_g_timings,
+       .check_camera_id = ov4689_check_camera_id,
+       .set_flip = ov4689_set_flip,
+       .configs = ov4689_configs,
+       .num_configs = ARRAY_SIZE(ov4689_configs),
+       .power_up_delays_ms = {5, 20, 0}
+};
+
+static int ov4689_probe(
+       struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       dev_info(&client->dev, "probing...\n");
+
+       ov4689_filltimings(&ov4689_custom_config);
+       v4l2_i2c_subdev_init(&ov4689.sd, client, &ov4689_camera_module_ops);
+
+       ov4689.custom = ov4689_custom_config;
+
+       dev_info(&client->dev, "probing successful\n");
+       return 0;
+}
+
+static int ov4689_remove(struct i2c_client *client)
+{
+       struct ov_camera_module *cam_mod = i2c_get_clientdata(client);
+
+       dev_info(&client->dev, "removing device...\n");
+
+       if (!client->adapter)
+               return -ENODEV; /* our client isn't attached */
+
+       ov_camera_module_release(cam_mod);
+
+       dev_info(&client->dev, "removed\n");
+       return 0;
+}
+
+static const struct i2c_device_id ov4689_id[] = {
+       { ov4689_DRIVER_NAME, 0 },
+       { }
+};
+
+static const struct of_device_id ov4689_of_match[] = {
+       {.compatible = "omnivision,ov4689-v4l2-i2c-subdev"},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov4689_id);
+
+static struct i2c_driver ov4689_i2c_driver = {
+       .driver = {
+               .name = ov4689_DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = ov4689_of_match
+       },
+       .probe = ov4689_probe,
+       .remove = ov4689_remove,
+       .id_table = ov4689_id,
+};
+
+module_i2c_driver(ov4689_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for ov4689");
+MODULE_AUTHOR("George");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/rockchip/ov8858_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/ov8858_v4l2-i2c-subdev.c
new file mode 100644 (file)
index 0000000..51fee5e
--- /dev/null
@@ -0,0 +1,2175 @@
+/*
+ * drivers/media/i2c/soc_camera/xgold/ov8858.c
+ *
+ * ov8858 sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * Note:
+ *    07/01/2014: new implementation using v4l2-subdev
+ *                        instead of v4l2-int-device.
+ */
+
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf-core.h>
+#include <linux/slab.h>
+#include "ov_camera_module.h"
+
+#define ov8858_DRIVER_NAME "ov8858"
+
+#define ov8858_FETCH_LSB_GAIN(VAL) ((VAL) & 0x00ff)
+#define ov8858_FETCH_MSB_GAIN(VAL) (((VAL) >> 8) & 0xff)
+#define ov8858_AEC_PK_LONG_GAIN_HIGH_REG 0x3508        /* Bit 6-13 */
+#define ov8858_AEC_PK_LONG_GAIN_LOW_REG         0x3509 /* Bits 0 -5 */
+
+#define ov8858_AEC_PK_LONG_EXPO_3RD_REG 0x3500 /* Exposure Bits 16-19 */
+#define ov8858_AEC_PK_LONG_EXPO_2ND_REG 0x3501 /* Exposure Bits 8-15 */
+#define ov8858_AEC_PK_LONG_EXPO_1ST_REG 0x3502 /* Exposure Bits 0-7 */
+
+#define ov8858_AEC_GROUP_UPDATE_ADDRESS 0x3208
+#define ov8858_AEC_GROUP_UPDATE_START_DATA 0x00
+#define ov8858_AEC_GROUP_UPDATE_END_DATA 0x10
+#define ov8858_AEC_GROUP_UPDATE_END_LAUNCH 0xA0
+
+#define ov8858_FETCH_3RD_BYTE_EXP(VAL) (((VAL) >> 16) & 0xF)   /* 4 Bits */
+#define ov8858_FETCH_2ND_BYTE_EXP(VAL) (((VAL) >> 8) & 0xFF)   /* 8 Bits */
+#define ov8858_FETCH_1ST_BYTE_EXP(VAL) ((VAL) & 0xFF)  /* 8 Bits */
+
+#define ov8858_PIDH_ADDR     0x300B
+#define ov8858_PIDL_ADDR     0x300C
+
+#define ov8858_TIMING_VTS_HIGH_REG 0x380e
+#define ov8858_TIMING_VTS_LOW_REG 0x380f
+#define ov8858_TIMING_HTS_HIGH_REG 0x380c
+#define ov8858_TIMING_HTS_LOW_REG 0x380d
+#define ov8858_INTEGRATION_TIME_MARGIN 8
+#define ov8858_FINE_INTG_TIME_MIN 0
+#define ov8858_FINE_INTG_TIME_MAX_MARGIN 0
+#define ov8858_COARSE_INTG_TIME_MIN 16
+#define ov8858_COARSE_INTG_TIME_MAX_MARGIN 4
+#define ov8858_TIMING_X_INC            0x3814
+#define ov8858_TIMING_Y_INC            0x3815
+#define ov8858_HORIZONTAL_START_HIGH_REG 0x3800
+#define ov8858_HORIZONTAL_START_LOW_REG 0x3801
+#define ov8858_VERTICAL_START_HIGH_REG 0x3802
+#define ov8858_VERTICAL_START_LOW_REG 0x3803
+#define ov8858_HORIZONTAL_END_HIGH_REG 0x3804
+#define ov8858_HORIZONTAL_END_LOW_REG 0x3805
+#define ov8858_VERTICAL_END_HIGH_REG 0x3806
+#define ov8858_VERTICAL_END_LOW_REG 0x3807
+#define ov8858_HORIZONTAL_OUTPUT_SIZE_HIGH_REG 0x3808
+#define ov8858_HORIZONTAL_OUTPUT_SIZE_LOW_REG 0x3809
+#define ov8858_VERTICAL_OUTPUT_SIZE_HIGH_REG 0x380a
+#define ov8858_VERTICAL_OUTPUT_SIZE_LOW_REG 0x380b
+#define ov8858_FLIP_REG                      0x3820
+#define ov8858_MIRROR_REG                      0x3821
+
+#define ov8858_EXT_CLK 26000000
+
+#define ov8858_FULL_SIZE_RESOLUTION_WIDTH 3264
+#define ov8858_BINING_SIZE_RESOLUTION_WIDTH 1632
+#define ov8858_VIDEO_SIZE_RESOLUTION_WIDTH 3200
+
+#define ov8858_EXP_VALID_FRAMES                4
+/* High byte of product ID */
+#define ov8858_PIDH_MAGIC 0x88
+/* Low byte of product ID  */
+#define ov8858_PIDL_MAGIC 0x58
+
+bool is_R1A_module;
+
+#define BG_RATIO_TYPICAL  0x129
+#define RG_RATIO_TYPICAL  0x11f
+
+struct ov8858_otp_struct {
+       int otp_en;
+       int flag;
+       int module_integrator_id;
+       int lens_id;
+       int production_year;
+       int production_month;
+       int production_day;
+       int rg_ratio;
+       int bg_ratio;
+       int lenc[240];
+       int checksum;
+       int VCM_start;
+       int VCM_end;
+       int VCM_dir;
+       int R_gain;
+       int G_gain;
+       int B_gain;
+};
+
+static struct ov8858_otp_struct *otp_ptr;
+static struct ov_camera_module ov8858;
+static struct ov_camera_module_custom_config ov8858_custom_config;
+
+/* ======================================================================== */
+/* Base sensor configs */
+/* ======================================================================== */
+
+/* MCLK:26MHz  3264x2448  18fps   mipi 4lane   481Mbps/lane */
+static struct ov_camera_module_reg
+       ov8858_init_tab_3264_2448_18fps[] = {
+       /* global setting */
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0103, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0300, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0301, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0302, 0x94},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0303, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0304, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0305, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0306, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030b, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030c, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030d, 0x6f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x031e, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3033, 0x24},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3600, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3601, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3602, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x22},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3607, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3608, 0x11},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3609, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360b, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360c, 0xd4},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360d, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360e, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360f, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3610, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3611, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3612, 0x88},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3613, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3614, 0x58},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3615, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3616, 0x4a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3617, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3618, 0x5a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3619, 0x70},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361a, 0x99},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361b, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361c, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361f, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3633, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3634, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3635, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3636, 0x12},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3645, 0x13},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3646, 0x83},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x364a, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x72},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3020, 0x93},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3031, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3034, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3106, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3305, 0xf1},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3308, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3309, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330b, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330c, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330f, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3307, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x4d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3505, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3509, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350c, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350d, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3510, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3511, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3512, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3700, 0x30},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3701, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3702, 0x50},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3704, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3705, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x82},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3707, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3708, 0x48},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3709, 0x66},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0x82},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370c, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3718, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3719, 0x31},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3712, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3714, 0x24},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371e, 0x31},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371f, 0x7f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3720, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3721, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3724, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3725, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3726, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3728, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3729, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372a, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372b, 0xa6},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372c, 0xa6},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372d, 0xa6},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372e, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372f, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3730, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3731, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3732, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3733, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3734, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3736, 0x30},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373a, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373b, 0x0b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373c, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373e, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3750, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3751, 0x0e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3755, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3758, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3759, 0x4c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375a, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375b, 0x26},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375c, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375d, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375f, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0x22},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3769, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376a, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3761, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3762, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3766, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376b, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3772, 0x46},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3773, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3774, 0x2c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3775, 0x13},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3776, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3777, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x17},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a0, 0x88},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a1, 0x7a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a2, 0x7a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a3, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a4, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a5, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a6, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a7, 0x88},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a8, 0x98},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a9, 0x98},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3760, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376f, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37aa, 0x88},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ab, 0x5c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ac, 0x5c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ad, 0x55},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ae, 0x19},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37af, 0x19},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b0, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b1, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b2, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b3, 0x84},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b4, 0x84},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b5, 0x60},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b6, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b7, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b8, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b9, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0xd3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x09},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0xa3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x60},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xc8},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x0b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x64},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xdc},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3815, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3820, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382b, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3837, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3841, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3846, 0x48},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d85, 0x16},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d8c, 0x73},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d8d, 0xde},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f08, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4000, 0xf1},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4005, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4002, 0x27},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4009, 0x81},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400b, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4011, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401b, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4020, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4021, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x2a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x2b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4028, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4029, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402a, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402c, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402d, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401f, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4034, 0x3f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x403d, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4300, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4301, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4302, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4316, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x58},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4503, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xcb},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x481f, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4821, 0x5f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4837, 0x21},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4850, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4851, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b00, 0x2a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b0d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d00, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d01, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d02, 0xc3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d03, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d04, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d05, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0xfe},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5002, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5003, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5046, 0x12},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5780, 0x3e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5781, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5782, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5783, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5784, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5785, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5786, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5787, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5788, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5789, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578a, 0xfd},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578b, 0xf5},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578c, 0xf5},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578d, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578e, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578f, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5790, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5791, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5792, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5793, 0x52},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5794, 0xa3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5871, 0x0d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5870, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x586e, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x586f, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x58f8, 0x3d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5901, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b00, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b01, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b02, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b03, 0xcf},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b05, 0x6c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e00, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e01, 0x41},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4825, 0x3a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4826, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4808, 0x25},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0xcc},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x470b, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4202, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400d, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4040, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x403e, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4041, 0xc6},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3007, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400a, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x58},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3021, 0x23},
+       /* @@ SIZE_3264X2448_15FPS_MIPI_4LANE based on R2A_EN_AM22 version db */
+       /* ;;pll2_VCO= 721.5, SYSclk=144.3, Dac_clk =360.75 */
+       /* ;;pll1_VCO= 962, pll_MIPIclk=481Mbps */
+       /* mipi_Pclk =60.125, Sclk1=120.25 */
+       /* ;;fps=15, 10bit,4lane, Vblanking=7.251ms */
+       /* update on 2015/06/25.*/
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0xc0},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x09},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0x90},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x0d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0xa0},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xbc},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x46},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0x60},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x36},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x37},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x58},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0x97}
+};
+
+static const struct ov_camera_module_reg
+       ov8858_init_tab_1632_1224_30fps_R1A[] = {
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0103, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0302, 0x1e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0303, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0304, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x09},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x031e, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3600, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3601, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3602, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x22},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x30},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3607, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3608, 0x11},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3609, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360b, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360c, 0xdc},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360d, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360e, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360f, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3610, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3611, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3612, 0x88},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3613, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3614, 0x58},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3615, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3616, 0x4a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3617, 0xb0},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3618, 0x56},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3619, 0x70},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361a, 0x99},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361b, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361c, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361f, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3633, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3634, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3635, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3636, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3645, 0x13},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3646, 0x83},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x364a, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3020, 0x93},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3031, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3034, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3106, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3305, 0xf1},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3308, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3309, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330b, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330c, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330f, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3307, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x4d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3505, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3509, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350c, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350d, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3510, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3511, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3512, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3700, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3701, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3702, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x19},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3704, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3705, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x35},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3707, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3708, 0x24},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3709, 0x33},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0xb5},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370c, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3718, 0x12},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3719, 0x31},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3712, 0x42},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3714, 0x24},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371e, 0x19},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371f, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3720, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3721, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3724, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3725, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3726, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3728, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3729, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372a, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372b, 0x53},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372c, 0xa3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372d, 0x53},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372e, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372f, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3730, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3731, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3732, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3733, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3734, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3736, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373a, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373b, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373c, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373e, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3755, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3758, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3759, 0x4c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375a, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375b, 0x13},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375c, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375d, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375f, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0x22},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3769, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376a, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3761, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3762, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3766, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376b, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3772, 0x23},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3773, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3774, 0x16},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3775, 0x12},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3776, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3777, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x1b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a0, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a1, 0x3d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a2, 0x3d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a3, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a4, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a5, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a6, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a7, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a8, 0x4c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a9, 0x4c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3760, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376f, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37aa, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ab, 0x2e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ac, 0x2e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ad, 0x33},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ae, 0x0d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37af, 0x0d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b0, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b1, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b2, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b3, 0x42},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b4, 0x42},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b5, 0x33},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b6, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b7, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b8, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b9, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0xd3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x09},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0xa3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x60},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xc8},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x88},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xdc},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3815, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3820, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x67},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382b, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3837, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3841, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3846, 0x48},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d85, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f08, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4000, 0xf1},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4005, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4002, 0x27},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4009, 0x81},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400b, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401b, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4020, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4021, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xb9},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x2a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x2b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4028, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4029, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402a, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402c, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402d, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401f, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4034, 0x3f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x403d, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4300, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4301, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4302, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4316, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x38},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4503, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xcb},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x481f, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4837, 0x16},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4850, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4851, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b00, 0x2a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b0d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d00, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d01, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d02, 0xc3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d03, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d04, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d05, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0x7e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5002, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5003, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5046, 0x12},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5901, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e00, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e01, 0x41},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4825, 0x3a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4826, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4808, 0x25},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x09},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x4d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x40},
+       /* {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x04}, */
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x35},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0xb5},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x1b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x60},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xc8},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x88},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xdc},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x67},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xb9},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x2a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x2b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x38},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xcb},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f}
+};
+
+static const struct ov_camera_module_reg
+       ov8858_init_tab_3264_2448_15fps_R1A[] =  {
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0103, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0302, 0x1e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0303, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0304, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x09},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x031e, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3600, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3601, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3602, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x22},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x30},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3607, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3608, 0x11},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3609, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360b, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360c, 0xdc},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360d, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360e, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360f, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3610, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3611, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3612, 0x88},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3613, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3614, 0x58},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3615, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3616, 0x4a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3617, 0xb0},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3618, 0x56},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3619, 0x70},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361a, 0x99},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361b, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361c, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361f, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3633, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3634, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3635, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3636, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3645, 0x13},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3646, 0x83},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x364a, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3020, 0x93},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3031, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3034, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3106, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3305, 0xf1},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3308, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3309, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330b, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330c, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330f, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3307, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x4d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3505, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3509, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350c, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350d, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3510, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3511, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3512, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3700, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3701, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3702, 0x28},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x19},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3704, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3705, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x35},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3707, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3708, 0x24},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3709, 0x33},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0xb5},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370c, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3718, 0x12},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3719, 0x31},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3712, 0x42},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3714, 0x24},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371e, 0x19},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371f, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3720, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3721, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3724, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3725, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3726, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3728, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3729, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372a, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372b, 0x53},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372c, 0xa3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372d, 0x53},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372e, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372f, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3730, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3731, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3732, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3733, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3734, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3736, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373a, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373b, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373c, 0x0a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373e, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3755, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3758, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3759, 0x4c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375a, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375b, 0x13},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375c, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375d, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375e, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375f, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0x22},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3769, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376a, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3761, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3762, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3766, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376b, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3772, 0x23},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3773, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3774, 0x16},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3775, 0x12},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3776, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3777, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x1b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a0, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a1, 0x3d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a2, 0x3d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a3, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a4, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a5, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a6, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a7, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a8, 0x4c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a9, 0x4c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3760, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376f, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37aa, 0x44},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ab, 0x2e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ac, 0x2e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ad, 0x33},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ae, 0x0d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37af, 0x0d},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b0, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b1, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b2, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b3, 0x42},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b4, 0x42},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b5, 0x33},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b6, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b7, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b8, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b9, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0xd3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x09},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0xa3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x60},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xc8},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x88},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xdc},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3815, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3820, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x67},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382b, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3837, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3841, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3846, 0x48},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d85, 0x14},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f08, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x80},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4000, 0xf1},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4005, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4002, 0x27},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4009, 0x81},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400b, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401b, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4020, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4021, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xb9},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x2a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x05},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x2b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4028, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4029, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402a, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402c, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402d, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401f, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4034, 0x3f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x403d, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4300, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4301, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4302, 0x0f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4316, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x38},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4503, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xcb},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x481f, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4837, 0x16},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4850, 0x10},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4851, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b00, 0x2a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b0d, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d00, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d01, 0x18},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d02, 0xc3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d03, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d04, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d05, 0xff},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0x7e},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5002, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5003, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5046, 0x12},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5901, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e00, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e01, 0x41},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4825, 0x3a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4826, 0x40},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4808, 0x25},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x02},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x04},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x03},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x9a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x20},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x6a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0x6a},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x32},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0xc0},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x09},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0x90},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x07},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x94},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x09},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xaa},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x46},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x06},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x00},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x0b},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xc3},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x36},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x37},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x08},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x0c},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x58},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x01},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0x97},
+       {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0xff}
+};
+
+/* ======================================================================== */
+static struct ov_camera_module_config ov8858_configs[] = {
+{
+               .name = "3264x2448_18fps",
+               .frm_fmt = {
+                       .width = 3264,
+                       .height = 2448,
+                       .code = MEDIA_BUS_FMT_SBGGR10_1X10
+               },
+               .frm_intrvl = {
+                       .interval = {
+                               .numerator = 1,
+                               .denominator = 18
+                       }
+               },
+               .auto_exp_enabled = false,
+               .auto_gain_enabled = false,
+               .auto_wb_enabled = false,
+               .reg_table = (void *)ov8858_init_tab_3264_2448_18fps,
+               .reg_table_num_entries =
+                       sizeof(ov8858_init_tab_3264_2448_18fps) /
+                       sizeof(ov8858_init_tab_3264_2448_18fps[0]),
+               .reg_diff_table = NULL,
+               .reg_diff_table_num_entries = 0,
+               .v_blanking_time_us = 7251,
+               PLTFRM_CAM_ITF_MIPI_CFG(0, 4, 481, 24000000)
+       }
+};
+
+static struct ov_camera_module_config ov8858_configs_R1A[] = {
+       {
+               .name = "1632x1224_30fps",
+               .frm_fmt = {
+                       .width = 1632,
+                       .height = 1224,
+                       .code = MEDIA_BUS_FMT_SBGGR10_1X10
+               },
+               .frm_intrvl = {
+                       .interval = {
+                               .numerator = 1,
+                               .denominator = 30
+                       }
+               },
+               .auto_exp_enabled = false,
+               .auto_gain_enabled = false,
+               .auto_wb_enabled = false,
+               .reg_table = (void *)ov8858_init_tab_1632_1224_30fps_R1A,
+               .reg_table_num_entries =
+                       sizeof(ov8858_init_tab_1632_1224_30fps_R1A) /
+                       sizeof(ov8858_init_tab_1632_1224_30fps_R1A[0]),
+               .v_blanking_time_us = 6579,
+               PLTFRM_CAM_ITF_MIPI_CFG(0, 2, 720, 24000000)
+       },
+       {
+               .name = "3264x2448_15fps",
+               .frm_fmt = {
+                       .width = 3264,
+                       .height = 2448,
+                       .code = MEDIA_BUS_FMT_SBGGR10_1X10
+               },
+               .frm_intrvl = {
+                       .interval = {
+                               .numerator = 1,
+                               .denominator = 15
+                       }
+               },
+               .auto_exp_enabled = false,
+               .auto_gain_enabled = false,
+               .auto_wb_enabled = false,
+               .reg_table = (void *)ov8858_init_tab_3264_2448_15fps_R1A,
+               .reg_table_num_entries =
+                       sizeof(ov8858_init_tab_3264_2448_15fps_R1A) /
+                       sizeof(ov8858_init_tab_3264_2448_15fps_R1A[0]),
+               .v_blanking_time_us = 6579,
+               PLTFRM_CAM_ITF_MIPI_CFG(0, 2, 720, 24000000)
+       }
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int ov8858_g_VTS(struct ov_camera_module *cam_mod, u32 *vts)
+{
+       u32 msb, lsb;
+       int ret;
+
+       ret = ov_camera_module_read_reg_table(
+               cam_mod,
+               ov8858_TIMING_VTS_HIGH_REG,
+               &msb);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       ret = ov_camera_module_read_reg_table(
+               cam_mod,
+               ov8858_TIMING_VTS_LOW_REG,
+               &lsb);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       *vts = (msb << 8) | lsb;
+       cam_mod->vts_cur = *vts;
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov8858_auto_adjust_fps(struct ov_camera_module *cam_mod,
+       u32 exp_time)
+{
+       int ret;
+       u32 vts;
+
+       if ((cam_mod->exp_config.exp_time + ov8858_COARSE_INTG_TIME_MAX_MARGIN)
+               > cam_mod->vts_min)
+               vts = cam_mod->exp_config.exp_time +
+                       ov8858_COARSE_INTG_TIME_MAX_MARGIN;
+       else
+               vts = cam_mod->vts_min;
+       ret = ov_camera_module_write_reg(cam_mod,
+               ov8858_TIMING_VTS_LOW_REG,
+               vts & 0xFF);
+       ret |= ov_camera_module_write_reg(cam_mod,
+               ov8858_TIMING_VTS_HIGH_REG,
+               (vts >> 8) & 0xFF);
+
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       } else {
+               ov_camera_module_pr_debug(cam_mod,
+                       "updated vts = %d,vts_min=%d\n", vts, cam_mod->vts_min);
+               cam_mod->vts_cur = vts;
+       }
+
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+static int ov8858_write_aec(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod,
+                                 "exp_time = %d, gain = %d, flash_mode = %d\n",
+                                 cam_mod->exp_config.exp_time,
+                                 cam_mod->exp_config.gain,
+                                 cam_mod->exp_config.flash_mode);
+
+       /*
+        * if the sensor is already streaming, write to shadow registers,
+        * if the sensor is in SW standby, write to active registers,
+        * if the sensor is off/registers are not writeable, do nothing
+        */
+       if ((cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY) ||
+               (cam_mod->state == OV_CAMERA_MODULE_STREAMING)) {
+               u32 a_gain = cam_mod->exp_config.gain;
+               u32 exp_time;
+
+               a_gain = a_gain > 0x7ff ? 0x7ff : a_gain;
+               a_gain = a_gain * cam_mod->exp_config.gain_percent / 100;
+               exp_time = cam_mod->exp_config.exp_time << 4;
+               if (cam_mod->state == OV_CAMERA_MODULE_STREAMING)
+                       ret = ov_camera_module_write_reg(cam_mod,
+                       ov8858_AEC_GROUP_UPDATE_ADDRESS,
+                       ov8858_AEC_GROUP_UPDATE_START_DATA);
+               if (!IS_ERR_VALUE(ret) && cam_mod->auto_adjust_fps)
+                       ret = ov8858_auto_adjust_fps(cam_mod,
+                                               cam_mod->exp_config.exp_time);
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       ov8858_AEC_PK_LONG_GAIN_HIGH_REG,
+                       ov8858_FETCH_MSB_GAIN(a_gain));
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       ov8858_AEC_PK_LONG_GAIN_LOW_REG,
+                       ov8858_FETCH_LSB_GAIN(a_gain));
+               ret = ov_camera_module_write_reg(cam_mod,
+                       ov8858_AEC_PK_LONG_EXPO_3RD_REG,
+                       ov8858_FETCH_3RD_BYTE_EXP(exp_time));
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       ov8858_AEC_PK_LONG_EXPO_2ND_REG,
+                       ov8858_FETCH_2ND_BYTE_EXP(exp_time));
+               ret |= ov_camera_module_write_reg(cam_mod,
+                       ov8858_AEC_PK_LONG_EXPO_1ST_REG,
+                       ov8858_FETCH_1ST_BYTE_EXP(exp_time));
+               if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) {
+                       ret = ov_camera_module_write_reg(cam_mod,
+                               ov8858_AEC_GROUP_UPDATE_ADDRESS,
+                               ov8858_AEC_GROUP_UPDATE_END_DATA);
+                       ret = ov_camera_module_write_reg(cam_mod,
+                               ov8858_AEC_GROUP_UPDATE_ADDRESS,
+                               ov8858_AEC_GROUP_UPDATE_END_LAUNCH);
+               }
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov8858_g_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       switch (ctrl_id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_EXPOSURE:
+       case V4L2_CID_FLASH_LED_MODE:
+               /* nothing to be done here */
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_debug(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+static int ov8858_filltimings(struct ov_camera_module_custom_config *custom)
+{
+       int i, j;
+       struct ov_camera_module_config *config;
+       struct ov_camera_module_timings *timings;
+       struct ov_camera_module_reg *reg_table;
+       int reg_table_num_entries;
+
+       for (i = 0; i < custom->num_configs; i++) {
+               config = &custom->configs[i];
+               reg_table = config->reg_table;
+               reg_table_num_entries = config->reg_table_num_entries;
+               timings = &config->timings;
+
+               memset(timings, 0x00, sizeof(*timings));
+               for (j = 0; j < reg_table_num_entries; j++) {
+                       switch (reg_table[j].reg) {
+                       case ov8858_TIMING_VTS_HIGH_REG:
+                               timings->frame_length_lines =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->frame_length_lines & 0xff));
+                               break;
+                       case ov8858_TIMING_VTS_LOW_REG:
+                               timings->frame_length_lines =
+                                       (reg_table[j].val |
+                                       (timings->frame_length_lines & 0xff00));
+                               break;
+                       case ov8858_TIMING_HTS_HIGH_REG:
+                               timings->line_length_pck =
+                                       ((reg_table[j].val << 8) |
+                                       timings->line_length_pck);
+                               break;
+                       case ov8858_TIMING_HTS_LOW_REG:
+                               timings->line_length_pck =
+                                       (reg_table[j].val |
+                                       (timings->line_length_pck & 0xff00));
+                               break;
+                       case ov8858_TIMING_X_INC:
+                               timings->binning_factor_x =
+                               ((reg_table[j].val >> 4) + 1) / 2;
+                               if (timings->binning_factor_x == 0)
+                                       timings->binning_factor_x = 1;
+                               break;
+                       case ov8858_TIMING_Y_INC:
+                               timings->binning_factor_y =
+                               ((reg_table[j].val >> 4) + 1) / 2;
+                               if (timings->binning_factor_y == 0)
+                                       timings->binning_factor_y = 1;
+                               break;
+                       case ov8858_HORIZONTAL_START_HIGH_REG:
+                               timings->crop_horizontal_start =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->crop_horizontal_start &
+                                       0xff));
+                               break;
+                       case ov8858_HORIZONTAL_START_LOW_REG:
+                               timings->crop_horizontal_start =
+                                       (reg_table[j].val |
+                                       (timings->crop_horizontal_start &
+                                       0xff00));
+                               break;
+                       case ov8858_VERTICAL_START_HIGH_REG:
+                               timings->crop_vertical_start =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->crop_vertical_start & 0xff));
+                               break;
+                       case ov8858_VERTICAL_START_LOW_REG:
+                               timings->crop_vertical_start =
+                                       ((reg_table[j].val) |
+                                       (timings->crop_vertical_start &
+                                       0xff00));
+                               break;
+                       case ov8858_HORIZONTAL_END_HIGH_REG:
+                               timings->crop_horizontal_end =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->crop_horizontal_end & 0xff));
+                               break;
+                       case ov8858_HORIZONTAL_END_LOW_REG:
+                               timings->crop_horizontal_end =
+                                       (reg_table[j].val |
+                                       (timings->crop_horizontal_end &
+                                       0xff00));
+                               break;
+                       case ov8858_VERTICAL_END_HIGH_REG:
+                               timings->crop_vertical_end =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->crop_vertical_end & 0xff));
+                               break;
+                       case ov8858_VERTICAL_END_LOW_REG:
+                               timings->crop_vertical_end =
+                                       (reg_table[j].val |
+                                       (timings->crop_vertical_end & 0xff00));
+                               break;
+                       case ov8858_HORIZONTAL_OUTPUT_SIZE_HIGH_REG:
+                               timings->sensor_output_width =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->sensor_output_width & 0xff));
+                               break;
+                       case ov8858_HORIZONTAL_OUTPUT_SIZE_LOW_REG:
+                               timings->sensor_output_width =
+                                       (reg_table[j].val |
+                                       (timings->sensor_output_width &
+                                       0xff00));
+                               break;
+                       case ov8858_VERTICAL_OUTPUT_SIZE_HIGH_REG:
+                               timings->sensor_output_height =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->sensor_output_height & 0xff));
+                               break;
+                       case ov8858_VERTICAL_OUTPUT_SIZE_LOW_REG:
+                               timings->sensor_output_height =
+                                       (reg_table[j].val |
+                                       (timings->sensor_output_height &
+                                       0xff00));
+                               break;
+                       case ov8858_AEC_PK_LONG_EXPO_1ST_REG:
+                               timings->exp_time =
+                                       ((reg_table[j].val) |
+                                       (timings->exp_time & 0xffff00));
+                               break;
+                       case ov8858_AEC_PK_LONG_EXPO_2ND_REG:
+                               timings->exp_time =
+                                       ((reg_table[j].val << 8) |
+                                       (timings->exp_time & 0x00ff00));
+                               break;
+                       case ov8858_AEC_PK_LONG_EXPO_3RD_REG:
+                               timings->exp_time =
+                                       (((reg_table[j].val & 0x0f) << 16) |
+                                       (timings->exp_time & 0xff0000));
+                               break;
+                       case ov8858_AEC_PK_LONG_GAIN_LOW_REG:
+                               timings->gain =
+                                       (reg_table[j].val |
+                                       (timings->gain & 0x0700));
+                               break;
+                       case ov8858_AEC_PK_LONG_GAIN_HIGH_REG:
+                               timings->gain =
+                                       (((reg_table[j].val & 0x07) << 8) |
+                                       (timings->gain & 0xff));
+                               break;
+                       }
+               }
+
+               timings->exp_time >>= 4;
+               timings->vt_pix_clk_freq_hz =
+                       config->frm_intrvl.interval.denominator
+                       * timings->frame_length_lines
+                       * timings->line_length_pck;
+
+               timings->coarse_integration_time_min =
+                       ov8858_COARSE_INTG_TIME_MIN;
+               timings->coarse_integration_time_max_margin =
+                       ov8858_COARSE_INTG_TIME_MAX_MARGIN;
+
+               /* OV Sensor do not use fine integration time. */
+               timings->fine_integration_time_min =
+                       ov8858_FINE_INTG_TIME_MIN;
+               timings->fine_integration_time_max_margin =
+                       ov8858_FINE_INTG_TIME_MAX_MARGIN;
+       }
+
+       return 0;
+}
+
+static int ov8858_g_timings(struct ov_camera_module *cam_mod,
+       struct ov_camera_module_timings *timings)
+{
+       int ret = 0;
+       unsigned int vts;
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config))
+               goto err;
+
+       *timings = cam_mod->active_config->timings;
+
+       vts = (!cam_mod->vts_cur) ?
+               timings->frame_length_lines :
+               cam_mod->vts_cur;
+       if (cam_mod->frm_intrvl_valid)
+               timings->vt_pix_clk_freq_hz =
+                       cam_mod->frm_intrvl.interval.denominator *
+                       vts * timings->line_length_pck;
+       else
+               timings->vt_pix_clk_freq_hz =
+               cam_mod->active_config->frm_intrvl.interval.denominator *
+               vts * timings->line_length_pck;
+
+       return ret;
+err:
+       ov_camera_module_pr_err(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov8858_s_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       switch (ctrl_id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_EXPOSURE:
+               ret = ov8858_write_aec(cam_mod);
+               break;
+       case V4L2_CID_FLASH_LED_MODE:
+               /* nothing to be done here */
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_debug(cam_mod,
+                       "failed with error (%d) 0x%x\n", ret, ctrl_id);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov8858_s_ext_ctrls(struct ov_camera_module *cam_mod,
+                                struct ov_camera_module_ext_ctrls *ctrls)
+{
+       int ret = 0;
+
+       /* Handles only exposure and gain together special case. */
+       if (ctrls->count == 1)
+               ret = ov8858_s_ctrl(cam_mod, ctrls->ctrls[0].id);
+       else if ((ctrls->count == 3) &&
+                ((ctrls->ctrls[0].id == V4L2_CID_GAIN &&
+                  ctrls->ctrls[1].id == V4L2_CID_EXPOSURE) ||
+                 (ctrls->ctrls[1].id == V4L2_CID_GAIN &&
+                  ctrls->ctrls[0].id == V4L2_CID_EXPOSURE)))
+               ret = ov8858_write_aec(cam_mod);
+       else
+               ret = -EINVAL;
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_debug(cam_mod,
+                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+int update_awb_gain(struct ov_camera_module *cam_mod)
+{
+       int R_gain = otp_ptr->R_gain;
+       int G_gain = otp_ptr->G_gain;
+       int B_gain = otp_ptr->B_gain;
+       int ret = 0;
+
+       if (R_gain > 0x400) {
+               ret |= ov_camera_module_write_reg(cam_mod, 0x5032,
+                                                       R_gain >> 8);
+               ret |= ov_camera_module_write_reg(cam_mod, 0x5033,
+                                                       R_gain & 0x00ff);
+       }
+       if (G_gain > 0x400) {
+               ret |= ov_camera_module_write_reg(cam_mod, 0x5034,
+                                                       G_gain >> 8);
+               ret |= ov_camera_module_write_reg(cam_mod, 0x5035,
+                                                       G_gain & 0x00ff);
+       }
+       if (B_gain > 0x400) {
+               ret |= ov_camera_module_write_reg(cam_mod, 0x5036,
+                                                       B_gain >> 8);
+               ret |= ov_camera_module_write_reg(cam_mod, 0x5037,
+                                                       B_gain & 0x00ff);
+       }
+
+       if (IS_ERR_VALUE(ret))
+               ov_camera_module_pr_err(cam_mod, "otp awb gain apply failed\n");
+       else
+               ov_camera_module_pr_debug(cam_mod,
+                                       "ov8858_update:%s, rgain:%x ggain %x bgain %x\n",
+                                       __func__, R_gain, G_gain, B_gain);
+
+       return ret;
+}
+
+int update_lenc(struct ov_camera_module *cam_mod)
+{
+       int i, temp = 0, ret = 0;
+
+       ret |= ov_camera_module_read_reg(cam_mod, 1, 0x5000, &temp);
+       temp = 0x80 | temp;
+       ret |= ov_camera_module_write_reg(cam_mod, 0x5000, temp);
+
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                                       "otp lenc apply failed at beginning\n");
+               return ret;
+       }
+
+       for (i = 0; i < 240; i++) {
+               ret |= ov_camera_module_write_reg(cam_mod, (0x5800 + i),
+                                               otp_ptr->lenc[i]);
+               if (IS_ERR_VALUE(ret)) {
+                       ov_camera_module_pr_err(cam_mod,
+                                               "otp lenc apply failed during procesing\n");
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int ov8858_start_streaming(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod,
+               "active config=%s\n", cam_mod->active_config->name);
+
+       if (otp_ptr && otp_ptr->otp_en == 1 &&
+       cam_mod->update_config &&
+       cam_mod->active_config->soft_reset) {
+               ov_camera_module_pr_debug(cam_mod,
+                                       "apply otp data for R2A module...\n");
+               update_awb_gain(cam_mod);
+               update_lenc(cam_mod);
+       }
+       ret = ov8858_g_VTS(cam_mod, &cam_mod->vts_min);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       if (IS_ERR_VALUE(ov_camera_module_write_reg(cam_mod, 0x0100, 1)))
+               goto err;
+
+       msleep(25);
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n",
+               ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov8858_stop_streaming(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       ret = ov_camera_module_write_reg(cam_mod, 0x0100, 0);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       msleep(25);
+
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+/*
+ * call this function after ov8858 initialization
+ * return value: 0 update success
+ * 1, no OTP
+ */
+int  __ov8858_read_otp_wb(struct ov_camera_module *cam_mod)
+{
+       int otp_flag = 0;
+       int addr = 0;
+       int ret = 0;
+       int temp1 = 0, temp2 = 0;
+
+       ret = ov_camera_module_read_reg(cam_mod, 1, 0x7010, &otp_flag);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "otp wb flag read failed\n");
+               return ret;
+       }
+       ov_camera_module_pr_debug(cam_mod, "otp wb flag is %0x\n", otp_flag);
+
+       if ((otp_flag & 0xc0) == 0x40)
+               addr = 0x7011; /* set addr to group 1 base */
+       else if ((otp_flag & 0x30) == 0x10)
+               addr = 0x7019; /* set addr to group 2 base */
+
+       if (addr != 0) {
+               ov_camera_module_pr_debug(cam_mod,
+                                       "otp wb addr is %0x\n", addr);
+               otp_ptr->flag = 0xc0;
+               ret |= ov_camera_module_read_reg(cam_mod, 1, addr,
+                                       &otp_ptr->module_integrator_id);
+               ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 1),
+                                       &otp_ptr->lens_id);
+               ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 2),
+                                       &otp_ptr->production_year);
+               ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 3),
+                                       &otp_ptr->production_month);
+               ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 4),
+                                       &otp_ptr->production_day);
+               ret |= ov_camera_module_read_reg(cam_mod, 1,
+                                       (addr + 5), &temp1);
+               ret |= ov_camera_module_read_reg(cam_mod, 1,
+                                       (addr + 7), &temp2);
+               otp_ptr->rg_ratio = (temp1 << 2) + (temp2 >> 6 & 0x03);
+               ret |= ov_camera_module_read_reg(cam_mod, 1,
+                                       (addr + 6), &temp1);
+               otp_ptr->bg_ratio = (temp1 << 2) + (temp2 >> 4 & 0x03);
+
+               if (IS_ERR_VALUE(ret)) {
+                       ov_camera_module_pr_err(cam_mod,
+                               "otp wb data read failed\n");
+                       return 1;
+               }
+
+       } else {
+               ov_camera_module_pr_debug(cam_mod,
+                       "no valid module info and awb data in otp\n");
+               otp_ptr->flag = 0;
+               otp_ptr->module_integrator_id = 0;
+               otp_ptr->lens_id = 0;
+               otp_ptr->production_year = 0;
+               otp_ptr->production_month = 0;
+               otp_ptr->production_day = 0;
+               otp_ptr->rg_ratio = 0;
+               otp_ptr->bg_ratio = 0;
+               return 1;
+       }
+
+       otp_ptr->R_gain = (RG_RATIO_TYPICAL * 1000) / otp_ptr->rg_ratio;
+       otp_ptr->B_gain = (BG_RATIO_TYPICAL * 1000) / otp_ptr->bg_ratio;
+       otp_ptr->G_gain = 1000;
+
+       if (otp_ptr->R_gain < 1000 || otp_ptr->B_gain < 1000) {
+               if (otp_ptr->R_gain < otp_ptr->B_gain)
+                       temp1 = otp_ptr->R_gain;
+               else
+                       temp1 = otp_ptr->B_gain;
+       } else {
+               temp1 = otp_ptr->G_gain;
+       }
+
+       otp_ptr->R_gain = 0x400 * otp_ptr->R_gain / temp1;
+       otp_ptr->B_gain = 0x400 * otp_ptr->B_gain / temp1;
+       otp_ptr->G_gain = 0x400 * otp_ptr->G_gain / temp1;
+
+       return ret;
+}
+
+int  __ov8858_read_otp_vcm(struct ov_camera_module *cam_mod)
+{
+       int otp_flag = 0;
+       int addr = 0;
+       int ret = 0;
+       int temp1 = 0, temp2 = 0;
+
+       ret = ov_camera_module_read_reg(cam_mod, 1, 0x7021, &otp_flag);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "otp vcm flag read failed\n");
+               return ret;
+       }
+       ov_camera_module_pr_debug(cam_mod, "otp vcm flag is %0x\n", otp_flag);
+
+       if ((otp_flag & 0xc0) == 0x40)
+               addr = 0x7022; /* set addr to group 1 base */
+       else if ((otp_flag & 0x30) == 0x10)
+               addr = 0x7025; /* set addr to group 2 base */
+
+       if (addr != 0) {
+               ov_camera_module_pr_debug(cam_mod,
+                                       "otp vcm addr is %0x\n", addr);
+               otp_ptr->flag |= 0x20;
+               ret |= ov_camera_module_read_reg(cam_mod, 1, addr, &temp1);
+               ret |= ov_camera_module_read_reg(cam_mod, 1,
+                                       (addr + 2), &temp2);
+               otp_ptr->VCM_start = (temp1 << 2) | (temp1 >> 6 & 0x03);
+               ret |= ov_camera_module_read_reg(cam_mod, 1,
+                                       (addr + 1), &temp1);
+               otp_ptr->VCM_end = (temp1 << 2) + (temp2 >> 4 & 0x03);
+               otp_ptr->VCM_dir = (temp2 >> 2 & 0x03);
+
+               if (IS_ERR_VALUE(ret))
+                       ov_camera_module_pr_err(cam_mod,
+                                               "otp vcm read fail!\n");
+       } else {
+               ov_camera_module_pr_debug(cam_mod,
+                                       "no valid vcm data in otp\n");
+               otp_ptr->VCM_end = 0;
+               otp_ptr->VCM_end = 0;
+               otp_ptr->VCM_dir = 0;
+               return 1;
+       }
+
+       return ret;
+}
+
+int  __ov8858_read_otp_lenc(struct ov_camera_module *cam_mod)
+{
+       int otp_flag = 0;
+       int addr = 0;
+       int ret = 0, i = 0;
+       int checksum = 0;
+
+       ret = ov_camera_module_read_reg(cam_mod, 1, 0x7028, &otp_flag);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "otp lenc flag read failed\n");
+               return ret;
+       }
+
+       ov_camera_module_pr_debug(cam_mod,
+                               "otp lenc flag is 0x%0x\n", otp_flag);
+
+       if ((otp_flag & 0xc0) == 0x40)
+               addr = 0x7029; /* set addr to group 1 base */
+       else if ((otp_flag & 0x30) == 0x10)
+               addr = 0x711a; /* set addr to group 2 base */
+
+       if (addr != 0) {
+               for (i = 0; i < 240; i++) {
+                       ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + i),
+                                                       &otp_ptr->lenc[i]);
+                       if (IS_ERR_VALUE(ret)) {
+                               ov_camera_module_pr_err(cam_mod,
+                                       "otp lenc data read failed\n");
+                                       return ret;
+                       }
+                       checksum += otp_ptr->lenc[i];
+               }
+
+               checksum = (checksum) % 255 + 1;
+               ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 240),
+                                               &otp_ptr->checksum);
+               if (IS_ERR_VALUE(ret)) {
+                       ov_camera_module_pr_err(cam_mod,
+                               "otp lenc checksum read failed\n");
+                               return ret;
+               }
+
+               if (otp_ptr->checksum == checksum) {
+                       otp_ptr->flag |= 0x10;
+               } else {
+                       ov_camera_module_pr_err(cam_mod,
+                               "otp lenc checksum no match!\n");
+                       return 1;
+               }
+       } else {
+               for (i = 0; i < 240; i++) {
+                       ov_camera_module_pr_debug(cam_mod,
+                               "no valid lenc data in otp\n");
+                       otp_ptr->lenc[i] = 0;
+               }
+               return 1;
+       }
+
+       return ret;
+}
+
+static int ov8858_otp_read(struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+       int temp = 0;
+       int reg = 0;
+       int otp_ret = 0;
+       struct i2c_client *client = v4l2_get_subdevdata(&cam_mod->sd);
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       if (otp_ptr) {
+               ov_camera_module_pr_debug(cam_mod, "OTP data loaded already\n");
+               return 0;
+       }
+
+       otp_ptr = kzalloc(sizeof(*otp_ptr), GFP_KERNEL);
+       if (!otp_ptr) {
+               ov_camera_module_pr_err(cam_mod, "otp alloc fail!\n");
+               return -ENOMEM;
+       }
+
+       ov_camera_module_write_reg(cam_mod, 0x0100, 1);
+
+       /* set 0x5002[3] to "0" before ops */
+       ret = ov_camera_module_read_reg(cam_mod, 1, 0x5002, &temp);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "otp read failed at phase1\n");
+               return ret;
+       }
+
+       ov_camera_module_pr_debug(cam_mod, "otp read phase1 pass\n");
+
+       ret |= ov_camera_module_write_reg(cam_mod, 0x5002,
+                       ((temp & (~0x08))));
+
+       ret |= ov_camera_module_write_reg(cam_mod, 0x3d84, 0xc0);
+       /* otp start addr */
+       ret |= ov_camera_module_write_reg(cam_mod, 0x3d88, 0x70);
+       ret |= ov_camera_module_write_reg(cam_mod, 0x3d89, 0x10);
+       /* otp end addr */
+       ret |= ov_camera_module_write_reg(cam_mod, 0x3d8A, 0x72);
+       ret |= ov_camera_module_write_reg(cam_mod, 0x3d8B, 0x0a);
+       /* load otp into buffer */
+       ret |= ov_camera_module_write_reg(cam_mod, 0x3d81, 0x01);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "otp read failed at phase2\n");
+               return ret;
+       }
+
+       ov_camera_module_pr_debug(cam_mod, "otp read phase2 pass\n");
+
+       usleep_range(10000, 15000);
+
+       /* otp wb read */
+       otp_ret = __ov8858_read_otp_wb(cam_mod);
+
+       if (otp_ret)
+               ov_camera_module_pr_err(cam_mod, "fail to read OTP wb!\n");
+       else
+               ov_camera_module_pr_debug(cam_mod, "read otp wb success\n");
+
+       /* otp vcm read */
+       otp_ret = __ov8858_read_otp_vcm(cam_mod);
+
+       if (otp_ret)
+               ov_camera_module_pr_err(cam_mod, "fail to read OTP vcm!\n");
+       else
+               ov_camera_module_pr_debug(cam_mod, "read otp vcm success\n");
+
+       /* otp lenc read */
+       otp_ret = __ov8858_read_otp_lenc(cam_mod);
+
+       if (otp_ret) {
+               ov_camera_module_pr_err(cam_mod, "fail to read OTP lenc!\n");
+               goto readotperr;
+       } else {
+               ov_camera_module_pr_debug(cam_mod, "read otp lenc success\n");
+       }
+       /* clear data afer read */
+       for (reg = 0x7010; reg <= 0x720a; reg++) {
+               ret |= ov_camera_module_write_reg(cam_mod, reg, 0);
+               if (IS_ERR_VALUE(ret)) {
+                       ov_camera_module_pr_err(cam_mod,
+                               "otp clear data failed at phase3\n");
+                       return ret;
+               }
+       }
+
+readotperr:
+       /* set 0x5002[3] to "1" after ops */
+       ret = ov_camera_module_read_reg(cam_mod, 1, 0x5002, &temp);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "otp exit failed at phase 4\n");
+               return ret;
+       }
+
+       ov_camera_module_pr_debug(cam_mod, "0x5002 is 0x%0x\n", temp);
+
+       ret |= ov_camera_module_write_reg(cam_mod, 0x5002,
+                                       (0x08 | (temp & (~0x08))));
+       ov_camera_module_write_reg(cam_mod, 0x0100, 0);
+       if (otp_ret) {
+               otp_ptr->otp_en = 0;
+               ov_camera_module_pr_err(
+               cam_mod,
+               "get otp data failed\n");
+       } else {
+               otp_ptr->otp_en = 1;
+               dev_info(&client->dev, "get otp data success\n");
+       }
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int ov8858_check_camera_id(struct ov_camera_module *cam_mod)
+{
+       u32 pidh, pidl, vid = 0;
+       int ret = 0;
+       struct i2c_client *client;
+
+       ov_camera_module_pr_debug(cam_mod, "\n");
+
+       ret |= ov_camera_module_read_reg(cam_mod, 1, ov8858_PIDH_ADDR, &pidh);
+       ret |= ov_camera_module_read_reg(cam_mod, 1, ov8858_PIDL_ADDR, &pidl);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "register read failed, camera module powered off?\n");
+               goto err;
+       }
+
+       if ((pidh == ov8858_PIDH_MAGIC) && (pidl == ov8858_PIDL_MAGIC))
+               ov_camera_module_pr_debug(cam_mod,
+                       "successfully detected camera ID 0x%02x%02x\n",
+                       pidh, pidl);
+       else {
+               ov_camera_module_pr_err(cam_mod,
+                       "wrong camera ID, expected 0x%02x%02x, detected 0x%02x%02x\n",
+                       ov8858_PIDH_MAGIC, ov8858_PIDL_MAGIC, pidh, pidl);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret |= ov_camera_module_read_reg(cam_mod, 1, 0x302a, &vid);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_pr_err(cam_mod,
+                       "register read failed, camera module powered off?\n");
+               goto err;
+       } else
+               ov_camera_module_pr_debug(cam_mod,
+                       "ov8858 chip version is 0x%2x\n", vid);
+
+       client = v4l2_get_subdevdata(&cam_mod->sd);
+       if ((vid == 0xb1) || (vid == 0xb0)) {
+               ov8858_custom_config.configs = ov8858_configs_R1A;
+               ov8858_custom_config.num_configs =
+                       sizeof(ov8858_configs_R1A)
+                       / sizeof(ov8858_configs_R1A[0]);
+               dev_info(&client->dev, "init setting for r1a\n");
+       } else {
+               dev_info(&client->dev, "init setting for r2a\n");
+       }
+
+       ov8858_filltimings(&ov8858_custom_config);
+       cam_mod->custom = ov8858_custom_config;
+
+       ov_camera_module_pr_debug(cam_mod,
+               "ov8858 chip version is 0x%2x,module config no. is %d\n",
+               vid,
+               cam_mod->custom.num_configs);
+       return 0;
+err:
+       ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret);
+       return ret;
+}
+
+static int ov8858_g_exposure_valid_frame(struct ov_camera_module *cam_mod)
+{
+       return ov8858_EXP_VALID_FRAMES;
+}
+
+/* ======================================================================== */
+/* This part is platform dependent */
+/* ======================================================================== */
+
+static struct v4l2_subdev_core_ops ov8858_camera_module_core_ops = {
+       .g_ctrl = ov_camera_module_g_ctrl,
+       .s_ctrl = ov_camera_module_s_ctrl,
+       .s_ext_ctrls = ov_camera_module_s_ext_ctrls,
+       .s_power = ov_camera_module_s_power,
+       .ioctl = ov_camera_module_ioctl
+};
+
+static struct v4l2_subdev_video_ops ov8858_camera_module_video_ops = {
+       .s_frame_interval = ov_camera_module_s_frame_interval,
+       .s_stream = ov_camera_module_s_stream
+};
+
+static struct v4l2_subdev_pad_ops ov8858_camera_module_pad_ops = {
+       .enum_frame_interval = ov_camera_module_enum_frameintervals,
+       .get_fmt = ov_camera_module_g_fmt,
+       .set_fmt = ov_camera_module_s_fmt,
+};
+
+static struct v4l2_subdev_ops ov8858_camera_module_ops = {
+       .core = &ov8858_camera_module_core_ops,
+       .video = &ov8858_camera_module_video_ops,
+       .pad = &ov8858_camera_module_pad_ops
+};
+
+static struct ov_camera_module_custom_config ov8858_custom_config = {
+       .start_streaming = ov8858_start_streaming,
+       .stop_streaming = ov8858_stop_streaming,
+       .s_ctrl = ov8858_s_ctrl,
+       .s_ext_ctrls = ov8858_s_ext_ctrls,
+       .g_ctrl = ov8858_g_ctrl,
+       .g_timings = ov8858_g_timings,
+       .check_camera_id = ov8858_check_camera_id,
+       .g_exposure_valid_frame = ov8858_g_exposure_valid_frame,
+       .read_otp = ov8858_otp_read,
+       .configs = ov8858_configs,
+       .num_configs = ARRAY_SIZE(ov8858_configs),
+       .power_up_delays_ms = {5, 20, 0}
+};
+
+static int ov8858_probe(
+       struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       dev_info(&client->dev, "probing...\n");
+       ov8858_filltimings(&ov8858_custom_config);
+       v4l2_i2c_subdev_init(&ov8858.sd, client, &ov8858_camera_module_ops);
+       ov8858.custom = ov8858_custom_config;
+
+       dev_info(&client->dev, "probing successful\n");
+
+       return 0;
+}
+
+static int ov8858_remove(struct i2c_client *client)
+{
+       struct ov_camera_module *cam_mod = i2c_get_clientdata(client);
+
+       dev_info(&client->dev, "removing device...\n");
+
+       if (!client->adapter)
+               return -ENODEV; /* our client isn't attached */
+
+       ov_camera_module_release(cam_mod);
+
+       kfree(otp_ptr);
+       dev_info(&client->dev, "removed\n");
+       return 0;
+}
+
+static const struct i2c_device_id ov8858_id[] = {
+       { ov8858_DRIVER_NAME, 0 },
+       { }
+};
+
+static const struct of_device_id ov8858_of_match[] = {
+       {.compatible = "omnivision,ov8858-v4l2-i2c-subdev"},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov8858_id);
+
+static struct i2c_driver ov8858_i2c_driver = {
+       .driver = {
+               .name = ov8858_DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = ov8858_of_match
+       },
+       .probe = ov8858_probe,
+       .remove = ov8858_remove,
+       .id_table = ov8858_id,
+};
+
+module_i2c_driver(ov8858_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for ov8858");
+MODULE_AUTHOR("George");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.c
new file mode 100644 (file)
index 0000000..ef404b8
--- /dev/null
@@ -0,0 +1,1127 @@
+/*
+ * ov_camera_module.c
+ *
+ * Generic omnivision sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-core.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+#include <media/v4l2-controls_rockchip.h>
+
+#include "ov_camera_module.h"
+
+static struct ov_camera_module *to_ov_camera_module(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct ov_camera_module, sd);
+}
+
+/* ======================================================================== */
+
+static void ov_camera_module_reset(
+       struct ov_camera_module *cam_mod)
+{
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       cam_mod->inited = false;
+       cam_mod->active_config = NULL;
+       cam_mod->update_config = true;
+       cam_mod->frm_fmt_valid = false;
+       cam_mod->frm_intrvl_valid = false;
+       cam_mod->exp_config.auto_exp = false;
+       cam_mod->exp_config.auto_gain = false;
+       cam_mod->wb_config.auto_wb = false;
+       cam_mod->hflip = false;
+       cam_mod->vflip = false;
+       cam_mod->auto_adjust_fps = true;
+       cam_mod->rotation = 0;
+       cam_mod->ctrl_updt = 0;
+       cam_mod->state = OV_CAMERA_MODULE_POWER_OFF;
+       cam_mod->state_before_suspend = OV_CAMERA_MODULE_POWER_OFF;
+
+       cam_mod->exp_config.exp_time = 0;
+       cam_mod->exp_config.gain = 0;
+       cam_mod->vts_cur = 0;
+}
+
+/* ======================================================================== */
+
+static void ov_camera_module_set_active_config(
+       struct ov_camera_module *cam_mod,
+       struct ov_camera_module_config *new_config)
+{
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (IS_ERR_OR_NULL(new_config)) {
+               cam_mod->active_config = new_config;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "no active config\n");
+       } else {
+               cam_mod->ctrl_updt &= OV_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP |
+                       OV_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN |
+                       OV_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+               if (new_config->auto_exp_enabled !=
+                       cam_mod->exp_config.auto_exp) {
+                       cam_mod->ctrl_updt |=
+                               OV_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP;
+                       cam_mod->exp_config.auto_exp =
+                               new_config->auto_exp_enabled;
+               }
+               if (new_config->auto_gain_enabled !=
+                       cam_mod->exp_config.auto_gain) {
+                       cam_mod->ctrl_updt |=
+                               OV_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN;
+                       cam_mod->exp_config.auto_gain =
+                               new_config->auto_gain_enabled;
+               }
+               if (new_config->auto_wb_enabled !=
+                       cam_mod->wb_config.auto_wb) {
+                       cam_mod->ctrl_updt |=
+                               OV_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+                       cam_mod->wb_config.auto_wb =
+                               new_config->auto_wb_enabled;
+               }
+               if (new_config != cam_mod->active_config) {
+                       cam_mod->update_config = true;
+                       cam_mod->active_config = new_config;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "activating config '%s'\n",
+                               cam_mod->active_config->name);
+               }
+       }
+}
+
+/* ======================================================================== */
+
+static struct ov_camera_module_config *ov_camera_module_find_config(
+       struct ov_camera_module *cam_mod,
+       struct v4l2_mbus_framefmt *fmt,
+       struct v4l2_subdev_frame_interval *frm_intrvl)
+{
+       u32 i;
+       unsigned long gcdiv;
+       struct v4l2_subdev_frame_interval norm_interval;
+
+       if (!IS_ERR_OR_NULL(fmt))
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "%dx%d, fmt code 0x%04x\n",
+                       fmt->width, fmt->height, fmt->code);
+
+       if (!IS_ERR_OR_NULL(frm_intrvl))
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "frame interval %d/%d\n",
+                       frm_intrvl->interval.numerator,
+                       frm_intrvl->interval.denominator);
+
+       for (i = 0; i < cam_mod->custom.num_configs; i++) {
+               if (!IS_ERR_OR_NULL(frm_intrvl)) {
+                       gcdiv = gcd(cam_mod->custom.configs[i].
+                               frm_intrvl.interval.numerator,
+                               cam_mod->custom.configs[i].
+                                       frm_intrvl.interval.denominator);
+                       norm_interval.interval.numerator =
+                               cam_mod->custom.configs[i].
+                                       frm_intrvl.interval.numerator / gcdiv;
+                       norm_interval.interval.denominator =
+                               cam_mod->custom.configs[i].
+                               frm_intrvl.interval.denominator / gcdiv;
+                       if ((frm_intrvl->interval.numerator !=
+                               norm_interval.interval.numerator) ||
+                               (frm_intrvl->interval.denominator !=
+                               norm_interval.interval.denominator))
+                               continue;
+               }
+               if (!IS_ERR_OR_NULL(fmt)) {
+                       if ((cam_mod->custom.configs[i].frm_fmt.width !=
+                               fmt->width) ||
+                               (cam_mod->custom.configs[i].frm_fmt.height !=
+                               fmt->height) ||
+                               (cam_mod->custom.configs[i].frm_fmt.code !=
+                               fmt->code)) {
+                               continue;
+                       }
+               }
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "found matching config %s\n",
+                       cam_mod->custom.configs[i].name);
+               return &cam_mod->custom.configs[i];
+       }
+       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+               "no matching config found\n");
+
+       return ERR_PTR(-EINVAL);
+}
+
+/* ======================================================================== */
+
+static int ov_camera_module_write_config(
+       struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+       struct ov_camera_module_reg *reg_table;
+       u32 reg_table_num_entries;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "no active sensor configuration");
+               ret = -EFAULT;
+               goto err;
+       }
+
+       if (!cam_mod->inited) {
+               cam_mod->active_config->soft_reset = true;
+               reg_table = cam_mod->active_config->reg_table;
+               reg_table_num_entries =
+                       cam_mod->active_config->reg_table_num_entries;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "write config %s\n",
+                               cam_mod->active_config->name);
+       } else {
+               if (cam_mod->active_config->reg_diff_table &&
+               cam_mod->active_config->reg_diff_table_num_entries) {
+                       cam_mod->active_config->soft_reset = false;
+                       reg_table = cam_mod->active_config->reg_diff_table;
+                       reg_table_num_entries =
+                               cam_mod->active_config->reg_diff_table_num_entries;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "write config %s%s\n",
+                               cam_mod->active_config->name, "_diff");
+               } else {
+                       cam_mod->active_config->soft_reset = true;
+                       reg_table = cam_mod->active_config->reg_table;
+                       reg_table_num_entries =
+                               cam_mod->active_config->reg_table_num_entries;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "write config %s\n",
+                               cam_mod->active_config->name);
+               }
+       }
+
+       if (!IS_ERR_OR_NULL(cam_mod->custom.set_flip))
+               cam_mod->custom.set_flip(cam_mod,
+                       reg_table, reg_table_num_entries);
+
+       ret = pltfrm_camera_module_write_reglist(&cam_mod->sd,
+               reg_table, reg_table_num_entries);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+       ret = pltfrm_camera_module_patch_config(&cam_mod->sd,
+               &cam_mod->frm_fmt,
+               &cam_mod->frm_intrvl);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+static int ov_camera_module_attach(
+       struct ov_camera_module *cam_mod)
+{
+       int ret = 0;
+       struct ov_camera_module_custom_config *custom;
+
+       custom = &cam_mod->custom;
+
+       if (custom->check_camera_id) {
+               ov_camera_module_s_power(&cam_mod->sd, 1);
+               ret = custom->check_camera_id(cam_mod);
+               ov_camera_module_s_power(&cam_mod->sd, 0);
+               if (ret != 0)
+                       goto err;
+       }
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       ov_camera_module_release(cam_mod);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_try_fmt(struct v4l2_subdev *sd,
+       struct v4l2_mbus_framefmt *fmt)
+{
+       struct ov_camera_module *cam_mod = to_ov_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n",
+               fmt->width, fmt->height, fmt->code);
+
+       if (IS_ERR_OR_NULL(ov_camera_module_find_config(cam_mod, fmt, NULL))) {
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "format not supported\n");
+               return -EINVAL;
+       }
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "format supported\n");
+
+       return 0;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_s_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format)
+{
+       struct ov_camera_module *cam_mod =  to_ov_camera_module(sd);
+       struct v4l2_mbus_framefmt *fmt = &format->format;
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n",
+               fmt->width, fmt->height, fmt->code);
+
+       if (IS_ERR_OR_NULL(ov_camera_module_find_config(cam_mod, fmt, NULL))) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "format %dx%d, code 0x%04x, not supported\n",
+                       fmt->width, fmt->height, fmt->code);
+               ret = -EINVAL;
+               goto err;
+       }
+       cam_mod->frm_fmt_valid = true;
+       cam_mod->frm_fmt = *fmt;
+       if (cam_mod->frm_intrvl_valid) {
+               ov_camera_module_set_active_config(cam_mod,
+                       ov_camera_module_find_config(cam_mod,
+                               fmt, &cam_mod->frm_intrvl));
+       }
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_g_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format)
+{
+       struct ov_camera_module *cam_mod =  to_ov_camera_module(sd);
+       struct v4l2_mbus_framefmt *fmt = &format->format;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (cam_mod->active_config) {
+               fmt->code = cam_mod->active_config->frm_fmt.code;
+               fmt->width = cam_mod->active_config->frm_fmt.width;
+               fmt->height = cam_mod->active_config->frm_fmt.height;
+               return 0;
+       }
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "no active config\n");
+
+       return -1;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_s_frame_interval(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_frame_interval *interval)
+{
+       struct ov_camera_module *cam_mod = to_ov_camera_module(sd);
+       unsigned long gcdiv;
+       struct v4l2_subdev_frame_interval norm_interval;
+       int ret = 0;
+
+       if ((interval->interval.denominator == 0) ||
+               (interval->interval.numerator == 0)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "invalid frame interval %d/%d\n",
+                       interval->interval.numerator,
+                       interval->interval.denominator);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d/%d (%dfps)\n",
+               interval->interval.numerator, interval->interval.denominator,
+               (interval->interval.denominator +
+               (interval->interval.numerator >> 1)) /
+               interval->interval.numerator);
+
+       /* normalize interval */
+       gcdiv = gcd(interval->interval.numerator,
+               interval->interval.denominator);
+       norm_interval.interval.numerator =
+               interval->interval.numerator / gcdiv;
+       norm_interval.interval.denominator =
+               interval->interval.denominator / gcdiv;
+
+       if (IS_ERR_OR_NULL(ov_camera_module_find_config(cam_mod,
+                       NULL, &norm_interval))) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "frame interval %d/%d not supported\n",
+                       interval->interval.numerator,
+                       interval->interval.denominator);
+               ret = -EINVAL;
+               goto err;
+       }
+       cam_mod->frm_intrvl_valid = true;
+       cam_mod->frm_intrvl = norm_interval;
+       if (cam_mod->frm_fmt_valid) {
+               ov_camera_module_set_active_config(cam_mod,
+                       ov_camera_module_find_config(cam_mod,
+                               &cam_mod->frm_fmt, interval));
+       }
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       int ret = 0;
+       struct ov_camera_module *cam_mod =  to_ov_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", enable);
+
+       if (enable) {
+               if (cam_mod->state == OV_CAMERA_MODULE_STREAMING)
+                       return 0;
+               if (IS_ERR_OR_NULL(cam_mod->active_config)) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "no active sensor configuration, cannot start streaming\n");
+                       ret = -EFAULT;
+                       goto err;
+               }
+               if (cam_mod->state != OV_CAMERA_MODULE_SW_STANDBY) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "sensor is not powered on (in state %d), cannot start streaming\n",
+                               cam_mod->state);
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if (cam_mod->update_config)
+                       ret = ov_camera_module_write_config(cam_mod);
+                       if (IS_ERR_VALUE(ret))
+                               goto err;
+
+               ret = cam_mod->custom.start_streaming(cam_mod);
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+               if (!cam_mod->inited && cam_mod->update_config)
+                       cam_mod->inited = true;
+               cam_mod->update_config = false;
+               cam_mod->ctrl_updt = 0;
+               mdelay(cam_mod->custom.power_up_delays_ms[2]);
+               cam_mod->state = OV_CAMERA_MODULE_STREAMING;
+       } else {
+               int pclk;
+               int wait_ms;
+               struct isp_supplemental_sensor_mode_data timings;
+
+               if (cam_mod->state != OV_CAMERA_MODULE_STREAMING)
+                       return 0;
+               ret = cam_mod->custom.stop_streaming(cam_mod);
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+
+               ret = ov_camera_module_ioctl(sd,
+                                       RK_VIDIOC_SENSOR_MODE_DATA,
+                                       &timings);
+
+               cam_mod->state = OV_CAMERA_MODULE_SW_STANDBY;
+
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+
+               pclk = timings.vt_pix_clk_freq_hz / 1000;
+
+               if (!pclk)
+                       goto err;
+
+               wait_ms =
+                       (timings.line_length_pck *
+                       timings.frame_length_lines) /
+                       pclk;
+
+               /*
+                * wait for a frame period to make sure that there is
+                * no pending frame left.
+                */
+
+               msleep(wait_ms + 1);
+       }
+
+       cam_mod->state_before_suspend = cam_mod->state;
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_s_power(struct v4l2_subdev *sd, int on)
+{
+       int ret = 0;
+       struct ov_camera_module *cam_mod =  to_ov_camera_module(sd);
+       struct v4l2_subdev *af_ctrl;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", on);
+
+       if (on) {
+               if (cam_mod->state == OV_CAMERA_MODULE_POWER_OFF) {
+                       ret = pltfrm_camera_module_s_power(&cam_mod->sd, 1);
+                       if (!IS_ERR_VALUE(ret)) {
+                               mdelay(cam_mod->custom.power_up_delays_ms[0]);
+                               cam_mod->state = OV_CAMERA_MODULE_HW_STANDBY;
+                       }
+               }
+               if (cam_mod->state == OV_CAMERA_MODULE_HW_STANDBY) {
+                       ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_PD,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+                       if (!IS_ERR_VALUE(ret)) {
+                               mdelay(cam_mod->custom.power_up_delays_ms[1]);
+                               cam_mod->state = OV_CAMERA_MODULE_SW_STANDBY;
+                               if (!IS_ERR_OR_NULL(
+                                   cam_mod->custom.init_common) &&
+                                   cam_mod->custom.init_common(
+                                   cam_mod))
+                               usleep_range(1000, 1500);
+
+                               af_ctrl = pltfrm_camera_module_get_af_ctrl(sd);
+                               if (!IS_ERR_OR_NULL(af_ctrl)) {
+                                       v4l2_subdev_call(af_ctrl,
+                                                        core, init, 0);
+                               }
+                       }
+               }
+               if (cam_mod->update_config) {
+                       ov_camera_module_write_config(cam_mod);
+                       cam_mod->update_config = false;
+               }
+       } else {
+               if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) {
+                       ret = ov_camera_module_s_stream(sd, 0);
+                       if (!IS_ERR_VALUE(ret))
+                               cam_mod->state = OV_CAMERA_MODULE_SW_STANDBY;
+               }
+               if (cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY) {
+                       ret = pltfrm_camera_module_set_pin_state(
+                               &cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_PD,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+
+                       if (!IS_ERR_VALUE(ret))
+                               cam_mod->state = OV_CAMERA_MODULE_HW_STANDBY;
+               }
+               if (cam_mod->state == OV_CAMERA_MODULE_HW_STANDBY) {
+                       ret = pltfrm_camera_module_s_power(&cam_mod->sd, 0);
+                       if (!IS_ERR_VALUE(ret)) {
+                               cam_mod->state = OV_CAMERA_MODULE_POWER_OFF;
+                               ov_camera_module_reset(cam_mod);
+                       }
+               }
+       }
+
+       cam_mod->state_before_suspend = cam_mod->state;
+
+       if (IS_ERR_VALUE(ret)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "%s failed, camera left in state %d\n",
+                       on ? "on" : "off", cam_mod->state);
+               goto err;
+       } else
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "camera powered %s\n", on ? "on" : "off");
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_g_ctrl(struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       struct ov_camera_module *cam_mod = to_ov_camera_module(sd);
+       int ret;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, " id 0x%x\n", ctrl->id);
+
+       if (ctrl->id == V4L2_CID_FLASH_LED_MODE) {
+               ctrl->value = cam_mod->exp_config.flash_mode;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_FLASH_LED_MODE %d\n",
+                       ctrl->value);
+               return 0;
+       }
+
+       if (IS_ERR_OR_NULL(cam_mod->active_config)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "no active configuration\n");
+               return -EFAULT;
+       }
+
+       if (ctrl->id == RK_V4L2_CID_VBLANKING) {
+               ctrl->value = cam_mod->active_config->v_blanking_time_us;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "RK_V4L2_CID_VBLANKING %d\n",
+                       ctrl->value);
+               return 0;
+       }
+
+       if ((cam_mod->state != OV_CAMERA_MODULE_SW_STANDBY) &&
+               (cam_mod->state != OV_CAMERA_MODULE_STREAMING)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "cannot get controls when camera is off\n");
+               return -EFAULT;
+       }
+
+       if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
+               struct v4l2_subdev *af_ctrl;
+
+               af_ctrl = pltfrm_camera_module_get_af_ctrl(sd);
+               if (!IS_ERR_OR_NULL(af_ctrl)) {
+                       ret = v4l2_subdev_call(af_ctrl, core, g_ctrl, ctrl);
+                       return ret;
+               }
+       }
+
+       if (!IS_ERR_OR_NULL(cam_mod->custom.g_ctrl)) {
+               ret = cam_mod->custom.g_ctrl(cam_mod, ctrl->id);
+               if (IS_ERR_VALUE(ret))
+                       return ret;
+       }
+
+       switch (ctrl->id) {
+       case V4L2_CID_GAIN:
+               ctrl->value = cam_mod->exp_config.gain;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                            "V4L2_CID_GAIN %d\n",
+                            ctrl->value);
+               break;
+       case V4L2_CID_EXPOSURE:
+               ctrl->value = cam_mod->exp_config.exp_time;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                            "V4L2_CID_EXPOSURE %d\n",
+                            ctrl->value);
+               break;
+       case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+               ctrl->value = cam_mod->wb_config.temperature;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+               ctrl->value = cam_mod->wb_config.preset_id;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               ctrl->value = cam_mod->exp_config.auto_gain;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTOGAIN %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_EXPOSURE_AUTO:
+               ctrl->value = cam_mod->exp_config.auto_exp;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_EXPOSURE_AUTO %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               ctrl->value = cam_mod->wb_config.auto_wb;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_WHITE_BALANCE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_FOCUS_ABSOLUTE:
+               ctrl->value = cam_mod->af_config.abs_pos;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_FOCUS_ABSOLUTE %d\n",
+                       ctrl->value);
+               break;
+       case V4L2_CID_HFLIP:
+       case V4L2_CID_VFLIP:
+               /* TBD */
+               /* fallthrough */
+       default:
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "failed, unknown ctrl %d\n", ctrl->id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int flash_light_ctrl(
+               struct v4l2_subdev *sd,
+               struct ov_camera_module *cam_mod,
+               int value)
+{
+       return 0;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls)
+{
+       int i;
+       int ctrl_cnt = 0;
+       struct ov_camera_module *cam_mod =  to_ov_camera_module(sd);
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+       if (ctrls->count == 0)
+               return -EINVAL;
+
+       for (i = 0; i < ctrls->count; i++) {
+               struct v4l2_ext_control *ctrl;
+               u32 ctrl_updt = 0;
+
+               ctrl = &ctrls->controls[i];
+
+               switch (ctrl->id) {
+               case V4L2_CID_GAIN:
+                       ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_GAIN;
+                       cam_mod->exp_config.gain = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_GAIN %d\n",
+                       ctrl->value);
+                       break;
+               case RK_V4L2_CID_GAIN_PERCENT:
+                       ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_GAIN;
+                       cam_mod->exp_config.gain_percent = ctrl->value;
+                       break;
+               case V4L2_CID_FLASH_LED_MODE:
+                       ret = flash_light_ctrl(sd, cam_mod, ctrl->value);
+                       if (ret == 0) {
+                               cam_mod->exp_config.flash_mode = ctrl->value;
+                               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                                       "V4L2_CID_FLASH_LED_MODE %d\n",
+                                       ctrl->value);
+                       }
+                       break;
+               case V4L2_CID_EXPOSURE:
+                       ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_EXP_TIME;
+                       cam_mod->exp_config.exp_time = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_EXPOSURE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+                       ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE;
+                       cam_mod->wb_config.temperature = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+                       ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_PRESET_WB;
+                       cam_mod->wb_config.preset_id = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_AUTOGAIN:
+                       ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN;
+                       cam_mod->exp_config.auto_gain = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTOGAIN %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_EXPOSURE_AUTO:
+                       ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP;
+                       cam_mod->exp_config.auto_exp = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_EXPOSURE_AUTO %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_AUTO_WHITE_BALANCE:
+                       ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+                       cam_mod->wb_config.auto_wb = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_AUTO_WHITE_BALANCE %d\n",
+                       ctrl->value);
+                       break;
+               case RK_V4L2_CID_AUTO_FPS:
+                       cam_mod->auto_adjust_fps = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "RK_V4L2_CID_AUTO_FPS %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_FOCUS_ABSOLUTE:
+                       {
+                               struct v4l2_subdev *af_ctrl;
+
+                               af_ctrl = pltfrm_camera_module_get_af_ctrl(sd);
+                               if (!IS_ERR_OR_NULL(af_ctrl)) {
+                                       struct v4l2_control single_ctrl;
+
+                                       single_ctrl.id =
+                                               V4L2_CID_FOCUS_ABSOLUTE;
+                                       single_ctrl.value = ctrl->value;
+                                       ret = v4l2_subdev_call(af_ctrl,
+                                               core, s_ctrl, &single_ctrl);
+                                       return ret;
+                               }
+                       }
+                       ctrl_updt =
+                               OV_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE;
+                       cam_mod->af_config.abs_pos = ctrl->value;
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_FOCUS_ABSOLUTE %d\n",
+                       ctrl->value);
+                       break;
+               case V4L2_CID_HFLIP:
+                       if (ctrl->value)
+                               cam_mod->hflip = true;
+                       else
+                               cam_mod->hflip = false;
+                       break;
+               case V4L2_CID_VFLIP:
+                       if (ctrl->value)
+                               cam_mod->vflip = true;
+                       else
+                               cam_mod->vflip = false;
+                       break;
+               default:
+                       pltfrm_camera_module_pr_warn(&cam_mod->sd,
+                       "ignoring unknown ctrl 0x%x\n", ctrl->id);
+                       break;
+               }
+
+               if (cam_mod->state != OV_CAMERA_MODULE_SW_STANDBY &&
+               cam_mod->state != OV_CAMERA_MODULE_STREAMING)
+                       cam_mod->ctrl_updt |= ctrl_updt;
+               else if (ctrl_updt)
+                       ctrl_cnt++;
+       }
+
+       /* if camera module is already streaming, write through */
+       if (ctrl_cnt &&
+               (cam_mod->state == OV_CAMERA_MODULE_STREAMING ||
+               cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY)) {
+               struct ov_camera_module_ext_ctrls ov_ctrls;
+
+               ov_ctrls.ctrls =
+               (struct ov_camera_module_ext_ctrl *)
+               kmalloc(ctrl_cnt * sizeof(struct ov_camera_module_ext_ctrl),
+                       GFP_KERNEL);
+
+               if (ov_ctrls.ctrls) {
+                       for (i = 0; i < ctrl_cnt; i++) {
+                               ov_ctrls.ctrls[i].id = ctrls->controls[i].id;
+                               ov_ctrls.ctrls[i].value =
+                                       ctrls->controls[i].value;
+                       }
+
+                       ov_ctrls.count = ctrl_cnt;
+
+                       ret = cam_mod->custom.s_ext_ctrls(cam_mod, &ov_ctrls);
+
+                       kfree(ov_ctrls.ctrls);
+               } else {
+                       ret = -ENOMEM;
+               }
+               if (IS_ERR_VALUE(ret))
+                       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                               "failed with error %d\n", ret);
+       }
+
+       return ret;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       struct ov_camera_module *cam_mod =  to_ov_camera_module(sd);
+       struct v4l2_ext_control ext_ctrl[1];
+       struct v4l2_ext_controls ext_ctrls;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+               "0x%x 0x%x\n", ctrl->id, ctrl->value);
+
+       ext_ctrl[0].id = ctrl->id;
+       ext_ctrl[0].value = ctrl->value;
+
+       ext_ctrls.count = 1;
+       ext_ctrls.controls = ext_ctrl;
+
+       return ov_camera_module_s_ext_ctrls(sd, &ext_ctrls);
+}
+
+/* ======================================================================== */
+
+long ov_camera_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg)
+{
+       struct ov_camera_module *cam_mod =  to_ov_camera_module(sd);
+       int ret;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "cmd: 0x%x\n", cmd);
+
+       if (cmd == RK_VIDIOC_SENSOR_MODE_DATA) {
+               struct ov_camera_module_timings ov_timings;
+               struct isp_supplemental_sensor_mode_data *timings =
+               (struct isp_supplemental_sensor_mode_data *)arg;
+
+               if (cam_mod->custom.g_timings)
+                       ret = cam_mod->custom.g_timings(cam_mod, &ov_timings);
+               else
+                       ret = -EPERM;
+
+               if (IS_ERR_VALUE(ret)) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "failed with error %d\n", ret);
+                       return ret;
+               }
+
+               timings->sensor_output_width = ov_timings.sensor_output_width;
+               timings->sensor_output_height = ov_timings.sensor_output_height;
+               timings->crop_horizontal_start =
+                       ov_timings.crop_horizontal_start;
+               timings->crop_vertical_start = ov_timings.crop_vertical_start;
+               timings->crop_horizontal_end = ov_timings.crop_horizontal_end;
+               timings->crop_vertical_end = ov_timings.crop_vertical_end;
+               timings->line_length_pck = ov_timings.line_length_pck;
+               timings->frame_length_lines = ov_timings.frame_length_lines;
+               timings->vt_pix_clk_freq_hz = ov_timings.vt_pix_clk_freq_hz;
+               timings->binning_factor_x = ov_timings.binning_factor_x;
+               timings->binning_factor_y = ov_timings.binning_factor_y;
+               timings->coarse_integration_time_max_margin =
+                       ov_timings.coarse_integration_time_max_margin;
+               timings->coarse_integration_time_min =
+                       ov_timings.coarse_integration_time_min;
+               timings->fine_integration_time_max_margin =
+                       ov_timings.fine_integration_time_max_margin;
+               timings->fine_integration_time_min =
+                       ov_timings.fine_integration_time_min;
+
+               if (cam_mod->custom.g_exposure_valid_frame)
+                       timings->exposure_valid_frame =
+                               cam_mod->custom.g_exposure_valid_frame(cam_mod);
+               if (cam_mod->exp_config.exp_time)
+                       timings->exp_time = cam_mod->exp_config.exp_time;
+               else
+                       timings->exp_time = ov_timings.exp_time;
+               if (cam_mod->exp_config.gain)
+                       timings->gain = cam_mod->exp_config.gain;
+               else
+                       timings->gain = ov_timings.gain;
+               return ret;
+       } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) {
+               struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg;
+               struct ov_camera_module_config *config;
+
+               if (cam_mod->custom.num_configs <= 0) {
+                       pltfrm_camera_module_pr_err(&cam_mod->sd,
+                               "cam_mod->custom.num_configs is NULL, Get interface config failed!\n");
+                       return -EINVAL;
+               }
+
+               if (IS_ERR_OR_NULL(cam_mod->active_config))
+                       config = &cam_mod->custom.configs[0];
+               else
+                       config = cam_mod->active_config;
+
+               *itf_cfg = config->itf_cfg;
+
+               pltfrm_camera_module_ioctl(sd, PLTFRM_CIFCAM_G_ITF_CFG, arg);
+               return 0;
+       } else if (cmd == PLTFRM_CIFCAM_ATTACH) {
+               ov_camera_module_init(cam_mod, &cam_mod->custom);
+               pltfrm_camera_module_ioctl(sd, cmd, arg);
+               return ov_camera_module_attach(cam_mod);
+       }
+
+       ret = pltfrm_camera_module_ioctl(sd, cmd, arg);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_get_flip_mirror(
+       struct ov_camera_module *cam_mod)
+{
+       return pltfrm_camera_module_get_flip_mirror(&cam_mod->sd);
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_enum_frameintervals(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_frame_interval_enum *fie)
+{
+       struct ov_camera_module *cam_mod =  to_ov_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", fie->index);
+
+       if (fie->index >= cam_mod->custom.num_configs)
+               return -EINVAL;
+       fie->code =
+               cam_mod->custom.configs[fie->index].frm_fmt.code;
+       fie->width =
+               cam_mod->custom.configs[fie->index].frm_fmt.width;
+       fie->height =
+               cam_mod->custom.configs[fie->index].frm_fmt.height;
+       fie->interval.numerator = cam_mod->custom.
+               configs[fie->index].frm_intrvl.interval.numerator;
+       fie->interval.denominator = cam_mod->custom.
+               configs[fie->index].frm_intrvl.interval.denominator;
+       return 0;
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_write_reglist(
+       struct ov_camera_module *cam_mod,
+       const struct ov_camera_module_reg reglist[],
+       int len)
+{
+       return pltfrm_camera_module_write_reglist(&cam_mod->sd, reglist, len);
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_write_reg(
+       struct ov_camera_module *cam_mod,
+       u16 reg,
+       u8 val)
+{
+       return pltfrm_camera_module_write_reg(&cam_mod->sd, reg, val);
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_read_reg(
+       struct ov_camera_module *cam_mod,
+       u16 data_length,
+       u16 reg,
+       u32 *val)
+{
+       return pltfrm_camera_module_read_reg(&cam_mod->sd,
+               data_length, reg, val);
+}
+
+/* ======================================================================== */
+
+int ov_camera_module_read_reg_table(
+       struct ov_camera_module *cam_mod,
+       u16 reg,
+       u32 *val)
+{
+       int i;
+
+       if (cam_mod->state == OV_CAMERA_MODULE_STREAMING)
+               return pltfrm_camera_module_read_reg(&cam_mod->sd,
+                       1, reg, val);
+
+       if (!IS_ERR_OR_NULL(cam_mod->active_config)) {
+               for (
+                       i = cam_mod->active_config->reg_table_num_entries - 1;
+                       i > 0;
+                       i--) {
+                       if (cam_mod->active_config->reg_table[i].reg == reg) {
+                               *val = cam_mod->active_config->reg_table[i].val;
+                               return 0;
+                       }
+               }
+       }
+
+       if (cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY)
+               return pltfrm_camera_module_read_reg(&cam_mod->sd,
+                       1, reg, val);
+
+       return -EFAULT;
+}
+
+int ov_camera_module_init(struct ov_camera_module *cam_mod,
+       struct ov_camera_module_custom_config *custom)
+{
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       ov_camera_module_reset(cam_mod);
+
+       if (IS_ERR_OR_NULL(custom->start_streaming) ||
+               IS_ERR_OR_NULL(custom->stop_streaming) ||
+               IS_ERR_OR_NULL(custom->s_ctrl) ||
+               IS_ERR_OR_NULL(custom->g_ctrl)) {
+               pltfrm_camera_module_pr_err(&cam_mod->sd,
+                       "mandatory callback function is missing\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = pltfrm_camera_module_init(&cam_mod->sd, &cam_mod->pltfm_data);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_PD,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+       ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd,
+                               PLTFRM_CAMERA_MODULE_PIN_RESET,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+       if (IS_ERR_VALUE(ret)) {
+               ov_camera_module_release(cam_mod);
+               goto err;
+       }
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(&cam_mod->sd,
+               "failed with error %d\n", ret);
+       return ret;
+}
+
+void ov_camera_module_release(struct ov_camera_module *cam_mod)
+{
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       if (cam_mod->otp_work.wq) {
+               flush_workqueue(cam_mod->otp_work.wq);
+               destroy_workqueue(cam_mod->otp_work.wq);
+               cam_mod->otp_work.wq = NULL;
+       }
+
+       cam_mod->custom.configs = NULL;
+
+       pltfrm_camera_module_release(&cam_mod->sd);
+       v4l2_device_unregister_subdev(&cam_mod->sd);
+}
diff --git a/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.h b/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.h
new file mode 100644 (file)
index 0000000..25af7f0
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * ov_camera_module.h
+ *
+ * Generic omnivision sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#ifndef OV_CAMERA_MODULE_H
+#define OV_CAMERA_MODULE_H
+#include <linux/workqueue.h>
+#include <linux/platform_data/rk_isp10_platform_camera_module.h>
+#include <linux/platform_data/rk_isp10_platform.h>
+
+/*
+ * TODO: references to v4l2 should be reomved from here and go into a
+ * platform dependent wrapper
+ */
+
+#define OV_CAMERA_MODULE_REG_TYPE_DATA PLTFRM_CAMERA_MODULE_REG_TYPE_DATA
+#define OV_CAMERA_MODULE_REG_TYPE_TIMEOUT PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT
+#define ov_camera_module_csi_config
+#define ov_camera_module_reg pltfrm_camera_module_reg
+#define OV_FLIP_BIT_MASK                                  0x2
+#define OV_MIRROR_BIT_MASK                                0x1
+
+#define OV_CAMERA_MODULE_CTRL_UPDT_GAIN                                0x01
+#define OV_CAMERA_MODULE_CTRL_UPDT_EXP_TIME                    0x02
+#define OV_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE      0x04
+#define OV_CAMERA_MODULE_CTRL_UPDT_AUTO_WB                     0x08
+#define OV_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN           0x10
+#define OV_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP                    0x20
+#define OV_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE      0x40
+#define OV_CAMERA_MODULE_CTRL_UPDT_PRESET_WB           0x80
+
+enum ov_camera_module_state {
+       OV_CAMERA_MODULE_POWER_OFF = 0,
+       OV_CAMERA_MODULE_HW_STANDBY = 1,
+       OV_CAMERA_MODULE_SW_STANDBY = 2,
+       OV_CAMERA_MODULE_STREAMING = 3
+};
+
+struct ov_camera_module;
+
+struct ov_camera_module_timings {
+       /* public */
+       u32 coarse_integration_time_min;
+       u32 coarse_integration_time_max_margin;
+       u32 fine_integration_time_min;
+       u32 fine_integration_time_max_margin;
+       u32 frame_length_lines;
+       u32 line_length_pck;
+       u32 vt_pix_clk_freq_hz;
+       u32 sensor_output_width;
+       u32 sensor_output_height;
+       u32 crop_horizontal_start; /* Sensor crop start cord. (x0,y0) */
+       u32 crop_vertical_start;
+       u32 crop_horizontal_end; /* Sensor crop end cord. (x1,y1) */
+       u32 crop_vertical_end;
+       u8 binning_factor_x;
+       u8 binning_factor_y;
+       u32 exp_time;
+       u32 gain;
+};
+
+struct ov_camera_module_config {
+       const char *name;
+       struct v4l2_mbus_framefmt frm_fmt;
+       struct v4l2_subdev_frame_interval frm_intrvl;
+       bool auto_exp_enabled;
+       bool auto_gain_enabled;
+       bool auto_wb_enabled;
+       struct ov_camera_module_reg *reg_table;
+       u32 reg_table_num_entries;
+       struct ov_camera_module_reg *reg_diff_table;
+       u32 reg_diff_table_num_entries;
+       u32 v_blanking_time_us;
+       u32 line_length_pck;
+       u32 frame_length_lines;
+       struct ov_camera_module_timings timings;
+       bool soft_reset;
+       bool ignore_measurement_check;
+
+       struct pltfrm_cam_itf itf_cfg;
+};
+
+struct ov_camera_module_exp_config {
+       s32 exp_time;
+       bool auto_exp;
+       u16 gain;
+       u16 gain_percent;
+       bool auto_gain;
+       enum v4l2_flash_led_mode flash_mode;
+};
+
+struct ov_camera_module_wb_config {
+       u32 temperature;
+       u32 preset_id;
+       bool auto_wb;
+};
+
+struct ov_camera_module_af_config {
+       u32 abs_pos;
+       u32 rel_pos;
+};
+
+struct ov_camera_module_ext_ctrl {
+       /* public */
+       u32 id;
+       u32 value;
+       __u32 reserved2[1];
+};
+
+struct ov_camera_module_ext_ctrls {
+       /* public */
+       u32 count;
+       struct ov_camera_module_ext_ctrl *ctrls;
+};
+
+/*
+ *     start_streaming: (mandatory) will be called when sensor should be
+ *             put into streaming mode right after the base config has been
+ *             written to the sensor. After a successful call of this function
+ *             the sensor should start delivering frame data.
+ *
+ *     stop_streaming: (mandatory) will be called when sensor should stop
+ *             delivering data. After a successful call of this function the
+ *             sensor should not deliver any more frame data.
+ *
+ *     check_camera_id: (optional) will be called when the sensor is
+ *             powered on. If provided should check the sensor ID/version
+ *             required by the custom driver. Register access should be
+ *             possible when this function is invoked.
+ *
+ *     s_ctrl: (mandatory) will be called at the successful end of
+ *             ov_camera_module_s_ctrl with the ctrl_id as argument.
+ *
+ *     priv: (optional) for private data used by the custom driver.
+ */
+struct ov_camera_module_custom_config {
+       int (*start_streaming)(struct ov_camera_module *cam_mod);
+       int (*stop_streaming)(struct ov_camera_module *cam_mod);
+       int (*check_camera_id)(struct ov_camera_module *cam_mod);
+       int (*s_ctrl)(struct ov_camera_module *cam_mod, u32 ctrl_id);
+       int (*g_ctrl)(struct ov_camera_module *cam_mod, u32 ctrl_id);
+       int (*g_timings)(struct ov_camera_module *cam_mod,
+               struct ov_camera_module_timings *timings);
+       int (*g_exposure_valid_frame)(struct ov_camera_module *cam_mod);
+       int (*s_ext_ctrls)(struct ov_camera_module *cam_mod,
+               struct ov_camera_module_ext_ctrls *ctrls);
+       int (*set_flip)(
+               struct ov_camera_module *cam_mod,
+               struct pltfrm_camera_module_reg reglist[],
+               int len);
+       int (*init_common)(struct ov_camera_module *cam_mod);
+       int (*read_otp)(struct ov_camera_module *cam_mod);
+       struct ov_camera_module_config *configs;
+       u32 num_configs;
+       u32 power_up_delays_ms[3];
+       void *priv;
+};
+
+struct ov_camera_module_otp_work {
+       struct work_struct work;
+       struct workqueue_struct *wq;
+       void *cam_mod;
+};
+
+struct ov_camera_module {
+       /* public */
+       struct v4l2_subdev sd;
+       struct v4l2_mbus_framefmt frm_fmt;
+       struct v4l2_subdev_frame_interval frm_intrvl;
+       struct ov_camera_module_exp_config exp_config;
+       struct ov_camera_module_wb_config wb_config;
+       struct ov_camera_module_af_config af_config;
+       struct ov_camera_module_custom_config custom;
+       enum ov_camera_module_state state;
+       enum ov_camera_module_state state_before_suspend;
+       struct ov_camera_module_config *active_config;
+       struct ov_camera_module_otp_work otp_work;
+       u32 ctrl_updt;
+       u32 vts_cur;
+       u32 vts_min;
+       bool auto_adjust_fps;
+       bool update_config;
+       bool frm_fmt_valid;
+       bool frm_intrvl_valid;
+       bool hflip;
+       bool vflip;
+       u32 rotation;
+       void *pltfm_data;
+       bool inited;
+};
+
+#define ov_camera_module_pr_info(cam_mod, fmt, arg...) \
+       pltfrm_camera_module_pr_info(&(cam_mod)->sd, fmt, ## arg)
+#define ov_camera_module_pr_debug(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_debug(&(cam_mod)->sd, fmt, ## arg)
+#define ov_camera_module_pr_warn(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_warn(&(cam_mod)->sd, fmt, ## arg)
+#define ov_camera_module_pr_err(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_err(&(cam_mod)->sd, fmt, ## arg)
+
+int ov_camera_module_write_reglist(
+       struct ov_camera_module *cam_mod,
+       const struct ov_camera_module_reg reglist[],
+       int len);
+
+int ov_camera_module_write_reg(
+       struct ov_camera_module *cam_mod,
+       u16 reg,
+       u8 val);
+
+int ov_camera_module_read_reg(
+       struct ov_camera_module *cam_mod,
+       u16 data_length,
+       u16 reg,
+       u32 *val);
+
+int ov_camera_module_read_reg_table(
+       struct ov_camera_module *cam_mod,
+       u16 reg,
+       u32 *val);
+
+int ov_camera_module_s_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format);
+
+int ov_camera_module_g_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format);
+
+int ov_camera_module_s_frame_interval(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_frame_interval *interval);
+
+int ov_camera_module_s_stream(
+       struct v4l2_subdev *sd,
+       int enable);
+
+int ov_camera_module_s_power(
+       struct v4l2_subdev *sd,
+       int on);
+
+int ov_camera_module_g_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl);
+
+int ov_camera_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl);
+
+int ov_camera_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls);
+
+int ov_camera_module_enum_frameintervals(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_frame_interval_enum *fie);
+
+int ov_camera_module_init(
+       struct ov_camera_module *cam_mod,
+       struct ov_camera_module_custom_config *custom);
+
+void ov_camera_module_release(
+       struct ov_camera_module *cam_mod);
+
+long ov_camera_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg);
+
+int ov_camera_module_get_flip_mirror(
+       struct ov_camera_module *cam_mod);
+#endif
diff --git a/drivers/media/i2c/soc_camera/rockchip/rk_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/rk_camera_module.c
new file mode 100644 (file)
index 0000000..80f3330
--- /dev/null
@@ -0,0 +1,1630 @@
+/*
+ * rk_camera_module.c
+ * (Based on Intel driver for sofiaxxx)
+ *
+ * Copyright (C) 2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef CONFIG_OF
+#error "this file requires device tree support"
+#endif
+
+#ifndef SOFIA_3G_CAMERA_MODULE_H
+#define SOFIA_3G_CAMERA_MODULE_H
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#ifdef CONFIG_PLATFORM_DEVICE_PM
+#include <linux/device_state_pm.h>
+#endif
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <media/v4l2-subdev.h>
+#include <linux/videodev2.h>
+#include <linux/lcm.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_data/rk_isp10_platform_camera_module.h>
+#include <linux/platform_data/rk_isp10_platform.h>
+#include <media/v4l2-controls_rockchip.h>
+
+#define OF_OV_GPIO_PD "rockchip,pd-gpio"
+#define OF_OV_GPIO_PWR "rockchip,pwr-gpio"
+#define OF_OV_GPIO_FLASH "rockchip,flash-gpio"
+#define OF_OV_GPIO_TORCH "rockchip,torch-gpio"
+#define OF_OV_GPIO_RESET "rockchip,rst-gpio"
+
+#define OF_CAMERA_MODULE_NAME "rockchip,camera-module-name"
+#define OF_CAMERA_MODULE_LEN_NAME "rockchip,camera-module-len-name"
+#define OF_CAMERA_MODULE_FOV_H "rockchip,camera-module-fov-h"
+#define OF_CAMERA_MODULE_FOV_V "rockchip,camera-module-fov-v"
+#define OF_CAMERA_MODULE_ORIENTATION "rockchip,camera-module-orientation"
+#define OF_CAMERA_MODULE_FOCAL_LENGTH "rockchip,camera-module-focal-length"
+#define OF_CAMERA_MODULE_FOCUS_DISTANCE "rockchip,camera-module-focus-distance"
+#define OF_CAMERA_MODULE_IQ_MIRROR "rockchip,camera-module-iq-mirror"
+#define OF_CAMERA_MODULE_IQ_FLIP "rockchip,camera-module-iq-flip"
+#define OF_CAMERA_MODULE_FLIP "rockchip,camera-module-flip"
+#define OF_CAMERA_MODULE_MIRROR "rockchip,camera-module-mirror"
+#define OF_CAMERA_FLASH_SUPPORT "rockchip,camera-module-flash-support"
+#define OF_CAMERA_FLASH_EXP_PERCENT "rockchip,camera-module-flash-exp-percent"
+#define OF_CAMERA_FLASH_TURN_ON_TIME "rockchip,camera-module-flash-turn-on-time"
+#define OF_CAMERA_FLASH_ON_TIMEOUT "rockchip,camera-module-flash-on-timeout"
+#define OF_CAMERA_MODULE_DEFRECT0 "rockchip,camera-module-defrect0"
+#define OF_CAMERA_MODULE_DEFRECT1 "rockchip,camera-module-defrect1"
+#define OF_CAMERA_MODULE_DEFRECT2 "rockchip,camera-module-defrect2"
+#define OF_CAMERA_MODULE_DEFRECT3 "rockchip,camera-module-defrect3"
+#define OF_CAMERA_MODULE_MIPI_DPHY_INDEX "rockchip,camera-module-mipi-dphy-index"
+
+#define OF_CAMERA_MODULE_REGULATORS "rockchip,camera-module-regulator-names"
+#define OF_CAMERA_MODULE_REGULATOR_VOLTAGES "rockchip,camera-module-regulator-voltages"
+#define OF_CAMERA_MODULE_MCLK_NAME "rockchip,camera-module-mclk-name"
+#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default"
+#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep"
+
+const char *PLTFRM_CAMERA_MODULE_PIN_PD = OF_OV_GPIO_PD;
+const char *PLTFRM_CAMERA_MODULE_PIN_PWR = OF_OV_GPIO_PWR;
+const char *PLTFRM_CAMERA_MODULE_PIN_FLASH = OF_OV_GPIO_FLASH;
+const char *PLTFRM_CAMERA_MODULE_PIN_TORCH = OF_OV_GPIO_TORCH;
+const char *PLTFRM_CAMERA_MODULE_PIN_RESET = OF_OV_GPIO_RESET;
+
+#define I2C_M_WR 0
+#define I2C_MSG_MAX 300
+#define I2C_DATA_MAX (I2C_MSG_MAX * 3)
+
+struct pltfrm_camera_module_gpio {
+       int pltfrm_gpio;
+       const char *label;
+       enum of_gpio_flags active_low;
+};
+
+struct pltfrm_camera_module_regulator {
+       struct regulator *regulator;
+       unsigned int min_uV;
+       unsigned int max_uV;
+};
+
+struct pltfrm_camera_module_regulators {
+       unsigned int cnt;
+       struct pltfrm_camera_module_regulator *regulator;
+};
+
+struct pltfrm_camera_module_fl {
+       const char *flash_driver_name;
+       /* flash ,torch */
+       char fl_init_status;
+       struct pltfrm_camera_module_gpio *fl_flash;
+       struct pltfrm_camera_module_gpio *fl_torch;
+       struct v4l2_subdev *flsh_ctrl;
+};
+
+struct pltfrm_camera_module_info_s {
+       const char *module_name;
+       const char *len_name;
+       const char *fov_h;
+       const char *fov_v;
+       const char *focal_length;
+       const char *focus_distance;
+       int facing;
+       int orientation;
+       bool iq_mirror;
+       bool iq_flip;
+       int flip;
+       int mirror;
+       int flash_support;
+       int flash_exp_percent;
+       int flash_turn_on_time;
+       int flash_on_timeout;
+};
+
+struct pltfrm_camera_module_mipi {
+       unsigned int dphy_index;
+};
+
+struct pltfrm_camera_module_itf {
+       union {
+               struct pltfrm_camera_module_mipi mipi;
+       } itf;
+};
+
+struct pltfrm_camera_module_data {
+       struct pltfrm_camera_module_gpio gpios[6];
+       struct pinctrl *pinctrl;
+       struct pinctrl_state *pins_default;
+       struct pinctrl_state *pins_sleep;
+       struct v4l2_subdev *af_ctrl;
+       /* move to struct pltfrm_camera_module_fl */
+       /* const char *flash_driver_name; */
+       struct pltfrm_camera_module_fl fl_ctrl;
+
+       struct pltfrm_camera_module_info_s info;
+       struct pltfrm_camera_module_itf itf;
+       struct pltfrm_cam_defrect defrects[4];
+       struct clk *mclk;
+       struct pltfrm_soc_cfg *soc_cfg;
+       struct pltfrm_camera_module_regulators regulators;
+
+       void *priv;
+};
+
+/* ======================================================================== */
+
+static int pltfrm_camera_module_set_pinctrl_state(
+       struct v4l2_subdev *sd,
+       struct pinctrl_state *state)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+       int ret = 0;
+
+       if (!IS_ERR_OR_NULL(state)) {
+               ret = pinctrl_select_state(pdata->pinctrl, state);
+               if (IS_ERR_VALUE(ret))
+                       pltfrm_camera_module_pr_debug(sd,
+                               "could not set pins\n");
+       }
+
+       return ret;
+}
+
+static int pltfrm_camera_module_init_gpio(
+       struct v4l2_subdev *sd)
+{
+       int ret = 0;
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+       int i = 0;
+
+       ret = pltfrm_camera_module_set_pinctrl_state(sd, pdata->pins_default);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       for (i = 0; i < ARRAY_SIZE(pdata->gpios); i++) {
+               if (gpio_is_valid(pdata->gpios[i].pltfrm_gpio)) {
+                       if (pdata->gpios[i].label ==
+                               PLTFRM_CAMERA_MODULE_PIN_FLASH ||
+                               pdata->gpios[i].label ==
+                               PLTFRM_CAMERA_MODULE_PIN_TORCH) {
+                               if (pdata->fl_ctrl.fl_init_status &&
+                   pdata->fl_ctrl.fl_flash->pltfrm_gpio ==
+                   pdata->fl_ctrl.fl_torch->pltfrm_gpio) {
+                                       pltfrm_camera_module_pr_info(
+                                               sd,
+                                               "fl gpio has been inited, continue!\n");
+                                       continue;
+                               }
+                               pdata->fl_ctrl.fl_init_status = 1;
+                       }
+
+                       pltfrm_camera_module_pr_debug(
+                           sd,
+                               "requesting GPIO #%d ('%s')\n",
+                               pdata->gpios[i].pltfrm_gpio,
+                               pdata->gpios[i].label);
+                       ret = gpio_request_one(
+                               pdata->gpios[i].pltfrm_gpio,
+                               GPIOF_DIR_OUT,
+                               pdata->gpios[i].label);
+                       if (IS_ERR_VALUE(ret)) {
+                               if ((pdata->gpios[i].label ==
+                                       PLTFRM_CAMERA_MODULE_PIN_RESET) ||
+                                       (pdata->gpios[i].label ==
+                                       PLTFRM_CAMERA_MODULE_PIN_PWR)) {
+                                       pltfrm_camera_module_pr_warn(sd,
+                                       "GPIO #%d ('%s') may be reused!\n",
+                                       pdata->gpios[i].pltfrm_gpio,
+                                       pdata->gpios[i].label);
+                               } else {
+                                       pltfrm_camera_module_pr_err(sd,
+                                               "failed to request GPIO #%d ('%s')\n",
+                                               pdata->gpios[i].pltfrm_gpio,
+                                               pdata->gpios[i].label);
+                                       goto err;
+                               }
+                       }
+
+                       if (pdata->gpios[i].label ==
+                               PLTFRM_CAMERA_MODULE_PIN_PD)
+                               ret = pltfrm_camera_module_set_pin_state(sd,
+                               pdata->gpios[i].label,
+                               PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+                       else if (pdata->gpios[i].label ==
+                               PLTFRM_CAMERA_MODULE_PIN_RESET)
+                               ret = pltfrm_camera_module_set_pin_state(sd,
+                                       pdata->gpios[i].label,
+                                       PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+                       else
+                               ret = pltfrm_camera_module_set_pin_state(sd,
+                                       pdata->gpios[i].label,
+                                       PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE
+                                       );
+               }
+       }
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(sd, "failed with error %d\n", ret);
+       for (; i < ARRAY_SIZE(pdata->gpios); i++)
+               pdata->gpios[i].pltfrm_gpio = -1;
+
+       return ret;
+}
+
+static struct pltfrm_camera_module_data *pltfrm_camera_module_get_data(
+       struct v4l2_subdev *sd)
+{
+       int ret = 0;
+       int elem_size, elem_index;
+       const char *str = "";
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct device_node *np = of_node_get(client->dev.of_node);
+       struct device_node *af_ctrl_node = NULL;
+       struct i2c_client *af_ctrl_client = NULL;
+       struct device_node *fl_ctrl_node = NULL;
+       struct i2c_client *fl_ctrl_client = NULL;
+       struct pltfrm_camera_module_data *pdata = NULL;
+       struct property *prop;
+
+       pltfrm_camera_module_pr_debug(sd, "\n");
+
+       pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+       if (IS_ERR_OR_NULL(pdata)) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       client->dev.platform_data = pdata;
+
+       ret = of_property_read_string(np, OF_CAMERA_MODULE_MCLK_NAME, &str);
+       if (ret) {
+               pltfrm_camera_module_pr_err(sd,
+                       "cannot not get camera-module-mclk-name property of node %s\n",
+                       np->name);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       pdata->mclk = devm_clk_get(&client->dev, str);
+       if (IS_ERR_OR_NULL(pdata->mclk)) {
+               pltfrm_camera_module_pr_err(sd,
+                       "cannot not get %s property of node %s\n",
+                       str, np->name);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       ret = of_property_read_string(np,
+                       "rockchip,camera-module-facing", &str);
+       if (ret) {
+               pltfrm_camera_module_pr_err(sd,
+                       "cannot not get camera-module-facing property of node %s\n",
+                       np->name);
+       } else {
+               pltfrm_camera_module_pr_debug(sd,
+                       "camera module camera-module-facing driver is %s\n",
+                       str);
+       }
+       pdata->info.facing = -1;
+       if (!strcmp(str, "back"))
+               pdata->info.facing = 0;
+       else if (!strcmp(str, "front"))
+               pdata->info.facing = 1;
+
+       ret = of_property_read_string(np, "rockchip,flash-driver",
+                       &pdata->fl_ctrl.flash_driver_name);
+       if (ret) {
+               pltfrm_camera_module_pr_debug(sd,
+                               "cannot not get flash-driver property of node %s\n",
+                               np->name);
+               pdata->fl_ctrl.flash_driver_name = "0";
+       } else {
+               pltfrm_camera_module_pr_info(sd,
+                       "camera module flash driver is %s\n",
+                       (char *)pdata->fl_ctrl.flash_driver_name);
+
+               /*parse flash node info*/
+               fl_ctrl_node = of_parse_phandle(np, "rockchip,fl-ctrl", 0);
+               if (!IS_ERR_OR_NULL(fl_ctrl_node)) {
+                       fl_ctrl_client =
+                               of_find_i2c_device_by_node(fl_ctrl_node);
+                       of_node_put(fl_ctrl_node);
+                       if (IS_ERR_OR_NULL(fl_ctrl_client)) {
+                               pltfrm_camera_module_pr_err(sd,
+                                       "cannot not get node\n");
+                               ret = -EFAULT;
+                               goto err;
+                       }
+                       pdata->fl_ctrl.flsh_ctrl =
+                               i2c_get_clientdata(fl_ctrl_client);
+                       if (IS_ERR_OR_NULL(pdata->fl_ctrl.flsh_ctrl)) {
+                               pltfrm_camera_module_pr_warn(sd,
+                                       "cannot not get camera i2c client, maybe not yet created, deferring device probing...\n");
+                               ret = -EPROBE_DEFER;
+                               goto err;
+                       }
+                       pltfrm_camera_module_pr_info(sd,
+                               "camera module has flash driver %s\n",
+                               pltfrm_dev_string(pdata->fl_ctrl.flsh_ctrl));
+               }
+       }
+
+       af_ctrl_node = of_parse_phandle(np, "rockchip,af-ctrl", 0);
+       if (!IS_ERR_OR_NULL(af_ctrl_node)) {
+               af_ctrl_client = of_find_i2c_device_by_node(af_ctrl_node);
+               of_node_put(af_ctrl_node);
+               if (IS_ERR_OR_NULL(af_ctrl_client)) {
+                       pltfrm_camera_module_pr_err(sd,
+                               "cannot not get node\n");
+                       ret = -EFAULT;
+                       goto err;
+               }
+               pdata->af_ctrl = i2c_get_clientdata(af_ctrl_client);
+               if (IS_ERR_OR_NULL(pdata->af_ctrl)) {
+                       pltfrm_camera_module_pr_warn(sd,
+                               "cannot not get camera i2c client, maybe not yet created, deferring device probing...\n");
+                       ret = -EPROBE_DEFER;
+                       goto err;
+               }
+               pltfrm_camera_module_pr_info(sd,
+                       "camera module has auto focus controller %s\n",
+                       pltfrm_dev_string(pdata->af_ctrl));
+       }
+
+       pdata->pinctrl = devm_pinctrl_get(&client->dev);
+       if (!IS_ERR(pdata->pinctrl)) {
+               pdata->pins_default = pinctrl_lookup_state(
+                                       pdata->pinctrl,
+                                       OF_CAMERA_PINCTRL_STATE_DEFAULT);
+               if (IS_ERR(pdata->pins_default))
+                       pltfrm_camera_module_pr_warn(sd,
+                       "could not get default pinstate\n");
+
+               pdata->pins_sleep = pinctrl_lookup_state(
+                               pdata->pinctrl, OF_CAMERA_PINCTRL_STATE_SLEEP);
+               if (IS_ERR(pdata->pins_sleep))
+                       pltfrm_camera_module_pr_warn(sd,
+                       "could not get sleep pinstate\n");
+       }
+
+       elem_size = of_property_count_elems_of_size(
+               np,
+               OF_CAMERA_MODULE_REGULATOR_VOLTAGES,
+               sizeof(u32));
+       prop = of_find_property(
+               np,
+               OF_CAMERA_MODULE_REGULATORS,
+               NULL);
+       if (!IS_ERR_VALUE(elem_size) &&
+               !IS_ERR_OR_NULL(prop)) {
+               struct pltfrm_camera_module_regulator *regulator;
+
+               pdata->regulators.regulator = devm_kzalloc(&client->dev,
+                       elem_size *
+                       sizeof(struct pltfrm_camera_module_regulator),
+                       GFP_KERNEL);
+               if (!pdata->regulators.regulator) {
+                       pltfrm_camera_module_pr_err(sd,
+                               "could not malloc pltfrm_camera_module_regulator\n");
+                       goto err;
+               }
+               pdata->regulators.cnt = elem_size;
+               str = NULL;
+               elem_index = 0;
+               regulator = pdata->regulators.regulator;
+               do {
+                       str = of_prop_next_string(prop, str);
+                       if (!str) {
+                               pltfrm_camera_module_pr_err(sd,
+                                       "%s is not match %s in dts\n",
+                                       OF_CAMERA_MODULE_REGULATORS,
+                                       OF_CAMERA_MODULE_REGULATOR_VOLTAGES);
+                               break;
+                       }
+                       regulator->regulator =
+                               devm_regulator_get_optional(&client->dev, str);
+                       if (IS_ERR(regulator->regulator))
+                               pltfrm_camera_module_pr_err(sd,
+                                       "devm_regulator_get %s failed\n",
+                                       str);
+                       of_property_read_u32_index(
+                               np,
+                               OF_CAMERA_MODULE_REGULATOR_VOLTAGES,
+                               elem_index++,
+                               &regulator->min_uV);
+                       regulator->max_uV = regulator->min_uV;
+                       regulator++;
+               } while (--elem_size);
+       }
+       pdata->gpios[0].label = PLTFRM_CAMERA_MODULE_PIN_PD;
+       pdata->gpios[0].pltfrm_gpio = of_get_named_gpio_flags(
+               np,
+               pdata->gpios[0].label,
+               0,
+               &pdata->gpios[0].active_low);
+
+       pdata->gpios[1].label = PLTFRM_CAMERA_MODULE_PIN_PWR;
+       pdata->gpios[1].pltfrm_gpio = of_get_named_gpio_flags(
+               np,
+               pdata->gpios[1].label,
+               0,
+               &pdata->gpios[1].active_low);
+
+       pdata->gpios[2].label = PLTFRM_CAMERA_MODULE_PIN_FLASH;
+       pdata->gpios[2].pltfrm_gpio = of_get_named_gpio_flags(
+               np,
+               pdata->gpios[2].label,
+               0,
+               &pdata->gpios[2].active_low);
+
+       /*set fl_ctrl  flash reference*/
+       pdata->fl_ctrl.fl_flash = &pdata->gpios[2];
+
+       pdata->gpios[3].label = PLTFRM_CAMERA_MODULE_PIN_TORCH;
+       pdata->gpios[3].pltfrm_gpio = of_get_named_gpio_flags(
+               np,
+               pdata->gpios[3].label,
+               0,
+               &pdata->gpios[3].active_low);
+
+       /*set fl_ctrl torch reference*/
+       pdata->fl_ctrl.fl_torch = &pdata->gpios[3];
+
+       pdata->gpios[4].label = PLTFRM_CAMERA_MODULE_PIN_RESET;
+       pdata->gpios[4].pltfrm_gpio = of_get_named_gpio_flags(
+               np,
+               pdata->gpios[4].label,
+               0,
+               &pdata->gpios[4].active_low);
+
+       ret = of_property_read_string(np, OF_CAMERA_MODULE_NAME,
+                       &pdata->info.module_name);
+       ret |= of_property_read_string(np, OF_CAMERA_MODULE_LEN_NAME,
+                       &pdata->info.len_name);
+       if (ret) {
+               pltfrm_camera_module_pr_err(
+                       sd,
+                       "could not get module %s and %s from dts!\n",
+                       OF_CAMERA_MODULE_NAME,
+                       OF_CAMERA_MODULE_LEN_NAME);
+       }
+
+       if (of_property_read_u32(
+               np,
+               OF_CAMERA_MODULE_ORIENTATION,
+               (unsigned int *)&pdata->info.orientation)) {
+               pdata->info.orientation = -1;
+               pltfrm_camera_module_pr_err(
+                       sd,
+                       "could not get module %s from dts!\n",
+                       OF_CAMERA_MODULE_ORIENTATION);
+       }
+
+       if (of_property_read_u32(
+               np,
+               OF_CAMERA_MODULE_FLIP,
+               (unsigned int *)&pdata->info.flip)) {
+               pdata->info.flip = -1;
+               pltfrm_camera_module_pr_err(
+                       sd,
+                       "could not get module %s from dts!\n",
+                       OF_CAMERA_MODULE_FLIP);
+       }
+
+       if (of_property_read_u32(
+               np,
+               OF_CAMERA_MODULE_MIRROR,
+               (unsigned int *)&pdata->info.mirror)) {
+               pdata->info.mirror = -1;
+               pltfrm_camera_module_pr_err(
+                       sd,
+                       "could not get module %s from dts!\n",
+                       OF_CAMERA_MODULE_MIRROR);
+       }
+
+       if (of_property_read_u32(
+               np,
+               OF_CAMERA_FLASH_SUPPORT,
+               (unsigned int *)&pdata->info.flash_support)) {
+               pdata->info.flash_support = -1;
+               pltfrm_camera_module_pr_err(
+                       sd,
+                       "could not get module %s from dts!\n",
+                       OF_CAMERA_FLASH_SUPPORT);
+       }
+
+       ret = of_property_read_string(np, OF_CAMERA_MODULE_FOV_H,
+                       &pdata->info.fov_h);
+       ret |= of_property_read_string(np, OF_CAMERA_MODULE_FOV_V,
+                       &pdata->info.fov_v);
+       if (ret) {
+               pltfrm_camera_module_pr_debug(
+                       sd,
+                       "could not get module %s and %s from dts!",
+                       OF_CAMERA_MODULE_FOV_H,
+                       OF_CAMERA_MODULE_FOV_V);
+       }
+
+       ret = of_property_read_string(np, OF_CAMERA_MODULE_FOCAL_LENGTH,
+                       &pdata->info.focal_length);
+       if (ret) {
+               pltfrm_camera_module_pr_debug(
+                       sd,
+                       "could not get %s from dts!\n",
+                       OF_CAMERA_MODULE_FOCAL_LENGTH);
+       }
+       ret = of_property_read_string(np, OF_CAMERA_MODULE_FOCUS_DISTANCE,
+                       &pdata->info.focus_distance);
+       if (ret) {
+               pltfrm_camera_module_pr_debug(
+                       sd,
+                       "could not get %s from dts!\n",
+                       OF_CAMERA_MODULE_FOCUS_DISTANCE);
+       }
+
+       ret = 0;
+       of_property_read_u32(np, OF_CAMERA_MODULE_IQ_MIRROR, &ret);
+       pdata->info.iq_mirror = (ret == 0) ? false : true;
+
+       ret = 0;
+       of_property_read_u32(np, OF_CAMERA_MODULE_IQ_FLIP, &ret);
+       pdata->info.iq_flip = (ret == 0) ? false : true;
+
+       if (of_property_read_u32(
+               np,
+               OF_CAMERA_FLASH_EXP_PERCENT,
+               (unsigned int *)&pdata->info.flash_exp_percent)) {
+               pdata->info.flash_exp_percent = -1;
+               pltfrm_camera_module_pr_debug(
+                       sd,
+                       "could not get module %s from dts!\n",
+                       OF_CAMERA_FLASH_EXP_PERCENT);
+       }
+
+       if (of_property_read_u32(
+               np,
+               OF_CAMERA_FLASH_TURN_ON_TIME,
+               (unsigned int *)&pdata->info.flash_turn_on_time)) {
+               pdata->info.flash_turn_on_time = -1;
+               pltfrm_camera_module_pr_debug(
+                       sd,
+                       "could not get module %s from dts!\n",
+                       OF_CAMERA_FLASH_TURN_ON_TIME);
+       }
+
+       if (of_property_read_u32(
+               np,
+               OF_CAMERA_FLASH_ON_TIMEOUT,
+               (unsigned int *)&pdata->info.flash_on_timeout)) {
+               pdata->info.flash_on_timeout = -1;
+               pltfrm_camera_module_pr_debug(
+                       sd,
+                       "could not get module %s from dts!\n",
+                       OF_CAMERA_FLASH_ON_TIMEOUT);
+       }
+
+       of_property_read_u32_array(
+               np,
+               OF_CAMERA_MODULE_DEFRECT0,
+               (unsigned int *)&pdata->defrects[0],
+               6);
+       of_property_read_u32_array(
+               np,
+               OF_CAMERA_MODULE_DEFRECT1,
+               (unsigned int *)&pdata->defrects[1],
+               6);
+       of_property_read_u32_array(
+               np,
+               OF_CAMERA_MODULE_DEFRECT2,
+               (unsigned int *)&pdata->defrects[2],
+               6);
+       of_property_read_u32_array(
+               np,
+               OF_CAMERA_MODULE_DEFRECT3,
+               (unsigned int *)&pdata->defrects[3],
+               6);
+
+       if (of_property_read_u32(
+               np,
+               OF_CAMERA_MODULE_MIPI_DPHY_INDEX,
+               (unsigned int *)&pdata->itf.itf.mipi.dphy_index)) {
+               pdata->itf.itf.mipi.dphy_index = -1;
+               pltfrm_camera_module_pr_debug(
+                       sd,
+                       "could not get module %s from dts!\n",
+                       OF_CAMERA_MODULE_MIPI_DPHY_INDEX);
+       }
+
+       of_node_put(np);
+       return pdata;
+err:
+       pltfrm_camera_module_pr_err(sd, "failed with error %d\n", ret);
+       if (!IS_ERR_OR_NULL(pdata->regulators.regulator)) {
+               devm_kfree(
+                       &client->dev,
+                       pdata->regulators.regulator);
+               pdata->regulators.regulator = NULL;
+       }
+
+       if (!IS_ERR_OR_NULL(pdata->mclk)) {
+               devm_clk_put(&client->dev, pdata->mclk);
+               pdata->mclk = NULL;
+       }
+       if (!IS_ERR_OR_NULL(pdata)) {
+               devm_kfree(&client->dev, pdata);
+               pdata = NULL;
+       }
+       of_node_put(np);
+       return ERR_PTR(ret);
+}
+
+static int pltfrm_camera_module_pix_frmt2code(
+       const char *pix_frmt)
+{
+       if (strcmp(pix_frmt, "BAYER_BGGR8") == 0)
+               return MEDIA_BUS_FMT_SBGGR8_1X8;
+       if (strcmp(pix_frmt, "BAYER_GBRG8") == 0)
+               return MEDIA_BUS_FMT_SGBRG8_1X8;
+       if (strcmp(pix_frmt, "BAYER_GRBG8") == 0)
+               return MEDIA_BUS_FMT_SGRBG8_1X8;
+       if (strcmp(pix_frmt, "BAYER_RGGB8") == 0)
+               return MEDIA_BUS_FMT_SRGGB8_1X8;
+       if (strcmp(pix_frmt, "BAYER_BGGR10") == 0)
+               return MEDIA_BUS_FMT_SBGGR10_1X10;
+       if (strcmp(pix_frmt, "BAYER_GBRG10") == 0)
+               return MEDIA_BUS_FMT_SGBRG10_1X10;
+       if (strcmp(pix_frmt, "BAYER_GRBG10") == 0)
+               return MEDIA_BUS_FMT_SGRBG10_1X10;
+       if (strcmp(pix_frmt, "BAYER_RGGB10") == 0)
+               return MEDIA_BUS_FMT_SRGGB10_1X10;
+       if (strcmp(pix_frmt, "BAYER_BGGR12") == 0)
+               return MEDIA_BUS_FMT_SBGGR12_1X12;
+       if (strcmp(pix_frmt, "BAYER_GBRG12") == 0)
+               return MEDIA_BUS_FMT_SGBRG12_1X12;
+       if (strcmp(pix_frmt, "BAYER_GRBG12") == 0)
+               return MEDIA_BUS_FMT_SGRBG12_1X12;
+       if (strcmp(pix_frmt, "BAYER_RGGB12") == 0)
+               return MEDIA_BUS_FMT_SRGGB12_1X12;
+       if (strcmp(pix_frmt, "YUYV8") == 0)
+               return MEDIA_BUS_FMT_YUYV8_2X8;
+       if (strcmp(pix_frmt, "YUYV10") == 0)
+               return MEDIA_BUS_FMT_YUYV10_2X10;
+       if (strcmp(pix_frmt, "UYUV8") == 0)
+               return MEDIA_BUS_FMT_UYVY8_2X8;
+       return -EINVAL;
+}
+
+static int pltfrm_camera_module_config_matches(
+       struct v4l2_subdev *sd,
+       struct device_node *config,
+       struct v4l2_mbus_framefmt *frm_fmt,
+       struct v4l2_subdev_frame_interval *frm_intrvl)
+{
+       int ret = 0;
+       struct property *prop;
+       const char *of_pix_fmt;
+       bool match = true;
+       u32 min, min2, max, max2;
+       u32 numerator, denominator;
+
+       pltfrm_camera_module_pr_debug(sd,
+               "pix_frm %d, %dx%d@%d/%dfps, config %s\n",
+               frm_fmt->code, frm_fmt->width, frm_fmt->height,
+               frm_intrvl->interval.denominator,
+               frm_intrvl->interval.numerator,
+               config->name);
+
+       /* check pixel format */
+       of_property_for_each_string(config, "rockchip,frm-pixel-format",
+               prop, of_pix_fmt) {
+               if (pltfrm_camera_module_pix_frmt2code(of_pix_fmt) ==
+                       frm_fmt->code) {
+                       match = true;
+                       break;
+               }
+       }
+
+       if (!match)
+               return 0;
+
+       /* check frame width */
+       ret = of_property_read_u32(config, "rockchip,frm-width", &min);
+       if (ret == -EINVAL) {
+               ret = of_property_read_u32_index(config,
+                               "rockchip,frm-width-range", 0, &min);
+               if (ret == -EINVAL) {
+                       min = 0;
+                       max = UINT_MAX;
+               } else if (IS_ERR_VALUE(ret)) {
+                       pltfrm_camera_module_pr_err(sd,
+                                       "malformed property 'rockchip,frm-width-range'\n");
+                       goto err;
+               } else {
+                       ret = of_property_read_u32_index(config,
+                                       "rockchip,frm-width-range", 1, &max);
+                       if (IS_ERR_VALUE(ret)) {
+                               pltfrm_camera_module_pr_err(sd,
+                               "malformed property 'rockchip,frm-width-range'\n");
+                               goto err;
+                       }
+               }
+       } else if (IS_ERR_VALUE(ret)) {
+               pltfrm_camera_module_pr_err(sd,
+                               "malformed property 'rockchip,frm-width'\n");
+               goto err;
+       } else {
+               max = min;
+       }
+       if ((frm_fmt->width < min) || (frm_fmt->width > max))
+               return 0;
+
+       /* check frame height */
+       ret = of_property_read_u32(config, "rockchip,frm-height", &min);
+       if (ret == -EINVAL) {
+               ret = of_property_read_u32_index(config,
+                               "rockchip,frm-height-range", 0, &min);
+               if (ret == -EINVAL) {
+                       min = 0;
+                       max = UINT_MAX;
+               } else if (IS_ERR_VALUE(ret)) {
+                       pltfrm_camera_module_pr_err(sd,
+                               "malformed property 'rockchip,frm-height-range'\n");
+                       goto err;
+               } else {
+                       ret = of_property_read_u32_index(config,
+                                       "rockchip,frm-height-range", 1, &max);
+                       if (IS_ERR_VALUE(ret)) {
+                               pltfrm_camera_module_pr_err(sd,
+                                       "malformed property 'rockchip,frm-height-range'\n");
+                               goto err;
+                       }
+               }
+       } else if (IS_ERR_VALUE(ret)) {
+               pltfrm_camera_module_pr_err(sd,
+                               "malformed property 'rockchip,frm-height'\n");
+               goto err;
+       } else {
+               max = min;
+       }
+       if ((frm_fmt->height < min) || (frm_fmt->height > max))
+               return 0;
+
+       /* check frame interval */
+       ret = of_property_read_u32_index(config,
+                               "rockchip,frm-interval", 0, &min);
+       if (ret == -EINVAL) {
+               ret = of_property_read_u32_index(config,
+                               "rockchip,frm-interval-range", 0, &min);
+               if (ret == -EINVAL) {
+                       min = 0;
+                       max = UINT_MAX;
+               } else if (IS_ERR_VALUE(ret)) {
+                       pltfrm_camera_module_pr_err(sd,
+                                       "malformed property 'rockchip,frm-interval-range'\n");
+                       goto err;
+               } else {
+                       ret |= of_property_read_u32_index(config,
+                               "rockchip,frm-interval-range", 1, &min2);
+                       ret |= of_property_read_u32_index(config,
+                               "rockchip,frm-interval-range", 2, &max);
+                       ret |= of_property_read_u32_index(config,
+                               "rockchip,frm-interval-range", 3, &max2);
+                       if (IS_ERR_VALUE(ret)) {
+                               pltfrm_camera_module_pr_err(sd,
+                                       "malformed property 'rockchip,frm-interval-range'\n");
+                               goto err;
+                       }
+               }
+       } else if (IS_ERR_VALUE(ret)) {
+               pltfrm_camera_module_pr_err(sd,
+                       "malformed property 'rockchip,frm-interval'\n");
+               goto err;
+       } else {
+               ret = of_property_read_u32_index(config,
+                       "rockchip,frm-interval", 1, &min2);
+               if (IS_ERR_VALUE(ret)) {
+                       pltfrm_camera_module_pr_err(sd,
+                               "malformed property 'rockchip,frm-interval'\n");
+                       goto err;
+               }
+               max = min;
+               max2 = min2;
+       }
+
+       /* normalize frame intervals */
+       denominator = lcm(min2, frm_intrvl->interval.denominator);
+       denominator = lcm(max2, denominator);
+       numerator = denominator / frm_intrvl->interval.denominator *
+               frm_intrvl->interval.numerator;
+
+       min = denominator / min2 * min;
+       max = denominator / max2 * max;
+
+       if ((numerator < min) || (numerator > max))
+               return 0;
+
+       return 1;
+err:
+       pltfrm_camera_module_pr_err(sd,
+                       "failed with error %d\n", ret);
+       return ret;
+}
+
+static int pltfrm_camera_module_write_reglist_node(
+       struct v4l2_subdev *sd,
+       struct device_node *config_node)
+{
+       struct property *reg_table_prop;
+       struct pltfrm_camera_module_reg *reg_table = NULL;
+       u32 reg_table_num_entries;
+       u32 i = 0;
+       int ret = 0;
+
+       reg_table_prop = of_find_property(config_node, "rockchip,reg-table",
+               &reg_table_num_entries);
+       if (!IS_ERR_OR_NULL(reg_table_prop)) {
+               if (((reg_table_num_entries / 12) == 0) ||
+               (reg_table_num_entries % 3)) {
+                       pltfrm_camera_module_pr_err(sd,
+                               "wrong register format in %s, must be 'type, address, value' per register\n",
+                               config_node->name);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               reg_table_num_entries /= 12;
+               reg_table = (struct pltfrm_camera_module_reg *)
+                       kmalloc(reg_table_num_entries *
+                               sizeof(struct pltfrm_camera_module_reg),
+                               GFP_KERNEL);
+               if (IS_ERR_OR_NULL(reg_table)) {
+                       pltfrm_camera_module_pr_err(sd,
+                               "memory allocation failed\n");
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               pltfrm_camera_module_pr_debug(sd,
+                       "patching config with %s (%d registers)\n",
+                        config_node->name, reg_table_num_entries);
+               for (i = 0; i < reg_table_num_entries; i++) {
+                       u32 val;
+
+                       ret |= of_property_read_u32_index(
+                               config_node, "rockchip,reg-table",
+                               3 * i, &val);
+                       reg_table[i].flag = val;
+                       ret |= of_property_read_u32_index(
+                               config_node, "rockchip,reg-table",
+                               3 * i + 1, &val);
+                       reg_table[i].reg = val;
+                       ret |= of_property_read_u32_index(
+                               config_node, "rockchip,reg-table",
+                               3 * i + 2, &val);
+                       reg_table[i].val = val;
+                       if (IS_ERR_VALUE(ret)) {
+                               pltfrm_camera_module_pr_err(sd,
+                                       "error while reading property %s at index %d\n",
+                                       "rockchip,reg-table", i);
+                               goto err;
+                       }
+               }
+               ret = pltfrm_camera_module_write_reglist(
+                       sd, reg_table, reg_table_num_entries);
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+               kfree(reg_table);
+               reg_table = NULL;
+       }
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(sd,
+                       "failed with error %d\n", ret);
+       kfree(reg_table);
+       return ret;
+}
+
+/* ======================================================================== */
+
+const char *pltfrm_dev_string(
+       struct v4l2_subdev *sd)
+{
+       struct i2c_client *client;
+
+       if (IS_ERR_OR_NULL(sd))
+               return "";
+       client = v4l2_get_subdevdata(sd);
+       if (IS_ERR_OR_NULL(client))
+               return "";
+       return dev_driver_string(&client->dev);
+}
+
+int pltfrm_camera_module_read_reg(
+               struct v4l2_subdev *sd,
+       u16 data_length,
+       u16 reg,
+       u32 *val)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = 0;
+       struct i2c_msg msg[1];
+       unsigned char data[4] = { 0, 0, 0, 0 };
+
+       if (!client->adapter) {
+               pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n");
+               return -ENODEV;
+       }
+
+       msg->addr = client->addr;
+       msg->flags = I2C_M_WR;
+       msg->len = 2;
+       msg->buf = data;
+
+       /* High byte goes out first */
+       data[0] = (u8)(reg >> 8);
+       data[1] = (u8)(reg & 0xff);
+
+       ret = i2c_transfer(client->adapter, msg, 1);
+       if (ret >= 0) {
+               mdelay(3);
+               msg->flags = I2C_M_RD;
+               msg->len = data_length;
+               i2c_transfer(client->adapter, msg, 1);
+       }
+       if (ret >= 0) {
+               *val = 0;
+               /* High byte comes first */
+               if (data_length == 1)
+                       *val = data[0];
+               else if (data_length == 2)
+                       *val = data[1] + (data[0] << 8);
+               else
+                       *val = data[3] + (data[2] << 8) +
+                           (data[1] << 16) + (data[0] << 24);
+               return 0;
+       }
+       pltfrm_camera_module_pr_err(sd,
+               "i2c read from offset 0x%08x failed with error %d\n", reg, ret);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int pltfrm_camera_module_write_reg(
+       struct v4l2_subdev *sd,
+       u16 reg, u8 val)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = 0;
+       struct i2c_msg msg[1];
+       unsigned char data[3];
+       int retries;
+
+       if (!client->adapter) {
+               pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n");
+               return -ENODEV;
+       }
+
+       for (retries = 0; retries < 5; retries++) {
+               msg->addr = client->addr;
+               msg->flags = I2C_M_WR;
+               msg->len = 3;
+               msg->buf = data;
+
+               /* high byte goes out first */
+               data[0] = (u8)(reg >> 8);
+               data[1] = (u8)(reg & 0xff);
+               data[2] = val;
+
+               ret = i2c_transfer(client->adapter, msg, 1);
+               if (ret == 1) {
+               pltfrm_camera_module_pr_debug(sd,
+                       "i2c write to offset 0x%08x success\n", reg);
+                       return 0;
+               }
+
+               pltfrm_camera_module_pr_debug(sd,
+                       "retrying I2C... %d\n", retries);
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(msecs_to_jiffies(20));
+       }
+       pltfrm_camera_module_pr_err(sd,
+               "i2c write to offset 0x%08x failed with error %d\n", reg, ret);
+       return ret;
+}
+
+/* ======================================================================== */
+int pltfrm_camera_module_write_reglist(
+       struct v4l2_subdev *sd,
+       const struct pltfrm_camera_module_reg reglist[],
+       int len)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = 0;
+       unsigned int k = 0, j = 0;
+       int i = 0;
+       struct i2c_msg *msg;
+       unsigned char *data;
+       unsigned int max_entries = len;
+
+       msg = kmalloc((sizeof(struct i2c_msg) * I2C_MSG_MAX),
+                                     GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+       data = kmalloc((sizeof(unsigned char) * I2C_DATA_MAX),
+                                    GFP_KERNEL);
+       if (!data) {
+               kfree(msg);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < max_entries; i++) {
+               switch (reglist[i].flag) {
+               case PLTFRM_CAMERA_MODULE_REG_TYPE_DATA:
+                       (msg + j)->addr = client->addr;
+                       (msg + j)->flags = I2C_M_WR;
+                       (msg + j)->len = 3;
+                       (msg + j)->buf = (data + k);
+
+                       data[k + 0] = (u8)((reglist[i].reg & 0xFF00) >> 8);
+                       data[k + 1] = (u8)(reglist[i].reg & 0xFF);
+                       data[k + 2] = (u8)(reglist[i].val & 0xFF);
+                       k = k + 3;
+                       j++;
+                       if (j == (I2C_MSG_MAX - 1)) {
+                               /* Bulk I2C transfer */
+                               pltfrm_camera_module_pr_debug(sd,
+                                       "messages transfers 1 0x%p msg %d bytes %d\n",
+                                       msg, j, k);
+                               ret = i2c_transfer(client->adapter, msg, j);
+                               if (ret < 0) {
+                                       pltfrm_camera_module_pr_err(sd,
+                                               "i2c transfer returned with err %d\n",
+                                               ret);
+                                       kfree(msg);
+                                       kfree(data);
+                                       return ret;
+                               }
+                               j = 0;
+                               k = 0;
+                               pltfrm_camera_module_pr_debug(sd,
+                                       "i2c_transfer return %d\n", ret);
+                       }
+                       break;
+               case PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT:
+                       if (j > 0) {
+                               /* Bulk I2C transfer */
+                               pltfrm_camera_module_pr_debug(sd,
+                                       "messages transfers 1 0x%p msg %d bytes %d\n",
+                                       msg, j, k);
+                               ret = i2c_transfer(client->adapter, msg, j);
+                               if (ret < 0) {
+                                       pltfrm_camera_module_pr_debug(sd,
+                                               "i2c transfer returned with err %d\n",
+                                               ret);
+                                       kfree(msg);
+                                       kfree(data);
+                                       return ret;
+                               }
+                               pltfrm_camera_module_pr_debug(sd,
+                                       "i2c_transfer return %d\n", ret);
+                       }
+                       mdelay(reglist[i].val);
+                       j = 0;
+                       k = 0;
+                       break;
+               default:
+                       pltfrm_camera_module_pr_debug(sd, "unknown command\n");
+                       kfree(msg);
+                       kfree(data);
+                       return -1;
+               }
+       }
+
+       if (j != 0) {   /* Remaining I2C message */
+               pltfrm_camera_module_pr_debug(sd,
+                       "messages transfers 1 0x%p msg %d bytes %d\n",
+                       msg, j, k);
+               ret = i2c_transfer(client->adapter, msg, j);
+               if (ret < 0) {
+                       pltfrm_camera_module_pr_err(sd,
+                               "i2c transfer returned with err %d\n", ret);
+                       kfree(msg);
+                       kfree(data);
+                       return ret;
+               }
+               pltfrm_camera_module_pr_debug(sd,
+                       "i2c_transfer return %d\n", ret);
+       }
+
+       kfree(msg);
+       kfree(data);
+       return 0;
+}
+
+static int pltfrm_camera_module_init_pm(
+       struct v4l2_subdev *sd,
+       struct pltfrm_soc_cfg *soc_cfg)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+
+       pdata->soc_cfg = soc_cfg;
+       return 0;
+}
+
+int pltfrm_camera_module_set_pm_state(
+       struct v4l2_subdev *sd,
+       int on)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+       struct pltfrm_soc_cfg *soc_cfg =
+               pdata->soc_cfg;
+       struct pltfrm_soc_mclk_para mclk_para;
+       struct pltfrm_soc_cfg_para cfg_para;
+       struct pltfrm_cam_itf itf_cfg;
+       unsigned int i;
+
+       if (on) {
+               if (IS_ERR_OR_NULL(soc_cfg)) {
+                       pltfrm_camera_module_pr_err(sd,
+                               "set_pm_state failed! soc_cfg is %p!\n",
+                               soc_cfg);
+                       return -EINVAL;
+               }
+
+               if (pdata->regulators.regulator) {
+                       for (i = 0; i < pdata->regulators.cnt; i++) {
+                               struct pltfrm_camera_module_regulator
+                                                       *regulator;
+
+                               regulator = pdata->regulators.regulator + i;
+                               if (IS_ERR(regulator->regulator))
+                                       continue;
+                               regulator_set_voltage(
+                                       regulator->regulator,
+                                       regulator->min_uV,
+                                       regulator->max_uV);
+                               if (regulator_enable(regulator->regulator))
+                                       pltfrm_camera_module_pr_err(sd,
+                                               "regulator_enable failed!\n");
+                       }
+               }
+
+               pltfrm_camera_module_set_pin_state(
+                       sd,
+                       PLTFRM_CAMERA_MODULE_PIN_PWR,
+                       PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+
+               pltfrm_camera_module_set_pin_state(
+                       sd,
+                       PLTFRM_CAMERA_MODULE_PIN_RESET,
+                       PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE);
+               usleep_range(100, 300);
+               pltfrm_camera_module_set_pin_state(
+                       sd,
+                       PLTFRM_CAMERA_MODULE_PIN_RESET,
+                       PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+
+               mclk_para.io_voltage = PLTFRM_IO_1V8;
+               mclk_para.drv_strength = PLTFRM_DRV_STRENGTH_2;
+               cfg_para.cmd = PLTFRM_MCLK_CFG;
+               cfg_para.cfg_para = (void *)&mclk_para;
+               soc_cfg->soc_cfg(&cfg_para);
+
+               if (v4l2_subdev_call(sd,
+                       core,
+                       ioctl,
+                       PLTFRM_CIFCAM_G_ITF_CFG,
+                       (void *)&itf_cfg) == 0) {
+                       clk_set_rate(pdata->mclk, itf_cfg.mclk_hz);
+               } else {
+                       pltfrm_camera_module_pr_err(sd,
+                               "PLTFRM_CIFCAM_G_ITF_CFG failed, mclk set 24m default.\n");
+                       clk_set_rate(pdata->mclk, 24000000);
+               }
+               clk_prepare_enable(pdata->mclk);
+       } else {
+               clk_disable_unprepare(pdata->mclk);
+
+               pltfrm_camera_module_set_pin_state(
+                       sd,
+                       PLTFRM_CAMERA_MODULE_PIN_PWR,
+                       PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE);
+
+               if (pdata->regulators.regulator) {
+                       for (i = 0; i < pdata->regulators.cnt; i++) {
+                               struct pltfrm_camera_module_regulator
+                                                       *regulator;
+
+                               regulator = pdata->regulators.regulator + i;
+                               if (IS_ERR(regulator->regulator))
+                                       continue;
+                               regulator_disable(regulator->regulator);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int pltfrm_camera_module_set_pin_state(
+       struct v4l2_subdev *sd,
+       const char *pin,
+       enum pltfrm_camera_module_pin_state state)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+       int gpio_val;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pdata->gpios); i++) {
+               if (pin == pdata->gpios[i].label) {
+                       if (!gpio_is_valid(pdata->gpios[i].pltfrm_gpio))
+                               return 0;
+                       if (state == PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE)
+                               gpio_val = (pdata->gpios[i].active_low ==
+                                       OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+                       else
+                               gpio_val = (pdata->gpios[i].active_low ==
+                                       OF_GPIO_ACTIVE_LOW) ? 1 : 0;
+                       gpio_set_value(pdata->gpios[i].pltfrm_gpio, gpio_val);
+                       pltfrm_camera_module_pr_debug(sd,
+                               "set GPIO #%d ('%s') to %s\n",
+                               pdata->gpios[i].pltfrm_gpio,
+                               pdata->gpios[i].label,
+                               gpio_val ? "HIGH" : "LOW");
+
+                       return 0;
+               }
+       }
+
+       pltfrm_camera_module_pr_err(sd,
+               "unknown pin '%s'\n",
+               pin);
+       return -EINVAL;
+}
+
+int pltfrm_camera_module_get_pin_state(
+       struct v4l2_subdev *sd,
+       const char *pin)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+       int gpio_val;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pdata->gpios); i++) {
+               if (pin == pdata->gpios[i].label) {
+                       if (!gpio_is_valid(pdata->gpios[i].pltfrm_gpio))
+                               return 0;
+                       gpio_val = gpio_get_value(pdata->gpios[i].pltfrm_gpio);
+                       pltfrm_camera_module_pr_debug(
+                               sd,
+                               "get GPIO #%d ('%s') is %s\n",
+                               pdata->gpios[i].pltfrm_gpio,
+                               pdata->gpios[i].label,
+                               gpio_val ? "HIGH" : "LOW");
+
+                       return gpio_val;
+               }
+       }
+
+       pltfrm_camera_module_pr_err(
+               sd,
+               "unknown pin '%s'\n",
+               pin);
+       return -EINVAL;
+}
+
+int pltfrm_camera_module_s_power(
+       struct v4l2_subdev *sd,
+       int on)
+{
+       int ret;
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+
+       pltfrm_camera_module_pr_debug(sd, "%s\n", on ? "on" : "off");
+
+       if (on) {
+               /* Enable clock and voltage to Secondary Camera Sensor */
+               ret = pltfrm_camera_module_set_pm_state(sd, on);
+               if (IS_ERR_VALUE(ret))
+                       pltfrm_camera_module_pr_err(sd,
+                               "set PM state failed (%d), could not power on camera\n",
+                               ret);
+               else {
+                       pltfrm_camera_module_pr_debug(sd,
+                               "set PM state to %d successful, camera module is on\n",
+                               on);
+                       ret = pltfrm_camera_module_set_pinctrl_state(
+                               sd, pdata->pins_default);
+               }
+       } else {
+               /* Disable clock and voltage to Secondary Camera Sensor  */
+               ret = pltfrm_camera_module_set_pinctrl_state(
+                       sd, pdata->pins_sleep);
+               if (!IS_ERR_VALUE(ret)) {
+                       ret = pltfrm_camera_module_set_pm_state(
+                               sd, on);
+                       if (IS_ERR_VALUE(ret))
+                               pltfrm_camera_module_pr_err(sd,
+                                       "set PM state failed (%d), could not power off camera\n",
+                                       ret);
+                       else
+                               pltfrm_camera_module_pr_debug(sd,
+                                       "set PM state to %d successful, camera module is off\n",
+                                       on);
+               }
+       }
+       return ret;
+}
+
+struct v4l2_subdev *pltfrm_camera_module_get_af_ctrl(
+       struct v4l2_subdev *sd)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+
+       return pdata->af_ctrl;
+}
+
+char *pltfrm_camera_module_get_flash_driver_name(
+       struct v4l2_subdev *sd)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+
+       return (char *)pdata->fl_ctrl.flash_driver_name;
+}
+
+struct v4l2_subdev *pltfrm_camera_module_get_fl_ctrl(
+       struct v4l2_subdev *sd)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+
+       return pdata->fl_ctrl.flsh_ctrl;
+}
+
+int pltfrm_camera_module_patch_config(
+       struct v4l2_subdev *sd,
+       struct v4l2_mbus_framefmt *frm_fmt,
+       struct v4l2_subdev_frame_interval *frm_intrvl)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct device_node *parent_node = of_node_get(client->dev.of_node);
+       struct device_node *child_node = NULL, *prev_node = NULL;
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(sd, "pix_fmt %d, %dx%d@%d/%dfps\n",
+               frm_fmt->code, frm_fmt->width, frm_fmt->height,
+               frm_intrvl->interval.denominator,
+               frm_intrvl->interval.numerator);
+
+       while (!IS_ERR_OR_NULL(child_node =
+               of_get_next_child(parent_node, prev_node))) {
+               if (strncasecmp(child_node->name,
+                       "rockchip,camera-module-config",
+                       strlen("rockchip,camera-module-config")) == 0) {
+                       ret = pltfrm_camera_module_config_matches(
+                               sd, child_node, frm_fmt, frm_intrvl);
+                       if (IS_ERR_VALUE(ret))
+                               goto err;
+                       if (ret) {
+                               ret =
+                                       pltfrm_camera_module_write_reglist_node(
+                                               sd, child_node);
+                               if (!IS_ERR_VALUE(ret))
+                                       goto err;
+                       }
+               }
+               of_node_put(prev_node);
+               prev_node = child_node;
+       }
+       of_node_put(prev_node);
+       of_node_put(parent_node);
+
+       return 0;
+err:
+       pltfrm_camera_module_pr_err(sd,
+                       "failed with error %d\n", ret);
+       of_node_put(prev_node);
+       of_node_put(child_node);
+       of_node_put(parent_node);
+       return ret;
+}
+
+int pltfrm_camera_module_init(
+       struct v4l2_subdev *sd,
+       void **pldata)
+{
+       int ret = 0;
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata;
+
+       pltfrm_camera_module_pr_debug(sd, "\n");
+
+       pdata = pltfrm_camera_module_get_data(sd);
+       if (IS_ERR_OR_NULL(pdata)) {
+               pltfrm_camera_module_pr_err(sd,
+                       "unable to get platform data\n");
+               return -EFAULT;
+       }
+
+       ret = pltfrm_camera_module_init_gpio(sd);
+       if (IS_ERR_VALUE(ret)) {
+               pltfrm_camera_module_pr_err(sd,
+                       "GPIO initialization failed (%d)\n", ret);
+       }
+
+       if (IS_ERR_VALUE(ret))
+               devm_kfree(&client->dev, pdata);
+       else
+               *(struct pltfrm_camera_module_data **)pldata = pdata;
+
+       return ret;
+}
+
+void pltfrm_camera_module_release(
+       struct v4l2_subdev *sd)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+       int i;
+
+       /* GPIOs also needs to be freed for other sensors to use */
+       for (i = 0; i < ARRAY_SIZE(pdata->gpios); i++) {
+               if (gpio_is_valid(pdata->gpios[i].pltfrm_gpio)) {
+                       pltfrm_camera_module_pr_debug(sd,
+                               "free GPIO #%d ('%s')\n",
+                               pdata->gpios[i].pltfrm_gpio,
+                               pdata->gpios[i].label);
+                       gpio_free(
+                               pdata->gpios[i].pltfrm_gpio);
+               }
+       }
+       for (i = 0; i < pdata->regulators.cnt; i++) {
+               if (!IS_ERR(pdata->regulators.regulator[i].regulator))
+                       devm_regulator_put(
+                               pdata->regulators.regulator[i].regulator);
+       }
+       if (pdata->pinctrl)
+               devm_pinctrl_put(pdata->pinctrl);
+}
+
+/* ======================================================================== */
+long pltfrm_camera_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+               dev_get_platdata(&client->dev);
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(sd, "cmd: 0x%x\n", cmd);
+
+       if (cmd == RK_VIDIOC_CAMERA_MODULEINFO) {
+               struct camera_module_info_s *p_camera_module =
+               (struct camera_module_info_s *)arg;
+
+               /*
+                * strlcpy((char *)p_camera_module->sensor_name,
+                * (char *)client->driver->driver.name,
+                * sizeof(p_camera_module->sensor_name));
+                */
+
+               if (pdata->info.module_name)
+                       strcpy((char *)p_camera_module->module_name,
+                               pdata->info.module_name);
+               else
+                       strcpy((char *)p_camera_module->module_name, "(null)");
+               if (pdata->info.len_name)
+                       strcpy((char *)p_camera_module->len_name,
+                               pdata->info.len_name);
+               else
+                       strcpy((char *)p_camera_module->len_name, "(null)");
+               if (pdata->info.fov_h)
+                       strcpy((char *)p_camera_module->fov_h,
+                               pdata->info.fov_h);
+               else
+                       strcpy((char *)p_camera_module->fov_h, "(null)");
+               if (pdata->info.fov_v)
+                       strcpy((char *)p_camera_module->fov_v,
+                               pdata->info.fov_v);
+               else
+                       strcpy((char *)p_camera_module->fov_v, "(null)");
+               if (pdata->info.focal_length)
+                       strcpy((char *)p_camera_module->focal_length,
+                       pdata->info.focal_length);
+               else
+                       strcpy((char *)p_camera_module->focal_length, "(null)");
+               if (pdata->info.focus_distance)
+                       strcpy((char *)p_camera_module->focus_distance,
+                               pdata->info.focus_distance);
+               else
+                       strcpy((char *)p_camera_module->focus_distance,
+                               "(null)");
+
+               p_camera_module->facing = pdata->info.facing;
+               p_camera_module->orientation = pdata->info.orientation;
+               p_camera_module->iq_mirror = pdata->info.iq_mirror;
+               p_camera_module->iq_flip = pdata->info.iq_flip;
+               p_camera_module->flash_support = pdata->info.flash_support;
+               p_camera_module->flash_exp_percent =
+                       pdata->info.flash_exp_percent;
+
+               return 0;
+       } else if (cmd == PLTFRM_CIFCAM_G_DEFRECT) {
+               struct pltfrm_cam_defrect *defrect =
+                       (struct pltfrm_cam_defrect *)arg;
+               unsigned int i;
+
+               for (i = 0; i < 4; i++) {
+                       if ((pdata->defrects[i].width == defrect->width) &&
+                               (pdata->defrects[i].height == defrect->height))
+                               defrect->defrect = pdata->defrects[i].defrect;
+               }
+               return 0;
+       } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) {
+               struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg;
+
+               if (PLTFRM_CAM_ITF_IS_MIPI(itf_cfg->type))
+                       itf_cfg->cfg.mipi.dphy_index =
+                               pdata->itf.itf.mipi.dphy_index;
+
+               return 0;
+       } else if (cmd == PLTFRM_CIFCAM_ATTACH) {
+               return pltfrm_camera_module_init_pm(sd,
+                       (struct pltfrm_soc_cfg *)arg);
+       }
+
+       return ret;
+}
+
+int pltfrm_camera_module_get_flip_mirror(
+       struct v4l2_subdev *sd)
+{
+       int mode = 0;
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct pltfrm_camera_module_data *pdata =
+       dev_get_platdata(&client->dev);
+
+       if ((pdata->info.flip == -1) && pdata->info.mirror == -1)
+               return -1;
+
+       if (pdata->info.flip)
+               mode |= 0x02;
+       else
+               mode &= ~0x02;
+
+       if (pdata->info.mirror)
+               mode |= 0x01;
+       else
+               mode &= ~0x01;
+
+       return mode;
+}
+#endif
+
diff --git a/drivers/media/i2c/soc_camera/rockchip/rk_camera_module_version.h b/drivers/media/i2c/soc_camera/rockchip/rk_camera_module_version.h
new file mode 100644 (file)
index 0000000..4e52a33
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * rk_camera_module.h
+ * (Based on Intel driver for sofiaxxx)
+ *
+ * Copyright (C) 2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _RK_CAMERA_MODULE_VERSION_H_
+#define _RK_CAMERA_MODULE_VERSION_H_
+#include <linux/version.h>
+
+/*
+ *       CIF DRIVER VERSION NOTE
+ *
+ * v0.1.0:
+ * 1. Initialize version;
+ * 2. Update sensor configuration after power up sensor in
+ * ov_camera_module_s_power.
+ * Because mipi datalane may be no still on LP11 state when
+ * sensor configuration;
+ *
+ */
+
+#define CONFIG_RK_CAMERA_MODULE_VERSION KERNEL_VERSION(0, 1, 0)
+
+#endif