/*
 *RockChip DP (Display port) register interface driver.
 *
 * 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.
 */

#include <linux/device.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/rockchip/cpu.h>
#include <linux/rockchip/grf.h>
#include "rk32_dp.h"


void rk32_edp_enable_video_mute(struct rk32_edp *edp, bool enable)
{
	u32 val;

	if (enable) {
		val = readl(edp->regs + VIDEO_CTL_1);
		val |= VIDEO_MUTE;
		writel(val, edp->regs + VIDEO_CTL_1);
	} else {
		val = readl(edp->regs + VIDEO_CTL_1);
		val &= ~VIDEO_MUTE;
		writel(val, edp->regs + VIDEO_CTL_1);
	}
}

void rk32_edp_stop_video(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + VIDEO_CTL_1);
	val &= ~VIDEO_EN;
	writel(val, edp->regs + VIDEO_CTL_1);
}

void rk32_edp_lane_swap(struct rk32_edp *edp, bool enable)
{
	u32 val;


	if (enable)
		val = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
	else
		val = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;

	writel(val, edp->regs + LANE_MAP);
}

void rk32_edp_init_refclk(struct rk32_edp *edp)
{
	u32 val;
	/*struct rk32_edp_platdata *pdata = edp->dev->platform_data;
	struct analog_param *analog_param = pdata->analog_param;

	val = TX_TERMINAL_CTRL_50_OHM;
	writel(val, edp->regs + ANALOG_CTL_1);*/

	val = SEL_24M;
	writel(val, edp->regs + ANALOG_CTL_2);

	if (edp->soctype == SOC_RK3399)
		val = 0x1 << 0;
	else
		val = REF_CLK_24M;
	writel(val, edp->regs + PLL_REG_1);

	val = 0x95;
	writel(val, edp->regs + PLL_REG_2);

	val = 0x40;
	writel(val, edp->regs + PLL_REG_3);

	val = 0x58;
	writel(val, edp->regs + PLL_REG_4);

	val = 0x22;
	writel(val, edp->regs + PLL_REG_5);

	val = 0x19;
	writel(val, edp->regs + SSC_REG);
	val = 0x87;
	writel(val, edp->regs + TX_REG_COMMON);
	val = 0x03;
	writel(val, edp->regs + DP_AUX);
	val = 0x46;
	writel(val, edp->regs + DP_BIAS);
	val = 0x55;
	writel(val, edp->regs + DP_RESERVE2);


	/*val = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO;
	writel(val, edp->regs + ANALOG_CTL_3);

	if (!analog_param) {
		val = PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM |
			TX_CUR1_2X | TX_CUR_16_MA;
		writel(val, edp->regs + PLL_FILTER_CTL_1);

		val = CH3_AMP_400_MV | CH2_AMP_400_MV |
			CH1_AMP_400_MV | CH0_AMP_400_MV;
		writel(val, edp->regs + TX_AMP_TUNING_CTL);
	} else {
		int tx_amp;

		val = PD_RING_OSC | TX_CUR1_2X | TX_CUR_16_MA;
		switch (analog_param->aux_tx_terminal_resistor) {
		case AUX_TX_37_5_OHM:
			val |= AUX_TERMINAL_CTRL_37_5_OHM;
			break;
		case AUX_TX_45_OHM:
			val |= AUX_TERMINAL_CTRL_45_OHM;
			break;
		case AUX_TX_50_OHM:
			val |= AUX_TERMINAL_CTRL_50_OHM;
			break;
		case AUX_TX_65_OHM:
			val |= AUX_TERMINAL_CTRL_65_OHM;
			break;
		}
		writel(val, edp->regs + PLL_FILTER_CTL_1);

		tx_amp = analog_param->tx_amplitude;
		if (tx_amp < 200000 || tx_amp > 500000) {
			dev_warn(edp->dev,
				 "TX amp out of range, defaulting to 400mV\n");
			tx_amp = 400000;
		}

		tx_amp = ((tx_amp - 400000) / 12500) & 0x1f;

		val = (tx_amp << CH3_AMP_SHIFT) | (tx_amp << CH2_AMP_SHIFT) |
			(tx_amp << CH1_AMP_SHIFT) | (tx_amp << CH0_AMP_SHIFT);
		writel(val, edp->regs + TX_AMP_TUNING_CTL);
	}*/
}

void rk32_edp_init_interrupt(struct rk32_edp *edp)
{
	/* Set interrupt pin assertion polarity as high */
	writel(INT_POL, edp->regs + INT_CTL);

	/* Clear pending valisers */
	writel(0xff, edp->regs + COMMON_INT_STA_1);
	writel(0x4f, edp->regs + COMMON_INT_STA_2);
	writel(0xff, edp->regs + COMMON_INT_STA_3);
	writel(0x27, edp->regs + COMMON_INT_STA_4);

	writel(0x7f, edp->regs + DP_INT_STA);

	/* 0:mask,1: unmask */
	writel(0x00, edp->regs + COMMON_INT_MASK_1);
	writel(0x00, edp->regs + COMMON_INT_MASK_2);
	writel(0x00, edp->regs + COMMON_INT_MASK_3);
	writel(0x00, edp->regs + COMMON_INT_MASK_4);
	writel(0x00, edp->regs + DP_INT_STA_MASK);
}

