]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
media: soc_camera: add tc358840 v4l2 driver
authorFrank Shi <fshi@nvidia.com>
Fri, 11 Dec 2015 09:56:48 +0000 (17:56 +0800)
committermobile promotions <svcmobile_promotions@nvidia.com>
Wed, 23 Dec 2015 10:02:06 +0000 (02:02 -0800)
Add TC358840 V4l2 driver
- support 1080p and 4K HDMI-in
- support UYVY and RGB888
- support hotplug detection
- support resolution select

Bug 200113762

Change-Id: I3921e8f0b679ac99d0e7df6193d7080795d3b673
Signed-off-by: Frank Shi <fshi@nvidia.com>
Reviewed-on: http://git-master/r/921825
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
arch/arm64/boot/dts/tegra210-platforms/tegra210-jetson-cv-hdmi-in.dtsi [new file with mode: 0644]
arch/arm64/mach-tegra/board-t210ref-camera.c
drivers/media/i2c/soc_camera/Kconfig
drivers/media/i2c/soc_camera/Makefile
drivers/media/i2c/soc_camera/tc358840.h [new file with mode: 0644]
drivers/media/i2c/soc_camera/tc358840_v4l2.c [new file with mode: 0644]
drivers/media/platform/soc_camera/camera_common.c
drivers/media/platform/soc_camera/tegra_camera/vi2.c

diff --git a/arch/arm64/boot/dts/tegra210-platforms/tegra210-jetson-cv-hdmi-in.dtsi b/arch/arm64/boot/dts/tegra210-platforms/tegra210-jetson-cv-hdmi-in.dtsi
new file mode 100644 (file)
index 0000000..c54edae
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <dt-bindings/media/camera.h>
+#include <dt-bindings/platform/t210/t210.h>
+
+#define CAM_I2C_BUS    6
+
+#define CAM0_RST_L     TEGRA_GPIO(S, 4)
+#define CAM0_PWDN              TEGRA_GPIO(S, 7)
+#define CAM1_RST_L     TEGRA_GPIO(S, 5)
+#define CAM1_PWDN              TEGRA_GPIO(T, 0)
+
+/ {
+       tegra-camera-platform {
+               compatible = "nvidia, tegra-camera-platform";
+       };
+
+       tc358840@0f {
+               rst = <&gpio CAM0_RST_L GPIO_ACTIVE_HIGH>;
+               int = <&gpio TEGRA_GPIO(X, 2) GPIO_ACTIVE_HIGH>;
+               vif-supply = <&en_vdd_cam>;
+               vdig-supply = <&en_vdd_cam_1v2>;
+       };
+
+       gpio@6000d000 {
+               gpio-init-names = "default";
+               gpio-init-0 = <&gpio_default>;
+
+               gpio_default: default {
+                       gpio-input = <
+                               TEGRA_GPIO(X, 2)
+                               >;
+                       gpio-output-low = <
+                               TEGRA_GPIO(S, 4)
+                               >;
+               };
+       };
+};
+
index 6fceb541d0c559915bd4f9859fe4d126ae31ad31..11258f73294aad86694b04f147486063c16d60ec 100644 (file)
@@ -476,6 +476,43 @@ static struct platform_device t210ref_ov5693_f_soc_camera_device = {
 };
 #endif
 
+#if IS_ENABLED(CONFIG_SOC_CAMERA_TC358840)
+static int t210ref_tc358840_power(struct device *dev, int enable)
+{
+       return 0;
+}
+
+static struct i2c_board_info t210ref_tc358840_camera_i2c_device = {
+       I2C_BOARD_INFO("tc358840_v4l2", 0x1f),
+};
+
+static struct tegra_camera_platform_data
+t210ref_tc358840_camera_platform_data = {
+       .flip_v                 = 0,
+       .flip_h                 = 0,
+       .port                   = TEGRA_CAMERA_PORT_CSI_C,
+       .lanes                  = 4,
+       .continuous_clk         = 1,
+};
+
+static struct soc_camera_link tc358840_iclink = {
+       .bus_id         = 0, /* This must match the .id of tegra_vi01_device */
+       .board_info     = &t210ref_tc358840_camera_i2c_device,
+       .module_name    = "tc358840_v4l2",
+       .i2c_adapter_id = 6, /* CAM I2C controller */
+       .power          = t210ref_tc358840_power,
+       .priv           = &t210ref_tc358840_camera_platform_data,
+};
+
+static struct platform_device t210ref_tc358840_soc_camera_device = {
+       .name   = "soc-camera-pdrv",
+       .id     = 0,
+       .dev    = {
+               .platform_data = &tc358840_iclink,
+       },
+};
+#endif
+
 int t210ref_camera_init(void)
 {
        pr_debug("%s: ++\n", __func__);
@@ -511,5 +548,10 @@ int t210ref_camera_init(void)
        }
 #endif
 
+#if IS_ENABLED(CONFIG_SOC_CAMERA_TC358840)
+       if (of_machine_is_compatible("nvidia,jetson-cv"))
+               platform_device_register(&t210ref_tc358840_soc_camera_device);
+#endif
+
        return 0;
 }
index f0ed930f335b217d564ab3efedf82bf87fe79d45..2c32d1b5beca7d320c07bc3421ceadb63ae18616 100644 (file)
@@ -139,3 +139,9 @@ config SOC_CAMERA_TW9910
        depends on SOC_CAMERA && I2C
        help
          This is a tw9910 video driver
+
+config SOC_CAMERA_TC358840
+       tristate "tc358840xbg support"
+       depends on SOC_CAMERA && I2C
+       help
+         This is a tc358840xbg video driver
index c30056f7e0d4ea5b6621f994ca09846871c8eedd..44c4c4c56d207859a4f19e2681066e6cfafc5541 100644 (file)
@@ -21,6 +21,7 @@ obj-$(CONFIG_SOC_CAMERA_OV9740)               += ov9740.o
 obj-$(CONFIG_SOC_CAMERA_OV13860)               += ov13860_v4l2.o
 obj-$(CONFIG_SOC_CAMERA_RJ54N1)                += rj54n1cb0c.o
 obj-$(CONFIG_SOC_CAMERA_TW9910)                += tw9910.o
+obj-$(CONFIG_SOC_CAMERA_TC358840)      += tc358840_v4l2.o
 
 ifneq (,$(filter $(CONFIG_SOC_CAMERA_OV5693),y m))
        ccflags-y += -I$(srctree)/drivers/media/platform/tegra
