/*
 * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * Modifyed by: Edison Fernández <edison.fernandez@ridgerun.com>
 * Added support to use it with Nitrogen6x
 *
 * 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/fsl_devices.h>
#include <linux/mutex.h>
#include <linux/mipi_csi2.h>
#include <linux/pwm.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-int-device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
//#include <mach/audmux.h>
#include <linux/slab.h>
#include "mxc_v4l2_capture.h"

#define CODEC_CLOCK 16500000
/* SSI clock sources */
#define IMX_SSP_SYS_CLK		0


#define TC358743_VOLTAGE_ANALOG		2800000
#define TC358743_VOLTAGE_DIGITAL_CORE	 1500000
#define TC358743_VOLTAGE_DIGITAL_IO	   1800000

#define MIN_FPS 30
#define MAX_FPS 60
#define DEFAULT_FPS 60

#define TC358743_XCLK_MIN 27000000
#define TC358743_XCLK_MAX 42000000

#define TC358743_CHIP_ID_HIGH_BYTE		0x0
#define TC358743_CHIP_ID_LOW_BYTE		0x0
#define TC3587430_HDMI_DETECT			0x0f //0x10

#define TC_VOLTAGE_ANALOG               2800000
#define TC_VOLTAGE_DIGITAL_CORE         1500000
#define TC_VOLTAGE_DIGITAL_IO           1800000

enum tc358743_mode {
	tc358743_mode_INIT, /*only for sensor init*/
	tc358743_mode_INIT1, /*only for sensor init*/
	tc358743_mode_480P_720_480,
	tc358743_mode_720P_60_1280_720,
	tc358743_mode_480P_640_480,
	tc358743_mode_1080P_1920_1080,
	tc358743_mode_INIT2, /*only for sensor init*/
	tc358743_mode_INIT3, /*only for sensor init*/
	tc358743_mode_INIT4, /*only for sensor init*/
	tc358743_mode_INIT5, /*only for sensor init*/
	tc358743_mode_INIT6, /*only for sensor init*/
	tc358743_mode_720P_1280_720,
	tc358743_mode_MAX ,
};

enum tc358743_frame_rate {
	tc358743_60_fps,
	tc358743_30_fps,
	tc358743_max_fps
};

struct reg_value {
	u16 u16RegAddr;
	u32 u32Val;
	u32 u32Mask;
	u8  u8Length;
	u32 u32Delay_ms;
};

struct tc358743_mode_info {
	enum tc358743_mode mode;
	u32 width;
	u32 height;
	u32 vformat;
	u32 fps;
	u32 lanes;
	u32 freq;
	struct reg_value *init_data_ptr;
	u32 init_data_size;
	__u32 flags;
};

static struct delayed_work det_work;
static struct sensor_data tc358743_data;
static int pwn_gpio, rst_gpio;
static struct regulator *io_regulator;
static struct regulator *core_regulator;
static struct regulator *analog_regulator;
static struct regulator *gpo_regulator;

static	u16 hpd_active = 1;

#define DET_WORK_TIMEOUT_DEFAULT 100
#define DET_WORK_TIMEOUT_DEFERRED 2000
#define MAX_BOUNCE 5

static DEFINE_MUTEX(access_lock);
static int det_work_disable = 0;
static int det_work_timeout = DET_WORK_TIMEOUT_DEFAULT;
static u32 hdmi_mode = 0, lock = 0, bounce = 0, fps = 0, audio = 2;

static int tc358743_init_mode(enum tc358743_frame_rate frame_rate,
			    enum tc358743_mode mode);

static int tc358743_toggle_hpd(int active);

static void tc_standby(s32 enable)
{
	if (gpio_is_valid(pwn_gpio))
		gpio_set_value(pwn_gpio, enable ? 1 : 0);
	pr_debug("tc_standby: powerdown=%x, power_gp=0x%x\n", enable, pwn_gpio);
	msleep(2);
}

static void tc_reset(void)
{
	/* camera reset */
	gpio_set_value(rst_gpio, 1);

	/* camera power dowmn */
	if (gpio_is_valid(pwn_gpio)) {
		gpio_set_value(pwn_gpio, 1);
		msleep(5);

		gpio_set_value(pwn_gpio, 0);
	}
	msleep(5);

	gpio_set_value(rst_gpio, 0);
	msleep(1);

	gpio_set_value(rst_gpio, 1);
	msleep(20);

	if (gpio_is_valid(pwn_gpio))
		gpio_set_value(pwn_gpio, 1);
}

static int tc_power_on(struct device *dev)
{
	int ret = 0;

	io_regulator = devm_regulator_get(dev, "DOVDD");
	if (!IS_ERR(io_regulator)) {
		regulator_set_voltage(io_regulator,
				      TC_VOLTAGE_DIGITAL_IO,
				      TC_VOLTAGE_DIGITAL_IO);
		ret = regulator_enable(io_regulator);
		if (ret) {
			pr_err("%s:io set voltage error\n", __func__);
			return ret;
		} else {
			dev_dbg(dev,
				"%s:io set voltage ok\n", __func__);
		}
	} else {
		pr_err("%s: cannot get io voltage error\n", __func__);
		io_regulator = NULL;
	}

	core_regulator = devm_regulator_get(dev, "DVDD");
	if (!IS_ERR(core_regulator)) {
		regulator_set_voltage(core_regulator,
				      TC_VOLTAGE_DIGITAL_CORE,
				      TC_VOLTAGE_DIGITAL_CORE);
		ret = regulator_enable(core_regulator);
		if (ret) {
			pr_err("%s:core set voltage error\n", __func__);
			return ret;
		} else {
			dev_dbg(dev,
				"%s:core set voltage ok\n", __func__);
		}
	} else {
		core_regulator = NULL;
		pr_err("%s: cannot get core voltage error\n", __func__);
	}

	analog_regulator = devm_regulator_get(dev, "AVDD");
	if (!IS_ERR(analog_regulator)) {
		regulator_set_voltage(analog_regulator,
				      TC_VOLTAGE_ANALOG,
				      TC_VOLTAGE_ANALOG);
		ret = regulator_enable(analog_regulator);
		if (ret) {
			pr_err("%s:analog set voltage error\n",
				__func__);
			return ret;
		} else {
			dev_dbg(dev,
				"%s:analog set voltage ok\n", __func__);
		}
	} else {
		analog_regulator = NULL;
		pr_err("%s: cannot get analog voltage error\n", __func__);
	}

	return ret;
}

static void det_work_enable(int i)
{
	mutex_lock(&access_lock);
	if (i) {
		det_work_timeout = DET_WORK_TIMEOUT_DEFERRED;
		schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout));
		det_work_disable = 0;
	} else {
		det_work_disable = 1;
		det_work_timeout = DET_WORK_TIMEOUT_DEFERRED;
	}
	mutex_unlock(&access_lock);
	pr_debug("%s: %d %d\n", __func__, det_work_disable, det_work_timeout);
}

static u8 cHDMIEDID[256] = {
	/* FIXME! This is the edid that my ASUS HDMI monitor returns */
	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x04, 0x69, 0xf3, 0x24, 0xd6, 0x12, 0x00, 0x00,
	0x16, 0x16, 0x01, 0x03, 0x80, 0x34, 0x1d, 0x78, 0x2a, 0xc7, 0x20, 0xa4, 0x55, 0x49, 0x99, 0x27,
	0x13, 0x50, 0x54, 0xbf, 0xef, 0x00, 0x71, 0x4f, 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, 0xb3, 0x00,
	0xd1, 0xc0, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
	0x45, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xff, 0x00, 0x43, 0x36, 0x4c,
	0x4d, 0x54, 0x46, 0x30, 0x30, 0x34, 0x38, 0x32, 0x32, 0x0a, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x37,
	0x4b, 0x1e, 0x55, 0x10, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
	0x00, 0x41, 0x53, 0x55, 0x53, 0x20, 0x56, 0x48, 0x32, 0x34, 0x32, 0x48, 0x0a, 0x20, 0x01, 0x78,
	0x02, 0x03, 0x22, 0x71, 0x4f, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x04, 0x14, 0x05, 0x0e, 0x0f,
	0x1d, 0x1e, 0x1f, 0x10, 0x23, 0x09, 0x07, 0x01, 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00,
	0x10, 0x00, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0x09, 0x25,
	0x21, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00,
	0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20, 0xb8, 0x28,
	0x55, 0x40, 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x90, 0x20, 0x40, 0x31, 0x20,
	0x0c, 0x40, 0x55, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73,
};

/*!
 * Maintains the information on the current state of the sesor.
 */

static struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000004, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000040, 0x00000000, 2, 0},
  {0x0014, 0x00000000, 0x00000000, 2, 0},
  {0x0016, 0x000005ff, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x0000402d, 0x00000000, 2, 0},
  {0x0022, 0x00000213, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00000e00, 0x00000000, 4, 0},
  {0x0214, 0x00000001, 0x00000000, 4, 0},
  {0x0218, 0x00000801, 0x00000000, 4, 0},
  {0x021c, 0x00000001, 0x00000000, 4, 0},
  {0x0220, 0x00000001, 0x00000000, 4, 0},
  {0x0224, 0x00004800, 0x00000000, 4, 0},
  {0x0228, 0x00000005, 0x00000000, 4, 0},
  {0x022c, 0x00000000, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xa300be82, 0x00000000, 4, 0},
// HDMI Interrupt Mask
  {0x8502, 0x00000001, 0x00000000, 1, 0},
  {0x8512, 0x000000fe, 0x00000000, 1, 0},
  {0x8514, 0x00000000, 0x00000000, 1, 0},
  {0x8515, 0x00000000, 0x00000000, 1, 0},
  {0x8516, 0x00000000, 0x00000000, 1, 0},
// HDMI Audio RefClk (26 MHz)
  {0x8531, 0x00000001, 0x00000000, 1, 0},
  {0x8540, 0x0000008c, 0x00000000, 1, 0},
  {0x8541, 0x0000000a, 0x00000000, 1, 0},
  {0x8630, 0x000000b0, 0x00000000, 1, 0},
  {0x8631, 0x0000001e, 0x00000000, 1, 0},
  {0x8632, 0x00000004, 0x00000000, 1, 0},
  {0x8670, 0x00000001, 0x00000000, 1, 0},
// HDMI PHY
  {0x8532, 0x00000080, 0x00000000, 1, 0},
  {0x8536, 0x00000040, 0x00000000, 1, 0},
  {0x853f, 0x0000000a, 0x00000000, 1, 0},
// EDID
  {0x85c7, 0x00000001, 0x00000000, 1, 0},
  {0x85cb, 0x00000001, 0x00000000, 1, 0},
// HDMI System
  {0x8543, 0x00000032, 0x00000000, 1, 0},
//  {0x8544, 0x00000000, 0x00000000, 1, 1000},
//  {0x8544, 0x00000001, 0x00000000, 1, 100},
  {0x8545, 0x00000031, 0x00000000, 1, 0},
  {0x8546, 0x0000002d, 0x00000000, 1, 0},
// HDCP Setting
  {0x85d1, 0x00000001, 0x00000000, 1, 0},
  {0x8560, 0x00000024, 0x00000000, 1, 0},
  {0x8563, 0x00000011, 0x00000000, 1, 0},
  {0x8564, 0x0000000f, 0x00000000, 1, 0},
// Video settings
  {0x8573, 0x00000081, 0x00000000, 1, 0},
  {0x8571, 0x00000002, 0x00000000, 1, 0},
// HDMI Audio In Setting
  {0x8600, 0x00000000, 0x00000000, 1, 0},
  {0x8602, 0x000000f3, 0x00000000, 1, 0},
  {0x8603, 0x00000002, 0x00000000, 1, 0},
  {0x8604, 0x0000000c, 0x00000000, 1, 0},
  {0x8606, 0x00000005, 0x00000000, 1, 0},
  {0x8607, 0x00000000, 0x00000000, 1, 0},
  {0x8620, 0x00000022, 0x00000000, 1, 0},
  {0x8640, 0x00000001, 0x00000000, 1, 0},
  {0x8641, 0x00000065, 0x00000000, 1, 0},
  {0x8642, 0x00000007, 0x00000000, 1, 0},
//  {0x8651, 0x00000003, 0x00000000, 1, 0},	// Inverted LRCK polarity - (Sony) format
  {0x8652, 0x00000002, 0x00000000, 1, 0},	// Left-justified I2S (Phillips) format
//  {0x8652, 0x00000000, 0x00000000, 1, 0},	// Right-justified (Sony) format
  {0x8665, 0x00000010, 0x00000000, 1, 0},
// InfoFrame Extraction
  {0x8709, 0x000000ff, 0x00000000, 1, 0},
  {0x870b, 0x0000002c, 0x00000000, 1, 0},
  {0x870c, 0x00000053, 0x00000000, 1, 0},
  {0x870d, 0x00000001, 0x00000000, 1, 0},
  {0x870e, 0x00000030, 0x00000000, 1, 0},
  {0x9007, 0x00000010, 0x00000000, 1, 0},
  {0x854a, 0x00000001, 0x00000000, 1, 0},
// Output Control
  {0x0004, 0x00000cf7, 0x00000000, 2, 0},
  };

static struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000004, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000000, 0x00000000, 2, 0},
  {0x0014, 0x0000ffff, 0x00000000, 2, 0},
  {0x0016, 0x000005ff, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x00004062, 0x00000000, 2, 0},
  {0x0022, 0x00000613, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00000d00, 0x00000000, 4, 0},
  {0x0214, 0x00000001, 0x00000000, 4, 0},
  {0x0218, 0x00000701, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000001, 0x00000000, 4, 0},
  {0x0224, 0x00004000, 0x00000000, 4, 0},
  {0x0228, 0x00000005, 0x00000000, 4, 0},
  {0x022c, 0x00000000, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0},
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xa300be86, 0x00000000, 4, 0},
// HDMI Interrupt Mask
  {0x8502, 0x00000001, 0x00000000, 1, 0},
  {0x8512, 0x000000fe, 0x00000000, 1, 0},
  {0x8514, 0x00000000, 0x00000000, 1, 0},
  {0x8515, 0x00000000, 0x00000000, 1, 0},
  {0x8516, 0x00000000, 0x00000000, 1, 0},
// HDMI Audio RefClk (26 MHz)
  {0x8531, 0x00000001, 0x00000000, 1, 0},
  {0x8540, 0x00000a8c, 0x00000000, 1, 0},
  {0x8630, 0x00041eb0, 0x00000000, 1, 0},
  {0x8670, 0x00000001, 0x00000000, 1, 0},
// HDMI PHY
  {0x8532, 0x00000080, 0x00000000, 1, 0},
  {0x8536, 0x00000040, 0x00000000, 1, 0},
  {0x853f, 0x0000000a, 0x00000000, 1, 0},
// HDMI System
  {0x8543, 0x00000032, 0x00000000, 1, 0},
  {0x8544, 0x00000000, 0x00000000, 1, 0},
  {0x8545, 0x00000031, 0x00000000, 1, 0},
  {0x8546, 0x0000002d, 0x00000000, 1, 0},
// EDID
  {0x85c7, 0x00000001, 0x00000000, 1, 0},
  {0x85cb, 0x00000001, 0x00000000, 1, 0},
// HDCP Setting
  {0x85d1, 0x00000001, 0x00000000, 1, 0},
  {0x8560, 0x00000024, 0x00000000, 1, 0},
  {0x8563, 0x00000011, 0x00000000, 1, 0},
  {0x8564, 0x0000000f, 0x00000000, 1, 0},
// RGB --> YUV Conversion
//  {0x8574, 0x00000000, 0x00000000, 1, 0},
  {0x8573, 0x00000081, 0x00000000, 1, 0},
  {0x8571, 0x00000002, 0x00000000, 1, 0},
