108a7aa1eSSimon Glass /*
208a7aa1eSSimon Glass  * Copyright (C) 2012 Samsung Electronics
308a7aa1eSSimon Glass  *
408a7aa1eSSimon Glass  * Author: Donghwa Lee <dh09.lee@samsung.com>
508a7aa1eSSimon Glass  *
608a7aa1eSSimon Glass  * SPDX-License-Identifier:	GPL-2.0+
708a7aa1eSSimon Glass  */
808a7aa1eSSimon Glass 
94af0d7e8SSimon Glass #include <common.h>
10bb5930d5SSimon Glass #include <dm.h>
1108a7aa1eSSimon Glass #include <common.h>
12bb5930d5SSimon Glass #include <display.h>
13bb5930d5SSimon Glass #include <fdtdec.h>
14bb5930d5SSimon Glass #include <libfdt.h>
1508a7aa1eSSimon Glass #include <malloc.h>
16bb5930d5SSimon Glass #include <video_bridge.h>
1708a7aa1eSSimon Glass #include <linux/compat.h>
1808a7aa1eSSimon Glass #include <linux/err.h>
1908a7aa1eSSimon Glass #include <asm/arch/clk.h>
2008a7aa1eSSimon Glass #include <asm/arch/cpu.h>
2108a7aa1eSSimon Glass #include <asm/arch/dp_info.h>
2208a7aa1eSSimon Glass #include <asm/arch/dp.h>
23bb5930d5SSimon Glass #include <asm/arch/pinmux.h>
247eb860dfSSimon Glass #include <asm/arch/power.h>
2508a7aa1eSSimon Glass 
2608a7aa1eSSimon Glass #include "exynos_dp_lowlevel.h"
2708a7aa1eSSimon Glass 
2808a7aa1eSSimon Glass DECLARE_GLOBAL_DATA_PTR;
2908a7aa1eSSimon Glass 
3008a7aa1eSSimon Glass static void exynos_dp_disp_info(struct edp_disp_info *disp_info)
3108a7aa1eSSimon Glass {
3208a7aa1eSSimon Glass 	disp_info->h_total = disp_info->h_res + disp_info->h_sync_width +
3308a7aa1eSSimon Glass 		disp_info->h_back_porch + disp_info->h_front_porch;
3408a7aa1eSSimon Glass 	disp_info->v_total = disp_info->v_res + disp_info->v_sync_width +
3508a7aa1eSSimon Glass 		disp_info->v_back_porch + disp_info->v_front_porch;
3608a7aa1eSSimon Glass 
3708a7aa1eSSimon Glass 	return;
3808a7aa1eSSimon Glass }
3908a7aa1eSSimon Glass 
408b449a66SSimon Glass static int exynos_dp_init_dp(struct exynos_dp *regs)
4108a7aa1eSSimon Glass {
4208a7aa1eSSimon Glass 	int ret;
438b449a66SSimon Glass 	exynos_dp_reset(regs);
4408a7aa1eSSimon Glass 
4508a7aa1eSSimon Glass 	/* SW defined function Normal operation */
468b449a66SSimon Glass 	exynos_dp_enable_sw_func(regs, DP_ENABLE);
4708a7aa1eSSimon Glass 
488b449a66SSimon Glass 	ret = exynos_dp_init_analog_func(regs);
4908a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS)
5008a7aa1eSSimon Glass 		return ret;
5108a7aa1eSSimon Glass 
528b449a66SSimon Glass 	exynos_dp_init_hpd(regs);
538b449a66SSimon Glass 	exynos_dp_init_aux(regs);
5408a7aa1eSSimon Glass 
5508a7aa1eSSimon Glass 	return ret;
5608a7aa1eSSimon Glass }
5708a7aa1eSSimon Glass 
5808a7aa1eSSimon Glass static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data)
5908a7aa1eSSimon Glass {
6008a7aa1eSSimon Glass 	int i;
6108a7aa1eSSimon Glass 	unsigned char sum = 0;
6208a7aa1eSSimon Glass 
6308a7aa1eSSimon Glass 	for (i = 0; i < EDID_BLOCK_LENGTH; i++)
6408a7aa1eSSimon Glass 		sum = sum + edid_data[i];
6508a7aa1eSSimon Glass 
6608a7aa1eSSimon Glass 	return sum;
6708a7aa1eSSimon Glass }
6808a7aa1eSSimon Glass 
698b449a66SSimon Glass static unsigned int exynos_dp_read_edid(struct exynos_dp *regs)
7008a7aa1eSSimon Glass {
7108a7aa1eSSimon Glass 	unsigned char edid[EDID_BLOCK_LENGTH * 2];
7208a7aa1eSSimon Glass 	unsigned int extend_block = 0;
7308a7aa1eSSimon Glass 	unsigned char sum;
7408a7aa1eSSimon Glass 	unsigned char test_vector;
7508a7aa1eSSimon Glass 	int retval;
7608a7aa1eSSimon Glass 
7708a7aa1eSSimon Glass 	/*
7808a7aa1eSSimon Glass 	 * EDID device address is 0x50.
7908a7aa1eSSimon Glass 	 * However, if necessary, you must have set upper address
8008a7aa1eSSimon Glass 	 * into E-EDID in I2C device, 0x30.
8108a7aa1eSSimon Glass 	 */
8208a7aa1eSSimon Glass 
8308a7aa1eSSimon Glass 	/* Read Extension Flag, Number of 128-byte EDID extension blocks */
848b449a66SSimon Glass 	exynos_dp_read_byte_from_i2c(regs, I2C_EDID_DEVICE_ADDR,
858c9b8dc0SSimon Glass 				     EDID_EXTENSION_FLAG, &extend_block);
8608a7aa1eSSimon Glass 
8708a7aa1eSSimon Glass 	if (extend_block > 0) {
8808a7aa1eSSimon Glass 		printf("DP EDID data includes a single extension!\n");
8908a7aa1eSSimon Glass 
9008a7aa1eSSimon Glass 		/* Read EDID data */
918b449a66SSimon Glass 		retval = exynos_dp_read_bytes_from_i2c(regs,
928c9b8dc0SSimon Glass 						I2C_EDID_DEVICE_ADDR,
9308a7aa1eSSimon Glass 						EDID_HEADER_PATTERN,
9408a7aa1eSSimon Glass 						EDID_BLOCK_LENGTH,
9508a7aa1eSSimon Glass 						&edid[EDID_HEADER_PATTERN]);
9608a7aa1eSSimon Glass 		if (retval != 0) {
9708a7aa1eSSimon Glass 			printf("DP EDID Read failed!\n");
9808a7aa1eSSimon Glass 			return -1;
9908a7aa1eSSimon Glass 		}
10008a7aa1eSSimon Glass 		sum = exynos_dp_calc_edid_check_sum(edid);
10108a7aa1eSSimon Glass 		if (sum != 0) {
10208a7aa1eSSimon Glass 			printf("DP EDID bad checksum!\n");
10308a7aa1eSSimon Glass 			return -1;
10408a7aa1eSSimon Glass 		}
10508a7aa1eSSimon Glass 
10608a7aa1eSSimon Glass 		/* Read additional EDID data */
1078b449a66SSimon Glass 		retval = exynos_dp_read_bytes_from_i2c(regs,
1088c9b8dc0SSimon Glass 				I2C_EDID_DEVICE_ADDR,
10908a7aa1eSSimon Glass 				EDID_BLOCK_LENGTH,
11008a7aa1eSSimon Glass 				EDID_BLOCK_LENGTH,
11108a7aa1eSSimon Glass 				&edid[EDID_BLOCK_LENGTH]);
11208a7aa1eSSimon Glass 		if (retval != 0) {
11308a7aa1eSSimon Glass 			printf("DP EDID Read failed!\n");
11408a7aa1eSSimon Glass 			return -1;
11508a7aa1eSSimon Glass 		}
11608a7aa1eSSimon Glass 		sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
11708a7aa1eSSimon Glass 		if (sum != 0) {
11808a7aa1eSSimon Glass 			printf("DP EDID bad checksum!\n");
11908a7aa1eSSimon Glass 			return -1;
12008a7aa1eSSimon Glass 		}
12108a7aa1eSSimon Glass 
1228b449a66SSimon Glass 		exynos_dp_read_byte_from_dpcd(regs, DPCD_TEST_REQUEST,
12308a7aa1eSSimon Glass 					      &test_vector);
12408a7aa1eSSimon Glass 		if (test_vector & DPCD_TEST_EDID_READ) {
1258b449a66SSimon Glass 			exynos_dp_write_byte_to_dpcd(regs,
1268c9b8dc0SSimon Glass 				DPCD_TEST_EDID_CHECKSUM,
12708a7aa1eSSimon Glass 				edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
1288b449a66SSimon Glass 			exynos_dp_write_byte_to_dpcd(regs,
1298c9b8dc0SSimon Glass 				DPCD_TEST_RESPONSE,
13008a7aa1eSSimon Glass 				DPCD_TEST_EDID_CHECKSUM_WRITE);
13108a7aa1eSSimon Glass 		}
13208a7aa1eSSimon Glass 	} else {
13308a7aa1eSSimon Glass 		debug("DP EDID data does not include any extensions.\n");
13408a7aa1eSSimon Glass 
13508a7aa1eSSimon Glass 		/* Read EDID data */
1368b449a66SSimon Glass 		retval = exynos_dp_read_bytes_from_i2c(regs,
1378c9b8dc0SSimon Glass 				I2C_EDID_DEVICE_ADDR,
13808a7aa1eSSimon Glass 				EDID_HEADER_PATTERN,
13908a7aa1eSSimon Glass 				EDID_BLOCK_LENGTH,
14008a7aa1eSSimon Glass 				&edid[EDID_HEADER_PATTERN]);
14108a7aa1eSSimon Glass 
14208a7aa1eSSimon Glass 		if (retval != 0) {
14308a7aa1eSSimon Glass 			printf("DP EDID Read failed!\n");
14408a7aa1eSSimon Glass 			return -1;
14508a7aa1eSSimon Glass 		}
14608a7aa1eSSimon Glass 		sum = exynos_dp_calc_edid_check_sum(edid);
14708a7aa1eSSimon Glass 		if (sum != 0) {
14808a7aa1eSSimon Glass 			printf("DP EDID bad checksum!\n");
14908a7aa1eSSimon Glass 			return -1;
15008a7aa1eSSimon Glass 		}
15108a7aa1eSSimon Glass 
1528b449a66SSimon Glass 		exynos_dp_read_byte_from_dpcd(regs, DPCD_TEST_REQUEST,
15308a7aa1eSSimon Glass 			&test_vector);
15408a7aa1eSSimon Glass 		if (test_vector & DPCD_TEST_EDID_READ) {
1558b449a66SSimon Glass 			exynos_dp_write_byte_to_dpcd(regs,
1568c9b8dc0SSimon Glass 				DPCD_TEST_EDID_CHECKSUM, edid[EDID_CHECKSUM]);
1578b449a66SSimon Glass 			exynos_dp_write_byte_to_dpcd(regs,
1588c9b8dc0SSimon Glass 				DPCD_TEST_RESPONSE,
15908a7aa1eSSimon Glass 				DPCD_TEST_EDID_CHECKSUM_WRITE);
16008a7aa1eSSimon Glass 		}
16108a7aa1eSSimon Glass 	}
16208a7aa1eSSimon Glass 
16308a7aa1eSSimon Glass 	debug("DP EDID Read success!\n");
16408a7aa1eSSimon Glass 
16508a7aa1eSSimon Glass 	return 0;
16608a7aa1eSSimon Glass }
16708a7aa1eSSimon Glass 
1688b449a66SSimon Glass static unsigned int exynos_dp_handle_edid(struct exynos_dp *regs,
1698b449a66SSimon Glass 					  struct exynos_dp_priv *priv)
17008a7aa1eSSimon Glass {
17108a7aa1eSSimon Glass 	unsigned char buf[12];
17208a7aa1eSSimon Glass 	unsigned int ret;
17308a7aa1eSSimon Glass 	unsigned char temp;
17408a7aa1eSSimon Glass 	unsigned char retry_cnt;
17508a7aa1eSSimon Glass 	unsigned char dpcd_rev[16];
17608a7aa1eSSimon Glass 	unsigned char lane_bw[16];
17708a7aa1eSSimon Glass 	unsigned char lane_cnt[16];
17808a7aa1eSSimon Glass 
17908a7aa1eSSimon Glass 	memset(dpcd_rev, 0, 16);
18008a7aa1eSSimon Glass 	memset(lane_bw, 0, 16);
18108a7aa1eSSimon Glass 	memset(lane_cnt, 0, 16);
18208a7aa1eSSimon Glass 	memset(buf, 0, 12);
18308a7aa1eSSimon Glass 
18408a7aa1eSSimon Glass 	retry_cnt = 5;
18508a7aa1eSSimon Glass 	while (retry_cnt) {
18608a7aa1eSSimon Glass 		/* Read DPCD 0x0000-0x000b */
1878b449a66SSimon Glass 		ret = exynos_dp_read_bytes_from_dpcd(regs, DPCD_DPCD_REV, 12,
18808a7aa1eSSimon Glass 						     buf);
18908a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS) {
19008a7aa1eSSimon Glass 			if (retry_cnt == 0) {
19108a7aa1eSSimon Glass 				printf("DP read_byte_from_dpcd() failed\n");
19208a7aa1eSSimon Glass 				return ret;
19308a7aa1eSSimon Glass 			}
19408a7aa1eSSimon Glass 			retry_cnt--;
19508a7aa1eSSimon Glass 		} else
19608a7aa1eSSimon Glass 			break;
19708a7aa1eSSimon Glass 	}
19808a7aa1eSSimon Glass 
19908a7aa1eSSimon Glass 	/* */
20008a7aa1eSSimon Glass 	temp = buf[DPCD_DPCD_REV];
20108a7aa1eSSimon Glass 	if (temp == DP_DPCD_REV_10 || temp == DP_DPCD_REV_11)
2028b449a66SSimon Glass 		priv->dpcd_rev = temp;
20308a7aa1eSSimon Glass 	else {
20408a7aa1eSSimon Glass 		printf("DP Wrong DPCD Rev : %x\n", temp);
20508a7aa1eSSimon Glass 		return -ENODEV;
20608a7aa1eSSimon Glass 	}
20708a7aa1eSSimon Glass 
20808a7aa1eSSimon Glass 	temp = buf[DPCD_MAX_LINK_RATE];
20908a7aa1eSSimon Glass 	if (temp == DP_LANE_BW_1_62 || temp == DP_LANE_BW_2_70)
2108b449a66SSimon Glass 		priv->lane_bw = temp;
21108a7aa1eSSimon Glass 	else {
21208a7aa1eSSimon Glass 		printf("DP Wrong MAX LINK RATE : %x\n", temp);
21308a7aa1eSSimon Glass 		return -EINVAL;
21408a7aa1eSSimon Glass 	}
21508a7aa1eSSimon Glass 
21608a7aa1eSSimon Glass 	/* Refer VESA Display Port Standard Ver1.1a Page 120 */
2178b449a66SSimon Glass 	if (priv->dpcd_rev == DP_DPCD_REV_11) {
21808a7aa1eSSimon Glass 		temp = buf[DPCD_MAX_LANE_COUNT] & 0x1f;
21908a7aa1eSSimon Glass 		if (buf[DPCD_MAX_LANE_COUNT] & 0x80)
2208b449a66SSimon Glass 			priv->dpcd_efc = 1;
22108a7aa1eSSimon Glass 		else
2228b449a66SSimon Glass 			priv->dpcd_efc = 0;
22308a7aa1eSSimon Glass 	} else {
22408a7aa1eSSimon Glass 		temp = buf[DPCD_MAX_LANE_COUNT];
2258b449a66SSimon Glass 		priv->dpcd_efc = 0;
22608a7aa1eSSimon Glass 	}
22708a7aa1eSSimon Glass 
22808a7aa1eSSimon Glass 	if (temp == DP_LANE_CNT_1 || temp == DP_LANE_CNT_2 ||
22908a7aa1eSSimon Glass 			temp == DP_LANE_CNT_4) {
2308b449a66SSimon Glass 		priv->lane_cnt = temp;
23108a7aa1eSSimon Glass 	} else {
23208a7aa1eSSimon Glass 		printf("DP Wrong MAX LANE COUNT : %x\n", temp);
23308a7aa1eSSimon Glass 		return -EINVAL;
23408a7aa1eSSimon Glass 	}
23508a7aa1eSSimon Glass 
2368b449a66SSimon Glass 	ret = exynos_dp_read_edid(regs);
23708a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
23808a7aa1eSSimon Glass 		printf("DP exynos_dp_read_edid() failed\n");
23908a7aa1eSSimon Glass 		return -EINVAL;
24008a7aa1eSSimon Glass 	}
24108a7aa1eSSimon Glass 
24208a7aa1eSSimon Glass 	return ret;
24308a7aa1eSSimon Glass }
24408a7aa1eSSimon Glass 
2458b449a66SSimon Glass static void exynos_dp_init_training(struct exynos_dp *regs)
24608a7aa1eSSimon Glass {
24708a7aa1eSSimon Glass 	/*
24808a7aa1eSSimon Glass 	 * MACRO_RST must be applied after the PLL_LOCK to avoid
24908a7aa1eSSimon Glass 	 * the DP inter pair skew issue for at least 10 us
25008a7aa1eSSimon Glass 	 */
2518b449a66SSimon Glass 	exynos_dp_reset_macro(regs);
25208a7aa1eSSimon Glass 
25308a7aa1eSSimon Glass 	/* All DP analog module power up */
2548b449a66SSimon Glass 	exynos_dp_set_analog_power_down(regs, POWER_ALL, 0);
25508a7aa1eSSimon Glass }
25608a7aa1eSSimon Glass 
2578b449a66SSimon Glass static unsigned int exynos_dp_link_start(struct exynos_dp *regs,
2588b449a66SSimon Glass 					 struct exynos_dp_priv *priv)
25908a7aa1eSSimon Glass {
26008a7aa1eSSimon Glass 	unsigned char buf[5];
26108a7aa1eSSimon Glass 	unsigned int ret = 0;
26208a7aa1eSSimon Glass 
26308a7aa1eSSimon Glass 	debug("DP: %s was called\n", __func__);
26408a7aa1eSSimon Glass 
2658b449a66SSimon Glass 	priv->lt_info.lt_status = DP_LT_CR;
2668b449a66SSimon Glass 	priv->lt_info.ep_loop = 0;
2678b449a66SSimon Glass 	priv->lt_info.cr_loop[0] = 0;
2688b449a66SSimon Glass 	priv->lt_info.cr_loop[1] = 0;
2698b449a66SSimon Glass 	priv->lt_info.cr_loop[2] = 0;
2708b449a66SSimon Glass 	priv->lt_info.cr_loop[3] = 0;
27108a7aa1eSSimon Glass 
27208a7aa1eSSimon Glass 		/* Set sink to D0 (Sink Not Ready) mode. */
2738b449a66SSimon Glass 	ret = exynos_dp_write_byte_to_dpcd(regs, DPCD_SINK_POWER_STATE,
27408a7aa1eSSimon Glass 					   DPCD_SET_POWER_STATE_D0);
27508a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
27608a7aa1eSSimon Glass 		printf("DP write_dpcd_byte failed\n");
27708a7aa1eSSimon Glass 		return ret;
27808a7aa1eSSimon Glass 	}
27908a7aa1eSSimon Glass 
28008a7aa1eSSimon Glass 	/* Set link rate and count as you want to establish */
2818b449a66SSimon Glass 	exynos_dp_set_link_bandwidth(regs, priv->lane_bw);
2828b449a66SSimon Glass 	exynos_dp_set_lane_count(regs, priv->lane_cnt);
28308a7aa1eSSimon Glass 
28408a7aa1eSSimon Glass 	/* Setup RX configuration */
2858b449a66SSimon Glass 	buf[0] = priv->lane_bw;
2868b449a66SSimon Glass 	buf[1] = priv->lane_cnt;
28708a7aa1eSSimon Glass 
2888b449a66SSimon Glass 	ret = exynos_dp_write_bytes_to_dpcd(regs, DPCD_LINK_BW_SET, 2, buf);
28908a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
29008a7aa1eSSimon Glass 		printf("DP write_dpcd_byte failed\n");
29108a7aa1eSSimon Glass 		return ret;
29208a7aa1eSSimon Glass 	}
29308a7aa1eSSimon Glass 
2948b449a66SSimon Glass 	exynos_dp_set_lane_pre_emphasis(regs, PRE_EMPHASIS_LEVEL_0,
2958b449a66SSimon Glass 			priv->lane_cnt);
29608a7aa1eSSimon Glass 
29708a7aa1eSSimon Glass 	/* Set training pattern 1 */
2988b449a66SSimon Glass 	exynos_dp_set_training_pattern(regs, TRAINING_PTN1);
29908a7aa1eSSimon Glass 
30008a7aa1eSSimon Glass 	/* Set RX training pattern */
30108a7aa1eSSimon Glass 	buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1;
30208a7aa1eSSimon Glass 
30308a7aa1eSSimon Glass 	buf[1] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
30408a7aa1eSSimon Glass 		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
30508a7aa1eSSimon Glass 	buf[2] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
30608a7aa1eSSimon Glass 		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
30708a7aa1eSSimon Glass 	buf[3] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
30808a7aa1eSSimon Glass 		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
30908a7aa1eSSimon Glass 	buf[4] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
31008a7aa1eSSimon Glass 		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
31108a7aa1eSSimon Glass 
3128b449a66SSimon Glass 	ret = exynos_dp_write_bytes_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET,
31308a7aa1eSSimon Glass 					    5, buf);
31408a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
31508a7aa1eSSimon Glass 		printf("DP write_dpcd_byte failed\n");
31608a7aa1eSSimon Glass 		return ret;
31708a7aa1eSSimon Glass 	}
31808a7aa1eSSimon Glass 
31908a7aa1eSSimon Glass 	return ret;
32008a7aa1eSSimon Glass }
32108a7aa1eSSimon Glass 
3228b449a66SSimon Glass static unsigned int exynos_dp_training_pattern_dis(struct exynos_dp *regs)
32308a7aa1eSSimon Glass {
32408a7aa1eSSimon Glass 	unsigned int ret = EXYNOS_DP_SUCCESS;
32508a7aa1eSSimon Glass 
3268b449a66SSimon Glass 	exynos_dp_set_training_pattern(regs, DP_NONE);
32708a7aa1eSSimon Glass 
3288b449a66SSimon Glass 	ret = exynos_dp_write_byte_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET,
32908a7aa1eSSimon Glass 					   DPCD_TRAINING_PATTERN_DISABLED);
33008a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
33108a7aa1eSSimon Glass 		printf("DP request_link_training_req failed\n");
33208a7aa1eSSimon Glass 		return -EAGAIN;
33308a7aa1eSSimon Glass 	}
33408a7aa1eSSimon Glass 
33508a7aa1eSSimon Glass 	return ret;
33608a7aa1eSSimon Glass }
33708a7aa1eSSimon Glass 
3388c9b8dc0SSimon Glass static unsigned int exynos_dp_enable_rx_to_enhanced_mode(
3398b449a66SSimon Glass 		struct exynos_dp *regs, unsigned char enable)
34008a7aa1eSSimon Glass {
34108a7aa1eSSimon Glass 	unsigned char data;
34208a7aa1eSSimon Glass 	unsigned int ret = EXYNOS_DP_SUCCESS;
34308a7aa1eSSimon Glass 
3448b449a66SSimon Glass 	ret = exynos_dp_read_byte_from_dpcd(regs, DPCD_LANE_COUNT_SET,
34508a7aa1eSSimon Glass 					    &data);
34608a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
34708a7aa1eSSimon Glass 		printf("DP read_from_dpcd failed\n");
34808a7aa1eSSimon Glass 		return -EAGAIN;
34908a7aa1eSSimon Glass 	}
35008a7aa1eSSimon Glass 
35108a7aa1eSSimon Glass 	if (enable)
35208a7aa1eSSimon Glass 		data = DPCD_ENHANCED_FRAME_EN | DPCD_LN_COUNT_SET(data);
35308a7aa1eSSimon Glass 	else
35408a7aa1eSSimon Glass 		data = DPCD_LN_COUNT_SET(data);
35508a7aa1eSSimon Glass 
3568b449a66SSimon Glass 	ret = exynos_dp_write_byte_to_dpcd(regs, DPCD_LANE_COUNT_SET, data);
35708a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
35808a7aa1eSSimon Glass 			printf("DP write_to_dpcd failed\n");
35908a7aa1eSSimon Glass 			return -EAGAIN;
36008a7aa1eSSimon Glass 
36108a7aa1eSSimon Glass 	}
36208a7aa1eSSimon Glass 
36308a7aa1eSSimon Glass 	return ret;
36408a7aa1eSSimon Glass }
36508a7aa1eSSimon Glass 
3668b449a66SSimon Glass static unsigned int exynos_dp_set_enhanced_mode(struct exynos_dp *regs,
3678c9b8dc0SSimon Glass 						unsigned char enhance_mode)
36808a7aa1eSSimon Glass {
36908a7aa1eSSimon Glass 	unsigned int ret = EXYNOS_DP_SUCCESS;
37008a7aa1eSSimon Glass 
3718b449a66SSimon Glass 	ret = exynos_dp_enable_rx_to_enhanced_mode(regs, enhance_mode);
37208a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
37308a7aa1eSSimon Glass 		printf("DP rx_enhance_mode failed\n");
37408a7aa1eSSimon Glass 		return -EAGAIN;
37508a7aa1eSSimon Glass 	}
37608a7aa1eSSimon Glass 
3778b449a66SSimon Glass 	exynos_dp_enable_enhanced_mode(regs, enhance_mode);
37808a7aa1eSSimon Glass 
37908a7aa1eSSimon Glass 	return ret;
38008a7aa1eSSimon Glass }
38108a7aa1eSSimon Glass 
3828b449a66SSimon Glass static int exynos_dp_read_dpcd_lane_stat(struct exynos_dp *regs,
3838b449a66SSimon Glass 					 struct exynos_dp_priv *priv,
38408a7aa1eSSimon Glass 					 unsigned char *status)
38508a7aa1eSSimon Glass {
38608a7aa1eSSimon Glass 	unsigned int ret, i;
38708a7aa1eSSimon Glass 	unsigned char buf[2];
38808a7aa1eSSimon Glass 	unsigned char lane_stat[DP_LANE_CNT_4] = {0,};
38908a7aa1eSSimon Glass 	unsigned char shift_val[DP_LANE_CNT_4] = {0,};
39008a7aa1eSSimon Glass 
39108a7aa1eSSimon Glass 	shift_val[0] = 0;
39208a7aa1eSSimon Glass 	shift_val[1] = 4;
39308a7aa1eSSimon Glass 	shift_val[2] = 0;
39408a7aa1eSSimon Glass 	shift_val[3] = 4;
39508a7aa1eSSimon Glass 
3968b449a66SSimon Glass 	ret = exynos_dp_read_bytes_from_dpcd(regs, DPCD_LANE0_1_STATUS, 2,
3978c9b8dc0SSimon Glass 					     buf);
39808a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
39908a7aa1eSSimon Glass 		printf("DP read lane status failed\n");
40008a7aa1eSSimon Glass 		return ret;
40108a7aa1eSSimon Glass 	}
40208a7aa1eSSimon Glass 
4038b449a66SSimon Glass 	for (i = 0; i < priv->lane_cnt; i++) {
40408a7aa1eSSimon Glass 		lane_stat[i] = (buf[(i / 2)] >> shift_val[i]) & 0x0f;
40508a7aa1eSSimon Glass 		if (lane_stat[0] != lane_stat[i]) {
40608a7aa1eSSimon Glass 			printf("Wrong lane status\n");
40708a7aa1eSSimon Glass 			return -EINVAL;
40808a7aa1eSSimon Glass 		}
40908a7aa1eSSimon Glass 	}
41008a7aa1eSSimon Glass 
41108a7aa1eSSimon Glass 	*status = lane_stat[0];
41208a7aa1eSSimon Glass 
41308a7aa1eSSimon Glass 	return ret;
41408a7aa1eSSimon Glass }
41508a7aa1eSSimon Glass 
4168b449a66SSimon Glass static unsigned int exynos_dp_read_dpcd_adj_req(struct exynos_dp *regs,
4178c9b8dc0SSimon Glass 		unsigned char lane_num, unsigned char *sw, unsigned char *em)
41808a7aa1eSSimon Glass {
41908a7aa1eSSimon Glass 	unsigned int ret = EXYNOS_DP_SUCCESS;
42008a7aa1eSSimon Glass 	unsigned char buf;
42108a7aa1eSSimon Glass 	unsigned int dpcd_addr;
42208a7aa1eSSimon Glass 	unsigned char shift_val[DP_LANE_CNT_4] = {0, 4, 0, 4};
42308a7aa1eSSimon Glass 
42408a7aa1eSSimon Glass 	/* lane_num value is used as array index, so this range 0 ~ 3 */
42508a7aa1eSSimon Glass 	dpcd_addr = DPCD_ADJUST_REQUEST_LANE0_1 + (lane_num / 2);
42608a7aa1eSSimon Glass 
4278b449a66SSimon Glass 	ret = exynos_dp_read_byte_from_dpcd(regs, dpcd_addr, &buf);
42808a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
42908a7aa1eSSimon Glass 		printf("DP read adjust request failed\n");
43008a7aa1eSSimon Glass 		return -EAGAIN;
43108a7aa1eSSimon Glass 	}
43208a7aa1eSSimon Glass 
43308a7aa1eSSimon Glass 	*sw = ((buf >> shift_val[lane_num]) & 0x03);
43408a7aa1eSSimon Glass 	*em = ((buf >> shift_val[lane_num]) & 0x0c) >> 2;
43508a7aa1eSSimon Glass 
43608a7aa1eSSimon Glass 	return ret;
43708a7aa1eSSimon Glass }
43808a7aa1eSSimon Glass 
4398b449a66SSimon Glass static int exynos_dp_equalizer_err_link(struct exynos_dp *regs,
4408b449a66SSimon Glass 					struct exynos_dp_priv *priv)
44108a7aa1eSSimon Glass {
44208a7aa1eSSimon Glass 	int ret;
44308a7aa1eSSimon Glass 
4448b449a66SSimon Glass 	ret = exynos_dp_training_pattern_dis(regs);
44508a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
44608a7aa1eSSimon Glass 		printf("DP training_pattern_disable() failed\n");
4478b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
44808a7aa1eSSimon Glass 	}
44908a7aa1eSSimon Glass 
4508b449a66SSimon Glass 	ret = exynos_dp_set_enhanced_mode(regs, priv->dpcd_efc);
45108a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
45208a7aa1eSSimon Glass 		printf("DP set_enhanced_mode() failed\n");
4538b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
45408a7aa1eSSimon Glass 	}
45508a7aa1eSSimon Glass 
45608a7aa1eSSimon Glass 	return ret;
45708a7aa1eSSimon Glass }
45808a7aa1eSSimon Glass 
4598b449a66SSimon Glass static int exynos_dp_reduce_link_rate(struct exynos_dp *regs,
4608b449a66SSimon Glass 				      struct exynos_dp_priv *priv)
46108a7aa1eSSimon Glass {
46208a7aa1eSSimon Glass 	int ret;
46308a7aa1eSSimon Glass 
4648b449a66SSimon Glass 	if (priv->lane_bw == DP_LANE_BW_2_70) {
4658b449a66SSimon Glass 		priv->lane_bw = DP_LANE_BW_1_62;
46608a7aa1eSSimon Glass 		printf("DP Change lane bw to 1.62Gbps\n");
4678b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_START;
46808a7aa1eSSimon Glass 		ret = EXYNOS_DP_SUCCESS;
46908a7aa1eSSimon Glass 	} else {
4708b449a66SSimon Glass 		ret = exynos_dp_training_pattern_dis(regs);
47108a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS)
47208a7aa1eSSimon Glass 			printf("DP training_patter_disable() failed\n");
47308a7aa1eSSimon Glass 
4748b449a66SSimon Glass 		ret = exynos_dp_set_enhanced_mode(regs, priv->dpcd_efc);
47508a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS)
47608a7aa1eSSimon Glass 			printf("DP set_enhanced_mode() failed\n");
47708a7aa1eSSimon Glass 
4788b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
47908a7aa1eSSimon Glass 	}
48008a7aa1eSSimon Glass 
48108a7aa1eSSimon Glass 	return ret;
48208a7aa1eSSimon Glass }
48308a7aa1eSSimon Glass 
4848b449a66SSimon Glass static unsigned int exynos_dp_process_clock_recovery(struct exynos_dp *regs,
4858b449a66SSimon Glass 					struct exynos_dp_priv *priv)
48608a7aa1eSSimon Glass {
48708a7aa1eSSimon Glass 	unsigned int ret = EXYNOS_DP_SUCCESS;
48808a7aa1eSSimon Glass 	unsigned char lane_stat;
48908a7aa1eSSimon Glass 	unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, };
49008a7aa1eSSimon Glass 	unsigned int i;
49108a7aa1eSSimon Glass 	unsigned char adj_req_sw;
49208a7aa1eSSimon Glass 	unsigned char adj_req_em;
49308a7aa1eSSimon Glass 	unsigned char buf[5];
49408a7aa1eSSimon Glass 
49508a7aa1eSSimon Glass 	debug("DP: %s was called\n", __func__);
49608a7aa1eSSimon Glass 	mdelay(1);
49708a7aa1eSSimon Glass 
4988b449a66SSimon Glass 	ret = exynos_dp_read_dpcd_lane_stat(regs, priv, &lane_stat);
49908a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
50008a7aa1eSSimon Glass 			printf("DP read lane status failed\n");
5018b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FAIL;
50208a7aa1eSSimon Glass 			return ret;
50308a7aa1eSSimon Glass 	}
50408a7aa1eSSimon Glass 
50508a7aa1eSSimon Glass 	if (lane_stat & DP_LANE_STAT_CR_DONE) {
50608a7aa1eSSimon Glass 		debug("DP clock Recovery training succeed\n");
5078b449a66SSimon Glass 		exynos_dp_set_training_pattern(regs, TRAINING_PTN2);
50808a7aa1eSSimon Glass 
5098b449a66SSimon Glass 		for (i = 0; i < priv->lane_cnt; i++) {
5108b449a66SSimon Glass 			ret = exynos_dp_read_dpcd_adj_req(regs, i,
5118c9b8dc0SSimon Glass 						&adj_req_sw, &adj_req_em);
51208a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
5138b449a66SSimon Glass 				priv->lt_info.lt_status = DP_LT_FAIL;
51408a7aa1eSSimon Glass 				return ret;
51508a7aa1eSSimon Glass 			}
51608a7aa1eSSimon Glass 
51708a7aa1eSSimon Glass 			lt_ctl_val[i] = 0;
51808a7aa1eSSimon Glass 			lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
51908a7aa1eSSimon Glass 
52008a7aa1eSSimon Glass 			if ((adj_req_sw == VOLTAGE_LEVEL_3)
52108a7aa1eSSimon Glass 				|| (adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
52208a7aa1eSSimon Glass 				lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 |
52308a7aa1eSSimon Glass 					MAX_PRE_EMPHASIS_REACH_3;
52408a7aa1eSSimon Glass 			}
5258b449a66SSimon Glass 			exynos_dp_set_lanex_pre_emphasis(regs,
5268c9b8dc0SSimon Glass 							 lt_ctl_val[i], i);
52708a7aa1eSSimon Glass 		}
52808a7aa1eSSimon Glass 
52908a7aa1eSSimon Glass 		buf[0] =  DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_2;
53008a7aa1eSSimon Glass 		buf[1] = lt_ctl_val[0];
53108a7aa1eSSimon Glass 		buf[2] = lt_ctl_val[1];
53208a7aa1eSSimon Glass 		buf[3] = lt_ctl_val[2];
53308a7aa1eSSimon Glass 		buf[4] = lt_ctl_val[3];
53408a7aa1eSSimon Glass 
5358b449a66SSimon Glass 		ret = exynos_dp_write_bytes_to_dpcd(regs,
53608a7aa1eSSimon Glass 				DPCD_TRAINING_PATTERN_SET, 5, buf);
53708a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS) {
53808a7aa1eSSimon Glass 			printf("DP write training pattern1 failed\n");
5398b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FAIL;
54008a7aa1eSSimon Glass 			return ret;
54108a7aa1eSSimon Glass 		} else
5428b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_ET;
54308a7aa1eSSimon Glass 	} else {
5448b449a66SSimon Glass 		for (i = 0; i < priv->lane_cnt; i++) {
5458c9b8dc0SSimon Glass 			lt_ctl_val[i] = exynos_dp_get_lanex_pre_emphasis(
5468b449a66SSimon Glass 						regs, i);
5478b449a66SSimon Glass 				ret = exynos_dp_read_dpcd_adj_req(regs, i,
54808a7aa1eSSimon Glass 						&adj_req_sw, &adj_req_em);
54908a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
55008a7aa1eSSimon Glass 				printf("DP read adj req failed\n");
5518b449a66SSimon Glass 				priv->lt_info.lt_status = DP_LT_FAIL;
55208a7aa1eSSimon Glass 				return ret;
55308a7aa1eSSimon Glass 			}
55408a7aa1eSSimon Glass 
55508a7aa1eSSimon Glass 			if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
55608a7aa1eSSimon Glass 					(adj_req_em == PRE_EMPHASIS_LEVEL_3))
5578b449a66SSimon Glass 				ret = exynos_dp_reduce_link_rate(regs,
5588b449a66SSimon Glass 								 priv);
55908a7aa1eSSimon Glass 
56008a7aa1eSSimon Glass 			if ((DRIVE_CURRENT_SET_0_GET(lt_ctl_val[i]) ==
56108a7aa1eSSimon Glass 						adj_req_sw) &&
56208a7aa1eSSimon Glass 				(PRE_EMPHASIS_SET_0_GET(lt_ctl_val[i]) ==
56308a7aa1eSSimon Glass 						adj_req_em)) {
5648b449a66SSimon Glass 				priv->lt_info.cr_loop[i]++;
5658b449a66SSimon Glass 				if (priv->lt_info.cr_loop[i] == MAX_CR_LOOP)
56608a7aa1eSSimon Glass 					ret = exynos_dp_reduce_link_rate(
5678b449a66SSimon Glass 							regs, priv);
56808a7aa1eSSimon Glass 			}
56908a7aa1eSSimon Glass 
57008a7aa1eSSimon Glass 			lt_ctl_val[i] = 0;
57108a7aa1eSSimon Glass 			lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
57208a7aa1eSSimon Glass 
57308a7aa1eSSimon Glass 			if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
57408a7aa1eSSimon Glass 					(adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
57508a7aa1eSSimon Glass 				lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 |
57608a7aa1eSSimon Glass 					MAX_PRE_EMPHASIS_REACH_3;
57708a7aa1eSSimon Glass 			}
5788b449a66SSimon Glass 			exynos_dp_set_lanex_pre_emphasis(regs,
5798c9b8dc0SSimon Glass 							 lt_ctl_val[i], i);
58008a7aa1eSSimon Glass 		}
58108a7aa1eSSimon Glass 
5828b449a66SSimon Glass 		ret = exynos_dp_write_bytes_to_dpcd(regs,
58308a7aa1eSSimon Glass 				DPCD_TRAINING_LANE0_SET, 4, lt_ctl_val);
58408a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS) {
58508a7aa1eSSimon Glass 			printf("DP write training pattern2 failed\n");
5868b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FAIL;
58708a7aa1eSSimon Glass 			return ret;
58808a7aa1eSSimon Glass 		}
58908a7aa1eSSimon Glass 	}
59008a7aa1eSSimon Glass 
59108a7aa1eSSimon Glass 	return ret;
59208a7aa1eSSimon Glass }
59308a7aa1eSSimon Glass 
5948c9b8dc0SSimon Glass static unsigned int exynos_dp_process_equalizer_training(
5958b449a66SSimon Glass 		struct exynos_dp *regs, struct exynos_dp_priv *priv)
59608a7aa1eSSimon Glass {
59708a7aa1eSSimon Glass 	unsigned int ret = EXYNOS_DP_SUCCESS;
59808a7aa1eSSimon Glass 	unsigned char lane_stat, adj_req_sw, adj_req_em, i;
59908a7aa1eSSimon Glass 	unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,};
60008a7aa1eSSimon Glass 	unsigned char interlane_aligned = 0;
60108a7aa1eSSimon Glass 	unsigned char f_bw;
60208a7aa1eSSimon Glass 	unsigned char f_lane_cnt;
60308a7aa1eSSimon Glass 	unsigned char sink_stat;
60408a7aa1eSSimon Glass 
60508a7aa1eSSimon Glass 	mdelay(1);
60608a7aa1eSSimon Glass 
6078b449a66SSimon Glass 	ret = exynos_dp_read_dpcd_lane_stat(regs, priv, &lane_stat);
60808a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
60908a7aa1eSSimon Glass 		printf("DP read lane status failed\n");
6108b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
61108a7aa1eSSimon Glass 		return ret;
61208a7aa1eSSimon Glass 	}
61308a7aa1eSSimon Glass 
61408a7aa1eSSimon Glass 	debug("DP lane stat : %x\n", lane_stat);
61508a7aa1eSSimon Glass 
61608a7aa1eSSimon Glass 	if (lane_stat & DP_LANE_STAT_CR_DONE) {
6178b449a66SSimon Glass 		ret = exynos_dp_read_byte_from_dpcd(regs,
6188c9b8dc0SSimon Glass 						    DPCD_LN_ALIGN_UPDATED,
61908a7aa1eSSimon Glass 						    &sink_stat);
62008a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS) {
6218b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FAIL;
62208a7aa1eSSimon Glass 
62308a7aa1eSSimon Glass 			return ret;
62408a7aa1eSSimon Glass 		}
62508a7aa1eSSimon Glass 
62608a7aa1eSSimon Glass 		interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE);
62708a7aa1eSSimon Glass 
6288b449a66SSimon Glass 		for (i = 0; i < priv->lane_cnt; i++) {
6298b449a66SSimon Glass 			ret = exynos_dp_read_dpcd_adj_req(regs, i,
63008a7aa1eSSimon Glass 					&adj_req_sw, &adj_req_em);
63108a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
63208a7aa1eSSimon Glass 				printf("DP read adj req 1 failed\n");
6338b449a66SSimon Glass 				priv->lt_info.lt_status = DP_LT_FAIL;
63408a7aa1eSSimon Glass 
63508a7aa1eSSimon Glass 				return ret;
63608a7aa1eSSimon Glass 			}
63708a7aa1eSSimon Glass 
63808a7aa1eSSimon Glass 			lt_ctl_val[i] = 0;
63908a7aa1eSSimon Glass 			lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
64008a7aa1eSSimon Glass 
64108a7aa1eSSimon Glass 			if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
64208a7aa1eSSimon Glass 				(adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
64308a7aa1eSSimon Glass 				lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3;
64408a7aa1eSSimon Glass 				lt_ctl_val[i] |= MAX_PRE_EMPHASIS_REACH_3;
64508a7aa1eSSimon Glass 			}
64608a7aa1eSSimon Glass 		}
64708a7aa1eSSimon Glass 
64808a7aa1eSSimon Glass 		if (((lane_stat&DP_LANE_STAT_CE_DONE) &&
64908a7aa1eSSimon Glass 			(lane_stat&DP_LANE_STAT_SYM_LOCK))
65008a7aa1eSSimon Glass 			&& (interlane_aligned == DPCD_INTERLANE_ALIGN_DONE)) {
65108a7aa1eSSimon Glass 			debug("DP Equalizer training succeed\n");
65208a7aa1eSSimon Glass 
6538b449a66SSimon Glass 			f_bw = exynos_dp_get_link_bandwidth(regs);
6548b449a66SSimon Glass 			f_lane_cnt = exynos_dp_get_lane_count(regs);
65508a7aa1eSSimon Glass 
65608a7aa1eSSimon Glass 			debug("DP final BandWidth : %x\n", f_bw);
65708a7aa1eSSimon Glass 			debug("DP final Lane Count : %x\n", f_lane_cnt);
65808a7aa1eSSimon Glass 
6598b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FINISHED;
66008a7aa1eSSimon Glass 
6618b449a66SSimon Glass 			exynos_dp_equalizer_err_link(regs, priv);
66208a7aa1eSSimon Glass 
66308a7aa1eSSimon Glass 		} else {
6648b449a66SSimon Glass 			priv->lt_info.ep_loop++;
66508a7aa1eSSimon Glass 
6668b449a66SSimon Glass 			if (priv->lt_info.ep_loop > MAX_EQ_LOOP) {
6678b449a66SSimon Glass 				if (priv->lane_bw == DP_LANE_BW_2_70) {
66808a7aa1eSSimon Glass 					ret = exynos_dp_reduce_link_rate(
6698b449a66SSimon Glass 							regs, priv);
67008a7aa1eSSimon Glass 				} else {
6718b449a66SSimon Glass 					priv->lt_info.lt_status =
67208a7aa1eSSimon Glass 								DP_LT_FAIL;
6738b449a66SSimon Glass 					exynos_dp_equalizer_err_link(regs,
6748b449a66SSimon Glass 								     priv);
67508a7aa1eSSimon Glass 				}
67608a7aa1eSSimon Glass 			} else {
6778b449a66SSimon Glass 				for (i = 0; i < priv->lane_cnt; i++)
67808a7aa1eSSimon Glass 					exynos_dp_set_lanex_pre_emphasis(
6798b449a66SSimon Glass 						regs, lt_ctl_val[i], i);
68008a7aa1eSSimon Glass 
6818b449a66SSimon Glass 				ret = exynos_dp_write_bytes_to_dpcd(regs,
68208a7aa1eSSimon Glass 						DPCD_TRAINING_LANE0_SET,
68308a7aa1eSSimon Glass 						4, lt_ctl_val);
68408a7aa1eSSimon Glass 				if (ret != EXYNOS_DP_SUCCESS) {
68508a7aa1eSSimon Glass 					printf("DP set lt pattern failed\n");
6868b449a66SSimon Glass 					priv->lt_info.lt_status =
68708a7aa1eSSimon Glass 								DP_LT_FAIL;
6888b449a66SSimon Glass 					exynos_dp_equalizer_err_link(regs,
6898b449a66SSimon Glass 								     priv);
69008a7aa1eSSimon Glass 				}
69108a7aa1eSSimon Glass 			}
69208a7aa1eSSimon Glass 		}
6938b449a66SSimon Glass 	} else if (priv->lane_bw == DP_LANE_BW_2_70) {
6948b449a66SSimon Glass 		ret = exynos_dp_reduce_link_rate(regs, priv);
69508a7aa1eSSimon Glass 	} else {
6968b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
6978b449a66SSimon Glass 		exynos_dp_equalizer_err_link(regs, priv);
69808a7aa1eSSimon Glass 	}
69908a7aa1eSSimon Glass 
70008a7aa1eSSimon Glass 	return ret;
70108a7aa1eSSimon Glass }
70208a7aa1eSSimon Glass 
7038b449a66SSimon Glass static unsigned int exynos_dp_sw_link_training(struct exynos_dp *regs,
7048b449a66SSimon Glass 					       struct exynos_dp_priv *priv)
70508a7aa1eSSimon Glass {
70608a7aa1eSSimon Glass 	unsigned int ret = 0;
70708a7aa1eSSimon Glass 	int training_finished;
70808a7aa1eSSimon Glass 
70908a7aa1eSSimon Glass 	/* Turn off unnecessary lane */
7108b449a66SSimon Glass 	if (priv->lane_cnt == 1)
7118b449a66SSimon Glass 		exynos_dp_set_analog_power_down(regs, CH1_BLOCK, 1);
71208a7aa1eSSimon Glass 
71308a7aa1eSSimon Glass 	training_finished = 0;
71408a7aa1eSSimon Glass 
7158b449a66SSimon Glass 	priv->lt_info.lt_status = DP_LT_START;
71608a7aa1eSSimon Glass 
71708a7aa1eSSimon Glass 	/* Process here */
71808a7aa1eSSimon Glass 	while (!training_finished) {
7198b449a66SSimon Glass 		switch (priv->lt_info.lt_status) {
72008a7aa1eSSimon Glass 		case DP_LT_START:
7218b449a66SSimon Glass 			ret = exynos_dp_link_start(regs, priv);
72208a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
72308a7aa1eSSimon Glass 				printf("DP LT:link start failed\n");
72408a7aa1eSSimon Glass 				return ret;
72508a7aa1eSSimon Glass 			}
72608a7aa1eSSimon Glass 			break;
72708a7aa1eSSimon Glass 		case DP_LT_CR:
7288b449a66SSimon Glass 			ret = exynos_dp_process_clock_recovery(regs,
7298b449a66SSimon Glass 							       priv);
73008a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
73108a7aa1eSSimon Glass 				printf("DP LT:clock recovery failed\n");
73208a7aa1eSSimon Glass 				return ret;
73308a7aa1eSSimon Glass 			}
73408a7aa1eSSimon Glass 			break;
73508a7aa1eSSimon Glass 		case DP_LT_ET:
7368b449a66SSimon Glass 			ret = exynos_dp_process_equalizer_training(regs,
7378b449a66SSimon Glass 								   priv);
73808a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
73908a7aa1eSSimon Glass 				printf("DP LT:equalizer training failed\n");
74008a7aa1eSSimon Glass 				return ret;
74108a7aa1eSSimon Glass 			}
74208a7aa1eSSimon Glass 			break;
74308a7aa1eSSimon Glass 		case DP_LT_FINISHED:
74408a7aa1eSSimon Glass 			training_finished = 1;
74508a7aa1eSSimon Glass 			break;
74608a7aa1eSSimon Glass 		case DP_LT_FAIL:
74708a7aa1eSSimon Glass 			return -1;
74808a7aa1eSSimon Glass 		}
74908a7aa1eSSimon Glass 	}
75008a7aa1eSSimon Glass 
75108a7aa1eSSimon Glass 	return ret;
75208a7aa1eSSimon Glass }
75308a7aa1eSSimon Glass 
7548b449a66SSimon Glass static unsigned int exynos_dp_set_link_train(struct exynos_dp *regs,
7558b449a66SSimon Glass 					     struct exynos_dp_priv *priv)
75608a7aa1eSSimon Glass {
75708a7aa1eSSimon Glass 	unsigned int ret;
75808a7aa1eSSimon Glass 
7598b449a66SSimon Glass 	exynos_dp_init_training(regs);
76008a7aa1eSSimon Glass 
7618b449a66SSimon Glass 	ret = exynos_dp_sw_link_training(regs, priv);
76208a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS)
76308a7aa1eSSimon Glass 		printf("DP dp_sw_link_training() failed\n");
76408a7aa1eSSimon Glass 
76508a7aa1eSSimon Glass 	return ret;
76608a7aa1eSSimon Glass }
76708a7aa1eSSimon Glass 
7688b449a66SSimon Glass static void exynos_dp_enable_scramble(struct exynos_dp *regs,
7698c9b8dc0SSimon Glass 				      unsigned int enable)
77008a7aa1eSSimon Glass {
77108a7aa1eSSimon Glass 	unsigned char data;
77208a7aa1eSSimon Glass 
77308a7aa1eSSimon Glass 	if (enable) {
7748b449a66SSimon Glass 		exynos_dp_enable_scrambling(regs, DP_ENABLE);
77508a7aa1eSSimon Glass 
7768b449a66SSimon Glass 		exynos_dp_read_byte_from_dpcd(regs,
7778c9b8dc0SSimon Glass 					      DPCD_TRAINING_PATTERN_SET, &data);
7788b449a66SSimon Glass 		exynos_dp_write_byte_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET,
77908a7aa1eSSimon Glass 			(u8)(data & ~DPCD_SCRAMBLING_DISABLED));
78008a7aa1eSSimon Glass 	} else {
7818b449a66SSimon Glass 		exynos_dp_enable_scrambling(regs, DP_DISABLE);
7828b449a66SSimon Glass 		exynos_dp_read_byte_from_dpcd(regs,
7838c9b8dc0SSimon Glass 					      DPCD_TRAINING_PATTERN_SET, &data);
7848b449a66SSimon Glass 		exynos_dp_write_byte_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET,
78508a7aa1eSSimon Glass 			(u8)(data | DPCD_SCRAMBLING_DISABLED));
78608a7aa1eSSimon Glass 	}
78708a7aa1eSSimon Glass }
78808a7aa1eSSimon Glass 
7898b449a66SSimon Glass static unsigned int exynos_dp_config_video(struct exynos_dp *regs,
7908b449a66SSimon Glass 					   struct exynos_dp_priv *priv)
79108a7aa1eSSimon Glass {
79208a7aa1eSSimon Glass 	unsigned int ret = 0;
79308a7aa1eSSimon Glass 	unsigned int retry_cnt;
79408a7aa1eSSimon Glass 
79508a7aa1eSSimon Glass 	mdelay(1);
79608a7aa1eSSimon Glass 
7978b449a66SSimon Glass 	if (priv->video_info.master_mode) {
79808a7aa1eSSimon Glass 		printf("DP does not support master mode\n");
79908a7aa1eSSimon Glass 		return -ENODEV;
80008a7aa1eSSimon Glass 	} else {
80108a7aa1eSSimon Glass 		/* debug slave */
8028b449a66SSimon Glass 		exynos_dp_config_video_slave_mode(regs,
8038b449a66SSimon Glass 						  &priv->video_info);
80408a7aa1eSSimon Glass 	}
80508a7aa1eSSimon Glass 
8068b449a66SSimon Glass 	exynos_dp_set_video_color_format(regs, &priv->video_info);
80708a7aa1eSSimon Glass 
8088b449a66SSimon Glass 	if (priv->video_info.bist_mode) {
8098b449a66SSimon Glass 		if (exynos_dp_config_video_bist(regs, priv) != 0)
81008a7aa1eSSimon Glass 			return -1;
81108a7aa1eSSimon Glass 	}
81208a7aa1eSSimon Glass 
8138b449a66SSimon Glass 	ret = exynos_dp_get_pll_lock_status(regs);
81408a7aa1eSSimon Glass 	if (ret != PLL_LOCKED) {
81508a7aa1eSSimon Glass 		printf("DP PLL is not locked yet\n");
81608a7aa1eSSimon Glass 		return -EIO;
81708a7aa1eSSimon Glass 	}
81808a7aa1eSSimon Glass 
8198b449a66SSimon Glass 	if (priv->video_info.master_mode == 0) {
82008a7aa1eSSimon Glass 		retry_cnt = 10;
82108a7aa1eSSimon Glass 		while (retry_cnt) {
8228b449a66SSimon Glass 			ret = exynos_dp_is_slave_video_stream_clock_on(regs);
82308a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
82408a7aa1eSSimon Glass 				if (retry_cnt == 0) {
82508a7aa1eSSimon Glass 					printf("DP stream_clock_on failed\n");
82608a7aa1eSSimon Glass 					return ret;
82708a7aa1eSSimon Glass 				}
82808a7aa1eSSimon Glass 				retry_cnt--;
82908a7aa1eSSimon Glass 				mdelay(1);
83008a7aa1eSSimon Glass 			} else
83108a7aa1eSSimon Glass 				break;
83208a7aa1eSSimon Glass 		}
83308a7aa1eSSimon Glass 	}
83408a7aa1eSSimon Glass 
83508a7aa1eSSimon Glass 	/* Set to use the register calculated M/N video */
8368b449a66SSimon Glass 	exynos_dp_set_video_cr_mn(regs, CALCULATED_M, 0, 0);
83708a7aa1eSSimon Glass 
83808a7aa1eSSimon Glass 	/* For video bist, Video timing must be generated by register */
8398b449a66SSimon Glass 	exynos_dp_set_video_timing_mode(regs, VIDEO_TIMING_FROM_CAPTURE);
84008a7aa1eSSimon Glass 
84108a7aa1eSSimon Glass 	/* Enable video bist */
8428b449a66SSimon Glass 	if (priv->video_info.bist_pattern != COLOR_RAMP &&
8438b449a66SSimon Glass 		priv->video_info.bist_pattern != BALCK_WHITE_V_LINES &&
8448b449a66SSimon Glass 		priv->video_info.bist_pattern != COLOR_SQUARE)
8458b449a66SSimon Glass 		exynos_dp_enable_video_bist(regs,
8468b449a66SSimon Glass 					    priv->video_info.bist_mode);
84708a7aa1eSSimon Glass 	else
8488b449a66SSimon Glass 		exynos_dp_enable_video_bist(regs, DP_DISABLE);
84908a7aa1eSSimon Glass 
85008a7aa1eSSimon Glass 	/* Disable video mute */
8518b449a66SSimon Glass 	exynos_dp_enable_video_mute(regs, DP_DISABLE);
85208a7aa1eSSimon Glass 
85308a7aa1eSSimon Glass 	/* Configure video Master or Slave mode */
8548b449a66SSimon Glass 	exynos_dp_enable_video_master(regs,
8558b449a66SSimon Glass 				      priv->video_info.master_mode);
85608a7aa1eSSimon Glass 
85708a7aa1eSSimon Glass 	/* Enable video */
8588b449a66SSimon Glass 	exynos_dp_start_video(regs);
85908a7aa1eSSimon Glass 
8608b449a66SSimon Glass 	if (priv->video_info.master_mode == 0) {
86108a7aa1eSSimon Glass 		retry_cnt = 100;
86208a7aa1eSSimon Glass 		while (retry_cnt) {
8638b449a66SSimon Glass 			ret = exynos_dp_is_video_stream_on(regs);
86408a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
86508a7aa1eSSimon Glass 				if (retry_cnt == 0) {
86608a7aa1eSSimon Glass 					printf("DP Timeout of video stream\n");
86708a7aa1eSSimon Glass 					return ret;
86808a7aa1eSSimon Glass 				}
86908a7aa1eSSimon Glass 				retry_cnt--;
87008a7aa1eSSimon Glass 				mdelay(5);
87108a7aa1eSSimon Glass 			} else
87208a7aa1eSSimon Glass 				break;
87308a7aa1eSSimon Glass 		}
87408a7aa1eSSimon Glass 	}
87508a7aa1eSSimon Glass 
87608a7aa1eSSimon Glass 	return ret;
87708a7aa1eSSimon Glass }
87808a7aa1eSSimon Glass 
879bb5930d5SSimon Glass static int exynos_dp_ofdata_to_platdata(struct udevice *dev)
88008a7aa1eSSimon Glass {
881bb5930d5SSimon Glass 	struct exynos_dp_priv *priv = dev_get_priv(dev);
882bb5930d5SSimon Glass 	const void *blob = gd->fdt_blob;
883e160f7d4SSimon Glass 	unsigned int node = dev_of_offset(dev);
884bb5930d5SSimon Glass 	fdt_addr_t addr;
88508a7aa1eSSimon Glass 
886a821c4afSSimon Glass 	addr = devfdt_get_addr(dev);
887bb5930d5SSimon Glass 	if (addr == FDT_ADDR_T_NONE) {
888bb5930d5SSimon Glass 		debug("Can't get the DP base address\n");
889bb5930d5SSimon Glass 		return -EINVAL;
890bb5930d5SSimon Glass 	}
891bb5930d5SSimon Glass 	priv->regs = (struct exynos_dp *)addr;
8928b449a66SSimon Glass 	priv->disp_info.h_res = fdtdec_get_int(blob, node,
89308a7aa1eSSimon Glass 							"samsung,h-res", 0);
8948b449a66SSimon Glass 	priv->disp_info.h_sync_width = fdtdec_get_int(blob, node,
89508a7aa1eSSimon Glass 						"samsung,h-sync-width", 0);
8968b449a66SSimon Glass 	priv->disp_info.h_back_porch = fdtdec_get_int(blob, node,
89708a7aa1eSSimon Glass 						"samsung,h-back-porch", 0);
8988b449a66SSimon Glass 	priv->disp_info.h_front_porch = fdtdec_get_int(blob, node,
89908a7aa1eSSimon Glass 						"samsung,h-front-porch", 0);
9008b449a66SSimon Glass 	priv->disp_info.v_res = fdtdec_get_int(blob, node,
90108a7aa1eSSimon Glass 						"samsung,v-res", 0);
9028b449a66SSimon Glass 	priv->disp_info.v_sync_width = fdtdec_get_int(blob, node,
90308a7aa1eSSimon Glass 						"samsung,v-sync-width", 0);
9048b449a66SSimon Glass 	priv->disp_info.v_back_porch = fdtdec_get_int(blob, node,
90508a7aa1eSSimon Glass 						"samsung,v-back-porch", 0);
9068b449a66SSimon Glass 	priv->disp_info.v_front_porch = fdtdec_get_int(blob, node,
90708a7aa1eSSimon Glass 						"samsung,v-front-porch", 0);
9088b449a66SSimon Glass 	priv->disp_info.v_sync_rate = fdtdec_get_int(blob, node,
90908a7aa1eSSimon Glass 						"samsung,v-sync-rate", 0);
91008a7aa1eSSimon Glass 
9118b449a66SSimon Glass 	priv->lt_info.lt_status = fdtdec_get_int(blob, node,
91208a7aa1eSSimon Glass 						"samsung,lt-status", 0);
91308a7aa1eSSimon Glass 
9148b449a66SSimon Glass 	priv->video_info.master_mode = fdtdec_get_int(blob, node,
91508a7aa1eSSimon Glass 						"samsung,master-mode", 0);
9168b449a66SSimon Glass 	priv->video_info.bist_mode = fdtdec_get_int(blob, node,
91708a7aa1eSSimon Glass 						"samsung,bist-mode", 0);
9188b449a66SSimon Glass 	priv->video_info.bist_pattern = fdtdec_get_int(blob, node,
91908a7aa1eSSimon Glass 						"samsung,bist-pattern", 0);
9208b449a66SSimon Glass 	priv->video_info.h_sync_polarity = fdtdec_get_int(blob, node,
92108a7aa1eSSimon Glass 						"samsung,h-sync-polarity", 0);
9228b449a66SSimon Glass 	priv->video_info.v_sync_polarity = fdtdec_get_int(blob, node,
92308a7aa1eSSimon Glass 						"samsung,v-sync-polarity", 0);
9248b449a66SSimon Glass 	priv->video_info.interlaced = fdtdec_get_int(blob, node,
92508a7aa1eSSimon Glass 						"samsung,interlaced", 0);
9268b449a66SSimon Glass 	priv->video_info.color_space = fdtdec_get_int(blob, node,
92708a7aa1eSSimon Glass 						"samsung,color-space", 0);
9288b449a66SSimon Glass 	priv->video_info.dynamic_range = fdtdec_get_int(blob, node,
92908a7aa1eSSimon Glass 						"samsung,dynamic-range", 0);
9308b449a66SSimon Glass 	priv->video_info.ycbcr_coeff = fdtdec_get_int(blob, node,
93108a7aa1eSSimon Glass 						"samsung,ycbcr-coeff", 0);
9328b449a66SSimon Glass 	priv->video_info.color_depth = fdtdec_get_int(blob, node,
93308a7aa1eSSimon Glass 						"samsung,color-depth", 0);
93408a7aa1eSSimon Glass 	return 0;
93508a7aa1eSSimon Glass }
93608a7aa1eSSimon Glass 
937bb5930d5SSimon Glass static int exynos_dp_bridge_init(struct udevice *dev)
93808a7aa1eSSimon Glass {
939bb5930d5SSimon Glass 	const int max_tries = 10;
940bb5930d5SSimon Glass 	int num_tries;
941bb5930d5SSimon Glass 	int ret;
94208a7aa1eSSimon Glass 
943bb5930d5SSimon Glass 	debug("%s\n", __func__);
944bb5930d5SSimon Glass 	ret = video_bridge_attach(dev);
945bb5930d5SSimon Glass 	if (ret) {
946bb5930d5SSimon Glass 		debug("video bridge init failed: %d\n", ret);
947bb5930d5SSimon Glass 		return ret;
94808a7aa1eSSimon Glass 	}
94908a7aa1eSSimon Glass 
950bb5930d5SSimon Glass 	/*
951bb5930d5SSimon Glass 	 * We need to wait for 90ms after bringing up the bridge since there
952bb5930d5SSimon Glass 	 * is a phantom "high" on the HPD chip during its bootup.  The phantom
953bb5930d5SSimon Glass 	 * high comes within 7ms of de-asserting PD and persists for at least
954bb5930d5SSimon Glass 	 * 15ms.  The real high comes roughly 50ms after PD is de-asserted. The
955bb5930d5SSimon Glass 	 * phantom high makes it hard for us to know when the NXP chip is up.
956bb5930d5SSimon Glass 	 */
957bb5930d5SSimon Glass 	mdelay(90);
95808a7aa1eSSimon Glass 
959bb5930d5SSimon Glass 	for (num_tries = 0; num_tries < max_tries; num_tries++) {
960bb5930d5SSimon Glass 		/* Check HPD. If it's high, or we don't have it, all is well */
961bb5930d5SSimon Glass 		ret = video_bridge_check_attached(dev);
962bb5930d5SSimon Glass 		if (!ret || ret == -ENOENT)
963bb5930d5SSimon Glass 			return 0;
9648c9b8dc0SSimon Glass 
965bb5930d5SSimon Glass 		debug("%s: eDP bridge failed to come up; try %d of %d\n",
966bb5930d5SSimon Glass 		      __func__, num_tries, max_tries);
967bb5930d5SSimon Glass 	}
96808a7aa1eSSimon Glass 
969bb5930d5SSimon Glass 	/* Immediately go into bridge reset if the hp line is not high */
970bb5930d5SSimon Glass 	return -EIO;
971bb5930d5SSimon Glass }
972bb5930d5SSimon Glass 
973bb5930d5SSimon Glass static int exynos_dp_bridge_setup(const void *blob)
974bb5930d5SSimon Glass {
975bb5930d5SSimon Glass 	const int max_tries = 2;
976bb5930d5SSimon Glass 	int num_tries;
977bb5930d5SSimon Glass 	struct udevice *dev;
978bb5930d5SSimon Glass 	int ret;
979bb5930d5SSimon Glass 
980bb5930d5SSimon Glass 	/* Configure I2C registers for Parade bridge */
981bb5930d5SSimon Glass 	ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &dev);
982bb5930d5SSimon Glass 	if (ret) {
983bb5930d5SSimon Glass 		debug("video bridge init failed: %d\n", ret);
984bb5930d5SSimon Glass 		return ret;
985bb5930d5SSimon Glass 	}
986bb5930d5SSimon Glass 
987bb5930d5SSimon Glass 	if (strncmp(dev->driver->name, "parade", 6)) {
988bb5930d5SSimon Glass 		/* Mux HPHPD to the special hotplug detect mode */
989bb5930d5SSimon Glass 		exynos_pinmux_config(PERIPH_ID_DPHPD, 0);
990bb5930d5SSimon Glass 	}
991bb5930d5SSimon Glass 
992bb5930d5SSimon Glass 	for (num_tries = 0; num_tries < max_tries; num_tries++) {
993bb5930d5SSimon Glass 		ret = exynos_dp_bridge_init(dev);
994bb5930d5SSimon Glass 		if (!ret)
995bb5930d5SSimon Glass 			return 0;
996bb5930d5SSimon Glass 		if (num_tries == max_tries - 1)
997bb5930d5SSimon Glass 			break;
998bb5930d5SSimon Glass 
999bb5930d5SSimon Glass 		/*
1000bb5930d5SSimon Glass 		* If we're here, the bridge chip failed to initialise.
1001bb5930d5SSimon Glass 		* Power down the bridge in an attempt to reset.
1002bb5930d5SSimon Glass 		*/
1003bb5930d5SSimon Glass 		video_bridge_set_active(dev, false);
1004bb5930d5SSimon Glass 
1005bb5930d5SSimon Glass 		/*
1006bb5930d5SSimon Glass 		* Arbitrarily wait 300ms here with DP_N low.  Don't know for
1007bb5930d5SSimon Glass 		* sure how long we should wait, but we're being paranoid.
1008bb5930d5SSimon Glass 		*/
1009bb5930d5SSimon Glass 		mdelay(300);
1010bb5930d5SSimon Glass 	}
1011bb5930d5SSimon Glass 
1012bb5930d5SSimon Glass 	return ret;
1013bb5930d5SSimon Glass }
1014bb5930d5SSimon Glass int exynos_dp_enable(struct udevice *dev, int panel_bpp,
1015bb5930d5SSimon Glass 		     const struct display_timing *timing)
1016bb5930d5SSimon Glass {
1017bb5930d5SSimon Glass 	struct exynos_dp_priv *priv = dev_get_priv(dev);
1018bb5930d5SSimon Glass 	struct exynos_dp *regs = priv->regs;
1019bb5930d5SSimon Glass 	unsigned int ret;
1020bb5930d5SSimon Glass 
1021bb5930d5SSimon Glass 	debug("%s: start\n", __func__);
10228b449a66SSimon Glass 	exynos_dp_disp_info(&priv->disp_info);
102308a7aa1eSSimon Glass 
1024bb5930d5SSimon Glass 	ret = exynos_dp_bridge_setup(gd->fdt_blob);
1025bb5930d5SSimon Glass 	if (ret && ret != -ENODEV)
1026bb5930d5SSimon Glass 		printf("LCD bridge failed to enable: %d\n", ret);
1027bb5930d5SSimon Glass 
10287eb860dfSSimon Glass 	exynos_dp_phy_ctrl(1);
102908a7aa1eSSimon Glass 
10308b449a66SSimon Glass 	ret = exynos_dp_init_dp(regs);
103108a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
103208a7aa1eSSimon Glass 		printf("DP exynos_dp_init_dp() failed\n");
103308a7aa1eSSimon Glass 		return ret;
103408a7aa1eSSimon Glass 	}
103508a7aa1eSSimon Glass 
10368b449a66SSimon Glass 	ret = exynos_dp_handle_edid(regs, priv);
103708a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
103808a7aa1eSSimon Glass 		printf("EDP handle_edid fail\n");
103908a7aa1eSSimon Glass 		return ret;
104008a7aa1eSSimon Glass 	}
104108a7aa1eSSimon Glass 
10428b449a66SSimon Glass 	ret = exynos_dp_set_link_train(regs, priv);
104308a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
104408a7aa1eSSimon Glass 		printf("DP link training fail\n");
104508a7aa1eSSimon Glass 		return ret;
104608a7aa1eSSimon Glass 	}
104708a7aa1eSSimon Glass 
10488b449a66SSimon Glass 	exynos_dp_enable_scramble(regs, DP_ENABLE);
10498b449a66SSimon Glass 	exynos_dp_enable_rx_to_enhanced_mode(regs, DP_ENABLE);
10508b449a66SSimon Glass 	exynos_dp_enable_enhanced_mode(regs, DP_ENABLE);
105108a7aa1eSSimon Glass 
10528b449a66SSimon Glass 	exynos_dp_set_link_bandwidth(regs, priv->lane_bw);
10538b449a66SSimon Glass 	exynos_dp_set_lane_count(regs, priv->lane_cnt);
105408a7aa1eSSimon Glass 
10558b449a66SSimon Glass 	exynos_dp_init_video(regs);
10568b449a66SSimon Glass 	ret = exynos_dp_config_video(regs, priv);
105708a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
105808a7aa1eSSimon Glass 		printf("Exynos DP init failed\n");
105908a7aa1eSSimon Glass 		return ret;
106008a7aa1eSSimon Glass 	}
106108a7aa1eSSimon Glass 
106208a7aa1eSSimon Glass 	debug("Exynos DP init done\n");
106308a7aa1eSSimon Glass 
106408a7aa1eSSimon Glass 	return ret;
106508a7aa1eSSimon Glass }
1066bb5930d5SSimon Glass 
1067bb5930d5SSimon Glass 
1068bb5930d5SSimon Glass static const struct dm_display_ops exynos_dp_ops = {
1069bb5930d5SSimon Glass 	.enable = exynos_dp_enable,
1070bb5930d5SSimon Glass };
1071bb5930d5SSimon Glass 
1072bb5930d5SSimon Glass static const struct udevice_id exynos_dp_ids[] = {
1073bb5930d5SSimon Glass 	{ .compatible = "samsung,exynos5-dp" },
1074bb5930d5SSimon Glass 	{ }
1075bb5930d5SSimon Glass };
1076bb5930d5SSimon Glass 
1077bb5930d5SSimon Glass U_BOOT_DRIVER(exynos_dp) = {
1078*9b73bcc6SDongjin Kim 	.name	= "exynos_dp",
1079bb5930d5SSimon Glass 	.id	= UCLASS_DISPLAY,
1080bb5930d5SSimon Glass 	.of_match = exynos_dp_ids,
1081bb5930d5SSimon Glass 	.ops	= &exynos_dp_ops,
1082bb5930d5SSimon Glass 	.ofdata_to_platdata	= exynos_dp_ofdata_to_platdata,
1083bb5930d5SSimon Glass 	.priv_auto_alloc_size	= sizeof(struct exynos_dp_priv),
1084bb5930d5SSimon Glass };
1085