From: "Shen, George" <george.s...@amd.com>

[Why]
Current implementation is not scalable and retrofits the existing
standard link training code for purposes outside of its original design.

[How]
Refactor vendor specific link training sequence into its own separate
function to be called instead of the standard link training function.

Reviewed-by: Wenjing Liu <wenjing....@amd.com>
Acked-by: Rodrigo Siqueira <rodrigo.sique...@amd.com>
Signed-off-by: George Shen <george.s...@amd.com>
---
 .../gpu/drm/amd/display/dc/core/dc_link_dp.c  | 338 +++++++++++++++++-
 1 file changed, 337 insertions(+), 1 deletion(-)

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 6f552f7ee1db..04878817e622 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
@@ -2427,6 +2427,338 @@ static enum link_training_result 
dp_perform_128b_132b_link_training(
 }
 #endif
 
+static enum link_training_result 
dc_link_dp_perform_fixed_vs_pe_training_sequence(
+       struct dc_link *link,
+       struct link_training_settings *lt_settings)
+{
+       const uint8_t vendor_lttpr_write_data_reset[4] = {0x1, 0x50, 0x63, 
0xFF};
+       const uint8_t offset = dp_convert_to_count(
+                       link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
+       const uint8_t vendor_lttpr_write_data_intercept_en[4] = {0x1, 0x55, 
0x63, 0x0};
+       const uint8_t vendor_lttpr_write_data_intercept_dis[4] = {0x1, 0x55, 
0x63, 0x68};
+       uint8_t vendor_lttpr_write_data_vs[4] = {0x1, 0x51, 0x63, 0x0};
+       uint8_t vendor_lttpr_write_data_pe[4] = {0x1, 0x52, 0x63, 0x0};
+       uint32_t vendor_lttpr_write_address = 0xF004F;
+       enum link_training_result status = LINK_TRAINING_SUCCESS;
+       uint8_t lane = 0;
+       union down_spread_ctrl downspread = {0};
+       union lane_count_set lane_count_set = {0};
+       uint8_t toggle_rate;
+       uint8_t rate;
+
+       /* Only 8b/10b is supported */
+       ASSERT(dp_get_link_encoding_format(&lt_settings->link_settings) ==
+                       DP_8b_10b_ENCODING);
+
+       if (offset != 0xFF) {
+               vendor_lttpr_write_address +=
+                               ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * 
(offset - 1));
+       }
+
+       /* Vendor specific: Reset lane settings */
+       core_link_write_dpcd(
+                       link,
+                       vendor_lttpr_write_address,
+                       &vendor_lttpr_write_data_reset[0],
+                       sizeof(vendor_lttpr_write_data_reset));
+       core_link_write_dpcd(
+                       link,
+                       vendor_lttpr_write_address,
+                       &vendor_lttpr_write_data_vs[0],
+                       sizeof(vendor_lttpr_write_data_vs));
+       core_link_write_dpcd(
+                       link,
+                       vendor_lttpr_write_address,
+                       &vendor_lttpr_write_data_pe[0],
+                       sizeof(vendor_lttpr_write_data_pe));
+
+       /* Vendor specific: Enable intercept */
+       core_link_write_dpcd(
+                       link,
+                       vendor_lttpr_write_address,
+                       &vendor_lttpr_write_data_intercept_en[0],
+                       sizeof(vendor_lttpr_write_data_intercept_en));
+
+       /* 1. set link rate, lane count and spread. */
+
+       downspread.raw = (uint8_t)(lt_settings->link_settings.link_spread);
+
+       lane_count_set.bits.LANE_COUNT_SET =
+       lt_settings->link_settings.lane_count;
+
+       lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing;
+       lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0;
+
+
+       if (lt_settings->pattern_for_eq < DP_TRAINING_PATTERN_SEQUENCE_4) {
+               lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED =
+                               
link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED;
+       }
+
+       core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL,
+               &downspread.raw, sizeof(downspread));
+
+       core_link_write_dpcd(link, DP_LANE_COUNT_SET,
+               &lane_count_set.raw, 1);
+
+#if defined(CONFIG_DRM_AMD_DC_DCN)
+       rate = get_dpcd_link_rate(&lt_settings->link_settings);
+#else
+       rate = (uint8_t) (lt_settings->link_settings.link_rate);
+#endif
+
+       /* Vendor specific: Toggle link rate */
+       toggle_rate = (rate == 0x6) ? 0xA : 0x6;
+
+       if (link->vendor_specific_lttpr_link_rate_wa == rate) {
+               core_link_write_dpcd(
+                               link,
+                               DP_LINK_BW_SET,
+                               &toggle_rate,
+                               1);
+       }
+
+       link->vendor_specific_lttpr_link_rate_wa = rate;
+
+       core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
+
+       DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x framing = 
%x\n %x spread = %x\n",
+               __func__,
+               DP_LINK_BW_SET,
+               lt_settings->link_settings.link_rate,
+               DP_LANE_COUNT_SET,
+               lt_settings->link_settings.lane_count,
+               lt_settings->enhanced_framing,
+               DP_DOWNSPREAD_CTRL,
+               lt_settings->link_settings.link_spread);
+
+       /* 2. Perform link training */
+
+       /* Perform Clock Recovery Sequence */
+       if (status == LINK_TRAINING_SUCCESS) {
+               uint32_t retries_cr;
+               uint32_t retry_count;
+               uint32_t wait_time_microsec;
+               enum dc_lane_count lane_count = 
lt_settings->link_settings.lane_count;
+               union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX];
+               union lane_align_status_updated dpcd_lane_status_updated;
+               union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
+
+               retries_cr = 0;
+               retry_count = 0;
+
+               while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
+                       (retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
+
+                       memset(&dpcd_lane_status, '\0', 
sizeof(dpcd_lane_status));
+                       memset(&dpcd_lane_status_updated, '\0',
+                       sizeof(dpcd_lane_status_updated));
+
+                       /* 1. call HWSS to set lane settings */
+                       dp_set_hw_lane_settings(
+                                       link,
+                                       lt_settings,
+                                       0);
+
+                       /* 2. update DPCD of the receiver */
+                       if (!retry_count) {
+                               /* EPR #361076 - write as a 5-byte burst,
+                                * but only for the 1-st iteration.
+                                */
+                               dpcd_set_lt_pattern_and_lane_settings(
+                                               link,
+                                               lt_settings,
+                                               lt_settings->pattern_for_cr,
+                                               0);
+                               /* Vendor specific: Disable intercept */
+                               core_link_write_dpcd(
+                                               link,
+                                               vendor_lttpr_write_address,
+                                               
&vendor_lttpr_write_data_intercept_dis[0],
+                                               
sizeof(vendor_lttpr_write_data_intercept_dis));
+                       } else {
+                               vendor_lttpr_write_data_vs[3] = 0;
+                               vendor_lttpr_write_data_pe[3] = 0;
+
+                               for (lane = 0; lane < lane_count; lane++) {
+                                       vendor_lttpr_write_data_vs[3] |=
+                                                       
lt_settings->dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET << (2 * lane);
+                                       vendor_lttpr_write_data_pe[3] |=
+                                                       
lt_settings->dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET << (2 * lane);
+                               }
+
+                               /* Vendor specific: Update VS and PE to DPRX 
requested value */
+                               core_link_write_dpcd(
+                                               link,
+                                               vendor_lttpr_write_address,
+                                               &vendor_lttpr_write_data_vs[0],
+                                               
sizeof(vendor_lttpr_write_data_vs));
+                               core_link_write_dpcd(
+                                               link,
+                                               vendor_lttpr_write_address,
+                                               &vendor_lttpr_write_data_pe[0],
+                                               
sizeof(vendor_lttpr_write_data_pe));
+
+                               dpcd_set_lane_settings(
+                                               link,
+                                               lt_settings,
+                                               0);
+                       }
+
+                       /* 3. wait receiver to lock-on*/
+                       wait_time_microsec = lt_settings->cr_pattern_time;
+
+                       dp_wait_for_training_aux_rd_interval(
+                                       link,
+                                       wait_time_microsec);
+
+                       /* 4. Read lane status and requested drive
+                        * settings as set by the sink
+                        */
+                       dp_get_lane_status_and_lane_adjust(
+                                       link,
+                                       lt_settings,
+                                       dpcd_lane_status,
+                                       &dpcd_lane_status_updated,
+                                       dpcd_lane_adjust,
+                                       0);
+
+                       /* 5. check CR done*/
+                       if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
+                               status = LINK_TRAINING_SUCCESS;
+                               break;
+                       }
+
+                       /* 6. max VS reached*/
+                       if (dp_is_max_vs_reached(lt_settings))
+                               break;
+
+                       /* 7. same lane settings */
+                       /* Note: settings are the same for all lanes,
+                        * so comparing first lane is sufficient
+                        */
+                       if 
(lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
+                                       
dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
+                               retries_cr++;
+                       else
+                               retries_cr = 0;
+
+                       /* 8. update VS/PE/PC2 in lt_settings*/
+                       dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
+                                       lt_settings->hw_lane_settings, 
lt_settings->dpcd_lane_settings);
+                       retry_count++;
+               }
+
+               if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) {
+                       ASSERT(0);
+                       DC_LOG_ERROR("%s: Link Training Error, could not get CR 
after %d tries. Possibly voltage swing issue",
+                               __func__,
+                               LINK_TRAINING_MAX_CR_RETRY);
+
+               }
+
+               status = dp_get_cr_failure(lane_count, dpcd_lane_status);
+       }
+
+       /* Perform Channel EQ Sequence */
+       if (status == LINK_TRAINING_SUCCESS) {
+               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};
+               union lane_adjust dpcd_lane_adjust[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, 0);
+
+               status = LINK_TRAINING_EQ_FAIL_EQ;
+
+               for (retries_ch_eq = 0; retries_ch_eq <= 
LINK_TRAINING_MAX_RETRY_COUNT;
+                       retries_ch_eq++) {
+
+                       dp_set_hw_lane_settings(link, lt_settings, 0);
+
+                       vendor_lttpr_write_data_vs[3] = 0;
+                       vendor_lttpr_write_data_pe[3] = 0;
+
+                       for (lane = 0; lane < lane_count; lane++) {
+                               vendor_lttpr_write_data_vs[3] |=
+                                               
lt_settings->dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET << (2 * lane);
+                               vendor_lttpr_write_data_pe[3] |=
+                                               
lt_settings->dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET << (2 * lane);
+                       }
+
+                       /* Vendor specific: Update VS and PE to DPRX requested 
value */
+                       core_link_write_dpcd(
+                                       link,
+                                       vendor_lttpr_write_address,
+                                       &vendor_lttpr_write_data_vs[0],
+                                       sizeof(vendor_lttpr_write_data_vs));
+                       core_link_write_dpcd(
+                                       link,
+                                       vendor_lttpr_write_address,
+                                       &vendor_lttpr_write_data_pe[0],
+                                       sizeof(vendor_lttpr_write_data_pe));
+
+                       /* 2. update DPCD*/
+                       if (!retries_ch_eq)
+                               /* EPR #361076 - write as a 5-byte burst,
+                                * but only for the 1-st iteration
+                                */
+
+                               dpcd_set_lt_pattern_and_lane_settings(
+                                       link,
+                                       lt_settings,
+                                       tr_pattern, 0);
+                       else
+                               dpcd_set_lane_settings(link, lt_settings, 0);
+
+                       /* 3. wait for receiver to lock-on*/
+                       wait_time_microsec = lt_settings->eq_pattern_time;
+
+                       dp_wait_for_training_aux_rd_interval(
+                                       link,
+                                       wait_time_microsec);
+
+                       /* 4. Read lane status and requested
+                        * drive settings as set by the sink
+                        */
+                       dp_get_lane_status_and_lane_adjust(
+                               link,
+                               lt_settings,
+                               dpcd_lane_status,
+                               &dpcd_lane_status_updated,
+                               dpcd_lane_adjust,
+                               0);
+
+                       /* 5. check CR done*/
+                       if (!dp_is_cr_done(lane_count, dpcd_lane_status)) {
+                               status = LINK_TRAINING_EQ_FAIL_CR;
+                               break;
+                       }
+
+                       /* 6. check CHEQ done*/
+                       if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) &&
+                                       dp_is_symbol_locked(lane_count, 
dpcd_lane_status) &&
+                                       
dp_is_interlane_aligned(dpcd_lane_status_updated)) {
+                               status = LINK_TRAINING_SUCCESS;
+                               break;
+                       }
+
+                       /* 7. update VS/PE/PC2 in lt_settings*/
+                       dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
+                                       lt_settings->hw_lane_settings, 
lt_settings->dpcd_lane_settings);
+               }
+       }
+
+       return status;
+}
+
+
 enum link_training_result dc_link_dp_perform_link_training(
        struct dc_link *link,
        const struct dc_link_settings *link_settings,
@@ -2466,7 +2798,11 @@ enum link_training_result 
dc_link_dp_perform_link_training(
         * Per DP specs starting from here, DPTX device shall not issue
         * Non-LT AUX transactions inside training mode.
         */
-       if (encoding == DP_8b_10b_ENCODING)
+       if (!link->dc->debug.apply_vendor_specific_lttpr_wa &&
+                       (link->chip_caps & 
EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) &&
+                       link->lttpr_mode == LTTPR_MODE_TRANSPARENT)
+               status = dc_link_dp_perform_fixed_vs_pe_training_sequence(link, 
&lt_settings);
+       else if (encoding == DP_8b_10b_ENCODING)
                status = dp_perform_8b_10b_link_training(link, &lt_settings);
 #if defined(CONFIG_DRM_AMD_DC_DCN)
        else if (encoding == DP_128b_132b_ENCODING)
-- 
2.25.1

Reply via email to