From: Samson Tam <Samson.Tam@xxxxxxx> [Why] In single display configuration, windowed MPO does not work with ODM combine. [How] For ODM + MPO window on one half of ODM, only 3 pipes should be allocated and scaling parameters adjusted to handle this case. Otherwise, we use 4 pipes. Move copy_surface_update_to_plane() before dc_add_plane_to_context() so that it gets the updated rect information when setting up the pipes. Add dc_check_boundary_crossing_for_windowed_mpo_with_odm() to force a full update when we cross a boundary requiring us to reconfigure the number of pipes between 3 and 4 pipes. Set config.enable_windowed_mpo_odm to true when we have the debug.enable_single_display_2to1_odm_policy set to true. Don't fail validating ODM with windowed MPO if config.enable_windowed_mpo_odm is true. Reviewed-by: Aric Cyr <Aric.Cyr@xxxxxxx> Acked-by: Solomon Chiu <solomon.chiu@xxxxxxx> Signed-off-by: Samson Tam <Samson.Tam@xxxxxxx> --- drivers/gpu/drm/amd/display/dc/core/dc.c | 94 ++++++++++++++ .../gpu/drm/amd/display/dc/core/dc_resource.c | 115 ++++++++++++++---- 2 files changed, 188 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index e13bf66f70e0..a448696ee8f2 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -2415,6 +2415,96 @@ static enum surface_update_type check_update_surfaces_for_stream( return overall_type; } +static bool dc_check_is_fullscreen_video(struct rect src, struct rect clip_rect) +{ + int view_height, view_width, clip_x, clip_y, clip_width, clip_height; + + view_height = src.height; + view_width = src.width; + + clip_x = clip_rect.x; + clip_y = clip_rect.y; + + clip_width = clip_rect.width; + clip_height = clip_rect.height; + + /* check for centered video accounting for off by 1 scaling truncation */ + if ((view_height - clip_y - clip_height <= clip_y + 1) && + (view_width - clip_x - clip_width <= clip_x + 1) && + (view_height - clip_y - clip_height >= clip_y - 1) && + (view_width - clip_x - clip_width >= clip_x - 1)) { + + /* when OS scales up/down to letter box, it may end up + * with few blank pixels on the border due to truncating. + * Add offset margin to account for this + */ + if (clip_x <= 4 || clip_y <= 4) + return true; + } + + return false; +} + +static enum surface_update_type check_boundary_crossing_for_windowed_mpo_with_odm(struct dc *dc, + struct dc_surface_update *srf_updates, int surface_count, + enum surface_update_type update_type) +{ + enum surface_update_type new_update_type = update_type; + int i, j; + struct pipe_ctx *pipe = NULL; + struct dc_stream_state *stream; + + /* Check that we are in windowed MPO with ODM + * - look for MPO pipe by scanning pipes for first pipe matching + * surface that has moved ( position change ) + * - MPO pipe will have top pipe + * - check that top pipe has ODM pointer + */ + if ((surface_count > 1) && dc->config.enable_windowed_mpo_odm) { + for (i = 0; i < surface_count; i++) { + if (srf_updates[i].surface && srf_updates[i].scaling_info + && srf_updates[i].surface->update_flags.bits.position_change) { + + for (j = 0; j < dc->res_pool->pipe_count; j++) { + if (srf_updates[i].surface == dc->current_state->res_ctx.pipe_ctx[j].plane_state) { + pipe = &dc->current_state->res_ctx.pipe_ctx[j]; + stream = pipe->stream; + break; + } + } + + if (pipe && pipe->top_pipe && (get_num_odm_splits(pipe->top_pipe) > 0) && stream + && !dc_check_is_fullscreen_video(stream->src, srf_updates[i].scaling_info->clip_rect)) { + struct rect old_clip_rect, new_clip_rect; + bool old_clip_rect_left, old_clip_rect_right, old_clip_rect_middle; + bool new_clip_rect_left, new_clip_rect_right, new_clip_rect_middle; + + old_clip_rect = srf_updates[i].surface->clip_rect; + new_clip_rect = srf_updates[i].scaling_info->clip_rect; + + old_clip_rect_left = ((old_clip_rect.x + old_clip_rect.width) <= (stream->src.x + (stream->src.width/2))); + old_clip_rect_right = (old_clip_rect.x >= (stream->src.x + (stream->src.width/2))); + old_clip_rect_middle = !old_clip_rect_left && !old_clip_rect_right; + + new_clip_rect_left = ((new_clip_rect.x + new_clip_rect.width) <= (stream->src.x + (stream->src.width/2))); + new_clip_rect_right = (new_clip_rect.x >= (stream->src.x + (stream->src.width/2))); + new_clip_rect_middle = !new_clip_rect_left && !new_clip_rect_right; + + if (old_clip_rect_left && new_clip_rect_middle) + new_update_type = UPDATE_TYPE_FULL; + else if (old_clip_rect_middle && new_clip_rect_right) + new_update_type = UPDATE_TYPE_FULL; + else if (old_clip_rect_right && new_clip_rect_middle) + new_update_type = UPDATE_TYPE_FULL; + else if (old_clip_rect_middle && new_clip_rect_left) + new_update_type = UPDATE_TYPE_FULL; + } + } + } + } + return new_update_type; +} + /* * dc_check_update_surfaces_for_stream() - Determine update type (fast, med, or full) * @@ -2446,6 +2536,10 @@ enum surface_update_type dc_check_update_surfaces_for_stream( updates[i].surface->update_flags.raw = 0xFFFFFFFF; } + if (type == UPDATE_TYPE_MED) + type = check_boundary_crossing_for_windowed_mpo_with_odm(dc, + updates, surface_count, type); + if (type == UPDATE_TYPE_FAST) { // If there's an available clock comparator, we use that. if (dc->clk_mgr->funcs->are_clock_states_equal) { 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 2aa42c710488..9eb7e7027622 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -757,6 +757,10 @@ static void calculate_split_count_and_index(struct pipe_ctx *pipe_ctx, int *spli (*split_idx)++; split_pipe = split_pipe->top_pipe; } + + /* MPO window on right side of ODM split */ + if (split_pipe && split_pipe->prev_odm_pipe && !pipe_ctx->prev_odm_pipe) + (*split_idx)++; } else { /*Get odm split index*/ struct pipe_ctx *split_pipe = pipe_ctx->prev_odm_pipe; @@ -803,7 +807,11 @@ static void calculate_recout(struct pipe_ctx *pipe_ctx) /* * Only the leftmost ODM pipe should be offset by a nonzero distance */ - if (!pipe_ctx->prev_odm_pipe || split_idx == split_count) { + if (pipe_ctx->top_pipe && pipe_ctx->top_pipe->prev_odm_pipe && !pipe_ctx->prev_odm_pipe) { + /* MPO window on right side of ODM split */ + data->recout.x = stream->dst.x + (surf_clip.x - stream->src.x - stream->src.width/2) * + stream->dst.width / stream->src.width; + } else if (!pipe_ctx->prev_odm_pipe || split_idx == split_count) { data->recout.x = stream->dst.x; if (stream->src.x < surf_clip.x) data->recout.x += (surf_clip.x - stream->src.x) * stream->dst.width @@ -1001,6 +1009,8 @@ static void calculate_inits_and_viewports(struct pipe_ctx *pipe_ctx) * stream->dst.height / stream->src.height; if (pipe_ctx->prev_odm_pipe && split_idx) ro_lb = data->h_active * split_idx - recout_full_x; + else if (pipe_ctx->top_pipe && pipe_ctx->top_pipe->prev_odm_pipe) + ro_lb = data->h_active * split_idx - recout_full_x + data->recout.x; else ro_lb = data->recout.x - recout_full_x; ro_tb = data->recout.y - recout_full_y; @@ -1106,9 +1116,26 @@ bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx) timing->h_border_left + timing->h_border_right; pipe_ctx->plane_res.scl_data.v_active = timing->v_addressable + timing->v_border_top + timing->v_border_bottom; - if (pipe_ctx->next_odm_pipe || pipe_ctx->prev_odm_pipe) + if (pipe_ctx->next_odm_pipe || pipe_ctx->prev_odm_pipe) { pipe_ctx->plane_res.scl_data.h_active /= get_num_odm_splits(pipe_ctx) + 1; + DC_LOG_SCALER("%s pipe %d: next_odm_pipe:%d prev_odm_pipe:%d\n", + __func__, + pipe_ctx->pipe_idx, + pipe_ctx->next_odm_pipe ? pipe_ctx->next_odm_pipe->pipe_idx : -1, + pipe_ctx->prev_odm_pipe ? pipe_ctx->prev_odm_pipe->pipe_idx : -1); + } /* ODM + windows MPO, where window is on either right or left ODM half */ + else if (pipe_ctx->top_pipe && (pipe_ctx->top_pipe->next_odm_pipe || pipe_ctx->top_pipe->prev_odm_pipe)) { + + pipe_ctx->plane_res.scl_data.h_active /= get_num_odm_splits(pipe_ctx->top_pipe) + 1; + + DC_LOG_SCALER("%s ODM + windows MPO: pipe:%d top_pipe:%d top_pipe->next_odm_pipe:%d top_pipe->prev_odm_pipe:%d\n", + __func__, + pipe_ctx->pipe_idx, + pipe_ctx->top_pipe->pipe_idx, + pipe_ctx->top_pipe->next_odm_pipe ? pipe_ctx->top_pipe->next_odm_pipe->pipe_idx : -1, + pipe_ctx->top_pipe->prev_odm_pipe ? pipe_ctx->top_pipe->prev_odm_pipe->pipe_idx : -1); + } /* depends on h_active */ calculate_recout(pipe_ctx); /* depends on pixel format */ @@ -1116,10 +1143,12 @@ bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx) /* depends on scaling ratios and recout, does not calculate offset yet */ calculate_viewport_size(pipe_ctx); - /* Stopgap for validation of ODM + MPO on one side of screen case */ - if (pipe_ctx->plane_res.scl_data.viewport.height < 1 || - pipe_ctx->plane_res.scl_data.viewport.width < 1) - return false; + if (!pipe_ctx->stream->ctx->dc->config.enable_windowed_mpo_odm) { + /* Stopgap for validation of ODM + MPO on one side of screen case */ + if (pipe_ctx->plane_res.scl_data.viewport.height < 1 || + pipe_ctx->plane_res.scl_data.viewport.width < 1) + return false; + } /* * LB calculations depend on vp size, h/v_active and scaling ratios @@ -1420,6 +1449,7 @@ bool dc_add_plane_to_context( struct pipe_ctx *head_pipe, *tail_pipe, *free_pipe; struct dc_stream_status *stream_status = NULL; + DC_LOGGER_INIT(stream->ctx->logger); for (i = 0; i < context->stream_count; i++) if (context->streams[i] == stream) { stream_status = &context->stream_status[i]; @@ -1466,23 +1496,66 @@ bool dc_add_plane_to_context( if (head_pipe != free_pipe) { tail_pipe = resource_get_tail_pipe(&context->res_ctx, head_pipe); ASSERT(tail_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; - if (!free_pipe->next_odm_pipe && tail_pipe->next_odm_pipe && tail_pipe->next_odm_pipe->bottom_pipe) { - free_pipe->next_odm_pipe = tail_pipe->next_odm_pipe->bottom_pipe; - tail_pipe->next_odm_pipe->bottom_pipe->prev_odm_pipe = free_pipe; - } - if (!free_pipe->prev_odm_pipe && tail_pipe->prev_odm_pipe && tail_pipe->prev_odm_pipe->bottom_pipe) { - free_pipe->prev_odm_pipe = tail_pipe->prev_odm_pipe->bottom_pipe; - tail_pipe->prev_odm_pipe->bottom_pipe->next_odm_pipe = free_pipe; + + /* ODM + window MPO, where MPO window is on right half only */ + if (free_pipe->plane_state && + (free_pipe->plane_state->clip_rect.x >= free_pipe->stream->src.x + free_pipe->stream->src.width/2) && + tail_pipe->next_odm_pipe) { + + DC_LOG_SCALER("%s - ODM + window MPO(right). free_pipe:%d tail_pipe->next_odm_pipe:%d\n", + __func__, + free_pipe->pipe_idx, + tail_pipe->next_odm_pipe ? tail_pipe->next_odm_pipe->pipe_idx : -1); + + free_pipe->stream_res.tg = tail_pipe->next_odm_pipe->stream_res.tg; + free_pipe->stream_res.abm = tail_pipe->next_odm_pipe->stream_res.abm; + free_pipe->stream_res.opp = tail_pipe->next_odm_pipe->stream_res.opp; + free_pipe->stream_res.stream_enc = tail_pipe->next_odm_pipe->stream_res.stream_enc; + free_pipe->stream_res.audio = tail_pipe->next_odm_pipe->stream_res.audio; + free_pipe->clock_source = tail_pipe->next_odm_pipe->clock_source; + + free_pipe->top_pipe = tail_pipe->next_odm_pipe; + tail_pipe->next_odm_pipe->bottom_pipe = free_pipe; + } else { + 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; + + if (!free_pipe->next_odm_pipe && tail_pipe->next_odm_pipe && tail_pipe->next_odm_pipe->bottom_pipe) { + free_pipe->next_odm_pipe = tail_pipe->next_odm_pipe->bottom_pipe; + tail_pipe->next_odm_pipe->bottom_pipe->prev_odm_pipe = free_pipe; + } + if (!free_pipe->prev_odm_pipe && tail_pipe->prev_odm_pipe && tail_pipe->prev_odm_pipe->bottom_pipe) { + free_pipe->prev_odm_pipe = tail_pipe->prev_odm_pipe->bottom_pipe; + tail_pipe->prev_odm_pipe->bottom_pipe->next_odm_pipe = free_pipe; + } } } + + /* ODM + window MPO, where MPO window is on left half only */ + if (free_pipe->plane_state && + (free_pipe->plane_state->clip_rect.x + free_pipe->plane_state->clip_rect.width <= + free_pipe->stream->src.x + free_pipe->stream->src.width/2)) { + DC_LOG_SCALER("%s - ODM + window MPO(left). free_pipe:%d\n", + __func__, + free_pipe->pipe_idx); + break; + } + /* ODM + window MPO, where MPO window is on right half only */ + if (free_pipe->plane_state && + (free_pipe->plane_state->clip_rect.x >= free_pipe->stream->src.x + free_pipe->stream->src.width/2)) { + DC_LOG_SCALER("%s - ODM + window MPO(right). free_pipe:%d\n", + __func__, + free_pipe->pipe_idx); + break; + } + head_pipe = head_pipe->next_odm_pipe; } /* assign new surfaces*/ -- 2.25.1