Commit 832aa63b authored by Paul Hsieh's avatar Paul Hsieh Committed by Alex Deucher
Browse files

drm/amd/display: Reset PHY in link re-training



[Why]
Link training failed randomly when plugging USB-C display in/out.

[How]
If link training failed, reset PHY in link re-training.

Signed-off-by: default avatarPaul Hsieh <paul.hsieh@amd.com>
Reviewed-by: default avatarWenjing Liu <Wenjing.Liu@amd.com>
Acked-by: default avatarLeo Li <sunpeng.li@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent a4cea116
Loading
Loading
Loading
Loading
+4 −28
Original line number Original line Diff line number Diff line
@@ -1495,7 +1495,6 @@ static enum dc_status enable_link_dp(
	bool skip_video_pattern;
	bool skip_video_pattern;
	struct dc_link *link = stream->link;
	struct dc_link *link = stream->link;
	struct dc_link_settings link_settings = {0};
	struct dc_link_settings link_settings = {0};
	enum dp_panel_mode panel_mode;
	bool fec_enable;
	bool fec_enable;
	int i;
	int i;
	bool apply_seamless_boot_optimization = false;
	bool apply_seamless_boot_optimization = false;
@@ -1531,40 +1530,17 @@ static enum dc_status enable_link_dp(
	if (state->clk_mgr && !apply_seamless_boot_optimization)
	if (state->clk_mgr && !apply_seamless_boot_optimization)
		state->clk_mgr->funcs->update_clocks(state->clk_mgr, state, false);
		state->clk_mgr->funcs->update_clocks(state->clk_mgr, state, false);


	dp_enable_link_phy(
		link,
		pipe_ctx->stream->signal,
		pipe_ctx->clock_source->id,
		&link_settings);

	if (stream->sink_patches.dppowerup_delay > 0) {
		int delay_dp_power_up_in_ms = stream->sink_patches.dppowerup_delay;

		msleep(delay_dp_power_up_in_ms);
	}

	panel_mode = dp_get_panel_mode(link);
	dp_set_panel_mode(link, panel_mode);

	/* We need to do this before the link training to ensure the idle pattern in SST
	 * mode will be sent right after the link training */
	link->link_enc->funcs->connect_dig_be_to_fe(link->link_enc,
						    pipe_ctx->stream_res.stream_enc->id, true);
	skip_video_pattern = true;
	skip_video_pattern = true;


	if (link_settings.link_rate == LINK_RATE_LOW)
	if (link_settings.link_rate == LINK_RATE_LOW)
			skip_video_pattern = false;
			skip_video_pattern = false;


	if (link->aux_access_disabled) {
	if (perform_link_training_with_retries(
		dc_link_dp_perform_link_training_skip_aux(link, &link_settings);

		link->cur_link_settings = link_settings;
		status = DC_OK;
	} else if (perform_link_training_with_retries(
			link,
			&link_settings,
			&link_settings,
			skip_video_pattern,
			skip_video_pattern,
			LINK_TRAINING_ATTEMPTS)) {
			LINK_TRAINING_ATTEMPTS,
			pipe_ctx,
			pipe_ctx->stream->signal)) {
		link->cur_link_settings = link_settings;
		link->cur_link_settings = link_settings;
		status = DC_OK;
		status = DC_OK;
	}
	}
+56 −12
Original line number Original line Diff line number Diff line
@@ -1433,23 +1433,58 @@ enum link_training_result dc_link_dp_perform_link_training(
}
}


bool perform_link_training_with_retries(
bool perform_link_training_with_retries(
	struct dc_link *link,
	const struct dc_link_settings *link_setting,
	const struct dc_link_settings *link_setting,
	bool skip_video_pattern,
	bool skip_video_pattern,
	int attempts)
	int attempts,
	struct pipe_ctx *pipe_ctx,
	enum signal_type signal)
{
{
	uint8_t j;
	uint8_t j;
	uint8_t delay_between_attempts = LINK_TRAINING_RETRY_DELAY;
	uint8_t delay_between_attempts = LINK_TRAINING_RETRY_DELAY;
	struct dc_stream_state *stream = pipe_ctx->stream;
	struct dc_link *link = stream->link;
	enum dp_panel_mode panel_mode = dp_get_panel_mode(link);


	for (j = 0; j < attempts; ++j) {
	for (j = 0; j < attempts; ++j) {


		if (dc_link_dp_perform_link_training(
		dp_enable_link_phy(
			link,
			signal,
			pipe_ctx->clock_source->id,
			link_setting);

		if (stream->sink_patches.dppowerup_delay > 0) {
			int delay_dp_power_up_in_ms = stream->sink_patches.dppowerup_delay;

			msleep(delay_dp_power_up_in_ms);
		}

		dp_set_panel_mode(link, panel_mode);

		/* We need to do this before the link training to ensure the idle pattern in SST
		 * mode will be sent right after the link training
		 */
		link->link_enc->funcs->connect_dig_be_to_fe(link->link_enc,
								pipe_ctx->stream_res.stream_enc->id, true);

		if (link->aux_access_disabled) {
			dc_link_dp_perform_link_training_skip_aux(link, link_setting);
			return true;
		} else if (dc_link_dp_perform_link_training(
				link,
				link,
				link_setting,
				link_setting,
				skip_video_pattern) == LINK_TRAINING_SUCCESS)
				skip_video_pattern) == LINK_TRAINING_SUCCESS)
			return true;
			return true;


		/* latest link training still fail, skip delay and keep PHY on
		 */
		if (j == (attempts - 1))
			break;

		dp_disable_link_phy(link, signal);

		msleep(delay_between_attempts);
		msleep(delay_between_attempts);

		delay_between_attempts += LINK_TRAINING_RETRY_DELAY;
		delay_between_attempts += LINK_TRAINING_RETRY_DELAY;
	}
	}


@@ -2770,18 +2805,27 @@ bool dc_link_handle_hpd_rx_irq(struct dc_link *link, union hpd_irq_data *out_hpd
					sizeof(hpd_irq_dpcd_data),
					sizeof(hpd_irq_dpcd_data),
					"Status: ");
					"Status: ");


		perform_link_training_with_retries(link,
			&link->cur_link_settings,
			true, LINK_TRAINING_ATTEMPTS);

		for (i = 0; i < MAX_PIPES; i++) {
		for (i = 0; i < MAX_PIPES; i++) {
			pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
			pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
			if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link)
				break;
		}

		if (pipe_ctx == NULL || pipe_ctx->stream == NULL)
			return false;

		dp_disable_link_phy(link, pipe_ctx->stream->signal);

		perform_link_training_with_retries(&link->cur_link_settings,
			true, LINK_TRAINING_ATTEMPTS,
			pipe_ctx,
			pipe_ctx->stream->signal);

		if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link &&
		if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link &&
				pipe_ctx->stream->dpms_off == false &&
				pipe_ctx->stream->dpms_off == false &&
				pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
				pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
			dc_link_allocate_mst_payload(pipe_ctx);
			dc_link_allocate_mst_payload(pipe_ctx);
		}
		}
		}


		status = false;
		status = false;
		if (out_link_loss)
		if (out_link_loss)