// HDMI Audio In Setting
  {0x8600, 0x00000000, 0x00000000, 1, 0},
  {0x8602, 0x000000f3, 0x00000000, 1, 0},
  {0x8603, 0x00000002, 0x00000000, 1, 0},
  {0x8604, 0x0000000c, 0x00000000, 1, 0},
  {0x8606, 0x00000005, 0x00000000, 1, 0},
  {0x8607, 0x00000000, 0x00000000, 1, 0},
  {0x8620, 0x00000022, 0x00000000, 1, 0},
  {0x8640, 0x00000001, 0x00000000, 1, 0},
  {0x8641, 0x00000065, 0x00000000, 1, 0},
  {0x8642, 0x00000007, 0x00000000, 1, 0},
  {0x8652, 0x00000002, 0x00000000, 1, 0},
  {0x8665, 0x00000010, 0x00000000, 1, 0},
// InfoFrame Extraction
  {0x8709, 0x000000ff, 0x00000000, 1, 0},
  {0x870b, 0x0000002c, 0x00000000, 1, 0},
  {0x870c, 0x00000053, 0x00000000, 1, 0},
  {0x870d, 0x00000001, 0x00000000, 1, 0},
  {0x870e, 0x00000030, 0x00000000, 1, 0},
  {0x9007, 0x00000010, 0x00000000, 1, 0},
  {0x854a, 0x00000001, 0x00000000, 1, 0},
// Output Control
  {0x0004, 0x00000cf7, 0x00000000, 2, 0},
};

static struct reg_value tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000084, 0x00000000, 2, 0},
  {0x0010, 0x0000001e, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x0000405c, 0x00000000, 2, 0},
  {0x0022, 0x00000613, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00000e00, 0x00000000, 4, 0},
  {0x0214, 0x00000001, 0x00000000, 4, 0},
  {0x0218, 0x00000801, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000001, 0x00000000, 4, 0},
  {0x0224, 0x00004000, 0x00000000, 4, 0},
  {0x0228, 0x00000006, 0x00000000, 4, 0},
  {0x022c, 0x00000000, 0x00000000, 4, 0},
  {0x0234, 0x00000007, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xa30080a2, 0x00000000, 4, 0},
// 1280x720 colorbar
  {0x000a, 0x00000a00, 0x00000000, 2, 0},
  {0x7080, 0x00000082, 0x00000000, 2, 0},
// 128 pixel black - repeat 128 times
  {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)},
