From: Jimmy Kizito <jimmy.kiz...@amd.com>

[Why]
Some displays may need several link training attempts before
link training succeeds.

[How]
If training succeeds after falling back to lower link bandwidth,
retry at original link bandwidth instead of abandoning link training
whenever link bandwidth is less than stream bandwidth.

Reviewed-by: Jun Lei <jun....@amd.com>
Acked-by: Qingqing Zhuo <qingqing.z...@amd.com>
Signed-off-by: Jimmy Kizito <jimmy.kiz...@amd.com>
---
 .../gpu/drm/amd/display/dc/core/dc_link_dp.c  | 77 +++++++++++++------
 1 file changed, 53 insertions(+), 24 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 975d631534b5..d8de8dbf3676 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
@@ -2783,31 +2783,37 @@ bool perform_link_training_with_retries(
        struct dc_link *link = stream->link;
        enum dp_panel_mode panel_mode = dp_get_panel_mode(link);
        enum link_training_result status = LINK_TRAINING_CR_FAIL_LANE0;
-       struct dc_link_settings current_setting = *link_setting;
+       struct dc_link_settings cur_link_settings = *link_setting;
        const struct link_hwss *link_hwss = get_link_hwss(link, 
&pipe_ctx->link_res);
        int fail_count = 0;
+       bool is_link_bw_low = false; /* link bandwidth < stream bandwidth */
+       bool is_link_bw_min = /* RBR x 1 */
+               (cur_link_settings.link_rate <= LINK_RATE_LOW) &&
+               (cur_link_settings.lane_count <= LANE_COUNT_ONE);
 
        dp_trace_commit_lt_init(link);
 
 
-       if (dp_get_link_encoding_format(&current_setting) == DP_8b_10b_ENCODING)
+       if (dp_get_link_encoding_format(&cur_link_settings) == 
DP_8b_10b_ENCODING)
                /* We need to do this before the link training to ensure the 
idle
                 * pattern in SST mode will be sent right after the link 
training
                 */
                link_hwss->setup_stream_encoder(pipe_ctx);
 
        dp_trace_set_lt_start_timestamp(link, false);
-       for (j = 0; j < attempts; ++j) {
+       j = 0;
+       while (j < attempts && fail_count < (attempts * 10)) {
 
-               DC_LOG_HW_LINK_TRAINING("%s: Beginning link training attempt %u 
of %d\n",
-                       __func__, (unsigned int)j + 1, attempts);
+               DC_LOG_HW_LINK_TRAINING("%s: Beginning link training attempt %u 
of %d @ rate(%d) x lane(%d)\n",
+                       __func__, (unsigned int)j + 1, attempts, 
cur_link_settings.link_rate,
+                       cur_link_settings.lane_count);
 
                dp_enable_link_phy(
                        link,
                        &pipe_ctx->link_res,
                        signal,
                        pipe_ctx->clock_source->id,
-                       &current_setting);
+                       &cur_link_settings);
 
                if (stream->sink_patches.dppowerup_delay > 0) {
                        int delay_dp_power_up_in_ms = 
stream->sink_patches.dppowerup_delay;
@@ -2832,30 +2838,30 @@ bool perform_link_training_with_retries(
                dp_set_panel_mode(link, panel_mode);
 
                if (link->aux_access_disabled) {
-                       dc_link_dp_perform_link_training_skip_aux(link, 
&pipe_ctx->link_res, &current_setting);
+                       dc_link_dp_perform_link_training_skip_aux(link, 
&pipe_ctx->link_res, &cur_link_settings);
                        return true;
                } else {
                        /** @todo Consolidate USB4 DP and DPx.x training. */
                        if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) {
                                status = 
dc_link_dpia_perform_link_training(link,
                                                &pipe_ctx->link_res,
-                                               &current_setting,
+                                               &cur_link_settings,
                                                skip_video_pattern);
 
                                /* Transmit idle pattern once training 
successful. */
-                               if (status == LINK_TRAINING_SUCCESS)
+                               if (status == LINK_TRAINING_SUCCESS && 
!is_link_bw_low)
                                        dp_set_hw_test_pattern(link, 
&pipe_ctx->link_res, DP_TEST_PATTERN_VIDEO_MODE, NULL, 0);
                        } else {
                                status = dc_link_dp_perform_link_training(link,
                                                &pipe_ctx->link_res,
-                                               &current_setting,
+                                               &cur_link_settings,
                                                skip_video_pattern);
                        }
 
                        dp_trace_lt_total_count_increment(link, false);
                        dp_trace_lt_result_update(link, status, false);
                        dp_trace_set_lt_end_timestamp(link, false);
-                       if (status == LINK_TRAINING_SUCCESS)
+                       if (status == LINK_TRAINING_SUCCESS && !is_link_bw_low)
                                return true;
                }
 
@@ -2866,8 +2872,9 @@ bool perform_link_training_with_retries(
                if (j == (attempts - 1) && link->ep_type == 
DISPLAY_ENDPOINT_PHY)
                        break;
 
-               DC_LOG_WARNING("%s: Link training attempt %u of %d failed\n",
-                       __func__, (unsigned int)j + 1, attempts);
+               DC_LOG_WARNING("%s: Link training attempt %u of %d failed @ 
rate(%d) x lane(%d)\n",
+                       __func__, (unsigned int)j + 1, attempts, 
cur_link_settings.link_rate,
+                       cur_link_settings.lane_count);
 
                dp_disable_link_phy(link, &pipe_ctx->link_res, signal);
 
@@ -2876,27 +2883,49 @@ bool perform_link_training_with_retries(
                        enum dc_connection_type type = dc_connection_none;
 
                        dc_link_detect_sink(link, &type);
-                       if (type == dc_connection_none)
+                       if (type == dc_connection_none) {
+                               DC_LOG_HW_LINK_TRAINING("%s: Aborting training 
because sink unplugged\n", __func__);
                                break;
-               } else if (do_fallback) {
+                       }
+               }
+
+               /* Try to train again at original settings if:
+                * - not falling back between training attempts;
+                * - aborted previous attempt due to reasons other than sink 
unplug;
+                * - successfully trained but at a link rate lower than that 
required by stream;
+                * - reached minimum link bandwidth.
+                */
+               if (!do_fallback || (status == LINK_TRAINING_ABORT) ||
+                               (status == LINK_TRAINING_SUCCESS && 
is_link_bw_low) ||
+                               is_link_bw_min) {
+                       j++;
+                       cur_link_settings = *link_setting;
+                       delay_between_attempts += LINK_TRAINING_RETRY_DELAY;
+                       is_link_bw_low = false;
+                       is_link_bw_min = (cur_link_settings.link_rate <= 
LINK_RATE_LOW) &&
+                               (cur_link_settings.lane_count <= 
LANE_COUNT_ONE);
+
+               } else if (do_fallback) { /* Try training at lower link 
bandwidth if doing fallback. */
                        uint32_t req_bw;
                        uint32_t link_bw;
 
-                       decide_fallback_link_setting(link, *link_setting, 
&current_setting, status);
-                       /* Fail link training if reduced link bandwidth no 
longer meets
-                        * stream requirements.
+                       decide_fallback_link_setting(link, *link_setting, 
&cur_link_settings, status);
+                       /* Flag if reduced link bandwidth no longer meets 
stream requirements or fallen back to
+                        * minimum link bandwidth.
                         */
                        req_bw = 
dc_bandwidth_in_kbps_from_timing(&stream->timing);
-                       link_bw = dc_link_bandwidth_kbps(link, 
&current_setting);
-                       if (req_bw > link_bw)
-                               break;
+                       link_bw = dc_link_bandwidth_kbps(link, 
&cur_link_settings);
+                       is_link_bw_low = (req_bw > link_bw);
+                       is_link_bw_min = ((cur_link_settings.link_rate <= 
LINK_RATE_LOW) &&
+                               (cur_link_settings.lane_count <= 
LANE_COUNT_ONE));
+
+                       if (is_link_bw_low)
+                               DC_LOG_WARNING("%s: Link bandwidth too low 
after fallback req_bw(%d) > link_bw(%d)\n",
+                                       __func__, req_bw, link_bw);
                }
 
                msleep(delay_between_attempts);
-
-               delay_between_attempts += LINK_TRAINING_RETRY_DELAY;
        }
-
        return false;
 }
 
-- 
2.25.1

Reply via email to