drm/amd/display: move dp link training logic to link_dp_training

[why]
Extract dp link training logic out to their own files.
link_dp_training - high level training sequence and helper functions.
link_dp_training_8b_10b - dp1.x training
link_dp_training_auxless - aux-less training
link_dp_traininig_dpia - dpia training
link_dp_training_fixed_vs_pe_retimer - fixed vs pe retimer training
link_dp_training_128b_132b - dp2.1 training

Tested-by: Daniel Wheeler <Daniel.Wheeler@amd.com>
Reviewed-by: Wesley Chalmers <Wesley.Chalmers@amd.com>
Acked-by: Rodrigo Siqueira <Rodrigo.Siqueira@amd.com>
Signed-off-by: Wenjing Liu <wenjing.liu@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Wenjing Liu 2022-12-14 14:26:46 -05:00 committed by Alex Deucher
parent 899dd5b835
commit 630168a973
22 changed files with 4545 additions and 4119 deletions

View File

@ -54,6 +54,7 @@
#include "link/link_dpcd.h"
#include "link/link_dp_trace.h"
#include "link/link_hpd.h"
#include "link/link_dp_training.h"
#include "dc/dcn30/dcn30_vpg.h"

File diff suppressed because it is too large Load Diff

View File

@ -975,6 +975,9 @@ struct dpcd_usb4_dp_tunneling_info {
/* TODO - Use DRM header to replace above once available */
#endif // DP_INTRA_HOP_AUX_REPLY_INDICATION
#ifndef DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE
#define DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE 0x50
#endif
union dp_main_line_channel_coding_cap {
struct {
uint8_t DP_8b_10b_SUPPORTED :1;

View File

@ -459,27 +459,6 @@ void dc_link_dp_set_drive_settings(
const struct link_resource *link_res,
struct link_training_settings *lt_settings);
bool dc_link_dp_perform_link_training_skip_aux(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting);
enum link_training_result dc_link_dp_perform_link_training(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
bool skip_video_pattern);
bool dc_link_dp_sync_lt_begin(struct dc_link *link);
enum link_training_result dc_link_dp_sync_lt_attempt(
struct dc_link *link,
const struct link_resource *link_res,
struct dc_link_settings *link_setting,
struct dc_link_training_overrides *lt_settings);
bool dc_link_dp_sync_lt_end(struct dc_link *link, bool link_down);
bool dc_link_dp_set_test_pattern(
struct dc_link *link,
enum dp_test_pattern test_pattern,
@ -601,4 +580,7 @@ bool reset_cur_dp_mst_topology(struct dc_link *link);
int dc_link_aux_transfer_raw(struct ddc_service *ddc,
struct aux_payload *payload,
enum aux_return_code_type *operation_result);
enum lttpr_mode dc_link_decide_lttpr_mode(struct dc_link *link,
struct dc_link_settings *link_setting);
#endif /* DC_LINK_H_ */

View File

@ -31,7 +31,6 @@
#define LINK_AUX_DEFAULT_LTTPR_TIMEOUT_PERIOD 3200 /*us*/
#define LINK_AUX_DEFAULT_TIMEOUT_PERIOD 552 /*us*/
#define MAX_MTP_SLOT_COUNT 64
#define DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE 0x50
#define TRAINING_AUX_RD_INTERVAL 100 //us
#define LINK_AUX_WAKE_TIMEOUT_MS 1500 // Timeout when trying to wake unresponsive DPRX.
@ -40,11 +39,6 @@ struct dc_stream_state;
struct dc_link_settings;
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,
/*
* Some receivers fail to train on first try and are good
* on subsequent tries. 2 retries should be plenty. If we
@ -74,17 +68,8 @@ bool decide_link_settings(
struct dc_stream_state *stream,
struct dc_link_settings *link_setting);
bool perform_link_training_with_retries(
const struct dc_link_settings *link_setting,
bool skip_video_pattern,
int attempts,
struct pipe_ctx *pipe_ctx,
enum signal_type signal,
bool do_fallback);
bool hpd_rx_irq_check_link_loss_status(
struct dc_link *link,
union hpd_irq_data *hpd_irq_dpcd_data);
bool hpd_rx_irq_check_link_loss_status(struct dc_link *link,
union hpd_irq_data *hpd_irq_dpcd_data);
bool is_mst_supported(struct dc_link *link);
@ -109,63 +94,6 @@ void dpcd_set_source_specific_data(struct dc_link *link);
void dpcd_write_cable_id_to_dprx(struct dc_link *link);
/* Write DPCD link configuration data. */
enum dc_status dpcd_set_link_settings(
struct dc_link *link,
const struct link_training_settings *lt_settings);
/* Write DPCD drive settings. */
enum dc_status dpcd_set_lane_settings(
struct dc_link *link,
const struct link_training_settings *link_training_setting,
uint32_t offset);
/* Read training status and adjustment requests from DPCD. */
enum dc_status dp_get_lane_status_and_lane_adjust(
struct dc_link *link,
const struct link_training_settings *link_training_setting,
union lane_status ln_status[LANE_COUNT_DP_MAX],
union lane_align_status_updated *ln_align,
union lane_adjust ln_adjust[LANE_COUNT_DP_MAX],
uint32_t offset);
void dp_wait_for_training_aux_rd_interval(
struct dc_link *link,
uint32_t wait_in_micro_secs);
bool dp_is_cr_done(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
enum link_training_result dp_get_cr_failure(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
bool dp_is_ch_eq_done(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
bool dp_is_symbol_locked(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
bool dp_is_interlane_aligned(union lane_align_status_updated align_status);
bool dp_is_max_vs_reached(
const struct link_training_settings *lt_settings);
void dp_hw_to_dpcd_lane_settings(
const struct link_training_settings *lt_settings,
const struct dc_lane_settings hw_lane_settings[LANE_COUNT_DP_MAX],
union dpcd_training_lane dpcd_lane_settings[]);
void dp_decide_lane_settings(
const struct link_training_settings *lt_settings,
const union lane_adjust ln_adjust[LANE_COUNT_DP_MAX],
struct dc_lane_settings hw_lane_settings[LANE_COUNT_DP_MAX],
union dpcd_training_lane dpcd_lane_settings[]);
uint32_t dp_translate_training_aux_read_interval(uint32_t dpcd_aux_read_interval);
enum dpcd_training_patterns
dc_dp_training_pattern_to_dpcd_training_pattern(
struct dc_link *link,
enum dc_dp_training_pattern pattern);
uint8_t dc_dp_initialize_scrambling_data_symbols(
struct dc_link *link,
enum dc_dp_training_pattern pattern);
enum dc_status dp_set_fec_ready(struct dc_link *link, const struct link_resource *link_res, bool ready);
void dp_set_fec_enable(struct dc_link *link, bool enable);
bool dp_set_dsc_enable(struct pipe_ctx *pipe_ctx, bool enable);
@ -183,32 +111,15 @@ void dp_decide_training_settings(
/* Convert PHY repeater count read from DPCD uint8_t. */
uint8_t dp_convert_to_count(uint8_t lttpr_repeater_count);
/* Check DPCD training status registers to detect link loss. */
enum link_training_result dp_check_link_loss_status(
struct dc_link *link,
const struct link_training_settings *link_training_setting);
enum dc_status dpcd_configure_lttpr_mode(
struct dc_link *link,
struct link_training_settings *lt_settings);
enum dp_link_encoding dp_get_link_encoding_format(const struct dc_link_settings *link_settings);
enum dc_status dp_retrieve_lttpr_cap(struct dc_link *link);
bool dp_is_lttpr_present(struct dc_link *link);
enum lttpr_mode dp_decide_lttpr_mode(struct dc_link *link, struct dc_link_settings *link_setting);
void dp_get_lttpr_mode_override(struct dc_link *link, enum lttpr_mode *override);
enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link);
enum lttpr_mode dp_decide_128b_132b_lttpr_mode(struct dc_link *link);
bool dpcd_write_128b_132b_sst_payload_allocation_table(
const struct dc_stream_state *stream,
struct dc_link *link,
struct link_mst_stream_allocation_table *proposed_table,
bool allocate);
enum dc_status dpcd_configure_channel_coding(
struct dc_link *link,
struct link_training_settings *lt_settings);
bool dpcd_poll_for_allocation_change_trigger(struct dc_link *link);
struct fixed31_32 calculate_sst_avg_time_slots_per_mtp(
@ -220,7 +131,6 @@ void enable_dp_hpo_output(struct dc_link *link,
void disable_dp_hpo_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal);
void setup_dp_hpo_stream(struct pipe_ctx *pipe_ctx, bool enable);
bool is_dp_128b_132b_signal(struct pipe_ctx *pipe_ctx);
void edp_panel_backlight_power_on(struct dc_link *link, bool wait_for_hpd);
@ -242,26 +152,20 @@ void dp_disable_link_phy(struct dc_link *link, const struct link_resource *link_
void dp_disable_link_phy_mst(struct dc_link *link, const struct link_resource *link_res,
enum signal_type signal);
bool dp_set_hw_training_pattern(
struct dc_link *link,
const struct link_resource *link_res,
enum dc_dp_training_pattern pattern,
uint32_t offset);
void dp_set_hw_lane_settings(
struct dc_link *link,
const struct link_resource *link_res,
const struct link_training_settings *link_settings,
uint32_t offset);
void dp_set_hw_test_pattern(
struct dc_link *link,
const struct link_resource *link_res,
enum dp_test_pattern test_pattern,
uint8_t *custom_pattern,
uint32_t custom_pattern_size);
void dp_retrain_link_dp_test(struct dc_link *link,
struct dc_link_settings *link_setting,
bool skip_video_pattern);
bool decide_fallback_link_setting(
struct dc_link *link,
struct dc_link_settings *max,
struct dc_link_settings *cur,
enum link_training_result training_result);
#endif /* __DC_LINK_DP_H__ */

View File

@ -24,7 +24,9 @@
# PHY, HPD, DDC and etc).
LINK = link_hwss_dio.o link_hwss_dpia.o link_hwss_hpo_dp.o link_dp_trace.o \
link_hpd.o link_ddc.o link_dpcd.o link_dp_dpia.o
link_hpd.o link_ddc.o link_dpcd.o link_dp_dpia.o link_dp_training.o \
link_dp_training_8b_10b.o link_dp_training_128b_132b.o link_dp_training_dpia.o \
link_dp_training_auxless.o link_dp_training_fixed_vs_pe_retimer.o
AMD_DAL_LINK = $(addprefix $(AMDDALPATH)/dc/link/,$(LINK))

View File

@ -34,12 +34,20 @@
#include "link_hwss.h"
#include "dm_helpers.h"
#include "dmub/inc/dmub_cmd.h"
#include "link/link_dpcd.h"
#include "link_dpcd.h"
#include "link_dp_training.h"
#include "dc_dmub_srv.h"
#define DC_LOGGER \
link->ctx->logger
/** @note Can remove once DP tunneling registers in upstream include/drm/drm_dp_helper.h */
/* DPCD DP Tunneling over USB4 */
#define DP_TUNNELING_CAPABILITIES_SUPPORT 0xe000d
#define DP_IN_ADAPTER_INFO 0xe000e
#define DP_USB4_DRIVER_ID 0xe000f
#define DP_USB4_ROUTER_TOPOLOGY_ID 0xe001b
enum dc_status dpcd_get_tunneling_device_data(struct dc_link *link)
{
enum dc_status status = DC_OK;
@ -47,19 +55,20 @@ enum dc_status dpcd_get_tunneling_device_data(struct dc_link *link)
uint8_t dpcd_topology_data[DPCD_USB4_TOPOLOGY_ID_LEN] = {0};
uint8_t i = 0;
status = core_link_read_dpcd(link,
status = core_link_read_dpcd(
link,
DP_TUNNELING_CAPABILITIES_SUPPORT,
dpcd_dp_tun_data,
sizeof(dpcd_dp_tun_data));
status = core_link_read_dpcd(link,
status = core_link_read_dpcd(
link,
DP_USB4_ROUTER_TOPOLOGY_ID,
dpcd_topology_data,
sizeof(dpcd_topology_data));
link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.raw =
dpcd_dp_tun_data[DP_TUNNELING_CAPABILITIES_SUPPORT -
DP_TUNNELING_CAPABILITIES_SUPPORT];
dpcd_dp_tun_data[DP_TUNNELING_CAPABILITIES_SUPPORT - DP_TUNNELING_CAPABILITIES_SUPPORT];
link->dpcd_caps.usb4_dp_tun_info.dpia_info.raw =
dpcd_dp_tun_data[DP_IN_ADAPTER_INFO - DP_TUNNELING_CAPABILITIES_SUPPORT];
link->dpcd_caps.usb4_dp_tun_info.usb4_driver_id =
@ -96,929 +105,3 @@ bool dc_link_dpia_query_hpd_status(struct dc_link *link)
return is_hpd_high;
}
/* Configure link as prescribed in link_setting; set LTTPR mode; and
* Initialize link training settings.
* Abort link training if sink unplug detected.
*
* @param link DPIA link being trained.
* @param[in] link_setting Lane count, link rate and downspread control.
* @param[out] lt_settings Link settings and drive settings (voltage swing and pre-emphasis).
*/
static enum link_training_result dpia_configure_link(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting,
struct link_training_settings *lt_settings)
{
enum dc_status status;
bool fec_enable;
DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) configuring\n - LTTPR mode(%d)\n",
__func__,
link->link_id.enum_id - ENUM_ID_1,
lt_settings->lttpr_mode);
dp_decide_training_settings(link,
link_setting,
lt_settings);
dp_get_lttpr_mode_override(link, &lt_settings->lttpr_mode);
status = dpcd_configure_channel_coding(link, lt_settings);
if (status != DC_OK && link->is_hpd_pending)
return LINK_TRAINING_ABORT;
/* Configure lttpr mode */
status = dpcd_configure_lttpr_mode(link, lt_settings);
if (status != DC_OK && link->is_hpd_pending)
return LINK_TRAINING_ABORT;
/* Set link rate, lane count and spread. */
status = dpcd_set_link_settings(link, lt_settings);
if (status != DC_OK && link->is_hpd_pending)
return LINK_TRAINING_ABORT;
if (link->preferred_training_settings.fec_enable)
fec_enable = *link->preferred_training_settings.fec_enable;
else
fec_enable = true;
status = dp_set_fec_ready(link, link_res, fec_enable);
if (status != DC_OK && link->is_hpd_pending)
return LINK_TRAINING_ABORT;
return LINK_TRAINING_SUCCESS;
}
static enum dc_status core_link_send_set_config(struct dc_link *link,
uint8_t msg_type,
uint8_t msg_data)
{
struct set_config_cmd_payload payload;
enum set_config_status set_config_result = SET_CONFIG_PENDING;
/* prepare set_config payload */
payload.msg_type = msg_type;
payload.msg_data = msg_data;
if (!link->ddc->ddc_pin && !link->aux_access_disabled &&
(dm_helpers_dmub_set_config_sync(link->ctx, link,
&payload, &set_config_result) == -1)) {
return DC_ERROR_UNEXPECTED;
}
/* set_config should return ACK if successful */
return (set_config_result == SET_CONFIG_ACK_RECEIVED) ? DC_OK : DC_ERROR_UNEXPECTED;
}
/* Build SET_CONFIG message data payload for specified message type. */
static uint8_t dpia_build_set_config_data(enum dpia_set_config_type type,
struct dc_link *link,
struct link_training_settings *lt_settings)
{
union dpia_set_config_data data;
data.raw = 0;
switch (type) {
case DPIA_SET_CFG_SET_LINK:
data.set_link.mode = lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT ? 1 : 0;
break;
case DPIA_SET_CFG_SET_PHY_TEST_MODE:
break;
case DPIA_SET_CFG_SET_VSPE:
/* Assume all lanes have same drive settings. */
data.set_vspe.swing = lt_settings->hw_lane_settings[0].VOLTAGE_SWING;
data.set_vspe.pre_emph = lt_settings->hw_lane_settings[0].PRE_EMPHASIS;
data.set_vspe.max_swing_reached =
lt_settings->hw_lane_settings[0].VOLTAGE_SWING ==
VOLTAGE_SWING_MAX_LEVEL ? 1 : 0;
data.set_vspe.max_pre_emph_reached =
lt_settings->hw_lane_settings[0].PRE_EMPHASIS ==
PRE_EMPHASIS_MAX_LEVEL ? 1 : 0;
break;
default:
ASSERT(false); /* Message type not supported by helper function. */
break;
}
return data.raw;
}
/* Convert DC training pattern to DPIA training stage. */
static enum dc_status convert_trng_ptn_to_trng_stg(enum dc_dp_training_pattern tps, enum dpia_set_config_ts *ts)
{
enum dc_status status = DC_OK;
switch (tps) {
case DP_TRAINING_PATTERN_SEQUENCE_1:
*ts = DPIA_TS_TPS1;
break;
case DP_TRAINING_PATTERN_SEQUENCE_2:
*ts = DPIA_TS_TPS2;
break;
case DP_TRAINING_PATTERN_SEQUENCE_3:
*ts = DPIA_TS_TPS3;
break;
case DP_TRAINING_PATTERN_SEQUENCE_4:
*ts = DPIA_TS_TPS4;
break;
case DP_TRAINING_PATTERN_VIDEOIDLE:
*ts = DPIA_TS_DPRX_DONE;
break;
default: /* TPS not supported by helper function. */
ASSERT(false);
*ts = DPIA_TS_DPRX_DONE;
status = DC_UNSUPPORTED_VALUE;
break;
}
return status;
}
/* Write training pattern to DPCD. */
static enum dc_status dpcd_set_lt_pattern(struct dc_link *link,
enum dc_dp_training_pattern pattern,
uint32_t hop)
{
union dpcd_training_pattern dpcd_pattern = {0};
uint32_t dpcd_tps_offset = DP_TRAINING_PATTERN_SET;
enum dc_status status;
if (hop != DPRX)
dpcd_tps_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 +
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (hop - 1));
/* DpcdAddress_TrainingPatternSet */
dpcd_pattern.v1_4.TRAINING_PATTERN_SET =
dc_dp_training_pattern_to_dpcd_training_pattern(link, pattern);
dpcd_pattern.v1_4.SCRAMBLING_DISABLE =
dc_dp_initialize_scrambling_data_symbols(link, pattern);
if (hop != DPRX) {
DC_LOG_HW_LINK_TRAINING("%s\n LTTPR Repeater ID: %d\n 0x%X pattern = %x\n",
__func__,
hop,
dpcd_tps_offset,
dpcd_pattern.v1_4.TRAINING_PATTERN_SET);
} else {
DC_LOG_HW_LINK_TRAINING("%s\n 0x%X pattern = %x\n",
__func__,
dpcd_tps_offset,
dpcd_pattern.v1_4.TRAINING_PATTERN_SET);
}
status = core_link_write_dpcd(link,
dpcd_tps_offset,
&dpcd_pattern.raw,
sizeof(dpcd_pattern.raw));
return status;
}
/* Execute clock recovery phase of link training for specified hop in display
* path.in non-transparent mode:
* - Driver issues both DPCD and SET_CONFIG transactions.
* - TPS1 is transmitted for any hops downstream of DPOA.
* - Drive (VS/PE) only transmitted for the hop immediately downstream of DPOA.
* - CR for the first hop (DPTX-to-DPIA) is assumed to be successful.
*
* @param link DPIA link being trained.
* @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
* @param hop The Hop in display path. DPRX = 0.
*/
static enum link_training_result dpia_training_cr_non_transparent(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t hop)
{
enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0;
uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */
enum dc_status status;
uint32_t retries_cr = 0; /* Number of consecutive attempts with same VS or PE. */
uint32_t retry_count = 0;
/* From DP spec, CR read interval is always 100us. */
uint32_t wait_time_microsec = TRAINING_AUX_RD_INTERVAL;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
uint8_t set_cfg_data;
enum dpia_set_config_ts ts;
repeater_cnt = dp_convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
/* Cap of LINK_TRAINING_MAX_CR_RETRY attempts at clock recovery.
* Fix inherited from perform_clock_recovery_sequence() -
* the DP equivalent of this function:
* Required for Synaptics MST hub which can put the LT in
* infinite loop by switching the VS between level 0 and level 1
* continuously.
*/
while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
/* DPTX-to-DPIA */
if (hop == repeater_cnt) {
/* Send SET_CONFIG(SET_LINK:LC,LR,LTTPR) to notify DPOA that
* non-transparent link training has started.
* This also enables the transmission of clk_sync packets.
*/
set_cfg_data = dpia_build_set_config_data(DPIA_SET_CFG_SET_LINK,
link,
lt_settings);
status = core_link_send_set_config(link,
DPIA_SET_CFG_SET_LINK,
set_cfg_data);
/* CR for this hop is considered successful as long as
* SET_CONFIG message is acknowledged by DPOA.
*/
if (status == DC_OK)
result = LINK_TRAINING_SUCCESS;
else
result = LINK_TRAINING_ABORT;
break;
}
/* DPOA-to-x */
/* Instruct DPOA to transmit TPS1 then update DPCD. */
if (retry_count == 0) {
status = convert_trng_ptn_to_trng_stg(lt_settings->pattern_for_cr, &ts);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
status = dpcd_set_lt_pattern(link, lt_settings->pattern_for_cr, hop);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
}
/* Update DPOA drive settings then DPCD. DPOA does only adjusts
* drive settings for hops immediately downstream.
*/
if (hop == repeater_cnt - 1) {
set_cfg_data = dpia_build_set_config_data(DPIA_SET_CFG_SET_VSPE,
link,
lt_settings);
status = core_link_send_set_config(link,
DPIA_SET_CFG_SET_VSPE,
set_cfg_data);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
}
status = dpcd_set_lane_settings(link, lt_settings, hop);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
dp_wait_for_training_aux_rd_interval(link, wait_time_microsec);
/* Read status and adjustment requests from DPCD. */
status = dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
hop);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
/* Check if clock recovery successful. */
if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
result = LINK_TRAINING_SUCCESS;
break;
}
result = dp_get_cr_failure(lane_count, dpcd_lane_status);
if (dp_is_max_vs_reached(lt_settings))
break;
/* Count number of attempts with same drive settings.
* Note: settings are the same for all lanes,
* so comparing first lane is sufficient.
*/
if ((lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
&& (lt_settings->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET ==
dpcd_lane_adjust[0].bits.PRE_EMPHASIS_LANE))
retries_cr++;
else
retries_cr = 0;
/* Update VS/PE. */
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings,
lt_settings->dpcd_lane_settings);
retry_count++;
}
/* Abort link training if clock recovery failed due to HPD unplug. */
if (link->is_hpd_pending)
result = LINK_TRAINING_ABORT;
DC_LOG_HW_LINK_TRAINING(
"%s\n DPIA(%d) clock recovery\n -hop(%d)\n - result(%d)\n - retries(%d)\n - status(%d)\n",
__func__,
link->link_id.enum_id - ENUM_ID_1,
hop,
result,
retry_count,
status);
return result;
}
/* Execute clock recovery phase of link training in transparent LTTPR mode:
* - Driver only issues DPCD transactions and leaves USB4 tunneling (SET_CONFIG) messages to DPIA.
* - Driver writes TPS1 to DPCD to kick off training.
* - Clock recovery (CR) for link is handled by DPOA, which reports result to DPIA on completion.
* - DPIA communicates result to driver by updating CR status when driver reads DPCD.
*
* @param link DPIA link being trained.
* @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
*/
static enum link_training_result dpia_training_cr_transparent(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0;
enum dc_status status;
uint32_t retries_cr = 0; /* Number of consecutive attempts with same VS or PE. */
uint32_t retry_count = 0;
uint32_t wait_time_microsec = lt_settings->cr_pattern_time;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
/* Cap of LINK_TRAINING_MAX_CR_RETRY attempts at clock recovery.
* Fix inherited from perform_clock_recovery_sequence() -
* the DP equivalent of this function:
* Required for Synaptics MST hub which can put the LT in
* infinite loop by switching the VS between level 0 and level 1
* continuously.
*/
while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
/* Write TPS1 (not VS or PE) to DPCD to start CR phase.
* DPIA sends SET_CONFIG(SET_LINK) to notify DPOA to
* start link training.
*/
if (retry_count == 0) {
status = dpcd_set_lt_pattern(link, lt_settings->pattern_for_cr, DPRX);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
}
dp_wait_for_training_aux_rd_interval(link, wait_time_microsec);
/* Read status and adjustment requests from DPCD. */
status = dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
DPRX);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
/* Check if clock recovery successful. */
if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
result = LINK_TRAINING_SUCCESS;
break;
}
result = dp_get_cr_failure(lane_count, dpcd_lane_status);
if (dp_is_max_vs_reached(lt_settings))
break;
/* Count number of attempts with same drive settings.
* Note: settings are the same for all lanes,
* so comparing first lane is sufficient.
*/
if ((lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
&& (lt_settings->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET ==
dpcd_lane_adjust[0].bits.PRE_EMPHASIS_LANE))
retries_cr++;
else
retries_cr = 0;
/* Update VS/PE. */
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
retry_count++;
}
/* Abort link training if clock recovery failed due to HPD unplug. */
if (link->is_hpd_pending)
result = LINK_TRAINING_ABORT;
DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) clock recovery\n"
" -hop(%d)\n - result(%d)\n - retries(%d)\n",
__func__,
link->link_id.enum_id - ENUM_ID_1,
DPRX,
result,
retry_count);
return result;
}
/* Execute clock recovery phase of link training for specified hop in display
* path.
*
* @param link DPIA link being trained.
* @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
* @param hop The Hop in display path. DPRX = 0.
*/
static enum link_training_result dpia_training_cr_phase(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t hop)
{
enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0;
if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT)
result = dpia_training_cr_non_transparent(link, link_res, lt_settings, hop);
else
result = dpia_training_cr_transparent(link, link_res, lt_settings);
return result;
}
/* Return status read interval during equalization phase. */
static uint32_t dpia_get_eq_aux_rd_interval(const struct dc_link *link,
const struct link_training_settings *lt_settings,
uint32_t hop)
{
uint32_t wait_time_microsec;
if (hop == DPRX)
wait_time_microsec = lt_settings->eq_pattern_time;
else
wait_time_microsec =
dp_translate_training_aux_read_interval(
link->dpcd_caps.lttpr_caps.aux_rd_interval[hop - 1]);
/* Check debug option for extending aux read interval. */
if (link->dc->debug.dpia_debug.bits.extend_aux_rd_interval)
wait_time_microsec = DPIA_DEBUG_EXTENDED_AUX_RD_INTERVAL_US;
return wait_time_microsec;
}
/* Execute equalization phase of link training for specified hop in display
* path in non-transparent mode:
* - driver issues both DPCD and SET_CONFIG transactions.
* - TPSx is transmitted for any hops downstream of DPOA.
* - Drive (VS/PE) only transmitted for the hop immediately downstream of DPOA.
* - EQ for the first hop (DPTX-to-DPIA) is assumed to be successful.
* - DPRX EQ only reported successful when both DPRX and DPIA requirements
* (clk sync packets sent) fulfilled.
*
* @param link DPIA link being trained.
* @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
* @param hop The Hop in display path. DPRX = 0.
*/
static enum link_training_result dpia_training_eq_non_transparent(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t hop)
{
enum link_training_result result = LINK_TRAINING_EQ_FAIL_EQ;
uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */
uint32_t retries_eq = 0;
enum dc_status status;
enum dc_dp_training_pattern tr_pattern;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
uint8_t set_cfg_data;
enum dpia_set_config_ts ts;
/* Training pattern is TPS4 for repeater;
* TPS2/3/4 for DPRX depending on what it supports.
*/
if (hop == DPRX)
tr_pattern = lt_settings->pattern_for_eq;
else
tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4;
repeater_cnt = dp_convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
for (retries_eq = 0; retries_eq < LINK_TRAINING_MAX_RETRY_COUNT; retries_eq++) {
/* DPTX-to-DPIA equalization always successful. */
if (hop == repeater_cnt) {
result = LINK_TRAINING_SUCCESS;
break;
}
/* Instruct DPOA to transmit TPSn then update DPCD. */
if (retries_eq == 0) {
status = convert_trng_ptn_to_trng_stg(tr_pattern, &ts);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
status = core_link_send_set_config(link,
DPIA_SET_CFG_SET_TRAINING,
ts);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
status = dpcd_set_lt_pattern(link, tr_pattern, hop);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
}
/* Update DPOA drive settings then DPCD. DPOA only adjusts
* drive settings for hop immediately downstream.
*/
if (hop == repeater_cnt - 1) {
set_cfg_data = dpia_build_set_config_data(DPIA_SET_CFG_SET_VSPE,
link,
lt_settings);
status = core_link_send_set_config(link,
DPIA_SET_CFG_SET_VSPE,
set_cfg_data);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
}
status = dpcd_set_lane_settings(link, lt_settings, hop);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
/* Extend wait time on second equalisation attempt on final hop to
* ensure clock sync packets have been sent.
*/
if (hop == DPRX && retries_eq == 1)
wait_time_microsec = max(wait_time_microsec, (uint32_t)DPIA_CLK_SYNC_DELAY);
else
wait_time_microsec = dpia_get_eq_aux_rd_interval(link, lt_settings, hop);
dp_wait_for_training_aux_rd_interval(link, wait_time_microsec);
/* Read status and adjustment requests from DPCD. */
status = dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
hop);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
/* CR can still fail during EQ phase. Fail training if CR fails. */
if (!dp_is_cr_done(lane_count, dpcd_lane_status)) {
result = LINK_TRAINING_EQ_FAIL_CR;
break;
}
if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) &&
dp_is_symbol_locked(link->cur_link_settings.lane_count, dpcd_lane_status) &&
dp_is_interlane_aligned(dpcd_lane_status_updated)) {
result = LINK_TRAINING_SUCCESS;
break;
}
/* Update VS/PE. */
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
}
/* Abort link training if equalization failed due to HPD unplug. */
if (link->is_hpd_pending)
result = LINK_TRAINING_ABORT;
DC_LOG_HW_LINK_TRAINING(
"%s\n DPIA(%d) equalization\n - hop(%d)\n - result(%d)\n - retries(%d)\n - status(%d)\n",
__func__,
link->link_id.enum_id - ENUM_ID_1,
hop,
result,
retries_eq,
status);
return result;
}
/* Execute equalization phase of link training for specified hop in display
* path in transparent LTTPR mode:
* - driver only issues DPCD transactions leaves USB4 tunneling (SET_CONFIG) messages to DPIA.
* - driver writes TPSx to DPCD to notify DPIA that is in equalization phase.
* - equalization (EQ) for link is handled by DPOA, which reports result to DPIA on completion.
* - DPIA communicates result to driver by updating EQ status when driver reads DPCD.
*
* @param link DPIA link being trained.
* @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
* @param hop The Hop in display path. DPRX = 0.
*/
static enum link_training_result dpia_training_eq_transparent(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
enum link_training_result result = LINK_TRAINING_EQ_FAIL_EQ;
uint32_t retries_eq = 0;
enum dc_status status;
enum dc_dp_training_pattern tr_pattern = lt_settings->pattern_for_eq;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
wait_time_microsec = dpia_get_eq_aux_rd_interval(link, lt_settings, DPRX);
for (retries_eq = 0; retries_eq < LINK_TRAINING_MAX_RETRY_COUNT; retries_eq++) {
if (retries_eq == 0) {
status = dpcd_set_lt_pattern(link, tr_pattern, DPRX);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
}
dp_wait_for_training_aux_rd_interval(link, wait_time_microsec);
/* Read status and adjustment requests from DPCD. */
status = dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
DPRX);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
break;
}
/* CR can still fail during EQ phase. Fail training if CR fails. */
if (!dp_is_cr_done(lane_count, dpcd_lane_status)) {
result = LINK_TRAINING_EQ_FAIL_CR;
break;
}
if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) &&
dp_is_symbol_locked(link->cur_link_settings.lane_count, dpcd_lane_status)) {
/* Take into consideration corner case for DP 1.4a LL Compliance CTS as USB4
* has to share encoders unlike DP and USBC
*/
if (dp_is_interlane_aligned(dpcd_lane_status_updated) || (link->is_automated && retries_eq)) {
result = LINK_TRAINING_SUCCESS;
break;
}
}
/* Update VS/PE. */
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
}
/* Abort link training if equalization failed due to HPD unplug. */
if (link->is_hpd_pending)
result = LINK_TRAINING_ABORT;
DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) equalization\n"
" - hop(%d)\n - result(%d)\n - retries(%d)\n",
__func__,
link->link_id.enum_id - ENUM_ID_1,
DPRX,
result,
retries_eq);
return result;
}
/* Execute equalization phase of link training for specified hop in display
* path.
*
* @param link DPIA link being trained.
* @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
* @param hop The Hop in display path. DPRX = 0.
*/
static enum link_training_result dpia_training_eq_phase(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t hop)
{
enum link_training_result result;
if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT)
result = dpia_training_eq_non_transparent(link, link_res, lt_settings, hop);
else
result = dpia_training_eq_transparent(link, link_res, lt_settings);
return result;
}
/* End training of specified hop in display path. */
static enum dc_status dpcd_clear_lt_pattern(struct dc_link *link, uint32_t hop)
{
union dpcd_training_pattern dpcd_pattern = {0};
uint32_t dpcd_tps_offset = DP_TRAINING_PATTERN_SET;
enum dc_status status;
if (hop != DPRX)
dpcd_tps_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 +
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (hop - 1));
status = core_link_write_dpcd(link,
dpcd_tps_offset,
&dpcd_pattern.raw,
sizeof(dpcd_pattern.raw));
return status;
}
/* End training of specified hop in display path.
*
* In transparent LTTPR mode:
* - driver clears training pattern for the specified hop in DPCD.
* In non-transparent LTTPR mode:
* - in addition to clearing training pattern, driver issues USB4 tunneling
* (SET_CONFIG) messages to notify DPOA when training is done for first hop
* (DPTX-to-DPIA) and last hop (DPRX).
*
* @param link DPIA link being trained.
* @param hop The Hop in display path. DPRX = 0.
*/
static enum link_training_result dpia_training_end(struct dc_link *link,
struct link_training_settings *lt_settings,
uint32_t hop)
{
enum link_training_result result = LINK_TRAINING_SUCCESS;
uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */
enum dc_status status;
if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
repeater_cnt = dp_convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
if (hop == repeater_cnt) { /* DPTX-to-DPIA */
/* Send SET_CONFIG(SET_TRAINING:0xff) to notify DPOA that
* DPTX-to-DPIA hop trained. No DPCD write needed for first hop.
*/
status = core_link_send_set_config(link,
DPIA_SET_CFG_SET_TRAINING,
DPIA_TS_UFP_DONE);
if (status != DC_OK)
result = LINK_TRAINING_ABORT;
} else { /* DPOA-to-x */
/* Write 0x0 to TRAINING_PATTERN_SET */
status = dpcd_clear_lt_pattern(link, hop);
if (status != DC_OK)
result = LINK_TRAINING_ABORT;
}
/* Notify DPOA that non-transparent link training of DPRX done. */
if (hop == DPRX && result != LINK_TRAINING_ABORT) {
status = core_link_send_set_config(link,
DPIA_SET_CFG_SET_TRAINING,
DPIA_TS_DPRX_DONE);
if (status != DC_OK)
result = LINK_TRAINING_ABORT;
}
} else { /* non-LTTPR or transparent LTTPR. */
/* Write 0x0 to TRAINING_PATTERN_SET */
status = dpcd_clear_lt_pattern(link, hop);
if (status != DC_OK)
result = LINK_TRAINING_ABORT;
}
DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) end\n - hop(%d)\n - result(%d)\n - LTTPR mode(%d)\n",
__func__,
link->link_id.enum_id - ENUM_ID_1,
hop,
result,
lt_settings->lttpr_mode);
return result;
}
/* When aborting training of specified hop in display path, clean up by:
* - Attempting to clear DPCD TRAINING_PATTERN_SET, LINK_BW_SET and LANE_COUNT_SET.
* - Sending SET_CONFIG(SET_LINK) with lane count and link rate set to 0.
*
* @param link DPIA link being trained.
* @param hop The Hop in display path. DPRX = 0.
*/
static void dpia_training_abort(struct dc_link *link,
struct link_training_settings *lt_settings,
uint32_t hop)
{
uint8_t data = 0;
uint32_t dpcd_tps_offset = DP_TRAINING_PATTERN_SET;
DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) aborting\n - LTTPR mode(%d)\n - HPD(%d)\n",
__func__,
link->link_id.enum_id - ENUM_ID_1,
lt_settings->lttpr_mode,
link->is_hpd_pending);
/* Abandon clean-up if sink unplugged. */
if (link->is_hpd_pending)
return;
if (hop != DPRX)
dpcd_tps_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 +
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (hop - 1));
core_link_write_dpcd(link, dpcd_tps_offset, &data, 1);
core_link_write_dpcd(link, DP_LINK_BW_SET, &data, 1);
core_link_write_dpcd(link, DP_LANE_COUNT_SET, &data, 1);
core_link_send_set_config(link, DPIA_SET_CFG_SET_LINK, data);
}
enum link_training_result dc_link_dpia_perform_link_training(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting,
bool skip_video_pattern)
{
enum link_training_result result;
struct link_training_settings lt_settings = {0};
uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */
int8_t repeater_id; /* Current hop. */
struct dc_link_settings link_settings = *link_setting; // non-const copy to pass in
lt_settings.lttpr_mode = dp_decide_lttpr_mode(link, &link_settings);
/* Configure link as prescribed in link_setting and set LTTPR mode. */
result = dpia_configure_link(link, link_res, link_setting, &lt_settings);
if (result != LINK_TRAINING_SUCCESS)
return result;
if (lt_settings.lttpr_mode == LTTPR_MODE_NON_TRANSPARENT)
repeater_cnt = dp_convert_to_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
/* Train each hop in turn starting with the one closest to DPTX.
* In transparent or non-LTTPR mode, train only the final hop (DPRX).
*/
for (repeater_id = repeater_cnt; repeater_id >= 0; repeater_id--) {
/* Clock recovery. */
result = dpia_training_cr_phase(link, link_res, &lt_settings, repeater_id);
if (result != LINK_TRAINING_SUCCESS)
break;
/* Equalization. */
result = dpia_training_eq_phase(link, link_res, &lt_settings, repeater_id);
if (result != LINK_TRAINING_SUCCESS)
break;
/* Stop training hop. */
result = dpia_training_end(link, &lt_settings, repeater_id);
if (result != LINK_TRAINING_SUCCESS)
break;
}
/* Double-check link status if training successful; gracefully abort
* training of current hop if training failed due to message tunneling
* failure; end training of hop if training ended conventionally and
* falling back to lower bandwidth settings possible.
*/
if (result == LINK_TRAINING_SUCCESS) {
msleep(5);
if (!link->is_automated)
result = dp_check_link_loss_status(link, &lt_settings);
} else if (result == LINK_TRAINING_ABORT) {
dpia_training_abort(link, &lt_settings, repeater_id);
} else {
dpia_training_end(link, &lt_settings, repeater_id);
}
return result;
}

