1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
208a7aa1eSSimon Glass /*
308a7aa1eSSimon Glass  * Copyright (C) 2012 Samsung Electronics
408a7aa1eSSimon Glass  *
508a7aa1eSSimon Glass  * Author: Donghwa Lee <dh09.lee@samsung.com>
608a7aa1eSSimon Glass  */
708a7aa1eSSimon Glass 
84af0d7e8SSimon Glass #include <common.h>
9bb5930d5SSimon Glass #include <dm.h>
1008a7aa1eSSimon Glass #include <common.h>
11bb5930d5SSimon Glass #include <display.h>
12bb5930d5SSimon Glass #include <fdtdec.h>
13b08c8c48SMasahiro Yamada #include <linux/libfdt.h>
1408a7aa1eSSimon Glass #include <malloc.h>
15bb5930d5SSimon Glass #include <video_bridge.h>
1608a7aa1eSSimon Glass #include <linux/compat.h>
1708a7aa1eSSimon Glass #include <linux/err.h>
1808a7aa1eSSimon Glass #include <asm/arch/clk.h>
1908a7aa1eSSimon Glass #include <asm/arch/cpu.h>
2008a7aa1eSSimon Glass #include <asm/arch/dp_info.h>
2108a7aa1eSSimon Glass #include <asm/arch/dp.h>
22bb5930d5SSimon Glass #include <asm/arch/pinmux.h>
237eb860dfSSimon Glass #include <asm/arch/power.h>
2408a7aa1eSSimon Glass 
2508a7aa1eSSimon Glass #include "exynos_dp_lowlevel.h"
2608a7aa1eSSimon Glass 
2708a7aa1eSSimon Glass DECLARE_GLOBAL_DATA_PTR;
2808a7aa1eSSimon Glass 
exynos_dp_disp_info(struct edp_disp_info * disp_info)2908a7aa1eSSimon Glass static void exynos_dp_disp_info(struct edp_disp_info *disp_info)
3008a7aa1eSSimon Glass {
3108a7aa1eSSimon Glass 	disp_info->h_total = disp_info->h_res + disp_info->h_sync_width +
3208a7aa1eSSimon Glass 		disp_info->h_back_porch + disp_info->h_front_porch;
3308a7aa1eSSimon Glass 	disp_info->v_total = disp_info->v_res + disp_info->v_sync_width +
3408a7aa1eSSimon Glass 		disp_info->v_back_porch + disp_info->v_front_porch;
3508a7aa1eSSimon Glass 
3608a7aa1eSSimon Glass 	return;
3708a7aa1eSSimon Glass }
3808a7aa1eSSimon Glass 
exynos_dp_init_dp(struct exynos_dp * regs)398b449a66SSimon Glass static int exynos_dp_init_dp(struct exynos_dp *regs)
4008a7aa1eSSimon Glass {
4108a7aa1eSSimon Glass 	int ret;
428b449a66SSimon Glass 	exynos_dp_reset(regs);
4308a7aa1eSSimon Glass 
4408a7aa1eSSimon Glass 	/* SW defined function Normal operation */
458b449a66SSimon Glass 	exynos_dp_enable_sw_func(regs, DP_ENABLE);
4608a7aa1eSSimon Glass 
478b449a66SSimon Glass 	ret = exynos_dp_init_analog_func(regs);
4808a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS)
4908a7aa1eSSimon Glass 		return ret;
5008a7aa1eSSimon Glass 
518b449a66SSimon Glass 	exynos_dp_init_hpd(regs);
528b449a66SSimon Glass 	exynos_dp_init_aux(regs);
5308a7aa1eSSimon Glass 
5408a7aa1eSSimon Glass 	return ret;
5508a7aa1eSSimon Glass }
5608a7aa1eSSimon Glass 
exynos_dp_calc_edid_check_sum(unsigned char * edid_data)5708a7aa1eSSimon Glass static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data)
5808a7aa1eSSimon Glass {
5908a7aa1eSSimon Glass 	int i;
6008a7aa1eSSimon Glass 	unsigned char sum = 0;
6108a7aa1eSSimon Glass 
6208a7aa1eSSimon Glass 	for (i = 0; i < EDID_BLOCK_LENGTH; i++)
6308a7aa1eSSimon Glass 		sum = sum + edid_data[i];
6408a7aa1eSSimon Glass 
6508a7aa1eSSimon Glass 	return sum;
6608a7aa1eSSimon Glass }
6708a7aa1eSSimon Glass 
exynos_dp_read_edid(struct exynos_dp * regs)688b449a66SSimon Glass static unsigned int exynos_dp_read_edid(struct exynos_dp *regs)
6908a7aa1eSSimon Glass {
7008a7aa1eSSimon Glass 	unsigned char edid[EDID_BLOCK_LENGTH * 2];
7108a7aa1eSSimon Glass 	unsigned int extend_block = 0;
7208a7aa1eSSimon Glass 	unsigned char sum;
7308a7aa1eSSimon Glass 	unsigned char test_vector;
7408a7aa1eSSimon Glass 	int retval;
7508a7aa1eSSimon Glass 
7608a7aa1eSSimon Glass 	/*
7708a7aa1eSSimon Glass 	 * EDID device address is 0x50.
7808a7aa1eSSimon Glass 	 * However, if necessary, you must have set upper address
7908a7aa1eSSimon Glass 	 * into E-EDID in I2C device, 0x30.
8008a7aa1eSSimon Glass 	 */
8108a7aa1eSSimon Glass 
8208a7aa1eSSimon Glass 	/* Read Extension Flag, Number of 128-byte EDID extension blocks */
838b449a66SSimon Glass 	exynos_dp_read_byte_from_i2c(regs, I2C_EDID_DEVICE_ADDR,
848c9b8dc0SSimon Glass 				     EDID_EXTENSION_FLAG, &extend_block);
8508a7aa1eSSimon Glass 
8608a7aa1eSSimon Glass 	if (extend_block > 0) {
8708a7aa1eSSimon Glass 		printf("DP EDID data includes a single extension!\n");
8808a7aa1eSSimon Glass 
8908a7aa1eSSimon Glass 		/* Read EDID data */
908b449a66SSimon Glass 		retval = exynos_dp_read_bytes_from_i2c(regs,
918c9b8dc0SSimon Glass 						I2C_EDID_DEVICE_ADDR,
9208a7aa1eSSimon Glass 						EDID_HEADER_PATTERN,
9308a7aa1eSSimon Glass 						EDID_BLOCK_LENGTH,
9408a7aa1eSSimon Glass 						&edid[EDID_HEADER_PATTERN]);
9508a7aa1eSSimon Glass 		if (retval != 0) {
9608a7aa1eSSimon Glass 			printf("DP EDID Read failed!\n");
9708a7aa1eSSimon Glass 			return -1;
9808a7aa1eSSimon Glass 		}
9908a7aa1eSSimon Glass 		sum = exynos_dp_calc_edid_check_sum(edid);
10008a7aa1eSSimon Glass 		if (sum != 0) {
10108a7aa1eSSimon Glass 			printf("DP EDID bad checksum!\n");
10208a7aa1eSSimon Glass 			return -1;
10308a7aa1eSSimon Glass 		}
10408a7aa1eSSimon Glass 
10508a7aa1eSSimon Glass 		/* Read additional EDID data */
1068b449a66SSimon Glass 		retval = exynos_dp_read_bytes_from_i2c(regs,
1078c9b8dc0SSimon Glass 				I2C_EDID_DEVICE_ADDR,
10808a7aa1eSSimon Glass 				EDID_BLOCK_LENGTH,
10908a7aa1eSSimon Glass 				EDID_BLOCK_LENGTH,
11008a7aa1eSSimon Glass 				&edid[EDID_BLOCK_LENGTH]);
11108a7aa1eSSimon Glass 		if (retval != 0) {
11208a7aa1eSSimon Glass 			printf("DP EDID Read failed!\n");
11308a7aa1eSSimon Glass 			return -1;
11408a7aa1eSSimon Glass 		}
11508a7aa1eSSimon Glass 		sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
11608a7aa1eSSimon Glass 		if (sum != 0) {
11708a7aa1eSSimon Glass 			printf("DP EDID bad checksum!\n");
11808a7aa1eSSimon Glass 			return -1;
11908a7aa1eSSimon Glass 		}
12008a7aa1eSSimon Glass 
1218b449a66SSimon Glass 		exynos_dp_read_byte_from_dpcd(regs, DPCD_TEST_REQUEST,
12208a7aa1eSSimon Glass 					      &test_vector);
12308a7aa1eSSimon Glass 		if (test_vector & DPCD_TEST_EDID_READ) {
1248b449a66SSimon Glass 			exynos_dp_write_byte_to_dpcd(regs,
1258c9b8dc0SSimon Glass 				DPCD_TEST_EDID_CHECKSUM,
12608a7aa1eSSimon Glass 				edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
1278b449a66SSimon Glass 			exynos_dp_write_byte_to_dpcd(regs,
1288c9b8dc0SSimon Glass 				DPCD_TEST_RESPONSE,
12908a7aa1eSSimon Glass 				DPCD_TEST_EDID_CHECKSUM_WRITE);
13008a7aa1eSSimon Glass 		}
13108a7aa1eSSimon Glass 	} else {
13208a7aa1eSSimon Glass 		debug("DP EDID data does not include any extensions.\n");
13308a7aa1eSSimon Glass 
13408a7aa1eSSimon Glass 		/* Read EDID data */
1358b449a66SSimon Glass 		retval = exynos_dp_read_bytes_from_i2c(regs,
1368c9b8dc0SSimon Glass 				I2C_EDID_DEVICE_ADDR,
13708a7aa1eSSimon Glass 				EDID_HEADER_PATTERN,
13808a7aa1eSSimon Glass 				EDID_BLOCK_LENGTH,
13908a7aa1eSSimon Glass 				&edid[EDID_HEADER_PATTERN]);
14008a7aa1eSSimon Glass 
14108a7aa1eSSimon Glass 		if (retval != 0) {
14208a7aa1eSSimon Glass 			printf("DP EDID Read failed!\n");
14308a7aa1eSSimon Glass 			return -1;
14408a7aa1eSSimon Glass 		}
14508a7aa1eSSimon Glass 		sum = exynos_dp_calc_edid_check_sum(edid);
14608a7aa1eSSimon Glass 		if (sum != 0) {
14708a7aa1eSSimon Glass 			printf("DP EDID bad checksum!\n");
14808a7aa1eSSimon Glass 			return -1;
14908a7aa1eSSimon Glass 		}
15008a7aa1eSSimon Glass 
1518b449a66SSimon Glass 		exynos_dp_read_byte_from_dpcd(regs, DPCD_TEST_REQUEST,
15208a7aa1eSSimon Glass 			&test_vector);
15308a7aa1eSSimon Glass 		if (test_vector & DPCD_TEST_EDID_READ) {
1548b449a66SSimon Glass 			exynos_dp_write_byte_to_dpcd(regs,
1558c9b8dc0SSimon Glass 				DPCD_TEST_EDID_CHECKSUM, edid[EDID_CHECKSUM]);
1568b449a66SSimon Glass 			exynos_dp_write_byte_to_dpcd(regs,
1578c9b8dc0SSimon Glass 				DPCD_TEST_RESPONSE,
15808a7aa1eSSimon Glass 				DPCD_TEST_EDID_CHECKSUM_WRITE);
15908a7aa1eSSimon Glass 		}
16008a7aa1eSSimon Glass 	}
16108a7aa1eSSimon Glass 
16208a7aa1eSSimon Glass 	debug("DP EDID Read success!\n");
16308a7aa1eSSimon Glass 
16408a7aa1eSSimon Glass 	return 0;
16508a7aa1eSSimon Glass }
16608a7aa1eSSimon Glass 
exynos_dp_handle_edid(struct exynos_dp * regs,struct exynos_dp_priv * priv)1678b449a66SSimon Glass static unsigned int exynos_dp_handle_edid(struct exynos_dp *regs,
1688b449a66SSimon Glass 					  struct exynos_dp_priv *priv)
16908a7aa1eSSimon Glass {
17008a7aa1eSSimon Glass 	unsigned char buf[12];
17108a7aa1eSSimon Glass 	unsigned int ret;
17208a7aa1eSSimon Glass 	unsigned char temp;
17308a7aa1eSSimon Glass 	unsigned char retry_cnt;
17408a7aa1eSSimon Glass 	unsigned char dpcd_rev[16];
17508a7aa1eSSimon Glass 	unsigned char lane_bw[16];
17608a7aa1eSSimon Glass 	unsigned char lane_cnt[16];
17708a7aa1eSSimon Glass 
17808a7aa1eSSimon Glass 	memset(dpcd_rev, 0, 16);
17908a7aa1eSSimon Glass 	memset(lane_bw, 0, 16);
18008a7aa1eSSimon Glass 	memset(lane_cnt, 0, 16);
18108a7aa1eSSimon Glass 	memset(buf, 0, 12);
18208a7aa1eSSimon Glass 
18308a7aa1eSSimon Glass 	retry_cnt = 5;
18408a7aa1eSSimon Glass 	while (retry_cnt) {
18508a7aa1eSSimon Glass 		/* Read DPCD 0x0000-0x000b */
1868b449a66SSimon Glass 		ret = exynos_dp_read_bytes_from_dpcd(regs, DPCD_DPCD_REV, 12,
18708a7aa1eSSimon Glass 						     buf);
18808a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS) {
18908a7aa1eSSimon Glass 			if (retry_cnt == 0) {
19008a7aa1eSSimon Glass 				printf("DP read_byte_from_dpcd() failed\n");
19108a7aa1eSSimon Glass 				return ret;
19208a7aa1eSSimon Glass 			}
19308a7aa1eSSimon Glass 			retry_cnt--;
19408a7aa1eSSimon Glass 		} else
19508a7aa1eSSimon Glass 			break;
19608a7aa1eSSimon Glass 	}
19708a7aa1eSSimon Glass 
19808a7aa1eSSimon Glass 	/* */
19908a7aa1eSSimon Glass 	temp = buf[DPCD_DPCD_REV];
20008a7aa1eSSimon Glass 	if (temp == DP_DPCD_REV_10 || temp == DP_DPCD_REV_11)
2018b449a66SSimon Glass 		priv->dpcd_rev = temp;
20208a7aa1eSSimon Glass 	else {
20308a7aa1eSSimon Glass 		printf("DP Wrong DPCD Rev : %x\n", temp);
20408a7aa1eSSimon Glass 		return -ENODEV;
20508a7aa1eSSimon Glass 	}
20608a7aa1eSSimon Glass 
20708a7aa1eSSimon Glass 	temp = buf[DPCD_MAX_LINK_RATE];
20808a7aa1eSSimon Glass 	if (temp == DP_LANE_BW_1_62 || temp == DP_LANE_BW_2_70)
2098b449a66SSimon Glass 		priv->lane_bw = temp;
21008a7aa1eSSimon Glass 	else {
21108a7aa1eSSimon Glass 		printf("DP Wrong MAX LINK RATE : %x\n", temp);
21208a7aa1eSSimon Glass 		return -EINVAL;
21308a7aa1eSSimon Glass 	}
21408a7aa1eSSimon Glass 
21508a7aa1eSSimon Glass 	/* Refer VESA Display Port Standard Ver1.1a Page 120 */
2168b449a66SSimon Glass 	if (priv->dpcd_rev == DP_DPCD_REV_11) {
21708a7aa1eSSimon Glass 		temp = buf[DPCD_MAX_LANE_COUNT] & 0x1f;
21808a7aa1eSSimon Glass 		if (buf[DPCD_MAX_LANE_COUNT] & 0x80)
2198b449a66SSimon Glass 			priv->dpcd_efc = 1;
22008a7aa1eSSimon Glass 		else
2218b449a66SSimon Glass 			priv->dpcd_efc = 0;
22208a7aa1eSSimon Glass 	} else {
22308a7aa1eSSimon Glass 		temp = buf[DPCD_MAX_LANE_COUNT];
2248b449a66SSimon Glass 		priv->dpcd_efc = 0;
22508a7aa1eSSimon Glass 	}
22608a7aa1eSSimon Glass 
22708a7aa1eSSimon Glass 	if (temp == DP_LANE_CNT_1 || temp == DP_LANE_CNT_2 ||
22808a7aa1eSSimon Glass 			temp == DP_LANE_CNT_4) {
2298b449a66SSimon Glass 		priv->lane_cnt = temp;
23008a7aa1eSSimon Glass 	} else {
23108a7aa1eSSimon Glass 		printf("DP Wrong MAX LANE COUNT : %x\n", temp);
23208a7aa1eSSimon Glass 		return -EINVAL;
23308a7aa1eSSimon Glass 	}
23408a7aa1eSSimon Glass 
2358b449a66SSimon Glass 	ret = exynos_dp_read_edid(regs);
23608a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
23708a7aa1eSSimon Glass 		printf("DP exynos_dp_read_edid() failed\n");
23808a7aa1eSSimon Glass 		return -EINVAL;
23908a7aa1eSSimon Glass 	}
24008a7aa1eSSimon Glass 
24108a7aa1eSSimon Glass 	return ret;
24208a7aa1eSSimon Glass }
24308a7aa1eSSimon Glass 
exynos_dp_init_training(struct exynos_dp * regs)2448b449a66SSimon Glass static void exynos_dp_init_training(struct exynos_dp *regs)
24508a7aa1eSSimon Glass {
24608a7aa1eSSimon Glass 	/*
24708a7aa1eSSimon Glass 	 * MACRO_RST must be applied after the PLL_LOCK to avoid
24808a7aa1eSSimon Glass 	 * the DP inter pair skew issue for at least 10 us
24908a7aa1eSSimon Glass 	 */
2508b449a66SSimon Glass 	exynos_dp_reset_macro(regs);
25108a7aa1eSSimon Glass 
25208a7aa1eSSimon Glass 	/* All DP analog module power up */
2538b449a66SSimon Glass 	exynos_dp_set_analog_power_down(regs, POWER_ALL, 0);
25408a7aa1eSSimon Glass }
25508a7aa1eSSimon Glass 
exynos_dp_link_start(struct exynos_dp * regs,struct exynos_dp_priv * priv)2568b449a66SSimon Glass static unsigned int exynos_dp_link_start(struct exynos_dp *regs,
2578b449a66SSimon Glass 					 struct exynos_dp_priv *priv)
25808a7aa1eSSimon Glass {
25908a7aa1eSSimon Glass 	unsigned char buf[5];
26008a7aa1eSSimon Glass 	unsigned int ret = 0;
26108a7aa1eSSimon Glass 
26208a7aa1eSSimon Glass 	debug("DP: %s was called\n", __func__);
26308a7aa1eSSimon Glass 
2648b449a66SSimon Glass 	priv->lt_info.lt_status = DP_LT_CR;
2658b449a66SSimon Glass 	priv->lt_info.ep_loop = 0;
2668b449a66SSimon Glass 	priv->lt_info.cr_loop[0] = 0;
2678b449a66SSimon Glass 	priv->lt_info.cr_loop[1] = 0;
2688b449a66SSimon Glass 	priv->lt_info.cr_loop[2] = 0;
2698b449a66SSimon Glass 	priv->lt_info.cr_loop[3] = 0;
27008a7aa1eSSimon Glass 
27108a7aa1eSSimon Glass 		/* Set sink to D0 (Sink Not Ready) mode. */
2728b449a66SSimon Glass 	ret = exynos_dp_write_byte_to_dpcd(regs, DPCD_SINK_POWER_STATE,
27308a7aa1eSSimon Glass 					   DPCD_SET_POWER_STATE_D0);
27408a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
27508a7aa1eSSimon Glass 		printf("DP write_dpcd_byte failed\n");
27608a7aa1eSSimon Glass 		return ret;
27708a7aa1eSSimon Glass 	}
27808a7aa1eSSimon Glass 
27908a7aa1eSSimon Glass 	/* Set link rate and count as you want to establish */
2808b449a66SSimon Glass 	exynos_dp_set_link_bandwidth(regs, priv->lane_bw);
2818b449a66SSimon Glass 	exynos_dp_set_lane_count(regs, priv->lane_cnt);
28208a7aa1eSSimon Glass 
28308a7aa1eSSimon Glass 	/* Setup RX configuration */
2848b449a66SSimon Glass 	buf[0] = priv->lane_bw;
2858b449a66SSimon Glass 	buf[1] = priv->lane_cnt;
28608a7aa1eSSimon Glass 
2878b449a66SSimon Glass 	ret = exynos_dp_write_bytes_to_dpcd(regs, DPCD_LINK_BW_SET, 2, buf);
28808a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
28908a7aa1eSSimon Glass 		printf("DP write_dpcd_byte failed\n");
29008a7aa1eSSimon Glass 		return ret;
29108a7aa1eSSimon Glass 	}
29208a7aa1eSSimon Glass 
2938b449a66SSimon Glass 	exynos_dp_set_lane_pre_emphasis(regs, PRE_EMPHASIS_LEVEL_0,
2948b449a66SSimon Glass 			priv->lane_cnt);
29508a7aa1eSSimon Glass 
29608a7aa1eSSimon Glass 	/* Set training pattern 1 */
2978b449a66SSimon Glass 	exynos_dp_set_training_pattern(regs, TRAINING_PTN1);
29808a7aa1eSSimon Glass 
29908a7aa1eSSimon Glass 	/* Set RX training pattern */
30008a7aa1eSSimon Glass 	buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1;
30108a7aa1eSSimon Glass 
30208a7aa1eSSimon Glass 	buf[1] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
30308a7aa1eSSimon Glass 		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
30408a7aa1eSSimon Glass 	buf[2] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
30508a7aa1eSSimon Glass 		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
30608a7aa1eSSimon Glass 	buf[3] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
30708a7aa1eSSimon Glass 		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
30808a7aa1eSSimon Glass 	buf[4] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
30908a7aa1eSSimon Glass 		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
31008a7aa1eSSimon Glass 
3118b449a66SSimon Glass 	ret = exynos_dp_write_bytes_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET,
31208a7aa1eSSimon Glass 					    5, buf);
31308a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
31408a7aa1eSSimon Glass 		printf("DP write_dpcd_byte failed\n");
31508a7aa1eSSimon Glass 		return ret;
31608a7aa1eSSimon Glass 	}
31708a7aa1eSSimon Glass 
31808a7aa1eSSimon Glass 	return ret;
31908a7aa1eSSimon Glass }
32008a7aa1eSSimon Glass 
exynos_dp_training_pattern_dis(struct exynos_dp * regs)3218b449a66SSimon Glass static unsigned int exynos_dp_training_pattern_dis(struct exynos_dp *regs)
32208a7aa1eSSimon Glass {
3231ef9aed9SHeinrich Schuchardt 	unsigned int ret;
32408a7aa1eSSimon Glass 
3258b449a66SSimon Glass 	exynos_dp_set_training_pattern(regs, DP_NONE);
32608a7aa1eSSimon Glass 
3278b449a66SSimon Glass 	ret = exynos_dp_write_byte_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET,
32808a7aa1eSSimon Glass 					   DPCD_TRAINING_PATTERN_DISABLED);
32908a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
33008a7aa1eSSimon Glass 		printf("DP request_link_training_req failed\n");
33108a7aa1eSSimon Glass 		return -EAGAIN;
33208a7aa1eSSimon Glass 	}
33308a7aa1eSSimon Glass 
33408a7aa1eSSimon Glass 	return ret;
33508a7aa1eSSimon Glass }
33608a7aa1eSSimon Glass 
exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp * regs,unsigned char enable)3378c9b8dc0SSimon Glass static unsigned int exynos_dp_enable_rx_to_enhanced_mode(
3388b449a66SSimon Glass 		struct exynos_dp *regs, unsigned char enable)
33908a7aa1eSSimon Glass {
34008a7aa1eSSimon Glass 	unsigned char data;
3411ef9aed9SHeinrich Schuchardt 	unsigned int ret;
34208a7aa1eSSimon Glass 
3438b449a66SSimon Glass 	ret = exynos_dp_read_byte_from_dpcd(regs, DPCD_LANE_COUNT_SET,
34408a7aa1eSSimon Glass 					    &data);
34508a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
34608a7aa1eSSimon Glass 		printf("DP read_from_dpcd failed\n");
34708a7aa1eSSimon Glass 		return -EAGAIN;
34808a7aa1eSSimon Glass 	}
34908a7aa1eSSimon Glass 
35008a7aa1eSSimon Glass 	if (enable)
35108a7aa1eSSimon Glass 		data = DPCD_ENHANCED_FRAME_EN | DPCD_LN_COUNT_SET(data);
35208a7aa1eSSimon Glass 	else
35308a7aa1eSSimon Glass 		data = DPCD_LN_COUNT_SET(data);
35408a7aa1eSSimon Glass 
3558b449a66SSimon Glass 	ret = exynos_dp_write_byte_to_dpcd(regs, DPCD_LANE_COUNT_SET, data);
35608a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
35708a7aa1eSSimon Glass 			printf("DP write_to_dpcd failed\n");
35808a7aa1eSSimon Glass 			return -EAGAIN;
35908a7aa1eSSimon Glass 
36008a7aa1eSSimon Glass 	}
36108a7aa1eSSimon Glass 
36208a7aa1eSSimon Glass 	return ret;
36308a7aa1eSSimon Glass }
36408a7aa1eSSimon Glass 
exynos_dp_set_enhanced_mode(struct exynos_dp * regs,unsigned char enhance_mode)3658b449a66SSimon Glass static unsigned int exynos_dp_set_enhanced_mode(struct exynos_dp *regs,
3668c9b8dc0SSimon Glass 						unsigned char enhance_mode)
36708a7aa1eSSimon Glass {
3681ef9aed9SHeinrich Schuchardt 	unsigned int ret;
36908a7aa1eSSimon Glass 
3708b449a66SSimon Glass 	ret = exynos_dp_enable_rx_to_enhanced_mode(regs, enhance_mode);
37108a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
37208a7aa1eSSimon Glass 		printf("DP rx_enhance_mode failed\n");
37308a7aa1eSSimon Glass 		return -EAGAIN;
37408a7aa1eSSimon Glass 	}
37508a7aa1eSSimon Glass 
3768b449a66SSimon Glass 	exynos_dp_enable_enhanced_mode(regs, enhance_mode);
37708a7aa1eSSimon Glass 
37808a7aa1eSSimon Glass 	return ret;
37908a7aa1eSSimon Glass }
38008a7aa1eSSimon Glass 
exynos_dp_read_dpcd_lane_stat(struct exynos_dp * regs,struct exynos_dp_priv * priv,unsigned char * status)3818b449a66SSimon Glass static int exynos_dp_read_dpcd_lane_stat(struct exynos_dp *regs,
3828b449a66SSimon Glass 					 struct exynos_dp_priv *priv,
38308a7aa1eSSimon Glass 					 unsigned char *status)
38408a7aa1eSSimon Glass {
38508a7aa1eSSimon Glass 	unsigned int ret, i;
38608a7aa1eSSimon Glass 	unsigned char buf[2];
38708a7aa1eSSimon Glass 	unsigned char lane_stat[DP_LANE_CNT_4] = {0,};
38808a7aa1eSSimon Glass 	unsigned char shift_val[DP_LANE_CNT_4] = {0,};
38908a7aa1eSSimon Glass 
39008a7aa1eSSimon Glass 	shift_val[0] = 0;
39108a7aa1eSSimon Glass 	shift_val[1] = 4;
39208a7aa1eSSimon Glass 	shift_val[2] = 0;
39308a7aa1eSSimon Glass 	shift_val[3] = 4;
39408a7aa1eSSimon Glass 
3958b449a66SSimon Glass 	ret = exynos_dp_read_bytes_from_dpcd(regs, DPCD_LANE0_1_STATUS, 2,
3968c9b8dc0SSimon Glass 					     buf);
39708a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
39808a7aa1eSSimon Glass 		printf("DP read lane status failed\n");
39908a7aa1eSSimon Glass 		return ret;
40008a7aa1eSSimon Glass 	}
40108a7aa1eSSimon Glass 
4028b449a66SSimon Glass 	for (i = 0; i < priv->lane_cnt; i++) {
40308a7aa1eSSimon Glass 		lane_stat[i] = (buf[(i / 2)] >> shift_val[i]) & 0x0f;
40408a7aa1eSSimon Glass 		if (lane_stat[0] != lane_stat[i]) {
40508a7aa1eSSimon Glass 			printf("Wrong lane status\n");
40608a7aa1eSSimon Glass 			return -EINVAL;
40708a7aa1eSSimon Glass 		}
40808a7aa1eSSimon Glass 	}
40908a7aa1eSSimon Glass 
41008a7aa1eSSimon Glass 	*status = lane_stat[0];
41108a7aa1eSSimon Glass 
41208a7aa1eSSimon Glass 	return ret;
41308a7aa1eSSimon Glass }
41408a7aa1eSSimon Glass 
exynos_dp_read_dpcd_adj_req(struct exynos_dp * regs,unsigned char lane_num,unsigned char * sw,unsigned char * em)4158b449a66SSimon Glass static unsigned int exynos_dp_read_dpcd_adj_req(struct exynos_dp *regs,
4168c9b8dc0SSimon Glass 		unsigned char lane_num, unsigned char *sw, unsigned char *em)
41708a7aa1eSSimon Glass {
4181ef9aed9SHeinrich Schuchardt 	unsigned int ret;
41908a7aa1eSSimon Glass 	unsigned char buf;
42008a7aa1eSSimon Glass 	unsigned int dpcd_addr;
42108a7aa1eSSimon Glass 	unsigned char shift_val[DP_LANE_CNT_4] = {0, 4, 0, 4};
42208a7aa1eSSimon Glass 
42308a7aa1eSSimon Glass 	/* lane_num value is used as array index, so this range 0 ~ 3 */
42408a7aa1eSSimon Glass 	dpcd_addr = DPCD_ADJUST_REQUEST_LANE0_1 + (lane_num / 2);
42508a7aa1eSSimon Glass 
4268b449a66SSimon Glass 	ret = exynos_dp_read_byte_from_dpcd(regs, dpcd_addr, &buf);
42708a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
42808a7aa1eSSimon Glass 		printf("DP read adjust request failed\n");
42908a7aa1eSSimon Glass 		return -EAGAIN;
43008a7aa1eSSimon Glass 	}
43108a7aa1eSSimon Glass 
43208a7aa1eSSimon Glass 	*sw = ((buf >> shift_val[lane_num]) & 0x03);
43308a7aa1eSSimon Glass 	*em = ((buf >> shift_val[lane_num]) & 0x0c) >> 2;
43408a7aa1eSSimon Glass 
43508a7aa1eSSimon Glass 	return ret;
43608a7aa1eSSimon Glass }
43708a7aa1eSSimon Glass 
exynos_dp_equalizer_err_link(struct exynos_dp * regs,struct exynos_dp_priv * priv)4388b449a66SSimon Glass static int exynos_dp_equalizer_err_link(struct exynos_dp *regs,
4398b449a66SSimon Glass 					struct exynos_dp_priv *priv)
44008a7aa1eSSimon Glass {
44108a7aa1eSSimon Glass 	int ret;
44208a7aa1eSSimon Glass 
4438b449a66SSimon Glass 	ret = exynos_dp_training_pattern_dis(regs);
44408a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
44508a7aa1eSSimon Glass 		printf("DP training_pattern_disable() failed\n");
4468b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
44708a7aa1eSSimon Glass 	}
44808a7aa1eSSimon Glass 
4498b449a66SSimon Glass 	ret = exynos_dp_set_enhanced_mode(regs, priv->dpcd_efc);
45008a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
45108a7aa1eSSimon Glass 		printf("DP set_enhanced_mode() failed\n");
4528b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
45308a7aa1eSSimon Glass 	}
45408a7aa1eSSimon Glass 
45508a7aa1eSSimon Glass 	return ret;
45608a7aa1eSSimon Glass }
45708a7aa1eSSimon Glass 
exynos_dp_reduce_link_rate(struct exynos_dp * regs,struct exynos_dp_priv * priv)4588b449a66SSimon Glass static int exynos_dp_reduce_link_rate(struct exynos_dp *regs,
4598b449a66SSimon Glass 				      struct exynos_dp_priv *priv)
46008a7aa1eSSimon Glass {
46108a7aa1eSSimon Glass 	int ret;
46208a7aa1eSSimon Glass 
4638b449a66SSimon Glass 	if (priv->lane_bw == DP_LANE_BW_2_70) {
4648b449a66SSimon Glass 		priv->lane_bw = DP_LANE_BW_1_62;
46508a7aa1eSSimon Glass 		printf("DP Change lane bw to 1.62Gbps\n");
4668b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_START;
46708a7aa1eSSimon Glass 		ret = EXYNOS_DP_SUCCESS;
46808a7aa1eSSimon Glass 	} else {
4698b449a66SSimon Glass 		ret = exynos_dp_training_pattern_dis(regs);
47008a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS)
47108a7aa1eSSimon Glass 			printf("DP training_patter_disable() failed\n");
47208a7aa1eSSimon Glass 
4738b449a66SSimon Glass 		ret = exynos_dp_set_enhanced_mode(regs, priv->dpcd_efc);
47408a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS)
47508a7aa1eSSimon Glass 			printf("DP set_enhanced_mode() failed\n");
47608a7aa1eSSimon Glass 
4778b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
47808a7aa1eSSimon Glass 	}
47908a7aa1eSSimon Glass 
48008a7aa1eSSimon Glass 	return ret;
48108a7aa1eSSimon Glass }
48208a7aa1eSSimon Glass 
exynos_dp_process_clock_recovery(struct exynos_dp * regs,struct exynos_dp_priv * priv)4838b449a66SSimon Glass static unsigned int exynos_dp_process_clock_recovery(struct exynos_dp *regs,
4848b449a66SSimon Glass 					struct exynos_dp_priv *priv)
48508a7aa1eSSimon Glass {
4861ef9aed9SHeinrich Schuchardt 	unsigned int ret;
48708a7aa1eSSimon Glass 	unsigned char lane_stat;
48808a7aa1eSSimon Glass 	unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, };
48908a7aa1eSSimon Glass 	unsigned int i;
49008a7aa1eSSimon Glass 	unsigned char adj_req_sw;
49108a7aa1eSSimon Glass 	unsigned char adj_req_em;
49208a7aa1eSSimon Glass 	unsigned char buf[5];
49308a7aa1eSSimon Glass 
49408a7aa1eSSimon Glass 	debug("DP: %s was called\n", __func__);
49508a7aa1eSSimon Glass 	mdelay(1);
49608a7aa1eSSimon Glass 
4978b449a66SSimon Glass 	ret = exynos_dp_read_dpcd_lane_stat(regs, priv, &lane_stat);
49808a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
49908a7aa1eSSimon Glass 			printf("DP read lane status failed\n");
5008b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FAIL;
50108a7aa1eSSimon Glass 			return ret;
50208a7aa1eSSimon Glass 	}
50308a7aa1eSSimon Glass 
50408a7aa1eSSimon Glass 	if (lane_stat & DP_LANE_STAT_CR_DONE) {
50508a7aa1eSSimon Glass 		debug("DP clock Recovery training succeed\n");
5068b449a66SSimon Glass 		exynos_dp_set_training_pattern(regs, TRAINING_PTN2);
50708a7aa1eSSimon Glass 
5088b449a66SSimon Glass 		for (i = 0; i < priv->lane_cnt; i++) {
5098b449a66SSimon Glass 			ret = exynos_dp_read_dpcd_adj_req(regs, i,
5108c9b8dc0SSimon Glass 						&adj_req_sw, &adj_req_em);
51108a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
5128b449a66SSimon Glass 				priv->lt_info.lt_status = DP_LT_FAIL;
51308a7aa1eSSimon Glass 				return ret;
51408a7aa1eSSimon Glass 			}
51508a7aa1eSSimon Glass 
51608a7aa1eSSimon Glass 			lt_ctl_val[i] = 0;
51708a7aa1eSSimon Glass 			lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
51808a7aa1eSSimon Glass 
51908a7aa1eSSimon Glass 			if ((adj_req_sw == VOLTAGE_LEVEL_3)
52008a7aa1eSSimon Glass 				|| (adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
52108a7aa1eSSimon Glass 				lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 |
52208a7aa1eSSimon Glass 					MAX_PRE_EMPHASIS_REACH_3;
52308a7aa1eSSimon Glass 			}
5248b449a66SSimon Glass 			exynos_dp_set_lanex_pre_emphasis(regs,
5258c9b8dc0SSimon Glass 							 lt_ctl_val[i], i);
52608a7aa1eSSimon Glass 		}
52708a7aa1eSSimon Glass 
52808a7aa1eSSimon Glass 		buf[0] =  DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_2;
52908a7aa1eSSimon Glass 		buf[1] = lt_ctl_val[0];
53008a7aa1eSSimon Glass 		buf[2] = lt_ctl_val[1];
53108a7aa1eSSimon Glass 		buf[3] = lt_ctl_val[2];
53208a7aa1eSSimon Glass 		buf[4] = lt_ctl_val[3];
53308a7aa1eSSimon Glass 
5348b449a66SSimon Glass 		ret = exynos_dp_write_bytes_to_dpcd(regs,
53508a7aa1eSSimon Glass 				DPCD_TRAINING_PATTERN_SET, 5, buf);
53608a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS) {
53708a7aa1eSSimon Glass 			printf("DP write training pattern1 failed\n");
5388b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FAIL;
53908a7aa1eSSimon Glass 			return ret;
54008a7aa1eSSimon Glass 		} else
5418b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_ET;
54208a7aa1eSSimon Glass 	} else {
5438b449a66SSimon Glass 		for (i = 0; i < priv->lane_cnt; i++) {
5448c9b8dc0SSimon Glass 			lt_ctl_val[i] = exynos_dp_get_lanex_pre_emphasis(
5458b449a66SSimon Glass 						regs, i);
5468b449a66SSimon Glass 				ret = exynos_dp_read_dpcd_adj_req(regs, i,
54708a7aa1eSSimon Glass 						&adj_req_sw, &adj_req_em);
54808a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
54908a7aa1eSSimon Glass 				printf("DP read adj req failed\n");
5508b449a66SSimon Glass 				priv->lt_info.lt_status = DP_LT_FAIL;
55108a7aa1eSSimon Glass 				return ret;
55208a7aa1eSSimon Glass 			}
55308a7aa1eSSimon Glass 
55408a7aa1eSSimon Glass 			if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
55508a7aa1eSSimon Glass 					(adj_req_em == PRE_EMPHASIS_LEVEL_3))
5568b449a66SSimon Glass 				ret = exynos_dp_reduce_link_rate(regs,
5578b449a66SSimon Glass 								 priv);
55808a7aa1eSSimon Glass 
55908a7aa1eSSimon Glass 			if ((DRIVE_CURRENT_SET_0_GET(lt_ctl_val[i]) ==
56008a7aa1eSSimon Glass 						adj_req_sw) &&
56108a7aa1eSSimon Glass 				(PRE_EMPHASIS_SET_0_GET(lt_ctl_val[i]) ==
56208a7aa1eSSimon Glass 						adj_req_em)) {
5638b449a66SSimon Glass 				priv->lt_info.cr_loop[i]++;
5648b449a66SSimon Glass 				if (priv->lt_info.cr_loop[i] == MAX_CR_LOOP)
56508a7aa1eSSimon Glass 					ret = exynos_dp_reduce_link_rate(
5668b449a66SSimon Glass 							regs, priv);
56708a7aa1eSSimon Glass 			}
56808a7aa1eSSimon Glass 
56908a7aa1eSSimon Glass 			lt_ctl_val[i] = 0;
57008a7aa1eSSimon Glass 			lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
57108a7aa1eSSimon Glass 
57208a7aa1eSSimon Glass 			if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
57308a7aa1eSSimon Glass 					(adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
57408a7aa1eSSimon Glass 				lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 |
57508a7aa1eSSimon Glass 					MAX_PRE_EMPHASIS_REACH_3;
57608a7aa1eSSimon Glass 			}
5778b449a66SSimon Glass 			exynos_dp_set_lanex_pre_emphasis(regs,
5788c9b8dc0SSimon Glass 							 lt_ctl_val[i], i);
57908a7aa1eSSimon Glass 		}
58008a7aa1eSSimon Glass 
5818b449a66SSimon Glass 		ret = exynos_dp_write_bytes_to_dpcd(regs,
58208a7aa1eSSimon Glass 				DPCD_TRAINING_LANE0_SET, 4, lt_ctl_val);
58308a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS) {
58408a7aa1eSSimon Glass 			printf("DP write training pattern2 failed\n");
5858b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FAIL;
58608a7aa1eSSimon Glass 			return ret;
58708a7aa1eSSimon Glass 		}
58808a7aa1eSSimon Glass 	}
58908a7aa1eSSimon Glass 
59008a7aa1eSSimon Glass 	return ret;
59108a7aa1eSSimon Glass }
59208a7aa1eSSimon Glass 
exynos_dp_process_equalizer_training(struct exynos_dp * regs,struct exynos_dp_priv * priv)5938c9b8dc0SSimon Glass static unsigned int exynos_dp_process_equalizer_training(
5948b449a66SSimon Glass 		struct exynos_dp *regs, struct exynos_dp_priv *priv)
59508a7aa1eSSimon Glass {
5961ef9aed9SHeinrich Schuchardt 	unsigned int ret;
59708a7aa1eSSimon Glass 	unsigned char lane_stat, adj_req_sw, adj_req_em, i;
59808a7aa1eSSimon Glass 	unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,};
59908a7aa1eSSimon Glass 	unsigned char interlane_aligned = 0;
60008a7aa1eSSimon Glass 	unsigned char f_bw;
60108a7aa1eSSimon Glass 	unsigned char f_lane_cnt;
60208a7aa1eSSimon Glass 	unsigned char sink_stat;
60308a7aa1eSSimon Glass 
60408a7aa1eSSimon Glass 	mdelay(1);
60508a7aa1eSSimon Glass 
6068b449a66SSimon Glass 	ret = exynos_dp_read_dpcd_lane_stat(regs, priv, &lane_stat);
60708a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
60808a7aa1eSSimon Glass 		printf("DP read lane status failed\n");
6098b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
61008a7aa1eSSimon Glass 		return ret;
61108a7aa1eSSimon Glass 	}
61208a7aa1eSSimon Glass 
61308a7aa1eSSimon Glass 	debug("DP lane stat : %x\n", lane_stat);
61408a7aa1eSSimon Glass 
61508a7aa1eSSimon Glass 	if (lane_stat & DP_LANE_STAT_CR_DONE) {
6168b449a66SSimon Glass 		ret = exynos_dp_read_byte_from_dpcd(regs,
6178c9b8dc0SSimon Glass 						    DPCD_LN_ALIGN_UPDATED,
61808a7aa1eSSimon Glass 						    &sink_stat);
61908a7aa1eSSimon Glass 		if (ret != EXYNOS_DP_SUCCESS) {
6208b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FAIL;
62108a7aa1eSSimon Glass 
62208a7aa1eSSimon Glass 			return ret;
62308a7aa1eSSimon Glass 		}
62408a7aa1eSSimon Glass 
62508a7aa1eSSimon Glass 		interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE);
62608a7aa1eSSimon Glass 
6278b449a66SSimon Glass 		for (i = 0; i < priv->lane_cnt; i++) {
6288b449a66SSimon Glass 			ret = exynos_dp_read_dpcd_adj_req(regs, i,
62908a7aa1eSSimon Glass 					&adj_req_sw, &adj_req_em);
63008a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
63108a7aa1eSSimon Glass 				printf("DP read adj req 1 failed\n");
6328b449a66SSimon Glass 				priv->lt_info.lt_status = DP_LT_FAIL;
63308a7aa1eSSimon Glass 
63408a7aa1eSSimon Glass 				return ret;
63508a7aa1eSSimon Glass 			}
63608a7aa1eSSimon Glass 
63708a7aa1eSSimon Glass 			lt_ctl_val[i] = 0;
63808a7aa1eSSimon Glass 			lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
63908a7aa1eSSimon Glass 
64008a7aa1eSSimon Glass 			if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
64108a7aa1eSSimon Glass 				(adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
64208a7aa1eSSimon Glass 				lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3;
64308a7aa1eSSimon Glass 				lt_ctl_val[i] |= MAX_PRE_EMPHASIS_REACH_3;
64408a7aa1eSSimon Glass 			}
64508a7aa1eSSimon Glass 		}
64608a7aa1eSSimon Glass 
64708a7aa1eSSimon Glass 		if (((lane_stat&DP_LANE_STAT_CE_DONE) &&
64808a7aa1eSSimon Glass 			(lane_stat&DP_LANE_STAT_SYM_LOCK))
64908a7aa1eSSimon Glass 			&& (interlane_aligned == DPCD_INTERLANE_ALIGN_DONE)) {
65008a7aa1eSSimon Glass 			debug("DP Equalizer training succeed\n");
65108a7aa1eSSimon Glass 
6528b449a66SSimon Glass 			f_bw = exynos_dp_get_link_bandwidth(regs);
6538b449a66SSimon Glass 			f_lane_cnt = exynos_dp_get_lane_count(regs);
65408a7aa1eSSimon Glass 
65508a7aa1eSSimon Glass 			debug("DP final BandWidth : %x\n", f_bw);
65608a7aa1eSSimon Glass 			debug("DP final Lane Count : %x\n", f_lane_cnt);
65708a7aa1eSSimon Glass 
6588b449a66SSimon Glass 			priv->lt_info.lt_status = DP_LT_FINISHED;
65908a7aa1eSSimon Glass 
6608b449a66SSimon Glass 			exynos_dp_equalizer_err_link(regs, priv);
66108a7aa1eSSimon Glass 
66208a7aa1eSSimon Glass 		} else {
6638b449a66SSimon Glass 			priv->lt_info.ep_loop++;
66408a7aa1eSSimon Glass 
6658b449a66SSimon Glass 			if (priv->lt_info.ep_loop > MAX_EQ_LOOP) {
6668b449a66SSimon Glass 				if (priv->lane_bw == DP_LANE_BW_2_70) {
66708a7aa1eSSimon Glass 					ret = exynos_dp_reduce_link_rate(
6688b449a66SSimon Glass 							regs, priv);
66908a7aa1eSSimon Glass 				} else {
6708b449a66SSimon Glass 					priv->lt_info.lt_status =
67108a7aa1eSSimon Glass 								DP_LT_FAIL;
6728b449a66SSimon Glass 					exynos_dp_equalizer_err_link(regs,
6738b449a66SSimon Glass 								     priv);
67408a7aa1eSSimon Glass 				}
67508a7aa1eSSimon Glass 			} else {
6768b449a66SSimon Glass 				for (i = 0; i < priv->lane_cnt; i++)
67708a7aa1eSSimon Glass 					exynos_dp_set_lanex_pre_emphasis(
6788b449a66SSimon Glass 						regs, lt_ctl_val[i], i);
67908a7aa1eSSimon Glass 
6808b449a66SSimon Glass 				ret = exynos_dp_write_bytes_to_dpcd(regs,
68108a7aa1eSSimon Glass 						DPCD_TRAINING_LANE0_SET,
68208a7aa1eSSimon Glass 						4, lt_ctl_val);
68308a7aa1eSSimon Glass 				if (ret != EXYNOS_DP_SUCCESS) {
68408a7aa1eSSimon Glass 					printf("DP set lt pattern failed\n");
6858b449a66SSimon Glass 					priv->lt_info.lt_status =
68608a7aa1eSSimon Glass 								DP_LT_FAIL;
6878b449a66SSimon Glass 					exynos_dp_equalizer_err_link(regs,
6888b449a66SSimon Glass 								     priv);
68908a7aa1eSSimon Glass 				}
69008a7aa1eSSimon Glass 			}
69108a7aa1eSSimon Glass 		}
6928b449a66SSimon Glass 	} else if (priv->lane_bw == DP_LANE_BW_2_70) {
6938b449a66SSimon Glass 		ret = exynos_dp_reduce_link_rate(regs, priv);
69408a7aa1eSSimon Glass 	} else {
6958b449a66SSimon Glass 		priv->lt_info.lt_status = DP_LT_FAIL;
6968b449a66SSimon Glass 		exynos_dp_equalizer_err_link(regs, priv);
69708a7aa1eSSimon Glass 	}
69808a7aa1eSSimon Glass 
69908a7aa1eSSimon Glass 	return ret;
70008a7aa1eSSimon Glass }
70108a7aa1eSSimon Glass 
exynos_dp_sw_link_training(struct exynos_dp * regs,struct exynos_dp_priv * priv)7028b449a66SSimon Glass static unsigned int exynos_dp_sw_link_training(struct exynos_dp *regs,
7038b449a66SSimon Glass 					       struct exynos_dp_priv *priv)
70408a7aa1eSSimon Glass {
70508a7aa1eSSimon Glass 	unsigned int ret = 0;
70608a7aa1eSSimon Glass 	int training_finished;
70708a7aa1eSSimon Glass 
70808a7aa1eSSimon Glass 	/* Turn off unnecessary lane */
7098b449a66SSimon Glass 	if (priv->lane_cnt == 1)
7108b449a66SSimon Glass 		exynos_dp_set_analog_power_down(regs, CH1_BLOCK, 1);
71108a7aa1eSSimon Glass 
71208a7aa1eSSimon Glass 	training_finished = 0;
71308a7aa1eSSimon Glass 
7148b449a66SSimon Glass 	priv->lt_info.lt_status = DP_LT_START;
71508a7aa1eSSimon Glass 
71608a7aa1eSSimon Glass 	/* Process here */
71708a7aa1eSSimon Glass 	while (!training_finished) {
7188b449a66SSimon Glass 		switch (priv->lt_info.lt_status) {
71908a7aa1eSSimon Glass 		case DP_LT_START:
7208b449a66SSimon Glass 			ret = exynos_dp_link_start(regs, priv);
72108a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
72208a7aa1eSSimon Glass 				printf("DP LT:link start failed\n");
72308a7aa1eSSimon Glass 				return ret;
72408a7aa1eSSimon Glass 			}
72508a7aa1eSSimon Glass 			break;
72608a7aa1eSSimon Glass 		case DP_LT_CR:
7278b449a66SSimon Glass 			ret = exynos_dp_process_clock_recovery(regs,
7288b449a66SSimon Glass 							       priv);
72908a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
73008a7aa1eSSimon Glass 				printf("DP LT:clock recovery failed\n");
73108a7aa1eSSimon Glass 				return ret;
73208a7aa1eSSimon Glass 			}
73308a7aa1eSSimon Glass 			break;
73408a7aa1eSSimon Glass 		case DP_LT_ET:
7358b449a66SSimon Glass 			ret = exynos_dp_process_equalizer_training(regs,
7368b449a66SSimon Glass 								   priv);
73708a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
73808a7aa1eSSimon Glass 				printf("DP LT:equalizer training failed\n");
73908a7aa1eSSimon Glass 				return ret;
74008a7aa1eSSimon Glass 			}
74108a7aa1eSSimon Glass 			break;
74208a7aa1eSSimon Glass 		case DP_LT_FINISHED:
74308a7aa1eSSimon Glass 			training_finished = 1;
74408a7aa1eSSimon Glass 			break;
74508a7aa1eSSimon Glass 		case DP_LT_FAIL:
74608a7aa1eSSimon Glass 			return -1;
74708a7aa1eSSimon Glass 		}
74808a7aa1eSSimon Glass 	}
74908a7aa1eSSimon Glass 
75008a7aa1eSSimon Glass 	return ret;
75108a7aa1eSSimon Glass }
75208a7aa1eSSimon Glass 
exynos_dp_set_link_train(struct exynos_dp * regs,struct exynos_dp_priv * priv)7538b449a66SSimon Glass static unsigned int exynos_dp_set_link_train(struct exynos_dp *regs,
7548b449a66SSimon Glass 					     struct exynos_dp_priv *priv)
75508a7aa1eSSimon Glass {
75608a7aa1eSSimon Glass 	unsigned int ret;
75708a7aa1eSSimon Glass 
7588b449a66SSimon Glass 	exynos_dp_init_training(regs);
75908a7aa1eSSimon Glass 
7608b449a66SSimon Glass 	ret = exynos_dp_sw_link_training(regs, priv);
76108a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS)
76208a7aa1eSSimon Glass 		printf("DP dp_sw_link_training() failed\n");
76308a7aa1eSSimon Glass 
76408a7aa1eSSimon Glass 	return ret;
76508a7aa1eSSimon Glass }
76608a7aa1eSSimon Glass 
exynos_dp_enable_scramble(struct exynos_dp * regs,unsigned int enable)7678b449a66SSimon Glass static void exynos_dp_enable_scramble(struct exynos_dp *regs,
7688c9b8dc0SSimon Glass 				      unsigned int enable)
76908a7aa1eSSimon Glass {
77008a7aa1eSSimon Glass 	unsigned char data;
77108a7aa1eSSimon Glass 
77208a7aa1eSSimon Glass 	if (enable) {
7738b449a66SSimon Glass 		exynos_dp_enable_scrambling(regs, DP_ENABLE);
77408a7aa1eSSimon Glass 
7758b449a66SSimon Glass 		exynos_dp_read_byte_from_dpcd(regs,
7768c9b8dc0SSimon Glass 					      DPCD_TRAINING_PATTERN_SET, &data);
7778b449a66SSimon Glass 		exynos_dp_write_byte_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET,
77808a7aa1eSSimon Glass 			(u8)(data & ~DPCD_SCRAMBLING_DISABLED));
77908a7aa1eSSimon Glass 	} else {
7808b449a66SSimon Glass 		exynos_dp_enable_scrambling(regs, DP_DISABLE);
7818b449a66SSimon Glass 		exynos_dp_read_byte_from_dpcd(regs,
7828c9b8dc0SSimon Glass 					      DPCD_TRAINING_PATTERN_SET, &data);
7838b449a66SSimon Glass 		exynos_dp_write_byte_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET,
78408a7aa1eSSimon Glass 			(u8)(data | DPCD_SCRAMBLING_DISABLED));
78508a7aa1eSSimon Glass 	}
78608a7aa1eSSimon Glass }
78708a7aa1eSSimon Glass 
exynos_dp_config_video(struct exynos_dp * regs,struct exynos_dp_priv * priv)7888b449a66SSimon Glass static unsigned int exynos_dp_config_video(struct exynos_dp *regs,
7898b449a66SSimon Glass 					   struct exynos_dp_priv *priv)
79008a7aa1eSSimon Glass {
79108a7aa1eSSimon Glass 	unsigned int ret = 0;
79208a7aa1eSSimon Glass 	unsigned int retry_cnt;
79308a7aa1eSSimon Glass 
79408a7aa1eSSimon Glass 	mdelay(1);
79508a7aa1eSSimon Glass 
7968b449a66SSimon Glass 	if (priv->video_info.master_mode) {
79708a7aa1eSSimon Glass 		printf("DP does not support master mode\n");
79808a7aa1eSSimon Glass 		return -ENODEV;
79908a7aa1eSSimon Glass 	} else {
80008a7aa1eSSimon Glass 		/* debug slave */
8018b449a66SSimon Glass 		exynos_dp_config_video_slave_mode(regs,
8028b449a66SSimon Glass 						  &priv->video_info);
80308a7aa1eSSimon Glass 	}
80408a7aa1eSSimon Glass 
8058b449a66SSimon Glass 	exynos_dp_set_video_color_format(regs, &priv->video_info);
80608a7aa1eSSimon Glass 
8078b449a66SSimon Glass 	if (priv->video_info.bist_mode) {
8088b449a66SSimon Glass 		if (exynos_dp_config_video_bist(regs, priv) != 0)
80908a7aa1eSSimon Glass 			return -1;
81008a7aa1eSSimon Glass 	}
81108a7aa1eSSimon Glass 
8128b449a66SSimon Glass 	ret = exynos_dp_get_pll_lock_status(regs);
81308a7aa1eSSimon Glass 	if (ret != PLL_LOCKED) {
81408a7aa1eSSimon Glass 		printf("DP PLL is not locked yet\n");
81508a7aa1eSSimon Glass 		return -EIO;
81608a7aa1eSSimon Glass 	}
81708a7aa1eSSimon Glass 
8188b449a66SSimon Glass 	if (priv->video_info.master_mode == 0) {
81908a7aa1eSSimon Glass 		retry_cnt = 10;
82008a7aa1eSSimon Glass 		while (retry_cnt) {
8218b449a66SSimon Glass 			ret = exynos_dp_is_slave_video_stream_clock_on(regs);
82208a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
82308a7aa1eSSimon Glass 				if (retry_cnt == 0) {
82408a7aa1eSSimon Glass 					printf("DP stream_clock_on failed\n");
82508a7aa1eSSimon Glass 					return ret;
82608a7aa1eSSimon Glass 				}
82708a7aa1eSSimon Glass 				retry_cnt--;
82808a7aa1eSSimon Glass 				mdelay(1);
82908a7aa1eSSimon Glass 			} else
83008a7aa1eSSimon Glass 				break;
83108a7aa1eSSimon Glass 		}
83208a7aa1eSSimon Glass 	}
83308a7aa1eSSimon Glass 
83408a7aa1eSSimon Glass 	/* Set to use the register calculated M/N video */
8358b449a66SSimon Glass 	exynos_dp_set_video_cr_mn(regs, CALCULATED_M, 0, 0);
83608a7aa1eSSimon Glass 
83708a7aa1eSSimon Glass 	/* For video bist, Video timing must be generated by register */
8388b449a66SSimon Glass 	exynos_dp_set_video_timing_mode(regs, VIDEO_TIMING_FROM_CAPTURE);
83908a7aa1eSSimon Glass 
84008a7aa1eSSimon Glass 	/* Enable video bist */
8418b449a66SSimon Glass 	if (priv->video_info.bist_pattern != COLOR_RAMP &&
8428b449a66SSimon Glass 		priv->video_info.bist_pattern != BALCK_WHITE_V_LINES &&
8438b449a66SSimon Glass 		priv->video_info.bist_pattern != COLOR_SQUARE)
8448b449a66SSimon Glass 		exynos_dp_enable_video_bist(regs,
8458b449a66SSimon Glass 					    priv->video_info.bist_mode);
84608a7aa1eSSimon Glass 	else
8478b449a66SSimon Glass 		exynos_dp_enable_video_bist(regs, DP_DISABLE);
84808a7aa1eSSimon Glass 
84908a7aa1eSSimon Glass 	/* Disable video mute */
8508b449a66SSimon Glass 	exynos_dp_enable_video_mute(regs, DP_DISABLE);
85108a7aa1eSSimon Glass 
85208a7aa1eSSimon Glass 	/* Configure video Master or Slave mode */
8538b449a66SSimon Glass 	exynos_dp_enable_video_master(regs,
8548b449a66SSimon Glass 				      priv->video_info.master_mode);
85508a7aa1eSSimon Glass 
85608a7aa1eSSimon Glass 	/* Enable video */
8578b449a66SSimon Glass 	exynos_dp_start_video(regs);
85808a7aa1eSSimon Glass 
8598b449a66SSimon Glass 	if (priv->video_info.master_mode == 0) {
86008a7aa1eSSimon Glass 		retry_cnt = 100;
86108a7aa1eSSimon Glass 		while (retry_cnt) {
8628b449a66SSimon Glass 			ret = exynos_dp_is_video_stream_on(regs);
86308a7aa1eSSimon Glass 			if (ret != EXYNOS_DP_SUCCESS) {
86408a7aa1eSSimon Glass 				if (retry_cnt == 0) {
86508a7aa1eSSimon Glass 					printf("DP Timeout of video stream\n");
86608a7aa1eSSimon Glass 					return ret;
86708a7aa1eSSimon Glass 				}
86808a7aa1eSSimon Glass 				retry_cnt--;
86908a7aa1eSSimon Glass 				mdelay(5);
87008a7aa1eSSimon Glass 			} else
87108a7aa1eSSimon Glass 				break;
87208a7aa1eSSimon Glass 		}
87308a7aa1eSSimon Glass 	}
87408a7aa1eSSimon Glass 
87508a7aa1eSSimon Glass 	return ret;
87608a7aa1eSSimon Glass }
87708a7aa1eSSimon Glass 
exynos_dp_ofdata_to_platdata(struct udevice * dev)878bb5930d5SSimon Glass static int exynos_dp_ofdata_to_platdata(struct udevice *dev)
87908a7aa1eSSimon Glass {
880bb5930d5SSimon Glass 	struct exynos_dp_priv *priv = dev_get_priv(dev);
881bb5930d5SSimon Glass 	const void *blob = gd->fdt_blob;
882e160f7d4SSimon Glass 	unsigned int node = dev_of_offset(dev);
883bb5930d5SSimon Glass 	fdt_addr_t addr;
88408a7aa1eSSimon Glass 
885a821c4afSSimon Glass 	addr = devfdt_get_addr(dev);
886bb5930d5SSimon Glass 	if (addr == FDT_ADDR_T_NONE) {
887bb5930d5SSimon Glass 		debug("Can't get the DP base address\n");
888bb5930d5SSimon Glass 		return -EINVAL;
889bb5930d5SSimon Glass 	}
890bb5930d5SSimon Glass 	priv->regs = (struct exynos_dp *)addr;
8918b449a66SSimon Glass 	priv->disp_info.h_res = fdtdec_get_int(blob, node,
89208a7aa1eSSimon Glass 							"samsung,h-res", 0);
8938b449a66SSimon Glass 	priv->disp_info.h_sync_width = fdtdec_get_int(blob, node,
89408a7aa1eSSimon Glass 						"samsung,h-sync-width", 0);
8958b449a66SSimon Glass 	priv->disp_info.h_back_porch = fdtdec_get_int(blob, node,
89608a7aa1eSSimon Glass 						"samsung,h-back-porch", 0);
8978b449a66SSimon Glass 	priv->disp_info.h_front_porch = fdtdec_get_int(blob, node,
89808a7aa1eSSimon Glass 						"samsung,h-front-porch", 0);
8998b449a66SSimon Glass 	priv->disp_info.v_res = fdtdec_get_int(blob, node,
90008a7aa1eSSimon Glass 						"samsung,v-res", 0);
9018b449a66SSimon Glass 	priv->disp_info.v_sync_width = fdtdec_get_int(blob, node,
90208a7aa1eSSimon Glass 						"samsung,v-sync-width", 0);
9038b449a66SSimon Glass 	priv->disp_info.v_back_porch = fdtdec_get_int(blob, node,
90408a7aa1eSSimon Glass 						"samsung,v-back-porch", 0);
9058b449a66SSimon Glass 	priv->disp_info.v_front_porch = fdtdec_get_int(blob, node,
90608a7aa1eSSimon Glass 						"samsung,v-front-porch", 0);
9078b449a66SSimon Glass 	priv->disp_info.v_sync_rate = fdtdec_get_int(blob, node,
90808a7aa1eSSimon Glass 						"samsung,v-sync-rate", 0);
90908a7aa1eSSimon Glass 
9108b449a66SSimon Glass 	priv->lt_info.lt_status = fdtdec_get_int(blob, node,
91108a7aa1eSSimon Glass 						"samsung,lt-status", 0);
91208a7aa1eSSimon Glass 
9138b449a66SSimon Glass 	priv->video_info.master_mode = fdtdec_get_int(blob, node,
91408a7aa1eSSimon Glass 						"samsung,master-mode", 0);
9158b449a66SSimon Glass 	priv->video_info.bist_mode = fdtdec_get_int(blob, node,
91608a7aa1eSSimon Glass 						"samsung,bist-mode", 0);
9178b449a66SSimon Glass 	priv->video_info.bist_pattern = fdtdec_get_int(blob, node,
91808a7aa1eSSimon Glass 						"samsung,bist-pattern", 0);
9198b449a66SSimon Glass 	priv->video_info.h_sync_polarity = fdtdec_get_int(blob, node,
92008a7aa1eSSimon Glass 						"samsung,h-sync-polarity", 0);
9218b449a66SSimon Glass 	priv->video_info.v_sync_polarity = fdtdec_get_int(blob, node,
92208a7aa1eSSimon Glass 						"samsung,v-sync-polarity", 0);
9238b449a66SSimon Glass 	priv->video_info.interlaced = fdtdec_get_int(blob, node,
92408a7aa1eSSimon Glass 						"samsung,interlaced", 0);
9258b449a66SSimon Glass 	priv->video_info.color_space = fdtdec_get_int(blob, node,
92608a7aa1eSSimon Glass 						"samsung,color-space", 0);
9278b449a66SSimon Glass 	priv->video_info.dynamic_range = fdtdec_get_int(blob, node,
92808a7aa1eSSimon Glass 						"samsung,dynamic-range", 0);
9298b449a66SSimon Glass 	priv->video_info.ycbcr_coeff = fdtdec_get_int(blob, node,
93008a7aa1eSSimon Glass 						"samsung,ycbcr-coeff", 0);
9318b449a66SSimon Glass 	priv->video_info.color_depth = fdtdec_get_int(blob, node,
93208a7aa1eSSimon Glass 						"samsung,color-depth", 0);
93308a7aa1eSSimon Glass 	return 0;
93408a7aa1eSSimon Glass }
93508a7aa1eSSimon Glass 
exynos_dp_bridge_init(struct udevice * dev)936bb5930d5SSimon Glass static int exynos_dp_bridge_init(struct udevice *dev)
93708a7aa1eSSimon Glass {
938bb5930d5SSimon Glass 	const int max_tries = 10;
939bb5930d5SSimon Glass 	int num_tries;
940bb5930d5SSimon Glass 	int ret;
94108a7aa1eSSimon Glass 
942bb5930d5SSimon Glass 	debug("%s\n", __func__);
943bb5930d5SSimon Glass 	ret = video_bridge_attach(dev);
944bb5930d5SSimon Glass 	if (ret) {
945bb5930d5SSimon Glass 		debug("video bridge init failed: %d\n", ret);
946bb5930d5SSimon Glass 		return ret;
94708a7aa1eSSimon Glass 	}
94808a7aa1eSSimon Glass 
949bb5930d5SSimon Glass 	/*
950bb5930d5SSimon Glass 	 * We need to wait for 90ms after bringing up the bridge since there
951bb5930d5SSimon Glass 	 * is a phantom "high" on the HPD chip during its bootup.  The phantom
952bb5930d5SSimon Glass 	 * high comes within 7ms of de-asserting PD and persists for at least
953bb5930d5SSimon Glass 	 * 15ms.  The real high comes roughly 50ms after PD is de-asserted. The
954bb5930d5SSimon Glass 	 * phantom high makes it hard for us to know when the NXP chip is up.
955bb5930d5SSimon Glass 	 */
956bb5930d5SSimon Glass 	mdelay(90);
95708a7aa1eSSimon Glass 
958bb5930d5SSimon Glass 	for (num_tries = 0; num_tries < max_tries; num_tries++) {
959bb5930d5SSimon Glass 		/* Check HPD. If it's high, or we don't have it, all is well */
960bb5930d5SSimon Glass 		ret = video_bridge_check_attached(dev);
961bb5930d5SSimon Glass 		if (!ret || ret == -ENOENT)
962bb5930d5SSimon Glass 			return 0;
9638c9b8dc0SSimon Glass 
964bb5930d5SSimon Glass 		debug("%s: eDP bridge failed to come up; try %d of %d\n",
965bb5930d5SSimon Glass 		      __func__, num_tries, max_tries);
966bb5930d5SSimon Glass 	}
96708a7aa1eSSimon Glass 
968bb5930d5SSimon Glass 	/* Immediately go into bridge reset if the hp line is not high */
969bb5930d5SSimon Glass 	return -EIO;
970bb5930d5SSimon Glass }
971bb5930d5SSimon Glass 
exynos_dp_bridge_setup(const void * blob)972bb5930d5SSimon Glass static int exynos_dp_bridge_setup(const void *blob)
973bb5930d5SSimon Glass {
974bb5930d5SSimon Glass 	const int max_tries = 2;
975bb5930d5SSimon Glass 	int num_tries;
976bb5930d5SSimon Glass 	struct udevice *dev;
977bb5930d5SSimon Glass 	int ret;
978bb5930d5SSimon Glass 
979bb5930d5SSimon Glass 	/* Configure I2C registers for Parade bridge */
980bb5930d5SSimon Glass 	ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &dev);
981bb5930d5SSimon Glass 	if (ret) {
982bb5930d5SSimon Glass 		debug("video bridge init failed: %d\n", ret);
983bb5930d5SSimon Glass 		return ret;
984bb5930d5SSimon Glass 	}
985bb5930d5SSimon Glass 
986bb5930d5SSimon Glass 	if (strncmp(dev->driver->name, "parade", 6)) {
987bb5930d5SSimon Glass 		/* Mux HPHPD to the special hotplug detect mode */
988bb5930d5SSimon Glass 		exynos_pinmux_config(PERIPH_ID_DPHPD, 0);
989bb5930d5SSimon Glass 	}
990bb5930d5SSimon Glass 
991bb5930d5SSimon Glass 	for (num_tries = 0; num_tries < max_tries; num_tries++) {
992bb5930d5SSimon Glass 		ret = exynos_dp_bridge_init(dev);
993bb5930d5SSimon Glass 		if (!ret)
994bb5930d5SSimon Glass 			return 0;
995bb5930d5SSimon Glass 		if (num_tries == max_tries - 1)
996bb5930d5SSimon Glass 			break;
997bb5930d5SSimon Glass 
998bb5930d5SSimon Glass 		/*
999bb5930d5SSimon Glass 		* If we're here, the bridge chip failed to initialise.
1000bb5930d5SSimon Glass 		* Power down the bridge in an attempt to reset.
1001bb5930d5SSimon Glass 		*/
1002bb5930d5SSimon Glass 		video_bridge_set_active(dev, false);
1003bb5930d5SSimon Glass 
1004bb5930d5SSimon Glass 		/*
1005bb5930d5SSimon Glass 		* Arbitrarily wait 300ms here with DP_N low.  Don't know for
1006bb5930d5SSimon Glass 		* sure how long we should wait, but we're being paranoid.
1007bb5930d5SSimon Glass 		*/
1008bb5930d5SSimon Glass 		mdelay(300);
1009bb5930d5SSimon Glass 	}
1010bb5930d5SSimon Glass 
1011bb5930d5SSimon Glass 	return ret;
1012bb5930d5SSimon Glass }
exynos_dp_enable(struct udevice * dev,int panel_bpp,const struct display_timing * timing)1013bb5930d5SSimon Glass int exynos_dp_enable(struct udevice *dev, int panel_bpp,
1014bb5930d5SSimon Glass 		     const struct display_timing *timing)
1015bb5930d5SSimon Glass {
1016bb5930d5SSimon Glass 	struct exynos_dp_priv *priv = dev_get_priv(dev);
1017bb5930d5SSimon Glass 	struct exynos_dp *regs = priv->regs;
1018bb5930d5SSimon Glass 	unsigned int ret;
1019bb5930d5SSimon Glass 
1020bb5930d5SSimon Glass 	debug("%s: start\n", __func__);
10218b449a66SSimon Glass 	exynos_dp_disp_info(&priv->disp_info);
102208a7aa1eSSimon Glass 
1023bb5930d5SSimon Glass 	ret = exynos_dp_bridge_setup(gd->fdt_blob);
1024bb5930d5SSimon Glass 	if (ret && ret != -ENODEV)
1025bb5930d5SSimon Glass 		printf("LCD bridge failed to enable: %d\n", ret);
1026bb5930d5SSimon Glass 
10277eb860dfSSimon Glass 	exynos_dp_phy_ctrl(1);
102808a7aa1eSSimon Glass 
10298b449a66SSimon Glass 	ret = exynos_dp_init_dp(regs);
103008a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
103108a7aa1eSSimon Glass 		printf("DP exynos_dp_init_dp() failed\n");
103208a7aa1eSSimon Glass 		return ret;
103308a7aa1eSSimon Glass 	}
103408a7aa1eSSimon Glass 
10358b449a66SSimon Glass 	ret = exynos_dp_handle_edid(regs, priv);
103608a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
103708a7aa1eSSimon Glass 		printf("EDP handle_edid fail\n");
103808a7aa1eSSimon Glass 		return ret;
103908a7aa1eSSimon Glass 	}
104008a7aa1eSSimon Glass 
10418b449a66SSimon Glass 	ret = exynos_dp_set_link_train(regs, priv);
104208a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
104308a7aa1eSSimon Glass 		printf("DP link training fail\n");
104408a7aa1eSSimon Glass 		return ret;
104508a7aa1eSSimon Glass 	}
104608a7aa1eSSimon Glass 
10478b449a66SSimon Glass 	exynos_dp_enable_scramble(regs, DP_ENABLE);
10488b449a66SSimon Glass 	exynos_dp_enable_rx_to_enhanced_mode(regs, DP_ENABLE);
10498b449a66SSimon Glass 	exynos_dp_enable_enhanced_mode(regs, DP_ENABLE);
105008a7aa1eSSimon Glass 
10518b449a66SSimon Glass 	exynos_dp_set_link_bandwidth(regs, priv->lane_bw);
10528b449a66SSimon Glass 	exynos_dp_set_lane_count(regs, priv->lane_cnt);
105308a7aa1eSSimon Glass 
10548b449a66SSimon Glass 	exynos_dp_init_video(regs);
10558b449a66SSimon Glass 	ret = exynos_dp_config_video(regs, priv);
105608a7aa1eSSimon Glass 	if (ret != EXYNOS_DP_SUCCESS) {
105708a7aa1eSSimon Glass 		printf("Exynos DP init failed\n");
105808a7aa1eSSimon Glass 		return ret;
105908a7aa1eSSimon Glass 	}
106008a7aa1eSSimon Glass 
106108a7aa1eSSimon Glass 	debug("Exynos DP init done\n");
106208a7aa1eSSimon Glass 
106308a7aa1eSSimon Glass 	return ret;
106408a7aa1eSSimon Glass }
1065bb5930d5SSimon Glass 
1066bb5930d5SSimon Glass 
1067bb5930d5SSimon Glass static const struct dm_display_ops exynos_dp_ops = {
1068bb5930d5SSimon Glass 	.enable = exynos_dp_enable,
1069bb5930d5SSimon Glass };
1070bb5930d5SSimon Glass 
1071bb5930d5SSimon Glass static const struct udevice_id exynos_dp_ids[] = {
1072bb5930d5SSimon Glass 	{ .compatible = "samsung,exynos5-dp" },
1073bb5930d5SSimon Glass 	{ }
1074bb5930d5SSimon Glass };
1075bb5930d5SSimon Glass 
1076bb5930d5SSimon Glass U_BOOT_DRIVER(exynos_dp) = {
10779b73bcc6SDongjin Kim 	.name	= "exynos_dp",
1078bb5930d5SSimon Glass 	.id	= UCLASS_DISPLAY,
1079bb5930d5SSimon Glass 	.of_match = exynos_dp_ids,
1080bb5930d5SSimon Glass 	.ops	= &exynos_dp_ops,
1081bb5930d5SSimon Glass 	.ofdata_to_platdata	= exynos_dp_ofdata_to_platdata,
1082bb5930d5SSimon Glass 	.priv_auto_alloc_size	= sizeof(struct exynos_dp_priv),
1083bb5930d5SSimon Glass };
1084