// 128 pixel blue - repeat 64 times
  {0x7000, 0x000000ff, 0x00000000, 2, 0},
  {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel red - repeat 64 times
  {0x7000, 0x00000000, 0x00000000, 2, 0},
  {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel pink - repeat 64 times
  {0x7000, 0x00007fff, 0x00000000, 2, 0},
  {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel green - repeat 64 times
  {0x7000, 0x00007f00, 0x00000000, 2, 0},
  {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel light blue - repeat 64 times
  {0x7000, 0x0000c0ff, 0x00000000, 2, 0},
  {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel yellow - repeat 64 times
  {0x7000, 0x0000ff00, 0x00000000, 2, 0},
  {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel white - repeat 64 times
  {0x7000, 0x0000ff7f, 0x00000000, 2, 0},
  {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)},
// 720 lines
  {0x7090, 0x000002cf, 0x00000000, 2, 0},
  {0x7092, 0x00000580, 0x00000000, 2, 0},
  {0x7094, 0x00000010, 0x00000000, 2, 0},
  {0x7080, 0x00000083, 0x00000000, 2, 0},
};

static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000084, 0x00000000, 2, 0},
  {0x0010, 0x0000001e, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x0000405c, 0x00000000, 2, 0},
  {0x0022, 0x00000613, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00000e00, 0x00000000, 4, 0},
  {0x0214, 0x00000001, 0x00000000, 4, 0},
  {0x0218, 0x00000801, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000001, 0x00000000, 4, 0},
  {0x0224, 0x00004000, 0x00000000, 4, 0},
  {0x0228, 0x00000006, 0x00000000, 4, 0},
  {0x022c, 0x00000000, 0x00000000, 4, 0},
  {0x0234, 0x0000001F, 0x00000000, 4, 0}, //{0x0234, 0x00000007, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xa30080a6, 0x00000000, 4, 0}, //{0x0500, 0xa30080a2, 0x00000000, 4, 0},
// 1280x720 colorbar
  {0x000a, 0x00000a00, 0x00000000, 2, 0},
  {0x7080, 0x00000082, 0x00000000, 2, 0},
// 128 pixel black - repeat 128 times
  {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)},
// 128 pixel blue - repeat 64 times
  {0x7000, 0x000000ff, 0x00000000, 2, 0},
  {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel red - repeat 64 times
  {0x7000, 0x00000000, 0x00000000, 2, 0},
  {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel pink - repeat 64 times
  {0x7000, 0x00007fff, 0x00000000, 2, 0},
  {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel green - repeat 64 times
  {0x7000, 0x00007f00, 0x00000000, 2, 0},
  {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel light blue - repeat 64 times
  {0x7000, 0x0000c0ff, 0x00000000, 2, 0},
  {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel yellow - repeat 64 times
  {0x7000, 0x0000ff00, 0x00000000, 2, 0},
  {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel white - repeat 64 times
  {0x7000, 0x0000ff7f, 0x00000000, 2, 0},
  {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)},
// 720 lines
  {0x7090, 0x000002cf, 0x00000000, 2, 0},
  {0x7092, 0x00000300, 0x00000000, 2, 0}, //{0x7092, 0x00000580, 0x00000000, 2, 0},
  {0x7094, 0x00000010, 0x00000000, 2, 0},
  {0x7080, 0x00000083, 0x00000000, 2, 0},
};


static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000084, 0x00000000, 2, 0},
  {0x0010, 0x0000001e, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x00004050, 0x00000000, 2, 0},
  {0x0022, 0x00000213, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00001800, 0x00000000, 4, 0},
  {0x0214, 0x00000002, 0x00000000, 4, 0},
  {0x0218, 0x00001102, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000003, 0x00000000, 4, 0},
  {0x0224, 0x00004000, 0x00000000, 4, 0},
  {0x0228, 0x00000007, 0x00000000, 4, 0},
  {0x022c, 0x00000001, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xa30080a6, 0x00000000, 4, 0},
// 1280x720 colorbar
  {0x000a, 0x00000800, 0x00000000, 2, 0},
  {0x7080, 0x00000082, 0x00000000, 2, 0},
// 128 pixel black - repeat 128 times
  {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)},
// 128 pixel blue - repeat 64 times
  {0x7000, 0x000000ff, 0x00000000, 2, 0},
  {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel red - repeat 64 times
  {0x7000, 0x00000000, 0x00000000, 2, 0},
  {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel pink - repeat 64 times
  {0x7000, 0x00007fff, 0x00000000, 2, 0},
  {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel green - repeat 64 times
  {0x7000, 0x00007f00, 0x00000000, 2, 0},
  {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel light blue - repeat 64 times
  {0x7000, 0x0000c0ff, 0x00000000, 2, 0},
  {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel yellow - repeat 64 times
  {0x7000, 0x0000ff00, 0x00000000, 2, 0},
  {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel white - repeat 64 times
  {0x7000, 0x0000ff7f, 0x00000000, 2, 0},
  {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)},
// 720 lines
  {0x0020, 0x0000406f, 0x00000000, 2, 100},
  {0x7090, 0x000002cf, 0x00000000, 2, 0},
  {0x7092, 0x00000540, 0x00000000, 2, 0},
  {0x7094, 0x00000010, 0x00000000, 2, 0},
  {0x7080, 0x00000083, 0x00000000, 2, 0},
};

static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000084, 0x00000000, 2, 0},
  {0x0010, 0x0000001e, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x000080c7, 0x00000000, 2, 0},
  {0x0022, 0x00000213, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00001e00, 0x00000000, 4, 0},
  {0x0214, 0x00000003, 0x00000000, 4, 0},
  {0x0218, 0x00001402, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000003, 0x00000000, 4, 0},
  {0x0224, 0x00004a00, 0x00000000, 4, 0},
  {0x0228, 0x00000008, 0x00000000, 4, 0},
  {0x022c, 0x00000002, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xa30080a6, 0x00000000, 4, 0},
// 1280x720 colorbar
  {0x000a, 0x00000a00, 0x00000000, 2, 0},
  {0x7080, 0x00000082, 0x00000000, 2, 0},
// 128 pixel black - repeat 128 times
  {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)},
// 128 pixel blue - repeat 64 times
  {0x7000, 0x000000ff, 0x00000000, 2, 0},
  {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel red - repeat 64 times
  {0x7000, 0x00000000, 0x00000000, 2, 0},
  {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel pink - repeat 64 times
  {0x7000, 0x00007fff, 0x00000000, 2, 0},
  {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel green - repeat 64 times
  {0x7000, 0x00007f00, 0x00000000, 2, 0},
  {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel light blue - repeat 64 times
  {0x7000, 0x0000c0ff, 0x00000000, 2, 0},
  {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel yellow - repeat 64 times
  {0x7000, 0x0000ff00, 0x00000000, 2, 0},
  {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel white - repeat 64 times
  {0x7000, 0x0000ff7f, 0x00000000, 2, 0},
  {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)},
// 720 lines
  {0x7090, 0x000002cf, 0x00000000, 2, 0},
  {0x7092, 0x000006b8, 0x00000000, 2, 0},
  {0x7094, 0x00000010, 0x00000000, 2, 0},
  {0x7080, 0x00000083, 0x00000000, 2, 0},
};

static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000084, 0x00000000, 2, 0},
  {0x0010, 0x0000001e, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x000080c7, 0x00000000, 2, 0},
  {0x0022, 0x00000213, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00001e00, 0x00000000, 4, 0},
  {0x0214, 0x00000003, 0x00000000, 4, 0},
  {0x0218, 0x00001402, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000003, 0x00000000, 4, 0},
  {0x0224, 0x00004a00, 0x00000000, 4, 0},
  {0x0228, 0x00000008, 0x00000000, 4, 0},
  {0x022c, 0x00000002, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xa30080a6, 0x00000000, 4, 0},
// 1920x1023 colorbar
  {0x000a, 0x00000f00, 0x00000000, 2, 0},
  {0x7080, 0x00000082, 0x00000000, 2, 0},
// 128 pixel black - repeat 128 times
  {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)},
// 128 pixel blue - repeat 64 times
  {0x7000, 0x000000ff, 0x00000000, 2, 0},
  {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel red - repeat 64 times
  {0x7000, 0x00000000, 0x00000000, 2, 0},
  {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel pink - repeat 64 times
  {0x7000, 0x00007fff, 0x00000000, 2, 0},
  {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel green - repeat 64 times
  {0x7000, 0x00007f00, 0x00000000, 2, 0},
  {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel light blue - repeat 64 times
  {0x7000, 0x0000c0ff, 0x00000000, 2, 0},
  {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel yellow - repeat 64 times
  {0x7000, 0x0000ff00, 0x00000000, 2, 0},
  {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)},
// 128 pixel white - repeat 64 times
  {0x7000, 0x0000ff7f, 0x00000000, 2, 0},
  {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)},
// 1023 lines
  {0x7090, 0x000003fe, 0x00000000, 2, 0},
  {0x7092, 0x000004d8, 0x00000000, 2, 0},
  {0x7094, 0x0000002d, 0x00000000, 2, 0},
  {0x7080, 0x00000083, 0x00000000, 2, 0},
};

static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000084, 0x00000000, 2, 0},
  {0x0010, 0x0000001e, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x00008073, 0x00000000, 2, 0},
  {0x0022, 0x00000213, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
//  {0x014c, 0x00000000, 0x00000000, 4, 0},
//  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00001200, 0x00000000, 4, 0},
  {0x0214, 0x00000002, 0x00000000, 4, 0},
  {0x0218, 0x00000b02, 0x00000000, 4, 0},
  {0x021c, 0x00000001, 0x00000000, 4, 0},
  {0x0220, 0x00000103, 0x00000000, 4, 0},
  {0x0224, 0x00004000, 0x00000000, 4, 0},
  {0x0228, 0x00000008, 0x00000000, 4, 0},
  {0x022c, 0x00000002, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000000, 0x00000000, 4, 0},
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xA3008082, 0x00000000, 4, 0},
// 640x480 colorbar
  {0x000a, 0x00000500, 0x00000000, 2, 0},
  {0x7080, 0x00000082, 0x00000000, 2, 0},
// 80 pixel black - repeate 80 times
  {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(80<<16)},
// 80 pixel blue - repeate 40 times
  {0x7000, 0x000000ff, 0x00000000, 2, 0},
  {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel red - repeate 40 times
  {0x7000, 0x00000000, 0x00000000, 2, 0},
  {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel pink - repeate 40 times
  {0x7000, 0x00007fff, 0x00000000, 2, 0},
  {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel green - repeate 40 times
  {0x7000, 0x00007f00, 0x00000000, 2, 0},
  {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel light blue - repeate 40 times
  {0x7000, 0x0000c0ff, 0x00000000, 2, 0},
  {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel yellow - repeate 40 times
  {0x7000, 0x0000ff00, 0x00000000, 2, 0},
  {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel white - repeate 40 times
  {0x7000, 0x0000ff7f, 0x00000000, 2, 0},
  {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(40<<16)},
// 480 lines
  {0x7090, 0x000001df, 0x00000000, 2, 0},
  {0x7092, 0x00000898, 0x00000000, 2, 0},
  {0x7094, 0x00000285, 0x00000000, 2, 0},
  {0x7080, 0x00000083, 0x00000000, 2, 0},
};

static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000084, 0x00000000, 2, 0},
  {0x0010, 0x0000001e, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x0000404F, 0x00000000, 2, 0},
  {0x0022, 0x00000613, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00001800, 0x00000000, 4, 0},
  {0x0214, 0x00000002, 0x00000000, 4, 0},
  {0x0218, 0x00001102, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000003, 0x00000000, 4, 0},
  {0x0224, 0x00004000, 0x00000000, 4, 0},
  {0x0228, 0x00000007, 0x00000000, 4, 0},
  {0x022c, 0x00000001, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0},
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xA30080A2, 0x00000000, 4, 0},
// 640x480 colorbar
  {0x000a, 0x00000500, 0x00000000, 2, 0},
  {0x7080, 0x00000082, 0x00000000, 2, 0},
// 80 pixel black - repeate 80 times
  {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(80<<16)},
// 80 pixel blue - repeate 40 times
  {0x7000, 0x000000ff, 0x00000000, 2, 0},
  {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel red - repeate 40 times
  {0x7000, 0x00000000, 0x00000000, 2, 0},
  {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel pink - repeate 40 times
  {0x7000, 0x00007fff, 0x00000000, 2, 0},
  {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel green - repeate 40 times
  {0x7000, 0x00007f00, 0x00000000, 2, 0},
  {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel light blue - repeate 40 times
  {0x7000, 0x0000c0ff, 0x00000000, 2, 0},
  {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel yellow - repeate 40 times
  {0x7000, 0x0000ff00, 0x00000000, 2, 0},
  {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(40<<16)},
// 80 pixel white - repeate 40 times
  {0x7000, 0x0000ff7f, 0x00000000, 2, 0},
  {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(40<<16)},
// 480 lines
  {0x7090, 0x000001df, 0x00000000, 2, 0},
  {0x7092, 0x00000700, 0x00000000, 2, 0},
  {0x7094, 0x00000010, 0x00000000, 2, 0},
  {0x7080, 0x00000083, 0x00000000, 2, 0},
};

//480p RGB2YUV442
static struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000004, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000040, 0x00000000, 2, 0},
//  {0x000a, 0x000005a0, 0x00000000, 2, 0},
//  {0x0010, 0x0000001e, 0x00000000, 2, 0},
  {0x0014, 0x00000000, 0x00000000, 2, 0},
  {0x0016, 0x000005ff, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x0000405c, 0x00000000, 2, 0},
  {0x0022, 0x00000613, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00000d00, 0x00000000, 4, 0},
  {0x0214, 0x00000001, 0x00000000, 4, 0},
  {0x0218, 0x00000701, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000001, 0x00000000, 4, 0},
  {0x0224, 0x00004000, 0x00000000, 4, 0},
  {0x0228, 0x00000005, 0x00000000, 4, 0},
  {0x022c, 0x00000000, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0},
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xA30080A2, 0x00000000, 4, 0},
// HDMI Interrupt Mask
  {0x8502, 0x00000001, 0x00000000, 1, 0},
  {0x8512, 0x000000fe, 0x00000000, 1, 0},
  {0x8514, 0x00000000, 0x00000000, 1, 0},
  {0x8515, 0x00000000, 0x00000000, 1, 0},
  {0x8516, 0x00000000, 0x00000000, 1, 0},
// HDMI Audio RefClk (26 MHz)
  {0x8531, 0x00000001, 0x00000000, 1, 0},
  {0x8540, 0x00000a8c, 0x00000000, 1, 0},
  {0x8630, 0x00041eb0, 0x00000000, 1, 0},
  {0x8670, 0x00000001, 0x00000000, 1, 0},
// HDMI PHY
  {0x8532, 0x00000080, 0x00000000, 1, 0},
  {0x8536, 0x00000040, 0x00000000, 1, 0},
  {0x853f, 0x0000000a, 0x00000000, 1, 0},
// HDMI System
  {0x8543, 0x00000032, 0x00000000, 1, 0},
  {0x8544, 0x00000000, 0x00000000, 1, 100},
//  {0x8544, 0x00000001, 0x00000000, 1, 100},
  {0x8545, 0x00000031, 0x00000000, 1, 0},
  {0x8546, 0x0000002d, 0x00000000, 1, 0},
// EDID
  {0x85c7, 0x00000001, 0x00000000, 1, 0},
  {0x85cb, 0x00000001, 0x00000000, 1, 0},
// HDCP Setting
  {0x85d1, 0x00000001, 0x00000000, 1, 0},
  {0x8560, 0x00000024, 0x00000000, 1, 0},
  {0x8563, 0x00000011, 0x00000000, 1, 0},
  {0x8564, 0x0000000f, 0x00000000, 1, 0},
// RGB --> YUV Conversion
  {0x8573, 0x00000081, 0x00000000, 1, 0},
  {0x8571, 0x00000002, 0x00000000, 1, 0},
// HDMI Audio In Setting
  {0x8600, 0x00000000, 0x00000000, 1, 0},
  {0x8602, 0x000000f3, 0x00000000, 1, 0},
  {0x8603, 0x00000002, 0x00000000, 1, 0},
  {0x8604, 0x0000000c, 0x00000000, 1, 0},
  {0x8606, 0x00000005, 0x00000000, 1, 0},
  {0x8607, 0x00000000, 0x00000000, 1, 0},
  {0x8620, 0x00000022, 0x00000000, 1, 0},
  {0x8640, 0x00000001, 0x00000000, 1, 0},
  {0x8641, 0x00000065, 0x00000000, 1, 0},
  {0x8642, 0x00000007, 0x00000000, 1, 0},
  {0x8652, 0x00000002, 0x00000000, 1, 0},
  {0x8665, 0x00000010, 0x00000000, 1, 0},
// InfoFrame Extraction
  {0x8709, 0x000000ff, 0x00000000, 1, 0},
  {0x870b, 0x0000002c, 0x00000000, 1, 0},
  {0x870c, 0x00000053, 0x00000000, 1, 0},
  {0x870d, 0x00000001, 0x00000000, 1, 0},
  {0x870e, 0x00000030, 0x00000000, 1, 0},
  {0x9007, 0x00000010, 0x00000000, 1, 0},
  {0x854a, 0x00000001, 0x00000000, 1, 0},
// Output Control
  {0x0004, 0x00000cf7, 0x00000000, 2, 0},
  };

//480p RGB2YUV442
static struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000004, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},
  {0x0006, 0x00000040, 0x00000000, 2, 0},
  {0x000a, 0x000005a0, 0x00000000, 2, 0},
//  {0x0010, 0x0000001e, 0x00000000, 2, 0},
  {0x0014, 0x00000000, 0x00000000, 2, 0},
  {0x0016, 0x000005ff, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x0000405b, 0x00000000, 2, 0},
  {0x0022, 0x00000613, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00000d00, 0x00000000, 4, 0},
  {0x0214, 0x00000001, 0x00000000, 4, 0},
  {0x0218, 0x00000701, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000001, 0x00000000, 4, 0},
  {0x0224, 0x00004000, 0x00000000, 4, 0},
  {0x0228, 0x00000005, 0x00000000, 4, 0},
  {0x022c, 0x00000000, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0},
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xA30080A2, 0x00000000, 4, 0},
// HDMI Interrupt Mask
  {0x8502, 0x00000001, 0x00000000, 1, 0},
  {0x8512, 0x000000fe, 0x00000000, 1, 0},
  {0x8514, 0x00000000, 0x00000000, 1, 0},
  {0x8515, 0x00000000, 0x00000000, 1, 0},
  {0x8516, 0x00000000, 0x00000000, 1, 0},
// HDMI Audio RefClk (27 MHz)
  {0x8531, 0x00000001, 0x00000000, 1, 0},
  {0x8540, 0x00000a8c, 0x00000000, 1, 0},
  {0x8630, 0x00041eb0, 0x00000000, 1, 0},
  {0x8670, 0x00000001, 0x00000000, 1, 0},
// HDMI PHY
  {0x8532, 0x00000080, 0x00000000, 1, 0},
  {0x8536, 0x00000040, 0x00000000, 1, 0},
  {0x853f, 0x0000000a, 0x00000000, 1, 0},
// HDMI System
  {0x8543, 0x00000032, 0x00000000, 1, 0},
  {0x8544, 0x00000000, 0x00000000, 1, 100},
//  {0x8544, 0x00000001, 0x00000000, 1, 100},
  {0x8545, 0x00000031, 0x00000000, 1, 0},
  {0x8546, 0x0000002d, 0x00000000, 1, 0},
// EDID
  {0x85c7, 0x00000001, 0x00000000, 1, 0},
  {0x85cb, 0x00000001, 0x00000000, 1, 0},
// HDCP Setting
  {0x85d1, 0x00000001, 0x00000000, 1, 0},
  {0x8560, 0x00000024, 0x00000000, 1, 0},
  {0x8563, 0x00000011, 0x00000000, 1, 0},
  {0x8564, 0x0000000f, 0x00000000, 1, 0},
// RGB --> YUV Conversion
  {0x8573, 0x00000081, 0x00000000, 1, 0},
  {0x8571, 0x00000002, 0x00000000, 1, 0},
// HDMI Audio In Setting
  {0x8600, 0x00000000, 0x00000000, 1, 0},
  {0x8602, 0x000000f3, 0x00000000, 1, 0},
  {0x8603, 0x00000002, 0x00000000, 1, 0},
  {0x8604, 0x0000000c, 0x00000000, 1, 0},
  {0x8606, 0x00000005, 0x00000000, 1, 0},
  {0x8607, 0x00000000, 0x00000000, 1, 0},
  {0x8620, 0x00000022, 0x00000000, 1, 0},
  {0x8640, 0x00000001, 0x00000000, 1, 0},
  {0x8641, 0x00000065, 0x00000000, 1, 0},
  {0x8642, 0x00000007, 0x00000000, 1, 0},
  {0x8652, 0x00000002, 0x00000000, 1, 0},
  {0x8665, 0x00000010, 0x00000000, 1, 0},
// InfoFrame Extraction
  {0x8709, 0x000000ff, 0x00000000, 1, 0},
  {0x870b, 0x0000002c, 0x00000000, 1, 0},
  {0x870c, 0x00000053, 0x00000000, 1, 0},
  {0x870d, 0x00000001, 0x00000000, 1, 0},
  {0x870e, 0x00000030, 0x00000000, 1, 0},
  {0x9007, 0x00000010, 0x00000000, 1, 0},
  {0x854a, 0x00000001, 0x00000000, 1, 0},
// Output Control
  {0x0004, 0x00000cf7, 0x00000000, 2, 0},
  };

static struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},
  {0x0004, 0x00000084, 0x00000000, 2, 0},
  {0x0002, 0x00000f00, 0x00000000, 2, 100},//0},
  {0x0002, 0x00000000, 0x00000000, 2, 1000},//0},
  {0x0006, 0x00000000, 0x00000000, 2, 0},
  {0x0014, 0x00000000, 0x00000000, 2, 0},
  {0x0016, 0x000005ff, 0x00000000, 2, 0},
// Program CSI Tx PLL
  {0x0020, 0x000080c7, 0x00000000, 2, 0},
  {0x0022, 0x00000213, 0x00000000, 2, 0},
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},
  {0x0144, 0x00000000, 0x00000000, 4, 0},
  {0x0148, 0x00000000, 0x00000000, 4, 0},
  {0x014c, 0x00000000, 0x00000000, 4, 0},
  {0x0150, 0x00000000, 0x00000000, 4, 0},
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00001e00, 0x00000000, 4, 0},
  {0x0214, 0x00000003, 0x00000000, 4, 0},
  {0x0218, 0x00001402, 0x00000000, 4, 0},
  {0x021c, 0x00000000, 0x00000000, 4, 0},
  {0x0220, 0x00000003, 0x00000000, 4, 0},
  {0x0224, 0x00004a00, 0x00000000, 4, 0},
  {0x0228, 0x00000008, 0x00000000, 4, 0},
  {0x022c, 0x00000002, 0x00000000, 4, 0},
  {0x0234, 0x0000001f, 0x00000000, 4, 0},
  {0x0238, 0x00000001, 0x00000000, 4, 0},
  {0x0204, 0x00000001, 0x00000000, 4, 0},
  {0x0518, 0x00000001, 0x00000000, 4, 0},
  {0x0500, 0xa30080a6, 0x00000000, 4, 0},
// HDMI Interrupt Mask
  {0x8502, 0x00000001, 0x00000000, 1, 0},
  {0x8512, 0x000000fe, 0x00000000, 1, 0},
  {0x8514, 0x00000000, 0x00000000, 1, 0},
  {0x8515, 0x00000000, 0x00000000, 1, 0},
  {0x8516, 0x00000000, 0x00000000, 1, 0},
// HDMI Audio RefClk (27 MHz)
  {0x8531, 0x00000001, 0x00000000, 1, 0},
  {0x8540, 0x00000a8c, 0x00000000, 1, 0},
  {0x8630, 0x00041eb0, 0x00000000, 1, 0},
  {0x8670, 0x00000001, 0x00000000, 1, 0},
// HDMI PHY
  {0x8532, 0x00000080, 0x00000000, 1, 0},
  {0x8536, 0x00000040, 0x00000000, 1, 0},
  {0x853f, 0x0000000a, 0x00000000, 1, 0},
// HDMI System
  {0x8543, 0x00000032, 0x00000000, 1, 0},
  {0x8544, 0x00000010, 0x00000000, 1, 100},
  {0x8545, 0x00000031, 0x00000000, 1, 0},
  {0x8546, 0x0000002d, 0x00000000, 1, 0},
// EDID
  {0x85c7, 0x00000001, 0x00000000, 1, 0},
  {0x85cb, 0x00000001, 0x00000000, 1, 0},
// HDCP Setting
  {0x85d1, 0x00000001, 0x00000000, 1, 0},
  {0x8560, 0x00000024, 0x00000000, 1, 0},
  {0x8563, 0x00000011, 0x00000000, 1, 0},
  {0x8564, 0x0000000f, 0x00000000, 1, 0},
// RGB --> YUV Conversion
  {0x8571, 0x00000002, 0x00000000, 1, 0},
  {0x8573, 0x00000081, 0x00000000, 1, 0},
  {0x8576, 0x00000060, 0x00000000, 1, 0},
// HDMI Audio In Setting
  {0x8600, 0x00000000, 0x00000000, 1, 0},
  {0x8602, 0x000000f3, 0x00000000, 1, 0},
  {0x8603, 0x00000002, 0x00000000, 1, 0},
  {0x8604, 0x0000000c, 0x00000000, 1, 0},
  {0x8606, 0x00000005, 0x00000000, 1, 0},
  {0x8607, 0x00000000, 0x00000000, 1, 0},
  {0x8620, 0x00000022, 0x00000000, 1, 0},
  {0x8640, 0x00000001, 0x00000000, 1, 0},
  {0x8641, 0x00000065, 0x00000000, 1, 0},
  {0x8642, 0x00000007, 0x00000000, 1, 0},
  {0x8652, 0x00000002, 0x00000000, 1, 0},
  {0x8665, 0x00000010, 0x00000000, 1, 0},
// InfoFrame Extraction
  {0x8709, 0x000000ff, 0x00000000, 1, 0},
  {0x870b, 0x0000002c, 0x00000000, 1, 0},
  {0x870c, 0x00000053, 0x00000000, 1, 0},
  {0x870d, 0x00000001, 0x00000000, 1, 0},
  {0x870e, 0x00000030, 0x00000000, 1, 0},
  {0x9007, 0x00000010, 0x00000000, 1, 0},
  {0x854a, 0x00000001, 0x00000000, 1, 0},
// Output Control
  {0x0004, 0x00000cf7, 0x00000000, 2, 0},
};

static struct reg_value tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz[] = {
  {0x7080, 0x00000000, 0x00000000, 2, 0},		// IR control resister
  {0x0004, 0x00000084, 0x00000000, 2, 0},		//  Internal Generated output pattern,Do not send InfoFrame data out to CSI2,Audio output to CSI2-TX i/f,I2C address index increments on every data byte transfer, disable audio and video TX buffers
  {0x0002, 0x00000f00, 0x00000000, 2, 100},//0},	// Reset devices and set normal operatio (not sleep)
  {0x0002, 0x00000000, 0x00000000, 2, 1000},//0}, 	// Clear reset bits
  {0x0006, 0x000001f8, 0x00000000, 2, 0},		// FIFO level = 1f8 = 504
  {0x0014, 0x00000000, 0x00000000, 2, 0},		// Clear interrupt status bits
  {0x0016, 0x000005ff, 0x00000000, 2, 0},		// Mask audio mute, CSI-TX, and the other interrups
// Program CSI Tx PLL
  //{0x0020, 0x000080c7, 0x00000000, 2, 0},		// Input divider setting = 0x8 -> Division ratio = (PRD3..0) + 1 = 9, Feedback divider setting = 0xc7 -> Division ratio = (FBD8...0) + 1 = 200
  {0x0020, 0x000080c7, 0x00000000, 2, 0},		// Input divider setting = 0x8 -> Division ratio = (PRD3..0) + 1 = 9, Feedback divider setting = 0xc7 -> Division ratio = (FBD8...0) + 1 = 200
  {0x0022, 0x00000213, 0x00000000, 2, 0},		// HSCK frequency = 500MHz – 1GHz HSCK frequency, Loop bandwidth setting = 50% of maximum loop bandwidth (default), REFCLK toggling –> normal operation, REFCLK stops -> no oscillation, Bypass clock = normal operation, clocks switched off (output LOW), PLL Reset normal operation, PLL Enable = PLL on
// CSI Tx PHY  (32-bit Registers)
  {0x0140, 0x00000000, 0x00000000, 4, 0},		// Clock Lane DPHY Control: Bypass Lane Enable from PPI Layer enable.
  {0x0144, 0x00000000, 0x00000000, 4, 0},		// Data Lane 0 DPHY Control: Bypass Lane Enable from PPI Layer enable.
  {0x0148, 0x00000000, 0x00000000, 4, 0},		// Data Lane 1 DPHY Control: Bypass Lane Enable from PPI Layer enable.
  {0x014c, 0x00000000, 0x00000000, 4, 0},		// Data Lane 2 DPHY Control: Bypass Lane Enable from PPI Layer enable.
  {0x0150, 0x00000000, 0x00000000, 4, 0},		// Data Lane 3 DPHY Control: Bypass Lane Enable from PPI Layer enable.
// CSI Tx PPI  (32-bit Registers)
  {0x0210, 0x00001e00, 0x00000000, 4, 0},		// LINEINITCNT: Line Initialization Wait Counter = 0x1e00 = 7680
  {0x0214, 0x00000003, 0x00000000, 4, 0},		// LPTXTIMECNT: SYSLPTX Timing Generation Counter = 3
  {0x0218, 0x00001402, 0x00000000, 4, 0},				// TCLK_HEADERCNT: TCLK_ZERO Counter = 0x14 = 20, TCLK_PREPARE Counter = 0x02 = 2
  {0x021c, 0x00000000, 0x00000000, 4, 0},		// TCLK_TRAILCNT: TCLK_TRAIL Counter = 0
  {0x0220, 0x00000003, 0x00000000, 4, 0},		// THS_HEADERCNT: THS_ZERO Counter = 0, THS_PREPARE Counter = 3
  {0x0224, 0x00004a00, 0x00000000, 4, 0},		// TWAKEUP: TWAKEUP Counter = 0x4a00 = 18944
  {0x0228, 0x00000008, 0x00000000, 4, 0},		// TCLK_POSTCNT: TCLK_POST Counter = 8
  {0x022c, 0x00000002, 0x00000000, 4, 0},		// THS_TRAILCNT: THS_TRAIL Counter = 2
  {0x0234, 0x0000001f, 0x00000000, 4, 0},		// HSTXVREGEN: Enable voltage regulators for lanes and clk
  {0x0238, 0x00000001, 0x00000000, 4, 0},		    // TXOPTIONCNTRL: Set Continuous Clock Mode
  {0x0204, 0x00000001, 0x00000000, 4, 0},		// PPI STARTCNTRL: start PPI function
  {0x0518, 0x00000001, 0x00000000, 4, 0},		// CSI_START: start
  {0x0500, 0xa30080a6, 0x00000000, 4, 0},		// CSI Configuration Register: set register 0x040C with data 0x80a6 (CSI MOde, Disables the HTX_TO timer, High-Speed data transfer is performed to Tx, DSCClk Stays in HS mode when Data Lane goes to LP, 4 Data Lanes,The EOT packet is automatically granted at the end of HS transfer then is transmitted)
// HDMI Interrupt Mask
  {0x8502, 0x00000001, 0x00000000, 1, 0},		// SYSTEM INTERRUPT: clear DDC power change detection interrupt
  {0x8512, 0x000000fe, 0x00000000, 1, 0},		// SYS INTERRUPT MASK: DDC power change detection interrupt not masked
  {0x8514, 0x00000000, 0x00000000, 1, 0},		// PACKET INTERRUPT MASK: unmask all
  {0x8515, 0x00000000, 0x00000000, 1, 0},		// CBIT INTERRUPT MASK: unmask all
  {0x8516, 0x00000000, 0x00000000, 1, 0},		// AUDIO INTERRUPT MASK: unmask all
// HDMI Audio RefClk (27 MHz)
  {0x8531, 0x00000001, 0x00000000, 1, 0},		// PHY CONTROL0: 27MHz, DDC5V detection operation.
  {0x8540, 0x00000a8c, 0x00000000, 1, 0},		// SYS FREQ0 Register: 27MHz
  {0x8630, 0x00041eb0, 0x00000000, 1, 0},		// Audio FS Lock Detect Control: for 27MHz
  {0x8670, 0x00000001, 0x00000000, 1, 0},		// AUDIO PLL Setting: For REFCLK = 27MHz
// HDMI PHY
  {0x8532, 0x00000080, 0x00000000, 1, 0},		//
  {0x8536, 0x00000040, 0x00000000, 1, 0},		//
  {0x853f, 0x0000000a, 0x00000000, 1, 0},		//
// HDMI System
  {0x8543, 0x00000032, 0x00000000, 1, 0},		// DDC CONTROL: DDC_ACK output terminal H active, DDC5V_active detect delay 200ms
  {0x8544, 0x00000010, 0x00000000, 1, 100},		// HPD Control Register: HOTPLUG output ON/OFF control mode = DDC5V detection interlock
  {0x8545, 0x00000031, 0x00000000, 1, 0},		// ANA CONTROL: PLL charge pump setting for Audio = normal, DAC/PLL power ON/OFF setting for Audio = ON
  {0x8546, 0x0000002d, 0x00000000, 1, 0},		// AVMUTE CONTROL: AVM_CTL = 0x2d
// EDID
  {0x85c7, 0x00000001, 0x00000000, 1, 0},		// EDID MODE REGISTER: nternal EDID-RAM & DDC2B mode
  {0x85cb, 0x00000001, 0x00000000, 1, 0},		// EDID Length REGISTER 2: EDID data size stored in RAM (upper address bits) = 0x1 (Size = 0x100 = 256)
// HDCP Setting
  {0x85d1, 0x00000001, 0x00000000, 1, 0},		//
  {0x8560, 0x00000024, 0x00000000, 1, 0},		// HDCP MODE: HDCP automatic reset when DVI⇔HDMI switched = on, HDCP Line Rekey timing switch = 7clk mode (Data island delay ON), Bcaps[5] KSVINFO_READY(0x8840[5]) auto clear mode = Auto clear using AKSV write
  {0x8563, 0x00000011, 0x00000000, 1, 0},		//
  {0x8564, 0x0000000f, 0x00000000, 1, 0},		//
// RGB --> YUV Conversion
  {0x8571, 0x00000002, 0x00000000, 1, 0},		//
  {0x8573, 0x000000c1, 0x00000000, 1, 0},		// VOUT SET2 REGISTER: 422 fixed output, Video Output 422 conversion mode selection 000: During 444 input, 3tap filter; during 422 input, simple decimation, Enable RGB888 to YUV422 Conversion (Fixed Color output)
  {0x8574, 0x00000008, 0x00000000, 1, 0},				// VOUT SET3 REGISTER (VOUT_SET3): Follow register bit 0x8573[7] setting
  {0x8576, 0x00000060, 0x00000000, 1, 0},		// VOUT_COLOR: Output Color = 601 YCbCr Limited, Input Pixel Repetition judgment = automatic, Input Pixel Repetition HOST setting = no repetition
// HDMI Audio In Setting
  {0x8600, 0x00000000, 0x00000000, 1, 0},
  {0x8602, 0x000000f3, 0x00000000, 1, 0},
  {0x8603, 0x00000002, 0x00000000, 1, 0},
  {0x8604, 0x0000000c, 0x00000000, 1, 0},
  {0x8606, 0x00000005, 0x00000000, 1, 0},
  {0x8607, 0x00000000, 0x00000000, 1, 0},
  {0x8620, 0x00000022, 0x00000000, 1, 0},
  {0x8640, 0x00000001, 0x00000000, 1, 0},
  {0x8641, 0x00000065, 0x00000000, 1, 0},
  {0x8642, 0x00000007, 0x00000000, 1, 0},
  {0x8652, 0x00000002, 0x00000000, 1, 0},
  {0x8665, 0x00000010, 0x00000000, 1, 0},
// InfoFrame Extraction
  {0x8709, 0x000000ff, 0x00000000, 1, 0},		// PACKET INTERRUPT MODE: all enable
  {0x870b, 0x0000002c, 0x00000000, 1, 0},		// NO PACKET LIMIT: NO_ACP_LIMIT = 0x2, NO_AVI_LIMIT = 0xC
  {0x870c, 0x00000053, 0x00000000, 1, 0},		// When VS receive interrupt is detected, VS storage register automatic clear, When ACP receive interrupt is detected, ACP storage register automatic clear, When AVI receive interrupt occurs, judge input video signal with RGB and no Repetition, When AVI receive interrupt is detected, AVI storage register automatic clear.
  {0x870d, 0x00000001, 0x00000000, 1, 0},		// ERROR PACKET LIMIT: Packet continuing receive error occurrence detection threshold = 1
  {0x870e, 0x00000030, 0x00000000, 1, 0},		// NO PACKET LIMIT:
  {0x9007, 0x00000010, 0x00000000, 1, 0},		//
  {0x854a, 0x00000001, 0x00000000, 1, 0},		// Initialization completed flag
// Output Control
  {0x0004, 0x00000cf7, 0x00000000, 2, 0},		// Configuration Control Register: Power Island Normal, I2S/TDM clock are free running,  Enable 2 Audio channels, Audio channel number Auto detect by HW, I2S/TDM Data no delay, Select YCbCr422 8-bit (HDMI YCbCr422 12-bit data format), Send InfoFrame data out to CSI2, Audio output to I2S i/f (valid for 2 channel only), I2C address index increments on every data byte transfer, Audio and Video tx buffres enable.
};

/* list of image formats supported by TCM825X sensor */
static const struct v4l2_fmtdesc tc358743_formats[] = {
	{
		.description	= "RGB888 (RGB24)",
		.pixelformat	= V4L2_PIX_FMT_RGB24,		/* 24  RGB-8-8-8     */
		.flags		= MIPI_DT_RGB888		//	0x24
	},
	{
		.description	= "RAW12 (Y/CbCr 4:2:0)",
		.pixelformat	= V4L2_PIX_FMT_UYVY,		/* 12  Y/CbCr 4:2:0  */
		.flags		= MIPI_DT_RAW12			//	0x2c
	},
	{
		.description	= "YUV 4:2:2 8-bit",
		.pixelformat	= V4L2_PIX_FMT_YUYV, 		/*  8  8-bit color   */
		.flags		= MIPI_DT_YUV422		//	0x1e /* UYVY...		*/
	},
};


static struct tc358743_mode_info tc358743_mode_info_data[2][tc358743_mode_MAX] = {
	[0][tc358743_mode_720P_60_1280_720] =
		{tc358743_mode_720P_60_1280_720,  1280, 720, 12, 0, 4, 133,
		tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz),
		MIPI_DT_YUV422
		},
	[0][tc358743_mode_1080P_1920_1080] =
		{tc358743_mode_1080P_1920_1080,  1920, 1080, 15, 0x0b, 4, 300,
		tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz),
		MIPI_DT_YUV422
		},
	[0][tc358743_mode_INIT1] =
		{tc358743_mode_INIT1,  1280, 720, 12, 0, 2, 125,
		tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz),
		MIPI_DT_YUV422
		},
	[0][tc358743_mode_INIT2] =
		{tc358743_mode_INIT2,  1280, 720, 12, 0, 4, 125,
		tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz),
		MIPI_DT_YUV422
		},
	[0][tc358743_mode_INIT] =
		{tc358743_mode_INIT,  640, 480, 6, 1, 2, 108,
		tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont),
		MIPI_DT_YUV422
		},
	[0][tc358743_mode_INIT4] =
		{tc358743_mode_INIT4,  640, 480, 6, 1, 2, 174,
		tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz),
		MIPI_DT_YUV422
		},
	[0][tc358743_mode_INIT3] =
		{tc358743_mode_INIT3,  1024, 720, 6, 1, 4, 300,
		tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz),
		MIPI_DT_YUV422
		},
	[0][tc358743_mode_720P_1280_720] =
		{tc358743_mode_720P_1280_720,  1280, 720, 12, (0x3e)<<8|(0x3c), 2, 125,
		tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz),
		MIPI_DT_YUV422,
		},
	[0][tc358743_mode_480P_720_480] =
		{tc358743_mode_480P_720_480,  720, 480, 6, (0x02)<<8|(0x00), 2, 125,
		tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz),
		MIPI_DT_YUV422,
		},
	[0][tc358743_mode_480P_640_480] =
		{tc358743_mode_480P_640_480,  640, 480, 6, (0x02)<<8|(0x00), 2, 125,
		tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz),
		MIPI_DT_YUV422,
		},
	[0][tc358743_mode_INIT5] =
		{tc358743_mode_INIT5,  1280, 720, 12, 0, 4, 300,
		tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz),
		MIPI_DT_YUV422
		},
	[0][tc358743_mode_INIT6] =
		{tc358743_mode_INIT6,  1920, 1023, 15, 0, 4, 300,
		tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz),
		MIPI_DT_YUV422
		},
	[1][tc358743_mode_720P_60_1280_720] =
		{tc358743_mode_720P_60_1280_720,  1280, 720, 12, 0, 4, 133,
		tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz),
		MIPI_DT_YUV422
		},
	[1][tc358743_mode_1080P_1920_1080] =
		{tc358743_mode_1080P_1920_1080,  1920, 1080, 15, 0xa, 4, 300,
		tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz),
		MIPI_DT_YUV422
		},
	[1][tc358743_mode_INIT1] =
		{tc358743_mode_INIT1,  1280, 720, 12, 0, 2, 125,
		tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz),
		MIPI_DT_YUV422
		},
	[1][tc358743_mode_INIT2] =
		{tc358743_mode_INIT2,  1280, 720, 12, 0, 4, 125,
		tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz),
		MIPI_DT_YUV422
		},

	[1][tc358743_mode_INIT] =
		{tc358743_mode_INIT,  640, 480, 6, 1, 2, 108,
		tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont),
		MIPI_DT_YUV422
		},
	[1][tc358743_mode_INIT4] =
		{tc358743_mode_INIT4,  640, 480, 6, 1, 2, 174,
		tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz),
		MIPI_DT_YUV422
		},
	[1][tc358743_mode_INIT3] =
		{tc358743_mode_INIT3,  1024, 720, 6, 1, 4, 300,
		tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz),
		MIPI_DT_YUV422
		},
	[1][tc358743_mode_720P_1280_720] =
		{tc358743_mode_720P_1280_720,  1280, 720, 12, (0x3e)<<8|(0x3c), 2, 125,
		tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz),
		MIPI_DT_YUV422,
		},
	[1][tc358743_mode_480P_720_480] =
		{tc358743_mode_480P_720_480,  720, 480, 6, (0x02)<<8|(0x00), 2, 125,
		tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz),
		MIPI_DT_YUV422,
		},
	[0][tc358743_mode_480P_640_480] =
		{tc358743_mode_480P_640_480,  640, 480, 1, (0x02)<<8|(0x00), 2, 125,
		tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz,
		ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz),
		MIPI_DT_YUV422,
		},
	[1][tc358743_mode_INIT5] =
		{tc358743_mode_INIT5,  1280, 720, 12, 0, 4, 300,
		tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz),
		MIPI_DT_YUV422
		},
	[1][tc358743_mode_INIT6] =
		{tc358743_mode_INIT6,  1920, 1023, 15, 0, 4, 300,
		tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz,
		ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz),
		MIPI_DT_YUV422
		},		
};