void rk32_edp_reset(struct rk32_edp *edp)
{
	u32 val;

	rk32_edp_stop_video(edp);
	rk32_edp_enable_video_mute(edp, 0);

	val = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N |
		AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N;
	writel(val, edp->regs + FUNC_EN_1);

	val = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
		SERDES_FIFO_FUNC_EN_N |
		LS_CLK_DOMAIN_FUNC_EN_N;
	writel(val, edp->regs + FUNC_EN_2);

	udelay(20);

	rk32_edp_lane_swap(edp, 0);

	writel(0x0, edp->regs + SYS_CTL_1);
	writel(0x40, edp->regs + SYS_CTL_2);
	writel(0x0, edp->regs + SYS_CTL_3);
	writel(0x0, edp->regs + SYS_CTL_4);

	writel(0x0, edp->regs + PKT_SEND_CTL);
	writel(0x0, edp->regs + HDCP_CTL);

	writel(0x5e, edp->regs + HPD_DEGLITCH_L);
	writel(0x1a, edp->regs + HPD_DEGLITCH_H);

	writel(0x10, edp->regs + LINK_DEBUG_CTL);

	writel(0x0, edp->regs + VIDEO_FIFO_THRD);
	writel(0x20, edp->regs + AUDIO_MARGIN);

	writel(0x4, edp->regs + M_VID_GEN_FILTER_TH);
	writel(0x2, edp->regs + M_AUD_GEN_FILTER_TH);

	writel(0x0, edp->regs + SOC_GENERAL_CTL);

}

void rk32_edp_config_interrupt(struct rk32_edp *edp)
{
	u32 val;

	/* 0: mask, 1: unmask */
	val = 0;
	writel(val, edp->regs + COMMON_INT_MASK_1);

	writel(val, edp->regs + COMMON_INT_MASK_2);

	writel(val, edp->regs + COMMON_INT_MASK_3);

	writel(val, edp->regs + COMMON_INT_MASK_4);

	writel(val, edp->regs + DP_INT_STA_MASK);
}

u32 rk32_edp_get_pll_lock_status(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + DEBUG_CTL);
	if (val & PLL_LOCK)
		return DP_PLL_LOCKED;
	else
		return DP_PLL_UNLOCKED;
}


void rk32_edp_analog_power_ctr(struct rk32_edp *edp, bool enable)
{
	u32 val;

	if (enable) {
		val = PD_EXP_BG | PD_AUX | PD_PLL |
			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
		writel(val, edp->regs + DP_PWRDN);
		udelay(10);
		writel(0x0, edp->regs + DP_PWRDN);
	} else {
		val = PD_EXP_BG | PD_AUX | PD_PLL |
			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
		writel(val, edp->regs + DP_PWRDN);
	}
}


void rk32_edp_init_analog_func(struct rk32_edp *edp)
{
	u32 val;
	int wt = 0;
	rk32_edp_analog_power_ctr(edp, 1);

	val = PLL_LOCK_CHG;
	writel(val, edp->regs + COMMON_INT_STA_1);

	val = readl(edp->regs + DEBUG_CTL);
	val &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
	writel(val, edp->regs + DEBUG_CTL);

	/* Power up PLL */
	while (wt < 100) {
		if (rk32_edp_get_pll_lock_status(edp) == DP_PLL_LOCKED) {
			dev_info(edp->dev, "edp pll locked\n");
			break;
		} else {
			wt++;
			udelay(5);
		}
	}

	/* Enable Serdes FIFO function and Link symbol clock domain module */
	val = readl(edp->regs + FUNC_EN_2);
	val &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
		| AUX_FUNC_EN_N | SSC_FUNC_EN_N);
	writel(val, edp->regs + FUNC_EN_2);
}

void rk32_edp_init_hpd(struct rk32_edp *edp)
{
	u32 val;

	val = HOTPLUG_CHG | HPD_LOST | PLUG;
	writel(val, edp->regs + COMMON_INT_STA_4);

	val = INT_HPD;
	writel(val, edp->regs + DP_INT_STA);

	val = readl(edp->regs + SYS_CTL_3);
	val |= (F_HPD | HPD_CTRL);
	writel(val, edp->regs + SYS_CTL_3);
}

void rk32_edp_reset_aux(struct rk32_edp *edp)
{
	u32 val;

	/* Disable AUX channel module */
	val = readl(edp->regs + FUNC_EN_2);
	val |= AUX_FUNC_EN_N;
	writel(val, edp->regs + FUNC_EN_2);
}

