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> 1008a7aa1eSSimon Glass #include <common.h> 1108a7aa1eSSimon Glass #include <malloc.h> 1208a7aa1eSSimon Glass #include <linux/compat.h> 1308a7aa1eSSimon Glass #include <linux/err.h> 1408a7aa1eSSimon Glass #include <asm/arch/clk.h> 1508a7aa1eSSimon Glass #include <asm/arch/cpu.h> 1608a7aa1eSSimon Glass #include <asm/arch/dp_info.h> 1708a7aa1eSSimon Glass #include <asm/arch/dp.h> 1808a7aa1eSSimon Glass #include <fdtdec.h> 1908a7aa1eSSimon Glass #include <libfdt.h> 2008a7aa1eSSimon Glass 2108a7aa1eSSimon Glass #include "exynos_dp_lowlevel.h" 2208a7aa1eSSimon Glass 2308a7aa1eSSimon Glass DECLARE_GLOBAL_DATA_PTR; 2408a7aa1eSSimon Glass 2508a7aa1eSSimon Glass void __exynos_set_dp_phy(unsigned int onoff) 2608a7aa1eSSimon Glass { 2708a7aa1eSSimon Glass } 2808a7aa1eSSimon Glass void exynos_set_dp_phy(unsigned int onoff) 2908a7aa1eSSimon Glass __attribute__((weak, alias("__exynos_set_dp_phy"))); 3008a7aa1eSSimon Glass 3108a7aa1eSSimon Glass static void exynos_dp_disp_info(struct edp_disp_info *disp_info) 3208a7aa1eSSimon Glass { 3308a7aa1eSSimon Glass disp_info->h_total = disp_info->h_res + disp_info->h_sync_width + 3408a7aa1eSSimon Glass disp_info->h_back_porch + disp_info->h_front_porch; 3508a7aa1eSSimon Glass disp_info->v_total = disp_info->v_res + disp_info->v_sync_width + 3608a7aa1eSSimon Glass disp_info->v_back_porch + disp_info->v_front_porch; 3708a7aa1eSSimon Glass 3808a7aa1eSSimon Glass return; 3908a7aa1eSSimon Glass } 4008a7aa1eSSimon Glass 41*8c9b8dc0SSimon Glass static int exynos_dp_init_dp(struct exynos_dp *dp_regs) 4208a7aa1eSSimon Glass { 4308a7aa1eSSimon Glass int ret; 44*8c9b8dc0SSimon Glass exynos_dp_reset(dp_regs); 4508a7aa1eSSimon Glass 4608a7aa1eSSimon Glass /* SW defined function Normal operation */ 47*8c9b8dc0SSimon Glass exynos_dp_enable_sw_func(dp_regs, DP_ENABLE); 4808a7aa1eSSimon Glass 49*8c9b8dc0SSimon Glass ret = exynos_dp_init_analog_func(dp_regs); 5008a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) 5108a7aa1eSSimon Glass return ret; 5208a7aa1eSSimon Glass 53*8c9b8dc0SSimon Glass exynos_dp_init_hpd(dp_regs); 54*8c9b8dc0SSimon Glass exynos_dp_init_aux(dp_regs); 5508a7aa1eSSimon Glass 5608a7aa1eSSimon Glass return ret; 5708a7aa1eSSimon Glass } 5808a7aa1eSSimon Glass 5908a7aa1eSSimon Glass static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data) 6008a7aa1eSSimon Glass { 6108a7aa1eSSimon Glass int i; 6208a7aa1eSSimon Glass unsigned char sum = 0; 6308a7aa1eSSimon Glass 6408a7aa1eSSimon Glass for (i = 0; i < EDID_BLOCK_LENGTH; i++) 6508a7aa1eSSimon Glass sum = sum + edid_data[i]; 6608a7aa1eSSimon Glass 6708a7aa1eSSimon Glass return sum; 6808a7aa1eSSimon Glass } 6908a7aa1eSSimon Glass 70*8c9b8dc0SSimon Glass static unsigned int exynos_dp_read_edid(struct exynos_dp *dp_regs) 7108a7aa1eSSimon Glass { 7208a7aa1eSSimon Glass unsigned char edid[EDID_BLOCK_LENGTH * 2]; 7308a7aa1eSSimon Glass unsigned int extend_block = 0; 7408a7aa1eSSimon Glass unsigned char sum; 7508a7aa1eSSimon Glass unsigned char test_vector; 7608a7aa1eSSimon Glass int retval; 7708a7aa1eSSimon Glass 7808a7aa1eSSimon Glass /* 7908a7aa1eSSimon Glass * EDID device address is 0x50. 8008a7aa1eSSimon Glass * However, if necessary, you must have set upper address 8108a7aa1eSSimon Glass * into E-EDID in I2C device, 0x30. 8208a7aa1eSSimon Glass */ 8308a7aa1eSSimon Glass 8408a7aa1eSSimon Glass /* Read Extension Flag, Number of 128-byte EDID extension blocks */ 85*8c9b8dc0SSimon Glass exynos_dp_read_byte_from_i2c(dp_regs, I2C_EDID_DEVICE_ADDR, 86*8c9b8dc0SSimon Glass EDID_EXTENSION_FLAG, &extend_block); 8708a7aa1eSSimon Glass 8808a7aa1eSSimon Glass if (extend_block > 0) { 8908a7aa1eSSimon Glass printf("DP EDID data includes a single extension!\n"); 9008a7aa1eSSimon Glass 9108a7aa1eSSimon Glass /* Read EDID data */ 92*8c9b8dc0SSimon Glass retval = exynos_dp_read_bytes_from_i2c(dp_regs, 93*8c9b8dc0SSimon Glass I2C_EDID_DEVICE_ADDR, 9408a7aa1eSSimon Glass EDID_HEADER_PATTERN, 9508a7aa1eSSimon Glass EDID_BLOCK_LENGTH, 9608a7aa1eSSimon Glass &edid[EDID_HEADER_PATTERN]); 9708a7aa1eSSimon Glass if (retval != 0) { 9808a7aa1eSSimon Glass printf("DP EDID Read failed!\n"); 9908a7aa1eSSimon Glass return -1; 10008a7aa1eSSimon Glass } 10108a7aa1eSSimon Glass sum = exynos_dp_calc_edid_check_sum(edid); 10208a7aa1eSSimon Glass if (sum != 0) { 10308a7aa1eSSimon Glass printf("DP EDID bad checksum!\n"); 10408a7aa1eSSimon Glass return -1; 10508a7aa1eSSimon Glass } 10608a7aa1eSSimon Glass 10708a7aa1eSSimon Glass /* Read additional EDID data */ 108*8c9b8dc0SSimon Glass retval = exynos_dp_read_bytes_from_i2c(dp_regs, 109*8c9b8dc0SSimon Glass I2C_EDID_DEVICE_ADDR, 11008a7aa1eSSimon Glass EDID_BLOCK_LENGTH, 11108a7aa1eSSimon Glass EDID_BLOCK_LENGTH, 11208a7aa1eSSimon Glass &edid[EDID_BLOCK_LENGTH]); 11308a7aa1eSSimon Glass if (retval != 0) { 11408a7aa1eSSimon Glass printf("DP EDID Read failed!\n"); 11508a7aa1eSSimon Glass return -1; 11608a7aa1eSSimon Glass } 11708a7aa1eSSimon Glass sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); 11808a7aa1eSSimon Glass if (sum != 0) { 11908a7aa1eSSimon Glass printf("DP EDID bad checksum!\n"); 12008a7aa1eSSimon Glass return -1; 12108a7aa1eSSimon Glass } 12208a7aa1eSSimon Glass 123*8c9b8dc0SSimon Glass exynos_dp_read_byte_from_dpcd(dp_regs, DPCD_TEST_REQUEST, 12408a7aa1eSSimon Glass &test_vector); 12508a7aa1eSSimon Glass if (test_vector & DPCD_TEST_EDID_READ) { 126*8c9b8dc0SSimon Glass exynos_dp_write_byte_to_dpcd(dp_regs, 127*8c9b8dc0SSimon Glass DPCD_TEST_EDID_CHECKSUM, 12808a7aa1eSSimon Glass edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); 129*8c9b8dc0SSimon Glass exynos_dp_write_byte_to_dpcd(dp_regs, 130*8c9b8dc0SSimon Glass DPCD_TEST_RESPONSE, 13108a7aa1eSSimon Glass DPCD_TEST_EDID_CHECKSUM_WRITE); 13208a7aa1eSSimon Glass } 13308a7aa1eSSimon Glass } else { 13408a7aa1eSSimon Glass debug("DP EDID data does not include any extensions.\n"); 13508a7aa1eSSimon Glass 13608a7aa1eSSimon Glass /* Read EDID data */ 137*8c9b8dc0SSimon Glass retval = exynos_dp_read_bytes_from_i2c(dp_regs, 138*8c9b8dc0SSimon Glass I2C_EDID_DEVICE_ADDR, 13908a7aa1eSSimon Glass EDID_HEADER_PATTERN, 14008a7aa1eSSimon Glass EDID_BLOCK_LENGTH, 14108a7aa1eSSimon Glass &edid[EDID_HEADER_PATTERN]); 14208a7aa1eSSimon Glass 14308a7aa1eSSimon Glass if (retval != 0) { 14408a7aa1eSSimon Glass printf("DP EDID Read failed!\n"); 14508a7aa1eSSimon Glass return -1; 14608a7aa1eSSimon Glass } 14708a7aa1eSSimon Glass sum = exynos_dp_calc_edid_check_sum(edid); 14808a7aa1eSSimon Glass if (sum != 0) { 14908a7aa1eSSimon Glass printf("DP EDID bad checksum!\n"); 15008a7aa1eSSimon Glass return -1; 15108a7aa1eSSimon Glass } 15208a7aa1eSSimon Glass 153*8c9b8dc0SSimon Glass exynos_dp_read_byte_from_dpcd(dp_regs, DPCD_TEST_REQUEST, 15408a7aa1eSSimon Glass &test_vector); 15508a7aa1eSSimon Glass if (test_vector & DPCD_TEST_EDID_READ) { 156*8c9b8dc0SSimon Glass exynos_dp_write_byte_to_dpcd(dp_regs, 157*8c9b8dc0SSimon Glass DPCD_TEST_EDID_CHECKSUM, edid[EDID_CHECKSUM]); 158*8c9b8dc0SSimon Glass exynos_dp_write_byte_to_dpcd(dp_regs, 159*8c9b8dc0SSimon Glass DPCD_TEST_RESPONSE, 16008a7aa1eSSimon Glass DPCD_TEST_EDID_CHECKSUM_WRITE); 16108a7aa1eSSimon Glass } 16208a7aa1eSSimon Glass } 16308a7aa1eSSimon Glass 16408a7aa1eSSimon Glass debug("DP EDID Read success!\n"); 16508a7aa1eSSimon Glass 16608a7aa1eSSimon Glass return 0; 16708a7aa1eSSimon Glass } 16808a7aa1eSSimon Glass 169*8c9b8dc0SSimon Glass static unsigned int exynos_dp_handle_edid(struct exynos_dp *dp_regs, 170*8c9b8dc0SSimon Glass struct edp_device_info *edp_info) 17108a7aa1eSSimon Glass { 17208a7aa1eSSimon Glass unsigned char buf[12]; 17308a7aa1eSSimon Glass unsigned int ret; 17408a7aa1eSSimon Glass unsigned char temp; 17508a7aa1eSSimon Glass unsigned char retry_cnt; 17608a7aa1eSSimon Glass unsigned char dpcd_rev[16]; 17708a7aa1eSSimon Glass unsigned char lane_bw[16]; 17808a7aa1eSSimon Glass unsigned char lane_cnt[16]; 17908a7aa1eSSimon Glass 18008a7aa1eSSimon Glass memset(dpcd_rev, 0, 16); 18108a7aa1eSSimon Glass memset(lane_bw, 0, 16); 18208a7aa1eSSimon Glass memset(lane_cnt, 0, 16); 18308a7aa1eSSimon Glass memset(buf, 0, 12); 18408a7aa1eSSimon Glass 18508a7aa1eSSimon Glass retry_cnt = 5; 18608a7aa1eSSimon Glass while (retry_cnt) { 18708a7aa1eSSimon Glass /* Read DPCD 0x0000-0x000b */ 188*8c9b8dc0SSimon Glass ret = exynos_dp_read_bytes_from_dpcd(dp_regs, DPCD_DPCD_REV, 12, 18908a7aa1eSSimon Glass buf); 19008a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 19108a7aa1eSSimon Glass if (retry_cnt == 0) { 19208a7aa1eSSimon Glass printf("DP read_byte_from_dpcd() failed\n"); 19308a7aa1eSSimon Glass return ret; 19408a7aa1eSSimon Glass } 19508a7aa1eSSimon Glass retry_cnt--; 19608a7aa1eSSimon Glass } else 19708a7aa1eSSimon Glass break; 19808a7aa1eSSimon Glass } 19908a7aa1eSSimon Glass 20008a7aa1eSSimon Glass /* */ 20108a7aa1eSSimon Glass temp = buf[DPCD_DPCD_REV]; 20208a7aa1eSSimon Glass if (temp == DP_DPCD_REV_10 || temp == DP_DPCD_REV_11) 20308a7aa1eSSimon Glass edp_info->dpcd_rev = temp; 20408a7aa1eSSimon Glass else { 20508a7aa1eSSimon Glass printf("DP Wrong DPCD Rev : %x\n", temp); 20608a7aa1eSSimon Glass return -ENODEV; 20708a7aa1eSSimon Glass } 20808a7aa1eSSimon Glass 20908a7aa1eSSimon Glass temp = buf[DPCD_MAX_LINK_RATE]; 21008a7aa1eSSimon Glass if (temp == DP_LANE_BW_1_62 || temp == DP_LANE_BW_2_70) 21108a7aa1eSSimon Glass edp_info->lane_bw = temp; 21208a7aa1eSSimon Glass else { 21308a7aa1eSSimon Glass printf("DP Wrong MAX LINK RATE : %x\n", temp); 21408a7aa1eSSimon Glass return -EINVAL; 21508a7aa1eSSimon Glass } 21608a7aa1eSSimon Glass 21708a7aa1eSSimon Glass /* Refer VESA Display Port Standard Ver1.1a Page 120 */ 21808a7aa1eSSimon Glass if (edp_info->dpcd_rev == DP_DPCD_REV_11) { 21908a7aa1eSSimon Glass temp = buf[DPCD_MAX_LANE_COUNT] & 0x1f; 22008a7aa1eSSimon Glass if (buf[DPCD_MAX_LANE_COUNT] & 0x80) 22108a7aa1eSSimon Glass edp_info->dpcd_efc = 1; 22208a7aa1eSSimon Glass else 22308a7aa1eSSimon Glass edp_info->dpcd_efc = 0; 22408a7aa1eSSimon Glass } else { 22508a7aa1eSSimon Glass temp = buf[DPCD_MAX_LANE_COUNT]; 22608a7aa1eSSimon Glass edp_info->dpcd_efc = 0; 22708a7aa1eSSimon Glass } 22808a7aa1eSSimon Glass 22908a7aa1eSSimon Glass if (temp == DP_LANE_CNT_1 || temp == DP_LANE_CNT_2 || 23008a7aa1eSSimon Glass temp == DP_LANE_CNT_4) { 23108a7aa1eSSimon Glass edp_info->lane_cnt = temp; 23208a7aa1eSSimon Glass } else { 23308a7aa1eSSimon Glass printf("DP Wrong MAX LANE COUNT : %x\n", temp); 23408a7aa1eSSimon Glass return -EINVAL; 23508a7aa1eSSimon Glass } 23608a7aa1eSSimon Glass 237*8c9b8dc0SSimon Glass ret = exynos_dp_read_edid(dp_regs); 23808a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 23908a7aa1eSSimon Glass printf("DP exynos_dp_read_edid() failed\n"); 24008a7aa1eSSimon Glass return -EINVAL; 24108a7aa1eSSimon Glass } 24208a7aa1eSSimon Glass 24308a7aa1eSSimon Glass return ret; 24408a7aa1eSSimon Glass } 24508a7aa1eSSimon Glass 246*8c9b8dc0SSimon Glass static void exynos_dp_init_training(struct exynos_dp *dp_regs) 24708a7aa1eSSimon Glass { 24808a7aa1eSSimon Glass /* 24908a7aa1eSSimon Glass * MACRO_RST must be applied after the PLL_LOCK to avoid 25008a7aa1eSSimon Glass * the DP inter pair skew issue for at least 10 us 25108a7aa1eSSimon Glass */ 252*8c9b8dc0SSimon Glass exynos_dp_reset_macro(dp_regs); 25308a7aa1eSSimon Glass 25408a7aa1eSSimon Glass /* All DP analog module power up */ 255*8c9b8dc0SSimon Glass exynos_dp_set_analog_power_down(dp_regs, POWER_ALL, 0); 25608a7aa1eSSimon Glass } 25708a7aa1eSSimon Glass 258*8c9b8dc0SSimon Glass static unsigned int exynos_dp_link_start(struct exynos_dp *dp_regs, 259*8c9b8dc0SSimon Glass struct edp_device_info *edp_info) 26008a7aa1eSSimon Glass { 26108a7aa1eSSimon Glass unsigned char buf[5]; 26208a7aa1eSSimon Glass unsigned int ret = 0; 26308a7aa1eSSimon Glass 26408a7aa1eSSimon Glass debug("DP: %s was called\n", __func__); 26508a7aa1eSSimon Glass 26608a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_CR; 26708a7aa1eSSimon Glass edp_info->lt_info.ep_loop = 0; 26808a7aa1eSSimon Glass edp_info->lt_info.cr_loop[0] = 0; 26908a7aa1eSSimon Glass edp_info->lt_info.cr_loop[1] = 0; 27008a7aa1eSSimon Glass edp_info->lt_info.cr_loop[2] = 0; 27108a7aa1eSSimon Glass edp_info->lt_info.cr_loop[3] = 0; 27208a7aa1eSSimon Glass 27308a7aa1eSSimon Glass /* Set sink to D0 (Sink Not Ready) mode. */ 274*8c9b8dc0SSimon Glass ret = exynos_dp_write_byte_to_dpcd(dp_regs, DPCD_SINK_POWER_STATE, 27508a7aa1eSSimon Glass DPCD_SET_POWER_STATE_D0); 27608a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 27708a7aa1eSSimon Glass printf("DP write_dpcd_byte failed\n"); 27808a7aa1eSSimon Glass return ret; 27908a7aa1eSSimon Glass } 28008a7aa1eSSimon Glass 28108a7aa1eSSimon Glass /* Set link rate and count as you want to establish */ 282*8c9b8dc0SSimon Glass exynos_dp_set_link_bandwidth(dp_regs, edp_info->lane_bw); 283*8c9b8dc0SSimon Glass exynos_dp_set_lane_count(dp_regs, edp_info->lane_cnt); 28408a7aa1eSSimon Glass 28508a7aa1eSSimon Glass /* Setup RX configuration */ 28608a7aa1eSSimon Glass buf[0] = edp_info->lane_bw; 28708a7aa1eSSimon Glass buf[1] = edp_info->lane_cnt; 28808a7aa1eSSimon Glass 289*8c9b8dc0SSimon Glass ret = exynos_dp_write_bytes_to_dpcd(dp_regs, DPCD_LINK_BW_SET, 2, buf); 29008a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 29108a7aa1eSSimon Glass printf("DP write_dpcd_byte failed\n"); 29208a7aa1eSSimon Glass return ret; 29308a7aa1eSSimon Glass } 29408a7aa1eSSimon Glass 295*8c9b8dc0SSimon Glass exynos_dp_set_lane_pre_emphasis(dp_regs, PRE_EMPHASIS_LEVEL_0, 29608a7aa1eSSimon Glass edp_info->lane_cnt); 29708a7aa1eSSimon Glass 29808a7aa1eSSimon Glass /* Set training pattern 1 */ 299*8c9b8dc0SSimon Glass exynos_dp_set_training_pattern(dp_regs, TRAINING_PTN1); 30008a7aa1eSSimon Glass 30108a7aa1eSSimon Glass /* Set RX training pattern */ 30208a7aa1eSSimon Glass buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1; 30308a7aa1eSSimon Glass 30408a7aa1eSSimon Glass buf[1] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | 30508a7aa1eSSimon Glass DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; 30608a7aa1eSSimon Glass buf[2] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | 30708a7aa1eSSimon Glass DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; 30808a7aa1eSSimon Glass buf[3] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | 30908a7aa1eSSimon Glass DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; 31008a7aa1eSSimon Glass buf[4] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | 31108a7aa1eSSimon Glass DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; 31208a7aa1eSSimon Glass 313*8c9b8dc0SSimon Glass ret = exynos_dp_write_bytes_to_dpcd(dp_regs, DPCD_TRAINING_PATTERN_SET, 31408a7aa1eSSimon Glass 5, buf); 31508a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 31608a7aa1eSSimon Glass printf("DP write_dpcd_byte failed\n"); 31708a7aa1eSSimon Glass return ret; 31808a7aa1eSSimon Glass } 31908a7aa1eSSimon Glass 32008a7aa1eSSimon Glass return ret; 32108a7aa1eSSimon Glass } 32208a7aa1eSSimon Glass 323*8c9b8dc0SSimon Glass static unsigned int exynos_dp_training_pattern_dis(struct exynos_dp *dp_regs) 32408a7aa1eSSimon Glass { 32508a7aa1eSSimon Glass unsigned int ret = EXYNOS_DP_SUCCESS; 32608a7aa1eSSimon Glass 327*8c9b8dc0SSimon Glass exynos_dp_set_training_pattern(dp_regs, DP_NONE); 32808a7aa1eSSimon Glass 329*8c9b8dc0SSimon Glass ret = exynos_dp_write_byte_to_dpcd(dp_regs, DPCD_TRAINING_PATTERN_SET, 33008a7aa1eSSimon Glass DPCD_TRAINING_PATTERN_DISABLED); 33108a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 33208a7aa1eSSimon Glass printf("DP request_link_training_req failed\n"); 33308a7aa1eSSimon Glass return -EAGAIN; 33408a7aa1eSSimon Glass } 33508a7aa1eSSimon Glass 33608a7aa1eSSimon Glass return ret; 33708a7aa1eSSimon Glass } 33808a7aa1eSSimon Glass 339*8c9b8dc0SSimon Glass static unsigned int exynos_dp_enable_rx_to_enhanced_mode( 340*8c9b8dc0SSimon Glass struct exynos_dp *dp_regs, unsigned char enable) 34108a7aa1eSSimon Glass { 34208a7aa1eSSimon Glass unsigned char data; 34308a7aa1eSSimon Glass unsigned int ret = EXYNOS_DP_SUCCESS; 34408a7aa1eSSimon Glass 345*8c9b8dc0SSimon Glass ret = exynos_dp_read_byte_from_dpcd(dp_regs, DPCD_LANE_COUNT_SET, 34608a7aa1eSSimon Glass &data); 34708a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 34808a7aa1eSSimon Glass printf("DP read_from_dpcd failed\n"); 34908a7aa1eSSimon Glass return -EAGAIN; 35008a7aa1eSSimon Glass } 35108a7aa1eSSimon Glass 35208a7aa1eSSimon Glass if (enable) 35308a7aa1eSSimon Glass data = DPCD_ENHANCED_FRAME_EN | DPCD_LN_COUNT_SET(data); 35408a7aa1eSSimon Glass else 35508a7aa1eSSimon Glass data = DPCD_LN_COUNT_SET(data); 35608a7aa1eSSimon Glass 357*8c9b8dc0SSimon Glass ret = exynos_dp_write_byte_to_dpcd(dp_regs, DPCD_LANE_COUNT_SET, data); 35808a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 35908a7aa1eSSimon Glass printf("DP write_to_dpcd failed\n"); 36008a7aa1eSSimon Glass return -EAGAIN; 36108a7aa1eSSimon Glass 36208a7aa1eSSimon Glass } 36308a7aa1eSSimon Glass 36408a7aa1eSSimon Glass return ret; 36508a7aa1eSSimon Glass } 36608a7aa1eSSimon Glass 367*8c9b8dc0SSimon Glass static unsigned int exynos_dp_set_enhanced_mode(struct exynos_dp *dp_regs, 368*8c9b8dc0SSimon Glass unsigned char enhance_mode) 36908a7aa1eSSimon Glass { 37008a7aa1eSSimon Glass unsigned int ret = EXYNOS_DP_SUCCESS; 37108a7aa1eSSimon Glass 372*8c9b8dc0SSimon Glass ret = exynos_dp_enable_rx_to_enhanced_mode(dp_regs, enhance_mode); 37308a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 37408a7aa1eSSimon Glass printf("DP rx_enhance_mode failed\n"); 37508a7aa1eSSimon Glass return -EAGAIN; 37608a7aa1eSSimon Glass } 37708a7aa1eSSimon Glass 378*8c9b8dc0SSimon Glass exynos_dp_enable_enhanced_mode(dp_regs, enhance_mode); 37908a7aa1eSSimon Glass 38008a7aa1eSSimon Glass return ret; 38108a7aa1eSSimon Glass } 38208a7aa1eSSimon Glass 383*8c9b8dc0SSimon Glass static int exynos_dp_read_dpcd_lane_stat(struct exynos_dp *dp_regs, 384*8c9b8dc0SSimon Glass struct edp_device_info *edp_info, 38508a7aa1eSSimon Glass unsigned char *status) 38608a7aa1eSSimon Glass { 38708a7aa1eSSimon Glass unsigned int ret, i; 38808a7aa1eSSimon Glass unsigned char buf[2]; 38908a7aa1eSSimon Glass unsigned char lane_stat[DP_LANE_CNT_4] = {0,}; 39008a7aa1eSSimon Glass unsigned char shift_val[DP_LANE_CNT_4] = {0,}; 39108a7aa1eSSimon Glass 39208a7aa1eSSimon Glass shift_val[0] = 0; 39308a7aa1eSSimon Glass shift_val[1] = 4; 39408a7aa1eSSimon Glass shift_val[2] = 0; 39508a7aa1eSSimon Glass shift_val[3] = 4; 39608a7aa1eSSimon Glass 397*8c9b8dc0SSimon Glass ret = exynos_dp_read_bytes_from_dpcd(dp_regs, DPCD_LANE0_1_STATUS, 2, 398*8c9b8dc0SSimon Glass buf); 39908a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 40008a7aa1eSSimon Glass printf("DP read lane status failed\n"); 40108a7aa1eSSimon Glass return ret; 40208a7aa1eSSimon Glass } 40308a7aa1eSSimon Glass 40408a7aa1eSSimon Glass for (i = 0; i < edp_info->lane_cnt; i++) { 40508a7aa1eSSimon Glass lane_stat[i] = (buf[(i / 2)] >> shift_val[i]) & 0x0f; 40608a7aa1eSSimon Glass if (lane_stat[0] != lane_stat[i]) { 40708a7aa1eSSimon Glass printf("Wrong lane status\n"); 40808a7aa1eSSimon Glass return -EINVAL; 40908a7aa1eSSimon Glass } 41008a7aa1eSSimon Glass } 41108a7aa1eSSimon Glass 41208a7aa1eSSimon Glass *status = lane_stat[0]; 41308a7aa1eSSimon Glass 41408a7aa1eSSimon Glass return ret; 41508a7aa1eSSimon Glass } 41608a7aa1eSSimon Glass 417*8c9b8dc0SSimon Glass static unsigned int exynos_dp_read_dpcd_adj_req(struct exynos_dp *dp_regs, 418*8c9b8dc0SSimon Glass unsigned char lane_num, unsigned char *sw, unsigned char *em) 41908a7aa1eSSimon Glass { 42008a7aa1eSSimon Glass unsigned int ret = EXYNOS_DP_SUCCESS; 42108a7aa1eSSimon Glass unsigned char buf; 42208a7aa1eSSimon Glass unsigned int dpcd_addr; 42308a7aa1eSSimon Glass unsigned char shift_val[DP_LANE_CNT_4] = {0, 4, 0, 4}; 42408a7aa1eSSimon Glass 42508a7aa1eSSimon Glass /* lane_num value is used as array index, so this range 0 ~ 3 */ 42608a7aa1eSSimon Glass dpcd_addr = DPCD_ADJUST_REQUEST_LANE0_1 + (lane_num / 2); 42708a7aa1eSSimon Glass 428*8c9b8dc0SSimon Glass ret = exynos_dp_read_byte_from_dpcd(dp_regs, dpcd_addr, &buf); 42908a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 43008a7aa1eSSimon Glass printf("DP read adjust request failed\n"); 43108a7aa1eSSimon Glass return -EAGAIN; 43208a7aa1eSSimon Glass } 43308a7aa1eSSimon Glass 43408a7aa1eSSimon Glass *sw = ((buf >> shift_val[lane_num]) & 0x03); 43508a7aa1eSSimon Glass *em = ((buf >> shift_val[lane_num]) & 0x0c) >> 2; 43608a7aa1eSSimon Glass 43708a7aa1eSSimon Glass return ret; 43808a7aa1eSSimon Glass } 43908a7aa1eSSimon Glass 440*8c9b8dc0SSimon Glass static int exynos_dp_equalizer_err_link(struct exynos_dp *dp_regs, 441*8c9b8dc0SSimon Glass struct edp_device_info *edp_info) 44208a7aa1eSSimon Glass { 44308a7aa1eSSimon Glass int ret; 44408a7aa1eSSimon Glass 445*8c9b8dc0SSimon Glass ret = exynos_dp_training_pattern_dis(dp_regs); 44608a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 44708a7aa1eSSimon Glass printf("DP training_pattern_disable() failed\n"); 44808a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 44908a7aa1eSSimon Glass } 45008a7aa1eSSimon Glass 451*8c9b8dc0SSimon Glass ret = exynos_dp_set_enhanced_mode(dp_regs, edp_info->dpcd_efc); 45208a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 45308a7aa1eSSimon Glass printf("DP set_enhanced_mode() failed\n"); 45408a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 45508a7aa1eSSimon Glass } 45608a7aa1eSSimon Glass 45708a7aa1eSSimon Glass return ret; 45808a7aa1eSSimon Glass } 45908a7aa1eSSimon Glass 460*8c9b8dc0SSimon Glass static int exynos_dp_reduce_link_rate(struct exynos_dp *dp_regs, 461*8c9b8dc0SSimon Glass struct edp_device_info *edp_info) 46208a7aa1eSSimon Glass { 46308a7aa1eSSimon Glass int ret; 46408a7aa1eSSimon Glass 46508a7aa1eSSimon Glass if (edp_info->lane_bw == DP_LANE_BW_2_70) { 46608a7aa1eSSimon Glass edp_info->lane_bw = DP_LANE_BW_1_62; 46708a7aa1eSSimon Glass printf("DP Change lane bw to 1.62Gbps\n"); 46808a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_START; 46908a7aa1eSSimon Glass ret = EXYNOS_DP_SUCCESS; 47008a7aa1eSSimon Glass } else { 471*8c9b8dc0SSimon Glass ret = exynos_dp_training_pattern_dis(dp_regs); 47208a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) 47308a7aa1eSSimon Glass printf("DP training_patter_disable() failed\n"); 47408a7aa1eSSimon Glass 475*8c9b8dc0SSimon Glass ret = exynos_dp_set_enhanced_mode(dp_regs, edp_info->dpcd_efc); 47608a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) 47708a7aa1eSSimon Glass printf("DP set_enhanced_mode() failed\n"); 47808a7aa1eSSimon Glass 47908a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 48008a7aa1eSSimon Glass } 48108a7aa1eSSimon Glass 48208a7aa1eSSimon Glass return ret; 48308a7aa1eSSimon Glass } 48408a7aa1eSSimon Glass 485*8c9b8dc0SSimon Glass static unsigned int exynos_dp_process_clock_recovery(struct exynos_dp *dp_regs, 486*8c9b8dc0SSimon Glass struct edp_device_info *edp_info) 48708a7aa1eSSimon Glass { 48808a7aa1eSSimon Glass unsigned int ret = EXYNOS_DP_SUCCESS; 48908a7aa1eSSimon Glass unsigned char lane_stat; 49008a7aa1eSSimon Glass unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, }; 49108a7aa1eSSimon Glass unsigned int i; 49208a7aa1eSSimon Glass unsigned char adj_req_sw; 49308a7aa1eSSimon Glass unsigned char adj_req_em; 49408a7aa1eSSimon Glass unsigned char buf[5]; 49508a7aa1eSSimon Glass 49608a7aa1eSSimon Glass debug("DP: %s was called\n", __func__); 49708a7aa1eSSimon Glass mdelay(1); 49808a7aa1eSSimon Glass 499*8c9b8dc0SSimon Glass ret = exynos_dp_read_dpcd_lane_stat(dp_regs, edp_info, &lane_stat); 50008a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 50108a7aa1eSSimon Glass printf("DP read lane status failed\n"); 50208a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 50308a7aa1eSSimon Glass return ret; 50408a7aa1eSSimon Glass } 50508a7aa1eSSimon Glass 50608a7aa1eSSimon Glass if (lane_stat & DP_LANE_STAT_CR_DONE) { 50708a7aa1eSSimon Glass debug("DP clock Recovery training succeed\n"); 508*8c9b8dc0SSimon Glass exynos_dp_set_training_pattern(dp_regs, TRAINING_PTN2); 50908a7aa1eSSimon Glass 51008a7aa1eSSimon Glass for (i = 0; i < edp_info->lane_cnt; i++) { 511*8c9b8dc0SSimon Glass ret = exynos_dp_read_dpcd_adj_req(dp_regs, i, 512*8c9b8dc0SSimon Glass &adj_req_sw, &adj_req_em); 51308a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 51408a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 51508a7aa1eSSimon Glass return ret; 51608a7aa1eSSimon Glass } 51708a7aa1eSSimon Glass 51808a7aa1eSSimon Glass lt_ctl_val[i] = 0; 51908a7aa1eSSimon Glass lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; 52008a7aa1eSSimon Glass 52108a7aa1eSSimon Glass if ((adj_req_sw == VOLTAGE_LEVEL_3) 52208a7aa1eSSimon Glass || (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { 52308a7aa1eSSimon Glass lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | 52408a7aa1eSSimon Glass MAX_PRE_EMPHASIS_REACH_3; 52508a7aa1eSSimon Glass } 526*8c9b8dc0SSimon Glass exynos_dp_set_lanex_pre_emphasis(dp_regs, 527*8c9b8dc0SSimon Glass lt_ctl_val[i], i); 52808a7aa1eSSimon Glass } 52908a7aa1eSSimon Glass 53008a7aa1eSSimon Glass buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_2; 53108a7aa1eSSimon Glass buf[1] = lt_ctl_val[0]; 53208a7aa1eSSimon Glass buf[2] = lt_ctl_val[1]; 53308a7aa1eSSimon Glass buf[3] = lt_ctl_val[2]; 53408a7aa1eSSimon Glass buf[4] = lt_ctl_val[3]; 53508a7aa1eSSimon Glass 536*8c9b8dc0SSimon Glass ret = exynos_dp_write_bytes_to_dpcd(dp_regs, 53708a7aa1eSSimon Glass DPCD_TRAINING_PATTERN_SET, 5, buf); 53808a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 53908a7aa1eSSimon Glass printf("DP write training pattern1 failed\n"); 54008a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 54108a7aa1eSSimon Glass return ret; 54208a7aa1eSSimon Glass } else 54308a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_ET; 54408a7aa1eSSimon Glass } else { 54508a7aa1eSSimon Glass for (i = 0; i < edp_info->lane_cnt; i++) { 546*8c9b8dc0SSimon Glass lt_ctl_val[i] = exynos_dp_get_lanex_pre_emphasis( 547*8c9b8dc0SSimon Glass dp_regs, i); 548*8c9b8dc0SSimon Glass ret = exynos_dp_read_dpcd_adj_req(dp_regs, i, 54908a7aa1eSSimon Glass &adj_req_sw, &adj_req_em); 55008a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 55108a7aa1eSSimon Glass printf("DP read adj req failed\n"); 55208a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 55308a7aa1eSSimon Glass return ret; 55408a7aa1eSSimon Glass } 55508a7aa1eSSimon Glass 55608a7aa1eSSimon Glass if ((adj_req_sw == VOLTAGE_LEVEL_3) || 55708a7aa1eSSimon Glass (adj_req_em == PRE_EMPHASIS_LEVEL_3)) 558*8c9b8dc0SSimon Glass ret = exynos_dp_reduce_link_rate(dp_regs, 559*8c9b8dc0SSimon Glass edp_info); 56008a7aa1eSSimon Glass 56108a7aa1eSSimon Glass if ((DRIVE_CURRENT_SET_0_GET(lt_ctl_val[i]) == 56208a7aa1eSSimon Glass adj_req_sw) && 56308a7aa1eSSimon Glass (PRE_EMPHASIS_SET_0_GET(lt_ctl_val[i]) == 56408a7aa1eSSimon Glass adj_req_em)) { 56508a7aa1eSSimon Glass edp_info->lt_info.cr_loop[i]++; 56608a7aa1eSSimon Glass if (edp_info->lt_info.cr_loop[i] == MAX_CR_LOOP) 56708a7aa1eSSimon Glass ret = exynos_dp_reduce_link_rate( 568*8c9b8dc0SSimon Glass dp_regs, edp_info); 56908a7aa1eSSimon Glass } 57008a7aa1eSSimon Glass 57108a7aa1eSSimon Glass lt_ctl_val[i] = 0; 57208a7aa1eSSimon Glass lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; 57308a7aa1eSSimon Glass 57408a7aa1eSSimon Glass if ((adj_req_sw == VOLTAGE_LEVEL_3) || 57508a7aa1eSSimon Glass (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { 57608a7aa1eSSimon Glass lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | 57708a7aa1eSSimon Glass MAX_PRE_EMPHASIS_REACH_3; 57808a7aa1eSSimon Glass } 579*8c9b8dc0SSimon Glass exynos_dp_set_lanex_pre_emphasis(dp_regs, 580*8c9b8dc0SSimon Glass lt_ctl_val[i], i); 58108a7aa1eSSimon Glass } 58208a7aa1eSSimon Glass 583*8c9b8dc0SSimon Glass ret = exynos_dp_write_bytes_to_dpcd(dp_regs, 58408a7aa1eSSimon Glass DPCD_TRAINING_LANE0_SET, 4, lt_ctl_val); 58508a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 58608a7aa1eSSimon Glass printf("DP write training pattern2 failed\n"); 58708a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 58808a7aa1eSSimon Glass return ret; 58908a7aa1eSSimon Glass } 59008a7aa1eSSimon Glass } 59108a7aa1eSSimon Glass 59208a7aa1eSSimon Glass return ret; 59308a7aa1eSSimon Glass } 59408a7aa1eSSimon Glass 595*8c9b8dc0SSimon Glass static unsigned int exynos_dp_process_equalizer_training( 596*8c9b8dc0SSimon Glass struct exynos_dp *dp_regs, struct edp_device_info *edp_info) 59708a7aa1eSSimon Glass { 59808a7aa1eSSimon Glass unsigned int ret = EXYNOS_DP_SUCCESS; 59908a7aa1eSSimon Glass unsigned char lane_stat, adj_req_sw, adj_req_em, i; 60008a7aa1eSSimon Glass unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,}; 60108a7aa1eSSimon Glass unsigned char interlane_aligned = 0; 60208a7aa1eSSimon Glass unsigned char f_bw; 60308a7aa1eSSimon Glass unsigned char f_lane_cnt; 60408a7aa1eSSimon Glass unsigned char sink_stat; 60508a7aa1eSSimon Glass 60608a7aa1eSSimon Glass mdelay(1); 60708a7aa1eSSimon Glass 608*8c9b8dc0SSimon Glass ret = exynos_dp_read_dpcd_lane_stat(dp_regs, edp_info, &lane_stat); 60908a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 61008a7aa1eSSimon Glass printf("DP read lane status failed\n"); 61108a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 61208a7aa1eSSimon Glass return ret; 61308a7aa1eSSimon Glass } 61408a7aa1eSSimon Glass 61508a7aa1eSSimon Glass debug("DP lane stat : %x\n", lane_stat); 61608a7aa1eSSimon Glass 61708a7aa1eSSimon Glass if (lane_stat & DP_LANE_STAT_CR_DONE) { 618*8c9b8dc0SSimon Glass ret = exynos_dp_read_byte_from_dpcd(dp_regs, 619*8c9b8dc0SSimon Glass DPCD_LN_ALIGN_UPDATED, 62008a7aa1eSSimon Glass &sink_stat); 62108a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 62208a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 62308a7aa1eSSimon Glass 62408a7aa1eSSimon Glass return ret; 62508a7aa1eSSimon Glass } 62608a7aa1eSSimon Glass 62708a7aa1eSSimon Glass interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE); 62808a7aa1eSSimon Glass 62908a7aa1eSSimon Glass for (i = 0; i < edp_info->lane_cnt; i++) { 630*8c9b8dc0SSimon Glass ret = exynos_dp_read_dpcd_adj_req(dp_regs, i, 63108a7aa1eSSimon Glass &adj_req_sw, &adj_req_em); 63208a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 63308a7aa1eSSimon Glass printf("DP read adj req 1 failed\n"); 63408a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 63508a7aa1eSSimon Glass 63608a7aa1eSSimon Glass return ret; 63708a7aa1eSSimon Glass } 63808a7aa1eSSimon Glass 63908a7aa1eSSimon Glass lt_ctl_val[i] = 0; 64008a7aa1eSSimon Glass lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; 64108a7aa1eSSimon Glass 64208a7aa1eSSimon Glass if ((adj_req_sw == VOLTAGE_LEVEL_3) || 64308a7aa1eSSimon Glass (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { 64408a7aa1eSSimon Glass lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3; 64508a7aa1eSSimon Glass lt_ctl_val[i] |= MAX_PRE_EMPHASIS_REACH_3; 64608a7aa1eSSimon Glass } 64708a7aa1eSSimon Glass } 64808a7aa1eSSimon Glass 64908a7aa1eSSimon Glass if (((lane_stat&DP_LANE_STAT_CE_DONE) && 65008a7aa1eSSimon Glass (lane_stat&DP_LANE_STAT_SYM_LOCK)) 65108a7aa1eSSimon Glass && (interlane_aligned == DPCD_INTERLANE_ALIGN_DONE)) { 65208a7aa1eSSimon Glass debug("DP Equalizer training succeed\n"); 65308a7aa1eSSimon Glass 654*8c9b8dc0SSimon Glass f_bw = exynos_dp_get_link_bandwidth(dp_regs); 655*8c9b8dc0SSimon Glass f_lane_cnt = exynos_dp_get_lane_count(dp_regs); 65608a7aa1eSSimon Glass 65708a7aa1eSSimon Glass debug("DP final BandWidth : %x\n", f_bw); 65808a7aa1eSSimon Glass debug("DP final Lane Count : %x\n", f_lane_cnt); 65908a7aa1eSSimon Glass 66008a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FINISHED; 66108a7aa1eSSimon Glass 662*8c9b8dc0SSimon Glass exynos_dp_equalizer_err_link(dp_regs, edp_info); 66308a7aa1eSSimon Glass 66408a7aa1eSSimon Glass } else { 66508a7aa1eSSimon Glass edp_info->lt_info.ep_loop++; 66608a7aa1eSSimon Glass 66708a7aa1eSSimon Glass if (edp_info->lt_info.ep_loop > MAX_EQ_LOOP) { 66808a7aa1eSSimon Glass if (edp_info->lane_bw == DP_LANE_BW_2_70) { 66908a7aa1eSSimon Glass ret = exynos_dp_reduce_link_rate( 670*8c9b8dc0SSimon Glass dp_regs, edp_info); 67108a7aa1eSSimon Glass } else { 67208a7aa1eSSimon Glass edp_info->lt_info.lt_status = 67308a7aa1eSSimon Glass DP_LT_FAIL; 674*8c9b8dc0SSimon Glass exynos_dp_equalizer_err_link(dp_regs, 675*8c9b8dc0SSimon Glass edp_info); 67608a7aa1eSSimon Glass } 67708a7aa1eSSimon Glass } else { 67808a7aa1eSSimon Glass for (i = 0; i < edp_info->lane_cnt; i++) 67908a7aa1eSSimon Glass exynos_dp_set_lanex_pre_emphasis( 680*8c9b8dc0SSimon Glass dp_regs, lt_ctl_val[i], i); 68108a7aa1eSSimon Glass 682*8c9b8dc0SSimon Glass ret = exynos_dp_write_bytes_to_dpcd(dp_regs, 68308a7aa1eSSimon Glass DPCD_TRAINING_LANE0_SET, 68408a7aa1eSSimon Glass 4, lt_ctl_val); 68508a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 68608a7aa1eSSimon Glass printf("DP set lt pattern failed\n"); 68708a7aa1eSSimon Glass edp_info->lt_info.lt_status = 68808a7aa1eSSimon Glass DP_LT_FAIL; 689*8c9b8dc0SSimon Glass exynos_dp_equalizer_err_link(dp_regs, 690*8c9b8dc0SSimon Glass edp_info); 69108a7aa1eSSimon Glass } 69208a7aa1eSSimon Glass } 69308a7aa1eSSimon Glass } 69408a7aa1eSSimon Glass } else if (edp_info->lane_bw == DP_LANE_BW_2_70) { 695*8c9b8dc0SSimon Glass ret = exynos_dp_reduce_link_rate(dp_regs, edp_info); 69608a7aa1eSSimon Glass } else { 69708a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_FAIL; 698*8c9b8dc0SSimon Glass exynos_dp_equalizer_err_link(dp_regs, edp_info); 69908a7aa1eSSimon Glass } 70008a7aa1eSSimon Glass 70108a7aa1eSSimon Glass return ret; 70208a7aa1eSSimon Glass } 70308a7aa1eSSimon Glass 704*8c9b8dc0SSimon Glass static unsigned int exynos_dp_sw_link_training(struct exynos_dp *dp_regs, 705*8c9b8dc0SSimon Glass struct edp_device_info *edp_info) 70608a7aa1eSSimon Glass { 70708a7aa1eSSimon Glass unsigned int ret = 0; 70808a7aa1eSSimon Glass int training_finished; 70908a7aa1eSSimon Glass 71008a7aa1eSSimon Glass /* Turn off unnecessary lane */ 71108a7aa1eSSimon Glass if (edp_info->lane_cnt == 1) 712*8c9b8dc0SSimon Glass exynos_dp_set_analog_power_down(dp_regs, CH1_BLOCK, 1); 71308a7aa1eSSimon Glass 71408a7aa1eSSimon Glass training_finished = 0; 71508a7aa1eSSimon Glass 71608a7aa1eSSimon Glass edp_info->lt_info.lt_status = DP_LT_START; 71708a7aa1eSSimon Glass 71808a7aa1eSSimon Glass /* Process here */ 71908a7aa1eSSimon Glass while (!training_finished) { 72008a7aa1eSSimon Glass switch (edp_info->lt_info.lt_status) { 72108a7aa1eSSimon Glass case DP_LT_START: 722*8c9b8dc0SSimon Glass ret = exynos_dp_link_start(dp_regs, edp_info); 72308a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 72408a7aa1eSSimon Glass printf("DP LT:link start failed\n"); 72508a7aa1eSSimon Glass return ret; 72608a7aa1eSSimon Glass } 72708a7aa1eSSimon Glass break; 72808a7aa1eSSimon Glass case DP_LT_CR: 729*8c9b8dc0SSimon Glass ret = exynos_dp_process_clock_recovery(dp_regs, 730*8c9b8dc0SSimon Glass edp_info); 73108a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 73208a7aa1eSSimon Glass printf("DP LT:clock recovery failed\n"); 73308a7aa1eSSimon Glass return ret; 73408a7aa1eSSimon Glass } 73508a7aa1eSSimon Glass break; 73608a7aa1eSSimon Glass case DP_LT_ET: 737*8c9b8dc0SSimon Glass ret = exynos_dp_process_equalizer_training(dp_regs, 738*8c9b8dc0SSimon Glass edp_info); 73908a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 74008a7aa1eSSimon Glass printf("DP LT:equalizer training failed\n"); 74108a7aa1eSSimon Glass return ret; 74208a7aa1eSSimon Glass } 74308a7aa1eSSimon Glass break; 74408a7aa1eSSimon Glass case DP_LT_FINISHED: 74508a7aa1eSSimon Glass training_finished = 1; 74608a7aa1eSSimon Glass break; 74708a7aa1eSSimon Glass case DP_LT_FAIL: 74808a7aa1eSSimon Glass return -1; 74908a7aa1eSSimon Glass } 75008a7aa1eSSimon Glass } 75108a7aa1eSSimon Glass 75208a7aa1eSSimon Glass return ret; 75308a7aa1eSSimon Glass } 75408a7aa1eSSimon Glass 755*8c9b8dc0SSimon Glass static unsigned int exynos_dp_set_link_train(struct exynos_dp *dp_regs, 756*8c9b8dc0SSimon Glass struct edp_device_info *edp_info) 75708a7aa1eSSimon Glass { 75808a7aa1eSSimon Glass unsigned int ret; 75908a7aa1eSSimon Glass 760*8c9b8dc0SSimon Glass exynos_dp_init_training(dp_regs); 76108a7aa1eSSimon Glass 762*8c9b8dc0SSimon Glass ret = exynos_dp_sw_link_training(dp_regs, edp_info); 76308a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) 76408a7aa1eSSimon Glass printf("DP dp_sw_link_training() failed\n"); 76508a7aa1eSSimon Glass 76608a7aa1eSSimon Glass return ret; 76708a7aa1eSSimon Glass } 76808a7aa1eSSimon Glass 769*8c9b8dc0SSimon Glass static void exynos_dp_enable_scramble(struct exynos_dp *dp_regs, 770*8c9b8dc0SSimon Glass unsigned int enable) 77108a7aa1eSSimon Glass { 77208a7aa1eSSimon Glass unsigned char data; 77308a7aa1eSSimon Glass 77408a7aa1eSSimon Glass if (enable) { 775*8c9b8dc0SSimon Glass exynos_dp_enable_scrambling(dp_regs, DP_ENABLE); 77608a7aa1eSSimon Glass 777*8c9b8dc0SSimon Glass exynos_dp_read_byte_from_dpcd(dp_regs, 778*8c9b8dc0SSimon Glass DPCD_TRAINING_PATTERN_SET, &data); 779*8c9b8dc0SSimon Glass exynos_dp_write_byte_to_dpcd(dp_regs, DPCD_TRAINING_PATTERN_SET, 78008a7aa1eSSimon Glass (u8)(data & ~DPCD_SCRAMBLING_DISABLED)); 78108a7aa1eSSimon Glass } else { 782*8c9b8dc0SSimon Glass exynos_dp_enable_scrambling(dp_regs, DP_DISABLE); 783*8c9b8dc0SSimon Glass exynos_dp_read_byte_from_dpcd(dp_regs, 784*8c9b8dc0SSimon Glass DPCD_TRAINING_PATTERN_SET, &data); 785*8c9b8dc0SSimon Glass exynos_dp_write_byte_to_dpcd(dp_regs, DPCD_TRAINING_PATTERN_SET, 78608a7aa1eSSimon Glass (u8)(data | DPCD_SCRAMBLING_DISABLED)); 78708a7aa1eSSimon Glass } 78808a7aa1eSSimon Glass } 78908a7aa1eSSimon Glass 790*8c9b8dc0SSimon Glass static unsigned int exynos_dp_config_video(struct exynos_dp *dp_regs, 791*8c9b8dc0SSimon Glass struct edp_device_info *edp_info) 79208a7aa1eSSimon Glass { 79308a7aa1eSSimon Glass unsigned int ret = 0; 79408a7aa1eSSimon Glass unsigned int retry_cnt; 79508a7aa1eSSimon Glass 79608a7aa1eSSimon Glass mdelay(1); 79708a7aa1eSSimon Glass 79808a7aa1eSSimon Glass if (edp_info->video_info.master_mode) { 79908a7aa1eSSimon Glass printf("DP does not support master mode\n"); 80008a7aa1eSSimon Glass return -ENODEV; 80108a7aa1eSSimon Glass } else { 80208a7aa1eSSimon Glass /* debug slave */ 803*8c9b8dc0SSimon Glass exynos_dp_config_video_slave_mode(dp_regs, 804*8c9b8dc0SSimon Glass &edp_info->video_info); 80508a7aa1eSSimon Glass } 80608a7aa1eSSimon Glass 807*8c9b8dc0SSimon Glass exynos_dp_set_video_color_format(dp_regs, &edp_info->video_info); 80808a7aa1eSSimon Glass 80908a7aa1eSSimon Glass if (edp_info->video_info.bist_mode) { 810*8c9b8dc0SSimon Glass if (exynos_dp_config_video_bist(dp_regs, edp_info) != 0) 81108a7aa1eSSimon Glass return -1; 81208a7aa1eSSimon Glass } 81308a7aa1eSSimon Glass 814*8c9b8dc0SSimon Glass ret = exynos_dp_get_pll_lock_status(dp_regs); 81508a7aa1eSSimon Glass if (ret != PLL_LOCKED) { 81608a7aa1eSSimon Glass printf("DP PLL is not locked yet\n"); 81708a7aa1eSSimon Glass return -EIO; 81808a7aa1eSSimon Glass } 81908a7aa1eSSimon Glass 82008a7aa1eSSimon Glass if (edp_info->video_info.master_mode == 0) { 82108a7aa1eSSimon Glass retry_cnt = 10; 82208a7aa1eSSimon Glass while (retry_cnt) { 823*8c9b8dc0SSimon Glass ret = exynos_dp_is_slave_video_stream_clock_on(dp_regs); 82408a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 82508a7aa1eSSimon Glass if (retry_cnt == 0) { 82608a7aa1eSSimon Glass printf("DP stream_clock_on failed\n"); 82708a7aa1eSSimon Glass return ret; 82808a7aa1eSSimon Glass } 82908a7aa1eSSimon Glass retry_cnt--; 83008a7aa1eSSimon Glass mdelay(1); 83108a7aa1eSSimon Glass } else 83208a7aa1eSSimon Glass break; 83308a7aa1eSSimon Glass } 83408a7aa1eSSimon Glass } 83508a7aa1eSSimon Glass 83608a7aa1eSSimon Glass /* Set to use the register calculated M/N video */ 837*8c9b8dc0SSimon Glass exynos_dp_set_video_cr_mn(dp_regs, CALCULATED_M, 0, 0); 83808a7aa1eSSimon Glass 83908a7aa1eSSimon Glass /* For video bist, Video timing must be generated by register */ 840*8c9b8dc0SSimon Glass exynos_dp_set_video_timing_mode(dp_regs, VIDEO_TIMING_FROM_CAPTURE); 84108a7aa1eSSimon Glass 84208a7aa1eSSimon Glass /* Enable video bist */ 84308a7aa1eSSimon Glass if (edp_info->video_info.bist_pattern != COLOR_RAMP && 84408a7aa1eSSimon Glass edp_info->video_info.bist_pattern != BALCK_WHITE_V_LINES && 84508a7aa1eSSimon Glass edp_info->video_info.bist_pattern != COLOR_SQUARE) 846*8c9b8dc0SSimon Glass exynos_dp_enable_video_bist(dp_regs, 847*8c9b8dc0SSimon Glass edp_info->video_info.bist_mode); 84808a7aa1eSSimon Glass else 849*8c9b8dc0SSimon Glass exynos_dp_enable_video_bist(dp_regs, DP_DISABLE); 85008a7aa1eSSimon Glass 85108a7aa1eSSimon Glass /* Disable video mute */ 852*8c9b8dc0SSimon Glass exynos_dp_enable_video_mute(dp_regs, DP_DISABLE); 85308a7aa1eSSimon Glass 85408a7aa1eSSimon Glass /* Configure video Master or Slave mode */ 855*8c9b8dc0SSimon Glass exynos_dp_enable_video_master(dp_regs, 856*8c9b8dc0SSimon Glass edp_info->video_info.master_mode); 85708a7aa1eSSimon Glass 85808a7aa1eSSimon Glass /* Enable video */ 859*8c9b8dc0SSimon Glass exynos_dp_start_video(dp_regs); 86008a7aa1eSSimon Glass 86108a7aa1eSSimon Glass if (edp_info->video_info.master_mode == 0) { 86208a7aa1eSSimon Glass retry_cnt = 100; 86308a7aa1eSSimon Glass while (retry_cnt) { 864*8c9b8dc0SSimon Glass ret = exynos_dp_is_video_stream_on(dp_regs); 86508a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 86608a7aa1eSSimon Glass if (retry_cnt == 0) { 86708a7aa1eSSimon Glass printf("DP Timeout of video stream\n"); 86808a7aa1eSSimon Glass return ret; 86908a7aa1eSSimon Glass } 87008a7aa1eSSimon Glass retry_cnt--; 87108a7aa1eSSimon Glass mdelay(5); 87208a7aa1eSSimon Glass } else 87308a7aa1eSSimon Glass break; 87408a7aa1eSSimon Glass } 87508a7aa1eSSimon Glass } 87608a7aa1eSSimon Glass 87708a7aa1eSSimon Glass return ret; 87808a7aa1eSSimon Glass } 87908a7aa1eSSimon Glass 88008a7aa1eSSimon Glass int exynos_dp_parse_dt(const void *blob, struct edp_device_info *edp_info) 88108a7aa1eSSimon Glass { 88208a7aa1eSSimon Glass unsigned int node = fdtdec_next_compatible(blob, 0, 88308a7aa1eSSimon Glass COMPAT_SAMSUNG_EXYNOS5_DP); 88408a7aa1eSSimon Glass if (node <= 0) { 88508a7aa1eSSimon Glass debug("exynos_dp: Can't get device node for dp\n"); 88608a7aa1eSSimon Glass return -ENODEV; 88708a7aa1eSSimon Glass } 88808a7aa1eSSimon Glass 88908a7aa1eSSimon Glass edp_info->disp_info.h_res = fdtdec_get_int(blob, node, 89008a7aa1eSSimon Glass "samsung,h-res", 0); 89108a7aa1eSSimon Glass edp_info->disp_info.h_sync_width = fdtdec_get_int(blob, node, 89208a7aa1eSSimon Glass "samsung,h-sync-width", 0); 89308a7aa1eSSimon Glass edp_info->disp_info.h_back_porch = fdtdec_get_int(blob, node, 89408a7aa1eSSimon Glass "samsung,h-back-porch", 0); 89508a7aa1eSSimon Glass edp_info->disp_info.h_front_porch = fdtdec_get_int(blob, node, 89608a7aa1eSSimon Glass "samsung,h-front-porch", 0); 89708a7aa1eSSimon Glass edp_info->disp_info.v_res = fdtdec_get_int(blob, node, 89808a7aa1eSSimon Glass "samsung,v-res", 0); 89908a7aa1eSSimon Glass edp_info->disp_info.v_sync_width = fdtdec_get_int(blob, node, 90008a7aa1eSSimon Glass "samsung,v-sync-width", 0); 90108a7aa1eSSimon Glass edp_info->disp_info.v_back_porch = fdtdec_get_int(blob, node, 90208a7aa1eSSimon Glass "samsung,v-back-porch", 0); 90308a7aa1eSSimon Glass edp_info->disp_info.v_front_porch = fdtdec_get_int(blob, node, 90408a7aa1eSSimon Glass "samsung,v-front-porch", 0); 90508a7aa1eSSimon Glass edp_info->disp_info.v_sync_rate = fdtdec_get_int(blob, node, 90608a7aa1eSSimon Glass "samsung,v-sync-rate", 0); 90708a7aa1eSSimon Glass 90808a7aa1eSSimon Glass edp_info->lt_info.lt_status = fdtdec_get_int(blob, node, 90908a7aa1eSSimon Glass "samsung,lt-status", 0); 91008a7aa1eSSimon Glass 91108a7aa1eSSimon Glass edp_info->video_info.master_mode = fdtdec_get_int(blob, node, 91208a7aa1eSSimon Glass "samsung,master-mode", 0); 91308a7aa1eSSimon Glass edp_info->video_info.bist_mode = fdtdec_get_int(blob, node, 91408a7aa1eSSimon Glass "samsung,bist-mode", 0); 91508a7aa1eSSimon Glass edp_info->video_info.bist_pattern = fdtdec_get_int(blob, node, 91608a7aa1eSSimon Glass "samsung,bist-pattern", 0); 91708a7aa1eSSimon Glass edp_info->video_info.h_sync_polarity = fdtdec_get_int(blob, node, 91808a7aa1eSSimon Glass "samsung,h-sync-polarity", 0); 91908a7aa1eSSimon Glass edp_info->video_info.v_sync_polarity = fdtdec_get_int(blob, node, 92008a7aa1eSSimon Glass "samsung,v-sync-polarity", 0); 92108a7aa1eSSimon Glass edp_info->video_info.interlaced = fdtdec_get_int(blob, node, 92208a7aa1eSSimon Glass "samsung,interlaced", 0); 92308a7aa1eSSimon Glass edp_info->video_info.color_space = fdtdec_get_int(blob, node, 92408a7aa1eSSimon Glass "samsung,color-space", 0); 92508a7aa1eSSimon Glass edp_info->video_info.dynamic_range = fdtdec_get_int(blob, node, 92608a7aa1eSSimon Glass "samsung,dynamic-range", 0); 92708a7aa1eSSimon Glass edp_info->video_info.ycbcr_coeff = fdtdec_get_int(blob, node, 92808a7aa1eSSimon Glass "samsung,ycbcr-coeff", 0); 92908a7aa1eSSimon Glass edp_info->video_info.color_depth = fdtdec_get_int(blob, node, 93008a7aa1eSSimon Glass "samsung,color-depth", 0); 93108a7aa1eSSimon Glass return 0; 93208a7aa1eSSimon Glass } 93308a7aa1eSSimon Glass 93408a7aa1eSSimon Glass unsigned int exynos_init_dp(void) 93508a7aa1eSSimon Glass { 93608a7aa1eSSimon Glass unsigned int ret; 93708a7aa1eSSimon Glass struct edp_device_info *edp_info; 938*8c9b8dc0SSimon Glass struct exynos_dp *dp_regs; 939*8c9b8dc0SSimon Glass int node; 94008a7aa1eSSimon Glass 94108a7aa1eSSimon Glass edp_info = kzalloc(sizeof(struct edp_device_info), GFP_KERNEL); 94208a7aa1eSSimon Glass if (!edp_info) { 94308a7aa1eSSimon Glass debug("failed to allocate edp device object.\n"); 94408a7aa1eSSimon Glass return -EFAULT; 94508a7aa1eSSimon Glass } 94608a7aa1eSSimon Glass 94708a7aa1eSSimon Glass if (exynos_dp_parse_dt(gd->fdt_blob, edp_info)) 94808a7aa1eSSimon Glass debug("unable to parse DP DT node\n"); 94908a7aa1eSSimon Glass 950*8c9b8dc0SSimon Glass node = fdtdec_next_compatible(gd->fdt_blob, 0, 951*8c9b8dc0SSimon Glass COMPAT_SAMSUNG_EXYNOS5_DP); 952*8c9b8dc0SSimon Glass if (node <= 0) 953*8c9b8dc0SSimon Glass debug("exynos_dp: Can't get device node for dp\n"); 954*8c9b8dc0SSimon Glass 955*8c9b8dc0SSimon Glass dp_regs = (struct exynos_dp *)fdtdec_get_addr(gd->fdt_blob, node, 956*8c9b8dc0SSimon Glass "reg"); 957*8c9b8dc0SSimon Glass if (dp_regs == NULL) 958*8c9b8dc0SSimon Glass debug("Can't get the DP base address\n"); 95908a7aa1eSSimon Glass 96008a7aa1eSSimon Glass exynos_dp_disp_info(&edp_info->disp_info); 96108a7aa1eSSimon Glass 96208a7aa1eSSimon Glass exynos_set_dp_phy(1); 96308a7aa1eSSimon Glass 964*8c9b8dc0SSimon Glass ret = exynos_dp_init_dp(dp_regs); 96508a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 96608a7aa1eSSimon Glass printf("DP exynos_dp_init_dp() failed\n"); 96708a7aa1eSSimon Glass return ret; 96808a7aa1eSSimon Glass } 96908a7aa1eSSimon Glass 970*8c9b8dc0SSimon Glass ret = exynos_dp_handle_edid(dp_regs, edp_info); 97108a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 97208a7aa1eSSimon Glass printf("EDP handle_edid fail\n"); 97308a7aa1eSSimon Glass return ret; 97408a7aa1eSSimon Glass } 97508a7aa1eSSimon Glass 976*8c9b8dc0SSimon Glass ret = exynos_dp_set_link_train(dp_regs, edp_info); 97708a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 97808a7aa1eSSimon Glass printf("DP link training fail\n"); 97908a7aa1eSSimon Glass return ret; 98008a7aa1eSSimon Glass } 98108a7aa1eSSimon Glass 982*8c9b8dc0SSimon Glass exynos_dp_enable_scramble(dp_regs, DP_ENABLE); 983*8c9b8dc0SSimon Glass exynos_dp_enable_rx_to_enhanced_mode(dp_regs, DP_ENABLE); 984*8c9b8dc0SSimon Glass exynos_dp_enable_enhanced_mode(dp_regs, DP_ENABLE); 98508a7aa1eSSimon Glass 986*8c9b8dc0SSimon Glass exynos_dp_set_link_bandwidth(dp_regs, edp_info->lane_bw); 987*8c9b8dc0SSimon Glass exynos_dp_set_lane_count(dp_regs, edp_info->lane_cnt); 98808a7aa1eSSimon Glass 989*8c9b8dc0SSimon Glass exynos_dp_init_video(dp_regs); 990*8c9b8dc0SSimon Glass ret = exynos_dp_config_video(dp_regs, edp_info); 99108a7aa1eSSimon Glass if (ret != EXYNOS_DP_SUCCESS) { 99208a7aa1eSSimon Glass printf("Exynos DP init failed\n"); 99308a7aa1eSSimon Glass return ret; 99408a7aa1eSSimon Glass } 99508a7aa1eSSimon Glass 99608a7aa1eSSimon Glass debug("Exynos DP init done\n"); 99708a7aa1eSSimon Glass 99808a7aa1eSSimon Glass return ret; 99908a7aa1eSSimon Glass } 1000