diff options
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3564-drm-amd-display-re-structure-odm-to-allow-4-to-1-sup.patch')
-rw-r--r-- | meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3564-drm-amd-display-re-structure-odm-to-allow-4-to-1-sup.patch | 1142 |
1 files changed, 1142 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3564-drm-amd-display-re-structure-odm-to-allow-4-to-1-sup.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3564-drm-amd-display-re-structure-odm-to-allow-4-to-1-sup.patch new file mode 100644 index 00000000..3a24bee8 --- /dev/null +++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3564-drm-amd-display-re-structure-odm-to-allow-4-to-1-sup.patch @@ -0,0 +1,1142 @@ +From 2141ed4abbffcf5e3b7adbd7381bdfd572f138c0 Mon Sep 17 00:00:00 2001 +From: Dmytro Laktyushkin <Dmytro.Laktyushkin@amd.com> +Date: Tue, 6 Aug 2019 17:17:28 -0400 +Subject: [PATCH 3564/4256] drm/amd/display: re structure odm to allow 4 to 1 + support + +Currently odm is handled using top_bottom pipe by special casing +the differing opps to differentiate from mpc combine. + +Since top/bottom pipe list was made to track mpc muxing this creates +difficulties in adding a 4 pipe odm case support. + +Rather than continue using mpc combine list, this change reworks odm +to use it's own linked list to keep track of odm combine pipes. This +also opens up options for using mpo with odm, if a practical use case +is ever found. + +Signed-off-by: Dmytro Laktyushkin <Dmytro.Laktyushkin@amd.com> +Reviewed-by: Charlene Liu <Charlene.Liu@amd.com> +Acked-by: Leo Li <sunpeng.li@amd.com> +--- + drivers/gpu/drm/amd/display/dc/core/dc.c | 10 +- + .../gpu/drm/amd/display/dc/core/dc_link_dp.c | 33 ++- + .../drm/amd/display/dc/core/dc_link_hwss.c | 36 +-- + .../gpu/drm/amd/display/dc/core/dc_resource.c | 150 ++++------- + .../display/dc/dce110/dce110_hw_sequencer.c | 7 +- + .../amd/display/dc/dcn10/dcn10_hw_sequencer.c | 2 +- + .../drm/amd/display/dc/dcn20/dcn20_hwseq.c | 90 ++++--- + .../drm/amd/display/dc/dcn20/dcn20_resource.c | 244 ++++++++++++------ + .../display/dc/dcn20/dcn20_stream_encoder.c | 2 +- + .../gpu/drm/amd/display/dc/inc/core_types.h | 2 + + .../amd/display/dc/inc/hw/stream_encoder.h | 2 +- + drivers/gpu/drm/amd/display/dc/inc/resource.h | 3 - + 12 files changed, 316 insertions(+), 265 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c +index af2586b5c238..bc713677faa9 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc.c +@@ -1869,9 +1869,7 @@ static void commit_planes_do_stream_update(struct dc *dc, + for (j = 0; j < dc->res_pool->pipe_count; j++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; + +- if (!pipe_ctx->top_pipe && +- pipe_ctx->stream && +- pipe_ctx->stream == stream) { ++ if (!pipe_ctx->top_pipe && !pipe_ctx->prev_odm_pipe && pipe_ctx->stream == stream) { + + if (stream_update->periodic_interrupt0 && + dc->hwss.setup_periodic_interrupt) +@@ -1897,7 +1895,7 @@ static void commit_planes_do_stream_update(struct dc *dc, + + if (stream_update->dither_option) { + #if defined(CONFIG_DRM_AMD_DC_DCN2_0) +- struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); ++ struct pipe_ctx *odm_pipe = pipe_ctx->next_odm_pipe; + #endif + resource_build_bit_depth_reduction_params(pipe_ctx->stream, + &pipe_ctx->stream->bit_depth_params); +@@ -1905,10 +1903,12 @@ static void commit_planes_do_stream_update(struct dc *dc, + &stream->bit_depth_params, + &stream->clamping); + #if defined(CONFIG_DRM_AMD_DC_DCN2_0) +- if (odm_pipe) ++ while (odm_pipe) { + odm_pipe->stream_res.opp->funcs->opp_program_fmt(odm_pipe->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); ++ odm_pipe = odm_pipe->next_odm_pipe; ++ } + #endif + } + +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +index 2e87942b3e9c..2aa44b28b673 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +@@ -3095,14 +3095,19 @@ static void set_crtc_test_pattern(struct dc_link *link, + controller_test_pattern, color_depth); + #if defined(CONFIG_DRM_AMD_DC_DCN2_0) + else if (opp->funcs->opp_set_disp_pattern_generator) { +- struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); ++ struct pipe_ctx *odm_pipe; ++ int opp_cnt = 1; + +- if (bot_odm_pipe) { +- struct output_pixel_processor *bot_opp = bot_odm_pipe->stream_res.opp; ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) ++ opp_cnt++; + +- bot_opp->funcs->opp_program_bit_depth_reduction(bot_opp, ¶ms); +- width /= 2; +- bot_opp->funcs->opp_set_disp_pattern_generator(bot_opp, ++ width /= opp_cnt; ++ ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { ++ struct output_pixel_processor *odm_opp = odm_pipe->stream_res.opp; ++ ++ odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms); ++ odm_opp->funcs->opp_set_disp_pattern_generator(odm_opp, + controller_test_pattern, + color_depth, + NULL, +@@ -3131,14 +3136,18 @@ static void set_crtc_test_pattern(struct dc_link *link, + color_depth); + #if defined(CONFIG_DRM_AMD_DC_DCN2_0) + else if (opp->funcs->opp_set_disp_pattern_generator) { +- struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); ++ struct pipe_ctx *odm_pipe; ++ int opp_cnt = 1; ++ ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) ++ opp_cnt++; + +- if (bot_odm_pipe) { +- struct output_pixel_processor *bot_opp = bot_odm_pipe->stream_res.opp; ++ width /= opp_cnt; ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { ++ struct output_pixel_processor *odm_opp = odm_pipe->stream_res.opp; + +- bot_opp->funcs->opp_program_bit_depth_reduction(bot_opp, ¶ms); +- width /= 2; +- bot_opp->funcs->opp_set_disp_pattern_generator(bot_opp, ++ odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms); ++ odm_opp->funcs->opp_set_disp_pattern_generator(odm_opp, + CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, + color_depth, + NULL, +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c +index 35c5467e60e8..fe9a4e4b9d1f 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c +@@ -275,7 +275,7 @@ void dp_retrain_link_dp_test(struct dc_link *link, + + for (i = 0; i < MAX_PIPES; i++) { + if (pipes[i].stream != NULL && +- !pipes[i].top_pipe && ++ !pipes[i].top_pipe && !pipes[i].prev_odm_pipe && + pipes[i].stream->link != NULL && + pipes[i].stream_res.stream_enc != NULL) { + udelay(100); +@@ -381,7 +381,11 @@ void dp_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) + struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc; + struct dc *core_dc = pipe_ctx->stream->ctx->dc; + struct dc_stream_state *stream = pipe_ctx->stream; +- struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); ++ struct pipe_ctx *odm_pipe; ++ int opp_cnt = 1; ++ ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) ++ opp_cnt++; + + if (enable) { + struct dsc_config dsc_cfg; +@@ -389,26 +393,24 @@ void dp_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) + enum optc_dsc_mode optc_dsc_mode; + + /* Enable DSC hw block */ +- dsc_cfg.pic_width = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right; ++ dsc_cfg.pic_width = (stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right) / opp_cnt; + dsc_cfg.pic_height = stream->timing.v_addressable + stream->timing.v_border_top + stream->timing.v_border_bottom; + dsc_cfg.pixel_encoding = stream->timing.pixel_encoding; + dsc_cfg.color_depth = stream->timing.display_color_depth; + dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg; ++ ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % opp_cnt == 0); ++ dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt; ++ ++ dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg); ++ dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst); ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { ++ struct display_stream_compressor *odm_dsc = odm_pipe->stream_res.dsc; + +- if (odm_pipe) { +- struct display_stream_compressor *bot_dsc = odm_pipe->stream_res.dsc; +- +- dsc_cfg.pic_width /= 2; +- ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % 2 == 0); +- dsc_cfg.dc_dsc_cfg.num_slices_h /= 2; +- dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg); +- bot_dsc->funcs->dsc_set_config(bot_dsc, &dsc_cfg, &dsc_optc_cfg); +- dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst); +- bot_dsc->funcs->dsc_enable(bot_dsc, odm_pipe->stream_res.opp->inst); +- } else { +- dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg); +- dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst); ++ odm_dsc->funcs->dsc_set_config(odm_dsc, &dsc_cfg, &dsc_optc_cfg); ++ odm_dsc->funcs->dsc_enable(odm_dsc, odm_pipe->stream_res.opp->inst); + } ++ dsc_cfg.dc_dsc_cfg.num_slices_h *= opp_cnt; ++ dsc_cfg.pic_width *= opp_cnt; + + optc_dsc_mode = dsc_optc_cfg.is_pixel_format_444 ? OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED; + +@@ -449,7 +451,7 @@ void dp_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) + + /* disable DSC block */ + pipe_ctx->stream_res.dsc->funcs->dsc_disable(pipe_ctx->stream_res.dsc); +- if (odm_pipe) ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + odm_pipe->stream_res.dsc->funcs->dsc_disable(odm_pipe->stream_res.dsc); + } + } +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +index fa94dfc04dce..1464f4c60089 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +@@ -1114,8 +1114,9 @@ struct pipe_ctx *resource_get_head_pipe_for_stream( + { + int i; + for (i = 0; i < MAX_PIPES; i++) { +- if (res_ctx->pipe_ctx[i].stream == stream && +- !res_ctx->pipe_ctx[i].top_pipe) { ++ if (res_ctx->pipe_ctx[i].stream == stream ++ && !res_ctx->pipe_ctx[i].top_pipe ++ && !res_ctx->pipe_ctx[i].prev_odm_pipe) { + return &res_ctx->pipe_ctx[i]; + break; + } +@@ -1123,15 +1124,11 @@ struct pipe_ctx *resource_get_head_pipe_for_stream( + return NULL; + } + +-static struct pipe_ctx *resource_get_tail_pipe_for_stream( ++static struct pipe_ctx *resource_get_tail_pipe( + struct resource_context *res_ctx, +- struct dc_stream_state *stream) ++ struct pipe_ctx *head_pipe) + { +- struct pipe_ctx *head_pipe, *tail_pipe; +- head_pipe = resource_get_head_pipe_for_stream(res_ctx, stream); +- +- if (!head_pipe) +- return NULL; ++ struct pipe_ctx *tail_pipe; + + tail_pipe = head_pipe->bottom_pipe; + +@@ -1147,31 +1144,20 @@ static struct pipe_ctx *resource_get_tail_pipe_for_stream( + * A free_pipe for a stream is defined here as a pipe + * that has no surface attached yet + */ +-static struct pipe_ctx *acquire_free_pipe_for_stream( ++static struct pipe_ctx *acquire_free_pipe_for_head( + struct dc_state *context, + const struct resource_pool *pool, +- struct dc_stream_state *stream) ++ struct pipe_ctx *head_pipe) + { + int i; + struct resource_context *res_ctx = &context->res_ctx; + +- struct pipe_ctx *head_pipe = NULL; +- +- /* Find head pipe, which has the back end set up*/ +- +- head_pipe = resource_get_head_pipe_for_stream(res_ctx, stream); +- +- if (!head_pipe) { +- ASSERT(0); +- return NULL; +- } +- + if (!head_pipe->plane_state) + return head_pipe; + + /* Re-use pipe already acquired for this stream if available*/ + for (i = pool->pipe_count - 1; i >= 0; i--) { +- if (res_ctx->pipe_ctx[i].stream == stream && ++ if (res_ctx->pipe_ctx[i].stream == head_pipe->stream && + !res_ctx->pipe_ctx[i].plane_state) { + return &res_ctx->pipe_ctx[i]; + } +@@ -1185,8 +1171,7 @@ static struct pipe_ctx *acquire_free_pipe_for_stream( + if (!pool->funcs->acquire_idle_pipe_for_layer) + return NULL; + +- return pool->funcs->acquire_idle_pipe_for_layer(context, pool, stream); +- ++ return pool->funcs->acquire_idle_pipe_for_layer(context, pool, head_pipe->stream); + } + + #if defined(CONFIG_DRM_AMD_DC_DCN1_0) +@@ -1200,7 +1185,7 @@ static int acquire_first_split_pipe( + for (i = 0; i < pool->pipe_count; i++) { + struct pipe_ctx *split_pipe = &res_ctx->pipe_ctx[i]; + +- if (split_pipe->top_pipe && !dc_res_is_odm_head_pipe(split_pipe) && ++ if (split_pipe->top_pipe && + split_pipe->top_pipe->plane_state == split_pipe->plane_state) { + split_pipe->top_pipe->bottom_pipe = split_pipe->bottom_pipe; + if (split_pipe->bottom_pipe) +@@ -1261,39 +1246,41 @@ bool dc_add_plane_to_context( + return false; + } + +- tail_pipe = resource_get_tail_pipe_for_stream(&context->res_ctx, stream); +- ASSERT(tail_pipe); ++ /* retain new surface, but only once per stream */ ++ dc_plane_state_retain(plane_state); + +- free_pipe = acquire_free_pipe_for_stream(context, pool, stream); ++ while (head_pipe) { ++ tail_pipe = resource_get_tail_pipe(&context->res_ctx, head_pipe); ++ ASSERT(tail_pipe); + +-#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +- if (!free_pipe) { +- int pipe_idx = acquire_first_split_pipe(&context->res_ctx, pool, stream); +- if (pipe_idx >= 0) +- free_pipe = &context->res_ctx.pipe_ctx[pipe_idx]; +- } +-#endif +- if (!free_pipe) +- return false; ++ free_pipe = acquire_free_pipe_for_head(context, pool, head_pipe); + +- /* retain new surfaces */ +- dc_plane_state_retain(plane_state); +- free_pipe->plane_state = plane_state; +- +- if (head_pipe != free_pipe) { +- free_pipe->stream_res.tg = tail_pipe->stream_res.tg; +- free_pipe->stream_res.abm = tail_pipe->stream_res.abm; +- free_pipe->stream_res.opp = tail_pipe->stream_res.opp; +- free_pipe->stream_res.stream_enc = tail_pipe->stream_res.stream_enc; +- free_pipe->stream_res.audio = tail_pipe->stream_res.audio; +- free_pipe->clock_source = tail_pipe->clock_source; +- free_pipe->top_pipe = tail_pipe; +- tail_pipe->bottom_pipe = free_pipe; +- } else if (free_pipe->bottom_pipe && free_pipe->bottom_pipe->plane_state == NULL) { +- ASSERT(free_pipe->bottom_pipe->stream_res.opp != free_pipe->stream_res.opp); +- free_pipe->bottom_pipe->plane_state = plane_state; +- } ++ #if defined(CONFIG_DRM_AMD_DC_DCN1_0) ++ if (!free_pipe) { ++ int pipe_idx = acquire_first_split_pipe(&context->res_ctx, pool, stream); ++ if (pipe_idx >= 0) ++ free_pipe = &context->res_ctx.pipe_ctx[pipe_idx]; ++ } ++ #endif ++ if (!free_pipe) { ++ dc_plane_state_release(plane_state); ++ return false; ++ } + ++ free_pipe->plane_state = plane_state; ++ ++ if (head_pipe != free_pipe) { ++ free_pipe->stream_res.tg = tail_pipe->stream_res.tg; ++ free_pipe->stream_res.abm = tail_pipe->stream_res.abm; ++ free_pipe->stream_res.opp = tail_pipe->stream_res.opp; ++ free_pipe->stream_res.stream_enc = tail_pipe->stream_res.stream_enc; ++ free_pipe->stream_res.audio = tail_pipe->stream_res.audio; ++ free_pipe->clock_source = tail_pipe->clock_source; ++ free_pipe->top_pipe = tail_pipe; ++ tail_pipe->bottom_pipe = free_pipe; ++ } ++ head_pipe = head_pipe->next_odm_pipe; ++ } + /* assign new surfaces*/ + stream_status->plane_states[stream_status->plane_count] = plane_state; + +@@ -1302,35 +1289,6 @@ bool dc_add_plane_to_context( + return true; + } + +-struct pipe_ctx *dc_res_get_odm_bottom_pipe(struct pipe_ctx *pipe_ctx) +-{ +- struct pipe_ctx *bottom_pipe = pipe_ctx->bottom_pipe; +- +- /* ODM should only be updated once per otg */ +- if (pipe_ctx->top_pipe) +- return NULL; +- +- while (bottom_pipe) { +- if (bottom_pipe->stream_res.opp != pipe_ctx->stream_res.opp) +- break; +- bottom_pipe = bottom_pipe->bottom_pipe; +- } +- +- return bottom_pipe; +-} +- +-bool dc_res_is_odm_head_pipe(struct pipe_ctx *pipe_ctx) +-{ +- struct pipe_ctx *top_pipe = pipe_ctx->top_pipe; +- +- if (!top_pipe) +- return false; +- if (top_pipe && top_pipe->stream_res.opp == pipe_ctx->stream_res.opp) +- return false; +- +- return true; +-} +- + bool dc_remove_plane_from_context( + const struct dc *dc, + struct dc_stream_state *stream, +@@ -1357,12 +1315,6 @@ bool dc_remove_plane_from_context( + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->plane_state == plane_state) { +- if (dc_res_is_odm_head_pipe(pipe_ctx)) { +- pipe_ctx->plane_state = NULL; +- pipe_ctx->bottom_pipe = NULL; +- continue; +- } +- + if (pipe_ctx->top_pipe) + pipe_ctx->top_pipe->bottom_pipe = pipe_ctx->bottom_pipe; + +@@ -1377,13 +1329,10 @@ bool dc_remove_plane_from_context( + * For head pipe detach surfaces from pipe for tail + * pipe just zero it out + */ +- if (!pipe_ctx->top_pipe) { ++ if (!pipe_ctx->top_pipe) + pipe_ctx->plane_state = NULL; +- if (!dc_res_get_odm_bottom_pipe(pipe_ctx)) +- pipe_ctx->bottom_pipe = NULL; +- } else { ++ else + memset(pipe_ctx, 0, sizeof(*pipe_ctx)); +- } + } + } + +@@ -1752,9 +1701,6 @@ enum dc_status dc_remove_stream_from_ctx( + for (i = 0; i < MAX_PIPES; i++) { + if (new_ctx->res_ctx.pipe_ctx[i].stream == stream && + !new_ctx->res_ctx.pipe_ctx[i].top_pipe) { +- struct pipe_ctx *odm_pipe = +- dc_res_get_odm_bottom_pipe(&new_ctx->res_ctx.pipe_ctx[i]); +- + del_pipe = &new_ctx->res_ctx.pipe_ctx[i]; + + ASSERT(del_pipe->stream_res.stream_enc); +@@ -1779,8 +1725,6 @@ enum dc_status dc_remove_stream_from_ctx( + dc->res_pool->funcs->remove_stream_from_ctx(dc, new_ctx, stream); + + memset(del_pipe, 0, sizeof(*del_pipe)); +- if (odm_pipe) +- memset(odm_pipe, 0, sizeof(*odm_pipe)); + + break; + } +@@ -2494,6 +2438,12 @@ void dc_resource_state_copy_construct( + + if (cur_pipe->bottom_pipe) + cur_pipe->bottom_pipe = &dst_ctx->res_ctx.pipe_ctx[cur_pipe->bottom_pipe->pipe_idx]; ++ ++ if (cur_pipe->next_odm_pipe) ++ cur_pipe->next_odm_pipe = &dst_ctx->res_ctx.pipe_ctx[cur_pipe->next_odm_pipe->pipe_idx]; ++ ++ if (cur_pipe->prev_odm_pipe) ++ cur_pipe->prev_odm_pipe = &dst_ctx->res_ctx.pipe_ctx[cur_pipe->prev_odm_pipe->pipe_idx]; + } + + for (i = 0; i < dst_ctx->stream_count; i++) { +diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +index 5a046e5bc756..2693404cab1a 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c ++++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +@@ -1338,7 +1338,7 @@ static enum dc_status apply_single_controller_ctx_to_hw( + struct drr_params params = {0}; + unsigned int event_triggers = 0; + #if defined(CONFIG_DRM_AMD_DC_DCN2_0) +- struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); ++ struct pipe_ctx *odm_pipe = pipe_ctx->next_odm_pipe; + #endif + + if (dc->hwss.disable_stream_gating) { +@@ -1406,7 +1406,7 @@ static enum dc_status apply_single_controller_ctx_to_hw( + &stream->bit_depth_params, + &stream->clamping); + #if defined(CONFIG_DRM_AMD_DC_DCN2_0) +- if (odm_pipe) { ++ while (odm_pipe) { + odm_pipe->stream_res.opp->funcs->opp_set_dyn_expansion( + odm_pipe->stream_res.opp, + COLOR_SPACE_YCBCR601, +@@ -1417,6 +1417,7 @@ static enum dc_status apply_single_controller_ctx_to_hw( + odm_pipe->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); ++ odm_pipe = odm_pipe->next_odm_pipe; + } + #endif + +@@ -2076,7 +2077,7 @@ enum dc_status dce110_apply_ctx_to_hw( + if (pipe_ctx_old->stream && !pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) + continue; + +- if (pipe_ctx->top_pipe) ++ if (pipe_ctx->top_pipe || pipe_ctx->prev_odm_pipe) + continue; + + status = apply_single_controller_ctx_to_hw( +diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +index 1835157b9fad..344190be93ae 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +@@ -2535,7 +2535,7 @@ struct pipe_ctx *find_top_pipe_for_stream( + if (pipe_ctx->stream != stream) + continue; + +- if (!pipe_ctx->top_pipe) ++ if (!pipe_ctx->top_pipe && !pipe_ctx->prev_odm_pipe) + return pipe_ctx; + } + return NULL; +diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c +index 57d9645659d8..c8c17fac2e24 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c +@@ -529,11 +529,9 @@ enum dc_status dcn20_enable_stream_timing( + struct dc_stream_state *stream = pipe_ctx->stream; + struct drr_params params = {0}; + unsigned int event_triggers = 0; +- +- +-#if defined(CONFIG_DRM_AMD_DC_DCN2_0) +- struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); +-#endif ++ struct pipe_ctx *odm_pipe; ++ int opp_cnt = 1; ++ int opp_inst[MAX_PIPES] = { pipe_ctx->stream_res.opp->inst }; + + /* by upper caller loop, pipe0 is parent pipe and be called first. + * back end is set up by for pipe0. Other children pipe share back end +@@ -544,14 +542,17 @@ enum dc_status dcn20_enable_stream_timing( + + /* TODO check if timing_changed, disable stream if timing changed */ + +- if (odm_pipe) { +- int opp_inst[2] = { pipe_ctx->stream_res.opp->inst, odm_pipe->stream_res.opp->inst }; ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { ++ opp_inst[opp_cnt] = odm_pipe->stream_res.opp->inst; ++ opp_cnt++; ++ } + ++ if (opp_cnt > 1) + pipe_ctx->stream_res.tg->funcs->set_odm_combine( + pipe_ctx->stream_res.tg, +- opp_inst, 2, ++ opp_inst, opp_cnt, + &pipe_ctx->stream->timing); +- } ++ + /* HW program guide assume display already disable + * by unplug sequence. OTG assume stop. + */ +@@ -575,7 +576,7 @@ enum dc_status dcn20_enable_stream_timing( + pipe_ctx->stream->signal, + true); + +- if (odm_pipe) ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + odm_pipe->stream_res.opp->funcs->opp_pipe_clock_control( + odm_pipe->stream_res.opp, + true); +@@ -661,7 +662,7 @@ bool dcn20_set_output_transfer_func(struct pipe_ctx *pipe_ctx, + */ + if (mpc->funcs->power_on_mpc_mem_pwr) + mpc->funcs->power_on_mpc_mem_pwr(mpc, mpcc_id, true); +- if ((pipe_ctx->top_pipe == NULL || dc_res_is_odm_head_pipe(pipe_ctx)) ++ if (pipe_ctx->top_pipe == NULL + && mpc->funcs->set_output_gamma && stream->out_transfer_func) { + if (stream->out_transfer_func->type == TF_TYPE_HWPWL) + params = &stream->out_transfer_func->pwl; +@@ -823,17 +824,21 @@ bool dcn20_set_input_transfer_func(struct pipe_ctx *pipe_ctx, + + static void dcn20_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx) + { +- struct pipe_ctx *combine_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); ++ struct pipe_ctx *odm_pipe; ++ int opp_cnt = 1; ++ int opp_inst[MAX_PIPES] = { pipe_ctx->stream_res.opp->inst }; + +- if (combine_pipe) { +- int opp_inst[2] = { pipe_ctx->stream_res.opp->inst, +- combine_pipe->stream_res.opp->inst }; ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { ++ opp_inst[opp_cnt] = odm_pipe->stream_res.opp->inst; ++ opp_cnt++; ++ } + ++ if (opp_cnt > 1) + pipe_ctx->stream_res.tg->funcs->set_odm_combine( + pipe_ctx->stream_res.tg, +- opp_inst, 2, ++ opp_inst, opp_cnt, + &pipe_ctx->stream->timing); +- } else ++ else + pipe_ctx->stream_res.tg->funcs->set_odm_bypass( + pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing); + } +@@ -848,7 +853,8 @@ void dcn20_blank_pixel_data( + struct dc_stream_state *stream = pipe_ctx->stream; + enum dc_color_space color_space = stream->output_color_space; + enum controller_dp_test_pattern test_pattern = CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR; +- struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); ++ struct pipe_ctx *odm_pipe; ++ int odm_cnt = 1; + + int width = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right; + int height = stream->timing.v_addressable + stream->timing.v_border_bottom + stream->timing.v_border_top; +@@ -856,8 +862,10 @@ void dcn20_blank_pixel_data( + /* get opp dpg blank color */ + color_space_to_black_color(dc, color_space, &black_color); + +- if (bot_odm_pipe) +- width = width / 2; ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) ++ odm_cnt++; ++ ++ width = width / odm_cnt; + + if (blank) { + if (stream_res->abm) +@@ -877,9 +885,9 @@ void dcn20_blank_pixel_data( + width, + height); + +- if (bot_odm_pipe) { +- bot_odm_pipe->stream_res.opp->funcs->opp_set_disp_pattern_generator( +- bot_odm_pipe->stream_res.opp, ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { ++ odm_pipe->stream_res.opp->funcs->opp_set_disp_pattern_generator( ++ odm_pipe->stream_res.opp, + dc->debug.visual_confirm != VISUAL_CONFIRM_DISABLE ? + CONTROLLER_DP_TEST_PATTERN_COLORRAMP : test_pattern, + stream->timing.display_color_depth, +@@ -1021,7 +1029,7 @@ static void dcn20_program_all_pipe_in_tree( + struct pipe_ctx *pipe_ctx, + struct dc_state *context) + { +- if (pipe_ctx->top_pipe == NULL) { ++ if (pipe_ctx->top_pipe == NULL && !pipe_ctx->prev_odm_pipe) { + bool blank = !is_pipe_tree_visible(pipe_ctx); + + pipe_ctx->stream_res.tg->funcs->program_global_sync( +@@ -1312,8 +1320,8 @@ bool dcn20_update_bandwidth( + + pipe_ctx->stream_res.tg->funcs->set_vtg_params( + pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing); +- +- dc->hwss.blank_pixel_data(dc, pipe_ctx, blank); ++ if (pipe_ctx->prev_odm_pipe == NULL) ++ dc->hwss.blank_pixel_data(dc, pipe_ctx, blank); + } + + pipe_ctx->plane_res.hubp->funcs->hubp_setup( +@@ -1403,12 +1411,15 @@ static void dcn20_disable_stream_gating(struct dc *dc, struct pipe_ctx *pipe_ctx + { + #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + struct dce_hwseq *hws = dc->hwseq; +- struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); + + if (pipe_ctx->stream_res.dsc) { ++ struct pipe_ctx *odm_pipe = pipe_ctx->next_odm_pipe; ++ + dcn20_dsc_pg_control(hws, pipe_ctx->stream_res.dsc->inst, true); +- if (bot_odm_pipe) +- dcn20_dsc_pg_control(hws, bot_odm_pipe->stream_res.dsc->inst, true); ++ while (odm_pipe) { ++ dcn20_dsc_pg_control(hws, odm_pipe->stream_res.dsc->inst, true); ++ odm_pipe = odm_pipe->next_odm_pipe; ++ } + } + #endif + } +@@ -1417,12 +1428,15 @@ static void dcn20_enable_stream_gating(struct dc *dc, struct pipe_ctx *pipe_ctx) + { + #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + struct dce_hwseq *hws = dc->hwseq; +- struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); + + if (pipe_ctx->stream_res.dsc) { ++ struct pipe_ctx *odm_pipe = pipe_ctx->next_odm_pipe; ++ + dcn20_dsc_pg_control(hws, pipe_ctx->stream_res.dsc->inst, false); +- if (bot_odm_pipe) +- dcn20_dsc_pg_control(hws, bot_odm_pipe->stream_res.dsc->inst, false); ++ while (odm_pipe) { ++ dcn20_dsc_pg_control(hws, odm_pipe->stream_res.dsc->inst, false); ++ odm_pipe = odm_pipe->next_odm_pipe; ++ } + } + #endif + } +@@ -1552,18 +1566,22 @@ void dcn20_unblank_stream(struct pipe_ctx *pipe_ctx, + struct encoder_unblank_param params = { { 0 } }; + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; +- params.odm = dc_res_get_odm_bottom_pipe(pipe_ctx); ++ struct pipe_ctx *odm_pipe; + ++ params.opp_cnt = 1; ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { ++ params.opp_cnt++; ++ } + /* only 3 items below are used by unblank */ + params.timing = pipe_ctx->stream->timing; + + params.link_settings.link_rate = link_settings->link_rate; + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) { +- if (optc1_is_two_pixels_per_containter(&stream->timing) || params.odm) ++ if (optc1_is_two_pixels_per_containter(&stream->timing) || params.opp_cnt) + params.timing.pix_clk_100hz /= 2; + pipe_ctx->stream_res.stream_enc->funcs->dp_set_odm_combine( +- pipe_ctx->stream_res.stream_enc, params.odm); ++ pipe_ctx->stream_res.stream_enc, params.opp_cnt); + pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(pipe_ctx->stream_res.stream_enc, ¶ms); + } + +@@ -1654,7 +1672,7 @@ static void dcn20_reset_hw_ctx_wrap( + if (!pipe_ctx_old->stream) + continue; + +- if (pipe_ctx_old->top_pipe) ++ if (pipe_ctx_old->top_pipe || pipe_ctx_old->prev_odm_pipe) + continue; + + if (!pipe_ctx->stream || +diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c +index c59f31dcdc0d..aa1342ccf8b4 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c +@@ -1317,7 +1317,11 @@ static void get_pixel_clock_parameters( + struct pixel_clk_params *pixel_clk_params) + { + const struct dc_stream_state *stream = pipe_ctx->stream; +- bool odm_combine = dc_res_get_odm_bottom_pipe(pipe_ctx) != NULL; ++ struct pipe_ctx *odm_pipe; ++ int opp_cnt = 1; ++ ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) ++ opp_cnt++; + + pixel_clk_params->requested_pix_clk_100hz = stream->timing.pix_clk_100hz; + pixel_clk_params->encoder_object_id = stream->link->link_enc->id; +@@ -1335,7 +1339,9 @@ static void get_pixel_clock_parameters( + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) + pixel_clk_params->color_depth = COLOR_DEPTH_888; + +- if (optc1_is_two_pixels_per_containter(&stream->timing) || odm_combine) ++ if (opp_cnt == 4) ++ pixel_clk_params->requested_pix_clk_100hz /= 4; ++ else if (optc1_is_two_pixels_per_containter(&stream->timing) || opp_cnt == 2) + pixel_clk_params->requested_pix_clk_100hz /= 2; + + if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) +@@ -1479,22 +1485,16 @@ static enum dc_status remove_dsc_from_stream_resource(struct dc *dc, + for (i = 0; i < MAX_PIPES; i++) { + if (new_ctx->res_ctx.pipe_ctx[i].stream == dc_stream && !new_ctx->res_ctx.pipe_ctx[i].top_pipe) { + pipe_ctx = &new_ctx->res_ctx.pipe_ctx[i]; +- break; ++ ++ if (pipe_ctx->stream_res.dsc) ++ release_dsc(&new_ctx->res_ctx, dc->res_pool, &pipe_ctx->stream_res.dsc); + } + } + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; +- +- if (pipe_ctx->stream_res.dsc) { +- struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); +- +- release_dsc(&new_ctx->res_ctx, dc->res_pool, &pipe_ctx->stream_res.dsc); +- if (odm_pipe) +- release_dsc(&new_ctx->res_ctx, dc->res_pool, &odm_pipe->stream_res.dsc); +- } +- +- return DC_OK; ++ else ++ return DC_OK; + } + #endif + +@@ -1593,17 +1593,94 @@ static void swizzle_to_dml_params( + } + } + +-static bool dcn20_split_stream_for_combine( ++static bool dcn20_split_stream_for_odm( ++ struct resource_context *res_ctx, ++ const struct resource_pool *pool, ++ struct pipe_ctx *prev_odm_pipe, ++ struct pipe_ctx *next_odm_pipe) ++{ ++ int pipe_idx = next_odm_pipe->pipe_idx; ++ struct scaler_data *sd = &prev_odm_pipe->plane_res.scl_data; ++ struct pipe_ctx *sec_next_pipe = next_odm_pipe->next_odm_pipe; ++ int new_width; ++ ++ *next_odm_pipe = *prev_odm_pipe; ++ next_odm_pipe->next_odm_pipe = sec_next_pipe; ++ ++ next_odm_pipe->pipe_idx = pipe_idx; ++ next_odm_pipe->plane_res.mi = pool->mis[next_odm_pipe->pipe_idx]; ++ next_odm_pipe->plane_res.hubp = pool->hubps[next_odm_pipe->pipe_idx]; ++ next_odm_pipe->plane_res.ipp = pool->ipps[next_odm_pipe->pipe_idx]; ++ next_odm_pipe->plane_res.xfm = pool->transforms[next_odm_pipe->pipe_idx]; ++ next_odm_pipe->plane_res.dpp = pool->dpps[next_odm_pipe->pipe_idx]; ++ next_odm_pipe->plane_res.mpcc_inst = pool->dpps[next_odm_pipe->pipe_idx]->inst; ++#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT ++ next_odm_pipe->stream_res.dsc = NULL; ++#endif ++ if (prev_odm_pipe->next_odm_pipe && prev_odm_pipe->next_odm_pipe != next_odm_pipe) { ++ ASSERT(!next_odm_pipe->next_odm_pipe); ++ next_odm_pipe->next_odm_pipe = prev_odm_pipe->next_odm_pipe; ++ next_odm_pipe->next_odm_pipe->prev_odm_pipe = next_odm_pipe; ++ } ++ prev_odm_pipe->next_odm_pipe = next_odm_pipe; ++ next_odm_pipe->prev_odm_pipe = prev_odm_pipe; ++ ASSERT(next_odm_pipe->top_pipe == NULL); ++ ++ if (prev_odm_pipe->plane_state) { ++ /* HACTIVE halved for odm combine */ ++ sd->h_active /= 2; ++ /* Copy scl_data to secondary pipe */ ++ next_odm_pipe->plane_res.scl_data = *sd; ++ ++ /* Calculate new vp and recout for left pipe */ ++ /* Need at least 16 pixels width per side */ ++ if (sd->recout.x + 16 >= sd->h_active) ++ return false; ++ new_width = sd->h_active - sd->recout.x; ++ sd->viewport.width -= dc_fixpt_floor(dc_fixpt_mul_int( ++ sd->ratios.horz, sd->recout.width - new_width)); ++ sd->viewport_c.width -= dc_fixpt_floor(dc_fixpt_mul_int( ++ sd->ratios.horz_c, sd->recout.width - new_width)); ++ sd->recout.width = new_width; ++ ++ /* Calculate new vp and recout for right pipe */ ++ sd = &next_odm_pipe->plane_res.scl_data; ++ new_width = sd->recout.width + sd->recout.x - sd->h_active; ++ /* Need at least 16 pixels width per side */ ++ if (new_width <= 16) ++ return false; ++ sd->viewport.width -= dc_fixpt_floor(dc_fixpt_mul_int( ++ sd->ratios.horz, sd->recout.width - new_width)); ++ sd->viewport_c.width -= dc_fixpt_floor(dc_fixpt_mul_int( ++ sd->ratios.horz_c, sd->recout.width - new_width)); ++ sd->recout.width = new_width; ++ sd->viewport.x += dc_fixpt_floor(dc_fixpt_mul_int( ++ sd->ratios.horz, sd->h_active - sd->recout.x)); ++ sd->viewport_c.x += dc_fixpt_floor(dc_fixpt_mul_int( ++ sd->ratios.horz_c, sd->h_active - sd->recout.x)); ++ sd->recout.x = 0; ++ } ++ next_odm_pipe->stream_res.opp = pool->opps[next_odm_pipe->pipe_idx]; ++#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT ++ if (next_odm_pipe->stream->timing.flags.DSC == 1) { ++ acquire_dsc(res_ctx, pool, &next_odm_pipe->stream_res.dsc); ++ ASSERT(next_odm_pipe->stream_res.dsc); ++ if (next_odm_pipe->stream_res.dsc == NULL) ++ return false; ++ } ++#endif ++ ++ return true; ++} ++ ++static void dcn20_split_stream_for_mpc( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct pipe_ctx *primary_pipe, +- struct pipe_ctx *secondary_pipe, +- bool is_odm_combine) ++ struct pipe_ctx *secondary_pipe) + { + int pipe_idx = secondary_pipe->pipe_idx; +- struct scaler_data *sd = &primary_pipe->plane_res.scl_data; + struct pipe_ctx *sec_bot_pipe = secondary_pipe->bottom_pipe; +- int new_width; + + *secondary_pipe = *primary_pipe; + secondary_pipe->bottom_pipe = sec_bot_pipe; +@@ -1626,57 +1703,9 @@ static bool dcn20_split_stream_for_combine( + primary_pipe->bottom_pipe = secondary_pipe; + secondary_pipe->top_pipe = primary_pipe; + +- if (is_odm_combine) { +- if (primary_pipe->plane_state) { +- /* HACTIVE halved for odm combine */ +- sd->h_active /= 2; +- /* Copy scl_data to secondary pipe */ +- secondary_pipe->plane_res.scl_data = *sd; +- +- /* Calculate new vp and recout for left pipe */ +- /* Need at least 16 pixels width per side */ +- if (sd->recout.x + 16 >= sd->h_active) +- return false; +- new_width = sd->h_active - sd->recout.x; +- sd->viewport.width -= dc_fixpt_floor(dc_fixpt_mul_int( +- sd->ratios.horz, sd->recout.width - new_width)); +- sd->viewport_c.width -= dc_fixpt_floor(dc_fixpt_mul_int( +- sd->ratios.horz_c, sd->recout.width - new_width)); +- sd->recout.width = new_width; +- +- /* Calculate new vp and recout for right pipe */ +- sd = &secondary_pipe->plane_res.scl_data; +- new_width = sd->recout.width + sd->recout.x - sd->h_active; +- /* Need at least 16 pixels width per side */ +- if (new_width <= 16) +- return false; +- sd->viewport.width -= dc_fixpt_floor(dc_fixpt_mul_int( +- sd->ratios.horz, sd->recout.width - new_width)); +- sd->viewport_c.width -= dc_fixpt_floor(dc_fixpt_mul_int( +- sd->ratios.horz_c, sd->recout.width - new_width)); +- sd->recout.width = new_width; +- sd->viewport.x += dc_fixpt_floor(dc_fixpt_mul_int( +- sd->ratios.horz, sd->h_active - sd->recout.x)); +- sd->viewport_c.x += dc_fixpt_floor(dc_fixpt_mul_int( +- sd->ratios.horz_c, sd->h_active - sd->recout.x)); +- sd->recout.x = 0; +- } +- secondary_pipe->stream_res.opp = pool->opps[secondary_pipe->pipe_idx]; +-#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT +- if (secondary_pipe->stream->timing.flags.DSC == 1) { +- acquire_dsc(res_ctx, pool, &secondary_pipe->stream_res.dsc); +- ASSERT(secondary_pipe->stream_res.dsc); +- if (secondary_pipe->stream_res.dsc == NULL) +- return false; +- } +-#endif +- } else { +- ASSERT(primary_pipe->plane_state); +- resource_build_scaling_params(primary_pipe); +- resource_build_scaling_params(secondary_pipe); +- } +- +- return true; ++ ASSERT(primary_pipe->plane_state); ++ resource_build_scaling_params(primary_pipe); ++ resource_build_scaling_params(secondary_pipe); + } + + void dcn20_populate_dml_writeback_from_context( +@@ -2106,20 +2135,24 @@ static bool dcn20_validate_dsc(struct dc *dc, struct dc_state *new_ctx) + struct pipe_ctx *pipe_ctx = &new_ctx->res_ctx.pipe_ctx[i]; + struct dc_stream_state *stream = pipe_ctx->stream; + struct dsc_config dsc_cfg; ++ struct pipe_ctx *odm_pipe; ++ int opp_cnt = 1; ++ ++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) ++ opp_cnt++; + + /* Only need to validate top pipe */ +- if (pipe_ctx->top_pipe || !stream || !stream->timing.flags.DSC) ++ if (pipe_ctx->top_pipe || pipe_ctx->prev_odm_pipe || !stream || !stream->timing.flags.DSC) + continue; + +- dsc_cfg.pic_width = stream->timing.h_addressable + stream->timing.h_border_left +- + stream->timing.h_border_right; ++ dsc_cfg.pic_width = (stream->timing.h_addressable + stream->timing.h_border_left ++ + stream->timing.h_border_right) / opp_cnt; + dsc_cfg.pic_height = stream->timing.v_addressable + stream->timing.v_border_top + + stream->timing.v_border_bottom; +- if (dc_res_get_odm_bottom_pipe(pipe_ctx)) +- dsc_cfg.pic_width /= 2; + dsc_cfg.pixel_encoding = stream->timing.pixel_encoding; + dsc_cfg.color_depth = stream->timing.display_color_depth; + dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg; ++ dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt; + + if (!pipe_ctx->stream_res.dsc->funcs->dsc_validate_stream(pipe_ctx->stream_res.dsc, &dsc_cfg)) + return false; +@@ -2143,6 +2176,8 @@ static struct pipe_ctx *dcn20_find_secondary_pipe(struct dc *dc, + * if this primary pipe has a bottom pipe in prev. state + * and if the bottom pipe is still available (which it should be), + * pick that pipe as secondary ++ * Same logic applies for ODM pipes. Since mpo is not allowed with odm ++ * check in else case. + */ + if (dc->current_state->res_ctx.pipe_ctx[primary_pipe->pipe_idx].bottom_pipe) { + preferred_pipe_idx = dc->current_state->res_ctx.pipe_ctx[primary_pipe->pipe_idx].bottom_pipe->pipe_idx; +@@ -2150,6 +2185,12 @@ static struct pipe_ctx *dcn20_find_secondary_pipe(struct dc *dc, + secondary_pipe = &res_ctx->pipe_ctx[preferred_pipe_idx]; + secondary_pipe->pipe_idx = preferred_pipe_idx; + } ++ } else if (dc->current_state->res_ctx.pipe_ctx[primary_pipe->pipe_idx].next_odm_pipe) { ++ preferred_pipe_idx = dc->current_state->res_ctx.pipe_ctx[primary_pipe->pipe_idx].next_odm_pipe->pipe_idx; ++ if (res_ctx->pipe_ctx[preferred_pipe_idx].stream == NULL) { ++ secondary_pipe = &res_ctx->pipe_ctx[preferred_pipe_idx]; ++ secondary_pipe->pipe_idx = preferred_pipe_idx; ++ } + } + + /* +@@ -2220,6 +2261,38 @@ bool dcn20_fast_validate_bw( + if (!pipes) + return false; + ++ /* merge previously split odm pipes since mode support needs to make the decision */ ++ for (i = 0; i < dc->res_pool->pipe_count; i++) { ++ struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; ++ struct pipe_ctx *odm_pipe = pipe->next_odm_pipe; ++ ++ if (pipe->prev_odm_pipe) ++ continue; ++ ++ pipe->next_odm_pipe = NULL; ++ while (odm_pipe) { ++ struct pipe_ctx *next_odm_pipe = odm_pipe->next_odm_pipe; ++ ++ odm_pipe->plane_state = NULL; ++ odm_pipe->stream = NULL; ++ odm_pipe->top_pipe = NULL; ++ odm_pipe->bottom_pipe = NULL; ++ odm_pipe->prev_odm_pipe = NULL; ++ odm_pipe->next_odm_pipe = NULL; ++#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT ++ if (odm_pipe->stream_res.dsc) ++ release_dsc(&context->res_ctx, dc->res_pool, &odm_pipe->stream_res.dsc); ++#endif ++ /* Clear plane_res and stream_res */ ++ memset(&odm_pipe->plane_res, 0, sizeof(odm_pipe->plane_res)); ++ memset(&odm_pipe->stream_res, 0, sizeof(odm_pipe->stream_res)); ++ odm_pipe = next_odm_pipe; ++ } ++ if (pipe->plane_state) ++ resource_build_scaling_params(pipe); ++ } ++ ++ /* merge previously mpc split pipes since mode support needs to make the decision */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; + struct pipe_ctx *hsplit_pipe = pipe->bottom_pipe; +@@ -2227,7 +2300,6 @@ bool dcn20_fast_validate_bw( + if (!hsplit_pipe || hsplit_pipe->plane_state != pipe->plane_state) + continue; + +- /* merge previously split pipe since mode support needs to make the decision */ + pipe->bottom_pipe = hsplit_pipe->bottom_pipe; + if (hsplit_pipe->bottom_pipe) + hsplit_pipe->bottom_pipe->top_pipe = pipe; +@@ -2235,10 +2307,7 @@ bool dcn20_fast_validate_bw( + hsplit_pipe->stream = NULL; + hsplit_pipe->top_pipe = NULL; + hsplit_pipe->bottom_pipe = NULL; +-#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT +- if (hsplit_pipe->stream_res.dsc && hsplit_pipe->stream_res.dsc != pipe->stream_res.dsc) +- release_dsc(&context->res_ctx, dc->res_pool, &hsplit_pipe->stream_res.dsc); +-#endif ++ + /* Clear plane_res and stream_res */ + memset(&hsplit_pipe->plane_res, 0, sizeof(hsplit_pipe->plane_res)); + memset(&hsplit_pipe->stream_res, 0, sizeof(hsplit_pipe->stream_res)); +@@ -2351,10 +2420,9 @@ bool dcn20_fast_validate_bw( + if (!pipe->top_pipe && !pipe->plane_state && context->bw_ctx.dml.vba.ODMCombineEnabled[pipe_idx]) { + hsplit_pipe = dcn20_find_secondary_pipe(dc, &context->res_ctx, dc->res_pool, pipe); + ASSERT(hsplit_pipe); +- if (!dcn20_split_stream_for_combine( ++ if (!dcn20_split_stream_for_odm( + &context->res_ctx, dc->res_pool, +- pipe, hsplit_pipe, +- true)) ++ pipe, hsplit_pipe)) + goto validate_fail; + pipe_split_from[hsplit_pipe->pipe_idx] = pipe_idx; + dcn20_build_mapped_resource(dc, context, pipe->stream); +@@ -2395,11 +2463,15 @@ bool dcn20_fast_validate_bw( + if (!hsplit_pipe) + continue; + +- if (!dcn20_split_stream_for_combine( ++ if (context->bw_ctx.dml.vba.ODMCombineEnabled[pipe_idx]) { ++ if (!dcn20_split_stream_for_odm( ++ &context->res_ctx, dc->res_pool, ++ pipe, hsplit_pipe)) ++ goto validate_fail; ++ } else ++ dcn20_split_stream_for_mpc( + &context->res_ctx, dc->res_pool, +- pipe, hsplit_pipe, +- context->bw_ctx.dml.vba.ODMCombineEnabled[pipe_idx])) +- goto validate_fail; ++ pipe, hsplit_pipe); + pipe_split_from[hsplit_pipe->pipe_idx] = pipe_idx; + } + } else if (hsplit_pipe && hsplit_pipe->plane_state == pipe->plane_state) { +diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.c +index a4e67286cdad..9cfc69ccdb39 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.c +@@ -458,7 +458,7 @@ void enc2_stream_encoder_dp_unblank( + uint64_t m_vid_l = n_vid; + + /* YCbCr 4:2:0 : Computed VID_M will be 2X the input rate */ +- if (is_two_pixels_per_containter(¶m->timing) || param->odm) { ++ if (is_two_pixels_per_containter(¶m->timing) || param->opp_cnt) { + /*this logic should be the same in get_pixel_clock_parameters() */ + n_multiply = 1; + } +diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h +index 74186cf1c285..bfe0d06d1c20 100644 +--- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h ++++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h +@@ -297,6 +297,8 @@ struct pipe_ctx { + + struct pipe_ctx *top_pipe; + struct pipe_ctx *bottom_pipe; ++ struct pipe_ctx *next_odm_pipe; ++ struct pipe_ctx *prev_odm_pipe; + + #ifdef CONFIG_DRM_AMD_DC_DCN1_0 + struct _vcs_dpi_display_dlg_regs_st dlg_regs; +diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h +index 8bb3e3d56ac9..fe9b7a10a1c3 100644 +--- a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h ++++ b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h +@@ -91,7 +91,7 @@ struct encoder_unblank_param { + struct dc_link_settings link_settings; + struct dc_crtc_timing timing; + #ifdef CONFIG_DRM_AMD_DC_DCN2_0 +- bool odm; ++ int opp_cnt; + #endif + }; + +diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h +index 47f81072d7e9..1cc1c8ce633b 100644 +--- a/drivers/gpu/drm/amd/display/dc/inc/resource.h ++++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h +@@ -179,7 +179,4 @@ void update_audio_usage( + + unsigned int resource_pixel_format_to_bpp(enum surface_pixel_format format); + +-struct pipe_ctx *dc_res_get_odm_bottom_pipe(struct pipe_ctx *pipe_ctx); +-bool dc_res_is_odm_head_pipe(struct pipe_ctx *pipe_ctx); +- + #endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_RESOURCE_H_ */ +-- +2.17.1 + |