static int tc358743_probe(struct i2c_client *adapter,
				const struct i2c_device_id *device_id);
static int tc358743_remove(struct i2c_client *client);

static s32 tc358743_read_reg(u16 reg, u32 *val);
static s32 tc358743_write_reg(u16 reg, u32 val, int len);

static const struct i2c_device_id tc358743_id[] = {
	{"tc358743_mipi", 0},
	{},
};

MODULE_DEVICE_TABLE(i2c, tc358743_id);

static struct i2c_driver tc358743_i2c_driver = {
	.driver = {
		  .owner = THIS_MODULE,
		  .name  = "tc358743_mipi",
		  },
	.probe  = tc358743_probe,
	.remove = tc358743_remove,
	.id_table = tc358743_id,
};

struct _reg_size
{
	u16 startaddr, endaddr;
	int size;
}
tc358743_read_reg_size [] =
{
	{0x0000, 0x005a, 2},
	{0x0140, 0x0150, 4},
	{0x0204, 0x0238, 4},
	{0x040c, 0x0418, 4},
	{0x044c, 0x0454, 4},
	{0x0500, 0x0518, 4},
	{0x0600, 0x06cc, 4},
	{0x7000, 0x7100, 2},
	{0x8500, 0x8bff, 1},
	{0x8c00, 0x8fff, 4},
	{0x9000, 0x90ff, 1},
	{0x9100, 0x92ff, 1},
	{0, 0, 0},
};

