// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 */
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>

#include <linux/list.h>

#include <linux/regmap.h>

#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <video/videomode.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
#include <drm/display/drm_dp_helper.h>
#include <linux/stdarg.h>
#else
#include <drm/drm_dp_helper.h>
#include <stdarg.h>
#endif

#include <linux/debugfs.h>
#include <linux/uaccess.h>
//#include <linux/workqueue.h>

#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/irq.h>

#include "mtk_tv_drm_edp.h"
#include "mtk_drm_dp_reg.h"

#include <linux/ioport.h>

#include "hwreg_render_video_pnl.h"

#define MTK_DP_AUX_WAIT_REPLY_COUNT		20
#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT	3

#define MTK_DP_MAX_PORTS			4
#define MTK_DP_PORT_NUM_CASE_1			1
#define MTK_DP_PORT_NUM_CASE_2			2
#define MTK_DP_PORT_NUM_CASE_4			4
#define MTK_DP_MAX_LANES			4
#define MTK_DP_MAX_LINK_RATE			MTK_DP_LINKRATE_HBR3

#define MTK_DP_TBC_BUF_READ_START_ADDR		0x08

#define MTK_DP_TRAIN_RETRY_LIMIT		8
#define MTK_DP_TRAIN_MAX_ITERATIONS		5

#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US	20

#define MTK_DP_BANK_NUM    6

#define MTK_EDP_SLEEP_TIME_10  10
#define MTK_EDP_SLEEP_TIME_100 100
#define MTK_EDP_SLEEP_TIME_1000 1000
#define MTK_EDP_DELAY_MULTIPLIER_2 2
#define MTK_EDP_DELAY_MULTIPLIER_5 5

enum mtk_edp_state {
	MTK_DP_STATE_INITIAL,
	MTK_DP_STATE_IDLE,
	MTK_DP_STATE_PREPARE,
	MTK_DP_STATE_NORMAL,
};

enum mtk_edp_train_state {
	MTK_DP_TRAIN_STATE_STARTUP = 0,
	MTK_DP_TRAIN_STATE_CHECKCAP,
	MTK_DP_TRAIN_STATE_CHECKEDID,
	MTK_DP_TRAIN_STATE_TRAINING_PRE,
	MTK_DP_TRAIN_STATE_TRAINING,
	MTK_DP_TRAIN_STATE_CHECKTIMING,
	MTK_DP_TRAIN_STATE_NORMAL,
	MTK_DP_TRAIN_STATE_POWERSAVE,
	MTK_DP_TRAIN_STATE_DPIDLE,
};

struct mtk_edp_train_info {
	bool tps3;
	bool tps4;
	bool sink_ssc;
	bool cable_plugged_in;
	bool cable_state_change;
	bool cr_done;
	bool eq_done;

	// link_rate is in multiple of 0.27Gbps
	int link_rate;
	int lane_count;

	int irq_status;
	int check_cap_count;
};

struct reg_segment {
	struct regmap *regs;
	unsigned int base_offset;
	unsigned int size;
};

struct mtk_edp {
	struct list_head list;
	int port_id;

	struct device *dev;

	struct drm_device *drm_dev;
	struct drm_bridge bridge;
	struct drm_bridge *next_bridge;
	struct drm_dp_aux aux;

	struct mutex edid_lock;
	struct edid *edid;

	u8 rx_cap[DP_RECEIVER_CAP_SIZE];

	struct mtk_edp_info info;
	enum mtk_edp_state state;

	struct mtk_edp_train_info train_info;
	enum mtk_edp_train_state train_state;
	unsigned int input_fmt;

	struct reg_segment segments[MTK_DP_BANK_NUM];
	struct clk_bulk_data *clks;
	int num_clks;

	bool powered;
	bool enabled;

	bool color_depth_10bit;

	struct mutex dp_lock;
};

struct mtk_edp *g_mtk_edp;
struct mtk_edp *mtk_edp_array[MTK_DP_MAX_PORTS];
static int port_num;
static int port_num_registered;
static LIST_HEAD(edp_list);

#define REG_BITS_WIDTH 32
#define VAL_BITS_WIDTH 32
#define REG_STRIDE     4
#define REG_BANK_SIZE  0x200

static struct regmap_config mtk_edp_regmap_bank_config = {
	.reg_bits     = REG_BITS_WIDTH,
	.val_bits     = VAL_BITS_WIDTH,
	.reg_stride   = REG_STRIDE,
	.max_register = REG_BANK_SIZE - 1,
	//.name        = "mtk-edp-registers-bankbase",
};

static struct regmap *offset_to_regmap(struct mtk_edp *mtk_edp,
		unsigned int offset, unsigned int *segment_offset)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(mtk_edp->segments); i++) {
		if (offset >= mtk_edp->segments[i].base_offset &&
			offset < mtk_edp->segments[i].base_offset +
					 mtk_edp->segments[i].size) {
			*segment_offset = offset - mtk_edp->segments[i].base_offset;
			return mtk_edp->segments[i].regs;
		}
	}

	// for subsys regmap
	for (i = 0; i < ARRAY_SIZE(g_mtk_edp->segments); i++) {
		if (offset >= g_mtk_edp->segments[i].base_offset &&
			offset < g_mtk_edp->segments[i].base_offset +
					 g_mtk_edp->segments[i].size) {
			*segment_offset = offset - g_mtk_edp->segments[i].base_offset;
			return g_mtk_edp->segments[i].regs;
		}
	}

	return NULL;
}

static u32 mtk_edp_read(struct mtk_edp *mtk_edp, u32 offset)
{
	u32 read_val = 0;
	int ret;
	unsigned int segment_offset = 0;
	struct regmap *regmap = NULL;

	regmap = offset_to_regmap(mtk_edp, offset, &segment_offset);
	if (regmap == NULL) {
		dev_err(mtk_edp->dev,
				"Failed to read register 0x%x\n",
				offset);
		return 0;
	}

	ret = regmap_read(regmap, segment_offset, &read_val);
	if (ret) {
		dev_err(mtk_edp->dev, "Failed to read register 0x%x(0x%x): %d\n",
				offset, segment_offset, ret);
		return 0;
	}

	return read_val;
}

static void mtk_edp_write(struct mtk_edp *mtk_edp, u32 offset, u32 val)
{
	int ret;
	unsigned int segment_offset = 0;
	struct regmap *regmap = NULL;

	regmap = offset_to_regmap(mtk_edp, offset, &segment_offset);
	if (regmap == NULL) {
		dev_err(mtk_edp->dev,
				"Failed to write register 0x%x with value 0x%x\n",
				offset, val);
		return;
	}

	ret = regmap_write(regmap, segment_offset, val);
	if (ret)
		dev_err(mtk_edp->dev,
				"Failed to write register 0x%x(0x%x) with value 0x%x: %d\n",
				offset, segment_offset, val, ret);
}

static void mtk_edp_update_bits(struct mtk_edp *mtk_edp, u32 offset, u32 val,
		u32 mask)
{
	int ret;
	unsigned int segment_offset = 0;
	struct regmap *regmap = NULL;

	regmap = offset_to_regmap(mtk_edp, offset, &segment_offset);
	if (regmap == NULL) {
		dev_err(mtk_edp->dev,
				"Failed to update register 0x%x with value 0x%x, mask 0x%x\n",
				offset, val, mask);
		return;
	}

	ret = regmap_update_bits(regmap, segment_offset, mask, val);
	if (ret)
		dev_err(mtk_edp->dev,
				"Failed to update register 0x%x(0x%x)"
				"with value 0x%x, mask 0x%x: %d\n",
				offset, segment_offset, val, mask, ret);
}

#define BYTES_PER_REG 2
#define LOW_BYTE_TO_HIGH_BYTE_SHIFT 8
static void mtk_edp_bulk_16bit_write(struct mtk_edp *mtk_edp,
		u32 offset, u8 *buf, size_t length)
{
	int i;
	int num_regs = (length + BYTES_PER_REG - 1) / BYTES_PER_REG;

	// 2 bytes per register
	for (i = 0; i < num_regs; i++) {
		u32 val = buf[i * BYTES_PER_REG] |
			(i * BYTES_PER_REG + 1 < length ?
			 buf[i * BYTES_PER_REG + 1] << LOW_BYTE_TO_HIGH_BYTE_SHIFT : 0);

		mtk_edp_write(mtk_edp, offset + i * REG_STRIDE, val);
	}
}

static void mtk_edp_msa_bypass_disable(struct mtk_edp *mtk_edp)
{
	const u16 bits_to_set = BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
				BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
				BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
				BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
				BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
				BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
				BIT(HSP_SEL_DP_ENC0_P0_SHIFT) |
				BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
				BIT(VSP_SEL_DP_ENC0_P0_SHIFT) |
				BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3030, bits_to_set,
			   bits_to_set);
}

static void mtk_edp_set_msa(struct mtk_edp *mtk_edp)
{
	struct mtk_edp_timings *timings = &g_mtk_edp->info.timings;

	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3010,
			    timings->htotal / port_num,
			    HTOTAL_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3018,
			    (timings->vm.hsync_len + timings->vm.hback_porch) / port_num,
			    HSTART_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3028,
			    (timings->vm.hsync_len / port_num) << HSW_SW_DP_ENC0_P0_SHIFT,
			    HSW_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3028,
			    0,
			    HSP_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3020,
			    timings->vm.hactive / port_num,
			    HWIDTH_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3014,
			    timings->vtotal / port_num,
			    VTOTAL_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_301C,
			    (timings->vm.vsync_len + timings->vm.vback_porch) / port_num,
			    VSTART_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_302C,
			    (timings->vm.vsync_len / port_num) << VSW_SW_DP_ENC0_P0_SHIFT,
			    VSW_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_302C,
			    0,
			    VSP_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3024,
			    timings->vm.vactive / port_num,
			    VHEIGHT_SW_DP_ENC0_P0_MASK);

	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
			   HDE_NUM_LAST_DP_ENC0_P0_MASK);

	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3154,
			    timings->htotal / port_num,
			    PGEN_HTOTAL_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3158,
			    timings->vm.hfront_porch / port_num,
			    PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_315C,
			    timings->vm.hsync_len / port_num,
			    PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3160,
			    (timings->vm.hback_porch + timings->vm.hsync_len) / port_num,
			    PGEN_HFDE_START_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3164,
			    timings->vm.hactive / port_num,
			    PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3168,
			    timings->vtotal / port_num,
			    PGEN_VTOTAL_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_316C,
			    timings->vm.vfront_porch / port_num,
			    PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3170,
			    timings->vm.vsync_len / port_num,
			    PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3174,
			    (timings->vm.vback_porch + timings->vm.vsync_len) / port_num,
			    PGEN_VFDE_START_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3178,
			    timings->vm.vactive / port_num,
			    PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);

	dev_info(mtk_edp->dev, "[eDPTX] %s\n", __func__);
	dev_info(mtk_edp->dev, "[eDPTX] Htt=%d Vtt=%d Ha=%d Va=%d fps=%d\n",
		 timings->htotal / port_num, timings->vtotal / port_num,
		 timings->vm.hactive / port_num, timings->vm.vactive / port_num,
		 timings->frame_rate);
}

static void mtk_edp_set_color_format(struct mtk_edp *mtk_edp,
				    enum mtk_edp_color_format color_format)
{
	u32 val;

	mtk_edp->info.format = color_format;

	dev_info(mtk_edp->dev, "[eDPTX] color format=%d [0:RGB444 1:YUV422 3:YUV420]\n",
		mtk_edp->info.format);

	// Update MISC0
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3034,
			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
			   DP_TEST_COLOR_FORMAT_MASK);

	switch (color_format) {
	case MTK_DP_COLOR_FORMAT_YUV_422:
		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
		break;
	case MTK_DP_COLOR_FORMAT_YUV_420:
		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
		break;
	case MTK_DP_COLOR_FORMAT_YONLY:
	case MTK_DP_COLOR_FORMAT_RAW:
	case MTK_DP_COLOR_FORMAT_RESERVED:
	case MTK_DP_COLOR_FORMAT_UNKNOWN:
		dev_warn(mtk_edp->dev, "Unsupported color format: %d\n",
			 color_format);
		fallthrough;
	case MTK_DP_COLOR_FORMAT_RGB_444:
	case MTK_DP_COLOR_FORMAT_YUV_444:
		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
		break;
	}

	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_303C, val,
			   PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
}

static void mtk_edp_set_color_depth(struct mtk_edp *mtk_edp,
				   enum mtk_edp_color_depth color_depth)
{
	u32 val;

	mtk_edp->info.depth = color_depth;

	//dev_info(mtk_edp->dev, "[eDPTX] color depth=%dbpp\n",
	//	(mtk_edp->info.depth * 2 + 6));
	dev_info(mtk_edp->dev, "[eDPTX] color depth=(%d*2)+6 bpc\n",
			mtk_edp->info.depth);

