aboutsummaryrefslogtreecommitdiffstats
path: root/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/0623-drm-amd-display-link-training-fallback-actions.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/0623-drm-amd-display-link-training-fallback-actions.patch')
-rw-r--r--meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/0623-drm-amd-display-link-training-fallback-actions.patch428
1 files changed, 428 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/0623-drm-amd-display-link-training-fallback-actions.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/0623-drm-amd-display-link-training-fallback-actions.patch
new file mode 100644
index 00000000..e4ccbcba
--- /dev/null
+++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/0623-drm-amd-display-link-training-fallback-actions.patch
@@ -0,0 +1,428 @@
+From 8430e07bea784739e289bbe3da8cf72a756a94bd Mon Sep 17 00:00:00 2001
+From: Ding Wang <Ding.Wang@amd.com>
+Date: Thu, 13 Jul 2017 12:09:57 -0400
+Subject: [PATCH 0623/4131] drm/amd/display: link training fallback actions
+
+Signed-off-by: Ding Wang <ding.wang@amd.com>
+Reviewed-by: Tony Cheng <Tony.Cheng@amd.com>
+Acked-by: Harry Wentland <Harry.Wentland@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+---
+ drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c | 239 +++++++++++++++++----
+ drivers/gpu/drm/amd/display/dc/dc.h | 2 +-
+ drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h | 8 +
+ .../drm/amd/display/include/link_service_types.h | 9 +
+ 4 files changed, 215 insertions(+), 43 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 98048fe..dd3f57f 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
+@@ -731,7 +731,7 @@ static enum hw_dp_training_pattern get_supported_tp(struct core_link *link)
+ return HW_DP_TRAINING_PATTERN_2;
+ }
+
+-static bool perform_channel_equalization_sequence(
++static enum link_training_result perform_channel_equalization_sequence(
+ struct core_link *link,
+ struct link_training_settings *lt_settings)
+ {
+@@ -777,19 +777,19 @@ static bool perform_channel_equalization_sequence(
+
+ /* 5. check CR done*/
+ if (!is_cr_done(lane_count, dpcd_lane_status))
+- return false;
++ return LINK_TRAINING_EQ_FAIL_CR;
+
+ /* 6. check CHEQ done*/
+ if (is_ch_eq_done(lane_count,
+ dpcd_lane_status,
+ &dpcd_lane_status_updated))
+- return true;
++ return LINK_TRAINING_SUCCESS;
+
+ /* 7. update VS/PE/PC2 in lt_settings*/
+ update_drive_settings(lt_settings, req_settings);
+ }
+
+- return false;
++ return LINK_TRAINING_EQ_FAIL_EQ;
+
+ }
+
+@@ -943,18 +943,17 @@ static inline bool perform_link_training_int(
+ return status;
+ }
+
+-bool dc_link_dp_perform_link_training(
++enum link_training_result dc_link_dp_perform_link_training(
+ struct dc_link *link,
+ const struct dc_link_settings *link_setting,
+ bool skip_video_pattern)
+ {
++ enum link_training_result status = LINK_TRAINING_SUCCESS;
+ struct core_link *core_link = DC_LINK_TO_CORE(link);
+- bool status;
+
+ char *link_rate = "Unknown";
+ struct link_training_settings lt_settings;
+
+- status = false;
+ memset(&lt_settings, '\0', sizeof(lt_settings));
+
+ lt_settings.link_settings.link_rate = link_setting->link_rate;
+@@ -976,16 +975,23 @@ bool dc_link_dp_perform_link_training(
+
+ /* 2. perform link training (set link training done
+ * to false is done as well)*/
+- if (perform_clock_recovery_sequence(core_link, &lt_settings)) {
+-
+- if (perform_channel_equalization_sequence(core_link,
+- &lt_settings))
+- status = true;
++ if (!perform_clock_recovery_sequence(core_link, &lt_settings)) {
++ status = LINK_TRAINING_CR_FAIL;
++ } else {
++ status = perform_channel_equalization_sequence(core_link,
++ &lt_settings);
+ }
+
+- if (status || !skip_video_pattern)
+- status = perform_link_training_int(core_link,
+- &lt_settings, status);
++ if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) {
++ if (!perform_link_training_int(core_link,
++ &lt_settings,
++ status == LINK_TRAINING_SUCCESS)) {
++ /* the next link training setting in this case
++ * would be the same as CR failure case.
++ */
++ status = LINK_TRAINING_CR_FAIL;
++ }
++ }
+
+ /* 6. print status message*/
+ switch (lt_settings.link_settings.link_rate) {
+@@ -1013,7 +1019,9 @@ bool dc_link_dp_perform_link_training(
+ CONN_MSG_LT(core_link, "%sx%d %s VS=%d, PE=%d",
+ link_rate,
+ lt_settings.link_settings.lane_count,
+- status ? "pass" : "fail",
++ (status == LINK_TRAINING_SUCCESS) ? "pass" :
++ ((status == LINK_TRAINING_CR_FAIL) ? "CR failed" :
++ "EQ failed"),
+ lt_settings.lane_settings[0].VOLTAGE_SWING,
+ lt_settings.lane_settings[0].PRE_EMPHASIS);
+
+@@ -1035,7 +1043,7 @@ bool perform_link_training_with_retries(
+ if (dc_link_dp_perform_link_training(
+ &link->public,
+ link_setting,
+- skip_video_pattern))
++ skip_video_pattern) == LINK_TRAINING_SUCCESS)
+ return true;
+
+ msleep(delay_between_attempts);
+@@ -1068,15 +1076,6 @@ static const struct dc_link_settings *get_link_training_fallback_table(
+ return &link_training_fallback_table[i];
+ }
+
+-static bool exceeded_limit_link_setting(
+- const struct dc_link_settings *link_setting,
+- const struct dc_link_settings *limit_link_setting)
+-{
+- return (link_setting->lane_count * link_setting->link_rate
+- > limit_link_setting->lane_count * limit_link_setting->link_rate ?
+- true : false);
+-}
+-
+ static struct dc_link_settings get_max_link_cap(struct core_link *link)
+ {
+ /* Set Default link settings */
+@@ -1109,13 +1108,15 @@ bool dp_hbr_verify_link_cap(
+ struct dc_link_settings *known_limit_link_setting)
+ {
+ struct dc_link_settings max_link_cap = {0};
++ struct dc_link_settings cur_link_setting = {0};
++ struct dc_link_settings *cur = &cur_link_setting;
++ struct dc_link_settings initial_link_settings = {0};
+ bool success;
+ bool skip_link_training;
+- const struct dc_link_settings *cur;
+ bool skip_video_pattern;
+- uint32_t i;
+ struct clock_source *dp_cs;
+ enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_EXTERNAL;
++ enum link_training_result status;
+
+ success = false;
+ skip_link_training = false;
+@@ -1142,19 +1143,16 @@ bool dp_hbr_verify_link_cap(
+ ASSERT(dp_cs);
+ }
+
+- for (i = 0; i < get_link_training_fallback_table_len(link) &&
+- !success; i++) {
+- cur = get_link_training_fallback_table(link, i);
+-
+- if (known_limit_link_setting->lane_count != LANE_COUNT_UNKNOWN &&
+- exceeded_limit_link_setting(cur,
+- known_limit_link_setting))
+- continue;
+-
+- if (!is_link_setting_supported(cur, &max_link_cap))
+- continue;
+-
++ /* link training starts with the maximum common settings
++ * supported by both sink and ASIC.
++ */
++ initial_link_settings = get_common_supported_link_settings(
++ *known_limit_link_setting,
++ max_link_cap);
++ cur_link_setting = initial_link_settings;
++ do {
+ skip_video_pattern = true;
++
+ if (cur->link_rate == LINK_RATE_LOW)
+ skip_video_pattern = false;
+
+@@ -1167,10 +1165,12 @@ bool dp_hbr_verify_link_cap(
+ if (skip_link_training)
+ success = true;
+ else {
+- success = dc_link_dp_perform_link_training(
++ status = dc_link_dp_perform_link_training(
+ &link->public,
+ cur,
+ skip_video_pattern);
++ if (status == LINK_TRAINING_SUCCESS)
++ success = true;
+ }
+
+ if (success)
+@@ -1181,7 +1181,8 @@ bool dp_hbr_verify_link_cap(
+ * based on the actual mode we're driving
+ */
+ dp_disable_link_phy(link, link->public.connector_signal);
+- }
++ } while (!success && decide_fallback_link_setting(
++ initial_link_settings, cur, status));
+
+ /* Link Training failed for all Link Settings
+ * (Lane Count is still unknown)
+@@ -1202,6 +1203,160 @@ bool dp_hbr_verify_link_cap(
+ return success;
+ }
+
++struct dc_link_settings get_common_supported_link_settings (
++ struct dc_link_settings link_setting_a,
++ struct dc_link_settings link_setting_b)
++{
++ struct dc_link_settings link_settings = {0};
++
++ link_settings.lane_count =
++ (link_setting_a.lane_count <=
++ link_setting_b.lane_count) ?
++ link_setting_a.lane_count :
++ link_setting_b.lane_count;
++ link_settings.link_rate =
++ (link_setting_a.link_rate <=
++ link_setting_b.link_rate) ?
++ link_setting_a.link_rate :
++ link_setting_b.link_rate;
++ link_settings.link_spread = LINK_SPREAD_DISABLED;
++
++ /* in DP compliance test, DPR-120 may have
++ * a random value in its MAX_LINK_BW dpcd field.
++ * We map it to the maximum supported link rate that
++ * is smaller than MAX_LINK_BW in this case.
++ */
++ if (link_settings.link_rate > LINK_RATE_HIGH3) {
++ link_settings.link_rate = LINK_RATE_HIGH3;
++ } else if (link_settings.link_rate < LINK_RATE_HIGH3
++ && link_settings.link_rate > LINK_RATE_HIGH2) {
++ link_settings.link_rate = LINK_RATE_HIGH2;
++ } else if (link_settings.link_rate < LINK_RATE_HIGH2
++ && link_settings.link_rate > LINK_RATE_HIGH) {
++ link_settings.link_rate = LINK_RATE_HIGH;
++ } else if (link_settings.link_rate < LINK_RATE_HIGH
++ && link_settings.link_rate > LINK_RATE_LOW) {
++ link_settings.link_rate = LINK_RATE_LOW;
++ } else if (link_settings.link_rate < LINK_RATE_LOW) {
++ link_settings.link_rate = LINK_RATE_UNKNOWN;
++ }
++
++ return link_settings;
++}
++
++bool reached_minimum_lane_count(enum dc_lane_count lane_count)
++{
++ return lane_count <= LANE_COUNT_ONE;
++}
++
++bool reached_minimum_link_rate(enum dc_link_rate link_rate)
++{
++ return link_rate <= LINK_RATE_LOW;
++}
++
++enum dc_lane_count reduce_lane_count(enum dc_lane_count lane_count)
++{
++ switch (lane_count) {
++ case LANE_COUNT_FOUR:
++ return LANE_COUNT_TWO;
++ case LANE_COUNT_TWO:
++ return LANE_COUNT_ONE;
++ case LANE_COUNT_ONE:
++ return LANE_COUNT_UNKNOWN;
++ default:
++ return LANE_COUNT_UNKNOWN;
++ }
++}
++
++enum dc_link_rate reduce_link_rate(enum dc_link_rate link_rate)
++{
++ switch (link_rate) {
++ case LINK_RATE_HIGH3:
++ return LINK_RATE_HIGH2;
++ case LINK_RATE_HIGH2:
++ return LINK_RATE_HIGH;
++ case LINK_RATE_HIGH:
++ return LINK_RATE_LOW;
++ case LINK_RATE_LOW:
++ return LINK_RATE_UNKNOWN;
++ default:
++ return LINK_RATE_UNKNOWN;
++ }
++}
++
++/*
++ * function: set link rate and lane count fallback based
++ * on current link setting and last link training result
++ * return value:
++ * true - link setting could be set
++ * false - has reached minimum setting
++ * and no further fallback could be done
++ */
++bool decide_fallback_link_setting(
++ struct dc_link_settings initial_link_settings,
++ struct dc_link_settings *current_link_setting,
++ enum link_training_result training_result)
++{
++ if (!current_link_setting)
++ return false;
++
++ switch (training_result) {
++ case LINK_TRAINING_CR_FAIL:
++ {
++ if (!reached_minimum_link_rate
++ (current_link_setting->link_rate)) {
++ current_link_setting->link_rate =
++ reduce_link_rate(
++ current_link_setting->link_rate);
++ } else if (!reached_minimum_lane_count
++ (current_link_setting->lane_count)) {
++ current_link_setting->link_rate =
++ initial_link_settings.link_rate;
++ current_link_setting->lane_count =
++ reduce_lane_count(
++ current_link_setting->lane_count);
++ } else {
++ return false;
++ }
++ break;
++ }
++ case LINK_TRAINING_EQ_FAIL_EQ:
++ {
++ if (!reached_minimum_lane_count
++ (current_link_setting->lane_count)) {
++ current_link_setting->lane_count =
++ reduce_lane_count(
++ current_link_setting->lane_count);
++ } else if (!reached_minimum_link_rate
++ (current_link_setting->link_rate)) {
++ current_link_setting->lane_count =
++ initial_link_settings.lane_count;
++ current_link_setting->link_rate =
++ reduce_link_rate(
++ current_link_setting->link_rate);
++ } else {
++ return false;
++ }
++ break;
++ }
++ case LINK_TRAINING_EQ_FAIL_CR:
++ {
++ if (!reached_minimum_link_rate
++ (current_link_setting->link_rate)) {
++ current_link_setting->link_rate =
++ reduce_link_rate(
++ current_link_setting->link_rate);
++ } else {
++ return false;
++ }
++ break;
++ }
++ default:
++ return false;
++ }
++ return true;
++}
++
+ static uint32_t bandwidth_in_kbps_from_timing(
+ const struct dc_crtc_timing *timing)
+ {
+diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
+index cd89814..6a22c91 100644
+--- a/drivers/gpu/drm/amd/display/dc/dc.h
++++ b/drivers/gpu/drm/amd/display/dc/dc.h
+@@ -777,7 +777,7 @@ void dc_link_dp_set_drive_settings(
+ const struct dc_link *link,
+ struct link_training_settings *lt_settings);
+
+-bool dc_link_dp_perform_link_training(
++enum link_training_result dc_link_dp_perform_link_training(
+ struct dc_link *link,
+ const struct dc_link_settings *link_setting,
+ bool skip_video_pattern);
+diff --git a/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h b/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h
+index 92c56e6..b6ef1bf 100644
+--- a/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h
++++ b/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h
+@@ -37,6 +37,14 @@ bool dp_hbr_verify_link_cap(
+ struct core_link *link,
+ struct dc_link_settings *known_limit_link_setting);
+
++bool decide_fallback_link_setting(struct dc_link_settings link_setting_init,
++ struct dc_link_settings *link_setting_current,
++ enum link_training_result training_result);
++
++struct dc_link_settings get_common_supported_link_settings (
++ struct dc_link_settings link_setting_a,
++ struct dc_link_settings link_setting_b);
++
+ bool dp_validate_mode_timing(
+ struct core_link *link,
+ const struct dc_crtc_timing *timing);
+diff --git a/drivers/gpu/drm/amd/display/include/link_service_types.h b/drivers/gpu/drm/amd/display/include/link_service_types.h
+index fe8b514..adea1a5 100644
+--- a/drivers/gpu/drm/amd/display/include/link_service_types.h
++++ b/drivers/gpu/drm/amd/display/include/link_service_types.h
+@@ -56,6 +56,15 @@ enum {
+ LINK_RATE_REF_FREQ_IN_KHZ = 27000 /*27MHz*/
+ };
+
++enum link_training_result {
++ LINK_TRAINING_SUCCESS,
++ LINK_TRAINING_CR_FAIL,
++ /* CR DONE bit is cleared during EQ step */
++ LINK_TRAINING_EQ_FAIL_CR,
++ /* other failure during EQ step */
++ LINK_TRAINING_EQ_FAIL_EQ,
++};
++
+ struct link_training_settings {
+ struct dc_link_settings link_settings;
+ struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX];
+--
+2.7.4
+