From: Mauricio Bellon <mauriciobellon@gmail.com>
To: amd-gfx@lists.freedesktop.org
Cc: Wayne Lin <Wayne.Lin@amd.com>,
Mario Limonciello <mario.limonciello@amd.com>,
Mauricio Bellon <mbellon@lenovo.com>,
Harry Wentland <harry.wentland@amd.com>,
Leo Li <sunpeng.li@amd.com>,
Rodrigo Siqueira <siqueira@igalia.com>,
Alex Deucher <alexander.deucher@amd.com>,
christian.koenig@amd.com, stable@vger.kernel.org
Subject: [PATCH 1/2] drm/amd/display: stabilize DCN35 ODM update sequencing
Date: Tue, 25 Aug 2026 18:18:18 -0300 [thread overview]
Message-ID: <20260825211819.100210-2-mauriciobellon@gmail.com> (raw)
In-Reply-To: <20260825211819.100210-1-mauriciobellon@gmail.com>
From: Mauricio Bellon <mbellon@lenovo.com>
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: stable@vger.kernel.org
Signed-off-by: Mauricio Bellon <mbellon@lenovo.com>
---
.../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
next prev parent reply other threads:[~2026-08-26 12:46 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-25 21:18 [PATCH 0/2] drm/amd/display: stabilize DCN35 ODM and DSC sequencing Mauricio Bellon
2026-08-25 21:18 ` Mauricio Bellon [this message]
2026-08-25 21:18 ` [PATCH 2/2] drm/amd/display: enable sink DSC before source DSC Mauricio Bellon
2026-09-08 18:14 ` Zuo, Jerry
2026-09-08 20:00 ` Mauricio Bellon
2026-10-02 20:27 ` Mauricio Bellon
2026-08-25 21:23 ` [PATCH 0/2] drm/amd/display: stabilize DCN35 ODM and DSC sequencing Mario Limonciello
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260825211819.100210-2-mauriciobellon@gmail.com \
--to=mauriciobellon@gmail.com \
--cc=Wayne.Lin@amd.com \
--cc=alexander.deucher@amd.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=christian.koenig@amd.com \
--cc=harry.wentland@amd.com \
--cc=mario.limonciello@amd.com \
--cc=mbellon@lenovo.com \
--cc=siqueira@igalia.com \
--cc=stable@vger.kernel.org \
--cc=sunpeng.li@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.