	// Update MISC0
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3034,
			   color_depth << DP_TEST_BIT_DEPTH_SHIFT,
			   DP_TEST_BIT_DEPTH_MASK);

	switch (color_depth) {
	case MTK_DP_COLOR_DEPTH_6BIT:
		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
		break;
	case MTK_DP_COLOR_DEPTH_8BIT:
		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
		break;
	case MTK_DP_COLOR_DEPTH_10BIT:
		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
		break;
	case MTK_DP_COLOR_DEPTH_12BIT:
		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
		break;
	case MTK_DP_COLOR_DEPTH_16BIT:
		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
		break;
	case MTK_DP_COLOR_DEPTH_UNKNOWN:
		dev_warn(mtk_edp->dev, "Unsupported color depth %d\n",
			 color_depth);
		return;
	}

	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_303C, val,
			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
}

static void mtk_edp_mn_overwrite_disable(struct mtk_edp *mtk_edp)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3004, 0,
			   VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
}

static void mtk_edp_set_sram_read_start(struct mtk_edp *mtk_edp, u32 val)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_303C,
			   val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
			   SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
}

#define MTK_EDP_SDP_DOWN_CNT_INIT_VALUE        0x20
#define MTK_EDP_SDP_DOWN_CNT_INIT_IN_HBLANK_VALUE 0x20
#define MTK_EDP_VIDEO_AFIFO_RDY_SEL_VALUE      0x2
#define MTK_EDP_FIFO_READ_START_POINT_VALUE    0x4
static void mtk_edp_setup_encoder(struct mtk_edp *mtk_edp)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_303C,
			   BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
			   VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3040,
			   MTK_EDP_SDP_DOWN_CNT_INIT_VALUE << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC1_P0_3364,
			   MTK_EDP_SDP_DOWN_CNT_INIT_IN_HBLANK_VALUE
			   << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC1_P0_3300,
			   MTK_EDP_VIDEO_AFIFO_RDY_SEL_VALUE
			   << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC1_P0_3364,
			   MTK_EDP_FIFO_READ_START_POINT_VALUE
			   << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
	mtk_edp_write(mtk_edp, MTK_DP_ENC1_P0_3368,
		     1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
			     1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
			     BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
			     1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
}

#define MTK_EDP_COLOR_BAR_PATTERN    0x4
static void mtk_edp_pg_disable(struct mtk_edp *mtk_edp)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3038, 0,
			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_31B0,
			   MTK_EDP_COLOR_BAR_PATTERN << PGEN_PATTERN_SEL_SHIFT,
			   PGEN_PATTERN_SEL_MASK);
}

static bool mtk_edp_plug_state(struct mtk_edp *mtk_edp)
{
	return !!(mtk_edp_read(mtk_edp, MTK_DP_AUX_P0_364C) &
			HPD_STATUS_AUX_TX_P0_MASK);
}

static void mtk_edp_aux_irq_clear(struct mtk_edp *mtk_edp)
{
	mtk_edp_write(mtk_edp, MTK_DP_AUX_P0_3640,
		     BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
		     BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
		     BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
		     BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
		     BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
		     BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
		     BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
}

#define MTK_EDP_REG_BIT_WIDTH      16
static void mtk_edp_aux_set_cmd(struct mtk_edp *mtk_edp, u8 cmd, u32 addr)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3644, cmd,
			   MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3648, addr,
			   MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_364C, addr >> MTK_EDP_REG_BIT_WIDTH,
			   MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
}

static void mtk_edp_aux_cmd_complete(struct mtk_edp *mtk_edp)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3650,
			   BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
				   PHY_FIFO_RST_AUX_TX_P0_MASK |
				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
}

static void mtk_edp_aux_request_ready(struct mtk_edp *mtk_edp)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3630,
			   BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
			   AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
}

static void mtk_edp_aux_fill_write_fifo(struct mtk_edp *mtk_edp, u8 *buf,
				       size_t length)
{
	mtk_edp_bulk_16bit_write(mtk_edp, MTK_DP_AUX_P0_3708, buf, length);
}

static void mtk_edp_aux_read_rx_fifo(struct mtk_edp *mtk_edp, u8 *buf,
				    size_t length, int read_delay)
{
	int read_pos;

	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3620, 0,
			   AUX_RD_MODE_AUX_TX_P0_MASK);

	for (read_pos = 0; read_pos < length; read_pos++) {
		mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3620,
				   BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
				   AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
		usleep_range(read_delay, read_delay * MTK_EDP_DELAY_MULTIPLIER_2);
		buf[read_pos] =
			(u8)(mtk_edp_read(mtk_edp, MTK_DP_AUX_P0_3620) &
			     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
	}
}

static void mtk_edp_aux_set_length(struct mtk_edp *mtk_edp, size_t length)
{
	if (length > 0) {
		mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3650,
				   (length - 1)
					   << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
		mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_362C, 0,
				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
	} else {
		mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_362C,
				   BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
	}
}

static int mtk_edp_aux_wait_for_completion(struct mtk_edp *mtk_edp, bool is_read)
{
	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;

	while (--wait_reply) {
		u32 aux_irq_status;

		if (is_read) {
			u32 fifo_status =
				mtk_edp_read(mtk_edp, MTK_DP_AUX_P0_3618);

			if (fifo_status &
			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
				return 0;
			}
		}

		aux_irq_status = mtk_edp_read(mtk_edp, MTK_DP_AUX_P0_3640);
		if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
			return 0;

		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
			return -ETIMEDOUT;

		usleep_range(MTK_EDP_SLEEP_TIME_100,
				MTK_EDP_SLEEP_TIME_100 * MTK_EDP_DELAY_MULTIPLIER_5);
	}

	return -ETIMEDOUT;
}

static int mtk_edp_aux_do_transfer(struct mtk_edp *mtk_edp, bool is_read, u8 cmd,
				  u32 addr, u8 *buf, size_t length)
{
	int ret;
	u32 reply_cmd;

	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
			(cmd == DP_AUX_NATIVE_READ && !length)))
		return -EINVAL;

	if (!is_read)
		mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3704,
				   BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);

	mtk_edp_aux_cmd_complete(mtk_edp);
	mtk_edp_aux_irq_clear(mtk_edp);
	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
			MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * MTK_EDP_DELAY_MULTIPLIER_2);

	mtk_edp_aux_set_cmd(mtk_edp, cmd, addr);
	mtk_edp_aux_set_length(mtk_edp, length);

	if (!is_read) {
		if (length)
			mtk_edp_aux_fill_write_fifo(mtk_edp, buf, length);

		mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3704,
				   AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
				   AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
	}

	mtk_edp_aux_request_ready(mtk_edp);

	ret = mtk_edp_aux_wait_for_completion(mtk_edp, is_read);

	reply_cmd = mtk_edp_read(mtk_edp, MTK_DP_AUX_P0_3624) &
		AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;

	//dev_info(mtk_edp->dev,
	//		"%s ret:%x, reply_cmd:%x", __func__, ret, reply_cmd);

	if (ret || reply_cmd) {
		u32 phy_status = mtk_edp_read(mtk_edp, MTK_DP_AUX_P0_3628) &
				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
			dev_err(mtk_edp->dev,
				"AUX Rx Aux hang, need SW reset\n");
			ret = -EIO;
		}

		mtk_edp_aux_cmd_complete(mtk_edp);
		mtk_edp_aux_irq_clear(mtk_edp);

		usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
				MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * MTK_EDP_DELAY_MULTIPLIER_2);
		dev_err(mtk_edp->dev,
			"Timeout ret:%x, reply_cmd:%x", ret, reply_cmd);
		return -ETIMEDOUT;
	}

	if (!length) {
		mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_362C, 0,
				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
	} else if (is_read) {
		int read_delay;

		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
		    cmd == DP_AUX_I2C_READ)
			read_delay = MTK_EDP_SLEEP_TIME_100 * MTK_EDP_DELAY_MULTIPLIER_5;
		else
			read_delay = MTK_EDP_SLEEP_TIME_100;

		mtk_edp_aux_read_rx_fifo(mtk_edp, buf, length, read_delay);
	}

	mtk_edp_aux_cmd_complete(mtk_edp);
	mtk_edp_aux_irq_clear(mtk_edp);
	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
			MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * MTK_EDP_DELAY_MULTIPLIER_2);

	return 0;
}

#define swing_to_mod_range(val) (((val + 1) << 2) - 1)
#define pe_to_mod_range(val) (((val + 1) << 4) - 1)
static void mtk_edp_set_swing_pre_emphasis_by_channel(int channel_id,
						      int swing_val, int preemphasis)
{
	u32 swing_val_mod;
	u32 preemphasis_mod;

	swing_val_mod = swing_to_mod_range(swing_val);
	preemphasis_mod = pe_to_mod_range(preemphasis);

	drv_hwreg_render_pnl_set_pre_emphasis_by_channel(channel_id, preemphasis_mod);
	drv_hwreg_render_pnl_set_swing_vreg_by_channel(channel_id, swing_val_mod);
}

static bool mtk_edp_set_swing_pre_emphasis(struct mtk_edp *mtk_edp, int lane_num,
					  int swing_val, int preemphasis)
{
	int channel_id = mtk_edp->port_id * MTK_DP_MAX_LANES + lane_num;

	if (lane_num < 0 || lane_num > MTK_DP_MAX_LANES-1)
		return false;

	dev_info(mtk_edp->dev, "[eDPTX] lane%d swing_val=0x%x pre_emphasis=0x%x\n",
		 lane_num, swing_val, preemphasis);

	mtk_edp_set_swing_pre_emphasis_by_channel(channel_id, swing_val, preemphasis);

	return true;
}

static void mtk_edp_reset_swing_pre_emphasis(struct mtk_edp *mtk_edp)
{
	int channel_id = mtk_edp->port_id * MTK_DP_MAX_LANES;
	int i;

	for (i = channel_id; i < channel_id + MTK_DP_MAX_LANES; i++)
		mtk_edp_set_swing_pre_emphasis_by_channel(i, 0, 0);
}

//static void mtk_edp_fec_enable(struct mtk_edp *mtk_edp, bool enable)
//{
//	dev_info(mtk_edp->dev, "[eDPTX] %s enable=%d\n", __func__, enable);
//
//	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_3540,
//			   enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
//			   FEC_EN_DP_TRANS_P0_MASK);
//}

static u32 mtk_edp_irq_get_clear(struct mtk_edp *mtk_edp)
{
	u32 irq_status = mtk_edp_read(mtk_edp, MTK_DP_AUX_P0_3608)
		& AUX_INT_STATUS_AUX_TX_P0_MASK;

	if (irq_status) {
		mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3668, irq_status,
				   AUX_INT_STATUS_AUX_TX_P0_MASK);
		mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3668, 0,
				   AUX_INT_STATUS_AUX_TX_P0_MASK);
	}

	return irq_status;
}

static void mtk_edp_hwirq_enable(struct mtk_edp *mtk_edp, bool enable)
{
	u32 val = 0;

	dev_info(mtk_edp->dev, "[eDPTX] %s enable=%d\n", __func__, enable);

	if (!enable)
		val = IRQ_MASK_DP_AUX_P0_DISC_IRQ |
			IRQ_MASK_DP_AUX_P0_CONN_IRQ |
			IRQ_MASK_DP_AUX_P0_INT_IRQ;
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3660, val,
			IRQ_MASK_DP_AUX_P0_DISC_IRQ |
			IRQ_MASK_DP_AUX_P0_CONN_IRQ |
			IRQ_MASK_DP_AUX_P0_INT_IRQ);
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3668,
			AUX_INT_STATUS_AUX_TX_P0_MASK, AUX_INT_STATUS_AUX_TX_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3668,
			0, AUX_INT_STATUS_AUX_TX_P0_MASK);
}

#define MTK_EDP_FEC_FIFO_UNDER_POINT_VALUE 2
void mtk_edp_initialize_settings(struct mtk_edp *mtk_edp)
{
	// do not have this register
	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_342C,
			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
			   XTAL_FREQ_DP_TRANS_P0_MASK);
	// default value is 1
	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_3540,
			   BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
			   FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_3540,
			   MTK_EDP_FEC_FIFO_UNDER_POINT_VALUE
			   << FEC_FIFO_UNDER_POINT_DP_TRANS_P0_SHIFT,
			   FEC_FIFO_UNDER_POINT_DP_TRANS_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_31EC,
			   BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
			   AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_304C, 0,
			   SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_TOP_IRQ_MASK, 0x0,
			RGS_IRQ_STATUS_AUXTOP_P0 |
			RGS_IRQ_STATUS_AUXTOP_P1 |
			RGS_IRQ_STATUS_AUXTOP_P2 |
			RGS_IRQ_STATUS_AUXTOP_P3);
	mtk_edp_update_bits(mtk_edp, DP_TX_TOP_IRQ_MASK,
			IRQ_OUT_HIGH_ACTIVE_FLDMASK,
			IRQ_OUT_HIGH_ACTIVE_FLDMASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_304C,
			BIT(VBID_VIDEO_MUTE_IDLE_PATTERN_SYNC_EN_DP_ENC0_P0_SHIFT),
			VBID_VIDEO_MUTE_IDLE_PATTERN_SYNC_EN_DP_ENC0_P0_MASK);
}

