From: Ding Wang <Ding.Wang@xxxxxxx> Change-Id: Ic9a00fdffa1809d00a725a067ce9d97ae0df0586 Signed-off-by: Ding Wang <ding.wang at amd.com> Reviewed-by: Tony Cheng <Tony.Cheng at amd.com> Acked-by: Harry Wentland <Harry.Wentland at 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(<_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, <_settings)) { - - if (perform_channel_equalization_sequence(core_link, - <_settings)) - status = true; + if (!perform_clock_recovery_sequence(core_link, <_settings)) { + status = LINK_TRAINING_CR_FAIL; + } else { + status = perform_channel_equalization_sequence(core_link, + <_settings); } - if (status || !skip_video_pattern) - status = perform_link_training_int(core_link, - <_settings, status); + if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) { + if (!perform_link_training_int(core_link, + <_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