static s32 tc358743_write_reg(u16 reg, u32 val, int len)
{
	int i = 0;
	u32 data = val;
	u8 au8Buf[6] = {0};
	int size = 0;

	while (0 != tc358743_read_reg_size[i].startaddr ||
	      0 != tc358743_read_reg_size[i].endaddr ||
	      0 != tc358743_read_reg_size[i].size) {
		if (tc358743_read_reg_size[i].startaddr <= reg
				&& tc358743_read_reg_size[i].endaddr >= reg) {
			size = tc358743_read_reg_size[i].size;
			break;
		}
		i++;
	}
	if (!size) {
		pr_err("%s:write reg error:reg=%x is not found\n",__func__, reg);
		return -1;
	}
	if (size == 3) {
		size = 2;
	} else if (size != len) {
		pr_err("%s:write reg len error:reg=%x %d instead of %d\n",
					__func__, reg, len, size);
		return 0;
	}

	while (len > 0) {
		i = 0;
		au8Buf[i++] = (reg >> 8) & 0xff;
		au8Buf[i++] = reg & 0xff;
		while (size-- > 0)
		{
			au8Buf[i++] = (u8)data;
			data >>= 8;
		}

		if (i2c_master_send(tc358743_data.i2c_client, au8Buf, i) < 0) {
			pr_err("%s:write reg error:reg=%x,val=%x\n",
				__func__, reg, val);
			return -1;
		}
		len -= (u8)size;
		reg += (u16)size; 
	}

	return 0;
}

static s32 tc358743_read_reg(u16 reg, u32 *val)
{
	u8 au8RegBuf[2] = {0};
	u32 u32RdVal = 0;
	int i=0;
	int size = 0;

	while (0 != tc358743_read_reg_size[i].startaddr ||
	      0 != tc358743_read_reg_size[i].endaddr ||
	      0 != tc358743_read_reg_size[i].size) {
		if (tc358743_read_reg_size[i].startaddr <= reg &&
			tc358743_read_reg_size[i].endaddr >= reg) {
			size = tc358743_read_reg_size[i].size;
			break;
		}
		i++;
	}
	if (!size)
		return -1;

	au8RegBuf[0] = reg >> 8;
	au8RegBuf[1] = reg & 0xff;

	if (2 != i2c_master_send(tc358743_data.i2c_client, au8RegBuf, 2)) {
		pr_err("%s:read reg error:reg=%x\n",
				__func__, reg);
		return -1;
	}

	if (size /*of(u32RdVal)*/ != i2c_master_recv(tc358743_data.i2c_client, (char *)&u32RdVal, size /*of(u32RdVal)*/)) {
		pr_err("%s:read reg error:reg=%x,val=%x\n",
				__func__, reg, u32RdVal);
		return -1;
	}
	*val = u32RdVal;
	return size;
}

static int tc358743_write_edid(u8 *edid, int len)
{
	int i = 0, off = 0;
	u8 au8Buf[8+2] = {0};
	int size = 0;
	u16 reg;

	reg = 0x8C00;
	off = 0;
	size = ARRAY_SIZE(au8Buf)-2;
	pr_debug("Write EDID: %d (%d)\n", len, size);
	while (len > 0) {
		i = 0;
		au8Buf[i++] = (reg >> 8) & 0xff;
		au8Buf[i++] = reg & 0xff;
		while (i < ARRAY_SIZE(au8Buf)) {
			au8Buf[i++] = edid[off++];
		}

		if (i2c_master_send(tc358743_data.i2c_client, au8Buf, i) < 0) {
			pr_err("%s:write reg error:reg=%x,val=%x\n",
				__func__, reg, off);
			return -1;
		}
		len -= (u8)size;
		reg += (u16)size;
	}
	pr_debug("Activate EDID\n");
	tc358743_write_reg(0x85c7, 0x01, 1);
	tc358743_write_reg(0x85ca, 0x00, 1);
	tc358743_write_reg(0x85cb, 0x01, 1);
	return 0;
}

static int tc358743_reset(struct sensor_data *sensor)
{
	u32 tgt_fps;	/* target frames per secound */
	enum tc358743_frame_rate frame_rate = tc358743_60_fps;
	int ret = -1;

	det_work_enable(0);
	while (ret) {
		tc_standby(1);
		mdelay(100);
		tc_standby(0);
		mdelay(1000);

		tgt_fps = sensor->streamcap.timeperframe.denominator /
				sensor->streamcap.timeperframe.numerator;

		if (tgt_fps == 60)
			frame_rate = tc358743_60_fps;
		else if (tgt_fps == 30)
			frame_rate = tc358743_30_fps;

		pr_debug("%s: capture mode: %d extended mode: %d fps: %d\n", __func__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode, tgt_fps);

		ret = tc358743_init_mode(frame_rate,
				sensor->streamcap.capturemode);
		if (ret)
			pr_err("%s: Fail to init tc35874! - retry\n", __func__);
	}
	det_work_enable(1);
	return ret;
}

void mipi_csi2_swreset(struct mipi_csi2_info *info);
#include "../../../../mxc/mipi/mxc_mipi_csi2.h"
static int tc358743_init_mode(enum tc358743_frame_rate frame_rate,
			    enum tc358743_mode mode)
{

	struct reg_value *pModeSetting = NULL;
	s32 i = 0;
	s32 iModeSettingArySize = 0;
	register u32 RepeateLines = 0;
	register int RepeateTimes = 0;
	register u32 Delay_ms = 0;
	register u16 RegAddr = 0;
	register u32 Mask = 0;
	register u32 Val = 0;
	u8  Length;
	u32 RegVal = 0;
	int retval = 0;
	void *mipi_csi2_info;
	u32 mipi_reg;
	u32 mipi_reg_test[10];

	pr_debug("%s rate: %d mode: %d\n", __func__, frame_rate, mode);
	if ((mode > tc358743_mode_MAX || mode < 0)
		&& (mode != tc358743_mode_INIT)) {
		pr_debug("%s Wrong tc358743 mode detected! %d. Set mode 0\n", __func__, mode);
		mode = 0;
	}

	mipi_csi2_info = mipi_csi2_get_info();
	pr_debug("%s rate: %d mode: %d, info %p\n", __func__, frame_rate, mode, mipi_csi2_info);

	/* initial mipi dphy */
	tc358743_toggle_hpd(!hpd_active);
	if (mipi_csi2_info) {
		pr_debug("%s: mipi_csi2_info:\n"
		"mipi_en:       %d\n"
		"ipu_id:        %d\n"
		"csi_id:        %d\n"
		"v_channel:     %d\n"
		"lanes:         %d\n"
		"datatype:      %d\n"
		"dphy_clk:      %p\n"
		"pixel_clk:     %p\n"
		"mipi_csi2_base:%p\n"
		"pdev:          %p\n"
		, __func__,
		((struct mipi_csi2_info *)mipi_csi2_info)->mipi_en,
		((struct mipi_csi2_info *)mipi_csi2_info)->ipu_id,
		((struct mipi_csi2_info *)mipi_csi2_info)->csi_id,
		((struct mipi_csi2_info *)mipi_csi2_info)->v_channel,
		((struct mipi_csi2_info *)mipi_csi2_info)->lanes,
		((struct mipi_csi2_info *)mipi_csi2_info)->datatype,
		((struct mipi_csi2_info *)mipi_csi2_info)->dphy_clk,
		((struct mipi_csi2_info *)mipi_csi2_info)->pixel_clk,
		((struct mipi_csi2_info *)mipi_csi2_info)->mipi_csi2_base,
		((struct mipi_csi2_info *)mipi_csi2_info)->pdev
		);
		if (!mipi_csi2_get_status(mipi_csi2_info))
			mipi_csi2_enable(mipi_csi2_info);

		if (mipi_csi2_get_status(mipi_csi2_info)) {
			int ifmt;
			if (tc358743_mode_info_data[frame_rate][mode].lanes != 0) {
				pr_debug("%s Change lanes: from %d to %d\n", __func__, ((struct mipi_csi2_info *)mipi_csi2_info)->lanes, tc358743_mode_info_data[frame_rate][mode].lanes);
				((struct mipi_csi2_info *)mipi_csi2_info)->lanes = tc358743_mode_info_data[frame_rate][mode].lanes;
				((struct mipi_csi2_info *)mipi_csi2_info)->lanes = tc358743_mode_info_data[frame_rate][mode].lanes;
			}
			pr_debug("Now Using %d lanes\n",mipi_csi2_set_lanes(mipi_csi2_info));

			/*Only reset MIPI CSI2 HW at sensor initialize*/
			if (!hdmi_mode)	// is this during reset
				mipi_csi2_reset(mipi_csi2_info);

			
			pr_debug("%s format: %x\n", __func__, tc358743_data.pix.pixelformat);
			for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++)
				if (tc358743_mode_info_data[frame_rate][mode].flags == tc358743_formats[ifmt].flags) {
					tc358743_data.pix.pixelformat = tc358743_formats[ifmt].pixelformat;
					pr_debug("%s: %s (%x, %x)\n", __func__, tc358743_formats[ifmt].description, tc358743_data.pix.pixelformat, tc358743_formats[ifmt].flags);
					mipi_csi2_set_datatype(mipi_csi2_info, tc358743_formats[ifmt].flags);
					break;
				}
			if (ifmt >= ARRAY_SIZE(tc358743_formats)) {
				pr_err("currently this sensor format (0x%x) can not be supported!\n", tc358743_data.pix.pixelformat);
				return -1;
			}
		} else {
			pr_err("Can not enable mipi csi2 driver!\n");
			return -1;
		}
	} else {
		pr_err("Fail to get mipi_csi2_info!\n");
		return -1;
	}

	{
		pModeSetting =
			tc358743_mode_info_data[frame_rate][mode].init_data_ptr;
		iModeSettingArySize =
			tc358743_mode_info_data[frame_rate][mode].init_data_size;

		tc358743_data.pix.width =
			tc358743_mode_info_data[frame_rate][mode].width;
		tc358743_data.pix.height =
			tc358743_mode_info_data[frame_rate][mode].height;
		pr_debug("%s: Set %d regs from %p for frs %d mode %d with width %d height %d\n", __func__,
			iModeSettingArySize,
			pModeSetting,
			frame_rate,
			mode,
			tc358743_data.pix.width,
			tc358743_data.pix.height);
		for (i = 0; i < iModeSettingArySize; ++i) {
			pModeSetting = tc358743_mode_info_data[frame_rate][mode].init_data_ptr + i;

			Delay_ms = pModeSetting->u32Delay_ms & (0xffff);
			RegAddr = pModeSetting->u16RegAddr;
			Val = pModeSetting->u32Val;
			Mask = pModeSetting->u32Mask;
			Length = pModeSetting->u8Length;
			if (Mask) {
				retval = tc358743_read_reg(RegAddr, &RegVal);
				if (retval < 0)
					break;

				RegVal &= ~(u8)Mask;
				Val &= Mask;
				Val |= RegVal;
			}

			retval = tc358743_write_reg(RegAddr, Val, Length);
			if (retval < 0)
				break;

			if (Delay_ms)
				msleep(Delay_ms);

			if (0 != ((pModeSetting->u32Delay_ms>>16) & (0xff))) {
				if (!RepeateTimes) {
					RepeateTimes = (pModeSetting->u32Delay_ms>>16) & (0xff);
					RepeateLines = (pModeSetting->u32Delay_ms>>24) & (0xff);
				}
				if (--RepeateTimes > 0) {
					i -= RepeateLines;
				}
			}
		}
		if (retval < 0) {
			pr_err("%s: Fail to write REGS to tc35874!\n", __func__);
			goto err;
		}
	}
	if (!hdmi_mode)	// is this during reset
		if ((retval = tc358743_write_edid(cHDMIEDID, ARRAY_SIZE(cHDMIEDID))))
			pr_err("%s: Fail to write EDID to tc35874!\n", __func__);

	tc358743_toggle_hpd(hpd_active);
	if (mipi_csi2_info) {
		unsigned int i = 0;

		/* wait for mipi sensor ready */
		mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info);
		while ((mipi_reg == 0x200) && (i < 10)) {
			mipi_reg_test[i] = mipi_reg;
			mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info);
			i++;
			msleep(10);
		}

		if (i >= 10) {
			pr_err("mipi csi2 can not receive sensor clk!\n");
			return -1;
		}

		{
			int j;
			for (j = 0; j < i; j++)
			{
				pr_debug("%d  mipi csi2 dphy status %x\n", j, mipi_reg_test[j]);
			}
		}

		i = 0;

		/* wait for mipi stable */
		mipi_reg = mipi_csi2_get_error1(mipi_csi2_info);
		while ((mipi_reg != 0x0) && (i < 10)) {
			mipi_reg_test[i] = mipi_reg;
			mipi_reg = mipi_csi2_get_error1(mipi_csi2_info);
			i++;
			msleep(10);
		}

		if (i >= 10) {
			pr_err("mipi csi2 can not reveive data correctly!\n");
			return -1;
		}

		{
			int j;
			for (j = 0; j < i; j++) {
				pr_debug("%d  mipi csi2 err1 %x\n", j, mipi_reg_test[j]);
			}
		}
	}
err:
	return (retval>0)?0:retval;
}

/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */

static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
{
	pr_debug("%s\n", __func__);
	if (s == NULL) {
		pr_err("   ERROR!! no slave device set!\n");
		return -1;
	}

	memset(p, 0, sizeof(*p));
	p->u.bt656.clock_curr = TC358743_XCLK_MIN; //tc358743_data.mclk;
	pr_debug("%s: clock_curr=mclk=%d\n", __func__, tc358743_data.mclk);
	p->if_type = V4L2_IF_TYPE_BT656;
	p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
	p->u.bt656.clock_min = TC358743_XCLK_MIN;
	p->u.bt656.clock_max = TC358743_XCLK_MAX;
	p->u.bt656.bt_sync_correct = 1;  /* Indicate external vsync */

	return 0;
}

/*!
 * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl
 * @s: pointer to standard V4L2 device structure
 * @on: indicates power mode (on or off)
 *
 * Turns the power on or off, depending on the value of on and returns the
 * appropriate error code.
 */
static int ioctl_s_power(struct v4l2_int_device *s, int on)
{
	struct sensor_data *sensor = s->priv;

	pr_debug("%s: %d\n", __func__, on);
	if (on && !sensor->on) {
		if (io_regulator)
			if (regulator_enable(io_regulator) != 0)
				return -EIO;
		if (core_regulator)
			if (regulator_enable(core_regulator) != 0)
				return -EIO;
		if (gpo_regulator)
			if (regulator_enable(gpo_regulator) != 0)
				return -EIO;
		if (analog_regulator)
			if (regulator_enable(analog_regulator) != 0)
				return -EIO;
		/* Make sure power on */
		tc_standby(0);

	} else if (!on && sensor->on) {
		if (analog_regulator)
			regulator_disable(analog_regulator);
		if (core_regulator)
			regulator_disable(core_regulator);
		if (io_regulator)
			regulator_disable(io_regulator);
		if (gpo_regulator)
			regulator_disable(gpo_regulator);
		if (!hdmi_mode)
			tc358743_reset(sensor);
	}

	sensor->on = on;

	return 0;
}