#define MTK_EDP_HPD_INT_THD_VALUE 0x32
static void mtk_edp_initialize_hpd_detect_settings(struct mtk_edp *mtk_edp)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_364C,
			MTK_EDP_HPD_INT_THD_VALUE << HPD_INT_THD_AUX_TX_P0_SHIFT,
			HPD_INT_THD_AUX_TX_P0_MASK);
}

#define MTK_EDP_AUX_CRYSTAL_FREQ_MHZ 12
#define MTK_EDP_AUX_TX_OVER_SAMPLE_RATE (MTK_EDP_AUX_CRYSTAL_FREQ_MHZ - 1)
#define MTK_EDP_AUX_RX_UI_CNT_THR (MTK_EDP_AUX_CRYSTAL_FREQ_MHZ / 2)
#define MTK_EDP_HPD_CONN_THD 5
#define MTK_EDP_HPD_DISC_THD 5
#define MTK_EDP_AUX_TIMEOUT_THR 0x1D0C
static void mtk_edp_initialize_aux_settings(struct mtk_edp *mtk_edp)
{
	// modify timeout threshold = 0x1D0C [12 : 8]
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_360C, MTK_EDP_AUX_TIMEOUT_THR,
			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3658, 0,
			   AUX_TX_OV_EN_AUX_TX_P0_MASK);
	//mtk_edp_write(mtk_edp, MTK_DP_AUX_P0_36A0, 0xFFFC);
	// 11 for 12M
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3634,
			   MTK_EDP_AUX_TX_OVER_SAMPLE_RATE
			   << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
	// 6 for 12M
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3614,
			   MTK_EDP_AUX_RX_UI_CNT_THR
			   << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);

	// atop enable
// mark
// CTP do not have
//	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_37C8,
//			   BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
//			   MTK_ATOP_EN_AUX_TX_P0_MASK);
// mark
//FPGA PHY setting
	mtk_edp_update_bits(mtk_edp, REG_36F4_AUX_TX_P0,
			1, IDLE_TO_PRECHARGE_DATA_ONE_EN_DP_AUX_TX_36F4_FLDMASK);

	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3690,
			   BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);


	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_3690,
			   BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
	// need add this, sync from CTP
	// //Con Thd = 1.5ms+Vx0.1ms
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_367C,
		MTK_EDP_HPD_CONN_THD << HPD_CONN_THD_AUX_TX_P0_FLDMASK_POS,
		HPD_CONN_THD_AUX_TX_P0_FLDMASK);
	//DisCon Thd = 1.5ms+Vx0.1ms
	mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_37A0,
		MTK_EDP_HPD_DISC_THD << HPD_DISC_THD_AUX_TX_P0_FLDMASK_POS,
		HPD_DISC_THD_AUX_TX_P0_FLDMASK);

}

#define MTK_EDP_SRAM_READ_START_DEFAULT_VALUE 8
#define MTK_EDP_VIDEO_SRAM_FIFO_CNT_RESET_SEL_VALUE 2
static void mtk_edp_initialize_digital_settings(struct mtk_edp *mtk_edp)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_304C, 0,
			VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);

	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC1_P0_3368,
			1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
			BS2BS_MODE_DP_ENC1_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3030,
			BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT),
			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3000,
			BIT(IDP_EN_DP_ENC0_P0_SHIFT),
			IDP_EN_DP_ENC0_P0_MASK);
	mtk_edp_msa_bypass_disable(mtk_edp);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3028, 0, HSP_SW_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_302C, 0, VSP_SW_DP_ENC0_P0_MASK);

	mtk_edp_set_color_format(mtk_edp, MTK_DP_COLOR_FORMAT_RGB_444);
	mtk_edp_set_color_depth(mtk_edp, MTK_DP_COLOR_DEPTH_8BIT);

	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3000,
			BIT(BS_SYMBOL_CNT_RESET_DP_ENC0_P0_SHIFT),
			BS_SYMBOL_CNT_RESET_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3004,
			BIT(DP_TX_MUX_DP_ENC0_P0_SHIFT),
			DP_TX_MUX_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC1_P0_3368,
			MTK_EDP_VIDEO_SRAM_FIFO_CNT_RESET_SEL_VALUE,
			VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC1_P0_3368,
			BIT(BS_FOLLOW_SEL_DP_ENC1_P0_SHIFT),
			BS_FOLLOW_SEL_DP_ENC1_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_304C, 0,
			   SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
	mtk_edp_set_sram_read_start(mtk_edp, MTK_EDP_SRAM_READ_START_DEFAULT_VALUE);

	// dp tx encoder reset all sw
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3004,
			BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
			DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
	usleep_range(MTK_EDP_SLEEP_TIME_1000,
			MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_5);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3004, 0,
			DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
}

static void mtk_edp_digital_sw_reset(struct mtk_edp *mtk_edp)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_340C,
			   BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
	usleep_range(MTK_EDP_SLEEP_TIME_1000,
			MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_5);
	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_340C, 0,
			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
}

static void mtk_edp_set_lanes(struct mtk_edp *mtk_edp, int lanes)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_35F0,
			   lanes == 0 ? 0 : LANES_DUMMY_REG_VALUE,
			   LANES_DUMMY_REG_DP_TRANS_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3000, lanes,
			   LANE_NUM_DP_ENC0_P0_MASK);
	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_34A4,
			   lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
			   LANE_NUM_DP_TRANS_P0_MASK);
}

static void mtk_edp_set_idle_pattern(struct mtk_edp *mtk_edp, bool enable)
{
	const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
			POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
			POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
			POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
}

#define MTK_EDP_TRAIN_TPSOFF 0
#define MTK_EDP_TRAIN_TPS1 1
#define MTK_EDP_TRAIN_TPS2 2
#define MTK_EDP_TRAIN_TPS3 3
#define MTK_EDP_TRAIN_TPS4 4
static void mtk_edp_train_set_pattern(struct mtk_edp *mtk_edp, int pattern)
{
	dev_info(mtk_edp->dev, "[eDPTX] %s pattern=0x%x\n", __func__, pattern);

	if (pattern < MTK_EDP_TRAIN_TPSOFF || pattern > MTK_EDP_TRAIN_TPS4) {
		dev_err(mtk_edp->dev,
			"Implementation error, no such pattern %d\n", pattern);
		return;
	}

	if (pattern == MTK_EDP_TRAIN_TPS1)
		mtk_edp_set_idle_pattern(mtk_edp, false);

	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_3400,
			   pattern ?
			   BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT :
			   0,
			   PATTERN1_EN_DP_TRANS_P0_MASK |
			   PATTERN2_EN_DP_TRANS_P0_MASK |
			   PATTERN3_EN_DP_TRANS_P0_MASK |
			   PATTERN4_EN_DP_TRANS_P0_MASK);
}

static void mtk_edp_set_enhanced_frame_mode(struct mtk_edp *mtk_edp, bool enable)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3000,
			   enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
			   ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
}

static void mtk_edp_training_set_scramble(struct mtk_edp *mtk_edp, bool enable)
{
	mtk_edp_update_bits(mtk_edp, MTK_DP_TRANS_P0_3404,
			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
			   DP_SCR_EN_DP_TRANS_P0_MASK);
}

static void mtk_edp_video_mute(struct mtk_edp *mtk_edp, bool enable)
{
	u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);

	dev_info(mtk_edp->dev, "[eDPTX] %s mute=%d\n", __func__, enable);

	if (enable)
		val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3000, val,
			   VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
				   VIDEO_MUTE_SW_DP_ENC0_P0_MASK);

	//if (mtk_edp_is_edp(mtk_edp))
	//	mtk_edp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
	//else
	//	mtk_edp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
}

//mark
static void mtk_edp_power_enable(struct mtk_edp *mtk_edp)
{
	dev_info(mtk_edp->dev, "[eDPTX] %s\n", __func__);

	mtk_edp_update_bits(mtk_edp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP_TPLL,
			DP_PWR_STATE_MASK);
// CTP mark this
	//mtk_edp_update_bits(mtk_edp, MTK_DP_TOP_RESET_AND_PROBE, 0,
	//		   SW_RST_B_PHYD);
	//usleep_range(10, 200);
	//mtk_edp_update_bits(mtk_edp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
	//		   SW_RST_B_PHYD);
	//mtk_edp_write(mtk_edp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP_TPLL);
}

static void mtk_edp_power_disable(struct mtk_edp *mtk_edp)
{
	dev_info(mtk_edp->dev, "[eDPTX] %s\n", __func__);

// CTP mark this
	mtk_edp_update_bits(mtk_edp, MTK_DP_TOP_PWR_STATE, 0x0,
			DP_PWR_STATE_MASK);
	//mtk_edp_write(mtk_edp, MTK_DP_TOP_PWR_STATE, 0);
	//usleep_range(10, 200);
}

#define MTK_EDP_DEFAULT_FRAME_RATE 60
static void mtk_edp_initialize_priv_data(struct mtk_edp *mtk_edp)
{
	// need to get from DTS
	mtk_edp->train_info.link_rate = mtk_edp->info.link_rate;
	mtk_edp->train_info.lane_count = MTK_DP_MAX_LANES;
	mtk_edp->train_info.irq_status = 0;
	mtk_edp->train_info.cable_plugged_in = false;
	mtk_edp->train_info.cable_state_change = false;
	mtk_edp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
	mtk_edp->state = MTK_DP_STATE_INITIAL;

	// need to get from DTS
	//mtk_edp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
	//if (mtk_edp->color_depth_10bit)
	//	mtk_edp->info.depth = MTK_DP_COLOR_DEPTH_10BIT;
	//else
	//	mtk_edp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
	//memset(&mtk_edp->info.timings, 0, sizeof(struct mtk_edp_timings));
	//mtk_edp->info.timings.frame_rate = MTK_EDP_DEFAULT_FRAME_RATE;

	//mtk_edp->has_fec = false;
}

//static void mtk_edp_sdp_set_down_cnt_init(struct mtk_edp *mtk_edp,
//					 u32 sram_read_start)
//{
//	u32 sdp_down_cnt_init = 0;
//	u32 dc_offset;
//
//	if (mtk_edp->info.timings.pix_rate_khz > 0)
//		sdp_down_cnt_init = sram_read_start *
//				    mtk_edp->train_info.link_rate * 2700 * 8 /
//				    (mtk_edp->info.timings.pix_rate_khz * 4);
//
//	switch (mtk_edp->train_info.lane_count) {
//	case 1:
//		sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
//			sdp_down_cnt_init : 0x1A;  //26
//		break;
//	case 2:
//		// case for LowResolution && High Audio Sample Rate
//		dc_offset = mtk_edp->info.timings.vtotal <= 525 ?
//			0x04 : 0x00;
//		sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
//			sdp_down_cnt_init : 0x10 + dc_offset; //20 or 16
//		break;
//	case 4:
//	default:
//		sdp_down_cnt_init = sdp_down_cnt_init > 0x06 ?
//			sdp_down_cnt_init : 0x06; //6
//		break;
//	}
//
//	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC0_P0_3040,
//			   sdp_down_cnt_init
//				   << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
//			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
//}

//static void mtk_edp_sdp_set_down_cnt_init_in_hblank(struct mtk_edp *mtk_edp)
//{
//	int pix_clk_mhz;
//	u32 dc_offset;
//	u32 spd_down_cnt_init = 0;
//
//	pix_clk_mhz = mtk_edp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
//				    mtk_edp->info.timings.pix_rate_khz / 2000 :
//				    mtk_edp->info.timings.pix_rate_khz / 1000;
//
//	switch (mtk_edp->train_info.lane_count) {
//	case 1:
//		spd_down_cnt_init = 0x20;
//		break;
//	case 2:
//		dc_offset = (mtk_edp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
//		spd_down_cnt_init = 0x18 + dc_offset;
//		break;
//	case 4:
//	default:
//		dc_offset = (mtk_edp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
//		if (pix_clk_mhz > mtk_edp->train_info.link_rate * 27)
//			spd_down_cnt_init = 0x8;
//		else
//			spd_down_cnt_init = 0x10 + dc_offset;
//		break;
//	}
//	mtk_edp_update_bits(mtk_edp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
//			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
//}

