mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
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>
This commit is contained in:
parent
9011443477
commit
820e393548
4 changed files with 215 additions and 43 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Add table
Reference in a new issue