From 2a5810782369b2128394e783d30728393c7bd62d Mon Sep 17 00:00:00 2001 From: James Lin Date: Mon, 22 Jun 2026 13:38:02 +0800 Subject: [PATCH] drm/amd/display: program dither on all OPP heads under ODM combine [why] The IGT test kms_plane_alpha_blend@coverage-vs-premult-vs-constant failed on DCN with ODM 2:1 combine enabled. The test compares the CRC of a coverage-blended plane against a premultiplied-blended plane and expects them to be identical. With ODM combine the whole-frame CRC mismatched (left segment matched, right segment differed). Root cause: dc_stream_set_dither_option() iterated the pipe_ctx array and broke out at the first pipe matching the stream (the OTG master / OPP instance 0), then programmed bit-depth reduction (dither) on that single OPP only. Under ODM combine there is more than one OPP head, so the secondary ODM segment (OPP instance 1) was never reprogrammed. When CRC capture requested dither off, only the left OPP got updated while the right OPP kept its previous dither setting, producing a different CRC for the right segment and thus a whole-frame CRC mismatch. [how] Use resource_get_otg_master_for_stream() and resource_get_opp_heads_for_otg_master() to retrieve every OPP head of the stream, then loop over all of them and call transform_set_pixel_storage_depth() and opp_program_bit_depth_reduction() on each. This keeps all OPP heads in sync under ODM combine, so dither is applied (or cleared) identically across every segment and the CRCs match. Reviewed-by: Wayne Lin Signed-off-by: James Lin Signed-off-by: George Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc.c | 53 +++++++++++++++--------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 28339e4b6d67..ec5d6ddeefbe 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -769,22 +769,23 @@ void dc_stream_set_dither_option(struct dc_stream_state *stream, { struct bit_depth_reduction_params params; struct dc_link *link = stream->link; - struct pipe_ctx *pipes = NULL; + struct resource_context *res_ctx = &link->dc->current_state->res_ctx; + struct pipe_ctx *otg_master; + struct pipe_ctx *opp_heads[MAX_PIPES]; + int opp_cnt; int i; - for (i = 0; i < MAX_PIPES; i++) { - if (link->dc->current_state->res_ctx.pipe_ctx[i].stream == - stream) { - pipes = &link->dc->current_state->res_ctx.pipe_ctx[i]; - break; - } - } - - if (!pipes) + otg_master = resource_get_otg_master_for_stream(res_ctx, stream); + if (!otg_master) return; if (option > DITHER_OPTION_MAX) return; + opp_cnt = resource_get_opp_heads_for_otg_master(otg_master, res_ctx, opp_heads); + + if (opp_cnt == 0) + return; + dc_exit_ips_for_hw_access(stream->ctx->dc); stream->dither_option = option; @@ -793,16 +794,30 @@ void dc_stream_set_dither_option(struct dc_stream_state *stream, resource_build_bit_depth_reduction_params(stream, ¶ms); stream->bit_depth_params = params; - if (pipes->plane_res.xfm && - pipes->plane_res.xfm->funcs->transform_set_pixel_storage_depth) { - pipes->plane_res.xfm->funcs->transform_set_pixel_storage_depth( - pipes->plane_res.xfm, - pipes->plane_res.scl_data.lb_params.depth, - &stream->bit_depth_params); - } + /* + * Program bit-depth reduction (dither) on every OPP head of the + * stream. Under ODM combine there is more than one OPP head and they + * must all be kept in sync, otherwise (e.g. when CRC capture requests + * dither off) a secondary ODM segment can keep dither enabled and + * produce a different CRC than the primary segment. + */ + for (i = 0; i < opp_cnt; i++) { + struct pipe_ctx *opp_head = opp_heads[i]; - pipes->stream_res.opp->funcs-> - opp_program_bit_depth_reduction(pipes->stream_res.opp, ¶ms); + if (opp_head->plane_res.xfm && + opp_head->plane_res.xfm->funcs->transform_set_pixel_storage_depth) { + opp_head->plane_res.xfm->funcs->transform_set_pixel_storage_depth( + opp_head->plane_res.xfm, + opp_head->plane_res.scl_data.lb_params.depth, + &stream->bit_depth_params); + } + + if (opp_head->stream_res.opp && + opp_head->stream_res.opp->funcs->opp_program_bit_depth_reduction) { + opp_head->stream_res.opp->funcs->opp_program_bit_depth_reduction( + opp_head->stream_res.opp, ¶ms); + } + } } bool dc_stream_set_gamut_remap(struct dc *dc, const struct dc_stream_state *stream)