#define MTK_EDP_CALC_SRAM_ADDR_PARAM (4 * 2 * 2)
static void mtk_edp_setup_tu(struct mtk_edp *mtk_edp)
{
	u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;

	if (mtk_edp->train_info.lane_count > 0) {
		sram_read_start = min_t(u32, MTK_DP_TBC_BUF_READ_START_ADDR,
			mtk_edp->info.timings.vm.hactive /
			(mtk_edp->train_info.lane_count * MTK_EDP_CALC_SRAM_ADDR_PARAM));
		mtk_edp_set_sram_read_start(mtk_edp, sram_read_start);
	}

	mtk_edp_setup_encoder(mtk_edp);
	//mtk_edp_sdp_set_down_cnt_init_in_hblank(mtk_edp);
	//mtk_edp_sdp_set_down_cnt_init(mtk_edp, sram_read_start);
}

//static void mtk_edp_calculate_pixrate(struct mtk_edp *mtk_edp)
//{
//	int target_frame_rate = 60;
//	int target_pixel_clk;
//
//	if (mtk_edp->info.timings.frame_rate > 0) {
//		target_frame_rate = mtk_edp->info.timings.frame_rate;
//		target_pixel_clk = (int)mtk_edp->info.timings.htotal *
//				   (int)mtk_edp->info.timings.vtotal *
//				   target_frame_rate;
//	} else if (mtk_edp->info.timings.pix_rate_khz > 0) {
//		target_pixel_clk = mtk_edp->info.timings.pix_rate_khz * 1000;
//	} else {
//		target_pixel_clk = (int)mtk_edp->info.timings.htotal *
//				   (int)mtk_edp->info.timings.vtotal *
//				   target_frame_rate;
//	}
//
//	if (target_pixel_clk > 0)
//		mtk_edp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
//}

static void mtk_edp_set_tx_out(struct mtk_edp *mtk_edp)
{
	mtk_edp_msa_bypass_disable(mtk_edp);
	//mtk_edp_calculate_pixrate(mtk_edp);
	mtk_edp_pg_disable(mtk_edp);
	mtk_edp_setup_tu(mtk_edp);
}

static void mtk_edp_edid_free(struct mtk_edp *mtk_edp)
{
	mutex_lock(&mtk_edp->edid_lock);
	kfree(mtk_edp->edid);
	mtk_edp->edid = NULL;
	mutex_unlock(&mtk_edp->edid_lock);
}

static void mtk_edp_hpd_sink_event(struct mtk_edp *mtk_edp)
{
	ssize_t ret;
	u8 sink_count;
	u8 link_status[DP_LINK_STATUS_SIZE] = {};
	u32 sink_count_reg;
	u32 link_status_reg;
	bool locked;

	sink_count_reg = DP_SINK_COUNT_ESI;
	link_status_reg = DP_LANE0_1_STATUS;

	ret = drm_dp_dpcd_readb(&mtk_edp->aux, sink_count_reg, &sink_count);
	if (ret < 0) {
		dev_info(mtk_edp->dev,
			 "Read sink count failed: %zd\n", ret);
		return;
	}

	ret = drm_dp_dpcd_readb(&mtk_edp->aux, DP_SINK_COUNT, &sink_count);
	if (ret < 0) {
		dev_info(mtk_edp->dev,
			 "Read DP_SINK_COUNT_ESI failed: %zd\n", ret);
		return;
	}

	ret = drm_dp_dpcd_read(&mtk_edp->aux, link_status_reg, link_status,
			       sizeof(link_status));
	if (!ret) {
		dev_info(mtk_edp->dev, "Read link status failed: %zd\n",
			 ret);
		return;
	}

	locked = drm_dp_channel_eq_ok(link_status,
				      mtk_edp->train_info.lane_count);
	if (!locked && mtk_edp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;

	if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
		drm_dp_dpcd_writeb(&mtk_edp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
				   DP_REMOTE_CONTROL_COMMAND_PENDING);

	if (DP_GET_SINK_COUNT(sink_count) &&
	    (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS]
		 & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
		mtk_edp_edid_free(mtk_edp);
		mtk_edp->train_info.check_cap_count = 0;
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
		msleep(MTK_EDP_SLEEP_TIME_10 * MTK_EDP_DELAY_MULTIPLIER_2);
	}
}

#define MTK_EDP_TRAIN_PRE_EMPH_LEVEL_3 3
#define MTK_EDP_LANES_PER_BYTE 2
#define MTK_EDP_LANE_ADJUST_SIZE 2
static void mtk_edp_train_update_swing_pre(struct mtk_edp *mtk_edp, int lanes,
					  u8 dpcd_adjust_req[MTK_EDP_LANE_ADJUST_SIZE])
{
	int lane;

	for (lane = 0; lane < lanes; ++lane) {
		u8 val;
		u8 swing;
		u8 preemphasis;
		int index = lane / MTK_EDP_LANES_PER_BYTE;
		int shift = lane % MTK_EDP_LANES_PER_BYTE ?
			DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;

		swing = (dpcd_adjust_req[index] >> shift) &
			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
		preemphasis = ((dpcd_adjust_req[index] >> shift) &
			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;

		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
			val |= DP_TRAIN_MAX_SWING_REACHED;
		if (preemphasis == MTK_EDP_TRAIN_PRE_EMPH_LEVEL_3)
			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;

		mtk_edp_set_swing_pre_emphasis(mtk_edp, lane, swing, preemphasis);
		drm_dp_dpcd_writeb(&mtk_edp->aux, DP_TRAINING_LANE0_SET + lane,
				   val);
	}

	// Wait for the signal to be stable enough
	usleep_range(MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_2,
			MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_5);
}

static void mtk_edp_read_link_status(struct mtk_edp *mtk_edp,
				    u8 link_status[DP_LINK_STATUS_SIZE])
{
	drm_dp_dpcd_read(&mtk_edp->aux, DP_LANE0_1_STATUS, link_status,
			 DP_LINK_STATUS_SIZE);
}

static void _drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
{
	int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
		DP_TRAINING_AUX_RD_MASK;

	if (rd_interval > 4)
		dev_err(g_mtk_edp->dev, "AUX interval %d, out of range (max 4)\n",
				rd_interval);

	if (rd_interval == 0 || dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
		udelay(100);
	else
		mdelay(rd_interval * 4);
}

static void _drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
{
	int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
		DP_TRAINING_AUX_RD_MASK;

	if (rd_interval > 4)
		dev_err(g_mtk_edp->dev, "AUX interval %d, out of range (max 4)\n",
				rd_interval);

	if (rd_interval == 0)
		udelay(400);
	else
		mdelay(rd_interval * 4);
}

#define MTK_EDP_PREV_LANE_ADJUST_DEFAULT 0xFF
#define MTK_EDP_LANE_ADJUST_INDEX 4
#define MTK_EDP_TRAIN_STATUS_INIT 0
#define MTK_EDP_TRAIN_STATUS_CR 1
#define MTK_EDP_TRAIN_STATUS_EQ 2
#define MTK_EDP_LANE_COUNT_DIV_2 2
static int mtk_edp_train_flow(struct mtk_edp *mtk_edp, int target_link_rate,
			     int target_lane_count)
{
	u8 link_status[DP_LINK_STATUS_SIZE] = {};
	u8 lane_adjust[MTK_EDP_LANE_ADJUST_SIZE] = {};
	bool pass_tps1 = false;
	bool pass_tps2_3 = false;
	int train_retries;
	int status_control;
	int iteration_count;
	u8 prev_lane_adjust;
	u8 val;

	drm_dp_dpcd_writeb(&mtk_edp->aux, DP_LINK_BW_SET, target_link_rate);
	drm_dp_dpcd_writeb(&mtk_edp->aux, DP_LANE_COUNT_SET,
			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);

	//if (mtk_edp->train_info.sink_ssc)
	//	drm_dp_dpcd_writeb(&mtk_edp->aux, DP_DOWNSPREAD_CTRL,
	//			   DP_SPREAD_AMP_0_5);

	train_retries = 0;
	status_control = MTK_EDP_TRAIN_STATUS_INIT;
	iteration_count = 1;
	prev_lane_adjust = MTK_EDP_PREV_LANE_ADJUST_DEFAULT;

	mtk_edp_set_lanes(mtk_edp, target_lane_count / MTK_EDP_LANE_COUNT_DIV_2);
	//mtk_edp_phy_configure(mtk_edp, target_link_rate, target_lane_count);

	dev_info(mtk_edp->dev, "[eDPTX] target_link_rate=0x%x target_lane_count=0x%x\n",
			target_link_rate, target_lane_count);

	do {
		train_retries++;
		if (!mtk_edp->train_info.cable_plugged_in ||
		    ((mtk_edp->train_info.irq_status &
		      MTK_DP_HPD_DISCONNECT) != 0x0)) {
			return -ENODEV;
		}

		if (mtk_edp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
			return -EAGAIN;

		if (!pass_tps1)	{
			mtk_edp_training_set_scramble(mtk_edp, false);

			if (status_control == MTK_EDP_TRAIN_STATUS_INIT) {
				status_control = MTK_EDP_TRAIN_STATUS_CR;
				mtk_edp_train_set_pattern(mtk_edp, MTK_EDP_TRAIN_TPS1);
				val = DP_LINK_SCRAMBLING_DISABLE |
				      DP_TRAINING_PATTERN_1;
				drm_dp_dpcd_writeb(&mtk_edp->aux,
						   DP_TRAINING_PATTERN_SET,
						   val);
				drm_dp_dpcd_read(&mtk_edp->aux,
						 DP_ADJUST_REQUEST_LANE0_1,
						 lane_adjust,
						 sizeof(lane_adjust));
				iteration_count++;

				mtk_edp_train_update_swing_pre(mtk_edp,
							      target_lane_count,
							      lane_adjust);
			}

			_drm_dp_link_train_clock_recovery_delay(mtk_edp->rx_cap);
			mtk_edp_read_link_status(mtk_edp, link_status);

			if (drm_dp_clock_recovery_ok(link_status, target_lane_count)) {
				mtk_edp->train_info.cr_done = true;
				pass_tps1 = true;
				train_retries = 0;
				iteration_count = 1;
				dev_info(mtk_edp->dev, "[eDPTX] Link train CR pass\n");
			} else if (prev_lane_adjust == link_status[MTK_EDP_LANE_ADJUST_INDEX]) {
				iteration_count++;
				if (prev_lane_adjust &
				    DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
					break;
			} else {
				prev_lane_adjust = link_status[MTK_EDP_LANE_ADJUST_INDEX];
			}
			// TODO refine this log
			dev_info(mtk_edp->dev, "[eDPTX] Link train CR fail\n");
		} else if (pass_tps1 && !pass_tps2_3) {
			if (status_control == MTK_EDP_TRAIN_STATUS_CR) {
				status_control = MTK_EDP_TRAIN_STATUS_EQ;
				if (mtk_edp->train_info.tps4) {
					mtk_edp_train_set_pattern(mtk_edp, MTK_EDP_TRAIN_TPS4);
					val = DP_TRAINING_PATTERN_4;
				} else if (mtk_edp->train_info.tps3) {
					mtk_edp_train_set_pattern(mtk_edp, MTK_EDP_TRAIN_TPS3);
					val = DP_LINK_SCRAMBLING_DISABLE |
					      DP_TRAINING_PATTERN_3;
				} else {
					mtk_edp_train_set_pattern(mtk_edp, MTK_EDP_TRAIN_TPS2);
					val = DP_LINK_SCRAMBLING_DISABLE |
					      DP_TRAINING_PATTERN_2;
				}
				drm_dp_dpcd_writeb(&mtk_edp->aux,
						   DP_TRAINING_PATTERN_SET,
						   val);

				drm_dp_dpcd_read(&mtk_edp->aux,
						 DP_ADJUST_REQUEST_LANE0_1,
						 lane_adjust,
						 sizeof(lane_adjust));

				iteration_count++;
				mtk_edp_train_update_swing_pre(mtk_edp,
							      target_lane_count,
							      lane_adjust);
			}

			_drm_dp_link_train_channel_eq_delay(mtk_edp->rx_cap);

			mtk_edp_read_link_status(mtk_edp, link_status);

			if (!drm_dp_clock_recovery_ok(link_status, target_lane_count)) {
				mtk_edp->train_info.cr_done = false;
				mtk_edp->train_info.eq_done = false;
				dev_info(mtk_edp->dev, "[eDPTX] Link train EQ fail\n");
				break;
			}

			if (drm_dp_channel_eq_ok(link_status, target_lane_count)) {
				mtk_edp->train_info.eq_done = true;
				pass_tps2_3 = true;
				dev_info(mtk_edp->dev, "[eDPTX] Link train EQ pass\n");
				break;
			}

			if (prev_lane_adjust == link_status[MTK_EDP_LANE_ADJUST_INDEX])
				iteration_count++;
			else
				prev_lane_adjust = link_status[MTK_EDP_LANE_ADJUST_INDEX];
		}

		drm_dp_dpcd_read(&mtk_edp->aux, DP_ADJUST_REQUEST_LANE0_1,
				lane_adjust, sizeof(lane_adjust));
		mtk_edp_train_update_swing_pre(mtk_edp, target_lane_count, lane_adjust);
	} while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
		 iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);

	drm_dp_dpcd_writeb(&mtk_edp->aux, DP_TRAINING_PATTERN_SET,
			   DP_TRAINING_PATTERN_DISABLE);
	mtk_edp_train_set_pattern(mtk_edp, MTK_EDP_TRAIN_TPSOFF);

	if (pass_tps2_3) {
		mtk_edp->train_info.link_rate = target_link_rate;
		mtk_edp->train_info.lane_count = target_lane_count;

		mtk_edp_training_set_scramble(mtk_edp, true);

		drm_dp_dpcd_writeb(&mtk_edp->aux, DP_LANE_COUNT_SET,
				   target_lane_count |
					   DP_LANE_COUNT_ENHANCED_FRAME_EN);
		mtk_edp_set_enhanced_frame_mode(mtk_edp, true);

		return 0;
	}

	return -ETIMEDOUT;
}

#define MTK_EDP_DPCD_EXT_SIZE 6
static int _drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux,
		u8 dpcd[DP_RECEIVER_CAP_SIZE])
{
	u8 dpcd_ext[MTK_EDP_DPCD_EXT_SIZE];
	int ret;

	/*
	 * Prior to DP1.3 the bit represented by
	 * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved.
	 * If it is set DP_DPCD_REV at 0000h could be at a value less than
	 * the true capability of the panel. The only way to check is to
	 * then compare 0000h and 2200h.
	 */
	if (!(dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
				DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT))
		return 0;

	ret = drm_dp_dpcd_read(aux, DP_DP13_DPCD_REV, &dpcd_ext,
			sizeof(dpcd_ext));
	if (ret < 0)
		return ret;
	if (ret != sizeof(dpcd_ext))
		return -EIO;

	if (dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) {
		dev_info(g_mtk_edp->dev,
				"%s: Extended DPCD rev less than base DPCD rev (%d > %d)\n",
				aux->name, dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]);
		return 0;
	}

	if (!memcmp(dpcd, dpcd_ext, sizeof(dpcd_ext)))
		return 0;

	//dev_info(g_mtk_edp->dev, "%s: Base DPCD: %*ph\n", aux->name, DP_RECEIVER_CAP_SIZE, dpcd);

	memcpy(dpcd, dpcd_ext, sizeof(dpcd_ext));

	return 0;
}

static int _drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
		u8 dpcd[DP_RECEIVER_CAP_SIZE])
{
	int ret;

	ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE);
	if (ret < 0)
		return ret;
	if (ret != DP_RECEIVER_CAP_SIZE || dpcd[DP_DPCD_REV] == 0)
		return -EIO;

	ret = _drm_dp_read_extended_dpcd_caps(aux, dpcd);
	if (ret < 0)
		return ret;

	//dev_info(g_mtk_edp->dev, "%s: DPCD: %*ph\n", aux->name, DP_RECEIVER_CAP_SIZE, dpcd);

	return ret;
}

