drm/amd/display: implement lttpr logic
authorabdoulaye berthe <abdoulaye.berthe@amd.com>
Wed, 24 Jul 2019 15:01:44 +0000 (11:01 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 13 Nov 2019 20:29:43 +0000 (15:29 -0500)
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>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c
drivers/gpu/drm/amd/display/dc/inc/link_hwss.h

index 94d5a0a..11b6e14 100644 (file)
@@ -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 */
index a519dbc..5efbdc1 100644 (file)
 #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);
 }
index 4eff5d3..9af7ee5 100644 (file)
@@ -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,