Commit 64c12b73 authored by abdoulaye berthe's avatar abdoulaye berthe Committed by Alex Deucher

drm/amd/display: implement lttpr logic

1-If at least one repeater is present in the link and we are in non
transparent mode, perform clock recovery then channel equalization
with all repeaters one by one before training DPRX.

2-Mark the end of LT with a repeater by setting training pattern 0
at the end of channel equalization with each repeater.
Signed-off-by: default avatarabdoulaye berthe <abdoulaye.berthe@amd.com>
Reviewed-by: default avatarAric Cyr <Aric.Cyr@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent bad7ab0b
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
link->ctx->logger link->ctx->logger
#define DP_REPEATER_CONFIGURATION_AND_STATUS_OFFSET 0x50 #define DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE 0x50
/* maximum pre emphasis level allowed for each voltage swing level*/ /* maximum pre emphasis level allowed for each voltage swing level*/
static const enum dc_pre_emphasis voltage_swing_to_pre_emphasis[] = { static const enum dc_pre_emphasis voltage_swing_to_pre_emphasis[] = {
...@@ -224,19 +224,31 @@ static enum dpcd_training_patterns ...@@ -224,19 +224,31 @@ static enum dpcd_training_patterns
return dpcd_tr_pattern; return dpcd_tr_pattern;
} }
static inline bool is_repeater(struct dc_link *link, uint32_t offset)
{
return (!link->is_lttpr_mode_transparent && offset != 0);
}
static void dpcd_set_lt_pattern_and_lane_settings( static void dpcd_set_lt_pattern_and_lane_settings(
struct dc_link *link, struct dc_link *link,
const struct link_training_settings *lt_settings, const struct link_training_settings *lt_settings,
enum dc_dp_training_pattern pattern) enum dc_dp_training_pattern pattern,
uint32_t offset)
{ {
union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = { { {0} } }; union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = { { {0} } };
const uint32_t dpcd_base_lt_offset =
DP_TRAINING_PATTERN_SET; uint32_t dpcd_base_lt_offset;
uint8_t dpcd_lt_buffer[5] = {0}; uint8_t dpcd_lt_buffer[5] = {0};
union dpcd_training_pattern dpcd_pattern = { {0} }; union dpcd_training_pattern dpcd_pattern = { {0} };
uint32_t lane; uint32_t lane;
uint32_t size_in_bytes; uint32_t size_in_bytes;
bool edp_workaround = false; /* TODO link_prop.INTERNAL */ bool edp_workaround = false; /* TODO link_prop.INTERNAL */
dpcd_base_lt_offset = DP_TRAINING_PATTERN_SET;
if (is_repeater(link, offset))
dpcd_base_lt_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 +
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
/***************************************************************** /*****************************************************************
* DpcdAddress_TrainingPatternSet * DpcdAddress_TrainingPatternSet
...@@ -244,12 +256,12 @@ static void dpcd_set_lt_pattern_and_lane_settings( ...@@ -244,12 +256,12 @@ static void dpcd_set_lt_pattern_and_lane_settings(
dpcd_pattern.v1_4.TRAINING_PATTERN_SET = dpcd_pattern.v1_4.TRAINING_PATTERN_SET =
dc_dp_training_pattern_to_dpcd_training_pattern(link, pattern); dc_dp_training_pattern_to_dpcd_training_pattern(link, pattern);
dpcd_lt_buffer[DP_TRAINING_PATTERN_SET - dpcd_base_lt_offset] dpcd_lt_buffer[DP_TRAINING_PATTERN_SET - DP_TRAINING_PATTERN_SET]
= dpcd_pattern.raw; = dpcd_pattern.raw;
DC_LOG_HW_LINK_TRAINING("%s\n %x pattern = %x\n", DC_LOG_HW_LINK_TRAINING("%s\n 0x%X pattern = %x\n",
__func__, __func__,
DP_TRAINING_PATTERN_SET, dpcd_base_lt_offset,
dpcd_pattern.v1_4.TRAINING_PATTERN_SET); dpcd_pattern.v1_4.TRAINING_PATTERN_SET);
/***************************************************************** /*****************************************************************
...@@ -271,19 +283,19 @@ static void dpcd_set_lt_pattern_and_lane_settings( ...@@ -271,19 +283,19 @@ static void dpcd_set_lt_pattern_and_lane_settings(
PRE_EMPHASIS_MAX_LEVEL ? 1 : 0); PRE_EMPHASIS_MAX_LEVEL ? 1 : 0);
} }
/* concatinate everything into one buffer*/ /* concatenate everything into one buffer*/
size_in_bytes = lt_settings->link_settings.lane_count * sizeof(dpcd_lane[0]); size_in_bytes = lt_settings->link_settings.lane_count * sizeof(dpcd_lane[0]);
// 0x00103 - 0x00102 // 0x00103 - 0x00102
memmove( memmove(
&dpcd_lt_buffer[DP_TRAINING_LANE0_SET - dpcd_base_lt_offset], &dpcd_lt_buffer[DP_TRAINING_LANE0_SET - DP_TRAINING_PATTERN_SET],
dpcd_lane, dpcd_lane,
size_in_bytes); size_in_bytes);
DC_LOG_HW_LINK_TRAINING("%s:\n %x VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n", DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n",
__func__, __func__,
DP_TRAINING_LANE0_SET, dpcd_base_lt_offset,
dpcd_lane[0].bits.VOLTAGE_SWING_SET, dpcd_lane[0].bits.VOLTAGE_SWING_SET,
dpcd_lane[0].bits.PRE_EMPHASIS_SET, dpcd_lane[0].bits.PRE_EMPHASIS_SET,
dpcd_lane[0].bits.MAX_SWING_REACHED, dpcd_lane[0].bits.MAX_SWING_REACHED,
...@@ -498,8 +510,12 @@ static void get_lane_status_and_drive_settings( ...@@ -498,8 +510,12 @@ static void get_lane_status_and_drive_settings(
const struct link_training_settings *link_training_setting, const struct link_training_settings *link_training_setting,
union lane_status *ln_status, union lane_status *ln_status,
union lane_align_status_updated *ln_status_updated, union lane_align_status_updated *ln_status_updated,
struct link_training_settings *req_settings) struct link_training_settings *req_settings,
uint32_t offset)
{ {
unsigned int lane01_status_address = DP_LANE0_1_STATUS;
uint8_t lane_adjust_offset = 4;
unsigned int lane01_adjust_address;
uint8_t dpcd_buf[6] = {0}; uint8_t dpcd_buf[6] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = { { {0} } }; union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = { { {0} } };
struct link_training_settings request_settings = { {0} }; struct link_training_settings request_settings = { {0} };
...@@ -507,9 +523,16 @@ static void get_lane_status_and_drive_settings( ...@@ -507,9 +523,16 @@ static void get_lane_status_and_drive_settings(
memset(req_settings, '\0', sizeof(struct link_training_settings)); memset(req_settings, '\0', sizeof(struct link_training_settings));
if (is_repeater(link, offset)) {
lane01_status_address =
DP_LANE0_1_STATUS_PHY_REPEATER1 +
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
lane_adjust_offset = 3;
}
core_link_read_dpcd( core_link_read_dpcd(
link, link,
DP_LANE0_1_STATUS, lane01_status_address,
(uint8_t *)(dpcd_buf), (uint8_t *)(dpcd_buf),
sizeof(dpcd_buf)); sizeof(dpcd_buf));
...@@ -520,22 +543,28 @@ static void get_lane_status_and_drive_settings( ...@@ -520,22 +543,28 @@ static void get_lane_status_and_drive_settings(
ln_status[lane].raw = ln_status[lane].raw =
get_nibble_at_index(&dpcd_buf[0], lane); get_nibble_at_index(&dpcd_buf[0], lane);
dpcd_lane_adjust[lane].raw = dpcd_lane_adjust[lane].raw =
get_nibble_at_index(&dpcd_buf[4], lane); get_nibble_at_index(&dpcd_buf[lane_adjust_offset], lane);
} }
ln_status_updated->raw = dpcd_buf[2]; ln_status_updated->raw = dpcd_buf[2];
DC_LOG_HW_LINK_TRAINING("%s:\n%x Lane01Status = %x\n %x Lane23Status = %x\n ", DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X Lane01Status = %x\n 0x%X Lane23Status = %x\n ",
__func__, __func__,
DP_LANE0_1_STATUS, dpcd_buf[0], lane01_status_address, dpcd_buf[0],
DP_LANE2_3_STATUS, dpcd_buf[1]); lane01_status_address + 1, dpcd_buf[1]);
lane01_adjust_address = DP_ADJUST_REQUEST_LANE0_1;
DC_LOG_HW_LINK_TRAINING("%s:\n %x Lane01AdjustRequest = %x\n %x Lane23AdjustRequest = %x\n", if (is_repeater(link, offset))
lane01_adjust_address = DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1 +
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X Lane01AdjustRequest = %x\n 0x%X Lane23AdjustRequest = %x\n",
__func__, __func__,
DP_ADJUST_REQUEST_LANE0_1, lane01_adjust_address,
dpcd_buf[4], dpcd_buf[lane_adjust_offset],
DP_ADJUST_REQUEST_LANE2_3, lane01_adjust_address + 1,
dpcd_buf[5]); dpcd_buf[lane_adjust_offset + 1]);
/*copy to req_settings*/ /*copy to req_settings*/
request_settings.link_settings.lane_count = request_settings.link_settings.lane_count =
...@@ -574,10 +603,18 @@ static void get_lane_status_and_drive_settings( ...@@ -574,10 +603,18 @@ static void get_lane_status_and_drive_settings(
static void dpcd_set_lane_settings( static void dpcd_set_lane_settings(
struct dc_link *link, struct dc_link *link,
const struct link_training_settings *link_training_setting) const struct link_training_settings *link_training_setting,
uint32_t offset)
{ {
union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}}; union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}};
uint32_t lane; uint32_t lane;
unsigned int lane0_set_address;
lane0_set_address = DP_TRAINING_LANE0_SET;
if (is_repeater(link, offset))
lane0_set_address = DP_TRAINING_LANE0_SET_PHY_REPEATER1 +
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
for (lane = 0; lane < for (lane = 0; lane <
(uint32_t)(link_training_setting-> (uint32_t)(link_training_setting->
...@@ -600,7 +637,7 @@ static void dpcd_set_lane_settings( ...@@ -600,7 +637,7 @@ static void dpcd_set_lane_settings(
} }
core_link_write_dpcd(link, core_link_write_dpcd(link,
DP_TRAINING_LANE0_SET, lane0_set_address,
(uint8_t *)(dpcd_lane), (uint8_t *)(dpcd_lane),
link_training_setting->link_settings.lane_count); link_training_setting->link_settings.lane_count);
...@@ -623,9 +660,9 @@ static void dpcd_set_lane_settings( ...@@ -623,9 +660,9 @@ static void dpcd_set_lane_settings(
} }
*/ */
DC_LOG_HW_LINK_TRAINING("%s\n %x VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n", DC_LOG_HW_LINK_TRAINING("%s\n 0x%X VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n",
__func__, __func__,
DP_TRAINING_LANE0_SET, lane0_set_address,
dpcd_lane[0].bits.VOLTAGE_SWING_SET, dpcd_lane[0].bits.VOLTAGE_SWING_SET,
dpcd_lane[0].bits.PRE_EMPHASIS_SET, dpcd_lane[0].bits.PRE_EMPHASIS_SET,
dpcd_lane[0].bits.MAX_SWING_REACHED, dpcd_lane[0].bits.MAX_SWING_REACHED,
...@@ -650,17 +687,6 @@ static bool is_max_vs_reached( ...@@ -650,17 +687,6 @@ static bool is_max_vs_reached(
} }
void dc_link_dp_set_drive_settings(
struct dc_link *link,
struct link_training_settings *lt_settings)
{
/* program ASIC PHY settings*/
dp_set_hw_lane_settings(link, lt_settings);
/* Notify DP sink the PHY settings from source */
dpcd_set_lane_settings(link, lt_settings);
}
static bool perform_post_lt_adj_req_sequence( static bool perform_post_lt_adj_req_sequence(
struct dc_link *link, struct dc_link *link,
struct link_training_settings *lt_settings) struct link_training_settings *lt_settings)
...@@ -693,7 +719,8 @@ static bool perform_post_lt_adj_req_sequence( ...@@ -693,7 +719,8 @@ static bool perform_post_lt_adj_req_sequence(
lt_settings, lt_settings,
dpcd_lane_status, dpcd_lane_status,
&dpcd_lane_status_updated, &dpcd_lane_status_updated,
&req_settings); &req_settings,
DPRX);
if (dpcd_lane_status_updated.bits. if (dpcd_lane_status_updated.bits.
POST_LT_ADJ_REQ_IN_PROGRESS == 0) POST_LT_ADJ_REQ_IN_PROGRESS == 0)
...@@ -750,6 +777,31 @@ static bool perform_post_lt_adj_req_sequence( ...@@ -750,6 +777,31 @@ static bool perform_post_lt_adj_req_sequence(
} }
/* Only used for channel equalization */
static uint32_t translate_training_aux_read_interval(uint32_t dpcd_aux_read_interval)
{
unsigned int aux_rd_interval_us = 400;
switch (dpcd_aux_read_interval) {
case 0x01:
aux_rd_interval_us = 400;
break;
case 0x02:
aux_rd_interval_us = 4000;
break;
case 0x03:
aux_rd_interval_us = 8000;
break;
case 0x04:
aux_rd_interval_us = 16000;
break;
default:
break;
}
return aux_rd_interval_us;
}
static enum link_training_result get_cr_failure(enum dc_lane_count ln_count, static enum link_training_result get_cr_failure(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status) union lane_status *dpcd_lane_status)
{ {
...@@ -768,37 +820,55 @@ static enum link_training_result get_cr_failure(enum dc_lane_count ln_count, ...@@ -768,37 +820,55 @@ static enum link_training_result get_cr_failure(enum dc_lane_count ln_count,
static enum link_training_result perform_channel_equalization_sequence( static enum link_training_result perform_channel_equalization_sequence(
struct dc_link *link, struct dc_link *link,
struct link_training_settings *lt_settings) struct link_training_settings *lt_settings,
uint32_t offset)
{ {
struct link_training_settings req_settings; struct link_training_settings req_settings;
enum dc_dp_training_pattern tr_pattern; enum dc_dp_training_pattern tr_pattern;
uint32_t retries_ch_eq; uint32_t retries_ch_eq;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_align_status_updated dpcd_lane_status_updated = { {0} }; union lane_align_status_updated dpcd_lane_status_updated = { {0} };
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = { { {0} } }; union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = { { {0} } };
/* Note: also check that TPS4 is a supported feature*/
tr_pattern = lt_settings->pattern_for_eq; tr_pattern = lt_settings->pattern_for_eq;
dp_set_hw_training_pattern(link, tr_pattern); if (is_repeater(link, offset))
tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4;
dp_set_hw_training_pattern(link, tr_pattern, offset);
for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT; for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT;
retries_ch_eq++) { retries_ch_eq++) {
dp_set_hw_lane_settings(link, lt_settings); dp_set_hw_lane_settings(link, lt_settings, offset);
/* 2. update DPCD*/ /* 2. update DPCD*/
if (!retries_ch_eq) if (!retries_ch_eq)
/* EPR #361076 - write as a 5-byte burst, /* EPR #361076 - write as a 5-byte burst,
* but only for the 1-st iteration*/ * but only for the 1-st iteration
*/
dpcd_set_lt_pattern_and_lane_settings( dpcd_set_lt_pattern_and_lane_settings(
link, link,
lt_settings, lt_settings,
tr_pattern); tr_pattern, offset);
else else
dpcd_set_lane_settings(link, lt_settings); dpcd_set_lane_settings(link, lt_settings, offset);
/* 3. wait for receiver to lock-on*/ /* 3. wait for receiver to lock-on*/
wait_for_training_aux_rd_interval(link, lt_settings->eq_pattern_time); wait_time_microsec = lt_settings->eq_pattern_time;
if (!link->is_lttpr_mode_transparent)
wait_time_microsec =
translate_training_aux_read_interval(
link->dpcd_caps.lttpr_caps.aux_rd_interval[offset]);
wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested /* 4. Read lane status and requested
* drive settings as set by the sink*/ * drive settings as set by the sink*/
...@@ -808,7 +878,8 @@ static enum link_training_result perform_channel_equalization_sequence( ...@@ -808,7 +878,8 @@ static enum link_training_result perform_channel_equalization_sequence(
lt_settings, lt_settings,
dpcd_lane_status, dpcd_lane_status,
&dpcd_lane_status_updated, &dpcd_lane_status_updated,
&req_settings); &req_settings,
offset);
/* 5. check CR done*/ /* 5. check CR done*/
if (!is_cr_done(lane_count, dpcd_lane_status)) if (!is_cr_done(lane_count, dpcd_lane_status))
...@@ -827,13 +898,16 @@ static enum link_training_result perform_channel_equalization_sequence( ...@@ -827,13 +898,16 @@ static enum link_training_result perform_channel_equalization_sequence(
return LINK_TRAINING_EQ_FAIL_EQ; return LINK_TRAINING_EQ_FAIL_EQ;
} }
#define TRAINING_AUX_RD_INTERVAL 100 //us
static enum link_training_result perform_clock_recovery_sequence( static enum link_training_result perform_clock_recovery_sequence(
struct dc_link *link, struct dc_link *link,
struct link_training_settings *lt_settings) struct link_training_settings *lt_settings,
uint32_t offset)
{ {
uint32_t retries_cr; uint32_t retries_cr;
uint32_t retry_count; uint32_t retry_count;
uint32_t wait_time_microsec;
struct link_training_settings req_settings; struct link_training_settings req_settings;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
enum dc_dp_training_pattern tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_1; enum dc_dp_training_pattern tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_1;
...@@ -843,7 +917,7 @@ static enum link_training_result perform_clock_recovery_sequence( ...@@ -843,7 +917,7 @@ static enum link_training_result perform_clock_recovery_sequence(
retries_cr = 0; retries_cr = 0;
retry_count = 0; retry_count = 0;
dp_set_hw_training_pattern(link, tr_pattern); dp_set_hw_training_pattern(link, tr_pattern, offset);
/* najeeb - The synaptics MST hub can put the LT in /* najeeb - The synaptics MST hub can put the LT in
* infinite loop by switching the VS * infinite loop by switching the VS
...@@ -860,7 +934,8 @@ static enum link_training_result perform_clock_recovery_sequence( ...@@ -860,7 +934,8 @@ static enum link_training_result perform_clock_recovery_sequence(
/* 1. call HWSS to set lane settings*/ /* 1. call HWSS to set lane settings*/
dp_set_hw_lane_settings( dp_set_hw_lane_settings(
link, link,
lt_settings); lt_settings,
offset);
/* 2. update DPCD of the receiver*/ /* 2. update DPCD of the receiver*/
if (!retries_cr) if (!retries_cr)
...@@ -869,16 +944,23 @@ static enum link_training_result perform_clock_recovery_sequence( ...@@ -869,16 +944,23 @@ static enum link_training_result perform_clock_recovery_sequence(
dpcd_set_lt_pattern_and_lane_settings( dpcd_set_lt_pattern_and_lane_settings(
link, link,
lt_settings, lt_settings,
tr_pattern); tr_pattern,
offset);
else else
dpcd_set_lane_settings( dpcd_set_lane_settings(
link, link,
lt_settings); lt_settings,
offset);
/* 3. wait receiver to lock-on*/ /* 3. wait receiver to lock-on*/
wait_time_microsec = lt_settings->cr_pattern_time;
if (!link->is_lttpr_mode_transparent)
wait_time_microsec = TRAINING_AUX_RD_INTERVAL;
wait_for_training_aux_rd_interval( wait_for_training_aux_rd_interval(
link, link,
lt_settings->cr_pattern_time); wait_time_microsec);
/* 4. Read lane status and requested drive /* 4. Read lane status and requested drive
* settings as set by the sink * settings as set by the sink
...@@ -888,7 +970,8 @@ static enum link_training_result perform_clock_recovery_sequence( ...@@ -888,7 +970,8 @@ static enum link_training_result perform_clock_recovery_sequence(
lt_settings, lt_settings,
dpcd_lane_status, dpcd_lane_status,
&dpcd_lane_status_updated, &dpcd_lane_status_updated,
&req_settings); &req_settings,
offset);
/* 5. check CR done*/ /* 5. check CR done*/
if (is_cr_done(lane_count, dpcd_lane_status)) if (is_cr_done(lane_count, dpcd_lane_status))
...@@ -1057,10 +1140,38 @@ static void initialize_training_settings( ...@@ -1057,10 +1140,38 @@ static void initialize_training_settings(
lt_settings->enhanced_framing = 1; lt_settings->enhanced_framing = 1;
} }
static uint8_t convert_to_count(uint8_t lttpr_repeater_count)
{
switch (lttpr_repeater_count) {
case 0x80: // 1 lttpr repeater
return 1;
case 0x40: // 2 lttpr repeaters
return 2;
case 0x20: // 3 lttpr repeaters
return 3;
case 0x10: // 4 lttpr repeaters
return 4;
case 0x08: // 5 lttpr repeaters
return 5;
case 0x04: // 6 lttpr repeaters
return 6;
case 0x02: // 7 lttpr repeaters
return 7;
case 0x01: // 8 lttpr repeaters
return 8;
default:
break;
}
return 0; // invalid value
}
static void configure_lttpr_mode(struct dc_link *link) static void configure_lttpr_mode(struct dc_link *link)
{ {
/* aux timeout is already set to extended */ /* aux timeout is already set to extended */
/* RESET/SET lttpr mode to enable non transparent mode */ /* RESET/SET lttpr mode to enable non transparent mode */
uint8_t repeater_cnt;
uint32_t aux_interval_address;
uint8_t repeater_id;
enum lttpr_mode repeater_mode = phy_repeater_mode_transparent; enum lttpr_mode repeater_mode = phy_repeater_mode_transparent;
core_link_write_dpcd(link, core_link_write_dpcd(link,
...@@ -1074,9 +1185,43 @@ static void configure_lttpr_mode(struct dc_link *link) ...@@ -1074,9 +1185,43 @@ static void configure_lttpr_mode(struct dc_link *link)
DP_PHY_REPEATER_MODE, DP_PHY_REPEATER_MODE,
(uint8_t *)&repeater_mode, (uint8_t *)&repeater_mode,
sizeof(repeater_mode)); sizeof(repeater_mode));
repeater_cnt = convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
for (repeater_id = repeater_cnt; repeater_id > 0; repeater_id--) {
aux_interval_address = DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 +
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (repeater_id - 1));
core_link_read_dpcd(
link,
aux_interval_address,
(uint8_t *)&link->dpcd_caps.lttpr_caps.aux_rd_interval[repeater_id - 1],
sizeof(link->dpcd_caps.lttpr_caps.aux_rd_interval[repeater_id - 1]));
link->dpcd_caps.lttpr_caps.aux_rd_interval[repeater_id - 1] &= 0x7F;
}
} }
} }
static void repeater_training_done(struct dc_link *link, uint32_t offset)
{
union dpcd_training_pattern dpcd_pattern = { {0} };
const uint32_t dpcd_base_lt_offset =
DP_TRAINING_PATTERN_SET_PHY_REPEATER1 +
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
/* Set training not in progress*/
dpcd_pattern.v1_4.TRAINING_PATTERN_SET = DPCD_TRAINING_PATTERN_VIDEOIDLE;
core_link_write_dpcd(
link,
dpcd_base_lt_offset,
&dpcd_pattern.raw,
1);
DC_LOG_HW_LINK_TRAINING("%s\n 0x%X pattern = %x\n",
__func__,
dpcd_base_lt_offset,
dpcd_pattern.v1_4.TRAINING_PATTERN_SET);
}
static void print_status_message( static void print_status_message(
struct dc_link *link, struct dc_link *link,
const struct link_training_settings *lt_settings, const struct link_training_settings *lt_settings,
...@@ -1156,6 +1301,17 @@ static void print_status_message( ...@@ -1156,6 +1301,17 @@ static void print_status_message(
lt_spread); lt_spread);
} }
void dc_link_dp_set_drive_settings(
struct dc_link *link,
struct link_training_settings *lt_settings)
{
/* program ASIC PHY settings*/
dp_set_hw_lane_settings(link, lt_settings, DPRX);
/* Notify DP sink the PHY settings from source */
dpcd_set_lane_settings(link, lt_settings, DPRX);
}
bool dc_link_dp_perform_link_training_skip_aux( bool dc_link_dp_perform_link_training_skip_aux(
struct dc_link *link, struct dc_link *link,
const struct dc_link_settings *link_setting) const struct dc_link_settings *link_setting)
...@@ -1172,10 +1328,10 @@ bool dc_link_dp_perform_link_training_skip_aux( ...@@ -1172,10 +1328,10 @@ bool dc_link_dp_perform_link_training_skip_aux(
/* 1. Perform_clock_recovery_sequence. */ /* 1. Perform_clock_recovery_sequence. */
/* transmit training pattern for clock recovery */ /* transmit training pattern for clock recovery */
dp_set_hw_training_pattern(link, pattern_for_cr); dp_set_hw_training_pattern(link, pattern_for_cr, DPRX);
/* call HWSS to set lane settings*/ /* call HWSS to set lane settings*/
dp_set_hw_lane_settings(link, &lt_settings); dp_set_hw_lane_settings(link, &lt_settings, DPRX);
/* wait receiver to lock-on*/ /* wait receiver to lock-on*/
wait_for_training_aux_rd_interval(link, lt_settings.cr_pattern_time); wait_for_training_aux_rd_interval(link, lt_settings.cr_pattern_time);
...@@ -1183,10 +1339,10 @@ bool dc_link_dp_perform_link_training_skip_aux( ...@@ -1183,10 +1339,10 @@ bool dc_link_dp_perform_link_training_skip_aux(
/* 2. Perform_channel_equalization_sequence. */ /* 2. Perform_channel_equalization_sequence. */
/* transmit training pattern for channel equalization. */ /* transmit training pattern for channel equalization. */
dp_set_hw_training_pattern(link, lt_settings.pattern_for_eq); dp_set_hw_training_pattern(link, lt_settings.pattern_for_eq, DPRX);
/* call HWSS to set lane settings*/ /* call HWSS to set lane settings*/
dp_set_hw_lane_settings(link, &lt_settings); dp_set_hw_lane_settings(link, &lt_settings, DPRX);
/* wait receiver to lock-on. */ /* wait receiver to lock-on. */
wait_for_training_aux_rd_interval(link, lt_settings.eq_pattern_time); wait_for_training_aux_rd_interval(link, lt_settings.eq_pattern_time);
...@@ -1208,9 +1364,12 @@ enum link_training_result dc_link_dp_perform_link_training( ...@@ -1208,9 +1364,12 @@ enum link_training_result dc_link_dp_perform_link_training(
{ {
enum link_training_result status = LINK_TRAINING_SUCCESS; enum link_training_result status = LINK_TRAINING_SUCCESS;
struct link_training_settings lt_settings; struct link_training_settings lt_settings;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
bool fec_enable; bool fec_enable;
#endif #endif
uint8_t repeater_cnt;
uint8_t repeater_id;
initialize_training_settings( initialize_training_settings(
link, link,
...@@ -1230,17 +1389,40 @@ enum link_training_result dc_link_dp_perform_link_training( ...@@ -1230,17 +1389,40 @@ enum link_training_result dc_link_dp_perform_link_training(
dp_set_fec_ready(link, fec_enable); dp_set_fec_ready(link, fec_enable);
#endif #endif
if (!link->is_lttpr_mode_transparent) {
/* Configure lttpr mode */ /* Configure lttpr mode */
if (!link->is_lttpr_mode_transparent)
configure_lttpr_mode(link); configure_lttpr_mode(link);
/* 2. perform link training (set link training done /* 2. perform link training (set link training done
* to false is done as well) * to false is done as well)
*/ */
status = perform_clock_recovery_sequence(link, &lt_settings); repeater_cnt = convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
for (repeater_id = repeater_cnt; (repeater_id > 0 && status == LINK_TRAINING_SUCCESS);
repeater_id--) {
status = perform_clock_recovery_sequence(link, &lt_settings, repeater_id);
if (status != LINK_TRAINING_SUCCESS)
break;
status = perform_channel_equalization_sequence(link,
&lt_settings,
repeater_id);
if (status != LINK_TRAINING_SUCCESS)
break;
repeater_training_done(link, repeater_id);
}
}
if (status == LINK_TRAINING_SUCCESS) {
status = perform_clock_recovery_sequence(link, &lt_settings, DPRX);
if (status == LINK_TRAINING_SUCCESS) { if (status == LINK_TRAINING_SUCCESS) {
status = perform_channel_equalization_sequence(link, status = perform_channel_equalization_sequence(link,
&lt_settings); &lt_settings,
DPRX);
}
} }
if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) { if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) {
...@@ -1393,10 +1575,11 @@ enum link_training_result dc_link_dp_sync_lt_attempt( ...@@ -1393,10 +1575,11 @@ enum link_training_result dc_link_dp_sync_lt_attempt(
/* 2. perform link training (set link training done /* 2. perform link training (set link training done
* to false is done as well) * to false is done as well)
*/ */
lt_status = perform_clock_recovery_sequence(link, &lt_settings); lt_status = perform_clock_recovery_sequence(link, &lt_settings, DPRX);
if (lt_status == LINK_TRAINING_SUCCESS) { if (lt_status == LINK_TRAINING_SUCCESS) {
lt_status = perform_channel_equalization_sequence(link, lt_status = perform_channel_equalization_sequence(link,
&lt_settings); &lt_settings,
DPRX);
} }
/* 3. Sync LT must skip TRAINING_PATTERN_SET:0 (video pattern)*/ /* 3. Sync LT must skip TRAINING_PATTERN_SET:0 (video pattern)*/
...@@ -3355,8 +3538,8 @@ bool dc_link_dp_set_test_pattern( ...@@ -3355,8 +3538,8 @@ bool dc_link_dp_set_test_pattern(
if (is_dp_phy_pattern(test_pattern)) { if (is_dp_phy_pattern(test_pattern)) {
/* Set DPCD Lane Settings before running test pattern */ /* Set DPCD Lane Settings before running test pattern */
if (p_link_settings != NULL) { if (p_link_settings != NULL) {
dp_set_hw_lane_settings(link, p_link_settings); dp_set_hw_lane_settings(link, p_link_settings, DPRX);
dpcd_set_lane_settings(link, p_link_settings); dpcd_set_lane_settings(link, p_link_settings, DPRX);
} }
/* Blank stream if running test pattern */ /* Blank stream if running test pattern */
......
...@@ -19,6 +19,36 @@ ...@@ -19,6 +19,36 @@
#include "resource.h" #include "resource.h"
#endif #endif
static uint8_t convert_to_count(uint8_t lttpr_repeater_count)
{
switch (lttpr_repeater_count) {
case 0x80: // 1 lttpr repeater
return 1;
case 0x40: // 2 lttpr repeaters
return 2;
case 0x20: // 3 lttpr repeaters
return 3;
case 0x10: // 4 lttpr repeaters
return 4;
case 0x08: // 5 lttpr repeaters
return 5;
case 0x04: // 6 lttpr repeaters
return 6;
case 0x02: // 7 lttpr repeaters
return 7;
case 0x01: // 8 lttpr repeaters
return 8;
default:
break;
}
return 0; // invalid value
}
static inline bool is_immediate_downstream(struct dc_link *link, uint32_t offset)
{
return (convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt) == offset);
}
enum dc_status core_link_read_dpcd( enum dc_status core_link_read_dpcd(
struct dc_link *link, struct dc_link *link,
uint32_t address, uint32_t address,
...@@ -212,7 +242,8 @@ void dp_disable_link_phy_mst(struct dc_link *link, enum signal_type signal) ...@@ -212,7 +242,8 @@ void dp_disable_link_phy_mst(struct dc_link *link, enum signal_type signal)
bool dp_set_hw_training_pattern( bool dp_set_hw_training_pattern(
struct dc_link *link, struct dc_link *link,
enum dc_dp_training_pattern pattern) enum dc_dp_training_pattern pattern,
uint32_t offset)
{ {
enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED; enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
...@@ -240,10 +271,14 @@ bool dp_set_hw_training_pattern( ...@@ -240,10 +271,14 @@ bool dp_set_hw_training_pattern(
void dp_set_hw_lane_settings( void dp_set_hw_lane_settings(
struct dc_link *link, struct dc_link *link,
const struct link_training_settings *link_settings) const struct link_training_settings *link_settings,
uint32_t offset)
{ {
struct link_encoder *encoder = link->link_enc; struct link_encoder *encoder = link->link_enc;
if (!link->is_lttpr_mode_transparent && !is_immediate_downstream(link, offset))
return;
/* call Encoder to set lane settings */ /* call Encoder to set lane settings */
encoder->funcs->dp_set_lane_settings(encoder, link_settings); encoder->funcs->dp_set_lane_settings(encoder, link_settings);
} }
......
...@@ -60,11 +60,13 @@ void dp_disable_link_phy_mst(struct dc_link *link, enum signal_type signal); ...@@ -60,11 +60,13 @@ void dp_disable_link_phy_mst(struct dc_link *link, enum signal_type signal);
bool dp_set_hw_training_pattern( bool dp_set_hw_training_pattern(
struct dc_link *link, struct dc_link *link,
enum dc_dp_training_pattern pattern); enum dc_dp_training_pattern pattern,
uint32_t offset);
void dp_set_hw_lane_settings( void dp_set_hw_lane_settings(
struct dc_link *link, struct dc_link *link,
const struct link_training_settings *link_settings); const struct link_training_settings *link_settings,
uint32_t offset);
void dp_set_hw_test_pattern( void dp_set_hw_test_pattern(
struct dc_link *link, struct dc_link *link,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment