media: soc_camera: add tc358749xgb sensor driver
authorzhoupeng <benjo.zhou@rock-chips.com>
Thu, 20 Jul 2017 07:49:59 +0000 (15:49 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Fri, 21 Jul 2017 08:22:06 +0000 (16:22 +0800)
Change-Id: I66ccb856cb2689bf26a8a12069b29c7ec6dae6f0
Signed-off-by: zhoupeng <benjo.zhou@rock-chips.com>
drivers/media/i2c/soc_camera/rockchip/Kconfig
drivers/media/i2c/soc_camera/rockchip/Makefile
drivers/media/i2c/soc_camera/rockchip/tc358749xbg_v4l2-i2c-subdev.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/tc_camera_module.c [new file with mode: 0644]
drivers/media/i2c/soc_camera/rockchip/tc_camera_module.h [new file with mode: 0644]

index b6c73e183e584cb2f03448f75966c65281bcab85..d277e180fc79d68b56ff7dda4a666a65d574a86b 100644 (file)
@@ -32,3 +32,10 @@ config VIDEO_OV7750
        default n
        ---help---
          This is ov7750 camera driver adapt to rockchip cif isp platform.
+
+config VIDEO_TC358749XBG
+       tristate "tc358749xbg driver adapt to rockchip cif isp platform"
+       depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C
+       default n
+       ---help---
+         This is tc358749xbg hdmi video driver adapt to rockchip cif isp platform.
index 5bb9c4e4bc48b3d60c5bf23b0c37c266caa47f95..ba29f02768e92daeff5a7a4069a6e76ae72d4201 100644 (file)
@@ -3,3 +3,4 @@ obj-$(CONFIG_VIDEO_OV2710) += ov_camera_module.o rk_camera_module.o ov2710_v4l2-
 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
 obj-$(CONFIG_VIDEO_OV7750) += ov_camera_module.o rk_camera_module.o ov7750_v4l2-i2c-subdev.o
+obj-$(CONFIG_VIDEO_TC358749XBG) += tc_camera_module.o rk_camera_module.o tc358749xbg_v4l2-i2c-subdev.o
diff --git a/drivers/media/i2c/soc_camera/rockchip/tc358749xbg_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/tc358749xbg_v4l2-i2c-subdev.c
new file mode 100644 (file)
index 0000000..f6408b8
--- /dev/null
@@ -0,0 +1,1114 @@
+/*
+ * drivers/media/i2c/soc_camera/xgold/tc358749xbg.c
+ *
+ * tc358749xbg sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ * Copyright (C) 2008 Texas Instruments.
+ * Author: zhoupeng <benjo.zhou@rock-chips.com>
+ *
+ * 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;
+ */
+
+#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 <linux/of_gpio.h>
+#include <linux/debugfs.h>
+#include <media/v4l2-controls_rockchip.h>
+#include "tc_camera_module.h"
+
+#define TC358749xbg_DRIVER_NAME "tc358749xbg"
+
+/* product ID */
+#define TC358749xbg_PID_MAGIC          0x4701
+#define TC358749xbg_PID_ADDR           0x0000
+
+#define INIT_END                              0x854A
+#define SYS_STATUS                            0x8520
+#define CONFCTL                               0x0004
+#define VI_REP                                0x8576
+#define MASK_VOUT_COLOR_SEL                   0xe0
+#define MASK_VOUT_COLOR_RGB_FULL              0x00
+#define MASK_VOUT_COLOR_RGB_LIMITED           0x20
+#define MASK_VOUT_COLOR_601_YCBCR_FULL        0x40
+#define MASK_VOUT_COLOR_601_YCBCR_LIMITED     0x60
+#define MASK_VOUT_COLOR_709_YCBCR_FULL        0x80
+#define MASK_VOUT_COLOR_709_YCBCR_LIMITED     0xa0
+#define MASK_VOUT_COLOR_FULL_TO_LIMITED       0xc0
+#define MASK_VOUT_COLOR_LIMITED_TO_FULL       0xe0
+#define MASK_IN_REP_HEN                       0x10
+#define MASK_IN_REP                           0x0f
+
+/* ======================================================================== */
+/* Base sensor configs */
+/* ======================================================================== */
+/* MCLK:24MHz  1920x1080  30fps   mipi 4lane   800Mbps/lane */
+static struct tc_camera_module_reg tc358749xbg_init_tab_1920_1080_60fps[] = {
+          //<!-- Software Reset -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0004, 0x0004, 2},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0002, 0x7F80, 2},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0002, 0x0000, 2},
+           //<!-- PLL Setting -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0020, 0x508A, 2},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0022, 0x0203, 2},
+       {TC_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 0x0001, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0022, 0x0213, 2},
+           //<!-- Misc Setting -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0006, 0x012C, 2},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0060, 0x0001, 2},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x7080, 0x0000, 2},
+           //<!-- Interrupt Control -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0014, 0x0000, 2},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0016, 0x05FF, 2},
+           //<!-- change 749 mipi out drive capability,
+           //to avoid pic error -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00000003, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0104, 0x00000003, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0108, 0x00000003, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x010C, 0x00000003, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0110, 0x00000003, 4},
+           //<!-- CSI Lane Enable -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0140, 0x00000000, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0144, 0x00000000, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0148, 0x00000000, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x014C, 0x00000000, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0150, 0x00000000, 4},
+           //<!-- CSI Transition Timing -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0210, 0x00001770, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0214, 0x00000005, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0218, 0x00001505, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x021C, 0x00000001, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0220, 0x00000105, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0224, 0x0000332C, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0228, 0x00000008, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x022C, 0x00000002, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0230, 0x00000005, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0234, 0x0000001F, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0238, 0x00000000, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x023C, 0x00040005, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0204, 0x00000001, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0518, 0x00000001, 4},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0500, 0xA3008086, 4},
+           //<!-- Data ID Setting -->
+           //<!-- HDMI Interrupt Mask -->
+           //<!-- HDMI Audio REFCLK -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8531, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8532, 0x80, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8540, 0x8C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8541, 0x0A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8630, 0xB0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8631, 0x1E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8632, 0x04, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8670, 0x01, 1},
+           //<!-- HDMI PHY -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8532, 0x80, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8536, 0x40, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x853F, 0x0A, 1},
+           //<!-- HDMI SYSTEM -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8543, 0x32, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8544, 0x10, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8545, 0x31, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8546, 0x2D, 1},
+           //<!-- EDID -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85C7, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85CA, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85CB, 0x01, 1},
+           //<!-- EDID Data -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C00, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C01, 0xFF, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C02, 0xFF, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C03, 0xFF, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C04, 0xFF, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C05, 0xFF, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C06, 0xFF, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C07, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C08, 0x52, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C09, 0x62, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0A, 0x88, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0B, 0x88, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0C, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0D, 0x88, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0E, 0x88, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0F, 0x88, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C10, 0x1C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C11, 0x15, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C12, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C13, 0x03, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C14, 0x80, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C15, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C16, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C17, 0x78, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C18, 0x0A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C19, 0x0D, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1A, 0xC9, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1B, 0xA0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1C, 0x57, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1D, 0x47, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1E, 0x98, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1F, 0x27, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C20, 0x12, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C21, 0x48, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C22, 0x4C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C23, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C24, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C25, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C26, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C27, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C28, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C29, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2A, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2B, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2C, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2D, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2E, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2F, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C30, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C31, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C32, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C33, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C34, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C35, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C36, 0x02, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C37, 0x3A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C38, 0x80, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C39, 0x18, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3A, 0x71, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3B, 0x38, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3C, 0x2D, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3D, 0x40, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3E, 0x58, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3F, 0x2C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C40, 0x45, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C41, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C42, 0xC4, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C43, 0x8E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C44, 0x21, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C45, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C46, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C47, 0x1E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C48, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C49, 0x1D, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4A, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4B, 0x72, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4C, 0x51, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4D, 0xD0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4E, 0x1E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4F, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C50, 0x6E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C51, 0x28, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C52, 0x55, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C53, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C54, 0xC4, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C55, 0x8E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C56, 0x21, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C57, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C58, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C59, 0x1E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5A, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5B, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5C, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5D, 0xFC, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5E, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5F, 0x54, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C60, 0x6F, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C61, 0x73, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C62, 0x68, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C63, 0x69, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C64, 0x62, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C65, 0x61, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C66, 0x2D, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C67, 0x48, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C68, 0x32, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C69, 0x44, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6A, 0x0A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6B, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6C, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6D, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6E, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6F, 0xFD, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C70, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C71, 0x17, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C72, 0x3D, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C73, 0x0F, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C74, 0x8C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C75, 0x17, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C76, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C77, 0x0A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C78, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C79, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7A, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7B, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7C, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7D, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7E, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7F, 0x92, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C80, 0x02, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C81, 0x03, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C82, 0x1A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C83, 0x74, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C84, 0x47, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C85, 0x10, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C86, 0x04, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C87, 0x02, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C88, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C89, 0x0A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8A, 0x04, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8B, 0x04, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8C, 0x23, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8D, 0x09, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8E, 0x07, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8F, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C90, 0x83, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C91, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C92, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C93, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C94, 0x65, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C95, 0x03, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C96, 0x0C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C97, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C98, 0x10, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C99, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9A, 0x8C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9B, 0x0A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9C, 0xD0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9D, 0x8A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9E, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9F, 0xE0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA0, 0x2D, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA1, 0x10, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA2, 0x10, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA3, 0x3E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA4, 0x96, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA5, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA6, 0x13, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA7, 0x8E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA8, 0x21, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA9, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAA, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAB, 0x1E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAC, 0xD8, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAD, 0x09, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAE, 0x80, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAF, 0xA0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB0, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB1, 0xE0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB2, 0x2D, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB3, 0x10, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB4, 0x10, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB5, 0x60, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB6, 0xA2, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB7, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB8, 0xC4, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB9, 0x8E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBA, 0x21, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBB, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBC, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBD, 0x18, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBE, 0x8C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBF, 0x0A, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC0, 0xD0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC1, 0x90, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC2, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC3, 0x40, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC4, 0x31, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC5, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC6, 0x0C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC7, 0x40, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC8, 0x55, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC9, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCA, 0x80, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCB, 0xB0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCC, 0x74, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCD, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCE, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCF, 0x18, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD0, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD1, 0x1D, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD2, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD3, 0x72, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD4, 0x51, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD5, 0xD0, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD6, 0x1E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD7, 0x20, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD8, 0x6E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD9, 0x28, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDA, 0x55, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDB, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDC, 0xC4, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDD, 0x8E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDE, 0x21, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDF, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE0, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE1, 0x1E, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE2, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE3, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE4, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE5, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE6, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE7, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE8, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE9, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEA, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEB, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEC, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CED, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEE, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEF, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF0, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF1, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF2, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF3, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF4, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF5, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF6, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF7, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF8, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF9, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFA, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFB, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFC, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFD, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFE, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFF, 0x86, 1},
+           //<!-- HDCP Setting -->
+           //<!-- Video Setting -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8573, 0xC1, 1},
+           //<!-- HDMI Audio Setting -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8600, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8602, 0xF3, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8603, 0x02, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8604, 0x0C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8606, 0x05, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8607, 0x00, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8620, 0x22, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8640, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8641, 0x65, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8642, 0x07, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8652, 0x02, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8665, 0x10, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85AA, 0x50, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85AF, 0xC6, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85AB, 0x00, 1},
+           //<!-- Info Frame Extraction -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x870B, 0x2C, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x870C, 0x53, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x870D, 0x01, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x870E, 0x30, 1},
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x9007, 0x10, 1},
+           //<!-- Let HDMI Source start access -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x854A, 0x01, 1},
+           //<!-- VIP -->
+           //<!-- VIP Main Controls -->
+           //<!-- De-Interlacer IP Controls -->
+           //<!-- LCD Controller -->
+           //<!-- YCbCr to RGB -->
+           //<!-- VIP coeff -->
+           //<!-- Let HDMI Source start access -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x854A, 0x01, 1},
+           //<!-- Wait until HDMI sync is established -->
+           //<i2c_write addr="0x0F" count="2" radix="16">85 20</i2c_write>
+           //<i2c_read addr="0x0F" count="1" radix="16" />
+           //<!-- Sequence: Check bit7 of 8x8520 -->
+       {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0004, 0x0CD7, 2},
+};
+
+/* ======================================================================== */
+static struct tc_camera_module_config tc358749xbg_configs[] = {
+       {
+               .name = "1920x1080_60fps",
+               .frm_fmt = {
+                       .width = 1920,
+                       .height = 1080,
+                       .code = MEDIA_BUS_FMT_UYVY8_2X8
+               },
+               .frm_intrvl = {
+                       .interval = {
+                               .numerator = 1,
+                               .denominator = 60
+                       }
+               },
+               .auto_exp_enabled = false,
+               .auto_gain_enabled = false,
+               .auto_wb_enabled = false,
+               .reg_table = (void *)tc358749xbg_init_tab_1920_1080_60fps,
+               .reg_table_num_entries =
+                       sizeof(tc358749xbg_init_tab_1920_1080_60fps) /
+                       sizeof(tc358749xbg_init_tab_1920_1080_60fps[0]),
+               .v_blanking_time_us = 3078,
+               .ignore_measurement_check = 1,
+               PLTFRM_CAM_ITF_MIPI_CFG(0, 4, 800, 24000000)
+       }
+};
+
+static struct tc35x_priv tc358749xbg_priv;
+
+static struct tc_camera_module *to_tc_camera_module(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct tc_camera_module, sd);
+}
+
+/*--------------------------------------------------------------------------*/
+static int tc358749xbg_set_flip(
+       struct tc_camera_module *cam_mod,
+       struct tc_camera_module_reg reglist[],
+       int len)
+{
+       int mode = 0;
+
+       mode = tc_camera_module_get_flip_mirror(cam_mod);
+
+       if (mode == -1) {
+               tc_camera_module_pr_debug(
+                       cam_mod,
+                       "dts don't set flip, return!\n");
+               return 0;
+       }
+
+       return 0;
+}
+
+static int tc358749xbg_write_aec(struct tc_camera_module *cam_mod)
+{
+       int ret = 0;
+
+       tc_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 (IS_ERR_VALUE(ret))
+               tc_camera_module_pr_err(cam_mod,
+                                       "failed with error (%d)\n", ret);
+       return ret;
+}
+
+int tc358749xbg_enable_stream(struct tc_camera_module *cam_mod, bool enable)
+{
+       u8 init_end, sys_status;
+       u16 confctl;
+
+       if (enable) {
+               init_end = tc_camera_module_read8_reg(cam_mod, INIT_END);
+               tc_camera_module_pr_debug(cam_mod,
+                                         "INIT_END(0x854A) = 0x%02x\n",
+                                         init_end);
+               sys_status = tc_camera_module_read8_reg(cam_mod, SYS_STATUS);
+               tc_camera_module_pr_debug(cam_mod,
+                                         "INIT_END(0x8520) = 0x%02x\n",
+                                         sys_status);
+       }
+
+       confctl = tc_camera_module_read16_reg(cam_mod, CONFCTL);
+       tc_camera_module_pr_debug(cam_mod,
+                                 "CONFCTL(0x0004) = 0x%04x\n",
+                                 confctl);
+
+       return 0;
+}
+
+int tc358749xbg_s_power(struct tc_camera_module *cam_mod, bool enable)
+{
+       tc_camera_module_pr_debug(cam_mod,
+                                 "power %d\n",
+                                 enable);
+
+       if (enable) {
+               gpiod_direction_output(tc358749xbg_priv.gpio_reset, 0);
+               gpiod_direction_output(tc358749xbg_priv.gpio_int, 0);
+               gpiod_direction_output(tc358749xbg_priv.gpio_stanby, 0);
+               gpiod_direction_output(tc358749xbg_priv.gpio_power18, 1);
+               gpiod_direction_output(tc358749xbg_priv.gpio_power33, 1);
+               gpiod_direction_output(tc358749xbg_priv.gpio_power, 1);
+               gpiod_direction_output(tc358749xbg_priv.gpio_stanby, 1);
+               gpiod_direction_output(tc358749xbg_priv.gpio_reset, 1);
+       } else {
+               gpiod_direction_output(tc358749xbg_priv.gpio_power, 0);
+               gpiod_direction_output(tc358749xbg_priv.gpio_reset, 1);
+               gpiod_direction_output(tc358749xbg_priv.gpio_reset, 0);
+       }
+
+       return 0;
+}
+
+static int tc358749xbg_g_fmt(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format)
+{
+       struct tc_camera_module *cam_mod =  to_tc_camera_module(sd);
+       struct v4l2_mbus_framefmt *fmt = &format->format;
+       u8 vi_rep = tc_camera_module_read8_reg(cam_mod, VI_REP);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                                     "read VI_REP return 0x%x\n", vi_rep);
+
+       if (format->pad != 0)
+               return -EINVAL;
+       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;
+               fmt->field = V4L2_FIELD_NONE;
+
+               switch (vi_rep & MASK_VOUT_COLOR_SEL) {
+               case MASK_VOUT_COLOR_RGB_FULL:
+               case MASK_VOUT_COLOR_RGB_LIMITED:
+                       fmt->colorspace = V4L2_COLORSPACE_SRGB;
+                       break;
+               case MASK_VOUT_COLOR_601_YCBCR_LIMITED:
+               case MASK_VOUT_COLOR_601_YCBCR_FULL:
+                       fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+                       break;
+               case MASK_VOUT_COLOR_709_YCBCR_FULL:
+               case MASK_VOUT_COLOR_709_YCBCR_LIMITED:
+                       fmt->colorspace = V4L2_COLORSPACE_REC709;
+                       break;
+               default:
+                       fmt->colorspace = 0;
+                       break;
+               }
+
+               return 0;
+       }
+
+       pltfrm_camera_module_pr_err(&cam_mod->sd, "no active config\n");
+
+       return -1;
+}
+
+static int tc358749xbg_g_ctrl(struct tc_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       tc_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))
+               tc_camera_module_pr_debug(cam_mod,
+                                         "failed with error (%d)\n",
+                                         ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int tc358749xbg_filltimings(
+       struct tc_camera_module_custom_config *custom)
+{
+       return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int tc358749xbg_g_timings(
+       struct tc_camera_module *cam_mod,
+       struct tc_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:
+       tc_camera_module_pr_err(cam_mod,
+                               "failed with error (%d)\n",
+                               ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int tc358749xbg_s_ctrl(struct tc_camera_module *cam_mod, u32 ctrl_id)
+{
+       int ret = 0;
+
+       tc_camera_module_pr_debug(cam_mod, "\n");
+
+       switch (ctrl_id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_EXPOSURE:
+               ret = tc358749xbg_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 = TC358749xbg_auto_adjust_fps(
+        * cam_mod,
+        * cam_mod->exp_config.exp_time);
+        * break;
+        */
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               tc_camera_module_pr_err(cam_mod,
+                                       "failed with error (%d)\n",
+                                       ret);
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int tc358749xbg_s_ext_ctrls(
+       struct tc_camera_module *cam_mod,
+       struct tc_camera_module_ext_ctrls *ctrls)
+{
+       int ret = 0;
+
+       /* Handles only exposure and gain together special case. */
+       if (ctrls->count == 1)
+               ret = tc358749xbg_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 = tc358749xbg_write_aec(cam_mod);
+       else
+               ret = -EINVAL;
+
+       if (IS_ERR_VALUE(ret))
+               tc_camera_module_pr_debug(cam_mod,
+                                         "failed with error (%d)\n",
+                                         ret);
+
+       return ret;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int tc358749xbg_check_camera_id(struct tc_camera_module *cam_mod)
+{
+       u16 pid;
+       int ret = 0;
+
+       tc_camera_module_pr_debug(cam_mod, "in\n");
+
+       pid = tc_camera_module_read16_reg(cam_mod, TC358749xbg_PID_ADDR);
+       tc_camera_module_pr_err(cam_mod,
+                               "read pid return 0x%x\n",
+                               pid);
+
+       if (pid == TC358749xbg_PID_MAGIC) {
+               tc_camera_module_pr_debug(cam_mod,
+                                         "successfully detected camera ID 0x%04x\n",
+                                         pid);
+       } else {
+               tc_camera_module_pr_err(cam_mod,
+                                       "wrong camera ID, expected 0x%04x, detected 0x%04x\n",
+                                       TC358749xbg_PID_MAGIC,
+                                       pid);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       return 0;
+err:
+       tc_camera_module_pr_err(cam_mod,
+                               "failed with error (%d)\n",
+                               ret);
+       return ret;
+}
+
+/* ======================================================================== */
+int tc_camera_358749xbg_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       return 0;
+}
+
+/* ======================================================================== */
+
+int tc_camera_358749xbg_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls)
+{
+       return 0;
+}
+
+long tc_camera_358749xbg_module_ioctl(
+       struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg)
+{
+       return 0;
+}
+
+/* ======================================================================== */
+/* This part is platform dependent */
+/* ======================================================================== */
+
+static struct v4l2_subdev_core_ops tc358749xbg_camera_module_core_ops = {
+       .g_ctrl = tc_camera_module_g_ctrl,
+       .s_ctrl = tc_camera_module_s_ctrl,
+       .s_ext_ctrls = tc_camera_module_s_ext_ctrls,
+       .s_power = tc_camera_module_s_power,
+       .ioctl = tc_camera_module_ioctl
+};
+
+static struct v4l2_subdev_video_ops tc358749xbg_camera_module_video_ops = {
+       .s_frame_interval = tc_camera_module_s_frame_interval,
+       .s_stream = tc_camera_module_s_stream
+};
+
+static struct v4l2_subdev_pad_ops tc358749xbg_camera_module_pad_ops = {
+       .enum_frame_interval = tc_camera_module_enum_frameintervals,
+       .get_fmt = tc358749xbg_g_fmt,
+       .set_fmt = tc_camera_module_s_fmt,
+};
+
+static struct v4l2_subdev_ops tc358749xbg_camera_module_ops = {
+       .core = &tc358749xbg_camera_module_core_ops,
+       .video = &tc358749xbg_camera_module_video_ops,
+       .pad = &tc358749xbg_camera_module_pad_ops
+};
+
+static struct tc_camera_module tc358749xbg;
+
+static struct tc_camera_module_custom_config tc358749xbg_custom_config = {
+       .s_ctrl = tc358749xbg_s_ctrl,
+       .g_ctrl = tc358749xbg_g_ctrl,
+       .s_ext_ctrls = tc358749xbg_s_ext_ctrls,
+       .g_timings = tc358749xbg_g_timings,
+       .set_flip = tc358749xbg_set_flip,
+       .check_camera_id = tc358749xbg_check_camera_id,
+       .enable_stream = tc358749xbg_enable_stream,
+       .s_power = tc358749xbg_s_power,
+       .configs = tc358749xbg_configs,
+       .num_configs = ARRAY_SIZE(tc358749xbg_configs),
+       .power_up_delays_ms = {5, 30, 30}
+};
+
+static int test_parse_dts(
+       struct tc_camera_module *cam_mod,
+       struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       struct device_node *np = of_node_get(client->dev.of_node);
+
+       tc_camera_module_pr_debug(cam_mod, "enter!\n");
+
+       if (!dev)
+               tc_camera_module_pr_debug(cam_mod, "dev is NULL!");
+
+       if (!np)
+               tc_camera_module_pr_debug(cam_mod, "np is NULL");
+
+       tc358749xbg_priv.gpio_int = devm_gpiod_get_optional(dev, "int",
+                                                           GPIOD_OUT_HIGH);
+       tc358749xbg_priv.gpio_power = devm_gpiod_get_optional(dev, "power",
+                                                             GPIOD_OUT_HIGH);
+       tc358749xbg_priv.gpio_power18 = devm_gpiod_get_optional(dev, "power18",
+                                                               GPIOD_OUT_HIGH);
+       tc358749xbg_priv.gpio_power33 = devm_gpiod_get_optional(dev, "power33",
+                                                               GPIOD_OUT_HIGH);
+       tc358749xbg_priv.gpio_csi_ctl = devm_gpiod_get_optional(dev, "csi-ctl",
+                                                               GPIOD_OUT_LOW);
+       tc358749xbg_priv.gpio_reset = devm_gpiod_get_optional(dev, "reset",
+                                                             GPIOD_OUT_HIGH);
+       tc358749xbg_priv.gpio_stanby = devm_gpiod_get_optional(dev, "stanby",
+                                                              GPIOD_OUT_LOW);
+       return 0;
+}
+
+static int test_deparse_dts(struct i2c_client *client)
+{
+       if (tc358749xbg_priv.gpio_int) {
+               gpiod_direction_input(tc358749xbg_priv.gpio_int);
+               gpiod_put(tc358749xbg_priv.gpio_int);
+       }
+
+       if (tc358749xbg_priv.gpio_reset) {
+               gpiod_direction_input(tc358749xbg_priv.gpio_reset);
+               gpiod_put(tc358749xbg_priv.gpio_reset);
+       }
+
+       if (tc358749xbg_priv.gpio_stanby) {
+               gpiod_direction_input(tc358749xbg_priv.gpio_stanby);
+               gpiod_put(tc358749xbg_priv.gpio_stanby);
+       }
+
+       if (tc358749xbg_priv.gpio_csi_ctl) {
+               gpiod_direction_input(tc358749xbg_priv.gpio_csi_ctl);
+               gpiod_put(tc358749xbg_priv.gpio_csi_ctl);
+       }
+
+       if (tc358749xbg_priv.gpio_power) {
+               gpiod_direction_input(tc358749xbg_priv.gpio_power);
+               gpiod_put(tc358749xbg_priv.gpio_power);
+       }
+
+       if (tc358749xbg_priv.gpio_power18) {
+               gpiod_direction_input(tc358749xbg_priv.gpio_power18);
+               gpiod_put(tc358749xbg_priv.gpio_power18);
+       }
+
+       if (tc358749xbg_priv.gpio_power33) {
+               gpiod_direction_input(tc358749xbg_priv.gpio_power33);
+               gpiod_put(tc358749xbg_priv.gpio_power33);
+       }
+
+       return 0;
+}
+
+static ssize_t tc358749xbg_debugfs_reg_write(
+       struct file *file,
+       const char __user *buf,
+       size_t count,
+       loff_t *ppos)
+{
+       struct tc_camera_module *cam_mod =
+               ((struct seq_file *)file->private_data)->private;
+
+       char kbuf[30];
+       char rw;
+       u32 reg;
+       u32 reg_value;
+       int len;
+       int ret;
+       int nbytes = min(count, sizeof(kbuf) - 1);
+
+       if (copy_from_user(kbuf, buf, nbytes))
+               return -EFAULT;
+
+       kbuf[nbytes] = '\0';
+       tc_camera_module_pr_debug(cam_mod, "kbuf is %s\n", kbuf);
+       ret = sscanf(kbuf, " %c %x %x %d", &rw, &reg, &reg_value, &len);
+       tc_camera_module_pr_err(cam_mod, "ret = %d!\n", ret);
+       if (ret != 4) {
+               tc_camera_module_pr_err(cam_mod, "sscanf failed!\n");
+               return 0;
+       }
+
+       if (rw == 'w') {
+               if (len == 1) {
+                       tc_camera_module_write8_reg(cam_mod, reg, reg_value);
+                       tc_camera_module_pr_err(cam_mod,
+                                               "%s(%d): write reg 0x%02x ---> 0x%x!\n",
+                                               __func__, __LINE__,
+                                               reg, reg_value);
+               } else if (len == 2) {
+                       tc_camera_module_write16_reg(cam_mod, reg, reg_value);
+                       tc_camera_module_pr_err(cam_mod,
+                                               "%s(%d): write reg 0x%02x ---> 0x%x!\n",
+                                               __func__, __LINE__,
+                                               reg, reg_value);
+               } else if (len == 4) {
+                       tc_camera_module_write32_reg(cam_mod, reg, reg_value);
+                       tc_camera_module_pr_err(cam_mod,
+                                               "%s(%d): write reg 0x%02x ---> 0x%x!\n",
+                                               __func__, __LINE__,
+                                               reg, reg_value);
+               } else {
+                       tc_camera_module_pr_err(cam_mod,
+                                               "len %d is err!\n",
+                                               len);
+               }
+       } else if (rw == 'r') {
+               if (len == 1) {
+                       reg_value = tc_camera_module_read8_reg(cam_mod, reg);
+                       tc_camera_module_pr_err(cam_mod,
+                                               "%s(%d): read reg 0x%02x ---> 0x%x!\n",
+                                               __func__, __LINE__,
+                                               reg, reg_value);
+               } else if (len == 2) {
+                       reg_value = tc_camera_module_read16_reg(cam_mod, reg);
+                       tc_camera_module_pr_err(cam_mod,
+                                               "%s(%d): read reg 0x%02x ---> 0x%x!\n",
+                                               __func__, __LINE__,
+                                               reg, reg_value);
+               } else if (len == 4) {
+                       reg_value = tc_camera_module_read32_reg(cam_mod, reg);
+                       tc_camera_module_pr_err(cam_mod,
+                                               "%s(%d): read reg 0x%02x ---> 0x%x!\n",
+                                               __func__, __LINE__,
+                                               reg, reg_value);
+               } else {
+                       tc_camera_module_pr_err(cam_mod,
+                                               "len %d is err!\n",
+                                               len);
+               }
+       } else {
+               tc_camera_module_pr_err(cam_mod,
+                                       "%c command is not support\n",
+                                       rw);
+       }
+
+       return count;
+}
+
+static int tc358749xbg_debugfs_reg_show(struct seq_file *s, void *v)
+{
+       int i;
+       u32 value;
+       int reg_table_num_entries =
+                       sizeof(tc358749xbg_init_tab_1920_1080_60fps) /
+                       sizeof(tc358749xbg_init_tab_1920_1080_60fps[0]);
+       struct tc_camera_module *cam_mod = s->private;
+
+       for (i = 0; i < reg_table_num_entries; i++) {
+               switch (tc358749xbg_init_tab_1920_1080_60fps[i].len) {
+               case 1:
+                       value = tc_camera_module_read8_reg(cam_mod,
+                                                          tc358749xbg_init_tab_1920_1080_60fps[i].reg);
+                       break;
+               case 2:
+                       value =  tc_camera_module_read16_reg(cam_mod,
+                                                            tc358749xbg_init_tab_1920_1080_60fps[i].reg);
+                       break;
+               case 4:
+                       value =  tc_camera_module_read32_reg(cam_mod,
+                                                            tc358749xbg_init_tab_1920_1080_60fps[i].reg);
+                       break;
+               default:
+                       printk(KERN_ERR "%s(%d): command no support!\n", __func__, __LINE__);
+                       break;
+               }
+               printk(KERN_ERR "%s(%d): reg 0x%04x ---> 0x%x\n", __func__, __LINE__,
+                      tc358749xbg_init_tab_1920_1080_60fps[i].reg, value);
+       }
+       return 0;
+}
+
+static int tc358749xbg_debugfs_open(struct inode *inode, struct file *file)
+{
+       struct specific_sensor *spsensor = inode->i_private;
+
+       return single_open(file, tc358749xbg_debugfs_reg_show, spsensor);
+}
+
+static const struct file_operations tc358749xbg_debugfs_fops = {
+       .owner                  = THIS_MODULE,
+       .open                   = tc358749xbg_debugfs_open,
+       .read                   = seq_read,
+       .write                  = tc358749xbg_debugfs_reg_write,
+       .llseek                 = seq_lseek,
+       .release                = single_release
+};
+
+static struct dentry *debugfs_dir;
+
+static int tc358749xbg_probe(
+       struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       dev_info(&client->dev, "probing...\n");
+
+       tc358749xbg_filltimings(&tc358749xbg_custom_config);
+       v4l2_i2c_subdev_init(&tc358749xbg.sd, client,
+                            &tc358749xbg_camera_module_ops);
+
+       tc358749xbg.custom = tc358749xbg_custom_config;
+
+       test_parse_dts(&tc358749xbg, client);
+       debugfs_dir = debugfs_create_dir("hdmiin", NULL);
+       if (IS_ERR(debugfs_dir))
+               printk(KERN_ERR "%s(%d): create debugfs dir failed!\n",
+                      __func__, __LINE__);
+       else
+               debugfs_create_file("register", S_IRUSR,
+                                   debugfs_dir, &tc358749xbg,
+                                   &tc358749xbg_debugfs_fops);
+
+       dev_info(&client->dev, "probing successful\n");
+       return 0;
+}
+
+/* ======================================================================== */
+
+static int tc358749xbg_remove(
+       struct i2c_client *client)
+{
+       struct tc_camera_module *cam_mod = i2c_get_clientdata(client);
+
+       dev_info(&client->dev, "remtcing device...\n");
+
+       if (!client->adapter)
+               return -ENODEV; /* our client isn't attached */
+
+       debugfs_remove_recursive(debugfs_dir);
+       test_deparse_dts(client);
+
+       tc_camera_module_release(cam_mod);
+
+       dev_info(&client->dev, "remtced\n");
+       return 0;
+}
+
+static const struct i2c_device_id tc358749xbg_id[] = {
+       { TC358749xbg_DRIVER_NAME, 0 },
+       { }
+};
+
+static const struct of_device_id tc358749xbg_of_match[] = {
+       {.compatible = "toshiba,tc358749xbg-v4l2-i2c-subdev"},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tc358749xbg_id);
+
+static struct i2c_driver tc358749xbg_i2c_driver = {
+       .driver = {
+               .name = TC358749xbg_DRIVER_NAME,
+               .of_match_table = tc358749xbg_of_match
+       },
+       .probe = tc358749xbg_probe,
+       .remove = tc358749xbg_remove,
+       .id_table = tc358749xbg_id,
+};
+
+module_i2c_driver(tc358749xbg_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for tc358749xbg");
+MODULE_AUTHOR("Benjo");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/rockchip/tc_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/tc_camera_module.c
new file mode 100644 (file)
index 0000000..50b02c1
--- /dev/null
@@ -0,0 +1,1309 @@
+/*
+ * tc_camera_module.c
+ *
+ * Generic toshiba sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * Author:zhoupeng<benjo.zhou@rock-chips.com>
+ * 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 <linux/platform_data/rk_isp10_platform.h>
+
+#include "tc_camera_module.h"
+
+#define I2C_M_WR 0
+#define I2C_MSG_MAX 300
+
+static struct tc_camera_module *to_tc_camera_module(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct tc_camera_module, sd);
+}
+
+/* ======================================================================== */
+
+static void tc_camera_module_reset(
+       struct tc_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 = TC_CAMERA_MODULE_POWER_OFF;
+       cam_mod->state_before_suspend = TC_CAMERA_MODULE_POWER_OFF;
+
+       cam_mod->exp_config.exp_time = 0;
+       cam_mod->exp_config.gain = 0;
+       cam_mod->vts_cur = 0;
+}
+
+/* ======================================================================== */
+
+static void tc_camera_module_set_active_config(
+       struct tc_camera_module *cam_mod,
+       struct tc_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_err(&cam_mod->sd,
+                       "no active config\n");
+       } else {
+               cam_mod->ctrl_updt &= TC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP |
+                       TC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN |
+                       TC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB;
+               if (new_config->auto_exp_enabled !=
+                       cam_mod->exp_config.auto_exp) {
+                       cam_mod->ctrl_updt |=
+                               TC_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 |=
+                               TC_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 |=
+                               TC_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_err(&cam_mod->sd,
+                               "activating config '%s'\n",
+                               cam_mod->active_config->name);
+               }
+       }
+}
+
+/* ======================================================================== */
+
+static struct tc_camera_module_config *tc_camera_module_find_config(
+       struct tc_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 tc_camera_module_write_config(
+       struct tc_camera_module *cam_mod)
+{
+       int ret = 0;
+       struct tc_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 = tc_camera_module_write_reglist(cam_mod,
+               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 tc_camera_module_attach(
+       struct tc_camera_module *cam_mod)
+{
+       int ret = 0;
+       struct tc_camera_module_custom_config *custom;
+
+       pltfrm_camera_module_pr_err(&cam_mod->sd, "enter\n");
+
+       custom = &cam_mod->custom;
+
+       if (custom->check_camera_id) {
+               tc_camera_module_s_power(&cam_mod->sd, 1);
+               ret = custom->check_camera_id(cam_mod);
+               tc_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);
+       tc_camera_module_release(cam_mod);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int tc_camera_module_try_fmt(struct v4l2_subdev *sd,
+       struct v4l2_mbus_framefmt *fmt)
+{
+       struct tc_camera_module *cam_mod = to_tc_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(tc_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 tc_camera_module_s_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format)
+{
+       struct tc_camera_module *cam_mod =  to_tc_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(tc_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) {
+               tc_camera_module_set_active_config(cam_mod,
+                       tc_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 tc_camera_module_g_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format)
+{
+       struct tc_camera_module *cam_mod =  to_tc_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 tc_camera_module_s_frame_interval(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_frame_interval *interval)
+{
+       struct tc_camera_module *cam_mod = to_tc_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(tc_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) {
+               tc_camera_module_set_active_config(cam_mod,
+                       tc_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 tc_camera_module_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       int ret = 0;
+       struct tc_camera_module *cam_mod =  to_tc_camera_module(sd);
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", enable);
+
+       if (enable) {
+               if (cam_mod->state == TC_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 != TC_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 = tc_camera_module_write_config(cam_mod);
+                       if (IS_ERR_VALUE(ret))
+                               goto err;
+               }
+
+               ret = cam_mod->custom.enable_stream(cam_mod, true);
+               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 = TC_CAMERA_MODULE_STREAMING;
+       } else {
+               int pclk;
+               int wait_ms;
+               struct isp_supplemental_sensor_mode_data timings;
+
+               if (cam_mod->state != TC_CAMERA_MODULE_STREAMING)
+                       return 0;
+               ret = cam_mod->custom.enable_stream(cam_mod, false);
+               if (IS_ERR_VALUE(ret))
+                       goto err;
+
+               ret = tc_camera_module_ioctl(sd,
+                                       RK_VIDIOC_SENSOR_MODE_DATA,
+                                       &timings);
+
+               cam_mod->state = TC_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 tc_camera_module_s_power(struct v4l2_subdev *sd, int on)
+{
+       int ret = 0;
+       struct tc_camera_module *cam_mod =  to_tc_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 == TC_CAMERA_MODULE_POWER_OFF) {
+                       cam_mod->custom.s_power(cam_mod, 1);
+                       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 = TC_CAMERA_MODULE_HW_STANDBY;
+                       }
+               }
+               if (cam_mod->state == TC_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 = TC_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) {
+                       tc_camera_module_write_config(cam_mod);
+                       cam_mod->update_config = false;
+               }
+       } else {
+               if (cam_mod->state == TC_CAMERA_MODULE_STREAMING) {
+                       ret = tc_camera_module_s_stream(sd, 0);
+                       if (!IS_ERR_VALUE(ret))
+                               cam_mod->state = TC_CAMERA_MODULE_SW_STANDBY;
+               }
+               if (cam_mod->state == TC_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 = TC_CAMERA_MODULE_HW_STANDBY;
+               }
+               if (cam_mod->state == TC_CAMERA_MODULE_HW_STANDBY) {
+                       cam_mod->custom.s_power(cam_mod, 0);
+                       ret = pltfrm_camera_module_s_power(&cam_mod->sd, 0);
+                       if (!IS_ERR_VALUE(ret)) {
+                               cam_mod->state = TC_CAMERA_MODULE_POWER_OFF;
+                               tc_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 tc_camera_module_g_ctrl(struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       struct tc_camera_module *cam_mod = to_tc_camera_module(sd);
+       int ret;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, " id 0x%x\n", ctrl->id);
+
+       if (ctrl->id == V4L2_CID_MIN_BUFFERS_FOR_CAPTURE) {
+               ctrl->value = 2;
+               pltfrm_camera_module_pr_debug(&cam_mod->sd,
+                       "V4L2_CID_MIN_BUFFERS_FOR_CAPTURE %d\n",
+                       ctrl->value);
+               return 0;
+       }
+
+       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 != TC_CAMERA_MODULE_SW_STANDBY) &&
+               (cam_mod->state != TC_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 tc_camera_module *cam_mod,
+               int value)
+{
+       return 0;
+}
+
+/* ======================================================================== */
+
+int tc_camera_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls)
+{
+       int i;
+       int ctrl_cnt = 0;
+       struct tc_camera_module *cam_mod =  to_tc_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 = TC_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 = TC_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 = TC_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 = TC_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 = TC_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 = TC_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 = TC_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 = TC_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 =
+                               TC_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 != TC_CAMERA_MODULE_SW_STANDBY &&
+                   cam_mod->state != TC_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 == TC_CAMERA_MODULE_STREAMING ||
+               cam_mod->state == TC_CAMERA_MODULE_SW_STANDBY)) {
+               struct tc_camera_module_ext_ctrls tc_ctrls;
+
+               tc_ctrls.ctrls =
+               (struct tc_camera_module_ext_ctrl *)
+               kmalloc(ctrl_cnt * sizeof(struct tc_camera_module_ext_ctrl),
+                       GFP_KERNEL);
+
+               if (tc_ctrls.ctrls) {
+                       for (i = 0; i < ctrl_cnt; i++) {
+                               tc_ctrls.ctrls[i].id = ctrls->controls[i].id;
+                               tc_ctrls.ctrls[i].value =
+                                       ctrls->controls[i].value;
+                       }
+
+                       tc_ctrls.count = ctrl_cnt;
+
+                       ret = cam_mod->custom.s_ext_ctrls(cam_mod, &tc_ctrls);
+
+                       kfree(tc_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 tc_camera_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl)
+{
+       struct tc_camera_module *cam_mod =  to_tc_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 tc_camera_module_s_ext_ctrls(sd, &ext_ctrls);
+}
+
+/* ======================================================================== */
+
+long tc_camera_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg)
+{
+       struct tc_camera_module *cam_mod =  to_tc_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 tc_camera_module_timings tc_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, &tc_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 = tc_timings.sensor_output_width;
+               timings->sensor_output_height = tc_timings.sensor_output_height;
+               timings->crop_horizontal_start =
+                       tc_timings.crop_horizontal_start;
+               timings->crop_vertical_start = tc_timings.crop_vertical_start;
+               timings->crop_horizontal_end = tc_timings.crop_horizontal_end;
+               timings->crop_vertical_end = tc_timings.crop_vertical_end;
+               timings->line_length_pck = tc_timings.line_length_pck;
+               timings->frame_length_lines = tc_timings.frame_length_lines;
+               timings->vt_pix_clk_freq_hz = tc_timings.vt_pix_clk_freq_hz;
+               timings->binning_factor_x = tc_timings.binning_factor_x;
+               timings->binning_factor_y = tc_timings.binning_factor_y;
+               timings->coarse_integration_time_max_margin =
+                       tc_timings.coarse_integration_time_max_margin;
+               timings->coarse_integration_time_min =
+                       tc_timings.coarse_integration_time_min;
+               timings->fine_integration_time_max_margin =
+                       tc_timings.fine_integration_time_max_margin;
+               timings->fine_integration_time_min =
+                       tc_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 = tc_timings.exp_time;
+               if (cam_mod->exp_config.gain)
+                       timings->gain = cam_mod->exp_config.gain;
+               else
+                       timings->gain = tc_timings.gain;
+               return ret;
+       } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) {
+               struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg;
+               struct tc_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) {
+               tc_camera_module_init(cam_mod, &cam_mod->custom);
+               pltfrm_camera_module_ioctl(sd, cmd, arg);
+               return tc_camera_module_attach(cam_mod);
+       }
+
+       ret = pltfrm_camera_module_ioctl(sd, cmd, arg);
+       return ret;
+}
+
+/* ======================================================================== */
+
+int tc_camera_module_get_flip_mirror(
+       struct tc_camera_module *cam_mod)
+{
+       return pltfrm_camera_module_get_flip_mirror(&cam_mod->sd);
+}
+
+/* ======================================================================== */
+
+int tc_camera_module_enum_frameintervals(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_frame_interval_enum *fie)
+{
+       struct tc_camera_module *cam_mod =  to_tc_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;
+}
+
+/* ======================================================================== */
+static int tc_camera_module_i2c_write(struct v4l2_subdev *sd, u16 reg,
+                                      u8 *values, u32 len)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret, i;
+       struct i2c_msg msg[1];
+       unsigned char data[32];
+       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 = 2 + len;
+               msg->buf = data;
+
+               /* high byte goes out first */
+               data[0] = (u8)(reg >> 8);
+               data[1] = (u8)(reg & 0xff);
+               for (i = 0; i < len; i++)
+                       data[2 + i] = values[i];
+
+               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_err(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;
+}
+
+/* ======================================================================== */
+static int tc_camera_module_i2c_read(struct v4l2_subdev *sd, u16 reg,
+                                      u32 *value, u32 len)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = 0;
+       unsigned char data[4] = {0, 0, 0, 0};
+       struct i2c_msg msg[1];
+
+       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 = len;
+               msg->buf = data;
+               i2c_transfer(client->adapter, msg, 1);
+       }
+       if (ret >= 0) {
+               if (len == 1)
+                       *value = data[0];
+               else if (len == 2)
+                       *value = data[0] + (data[1] << 8);
+               else
+                       *value = data[0] + (data[1] << 8) +
+                           (data[2] << 16) + (data[3] << 24);
+               return 0;
+       }
+
+       return ret;
+}
+
+/* ===============================I2c==================================== */
+
+int tc_camera_module_write8_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg,
+       u8 val)
+{
+       return tc_camera_module_i2c_write(&cam_mod->sd, reg, &val, 1);
+}
+
+int tc_camera_module_write16_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg,
+       u16 val)
+{
+       return tc_camera_module_i2c_write(&cam_mod->sd, reg, (u8 *)&val, 2);
+}
+
+int tc_camera_module_write32_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg,
+       u32 val)
+{
+       return tc_camera_module_i2c_write(&cam_mod->sd, reg, (u8 *)&val, 4);
+}
+
+u8 tc_camera_module_read8_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg)
+{
+       u32 val;
+       u8 ret;
+
+       tc_camera_module_i2c_read(&cam_mod->sd, reg, &val, 1);
+       ret = val & 0xff;
+
+       return ret;
+}
+
+u16 tc_camera_module_read16_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg)
+{
+       u32 val;
+       u16 ret;
+
+       tc_camera_module_i2c_read(&cam_mod->sd, reg, &val, 2);
+       ret = val & 0xffff;
+
+       return ret;
+}
+
+u32 tc_camera_module_read32_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg)
+{
+       u32 val;
+
+       tc_camera_module_i2c_read(&cam_mod->sd, reg, &val, 4);
+
+       return val;
+}
+
+int tc_camera_module_read_reg_table(
+       struct tc_camera_module *cam_mod,
+       u16 reg,
+       u32 *val)
+{
+       int i;
+
+       if (cam_mod->state == TC_CAMERA_MODULE_STREAMING)
+               return tc_camera_module_i2c_read(&cam_mod->sd,
+                       reg, val, 1);
+
+       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 == TC_CAMERA_MODULE_SW_STANDBY)
+               return tc_camera_module_i2c_read(&cam_mod->sd,
+                       reg, val, 1);
+
+       return -EFAULT;
+}
+
+int tc_camera_module_write_reglist(
+       struct tc_camera_module *cam_mod,
+       const struct tc_camera_module_reg reglist[],
+       int len)
+{
+       int i = 0;
+
+       for (i = 0; i < len; i++) {
+               switch (reglist[i].flag) {
+               case TC_CAMERA_MODULE_REG_TYPE_DATA:
+                       switch (reglist[i].len) {
+                       case TC_CAMERA_MOUDLE_REG_VALUE_LEN_8BIT:
+                               tc_camera_module_write8_reg(cam_mod, reglist[i].reg, (u8)reglist[i].val);
+                               break;
+                       case TC_CAMERA_MOUDLE_REG_VALUE_LEN_16BIT:
+                               tc_camera_module_write16_reg(cam_mod, reglist[i].reg, (u16)reglist[i].val);
+                               break;
+                       case TC_CAMERA_MOUDLE_REG_VALUE_LEN_32BIT:
+                               tc_camera_module_write32_reg(cam_mod, reglist[i].reg, (u32)reglist[i].val);
+                               break;
+                       default:
+                               pltfrm_camera_module_pr_err(&cam_mod->sd, "unknown value len, default 8bit\n");
+                               tc_camera_module_write8_reg(cam_mod, reglist[i].reg, (u8)reglist[i].val);
+                               break;
+                       }
+                       break;
+               case PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT:
+                       mdelay(reglist[i].val);
+                       break;
+               default:
+                       pltfrm_camera_module_pr_err(&cam_mod->sd, "unknown command\n");
+                       return -1;
+               }
+       }
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "write reg list len %d\n", len);
+       return 0;
+}
+
+int tc_camera_module_init(struct tc_camera_module *cam_mod,
+       struct tc_camera_module_custom_config *custom)
+{
+       int ret = 0;
+
+       pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n");
+
+       tc_camera_module_reset(cam_mod);
+
+       if (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)) {
+               tc_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 tc_camera_module_release(struct tc_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/tc_camera_module.h b/drivers/media/i2c/soc_camera/rockchip/tc_camera_module.h
new file mode 100644 (file)
index 0000000..c4c1d25
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * tc_camera_module.h
+ *
+ * Generic toshiba sensor driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * Copyright (C) 2012-2014 Intel Mobile Communications GmbH
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * Author:zhoupeng<benjo.zhou@rock-chips.com>
+ * 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 TC_CAMERA_MODULE_H
+#define TC_CAMERA_MODULE_H
+#include <linux/workqueue.h>
+#include <linux/hdmi.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 TC_CAMERA_MODULE_REG_TYPE_DATA PLTFRM_CAMERA_MODULE_REG_TYPE_DATA
+#define TC_CAMERA_MODULE_REG_TYPE_TIMEOUT PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT
+#define TC_CAMERA_MOUDLE_REG_VALUE_LEN_8BIT    1
+#define TC_CAMERA_MOUDLE_REG_VALUE_LEN_16BIT   2
+#define TC_CAMERA_MOUDLE_REG_VALUE_LEN_32BIT   4
+#define tc_camera_module_csi_config
+#define TC_FLIP_BIT_MASK                                       0x2
+#define TC_MIRROR_BIT_MASK                                     0x1
+
+#define TC_CAMERA_MODULE_CTRL_UPDT_GAIN                                0x01
+#define TC_CAMERA_MODULE_CTRL_UPDT_EXP_TIME                    0x02
+#define TC_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE              0x04
+#define TC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB                     0x08
+#define TC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN                   0x10
+#define TC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP                    0x20
+#define TC_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE              0x40
+#define TC_CAMERA_MODULE_CTRL_UPDT_PRESET_WB                   0x80
+
+enum tc_camera_module_state {
+       TC_CAMERA_MODULE_POWER_OFF = 0,
+       TC_CAMERA_MODULE_HW_STANDBY = 1,
+       TC_CAMERA_MODULE_SW_STANDBY = 2,
+       TC_CAMERA_MODULE_STREAMING = 3
+};
+
+struct tc_camera_module;
+
+struct tc_camera_module_reg {
+       u32 flag;
+       u16 reg;
+       u32 val;
+       u8 len;
+};
+
+struct tc_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 tc_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 tc_camera_module_reg *reg_table;
+       u32 reg_table_num_entries;
+       struct tc_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 tc_camera_module_timings timings;
+       bool soft_reset;
+       bool ignore_measurement_check;
+
+       struct pltfrm_cam_itf itf_cfg;
+};
+
+struct tc_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 tc_camera_module_wb_config {
+       u32 temperature;
+       u32 preset_id;
+       bool auto_wb;
+};
+
+struct tc_camera_module_af_config {
+       u32 abs_pos;
+       u32 rel_pos;
+};
+
+struct tc_camera_module_ext_ctrl {
+       /* public */
+       u32 id;
+       u32 value;
+       __u32 reserved2[1];
+};
+
+struct tc_camera_module_ext_ctrls {
+       /* public */
+       u32 count;
+       struct tc_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 prtcided 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
+ *             tc_camera_module_s_ctrl with the ctrl_id as argument.
+ *
+ *     priv: (optional) for private data used by the custom driver.
+ */
+struct tc_camera_module_custom_config {
+       int (*check_camera_id)(struct tc_camera_module *cam_mod);
+       int (*s_ctrl)(struct tc_camera_module *cam_mod, u32 ctrl_id);
+       int (*g_ctrl)(struct tc_camera_module *cam_mod, u32 ctrl_id);
+       int (*g_timings)(struct tc_camera_module *cam_mod,
+               struct tc_camera_module_timings *timings);
+       int (*g_exposure_valid_frame)(struct tc_camera_module *cam_mod);
+       int (*s_ext_ctrls)(struct tc_camera_module *cam_mod,
+               struct tc_camera_module_ext_ctrls *ctrls);
+       int (*set_flip)(
+               struct tc_camera_module *cam_mod,
+               struct tc_camera_module_reg reglist[],
+               int len);
+       int (*init_common)(struct tc_camera_module *cam_mod);
+       int (*read_otp)(struct tc_camera_module *cam_mod);
+       int (*enable_stream)(struct tc_camera_module *cam_mod, bool enable);
+       int (*s_power)(struct tc_camera_module *cam_mod, bool enable);
+       struct tc_camera_module_config *configs;
+       u32 num_configs;
+       u32 power_up_delays_ms[3];
+       void *priv;
+};
+
+struct tc_camera_module_otp_work {
+       struct work_struct work;
+       struct workqueue_struct *wq;
+       void *cam_mod;
+};
+
+struct tc_camera_module {
+       /* public */
+       struct v4l2_subdev sd;
+       struct v4l2_mbus_framefmt frm_fmt;
+       struct v4l2_subdev_frame_interval frm_intrvl;
+       struct tc_camera_module_exp_config exp_config;
+       struct tc_camera_module_wb_config wb_config;
+       struct tc_camera_module_af_config af_config;
+       struct tc_camera_module_custom_config custom;
+       enum tc_camera_module_state state;
+       enum tc_camera_module_state state_before_suspend;
+       struct tc_camera_module_config *active_config;
+       struct tc_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;
+};
+
+struct tc35x_priv {
+       struct regmap                   *regmap;
+       struct i2c_client               *client;
+       struct device                   *dev;
+       struct gpio_desc                *gpio_power;
+       struct gpio_desc                *gpio_power18;
+       struct gpio_desc                *gpio_power33;
+       struct gpio_desc                *gpio_csi_ctl;
+       struct gpio_desc                *gpio_reset;
+       struct gpio_desc                *gpio_stanby;
+       struct gpio_desc                *gpio_int;
+};
+
+#define tc_camera_module_pr_info(cam_mod, fmt, arg...) \
+       pltfrm_camera_module_pr_info(&(cam_mod)->sd, fmt, ## arg)
+#define tc_camera_module_pr_debug(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_debug(&(cam_mod)->sd, fmt, ## arg)
+#define tc_camera_module_pr_warn(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_warn(&(cam_mod)->sd, fmt, ## arg)
+#define tc_camera_module_pr_err(cam_mod, fmt, arg...) \
+               pltfrm_camera_module_pr_err(&(cam_mod)->sd, fmt, ## arg)
+
+int tc_camera_module_write8_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg,
+       u8 val);
+
+int tc_camera_module_write16_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg,
+       u16 val);
+
+int tc_camera_module_write32_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg,
+       u32 val);
+
+u8 tc_camera_module_read8_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg);
+
+u16 tc_camera_module_read16_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg);
+
+u32 tc_camera_module_read32_reg(
+       struct tc_camera_module *cam_mod,
+       u16 reg);
+
+int tc_camera_module_read_reg_table(
+       struct tc_camera_module *cam_mod,
+       u16 reg,
+       u32 *val);
+
+int tc_camera_module_write_reglist(
+       struct tc_camera_module *cam_mod,
+       const struct tc_camera_module_reg reglist[],
+       int len);
+
+int tc_camera_module_s_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format);
+
+int tc_camera_module_g_fmt(struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_format *format);
+
+int tc_camera_module_s_frame_interval(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_frame_interval *interval);
+
+int tc_camera_module_s_stream(
+       struct v4l2_subdev *sd,
+       int enable);
+
+int tc_camera_module_s_power(
+       struct v4l2_subdev *sd,
+       int on);
+
+int tc_camera_module_g_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl);
+
+int tc_camera_module_s_ctrl(
+       struct v4l2_subdev *sd,
+       struct v4l2_control *ctrl);
+
+int tc_camera_module_s_ext_ctrls(
+       struct v4l2_subdev *sd,
+       struct v4l2_ext_controls *ctrls);
+
+int tc_camera_module_enum_frameintervals(
+       struct v4l2_subdev *sd,
+       struct v4l2_subdev_pad_config *cfg,
+       struct v4l2_subdev_frame_interval_enum *fie);
+
+int tc_camera_module_init(
+       struct tc_camera_module *cam_mod,
+       struct tc_camera_module_custom_config *custom);
+
+void tc_camera_module_release(
+       struct tc_camera_module *cam_mod);
+
+long tc_camera_module_ioctl(struct v4l2_subdev *sd,
+       unsigned int cmd,
+       void *arg);
+
+int tc_camera_module_get_flip_mirror(
+       struct tc_camera_module *cam_mod);
+#endif