void rk32_edp_init_aux(struct rk32_edp *edp)
{
	u32 val;

	/* Clear inerrupts related to AUX channel */
	val = RPLY_RECEIV | AUX_ERR;
	writel(val, edp->regs + DP_INT_STA);

	rk32_edp_reset_aux(edp);

	/* Disable AUX transaction H/W retry */
	/*val = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
		AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
	writel(val, edp->regs + AUX_HW_RETRY_CTL) ;*/

	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
	val = DEFER_CTRL_EN | DEFER_COUNT(1);
	writel(val, edp->regs + AUX_CH_DEFER_CTL);

	/* Enable AUX channel module */
	val = readl(edp->regs + FUNC_EN_2);
	val &= ~AUX_FUNC_EN_N;
	writel(val, edp->regs + FUNC_EN_2);
}

int rk32_edp_get_plug_in_status(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + SYS_CTL_3);
	if (val & HPD_STATUS)
		return 0;

	return -EINVAL;
}

void rk32_edp_enable_sw_function(struct rk32_edp *edp)
{
	u32 val;
	val = readl(edp->regs + FUNC_EN_1);
	val &= ~SW_FUNC_EN_N;
	writel(val, edp->regs + FUNC_EN_1);
}

int rk32_edp_start_aux_transaction(struct rk32_edp *edp)
{
	int val;
	int retval = 0;
	int timeout_loop = 0;
	int aux_timeout = 0;

	/* Enable AUX CH operation */
	val = readl(edp->regs + AUX_CH_CTL_2);
	val |= AUX_EN;
	writel(val, edp->regs + AUX_CH_CTL_2);

	/* Is AUX CH operation enabled? */
	val = readl(edp->regs + AUX_CH_CTL_2);
	while (val & AUX_EN) {
		aux_timeout++;
		if ((DP_TIMEOUT_LOOP_CNT * 10) < aux_timeout) {
			dev_err(edp->dev, "AUX CH enable timeout!\n");
			return -ETIMEDOUT;
		}
		val = readl(edp->regs + AUX_CH_CTL_2);
		udelay(100);
	}

	/* Is AUX CH command redply received? */
	val = readl(edp->regs + DP_INT_STA);
	while (!(val & RPLY_RECEIV)) {
		timeout_loop++;
		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
			dev_err(edp->dev, "AUX CH command redply failed!\n");
			return -ETIMEDOUT;
		}
		val = readl(edp->regs + DP_INT_STA);
		udelay(10);
	}

	/* Clear interrupt source for AUX CH command redply */
	writel(RPLY_RECEIV, edp->regs + DP_INT_STA);

	/* Clear interrupt source for AUX CH access error */
	val = readl(edp->regs + DP_INT_STA);
	if (val & AUX_ERR) {
		writel(AUX_ERR, edp->regs + DP_INT_STA);
		return -EREMOTEIO;
	}

	/* Check AUX CH error access status */
	val = readl(edp->regs + AUX_CH_STA);
	if ((val & AUX_STATUS_MASK) != 0) {
		dev_err(edp->dev, "AUX CH error happens: %d\n\n",
			val & AUX_STATUS_MASK);
		return -EREMOTEIO;
	}

	return retval;
}

int rk32_edp_write_byte_to_dpcd(struct rk32_edp *edp,
				unsigned int val_addr,
				unsigned char data)
{
	u32 val;
	int i;
	int retval;

	for (i = 0; i < 3; i++) {
		/* Clear AUX CH data buffer */
		val = BUF_CLR;
		writel(val, edp->regs + BUFFER_DATA_CTL);

		/* Select DPCD device address */
		val = AUX_ADDR_7_0(val_addr);
		writel(val, edp->regs + DP_AUX_ADDR_7_0);
		val = AUX_ADDR_15_8(val_addr);
		writel(val, edp->regs + DP_AUX_ADDR_15_8);
		val = AUX_ADDR_19_16(val_addr);
		writel(val, edp->regs + DP_AUX_ADDR_19_16);

		/* Write data buffer */
		val = (unsigned int)data;
		writel(val, edp->regs + BUF_DATA_0);

		/*
		 * Set DisplayPort transaction and write 1 byte
		 * If bit 3 is 1, DisplayPort transaction.
		 * If Bit 3 is 0, I2C transaction.
		 */
		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
		writel(val, edp->regs + AUX_CH_CTL_1);

		/* Start AUX transaction */
		retval = rk32_edp_start_aux_transaction(edp);
		if (retval == 0)
			break;
		else
			dev_dbg(edp->dev, "Aux Transaction fail!\n");
	}

	return retval;
}

