diff options
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4398-drm-amd-display-implement-lttpr-logic.patch')
-rw-r--r-- | meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4398-drm-amd-display-implement-lttpr-logic.patch | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4398-drm-amd-display-implement-lttpr-logic.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4398-drm-amd-display-implement-lttpr-logic.patch new file mode 100644 index 00000000..556dee89 --- /dev/null +++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4398-drm-amd-display-implement-lttpr-logic.patch @@ -0,0 +1,729 @@ +From b8a60f6929135d3786e37046d7e736a6b975237d Mon Sep 17 00:00:00 2001 +From: abdoulaye berthe <abdoulaye.berthe@amd.com> +Date: Wed, 24 Jul 2019 11:01:44 -0400 +Subject: [PATCH 4398/4736] drm/amd/display: implement lttpr logic + +1-If at least one repeater is present in the link and we are in non +transparent mode, perform clock recovery then channel equalization +with all repeaters one by one before training DPRX. + +2-Mark the end of LT with a repeater by setting training pattern 0 +at the end of channel equalization with each repeater. + +Signed-off-by: abdoulaye berthe <abdoulaye.berthe@amd.com> +Reviewed-by: Aric Cyr <Aric.Cyr@amd.com> +--- + .../gpu/drm/amd/display/dc/core/dc_link_dp.c | 319 ++++++++++++++---- + .../drm/amd/display/dc/core/dc_link_hwss.c | 39 ++- + .../gpu/drm/amd/display/dc/inc/link_hwss.h | 6 +- + 3 files changed, 292 insertions(+), 72 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +index 94d5a0ac308f..11b6e14b345e 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +@@ -22,7 +22,7 @@ + link->ctx->logger + + +-#define DP_REPEATER_CONFIGURATION_AND_STATUS_OFFSET 0x50 ++#define DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE 0x50 + + /* maximum pre emphasis level allowed for each voltage swing level*/ + static const enum dc_pre_emphasis voltage_swing_to_pre_emphasis[] = { +@@ -224,19 +224,31 @@ static enum dpcd_training_patterns + return dpcd_tr_pattern; + } + ++static inline bool is_repeater(struct dc_link *link, uint32_t offset) ++{ ++ return (!link->is_lttpr_mode_transparent && offset != 0); ++} ++ + static void dpcd_set_lt_pattern_and_lane_settings( + struct dc_link *link, + const struct link_training_settings *lt_settings, +- enum dc_dp_training_pattern pattern) ++ enum dc_dp_training_pattern pattern, ++ uint32_t offset) + { + union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = { { {0} } }; +- const uint32_t dpcd_base_lt_offset = +- DP_TRAINING_PATTERN_SET; ++ ++ uint32_t dpcd_base_lt_offset; ++ + uint8_t dpcd_lt_buffer[5] = {0}; + union dpcd_training_pattern dpcd_pattern = { {0} }; + uint32_t lane; + uint32_t size_in_bytes; + bool edp_workaround = false; /* TODO link_prop.INTERNAL */ ++ dpcd_base_lt_offset = DP_TRAINING_PATTERN_SET; ++ ++ if (is_repeater(link, offset)) ++ dpcd_base_lt_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + ++ ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); + + /***************************************************************** + * DpcdAddress_TrainingPatternSet +@@ -244,12 +256,12 @@ static void dpcd_set_lt_pattern_and_lane_settings( + dpcd_pattern.v1_4.TRAINING_PATTERN_SET = + dc_dp_training_pattern_to_dpcd_training_pattern(link, pattern); + +- dpcd_lt_buffer[DP_TRAINING_PATTERN_SET - dpcd_base_lt_offset] ++ dpcd_lt_buffer[DP_TRAINING_PATTERN_SET - DP_TRAINING_PATTERN_SET] + = dpcd_pattern.raw; + +- DC_LOG_HW_LINK_TRAINING("%s\n %x pattern = %x\n", ++ DC_LOG_HW_LINK_TRAINING("%s\n 0x%X pattern = %x\n", + __func__, +- DP_TRAINING_PATTERN_SET, ++ dpcd_base_lt_offset, + dpcd_pattern.v1_4.TRAINING_PATTERN_SET); + + /***************************************************************** +@@ -271,19 +283,19 @@ static void dpcd_set_lt_pattern_and_lane_settings( + PRE_EMPHASIS_MAX_LEVEL ? 1 : 0); + } + +- /* concatinate everything into one buffer*/ ++ /* concatenate everything into one buffer*/ + + size_in_bytes = lt_settings->link_settings.lane_count * sizeof(dpcd_lane[0]); + + // 0x00103 - 0x00102 + memmove( +- &dpcd_lt_buffer[DP_TRAINING_LANE0_SET - dpcd_base_lt_offset], ++ &dpcd_lt_buffer[DP_TRAINING_LANE0_SET - DP_TRAINING_PATTERN_SET], + dpcd_lane, + size_in_bytes); + +- DC_LOG_HW_LINK_TRAINING("%s:\n %x VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n", ++ DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n", + __func__, +- DP_TRAINING_LANE0_SET, ++ dpcd_base_lt_offset, + dpcd_lane[0].bits.VOLTAGE_SWING_SET, + dpcd_lane[0].bits.PRE_EMPHASIS_SET, + dpcd_lane[0].bits.MAX_SWING_REACHED, +@@ -498,8 +510,12 @@ static void get_lane_status_and_drive_settings( + const struct link_training_settings *link_training_setting, + union lane_status *ln_status, + union lane_align_status_updated *ln_status_updated, +- struct link_training_settings *req_settings) ++ struct link_training_settings *req_settings, ++ uint32_t offset) + { ++ unsigned int lane01_status_address = DP_LANE0_1_STATUS; ++ uint8_t lane_adjust_offset = 4; ++ unsigned int lane01_adjust_address; + uint8_t dpcd_buf[6] = {0}; + union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = { { {0} } }; + struct link_training_settings request_settings = { {0} }; +@@ -507,9 +523,16 @@ static void get_lane_status_and_drive_settings( + + memset(req_settings, '\0', sizeof(struct link_training_settings)); + ++ if (is_repeater(link, offset)) { ++ lane01_status_address = ++ DP_LANE0_1_STATUS_PHY_REPEATER1 + ++ ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); ++ lane_adjust_offset = 3; ++ } ++ + core_link_read_dpcd( + link, +- DP_LANE0_1_STATUS, ++ lane01_status_address, + (uint8_t *)(dpcd_buf), + sizeof(dpcd_buf)); + +@@ -520,22 +543,28 @@ static void get_lane_status_and_drive_settings( + ln_status[lane].raw = + get_nibble_at_index(&dpcd_buf[0], lane); + dpcd_lane_adjust[lane].raw = +- get_nibble_at_index(&dpcd_buf[4], lane); ++ get_nibble_at_index(&dpcd_buf[lane_adjust_offset], lane); + } + + ln_status_updated->raw = dpcd_buf[2]; + +- DC_LOG_HW_LINK_TRAINING("%s:\n%x Lane01Status = %x\n %x Lane23Status = %x\n ", ++ DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X Lane01Status = %x\n 0x%X Lane23Status = %x\n ", + __func__, +- DP_LANE0_1_STATUS, dpcd_buf[0], +- DP_LANE2_3_STATUS, dpcd_buf[1]); ++ lane01_status_address, dpcd_buf[0], ++ lane01_status_address + 1, dpcd_buf[1]); ++ ++ lane01_adjust_address = DP_ADJUST_REQUEST_LANE0_1; ++ ++ if (is_repeater(link, offset)) ++ lane01_adjust_address = DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1 + ++ ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); + +- DC_LOG_HW_LINK_TRAINING("%s:\n %x Lane01AdjustRequest = %x\n %x Lane23AdjustRequest = %x\n", ++ DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X Lane01AdjustRequest = %x\n 0x%X Lane23AdjustRequest = %x\n", + __func__, +- DP_ADJUST_REQUEST_LANE0_1, +- dpcd_buf[4], +- DP_ADJUST_REQUEST_LANE2_3, +- dpcd_buf[5]); ++ lane01_adjust_address, ++ dpcd_buf[lane_adjust_offset], ++ lane01_adjust_address + 1, ++ dpcd_buf[lane_adjust_offset + 1]); + + /*copy to req_settings*/ + request_settings.link_settings.lane_count = +@@ -574,10 +603,18 @@ static void get_lane_status_and_drive_settings( + + static void dpcd_set_lane_settings( + struct dc_link *link, +- const struct link_training_settings *link_training_setting) ++ const struct link_training_settings *link_training_setting, ++ uint32_t offset) + { + union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}}; + uint32_t lane; ++ unsigned int lane0_set_address; ++ ++ lane0_set_address = DP_TRAINING_LANE0_SET; ++ ++ if (is_repeater(link, offset)) ++ lane0_set_address = DP_TRAINING_LANE0_SET_PHY_REPEATER1 + ++ ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); + + for (lane = 0; lane < + (uint32_t)(link_training_setting-> +@@ -600,7 +637,7 @@ static void dpcd_set_lane_settings( + } + + core_link_write_dpcd(link, +- DP_TRAINING_LANE0_SET, ++ lane0_set_address, + (uint8_t *)(dpcd_lane), + link_training_setting->link_settings.lane_count); + +@@ -623,9 +660,9 @@ static void dpcd_set_lane_settings( + } + */ + +- DC_LOG_HW_LINK_TRAINING("%s\n %x VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n", ++ DC_LOG_HW_LINK_TRAINING("%s\n 0x%X VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n", + __func__, +- DP_TRAINING_LANE0_SET, ++ lane0_set_address, + dpcd_lane[0].bits.VOLTAGE_SWING_SET, + dpcd_lane[0].bits.PRE_EMPHASIS_SET, + dpcd_lane[0].bits.MAX_SWING_REACHED, +@@ -650,17 +687,6 @@ static bool is_max_vs_reached( + + } + +-void dc_link_dp_set_drive_settings( +- struct dc_link *link, +- struct link_training_settings *lt_settings) +-{ +- /* program ASIC PHY settings*/ +- dp_set_hw_lane_settings(link, lt_settings); +- +- /* Notify DP sink the PHY settings from source */ +- dpcd_set_lane_settings(link, lt_settings); +-} +- + static bool perform_post_lt_adj_req_sequence( + struct dc_link *link, + struct link_training_settings *lt_settings) +@@ -693,7 +719,8 @@ static bool perform_post_lt_adj_req_sequence( + lt_settings, + dpcd_lane_status, + &dpcd_lane_status_updated, +- &req_settings); ++ &req_settings, ++ DPRX); + + if (dpcd_lane_status_updated.bits. + POST_LT_ADJ_REQ_IN_PROGRESS == 0) +@@ -750,6 +777,31 @@ static bool perform_post_lt_adj_req_sequence( + + } + ++/* Only used for channel equalization */ ++static uint32_t translate_training_aux_read_interval(uint32_t dpcd_aux_read_interval) ++{ ++ unsigned int aux_rd_interval_us = 400; ++ ++ switch (dpcd_aux_read_interval) { ++ case 0x01: ++ aux_rd_interval_us = 400; ++ break; ++ case 0x02: ++ aux_rd_interval_us = 4000; ++ break; ++ case 0x03: ++ aux_rd_interval_us = 8000; ++ break; ++ case 0x04: ++ aux_rd_interval_us = 16000; ++ break; ++ default: ++ break; ++ } ++ ++ return aux_rd_interval_us; ++} ++ + static enum link_training_result get_cr_failure(enum dc_lane_count ln_count, + union lane_status *dpcd_lane_status) + { +@@ -768,37 +820,55 @@ static enum link_training_result get_cr_failure(enum dc_lane_count ln_count, + + static enum link_training_result perform_channel_equalization_sequence( + struct dc_link *link, +- struct link_training_settings *lt_settings) ++ struct link_training_settings *lt_settings, ++ uint32_t offset) + { + struct link_training_settings req_settings; + enum dc_dp_training_pattern tr_pattern; + uint32_t retries_ch_eq; ++ uint32_t wait_time_microsec; + enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; + union lane_align_status_updated dpcd_lane_status_updated = { {0} }; + union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = { { {0} } }; + ++ /* Note: also check that TPS4 is a supported feature*/ ++ + tr_pattern = lt_settings->pattern_for_eq; + +- dp_set_hw_training_pattern(link, tr_pattern); ++ if (is_repeater(link, offset)) ++ tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4; ++ ++ dp_set_hw_training_pattern(link, tr_pattern, offset); + + for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT; + retries_ch_eq++) { + +- dp_set_hw_lane_settings(link, lt_settings); ++ dp_set_hw_lane_settings(link, lt_settings, offset); + + /* 2. update DPCD*/ + if (!retries_ch_eq) + /* EPR #361076 - write as a 5-byte burst, +- * but only for the 1-st iteration*/ ++ * but only for the 1-st iteration ++ */ ++ + dpcd_set_lt_pattern_and_lane_settings( + link, + lt_settings, +- tr_pattern); ++ tr_pattern, offset); + else +- dpcd_set_lane_settings(link, lt_settings); ++ dpcd_set_lane_settings(link, lt_settings, offset); + + /* 3. wait for receiver to lock-on*/ +- wait_for_training_aux_rd_interval(link, lt_settings->eq_pattern_time); ++ wait_time_microsec = lt_settings->eq_pattern_time; ++ ++ if (!link->is_lttpr_mode_transparent) ++ wait_time_microsec = ++ translate_training_aux_read_interval( ++ link->dpcd_caps.lttpr_caps.aux_rd_interval[offset]); ++ ++ wait_for_training_aux_rd_interval( ++ link, ++ wait_time_microsec); + + /* 4. Read lane status and requested + * drive settings as set by the sink*/ +@@ -808,7 +878,8 @@ static enum link_training_result perform_channel_equalization_sequence( + lt_settings, + dpcd_lane_status, + &dpcd_lane_status_updated, +- &req_settings); ++ &req_settings, ++ offset); + + /* 5. check CR done*/ + if (!is_cr_done(lane_count, dpcd_lane_status)) +@@ -827,13 +898,16 @@ static enum link_training_result perform_channel_equalization_sequence( + return LINK_TRAINING_EQ_FAIL_EQ; + + } ++#define TRAINING_AUX_RD_INTERVAL 100 //us + + static enum link_training_result perform_clock_recovery_sequence( + struct dc_link *link, +- struct link_training_settings *lt_settings) ++ struct link_training_settings *lt_settings, ++ uint32_t offset) + { + uint32_t retries_cr; + uint32_t retry_count; ++ uint32_t wait_time_microsec; + struct link_training_settings req_settings; + enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; + enum dc_dp_training_pattern tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_1; +@@ -843,7 +917,7 @@ static enum link_training_result perform_clock_recovery_sequence( + retries_cr = 0; + retry_count = 0; + +- dp_set_hw_training_pattern(link, tr_pattern); ++ dp_set_hw_training_pattern(link, tr_pattern, offset); + + /* najeeb - The synaptics MST hub can put the LT in + * infinite loop by switching the VS +@@ -860,7 +934,8 @@ static enum link_training_result perform_clock_recovery_sequence( + /* 1. call HWSS to set lane settings*/ + dp_set_hw_lane_settings( + link, +- lt_settings); ++ lt_settings, ++ offset); + + /* 2. update DPCD of the receiver*/ + if (!retries_cr) +@@ -869,16 +944,23 @@ static enum link_training_result perform_clock_recovery_sequence( + dpcd_set_lt_pattern_and_lane_settings( + link, + lt_settings, +- tr_pattern); ++ tr_pattern, ++ offset); + else + dpcd_set_lane_settings( + link, +- lt_settings); ++ lt_settings, ++ offset); + + /* 3. wait receiver to lock-on*/ ++ wait_time_microsec = lt_settings->cr_pattern_time; ++ ++ if (!link->is_lttpr_mode_transparent) ++ wait_time_microsec = TRAINING_AUX_RD_INTERVAL; ++ + wait_for_training_aux_rd_interval( + link, +- lt_settings->cr_pattern_time); ++ wait_time_microsec); + + /* 4. Read lane status and requested drive + * settings as set by the sink +@@ -888,7 +970,8 @@ static enum link_training_result perform_clock_recovery_sequence( + lt_settings, + dpcd_lane_status, + &dpcd_lane_status_updated, +- &req_settings); ++ &req_settings, ++ offset); + + /* 5. check CR done*/ + if (is_cr_done(lane_count, dpcd_lane_status)) +@@ -1057,10 +1140,38 @@ static void initialize_training_settings( + lt_settings->enhanced_framing = 1; + } + ++static uint8_t convert_to_count(uint8_t lttpr_repeater_count) ++{ ++ switch (lttpr_repeater_count) { ++ case 0x80: // 1 lttpr repeater ++ return 1; ++ case 0x40: // 2 lttpr repeaters ++ return 2; ++ case 0x20: // 3 lttpr repeaters ++ return 3; ++ case 0x10: // 4 lttpr repeaters ++ return 4; ++ case 0x08: // 5 lttpr repeaters ++ return 5; ++ case 0x04: // 6 lttpr repeaters ++ return 6; ++ case 0x02: // 7 lttpr repeaters ++ return 7; ++ case 0x01: // 8 lttpr repeaters ++ return 8; ++ default: ++ break; ++ } ++ return 0; // invalid value ++} ++ + static void configure_lttpr_mode(struct dc_link *link) + { + /* aux timeout is already set to extended */ + /* RESET/SET lttpr mode to enable non transparent mode */ ++ uint8_t repeater_cnt; ++ uint32_t aux_interval_address; ++ uint8_t repeater_id; + enum lttpr_mode repeater_mode = phy_repeater_mode_transparent; + + core_link_write_dpcd(link, +@@ -1074,9 +1185,43 @@ static void configure_lttpr_mode(struct dc_link *link) + DP_PHY_REPEATER_MODE, + (uint8_t *)&repeater_mode, + sizeof(repeater_mode)); ++ ++ repeater_cnt = convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); ++ for (repeater_id = repeater_cnt; repeater_id > 0; repeater_id--) { ++ aux_interval_address = DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 + ++ ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (repeater_id - 1)); ++ core_link_read_dpcd( ++ link, ++ aux_interval_address, ++ (uint8_t *)&link->dpcd_caps.lttpr_caps.aux_rd_interval[repeater_id - 1], ++ sizeof(link->dpcd_caps.lttpr_caps.aux_rd_interval[repeater_id - 1])); ++ link->dpcd_caps.lttpr_caps.aux_rd_interval[repeater_id - 1] &= 0x7F; ++ } + } + } + ++static void repeater_training_done(struct dc_link *link, uint32_t offset) ++{ ++ union dpcd_training_pattern dpcd_pattern = { {0} }; ++ ++ const uint32_t dpcd_base_lt_offset = ++ DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + ++ ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); ++ /* Set training not in progress*/ ++ dpcd_pattern.v1_4.TRAINING_PATTERN_SET = DPCD_TRAINING_PATTERN_VIDEOIDLE; ++ ++ core_link_write_dpcd( ++ link, ++ dpcd_base_lt_offset, ++ &dpcd_pattern.raw, ++ 1); ++ ++ DC_LOG_HW_LINK_TRAINING("%s\n 0x%X pattern = %x\n", ++ __func__, ++ dpcd_base_lt_offset, ++ dpcd_pattern.v1_4.TRAINING_PATTERN_SET); ++} ++ + static void print_status_message( + struct dc_link *link, + const struct link_training_settings *lt_settings, +@@ -1156,6 +1301,17 @@ static void print_status_message( + lt_spread); + } + ++void dc_link_dp_set_drive_settings( ++ struct dc_link *link, ++ struct link_training_settings *lt_settings) ++{ ++ /* program ASIC PHY settings*/ ++ dp_set_hw_lane_settings(link, lt_settings, DPRX); ++ ++ /* Notify DP sink the PHY settings from source */ ++ dpcd_set_lane_settings(link, lt_settings, DPRX); ++} ++ + bool dc_link_dp_perform_link_training_skip_aux( + struct dc_link *link, + const struct dc_link_settings *link_setting) +@@ -1172,10 +1328,10 @@ bool dc_link_dp_perform_link_training_skip_aux( + /* 1. Perform_clock_recovery_sequence. */ + + /* transmit training pattern for clock recovery */ +- dp_set_hw_training_pattern(link, pattern_for_cr); ++ dp_set_hw_training_pattern(link, pattern_for_cr, DPRX); + + /* call HWSS to set lane settings*/ +- dp_set_hw_lane_settings(link, <_settings); ++ dp_set_hw_lane_settings(link, <_settings, DPRX); + + /* wait receiver to lock-on*/ + wait_for_training_aux_rd_interval(link, lt_settings.cr_pattern_time); +@@ -1183,10 +1339,10 @@ bool dc_link_dp_perform_link_training_skip_aux( + /* 2. Perform_channel_equalization_sequence. */ + + /* transmit training pattern for channel equalization. */ +- dp_set_hw_training_pattern(link, lt_settings.pattern_for_eq); ++ dp_set_hw_training_pattern(link, lt_settings.pattern_for_eq, DPRX); + + /* call HWSS to set lane settings*/ +- dp_set_hw_lane_settings(link, <_settings); ++ dp_set_hw_lane_settings(link, <_settings, DPRX); + + /* wait receiver to lock-on. */ + wait_for_training_aux_rd_interval(link, lt_settings.eq_pattern_time); +@@ -1208,9 +1364,12 @@ enum link_training_result dc_link_dp_perform_link_training( + { + enum link_training_result status = LINK_TRAINING_SUCCESS; + struct link_training_settings lt_settings; ++ + #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + bool fec_enable; + #endif ++ uint8_t repeater_cnt; ++ uint8_t repeater_id; + + initialize_training_settings( + link, +@@ -1230,17 +1389,40 @@ enum link_training_result dc_link_dp_perform_link_training( + dp_set_fec_ready(link, fec_enable); + #endif + +- /* Configure lttpr mode */ +- if (!link->is_lttpr_mode_transparent) ++ if (!link->is_lttpr_mode_transparent) { ++ /* Configure lttpr mode */ + configure_lttpr_mode(link); + +- /* 2. perform link training (set link training done +- * to false is done as well) +- */ +- status = perform_clock_recovery_sequence(link, <_settings); ++ /* 2. perform link training (set link training done ++ * to false is done as well) ++ */ ++ repeater_cnt = convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); ++ ++ for (repeater_id = repeater_cnt; (repeater_id > 0 && status == LINK_TRAINING_SUCCESS); ++ repeater_id--) { ++ status = perform_clock_recovery_sequence(link, <_settings, repeater_id); ++ ++ if (status != LINK_TRAINING_SUCCESS) ++ break; ++ ++ status = perform_channel_equalization_sequence(link, ++ <_settings, ++ repeater_id); ++ ++ if (status != LINK_TRAINING_SUCCESS) ++ break; ++ ++ repeater_training_done(link, repeater_id); ++ } ++ } ++ ++ if (status == LINK_TRAINING_SUCCESS) { ++ status = perform_clock_recovery_sequence(link, <_settings, DPRX); + if (status == LINK_TRAINING_SUCCESS) { + status = perform_channel_equalization_sequence(link, +- <_settings); ++ <_settings, ++ DPRX); ++ } + } + + if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) { +@@ -1393,10 +1575,11 @@ enum link_training_result dc_link_dp_sync_lt_attempt( + /* 2. perform link training (set link training done + * to false is done as well) + */ +- lt_status = perform_clock_recovery_sequence(link, <_settings); ++ lt_status = perform_clock_recovery_sequence(link, <_settings, DPRX); + if (lt_status == LINK_TRAINING_SUCCESS) { + lt_status = perform_channel_equalization_sequence(link, +- <_settings); ++ <_settings, ++ DPRX); + } + + /* 3. Sync LT must skip TRAINING_PATTERN_SET:0 (video pattern)*/ +@@ -3355,8 +3538,8 @@ bool dc_link_dp_set_test_pattern( + if (is_dp_phy_pattern(test_pattern)) { + /* Set DPCD Lane Settings before running test pattern */ + if (p_link_settings != NULL) { +- dp_set_hw_lane_settings(link, p_link_settings); +- dpcd_set_lane_settings(link, p_link_settings); ++ dp_set_hw_lane_settings(link, p_link_settings, DPRX); ++ dpcd_set_lane_settings(link, p_link_settings, DPRX); + } + + /* Blank stream if running test pattern */ +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c +index a519dbc5ecb6..5efbdc1eb173 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c +@@ -19,6 +19,36 @@ + #include "resource.h" + #endif + ++static uint8_t convert_to_count(uint8_t lttpr_repeater_count) ++{ ++ switch (lttpr_repeater_count) { ++ case 0x80: // 1 lttpr repeater ++ return 1; ++ case 0x40: // 2 lttpr repeaters ++ return 2; ++ case 0x20: // 3 lttpr repeaters ++ return 3; ++ case 0x10: // 4 lttpr repeaters ++ return 4; ++ case 0x08: // 5 lttpr repeaters ++ return 5; ++ case 0x04: // 6 lttpr repeaters ++ return 6; ++ case 0x02: // 7 lttpr repeaters ++ return 7; ++ case 0x01: // 8 lttpr repeaters ++ return 8; ++ default: ++ break; ++ } ++ return 0; // invalid value ++} ++ ++static inline bool is_immediate_downstream(struct dc_link *link, uint32_t offset) ++{ ++ return (convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt) == offset); ++} ++ + enum dc_status core_link_read_dpcd( + struct dc_link *link, + uint32_t address, +@@ -212,7 +242,8 @@ void dp_disable_link_phy_mst(struct dc_link *link, enum signal_type signal) + + bool dp_set_hw_training_pattern( + struct dc_link *link, +- enum dc_dp_training_pattern pattern) ++ enum dc_dp_training_pattern pattern, ++ uint32_t offset) + { + enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED; + +@@ -240,10 +271,14 @@ bool dp_set_hw_training_pattern( + + void dp_set_hw_lane_settings( + struct dc_link *link, +- const struct link_training_settings *link_settings) ++ const struct link_training_settings *link_settings, ++ uint32_t offset) + { + struct link_encoder *encoder = link->link_enc; + ++ if (!link->is_lttpr_mode_transparent && !is_immediate_downstream(link, offset)) ++ return; ++ + /* call Encoder to set lane settings */ + encoder->funcs->dp_set_lane_settings(encoder, link_settings); + } +diff --git a/drivers/gpu/drm/amd/display/dc/inc/link_hwss.h b/drivers/gpu/drm/amd/display/dc/inc/link_hwss.h +index 4eff5d38a2f9..9af7ee5bc8ee 100644 +--- a/drivers/gpu/drm/amd/display/dc/inc/link_hwss.h ++++ b/drivers/gpu/drm/amd/display/dc/inc/link_hwss.h +@@ -60,11 +60,13 @@ void dp_disable_link_phy_mst(struct dc_link *link, enum signal_type signal); + + bool dp_set_hw_training_pattern( + struct dc_link *link, +- enum dc_dp_training_pattern pattern); ++ enum dc_dp_training_pattern pattern, ++ uint32_t offset); + + void dp_set_hw_lane_settings( + struct dc_link *link, +- const struct link_training_settings *link_settings); ++ const struct link_training_settings *link_settings, ++ uint32_t offset); + + void dp_set_hw_test_pattern( + struct dc_link *link, +-- +2.17.1 + |