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