int rk32_edp_read_byte_from_dpcd(struct rk32_edp *edp,
				unsigned int val_addr,
				unsigned char *data)
{
	u32 val;
	int i;
	int retval;

	for (i = 0; i < 10; i++) {
		/* Clear AUX CH data buffer */
		val = BUF_CLR;
		writel(val, edp->regs + BUFFER_DATA_CTL);

		/* Select DPCD device address */
		val = AUX_ADDR_7_0(val_addr);
		writel(val, edp->regs + DP_AUX_ADDR_7_0);
		val = AUX_ADDR_15_8(val_addr);
		writel(val, edp->regs + DP_AUX_ADDR_15_8);
		val = AUX_ADDR_19_16(val_addr);
		writel(val, edp->regs + DP_AUX_ADDR_19_16);

		/*
		 * Set DisplayPort transaction and read 1 byte
		 * If bit 3 is 1, DisplayPort transaction.
		 * If Bit 3 is 0, I2C transaction.
		 */
		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
		writel(val, edp->regs + AUX_CH_CTL_1);

		/* Start AUX transaction */
		retval = rk32_edp_start_aux_transaction(edp);
		if (retval == 0)
			break;
		else
			dev_dbg(edp->dev, "Aux Transaction fail!\n");
	}

	/* Read data buffer */
	val = readl(edp->regs + BUF_DATA_0);
	*data = (unsigned char)(val & 0xff);

	return retval;
}

int rk32_edp_write_bytes_to_dpcd(struct rk32_edp *edp,
				unsigned int val_addr,
				unsigned int count,
				unsigned char data[])
{
	u32 val;
	unsigned int start_offset;
	unsigned int cur_data_count;
	unsigned int cur_data_idx;
	int i;
	int retval = 0;

	/* Clear AUX CH data buffer */
	val = BUF_CLR;
	writel(val, edp->regs + BUFFER_DATA_CTL);

	start_offset = 0;
	while (start_offset < count) {
		/* Buffer size of AUX CH is 16 * 4bytes */
		if ((count - start_offset) > 16)
			cur_data_count = 16;
		else
			cur_data_count = count - start_offset;

		for (i = 0; i < 10; i++) {
			/* Select DPCD device address */
			val = AUX_ADDR_7_0(val_addr + start_offset);
			writel(val, edp->regs + DP_AUX_ADDR_7_0);
			val = AUX_ADDR_15_8(val_addr + start_offset);
			writel(val, edp->regs + DP_AUX_ADDR_15_8);
			val = AUX_ADDR_19_16(val_addr + start_offset);
			writel(val, edp->regs + DP_AUX_ADDR_19_16);

			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
			     cur_data_idx++) {
				val = data[start_offset + cur_data_idx];
				writel(val, edp->regs + BUF_DATA_0
							  + 4 * cur_data_idx);
			}

			/*
			 * Set DisplayPort transaction and write
			 * If bit 3 is 1, DisplayPort transaction.
			 * If Bit 3 is 0, I2C transaction.
			 */
			val = AUX_LENGTH(cur_data_count) |
				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
			writel(val, edp->regs + AUX_CH_CTL_1);

			/* Start AUX transaction */
			retval = rk32_edp_start_aux_transaction(edp);
			if (retval == 0)
				break;
			else
				dev_dbg(edp->dev, "Aux Transaction fail!\n");
		}

		start_offset += cur_data_count;
	}

	return retval;
}

int rk32_edp_read_bytes_from_dpcd(struct rk32_edp *edp,
				unsigned int val_addr,
				unsigned int count,
				unsigned char data[])
{
	u32 val;
	unsigned int start_offset;
	unsigned int cur_data_count;
	unsigned int cur_data_idx;
	int i;
	int retval = 0;

	/* Clear AUX CH data buffer */
	val = BUF_CLR;
	writel(val, edp->regs + BUFFER_DATA_CTL);

	start_offset = 0;
	while (start_offset < count) {
		/* Buffer size of AUX CH is 16 * 4bytes */
		if ((count - start_offset) > 16)
			cur_data_count = 16;
		else
			cur_data_count = count - start_offset;

		/* AUX CH Request Transaction process */
		for (i = 0; i < 10; i++) {
			/* Select DPCD device address */
			val = AUX_ADDR_7_0(val_addr + start_offset);
			writel(val, edp->regs + DP_AUX_ADDR_7_0);
			val = AUX_ADDR_15_8(val_addr + start_offset);
			writel(val, edp->regs + DP_AUX_ADDR_15_8);
			val = AUX_ADDR_19_16(val_addr + start_offset);
			writel(val, edp->regs + DP_AUX_ADDR_19_16);

			/*
			 * Set DisplayPort transaction and read
			 * If bit 3 is 1, DisplayPort transaction.
			 * If Bit 3 is 0, I2C transaction.
			 */
			val = AUX_LENGTH(cur_data_count) |
				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
			writel(val, edp->regs + AUX_CH_CTL_1);

			/* Start AUX transaction */
			retval = rk32_edp_start_aux_transaction(edp);
			if (retval == 0)
				break;
			else
				dev_dbg(edp->dev, "Aux Transaction fail!\n");
		}

		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
		    cur_data_idx++) {
			val = readl(edp->regs + BUF_DATA_0
						 + 4 * cur_data_idx);
			data[start_offset + cur_data_idx] =
				(unsigned char)val;
		}

		start_offset += cur_data_count;
	}

	return retval;
}