diff --git a/drivers/media/i2c/soc_camera/tc358840.h b/drivers/media/i2c/soc_camera/tc358840.h
new file mode 100644 (file)
index 0000000..be3b587
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+enum cmd_type {
+       WR8 = 0,
+       WR16,
+       WR32,
+       RD8,
+       RD16,
+       DELAY,
+       CMD_END,
+};
+
+/*
+ * How CSI port 0/1 splits the HDMI input
+ * DISABLE - disable CSI2 TX split
+ * MANUAL - enable, manually configure the window
+ * LEFT_RIGHT - enable auto split, left half by TX0; right half by TX1
+ * RIGHT_LEFT - enable auto split, left half by TX1; right half by TX0
+ */
+enum splitter_type {
+       LEFT_RIGHT = 0,
+       RIGHT_LEFT,
+       MANUAL,
+       DISABLE,
+};
+
+struct tc358840_reg {
+       unsigned char cmd_type;
+       unsigned int addr;
+       unsigned int val;
+};
+
+struct hdmi_mode {
+       int hactive;
+       int vactive;
+       bool changed;
+};
+
+struct csi_tx_window {
+       int first_pixel;
+       int len;
+};
+
+struct split_mode {
+       u8 mode;
+       struct csi_tx_window tx0;
+       struct csi_tx_window tx1;
+};
+
+struct tc358840 {
+       struct v4l2_subdev              *subdev;
+       struct i2c_client               *i2c_client;
+       struct regmap                   *regmap;
+
+       struct camera_common_power_rail power;
+       int                             num_ctrls;
+       struct v4l2_ctrl_handler        ctrl_handler;
+
+       struct camera_common_data       *s_data;
+
+#ifdef CONFIG_SWITCH
+       struct switch_dev               hpd_switch;
+#endif
+
+       int                             hpd_gpio;
+       int                             rst_gpio;
+       int                             int_gpio;
+       int                             cam_1v8_gpio;
+       int                             cam_1v2_gpio;
+
+       struct                          hdmi_mode input;
+       unsigned int                    hpd_irq;
+       unsigned int                    chip_irq;
+       struct                          split_mode split;
+
+       struct v4l2_ctrl                *ctrls[];
+};
+
+
+#define SYSCTL 0x0002
+#define CONFCTL0       0x0004
+#define CONFCTL1       0x0006
+#define CONFCTL1       0x0006
+#define INTSTATUS      0x0014
+#define INTSTATUS_HDMI_INT     0x0200
+#define INTMASK        0x0016
+#define SYSINTSTATUS   0x001A
+
+#define TX0CSI_TX_CLKEN        0x0108
+#define TX0CSI_TX_CLKSEL       0x010C
+#define TX0MODE_CONFIG 0x0110
+#define TX0LANE_ENABLE 0x0118
+#define TX0CSITX_START 0x011C
+#define TX0LINE_INIT_COUNT     0x0120
+#define TX0HSTX_TO_COUNT       0x0124
+#define TX0FUNC_ENABLE 0x0128
+#define TX0CSI_LPTX_MODE       0x012C
+#define TX0CSI_TATO_COUNT      0x0130
+#define TX0CSI_PRESP_BTA_COUNT 0x0134
+#define TX0CSI_PRESP_LPR_COUNT 0x0138
+#define TX0CSI_PRESP_LPW_COUNT 0x013C
+#define TX0CSI_PRESP_HSR_COUNT 0x0140
+#define TX0CSI_PRESP_HSW_COUNT 0x0144
+#define TX0CSI_PR_TO_COUNT     0x0148
+#define TX0CSI_LRX_H_TO_COUNT  0x014C
+#define TX0FUNC_MODE   0x0150
+#define TX0CSI_RX_VC_ENABLE    0x0154
+#define TX0IND_TO_COUNT        0x0158
+#define TX0INIT_INT_MSK        0x0164
+#define TX0CSI_HSYNC_STOP_COUNT        0x0168
+#define TX0APF_VDELAYCNT       0x0170
+#define TX0APF_VC_CONFIG       0x0178
+#define TX0CSI_RX_STATE_INT_MASK       0x01A4
+#define TX0CSI_RXTRG_INT_MASK  0x01AC
+#define TX0CSI_LPRX_THRESH_COUNT       0x01C0
+
+#define TX0CSI_PRTO_INT_MASK   0x020C
+#define TX0APP_SIDE_ERR_INT_MASK       0x0214
+#define TX0CSI_RX_ERR_INT_MASK 0x021C
+#define TX0CSI_LPTX_INT_MASK   0x0224
+#define TX0DPHY_DLYCNTRL       0x0240
+#define TX0LPTXTIMECNT 0x0254
+#define TX0TCLK_HEADERCNT      0x0258
+#define TX0TCLK_TRAILCNT       0x025C
+#define TX0THS_HEADERCNT       0x0260
+#define TX0TWAKEUPCNT  0x0264
+#define TX0TCLK_POSTCNT        0x0268
+#define TX0THS_TRAILCNT        0x026C
+#define TX0HSTXVREGCNT 0x0270
+#define TX0HSTXVREGEN  0x0274
+#define TX0BTA_COUNT   0x0278
+#define TX0DPHY_TX_ADJUST      0x027C
+#define TX0DPHY_CAP    0x0288
+#define TX0MIPI_PLL_CONTROL    0x02A0
+#define TX0MIPI_PLL_CNF        0x02AC
+
+#define TX1CSI_TX_CLKEN        0x0308
+#define TX1CSI_TX_CLKSEL       0x030C
+#define TX1LANE_ENABLE 0x0318
+#define TX1MODE_CONFIG 0x0310
+#define TX1CSITX_START 0x031C
+#define TX1LINE_INIT_COUNT     0x0320
+#define TX1HSTX_TO_COUNT       0x0324
+#define TX1FUNC_ENABLE 0x0328
+#define TX1CSI_LPTX_MODE       0x032C
+#define TX1CSI_TATO_COUNT      0x0330
+#define TX1CSI_PRESP_BTA_COUNT 0x0334
+#define TX1CSI_PRESP_LPR_COUNT 0x0338
+#define TX1CSI_PRESP_LPW_COUNT 0x033C
+#define TX1CSI_PRESP_HSR_COUNT 0x0340
+#define TX1CSI_PRESP_HSW_COUNT 0x0344
+#define TX1CSI_PR_TO_COUNT     0x0348
+#define TX1CSI_LRX_H_TO_COUNT  0x034C
+#define TX1FUNC_MODE   0x0350
+#define TX1CSI_RX_VC_ENABLE    0x0354
+#define TX1IND_TO_COUNT        0x0358
+#define TX1INIT_INT_MSK        0x0364
+#define TX1CSI_HSYNC_STOP_COUNT        0x0368
+#define TX1APF_VDELAYCNT       0x0370
+#define TX1APF_VC_CONFIG       0x0378
+#define TX1CSI_RX_STATE_INT_MASK       0x03A4
+#define TX1CSI_RXTRG_INT_MASK  0x03AC
+#define TX1CSI_LPRX_THRESH_COUNT       0x03C0
+
+#define TX1CSI_PRTO_INT_MASK   0x040C
+#define TX1APP_SIDE_ERR_INT_MASK       0x0414
+#define TX1CSI_RX_ERR_INT_MASK 0x041C
+#define TX1CSI_LPTX_INT_MASK   0x0424
+#define TX1DPHY_DLYCNTRL       0x0440
+#define TX1LPTXTIMECNT 0x0454
+#define TX1TCLK_HEADERCNT      0x0458
+#define TX1TCLK_TRAILCNT       0x045C
+#define TX1THS_HEADERCNT       0x0460
+#define TX1TWAKEUPCNT  0x0464
+#define TX1TCLK_POSTCNT        0x0468
+#define TX1THS_TRAILCNT        0x046C
+#define TX1HSTXVREGCNT 0x0470
+#define TX1HSTXVREGEN  0x0474
+#define TX1BTA_COUNT   0x0478
+#define TX1DPHY_TX_ADJUST      0x047C
+#define TX1DPHY_CAP    0x0488
+#define TX1MIPI_PLL_CONTROL    0x04A0
+#define TX1MIPI_PLL_CNF        0x04AC
+#define TX1MIPI_PLL_CONTROL    0x04A0
+
+#define STX0_CTL       0x5000
+#define STX0_VPID1     0x5002
+#define STX0_VPID2     0x5004
+#define STX0_VPID3     0x5006
+#define STX0_WC        0x5008
+#define STX0_FPX       0x500C
+#define STX0_LPX       0x500E
+#define STX0_MAXFCNT   0x0510
+#define STX1_MAXFCNT   0x0514
+#define STX1_CTL       0x5080
+#define STX1_VPID1     0x5082
+#define STX1_VPID2     0x5084
+#define STX1_VPID3     0x5086
+#define STX1_WC        0x5088
+#define STX1_FPX       0x508C
+#define STX1_LPX       0x508E
+
+#define CB_CTL 0x7000
+#define CB_HSW 0x7008
+#define CB_VSW 0x700A
+#define CB_HTOTAL      0x700C
+#define CB_VTOTAL      0x700E
+#define CB_HACT        0x7010
+#define CB_VACT        0x7012
+#define CB_HSTART      0x7014
+#define CB_VSTART      0x7016
+
+#define PX_FREQ0       0x8405
+#define PX_FREQ1       0x8406
+#define PHY_CTL        0x8410
+#define PHY_ENB        0x8413
+#define EQ_BYPS        0x8420
+#define APLL_CTL       0x84F0
+#define DDCIO_CTL      0x84F4
+
+#define SYS_INT        0x8502
+#define MISC_INT       0x850B
+#define SYS_INTM       0x8512
+#define CLK_INTM       0x8513
+#define PACKET_INTM    0x8514
+#define AUDIO_IMNTM    0x8515
+#define ABUF_INTM      0x8516
+#define MISC_INTM      0X851B
+#define SYS_STATUS     0x8520
+#define VI_STATUS0     0x8521
+#define VI_STATUS1     0x8522
+#define VI_STATUS2     0x8525
+#define CLK_STATUS     0x8526
+#define VI_STATUS3     0x8528
+#define SYS_FREQ0      0x8540
+#define SYS_FREQ1      0x8541
+#define DDC_CTL        0x8543
+#define HPD_CTL        0x8544
+#define INIT_END       0x854A
+#define HDCP_MODE      0x8560
+#define DE_HPOS0       0x8580
+#define DE_HPOS1       0x8581
+#define DE_HWID0       0x8582
+#define DE_HWID1       0x8583
+#define DE_POS_A0      0x8584
+#define DE_POS_A1      0x8585
+#define DE_POS_B0      0x8586
+#define DE_POS_B1      0x8587
+#define DE_VWID0       0x858C
+#define DE_VWID1       0x858D
+#define H_SIZE0        0x858E
+#define H_SIZE1        0x858F
+#define V_SIZE0        0x8590
+#define V_SIZE1        0x8591
+
+#define EDID_MODE      0x85E0
+#define EDID_LEN1_2    0x85E3
+
+#define AUD_AUTO_MUTE  0x8600
+#define AUTO_CMD0      0x8602
+#define AUTO_CMD1      0x8603
+#define AUTO_CMD2      0x8604
+#define BUFINIT_START  0x8606
+#define FS_MUTE        0x8607
+#define LOCKDET_FREQ0  0x8630
+#define LOCKDET_REF1_2 0x8631
+#define NCO_F0_MOD     0x8670
+#define SDO_MODE1      0x8652
+#define NCO_48F0A_D    0x8671
+#define NCO_44F0A_D    0x8675
+#define AUD_MODE       0x8680
+
+#define EDID_RAM_START 0x8C00
+
+#define CSC_SCLK0      0x8A0C
+#define CSC_SCLK1      0x8A0D
+#define VOUT_FMT       0x8A00
+#define VOUT_FIL       0x8A01
+#define VOUT_SYNC0     0x8A02
+#define VOUT_COLOR     0x8A08
+
+#define EDID_RAM_SIZE  256
diff --git a/drivers/media/i2c/soc_camera/tc358840_v4l2.c b/drivers/media/i2c/soc_camera/tc358840_v4l2.c
new file mode 100644 (file)
index 0000000..157ac53
--- /dev/null
@@ -0,0 +1,1381 @@
+/*
+ * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#ifdef CONFIG_SWITCH
+#include <linux/switch.h>
+#endif
+
+#include <linux/seq_file.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+#include <media/v4l2-chip-ident.h>
+#include <media/camera_common.h>
+#include <media/tegra_v4l2_camera.h>
+
+#include "tc358840.h"
+
+#define COLOR_BAR_MODE 0
+#define ENABLE_HPD_PIN 0
+
+#define TC358840_DEFAULT_MODE     0
+#define TC358840_DEFAULT_WIDTH    1920
+#define TC358840_DEFAULT_HEIGHT   1080
+#define TC358840_DEFAULT_DATAFMT  V4L2_MBUS_FMT_UYVY8_2X8
+#define TC358840_DEFAULT_CLK_FREQ 24000000
+
+static const struct camera_common_frmfmt tc358840_frmfmt[] = {
+       {{960,  1080}, 0, 0},
+       {{1920, 2160}, 0, 0},
+       {{1920, 1080}, 0, 0},
+       {{3840, 2160}, 0, 0},
+};
+
+static struct tc358840_reg startup_seq1[] = {
+       /* SOFTWARE RESET */
+       {WR16, CONFCTL0, 0x80C4},
+       {WR16, SYSCTL, 0x3F01},
+       {WR16, SYSCTL, 0x0000},
+       {WR16, CONFCTL1, 0x0008},
+       /* CSI-TX0 TRANSITION TIMING */
+       {WR32, TX0CSI_TX_CLKEN, 0x00000001},
+       {WR32, TX0CSI_TX_CLKSEL, 0x00000001},
+       {WR32, TX0MIPI_PLL_CONTROL, 0x00000001},
+       {WR32, TX0MIPI_PLL_CNF, 0x000090E5},
+       {DELAY, 0},
+       {WR32, TX0MIPI_PLL_CONTROL, 0x00000003},
+       {WR32, TX0LANE_ENABLE, 0x00000014},
+       {WR32, TX0LINE_INIT_COUNT, 0x00000FA0},
+       {WR32, TX0HSTX_TO_COUNT, 0x00000000},
+       {WR32, TX0FUNC_ENABLE, 0x00000101},
+       /* REMEND, TX0CSI_LPTX_MODE, 0x00000000 */
+       {WR32, TX0CSI_TATO_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_BTA_COUNT, 0x00005000},
+       {WR32, TX0CSI_PRESP_LPR_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_LPW_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_HSR_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_HSW_COUNT, 0x00010000},
+       {WR32, TX0CSI_PR_TO_COUNT, 0x00001000},
+       {WR32, TX0CSI_LRX_H_TO_COUNT, 0x00010000},
+       {WR32, TX0FUNC_MODE, 0x00000400},
+       {WR32, TX0CSI_RX_VC_ENABLE, 0x00000001},
+       {WR32, TX0IND_TO_COUNT, 0x000000C8},
+       /* REMEND, TX0INIT_INT_MSK, 0x00000014 */
+       {WR32, TX0CSI_HSYNC_STOP_COUNT, 0x0000002A},
+       /* REMND, TX0APF_VDELAYCNT, 0x00000466 */
+       /* REMEND, TX0APF_VC_CONFIG, 0x00000001 */
+       {WR32, TX0CSI_RX_STATE_INT_MASK, 0x00000000},
+       /* REMEND, TX0CSI_RXTRG_INT_MASK, 0x00000000 */
+       {WR32, TX0CSI_LPRX_THRESH_COUNT, 0x00000015},
+       /* REMEND, TX0CSI_PRTO_INT_MASK, 0x00000000 */
+       {WR32, TX0APP_SIDE_ERR_INT_MASK, 0x00000000},
+       {WR32, TX0CSI_RX_ERR_INT_MASK, 0x00000080},
+       {WR32, TX0CSI_LPTX_INT_MASK, 0x00000000},
+       /* REMEND, TX0DPHY_DLYCNTRL, 0x00000000 */
+       {WR32, TX0LPTXTIMECNT, 0x00000004},
+       {WR32, TX0TCLK_HEADERCNT, 0x00180203},
+       {WR32, TX0TCLK_TRAILCNT, 0x00040005},
+       {WR32, TX0THS_HEADERCNT, 0x000D0004},
+       {WR32, TX0TWAKEUPCNT, 0x00003E80},
+       {WR32, TX0TCLK_POSTCNT, 0x0000000A},
+       {WR32, TX0THS_TRAILCNT, 0x00080006},
+       {WR32, TX0HSTXVREGCNT, 0x00000020},
+       {WR32, TX0HSTXVREGEN, 0x0000001F},
+       {WR32, TX0BTA_COUNT, 0x00040003},
+       {WR32, TX0DPHY_TX_ADJUST, 0x00000002},
+       /* REMEND, TX0DPHY_CAP, 0x000002AA */
+       {WR32, TX0CSITX_START, 0x00000001},
+       /* CSI-TX1 TRANSITION TIMING */
+       {WR32, TX1CSI_TX_CLKEN, 0x00000001},
+       {WR32, TX1CSI_TX_CLKSEL, 0x00000001},
+       {WR32, TX1MIPI_PLL_CONTROL, 0x00000001},
+       {WR32, TX1MIPI_PLL_CNF, 0x000090E5},
+       {DELAY, 0},
+       {WR32, TX1MIPI_PLL_CONTROL, 0x00000003},
+       {WR32, TX1LANE_ENABLE, 0x00000014},
+       {WR32, TX1LINE_INIT_COUNT, 0x00000FA0},
+       {WR32, TX1HSTX_TO_COUNT, 0x00000000},
+       {WR32, TX1FUNC_ENABLE, 0x00000101},
+       /* REMEND, TX1CSI_LPTX_MODE, 0x00000004 */
+       {WR32, TX1CSI_TATO_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_BTA_COUNT, 0x00005000},
+       {WR32, TX1CSI_PRESP_LPR_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_LPW_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_HSR_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_HSW_COUNT, 0x00010000},
+       {WR32, TX1CSI_PR_TO_COUNT, 0x00001000},
+       {WR32, TX1CSI_LRX_H_TO_COUNT, 0x00010000},
+       {WR32, TX1FUNC_MODE, 0x00000400},
+       {WR32, TX1CSI_RX_VC_ENABLE, 0x00000001},
+       {WR32, TX1IND_TO_COUNT, 0x000000C8},
+       /* REMEND, TX1INIT_INT_MSK, 0x00000015 */
+       {WR32, TX1CSI_HSYNC_STOP_COUNT, 0x0000002A},
+       /* REMND, TX1APF_VDELAYCNT, 0x00000466 */
+       /* REMEND, TX1APF_VC_CONFIG, 0x00000001 */
+       {WR32, TX1CSI_RX_STATE_INT_MASK, 0x00000000},
+       /* REMEND, TX1CSI_RXTRG_INT_MASK, 0x00000000 */
+       {WR32, TX1CSI_LPRX_THRESH_COUNT, 0x00000015},
+       /* REMEND, TX1CSI_PRTO_INT_MASK, 0x00000000 */
+       {WR32, TX1APP_SIDE_ERR_INT_MASK, 0x00000000},
+       {WR32, TX1CSI_RX_ERR_INT_MASK, 0x00000080},
+       {WR32, TX1CSI_LPTX_INT_MASK, 0x00000000},
+       /* REMEND, TX1DPHY_DLYCNTRL, 0x00000000 */
+       {WR32, TX1LPTXTIMECNT, 0x00000004},
+       {WR32, TX1TCLK_HEADERCNT, 0x00180203},
+       {WR32, TX1TCLK_TRAILCNT, 0x00040005},
+       {WR32, TX1THS_HEADERCNT, 0x000D0004},
+       {WR32, TX1TWAKEUPCNT, 0x00003E80},
+       {WR32, TX1TCLK_POSTCNT, 0x0000000A},
+       {WR32, TX1THS_TRAILCNT, 0x00080006},
+       {WR32, TX1HSTXVREGCNT, 0x00000020},
+       {WR32, TX1HSTXVREGEN, 0x0000001F},
+       {WR32, TX1BTA_COUNT, 0x00040003},
+       {WR32, TX1DPHY_TX_ADJUST, 0x00000002},
+       /* REMEND, TX1DPHY_CAP, 0x00000000 */
+       {WR32, TX1CSITX_START, 0x00000001},
+       /* SYNC SIGNAL POLARITY */
+       {WR32, TX0MODE_CONFIG, 0x00000006},
+       {WR32, TX1MODE_CONFIG, 0x00000006},
+       /* SPLIT CONTROL, VPID */
+       {WR16, STX0_CTL, 0x0000},
+       /* REMND, STX0_VPID1, 0x3435 */
+       /* REMND, STX0_VPID2, 0x3600 */
+       /* REMND, STX0_VPID3, 0x0024 */
+       /* REMND, {WR16, STX0_WC, 0x0F00},*/
+       {WR16, STX0_FPX, 0x8000},
+       /* REMND, STX0_LPX, 077F */
+       {WR16, STX1_CTL, 0x0000},
+       /* REMND, STX1_VPID1, 0x3435 */
+       /* REMND, STX1_VPID2, 0x3600 */
+       /* REMND, STX1_VPID3, 0x0024 */
+       /* REMND{WR16, STX1_WC, 0x0F00}, */
+       /* REMND, STX1_FPX, 0x0000 */
+       /* REMND, STX1_LPX, 077F */
+       /* FRAME COUNT CONTROL */
+       /* REMND, STX0_MAXFCNT, 0x0000 */
+       /* REMND, STX1_MAXFCNT, 0x0000 */
+       /* HDMI PHY */
+       {WR8, PHY_CTL, 0x03},
+       {WR8, PHY_ENB, 0x3F},
+       {WR8, EQ_BYPS, 0x07},
+       {WR8, APLL_CTL, 0x31},
+       {WR8, DDCIO_CTL, 0x01},
+       /* REM, HDMI CLOCK */
+       {WR16, SYS_FREQ0, 0x0FA0},
+       {WR8, LOCKDET_FREQ0, 0x80},
+       {WR16, LOCKDET_REF1_2, 0x061A},
+       {WR8, NCO_F0_MOD, 0x02},
+       {WR16, CSC_SCLK0, 0xFA0},
+       /* REM, HDMI INTERRUPT MASK, CLEAR */
+       {WR8, SYS_INT, 0xFF},
+       {WR8, SYS_INTM, 0xFE},
+       {WR8, MISC_INT, 0xFF},
+       {WR8, MISC_INTM, 0xFD},
+       /* REMND, PACKET_INTM, 00 */
+       /* REMND, AUDIO_IMNTM, 00 */
+       /* REMND, ABUF_INTM, 00 */
+       /* INTERRUPT CONTROL (TOP LEVEL) */
+       {WR16, INTSTATUS, 0x0F3F},
+       {WR16, INTMASK, 0x0D3F},
+       {WR8, CLK_INTM, 0},
+       /* EDID */
+       {WR8, EDID_MODE, 0x01},
+       {WR16, EDID_LEN1_2, EDID_RAM_SIZE},
+       {CMD_END, 0},
+};
+
+static struct tc358840_reg startup_seq2[] = {
+       /* VIDEO COLOR FORMAT SETTING */
+       {WR8, VOUT_FMT, 0x01},
+       {WR8, VOUT_FIL, 0x14},
+       {WR8, VOUT_SYNC0, 0x42},
+       {WR8, VOUT_COLOR, 0x31},
+       /* HDMI SYSTEM */
+       {WR8, DDC_CTL, 0x02},
+       {WR8, HPD_CTL, 0x10},
+       /* HDMI AUDIO SETTING */
+       {WR8, AUD_AUTO_MUTE, 0x00},
+       {WR8, AUTO_CMD0, 0xF3},
+       {WR8, AUTO_CMD1, 0x02},
+       {WR8, AUTO_CMD2, 0x0C},
+       {WR8, BUFINIT_START, 0x05},
+       {WR8, FS_MUTE, 0x00},
+       {WR8, SDO_MODE1, 0x02},
+       {WR32, NCO_48F0A_D, 0x02752546},
+       {WR32, NCO_44F0A_D, 0x0242070B},
+       {WR8, AUD_MODE, 0x00},
+       /* LET HDMI SOURCE START ACCESS */
+       {WR8, INIT_END, 0x01},
+       {CMD_END, 0},
+};
+
+__maybe_unused
+static struct tc358840_reg color_bar_3840_2160_seq[] = {
+       {WR16, CONFCTL0, 0x80C4},
+       {WR16, SYSCTL, 0x3F01},
+       {WR16, SYSCTL, 0x0000},
+       {WR16, CONFCTL1, 0x0000},
+       {WR16, CB_CTL, 0x0009},
+       /* CSI-TX1 TRANSITION TIMING */
+       {WR32, TX1CSI_TX_CLKEN, 0x00000001},
+       {WR32, TX1CSI_TX_CLKSEL, 0x00000001},
+       {WR32, TX1MIPI_PLL_CONTROL, 0x00000001},
+       {WR32, TX1MIPI_PLL_CNF, 0x0000907C},
+       {DELAY, 0},
+       {WR32, TX1MIPI_PLL_CONTROL, 0x00000003},
+       {WR32, TX1LANE_ENABLE, 0x00000014},
+       {WR32, TX1LINE_INIT_COUNT, 0x00000FA0},
+       {WR32, TX1HSTX_TO_COUNT, 0x00000000},
+       {WR32, TX1FUNC_ENABLE, 0x00000101},
+       {WR32, TX1CSI_TATO_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_BTA_COUNT, 0x00005000},
+       {WR32, TX1CSI_PRESP_LPR_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_LPW_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_HSR_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_HSW_COUNT, 0x00010000},
+       {WR32, TX1CSI_PR_TO_COUNT, 0x00001000},
+       {WR32, TX1CSI_LRX_H_TO_COUNT, 0x00010000},
+       {WR32, TX1FUNC_MODE, 0x00000160},
+       {WR32, TX1CSI_RX_VC_ENABLE, 0x00000001},
+       {WR32, TX1IND_TO_COUNT, 0x000000C8},
+       {WR32, TX1CSI_HSYNC_STOP_COUNT, 0x0000002A},
+       {WR32, TX1APF_VDELAYCNT, 0x00000455},
+       {WR32, TX1CSI_RX_STATE_INT_MASK, 0x00000000},
+       {WR32, TX1CSI_LPRX_THRESH_COUNT, 0x00000015},
+       {WR32, TX1APP_SIDE_ERR_INT_MASK, 0x00000000},
+       {WR32, TX1CSI_RX_ERR_INT_MASK, 0x00000080},
+       {WR32, TX1CSI_LPTX_INT_MASK, 0x00000000},
+       {WR32, TX1LPTXTIMECNT, 0x00000004},
+       {WR32, TX1TCLK_HEADERCNT, 0x00180203},
+       {WR32, TX1TCLK_TRAILCNT, 0x00040005},
+       {WR32, TX1THS_HEADERCNT, 0x000D0004},
+       {WR32, TX1TWAKEUPCNT, 0x00003E80},
+       {WR32, TX1TCLK_POSTCNT, 0x0000000A},
+       {WR32, TX1THS_TRAILCNT, 0x00080006},
+       {WR32, TX1HSTXVREGCNT, 0x00000020},
+       {WR32, TX1HSTXVREGEN, 0x0000001F},
+       {WR32, TX1BTA_COUNT, 0x00040003},
+       {WR32, TX1DPHY_TX_ADJUST, 0x00000002},
+       {WR32, TX1CSITX_START, 0x00000001},
+       /* CSI-TX0 TRANSITION TIMING */
+       {WR32, TX0CSI_TX_CLKEN, 0x00000001},
+       {WR32, TX0CSI_TX_CLKSEL, 0x00000001},
+       {WR32, TX0MIPI_PLL_CONTROL, 0x00000001},
+       {WR32, TX0MIPI_PLL_CNF, 0x0000907C},
+       {DELAY, 0},
+       {WR32, TX0MIPI_PLL_CONTROL, 0x00000003},
+       {WR32, TX0LANE_ENABLE, 0x00000014},
+       {WR32, TX0LINE_INIT_COUNT, 0x00000FA0},
+       {WR32, TX0HSTX_TO_COUNT, 0x00000000},
+       {WR32, TX0FUNC_ENABLE, 0x00000101},
+       {WR32, TX0CSI_TATO_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_BTA_COUNT, 0x00005000},
+       {WR32, TX0CSI_PRESP_LPR_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_LPW_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_HSR_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_HSW_COUNT, 0x00010000},
+       {WR32, TX0CSI_PR_TO_COUNT, 0x00001000},
+       {WR32, TX0CSI_LRX_H_TO_COUNT, 0x00010000},
+       {WR32, TX0FUNC_MODE, 0x00000160},
+       {WR32, TX0CSI_RX_VC_ENABLE, 0x00000001},
+       {WR32, TX0IND_TO_COUNT, 0x000000C8},
+       {WR32, TX0CSI_HSYNC_STOP_COUNT, 0x0000002A},
+       {WR32, TX0APF_VDELAYCNT, 0x00000455},
+       {WR32, TX0CSI_RX_STATE_INT_MASK, 0x00000000},
+       {WR32, TX0CSI_LPRX_THRESH_COUNT, 0x00000015},
+       {WR32, TX0APP_SIDE_ERR_INT_MASK, 0x00000000},
+       {WR32, TX0CSI_RX_ERR_INT_MASK, 0x00000080},
+       {WR32, TX0CSI_LPTX_INT_MASK, 0x00000000},
+       {WR32, TX0LPTXTIMECNT, 0x00000004},
+       {WR32, TX0TCLK_HEADERCNT, 0x00180203},
+       {WR32, TX0TCLK_TRAILCNT, 0x00040005},
+       {WR32, TX0THS_HEADERCNT, 0x000D0004},
+       {WR32, TX0TWAKEUPCNT, 0x00003E80},
+       {WR32, TX0TCLK_POSTCNT, 0x0000000A},
+       {WR32, TX0THS_TRAILCNT, 0x00080006},
+       {WR32, TX0HSTXVREGCNT, 0x00000020},
+       {WR32, TX0HSTXVREGEN, 0x0000001F},
+       {WR32, TX0BTA_COUNT, 0x00040003},
+       {WR32, TX0DPHY_TX_ADJUST, 0x00000002},
+       {WR32, TX0CSITX_START, 0x00000001},
+       /* SPLIT CONTROL */
+       {WR16, STX0_CTL, 0x0000},
+       {WR16, STX0_FPX, 0x8000},
+       {WR16, STX1_CTL, 0x0000},
+       /* COLOR BAR SETTING */
+       {WR8, VOUT_FMT, 0x01},
+       {WR8, VOUT_FIL, 0x14},
+       {WR8, VOUT_COLOR, 0x31},
+       {WR16, CB_CTL, 0x000D},
+       {WR16, CB_HSW, 0x0020},
+       {WR16, CB_VSW, 0x000A},
+       {WR16, CB_HTOTAL, 0x0FA0},
+       {WR16, CB_VTOTAL, 0x08A7},
+       {WR16, CB_HACT, 0x0F00},
+       {WR16, CB_VACT, 0x0870},
+       {WR16, CB_HSTART, 0x0070},
+       {WR16, CB_VSTART, 0x0034},
+       /* START VIDEO TX */
+       {WR16, CONFCTL0, 0x8CF7},
+       {CMD_END, 0},
+};
+
+__maybe_unused
+static struct tc358840_reg color_bar_1920_1080_seq[] = {
+       {WR16, CONFCTL0, 0x80C4},
+       {WR16, SYSCTL, 0x3F01},
+       {WR16, SYSCTL, 0x0000},
+       {WR16, CONFCTL1, 0x0000},
+       {WR16, CB_CTL, 0x0009},
+       /* CSI-TX1 TRANSITION TIMING */
+       {WR32, TX1CSI_TX_CLKEN, 0x00000001},
+       {WR32, TX1CSI_TX_CLKSEL, 0x00000001},
+       {WR32, TX1MIPI_PLL_CONTROL, 0x00000001},
+       {WR32, TX1MIPI_PLL_CNF, 0x0000907C},
+       {DELAY, 0},
+       {WR32, TX1MIPI_PLL_CONTROL, 0x00000003},
+       {WR32, TX1LANE_ENABLE, 0x00000014},
+       {WR32, TX1LINE_INIT_COUNT, 0x00000FA0},
+       {WR32, TX1HSTX_TO_COUNT, 0x00000000},
+       {WR32, TX1FUNC_ENABLE, 0x00000101},
+       {WR32, TX1CSI_TATO_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_BTA_COUNT, 0x00005000},
+       {WR32, TX1CSI_PRESP_LPR_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_LPW_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_HSR_COUNT, 0x00010000},
+       {WR32, TX1CSI_PRESP_HSW_COUNT, 0x00010000},
+       {WR32, TX1CSI_PR_TO_COUNT, 0x00001000},
+       {WR32, TX1CSI_LRX_H_TO_COUNT, 0x00010000},
+       {WR32, TX1FUNC_MODE, 0x00000160},
+       {WR32, TX1CSI_RX_VC_ENABLE, 0x00000001},
+       {WR32, TX1IND_TO_COUNT, 0x000000C8},
+       {WR32, TX1CSI_HSYNC_STOP_COUNT, 0x0000002A},
+       {WR32, TX1APF_VDELAYCNT, 0x00000630},
+       {WR32, TX1CSI_RX_STATE_INT_MASK, 0x00000000},
+       {WR32, TX1CSI_LPRX_THRESH_COUNT, 0x00000015},
+       {WR32, TX1APP_SIDE_ERR_INT_MASK, 0x00000000},
+       {WR32, TX1CSI_RX_ERR_INT_MASK, 0x00000080},
+       {WR32, TX1CSI_LPTX_INT_MASK, 0x00000000},
+       {WR32, TX1LPTXTIMECNT, 0x00000004},
+       {WR32, TX1TCLK_HEADERCNT, 0x00180203},
+       {WR32, TX1TCLK_TRAILCNT, 0x00040005},
+       {WR32, TX1THS_HEADERCNT, 0x000D0004},
+       {WR32, TX1TWAKEUPCNT, 0x00003E80},
+       {WR32, TX1TCLK_POSTCNT, 0x0000000A},
+       {WR32, TX1THS_TRAILCNT, 0x00080006},
+       {WR32, TX1HSTXVREGCNT, 0x00000020},
+       {WR32, TX1HSTXVREGEN, 0x0000001F},
+       {WR32, TX1BTA_COUNT, 0x00040003},
+       {WR32, TX1DPHY_TX_ADJUST, 0x00000002},
+       {WR32, TX1CSITX_START, 0x00000001},
+       /* CSI-TX0 TRANSITION TIMING */
+       {WR32, TX0CSI_TX_CLKEN, 0x00000001},
+       {WR32, TX0CSI_TX_CLKSEL, 0x00000001},
+       {WR32, TX0MIPI_PLL_CONTROL, 0x00000001},
+       {WR32, TX0MIPI_PLL_CNF, 0x0000907C},
+       {DELAY, 0},
+       {WR32, TX0MIPI_PLL_CONTROL, 0x00000003},
+       {WR32, TX0LANE_ENABLE, 0x00000014},
+       {WR32, TX0LINE_INIT_COUNT, 0x00000FA0},
+       {WR32, TX0HSTX_TO_COUNT, 0x00000000},
+       {WR32, TX0FUNC_ENABLE, 0x00000101},
+       {WR32, TX0CSI_TATO_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_BTA_COUNT, 0x00005000},
+       {WR32, TX0CSI_PRESP_LPR_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_LPW_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_HSR_COUNT, 0x00010000},
+       {WR32, TX0CSI_PRESP_HSW_COUNT, 0x00010000},
+       {WR32, TX0CSI_PR_TO_COUNT, 0x00001000},
+       {WR32, TX0CSI_LRX_H_TO_COUNT, 0x00010000},
+       {WR32, TX0FUNC_MODE, 0x00000160},
+       {WR32, TX0CSI_RX_VC_ENABLE, 0x00000001},
+       {WR32, TX0IND_TO_COUNT, 0x000000C8},
+       {WR32, TX0CSI_HSYNC_STOP_COUNT, 0x0000002A},
+       {WR32, TX0APF_VDELAYCNT, 0x00000630},
+       {WR32, TX0CSI_RX_STATE_INT_MASK, 0x00000000},
+       {WR32, TX0CSI_LPRX_THRESH_COUNT, 0x00000015},
+       {WR32, TX0APP_SIDE_ERR_INT_MASK, 0x00000000},
+       {WR32, TX0CSI_RX_ERR_INT_MASK, 0x00000080},
+       {WR32, TX0CSI_LPTX_INT_MASK, 0x00000000},
+       {WR32, TX0LPTXTIMECNT, 0x00000004},
+       {WR32, TX0TCLK_HEADERCNT, 0x00180203},
+       {WR32, TX0TCLK_TRAILCNT, 0x00040005},
+       {WR32, TX0THS_HEADERCNT, 0x000D0004},
+       {WR32, TX0TWAKEUPCNT, 0x00003E80},
+       {WR32, TX0TCLK_POSTCNT, 0x0000000A},
+       {WR32, TX0THS_TRAILCNT, 0x00080006},
+       {WR32, TX0HSTXVREGCNT, 0x00000020},
+       {WR32, TX0HSTXVREGEN, 0x0000001F},
+       {WR32, TX0BTA_COUNT, 0x00040003},
+       {WR32, TX0DPHY_TX_ADJUST, 0x00000002},
+       {WR32, TX0CSITX_START, 0x00000001},
+       /* SPLIT CONTROL */
+       {WR16, STX0_CTL, 0x0000},
+       {WR16, STX0_FPX, 0x8000},
+       {WR16, STX1_CTL, 0x0000},
+       /* COLOR BAR SETTING */
+       {WR8, VOUT_FMT, 0x01},
+       {WR8, VOUT_FIL, 0x14},
+       {WR8, VOUT_COLOR, 0x31},
+       {WR16, CB_CTL, 0x000D},
+       {WR16, CB_HSW, 0x0020},
+       {WR16, CB_VSW, 0x000A},
+       {WR16, CB_HTOTAL, 0x0820},
+       {WR16, CB_VTOTAL, 0x046F},
+       {WR16, CB_HACT, 0x0780},
+       {WR16, CB_VACT, 0x0438},
+       {WR16, CB_HSTART, 0x0070},
+       {WR16, CB_VSTART, 0x0034},
+       /* START VIDEO TX */
+       {WR16, CONFCTL0, 0x8CF7},
+       {DELAY, 0},
+       {CMD_END, 0},
+};
+
+static u8 edid[] = {
+0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+0x52, 0x62, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88,
+0x1C, 0x15, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78,
+0x0A, 0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27,
+0x12, 0x48, 0x4C, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xEC, 0x68,
+0x00, 0xA0, 0xF0, 0x70, 0x37, 0x80, 0x30, 0x20,
+0x3A, 0x00, 0x00, 0x70, 0xF8, 0x00, 0x00, 0x1C,
+0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40,
+0x58, 0x2C, 0x45, 0x00, 0xC4, 0x8E, 0x21, 0x00,
+0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x54,
+0x6F, 0x73, 0x68, 0x69, 0x62, 0x61, 0x2D, 0x55,
+0x48, 0x32, 0x44, 0x0A, 0x00, 0x00, 0x00, 0xFD,
+0x00, 0x17, 0x3D, 0x0F, 0x8C, 0x17, 0x00, 0x0A,
+0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xAF,
+0x02, 0x03, 0x1A, 0x74, 0x47, 0x32, 0x10, 0x32,
+0x32, 0x32, 0x32, 0x32, 0x23, 0x09, 0x07, 0x01,
+0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0C, 0x00,
+0x10, 0x00, 0xEC, 0x68, 0x00, 0xA0, 0xF0, 0x70,
+0x37, 0x80, 0x30, 0x20, 0x3A, 0x00, 0x00, 0x70,
+0xF8, 0x00, 0x00, 0x1C, 0xEC, 0x68, 0x00, 0xA0,
+0xF0, 0x70, 0x37, 0x80, 0x30, 0x20, 0x3A, 0x00,
+0x00, 0x70, 0xF8, 0x00, 0x00, 0x1C, 0xEC, 0x68,
+0x00, 0xA0, 0xF0, 0x70, 0x37, 0x80, 0x30, 0x20,
+0x3A, 0x00, 0x00, 0x70, 0xF8, 0x00, 0x00, 0x1C,
+0xEC, 0x68, 0x00, 0xA0, 0xF0, 0x70, 0x37, 0x80,
+0x30, 0x20, 0x3A, 0x00, 0x00, 0x70, 0xF8, 0x00,
+0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A,
+};
+
+static int tc358840_s_ctrl(struct v4l2_ctrl *ctrl);
+
+static const struct v4l2_ctrl_ops tc358840_ctrl_ops = {
+       .s_ctrl         = tc358840_s_ctrl,
+};
+
+static struct v4l2_ctrl_config ctrl_config_list[] = {
+/* Do not change the name field for the controls! */
+};
+
+static const struct regmap_config sensor_regmap_config = {
+       .reg_bits = 16,
+       .val_bits = 8,
+};
+
+/* TC358840XBG requires register address in big endian
+ * and register value in little endian.
+ * Regmap currently sends it all in big endian
+ */
+__maybe_unused
+static int tc358840_read_reg8(struct tc358840 *priv,
+                               unsigned int addr, unsigned int *val)
+{
+       regmap_read(priv->regmap, addr, val);
+       dev_dbg(&priv->i2c_client->dev, "%#x = %#x\n", addr, *val);
+       return *val;
+}
+
+__maybe_unused
+static int tc358840_read_reg16(struct tc358840 *priv,
+                               unsigned int addr, unsigned int *val)
+{
+       regmap_raw_read(priv->regmap, addr, val, 2);
+       dev_dbg(&priv->i2c_client->dev, "%#x = %#x\n", addr, *val);
+       return *val;
+}
+
+__maybe_unused
+static int tc358840_read_reg32(struct tc358840 *priv,
+                               unsigned int addr, unsigned int *val)
+{
+       regmap_raw_read(priv->regmap, addr, val, 4);
+       dev_dbg(&priv->i2c_client->dev, "%#x = %#x\n", addr, *val);
+       return *val;
+}
+
+static int tc358840_write_reg8(struct tc358840 *priv,
+                               unsigned int addr, unsigned int val)
+{
+       dev_dbg(&priv->i2c_client->dev, "%#x = %#x\n", addr, val);
+       return regmap_write(priv->regmap, addr, val);
+}
+
+static int tc358840_write_reg16(struct tc358840 *priv,
+                               unsigned int addr, unsigned int val)
+{
+       dev_dbg(&priv->i2c_client->dev, "%#x = %#x\n", addr, val);
+       return regmap_raw_write(priv->regmap, addr, &val, 2);
+}
+
+static int tc358840_write_reg32(struct tc358840 *priv,
+                               unsigned int addr, unsigned int val)
+{
+       dev_dbg(&priv->i2c_client->dev, "%#x = %#x\n", addr, val);
+       return regmap_raw_write(priv->regmap, addr, &val, 4);
+}
+
+static int tc358840_write_table(
+       struct tc358840 *priv,
+       const struct tc358840_reg table[])
+{
+       int val, err = 0;
+       const struct tc358840_reg *next;
+       int (*reg_write)(struct tc358840 *priv,
+                       unsigned int addr, unsigned int val);
+
+       for (next = table; next->cmd_type != CMD_END; next++) {
+               switch (next->cmd_type) {
+               case DELAY:
+                       msleep(1);
+                       continue;
+               case WR8:
+                       reg_write = tc358840_write_reg8;
+                       break;
+               case WR16:
+                       reg_write = tc358840_write_reg16;
+                       break;
+               case WR32:
+                       reg_write = tc358840_write_reg32;
+                       break;
+               default:
+                       pr_err("%s: cmd type not supported!\n", __func__);
+                       return -EINVAL;
+               }
+
+               val = next->val;
+               err = reg_write(priv, next->addr, val);
+
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static int program_edid_ram(struct tc358840 *priv, u8 *edid)
+{
+       int i, err = 0;
+       unsigned int val;
+
+       for (i = 0; i < EDID_RAM_SIZE; i++) {
+               val = edid[i];
+               err = tc358840_write_reg8(priv, EDID_RAM_START + i, val);
+               if (err) {
+                       dev_info(&priv->i2c_client->dev,
+                               "%s: write error@%#x!\n", __func__,
+                               EDID_RAM_START + i);
+                       return err;
+               }
+       }
+       return 0;
+}
+
+static void run_init_seq(struct tc358840 *priv)
+{
+       /* step1: program startup sequence 1/2*/
+       tc358840_write_table(priv, startup_seq1);
+
+       /* step2: program EDID ram regs */
+       program_edid_ram(priv, edid);
+
+       /* step3: program startup sequence 2/2*/
+       tc358840_write_table(priv, startup_seq2);
+}
+
+static int sync_hdmi(struct tc358840 *priv)
+{
+       int retry, max_try = 30, ret = 0;
+       unsigned int sys_status;
+
+       /* step4: wait HDMI sync */
+       for (retry = 0; retry < max_try; retry++) {
+               /* need to wait bit 7 of SYS_STATUS reg */
+               tc358840_read_reg8(priv, SYS_STATUS, &sys_status);
+               if (sys_status & 0x80)
+                       break;
+
+               msleep(100);
+       }
+
+       if (retry == max_try) {
+               dev_err(&priv->i2c_client->dev,
+                       "%s: fail to wait HDMI sync!\n", __func__);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static void start_csi_tx(struct tc358840 *priv)
+{
+       /* step5: start video TX*/
+       tc358840_write_reg16(priv, CONFCTL0, 0x8CF7);
+       tc358840_write_reg16(priv, CONFCTL1, 0x0000);
+}
+
+__maybe_unused
+static void set_csi_split(struct tc358840 *priv,
+       enum splitter_type split)
+{
+       u16 fpx, ctl;
+
+       priv->split.mode = split;
+       tc358840_read_reg16(priv, STX0_FPX, &fpx);
+
+       switch (split) {
+       case LEFT_RIGHT:
+               ctl = (0x0 << 8);
+               fpx |= (0x0 << 14);
+               fpx |= (0x1 << 15);
+               priv->split.tx0.first_pixel = 0;
+               priv->split.tx0.len = priv->input.hactive / 2;
+               priv->split.tx1.first_pixel = (priv->input.hactive / 2) - 1;
+               priv->split.tx1.len = priv->input.hactive / 2;
+               break;
+       case RIGHT_LEFT:
+               ctl = (0x0 << 8);
+               fpx |= (0x1 << 14);
+               fpx |= (0x1 << 15);
+               priv->split.tx1.first_pixel = 0;
+               priv->split.tx1.len = priv->input.hactive / 2;
+               priv->split.tx0.first_pixel = (priv->input.hactive / 2) - 1;
+               priv->split.tx0.len = priv->input.hactive / 2;
+               break;
+       case MANUAL:
+               ctl = (0x0 << 8);
+               fpx |= (0x0 << 15);
+               break;
+       case DISABLE:
+               ctl = (0x1 << 8);
+               priv->split.tx0.first_pixel = 0;
+               priv->split.tx0.len = priv->input.hactive;
+               priv->split.tx1.first_pixel = 0;
+               priv->split.tx1.len = priv->input.hactive;
+               break;
+       default:
+               dev_err(&priv->i2c_client->dev,
+                       "%s: invalid csi split mode!\n", __func__);
+               break;
+       }
+
+       tc358840_write_reg16(priv, STX0_CTL, ctl);
+       tc358840_write_reg16(priv, STX1_CTL, ctl);
+       tc358840_write_reg16(priv, STX0_FPX, fpx);
+}
+
+/*
+ * offset: start from 0
+ */
+static void set_csi_window(struct tc358840 *priv,
+       int index, int offset, int len)
+{
+       int fist_pixel, last_pixel;
+
+       /* auto split by default, set to manual */
+       set_csi_split(priv, MANUAL);
+
+       fist_pixel = offset & 0x0FFF;
+       last_pixel = (offset + len) & 0x0FFF;
+
+       if (index == 0) {
+               priv->split.tx0.first_pixel = fist_pixel;
+               priv->split.tx0.len = last_pixel - fist_pixel + 1;
+               tc358840_write_reg16(priv, STX0_FPX, fist_pixel);
+               tc358840_write_reg16(priv, STX0_LPX, last_pixel);
+       } else if (index == 1) {
+               priv->split.tx1.first_pixel = fist_pixel;
+               priv->split.tx1.len = last_pixel - fist_pixel + 1;
+               tc358840_write_reg16(priv, STX1_FPX, fist_pixel);
+               tc358840_write_reg16(priv, STX1_LPX, last_pixel);
+       } else
+               dev_err(&priv->i2c_client->dev,
+                       "%s: invalid csi index!\n", __func__);
+}
+
+__maybe_unused
+static void stop_tx(struct tc358840 *priv)
+{
+       /* Disable audio and video TX */
+       tc358840_write_reg16(priv, CONFCTL0, 0x8CD4);
+}
+
+__maybe_unused
+static void tc358840_dbg_print(struct tc358840 *priv,
+       u8 type, unsigned int addr, char *reg)
+{
+       unsigned int val;
+       int (*reg_read)(struct tc358840 *priv,
+                       unsigned int addr, unsigned int *val);
+
+       if (type == RD8)
+               reg_read = tc358840_read_reg8;
+       else if (type == RD16)
+               reg_read = tc358840_read_reg16;
+       else
+               return;
+
+       reg_read(priv, addr, &val);
+       dev_info(&priv->i2c_client->dev, "reg %s = %#x\n", reg, val);
+}
+
+#if defined DEBUG
+#define dbg_print(priv, type, reg) \
+       tc358840_dbg_print(priv, type, reg, #reg)
+#else
+#define dbg_print(priv, type, reg)
+#endif
+
+static void tc358840_hdmiin_timing_chk(struct tc358840 *priv)
+{
+       dbg_print(priv, RD8, SYS_FREQ0);
+       dbg_print(priv, RD8, SYS_FREQ1);
+       dbg_print(priv, RD8, CSC_SCLK0);
+       dbg_print(priv, RD8, CSC_SCLK1);
+
+       /* HDMI-In video timing check*/
+       /* PCLK */
+       dbg_print(priv, RD8, PX_FREQ0);
+       dbg_print(priv, RD8, PX_FREQ1);
+       /* Horizontal */
+       dbg_print(priv, RD8, H_SIZE0);
+       dbg_print(priv, RD8, H_SIZE1);
+       dbg_print(priv, RD8, DE_HPOS0);
+       dbg_print(priv, RD8, DE_HPOS1);
+       dbg_print(priv, RD8, DE_HWID0);
+       dbg_print(priv, RD8, DE_HWID1);
+       /* Vertical */
+       dbg_print(priv, RD8, V_SIZE0);
+       dbg_print(priv, RD8, V_SIZE1);
+       dbg_print(priv, RD8, DE_POS_A0);
+       dbg_print(priv, RD8, DE_POS_A1);
+       dbg_print(priv, RD8, DE_POS_B0);
+       dbg_print(priv, RD8, DE_POS_B1);
+       dbg_print(priv, RD8, DE_VWID0);
+       dbg_print(priv, RD8, DE_VWID1);
+       /* Vsync, Hsync porarity */
+       dbg_print(priv, RD8, CLK_STATUS);
+
+       dbg_print(priv, RD8, SYS_STATUS);
+       dbg_print(priv, RD8, VI_STATUS0);
+       dbg_print(priv, RD8, VI_STATUS1);
+       dbg_print(priv, RD8, VI_STATUS2);
+       dbg_print(priv, RD8, VI_STATUS3);
+}
+
+static int tc358840_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       struct camera_common_data *s_data =
+               container_of(sd, struct camera_common_data, subdev);
+       struct tc358840 *priv = (struct tc358840 *)s_data->priv;
+       const struct camera_common_colorfmt *fmt = s_data->colorfmt;
+       int err = 0;
+
+       dev_dbg(&priv->i2c_client->dev, "%s++\n", __func__);
+
+       if (!enable) {
+               /* TODO: stop the stream here */
+               return 0;
+       }
+
+#if COLOR_BAR_MODE
+       if (s_data->fmt_height == 1080)
+               tc358840_write_table(priv, color_bar_1920_1080_seq);
+       else
+               tc358840_write_table(priv, color_bar_3840_2160_seq);
+#endif
+
+       switch (fmt->code) {
+       case V4L2_MBUS_FMT_UYVY8_2X8:
+               tc358840_write_reg16(priv, CONFCTL0, 0x8CF7);
+               tc358840_write_reg8(priv, VOUT_FMT, 0x01);
+               tc358840_write_reg8(priv, VOUT_COLOR, 0x31);
+               break;
+       case V4L2_MBUS_FMT_RGBA8888_4X8_LE:
+               tc358840_write_reg16(priv, CONFCTL0, 0x8C37);
+               tc358840_write_reg8(priv, VOUT_FMT, 0x02);
+               tc358840_write_reg8(priv, VOUT_COLOR, 0x11);
+               break;
+       default:
+               dev_err(&priv->i2c_client->dev,
+                       "%s: invalid pixel format %08x!\n",
+                       __func__, fmt->code);
+               break;
+       }
+
+       return 0;
+exit:
+       dev_dbg(&priv->i2c_client->dev, "%s: error setting stream\n", __func__);
+       return err;
+}
+
+static int tc358840_s_power(struct v4l2_subdev *sd, int on)
+{
+       struct camera_common_data *s_data =
+               container_of(sd, struct camera_common_data, subdev);
+       struct tc358840 *priv = (struct tc358840 *)s_data->priv;
+       struct camera_common_power_rail *pw = &priv->power;
+
+       dev_dbg(&priv->i2c_client->dev, "%s: power %s\n",
+                       __func__, on ? "on" : "off");
+
+       if (on) {
+               if (pw->dvdd)
+                       regulator_enable(pw->dvdd);
+               if (pw->iovdd)
+                       regulator_enable(pw->iovdd);
+       } else {
+               if (pw->dvdd)
+                       regulator_disable(pw->dvdd);
+               if (pw->iovdd)
+                       regulator_disable(pw->iovdd);
+       }
+
+       return 0;
+}
+
+static struct v4l2_subdev_video_ops tc358840_subdev_video_ops = {
+       .s_stream       = tc358840_s_stream,
+       .s_mbus_fmt     = camera_common_s_fmt,
+       .g_mbus_fmt     = camera_common_g_fmt,
+       .try_mbus_fmt   = camera_common_try_fmt,
+       .enum_mbus_fmt  = camera_common_enum_fmt,
+       .g_mbus_config  = camera_common_g_mbus_config,
+};
+
+static struct v4l2_subdev_core_ops tc358840_subdev_core_ops = {
+       .g_chip_ident   = camera_common_g_chip_ident,
+       .s_power        = tc358840_s_power,
+};
+
+static struct v4l2_subdev_ops tc358840_subdev_ops = {
+       .core   = &tc358840_subdev_core_ops,
+       .video  = &tc358840_subdev_video_ops,
+};
+
+static void tc358840_check_id(struct tc358840 *priv)
+{
+       unsigned int chip_rev_id;
+
+       tc358840_read_reg16(priv, 0x0000, &chip_rev_id);
+       dev_info(&priv->i2c_client->dev, "chip id %x, rev id %x\n",
+               (chip_rev_id >> 8) & 0xFF, chip_rev_id & 0xFF);
+}
+
+static irqreturn_t hpd_irq_handler(int irq, void *data)
+{
+       struct tc358840 *priv = (struct tc358840 *)data;
+       int hpd_state = gpio_get_value(priv->hpd_gpio);
+
+       /* hotplug pin is low active */
+       hpd_state = !hpd_state;
+
+#if !COLOR_BAR_MODE
+       if (hpd_state) {
+               /*
+                * FIXME: Very eash to hit csi syncpt timeout
+                * if not redo the init sequence.
+                */
+               dev_info(&priv->i2c_client->dev,
+                       "hot plug detected, doing init...\n");
+               run_init_seq(priv);
+       } else {
+               dev_info(&priv->i2c_client->dev,
+                       "hot unplug detected, stoping tx...\n");
+               stop_tx(priv);
+       }
+#endif
+
+#ifdef CONFIG_SWITCH
+       switch_set_state(&priv->hpd_switch, hpd_state);
+#endif
+       return IRQ_HANDLED;
+}
+
+static int tc358840_pwr_init(struct tc358840 *priv)
+{
+       struct i2c_client *client = priv->i2c_client;
+       struct camera_common_power_rail *pw = &priv->power;
+       int err;
+
+       err = camera_common_regulator_get(client, &pw->iovdd, "vif");
+       if (err < 0) {
+               dev_err(&client->dev, "cannot get regulator vif %d\n", err);
+               return -EINVAL;
+       }
+
+       err = camera_common_regulator_get(client, &pw->dvdd, "vdig");
+       if (err < 0) {
+               dev_err(&client->dev, "cannot get regulator vdig %d\n", err);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static irqreturn_t int_irq_handler(int irq, void *data)
+{
+       struct tc358840 *info = (struct tc358840_info *)data;
+       u16 int_status, val2;
+       u8 hdmi_int0, hdmi_int1, clk_int, sys_int, sys_status;
+       int offset;
+
+       /* FIXME: somehow below delays are needed to make irq working */
+       tc358840_read_reg16(info, INTSTATUS, &int_status);
+       msleep(1);
+       tc358840_read_reg8(info, 0x8500, &hdmi_int0);
+       msleep(1);
+       tc358840_read_reg8(info, 0x8501, &hdmi_int1);
+       msleep(1);
+       tc358840_read_reg8(info, 0x8502, &sys_int);
+       msleep(1);
+       tc358840_read_reg8(info, 0x8503, &clk_int);
+       msleep(1);
+
+       if (int_status & INTSTATUS_HDMI_INT) {
+               /*
+                * Need to check if bit1 of hdmi_int1 is set
+                * But somehow it's not while it shall be
+                * Thus check bit5 of clk_int directly
+                */
+               if (clk_int & 0x20) {
+                       stop_tx(info);
+                       sync_hdmi(info);
+
+                       /* Update hdmi input resolution */
+                       info->input.changed = true;
+                       tc358840_read_reg16(info,
+                               DE_HWID0, &info->input.hactive);
+                       tc358840_read_reg16(info,
+                               DE_VWID0, &info->input.vactive);
+                       dev_info(&info->i2c_client->dev,
+                               "hdmi in hactive: %d, vactive: %d\n",
+                               info->input.hactive, info->input.vactive);
+                       tc358840_hdmiin_timing_chk(info);
+
+                       /* Use single CSI port for resolutions below 1080p */
+                       if (info->input.hactive < 1920)
+                               set_csi_split(info, DISABLE);
+                       else
+                               set_csi_split(info, LEFT_RIGHT);
+
+                       start_csi_tx(info);
+               }
+
+#if ENABLE_HPD_PIN
+               /* Check bit 0 (DDC change) of SYS_INT */
+               if (sys_int & 0x01) {
+                       tc358840_read_reg8(info, SYS_STATUS, &sys_status);
+                       if (sys_status & 0x01) {
+                               dev_info(&info->i2c_client->dev,
+                                       "%s: hot plug detected, doing init...\n",
+                                       __func__);
+                       } else {
+                               dev_info(&info->i2c_client->dev,
+                                       "%s: hot unplug detected, stoping tx...\n",
+                                       __func__);
+                               stop_tx(info);
+                       }
+               }
+#endif
+       }
+
+       tc358840_read_reg16(info, SYSINTSTATUS, &val2);
+
+       /* clear the interrupt status */
+       for (offset = 0x8502; offset <= 0x850f; offset++)
+               tc358840_write_reg8(info, offset, 0xFF);
+
+       tc358840_write_reg16(info, INTSTATUS, int_status);
+
+       return IRQ_HANDLED;
+}
+
+static int toggle_reset(struct tc358840 *priv)
+{
+       struct i2c_client *client = priv->i2c_client;
+       struct device_node *np = client->dev.of_node;
+       int err;
+
+       priv->rst_gpio = of_get_named_gpio(np, "rst", 0);
+       if (!gpio_is_valid(priv->rst_gpio)) {
+               dev_err(&client->dev, "Invalid reset gpio\n");
+               return -EINVAL;
+       }
+
+       err = gpio_request(priv->rst_gpio, "hdmi-reset");
+       if (err < 0)
+               dev_err(&client->dev,
+                       "rst gpio request failed %d\n", err);
+
+       /*
+        * FIXME: VDD33_HDMI to RESETN should >= 200ns, make it 1ms
+        */
+       gpio_direction_output(priv->rst_gpio, 0);
+       msleep(1);
+       gpio_direction_output(priv->rst_gpio, 1);
+       /*
+        * FIXME: core clock will be stable after 0.7 ~ 1ms after
+        * the de-assertion of the reset pin, thus make it 1ms
+        */
+       msleep(1);
+
+       return 0;
+}
+
+#if ENABLE_HPD_PIN
+static int hpd_init(struct tc358840 *priv)
+{
+       struct i2c_client *client = priv->i2c_client;
+       struct device_node *np = client->dev.of_node;
+       int irq;
+       int err;
+
+       priv->hpd_gpio = of_get_named_gpio(np, "hpd", 0);
+       if (!gpio_is_valid(priv->hpd_gpio)) {
+               dev_err(&client->dev, "Invalid hotplug gpio\n");
+               return -EINVAL;
+       }
+
+       irq = gpio_to_irq(priv->hpd_gpio);
+       if (irq < 0) {
+               dev_err(&client->dev,
+                       "hpd gpio to irq map failed\n");
+               return -EINVAL;
+       }
+
+       err = gpio_request(priv->hpd_gpio, "hdmi-in-hpd");
+       if (err < 0)
+               dev_err(&client->dev,
+                       "hpd gpio request failed %d\n", err);
+       gpio_direction_input(priv->hpd_gpio);
+
+       err = request_threaded_irq(irq,
+                               NULL, hpd_irq_handler,
+                               (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+                               IRQF_ONESHOT),
+                               "hdmi-in-hpd", priv);
+       if (err) {
+               dev_err(&client->dev,
+                       "request threaded irq failed: %d\n", err);
+               goto fail;
+       }
+
+
+       return 0;
+fail:
+       gpio_free(priv->hpd_gpio);
+       return err;
+}
+#endif
+
+static int tc358840_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct tc358840 *priv =
+               container_of(ctrl->handler, struct tc358840, ctrl_handler);
+       int err = 0;
+
+       switch (ctrl->id) {
+               /* TODO: Add tc358840 specific ctrl here */
+       default:
+               pr_err("%s: unknown ctrl id.\n", __func__);
+               return -EINVAL;
+       }
+
+       return err;
+}
+
+static int tc358840_ctrls_init(struct tc358840 *priv)
+{
+       struct i2c_client *client = priv->i2c_client;
+       struct v4l2_ctrl *ctrl;
+       int num_ctrls;
+       int err;
+       int i;
+
+       dev_dbg(&client->dev, "%s++\n", __func__);
+
+       num_ctrls = ARRAY_SIZE(ctrl_config_list);
+       v4l2_ctrl_handler_init(&priv->ctrl_handler, num_ctrls);
+
+       for (i = 0; i < num_ctrls; i++) {
+               ctrl = v4l2_ctrl_new_custom(&priv->ctrl_handler,
+                       &ctrl_config_list[i], NULL);
+               if (ctrl == NULL) {
+                       dev_err(&client->dev, "Failed to init %s ctrl\n",
+                               ctrl_config_list[i].name);
+                       continue;
+               }
+
+               priv->ctrls[i] = ctrl;
+       }
+
+       priv->num_ctrls = num_ctrls;
+       priv->subdev->ctrl_handler = &priv->ctrl_handler;
+       if (priv->ctrl_handler.error) {
+               dev_err(&client->dev, "Error %d adding controls\n",
+                       priv->ctrl_handler.error);
+               err = priv->ctrl_handler.error;
+               goto error;
+       }
+
+       err = v4l2_ctrl_handler_setup(&priv->ctrl_handler);
+       if (err) {
+               dev_err(&client->dev,
+                       "Error %d setting default controls\n", err);
+               goto error;
+       }
+
+       return 0;
+
+error:
+       v4l2_ctrl_handler_free(&priv->ctrl_handler);
+       return err;
+}
+
+static int irq_init(struct tc358840 *info)
+{
+       struct i2c_client *client = info->i2c_client;
+       struct device_node *np = client->dev.of_node;
+       int irq;
+       int err;
+
+       info->int_gpio = of_get_named_gpio(np, "int", 0);
+       if (!gpio_is_valid(info->int_gpio)) {
+               dev_err(&client->dev, "Invalid interrupt gpio\n");
+               return -EINVAL;
+       }
+
+       irq = gpio_to_irq(info->int_gpio);
+       if (irq < 0) {
+               dev_err(&client->dev,
+                       "int gpio to irq map failed\n");
+               return -EINVAL;
+       }
+
+       err = gpio_request(info->int_gpio, "hdmi-in-int");
+       if (err < 0)
+               dev_err(&client->dev,
+                       "interrupt gpio request failed %d\n", err);
+       gpio_direction_input(info->int_gpio);
+
+       err = request_threaded_irq(irq,
+                               NULL, int_irq_handler,
+                               (IRQF_TRIGGER_RISING | IRQF_ONESHOT),
+                               "hdmi-in-int", info);
+       if (err) {
+               dev_err(&client->dev,
+                       "%s: request threaded irq failed: %d\n",
+                               __func__, err);
+               goto fail;
+       }
+
+       info->chip_irq = irq;
+
+       return 0;
+fail:
+       gpio_free(info->int_gpio);
+       return err;
+}
+
+static int tc358840_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct camera_common_data *common_data;
+       struct tc358840 *priv;
+       int err;
+
+       pr_info("[TC358840]: probing v4l2 sensor.\n");
+
+       common_data = devm_kzalloc(&client->dev,
+                           sizeof(struct camera_common_data), GFP_KERNEL);
+       if (!common_data) {
+               dev_err(&client->dev, "unable to allocate memory!\n");
+               return -ENOMEM;
+       }
+
+       priv = devm_kzalloc(&client->dev,
+                       sizeof(struct tc358840) + sizeof(struct v4l2_ctrl *) *
+                       ARRAY_SIZE(ctrl_config_list),
+                       GFP_KERNEL);
+       if (!priv) {
+               dev_err(&client->dev, "unable to allocate memory!\n");
+               return -ENOMEM;
+       }
+
+       priv->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
+       if (IS_ERR(priv->regmap)) {
+               dev_err(&client->dev,
+                       "regmap init failed: %ld\n", PTR_ERR(priv->regmap));
+               return -ENODEV;
+       }
+
+       client->dev.of_node = of_find_node_by_name(NULL, "tc358840");
+
+       common_data->ctrl_handler       = &priv->ctrl_handler;
+       common_data->i2c_client         = client;
+       common_data->frmfmt             = &tc358840_frmfmt[0];
+       common_data->colorfmt           = camera_common_find_datafmt(
+                                         TC358840_DEFAULT_DATAFMT);
+       common_data->priv               = (void *)priv;
+       common_data->ident              = V4L2_IDENT_UNKNOWN;
+       common_data->numfmts            = ARRAY_SIZE(tc358840_frmfmt);
+       common_data->def_mode           = TC358840_DEFAULT_MODE;
+       common_data->def_width          = TC358840_DEFAULT_WIDTH;
+       common_data->def_height         = TC358840_DEFAULT_HEIGHT;
+       common_data->def_clk_freq       = TC358840_DEFAULT_CLK_FREQ;
+
+       priv->i2c_client = client;
+       priv->s_data = common_data;
+       priv->subdev = &common_data->subdev;
+
+       v4l2_i2c_subdev_init(&common_data->subdev, client,
+                            &tc358840_subdev_ops);
+
+       err = tc358840_ctrls_init(priv);
+       if (err)
+               return err;
+
+       /* Power init */
+       tc358840_pwr_init(priv);
+       tc358840_s_power(priv->subdev, 1);
+
+       /* Reset the chip */
+       toggle_reset(priv);
+
+       /* Read the chip and revision id */
+       tc358840_check_id(priv);
+
+       /* reset resolution info */
+       priv->input.hactive = 0;
+       priv->input.vactive = 0;
+
+#if ENABLE_HPD_PIN
+       hpd_init(priv);
+#endif
+
+       irq_init(priv);
+
+       run_init_seq(priv);
+
+       /* register a switch device for hpd status */
+#ifdef CONFIG_SWITCH
+       priv->hpd_switch.name = "hdmi-in";
+       err = switch_dev_register(&priv->hpd_switch);
+#endif
+
+       return 0;
+}
+
+static int tc358840_remove(struct i2c_client *client)
+{
+       struct soc_camera_subdev_desc *ssdd;
+       struct camera_common_data *s_data = to_camera_common_data(client);
+       struct tc358840 *priv = (struct tc358840 *)s_data->priv;
+
+       ssdd = soc_camera_i2c_to_desc(client);
+       if (ssdd->free_bus)
+               ssdd->free_bus(ssdd);
+
+       v4l2_ctrl_handler_free(&priv->ctrl_handler);
+
+#ifdef CONFIG_SWITCH
+       switch_dev_unregister(&priv->hpd_switch);
+#endif
+       kfree(s_data);
+       kfree(priv);
+       return 0;
+}
+
+static const struct i2c_device_id tc358840_id[] = {
+       { "tc358840_v4l2", 0 },
+       { },
+};
+
+MODULE_DEVICE_TABLE(i2c, tc358840_id);
+
+static const struct of_device_id tc358840_of_match[] = {
+       { .compatible = "tosh,hdmi2csi" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, tc358840_of_match);
+
+#ifdef CONFIG_PM_SLEEP
+static int tc358840_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct camera_common_data *s_data = to_camera_common_data(client);
+       struct tc358840 *priv = (struct tc358840 *)s_data->priv;
+       int hpd_irq;
+
+       hpd_irq = gpio_to_irq(priv->hpd_gpio);
+       enable_irq_wake(hpd_irq);
+       return 0;
+}
+
+static int tc358840_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct camera_common_data *s_data = to_camera_common_data(client);
+       struct tc358840 *priv = (struct tc358840 *)s_data->priv;
+       int hpd_irq;
+
+       hpd_irq = gpio_to_irq(priv->hpd_gpio);
+       disable_irq_wake(hpd_irq);
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tc358840_pm_ops,
+       tc358840_suspend, tc358840_resume);
+static struct i2c_driver tc358840_i2c_driver = {
+       .driver = {
+               .name = "tc358840_v4l2",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(tc358840_of_match),
+               .pm = &tc358840_pm_ops,
+       },
+       .probe = tc358840_probe,
+       .remove = tc358840_remove,
+       .id_table = tc358840_id,
+};
+
+module_i2c_driver(tc358840_i2c_driver);
+
+MODULE_AUTHOR("Frank Shi <fshi@nvidia.com>");
+MODULE_LICENSE("GPL v2");
index ef730adf89fe52bc373d95af97ec667d76495fd3..623380e1f852eb81267d878d491266c0135ee103 100644 (file)
@@ -31,6 +31,8 @@
 static const struct camera_common_colorfmt camera_common_color_fmts[] = {
        {V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB},
        {V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB},
+       {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB},
+       {V4L2_MBUS_FMT_RGBA8888_4X8_LE, V4L2_COLORSPACE_SRGB},
 };
 
 static struct tegra_io_dpd camera_common_csi_io[] = {
@@ -297,7 +299,9 @@ int camera_common_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
                        __func__, mf->width, mf->height);
 
        if (mf->code != V4L2_MBUS_FMT_SRGGB8_1X8 &&
-               mf->code != V4L2_MBUS_FMT_SRGGB10_1X10)
+               mf->code != V4L2_MBUS_FMT_SRGGB10_1X10 &&
+               mf->code != V4L2_MBUS_FMT_RGBA8888_4X8_LE &&
+               mf->code != V4L2_MBUS_FMT_UYVY8_2X8)
                mf->code = V4L2_MBUS_FMT_SRGGB10_1X10;
 
        if (!fmt_match) {
index 87a0d8a4f495818c8b7488338a288ab6d0513a15..d593eb014f1129f61d7ce4123948a3abb4a4c2b9 100644 (file)
@@ -1249,6 +1249,11 @@ static int vi2_channel_capture_setup(struct vi2_channel *chan)
                data_type = TEGRA_IMAGE_DT_RAW12;
                image_size = width * 12 / 8;
                bypass_pixel_transform = 1;
+       } else if (chan->code == V4L2_MBUS_FMT_RGBA8888_4X8_LE) {
+               format = TEGRA_IMAGE_FORMAT_T_A8B8G8R8;
+               data_type = TEGRA_IMAGE_DT_RGB888;
+               image_size = width * 3;
+               bypass_pixel_transform = 0;
        }
 
        csi_regs_write(vi2_cam, chan, TEGRA_VI_CSI_IMAGE_DEF,