View File

@ -28,57 +28,6 @@
#define __DC_LINK_DPIA_H__
#include "link.h"
/* This module implements functionality for training DPIA links. */
/* The approximate time (us) it takes to transmit 9 USB4 DP clock sync packets. */
#define DPIA_CLK_SYNC_DELAY 16000
/* Extend interval between training status checks for manual testing. */
#define DPIA_DEBUG_EXTENDED_AUX_RD_INTERVAL_US 60000000
/** @note Can remove once DP tunneling registers in upstream include/drm/drm_dp_helper.h */
/* DPCD DP Tunneling over USB4 */
#define DP_TUNNELING_CAPABILITIES_SUPPORT 0xe000d
#define DP_IN_ADAPTER_INFO 0xe000e
#define DP_USB4_DRIVER_ID 0xe000f
#define DP_USB4_ROUTER_TOPOLOGY_ID 0xe001b
/* SET_CONFIG message types sent by driver. */
enum dpia_set_config_type {
DPIA_SET_CFG_SET_LINK = 0x01,
DPIA_SET_CFG_SET_PHY_TEST_MODE = 0x05,
DPIA_SET_CFG_SET_TRAINING = 0x18,
DPIA_SET_CFG_SET_VSPE = 0x19
};
/* Training stages (TS) in SET_CONFIG(SET_TRAINING) message. */
enum dpia_set_config_ts {
DPIA_TS_DPRX_DONE = 0x00, /* Done training DPRX. */
DPIA_TS_TPS1 = 0x01,
DPIA_TS_TPS2 = 0x02,
DPIA_TS_TPS3 = 0x03,
DPIA_TS_TPS4 = 0x07,
DPIA_TS_UFP_DONE = 0xff /* Done training DPTX-to-DPIA hop. */
};
/* SET_CONFIG message data associated with messages sent by driver. */
union dpia_set_config_data {
struct {
uint8_t mode : 1;
uint8_t reserved : 7;
} set_link;
struct {
uint8_t stage;
} set_training;
struct {
uint8_t swing : 2;
uint8_t max_swing_reached : 1;
uint8_t pre_emph : 2;
uint8_t max_pre_emph_reached : 1;
uint8_t reserved : 2;
} set_vspe;
uint8_t raw;
};
/* Read tunneling device capability from DPCD and update link capability
* accordingly.
@ -90,14 +39,5 @@ enum dc_status dpcd_get_tunneling_device_data(struct dc_link *link);
*/
bool dc_link_dpia_query_hpd_status(struct dc_link *link);
/* Train DP tunneling link for USB4 DPIA display endpoint.
* DPIA equivalent of dc_link_dp_perfrorm_link_training.
* Aborts link training upon detection of sink unplug.
*/
enum link_training_result dc_link_dpia_perform_link_training(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting,
bool skip_video_pattern);
#endif /* __DC_LINK_DPIA_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,179 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef __DC_LINK_DP_TRAINING_H__
#define __DC_LINK_DP_TRAINING_H__
#include "link.h"
bool perform_link_training_with_retries(
const struct dc_link_settings *link_setting,
bool skip_video_pattern,
int attempts,
struct pipe_ctx *pipe_ctx,
enum signal_type signal,
bool do_fallback);
enum link_training_result dp_perform_link_training(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
bool skip_video_pattern);
bool dp_set_hw_training_pattern(
struct dc_link *link,
const struct link_resource *link_res,
enum dc_dp_training_pattern pattern,
uint32_t offset);
void dp_set_hw_test_pattern(
struct dc_link *link,
const struct link_resource *link_res,
enum dp_test_pattern test_pattern,
uint8_t *custom_pattern,
uint32_t custom_pattern_size);
void dpcd_set_training_pattern(
struct dc_link *link,
enum dc_dp_training_pattern training_pattern);
/* Write DPCD drive settings. */
enum dc_status dpcd_set_lane_settings(
struct dc_link *link,
const struct link_training_settings *link_training_setting,
uint32_t offset);
/* Write DPCD link configuration data. */
enum dc_status dpcd_set_link_settings(
struct dc_link *link,
const struct link_training_settings *lt_settings);
void dpcd_set_lt_pattern_and_lane_settings(
struct dc_link *link,
const struct link_training_settings *lt_settings,
enum dc_dp_training_pattern pattern,
uint32_t offset);
/* Read training status and adjustment requests from DPCD. */
enum dc_status dp_get_lane_status_and_lane_adjust(
struct dc_link *link,
const struct link_training_settings *link_training_setting,
union lane_status ln_status[LANE_COUNT_DP_MAX],
union lane_align_status_updated *ln_align,
union lane_adjust ln_adjust[LANE_COUNT_DP_MAX],
uint32_t offset);
enum dc_status dpcd_configure_lttpr_mode(
struct dc_link *link,
struct link_training_settings *lt_settings);
enum dc_status configure_lttpr_mode_transparent(struct dc_link *link);
enum dc_status dpcd_configure_channel_coding(
struct dc_link *link,
struct link_training_settings *lt_settings);
void repeater_training_done(struct dc_link *link, uint32_t offset);
void start_clock_recovery_pattern_early(struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset);
void dp_decide_training_settings(
struct dc_link *link,
const struct dc_link_settings *link_settings,
struct link_training_settings *lt_settings);
void dp_decide_lane_settings(
const struct link_training_settings *lt_settings,
const union lane_adjust ln_adjust[LANE_COUNT_DP_MAX],
struct dc_lane_settings hw_lane_settings[LANE_COUNT_DP_MAX],
union dpcd_training_lane dpcd_lane_settings[LANE_COUNT_DP_MAX]);
enum dc_dp_training_pattern decide_cr_training_pattern(
const struct dc_link_settings *link_settings);
enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *link,
const struct dc_link_settings *link_settings);
void dp_get_lttpr_mode_override(struct dc_link *link,
enum lttpr_mode *override);
void override_training_settings(
struct dc_link *link,
const struct dc_link_training_overrides *overrides,
struct link_training_settings *lt_settings);
/* Check DPCD training status registers to detect link loss. */
enum link_training_result dp_check_link_loss_status(
struct dc_link *link,
const struct link_training_settings *link_training_setting);
bool dp_is_cr_done(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
bool dp_is_ch_eq_done(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
bool dp_is_symbol_locked(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
bool dp_is_interlane_aligned(union lane_align_status_updated align_status);
bool is_repeater(const struct link_training_settings *lt_settings, uint32_t offset);
bool dp_is_max_vs_reached(
const struct link_training_settings *lt_settings);
uint8_t get_dpcd_link_rate(const struct dc_link_settings *link_settings);
enum link_training_result dp_get_cr_failure(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
void dp_hw_to_dpcd_lane_settings(
const struct link_training_settings *lt_settings,
const struct dc_lane_settings hw_lane_settings[LANE_COUNT_DP_MAX],
union dpcd_training_lane dpcd_lane_settings[LANE_COUNT_DP_MAX]);
void dp_wait_for_training_aux_rd_interval(
struct dc_link *link,
uint32_t wait_in_micro_secs);
enum dpcd_training_patterns
dp_training_pattern_to_dpcd_training_pattern(
struct dc_link *link,
enum dc_dp_training_pattern pattern);
uint8_t dp_initialize_scrambling_data_symbols(
struct dc_link *link,
enum dc_dp_training_pattern pattern);
void dp_log_training_result(
struct dc_link *link,
const struct link_training_settings *lt_settings,
enum link_training_result status);
uint32_t dp_translate_training_aux_read_interval(
uint32_t dpcd_aux_read_interval);
#endif /* __DC_LINK_DP_TRAINING_H__ */

View File

@ -0,0 +1,258 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
/* FILE POLICY AND INTENDED USAGE:
* This file implements dp 128b/132b link training software policies and
* sequences.
*/
#include "link_dp_training_128b_132b.h"
#include "link_dp_training_8b_10b.h"
#include "link_dpcd.h"
#include "dc_link_dp.h"
#define DC_LOGGER \
link->ctx->logger
static enum dc_status dpcd_128b_132b_set_lane_settings(
struct dc_link *link,
const struct link_training_settings *link_training_setting)
{
enum dc_status status = core_link_write_dpcd(link,
DP_TRAINING_LANE0_SET,
(uint8_t *)(link_training_setting->dpcd_lane_settings),
sizeof(link_training_setting->dpcd_lane_settings));
DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X TX_FFE_PRESET_VALUE = %x\n",
__func__,
DP_TRAINING_LANE0_SET,
link_training_setting->dpcd_lane_settings[0].tx_ffe.PRESET_VALUE);
return status;
}
static void dpcd_128b_132b_get_aux_rd_interval(struct dc_link *link,
uint32_t *interval_in_us)
{
union dp_128b_132b_training_aux_rd_interval dpcd_interval;
uint32_t interval_unit = 0;
dpcd_interval.raw = 0;
core_link_read_dpcd(link, DP_128B132B_TRAINING_AUX_RD_INTERVAL,
&dpcd_interval.raw, sizeof(dpcd_interval.raw));
interval_unit = dpcd_interval.bits.UNIT ? 1 : 2; /* 0b = 2 ms, 1b = 1 ms */
/* (128b/132b_TRAINING_AUX_RD_INTERVAL value + 1) *
* INTERVAL_UNIT. The maximum is 256 ms
*/
*interval_in_us = (dpcd_interval.bits.VALUE + 1) * interval_unit * 1000;
}
static enum link_training_result dp_perform_128b_132b_channel_eq_done_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
uint8_t loop_count;
uint32_t aux_rd_interval = 0;
uint32_t wait_time = 0;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
enum dc_status status = DC_OK;
enum link_training_result result = LINK_TRAINING_SUCCESS;
/* Transmit 128b/132b_TPS1 over Main-Link */
dp_set_hw_training_pattern(link, link_res, lt_settings->pattern_for_cr, DPRX);
/* Set TRAINING_PATTERN_SET to 01h */
dpcd_set_training_pattern(link, lt_settings->pattern_for_cr);
/* Adjust TX_FFE_PRESET_VALUE and Transmit 128b/132b_TPS2 over Main-Link */
dpcd_128b_132b_get_aux_rd_interval(link, &aux_rd_interval);
dp_get_lane_status_and_lane_adjust(link, lt_settings, dpcd_lane_status,
&dpcd_lane_status_updated, dpcd_lane_adjust, DPRX);
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX);
dp_set_hw_training_pattern(link, link_res, lt_settings->pattern_for_eq, DPRX);
/* Set loop counter to start from 1 */
loop_count = 1;
/* Set TRAINING_PATTERN_SET to 02h and TX_FFE_PRESET_VALUE in one AUX transaction */
dpcd_set_lt_pattern_and_lane_settings(link, lt_settings,
lt_settings->pattern_for_eq, DPRX);
/* poll for channel EQ done */
while (result == LINK_TRAINING_SUCCESS) {
dp_wait_for_training_aux_rd_interval(link, aux_rd_interval);
wait_time += aux_rd_interval;
status = dp_get_lane_status_and_lane_adjust(link, lt_settings, dpcd_lane_status,
&dpcd_lane_status_updated, dpcd_lane_adjust, DPRX);
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
dpcd_128b_132b_get_aux_rd_interval(link, &aux_rd_interval);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
} else if (dp_is_ch_eq_done(lt_settings->link_settings.lane_count,
dpcd_lane_status)) {
/* pass */
break;
} else if (loop_count >= lt_settings->eq_loop_count_limit) {
result = DP_128b_132b_MAX_LOOP_COUNT_REACHED;
} else if (dpcd_lane_status_updated.bits.LT_FAILED_128b_132b) {
result = DP_128b_132b_LT_FAILED;
} else {
dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX);
dpcd_128b_132b_set_lane_settings(link, lt_settings);
}
loop_count++;
}
/* poll for EQ interlane align done */
while (result == LINK_TRAINING_SUCCESS) {
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
} else if (dpcd_lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b) {
/* pass */
break;
} else if (wait_time >= lt_settings->eq_wait_time_limit) {
result = DP_128b_132b_CHANNEL_EQ_DONE_TIMEOUT;
} else if (dpcd_lane_status_updated.bits.LT_FAILED_128b_132b) {
result = DP_128b_132b_LT_FAILED;
} else {
dp_wait_for_training_aux_rd_interval(link,
lt_settings->eq_pattern_time);
wait_time += lt_settings->eq_pattern_time;
status = dp_get_lane_status_and_lane_adjust(link, lt_settings, dpcd_lane_status,
&dpcd_lane_status_updated, dpcd_lane_adjust, DPRX);
}
}
return result;
}
static enum link_training_result dp_perform_128b_132b_cds_done_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
/* Assumption: assume hardware has transmitted eq pattern */
enum dc_status status = DC_OK;
enum link_training_result result = LINK_TRAINING_SUCCESS;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
uint32_t wait_time = 0;
/* initiate CDS done sequence */
dpcd_set_training_pattern(link, lt_settings->pattern_for_cds);
/* poll for CDS interlane align done and symbol lock */
while (result == LINK_TRAINING_SUCCESS) {
dp_wait_for_training_aux_rd_interval(link,
lt_settings->cds_pattern_time);
wait_time += lt_settings->cds_pattern_time;
status = dp_get_lane_status_and_lane_adjust(link, lt_settings, dpcd_lane_status,
&dpcd_lane_status_updated, dpcd_lane_adjust, DPRX);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
} else if (dp_is_symbol_locked(lt_settings->link_settings.lane_count, dpcd_lane_status) &&
dpcd_lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b) {
/* pass */
break;
} else if (dpcd_lane_status_updated.bits.LT_FAILED_128b_132b) {
result = DP_128b_132b_LT_FAILED;
} else if (wait_time >= lt_settings->cds_wait_time_limit) {
result = DP_128b_132b_CDS_DONE_TIMEOUT;
}
}
return result;
}
enum link_training_result dp_perform_128b_132b_link_training(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
enum link_training_result result = LINK_TRAINING_SUCCESS;
/* TODO - DP2.0 Link: remove legacy_dp2_lt logic */
if (link->dc->debug.legacy_dp2_lt) {
struct link_training_settings legacy_settings;
decide_8b_10b_training_settings(link,
&lt_settings->link_settings,
&legacy_settings);
return dp_perform_8b_10b_link_training(link, link_res, &legacy_settings);
}
dpcd_set_link_settings(link, lt_settings);
if (result == LINK_TRAINING_SUCCESS)
result = dp_perform_128b_132b_channel_eq_done_sequence(link, link_res, lt_settings);
if (result == LINK_TRAINING_SUCCESS)
result = dp_perform_128b_132b_cds_done_sequence(link, link_res, lt_settings);
return result;
}
void decide_128b_132b_training_settings(struct dc_link *link,
const struct dc_link_settings *link_settings,
struct link_training_settings *lt_settings)
{
memset(lt_settings, 0, sizeof(*lt_settings));
lt_settings->link_settings = *link_settings;
/* TODO: should decide link spread when populating link_settings */
lt_settings->link_settings.link_spread = link->dp_ss_off ? LINK_SPREAD_DISABLED :
LINK_SPREAD_05_DOWNSPREAD_30KHZ;
lt_settings->pattern_for_cr = decide_cr_training_pattern(link_settings);
lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_settings);
lt_settings->eq_pattern_time = 2500;
lt_settings->eq_wait_time_limit = 400000;
lt_settings->eq_loop_count_limit = 20;
lt_settings->pattern_for_cds = DP_128b_132b_TPS2_CDS;
lt_settings->cds_pattern_time = 2500;
lt_settings->cds_wait_time_limit = (dp_convert_to_count(
link->dpcd_caps.lttpr_caps.phy_repeater_cnt) + 1) * 20000;
lt_settings->disallow_per_lane_settings = true;
lt_settings->lttpr_mode = dp_decide_128b_132b_lttpr_mode(link);
dp_hw_to_dpcd_lane_settings(lt_settings,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
}
enum lttpr_mode dp_decide_128b_132b_lttpr_mode(struct dc_link *link)
{
enum lttpr_mode mode = LTTPR_MODE_NON_LTTPR;
if (dp_is_lttpr_present(link))
mode = LTTPR_MODE_NON_TRANSPARENT;
DC_LOG_DC("128b_132b chose LTTPR_MODE %d.\n", mode);
return mode;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef __DC_LINK_DP_TRAINING_128B_132B_H__
#define __DC_LINK_DP_TRAINING_128B_132B_H__
#include "link_dp_training.h"
enum link_training_result dp_perform_128b_132b_link_training(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings);
void decide_128b_132b_training_settings(struct dc_link *link,
const struct dc_link_settings *link_settings,
struct link_training_settings *lt_settings);
enum lttpr_mode dp_decide_128b_132b_lttpr_mode(struct dc_link *link);
#endif /* __DC_LINK_DP_TRAINING_128B_132B_H__ */

View File

@ -0,0 +1,413 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
/* FILE POLICY AND INTENDED USAGE:
* This file implements dp 8b/10b link training software policies and
* sequences.
*/
#include "link_dp_training_8b_10b.h"
#include "link_dpcd.h"
#include "dc_link_dp.h"
#define DC_LOGGER \
link->ctx->logger
static int32_t get_cr_training_aux_rd_interval(struct dc_link *link,
const struct dc_link_settings *link_settings)
{
union training_aux_rd_interval training_rd_interval;
uint32_t wait_in_micro_secs = 100;
memset(&training_rd_interval, 0, sizeof(training_rd_interval));
if (dp_get_link_encoding_format(link_settings) == DP_8b_10b_ENCODING &&
link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) {
core_link_read_dpcd(
link,
DP_TRAINING_AUX_RD_INTERVAL,
(uint8_t *)&training_rd_interval,
sizeof(training_rd_interval));
if (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL)
wait_in_micro_secs = training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000;
}
return wait_in_micro_secs;
}
static uint32_t get_eq_training_aux_rd_interval(
struct dc_link *link,
const struct dc_link_settings *link_settings)
{
union training_aux_rd_interval training_rd_interval;
memset(&training_rd_interval, 0, sizeof(training_rd_interval));
if (dp_get_link_encoding_format(link_settings) == DP_128b_132b_ENCODING) {
core_link_read_dpcd(
link,
DP_128B132B_TRAINING_AUX_RD_INTERVAL,
(uint8_t *)&training_rd_interval,
sizeof(training_rd_interval));
} else if (dp_get_link_encoding_format(link_settings) == DP_8b_10b_ENCODING &&
link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) {
core_link_read_dpcd(
link,
DP_TRAINING_AUX_RD_INTERVAL,
(uint8_t *)&training_rd_interval,
sizeof(training_rd_interval));
}
switch (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) {
case 0: return 400;
case 1: return 4000;
case 2: return 8000;
case 3: return 12000;
case 4: return 16000;
case 5: return 32000;
case 6: return 64000;
default: return 400;
}
}
void decide_8b_10b_training_settings(
struct dc_link *link,
const struct dc_link_settings *link_setting,
struct link_training_settings *lt_settings)
{
memset(lt_settings, '\0', sizeof(struct link_training_settings));
/* Initialize link settings */
lt_settings->link_settings.use_link_rate_set = link_setting->use_link_rate_set;
lt_settings->link_settings.link_rate_set = link_setting->link_rate_set;
lt_settings->link_settings.link_rate = link_setting->link_rate;
lt_settings->link_settings.lane_count = link_setting->lane_count;
/* TODO hard coded to SS for now
* lt_settings.link_settings.link_spread =
* dal_display_path_is_ss_supported(
* path_mode->display_path) ?
* LINK_SPREAD_05_DOWNSPREAD_30KHZ :
* LINK_SPREAD_DISABLED;
*/
lt_settings->link_settings.link_spread = link->dp_ss_off ?
LINK_SPREAD_DISABLED : LINK_SPREAD_05_DOWNSPREAD_30KHZ;
lt_settings->cr_pattern_time = get_cr_training_aux_rd_interval(link, link_setting);
lt_settings->eq_pattern_time = get_eq_training_aux_rd_interval(link, link_setting);
lt_settings->pattern_for_cr = decide_cr_training_pattern(link_setting);
lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_setting);
lt_settings->enhanced_framing = 1;
lt_settings->should_set_fec_ready = true;
lt_settings->disallow_per_lane_settings = true;
lt_settings->always_match_dpcd_with_hw_lane_settings = true;
lt_settings->lttpr_mode = dp_decide_8b_10b_lttpr_mode(link);
dp_hw_to_dpcd_lane_settings(lt_settings, lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
}
enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link)
{
bool is_lttpr_present = dp_is_lttpr_present(link);
bool vbios_lttpr_force_non_transparent = link->dc->caps.vbios_lttpr_enable;
bool vbios_lttpr_aware = link->dc->caps.vbios_lttpr_aware;
if (!is_lttpr_present)
return LTTPR_MODE_NON_LTTPR;
if (vbios_lttpr_aware) {
if (vbios_lttpr_force_non_transparent) {
DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT due to VBIOS DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE set to 1.\n");
return LTTPR_MODE_NON_TRANSPARENT;
} else {
DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT by default due to VBIOS not set DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE set to 1.\n");
return LTTPR_MODE_TRANSPARENT;
}
}
if (link->dc->config.allow_lttpr_non_transparent_mode.bits.DP1_4A &&
link->dc->caps.extended_aux_timeout_support) {
DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT by default and dc->config.allow_lttpr_non_transparent_mode.bits.DP1_4A set to 1.\n");
return LTTPR_MODE_NON_TRANSPARENT;
}
DC_LOG_DC("chose LTTPR_MODE_NON_LTTPR.\n");
return LTTPR_MODE_NON_LTTPR;
}
enum link_training_result perform_8b_10b_clock_recovery_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset)
{
uint32_t retries_cr;
uint32_t retry_count;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX];
union lane_align_status_updated dpcd_lane_status_updated;
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
retries_cr = 0;
retry_count = 0;
memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status));
memset(&dpcd_lane_status_updated, '\0',
sizeof(dpcd_lane_status_updated));
if (!link->ctx->dc->work_arounds.lt_early_cr_pattern)
dp_set_hw_training_pattern(link, link_res, lt_settings->pattern_for_cr, offset);
/* najeeb - The synaptics MST hub can put the LT in
* infinite loop by switching the VS
*/
/* between level 0 and level 1 continuously, here
* we try for CR lock for LinkTrainingMaxCRRetry count*/
while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
/* 1. call HWSS to set lane settings*/
dp_set_hw_lane_settings(
link,
link_res,
lt_settings,
offset);
/* 2. update DPCD of the receiver*/
if (!retry_count)
/* EPR #361076 - write as a 5-byte burst,
* but only for the 1-st iteration.*/
dpcd_set_lt_pattern_and_lane_settings(
link,
lt_settings,
lt_settings->pattern_for_cr,
offset);
else
dpcd_set_lane_settings(
link,
lt_settings,
offset);
/* 3. wait receiver to lock-on*/
wait_time_microsec = lt_settings->cr_pattern_time;
dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested drive
* settings as set by the sink
*/
dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
offset);
/* 5. check CR done*/
if (dp_is_cr_done(lane_count, dpcd_lane_status))
return LINK_TRAINING_SUCCESS;
/* 6. max VS reached*/
if ((dp_get_link_encoding_format(&lt_settings->link_settings) ==
DP_8b_10b_ENCODING) &&
dp_is_max_vs_reached(lt_settings))
break;
/* 7. same lane settings*/
/* Note: settings are the same for all lanes,
* so comparing first lane is sufficient*/
if ((dp_get_link_encoding_format(&lt_settings->link_settings) == DP_8b_10b_ENCODING) &&
lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
retries_cr++;
else if ((dp_get_link_encoding_format(&lt_settings->link_settings) == DP_128b_132b_ENCODING) &&
lt_settings->dpcd_lane_settings[0].tx_ffe.PRESET_VALUE ==
dpcd_lane_adjust[0].tx_ffe.PRESET_VALUE)
retries_cr++;
else
retries_cr = 0;
/* 8. update VS/PE/PC2 in lt_settings*/
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
retry_count++;
}
if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) {
ASSERT(0);
DC_LOG_ERROR("%s: Link Training Error, could not get CR after %d tries. Possibly voltage swing issue",
__func__,
LINK_TRAINING_MAX_CR_RETRY);
}
return dp_get_cr_failure(lane_count, dpcd_lane_status);
}
enum link_training_result perform_8b_10b_channel_equalization_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset)
{
enum dc_dp_training_pattern tr_pattern;
uint32_t retries_ch_eq;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
/* Note: also check that TPS4 is a supported feature*/
tr_pattern = lt_settings->pattern_for_eq;
if (is_repeater(lt_settings, offset) && dp_get_link_encoding_format(&lt_settings->link_settings) == DP_8b_10b_ENCODING)
tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4;
dp_set_hw_training_pattern(link, link_res, tr_pattern, offset);
for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT;
retries_ch_eq++) {
dp_set_hw_lane_settings(link, link_res, lt_settings, offset);
/* 2. update DPCD*/
if (!retries_ch_eq)
/* EPR #361076 - write as a 5-byte burst,
* but only for the 1-st iteration
*/
dpcd_set_lt_pattern_and_lane_settings(
link,
lt_settings,
tr_pattern, offset);
else
dpcd_set_lane_settings(link, lt_settings, offset);
/* 3. wait for receiver to lock-on*/
wait_time_microsec = lt_settings->eq_pattern_time;
if (is_repeater(lt_settings, offset))
wait_time_microsec =
dp_translate_training_aux_read_interval(
link->dpcd_caps.lttpr_caps.aux_rd_interval[offset - 1]);
dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested
* drive settings as set by the sink*/
dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
offset);
/* 5. check CR done*/
if (!dp_is_cr_done(lane_count, dpcd_lane_status))
return dpcd_lane_status[0].bits.CR_DONE_0 ?
LINK_TRAINING_EQ_FAIL_CR_PARTIAL :
LINK_TRAINING_EQ_FAIL_CR;
/* 6. check CHEQ done*/
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*/
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
}
return LINK_TRAINING_EQ_FAIL_EQ;
}
enum link_training_result dp_perform_8b_10b_link_training(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
enum link_training_result status = LINK_TRAINING_SUCCESS;
uint8_t repeater_cnt;
uint8_t repeater_id;
uint8_t lane = 0;
if (link->ctx->dc->work_arounds.lt_early_cr_pattern)
start_clock_recovery_pattern_early(link, link_res, lt_settings, DPRX);
/* 1. set link rate, lane count and spread. */
dpcd_set_link_settings(link, lt_settings);
if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
/* 2. perform link training (set link training done
* to false is done as well)
*/
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_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, repeater_id);
if (status != LINK_TRAINING_SUCCESS) {
repeater_training_done(link, repeater_id);
break;
}
status = perform_8b_10b_channel_equalization_sequence(link,
link_res,
lt_settings,
repeater_id);
repeater_training_done(link, repeater_id);
if (status != LINK_TRAINING_SUCCESS)
break;
for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) {
lt_settings->dpcd_lane_settings[lane].raw = 0;
lt_settings->hw_lane_settings[lane].VOLTAGE_SWING = 0;
lt_settings->hw_lane_settings[lane].PRE_EMPHASIS = 0;
}
}
}
if (status == LINK_TRAINING_SUCCESS) {
status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, DPRX);
if (status == LINK_TRAINING_SUCCESS) {
status = perform_8b_10b_channel_equalization_sequence(link,
link_res,
lt_settings,
DPRX);
}
}
return status;
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef __DC_LINK_DP_TRAINING_8B_10B_H__
#define __DC_LINK_DP_TRAINING_8B_10B_H__
#include "link_dp_training.h"
/* to avoid infinite loop where-in the receiver
* switches between different VS
*/
#define LINK_TRAINING_MAX_CR_RETRY 100
#define LINK_TRAINING_MAX_RETRY_COUNT 5
enum link_training_result dp_perform_8b_10b_link_training(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings);
enum link_training_result perform_8b_10b_clock_recovery_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset);
enum link_training_result perform_8b_10b_channel_equalization_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset);
enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link);
void decide_8b_10b_training_settings(
struct dc_link *link,
const struct dc_link_settings *link_setting,
struct link_training_settings *lt_settings);
#endif /* __DC_LINK_DP_TRAINING_8B_10B_H__ */

