Commit 9c6669c2 authored by Michael Strauss's avatar Michael Strauss Committed by Alex Deucher
Browse files

drm/amd/display: Fix Link Override Sequencing When Switching Between DIO/HPO



[WHY]
When performing certain link maintenance compliance tests or forcing link
settings, changing between 128b/132b and 8b/10b rates no longer works on
some ASICs. Some rate divider updates only occur when we set
timings or validate state, which is not performed currently when toggling
DPMS to change rates.

[HOW]
Re-calculate dividers and reprogram audio when switching between DIO
and HPO through DP compliance/escape code path.
Add OTG disable/re-enable so we don't touch the clock while OTG is active.
Acquire dcLock before forcing link settings to avoid thread synchronization
errors due to added programming in escape code path and potential HPD
interrupts.

Reviewed-by: default avatarGeorge Shen <george.shen@amd.com>
Signed-off-by: default avatarMichael Strauss <michael.strauss@amd.com>
Signed-off-by: default avatarMike Katsnelson <mike.katsnelson@amd.com>
Signed-off-by: default avatarRay Wu <ray.wu@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 51496c77
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1381,7 +1381,7 @@ static void populate_audio_dp_link_info(
	}
}

static void build_audio_output(
void build_audio_output(
	struct dc_state *state,
	const struct pipe_ctx *pipe_ctx,
	struct audio_output *audio_output)
+4 −0
Original line number Diff line number Diff line
@@ -110,5 +110,9 @@ void dce110_enable_dp_link_output(
		enum signal_type signal,
		enum clock_source_id clock_source,
		const struct dc_link_settings *link_settings);
void build_audio_output(
		struct dc_state *state,
		const struct pipe_ctx *pipe_ctx,
		struct audio_output *audio_output);
#endif /* __DC_HWSS_DCE110_H__ */
+5 −0
Original line number Diff line number Diff line
@@ -223,6 +223,11 @@ struct resource_funcs {
			const struct dc_stream_state *stream);
	bool (*program_mcache_pipe_config)(struct dc_state *context,
		const struct dc_mcache_params *mcache_params);
	enum dc_status (*update_dc_state_for_encoder_switch)(struct dc_link *link,
		struct dc_link_settings *link_setting,
		uint8_t pipe_count,
		struct pipe_ctx *pipes,
		struct audio_output *audio_output);
};

