summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c')
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c1009
1 files changed, 1009 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c b/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c
new file mode 100644
index 000000000000..d2046e770c50
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c
@@ -0,0 +1,1009 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2023 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dml2_mall_phantom.h"
+
+#include "dml2_dc_types.h"
+#include "dml2_internal_types.h"
+#include "dml2_utils.h"
+#include "dml2_dc_resource_mgmt.h"
+
+#define MAX_ODM_FACTOR 4
+#define MAX_MPCC_FACTOR 4
+
+struct dc_plane_pipe_pool {
+ int pipes_assigned_to_plane[MAX_ODM_FACTOR][MAX_MPCC_FACTOR];
+ bool pipe_used[MAX_ODM_FACTOR][MAX_MPCC_FACTOR];
+ int num_pipes_assigned_to_plane_for_mpcc_combine;
+ int num_pipes_assigned_to_plane_for_odm_combine;
+};
+
+struct dc_pipe_mapping_scratch {
+ struct {
+ unsigned int odm_factor;
+ unsigned int odm_slice_end_x[MAX_PIPES];
+ struct pipe_ctx *next_higher_pipe_for_odm_slice[MAX_PIPES];
+ } odm_info;
+ struct {
+ unsigned int mpc_factor;
+ struct pipe_ctx *prev_odm_pipe;
+ } mpc_info;
+
+ struct dc_plane_pipe_pool pipe_pool;
+};
+
+static bool get_plane_id(const struct dc_state *state, const struct dc_plane_state *plane,
+ unsigned int stream_id, unsigned int *plane_id)
+{
+ int i, j;
+
+ if (!plane_id)
+ return false;
+
+ for (i = 0; i < state->stream_count; i++) {
+ if (state->streams[i]->stream_id == stream_id) {
+ for (j = 0; j < state->stream_status[i].plane_count; j++) {
+ if (state->stream_status[i].plane_states[j] == plane) {
+ *plane_id = (i << 16) | j;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static int find_disp_cfg_idx_by_plane_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int plane_id)
+{
+ int i;
+
+ for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
+ if (mapping->disp_cfg_to_plane_id_valid[i] && mapping->disp_cfg_to_plane_id[i] == plane_id)
+ return i;
+ }
+
+ return -1;
+}
+
+static int find_disp_cfg_idx_by_stream_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int stream_id)
+{
+ int i;
+
+ for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
+ if (mapping->disp_cfg_to_stream_id_valid[i] && mapping->disp_cfg_to_stream_id[i] == stream_id)
+ return i;
+ }
+
+ return -1;
+}
+
+// The master pipe of a stream is defined as the top pipe in odm slice 0
+static struct pipe_ctx *find_master_pipe_of_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id)
+{
+ int i;
+
+ for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
+ if (state->res_ctx.pipe_ctx[i].stream && state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
+ if (!state->res_ctx.pipe_ctx[i].prev_odm_pipe && !state->res_ctx.pipe_ctx[i].top_pipe)
+ return &state->res_ctx.pipe_ctx[i];
+ }
+ }
+
+ return NULL;
+}
+
+static struct pipe_ctx *find_master_pipe_of_plane(struct dml2_context *ctx,
+ struct dc_state *state, unsigned int plane_id)
+{
+ int i;
+ unsigned int plane_id_assigned_to_pipe;
+
+ for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
+ if (state->res_ctx.pipe_ctx[i].plane_state && get_plane_id(state, state->res_ctx.pipe_ctx[i].plane_state,
+ state->res_ctx.pipe_ctx[i].stream->stream_id, &plane_id_assigned_to_pipe)) {
+ if (plane_id_assigned_to_pipe == plane_id)
+ return &state->res_ctx.pipe_ctx[i];
+ }
+ }
+
+ return NULL;
+}
+
+static unsigned int find_pipes_assigned_to_plane(struct dml2_context *ctx,
+ struct dc_state *state, unsigned int plane_id, unsigned int *pipes)
+{
+ int i;
+ unsigned int num_found = 0;
+ unsigned int plane_id_assigned_to_pipe;
+
+ for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
+ if (state->res_ctx.pipe_ctx[i].plane_state && get_plane_id(state, state->res_ctx.pipe_ctx[i].plane_state,
+ state->res_ctx.pipe_ctx[i].stream->stream_id, &plane_id_assigned_to_pipe)) {
+ if (plane_id_assigned_to_pipe == plane_id)
+ pipes[num_found++] = i;
+ }
+ }
+
+ return num_found;
+}
+
+static bool validate_pipe_assignment(const struct dml2_context *ctx, const struct dc_state *state, const struct dml_display_cfg_st *disp_cfg, const struct dml2_dml_to_dc_pipe_mapping *mapping)
+{
+// int i, j, k;
+//
+// unsigned int plane_id;
+//
+// unsigned int disp_cfg_index;
+//
+// unsigned int pipes_assigned_to_plane[MAX_PIPES];
+// unsigned int num_pipes_assigned_to_plane;
+//
+// struct pipe_ctx *top_pipe;
+//
+// for (i = 0; i < state->stream_count; i++) {
+// for (j = 0; j < state->stream_status[i]->plane_count; j++) {
+// if (get_plane_id(state, state->stream_status.plane_states[j], &plane_id)) {
+// disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
+// num_pipes_assigned_to_plane = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes_assigned_to_plane);
+//
+// if (disp_cfg_index >= 0 && num_pipes_assigned_to_plane > 0) {
+// // Verify the number of pipes assigned matches
+// if (disp_cfg->hw.DPPPerSurface != num_pipes_assigned_to_plane)
+// return false;
+//
+// top_pipe = find_top_pipe_in_tree(state->res_ctx.pipe_ctx[pipes_assigned_to_plane[0]]);
+//
+// // Verify MPC and ODM combine
+// if (disp_cfg->hw.ODMMode == dml_odm_mode_bypass) {
+// verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, false);
+// } else {
+// verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, true);
+// }
+//
+// // TODO: could also do additional verification that the pipes in tree are the same as
+// // pipes_assigned_to_plane
+// } else {
+// ASSERT(false);
+// return false;
+// }
+// } else {
+// ASSERT(false);
+// return false;
+// }
+// }
+// }
+ return true;
+}
+
+static bool is_plane_using_pipe(const struct pipe_ctx *pipe)
+{
+ if (pipe->plane_state)
+ return true;
+
+ return false;
+}
+
+static bool is_pipe_free(const struct pipe_ctx *pipe)
+{
+ if (!pipe->plane_state && !pipe->stream)
+ return true;
+
+ return false;
+}
+
+static unsigned int find_preferred_pipe_candidates(const struct dc_state *existing_state,
+ const int pipe_count,
+ const unsigned int stream_id,
+ unsigned int *preferred_pipe_candidates)
+{
+ unsigned int num_preferred_candidates = 0;
+ int i;
+
+ /* There is only one case which we consider for adding a pipe to the preferred
+ * pipe candidate array:
+ *
+ * 1. If the existing stream id of the pipe is equivalent to the stream id
+ * of the stream we are trying to achieve MPC/ODM combine for. This allows
+ * us to minimize the changes in pipe topology during the transition.
+ *
+ * However this condition comes with a caveat. We need to ignore pipes that will
+ * require a change in OPP but still have the same stream id. For example during
+ * an MPC to ODM transiton.
+ */
+ if (existing_state) {
+ for (i = 0; i < pipe_count; i++) {
+ if (existing_state->res_ctx.pipe_ctx[i].stream && existing_state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
+ if (existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
+ existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i)
+ continue;
+
+ preferred_pipe_candidates[num_preferred_candidates++] = i;
+ }
+ }
+ }
+
+ return num_preferred_candidates;
+}
+
+static unsigned int find_last_resort_pipe_candidates(const struct dc_state *existing_state,
+ const int pipe_count,
+ const unsigned int stream_id,
+ unsigned int *last_resort_pipe_candidates)
+{
+ unsigned int num_last_resort_candidates = 0;
+ int i;
+
+ /* There are two cases where we would like to add a given pipe into the last
+ * candidate array:
+ *
+ * 1. If the pipe requires a change in OPP, for example during an MPC
+ * to ODM transiton.
+ *
+ * 2. If the pipe already has an enabled OTG.
+ */
+ if (existing_state) {
+ for (i = 0; i < pipe_count; i++) {
+ if ((existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
+ existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i) ||
+ existing_state->res_ctx.pipe_ctx[i].stream_res.tg)
+ last_resort_pipe_candidates[num_last_resort_candidates++] = i;
+ }
+ }
+
+ return num_last_resort_candidates;
+}
+
+static bool is_pipe_in_candidate_array(const unsigned int pipe_idx,
+ const unsigned int *candidate_array,
+ const unsigned int candidate_array_size)
+{
+ int i;
+
+ for (i = 0; i < candidate_array_size; i++) {
+ if (candidate_array[i] == pipe_idx)
+ return true;
+ }
+
+ return false;
+}
+
+static bool find_more_pipes_for_stream(struct dml2_context *ctx,
+ struct dc_state *state, // The state we want to find a free mapping in
+ unsigned int stream_id, // The stream we want this pipe to drive
+ int *assigned_pipes,
+ int *assigned_pipe_count,
+ int pipes_needed,
+ const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
+{
+ struct pipe_ctx *pipe = NULL;
+ unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
+ unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
+ unsigned int num_preferred_candidates = 0;
+ unsigned int num_last_resort_candidates = 0;
+ int i;
+
+ if (existing_state) {
+ num_preferred_candidates =
+ find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
+
+ num_last_resort_candidates =
+ find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
+ }
+
+ // First see if any of the preferred are unmapped, and choose those instead
+ for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
+ pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
+ if (!is_plane_using_pipe(pipe)) {
+ pipes_needed--;
+ // TODO: This doens't make sense really, pipe_idx should always be valid
+ pipe->pipe_idx = preferred_pipe_candidates[i];
+ assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
+ }
+ }
+
+ // We like to pair pipes starting from the higher order indicies for combining
+ for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
+ // Ignore any pipes that are the preferred or last resort candidate
+ if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
+ is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
+ continue;
+
+ pipe = &state->res_ctx.pipe_ctx[i];
+ if (!is_plane_using_pipe(pipe)) {
+ pipes_needed--;
+ // TODO: This doens't make sense really, pipe_idx should always be valid
+ pipe->pipe_idx = i;
+ assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
+ }
+ }
+
+ // Only use the last resort pipe candidates as a last resort
+ for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
+ pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
+ if (!is_plane_using_pipe(pipe)) {
+ pipes_needed--;
+ // TODO: This doens't make sense really, pipe_idx should always be valid
+ pipe->pipe_idx = last_resort_pipe_candidates[i];
+ assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
+ }
+ }
+
+ ASSERT(pipes_needed <= 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
+
+ return pipes_needed <= 0;
+}
+
+static bool find_more_free_pipes(struct dml2_context *ctx,
+ struct dc_state *state, // The state we want to find a free mapping in
+ unsigned int stream_id, // The stream we want this pipe to drive
+ int *assigned_pipes,
+ int *assigned_pipe_count,
+ int pipes_needed,
+ const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
+{
+ struct pipe_ctx *pipe = NULL;
+ unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
+ unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
+ unsigned int num_preferred_candidates = 0;
+ unsigned int num_last_resort_candidates = 0;
+ int i;
+
+ if (existing_state) {
+ num_preferred_candidates =
+ find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
+
+ num_last_resort_candidates =
+ find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
+ }
+
+ // First see if any of the preferred are unmapped, and choose those instead
+ for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
+ pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
+ if (is_pipe_free(pipe)) {
+ pipes_needed--;
+ // TODO: This doens't make sense really, pipe_idx should always be valid
+ pipe->pipe_idx = preferred_pipe_candidates[i];
+ assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
+ }
+ }
+
+ // We like to pair pipes starting from the higher order indicies for combining
+ for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
+ // Ignore any pipes that are the preferred or last resort candidate
+ if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
+ is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
+ continue;
+
+ pipe = &state->res_ctx.pipe_ctx[i];
+ if (is_pipe_free(pipe)) {
+ pipes_needed--;
+ // TODO: This doens't make sense really, pipe_idx should always be valid
+ pipe->pipe_idx = i;
+ assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
+ }
+ }
+
+ // Only use the last resort pipe candidates as a last resort
+ for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
+ pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
+ if (is_pipe_free(pipe)) {
+ pipes_needed--;
+ // TODO: This doens't make sense really, pipe_idx should always be valid
+ pipe->pipe_idx = last_resort_pipe_candidates[i];
+ assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
+ }
+ }
+
+ ASSERT(pipes_needed == 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
+
+ return pipes_needed == 0;
+}
+
+static void sort_pipes_for_splitting(struct dc_plane_pipe_pool *pipes)
+{
+ bool sorted, swapped;
+ unsigned int cur_index;
+ unsigned int temp;
+ int odm_slice_index;
+
+ for (odm_slice_index = 0; odm_slice_index < pipes->num_pipes_assigned_to_plane_for_odm_combine; odm_slice_index++) {
+ // Sort each MPCC set
+ //Un-optimized bubble sort, but that's okay for array sizes <= 6
+
+ if (pipes->num_pipes_assigned_to_plane_for_mpcc_combine <= 1)
+ sorted = true;
+ else
+ sorted = false;
+
+ cur_index = 0;
+ swapped = false;
+ while (!sorted) {
+ if (pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] > pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1]) {
+ temp = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index];
+ pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1];
+ pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1] = temp;
+
+ swapped = true;
+ }
+
+ cur_index++;
+
+ if (cur_index == pipes->num_pipes_assigned_to_plane_for_mpcc_combine - 1) {
+ cur_index = 0;
+
+ if (swapped)
+ sorted = false;
+ else
+ sorted = true;
+
+ swapped = false;
+ }
+
+ }
+ }
+}
+
+// For example, 3840 x 2160, ODM2:1 has a slice array of [1919, 3839], meaning, slice0 spans h_pixels 0->1919, and slice1 spans 1920->3840
+static void calculate_odm_slices(const struct dc_stream_state *stream, unsigned int odm_factor, unsigned int *odm_slice_end_x)
+{
+ unsigned int slice_size = 0;
+ int i;
+
+ if (odm_factor < 1 || odm_factor > 4) {
+ ASSERT(false);
+ return;
+ }
+
+ slice_size = stream->src.width / odm_factor;
+
+ for (i = 0; i < odm_factor; i++)
+ odm_slice_end_x[i] = (slice_size * (i + 1)) - 1;
+
+ odm_slice_end_x[odm_factor - 1] = stream->src.width - 1;
+}
+
+static bool is_plane_in_odm_slice(const struct dc_plane_state *plane, unsigned int slice_index, unsigned int *odm_slice_end_x, unsigned int num_slices)
+{
+ unsigned int slice_start_x, slice_end_x;
+
+ if (slice_index == 0)
+ slice_start_x = 0;
+ else
+ slice_start_x = odm_slice_end_x[slice_index - 1] + 1;
+
+ slice_end_x = odm_slice_end_x[slice_index];
+
+ if (plane->clip_rect.x + plane->clip_rect.width < slice_start_x)
+ return false;
+
+ if (plane->clip_rect.x > slice_end_x)
+ return false;
+
+ return true;
+}
+
+static void add_odm_slice_to_odm_tree(struct dml2_context *ctx,
+ struct dc_state *state,
+ struct dc_pipe_mapping_scratch *scratch,
+ unsigned int odm_slice_index)
+{
+ struct pipe_ctx *pipe = NULL;
+ int i;
+
+ // MPCC Combine + ODM Combine is not supported, so there should never be a case where the current plane
+ // has more than 1 pipe mapped to it for a given slice.
+ ASSERT(scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine == 1 || scratch->pipe_pool.num_pipes_assigned_to_plane_for_odm_combine == 1);
+
+ for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
+ pipe = &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]];
+
+ if (scratch->mpc_info.prev_odm_pipe)
+ scratch->mpc_info.prev_odm_pipe->next_odm_pipe = pipe;
+
+ pipe->prev_odm_pipe = scratch->mpc_info.prev_odm_pipe;
+ pipe->next_odm_pipe = NULL;
+ }
+ scratch->mpc_info.prev_odm_pipe = pipe;
+}
+
+static struct pipe_ctx *add_plane_to_blend_tree(struct dml2_context *ctx,
+ struct dc_state *state,
+ const struct dc_plane_state *plane,
+ struct dc_plane_pipe_pool *pipe_pool,
+ unsigned int odm_slice,
+ struct pipe_ctx *top_pipe)
+{
+ int i;
+
+ for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
+ if (top_pipe)
+ top_pipe->bottom_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
+
+ pipe_pool->pipe_used[odm_slice][i] = true;
+
+ state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].top_pipe = top_pipe;
+ state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].bottom_pipe = NULL;
+
+ top_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
+ }
+
+ // After running the above loop, the top pipe actually ends up pointing to the bottom of this MPCC combine tree, so we are actually
+ // returning the bottom pipe here
+ return top_pipe;
+}
+
+static unsigned int find_pipes_assigned_to_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id, unsigned int *pipes)
+{
+ int i;
+ unsigned int num_found = 0;
+
+ for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
+ if (state->res_ctx.pipe_ctx[i].stream && state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
+ pipes[num_found++] = i;
+ }
+ }
+
+ return num_found;
+}
+
+static struct pipe_ctx *assign_pipes_to_stream(struct dml2_context *ctx, struct dc_state *state,
+ const struct dc_stream_state *stream,
+ int odm_factor,
+ struct dc_plane_pipe_pool *pipe_pool,
+ const struct dc_state *existing_state)
+{
+ struct pipe_ctx *master_pipe;
+ unsigned int pipes_needed;
+ unsigned int pipes_assigned;
+ unsigned int pipes[MAX_PIPES] = {0};
+ unsigned int next_pipe_to_assign;
+ int odm_slice;
+
+ pipes_needed = odm_factor;
+
+ master_pipe = find_master_pipe_of_stream(ctx, state, stream->stream_id);
+ ASSERT(master_pipe);
+
+ pipes_assigned = find_pipes_assigned_to_stream(ctx, state, stream->stream_id, pipes);
+
+ find_more_free_pipes(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
+
+ ASSERT(pipes_assigned == pipes_needed);
+
+ next_pipe_to_assign = 0;
+ for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
+ pipe_pool->pipes_assigned_to_plane[odm_slice][0] = pipes[next_pipe_to_assign++];
+
+ pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = 1;
+ pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
+
+ return master_pipe;
+}
+
+static struct pipe_ctx *assign_pipes_to_plane(struct dml2_context *ctx, struct dc_state *state,
+ const struct dc_stream_state *stream,
+ const struct dc_plane_state *plane,
+ int odm_factor,
+ int mpc_factor,
+ struct dc_plane_pipe_pool *pipe_pool,
+ const struct dc_state *existing_state)
+{
+ struct pipe_ctx *master_pipe = NULL;
+ unsigned int plane_id;
+ unsigned int pipes_needed;
+ unsigned int pipes_assigned;
+ unsigned int pipes[MAX_PIPES] = {0};
+ unsigned int next_pipe_to_assign;
+ int odm_slice, mpc_slice;
+
+ if (!get_plane_id(state, plane, stream->stream_id, &plane_id)) {
+ ASSERT(false);
+ return master_pipe;
+ }
+
+ pipes_needed = mpc_factor * odm_factor;
+
+ master_pipe = find_master_pipe_of_plane(ctx, state, plane_id);
+ ASSERT(master_pipe);
+
+ pipes_assigned = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes);
+
+ find_more_pipes_for_stream(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
+
+ ASSERT(pipes_assigned >= pipes_needed);
+
+ next_pipe_to_assign = 0;
+ for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
+ for (mpc_slice = 0; mpc_slice < mpc_factor; mpc_slice++)
+ pipe_pool->pipes_assigned_to_plane[odm_slice][mpc_slice] = pipes[next_pipe_to_assign++];
+
+ pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = mpc_factor;
+ pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
+
+ return master_pipe;
+}
+
+static bool is_pipe_used(const struct dc_plane_pipe_pool *pool, unsigned int pipe_idx)
+{
+ int i, j;
+
+ for (i = 0; i < pool->num_pipes_assigned_to_plane_for_odm_combine; i++) {
+ for (j = 0; j < pool->num_pipes_assigned_to_plane_for_mpcc_combine; j++) {
+ if (pool->pipes_assigned_to_plane[i][j] == pipe_idx && pool->pipe_used[i][j])
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void free_pipe(struct pipe_ctx *pipe)
+{
+ memset(pipe, 0, sizeof(struct pipe_ctx));
+}
+
+static void free_unused_pipes_for_plane(struct dml2_context *ctx, struct dc_state *state,
+ const struct dc_plane_state *plane, const struct dc_plane_pipe_pool *pool, unsigned int stream_id)
+{
+ int i;
+ for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
+ if (state->res_ctx.pipe_ctx[i].plane_state == plane &&
+ state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id &&
+ !is_pipe_used(pool, state->res_ctx.pipe_ctx[i].pipe_idx)) {
+ free_pipe(&state->res_ctx.pipe_ctx[i]);
+ }
+ }
+}
+
+static void remove_pipes_from_blend_trees(struct dml2_context *ctx, struct dc_state *state, struct dc_plane_pipe_pool *pipe_pool, unsigned int odm_slice)
+{
+ struct pipe_ctx *pipe;
+ int i;
+
+ for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
+ pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][0]];
+ if (pipe->top_pipe)
+ pipe->top_pipe->bottom_pipe = pipe->bottom_pipe;
+
+ if (pipe->bottom_pipe)
+ pipe->bottom_pipe = pipe->top_pipe;
+
+ pipe_pool->pipe_used[odm_slice][i] = true;
+ }
+}
+
+static void map_pipes_for_stream(struct dml2_context *ctx, struct dc_state *state, const struct dc_stream_state *stream,
+ struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
+{
+ int odm_slice_index;
+ struct pipe_ctx *master_pipe = NULL;
+
+
+ master_pipe = assign_pipes_to_stream(ctx, state, stream, scratch->odm_info.odm_factor, &scratch->pipe_pool, existing_state);
+ sort_pipes_for_splitting(&scratch->pipe_pool);
+
+ for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
+ remove_pipes_from_blend_trees(ctx, state, &scratch->pipe_pool, odm_slice_index);
+
+ add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
+
+ ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
+ master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][0]], true);
+ }
+}
+
+static void map_pipes_for_plane(struct dml2_context *ctx, struct dc_state *state, const struct dc_stream_state *stream, const struct dc_plane_state *plane,
+ struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
+{
+ int odm_slice_index;
+ unsigned int plane_id;
+ struct pipe_ctx *master_pipe = NULL;
+ int i;
+
+ if (!get_plane_id(state, plane, stream->stream_id, &plane_id)) {
+ ASSERT(false);
+ return;
+ }
+
+ master_pipe = assign_pipes_to_plane(ctx, state, stream, plane, scratch->odm_info.odm_factor, scratch->mpc_info.mpc_factor, &scratch->pipe_pool, existing_state);
+ sort_pipes_for_splitting(&scratch->pipe_pool);
+
+ for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
+ // We build the tree for one ODM slice at a time.
+ // Each ODM slice shares a common OPP
+ if (!is_plane_in_odm_slice(plane, odm_slice_index, scratch->odm_info.odm_slice_end_x, scratch->odm_info.odm_factor)) {
+ continue;
+ }
+
+ // Now we have a list of all pipes to be used for this plane/stream, now setup the tree.
+ scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index] = add_plane_to_blend_tree(ctx, state,
+ plane,
+ &scratch->pipe_pool,
+ odm_slice_index,
+ scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index]);
+
+ add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
+
+ for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
+
+ ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
+ master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]], true);
+ }
+ }
+
+ free_unused_pipes_for_plane(ctx, state, plane, &scratch->pipe_pool, stream->stream_id);
+}
+
+static unsigned int get_mpc_factor(struct dml2_context *ctx,
+ const struct dc_state *state,
+ const struct dml_display_cfg_st *disp_cfg,
+ struct dml2_dml_to_dc_pipe_mapping *mapping,
+ const struct dc_stream_status *status, unsigned int stream_id,
+ int plane_idx)
+{
+ unsigned int plane_id;
+ unsigned int cfg_idx;
+
+ get_plane_id(state, status->plane_states[plane_idx], stream_id, &plane_id);
+ cfg_idx = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
+ if (ctx->architecture == dml2_architecture_20)
+ return (unsigned int)disp_cfg->hw.DPPPerSurface[cfg_idx];
+ ASSERT(false);
+ return 1;
+}
+
+static unsigned int get_odm_factor(
+ const struct dml2_context *ctx,
+ const struct dml_display_cfg_st *disp_cfg,
+ struct dml2_dml_to_dc_pipe_mapping *mapping,
+ const struct dc_stream_state *stream)
+{
+ unsigned int cfg_idx = find_disp_cfg_idx_by_stream_id(
+ mapping, stream->stream_id);
+
+ if (ctx->architecture == dml2_architecture_20)
+ switch (disp_cfg->hw.ODMMode[cfg_idx]) {
+ case dml_odm_mode_bypass:
+ return 1;
+ case dml_odm_mode_combine_2to1:
+ return 2;
+ case dml_odm_mode_combine_4to1:
+ return 4;
+ default:
+ break;
+ }
+ ASSERT(false);
+ return 1;
+}
+
+static void populate_mpc_factors_for_stream(
+ struct dml2_context *ctx,
+ const struct dml_display_cfg_st *disp_cfg,
+ struct dml2_dml_to_dc_pipe_mapping *mapping,
+ const struct dc_state *state,
+ unsigned int stream_idx,
+ unsigned int odm_factor,
+ unsigned int mpc_factors[MAX_PIPES])
+{
+ const struct dc_stream_status *status = &state->stream_status[stream_idx];
+ unsigned int stream_id = state->streams[stream_idx]->stream_id;
+ int i;
+
+ for (i = 0; i < status->plane_count; i++)
+ if (odm_factor == 1)
+ mpc_factors[i] = get_mpc_factor(
+ ctx, state, disp_cfg, mapping, status,
+ stream_id, i);
+ else
+ mpc_factors[i] = 1;
+}
+
+static void populate_odm_factors(const struct dml2_context *ctx,
+ const struct dml_display_cfg_st *disp_cfg,
+ struct dml2_dml_to_dc_pipe_mapping *mapping,
+ const struct dc_state *state,
+ unsigned int odm_factors[MAX_PIPES])
+{
+ int i;
+
+ for (i = 0; i < state->stream_count; i++)
+ odm_factors[i] = get_odm_factor(
+ ctx, disp_cfg, mapping, state->streams[i]);
+}
+
+static bool map_dc_pipes_for_stream(struct dml2_context *ctx,
+ struct dc_state *state,
+ const struct dc_state *existing_state,
+ const struct dc_stream_state *stream,
+ const struct dc_stream_status *status,
+ unsigned int odm_factor,
+ unsigned int mpc_factors[MAX_PIPES])
+{
+ int plane_idx;
+ bool result = true;
+
+ if (odm_factor == 1)
+ /*
+ * ODM and MPC combines are by DML design mutually exclusive.
+ * ODM factor of 1 means MPC factors may be greater than 1.
+ * In this case, we want to set ODM factor to 1 first to free up
+ * pipe resources from previous ODM configuration before setting
+ * up MPC combine to acquire more pipe resources.
+ */
+ result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
+ state,
+ existing_state,
+ ctx->config.callbacks.dc->res_pool,
+ stream,
+ odm_factor);
+ for (plane_idx = 0; plane_idx < status->plane_count; plane_idx++)
+ result &= ctx->config.callbacks.update_pipes_for_plane_with_slice_count(
+ state,
+ existing_state,
+ ctx->config.callbacks.dc->res_pool,
+ status->plane_states[plane_idx],
+ mpc_factors[plane_idx]);
+ if (odm_factor > 1)
+ result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
+ state,
+ existing_state,
+ ctx->config.callbacks.dc->res_pool,
+ stream,
+ odm_factor);
+ return result;
+}
+
+static bool map_dc_pipes_with_callbacks(struct dml2_context *ctx,
+ struct dc_state *state,
+ const struct dml_display_cfg_st *disp_cfg,
+ struct dml2_dml_to_dc_pipe_mapping *mapping,
+ const struct dc_state *existing_state)
+{
+ unsigned int odm_factors[MAX_PIPES];
+ unsigned int mpc_factors_for_stream[MAX_PIPES];
+ int i;
+ bool result = true;
+
+ populate_odm_factors(ctx, disp_cfg, mapping, state, odm_factors);
+ for (i = 0; i < state->stream_count; i++) {
+ populate_mpc_factors_for_stream(ctx, disp_cfg, mapping, state,
+ i, odm_factors[i], mpc_factors_for_stream);
+ result &= map_dc_pipes_for_stream(ctx, state, existing_state,
+ state->streams[i],
+ &state->stream_status[i],
+ odm_factors[i], mpc_factors_for_stream);
+ }
+ return result;
+}
+
+bool dml2_map_dc_pipes(struct dml2_context *ctx, struct dc_state *state, const struct dml_display_cfg_st *disp_cfg, struct dml2_dml_to_dc_pipe_mapping *mapping, const struct dc_state *existing_state)
+{
+ int stream_index, plane_index, i;
+
+ unsigned int stream_disp_cfg_index;
+ unsigned int plane_disp_cfg_index;
+
+ unsigned int plane_id;
+ unsigned int stream_id;
+
+ const unsigned int *ODMMode, *DPPPerSurface;
+ unsigned int odm_mode_array[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0}, dpp_per_surface_array[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0};
+ struct dc_pipe_mapping_scratch scratch;
+
+ if (ctx->config.map_dc_pipes_with_callbacks)
+ return map_dc_pipes_with_callbacks(
+ ctx, state, disp_cfg, mapping, existing_state);
+
+ if (ctx->architecture == dml2_architecture_21) {
+ /*
+ * Extract ODM and DPP outputs from DML2.1 and map them in an array as required for pipe mapping in dml2_map_dc_pipes.
+ * As data cannot be directly extracted in const pointers, assign these arrays to const pointers before proceeding to
+ * maximize the reuse of existing code. Const pointers are required because dml2.0 dml_display_cfg_st is const.
+ *
+ */
+ ODMMode = (const unsigned int *)odm_mode_array;
+ DPPPerSurface = (const unsigned int *)dpp_per_surface_array;
+ } else {
+ ODMMode = (unsigned int *)disp_cfg->hw.ODMMode;
+ DPPPerSurface = disp_cfg->hw.DPPPerSurface;
+ }
+
+ for (stream_index = 0; stream_index < state->stream_count; stream_index++) {
+ memset(&scratch, 0, sizeof(struct dc_pipe_mapping_scratch));
+
+ stream_id = state->streams[stream_index]->stream_id;
+ stream_disp_cfg_index = find_disp_cfg_idx_by_stream_id(mapping, stream_id);
+
+ if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_bypass) {
+ scratch.odm_info.odm_factor = 1;
+ } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_2to1) {
+ scratch.odm_info.odm_factor = 2;
+ } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_4to1) {
+ scratch.odm_info.odm_factor = 4;
+ } else {
+ ASSERT(false);
+ scratch.odm_info.odm_factor = 1;
+ }
+
+ calculate_odm_slices(state->streams[stream_index], scratch.odm_info.odm_factor, scratch.odm_info.odm_slice_end_x);
+
+ // If there are no planes, you still want to setup ODM...
+ if (state->stream_status[stream_index].plane_count == 0) {
+ map_pipes_for_stream(ctx, state, state->streams[stream_index], &scratch, existing_state);
+ }
+
+ for (plane_index = 0; plane_index < state->stream_status[stream_index].plane_count; plane_index++) {
+ // Planes are ordered top to bottom.
+ if (get_plane_id(state, state->stream_status[stream_index].plane_states[plane_index],
+ stream_id, &plane_id)) {
+ plane_disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
+
+ // Setup mpc_info for this plane
+ scratch.mpc_info.prev_odm_pipe = NULL;
+ if (scratch.odm_info.odm_factor == 1) {
+ // If ODM combine is not inuse, then the number of pipes
+ // per plane is determined by MPC combine factor
+ scratch.mpc_info.mpc_factor = DPPPerSurface[plane_disp_cfg_index];
+
+ //For stereo timings, we need to pipe split
+ if (dml2_is_stereo_timing(state->streams[stream_index]))
+ scratch.mpc_info.mpc_factor = 2;
+ } else {
+ // If ODM combine is enabled, then we use at most 1 pipe per
+ // odm slice per plane, i.e. MPC combine is never used
+ scratch.mpc_info.mpc_factor = 1;
+ }
+
+ ASSERT(scratch.odm_info.odm_factor * scratch.mpc_info.mpc_factor > 0);
+
+ // Clear the pool assignment scratch (which is per plane)
+ memset(&scratch.pipe_pool, 0, sizeof(struct dc_plane_pipe_pool));
+
+ map_pipes_for_plane(ctx, state, state->streams[stream_index], state->stream_status[stream_index].plane_states[plane_index], &scratch, existing_state);
+ } else {
+ // Plane ID cannot be generated, therefore no DML mapping can be performed.
+ ASSERT(false);
+ }
+ }
+
+ }
+
+ if (!validate_pipe_assignment(ctx, state, disp_cfg, mapping))
+ ASSERT(false);
+
+ for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
+ struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
+
+ if (pipe->plane_state) {
+ if (!ctx->config.callbacks.build_scaling_params(pipe)) {
+ ASSERT(false);
+ }
+ }
+ }
+
+ return true;
+}