/*!
 * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
 * @s: pointer to standard V4L2 device structure
 * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
 *
 * Returns the sensor's video CAPTURE parameters.
 */
static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
{
	struct sensor_data *sensor = s->priv;
	struct v4l2_captureparm *cparm = &a->parm.capture;
	int ret = 0;

	pr_debug("%s type: %x\n", __func__, a->type);
	switch (a->type) {
	/* This is the only case currently handled. */
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		memset(a, 0, sizeof(*a));
		a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		cparm->capability = sensor->streamcap.capability;
		cparm->timeperframe = sensor->streamcap.timeperframe;
		cparm->capturemode = sensor->streamcap.capturemode;
		cparm->extendedmode = sensor->streamcap.extendedmode;
		ret = 0;
		break;

	/* These are all the possible cases. */
	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
	case V4L2_BUF_TYPE_VBI_CAPTURE:
	case V4L2_BUF_TYPE_VBI_OUTPUT:
	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
		ret = -EINVAL;
		break;

	default:
		pr_debug("   type is unknown - %d\n", a->type);
		ret = -EINVAL;
		break;
	}

	det_work_enable(1);
	pr_debug("%s done %d\n", __func__, ret);
	return ret;
}

static int tc358743_toggle_hpd(int active)
{
	int ret = 0;
	if (active) {
		ret += tc358743_write_reg(0x8544, 0x00, 1);
		mdelay(500);
		ret += tc358743_write_reg(0x8544, 0x10, 1);
	} else {
		ret += tc358743_write_reg(0x8544, 0x10, 1);
		mdelay(500);
		ret += tc358743_write_reg(0x8544, 0x00, 1);
	}
	return ret;
}

/*!
 * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
 * @s: pointer to standard V4L2 device structure
 * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
 *
 * Configures the sensor to use the input parameters, if possible.  If
 * not possible, reverts to the old parameters and returns the
 * appropriate error code.
 */
static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
{
	struct sensor_data *sensor = s->priv;
	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
	u32 tgt_fps;	/* target frames per secound */
	enum tc358743_frame_rate frame_rate = tc358743_60_fps, frame_rate_now = tc358743_60_fps;
	int ret = 0;

	pr_debug("%s\n", __func__);
	det_work_enable(0);
	/* Make sure power on */
	tc_standby(0);

	switch (a->type) {
	/* This is the only case currently handled. */
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		/* Check that the new frame rate is allowed. */
		if ((timeperframe->numerator == 0) ||
		    (timeperframe->denominator == 0)) {
			timeperframe->denominator = DEFAULT_FPS;
			timeperframe->numerator = 1;
		}

		tgt_fps = timeperframe->denominator /
			  timeperframe->numerator;

		if (tgt_fps > MAX_FPS) {
			timeperframe->denominator = MAX_FPS;
			timeperframe->numerator = 1;
		} else if (tgt_fps < MIN_FPS) {
			timeperframe->denominator = MIN_FPS;
			timeperframe->numerator = 1;
		}

		/* Actual frame rate we use */
		tgt_fps = timeperframe->denominator /
			  timeperframe->numerator;

		if (tgt_fps == 60)
			frame_rate = tc358743_60_fps;
		else if (tgt_fps == 30)
			frame_rate = tc358743_30_fps;
		else {
			pr_err(" The camera frame rate is not supported!\n");
			ret = -EINVAL;
			break;
		}

		if ((u32)a->parm.capture.capturemode > tc358743_mode_MAX) {
			a->parm.capture.capturemode = 0;
			pr_debug("%s: Forse extended mode: %d \n", __func__,(u32)a->parm.capture.capturemode);
		}

		tgt_fps = sensor->streamcap.timeperframe.denominator /
			  sensor->streamcap.timeperframe.numerator;

		if (tgt_fps == 60)
			frame_rate_now = tc358743_60_fps;
		else if (tgt_fps == 30)
			frame_rate_now = tc358743_30_fps;

		if (frame_rate_now != frame_rate ||
		   sensor->streamcap.capturemode != (u32)a->parm.capture.capturemode ||
		   sensor->streamcap.extendedmode != (u32)a->parm.capture.extendedmode) {
			sensor->streamcap.timeperframe = *timeperframe;
			sensor->streamcap.capturemode =
					(u32)a->parm.capture.capturemode;
			sensor->streamcap.extendedmode =
					(u32)a->parm.capture.extendedmode;

			pr_debug("%s: capture mode: %d extended mode: %d \n", __func__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode);

			ret = tc358743_init_mode(frame_rate,
						sensor->streamcap.capturemode);
		} else {
			pr_debug("%s: Keep current settings\n", __func__);
		}
		break;

	/* These are all the possible cases. */
	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
	case V4L2_BUF_TYPE_VBI_CAPTURE:
	case V4L2_BUF_TYPE_VBI_OUTPUT:
	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
		pr_debug("   type is not " \
			"V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n",
			a->type);
		ret = -EINVAL;
		break;

	default:
		pr_debug("   type is unknown - %d\n", a->type);
		ret = -EINVAL;
		break;
	}

	if (ret)
		det_work_enable(1);
	return ret;
}

/*!
 * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
 * @s: pointer to standard V4L2 device structure
 * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
 *
 * If the requested control is supported, returns the control's current
 * value from the video_control[] array.  Otherwise, returns -EINVAL
 * if the control is not supported.
 */
static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
{
	int ret = 0;

	pr_debug("%s\n", __func__);
	switch (vc->id) {
	case V4L2_CID_BRIGHTNESS:
		vc->value = tc358743_data.brightness;
		break;
	case V4L2_CID_HUE:
		vc->value = tc358743_data.hue;
		break;
	case V4L2_CID_CONTRAST:
		vc->value = tc358743_data.contrast;
		break;
	case V4L2_CID_SATURATION:
		vc->value = tc358743_data.saturation;
		break;
	case V4L2_CID_RED_BALANCE:
		vc->value = tc358743_data.red;
		break;
	case V4L2_CID_BLUE_BALANCE:
		vc->value = tc358743_data.blue;
		break;
	case V4L2_CID_EXPOSURE:
		vc->value = tc358743_data.ae_mode;
		break;
	default:
		ret = -EINVAL;
	}

	return ret;
}

/*!
 * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
 * @s: pointer to standard V4L2 device structure
 * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
 *
 * If the requested control is supported, sets the control's current
 * value in HW (and updates the video_control[] array).  Otherwise,
 * returns -EINVAL if the control is not supported.
 */
static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
{
	int retval = 0;

	pr_debug("In tc358743:ioctl_s_ctrl %d\n",
		 vc->id);

	switch (vc->id) {
	case V4L2_CID_BRIGHTNESS:
		break;
	case V4L2_CID_CONTRAST:
		break;
	case V4L2_CID_SATURATION:
		break;
	case V4L2_CID_HUE:
		break;
	case V4L2_CID_AUTO_WHITE_BALANCE:
		break;
	case V4L2_CID_DO_WHITE_BALANCE:
		break;
	case V4L2_CID_RED_BALANCE:
		break;
	case V4L2_CID_BLUE_BALANCE:
		break;
	case V4L2_CID_GAMMA:
		break;
	case V4L2_CID_EXPOSURE:
		break;
	case V4L2_CID_AUTOGAIN:
		break;
	case V4L2_CID_GAIN:
		break;
	case V4L2_CID_HFLIP:
		break;
	case V4L2_CID_VFLIP:
		break;
	default:
		retval = -EPERM;
		break;
	}

	return retval;
}

int get_pixelformat(int index)
{
	int ifmt;

	for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++)
		if (tc358743_mode_info_data[0][index].flags == tc358743_formats[ifmt].flags)
			break;

	if (ifmt == ARRAY_SIZE(tc358743_formats))
		ifmt = 0;	/* Default = RBG888 */
	return ifmt;
}

/*!
 * ioctl_enum_framesizes - V4L2 sensor interface handler for
 *			   VIDIOC_ENUM_FRAMESIZES ioctl
 * @s: pointer to standard V4L2 device structure
 * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
 *
 * Return 0 if successful, otherwise -EINVAL.
 */
static int ioctl_enum_framesizes(struct v4l2_int_device *s,
				 struct v4l2_frmsizeenum *fsize)
{
	pr_debug("%s, INDEX: %d\n", __func__,fsize->index);
	if (fsize->index > tc358743_mode_MAX)
		return -EINVAL;

	fsize->pixel_format = tc358743_formats[get_pixelformat(fsize->index)].pixelformat; 
	fsize->discrete.width =
			    tc358743_mode_info_data[0][fsize->index].width;
	fsize->discrete.height =
			    tc358743_mode_info_data[0][fsize->index].height;
	pr_debug("%s %d:%d format: %x\n", __func__, fsize->discrete.width, fsize->discrete.height, fsize->pixel_format);
	return 0;
}

/*!
 * ioctl_g_chip_ident - V4L2 sensor interface handler for
 *			VIDIOC_DBG_G_CHIP_IDENT ioctl
 * @s: pointer to standard V4L2 device structure
 * @id: pointer to int
 *
 * Return 0.
 */
static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id)
{
	((struct v4l2_dbg_chip_ident *)id)->match.type =
					V4L2_CHIP_MATCH_I2C_DRIVER;
	strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name,
		"tc358743_mipi");

	return 0;
}

/*!
 * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
 * @s: pointer to standard V4L2 device structure
 */
static int ioctl_init(struct v4l2_int_device *s)
{
	pr_debug("%s\n", __func__);
	return 0;
}

/*!
 * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT
 * @s: pointer to standard V4L2 device structure
 * @fmt: pointer to standard V4L2 fmt description structure
 *
 * Return 0.
 */
static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
			      struct v4l2_fmtdesc *fmt)
{
	pr_debug("%s\n", __func__);
	if (fmt->index > tc358743_mode_MAX)
		return -EINVAL;

	fmt->pixelformat = tc358743_formats[get_pixelformat(fmt->index)].pixelformat;

	pr_debug("%s: format: %x\n", __func__, fmt->pixelformat);
	return 0;
}

static int ioctl_try_fmt_cap(struct v4l2_int_device *s,
			     struct v4l2_format *f)
{
	struct sensor_data *sensor = s->priv;
	u32 tgt_fps;	/* target frames per secound */
	enum tc358743_frame_rate frame_rate;
//	enum image_size isize;
	int ifmt;
	struct v4l2_pix_format *pix = &f->fmt.pix;
	pr_debug("%s\n", __func__);

	tgt_fps = sensor->streamcap.timeperframe.denominator /
		  sensor->streamcap.timeperframe.numerator;

	if (tgt_fps == 60) {
		frame_rate = tc358743_60_fps;
	} else if (tgt_fps == 30) {
		frame_rate = tc358743_30_fps;
	} else {
		pr_debug("%s: %d fps (%d,%d) is not supported\n", __func__, tgt_fps, sensor->streamcap.timeperframe.denominator,sensor->streamcap.timeperframe.numerator);
		return -EINVAL;
	}

	tc358743_data.pix.width = pix->width = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].width;
	tc358743_data.pix.height = pix->height = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].height;

	for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++)
		if (tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].flags == tc358743_formats[ifmt].flags)
			break;

	if (ifmt == ARRAY_SIZE(tc358743_formats))
		ifmt = 0;	/* Default = RBG888 */

	tc358743_data.pix.pixelformat = pix->pixelformat = tc358743_formats[ifmt].pixelformat;
	pix->field = V4L2_FIELD_NONE;
	pix->bytesperline = pix->width * 4;
	pix->sizeimage = pix->bytesperline * pix->height;
	pix->priv = 0;

	switch (pix->pixelformat) {
	case V4L2_PIX_FMT_UYVY:
	default:
		pix->colorspace = V4L2_COLORSPACE_SRGB;
		break;
	}

	{
		u32 u32val;
		int ret = tc358743_read_reg(0x8520,&u32val);
		pr_debug("SYS_STATUS: 0x%x, ret val: %d \n",u32val,ret);
		ret = tc358743_read_reg(0x8521,&u32val);
		pr_debug("VI_STATUS0: 0x%x, ret val: %d \n",u32val,ret);
		ret = tc358743_read_reg(0x8522,&u32val);
		pr_debug("VI_STATUS1: 0x%x, ret val: %d \n",u32val,ret);
		ret = tc358743_read_reg(0x8525,&u32val);
		pr_debug("VI_STATUS2: 0x%x, ret val: %d \n",u32val,ret);
		ret = tc358743_read_reg(0x8528,&u32val);
		pr_debug("VI_STATUS3: 0x%x, ret val: %d \n",u32val,ret);
		pr_debug("%s %d:%d format: %x\n", __func__, pix->width, pix->height, pix->pixelformat);
	}
	return 0;
}

/*!
 * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
 * @s: pointer to standard V4L2 device structure
 * @f: pointer to standard V4L2 v4l2_format structure
 *
 * Returns the sensor's current pixel format in the v4l2_format
 * parameter.
 */
static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
{

	pr_debug("%s\n", __func__);
	return ioctl_try_fmt_cap(s, f);
}

/*!
 * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
 * @s: pointer to standard V4L2 device structure
 *
 * Initialise the device when slave attaches to the master.
 */
static int ioctl_dev_init(struct v4l2_int_device *s)
{
	struct sensor_data *sensor = s->priv;
	u32 tgt_xclk;	/* target xclk */
	u32 tgt_fps;	/* target frames per secound */
	int ret = 0;
	enum tc358743_frame_rate frame_rate;
	void *mipi_csi2_info;

	pr_debug("%s\n", __func__);
	tc358743_data.on = true;

	/* mclk */
	tgt_xclk = tc358743_data.mclk;
	tgt_xclk = min(tgt_xclk, (u32)TC358743_XCLK_MAX);
	tgt_xclk = max(tgt_xclk, (u32)TC358743_XCLK_MIN);
	tc358743_data.mclk = tgt_xclk;

	pr_debug("%s: Setting mclk to %d MHz\n", __func__, tc358743_data.mclk / 1000000);
//	set_mclk_rate(&tc358743_data.mclk, tc358743_data.mclk_source);
//	pr_debug("%s: After mclk to %d MHz\n", __func__, tc358743_data.mclk / 1000000);

	/* Default camera frame rate is set in probe */
	tgt_fps = sensor->streamcap.timeperframe.denominator /
		  sensor->streamcap.timeperframe.numerator;

	if (tgt_fps == 60)
		frame_rate = tc358743_60_fps;
	else if (tgt_fps == 30)
		frame_rate = tc358743_30_fps;
	else
		return -EINVAL;

	mipi_csi2_info = mipi_csi2_get_info();

	/* enable mipi csi2 */
	if (mipi_csi2_info) {
		mipi_csi2_enable(mipi_csi2_info);
	} else {
		pr_err("Fail to get mipi_csi2_info!\n");
		return -EPERM;
	}

	pr_debug("%s done\n", __func__);
	return ret;
}

/*!
 * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
 * @s: pointer to standard V4L2 device structure
 *
 * Delinitialise the device when slave detaches to the master.
 */
static int ioctl_dev_exit(struct v4l2_int_device *s)
{
	void *mipi_csi2_info;

	mipi_csi2_info = mipi_csi2_get_info();

	/* disable mipi csi2 */
	if (mipi_csi2_info)
		if (mipi_csi2_get_status(mipi_csi2_info))
			mipi_csi2_disable(mipi_csi2_info);

	return 0;
}

