[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