From: Samson Tam <samson....@amd.com>

[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....@amd.com>
Acked-by: Solomon Chiu <solomon.c...@amd.com>
Signed-off-by: Samson Tam <samson....@amd.com>
---
 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

Reply via email to