/*!
 * This structure defines all the ioctls for this module and links them to the
 * enumeration.
 */
static struct v4l2_int_ioctl_desc tc358743_ioctl_desc[] = {
	{vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init},
	{vidioc_int_dev_exit_num, ioctl_dev_exit},
	{vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power},
	{vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm},
	{vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init},
	{vidioc_int_enum_fmt_cap_num,
				(v4l2_int_ioctl_func *) ioctl_enum_fmt_cap},
	{vidioc_int_try_fmt_cap_num,
				(v4l2_int_ioctl_func *)ioctl_try_fmt_cap},
	{vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_g_fmt_cap},
	{vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm},
	{vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm},
	{vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl},
	{vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl},
	{vidioc_int_enum_framesizes_num,
				(v4l2_int_ioctl_func *) ioctl_enum_framesizes},
	{vidioc_int_g_chip_ident_num,
				(v4l2_int_ioctl_func *) ioctl_g_chip_ident},
};

static struct v4l2_int_slave tc358743_slave = {
	.ioctls = tc358743_ioctl_desc,
	.num_ioctls = ARRAY_SIZE(tc358743_ioctl_desc),
};

static struct v4l2_int_device tc358743_int_device = {
	.module = THIS_MODULE,
	.name = "tc358743",
	.type = v4l2_int_type_slave,
	.u = {
		.slave = &tc358743_slave,
	},
};


#ifdef AUDIO_ENABLE
struct imx_ssi {
	struct platform_device *ac97_dev;

	struct snd_soc_dai *imx_ac97;
	struct clk *clk;
	void __iomem *base;
	int irq;
	int fiq_enable;
	unsigned int offset;

	unsigned int flags;

	void (*ac97_reset) (struct snd_ac97 *ac97);
	void (*ac97_warm_reset)(struct snd_ac97 *ac97);

	struct imx_pcm_dma_params	dma_params_rx;
	struct imx_pcm_dma_params	dma_params_tx;

	int enabled;

	struct platform_device *soc_platform_pdev;
	struct platform_device *soc_platform_pdev_fiq;
};
#define SSI_SCR			0x10
#define SSI_SRCR		0x20
#define SSI_STCCR		0x24
#define SSI_SRCCR		0x28
#define SSI_SCR_I2S_MODE_NORM	(0 << 5)
#define SSI_SCR_I2S_MODE_MSTR	(1 << 5)
#define SSI_SCR_I2S_MODE_SLAVE	(2 << 5)
#define SSI_I2S_MODE_MASK	(3 << 5)
#define SSI_SCR_SYN		(1 << 4)
#define SSI_SRCR_RSHFD		(1 << 4)
#define SSI_SRCR_RSCKP		(1 << 3)
#define SSI_SRCR_RFSI		(1 << 2)
#define SSI_SRCR_REFS		(1 << 0)
#define SSI_STCCR_WL(x)		((((x) - 2) >> 1) << 13)
#define SSI_STCCR_WL_MASK	(0xf << 13)
#define SSI_SRCCR_WL(x)		((((x) - 2) >> 1) << 13)
#define SSI_SRCCR_WL_MASK	(0xf << 13)
/* Audio setup */

static int imxpac_tc358743_hw_params(struct snd_pcm_substream *substream,
			    struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	int ret;

	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
				SND_SOC_DAIFMT_NB_IF |
				SND_SOC_DAIFMT_CBM_CFM);
	if (ret) {
		pr_err("%s: failed set cpu dai format\n", __func__);
		return ret;
	}

	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
				SND_SOC_DAIFMT_NB_NF |
				SND_SOC_DAIFMT_CBM_CFM);
	if (ret) {
		pr_err("%s: failed set codec dai format\n", __func__);
		return ret;
	}

	ret = snd_soc_dai_set_sysclk(codec_dai, 0,
				     CODEC_CLOCK, SND_SOC_CLOCK_OUT);
	if (ret) {
		pr_err("%s: failed setting codec sysclk\n", __func__);
		return ret;
	}
	snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0);

	ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
				SND_SOC_CLOCK_IN);
	if (ret) {
		pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
		return ret;
	}
#if 1
// clear SSI_SRCR_RXBIT0 and SSI_SRCR_RSHFD in order to push Right-justified MSB data fro 
	{
		struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
		u32 scr = 0, srcr = 0, stccr = 0, srccr = 0;

		pr_debug("%s: base %p\n", __func__, (void *)ssi->base);
		scr = readl(ssi->base + SSI_SCR);
		pr_debug("%s: SSI_SCR before:   %p\n", __func__, (void *)scr);
		writel(scr, ssi->base + SSI_SCR);
		pr_debug("%s: SSI_SCR after:    %p\n", __func__, (void *)scr);

		srcr = readl(ssi->base + SSI_SRCR);
		pr_debug("%s: SSI_SRCR before:  %p\n", __func__, (void *)srcr);
		writel(srcr, ssi->base + SSI_SRCR);
		pr_debug("%s: SSI_SRCR after:   %p\n", __func__, (void *)srcr);

		stccr = readl(ssi->base + SSI_STCCR);
		pr_debug("%s: SSI_STCCR before: %p\n", __func__, (void *)stccr);
		stccr &= ~SSI_STCCR_WL_MASK;
		stccr |= SSI_STCCR_WL(16);
		writel(stccr, ssi->base + SSI_STCCR);
		pr_debug("%s: SSI_STCCR after:  %p\n", __func__, (void *)stccr);

		srccr = readl(ssi->base + SSI_SRCCR);
		pr_debug("%s: SSI_SRCCR before: %p\n", __func__, (void *)srccr);
		srccr &= ~SSI_SRCCR_WL_MASK;
		srccr |= SSI_SRCCR_WL(16);
		writel(srccr, ssi->base + SSI_SRCCR);
		pr_debug("%s: SSI_SRCCR after:  %p\n", __func__, (void *)srccr);
	}
#endif
	return 0;
}



/* Headphones jack detection DAPM pins */
static struct snd_soc_jack_pin hs_jack_pins_a[] = {
};

/* imx_3stack card dapm widgets */
static struct snd_soc_dapm_widget imx_3stack_dapm_widgets_a[] = {
};



static struct snd_kcontrol_new tc358743_machine_controls_a[] = {
};

/* imx_3stack machine connections to the codec pins */
static struct snd_soc_dapm_route audio_map_a[] = {
};

static int imx_3stack_tc358743_init(struct snd_soc_pcm_runtime *rtd)
{
	struct snd_soc_codec *codec = rtd->codec;
	int ret;
	struct snd_soc_jack *hs_jack;

	struct snd_soc_jack_pin *hs_jack_pins;
	int hs_jack_pins_size;
	struct snd_soc_dapm_widget *imx_3stack_dapm_widgets;
	int imx_3stack_dapm_widgets_size;
	struct snd_kcontrol_new *tc358743_machine_controls;
	int tc358743_machine_controls_size;
	struct snd_soc_dapm_route *audio_map;
	int audio_map_size;
	int gpio_num = -1;
	char *gpio_name;

	pr_debug("%s started\n", __func__);

	hs_jack_pins = hs_jack_pins_a;
	hs_jack_pins_size = ARRAY_SIZE(hs_jack_pins_a);
	imx_3stack_dapm_widgets = imx_3stack_dapm_widgets_a;
	imx_3stack_dapm_widgets_size = ARRAY_SIZE(imx_3stack_dapm_widgets_a);
	tc358743_machine_controls = tc358743_machine_controls_a;
	tc358743_machine_controls_size = ARRAY_SIZE(tc358743_machine_controls_a);
	audio_map = audio_map_a;
	audio_map_size = ARRAY_SIZE(audio_map_a);
	gpio_num = -1; //card_a_gpio_num;
	gpio_name = NULL;

	ret = snd_soc_add_controls(codec, tc358743_machine_controls,
			tc358743_machine_controls_size);
	if (ret) {
		pr_err("%s: snd_soc_add_controls failed. err = %d\n", __func__, ret);
		return ret;
	}
	/* Add imx_3stack specific widgets */
	snd_soc_dapm_new_controls(&codec->dapm, imx_3stack_dapm_widgets,
				  imx_3stack_dapm_widgets_size);

	/* Set up imx_3stack specific audio path audio_map */
	snd_soc_dapm_add_routes(&codec->dapm, audio_map, audio_map_size);

	snd_soc_dapm_enable_pin(&codec->dapm, hs_jack_pins->pin);
	snd_soc_dapm_sync(&codec->dapm);

	hs_jack = kzalloc(sizeof(struct snd_soc_jack), GFP_KERNEL);


	ret = snd_soc_jack_new(codec, hs_jack_pins->pin,
					SND_JACK_HEADPHONE, hs_jack);
	if (ret) {
		pr_err("%s: snd_soc_jack_new failed. err = %d\n", __func__, ret);
		return ret;
	}

	ret = snd_soc_jack_add_pins(hs_jack,hs_jack_pins_size,
					hs_jack_pins);
	if (ret) {
		pr_err("%s:  snd_soc_jack_add_pinsfailed. err = %d\n", __func__, ret);
		return ret;
	}
	return 0;
}


static struct snd_soc_ops imxpac_tc358743_snd_ops = {
	.hw_params	= imxpac_tc358743_hw_params,
};

static struct snd_soc_dai_link imxpac_tc358743_dai = {
	.name		= "tc358743",
	.stream_name	= "TC358743",
	.codec_dai_name	= "tc358743-hifi",
	.platform_name	= "imx-pcm-audio.2",
	.codec_name	= "tc358743_mipi.1-000f",
	.cpu_dai_name	= "imx-ssi.2",
	.init		= imx_3stack_tc358743_init,
	.ops		= &imxpac_tc358743_snd_ops,
};

static struct snd_soc_card imxpac_tc358743 = {
	.name		= "cpuimx-audio_hdmi_in",
	.dai_link	= &imxpac_tc358743_dai,
	.num_links	= 1,
};

static struct platform_device *imxpac_tc358743_snd_device;
static struct platform_device *imxpac_tc358743_snd_device;

static int imx_audmux_config(int slave, int master)
{
	unsigned int ptcr, pdcr;
	slave = slave - 1;
	master = master - 1;

	/* SSI0 mastered by port 5 */
	ptcr = MXC_AUDMUX_V2_PTCR_SYN |
		MXC_AUDMUX_V2_PTCR_TFSDIR |
		MXC_AUDMUX_V2_PTCR_TFSEL(master | 0x8) |
		MXC_AUDMUX_V2_PTCR_TCLKDIR |
	MXC_AUDMUX_V2_PTCR_RFSDIR |
	MXC_AUDMUX_V2_PTCR_RFSEL(master | 0x8) |
	MXC_AUDMUX_V2_PTCR_RCLKDIR |
	MXC_AUDMUX_V2_PTCR_RCSEL(master | 0x8) |
		MXC_AUDMUX_V2_PTCR_TCSEL(master | 0x8);
	pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master);
	mxc_audmux_v2_configure_port(slave, ptcr, pdcr);

	ptcr = MXC_AUDMUX_V2_PTCR_SYN;
	pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master);
	mxc_audmux_v2_configure_port(master, ptcr, pdcr);
	return 0;
}

static int __devinit imx_tc358743_probe(struct platform_device *pdev)
{
	struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
	int ret = 0;


	imx_audmux_config(plat->src_port, plat->ext_port);

	ret = -EINVAL;
	if (plat->init && plat->init())
		return ret;

	printk("%s %d %s\n",__func__,__LINE__,pdev->name);
	return 0;
}

static int imx_tc358743_remove(struct platform_device *pdev)
{
	struct mxc_audio_platform_data *plat = pdev->dev.platform_data;

	if (plat->finit)
		plat->finit();

	return 0;
}

static struct platform_driver imx_tc358743_audio1_driver = {
	.probe = imx_tc358743_probe,
	.remove = imx_tc358743_remove,
	.driver = {
		   .name = "imx-tc358743",
		   },
};


/* Codec setup */
static int tc358743_codec_probe(struct snd_soc_codec *codec)
{
	return 0;
}

static int tc358743_codec_remove(struct snd_soc_codec *codec)
{
	return 0;
}

static int tc358743_codec_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
//	tc358743_set_bias_level(codec, SND_SOC_BIAS_OFF);
	return 0;
}

static int tc358743_codec_resume(struct snd_soc_codec *codec)
{
//	tc358743_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
	return 0;
}

static int tc358743_set_bias_level(struct snd_soc_codec *codec,
				enum snd_soc_bias_level level)
{
	return 0;
}

static const u8 tc358743_reg[0] = {
};

static struct snd_soc_codec_driver soc_codec_dev_tc358743 = {
	.set_bias_level = tc358743_set_bias_level,
	.reg_cache_size = ARRAY_SIZE(tc358743_reg),
	.reg_word_size = sizeof(u8),
	.reg_cache_default = tc358743_reg,
	.probe = tc358743_codec_probe,
	.remove = tc358743_codec_remove,
	.suspend = tc358743_codec_suspend,
	.resume = tc358743_codec_resume,
};

#define AIC3X_RATES	SNDRV_PCM_RATE_8000_96000
#define AIC3X_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
			SNDRV_PCM_FMTBIT_S24_LE)

static int tc358743_hw_params(struct snd_pcm_substream *substream,
			   struct snd_pcm_hw_params *params,
			   struct snd_soc_dai *dai)
{
	return 0;
}

static int tc358743_mute(struct snd_soc_dai *dai, int mute)
{
	return 0;
}

static int tc358743_set_dai_sysclk(struct snd_soc_dai *codec_dai,
				int clk_id, unsigned int freq, int dir)
{
	return 0;
}

static int tc358743_set_dai_fmt(struct snd_soc_dai *codec_dai,
				unsigned int fmt)
{
	return 0;
}

static struct snd_soc_dai_ops tc358743_dai_ops = {
	.hw_params	= tc358743_hw_params,
	.digital_mute	= tc358743_mute,
	.set_sysclk	= tc358743_set_dai_sysclk,
	.set_fmt	= tc358743_set_dai_fmt,
};

static struct snd_soc_dai_driver tc358743_dai = {
	.name = "tc358743-hifi",
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = AIC3X_RATES,
		.formats = AIC3X_FORMATS,},
	.ops = &tc358743_dai_ops,
	.symmetric_rates = 1,
};

#endif

static char tc358743_mode_list[16][12] =
{
	"None",
	"VGA",
	"240p/480i",
	"288p/576i",
	"W240p/480i",
	"W288p/576i",
	"480p",
	"576p",
	"W480p",
	"W576p",
	"WW480p",
	"WW576p",
	"720p",
	"1035i",
	"1080i",
	"1080p"
};

static char tc358743_fps_list[tc358743_max_fps+1] =
{
[tc358743_60_fps] = 60,
[tc358743_30_fps] = 30,
[tc358743_max_fps] = 0
};

static int tc358743_audio_list[16] =
{
	44100,
	0,
	48000,
	32000,
	22050,
	384000,
	24000,
	352800,
	88200,
	768000,
	96000,
	705600,
	176400,
	0,
	192000,
	0
};

static char str_on[80];
static void report_netlink(void)
{
	char *envp[2];
	envp[0] = &str_on[0];
	envp[1] = NULL;
	sprintf(envp[0], "HDMI RX: %d (%s) %d %d", (unsigned char)hdmi_mode & 0xf, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], tc358743_fps_list[fps], tc358743_audio_list[audio]);
	kobject_uevent_env(&(tc358743_data.i2c_client->dev.kobj), KOBJ_CHANGE, envp);
	det_work_timeout = DET_WORK_TIMEOUT_DEFAULT;
	pr_debug("%s: HDMI RX (%d) mode: %s fps: %d (%d, %d) audio: %d\n", __func__, (unsigned char)hdmi_mode, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout, tc358743_audio_list[audio]);
}

