aboutsummaryrefslogtreecommitdiffstats
path: root/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4398-drm-amd-display-implement-lttpr-logic.patch
diff options
context:
space:
mode:
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.patch729
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, &lt_settings);
++ dp_set_hw_lane_settings(link, &lt_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, &lt_settings);
++ dp_set_hw_lane_settings(link, &lt_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, &lt_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, &lt_settings, repeater_id);
++
++ if (status != LINK_TRAINING_SUCCESS)
++ break;
++
++ status = perform_channel_equalization_sequence(link,
++ &lt_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, &lt_settings, DPRX);
+ if (status == LINK_TRAINING_SUCCESS) {
+ status = perform_channel_equalization_sequence(link,
+- &lt_settings);
++ &lt_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, &lt_settings);
++ lt_status = perform_clock_recovery_sequence(link, &lt_settings, DPRX);
+ if (lt_status == LINK_TRAINING_SUCCESS) {
+ lt_status = perform_channel_equalization_sequence(link,
+- &lt_settings);
++ &lt_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
+