drm/amd/display: Fix two MPO videos in single display ODM combine mode
authorSamson Tam <Samson.Tam@amd.com>
Tue, 12 Jul 2022 09:54:38 +0000 (05:54 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 25 Jul 2022 21:15:27 +0000 (17:15 -0400)
[Why]
In single display ODM combine mode, two MPO videos ( three
 planes ) are not working

[How]
When we detect three planes, don't set odm combine 2to1 policy
 for the MPO planes.  Otherwise, we run out of pipes available
Add support for two MPO videos in dc_add_plane_to_context().
 Don't allow both videos to be on the same side of the
 display.
Add extra check when fetching free pipe for two MPO videos.

Reviewed-by: Alvin Lee <Alvin.Lee2@amd.com>
Acked-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Samson Tam <Samson.Tam@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/core/dc_resource.c
drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c
drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.h

index 752ba4a..ffc0f1c 100644 (file)
@@ -1463,6 +1463,7 @@ bool dc_add_plane_to_context(
        struct dc_stream_status *stream_status = NULL;
        struct pipe_ctx *prev_right_head = NULL;
        struct pipe_ctx *free_right_pipe = NULL;
+       struct pipe_ctx *prev_left_head = NULL;
 
        DC_LOGGER_INIT(stream->ctx->logger);
        for (i = 0; i < context->stream_count; i++)
@@ -1514,8 +1515,16 @@ bool dc_add_plane_to_context(
 
                        /* 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) {
+                               (free_pipe->plane_state->clip_rect.x >= free_pipe->stream->src.x + free_pipe->stream->src.width/2) &&
+                               tail_pipe->next_odm_pipe) {
+
+                               /* For ODM + window MPO, in 3 plane case, if we already have a MPO window on
+                                *  the right side, then we will invalidate a 2nd one on the right side
+                                */
+                               if (head_pipe->next_odm_pipe && tail_pipe->next_odm_pipe->bottom_pipe) {
+                                       dc_plane_state_release(plane_state);
+                                       return false;
+                               }
 
                                DC_LOG_SCALER("%s - ODM + window MPO(right). free_pipe:%d  tail_pipe->next_odm_pipe:%d\n",
                                                __func__,
@@ -1530,20 +1539,42 @@ bool dc_add_plane_to_context(
                                 * - If not, continue to use free_pipe
                                 * - If the right side already has a pipe, use that pipe instead if its available
                                 */
+
+                               /*
+                                * We also want to avoid the case where with three plane ( 2 MPO videos ), we have
+                                *  both videos on the left side so one of the videos is invalidated.  Then we
+                                *  move the invalidated video back to the right side.  If the order of the plane
+                                *  states is such that the right MPO plane is processed first, the free pipe
+                                *  selected by the head will be the left MPO pipe. But since there was no right
+                                *  MPO pipe, it will assign the free pipe to the right MPO pipe instead and
+                                *  a pipe reallocation will occur.
+                                * Check the old context to see if the left side already has a pipe allocated
+                                * - If not, continue to use free_pipe
+                                * - If the left side is already using this pipe, then pick another pipe for right
+                                */
+
                                prev_right_head = &dc->current_state->res_ctx.pipe_ctx[tail_pipe->next_odm_pipe->pipe_idx];
-                               if ((prev_right_head->bottom_pipe) && (free_pipe->pipe_idx != prev_right_head->bottom_pipe->pipe_idx)) {
+                               if ((prev_right_head->bottom_pipe) &&
+                                       (free_pipe->pipe_idx != prev_right_head->bottom_pipe->pipe_idx)) {
                                        free_right_pipe = acquire_free_pipe_for_head(context, pool, tail_pipe->next_odm_pipe);
-                                       if (free_right_pipe) {
-                                               free_pipe->stream = NULL;
-                                               memset(&free_pipe->stream_res, 0, sizeof(struct stream_resource));
-                                               memset(&free_pipe->plane_res, 0, sizeof(struct plane_resource));
-                                               free_pipe->plane_state = NULL;
-                                               free_pipe->pipe_idx = 0;
-                                               free_right_pipe->plane_state = plane_state;
-                                               free_pipe = free_right_pipe;
+                               } else {
+                                       prev_left_head = &dc->current_state->res_ctx.pipe_ctx[head_pipe->pipe_idx];
+                                       if ((prev_left_head->bottom_pipe) &&
+                                               (free_pipe->pipe_idx == prev_left_head->bottom_pipe->pipe_idx)) {
+                                               free_right_pipe = acquire_free_pipe_for_head(context, pool, head_pipe);
                                        }
                                }
 
+                               if (free_right_pipe) {
+                                       free_pipe->stream = NULL;
+                                       memset(&free_pipe->stream_res, 0, sizeof(struct stream_resource));
+                                       memset(&free_pipe->plane_res, 0, sizeof(struct plane_resource));
+                                       free_pipe->plane_state = NULL;
+                                       free_pipe->pipe_idx = 0;
+                                       free_right_pipe->plane_state = plane_state;
+                                       free_pipe = free_right_pipe;
+                               }
+
                                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;
@@ -1553,7 +1584,63 @@ bool dc_add_plane_to_context(
 
                                free_pipe->top_pipe = tail_pipe->next_odm_pipe;
                                tail_pipe->next_odm_pipe->bottom_pipe = free_pipe;
+                       } else if (free_pipe->plane_state &&
+                               (free_pipe->plane_state->clip_rect.x >= free_pipe->stream->src.x + free_pipe->stream->src.width/2)
+                               && head_pipe->next_odm_pipe) {
+
+                               /* For ODM + window MPO, support 3 plane ( 2 MPO ) case.
+                                * Here we have a desktop ODM + left window MPO and a new MPO window appears
+                                *  on the right side only.  It fails the first case, because tail_pipe is the
+                                *  left window MPO, so it has no next_odm_pipe.  So in this scenario, we check
+                                *  for head_pipe->next_odm_pipe instead
+                                */
+                               DC_LOG_SCALER("%s - ODM + win MPO (left) + win MPO (right). free_pipe:%d  head_pipe->next_odm:%d\n",
+                                               __func__,
+                                               free_pipe->pipe_idx,
+                                               head_pipe->next_odm_pipe ? head_pipe->next_odm_pipe->pipe_idx : -1);
+
+                               /*
+                                * We want to avoid the case where the right side already has a pipe assigned to
+                                *  it and is different from free_pipe ( which would cause trigger a pipe
+                                *  reallocation ).
+                                * Check the old context to see if the right side already has a pipe allocated
+                                * - If not, continue to use free_pipe
+                                * - If the right side already has a pipe, use that pipe instead if its available
+                                */
+                               prev_right_head = &dc->current_state->res_ctx.pipe_ctx[head_pipe->next_odm_pipe->pipe_idx];
+                               if ((prev_right_head->bottom_pipe) &&
+                                       (free_pipe->pipe_idx != prev_right_head->bottom_pipe->pipe_idx)) {
+                                       free_right_pipe = acquire_free_pipe_for_head(context, pool, head_pipe->next_odm_pipe);
+                                       if (free_right_pipe) {
+                                               free_pipe->stream = NULL;
+                                               memset(&free_pipe->stream_res, 0, sizeof(struct stream_resource));
+                                               memset(&free_pipe->plane_res, 0, sizeof(struct plane_resource));
+                                               free_pipe->plane_state = NULL;
+                                               free_pipe->pipe_idx = 0;
+                                               free_right_pipe->plane_state = plane_state;
+                                               free_pipe = free_right_pipe;
+                                       }
+                               }
+
+                               free_pipe->stream_res.tg = head_pipe->next_odm_pipe->stream_res.tg;
+                               free_pipe->stream_res.abm = head_pipe->next_odm_pipe->stream_res.abm;
+                               free_pipe->stream_res.opp = head_pipe->next_odm_pipe->stream_res.opp;
+                               free_pipe->stream_res.stream_enc = head_pipe->next_odm_pipe->stream_res.stream_enc;
+                               free_pipe->stream_res.audio = head_pipe->next_odm_pipe->stream_res.audio;
+                               free_pipe->clock_source = head_pipe->next_odm_pipe->clock_source;
+
+                               free_pipe->top_pipe = head_pipe->next_odm_pipe;
+                               head_pipe->next_odm_pipe->bottom_pipe = free_pipe;
                        } else {
+
+                               /* For ODM + window MPO, in 3 plane case, if we already have a MPO window on
+                                *  the left side, then we will invalidate a 2nd one on the left side
+                                */
+                               if (head_pipe->next_odm_pipe && tail_pipe->top_pipe) {
+                                       dc_plane_state_release(plane_state);
+                                       return false;
+                               }
+
                                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;
@@ -1564,21 +1651,28 @@ bool dc_add_plane_to_context(
                                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;
+                               /* Connect MPO pipes together if MPO window is in the centre */
+                               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))) {
+                                       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)) {
+                       (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);
@@ -1586,7 +1680,7 @@ bool dc_add_plane_to_context(
                }
                /* 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)) {
+                       (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);
index e551d29..314dec5 100644 (file)
@@ -1820,11 +1820,12 @@ int dcn32_populate_dml_pipes_from_context(
        struct resource_context *res_ctx = &context->res_ctx;
        struct pipe_ctx *pipe;
        bool subvp_in_use = false, is_pipe_split_expected[MAX_PIPES];
+       int plane_count = 0;
+       struct dc_crtc_timing *timing;
 
        dcn20_populate_dml_pipes_from_context(dc, context, pipes, fast_validate);
 
        for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) {
-               struct dc_crtc_timing *timing;
 
                if (!res_ctx->pipe_ctx[i].stream)
                        continue;
@@ -1876,11 +1877,12 @@ int dcn32_populate_dml_pipes_from_context(
                        }
                }
 
-               pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_dal;
-               if (context->stream_count == 1) {
-                       if (dc->debug.enable_single_display_2to1_odm_policy)
-                               pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_2to1;
-               }
+               /* Calculate the number of planes we have so we can determine
+                *  whether to apply ODM 2to1 policy or not
+                */
+               if (pipe->stream && !pipe->prev_odm_pipe &&
+                               (!pipe->top_pipe || pipe->top_pipe->plane_state != pipe->plane_state))
+                       ++plane_count;
 
                DC_FP_START();
                is_pipe_split_expected[i] = dcn32_predict_pipe_split(context, pipes[i].pipe, i);
@@ -1889,6 +1891,28 @@ int dcn32_populate_dml_pipes_from_context(
                pipe_cnt++;
        }
 
+       /* Determine whether we will apply ODM 2to1 policy
+        * Applies to single display and where the number of planes is less than 3
+        * For 3 plane case ( 2 MPO planes ), we will not set the policy for the MPO pipes
+        */
+       for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) {
+               if (!res_ctx->pipe_ctx[i].stream)
+                       continue;
+               pipe = &res_ctx->pipe_ctx[i];
+               timing = &pipe->stream->timing;
+
+               pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_dal;
+               res_ctx->pipe_ctx[i].stream->odm_2to1_policy_applied  = false;
+               if (context->stream_count == 1 && timing->dsc_cfg.num_slices_h != 1) {
+                       if (dc->debug.enable_single_display_2to1_odm_policy) {
+                               if (!((plane_count > 2) && pipe->top_pipe))
+                                       pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_2to1;
+                       }
+                       res_ctx->pipe_ctx[i].stream->odm_2to1_policy_applied = true;
+               }
+               pipe_cnt++;
+       }
+
        /* For DET allocation, we don't want to use DML policy (not optimal for utilizing all
         * the DET available for each pipe). Use the DET override input to maintain our driver
         * policy.
@@ -1947,7 +1971,7 @@ static struct resource_funcs dcn32_res_pool_funcs = {
        .validate_bandwidth = dcn32_validate_bandwidth,
        .calculate_wm_and_dlg = dcn32_calculate_wm_and_dlg,
        .populate_dml_pipes = dcn32_populate_dml_pipes_from_context,
-       .acquire_idle_pipe_for_layer = dcn20_acquire_idle_pipe_for_layer,
+       .acquire_idle_pipe_for_head_pipe_in_layer = dcn32_acquire_idle_pipe_for_head_pipe_in_layer,
        .add_stream_to_ctx = dcn30_add_stream_to_ctx,
        .add_dsc_to_stream_resource = dcn20_add_dsc_to_stream_resource,
        .remove_stream_from_ctx = dcn20_remove_stream_from_ctx,
@@ -1976,7 +2000,7 @@ static bool dcn32_resource_construct(
        uint32_t pipe_fuses = 0;
        uint32_t num_pipes  = 4;
 
-    DC_FP_START();
+       DC_FP_START();
 
        ctx->dc_bios->regs = &bios_regs;
 
@@ -2316,13 +2340,13 @@ static bool dcn32_resource_construct(
                pool->base.oem_device = NULL;
        }
 
-    DC_FP_END();
+       DC_FP_END();
 
        return true;
 
 create_fail:
 
-    DC_FP_END();
+       DC_FP_END();
 
        dcn32_resource_destruct(pool);
 
@@ -2346,3 +2370,108 @@ struct resource_pool *dcn32_create_resource_pool(
        kfree(pool);
        return NULL;
 }
+
+static struct pipe_ctx *find_idle_secondary_pipe_check_mpo(
+               struct resource_context *res_ctx,
+               const struct resource_pool *pool,
+               const struct pipe_ctx *primary_pipe)
+{
+       int i;
+       struct pipe_ctx *secondary_pipe = NULL;
+       struct pipe_ctx *next_odm_mpo_pipe = NULL;
+       int primary_index, preferred_pipe_idx;
+       struct pipe_ctx *old_primary_pipe = NULL;
+
+       /*
+        * Modified from find_idle_secondary_pipe
+        * With windowed MPO and ODM, we want to avoid the case where we want a
+        *  free pipe for the left side but the free pipe is being used on the
+        *  right side.
+        * Add check on current_state if the primary_pipe is the left side,
+        *  to check the right side ( primary_pipe->next_odm_pipe ) to see if
+        *  it is using a pipe for MPO ( primary_pipe->next_odm_pipe->bottom_pipe )
+        * - If so, then don't use this pipe
+        * EXCEPTION - 3 plane ( 2 MPO plane ) case
+        * - in this case, the primary pipe has already gotten a free pipe for the
+        *  MPO window in the left
+        * - when it tries to get a free pipe for the MPO window on the right,
+        *  it will see that it is already assigned to the right side
+        *  ( primary_pipe->next_odm_pipe ).  But in this case, we want this
+        *  free pipe, since it will be for the right side.  So add an
+        *  additional condition, that skipping the free pipe on the right only
+        *  applies if the primary pipe has no bottom pipe currently assigned
+        */
+       if (primary_pipe) {
+               primary_index = primary_pipe->pipe_idx;
+               old_primary_pipe = &primary_pipe->stream->ctx->dc->current_state->res_ctx.pipe_ctx[primary_index];
+               if ((old_primary_pipe->next_odm_pipe) && (old_primary_pipe->next_odm_pipe->bottom_pipe)
+                       && (!primary_pipe->bottom_pipe))
+                       next_odm_mpo_pipe = old_primary_pipe->next_odm_pipe->bottom_pipe;
+
+               preferred_pipe_idx = (pool->pipe_count - 1) - primary_pipe->pipe_idx;
+               if ((res_ctx->pipe_ctx[preferred_pipe_idx].stream == NULL) &&
+                       !(next_odm_mpo_pipe && next_odm_mpo_pipe->pipe_idx == preferred_pipe_idx)) {
+                       secondary_pipe = &res_ctx->pipe_ctx[preferred_pipe_idx];
+                       secondary_pipe->pipe_idx = preferred_pipe_idx;
+               }
+       }
+
+       /*
+        * search backwards for the second pipe to keep pipe
+        * assignment more consistent
+        */
+       if (!secondary_pipe)
+               for (i = pool->pipe_count - 1; i >= 0; i--) {
+                       if ((res_ctx->pipe_ctx[i].stream == NULL) &&
+                               !(next_odm_mpo_pipe && next_odm_mpo_pipe->pipe_idx == i)) {
+                               secondary_pipe = &res_ctx->pipe_ctx[i];
+                               secondary_pipe->pipe_idx = i;
+                               break;
+                       }
+               }
+
+       return secondary_pipe;
+}
+
+struct pipe_ctx *dcn32_acquire_idle_pipe_for_head_pipe_in_layer(
+               struct dc_state *state,
+               const struct resource_pool *pool,
+               struct dc_stream_state *stream,
+               struct pipe_ctx *head_pipe)
+{
+       struct resource_context *res_ctx = &state->res_ctx;
+       struct pipe_ctx *idle_pipe, *pipe;
+       struct resource_context *old_ctx = &stream->ctx->dc->current_state->res_ctx;
+       int head_index;
+
+       if (!head_pipe)
+               ASSERT(0);
+
+       /*
+        * Modified from dcn20_acquire_idle_pipe_for_layer
+        * Check if head_pipe in old_context already has bottom_pipe allocated.
+        * - If so, check if that pipe is available in the current context.
+        * --  If so, reuse pipe from old_context
+        */
+       head_index = head_pipe->pipe_idx;
+       pipe = &old_ctx->pipe_ctx[head_index];
+       if (pipe->bottom_pipe && res_ctx->pipe_ctx[pipe->bottom_pipe->pipe_idx].stream == NULL) {
+               idle_pipe = &res_ctx->pipe_ctx[pipe->bottom_pipe->pipe_idx];
+               idle_pipe->pipe_idx = pipe->bottom_pipe->pipe_idx;
+       } else {
+               idle_pipe = find_idle_secondary_pipe_check_mpo(res_ctx, pool, head_pipe);
+               if (!idle_pipe)
+                       return NULL;
+       }
+
+       idle_pipe->stream = head_pipe->stream;
+       idle_pipe->stream_res.tg = head_pipe->stream_res.tg;
+       idle_pipe->stream_res.opp = head_pipe->stream_res.opp;
+
+       idle_pipe->plane_res.hubp = pool->hubps[idle_pipe->pipe_idx];
+       idle_pipe->plane_res.ipp = pool->ipps[idle_pipe->pipe_idx];
+       idle_pipe->plane_res.dpp = pool->dpps[idle_pipe->pipe_idx];
+       idle_pipe->plane_res.mpcc_inst = pool->dpps[idle_pipe->pipe_idx]->inst;
+
+       return idle_pipe;
+}
index fc0fe48..efd4498 100644 (file)
@@ -99,6 +99,12 @@ bool dcn32_subvp_in_use(struct dc *dc,
 
 bool dcn32_mpo_in_use(struct dc_state *context);
 
+struct pipe_ctx *dcn32_acquire_idle_pipe_for_head_pipe_in_layer(
+               struct dc_state *state,
+               const struct resource_pool *pool,
+               struct dc_stream_state *stream,
+               struct pipe_ctx *head_pipe);
+
 void dcn32_determine_det_override(struct dc_state *context, display_e2e_pipe_params_st *pipes,
                bool *is_pipe_split_expected, int pipe_cnt);