Commit 7727e7b6 authored by Michael Strauss's avatar Michael Strauss Committed by Alex Deucher
Browse files

drm/amd/display: Improve robustness of FIXED_VS link training at DP1 rates



[WHY]
New sequence for transparent mode DP1.x link training was provided by LTTPR
vendor

[HOW]
Implement new FIXED_VS sequence, increase LT retry count to minimize
any potential intermittent lightup failures

Reviewed-by: default avatarJun Lei <Jun.Lei@amd.com>
Acked-by: default avatarQingqing Zhuo <qingqing.zhuo@amd.com>
Signed-off-by: default avatarMichael Strauss <michael.strauss@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 554836cc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -405,6 +405,7 @@ struct dc_config {
	bool force_bios_enable_lttpr;
	uint8_t force_bios_fixed_vs;
	int sdpif_request_limit_words_per_umc;
	bool use_old_fixed_vs_sequence;
	bool disable_subvp_drr;
};

+2 −0
Original line number Diff line number Diff line
@@ -1965,6 +1965,8 @@ static bool dcn31_resource_construct(
	dc->caps.color.mpc.ogam_rom_caps.hlg = 0;
	dc->caps.color.mpc.ocsc = 1;

	dc->config.use_old_fixed_vs_sequence = true;

	/* Use pipe context based otg sync logic */
	dc->config.use_pipe_ctx_sync_logic = true;

+7 −1
Original line number Diff line number Diff line
@@ -2035,6 +2035,12 @@ static enum dc_status enable_link_dp(struct dc_state *state,
	uint32_t post_oui_delay = 30; // 30ms
	/* Reduce link bandwidth between failed link training attempts. */
	bool do_fallback = false;
	int lt_attempts = LINK_TRAINING_ATTEMPTS;

	// Increase retry count if attempting DP1.x on FIXED_VS link
	if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) &&
			link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING)
		lt_attempts = 10;

	// check for seamless boot
	for (i = 0; i < state->stream_count; i++) {
@@ -2099,7 +2105,7 @@ static enum dc_status enable_link_dp(struct dc_state *state,

	if (perform_link_training_with_retries(link_settings,
					       skip_video_pattern,
					       LINK_TRAINING_ATTEMPTS,
					       lt_attempts,
					       pipe_ctx,
					       pipe_ctx->stream->signal,
					       do_fallback)) {
+4 −1
Original line number Diff line number Diff line
@@ -1496,6 +1496,9 @@ enum link_training_result dp_perform_link_training(
	 * Non-LT AUX transactions inside training mode.
	 */
	if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && encoding == DP_8b_10b_ENCODING)
		if (link->dc->config.use_old_fixed_vs_sequence)
			status = dp_perform_fixed_vs_pe_training_sequence_legacy(link, link_res, &lt_settings);
		else
			status = dp_perform_fixed_vs_pe_training_sequence(link, link_res, &lt_settings);
	else if (encoding == DP_8b_10b_ENCODING)
		status = dp_perform_8b_10b_link_training(link, link_res, &lt_settings);
+377 −1
Original line number Diff line number Diff line
@@ -223,7 +223,7 @@ static enum link_training_result perform_fixed_vs_pe_nontransparent_training_seq
}


enum link_training_result dp_perform_fixed_vs_pe_training_sequence(
enum link_training_result dp_perform_fixed_vs_pe_training_sequence_legacy(
	struct dc_link *link,
	const struct link_resource *link_res,
	struct link_training_settings *lt_settings)
@@ -577,3 +577,379 @@ enum link_training_result dp_perform_fixed_vs_pe_training_sequence(

	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_parse_lttpr_repeater_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, 0x6E};
	const uint8_t vendor_lttpr_write_data_adicora_eq1[4] = {0x1, 0x55, 0x63, 0x2E};
	const uint8_t vendor_lttpr_write_data_adicora_eq2[4] = {0x1, 0x55, 0x63, 0x01};
	const uint8_t vendor_lttpr_write_data_adicora_eq3[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(link_dp_get_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};

		core_link_write_dpcd(
				link,
				vendor_lttpr_write_address,
				&vendor_lttpr_write_data_adicora_eq1[0],
				sizeof(vendor_lttpr_write_data_adicora_eq1));
		core_link_write_dpcd(
				link,
				vendor_lttpr_write_address,
				&vendor_lttpr_write_data_adicora_eq2[0],
				sizeof(vendor_lttpr_write_data_adicora_eq2));

		/* 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);

				core_link_write_dpcd(
					link,
					vendor_lttpr_write_address,
					&vendor_lttpr_write_data_adicora_eq3[0],
					sizeof(vendor_lttpr_write_data_adicora_eq3));
			} 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;
}
Loading