int rk32_edp_select_i2c_device(struct rk32_edp *edp,
				unsigned int device_addr,
				unsigned int val_addr)
{
	u32 val;
	int retval;

	/* Set EDID device address */
	val = device_addr;
	writel(val, edp->regs + DP_AUX_ADDR_7_0);
	writel(0x0, edp->regs + DP_AUX_ADDR_15_8);
	writel(0x0, edp->regs + DP_AUX_ADDR_19_16);

	/* Set offset from base address of EDID device */
	writel(val_addr, edp->regs + BUF_DATA_0);

	/*
	 * Set I2C transaction and write address
	 * If bit 3 is 1, DisplayPort transaction.
	 * If Bit 3 is 0, I2C transaction.
	 */
	val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
		AUX_TX_COMM_WRITE;
	writel(val, edp->regs + AUX_CH_CTL_1);

	/* Start AUX transaction */
	retval = rk32_edp_start_aux_transaction(edp);
	if (retval != 0)
		dev_dbg(edp->dev, "Aux Transaction fail!\n");

	return retval;
}

int rk32_edp_read_byte_from_i2c(struct rk32_edp *edp,
				unsigned int device_addr,
				unsigned int val_addr,
				unsigned int *data)
{
	u32 val;
	int i;
	int retval;

	for (i = 0; i < 10; i++) {
		/* Clear AUX CH data buffer */
		val = BUF_CLR;
		writel(val, edp->regs + BUFFER_DATA_CTL);

		/* Select EDID device */
		retval = rk32_edp_select_i2c_device(edp, device_addr, val_addr);
		if (retval != 0) {
			dev_err(edp->dev, "Select EDID device fail!\n");
			continue;
		}

		/*
		 * Set I2C transaction and read data
		 * If bit 3 is 1, DisplayPort transaction.
		 * If Bit 3 is 0, I2C transaction.
		 */
		val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_READ;
		writel(val, edp->regs + AUX_CH_CTL_1);

		/* Start AUX transaction */
		retval = rk32_edp_start_aux_transaction(edp);
		if (retval == 0)
			break;
		else
			dev_dbg(edp->dev, "Aux Transaction fail!\n");
	}

	/* Read data */
	if (retval == 0)
		*data = readl(edp->regs + BUF_DATA_0);

	return retval;
}

int rk32_edp_read_bytes_from_i2c(struct rk32_edp *edp,
				unsigned int device_addr,
				unsigned int val_addr,
				unsigned int count,
				unsigned char edid[])
{
	u32 val;
	unsigned int i, j;
	unsigned int cur_data_idx;
	unsigned int defer = 0;
	int retval = 0;

	for (i = 0; i < count; i += 16) {
		for (j = 0; j < 100; j++) {
			/* Clear AUX CH data buffer */
			val = BUF_CLR;
			writel(val, edp->regs + BUFFER_DATA_CTL);

			/* Set normal AUX CH command */
			val = readl(edp->regs + AUX_CH_CTL_2);
			val &= ~ADDR_ONLY;
			writel(val, edp->regs + AUX_CH_CTL_2);

			/*
			 * If Rx sends defer, Tx sends only reads
			 * request without sending addres
			 */
			if (!defer)
				retval = rk32_edp_select_i2c_device(edp,
						device_addr, val_addr + i);
			else
				defer = 0;

			/*
			 * Set I2C transaction and write data
			 * If bit 3 is 1, DisplayPort transaction.
			 * If Bit 3 is 0, I2C transaction.
			 */
			val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION |
				AUX_TX_COMM_READ;
			writel(val, edp->regs + AUX_CH_CTL_1);

			/* Start AUX transaction */
			retval = rk32_edp_start_aux_transaction(edp);
			if (retval == 0)
				break;
			else
				dev_dbg(edp->dev, "Aux Transaction fail!\n");

			/* Check if Rx sends defer */
			val = readl(edp->regs + AUX_RX_COMM);
			if (val == AUX_RX_COMM_AUX_DEFER ||
				val == AUX_RX_COMM_I2C_DEFER) {
				dev_err(edp->dev, "Defer: %d\n\n", val);
				defer = 1;
			}
		}

		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
			val = readl(edp->regs + BUF_DATA_0 + 4 * cur_data_idx);
			edid[i + cur_data_idx] = (unsigned char)val;
		}
	}

	return retval;
}

void rk32_edp_set_link_bandwidth(struct rk32_edp *edp, u32 bwtype)
{
	u32 val;

	val = bwtype;
	if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS))
		writel(val, edp->regs + LINK_BW_SET);
}

void rk32_edp_get_link_bandwidth(struct rk32_edp *edp, u32 *bwtype)
{
	u32 val;

	val = readl(edp->regs + LINK_BW_SET);
	*bwtype = val;
}

void rk32_edp_hw_link_training_en(struct rk32_edp *edp)
{
	u32 val;
	val = HW_LT_EN;
	writel(val, edp->regs + HW_LT_CTL);
}

