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>
14*0e00a84cSMasahiro Yamada #include <linux/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
exynos_dp_disp_info(struct edp_disp_info * disp_info)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
exynos_dp_init_dp(struct exynos_dp * regs)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
exynos_dp_calc_edid_check_sum(unsigned char * edid_data)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
exynos_dp_read_edid(struct exynos_dp * regs)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
exynos_dp_handle_edid(struct exynos_dp * regs,struct exynos_dp_priv * priv)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
exynos_dp_init_training(struct exynos_dp * regs)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
exynos_dp_link_start(struct exynos_dp * regs,struct exynos_dp_priv * priv)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
exynos_dp_training_pattern_dis(struct exynos_dp * regs)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
exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp * regs,unsigned char enable)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
exynos_dp_set_enhanced_mode(struct exynos_dp * regs,unsigned char enhance_mode)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
exynos_dp_read_dpcd_lane_stat(struct exynos_dp * regs,struct exynos_dp_priv * priv,unsigned char * status)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
exynos_dp_read_dpcd_adj_req(struct exynos_dp * regs,unsigned char lane_num,unsigned char * sw,unsigned char * em)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
exynos_dp_equalizer_err_link(struct exynos_dp * regs,struct exynos_dp_priv * priv)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
exynos_dp_reduce_link_rate(struct exynos_dp * regs,struct exynos_dp_priv * priv)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
exynos_dp_process_clock_recovery(struct exynos_dp * regs,struct exynos_dp_priv * priv)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
exynos_dp_process_equalizer_training(struct exynos_dp * regs,struct exynos_dp_priv * priv)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
exynos_dp_sw_link_training(struct exynos_dp * regs,struct exynos_dp_priv * priv)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
exynos_dp_set_link_train(struct exynos_dp * regs,struct exynos_dp_priv * priv)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
exynos_dp_enable_scramble(struct exynos_dp * regs,unsigned int enable)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
exynos_dp_config_video(struct exynos_dp * regs,struct exynos_dp_priv * priv)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
exynos_dp_ofdata_to_platdata(struct udevice * dev)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
exynos_dp_bridge_init(struct udevice * dev)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
exynos_dp_bridge_setup(const void * blob)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 }
exynos_dp_enable(struct udevice * dev,int panel_bpp,const struct display_timing * timing)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) = {
1078bb5930d5SSimon Glass .name = "eexynos_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