+3 −11
Original line number Original line Diff line number Diff line
@@ -333,20 +333,12 @@ void dp_retrain_link_dp_test(struct dc_link *link,
			memset(&link->cur_link_settings, 0,
			memset(&link->cur_link_settings, 0,
				sizeof(link->cur_link_settings));
				sizeof(link->cur_link_settings));


			link->link_enc->funcs->enable_dp_output(
						link->link_enc,
						link_setting,
						pipes[i].clock_source->id);
			link->cur_link_settings = *link_setting;

			dp_receiver_power_ctrl(link, true);

			perform_link_training_with_retries(
			perform_link_training_with_retries(
					link,
					link_setting,
					link_setting,
					skip_video_pattern,
					skip_video_pattern,
					LINK_TRAINING_ATTEMPTS);
					LINK_TRAINING_ATTEMPTS,

					&pipes[i],
					SIGNAL_TYPE_DISPLAY_PORT);


			link->dc->hwss.enable_stream(&pipes[i]);
			link->dc->hwss.enable_stream(&pipes[i]);


+3 −2
Original line number Original line Diff line number Diff line
@@ -57,10 +57,11 @@ void decide_link_settings(
	struct dc_link_settings *link_setting);
	struct dc_link_settings *link_setting);


bool perform_link_training_with_retries(
bool perform_link_training_with_retries(
	struct dc_link *link,
	const struct dc_link_settings *link_setting,
	const struct dc_link_settings *link_setting,
	bool skip_video_pattern,
	bool skip_video_pattern,
	int attempts);
	int attempts,
	struct pipe_ctx *pipe_ctx,
	enum signal_type signal);


bool is_mst_supported(struct dc_link *link);
bool is_mst_supported(struct dc_link *link);