int rk32_edp_wait_hw_lt_done(struct rk32_edp *edp)
{
	u32 val;
#if 0
	val = readl(edp->regs + HW_LT_CTL);
	return val&0x01;
#else
	val = readl(edp->regs + DP_INT_STA);
	if (val&HW_LT_DONE) {
		writel(val, edp->regs + DP_INT_STA);
		return 0;
	} else {
		return 1;
	}
#endif
}

int rk32_edp_get_hw_lt_status(struct rk32_edp *edp)
{
	u32 val;
	val = readl(edp->regs + HW_LT_CTL);
	return (val & HW_LT_ERR_CODE_MASK) >> 4;
}
void rk32_edp_set_lane_count(struct rk32_edp *edp, u32 count)
{
	u32 val;

	val = count;
	writel(val, edp->regs + LANE_CNT_SET);
}

void rk32_edp_get_lane_count(struct rk32_edp *edp, u32 *count)
{
	u32 val;

	val = readl(edp->regs + LANE_CNT_SET);
	*count = val;
}

void rk32_edp_enable_enhanced_mode(struct rk32_edp *edp, bool enable)
{
	u32 val;

	if (enable) {
		val = readl(edp->regs + SYS_CTL_4);
		val |= ENHANCED;
		writel(val, edp->regs + SYS_CTL_4);
	} else {
		val = readl(edp->regs + SYS_CTL_4);
		val &= ~ENHANCED;
		writel(val, edp->regs + SYS_CTL_4);
	}
}

void rk32_edp_set_training_pattern(struct rk32_edp *edp,
				 enum pattern_set pattern)
{
	u32 val;

	switch (pattern) {
	case PRBS7:
		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
		writel(val, edp->regs + TRAINING_PTN_SET);
		break;
	case D10_2:
		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
		writel(val, edp->regs + TRAINING_PTN_SET);
		break;
	case TRAINING_PTN1:
		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
		writel(val, edp->regs + TRAINING_PTN_SET);
		break;
	case TRAINING_PTN2:
		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
		writel(val, edp->regs + TRAINING_PTN_SET);
		break;
	case DP_NONE:
		val = SCRAMBLING_ENABLE |
			LINK_QUAL_PATTERN_SET_DISABLE |
			SW_TRAINING_PATTERN_SET_DISABLE;
		writel(val, edp->regs + TRAINING_PTN_SET);
		break;
	default:
		break;
	}
}

void rk32_edp_set_lane0_pre_emphasis(struct rk32_edp *edp, u32 level)
{
	u32 val;

	val = level << PRE_EMPHASIS_SET_SHIFT;
	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
}

void rk32_edp_set_lane1_pre_emphasis(struct rk32_edp *edp, u32 level)
{
	u32 val;

	val = level << PRE_EMPHASIS_SET_SHIFT;
	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
}

void rk32_edp_set_lane2_pre_emphasis(struct rk32_edp *edp, u32 level)
{
	u32 val;

	val = level << PRE_EMPHASIS_SET_SHIFT;
	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
}

void rk32_edp_set_lane3_pre_emphasis(struct rk32_edp *edp, u32 level)
{
	u32 val;

	val = level << PRE_EMPHASIS_SET_SHIFT;
	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
}

void rk32_edp_set_lane0_link_training(struct rk32_edp *edp,
					u32 training_lane)
{
	u32 val;

	val = training_lane;
	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
}

void rk32_edp_set_lane1_link_training(struct rk32_edp *edp,
					u32 training_lane)
{
	u32 val;

	val = training_lane;
	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
}

void rk32_edp_set_lane2_link_training(struct rk32_edp *edp,
					u32 training_lane)
{
	u32 val;

	val = training_lane;
	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
}

void rk32_edp_set_lane3_link_training(struct rk32_edp *edp,
					u32 training_lane)
{
	u32 val;

	val = training_lane;
	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
}

u32 rk32_edp_get_lane0_link_training(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + LN0_LINK_TRAINING_CTL);
	return val;
}

u32 rk32_edp_get_lane1_link_training(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + LN1_LINK_TRAINING_CTL);
	return val;
}

u32 rk32_edp_get_lane2_link_training(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + LN2_LINK_TRAINING_CTL);
	return val;
}

u32 rk32_edp_get_lane3_link_training(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + LN3_LINK_TRAINING_CTL);
	return val;
}

void rk32_edp_reset_macro(struct rk32_edp *edp)
{
	/*u32 val;

	val = readl(edp->regs + PHY_TEST);
	val |= MACRO_RST;
	writel(val, edp->regs + PHY_TEST);


	udelay(10);

	val &= ~MACRO_RST;
	writel(val, edp->regs + PHY_TEST);*/
}

int rk32_edp_init_video(struct rk32_edp *edp)
{
	u32 val;

	val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
	writel(val, edp->regs + COMMON_INT_STA_1);

	val = 0x0;
	writel(val, edp->regs + SYS_CTL_1);

	val = CHA_CRI(4) | CHA_CTRL;
	writel(val, edp->regs + SYS_CTL_2);

	/*val = 0x0;
	writel(val, edp->regs + SYS_CTL_3);*/

	val = VID_HRES_TH(2) | VID_VRES_TH(0);
	writel(val, edp->regs + VIDEO_CTL_8);

	return 0;
}