static bool mtk_edp_parse_capabilities(struct mtk_edp *mtk_edp)
{
	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
	struct mtk_edp_train_info *train_info = &mtk_edp->train_info;

	if (!mtk_edp_plug_state(mtk_edp))
		return false;

	drm_dp_dpcd_writeb(&mtk_edp->aux, DP_SET_POWER, DP_SET_POWER_D0);
	usleep_range(MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_2,
			MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_5);

	_drm_dp_read_dpcd_caps(&mtk_edp->aux, buf);

	memcpy(mtk_edp->rx_cap, buf, min(sizeof(mtk_edp->rx_cap), sizeof(buf)));
	mtk_edp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;

	train_info->link_rate =
		min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
	train_info->lane_count =
		min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));

	train_info->tps3 = drm_dp_tps3_supported(buf);
	train_info->tps4 = drm_dp_tps4_supported(buf);

	//train_info->sink_ssc =
	//	!!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);

	train_info->sink_ssc = false;

	return true;
}

static void mtk_edp_train_change_mode(struct mtk_edp *mtk_edp)
{
	//phy_reset(mtk_edp->phy);
	mtk_edp_reset_swing_pre_emphasis(mtk_edp);

	usleep_range(MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_2,
			MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_5);
}

#define MTK_EDP_MAX_PLUG_WAIT_ATTEMPTS 7
#define MTK_EDP_MAX_TRAINING_ATTEMPTS 6
static int mtk_edp_train_start(struct mtk_edp *mtk_edp)
{
	// 1.  detect plug or not
	// 2.  get link rate from mtk_edp->rx_cap[1] [2]&0x1F
	// 2.1 clear cr_done eq_done
	// 3.  training
	// 4.  down rate/lane_count ->3->4 tuil 5
	// 5.  return 0

	int ret = 0;
	int lane_count;
	int link_rate;
	int train_limit;
	//int max_link_rate;
	int plug_wait;

	for (plug_wait = MTK_EDP_MAX_PLUG_WAIT_ATTEMPTS;
			!mtk_edp_plug_state(mtk_edp) && plug_wait > 0;
			--plug_wait)
		usleep_range(MTK_EDP_SLEEP_TIME_1000,
				MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_5);
	if (plug_wait == 0) {
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
		return -ENODEV;
	}

	link_rate  = mtk_edp->rx_cap[DP_MAX_LINK_RATE];
	lane_count = mtk_edp->rx_cap[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;

	mtk_edp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
	mtk_edp->train_info.lane_count =
		min(MTK_DP_MAX_LANES, lane_count);
	link_rate = mtk_edp->train_info.link_rate;
	lane_count = mtk_edp->train_info.lane_count;

	// check link_rate valid
	switch (link_rate) {
	case MTK_DP_LINKRATE_RBR:
	case MTK_DP_LINKRATE_HBR:
	case MTK_DP_LINKRATE_HBR2:
	case MTK_DP_LINKRATE_HBR25:
	case MTK_DP_LINKRATE_HBR3:
		break;
	default:
		mtk_edp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
		break;
	};

	//max_link_rate = link_rate;
	for (train_limit = 0; train_limit <= MTK_EDP_MAX_TRAINING_ATTEMPTS; ++train_limit) {
		mtk_edp->train_info.cr_done = false;
		mtk_edp->train_info.eq_done = false;

		mtk_edp_train_change_mode(mtk_edp);
		ret = mtk_edp_train_flow(mtk_edp, link_rate, lane_count);
		if (ret == -ENODEV || ret == -EAGAIN)
			return ret;

		if (mtk_edp->train_info.cr_done && mtk_edp->train_info.eq_done)
			return 0;

		//if (!mtk_edp->train_info.cr_done) {
		//	// down link rate retry
		//	switch (link_rate) {
		//	case MTK_DP_LINKRATE_RBR:
		//		lane_count = lane_count / 2;
		//		link_rate = max_link_rate;
		//		if (lane_count == 0x0) {
		//			mtk_edp->train_state =
		//				MTK_DP_TRAIN_STATE_DPIDLE;
		//			return -EIO;
		//		}
		//		break;
		//	case MTK_DP_LINKRATE_HBR:
		//		link_rate = MTK_DP_LINKRATE_RBR;
		//		break;
		//	case MTK_DP_LINKRATE_HBR2:
		//		link_rate = MTK_DP_LINKRATE_HBR;
		//		break;
		//	case MTK_DP_LINKRATE_HBR3:
		//		link_rate = MTK_DP_LINKRATE_HBR2;
		//		break;
		//	default:
		//		return -EINVAL;
		//	};
		//} else if (!mtk_edp->train_info.eq_done) {
		//	// down lane count retry
		//	lane_count /= 2;
		//	if (lane_count == 0)
		//		return -EIO;
		//} else {
		//	// all done
		//	return 0;
		//}
	}

	return -ETIMEDOUT;
}

static int mtk_edp_train_handler(struct mtk_edp *mtk_edp)
{
	int ret = 0;

	if (mtk_edp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
		return ret;

	switch (mtk_edp->train_state) {
	case MTK_DP_TRAIN_STATE_STARTUP:
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
		break;

	case MTK_DP_TRAIN_STATE_CHECKCAP:
		if (mtk_edp_parse_capabilities(mtk_edp)) {
			mtk_edp->train_info.check_cap_count = 0;
			mtk_edp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
		} else {
			mtk_edp->train_info.check_cap_count++;

			if (mtk_edp->train_info.check_cap_count >
			    MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
				mtk_edp->train_info.check_cap_count = 0;
				mtk_edp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
				ret = -ETIMEDOUT;
			}
		}
		break;

	case MTK_DP_TRAIN_STATE_CHECKEDID:
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
		break;

	case MTK_DP_TRAIN_STATE_TRAINING_PRE:
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
		break;

	case MTK_DP_TRAIN_STATE_TRAINING:
		ret = mtk_edp_train_start(mtk_edp);
		if (!ret) {
			mtk_edp_video_mute(mtk_edp, true);
			mtk_edp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
			//mtk_edp_fec_enable(mtk_edp, mtk_edp->has_fec);
		} else if (ret != -EAGAIN) {
			mtk_edp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
		}

		ret = 0;
		break;

	case MTK_DP_TRAIN_STATE_CHECKTIMING:
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
		break;
	case MTK_DP_TRAIN_STATE_NORMAL:
		break;
	case MTK_DP_TRAIN_STATE_POWERSAVE:
		break;
	case MTK_DP_TRAIN_STATE_DPIDLE:
		break;
	default:
		break;
	}

	return ret;
}

static void mtk_edp_video_enable(struct mtk_edp *mtk_edp, bool enable)
{
	dev_info(mtk_edp->dev, "[eDPTX] %s enable=%d\n", __func__, enable);

	if (enable) {
		mtk_edp_set_tx_out(mtk_edp);
		mtk_edp_video_mute(mtk_edp, false);
	} else {
		mtk_edp_video_mute(mtk_edp, true);
	}
}

static void mtk_edp_video_config(struct mtk_edp *mtk_edp)
{
	mtk_edp_mn_overwrite_disable(mtk_edp);

	mtk_edp_set_msa(mtk_edp);

	mtk_edp_set_color_depth(mtk_edp, g_mtk_edp->info.depth);
	mtk_edp_set_color_format(mtk_edp, g_mtk_edp->info.format);
}

static int mtk_edp_state_handler(struct mtk_edp *mtk_edp)
{
	int ret = 0;

	switch (mtk_edp->state) {
	case MTK_DP_STATE_INITIAL:
		mtk_edp_video_mute(mtk_edp, true);
		mtk_edp->state = MTK_DP_STATE_IDLE;
		break;

	case MTK_DP_STATE_IDLE:
		if (mtk_edp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
			mtk_edp->state = MTK_DP_STATE_PREPARE;
		break;

	case MTK_DP_STATE_PREPARE:
		mtk_edp_video_config(mtk_edp);
		mtk_edp_video_enable(mtk_edp, true);

		mtk_edp->state = MTK_DP_STATE_NORMAL;
		break;

	case MTK_DP_STATE_NORMAL:
		if (mtk_edp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
			mtk_edp_video_mute(mtk_edp, true);
			//mtk_edp_sdp_stop_sending(mtk_edp);
			mtk_edp->state = MTK_DP_STATE_IDLE;
		}
		break;

	default:
		break;
	}

	return ret;
}

static void mtk_edp_init_phy_clk(struct mtk_edp *mtk_edp)
{
	dev_info(mtk_edp->dev, "[eDPTX] %s\n", __func__);

	mtk_edp_write(mtk_edp, REG_00AC_CKGEN_EDPTX_PHY, MTK_EDP_ENABLE_ALL_CLK_SW_EN);
	mtk_edp_write(mtk_edp, REG_00B0_CKGEN_EDPTX_PHY, MTK_EDP_ENABLE_ALL_CLK_SW_EN);
	mtk_edp_write(mtk_edp, REG_00B4_CKGEN_EDPTX_PHY, MTK_EDP_ENABLE_ALL_CLK_SW_EN);
	mtk_edp_write(mtk_edp, REG_00B8_CKGEN_EDPTX_PHY, MTK_EDP_ENABLE_ALL_CLK_SW_EN);
	mtk_edp_write(mtk_edp, REG_00BC_CKGEN_EDPTX_PHY, MTK_EDP_ENABLE_ALL_CLK_SW_EN);
}

#define MTK_EDP_DATA_SEL_1PORT 0x00FAC688
#define MTK_EDP_DATA_SEL_2PORT 0x01FAC688
#define MTK_EDP_DATA_SEL_4PORT 0x02FAC688

#define MTK_EDP_PORT_ID_0 0
#define MTK_EDP_PORT_ID_1 1
#define MTK_EDP_PORT_ID_2 2
#define MTK_EDP_PORT_ID_3 3

//#define EDPCLOCK_SEL_DIV1 (1)
//#define EDPCLOCK_SEL_DIV2 (7)
//#define EDPCLOCK_SEL_DIV4 (6)
//#define EDPCLOCK_SEL_DIV8 (5)
//#define EDPCLOCK_SEL_DIV16 (4)
//#define EDPCLOCK_SEL_DIV32 (3)
//#define EDPCLOCK_SEL_DIV64 (2)
static void mtk_edp_drv_set_mux(struct mtk_edp *mtk_edp, int port_num, int mod_8P_pix)
{
	if (port_num == MTK_DP_PORT_NUM_CASE_1) {
		mtk_edp_write(mtk_edp, EDP_TX_TOP_PIX_DATA_SEL, MTK_EDP_DATA_SEL_1PORT);
		mtk_edp_update_bits(mtk_edp, EDP_TX_TOP_PIX_DATA_CONTROL,
				0 << DPTX_ENC0_SEL_SHIFT, DPTX_ENC0_SEL_MASK);
		//mtk_edp_write(mtk_edp, EDP_TX_TOP_PIX_DATA_CONTROL, 0);
	} else if (port_num == MTK_DP_PORT_NUM_CASE_2) {
		mtk_edp_write(mtk_edp, EDP_TX_TOP_PIX_DATA_SEL, MTK_EDP_DATA_SEL_2PORT);
		mtk_edp_update_bits(mtk_edp, EDP_TX_TOP_PIX_DATA_CONTROL,
				0 << DPTX_ENC0_SEL_SHIFT, DPTX_ENC0_SEL_MASK);
		mtk_edp_update_bits(mtk_edp, EDP_TX_TOP_PIX_DATA_CONTROL,
				1 << DPTX_ENC1_SEL_SHIFT, DPTX_ENC1_SEL_MASK);
	} else if (port_num == MTK_DP_PORT_NUM_CASE_4) {
		mtk_edp_write(mtk_edp, EDP_TX_TOP_PIX_DATA_SEL, MTK_EDP_DATA_SEL_4PORT);
		mtk_edp_update_bits(mtk_edp, EDP_TX_TOP_PIX_DATA_CONTROL,
				0 << DPTX_ENC0_SEL_SHIFT, DPTX_ENC0_SEL_MASK);
		mtk_edp_update_bits(mtk_edp, EDP_TX_TOP_PIX_DATA_CONTROL,
				1 << DPTX_ENC1_SEL_SHIFT, DPTX_ENC1_SEL_MASK);
		mtk_edp_update_bits(mtk_edp, EDP_TX_TOP_PIX_DATA_CONTROL,
				MTK_EDP_PORT_ID_2 << DPTX_ENC2_SEL_SHIFT, DPTX_ENC2_SEL_MASK);
		mtk_edp_update_bits(mtk_edp, EDP_TX_TOP_PIX_DATA_CONTROL,
				MTK_EDP_PORT_ID_3 << DPTX_ENC3_SEL_SHIFT, DPTX_ENC3_SEL_MASK);
	}

	//u8 port_div = 1;
	//u32 val;

	//if (port_num == DEC_1) {
	//	mtk_edp_write(mtk_edp, EDP_TX_TOP_PIX_DATA_SEL, MTK_EDP_DATA_SEL_1PORT);
	//	port_div = DEC_3;
	//} else if(port_num == DEC_2) {
	//	mtk_edp_write(mtk_edp, EDP_TX_TOP_PIX_DATA_SEL, MTK_EDP_DATA_SEL_2PORT);
	//	port_div = DEC_2;
	//} else if(port_num == DEC_4) {
	//	mtk_edp_write(mtk_edp, EDP_TX_TOP_PIX_DATA_SEL, MTK_EDP_DATA_SEL_4PORT);
	//	port_div = DEC_1;
	//}

	//[0]: ref clk
	//[1]: div1
	//[2]: div64
	//[3]: div32
	//[4]: div16
	//[5]: div8
	//[6]: div4
	//[7]: div2
	//if(mod_8P_pix ==7)
	//	val = mod_8P_pix | (0x01<<3) | (port_div<<18);
	//else
	//	val = mod_8P_pix | ((mod_8P_pix+1)<<3)|(port_div<<18);

	////DPTX_MSG("mux  = 0x%x\n", val);
	////mtk_edp_write(0x1c000000+0x3810*0x200 + 0x2d*4 ,0x000c003e); //mux setting
	//mtk_edp_write(mtk_edp, EDP_TX_TOP_PIX_CLK_SEL, val); //mux setting
}

//mark
static void mtk_dptx_hal_phy_setting(struct mtk_edp *mtk_edp)
{
	mtk_edp_update_bits(mtk_edp, DP_TX_TOP_PHYD_CLK,
			BIT(DPTX_LSO_CLK_DIS_SHIFT),
			DPTX_LSO_CLK_DIS_MASK);
	mtk_edp_update_bits(mtk_edp, DP_TX_TOP_PHYD_CLK,
			BIT(DPTX_AUXO_CLK_DIS_SHIFT),
			DPTX_AUXO_CLK_DIS_MASK);
}
static void mtk_edp_init_port(struct mtk_edp *mtk_edp)
{
	dev_info(mtk_edp->dev, "[eDPTX] %s+\n", __func__);

	//mtk_edp_set_idle_pattern(mtk_edp, true);
	//mtk_edp_fec_enable(mtk_edp, false);    // already default value
	//mtk_dptx_hal_dsc_enable(edptx_id,FALSE);    // already default value

	mtk_edp_initialize_priv_data(mtk_edp);

	mtk_edp_init_phy_clk(mtk_edp);

	mtk_edp_initialize_settings(mtk_edp);
	mtk_edp_initialize_aux_settings(mtk_edp);
	mtk_edp_initialize_digital_settings(mtk_edp);
	mtk_dptx_hal_phy_setting(mtk_edp);
	mtk_edp_initialize_hpd_detect_settings(mtk_edp);

	mtk_edp_digital_sw_reset(mtk_edp);
	dev_info(mtk_edp->dev, "[eDPTX] %s- %x\n", __func__,
			mtk_edp_read(mtk_edp, MTK_DP_TOP_SOFT_PROBE));
}

static irqreturn_t mtk_edp_hpd_event_thread(int hpd, void *dev)
{
	struct mtk_edp *mtk_edp = dev;
	//int event;
	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
	int i;

	for (i = 0; i < port_num; i++) {
		mtk_edp = mtk_edp_array[i];

		//event = mtk_edp_plug_state(mtk_edp) ? connector_status_connected :
		//	connector_status_disconnected;

		//if (event < 0)
		//	return IRQ_HANDLED;
		//

		//if (mtk_edp->bridge.dev) {
		//	dev_info(mtk_edp->dev, "[eDPTX] %s event=%d [1:connect 2:disconnect]\n",
		//		__func__, event);
		//	drm_helper_hpd_irq_event(mtk_edp->bridge.dev);
		//}

		if (mtk_edp->train_info.cable_state_change) {
			mtk_edp->train_info.cable_state_change = false;

			mtk_edp->train_state = MTK_DP_TRAIN_STATE_STARTUP;

			//if (mtk_edp->edp_ui_enable) {
			//	if ((mtk_edp->external_monitor &&
			//		 event == connector_status_connected) ||
			//	    (!mtk_edp->external_monitor && event > 0)) {
			//		edp_notify_uevent_user(&edptx_notify_data,
			//			DPTX_STATE_ACTIVE);
			//	} else {
			//		edp_notify_uevent_user(&edptx_notify_data,
			//			DPTX_STATE_NO_DEVICE);
			//	}
			//}

			if (!mtk_edp->train_info.cable_plugged_in || !mtk_edp_plug_state(mtk_edp)) {
				dev_info(mtk_edp->dev, "[eDPTX] MTK_DP_HPD_DISCONNECT\n");
				mtk_edp_video_mute(mtk_edp, true);

				mtk_edp_initialize_priv_data(mtk_edp);
				mtk_edp_set_idle_pattern(mtk_edp, true);
				//if (mtk_edp->has_fec)
				//	mtk_edp_fec_enable(mtk_edp, false);
				// mark
				//mtk_edp_sdp_stop_sending(mtk_edp);

				mtk_edp_edid_free(mtk_edp);
				mtk_edp_update_bits(mtk_edp, MTK_DP_TOP_PWR_STATE,
						DP_PWR_STATE_BANDGAP_TPLL,
						DP_PWR_STATE_MASK);
			} else {
				dev_info(mtk_edp->dev, "[eDPTX] MTK_DP_HPD_CONNECT\n");
				mtk_edp_update_bits(mtk_edp, MTK_DP_TOP_PWR_STATE,
						DP_PWR_STATE_BANDGAP_TPLL_LANE,
						DP_PWR_STATE_MASK);
				_drm_dp_read_dpcd_caps(&mtk_edp->aux, buf);
				mtk_edp->train_info.link_rate =
					min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
				mtk_edp->train_info.lane_count =
					min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
			}
		}

		if (mtk_edp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
			dev_info(mtk_edp->dev, "[eDPTX] MTK_DP_HPD_INTERRUPT\n");
			mtk_edp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
			mtk_edp_hpd_sink_event(mtk_edp);
		}
	}

	return IRQ_HANDLED;
}

static irqreturn_t mtk_edp_hpd_isr_handler(struct mtk_edp *mtk_edp)
{
	bool connected;
	u16 irq_status = mtk_edp_irq_get_clear(mtk_edp);
	struct mtk_edp_train_info *train_info = &mtk_edp->train_info;

	train_info->irq_status |= irq_status;

	if (!train_info->irq_status)
		return IRQ_HANDLED;

	connected = mtk_edp_plug_state(mtk_edp);
	if (connected)
		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
	else
		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
	//if (connected || !train_info->cable_plugged_in)
	//	train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
	//else if (!connected || train_info->cable_plugged_in)
	//	train_info->irq_status &= ~MTK_DP_HPD_CONNECT;

	if (!(train_info->irq_status &
	      (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
		return IRQ_HANDLED;

	if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
		train_info->cable_plugged_in = true;
	} else {
		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
		train_info->cable_plugged_in = false;
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
	}
	train_info->cable_state_change = true;
	return IRQ_WAKE_THREAD;
}

static irqreturn_t mtk_edp_hpd_event(int hpd, void *dev)
{
	struct mtk_edp *mtk_edp = dev;
	struct mtk_edp *mtk_edp_iter;
	u32 irq_status;
	irqreturn_t temp;
	irqreturn_t ret = IRQ_HANDLED;
	int i;
	u32 irq_check[MTK_DP_MAX_PORTS] = {
		RGS_IRQ_STATUS_AUXTOP_P0,
		RGS_IRQ_STATUS_AUXTOP_P1,
		RGS_IRQ_STATUS_AUXTOP_P2,
		RGS_IRQ_STATUS_AUXTOP_P3};

	irq_status = mtk_edp_read(mtk_edp, MTK_DP_TOP_IRQ_STATUS);

	if (!irq_status)
		return IRQ_HANDLED;

	for (i = 0; i < port_num; i++) {
		mtk_edp_iter = mtk_edp_array[i];

		if (irq_status & irq_check[i]) {
			temp = mtk_edp_hpd_isr_handler(mtk_edp_iter);
			if (temp == IRQ_WAKE_THREAD)
				ret = IRQ_WAKE_THREAD;
		}
	}

	return ret;
}

//static int mtk_edp_read_eDP_bank_test(struct mtk_edp *mtk_edp,
//				 struct platform_device *pdev)
//{
//	int i = 0;
//
//	//if (mtk_edp->regs == NULL) {
//	//	dev_info(mtk_edp->dev, "[eDPTX] %s mtk_edp->regs == NULL!!!\n", __func__);
//	//	return 0;
//	//}
//
//	dev_info(mtk_edp->dev, "[eDPTX] %s+\n", __func__);
//	for (i = 0; i < REG_BANK_SIZE; i++)
//		dev_info(mtk_edp->dev, "%4x %8x\n",
//				i * REG_STRIDE, mtk_edp_read(mtk_edp, i * REG_STRIDE));
//	dev_info(mtk_edp->dev, "[eDPTX] %s-\n", __func__);
//
//	return 0;
//}

static int mtk_edp_dt_parse_pdata(struct mtk_edp *mtk_edp,
				 struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct resource *r;
	void __iomem *base;
	int i;
	unsigned int temp_base = 0;
	struct regmap *regs;
	char regmap_name[NAME_MAX];
	int result;

	dev_info(mtk_edp->dev, "[eDPTX] %s+\n", __func__);

	for (i = 0; i < ARRAY_SIZE(mtk_edp->segments); i++) {
		r = platform_get_resource(pdev, IORESOURCE_MEM, i);
		if (r == NULL) {
			mtk_edp->segments[i].regs = NULL;
			mtk_edp->segments[i].base_offset = 0;
			mtk_edp->segments[i].size = 0;
			continue;
		}
		if (i == 0)
			temp_base = r->start;

		base = devm_platform_ioremap_resource(pdev, i);
		if (IS_ERR(base))
			return PTR_ERR(base);

		result = snprintf(regmap_name, sizeof(regmap_name), "mtk-edp-registers-bank%d", i);
		if (result < 0) {
			dev_err(dev, "snprintf failed to write to regmap_name\n");
			return -EIO;
		} else if (result >= sizeof(regmap_name)) {
			dev_warn(dev, "snprintf output was truncated\n");
			return -ENOSPC;
		}
		mtk_edp_regmap_bank_config.name = regmap_name;

		regs = devm_regmap_init_mmio(dev, base, &mtk_edp_regmap_bank_config);
		if (IS_ERR(regs))
			return PTR_ERR(regs);

		mtk_edp->segments[i].regs = regs;
		mtk_edp->segments[i].base_offset = r->start - temp_base;
		mtk_edp->segments[i].size = r->end - r->start;
		dev_info(mtk_edp->dev, "[eDPTX] %s base:%x size:%x i:%d+\n",
				__func__, mtk_edp->segments[i].base_offset,
				mtk_edp->segments[i].size, i);
	}

	mtk_edp->num_clks = devm_clk_bulk_get_all(dev, &mtk_edp->clks);
	if (mtk_edp->num_clks < 0) {
		dev_err(dev, "[eDPTX] Failed to get dptx clock: %d\n", mtk_edp->num_clks);
		mtk_edp->clks = NULL;
		mtk_edp->num_clks = 0;
	}

	dev_info(mtk_edp->dev, "[eDPTX] %s- num_clks:%d\n", __func__, mtk_edp->num_clks);

	return 0;
}

#define MTK_EDP_AUX_RETRY_TIME 3
static ssize_t mtk_edp_aux_transfer(struct drm_dp_aux *mtk_aux,
				   struct drm_dp_aux_msg *msg)
{
	ssize_t err = -EAGAIN;
	struct mtk_edp *mtk_edp;
	bool is_read;
	u8 request;
	size_t accessed_bytes = 0;
	int retry = MTK_EDP_AUX_RETRY_TIME, ret = 0;

	mtk_edp = container_of(mtk_aux, struct mtk_edp, aux);

	if (!mtk_edp->train_info.cable_plugged_in ||
	    mtk_edp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
		err = -EAGAIN;
		goto err;
	}

	switch (msg->request) {
	case DP_AUX_I2C_MOT:
	case DP_AUX_I2C_WRITE:
	case DP_AUX_NATIVE_WRITE:
	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
		is_read = false;
		break;
	case DP_AUX_I2C_READ:
	case DP_AUX_NATIVE_READ:
	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
		request = msg->request;
		is_read = true;
		break;
	default:
		dev_err(mtk_aux->dev, "invalid aux cmd = %d\n",
			msg->request);
		err = -EINVAL;
		goto err;
	}

	if (msg->size == 0) {
		mtk_edp_aux_do_transfer(mtk_edp, is_read, request,
					     msg->address + accessed_bytes,
					     msg->buffer + accessed_bytes,
					     0);
	} else {
		while (accessed_bytes < msg->size) {
			size_t to_access = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
					       msg->size - accessed_bytes);
			retry = MTK_EDP_AUX_RETRY_TIME;
			while (retry--) {
				ret = mtk_edp_aux_do_transfer(mtk_edp, is_read, request,
							     msg->address + accessed_bytes,
							     msg->buffer + accessed_bytes,
							     to_access);
				if (ret == 0)
					break;
				udelay(MTK_EDP_SLEEP_TIME_10 * MTK_EDP_DELAY_MULTIPLIER_5);
			}
			if ((retry == 0) && ret) {
				dev_err(mtk_aux->dev,
					 "Failed to do AUX transfer: %d\n", ret);
				break;
			}
			accessed_bytes += to_access;
		}
	}
err:
	if (!ret) {
		msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
		ret = msg->size;
	} else {
		msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
		return err;
	}

	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
	return msg->size;
}

static void mtk_edp_aux_init(struct mtk_edp *mtk_edp)
{
	drm_dp_aux_init(&mtk_edp->aux);
	mtk_edp->aux.name = "aux_mtk_dp";
	mtk_edp->aux.transfer = mtk_edp_aux_transfer;
}

static void mtk_edp_poweroff(struct mtk_edp *mtk_edp)
{
	dev_info(mtk_edp->dev, "[eDPTX] %s+\n", __func__);

	mutex_lock(&mtk_edp->dp_lock);

	if (!mtk_edp->powered) {
		dev_info(mtk_edp->dev, "[eDPTX] edp has been powered off, return\n");
		mutex_unlock(&mtk_edp->dp_lock);
		return;
	}

	mtk_edp_hwirq_enable(mtk_edp, false);
	mtk_edp_power_disable(mtk_edp);
	//phy_exit(mtk_edp->phy);
	clk_bulk_disable_unprepare(mtk_edp->num_clks, mtk_edp->clks);

	mtk_edp->powered = false;

	mutex_unlock(&mtk_edp->dp_lock);

	dev_info(mtk_edp->dev, "[eDPTX] %s-\n", __func__);
}

static int mtk_edp_poweron(struct mtk_edp *mtk_edp)
{
	int ret = 0;

	dev_info(mtk_edp->dev, "[eDPTX] %s+\n", __func__);

	mutex_lock(&mtk_edp->dp_lock);

	if (mtk_edp->powered) {
		dev_info(mtk_edp->dev, "[eDPTX] edp has been powered on, return\n");
		mtk_edp_hwirq_enable(mtk_edp, true);
		mtk_edp_initialize_priv_data(mtk_edp);
		mutex_unlock(&mtk_edp->dp_lock);
		return 0;
	}

	ret = clk_bulk_prepare_enable(mtk_edp->num_clks, mtk_edp->clks);
	if (ret) {
		dev_err(mtk_edp->dev, "[eDPTX] Fail to enable clock: %d\n", ret);
		goto err;
	} else {
		dev_err(mtk_edp->dev, "[eDPTX] ok to enable clock: %d\n", ret);
	}
	//ret = phy_init(mtk_edp->phy);
	//if (ret) {
	//	dev_err(mtk_edp->dev, "[eDPTX] Failed to initialize phy: %d\n", ret);
	//	goto err_phy_init;
	//}
	//ret = mtk_edp_phy_configure(mtk_edp, MTK_DP_LINKRATE_RBR, 1);
	//if (ret) {
	//	dev_err(mtk_edp->dev, "[eDPTX] Failed to configure phy: %d\n", ret);
	//	goto err_phy_config;
	//}

	mtk_edp_init_port(mtk_edp);
	mtk_edp_power_enable(mtk_edp);
	mtk_edp_hwirq_enable(mtk_edp, true);

	mtk_edp->powered = true;

	dev_info(mtk_edp->dev, "[eDPTX] %s-\n", __func__);

	mutex_unlock(&mtk_edp->dp_lock);
	return 0;
//err_phy_config:
//	phy_exit(mtk_edp->phy);
//err_phy_init:
//	clk_bulk_disable(mtk_edp->num_clks, mtk_edp->clks);
err:
	mutex_unlock(&mtk_edp->dp_lock);
	return ret;
}

void mtk_edp_change_train_forcepass_status(int status)
{
	dev_info(g_mtk_edp->dev, "[eDPTX] %s %d-\n", __func__, status);
}
EXPORT_SYMBOL(mtk_edp_change_train_forcepass_status);

void sml_change_RX_irq_status(int status)
{
	struct mtk_edp *mtk_edp;
	int i;

	for (i = 0; i < port_num; i++) {
		mtk_edp = mtk_edp_array[i];

		if (status) {
			// set force hpd enable
			mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_37A4,
					BIT(IO_HPD_C_OV_EN_AUX_TX_P0_SHIFT),
					IO_HPD_C_OV_EN_AUX_TX_P0_MASK);
			// force high
			mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_37A4,
					BIT(IO_HPD_C_OV_AUX_TX_P0_SHIFT),
					IO_HPD_C_OV_AUX_TX_P0_MASK);
		} else {
			// force low
			mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_37A4,
					BIT(IO_HPD_C_OV_EN_AUX_TX_P0_SHIFT),
					IO_HPD_C_OV_EN_AUX_TX_P0_MASK);
			// set force hpd disable
			mtk_edp_update_bits(mtk_edp, MTK_DP_AUX_P0_37A4, 0,
					IO_HPD_C_OV_AUX_TX_P0_MASK);
		}
		usleep_range(MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_2,
				MTK_EDP_SLEEP_TIME_1000 * MTK_EDP_DELAY_MULTIPLIER_5);
		dev_info(mtk_edp->dev, "[eDPTX] get hpd status: %d\n",
				mtk_edp_plug_state(mtk_edp));
	}
}
EXPORT_SYMBOL(sml_change_RX_irq_status);

int mtk_edp_poweron_all(void)
{
	struct mtk_edp *mtk_edp;
	int ret = 0;
	int i;

	mtk_edp_drv_set_mux(g_mtk_edp, port_num, g_mtk_edp->info.sc_clk_div);

	for (i = 0; i < port_num; i++) {
		mtk_edp = mtk_edp_array[i];

		ret = mtk_edp_poweron(mtk_edp);
		if (ret) {
			dev_err(mtk_edp->dev, "[eDPTX] Power on failed %d\n", ret);
			return ret;
		}
	}

	return 0;
}
EXPORT_SYMBOL(mtk_edp_poweron_all);

void mtk_edp_poweroff_all(void)
{
	struct mtk_edp *mtk_edp;
	int i;

	for (i = 0; i < port_num; i++) {
		mtk_edp = mtk_edp_array[i];

		mtk_edp_poweroff(mtk_edp);
	}
}
EXPORT_SYMBOL(mtk_edp_poweroff_all);

void mtk_edp_parse_timings(struct mtk_edp_info *edp_info_in)
{
	struct mtk_edp_info *edp_info = &g_mtk_edp->info;
	struct mtk_edp_timings *timings = &g_mtk_edp->info.timings;

	dev_info(g_mtk_edp->dev, "[eDPTX] %s\n", __func__);

	memcpy(edp_info, edp_info_in, sizeof(struct mtk_edp_info));

	port_num = edp_info_in->port_num;
	if (port_num > port_num_registered)
		dev_info(g_mtk_edp->dev,
				"[eDPTX] %s port_num > port_num_registered\n", __func__);
	port_num = port_num > port_num_registered ? port_num_registered : port_num;

	dev_info(g_mtk_edp->dev, "[eDPTX] %s Htt=%d Vtt=%d Ha=%d Va=%d fps=%d\n",
		__func__,
		timings->htotal, timings->vtotal, timings->vm.hactive,
		timings->vm.vactive, timings->frame_rate);
}
EXPORT_SYMBOL(mtk_edp_parse_timings);

#define MTK_EDP_MAX_TRAINING_LOOP 50
int mtk_edp_train_all(void)
{
	struct mtk_edp *mtk_edp;
	int ret = 0;
	int i, j;

	for (j = 0; j < port_num; j++) {
		mtk_edp = mtk_edp_array[j];

		if (mtk_edp->enabled) {
			dev_info(mtk_edp->dev, "[eDPTX] edp has beed enabled, return\n");
			continue;
		}

		for (i = 0; i < MTK_EDP_MAX_TRAINING_LOOP; i++) {
			ret = mtk_edp_train_handler(mtk_edp);
			if (ret) {
				dev_err(mtk_edp->dev, "Train handler failed %d\n", ret);
				//return ret;
				goto err;
			}

			ret = mtk_edp_state_handler(mtk_edp);
			if (ret) {
				dev_err(mtk_edp->dev, "State handler failed %d\n", ret);
				//return ret;
				goto err;
			}
		}

		mtk_edp->enabled = true;
err:
		dev_info(mtk_edp->dev, "[eDPTX] edp enable status: %d\n", mtk_edp->enabled);
	}

	return ret;
}
EXPORT_SYMBOL(mtk_edp_train_all);

void mtk_edp_disable_all(void)
{
	struct mtk_edp *mtk_edp;
	int i;

	for (i = 0; i < port_num; i++) {
		mtk_edp = mtk_edp_array[i];

		if (!mtk_edp->enabled) {
			dev_info(mtk_edp->dev, "[eDPTX] edp has beed disabled, return\n");
			continue;
		}

		mtk_edp_video_mute(mtk_edp, true);
		mtk_edp->state = MTK_DP_STATE_IDLE;
		mtk_edp->train_state = MTK_DP_TRAIN_STATE_STARTUP;

		mtk_edp->enabled = false;
		// mark
		msleep(MTK_EDP_SLEEP_TIME_100);
	}
}
EXPORT_SYMBOL(mtk_edp_disable_all);

static const struct drm_bridge_funcs mtk_edp_bridge_funcs = {
};

#define MTK_EDP_DPCD_READ_BUFFER_SIZE 16
static void dpcd_read(unsigned int addr, ssize_t len)
{
	ssize_t ret;
	struct mtk_edp *mtk_edp;
	u8 data[MTK_EDP_DPCD_READ_BUFFER_SIZE] = {0};
	int i;

	for (i = 0; i < port_num; i++) {
		mtk_edp = mtk_edp_array[i];

		ret = drm_dp_dpcd_read(&mtk_edp->aux, addr, data, len);
		if (!ret) {
			dev_info(mtk_edp->dev, "Read link status failed: %zd\n", ret);
			return;
		}
	}
}

#define MTK_EDP_TIMING_HACTIVE         3840
#define MTK_EDP_TIMING_HFRONT_PORCH    176
#define MTK_EDP_TIMING_HSYNC_LEN       88
#define MTK_EDP_TIMING_HBACK_PORCH     296
#define MTK_EDP_TIMING_VACTIVE         2160
#define MTK_EDP_TIMING_VFRONT_PORCH    8
#define MTK_EDP_TIMING_VSYNC_LEN       10
#define MTK_EDP_TIMING_VBACK_PORCH     72
#define MTK_EDP_TIMING_PIXELCLOCK      1193280000
#define MTK_EDP_TIMING_FLAGS           0
#define MTK_EDP_TIMING_HTOTAL          4400
#define MTK_EDP_TIMING_VTOTAL          2250
#define MTK_EDP_TIMING_FRAME_RATE      60
#define MTK_EDP_TIMING_PIX_RATE_KHZ    0
static void mtk_edp_set_timing_demo(void)
{
	struct mtk_edp_info edp_info = {0};
	struct mtk_edp_timings *timings = &edp_info.timings;

	timings->vm.hactive        = MTK_EDP_TIMING_HACTIVE;
	timings->vm.hfront_porch   = MTK_EDP_TIMING_HFRONT_PORCH;
	timings->vm.hsync_len      = MTK_EDP_TIMING_HSYNC_LEN;
	timings->vm.hback_porch    = MTK_EDP_TIMING_HBACK_PORCH;
	timings->vm.vactive        = MTK_EDP_TIMING_VACTIVE;
	timings->vm.vfront_porch   = MTK_EDP_TIMING_VFRONT_PORCH;
	timings->vm.vsync_len      = MTK_EDP_TIMING_VSYNC_LEN;
	timings->vm.vback_porch    = MTK_EDP_TIMING_VBACK_PORCH;
	timings->vm.pixelclock     = MTK_EDP_TIMING_PIXELCLOCK;
	timings->vm.flags          = MTK_EDP_TIMING_FLAGS;
	timings->htotal            = MTK_EDP_TIMING_HTOTAL;
	timings->vtotal            = MTK_EDP_TIMING_VTOTAL;
	timings->frame_rate        = MTK_EDP_TIMING_FRAME_RATE;
	timings->pix_rate_khz      = MTK_EDP_TIMING_PIX_RATE_KHZ;

	edp_info.port_num = 1;

	mtk_edp_parse_timings(&edp_info);
}

#define MAX_PARAMS 5
#define PARAM_SIZE 12

#define BASE_HEX 16
#define BASE_DEC 10
#define PARAM_INDEX_ADDR 1
#define PARAM_INDEX_LEN  2
#define PARAM_INDEX_VALUE  2
static ssize_t dp_rx_irq_debugfs_write(struct file *file, const char __user *ubuf,
		size_t len, loff_t *offp)
{
	// TODO refine this, not use g_mtk_edp.
	char buf[PARAM_SIZE];
	char *param, *buf_tmp;
	char *params[MAX_PARAMS];
	int count = 0;
	int val1 = 0;
	int val2 = 0;
	//int val3 = 0;
	int ret;
	int i;

	struct mtk_edp *mtk_edp;

	if (len > sizeof(buf) - 1)
		return -EINVAL;

	if (copy_from_user(buf, ubuf, len))
		return -EFAULT;

	buf[len] = '\0';

	buf_tmp = buf;
	while ((param = strsep(&buf_tmp, " ")) != NULL) {
		if (count < 0 || count >= MAX_PARAMS) {
			dev_err(g_mtk_edp->dev, "%s too many params", __func__);
			return -EINVAL;
		}
		params[count] = param;
		count++;
	}

	if (sysfs_streq(params[0], "1")) {
		if (sysfs_streq(params[1], "1")) {
			mtk_edp_set_timing_demo();
			mtk_edp_poweron_all();
			sml_change_RX_irq_status(1);
			mtk_edp_train_all();
		} else if (sysfs_streq(params[1], "0")) {
			sml_change_RX_irq_status(0);
			mtk_edp_disable_all();
			mtk_edp_poweroff_all();
		}
	} else if (sysfs_streq(params[0], "4")) {
		ret = kstrtoint(params[PARAM_INDEX_ADDR], BASE_HEX, &val1);
		if (ret) {
			dev_err(g_mtk_edp->dev, "Failed to convert: %s",
					params[PARAM_INDEX_ADDR]);
			return -EINVAL;
		}
		ret = kstrtoint(params[PARAM_INDEX_LEN], BASE_DEC, &val2);
		if (ret) {
			dev_err(g_mtk_edp->dev, "Failed to convert: %s",
					params[PARAM_INDEX_LEN]);
			return -EINVAL;
		}

		dpcd_read(val1, val2);
	//} else if (sysfs_streq(params[0], "5")) {
	//	ret = kstrtoint(params[PARAM_INDEX_ADDR], BASE_HEX, &val1);
	//	if (ret) {
	//		dev_err(g_mtk_edp->dev, "Failed to convert: %s",
	//				params[PARAM_INDEX_ADDR]);
	//		return -EINVAL;
	//	}
	//	ret = kstrtoint(params[PARAM_INDEX_VALUE], BASE_HEX, &val2);
	//	if (ret) {
	//		dev_err(g_mtk_edp->dev, "Failed to convert: %s",
	//				params[PARAM_INDEX_VALUE]);
	//		return -EINVAL;
	//	}

	//	for (i = 0; i < port_num; i++) {
	//		mtk_edp = mtk_edp_array[i];

	//		drm_dp_dpcd_writeb(&mtk_edp->aux, val1, val2);
	//	}
	} else if (sysfs_streq(params[0], "7")) {
		if (sysfs_streq(params[1], "0")) {
			sml_change_RX_irq_status(0);
		} else if (sysfs_streq(params[1], "1")) {
			sml_change_RX_irq_status(1);
		} else if (sysfs_streq(params[1], "2")) {
			for (i = 0; i < port_num; i++) {
				mtk_edp = mtk_edp_array[i];

				dev_info(mtk_edp->dev, "[eDPTX] get hpd status: %d\n",
						mtk_edp_plug_state(mtk_edp));
			}
		}
	//} else if (sysfs_streq(params[0], "8")) {
	//	ret = kstrtoint(params[PARAM_INDEX_ADDR], BASE_DEC, &val1);
	//	if (ret) {
	//		dev_err(g_mtk_edp->dev, "Failed to convert: %s",
	//				params[PARAM_INDEX_ADDR]);
	//		return -EINVAL;
	//	}
	//	ret = kstrtoint(params[PARAM_INDEX_LEN], BASE_DEC, &val2);
	//	if (ret) {
	//		dev_err(g_mtk_edp->dev, "Failed to convert: %s",
	//				params[PARAM_INDEX_LEN]);
	//		return -EINVAL;
	//	}
	//	ret = kstrtoint(params[3], BASE_DEC, &val3);
	//	if (ret) {
	//		dev_err(g_mtk_edp->dev, "Failed to convert: %s",
	//				params[3]);
	//		return -EINVAL;
	//	}

	//	mtk_edp_set_swing_pre_emphasis_by_channel(val1, val2, val3);
	} else {
		return -EINVAL;
	}

	return len;
}

static const struct file_operations edp_rx_irq_debugfs_fops = {
	.owner = THIS_MODULE,
	.write = dp_rx_irq_debugfs_write,
};

static int mtk_edp_probe(struct platform_device *pdev)
{
	struct mtk_edp *mtk_edp;
	struct device *dev = &pdev->dev;
	int ret;
	struct dentry *dentry;

	int irq_num = 0;

	dev_info(dev, "[eDPTX] %s+\n", __func__);

	mtk_edp = devm_kzalloc(dev, sizeof(*mtk_edp), GFP_KERNEL);
	if (!mtk_edp)
		return -ENOMEM;

	mtk_edp->dev = dev;

	irq_num = platform_get_irq(pdev, 0);
	if (irq_num < 0) {
		dev_info(dev, "[eDPTX] slave port do not have irq\n");
	} else {
		dev_info(dev, "[eDPTX] %s irq_num: %d\n", __func__, irq_num);
		ret = devm_request_threaded_irq(dev, irq_num, mtk_edp_hpd_event,
				mtk_edp_hpd_event_thread,
				IRQ_TYPE_LEVEL_HIGH,
				dev_name(dev), mtk_edp);
		if (ret) {
			dev_err(dev, "[eDPTX] failed to request mediatek dptx irq\n");
			return -EPROBE_DEFER;
		}

		if (g_mtk_edp == NULL)
			g_mtk_edp = mtk_edp;

#define MTK_EDP_DEBUGFS_WRITE_PERMISSION 0200
		dentry = debugfs_create_file("edp_rx_irq", MTK_EDP_DEBUGFS_WRITE_PERMISSION,
				NULL, NULL, &edp_rx_irq_debugfs_fops);
		if (!dentry) {
			dev_err(&pdev->dev, "Failed to create dp_rx_irq debugfs file\n");
			return -ENOMEM;
		}

	}

	ret = mtk_edp_dt_parse_pdata(mtk_edp, pdev);
	if (ret) {
		dev_err(dev, "[eDPTX] failed to parst dts data, ret:%d\n", ret);
		return ret;
	}

	mtk_edp_aux_init(mtk_edp);

	mutex_init(&mtk_edp->dp_lock);
	mutex_init(&mtk_edp->edid_lock);

	platform_set_drvdata(pdev, mtk_edp);

	mtk_edp->bridge.funcs = &mtk_edp_bridge_funcs;
	mtk_edp->bridge.of_node = dev->of_node;
	//mtk_edp->bridge.type = DRM_MODE_CONNECTOR_eDP;
	drm_bridge_add(&mtk_edp->bridge);

	//[?] cause mboot has init eDP, so just set to true.
	//if (mtk_edp_get_lk_display()) {
	//	/* TODO keep bootlogo display*/
	//	dev_info(dev, "[eDPTX] edp display, power on default");
	//	mtk_edp->powered = true;
	//	mtk_edp->enabled = true;
	//}

	if (port_num_registered >= 0 && port_num_registered < MTK_DP_MAX_PORTS) {
		list_add(&mtk_edp->list, &edp_list);
		mtk_edp_array[port_num_registered] = mtk_edp;
		dev_info(dev, "[eDPTX] %s port_id: %d\n", __func__, port_num_registered);
		port_num_registered++;
	} else {
		dev_err(dev, "[eDPTX] %s port_id out of range, port_id:%d\n",
				__func__, port_num_registered);
	}

	dev_info(dev, "[eDPTX] %s-\n", __func__);

	return 0;
}

static int mtk_edp_remove(struct platform_device *pdev)
{
	struct mtk_edp *mtk_edp = platform_get_drvdata(pdev);

	//platform_device_unregister(mtk_edp->phy_dev);

	mtk_edp_video_mute(mtk_edp, true);

	//pm_runtime_disable(&pdev->dev);

	return 0;
}

static const struct of_device_id mtk_edp_of_match[] = {
	{
		.compatible = "mediatek,mt5877-edp-tx",
	},
	{},
};
MODULE_DEVICE_TABLE(of, mtk_edp_of_match);

struct platform_driver mtk_edp_driver = {
	.probe = mtk_edp_probe,
	.remove = mtk_edp_remove,
	.driver = {
		.name = "mediatek-drm-edp",
		.of_match_table = mtk_edp_of_match,
		//.pm = &mtk_edp_pm_ops,
	},
};
