[PATCH 1/2] drm/amd/display: stabilize DCN35 ODM update sequencing

Mauricio Bellon <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx,org.kernel.vger.stable
Message-ID <[email protected]>
From: Mauricio Bellon <[email protected]>

Two related direct DisplayPort failures occur with a high-refresh DSC
stream using two-way ODM on DCN35. A normal transition from 4K120 to
4K144 can commit successfully but produce no visible output. After 4K144
is working, a long-pulse hotplug can reconstruct the same DSC/ODM stream
but leave the monitor black.

The horizontal timing divider changes together with the ODM topology.
DCN35 does not expose wait_odm_doublebuffer_pending_clear(), so the
common completion path can continue before the divider update has
latched. Hook up the compatible DCN32 wait implementation and wait
immediately after dcn35_update_odm() programs combine or bypass.

Keep the divider under manual control while changing the ODM topology.
After the pending update completes, restore manual mode explicitly from
the target stream using the same FRL, horizontal-timing divisibility, and
virtual-signal policy as stream timing setup. Keep that restoration
separate from hwss_wait_for_odm_update_pending_complete() so the wait
helper remains limited to waiting for hardware updates.

The affected 4K144 timing has odd horizontal blank boundaries and
requires manual divider mode, while the 4K120 timing is divisible by two
and uses automatic mode. The ODM combine and bypass functions explicitly
program OTG_H_TIMING_DIV_MODE before completion.

Cc: [email protected]
Signed-off-by: Mauricio Bellon <[email protected]>
---
 .../amd/display/dc/hwss/dcn35/dcn35_hwseq.c   | 20 +++++++++++++++++--
 .../amd/display/dc/optc/dcn35/dcn35_optc.c    |  2 ++
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
index 6e1079377..a5b914b81 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
@@ -433,18 +433,30 @@ void dcn35_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *
 {
 	(void)context;
 	struct pipe_ctx *odm_pipe;
+	struct pipe_ctx *current_pipe_ctx =
+		&dc->current_state->res_ctx.pipe_ctx[pipe_ctx->pipe_idx];
+	struct timing_generator *tg = pipe_ctx->stream_res.tg;
 	int opp_cnt = 0;
 	int opp_inst[MAX_PIPES] = {0};
 	int odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, false);
 	int last_odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, true);
 	struct mpc *mpc = dc->res_pool->mpc;
+	bool target_manual_mode;
+	bool update_odm;
 	int i;
 
 	opp_cnt = get_odm_config(pipe_ctx, opp_inst);
+	update_odm = opp_cnt > 1 || current_pipe_ctx->next_odm_pipe;
+	target_manual_mode = dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) ||
+		!is_h_timing_divisible_by_2(pipe_ctx->stream) ||
+		dc_is_virtual_signal(pipe_ctx->stream->signal);
 
 	if (!(pipe_ctx->stream_res.hpo_frl_stream_enc &&
 			pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_fifo_odm_enabled &&
 			pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_fifo_odm_enabled(pipe_ctx->stream_res.hpo_frl_stream_enc))) {
+		if (update_odm && tg->funcs->set_h_timing_div_manual_mode)
+			tg->funcs->set_h_timing_div_manual_mode(tg, true);
+
 	if (opp_cnt > 1)
 		pipe_ctx->stream_res.tg->funcs->set_odm_combine(
 				pipe_ctx->stream_res.tg,
@@ -454,6 +466,12 @@ void dcn35_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *
 		pipe_ctx->stream_res.tg->funcs->set_odm_bypass(
 				pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing);
 
+		if (update_odm && tg->funcs->wait_odm_doublebuffer_pending_clear)
+			tg->funcs->wait_odm_doublebuffer_pending_clear(tg);
+
+		if (update_odm && tg->funcs->set_h_timing_div_manual_mode)
+			tg->funcs->set_h_timing_div_manual_mode(tg, target_manual_mode);
+
 	if (mpc->funcs->set_out_rate_control) {
 		for (i = 0; i < opp_cnt; ++i) {
 			mpc->funcs->set_out_rate_control(
@@ -472,8 +490,6 @@ void dcn35_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *
 	}
 
 	if (pipe_ctx->stream_res.dsc) {
-		struct pipe_ctx *current_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[pipe_ctx->pipe_idx];
-
 		update_dsc_on_stream(pipe_ctx, pipe_ctx->stream->timing.flags.DSC != 0);
 
 		/* Check if no longer using pipe for ODM, then need to disconnect DSC for that pipe */
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
index 897f857db..c26a2fd70 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
@@ -603,6 +603,8 @@ static const struct timing_generator_funcs dcn35_tg_funcs = {
 		.set_odm_combine = optc35_set_odm_combine,
 		.get_optc_source = optc2_get_optc_source,
 		.wait_otg_disable = optc35_wait_otg_disable,
+		.wait_odm_doublebuffer_pending_clear =
+			optc32_wait_odm_doublebuffer_pending_clear,
 		.set_h_timing_div_manual_mode = optc32_set_h_timing_div_manual_mode,
 		.set_out_mux = optc3_set_out_mux,
 		.set_drr_trigger_window = optc3_set_drr_trigger_window,
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.