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