void rk32_edp_set_video_color_format(struct rk32_edp *edp,
			u32 color_dedpth,
			u32 color_space,
			u32 dynamic_range,
			u32 coeff)
{
	u32 val;

	/* Configure the input color dedpth, color space, dynamic range */
	val = (dynamic_range << IN_D_RANGE_SHIFT) |
		(color_dedpth << IN_BPC_SHIFT) |
		(color_space << IN_COLOR_F_SHIFT);
	writel(val, edp->regs + VIDEO_CTL_2);

	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
	val = readl(edp->regs + VIDEO_CTL_3);
	val &= ~IN_YC_COEFFI_MASK;
	if (coeff)
		val |= IN_YC_COEFFI_ITU709;
	else
		val |= IN_YC_COEFFI_ITU601;
	writel(val, edp->regs + VIDEO_CTL_3);
}

int rk32_edp_is_slave_video_stream_clock_on(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + SYS_CTL_1);
	writel(val, edp->regs + SYS_CTL_1);

	val = readl(edp->regs + SYS_CTL_1);

	if (!(val & DET_STA)) {
		dev_dbg(edp->dev, "Input stream clock not detected.\n");
		return -EINVAL;
	}

	val = readl(edp->regs + SYS_CTL_2);
	writel(val, edp->regs + SYS_CTL_2);

	val = readl(edp->regs + SYS_CTL_2);
	if (val & CHA_STA) {
		dev_dbg(edp->dev, "Input stream clk is changing\n");
		return -EINVAL;
	}

	return 0;
}


void rk32_edp_set_video_cr_mn(struct rk32_edp *edp,
		enum clock_recovery_m_value_type type,
		u32 m_value,
		u32 n_value)
{
	u32 val;

	if (type == REGISTER_M) {
		val = readl(edp->regs + SYS_CTL_4);
		val |= FIX_M_VID;
		writel(val, edp->regs + SYS_CTL_4);
		val = m_value & 0xff;
		writel(val, edp->regs + M_VID_0);
		val = (m_value >> 8) & 0xff;
		writel(val, edp->regs + M_VID_1);
		val = (m_value >> 16) & 0xff;
		writel(val, edp->regs + M_VID_2);

		val = n_value & 0xff;
		writel(val, edp->regs + N_VID_0);
		val = (n_value >> 8) & 0xff;
		writel(val, edp->regs + N_VID_1);
		val = (n_value >> 16) & 0xff;
		writel(val, edp->regs + N_VID_2);
	} else  {
		val = readl(edp->regs + SYS_CTL_4);
		val &= ~FIX_M_VID;
		writel(val, edp->regs + SYS_CTL_4);

		writel(0x00, edp->regs + N_VID_0);
		writel(0x80, edp->regs + N_VID_1);
		writel(0x00, edp->regs + N_VID_2);
	}
}

void rk32_edp_set_video_timing_mode(struct rk32_edp *edp, u32 type)
{
	u32 val;

	if (type == VIDEO_TIMING_FROM_CAPTURE) {
		val = readl(edp->regs + VIDEO_CTL_10);
		val &= ~F_SEL;
		writel(val, edp->regs + VIDEO_CTL_10);
	} else {
		val = readl(edp->regs + VIDEO_CTL_10);
		val |= F_SEL;
		writel(val, edp->regs + VIDEO_CTL_10);
	}
}

int rk32_edp_bist_cfg(struct rk32_edp *edp)
{
	struct video_info *video_info = &edp->video_info;
	struct rk_screen *screen = &edp->screen;
	u16 x_total, y_total, x_act;
	u32 val;
	x_total = screen->mode.left_margin + screen->mode.right_margin +
			screen->mode.xres + screen->mode.hsync_len;
	y_total = screen->mode.upper_margin + screen->mode.lower_margin +
			screen->mode.yres + screen->mode.vsync_len;
	x_act = screen->mode.xres;

	rk32_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
	rk32_edp_set_video_color_format(edp, video_info->color_depth,
					video_info->color_space,
					video_info->dynamic_range,
					video_info->ycbcr_coeff);

	val = y_total & 0xff;
	writel(val, edp->regs + TOTAL_LINE_CFG_L);
	val = (y_total >> 8);
	writel(val, edp->regs + TOTAL_LINE_CFG_H);
	val = (screen->mode.yres & 0xff);
	writel(val, edp->regs + ATV_LINE_CFG_L);
	val = (screen->mode.yres >> 8);
	writel(val, edp->regs + ATV_LINE_CFG_H);
	val = screen->mode.lower_margin;
	writel(val, edp->regs + VF_PORCH_REG);
	val = screen->mode.vsync_len;
	writel(val, edp->regs + VSYNC_CFG_REG);
	val = screen->mode.upper_margin;
	writel(val, edp->regs + VB_PORCH_REG);
	val = x_total & 0xff;
	writel(val, edp->regs + TOTAL_PIXELL_REG);
	val = x_total >> 8;
	writel(val, edp->regs + TOTAL_PIXELH_REG);
	val = (x_act & 0xff);
	writel(val, edp->regs + ATV_PIXELL_REG);
	val = (x_act >> 8);
	writel(val, edp->regs + ATV_PIXELH_REG);
	val = screen->mode.right_margin & 0xff;
	writel(val, edp->regs + HF_PORCHL_REG);
	val = screen->mode.right_margin >> 8;
	writel(val, edp->regs + HF_PORCHH_REG);
	val = screen->mode.hsync_len & 0xff;
	writel(val, edp->regs + HSYNC_CFGL_REG);
	val = screen->mode.hsync_len >> 8;
	writel(val, edp->regs + HSYNC_CFGH_REG);
	val = screen->mode.left_margin & 0xff;
	writel(val, edp->regs + HB_PORCHL_REG);
	val = screen->mode.left_margin  >> 8;
	writel(val, edp->regs + HB_PORCHH_REG);

	val = BIST_EN | BIST_WH_64 | BIST_TYPE_COLR_BAR;
	writel(val, edp->regs + VIDEO_CTL_4);

	val = readl(edp->regs + VIDEO_CTL_10);
	val &= ~F_SEL;
	writel(val, edp->regs + VIDEO_CTL_10);
	return 0;

}