View File

@ -0,0 +1,80 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
/* FILE POLICY AND INTENDED USAGE:
*
*/
#include "link_dp_training_auxless.h"
#include "dc_link_dp.h"
#define DC_LOGGER \
link->ctx->logger
bool dc_link_dp_perform_link_training_skip_aux(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting)
{
struct link_training_settings lt_settings = {0};
dp_decide_training_settings(
link,
link_setting,
&lt_settings);
override_training_settings(
link,
&link->preferred_training_settings,
&lt_settings);
/* 1. Perform_clock_recovery_sequence. */
/* transmit training pattern for clock recovery */
dp_set_hw_training_pattern(link, link_res, lt_settings.pattern_for_cr, DPRX);
/* call HWSS to set lane settings*/
dp_set_hw_lane_settings(link, link_res, &lt_settings, DPRX);
/* wait receiver to lock-on*/
dp_wait_for_training_aux_rd_interval(link, lt_settings.cr_pattern_time);
/* 2. Perform_channel_equalization_sequence. */
/* transmit training pattern for channel equalization. */
dp_set_hw_training_pattern(link, link_res, lt_settings.pattern_for_eq, DPRX);
/* call HWSS to set lane settings*/
dp_set_hw_lane_settings(link, link_res, &lt_settings, DPRX);
/* wait receiver to lock-on. */
dp_wait_for_training_aux_rd_interval(link, lt_settings.eq_pattern_time);
/* 3. Perform_link_training_int. */
/* Mainlink output idle pattern. */
dp_set_hw_test_pattern(link, link_res, DP_TEST_PATTERN_VIDEO_MODE, NULL, 0);
dp_log_training_result(link, &lt_settings, LINK_TRAINING_SUCCESS);
return true;
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef __DC_LINK_DP_TRAINING_AUXLESS_H__
#define __DC_LINK_DP_TRAINING_AUXLESS_H__
#include "link_dp_training.h"
bool dc_link_dp_perform_link_training_skip_aux(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting);
#endif /* __DC_LINK_DP_TRAINING_AUXLESS_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef __DC_LINK_DP_TRAINING_DPIA_H__
#define __DC_LINK_DP_TRAINING_DPIA_H__
#include "link_dp_training.h"
/* Train DP tunneling link for USB4 DPIA display endpoint.
* DPIA equivalent of dc_link_dp_perfrorm_link_training.
* Aborts link training upon detection of sink unplug.
*/
enum link_training_result dc_link_dpia_perform_link_training(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting,
bool skip_video_pattern);
#endif /* __DC_LINK_DP_TRAINING_DPIA_H__ */

View File

@ -0,0 +1,578 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
/* FILE POLICY AND INTENDED USAGE:
* This file implements 8b/10b link training specially modified to support an
* embedded retimer chip. This retimer chip is referred as fixed vs pe retimer.
* Unlike native dp connection this chip requires a modified link training
* protocol based on 8b/10b link training. Since this is a non standard sequence
* and we must support this hardware, we decided to isolate it in its own
* training sequence inside its own file.
*/
#include "link_dp_training_fixed_vs_pe_retimer.h"
#include "link_dp_training_8b_10b.h"
#include "link_dpcd.h"
#include "dc_link_dp.h"
#define DC_LOGGER \
link->ctx->logger
void dp_fixed_vs_pe_read_lane_adjust(
struct dc_link *link,
union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX])
{
const uint8_t vendor_lttpr_write_data_vs[3] = {0x0, 0x53, 0x63};
const uint8_t vendor_lttpr_write_data_pe[3] = {0x0, 0x54, 0x63};
const uint8_t offset = dp_convert_to_count(
link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
uint32_t vendor_lttpr_write_address = 0xF004F;
uint32_t vendor_lttpr_read_address = 0xF0053;
uint8_t dprx_vs = 0;
uint8_t dprx_pe = 0;
uint8_t lane;
if (offset != 0xFF) {
vendor_lttpr_write_address +=
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
vendor_lttpr_read_address +=
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
}
/* W/A to read lane settings requested by DPRX */
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_vs[0],
sizeof(vendor_lttpr_write_data_vs));
core_link_read_dpcd(
link,
vendor_lttpr_read_address,
&dprx_vs,
1);
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_pe[0],
sizeof(vendor_lttpr_write_data_pe));
core_link_read_dpcd(
link,
vendor_lttpr_read_address,
&dprx_pe,
1);
for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) {
dpcd_lane_adjust[lane].bits.VOLTAGE_SWING_SET = (dprx_vs >> (2 * lane)) & 0x3;
dpcd_lane_adjust[lane].bits.PRE_EMPHASIS_SET = (dprx_pe >> (2 * lane)) & 0x3;
}
}
void dp_fixed_vs_pe_set_retimer_lane_settings(
struct dc_link *link,
const union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX],
uint8_t lane_count)
{
const uint8_t offset = dp_convert_to_count(
link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
const uint8_t vendor_lttpr_write_data_reset[4] = {0x1, 0x50, 0x63, 0xFF};
uint32_t vendor_lttpr_write_address = 0xF004F;
uint8_t vendor_lttpr_write_data_vs[4] = {0x1, 0x51, 0x63, 0x0};
uint8_t vendor_lttpr_write_data_pe[4] = {0x1, 0x52, 0x63, 0x0};
uint8_t lane = 0;
if (offset != 0xFF) {
vendor_lttpr_write_address +=
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
}
for (lane = 0; lane < lane_count; lane++) {
vendor_lttpr_write_data_vs[3] |=
dpcd_lane_adjust[lane].bits.VOLTAGE_SWING_SET << (2 * lane);
vendor_lttpr_write_data_pe[3] |=
dpcd_lane_adjust[lane].bits.PRE_EMPHASIS_SET << (2 * lane);
}
/* Force LTTPR to output desired VS and PE */
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_reset[0],
sizeof(vendor_lttpr_write_data_reset));
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_vs[0],
sizeof(vendor_lttpr_write_data_vs));
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_pe[0],
sizeof(vendor_lttpr_write_data_pe));
}
static enum link_training_result perform_fixed_vs_pe_nontransparent_training_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
enum link_training_result status = LINK_TRAINING_SUCCESS;
uint8_t lane = 0;
uint8_t toggle_rate = 0x6;
uint8_t target_rate = 0x6;
bool apply_toggle_rate_wa = false;
uint8_t repeater_cnt;
uint8_t repeater_id;
/* Fixed VS/PE specific: Force CR AUX RD Interval to at least 16ms */
if (lt_settings->cr_pattern_time < 16000)
lt_settings->cr_pattern_time = 16000;
/* Fixed VS/PE specific: Toggle link rate */
apply_toggle_rate_wa = (link->vendor_specific_lttpr_link_rate_wa == target_rate);
target_rate = get_dpcd_link_rate(&lt_settings->link_settings);
toggle_rate = (target_rate == 0x6) ? 0xA : 0x6;
if (apply_toggle_rate_wa)
lt_settings->link_settings.link_rate = toggle_rate;
if (link->ctx->dc->work_arounds.lt_early_cr_pattern)
start_clock_recovery_pattern_early(link, link_res, lt_settings, DPRX);
/* 1. set link rate, lane count and spread. */
dpcd_set_link_settings(link, lt_settings);
/* Fixed VS/PE specific: Toggle link rate back*/
if (apply_toggle_rate_wa) {
core_link_write_dpcd(
link,
DP_LINK_BW_SET,
&target_rate,
1);
}
link->vendor_specific_lttpr_link_rate_wa = target_rate;
if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
/* 2. perform link training (set link training done
* to false is done as well)
*/
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_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, repeater_id);
if (status != LINK_TRAINING_SUCCESS) {
repeater_training_done(link, repeater_id);
break;
}
status = perform_8b_10b_channel_equalization_sequence(link,
link_res,
lt_settings,
repeater_id);
repeater_training_done(link, repeater_id);
if (status != LINK_TRAINING_SUCCESS)
break;
for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) {
lt_settings->dpcd_lane_settings[lane].raw = 0;
lt_settings->hw_lane_settings[lane].VOLTAGE_SWING = 0;
lt_settings->hw_lane_settings[lane].PRE_EMPHASIS = 0;
}
}
}
if (status == LINK_TRAINING_SUCCESS) {
status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, DPRX);
if (status == LINK_TRAINING_SUCCESS) {
status = perform_8b_10b_channel_equalization_sequence(link,
link_res,
lt_settings,
DPRX);
}
}
return status;
}
enum link_training_result dp_perform_fixed_vs_pe_training_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
const uint8_t vendor_lttpr_write_data_reset[4] = {0x1, 0x50, 0x63, 0xFF};
const uint8_t offset = dp_convert_to_count(
link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
const uint8_t vendor_lttpr_write_data_intercept_en[4] = {0x1, 0x55, 0x63, 0x0};
const uint8_t vendor_lttpr_write_data_intercept_dis[4] = {0x1, 0x55, 0x63, 0x68};
uint32_t pre_disable_intercept_delay_ms = link->dc->debug.fixed_vs_aux_delay_config_wa;
uint8_t vendor_lttpr_write_data_vs[4] = {0x1, 0x51, 0x63, 0x0};
uint8_t vendor_lttpr_write_data_pe[4] = {0x1, 0x52, 0x63, 0x0};
uint32_t vendor_lttpr_write_address = 0xF004F;
enum link_training_result status = LINK_TRAINING_SUCCESS;
uint8_t lane = 0;
union down_spread_ctrl downspread = {0};
union lane_count_set lane_count_set = {0};
uint8_t toggle_rate;
uint8_t rate;
/* Only 8b/10b is supported */
ASSERT(dp_get_link_encoding_format(&lt_settings->link_settings) ==
DP_8b_10b_ENCODING);
if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
status = perform_fixed_vs_pe_nontransparent_training_sequence(link, link_res, lt_settings);
return status;
}
if (offset != 0xFF) {
vendor_lttpr_write_address +=
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
/* Certain display and cable configuration require extra delay */
if (offset > 2)
pre_disable_intercept_delay_ms = link->dc->debug.fixed_vs_aux_delay_config_wa * 2;
}
/* Vendor specific: Reset lane settings */
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_reset[0],
sizeof(vendor_lttpr_write_data_reset));
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_vs[0],
sizeof(vendor_lttpr_write_data_vs));
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_pe[0],
sizeof(vendor_lttpr_write_data_pe));
/* Vendor specific: Enable intercept */
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_intercept_en[0],
sizeof(vendor_lttpr_write_data_intercept_en));
/* 1. set link rate, lane count and spread. */
downspread.raw = (uint8_t)(lt_settings->link_settings.link_spread);
lane_count_set.bits.LANE_COUNT_SET =
lt_settings->link_settings.lane_count;
lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing;
lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0;
if (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,
&downspread.raw, sizeof(downspread));
core_link_write_dpcd(link, DP_LANE_COUNT_SET,
&lane_count_set.raw, 1);
rate = get_dpcd_link_rate(&lt_settings->link_settings);
/* Vendor specific: Toggle link rate */
toggle_rate = (rate == 0x6) ? 0xA : 0x6;
if (link->vendor_specific_lttpr_link_rate_wa == rate) {
core_link_write_dpcd(
link,
DP_LINK_BW_SET,
&toggle_rate,
1);
}
link->vendor_specific_lttpr_link_rate_wa = rate;
core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x framing = %x\n %x spread = %x\n",
__func__,
DP_LINK_BW_SET,
lt_settings->link_settings.link_rate,
DP_LANE_COUNT_SET,
lt_settings->link_settings.lane_count,
lt_settings->enhanced_framing,
DP_DOWNSPREAD_CTRL,
lt_settings->link_settings.link_spread);
/* 2. Perform link training */
/* Perform Clock Recovery Sequence */
if (status == LINK_TRAINING_SUCCESS) {
const uint8_t max_vendor_dpcd_retries = 10;
uint32_t retries_cr;
uint32_t retry_count;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX];
union lane_align_status_updated dpcd_lane_status_updated;
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
enum dc_status dpcd_status = DC_OK;
uint8_t i = 0;
retries_cr = 0;
retry_count = 0;
memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status));
memset(&dpcd_lane_status_updated, '\0',
sizeof(dpcd_lane_status_updated));
while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
/* 1. call HWSS to set lane settings */
dp_set_hw_lane_settings(
link,
link_res,
lt_settings,
0);
/* 2. update DPCD of the receiver */
if (!retry_count) {
/* EPR #361076 - write as a 5-byte burst,
* but only for the 1-st iteration.
*/
dpcd_set_lt_pattern_and_lane_settings(
link,
lt_settings,
lt_settings->pattern_for_cr,
0);
/* Vendor specific: Disable intercept */
for (i = 0; i < max_vendor_dpcd_retries; i++) {
msleep(pre_disable_intercept_delay_ms);
dpcd_status = core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_intercept_dis[0],
sizeof(vendor_lttpr_write_data_intercept_dis));
if (dpcd_status == DC_OK)
break;
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_intercept_en[0],
sizeof(vendor_lttpr_write_data_intercept_en));
}
} else {
vendor_lttpr_write_data_vs[3] = 0;
vendor_lttpr_write_data_pe[3] = 0;
for (lane = 0; lane < lane_count; lane++) {
vendor_lttpr_write_data_vs[3] |=
lt_settings->dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET << (2 * lane);
vendor_lttpr_write_data_pe[3] |=
lt_settings->dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET << (2 * lane);
}
/* Vendor specific: Update VS and PE to DPRX requested value */
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_vs[0],
sizeof(vendor_lttpr_write_data_vs));
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_pe[0],
sizeof(vendor_lttpr_write_data_pe));
dpcd_set_lane_settings(
link,
lt_settings,
0);
}
/* 3. wait receiver to lock-on*/
wait_time_microsec = lt_settings->cr_pattern_time;
dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested drive
* settings as set by the sink
*/
dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
0);
/* 5. check CR done*/
if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
status = LINK_TRAINING_SUCCESS;
break;
}
/* 6. max VS reached*/
if (dp_is_max_vs_reached(lt_settings))
break;
/* 7. same lane settings */
/* Note: settings are the same for all lanes,
* so comparing first lane is sufficient
*/
if (lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
retries_cr++;
else
retries_cr = 0;
/* 8. update VS/PE/PC2 in lt_settings*/
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
retry_count++;
}
if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) {
ASSERT(0);
DC_LOG_ERROR("%s: Link Training Error, could not get CR after %d tries. Possibly voltage swing issue",
__func__,
LINK_TRAINING_MAX_CR_RETRY);
}
status = dp_get_cr_failure(lane_count, dpcd_lane_status);
}
/* Perform Channel EQ Sequence */
if (status == LINK_TRAINING_SUCCESS) {
enum dc_dp_training_pattern tr_pattern;
uint32_t retries_ch_eq;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
/* Note: also check that TPS4 is a supported feature*/
tr_pattern = lt_settings->pattern_for_eq;
dp_set_hw_training_pattern(link, link_res, tr_pattern, 0);
status = LINK_TRAINING_EQ_FAIL_EQ;
for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT;
retries_ch_eq++) {
dp_set_hw_lane_settings(link, link_res, lt_settings, 0);
vendor_lttpr_write_data_vs[3] = 0;
vendor_lttpr_write_data_pe[3] = 0;
for (lane = 0; lane < lane_count; lane++) {
vendor_lttpr_write_data_vs[3] |=
lt_settings->dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET << (2 * lane);
vendor_lttpr_write_data_pe[3] |=
lt_settings->dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET << (2 * lane);
}
/* Vendor specific: Update VS and PE to DPRX requested value */
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_vs[0],
sizeof(vendor_lttpr_write_data_vs));
core_link_write_dpcd(
link,
vendor_lttpr_write_address,
&vendor_lttpr_write_data_pe[0],
sizeof(vendor_lttpr_write_data_pe));
/* 2. update DPCD*/
if (!retries_ch_eq)
/* EPR #361076 - write as a 5-byte burst,
* but only for the 1-st iteration
*/
dpcd_set_lt_pattern_and_lane_settings(
link,
lt_settings,
tr_pattern, 0);
else
dpcd_set_lane_settings(link, lt_settings, 0);
/* 3. wait for receiver to lock-on*/
wait_time_microsec = lt_settings->eq_pattern_time;
dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested
* drive settings as set by the sink
*/
dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
0);
/* 5. check CR done*/
if (!dp_is_cr_done(lane_count, dpcd_lane_status)) {
status = LINK_TRAINING_EQ_FAIL_CR;
break;
}
/* 6. check CHEQ done*/
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)) {
status = LINK_TRAINING_SUCCESS;
break;
}
/* 7. update VS/PE/PC2 in lt_settings*/
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
}
}
return status;
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef __DC_LINK_DP_FIXED_VS_PE_RETIMER_H__
#define __DC_LINK_DP_FIXED_VS_PE_RETIMER_H__
#include "link_dp_training.h"
enum link_training_result dp_perform_fixed_vs_pe_training_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings);
void dp_fixed_vs_pe_set_retimer_lane_settings(
struct dc_link *link,
const union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX],
uint8_t lane_count);
void dp_fixed_vs_pe_read_lane_adjust(
struct dc_link *link,
union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX]);
#endif /* __DC_LINK_DP_FIXED_VS_PE_RETIMER_H__ */

View File

@ -26,6 +26,7 @@
#ifndef __LINK_DPCD_H__
#define __LINK_DPCD_H__
#include "link.h"
#include "dpcd_defs.h"
enum dc_status core_link_read_dpcd(
struct dc_link *link,

View File

@ -133,6 +133,11 @@ static const uint8_t DP_SINK_DEVICE_STR_ID_2[] = {7, 1, 8, 7, 5};
static const u8 DP_SINK_BRANCH_DEV_NAME_7580[] = "7580\x80u";
/*Travis*/
static const uint8_t DP_VGA_LVDS_CONVERTER_ID_2[] = "sivarT";
/*Nutmeg*/
static const uint8_t DP_VGA_LVDS_CONVERTER_ID_3[] = "dnomlA";
/*MST Dock*/
static const uint8_t SYNAPTICS_DEVICE_ID[] = "SYNA";