struct audio_support{
+57 −6
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include "dm_helpers.h"
#include "dc_dmub_srv.h"
#include "dce/dmub_hw_lock_mgr.h"
#include "clk_mgr.h"

#define DC_LOGGER \
	link->ctx->logger
@@ -67,10 +68,17 @@ static void dp_retrain_link_dp_test(struct dc_link *link,
{
	struct pipe_ctx *pipes[MAX_PIPES];
	struct dc_state *state = link->dc->current_state;
	struct dc_stream_update stream_update = { 0 };
	bool dpms_off = false;
	bool needs_divider_update = false;
	bool was_hpo_acquired = resource_is_hpo_acquired(link->dc->current_state);
	bool is_hpo_acquired;
	uint8_t count;
	int i;
	struct audio_output audio_output[MAX_PIPES];

	needs_divider_update = (link->dc->link_srv->dp_get_encoding_format(link_setting) !=
	link->dc->link_srv->dp_get_encoding_format((const struct dc_link_settings *) &link->cur_link_settings));

	udelay(100);

@@ -83,16 +91,59 @@ static void dp_retrain_link_dp_test(struct dc_link *link,
				link->dc,
				state,
				pipes[i]);

		// Disable OTG and re-enable after updating clocks
		pipes[i]->stream_res.tg->funcs->disable_crtc(pipes[i]->stream_res.tg);
	}

	if (needs_divider_update && link->dc->res_pool->funcs->update_dc_state_for_encoder_switch) {
		link->dc->res_pool->funcs->update_dc_state_for_encoder_switch(link,
				link_setting, count,
				*pipes, &audio_output[0]);
		for (i = 0; i < count; i++) {
			pipes[i]->clock_source->funcs->program_pix_clk(
					pipes[i]->clock_source,
					&pipes[i]->stream_res.pix_clk_params,
					link->dc->link_srv->dp_get_encoding_format(&pipes[i]->link_config.dp_link_settings),
					&pipes[i]->pll_settings);

			if (pipes[i]->stream_res.audio != NULL) {
				const struct link_hwss *link_hwss = get_link_hwss(
					link, &pipes[i]->link_res);

				link_hwss->setup_audio_output(pipes[i], &audio_output[i],
						pipes[i]->stream_res.audio->inst);

				pipes[i]->stream_res.audio->funcs->az_configure(
						pipes[i]->stream_res.audio,
						pipes[i]->stream->signal,
						&audio_output[i].crtc_info,
						&pipes[i]->stream->audio_info,
						&audio_output[i].dp_link_info);

				if (link->dc->config.disable_hbr_audio_dp2 &&
						pipes[i]->stream_res.audio->funcs->az_disable_hbr_audio &&
						link->dc->link_srv->dp_is_128b_132b_signal(pipes[i]))
					pipes[i]->stream_res.audio->funcs->az_disable_hbr_audio(pipes[i]->stream_res.audio);
			}
		}
	}

	if (link->dc->hwss.setup_hpo_hw_control) {
	// Toggle on HPO I/O if necessary
	is_hpo_acquired = resource_is_hpo_acquired(state);
		if (was_hpo_acquired != is_hpo_acquired)
	if (was_hpo_acquired != is_hpo_acquired && link->dc->hwss.setup_hpo_hw_control)
		link->dc->hwss.setup_hpo_hw_control(link->dc->hwseq, is_hpo_acquired);
	}

	for (i = count-1; i >= 0; i--)
		link_set_dpms_on(state, pipes[i]);
	for (i = 0; i < count; i++)
		pipes[i]->stream_res.tg->funcs->enable_crtc(pipes[i]->stream_res.tg);

	// Set DPMS on with stream update
	for (i = 0; i < state->stream_count; i++)
		if (state->streams[i] && state->streams[i]->link && state->streams[i]->link == link) {
			stream_update.stream = state->streams[i];
			stream_update.dpms_off = &dpms_off;
			dc_update_planes_and_stream(state->clk_mgr->ctx->dc, NULL, 0, state->streams[i], &stream_update);
		}
}

static void dp_test_send_link_training(struct dc_link *link)
+35 −1
Original line number Diff line number Diff line
@@ -1850,7 +1850,9 @@ static struct resource_funcs dcn31_res_pool_funcs = {
	.patch_unknown_plane_state = dcn20_patch_unknown_plane_state,
	.get_panel_config_defaults = dcn31_get_panel_config_defaults,
	.get_det_buffer_size = dcn31_get_det_buffer_size,
	.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe
	.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe,
	.update_dc_state_for_encoder_switch = dcn31_update_dc_state_for_encoder_switch,
	.build_pipe_pix_clk_params = dcn20_build_pipe_pix_clk_params
};

static struct clock_source *dcn30_clock_source_create(
@@ -2231,3 +2233,35 @@ struct resource_pool *dcn31_create_resource_pool(
	kfree(pool);
	return NULL;
}

enum dc_status dcn31_update_dc_state_for_encoder_switch(struct dc_link *link,
	struct dc_link_settings *link_setting,
	uint8_t pipe_count,
	struct pipe_ctx *pipes,
	struct audio_output *audio_output)
{
	struct dc_state *state = link->dc->current_state;
	int i;

#if defined(CONFIG_DRM_AMD_DC_FP)
	for (i = 0; i < state->stream_count; i++)
		if (state->streams[i] && state->streams[i]->link && state->streams[i]->link == link)
			link->dc->hwss.calculate_pix_rate_divider((struct dc *)link->dc, state, state->streams[i]);

	for (i = 0; i < pipe_count; i++) {
		link->dc->res_pool->funcs->build_pipe_pix_clk_params(&pipes[i]);

		// Setup audio
		if (pipes[i].stream_res.audio != NULL)
			build_audio_output(state, &pipes[i], &audio_output[i]);
	}
#else
	/* This DCN requires rate divider updates and audio reprogramming to allow DP1<-->DP2 link rate switching,
	 * but the above will not compile on architectures without an FPU.
	 */
	DC_LOG_WARNING("%s: DP1<-->DP2 link retraining will not work on this DCN on non-FPU platforms", __func__);
	ASSERT(0);
#endif

	return DC_OK;
}
Loading