summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c')
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c862
1 files changed, 539 insertions, 323 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 3ff3d9e90983..b8832bdde2bc 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
@@ -14,6 +14,7 @@
#include "dpcd_defs.h"
#include "dc_dmub_srv.h"
#include "dce/dmub_hw_lock_mgr.h"
+#include "inc/link_enc_cfg.h"
/*Travis*/
static const uint8_t DP_VGA_LVDS_CONVERTER_ID_2[] = "sivarT";
@@ -24,7 +25,7 @@ static const uint8_t DP_VGA_LVDS_CONVERTER_ID_3[] = "dnomlA";
link->ctx->logger
#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
-#define DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE 0x50
+#include "link_dpcd.h"
/* maximum pre emphasis level allowed for each voltage swing level*/
static const enum dc_pre_emphasis
@@ -38,14 +39,6 @@ enum {
POST_LT_ADJ_REQ_TIMEOUT = 200
};
-enum {
- LINK_TRAINING_MAX_RETRY_COUNT = 5,
- /* to avoid infinite loop where-in the receiver
- * switches between different VS
- */
- LINK_TRAINING_MAX_CR_RETRY = 100
-};
-
static bool decide_fallback_link_setting(
struct dc_link_settings initial_link_settings,
struct dc_link_settings *current_link_setting,
@@ -96,7 +89,7 @@ static uint32_t get_eq_training_aux_rd_interval(
return wait_in_micro_secs;
}
-static void wait_for_training_aux_rd_interval(
+void dp_wait_for_training_aux_rd_interval(
struct dc_link *link,
uint32_t wait_in_micro_secs)
{
@@ -107,10 +100,50 @@ static void wait_for_training_aux_rd_interval(
wait_in_micro_secs);
}
+enum dpcd_training_patterns
+ dc_dp_training_pattern_to_dpcd_training_pattern(
+ struct dc_link *link,
+ enum dc_dp_training_pattern pattern)
+{
+ enum dpcd_training_patterns dpcd_tr_pattern =
+ DPCD_TRAINING_PATTERN_VIDEOIDLE;
+
+ switch (pattern) {
+ case DP_TRAINING_PATTERN_SEQUENCE_1:
+ dpcd_tr_pattern = DPCD_TRAINING_PATTERN_1;
+ break;
+ case DP_TRAINING_PATTERN_SEQUENCE_2:
+ dpcd_tr_pattern = DPCD_TRAINING_PATTERN_2;
+ break;
+ case DP_TRAINING_PATTERN_SEQUENCE_3:
+ dpcd_tr_pattern = DPCD_TRAINING_PATTERN_3;
+ break;
+ case DP_TRAINING_PATTERN_SEQUENCE_4:
+ dpcd_tr_pattern = DPCD_TRAINING_PATTERN_4;
+ break;
+ case DP_TRAINING_PATTERN_VIDEOIDLE:
+ dpcd_tr_pattern = DPCD_TRAINING_PATTERN_VIDEOIDLE;
+ break;
+ default:
+ ASSERT(0);
+ DC_LOG_HW_LINK_TRAINING("%s: Invalid HW Training pattern: %d\n",
+ __func__, pattern);
+ break;
+ }
+
+ return dpcd_tr_pattern;
+}
+
static void dpcd_set_training_pattern(
struct dc_link *link,
- union dpcd_training_pattern dpcd_pattern)
+ enum dc_dp_training_pattern training_pattern)
{
+ union dpcd_training_pattern dpcd_pattern = { {0} };
+
+ dpcd_pattern.v1_4.TRAINING_PATTERN_SET =
+ dc_dp_training_pattern_to_dpcd_training_pattern(
+ link, training_pattern);
+
core_link_write_dpcd(
link,
DP_TRAINING_PATTERN_SET,
@@ -132,10 +165,22 @@ static enum dc_dp_training_pattern decide_cr_training_pattern(
static enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *link,
const struct dc_link_settings *link_settings)
{
+ struct link_encoder *link_enc;
enum dc_dp_training_pattern highest_tp = DP_TRAINING_PATTERN_SEQUENCE_2;
- struct encoder_feature_support *features = &link->link_enc->features;
+ struct encoder_feature_support *features;
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
+ /* Access link encoder capability based on whether it is statically
+ * or dynamically assigned to a link.
+ */
+ if (link->is_dig_mapping_flexible &&
+ link->dc->res_pool->funcs->link_encs_assign)
+ link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link);
+ else
+ link_enc = link->link_enc;
+ ASSERT(link_enc);
+ features = &link_enc->features;
+
if (features->flags.bits.IS_TPS3_CAPABLE)
highest_tp = DP_TRAINING_PATTERN_SEQUENCE_3;
@@ -153,11 +198,12 @@ static enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *li
return DP_TRAINING_PATTERN_SEQUENCE_2;
}
-static void dpcd_set_link_settings(
+enum dc_status dpcd_set_link_settings(
struct dc_link *link,
const struct link_training_settings *lt_settings)
{
uint8_t rate;
+ enum dc_status status;
union down_spread_ctrl downspread = { {0} };
union lane_count_set lane_count_set = { {0} };
@@ -172,15 +218,16 @@ static void dpcd_set_link_settings(
lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0;
- if (lt_settings->pattern_for_eq < DP_TRAINING_PATTERN_SEQUENCE_4) {
+ if (link->ep_type == DISPLAY_ENDPOINT_PHY &&
+ lt_settings->pattern_for_eq < DP_TRAINING_PATTERN_SEQUENCE_4) {
lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED =
link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED;
}
- core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL,
+ status = core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL,
&downspread.raw, sizeof(downspread));
- core_link_write_dpcd(link, DP_LANE_COUNT_SET,
+ status = core_link_write_dpcd(link, DP_LANE_COUNT_SET,
&lane_count_set.raw, 1);
if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 &&
@@ -196,12 +243,12 @@ static void dpcd_set_link_settings(
core_link_read_dpcd(link, DP_SUPPORTED_LINK_RATES,
supported_link_rates, sizeof(supported_link_rates));
}
- core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
- core_link_write_dpcd(link, DP_LINK_RATE_SET,
+ status = core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
+ status = core_link_write_dpcd(link, DP_LINK_RATE_SET,
&lt_settings->link_settings.link_rate_set, 1);
} else {
rate = (uint8_t) (lt_settings->link_settings.link_rate);
- core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
+ status = core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
}
if (rate) {
@@ -225,40 +272,11 @@ static void dpcd_set_link_settings(
DP_DOWNSPREAD_CTRL,
lt_settings->link_settings.link_spread);
}
-}
-static enum dpcd_training_patterns
- dc_dp_training_pattern_to_dpcd_training_pattern(
- struct dc_link *link,
- enum dc_dp_training_pattern pattern)
-{
- enum dpcd_training_patterns dpcd_tr_pattern =
- DPCD_TRAINING_PATTERN_VIDEOIDLE;
-
- switch (pattern) {
- case DP_TRAINING_PATTERN_SEQUENCE_1:
- dpcd_tr_pattern = DPCD_TRAINING_PATTERN_1;
- break;
- case DP_TRAINING_PATTERN_SEQUENCE_2:
- dpcd_tr_pattern = DPCD_TRAINING_PATTERN_2;
- break;
- case DP_TRAINING_PATTERN_SEQUENCE_3:
- dpcd_tr_pattern = DPCD_TRAINING_PATTERN_3;
- break;
- case DP_TRAINING_PATTERN_SEQUENCE_4:
- dpcd_tr_pattern = DPCD_TRAINING_PATTERN_4;
- break;
- default:
- ASSERT(0);
- DC_LOG_HW_LINK_TRAINING("%s: Invalid HW Training pattern: %d\n",
- __func__, pattern);
- break;
- }
-
- return dpcd_tr_pattern;
+ return status;
}
-static uint8_t dc_dp_initialize_scrambling_data_symbols(
+uint8_t dc_dp_initialize_scrambling_data_symbols(
struct dc_link *link,
enum dc_dp_training_pattern pattern)
{
@@ -407,7 +425,7 @@ static void dpcd_set_lt_pattern_and_lane_settings(
link->cur_lane_setting = lt_settings->lane_settings[0];
}
-static bool is_cr_done(enum dc_lane_count ln_count,
+bool dp_is_cr_done(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status)
{
uint32_t lane;
@@ -419,24 +437,34 @@ static bool is_cr_done(enum dc_lane_count ln_count,
return true;
}
-static bool is_ch_eq_done(enum dc_lane_count ln_count,
- union lane_status *dpcd_lane_status,
- union lane_align_status_updated *lane_status_updated)
+bool dp_is_ch_eq_done(enum dc_lane_count ln_count,
+ union lane_status *dpcd_lane_status)
{
+ bool done = true;
uint32_t lane;
- if (!lane_status_updated->bits.INTERLANE_ALIGN_DONE)
- return false;
- else {
- for (lane = 0; lane < (uint32_t)(ln_count); lane++) {
- if (!dpcd_lane_status[lane].bits.SYMBOL_LOCKED_0 ||
- !dpcd_lane_status[lane].bits.CHANNEL_EQ_DONE_0)
- return false;
- }
- }
- return true;
+ for (lane = 0; lane < (uint32_t)(ln_count); lane++)
+ if (!dpcd_lane_status[lane].bits.CHANNEL_EQ_DONE_0)
+ done = false;
+ return done;
}
-static void update_drive_settings(
+bool dp_is_symbol_locked(enum dc_lane_count ln_count,
+ union lane_status *dpcd_lane_status)
+{
+ bool locked = true;
+ uint32_t lane;
+ for (lane = 0; lane < (uint32_t)(ln_count); lane++)
+ if (!dpcd_lane_status[lane].bits.SYMBOL_LOCKED_0)
+ locked = false;
+ return locked;
+}
+
+bool dp_is_interlane_aligned(union lane_align_status_updated align_status)
+{
+ return align_status.bits.INTERLANE_ALIGN_DONE == 1;
+}
+
+void dp_update_drive_settings(
struct link_training_settings *dest,
struct link_training_settings src)
{
@@ -580,7 +608,7 @@ static void find_max_drive_settings(
}
-static void get_lane_status_and_drive_settings(
+enum dc_status dp_get_lane_status_and_drive_settings(
struct dc_link *link,
const struct link_training_settings *link_training_setting,
union lane_status *ln_status,
@@ -595,6 +623,7 @@ static void get_lane_status_and_drive_settings(
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = { { {0} } };
struct link_training_settings request_settings = { {0} };
uint32_t lane;
+ enum dc_status status;
memset(req_settings, '\0', sizeof(struct link_training_settings));
@@ -605,7 +634,7 @@ static void get_lane_status_and_drive_settings(
lane_adjust_offset = 3;
}
- core_link_read_dpcd(
+ status = core_link_read_dpcd(
link,
lane01_status_address,
(uint8_t *)(dpcd_buf),
@@ -693,9 +722,10 @@ static void get_lane_status_and_drive_settings(
* read DpcdAddress_AdjustRequestPostCursor2 = 0x020C
*/
+ return status;
}
-static void dpcd_set_lane_settings(
+enum dc_status dpcd_set_lane_settings(
struct dc_link *link,
const struct link_training_settings *link_training_setting,
uint32_t offset)
@@ -703,6 +733,7 @@ static void dpcd_set_lane_settings(
union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}};
uint32_t lane;
unsigned int lane0_set_address;
+ enum dc_status status;
lane0_set_address = DP_TRAINING_LANE0_SET;
@@ -730,7 +761,7 @@ static void dpcd_set_lane_settings(
PRE_EMPHASIS_MAX_LEVEL ? 1 : 0);
}
- core_link_write_dpcd(link,
+ status = core_link_write_dpcd(link,
lane0_set_address,
(uint8_t *)(dpcd_lane),
link_training_setting->link_settings.lane_count);
@@ -776,9 +807,10 @@ static void dpcd_set_lane_settings(
}
link->cur_lane_setting = link_training_setting->lane_settings[0];
+ return status;
}
-static bool is_max_vs_reached(
+bool dp_is_max_vs_reached(
const struct link_training_settings *lt_settings)
{
uint32_t lane;
@@ -820,25 +852,24 @@ static bool perform_post_lt_adj_req_sequence(
union lane_align_status_updated
dpcd_lane_status_updated;
- get_lane_status_and_drive_settings(
- link,
- lt_settings,
- dpcd_lane_status,
- &dpcd_lane_status_updated,
- &req_settings,
- DPRX);
+ dp_get_lane_status_and_drive_settings(
+ link,
+ lt_settings,
+ dpcd_lane_status,
+ &dpcd_lane_status_updated,
+ &req_settings,
+ DPRX);
if (dpcd_lane_status_updated.bits.
POST_LT_ADJ_REQ_IN_PROGRESS == 0)
return true;
- if (!is_cr_done(lane_count, dpcd_lane_status))
+ if (!dp_is_cr_done(lane_count, dpcd_lane_status))
return false;
- if (!is_ch_eq_done(
- lane_count,
- dpcd_lane_status,
- &dpcd_lane_status_updated))
+ if (!dp_is_ch_eq_done(lane_count, dpcd_lane_status) ||
+ !dp_is_symbol_locked(lane_count, dpcd_lane_status) ||
+ !dp_is_interlane_aligned(dpcd_lane_status_updated))
return false;
for (lane = 0; lane < (uint32_t)(lane_count); lane++) {
@@ -856,7 +887,7 @@ static bool perform_post_lt_adj_req_sequence(
}
if (req_drv_setting_changed) {
- update_drive_settings(
+ dp_update_drive_settings(
lt_settings, req_settings);
dc_link_dp_set_drive_settings(link,
@@ -884,7 +915,7 @@ 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)
+uint32_t dp_translate_training_aux_read_interval(uint32_t dpcd_aux_read_interval)
{
unsigned int aux_rd_interval_us = 400;
@@ -908,7 +939,7 @@ static uint32_t translate_training_aux_read_interval(uint32_t dpcd_aux_read_inte
return aux_rd_interval_us;
}
-static enum link_training_result get_cr_failure(enum dc_lane_count ln_count,
+enum link_training_result dp_get_cr_failure(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status)
{
enum link_training_result result = LINK_TRAINING_SUCCESS;
@@ -969,17 +1000,17 @@ static enum link_training_result perform_channel_equalization_sequence(
if (is_repeater(link, offset))
wait_time_microsec =
- translate_training_aux_read_interval(
+ dp_translate_training_aux_read_interval(
link->dpcd_caps.lttpr_caps.aux_rd_interval[offset - 1]);
- wait_for_training_aux_rd_interval(
+ dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested
* drive settings as set by the sink*/
- get_lane_status_and_drive_settings(
+ dp_get_lane_status_and_drive_settings(
link,
lt_settings,
dpcd_lane_status,
@@ -988,23 +1019,22 @@ static enum link_training_result perform_channel_equalization_sequence(
offset);
/* 5. check CR done*/
- if (!is_cr_done(lane_count, dpcd_lane_status))
+ if (!dp_is_cr_done(lane_count, dpcd_lane_status))
return LINK_TRAINING_EQ_FAIL_CR;
/* 6. check CHEQ done*/
- if (is_ch_eq_done(lane_count,
- dpcd_lane_status,
- &dpcd_lane_status_updated))
+ if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) &&
+ dp_is_symbol_locked(lane_count, dpcd_lane_status) &&
+ dp_is_interlane_aligned(dpcd_lane_status_updated))
return LINK_TRAINING_SUCCESS;
/* 7. update VS/PE/PC2 in lt_settings*/
- update_drive_settings(lt_settings, req_settings);
+ dp_update_drive_settings(lt_settings, req_settings);
}
return LINK_TRAINING_EQ_FAIL_EQ;
}
-#define TRAINING_AUX_RD_INTERVAL 100 //us
static void start_clock_recovery_pattern_early(struct dc_link *link,
struct link_training_settings *lt_settings,
@@ -1075,14 +1105,14 @@ static enum link_training_result perform_clock_recovery_sequence(
if (link->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT)
wait_time_microsec = TRAINING_AUX_RD_INTERVAL;
- wait_for_training_aux_rd_interval(
+ dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested drive
* settings as set by the sink
*/
- get_lane_status_and_drive_settings(
+ dp_get_lane_status_and_drive_settings(
link,
lt_settings,
dpcd_lane_status,
@@ -1091,11 +1121,11 @@ static enum link_training_result perform_clock_recovery_sequence(
offset);
/* 5. check CR done*/
- if (is_cr_done(lane_count, dpcd_lane_status))
+ if (dp_is_cr_done(lane_count, dpcd_lane_status))
return LINK_TRAINING_SUCCESS;
/* 6. max VS reached*/
- if (is_max_vs_reached(lt_settings))
+ if (dp_is_max_vs_reached(lt_settings))
break;
/* 7. same lane settings*/
@@ -1110,7 +1140,7 @@ static enum link_training_result perform_clock_recovery_sequence(
retries_cr = 0;
/* 8. update VS/PE/PC2 in lt_settings*/
- update_drive_settings(lt_settings, req_settings);
+ dp_update_drive_settings(lt_settings, req_settings);
retry_count++;
}
@@ -1123,10 +1153,10 @@ static enum link_training_result perform_clock_recovery_sequence(
}
- return get_cr_failure(lane_count, dpcd_lane_status);
+ return dp_get_cr_failure(lane_count, dpcd_lane_status);
}
-static inline enum link_training_result perform_link_training_int(
+static inline enum link_training_result dp_transition_to_video_idle(
struct dc_link *link,
struct link_training_settings *lt_settings,
enum link_training_result status)
@@ -1142,8 +1172,16 @@ static inline enum link_training_result perform_link_training_int(
* TPS4 must be used instead of POST_LT_ADJ_REQ.
*/
if (link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED != 1 ||
- lt_settings->pattern_for_eq == DP_TRAINING_PATTERN_SEQUENCE_4)
+ lt_settings->pattern_for_eq == DP_TRAINING_PATTERN_SEQUENCE_4) {
+ /* delay 5ms after Main Link output idle pattern and then check
+ * DPCD 0202h.
+ */
+ if (link->connector_signal != SIGNAL_TYPE_EDP && status == LINK_TRAINING_SUCCESS) {
+ msleep(5);
+ status = dp_check_link_loss_status(link, lt_settings);
+ }
return status;
+ }
if (status == LINK_TRAINING_SUCCESS &&
perform_post_lt_adj_req_sequence(link, lt_settings) == false)
@@ -1162,7 +1200,7 @@ static inline enum link_training_result perform_link_training_int(
return status;
}
-static enum link_training_result check_link_loss_status(
+enum link_training_result dp_check_link_loss_status(
struct dc_link *link,
const struct link_training_settings *link_training_setting)
{
@@ -1200,7 +1238,7 @@ static enum link_training_result check_link_loss_status(
return status;
}
-static void initialize_training_settings(
+static inline void decide_8b_10b_training_settings(
struct dc_link *link,
const struct dc_link_settings *link_setting,
const struct dc_link_training_overrides *overrides,
@@ -1244,6 +1282,8 @@ static void initialize_training_settings(
else
lt_settings->link_settings.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ;
+ lt_settings->lttpr_mode = link->lttpr_mode;
+
/* Initialize lane settings overrides */
if (overrides->voltage_swing != NULL)
lt_settings->voltage_swing = overrides->voltage_swing;
@@ -1294,9 +1334,25 @@ static void initialize_training_settings(
lt_settings->enhanced_framing = *overrides->enhanced_framing;
else
lt_settings->enhanced_framing = 1;
+
+ if (link->preferred_training_settings.fec_enable != NULL)
+ lt_settings->should_set_fec_ready = *link->preferred_training_settings.fec_enable;
+ else
+ lt_settings->should_set_fec_ready = true;
}
-static uint8_t convert_to_count(uint8_t lttpr_repeater_count)
+void dp_decide_training_settings(
+ struct dc_link *link,
+ const struct dc_link_settings *link_settings,
+ const struct dc_link_training_overrides *overrides,
+ struct link_training_settings *lt_settings)
+{
+ if (dp_get_link_encoding_format(link_settings) == DP_8b_10b_ENCODING)
+ decide_8b_10b_training_settings(link, link_settings, overrides, lt_settings);
+}
+
+
+uint8_t dp_convert_to_count(uint8_t lttpr_repeater_count)
{
switch (lttpr_repeater_count) {
case 0x80: // 1 lttpr repeater
@@ -1321,17 +1377,20 @@ static uint8_t convert_to_count(uint8_t lttpr_repeater_count)
return 0; // invalid value
}
-static void configure_lttpr_mode_transparent(struct dc_link *link)
+enum dc_status configure_lttpr_mode_transparent(struct dc_link *link)
{
uint8_t repeater_mode = DP_PHY_REPEATER_MODE_TRANSPARENT;
- core_link_write_dpcd(link,
+ DC_LOG_HW_LINK_TRAINING("%s\n Set LTTPR to Transparent Mode\n", __func__);
+ return core_link_write_dpcd(link,
DP_PHY_REPEATER_MODE,
(uint8_t *)&repeater_mode,
sizeof(repeater_mode));
}
-static void configure_lttpr_mode_non_transparent(struct dc_link *link)
+enum dc_status configure_lttpr_mode_non_transparent(
+ struct dc_link *link,
+ const struct link_training_settings *lt_settings)
{
/* aux timeout is already set to extended */
/* RESET/SET lttpr mode to enable non transparent mode */
@@ -1341,11 +1400,16 @@ static void configure_lttpr_mode_non_transparent(struct dc_link *link)
enum dc_status result = DC_ERROR_UNEXPECTED;
uint8_t repeater_mode = DP_PHY_REPEATER_MODE_TRANSPARENT;
- DC_LOG_HW_LINK_TRAINING("%s\n Set LTTPR to Transparent Mode\n", __func__);
- result = core_link_write_dpcd(link,
- DP_PHY_REPEATER_MODE,
- (uint8_t *)&repeater_mode,
- sizeof(repeater_mode));
+ enum dp_link_encoding encoding = dp_get_link_encoding_format(&lt_settings->link_settings);
+
+ if (encoding == DP_8b_10b_ENCODING) {
+ DC_LOG_HW_LINK_TRAINING("%s\n Set LTTPR to Transparent Mode\n", __func__);
+ result = core_link_write_dpcd(link,
+ DP_PHY_REPEATER_MODE,
+ (uint8_t *)&repeater_mode,
+ sizeof(repeater_mode));
+
+ }
if (result == DC_OK) {
link->dpcd_caps.lttpr_caps.mode = repeater_mode;
@@ -1365,18 +1429,22 @@ static void configure_lttpr_mode_non_transparent(struct dc_link *link)
link->dpcd_caps.lttpr_caps.mode = 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;
+ if (encoding == DP_8b_10b_ENCODING) {
+ repeater_cnt = dp_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;
+ }
}
}
+
+ return result;
}
static void repeater_training_done(struct dc_link *link, uint32_t offset)
@@ -1510,7 +1578,7 @@ bool dc_link_dp_perform_link_training_skip_aux(
{
struct link_training_settings lt_settings;
- initialize_training_settings(
+ dp_decide_training_settings(
link,
link_setting,
&link->preferred_training_settings,
@@ -1525,7 +1593,7 @@ bool dc_link_dp_perform_link_training_skip_aux(
dp_set_hw_lane_settings(link, &lt_settings, DPRX);
/* wait receiver to lock-on*/
- wait_for_training_aux_rd_interval(link, lt_settings.cr_pattern_time);
+ dp_wait_for_training_aux_rd_interval(link, lt_settings.cr_pattern_time);
/* 2. Perform_channel_equalization_sequence. */
@@ -1536,7 +1604,7 @@ bool dc_link_dp_perform_link_training_skip_aux(
dp_set_hw_lane_settings(link, &lt_settings, DPRX);
/* wait receiver to lock-on. */
- wait_for_training_aux_rd_interval(link, lt_settings.eq_pattern_time);
+ dp_wait_for_training_aux_rd_interval(link, lt_settings.eq_pattern_time);
/* 3. Perform_link_training_int. */
@@ -1548,60 +1616,78 @@ bool dc_link_dp_perform_link_training_skip_aux(
return true;
}
-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 dc_status dpcd_configure_lttpr_mode(struct dc_link *link, struct link_training_settings *lt_settings)
{
- enum link_training_result status = LINK_TRAINING_SUCCESS;
- struct link_training_settings lt_settings;
- union dpcd_training_pattern dpcd_pattern = { { 0 } };
+ enum dc_status status = DC_OK;
- bool fec_enable;
- uint8_t repeater_cnt;
- uint8_t repeater_id;
+ if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT)
+ status = configure_lttpr_mode_non_transparent(link, lt_settings);
+ else
+ status = configure_lttpr_mode_transparent(link);
+
+ return status;
+}
+
+static void dpcd_exit_training_mode(struct dc_link *link)
+{
+
+ /* clear training pattern set */
+ dpcd_set_training_pattern(link, DP_TRAINING_PATTERN_VIDEOIDLE);
+}
- initialize_training_settings(
+enum dc_status dpcd_configure_channel_coding(struct dc_link *link,
+ struct link_training_settings *lt_settings)
+{
+ enum dp_link_encoding encoding =
+ dp_get_link_encoding_format(
+ &lt_settings->link_settings);
+ enum dc_status status;
+
+ status = core_link_write_dpcd(
link,
- link_setting,
- &link->preferred_training_settings,
- &lt_settings);
+ DP_MAIN_LINK_CHANNEL_CODING_SET,
+ (uint8_t *) &encoding,
+ 1);
+ DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X MAIN_LINK_CHANNEL_CODING_SET = %x\n",
+ __func__,
+ DP_MAIN_LINK_CHANNEL_CODING_SET,
+ encoding);
- /* Configure lttpr mode */
- if (link->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT)
- configure_lttpr_mode_non_transparent(link);
- else if (link->lttpr_mode == LTTPR_MODE_TRANSPARENT)
- configure_lttpr_mode_transparent(link);
+ return status;
+}
- if (link->ctx->dc->work_arounds.lt_early_cr_pattern)
- start_clock_recovery_pattern_early(link, &lt_settings, DPRX);
+static enum link_training_result dp_perform_8b_10b_link_training(
+ struct dc_link *link,
+ struct link_training_settings *lt_settings)
+{
+ enum link_training_result status = LINK_TRAINING_SUCCESS;
- /* 1. set link rate, lane count and spread. */
- dpcd_set_link_settings(link, &lt_settings);
+ uint8_t repeater_cnt;
+ uint8_t repeater_id;
+ uint8_t lane = 0;
- if (link->preferred_training_settings.fec_enable != NULL)
- fec_enable = *link->preferred_training_settings.fec_enable;
- else
- fec_enable = true;
+ if (link->ctx->dc->work_arounds.lt_early_cr_pattern)
+ start_clock_recovery_pattern_early(link, lt_settings, DPRX);
- dp_set_fec_ready(link, fec_enable);
+ /* 1. set link rate, lane count and spread. */
+ dpcd_set_link_settings(link, lt_settings);
if (link->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
/* 2. perform link training (set link training done
* to false is done as well)
*/
- repeater_cnt = convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
+ repeater_cnt = dp_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);
+ status = perform_clock_recovery_sequence(link, lt_settings, repeater_id);
if (status != LINK_TRAINING_SUCCESS)
break;
status = perform_channel_equalization_sequence(link,
- &lt_settings,
+ lt_settings,
repeater_id);
if (status != LINK_TRAINING_SUCCESS)
@@ -1609,77 +1695,69 @@ enum link_training_result dc_link_dp_perform_link_training(
repeater_training_done(link, repeater_id);
}
+
+ for (lane = 0; lane < (uint8_t)lt_settings->link_settings.lane_count; lane++)
+ lt_settings->lane_settings[lane].VOLTAGE_SWING = VOLTAGE_SWING_LEVEL0;
}
if (status == LINK_TRAINING_SUCCESS) {
- status = perform_clock_recovery_sequence(link, &lt_settings, DPRX);
+ status = perform_clock_recovery_sequence(link, lt_settings, DPRX);
if (status == LINK_TRAINING_SUCCESS) {
status = perform_channel_equalization_sequence(link,
- &lt_settings,
+ lt_settings,
DPRX);
}
}
- /* 3. set training not in progress*/
- dpcd_pattern.v1_4.TRAINING_PATTERN_SET = DPCD_TRAINING_PATTERN_VIDEOIDLE;
- dpcd_set_training_pattern(link, dpcd_pattern);
- if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) {
- status = perform_link_training_int(link,
- &lt_settings,
- status);
- }
-
- /* delay 5ms after Main Link output idle pattern and then check
- * DPCD 0202h.
- */
- if (link->connector_signal != SIGNAL_TYPE_EDP && status == LINK_TRAINING_SUCCESS) {
- msleep(5);
- status = check_link_loss_status(link, &lt_settings);
- }
-
- /* 6. print status message*/
- print_status_message(link, &lt_settings, status);
-
- if (status != LINK_TRAINING_SUCCESS)
- link->ctx->dc->debug_data.ltFailCount++;
-
return status;
}
-static enum dp_panel_mode try_enable_assr(struct dc_stream_state *stream)
+enum link_training_result dc_link_dp_perform_link_training(
+ struct dc_link *link,
+ const struct dc_link_settings *link_settings,
+ bool skip_video_pattern)
{
- struct dc_link *link = stream->link;
- enum dp_panel_mode panel_mode = dp_get_panel_mode(link);
-#ifdef CONFIG_DRM_AMD_DC_HDCP
- struct cp_psp *cp_psp = &stream->ctx->cp_psp;
-#endif
+ enum link_training_result status = LINK_TRAINING_SUCCESS;
+ struct link_training_settings lt_settings;
+ enum dp_link_encoding encoding =
+ dp_get_link_encoding_format(link_settings);
- /* ASSR must be supported on the panel */
- if (panel_mode == DP_PANEL_MODE_DEFAULT)
- return panel_mode;
+ /* decide training settings */
+ dp_decide_training_settings(
+ link,
+ link_settings,
+ &link->preferred_training_settings,
+ &lt_settings);
- /* eDP or internal DP only */
- if (link->connector_signal != SIGNAL_TYPE_EDP &&
- !(link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT &&
- link->is_internal_display))
- return DP_PANEL_MODE_DEFAULT;
+ /* reset previous training states */
+ dpcd_exit_training_mode(link);
-#ifdef CONFIG_DRM_AMD_DC_HDCP
- if (cp_psp && cp_psp->funcs.enable_assr) {
- if (!cp_psp->funcs.enable_assr(cp_psp->handle, link)) {
- /* since eDP implies ASSR on, change panel
- * mode to disable ASSR
- */
- panel_mode = DP_PANEL_MODE_DEFAULT;
- }
- } else
- panel_mode = DP_PANEL_MODE_DEFAULT;
+ /* configure link prior to entering training mode */
+ dpcd_configure_lttpr_mode(link, &lt_settings);
+ dp_set_fec_ready(link, lt_settings.should_set_fec_ready);
+ dpcd_configure_channel_coding(link, &lt_settings);
-#else
- /* turn off ASSR if the implementation is not compiled in */
- panel_mode = DP_PANEL_MODE_DEFAULT;
-#endif
- return panel_mode;
+ /* enter training mode:
+ * Per DP specs starting from here, DPTX device shall not issue
+ * Non-LT AUX transactions inside training mode.
+ */
+ if (encoding == DP_8b_10b_ENCODING)
+ status = dp_perform_8b_10b_link_training(link, &lt_settings);
+ else
+ ASSERT(0);
+
+ /* exit training mode and switch to video idle */
+ dpcd_exit_training_mode(link);
+ if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern)
+ status = dp_transition_to_video_idle(link,
+ &lt_settings,
+ status);
+
+ /* dump debug data */
+ print_status_message(link, &lt_settings, status);
+ if (status != LINK_TRAINING_SUCCESS)
+ link->ctx->dc->debug_data.ltFailCount++;
+ return status;
}
bool perform_link_training_with_retries(
@@ -1687,18 +1765,31 @@ bool perform_link_training_with_retries(
bool skip_video_pattern,
int attempts,
struct pipe_ctx *pipe_ctx,
- enum signal_type signal)
+ enum signal_type signal,
+ bool do_fallback)
{
- uint8_t j;
+ int j;
uint8_t delay_between_attempts = LINK_TRAINING_RETRY_DELAY;
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc_link *link = stream->link;
- enum dp_panel_mode panel_mode;
+ enum dp_panel_mode panel_mode = dp_get_panel_mode(link);
+ struct link_encoder *link_enc;
+ enum link_training_result status = LINK_TRAINING_CR_FAIL_LANE0;
+ struct dc_link_settings current_setting = *link_setting;
+
+ /* Dynamically assigned link encoders associated with stream rather than
+ * link.
+ */
+ if (link->dc->res_pool->funcs->link_encs_assign)
+ link_enc = stream->link_enc;
+ else
+ link_enc = link->link_enc;
+ ASSERT(link_enc);
/* 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->link_enc->funcs->connect_dig_be_to_fe(link->link_enc,
+ link_enc->funcs->connect_dig_be_to_fe(link_enc,
pipe_ctx->stream_res.stream_enc->id, true);
for (j = 0; j < attempts; ++j) {
@@ -1710,7 +1801,7 @@ bool perform_link_training_with_retries(
link,
signal,
pipe_ctx->clock_source->id,
- link_setting);
+ &current_setting);
if (stream->sink_patches.dppowerup_delay > 0) {
int delay_dp_power_up_in_ms = stream->sink_patches.dppowerup_delay;
@@ -1718,21 +1809,31 @@ bool perform_link_training_with_retries(
msleep(delay_dp_power_up_in_ms);
}
- panel_mode = try_enable_assr(stream);
+#ifdef CONFIG_DRM_AMD_DC_HDCP
+ if (panel_mode == DP_PANEL_MODE_EDP) {
+ struct cp_psp *cp_psp = &stream->ctx->cp_psp;
+
+ if (cp_psp && cp_psp->funcs.enable_assr) {
+ if (!cp_psp->funcs.enable_assr(cp_psp->handle, link)) {
+ /* since eDP implies ASSR on, change panel
+ * mode to disable ASSR
+ */
+ panel_mode = DP_PANEL_MODE_DEFAULT;
+ }
+ } else
+ panel_mode = DP_PANEL_MODE_DEFAULT;
+ }
+#endif
+
dp_set_panel_mode(link, panel_mode);
- DC_LOG_DETECTION_DP_CAPS("Link: %d ASSR enabled: %d\n",
- link->link_index,
- panel_mode != DP_PANEL_MODE_DEFAULT);
if (link->aux_access_disabled) {
- dc_link_dp_perform_link_training_skip_aux(link, link_setting);
+ dc_link_dp_perform_link_training_skip_aux(link, &current_setting);
return true;
} else {
- enum link_training_result status = LINK_TRAINING_CR_FAIL_LANE0;
-
status = dc_link_dp_perform_link_training(
link,
- link_setting,
+ &current_setting,
skip_video_pattern);
if (status == LINK_TRAINING_SUCCESS)
return true;
@@ -1740,7 +1841,7 @@ bool perform_link_training_with_retries(
/* latest link training still fail, skip delay and keep PHY on
*/
- if (j == (attempts - 1))
+ if (j == (attempts - 1) && link->ep_type == DISPLAY_ENDPOINT_PHY)
break;
DC_LOG_WARNING("%s: Link training attempt %u of %d failed\n",
@@ -1748,6 +1849,19 @@ bool perform_link_training_with_retries(
dp_disable_link_phy(link, signal);
+ /* Abort link training if failure due to sink being unplugged. */
+ if (status == LINK_TRAINING_ABORT)
+ break;
+ else if (do_fallback) {
+ decide_fallback_link_setting(*link_setting, &current_setting, status);
+ /* Fail link training if reduced link bandwidth no longer meets
+ * stream requirements.
+ */
+ if (dc_bandwidth_in_kbps_from_timing(&stream->timing) <
+ dc_link_bandwidth_kbps(link, &current_setting))
+ break;
+ }
+
msleep(delay_between_attempts);
delay_between_attempts += LINK_TRAINING_RETRY_DELAY;
@@ -1784,6 +1898,8 @@ static void set_dp_mst_mode(struct dc_link *link, bool mst_enable)
link->type = dc_connection_single;
link->local_sink = link->remote_sinks[0];
link->local_sink->sink_signal = SIGNAL_TYPE_DISPLAY_PORT;
+ dc_sink_retain(link->local_sink);
+ dm_helpers_dp_mst_stop_top_mgr(link->ctx, link);
} else if (mst_enable == true &&
link->type == dc_connection_single &&
link->remote_sinks[0] != NULL) {
@@ -1823,7 +1939,7 @@ enum link_training_result dc_link_dp_sync_lt_attempt(
enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_EXTERNAL;
bool fec_enable = false;
- initialize_training_settings(
+ dp_decide_training_settings(
link,
link_settings,
lt_overrides,
@@ -1893,6 +2009,24 @@ bool dc_link_dp_sync_lt_end(struct dc_link *link, bool link_down)
return true;
}
+bool dc_link_dp_get_max_link_enc_cap(const struct dc_link *link, struct dc_link_settings *max_link_enc_cap)
+{
+ if (!max_link_enc_cap) {
+ DC_LOG_ERROR("%s: Could not return max link encoder caps", __func__);
+ return false;
+ }
+
+ if (link->link_enc->funcs->get_max_link_cap) {
+ link->link_enc->funcs->get_max_link_cap(link->link_enc, max_link_enc_cap);
+ return true;
+ }
+
+ DC_LOG_ERROR("%s: Max link encoder caps unknown", __func__);
+ max_link_enc_cap->lane_count = 1;
+ max_link_enc_cap->link_rate = 6;
+ return false;
+}
+
static struct dc_link_settings get_max_link_cap(struct dc_link *link)
{
struct dc_link_settings max_link_cap = {0};
@@ -1976,7 +2110,7 @@ enum dc_status read_hpd_rx_irq_data(
return retval;
}
-static bool hpd_rx_irq_check_link_loss_status(
+bool hpd_rx_irq_check_link_loss_status(
struct dc_link *link,
union hpd_irq_data *hpd_irq_dpcd_data)
{
@@ -2173,7 +2307,7 @@ bool dp_verify_link_cap_with_retries(
struct dc_link_settings *known_limit_link_setting,
int attempts)
{
- uint8_t i = 0;
+ int i = 0;
bool success = false;
for (i = 0; i < attempts; i++) {
@@ -2411,6 +2545,12 @@ bool dp_validate_mode_timing(
const struct dc_link_settings *link_setting;
+ /* According to spec, VSC SDP should be used if pixel format is YCbCr420 */
+ if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 &&
+ !link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED &&
+ dal_graphics_object_id_get_connector_id(link->link_id) != CONNECTOR_ID_VIRTUAL)
+ return false;
+
/*always DP fail safe mode*/
if ((timing->pix_clk_100hz / 10) == (uint32_t) 25175 &&
timing->h_addressable == (uint32_t) 640 &&
@@ -2495,7 +2635,11 @@ bool decide_edp_link_settings(struct dc_link *link, struct dc_link_settings *lin
struct dc_link_settings current_link_setting;
uint32_t link_bw;
- if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14 ||
+ /*
+ * edp_supported_link_rates_count is only valid for eDP v1.4 or higher.
+ * Per VESA eDP spec, "The DPCD revision for eDP v1.4 is 13h"
+ */
+ if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_13 ||
link->dpcd_caps.edp_supported_link_rates_count == 0) {
*link_setting = link->verified_link_cap;
return true;
@@ -2593,13 +2737,11 @@ static bool allow_hpd_rx_irq(const struct dc_link *link)
/*
* Don't handle RX IRQ unless one of following is met:
* 1) The link is established (cur_link_settings != unknown)
- * 2) We kicked off MST detection
- * 3) We know we're dealing with an active dongle
+ * 2) We know we're dealing with a branch device, SST or MST
*/
if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
- (link->type == dc_connection_mst_branch) ||
- is_dp_active_dongle(link))
+ is_dp_branch_device(link))
return true;
return false;
@@ -2697,9 +2839,10 @@ static void dp_test_send_phy_test_pattern(struct dc_link *link)
union phy_test_pattern dpcd_test_pattern;
union lane_adjust dpcd_lane_adjustment[2];
unsigned char dpcd_post_cursor_2_adjustment = 0;
- unsigned char test_80_bit_pattern[
+ unsigned char test_pattern_buffer[
(DP_TEST_80BIT_CUSTOM_PATTERN_79_72 -
DP_TEST_80BIT_CUSTOM_PATTERN_7_0)+1] = {0};
+ unsigned int test_pattern_size = 0;
enum dp_test_pattern test_pattern;
struct dc_link_training_settings link_settings;
union lane_adjust dpcd_lane_adjust;
@@ -2769,12 +2912,15 @@ static void dp_test_send_phy_test_pattern(struct dc_link *link)
break;
}
- if (test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM)
+ if (test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM) {
+ test_pattern_size = (DP_TEST_80BIT_CUSTOM_PATTERN_79_72 -
+ DP_TEST_80BIT_CUSTOM_PATTERN_7_0) + 1;
core_link_read_dpcd(
link,
DP_TEST_80BIT_CUSTOM_PATTERN_7_0,
- test_80_bit_pattern,
- sizeof(test_80_bit_pattern));
+ test_pattern_buffer,
+ test_pattern_size);
+ }
/* prepare link training settings */
link_settings.link = link->cur_link_settings;
@@ -2812,9 +2958,8 @@ static void dp_test_send_phy_test_pattern(struct dc_link *link)
test_pattern,
DP_TEST_PATTERN_COLOR_SPACE_UNDEFINED,
&link_training_settings,
- test_80_bit_pattern,
- (DP_TEST_80BIT_CUSTOM_PATTERN_79_72 -
- DP_TEST_80BIT_CUSTOM_PATTERN_7_0)+1);
+ test_pattern_buffer,
+ test_pattern_size);
}
static void dp_test_send_link_test_pattern(struct dc_link *link)
@@ -2899,6 +3044,22 @@ static void dp_test_send_link_test_pattern(struct dc_link *link)
break;
}
+ switch (dpcd_test_params.bits.CLR_FORMAT) {
+ case 0:
+ pipe_ctx->stream->timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ break;
+ case 1:
+ pipe_ctx->stream->timing.pixel_encoding = PIXEL_ENCODING_YCBCR422;
+ break;
+ case 2:
+ pipe_ctx->stream->timing.pixel_encoding = PIXEL_ENCODING_YCBCR444;
+ break;
+ default:
+ pipe_ctx->stream->timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ break;
+ }
+
+
if (requestColorDepth != COLOR_DEPTH_UNDEFINED
&& pipe_ctx->stream->timing.display_color_depth != requestColorDepth) {
DC_LOG_DEBUG("%s: original bpc %d, changing to %d\n",
@@ -2906,9 +3067,10 @@ static void dp_test_send_link_test_pattern(struct dc_link *link)
pipe_ctx->stream->timing.display_color_depth,
requestColorDepth);
pipe_ctx->stream->timing.display_color_depth = requestColorDepth;
- dp_update_dsc_config(pipe_ctx);
}
+ dp_update_dsc_config(pipe_ctx);
+
dc_link_dp_set_test_pattern(
link,
test_pattern,
@@ -3164,7 +3326,7 @@ bool dc_link_handle_hpd_rx_irq(struct dc_link *link, union hpd_irq_data *out_hpd
*out_link_loss = true;
}
- if (link->type == dc_connection_active_dongle &&
+ if (link->type == dc_connection_sst_branch &&
hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT
!= link->dpcd_sink_count)
status = true;
@@ -3215,6 +3377,12 @@ bool is_mst_supported(struct dc_link *link)
bool is_dp_active_dongle(const struct dc_link *link)
{
+ return (link->dpcd_caps.dongle_type >= DISPLAY_DONGLE_DP_VGA_CONVERTER) &&
+ (link->dpcd_caps.dongle_type <= DISPLAY_DONGLE_DP_HDMI_CONVERTER);
+}
+
+bool is_dp_branch_device(const struct dc_link *link)
+{
return link->dpcd_caps.is_branch_dev;
}
@@ -3432,79 +3600,16 @@ static bool dpcd_read_sink_ext_caps(struct dc_link *link)
return true;
}
-static bool retrieve_link_cap(struct dc_link *link)
+bool dp_retrieve_lttpr_cap(struct dc_link *link)
{
- /* DP_ADAPTER_CAP - DP_DPCD_REV + 1 == 16 and also DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT + 1 == 16,
- * which means size 16 will be good for both of those DPCD register block reads
- */
- uint8_t dpcd_data[16];
uint8_t lttpr_dpcd_data[6];
-
- /*Only need to read 1 byte starting from DP_DPRX_FEATURE_ENUMERATION_LIST.
- */
- uint8_t dpcd_dprx_data = '\0';
- uint8_t dpcd_power_state = '\0';
-
- struct dp_device_vendor_id sink_id;
- union down_stream_port_count down_strm_port_count;
- union edp_configuration_cap edp_config_cap;
- union dp_downstream_port_present ds_port = { 0 };
- enum dc_status status = DC_ERROR_UNEXPECTED;
- uint32_t read_dpcd_retry_cnt = 3;
- int i;
- struct dp_sink_hw_fw_revision dp_hw_fw_revision;
- bool is_lttpr_present = false;
- const uint32_t post_oui_delay = 30; // 30ms
bool vbios_lttpr_enable = false;
bool vbios_lttpr_interop = false;
struct dc_bios *bios = link->dc->ctx->dc_bios;
+ enum dc_status status = DC_ERROR_UNEXPECTED;
+ bool is_lttpr_present = false;
- memset(dpcd_data, '\0', sizeof(dpcd_data));
memset(lttpr_dpcd_data, '\0', sizeof(lttpr_dpcd_data));
- memset(&down_strm_port_count,
- '\0', sizeof(union down_stream_port_count));
- memset(&edp_config_cap, '\0',
- sizeof(union edp_configuration_cap));
-
- /* if extended timeout is supported in hardware,
- * default to LTTPR timeout (3.2ms) first as a W/A for DP link layer
- * CTS 4.2.1.1 regression introduced by CTS specs requirement update.
- */
- dc_link_aux_try_to_configure_timeout(link->ddc,
- LINK_AUX_DEFAULT_LTTPR_TIMEOUT_PERIOD);
-
- status = core_link_read_dpcd(link, DP_SET_POWER,
- &dpcd_power_state, sizeof(dpcd_power_state));
-
- /* Delay 1 ms if AUX CH is in power down state. Based on spec
- * section 2.3.1.2, if AUX CH may be powered down due to
- * write to DPCD 600h = 2. Sink AUX CH is monitoring differential
- * signal and may need up to 1 ms before being able to reply.
- */
- if (status != DC_OK || dpcd_power_state == DP_SET_POWER_D3)
- udelay(1000);
-
- dpcd_set_source_specific_data(link);
- /* Sink may need to configure internals based on vendor, so allow some
- * time before proceeding with possibly vendor specific transactions
- */
- msleep(post_oui_delay);
-
- for (i = 0; i < read_dpcd_retry_cnt; i++) {
- status = core_link_read_dpcd(
- link,
- DP_DPCD_REV,
- dpcd_data,
- sizeof(dpcd_data));
- if (status == DC_OK)
- break;
- }
-
- if (status != DC_OK) {
- dm_error("%s: Read dpcd data failed.\n", __func__);
- return false;
- }
-
/* Query BIOS to determine if LTTPR functionality is forced on by system */
if (bios->funcs->get_lttpr_caps) {
enum bp_result bp_query_result;
@@ -3550,6 +3655,10 @@ static bool retrieve_link_cap(struct dc_link *link)
DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV,
lttpr_dpcd_data,
sizeof(lttpr_dpcd_data));
+ if (status != DC_OK) {
+ dm_error("%s: Read LTTPR caps data failed.\n", __func__);
+ return false;
+ }
link->dpcd_caps.lttpr_caps.revision.raw =
lttpr_dpcd_data[DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV -
@@ -3575,20 +3684,92 @@ static bool retrieve_link_cap(struct dc_link *link)
lttpr_dpcd_data[DP_PHY_REPEATER_EXTENDED_WAIT_TIMEOUT -
DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV];
- is_lttpr_present = (link->dpcd_caps.lttpr_caps.phy_repeater_cnt > 0 &&
+ /* Attempt to train in LTTPR transparent mode if repeater count exceeds 8. */
+ is_lttpr_present = (dp_convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt) != 0 &&
link->dpcd_caps.lttpr_caps.max_lane_count > 0 &&
link->dpcd_caps.lttpr_caps.max_lane_count <= 4 &&
link->dpcd_caps.lttpr_caps.revision.raw >= 0x14);
- if (is_lttpr_present)
+ if (is_lttpr_present) {
CONN_DATA_DETECT(link, lttpr_dpcd_data, sizeof(lttpr_dpcd_data), "LTTPR Caps: ");
- else
+ configure_lttpr_mode_transparent(link);
+ } else
link->lttpr_mode = LTTPR_MODE_NON_LTTPR;
}
+ return is_lttpr_present;
+}
+
+static bool retrieve_link_cap(struct dc_link *link)
+{
+ /* DP_ADAPTER_CAP - DP_DPCD_REV + 1 == 16 and also DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT + 1 == 16,
+ * which means size 16 will be good for both of those DPCD register block reads
+ */
+ uint8_t dpcd_data[16];
+ /*Only need to read 1 byte starting from DP_DPRX_FEATURE_ENUMERATION_LIST.
+ */
+ uint8_t dpcd_dprx_data = '\0';
+ uint8_t dpcd_power_state = '\0';
+
+ struct dp_device_vendor_id sink_id;
+ union down_stream_port_count down_strm_port_count;
+ union edp_configuration_cap edp_config_cap;
+ union dp_downstream_port_present ds_port = { 0 };
+ enum dc_status status = DC_ERROR_UNEXPECTED;
+ uint32_t read_dpcd_retry_cnt = 3;
+ int i;
+ struct dp_sink_hw_fw_revision dp_hw_fw_revision;
+ const uint32_t post_oui_delay = 30; // 30ms
+ bool is_lttpr_present = false;
+
+ memset(dpcd_data, '\0', sizeof(dpcd_data));
+ memset(&down_strm_port_count,
+ '\0', sizeof(union down_stream_port_count));
+ memset(&edp_config_cap, '\0',
+ sizeof(union edp_configuration_cap));
+
+ /* if extended timeout is supported in hardware,
+ * default to LTTPR timeout (3.2ms) first as a W/A for DP link layer
+ * CTS 4.2.1.1 regression introduced by CTS specs requirement update.
+ */
+ dc_link_aux_try_to_configure_timeout(link->ddc,
+ LINK_AUX_DEFAULT_LTTPR_TIMEOUT_PERIOD);
+
+ is_lttpr_present = dp_retrieve_lttpr_cap(link);
+
+ status = core_link_read_dpcd(link, DP_SET_POWER,
+ &dpcd_power_state, sizeof(dpcd_power_state));
+
+ /* Delay 1 ms if AUX CH is in power down state. Based on spec
+ * section 2.3.1.2, if AUX CH may be powered down due to
+ * write to DPCD 600h = 2. Sink AUX CH is monitoring differential
+ * signal and may need up to 1 ms before being able to reply.
+ */
+ if (status != DC_OK || dpcd_power_state == DP_SET_POWER_D3)
+ udelay(1000);
+
+ dpcd_set_source_specific_data(link);
+ /* Sink may need to configure internals based on vendor, so allow some
+ * time before proceeding with possibly vendor specific transactions
+ */
+ msleep(post_oui_delay);
+
+ for (i = 0; i < read_dpcd_retry_cnt; i++) {
+ status = core_link_read_dpcd(
+ link,
+ DP_DPCD_REV,
+ dpcd_data,
+ sizeof(dpcd_data));
+ if (status == DC_OK)
+ break;
+ }
+
+ if (status != DC_OK) {
+ dm_error("%s: Read receiver caps dpcd data failed.\n", __func__);
+ return false;
+ }
if (!is_lttpr_present)
dc_link_aux_try_to_configure_timeout(link->ddc, LINK_AUX_DEFAULT_TIMEOUT_PERIOD);
-
{
union training_aux_rd_interval aux_rd_interval;
@@ -3892,7 +4073,11 @@ void detect_edp_sink_caps(struct dc_link *link)
link->dpcd_caps.edp_supported_link_rates_count = 0;
memset(supported_link_rates, 0, sizeof(supported_link_rates));
- if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 &&
+ /*
+ * edp_supported_link_rates_count is only valid for eDP v1.4 or higher.
+ * Per VESA eDP spec, "The DPCD revision for eDP v1.4 is 13h"
+ */
+ if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_13 &&
(link->dc->debug.optimize_edp_link_rate ||
link->reported_link_cap.link_rate == LINK_RATE_UNKNOWN)) {
// Read DPCD 00010h - 0001Fh 16 bytes at one shot
@@ -4472,50 +4657,74 @@ enum dp_panel_mode dp_get_panel_mode(struct dc_link *link)
return DP_PANEL_MODE_DEFAULT;
}
-void dp_set_fec_ready(struct dc_link *link, bool ready)
+enum dc_status dp_set_fec_ready(struct dc_link *link, bool ready)
{
/* FEC has to be "set ready" before the link training.
* The policy is to always train with FEC
* if the sink supports it and leave it enabled on link.
* If FEC is not supported, disable it.
*/
- struct link_encoder *link_enc = link->link_enc;
+ struct link_encoder *link_enc = NULL;
+ enum dc_status status = DC_OK;
uint8_t fec_config = 0;
+ /* Access link encoder based on whether it is statically
+ * or dynamically assigned to a link.
+ */
+ if (link->is_dig_mapping_flexible &&
+ link->dc->res_pool->funcs->link_encs_assign)
+ link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link);
+ else
+ link_enc = link->link_enc;
+ ASSERT(link_enc);
+
if (!dc_link_should_enable_fec(link))
- return;
+ return status;
if (link_enc->funcs->fec_set_ready &&
link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) {
if (ready) {
fec_config = 1;
- if (core_link_write_dpcd(link,
+ status = core_link_write_dpcd(link,
DP_FEC_CONFIGURATION,
&fec_config,
- sizeof(fec_config)) == DC_OK) {
+ sizeof(fec_config));
+ if (status == DC_OK) {
link_enc->funcs->fec_set_ready(link_enc, true);
link->fec_state = dc_link_fec_ready;
} else {
- link->link_enc->funcs->fec_set_ready(link->link_enc, false);
+ link_enc->funcs->fec_set_ready(link->link_enc, false);
link->fec_state = dc_link_fec_not_ready;
dm_error("dpcd write failed to set fec_ready");
}
} else if (link->fec_state == dc_link_fec_ready) {
fec_config = 0;
- core_link_write_dpcd(link,
+ status = core_link_write_dpcd(link,
DP_FEC_CONFIGURATION,
&fec_config,
sizeof(fec_config));
- link->link_enc->funcs->fec_set_ready(
- link->link_enc, false);
+ link_enc->funcs->fec_set_ready(link_enc, false);
link->fec_state = dc_link_fec_not_ready;
}
}
+
+ return status;
}
void dp_set_fec_enable(struct dc_link *link, bool enable)
{
- struct link_encoder *link_enc = link->link_enc;
+ struct link_encoder *link_enc = NULL;
+
+ /* Access link encoder based on whether it is statically
+ * or dynamically assigned to a link.
+ */
+ if (link->is_dig_mapping_flexible &&
+ link->dc->res_pool->funcs->link_encs_assign)
+ link_enc = link_enc_cfg_get_link_enc_used_by_link(
+ link->dc->current_state, link);
+ else
+ link_enc = link->link_enc;
+ ASSERT(link_enc);
if (!dc_link_should_enable_fec(link))
return;
@@ -4766,4 +4975,11 @@ bool is_edp_ilr_optimization_required(struct dc_link *link, struct dc_crtc_timin
return false;
}
+enum dp_link_encoding dp_get_link_encoding_format(const struct dc_link_settings *link_settings)
+{
+ if ((link_settings->link_rate >= LINK_RATE_LOW) &&
+ (link_settings->link_rate <= LINK_RATE_HIGH3))
+ return DP_8b_10b_ENCODING;
+ return DP_UNKNOWN_ENCODING;
+}