From: Wenjing Liu <wenjing.liu@xxxxxxx> [why] generic dc resource file should not know what an optimal idle pipe is because this is dcn hardware dependent. [how] We move the optimial pipe searching logic in dcn specific layer. Reviewed-by: Jun Lei <jun.lei@xxxxxxx> Acked-by: Tom Chung <chiahsuan.chung@xxxxxxx> Signed-off-by: Wenjing Liu <wenjing.liu@xxxxxxx> --- .../gpu/drm/amd/display/dc/core/dc_resource.c | 184 +++++++----------- .../drm/amd/display/dc/dcn32/dcn32_resource.c | 87 ++++++++- drivers/gpu/drm/amd/display/dc/inc/resource.h | 19 +- 3 files changed, 174 insertions(+), 116 deletions(-) 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 218fe2c401e1..103dfe82dc28 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -1623,139 +1623,101 @@ struct pipe_ctx *find_idle_secondary_pipe_legacy( return secondary_pipe; } -/* - * Find the most optimal idle pipe from res_ctx, which could be used as a - * secondary dpp pipe for input opp head pipe. - * - * an idle pipe - a pipe in input res_ctx not yet used for any streams or - * planes. - * secondary dpp pipe - a pipe gets inserted to a head OPP pipe's MPC blending - * tree. This is typical used for rendering MPO planes or additional offset - * areas in MPCC combine. - * - * Hardware Transition Minimization Algorithm for Finding a Secondary DPP Pipe - * ------------------------------------------------------------------------- - * - * PROBLEM: - * - * 1. There is a hardware limitation that a secondary DPP pipe cannot be - * transferred from one MPC blending tree to the other in a single frame. - * Otherwise it could cause glitches on the screen. - * - * For instance, we cannot transition from state 1 to state 2 in one frame. This - * is because PIPE1 is transferred from PIPE0's MPC blending tree over to - * PIPE2's MPC blending tree, which is not supported by hardware. - * To support this transition we need to first remove PIPE1 from PIPE0's MPC - * blending tree in one frame and then insert PIPE1 to PIPE2's MPC blending tree - * in the next frame. This is not optimal as it will delay the flip for two - * frames. - * - * State 1: - * PIPE0 -- secondary DPP pipe --> (PIPE1) - * PIPE2 -- secondary DPP pipe --> NONE - * - * State 2: - * PIPE0 -- secondary DPP pipe --> NONE - * PIPE2 -- secondary DPP pipe --> (PIPE1) - * - * 2. We want to in general minimize the unnecessary changes in pipe topology. - * If a pipe is already added in current blending tree and there are no changes - * to plane topology, we don't want to swap it with another idle pipe - * unnecessarily in every update. Powering up and down a pipe would require a - * full update which delays the flip for 1 frame. If we use the original pipe - * we don't have to toggle its power. So we can flip faster. - */ -struct pipe_ctx *find_optimal_idle_pipe_as_secondary_dpp_pipe( +int resource_find_idle_pipe_used_in_cur_mpc_blending_tree( const struct resource_context *cur_res_ctx, struct resource_context *new_res_ctx, - const struct resource_pool *pool, - const struct pipe_ctx *new_head) + const struct pipe_ctx *cur_opp_head) { - const struct pipe_ctx *cur_head, *cur_sec; - struct pipe_ctx *new_sec; - bool found = false; - int i; - - cur_head = &cur_res_ctx->pipe_ctx[new_head->pipe_idx]; - cur_sec = cur_head->bottom_pipe; + const struct pipe_ctx *cur_sec_dpp = cur_opp_head->bottom_pipe; + struct pipe_ctx *new_sec_dpp; + int idle_pipe_idx = IDLE_PIPE_INDEX_NOT_FOUND; - while (cur_sec) { + while (cur_sec_dpp) { /* find an idle pipe used in current opp blend tree, * this is to avoid MPO pipe switching to different opp blending * tree */ - new_sec = &new_res_ctx->pipe_ctx[cur_sec->pipe_idx]; - if (new_sec->plane_state == NULL && new_sec->stream == NULL) { - new_sec->pipe_idx = cur_sec->pipe_idx; - found = true; + new_sec_dpp = &new_res_ctx->pipe_ctx[cur_sec_dpp->pipe_idx]; + if (new_sec_dpp->plane_state == NULL && + new_sec_dpp->stream == NULL) { + idle_pipe_idx = cur_sec_dpp->pipe_idx; break; } - cur_sec = cur_sec->bottom_pipe; + cur_sec_dpp = cur_sec_dpp->bottom_pipe; } - /* Up until here if we have not found an idle secondary pipe, we will - * need to wait for at least one frame to complete the transition - * sequence. - */ - if (!found) { - /* find a free pipe not used in current res ctx, this is to - * avoid tearing down other pipe's topology - */ - for (i = 0; i < pool->pipe_count; i++) { - cur_sec = &cur_res_ctx->pipe_ctx[i]; - new_sec = &new_res_ctx->pipe_ctx[i]; - - if (cur_sec->plane_state == NULL && - cur_sec->stream == NULL && - new_sec->plane_state == NULL && - new_sec->stream == NULL) { - new_sec->pipe_idx = i; - found = true; - break; - } + return idle_pipe_idx; +} + +int recource_find_idle_pipe_not_used_in_cur_res_ctx( + const struct resource_context *cur_res_ctx, + struct resource_context *new_res_ctx, + const struct resource_pool *pool) +{ + int idle_pipe_idx = IDLE_PIPE_INDEX_NOT_FOUND; + const struct pipe_ctx *new_sec_dpp, *cur_sec_dpp; + int i; + + for (i = 0; i < pool->pipe_count; i++) { + cur_sec_dpp = &cur_res_ctx->pipe_ctx[i]; + new_sec_dpp = &new_res_ctx->pipe_ctx[i]; + + if (cur_sec_dpp->plane_state == NULL && + cur_sec_dpp->stream == NULL && + new_sec_dpp->plane_state == NULL && + new_sec_dpp->stream == NULL) { + idle_pipe_idx = i; + break; } } - /* Up until here if we have not found an idle secondary pipe, we will - * need to wait for at least two frames to complete the transition - * sequence. It really doesn't matter which pipe we decide take from - * current enabled pipes. It won't save our frame time when we swap only - * one pipe or more pipes. - */ - if (!found) { - /* find a free pipe by taking away a secondary dpp pipe from an - * MPCC combine in current context - */ - for (i = 0; i < pool->pipe_count; i++) { - cur_sec = &cur_res_ctx->pipe_ctx[i]; - new_sec = &new_res_ctx->pipe_ctx[i]; - - if (cur_sec->plane_state && - cur_sec->bottom_pipe && - cur_sec->bottom_pipe->plane_state == cur_sec->plane_state && - new_sec->plane_state == NULL && - new_sec->stream == NULL) { - found = true; - new_sec->pipe_idx = i; - break; - } + return idle_pipe_idx; +} + +int resource_find_idle_pipe_used_as_cur_sec_dpp_in_mpcc_combine( + const struct resource_context *cur_res_ctx, + struct resource_context *new_res_ctx, + const struct resource_pool *pool) +{ + int idle_pipe_idx = IDLE_PIPE_INDEX_NOT_FOUND; + const struct pipe_ctx *new_sec_dpp, *cur_sec_dpp; + int i; + + for (i = 0; i < pool->pipe_count; i++) { + cur_sec_dpp = &cur_res_ctx->pipe_ctx[i]; + new_sec_dpp = &new_res_ctx->pipe_ctx[i]; + + if (cur_sec_dpp->plane_state && + cur_sec_dpp->top_pipe && + cur_sec_dpp->top_pipe->plane_state == cur_sec_dpp->plane_state && + new_sec_dpp->plane_state == NULL && + new_sec_dpp->stream == NULL) { + idle_pipe_idx = i; + break; } } - if (!found) { - /* find any pipe not used by new state */ - for (i = 0; i < pool->pipe_count; i++) { - new_sec = &new_res_ctx->pipe_ctx[i]; + return idle_pipe_idx; +} - if (new_sec->plane_state == NULL) { - found = true; - new_sec->pipe_idx = i; - break; - } +int resource_find_any_idle_pipe(struct resource_context *new_res_ctx, + const struct resource_pool *pool) +{ + int idle_pipe_idx = IDLE_PIPE_INDEX_NOT_FOUND; + const struct pipe_ctx *new_sec_dpp; + int i; + + for (i = 0; i < pool->pipe_count; i++) { + new_sec_dpp = &new_res_ctx->pipe_ctx[i]; + + if (new_sec_dpp->plane_state == NULL && + new_sec_dpp->stream == NULL) { + idle_pipe_idx = i; + break; } } - return found ? new_sec : NULL; + return idle_pipe_idx; } /* TODO: Unify the pipe naming convention: diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c index fd6511da2903..2615a89d580b 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c @@ -2485,18 +2485,100 @@ struct resource_pool *dcn32_create_resource_pool( return NULL; } +/* + * Find the most optimal idle pipe from res_ctx, which could be used as a + * secondary dpp pipe for input opp head pipe. + * + * an idle pipe - a pipe in input res_ctx not yet used for any streams or + * planes. + * secondary dpp pipe - a pipe gets inserted to a head OPP pipe's MPC blending + * tree. This is typical used for rendering MPO planes or additional offset + * areas in MPCC combine. + * + * Hardware Transition Minimization Algorithm for Finding a Secondary DPP Pipe + * ------------------------------------------------------------------------- + * + * PROBLEM: + * + * 1. There is a hardware limitation that a secondary DPP pipe cannot be + * transferred from one MPC blending tree to the other in a single frame. + * Otherwise it could cause glitches on the screen. + * + * For instance, we cannot transition from state 1 to state 2 in one frame. This + * is because PIPE1 is transferred from PIPE0's MPC blending tree over to + * PIPE2's MPC blending tree, which is not supported by hardware. + * To support this transition we need to first remove PIPE1 from PIPE0's MPC + * blending tree in one frame and then insert PIPE1 to PIPE2's MPC blending tree + * in the next frame. This is not optimal as it will delay the flip for two + * frames. + * + * State 1: + * PIPE0 -- secondary DPP pipe --> (PIPE1) + * PIPE2 -- secondary DPP pipe --> NONE + * + * State 2: + * PIPE0 -- secondary DPP pipe --> NONE + * PIPE2 -- secondary DPP pipe --> (PIPE1) + * + * 2. We want to in general minimize the unnecessary changes in pipe topology. + * If a pipe is already added in current blending tree and there are no changes + * to plane topology, we don't want to swap it with another idle pipe + * unnecessarily in every update. Powering up and down a pipe would require a + * full update which delays the flip for 1 frame. If we use the original pipe + * we don't have to toggle its power. So we can flip faster. + */ +static int find_optimal_idle_pipe_as_secondary_dpp_pipe( + const struct resource_context *cur_res_ctx, + struct resource_context *new_res_ctx, + const struct resource_pool *pool, + const struct pipe_ctx *new_opp_head) +{ + const struct pipe_ctx *cur_opp_head; + int idle_pipe_idx; + + cur_opp_head = &cur_res_ctx->pipe_ctx[new_opp_head->pipe_idx]; + idle_pipe_idx = resource_find_idle_pipe_used_in_cur_mpc_blending_tree( + cur_res_ctx, new_res_ctx, cur_opp_head); + + /* Up until here if we have not found an idle secondary pipe, we will + * need to wait for at least one frame to complete the transition + * sequence. + */ + if (idle_pipe_idx == IDLE_PIPE_INDEX_NOT_FOUND) + idle_pipe_idx = recource_find_idle_pipe_not_used_in_cur_res_ctx( + cur_res_ctx, new_res_ctx, pool); + + /* Up until here if we have not found an idle secondary pipe, we will + * need to wait for at least two frames to complete the transition + * sequence. It really doesn't matter which pipe we decide take from + * current enabled pipes. It won't save our frame time when we swap only + * one pipe or more pipes. + */ + if (idle_pipe_idx == IDLE_PIPE_INDEX_NOT_FOUND) + idle_pipe_idx = resource_find_idle_pipe_used_as_cur_sec_dpp_in_mpcc_combine( + cur_res_ctx, new_res_ctx, pool); + + if (idle_pipe_idx == IDLE_PIPE_INDEX_NOT_FOUND) + idle_pipe_idx = resource_find_any_idle_pipe(new_res_ctx, pool); + + return idle_pipe_idx; +} + struct pipe_ctx *dcn32_acquire_idle_pipe_for_layer( const struct dc_state *cur_ctx, struct dc_state *new_ctx, const struct resource_pool *pool, const struct pipe_ctx *opp_head_pipe) { - struct pipe_ctx *idle_pipe = + int idle_pipe_idx = find_optimal_idle_pipe_as_secondary_dpp_pipe( &cur_ctx->res_ctx, &new_ctx->res_ctx, pool, opp_head_pipe); + struct pipe_ctx *idle_pipe; - if (idle_pipe) { + if (idle_pipe_idx >= 0) { + idle_pipe = &new_ctx->res_ctx.pipe_ctx[idle_pipe_idx]; + idle_pipe->pipe_idx = idle_pipe_idx; idle_pipe->stream = opp_head_pipe->stream; idle_pipe->stream_res.tg = opp_head_pipe->stream_res.tg; idle_pipe->stream_res.opp = opp_head_pipe->stream_res.opp; @@ -2508,6 +2590,7 @@ struct pipe_ctx *dcn32_acquire_idle_pipe_for_layer( pool->dpps[idle_pipe->pipe_idx]->inst; } else { ASSERT(opp_head_pipe); + idle_pipe = NULL; } return idle_pipe; diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h index 6ac6680711ff..c518ee8b1a03 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/resource.h +++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h @@ -37,6 +37,7 @@ #define IS_PIPE_SYNCD_VALID(pipe) ((((pipe)->pipe_idx_syncd) & 0x80)?1:0) #define GET_PIPE_SYNCD_FROM_PIPE(pipe) ((pipe)->pipe_idx_syncd & 0x7F) #define SET_PIPE_SYNCD_TO_PIPE(pipe, pipe_syncd) ((pipe)->pipe_idx_syncd = (0x80 | pipe_syncd)) +#define IDLE_PIPE_INDEX_NOT_FOUND -1 enum dce_version resource_parse_asic_id( struct hw_asic_id asic_id); @@ -158,11 +159,23 @@ struct pipe_ctx *find_idle_secondary_pipe_legacy( const struct resource_pool *pool, const struct pipe_ctx *primary_pipe); -struct pipe_ctx *find_optimal_idle_pipe_as_secondary_dpp_pipe( +int resource_find_idle_pipe_used_in_cur_mpc_blending_tree( const struct resource_context *cur_res_ctx, struct resource_context *new_res_ctx, - const struct resource_pool *pool, - const struct pipe_ctx *new_pri); + const struct pipe_ctx *cur_opp_head); + +int recource_find_idle_pipe_not_used_in_cur_res_ctx( + const struct resource_context *cur_res_ctx, + struct resource_context *new_res_ctx, + const struct resource_pool *pool); + +int resource_find_idle_pipe_used_as_cur_sec_dpp_in_mpcc_combine( + const struct resource_context *cur_res_ctx, + struct resource_context *new_res_ctx, + const struct resource_pool *pool); + +int resource_find_any_idle_pipe(struct resource_context *new_res_ctx, + const struct resource_pool *pool); bool resource_validate_attach_surfaces( const struct dc_validation_set set[], -- 2.25.1