static void det_worker(struct work_struct *work)
{
	u32 u32val;
	u16 reg;
	int ret;

	mutex_lock(&access_lock);
	if (!det_work_disable) {
		reg = 0x8621;
		ret = tc358743_read_reg(reg, &u32val);
		if (ret > 0) {
			if (audio != (((unsigned char)u32val) & 0x0f)) {
				audio = ((unsigned char)u32val) & 0x0f;
				report_netlink();
			}
		}
		reg = 0x852f;
		ret = tc358743_read_reg(reg, &u32val);
		if (ret > 0) {
			while (1) {
				if (u32val & TC3587430_HDMI_DETECT) {
					lock = u32val & TC3587430_HDMI_DETECT;
					reg = 0x8521;
					ret = tc358743_read_reg(reg, &u32val);
					if (ret < 0) {
						pr_err("%s: Error reading mode\n", __func__);
					}
				} else {
					if (lock) {		// check if it is realy un-plug
						lock = 0;
						u32val = 0x0;
						hdmi_mode = 0xF0;	// fake mode to detect un-plug if mode was not detected before.
					}
				}
				if ((unsigned char)hdmi_mode != (unsigned char)u32val) {
					if (u32val)
						det_work_timeout = DET_WORK_TIMEOUT_DEFERRED;
					else
						det_work_timeout = DET_WORK_TIMEOUT_DEFAULT;
					bounce = MAX_BOUNCE;
					pr_debug("%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", __func__, (unsigned char)hdmi_mode, (unsigned char)u32val, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout);
					hdmi_mode = u32val;
				} else if (bounce) {
					bounce--;
					det_work_timeout = DET_WORK_TIMEOUT_DEFAULT;
				}

				if (1 == bounce) {
					if (hdmi_mode >= 0xe) {
						reg = 0x852f;
						ret = tc358743_read_reg(reg, &u32val);
						if (ret > 0)
							fps = ((((unsigned char)u32val) & 0x0f) > 0xa)? tc358743_60_fps: tc358743_30_fps;
					}
					reg = 0x8621;
					ret = tc358743_read_reg(reg, &u32val);
					if (ret > 0) {
						audio = ((unsigned char)u32val) & 0x0f;
						report_netlink();
					}
				}
				break;
			}
		} else {
			pr_err("%s: Error reading lock\n", __func__);
		}
	} else {
		det_work_timeout = DET_WORK_TIMEOUT_DEFERRED;
	}
	mutex_unlock(&access_lock);
	schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout));
}

static irqreturn_t tc358743_detect_handler(int irq, void *data)
{

	pr_debug("%s: IRQ %d\n", __func__, tc358743_data.i2c_client->irq);
	schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout));
	return IRQ_HANDLED;
}


/*!
 * tc358743 I2C probe function
 *
 * @param adapter	    struct i2c_adapter *
 * @return  Error code indicating success or failure
 */
#define DUMP_LENGTH 256
static	u16 regoffs = 0;

static ssize_t tc358743_show_regdump(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	int i, len = 0;
	int retval;
	u32 u32val;

	mutex_lock(&access_lock);
	for (i=0; i<DUMP_LENGTH; ) {
		retval = tc358743_read_reg(regoffs+i, &u32val);
		if (retval < 0) {
			u32val =0xff;
			retval = 1;
		}
		while (retval-- > 0) {
			if (0 == (i & 0xf))
				len += sprintf(buf+len, "\n%04X:", regoffs+i);
			len += sprintf(buf+len, " %02X", u32val&0xff);
			u32val >>= 8;
			i++;
		}
	}
	mutex_unlock(&access_lock);
	len += sprintf(buf+len, "\n");
	return len;
}

static DEVICE_ATTR(regdump, S_IRUGO, tc358743_show_regdump, NULL);

static ssize_t tc358743_store_regoffs(struct device *device,
				struct device_attribute *attr,
				const char *buf, size_t count)
{
	u32 val;
	int retval;
	retval = sscanf(buf, "%x", &val);
	if (1 == retval)
		regoffs = (u16)val;
	return count;
}

static ssize_t tc358743_show_regoffs(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	int len = 0;

	len += sprintf(buf+len, "0x%04X\n", regoffs);
	return len;
}

static DEVICE_ATTR(regoffs, S_IRUGO|S_IWUSR, tc358743_show_regoffs, tc358743_store_regoffs);

static ssize_t tc358743_store_hpd(struct device *device,
				struct device_attribute *attr,
				const char *buf, size_t count)
{
	u32 val;
	int retval;
	retval = sscanf(buf, "%d", &val);
	if (1 == retval)
		hpd_active = (u16)val;
	return count;
}

static ssize_t tc358743_show_hpd(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	int len = 0;

	len += sprintf(buf+len, "%d\n", hpd_active);
	return len;
}

static DEVICE_ATTR(hpd, S_IRUGO|S_IWUSR, tc358743_show_hpd, tc358743_store_hpd);

static ssize_t tc358743_show_hdmirx(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	int len = 0;

	len += sprintf(buf+len, "%d\n", hdmi_mode);
	return len;
}

static DEVICE_ATTR(hdmirx, S_IRUGO, tc358743_show_hdmirx, NULL);

static ssize_t tc358743_show_fps(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	int len = 0;

	len += sprintf(buf+len, "%d\n", tc358743_fps_list[fps]);
	return len;
}

static DEVICE_ATTR(fps, S_IRUGO, tc358743_show_fps, NULL);

#ifdef AUDIO_ENABLE
static ssize_t tc358743_show_audio(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	int len = 0;

	len += sprintf(buf+len, "%d\n", tc358743_audio_list[audio]);
	return len;
}

static DEVICE_ATTR(audio, S_IRUGO, tc358743_show_audio, NULL);
#endif

static int tc358743_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct pwm_device *pwm;
	struct device *dev = &client->dev;
	int retval;
	struct regmap *gpr;
	struct sensor_data *sensor = &tc358743_data;
	u32 u32val;


	/* request power down pin */
	pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
	if (!gpio_is_valid(pwn_gpio)) {
		dev_warn(dev, "no sensor pwdn pin available");
	} else {
		retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
					"tc_mipi_pwdn");
		if (retval < 0) {
			dev_warn(dev, "request of pwn_gpio failed");
			return retval;
		}
	}
	/* request reset pin */
	rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
	if (!gpio_is_valid(rst_gpio)) {
		dev_warn(dev, "no sensor reset pin available");
		return -EINVAL;
	}
	retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
					"tc_mipi_reset");
	if (retval < 0) {
		dev_warn(dev, "request of tc_mipi_reset failed");
		return retval;
	}

	/* Set initial values for the sensor struct. */
	memset(sensor, 0, sizeof(*sensor));

	sensor->sensor_clk = devm_clk_get(dev, "csi_mclk");
	if (IS_ERR(sensor->sensor_clk)) {
		/* assuming clock enabled by default */
		sensor->sensor_clk = NULL;
		dev_err(dev, "clock-frequency missing or invalid\n");
		return PTR_ERR(sensor->sensor_clk);
	}

	retval = of_property_read_u32(dev->of_node, "mclk",
					&(sensor->mclk));
	if (retval) {
		dev_err(dev, "mclk missing or invalid\n");
		return retval;
	}

	retval = of_property_read_u32(dev->of_node, "mclk_source",
					(u32 *) &(sensor->mclk_source));
	if (retval) {
		dev_err(dev, "mclk_source missing or invalid\n");
		return retval;
	}

	retval = of_property_read_u32(dev->of_node, "ipu_id",
					&sensor->ipu_id);
	if (retval) {
		dev_err(dev, "ipu_id missing or invalid\n");
		return retval;
	}

	retval = of_property_read_u32(dev->of_node, "csi_id",
					&(sensor->csi));
	if (retval) {
		dev_err(dev, "csi id missing or invalid\n");
		return retval;
	}
	if (((unsigned)sensor->ipu_id > 1) || ((unsigned)sensor->csi > 1)) {
		dev_err(dev, "invalid ipu/csi\n");
		return -EINVAL;
	}

	clk_prepare_enable(sensor->sensor_clk);

	sensor->io_init = tc_reset;
	sensor->i2c_client = client;
	sensor->pix.pixelformat = tc358743_formats[0].pixelformat;
	sensor->streamcap.capability = V4L2_MODE_HIGHQUALITY |
					   V4L2_CAP_TIMEPERFRAME;
	sensor->streamcap.capturemode = 0;
	sensor->streamcap.extendedmode = tc358743_mode_1080P_1920_1080;
	sensor->streamcap.timeperframe.denominator = DEFAULT_FPS;
	sensor->streamcap.timeperframe.numerator = 1;

	sensor->pix.width = tc358743_mode_info_data[0][sensor->streamcap.capturemode].width;
	sensor->pix.height = tc358743_mode_info_data[0][sensor->streamcap.capturemode].height;
	pr_debug("%s: format: %x, capture mode: %d extended mode: %d fps: %d width: %d height: %d\n",__func__,
	sensor->pix.pixelformat,
	sensor->streamcap.capturemode, sensor->streamcap.extendedmode,
	sensor->streamcap.timeperframe.denominator *
	sensor->streamcap.timeperframe.numerator,
	sensor->pix.width,
	sensor->pix.height);

	pwm = pwm_get(dev, NULL);
	if (!IS_ERR(pwm)) {
		dev_info(dev, "found pwm%d, period=%d\n", pwm->pwm, pwm->period);
		pwm_config(pwm, pwm->period >> 1, pwm->period);
		pwm_enable(pwm);
	}

	tc_power_on(dev);
	tc_reset();
	tc_standby(0);

	retval = tc358743_read_reg(TC358743_CHIP_ID_HIGH_BYTE, &u32val);
	if (retval < 0) {
		pr_err("%s:cannot find camera\n", __func__);
		retval = -ENODEV;
		goto err4;
	}

	gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
	if (!IS_ERR(gpr)) {
		if (of_machine_is_compatible("fsl,imx6q")) {
			if (sensor->csi == sensor->ipu_id) {
				int mask = sensor->csi ? (1 << 20) : (1 << 19);

				regmap_update_bits(gpr, IOMUXC_GPR1, mask, 0);
			}
		} else if (of_machine_is_compatible("fsl,imx6dl")) {
			int mask = sensor->csi ? (7 << 3) : (7 << 0);
			int val =  sensor->csi ? (3 << 3) : (0 << 0);

			if (sensor->ipu_id) {
				dev_err(dev, "invalid ipu\n");
				return -EINVAL;
			}
			regmap_update_bits(gpr, IOMUXC_GPR13, mask, val);
		}
	} else {
		pr_err("%s: failed to find fsl,imx6q-iomux-gpr regmap\n",
		       __func__);
	}

	tc358743_int_device.priv = sensor;

	//retval = device_create_file(&client->dev, &dev_attr_audio);
	retval = device_create_file(&client->dev, &dev_attr_fps);
	retval = device_create_file(&client->dev, &dev_attr_hdmirx);
	retval = device_create_file(&client->dev, &dev_attr_hpd);
	retval = device_create_file(&client->dev, &dev_attr_regoffs);
	retval = device_create_file(&client->dev, &dev_attr_regdump);

	if (retval) {
		pr_err("%s:  create bin file failed, error=%d\n",
			__func__, retval);
		goto err4;
	}

#ifdef AUDIO_ENABLE
/* Audio setup */
	retval = snd_soc_register_codec(&client->dev,
			&soc_codec_dev_tc358743, &tc358743_dai, 1);
	if (retval) {
		pr_err("%s:  register failed, error=%d\n",
			__func__, retval);
		goto err4;
	}

	retval = platform_driver_register(&imx_tc358743_audio1_driver);
	if (retval) {
		pr_err("%s: Platform driver register failed, error=%d\n",
			__func__, retval);
		goto err4;
	}

	imxpac_tc358743_snd_device = platform_device_alloc("soc-audio", 5);
	if (!imxpac_tc358743_snd_device) {
		pr_err("%s: Platform device allocation failed, error=%d\n",
			__func__, retval);
		goto err4;
	}

	platform_set_drvdata(imxpac_tc358743_snd_device, &imxpac_tc358743);
	retval = platform_device_add(imxpac_tc358743_snd_device);

	if (retval) {
		pr_err("%s: Platform device add failed, error=%d\n",
			__func__, retval);
		platform_device_put(imxpac_tc358743_snd_device);
		goto err4;
	}
#endif

#if 1
	INIT_DELAYED_WORK(&(det_work), det_worker);
	if (sensor->i2c_client->irq) {
		retval = request_irq(sensor->i2c_client->irq, tc358743_detect_handler,
				IRQF_SHARED | IRQF_TRIGGER_FALLING,
				"tc358743_det", sensor);
		if (retval < 0)
			dev_warn(&sensor->i2c_client->dev,
				"cound not request det irq %d\n",
				sensor->i2c_client->irq);
	}

	schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout));
#endif
	retval = tc358743_reset(sensor);

	tc_standby(1);
	retval = v4l2_int_device_register(&tc358743_int_device);
	if (retval) {
		pr_err("%s:  v4l2_int_device_register failed, error=%d\n",
			__func__, retval);
		goto err4;
	}
	pr_debug("%s: finished, error=%d\n",
			__func__, retval);
	return retval;

err4:
	pr_err("%s: failed, error=%d\n",
			__func__, retval);
	return retval;
}

/*!
 * tc358743 I2C detach function
 *
 * @param client	    struct i2c_client *
 * @return  Error code indicating success or failure
 */
static int tc358743_remove(struct i2c_client *client)
{
	// Stop delayed work
	cancel_delayed_work_sync(&(det_work));

	// Remove IRQ
	if (tc358743_data.i2c_client->irq) {
		free_irq(tc358743_data.i2c_client->irq,  &tc358743_data);
	}

	/*Remove sysfs entries*/
	device_remove_file(&client->dev, &dev_attr_fps);
	device_remove_file(&client->dev, &dev_attr_hdmirx);
	device_remove_file(&client->dev, &dev_attr_hpd);
	device_remove_file(&client->dev, &dev_attr_regoffs);
	device_remove_file(&client->dev, &dev_attr_regdump);

	v4l2_int_device_unregister(&tc358743_int_device);

	if (gpo_regulator) {
		regulator_disable(gpo_regulator);
		regulator_put(gpo_regulator);
	}

	if (analog_regulator) {
		regulator_disable(analog_regulator);
		regulator_put(analog_regulator);
	}

	if (core_regulator) {
		regulator_disable(core_regulator);
		regulator_put(core_regulator);
	}

	if (io_regulator) {
		regulator_disable(io_regulator);
		regulator_put(io_regulator);
	}

	return 0;
}

/*!
 * tc358743 init function
 * Called by insmod tc358743_camera.ko.
 *
 * @return  Error code indicating success or failure
 */
static __init int tc358743_init(void)
{
	int err;

	err = i2c_add_driver(&tc358743_i2c_driver);
	if (err != 0)
		pr_err("%s:driver registration failed, error=%d\n",
			__func__, err);

	return err;
}

/*!
 * tc358743 cleanup function
 * Called on rmmod tc358743_camera.ko
 *
 * @return  Error code indicating success or failure
 */
static void __exit tc358743_clean(void)
{
	i2c_del_driver(&tc358743_i2c_driver);
}

module_init(tc358743_init);
module_exit(tc358743_clean);

MODULE_AUTHOR("Panasonic Avionics Corp.");
MODULE_DESCRIPTION("Toshiba TC358743 HDMI-to-CSI2 Bridge MIPI Input Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");
MODULE_ALIAS("CSI");