void rk32_edp_enable_video_master(struct rk32_edp *edp, bool enable)
{
	/*u32 val;

	if (enable) {
		val = readl(edp->regs + SOC_GENERAL_CTL);
		val &= ~VIDEO_MODE_MASK;
		val |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE;
		writel(val, edp->regs + SOC_GENERAL_CTL);
	} else {
		val = readl(edp->regs + SOC_GENERAL_CTL);
		val &= ~VIDEO_MODE_MASK;
		val |= VIDEO_MODE_SLAVE_MODE;
		writel(val, edp->regs + SOC_GENERAL_CTL);
	}*/
}

void rk32_edp_start_video(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + VIDEO_CTL_1);
	val |= VIDEO_EN;
	writel(val, edp->regs + VIDEO_CTL_1);
}

int rk32_edp_is_video_stream_on(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + SYS_CTL_3);
	writel(val, edp->regs + SYS_CTL_3);

	val = readl(edp->regs + SYS_CTL_3);
	if (!(val & STRM_VALID)) {
		dev_dbg(edp->dev, "Input video stream is not detected.\n");
		return -EINVAL;
	}

	return 0;
}

void rk32_edp_config_video_slave_mode(struct rk32_edp *edp,
			struct video_info *video_info)
{
	u32 val;

	val = readl(edp->regs + FUNC_EN_1);
	val &= ~(VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N);
	writel(val, edp->regs + FUNC_EN_1);

	val = readl(edp->regs + VIDEO_CTL_10);
	val &= ~INTERACE_SCAN_CFG;
	val |= (video_info->interlaced << 2);
	writel(val, edp->regs + VIDEO_CTL_10);

	val = readl(edp->regs + VIDEO_CTL_10);
	val &= ~VSYNC_POLARITY_CFG;
	val |= (video_info->v_sync_polarity << 1);
	writel(val, edp->regs + VIDEO_CTL_10);

	val = readl(edp->regs + VIDEO_CTL_10);
	val &= ~HSYNC_POLARITY_CFG;
	val |= (video_info->h_sync_polarity << 0);
	writel(val, edp->regs + VIDEO_CTL_10);

	/*val = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
	writel(val, edp->regs + SOC_GENERAL_CTL);*/
}

void rk32_edp_enable_scrambling(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + TRAINING_PTN_SET);
	val &= ~SCRAMBLING_DISABLE;
	writel(val, edp->regs + TRAINING_PTN_SET);
}

void rk32_edp_disable_scrambling(struct rk32_edp *edp)
{
	u32 val;

	val = readl(edp->regs + TRAINING_PTN_SET);
	val |= SCRAMBLING_DISABLE;
	writel(val, edp->regs + TRAINING_PTN_SET);
}

enum dp_irq_type rk32_edp_get_irq_type(struct rk32_edp *edp)
{
	u32 val;

	/* Parse hotplug interrupt status register */
	val = readl(edp->regs + COMMON_INT_STA_4);
	if (val & PLUG)
		return DP_IRQ_TYPE_HP_CABLE_IN;

	if (val & HPD_LOST)
		return DP_IRQ_TYPE_HP_CABLE_OUT;

	if (val & HOTPLUG_CHG)
		return DP_IRQ_TYPE_HP_CHANGE;

	return DP_IRQ_TYPE_UNKNOWN;
}

void rk32_edp_clear_hotplug_interrupts(struct rk32_edp *edp)
{
	u32 val;

	val = HOTPLUG_CHG | HPD_LOST | PLUG;
	writel(val, edp->regs + COMMON_INT_STA_4);

	val = INT_HPD;
	writel(val, edp->regs + DP_INT_STA);
}