| 1 | /* SPDX-License-Identifier: MIT */ |
| 2 | /* |
| 3 | * Copyright 2023 Advanced Micro Devices, Inc. |
| 4 | * |
| 5 | * Permission is hereby granted, free of charge, to any person obtaining a |
| 6 | * copy of this software and associated documentation files (the "Software"), |
| 7 | * to deal in the Software without restriction, including without limitation |
| 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| 9 | * and/or sell copies of the Software, and to permit persons to whom the |
| 10 | * Software is furnished to do so, subject to the following conditions: |
| 11 | * |
| 12 | * The above copyright notice and this permission notice shall be included in |
| 13 | * all copies or substantial portions of the Software. |
| 14 | * |
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 18 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| 21 | * OTHER DEALINGS IN THE SOFTWARE. |
| 22 | * |
| 23 | * Authors: AMD |
| 24 | * |
| 25 | */ |
| 26 | |
| 27 | |
| 28 | #include "dml2_dc_types.h" |
| 29 | #include "dml2_internal_types.h" |
| 30 | #include "dml2_utils.h" |
| 31 | #include "dml2_mall_phantom.h" |
| 32 | |
| 33 | unsigned int dml2_helper_calculate_num_ways_for_subvp(struct dml2_context *ctx, struct dc_state *context) |
| 34 | { |
| 35 | uint32_t num_ways = 0; |
| 36 | uint32_t bytes_per_pixel = 0; |
| 37 | uint32_t cache_lines_used = 0; |
| 38 | uint32_t lines_per_way = 0; |
| 39 | uint32_t total_cache_lines = 0; |
| 40 | uint32_t bytes_in_mall = 0; |
| 41 | uint32_t num_mblks = 0; |
| 42 | uint32_t cache_lines_per_plane = 0; |
| 43 | uint32_t i = 0; |
| 44 | uint32_t mblk_width = 0; |
| 45 | uint32_t mblk_height = 0; |
| 46 | uint32_t full_vp_width_blk_aligned = 0; |
| 47 | uint32_t mall_alloc_width_blk_aligned = 0; |
| 48 | uint32_t mall_alloc_height_blk_aligned = 0; |
| 49 | |
| 50 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 51 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
| 52 | |
| 53 | // Find the phantom pipes |
| 54 | if (pipe->stream && pipe->plane_state && !pipe->top_pipe && !pipe->prev_odm_pipe && |
| 55 | ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) { |
| 56 | bytes_per_pixel = pipe->plane_state->format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616 ? 8 : 4; |
| 57 | mblk_width = ctx->config.mall_cfg.mblk_width_pixels; |
| 58 | mblk_height = bytes_per_pixel == 4 ? mblk_width = ctx->config.mall_cfg.mblk_height_4bpe_pixels : ctx->config.mall_cfg.mblk_height_8bpe_pixels; |
| 59 | |
| 60 | /* full_vp_width_blk_aligned = FLOOR(vp_x_start + full_vp_width + blk_width - 1, blk_width) - |
| 61 | * FLOOR(vp_x_start, blk_width) |
| 62 | */ |
| 63 | full_vp_width_blk_aligned = ((pipe->plane_res.scl_data.viewport.x + |
| 64 | pipe->plane_res.scl_data.viewport.width + mblk_width - 1) / mblk_width * mblk_width) + |
| 65 | (pipe->plane_res.scl_data.viewport.x / mblk_width * mblk_width); |
| 66 | |
| 67 | /* mall_alloc_width_blk_aligned_l/c = full_vp_width_blk_aligned_l/c */ |
| 68 | mall_alloc_width_blk_aligned = full_vp_width_blk_aligned; |
| 69 | |
| 70 | /* mall_alloc_height_blk_aligned_l/c = CEILING(sub_vp_height_l/c - 1, blk_height_l/c) + blk_height_l/c */ |
| 71 | mall_alloc_height_blk_aligned = (pipe->stream->timing.v_addressable - 1 + mblk_height - 1) / |
| 72 | mblk_height * mblk_height + mblk_height; |
| 73 | |
| 74 | /* full_mblk_width_ub_l/c = malldml2_mall_phantom.c_alloc_width_blk_aligned_l/c; |
| 75 | * full_mblk_height_ub_l/c = mall_alloc_height_blk_aligned_l/c; |
| 76 | * num_mblk_l/c = (full_mblk_width_ub_l/c / mblk_width_l/c) * (full_mblk_height_ub_l/c / mblk_height_l/c); |
| 77 | * (Should be divisible, but round up if not) |
| 78 | */ |
| 79 | num_mblks = ((mall_alloc_width_blk_aligned + mblk_width - 1) / mblk_width) * |
| 80 | ((mall_alloc_height_blk_aligned + mblk_height - 1) / mblk_height); |
| 81 | bytes_in_mall = num_mblks * ctx->config.mall_cfg.mblk_size_bytes; |
| 82 | // cache lines used is total bytes / cache_line size. Add +2 for worst case alignment |
| 83 | // (MALL is 64-byte aligned) |
| 84 | cache_lines_per_plane = bytes_in_mall / ctx->config.mall_cfg.cache_line_size_bytes + 2; |
| 85 | |
| 86 | // For DCC we must cache the meat surface, so double cache lines required |
| 87 | if (pipe->plane_state->dcc.enable) |
| 88 | cache_lines_per_plane *= 2; |
| 89 | cache_lines_used += cache_lines_per_plane; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | total_cache_lines = ctx->config.mall_cfg.max_cab_allocation_bytes / ctx->config.mall_cfg.cache_line_size_bytes; |
| 94 | lines_per_way = total_cache_lines / ctx->config.mall_cfg.cache_num_ways; |
| 95 | num_ways = cache_lines_used / lines_per_way; |
| 96 | if (cache_lines_used % lines_per_way > 0) |
| 97 | num_ways++; |
| 98 | |
| 99 | return num_ways; |
| 100 | } |
| 101 | |
| 102 | static void merge_pipes_for_subvp(struct dml2_context *ctx, struct dc_state *context) |
| 103 | { |
| 104 | int i; |
| 105 | |
| 106 | /* merge pipes if necessary */ |
| 107 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 108 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
| 109 | |
| 110 | // For now merge all pipes for SubVP since pipe split case isn't supported yet |
| 111 | |
| 112 | /* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */ |
| 113 | if (pipe->prev_odm_pipe) { |
| 114 | /*split off odm pipe*/ |
| 115 | pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe; |
| 116 | if (pipe->next_odm_pipe) |
| 117 | pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe; |
| 118 | |
| 119 | pipe->bottom_pipe = NULL; |
| 120 | pipe->next_odm_pipe = NULL; |
| 121 | pipe->plane_state = NULL; |
| 122 | pipe->stream = NULL; |
| 123 | pipe->top_pipe = NULL; |
| 124 | pipe->prev_odm_pipe = NULL; |
| 125 | if (pipe->stream_res.dsc) |
| 126 | ctx->config.svp_pstate.callbacks.release_dsc(&context->res_ctx, ctx->config.svp_pstate.callbacks.dc->res_pool, &pipe->stream_res.dsc); |
| 127 | memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); |
| 128 | memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); |
| 129 | } else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) { |
| 130 | struct pipe_ctx *top_pipe = pipe->top_pipe; |
| 131 | struct pipe_ctx *bottom_pipe = pipe->bottom_pipe; |
| 132 | |
| 133 | top_pipe->bottom_pipe = bottom_pipe; |
| 134 | if (bottom_pipe) |
| 135 | bottom_pipe->top_pipe = top_pipe; |
| 136 | |
| 137 | pipe->top_pipe = NULL; |
| 138 | pipe->bottom_pipe = NULL; |
| 139 | pipe->plane_state = NULL; |
| 140 | pipe->stream = NULL; |
| 141 | memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); |
| 142 | memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | static bool all_pipes_have_stream_and_plane(struct dml2_context *ctx, const struct dc_state *context) |
| 148 | { |
| 149 | int i; |
| 150 | |
| 151 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 152 | const struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
| 153 | |
| 154 | if (!pipe->stream) |
| 155 | continue; |
| 156 | |
| 157 | if (!pipe->plane_state) |
| 158 | return false; |
| 159 | } |
| 160 | return true; |
| 161 | } |
| 162 | |
| 163 | static bool mpo_in_use(const struct dc_state *context) |
| 164 | { |
| 165 | int i; |
| 166 | |
| 167 | for (i = 0; i < context->stream_count; i++) { |
| 168 | if (context->stream_status[i].plane_count > 1) |
| 169 | return true; |
| 170 | } |
| 171 | return false; |
| 172 | } |
| 173 | |
| 174 | /* |
| 175 | * dcn32_get_num_free_pipes: Calculate number of free pipes |
| 176 | * |
| 177 | * This function assumes that a "used" pipe is a pipe that has |
| 178 | * both a stream and a plane assigned to it. |
| 179 | * |
| 180 | * @dc: current dc state |
| 181 | * @context: new dc state |
| 182 | * |
| 183 | * Return: |
| 184 | * Number of free pipes available in the context |
| 185 | */ |
| 186 | static unsigned int get_num_free_pipes(struct dml2_context *ctx, struct dc_state *state) |
| 187 | { |
| 188 | unsigned int i; |
| 189 | unsigned int free_pipes = 0; |
| 190 | unsigned int num_pipes = 0; |
| 191 | |
| 192 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 193 | struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i]; |
| 194 | |
| 195 | if (pipe->stream && !pipe->top_pipe) { |
| 196 | while (pipe) { |
| 197 | num_pipes++; |
| 198 | pipe = pipe->bottom_pipe; |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | free_pipes = ctx->config.dcn_pipe_count - num_pipes; |
| 204 | return free_pipes; |
| 205 | } |
| 206 | |
| 207 | /* |
| 208 | * dcn32_assign_subvp_pipe: Function to decide which pipe will use Sub-VP. |
| 209 | * |
| 210 | * We enter this function if we are Sub-VP capable (i.e. enough pipes available) |
| 211 | * and regular P-State switching (i.e. VACTIVE/VBLANK) is not supported, or if |
| 212 | * we are forcing SubVP P-State switching on the current config. |
| 213 | * |
| 214 | * The number of pipes used for the chosen surface must be less than or equal to the |
| 215 | * number of free pipes available. |
| 216 | * |
| 217 | * In general we choose surfaces with the longest frame time first (better for SubVP + VBLANK). |
| 218 | * For multi-display cases the ActiveDRAMClockChangeMargin doesn't provide enough info on its own |
| 219 | * for determining which should be the SubVP pipe (need a way to determine if a pipe / plane doesn't |
| 220 | * support MCLK switching naturally [i.e. ACTIVE or VBLANK]). |
| 221 | * |
| 222 | * @param dc: current dc state |
| 223 | * @param context: new dc state |
| 224 | * @param index: [out] dc pipe index for the pipe chosen to have phantom pipes assigned |
| 225 | * |
| 226 | * Return: |
| 227 | * True if a valid pipe assignment was found for Sub-VP. Otherwise false. |
| 228 | */ |
| 229 | static bool assign_subvp_pipe(struct dml2_context *ctx, struct dc_state *context, unsigned int *index) |
| 230 | { |
| 231 | unsigned int i, pipe_idx; |
| 232 | unsigned int max_frame_time = 0; |
| 233 | bool valid_assignment_found = false; |
| 234 | unsigned int free_pipes = 2; //dcn32_get_num_free_pipes(dc, context); |
| 235 | bool current_assignment_freesync = false; |
| 236 | struct vba_vars_st *vba = &context->bw_ctx.dml.vba; |
| 237 | |
| 238 | for (i = 0, pipe_idx = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 239 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
| 240 | unsigned int num_pipes = 0; |
| 241 | unsigned int refresh_rate = 0; |
| 242 | |
| 243 | if (!pipe->stream) |
| 244 | continue; |
| 245 | |
| 246 | // Round up |
| 247 | refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 + |
| 248 | pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1) |
| 249 | / (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total); |
| 250 | /* SubVP pipe candidate requirements: |
| 251 | * - Refresh rate < 120hz |
| 252 | * - Not able to switch in vactive naturally (switching in active means the |
| 253 | * DET provides enough buffer to hide the P-State switch latency -- trying |
| 254 | * to combine this with SubVP can cause issues with the scheduling). |
| 255 | */ |
| 256 | if (pipe->plane_state && !pipe->top_pipe && |
| 257 | ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_NONE && refresh_rate < 120 && |
| 258 | vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] <= 0) { |
| 259 | while (pipe) { |
| 260 | num_pipes++; |
| 261 | pipe = pipe->bottom_pipe; |
| 262 | } |
| 263 | |
| 264 | pipe = &context->res_ctx.pipe_ctx[i]; |
| 265 | if (num_pipes <= free_pipes) { |
| 266 | struct dc_stream_state *stream = pipe->stream; |
| 267 | unsigned int frame_us = (stream->timing.v_total * stream->timing.h_total / |
| 268 | (double)(stream->timing.pix_clk_100hz * 100)) * 1000000; |
| 269 | if (frame_us > max_frame_time && !stream->ignore_msa_timing_param) { |
| 270 | *index = i; |
| 271 | max_frame_time = frame_us; |
| 272 | valid_assignment_found = true; |
| 273 | current_assignment_freesync = false; |
| 274 | /* For the 2-Freesync display case, still choose the one with the |
| 275 | * longest frame time |
| 276 | */ |
| 277 | } else if (stream->ignore_msa_timing_param && (!valid_assignment_found || |
| 278 | (current_assignment_freesync && frame_us > max_frame_time))) { |
| 279 | *index = i; |
| 280 | valid_assignment_found = true; |
| 281 | current_assignment_freesync = true; |
| 282 | } |
| 283 | } |
| 284 | } |
| 285 | pipe_idx++; |
| 286 | } |
| 287 | return valid_assignment_found; |
| 288 | } |
| 289 | |
| 290 | /* |
| 291 | * enough_pipes_for_subvp: Function to check if there are "enough" pipes for SubVP. |
| 292 | * |
| 293 | * This function returns true if there are enough free pipes |
| 294 | * to create the required phantom pipes for any given stream |
| 295 | * (that does not already have phantom pipe assigned). |
| 296 | * |
| 297 | * e.g. For a 2 stream config where the first stream uses one |
| 298 | * pipe and the second stream uses 2 pipes (i.e. pipe split), |
| 299 | * this function will return true because there is 1 remaining |
| 300 | * pipe which can be used as the phantom pipe for the non pipe |
| 301 | * split pipe. |
| 302 | * |
| 303 | * @dc: current dc state |
| 304 | * @context: new dc state |
| 305 | * |
| 306 | * Return: |
| 307 | * True if there are enough free pipes to assign phantom pipes to at least one |
| 308 | * stream that does not already have phantom pipes assigned. Otherwise false. |
| 309 | */ |
| 310 | static bool enough_pipes_for_subvp(struct dml2_context *ctx, struct dc_state *state) |
| 311 | { |
| 312 | unsigned int i, split_cnt, free_pipes; |
| 313 | unsigned int min_pipe_split = ctx->config.dcn_pipe_count + 1; // init as max number of pipes + 1 |
| 314 | bool subvp_possible = false; |
| 315 | |
| 316 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 317 | struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i]; |
| 318 | |
| 319 | // Find the minimum pipe split count for non SubVP pipes |
| 320 | if (pipe->stream && !pipe->top_pipe && |
| 321 | ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(state, pipe) == SUBVP_NONE) { |
| 322 | split_cnt = 0; |
| 323 | while (pipe) { |
| 324 | split_cnt++; |
| 325 | pipe = pipe->bottom_pipe; |
| 326 | } |
| 327 | |
| 328 | if (split_cnt < min_pipe_split) |
| 329 | min_pipe_split = split_cnt; |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | free_pipes = get_num_free_pipes(ctx, state); |
| 334 | |
| 335 | // SubVP only possible if at least one pipe is being used (i.e. free_pipes |
| 336 | // should not equal to the pipe_count) |
| 337 | if (free_pipes >= min_pipe_split && free_pipes < ctx->config.dcn_pipe_count) |
| 338 | subvp_possible = true; |
| 339 | |
| 340 | return subvp_possible; |
| 341 | } |
| 342 | |
| 343 | /* |
| 344 | * subvp_subvp_schedulable: Determine if SubVP + SubVP config is schedulable |
| 345 | * |
| 346 | * High level algorithm: |
| 347 | * 1. Find longest microschedule length (in us) between the two SubVP pipes |
| 348 | * 2. Check if the worst case overlap (VBLANK in middle of ACTIVE) for both |
| 349 | * pipes still allows for the maximum microschedule to fit in the active |
| 350 | * region for both pipes. |
| 351 | * |
| 352 | * @dc: current dc state |
| 353 | * @context: new dc state |
| 354 | * |
| 355 | * Return: |
| 356 | * bool - True if the SubVP + SubVP config is schedulable, false otherwise |
| 357 | */ |
| 358 | static bool subvp_subvp_schedulable(struct dml2_context *ctx, struct dc_state *context) |
| 359 | { |
| 360 | struct pipe_ctx *subvp_pipes[2]; |
| 361 | struct dc_stream_state *phantom = NULL; |
| 362 | uint32_t microschedule_lines = 0; |
| 363 | uint32_t index = 0; |
| 364 | uint32_t i; |
| 365 | uint32_t max_microschedule_us = 0; |
| 366 | int32_t vactive1_us, vactive2_us, vblank1_us, vblank2_us; |
| 367 | |
| 368 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 369 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
| 370 | uint32_t time_us = 0; |
| 371 | |
| 372 | /* Loop to calculate the maximum microschedule time between the two SubVP pipes, |
| 373 | * and also to store the two main SubVP pipe pointers in subvp_pipes[2]. |
| 374 | */ |
| 375 | if (pipe->stream && pipe->plane_state && !pipe->top_pipe && |
| 376 | ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_MAIN) { |
| 377 | phantom = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(context, pipe->stream); |
| 378 | microschedule_lines = (phantom->timing.v_total - phantom->timing.v_front_porch) + |
| 379 | phantom->timing.v_addressable; |
| 380 | |
| 381 | // Round up when calculating microschedule time (+ 1 at the end) |
| 382 | time_us = (microschedule_lines * phantom->timing.h_total) / |
| 383 | (double)(phantom->timing.pix_clk_100hz * 100) * 1000000 + |
| 384 | ctx->config.svp_pstate.subvp_prefetch_end_to_mall_start_us + |
| 385 | ctx->config.svp_pstate.subvp_fw_processing_delay_us + 1; |
| 386 | if (time_us > max_microschedule_us) |
| 387 | max_microschedule_us = time_us; |
| 388 | |
| 389 | subvp_pipes[index] = pipe; |
| 390 | index++; |
| 391 | |
| 392 | // Maximum 2 SubVP pipes |
| 393 | if (index == 2) |
| 394 | break; |
| 395 | } |
| 396 | } |
| 397 | vactive1_us = ((subvp_pipes[0]->stream->timing.v_addressable * subvp_pipes[0]->stream->timing.h_total) / |
| 398 | (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000; |
| 399 | vactive2_us = ((subvp_pipes[1]->stream->timing.v_addressable * subvp_pipes[1]->stream->timing.h_total) / |
| 400 | (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000; |
| 401 | vblank1_us = (((subvp_pipes[0]->stream->timing.v_total - subvp_pipes[0]->stream->timing.v_addressable) * |
| 402 | subvp_pipes[0]->stream->timing.h_total) / |
| 403 | (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000; |
| 404 | vblank2_us = (((subvp_pipes[1]->stream->timing.v_total - subvp_pipes[1]->stream->timing.v_addressable) * |
| 405 | subvp_pipes[1]->stream->timing.h_total) / |
| 406 | (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000; |
| 407 | |
| 408 | if ((vactive1_us - vblank2_us) / 2 > max_microschedule_us && |
| 409 | (vactive2_us - vblank1_us) / 2 > max_microschedule_us) |
| 410 | return true; |
| 411 | |
| 412 | return false; |
| 413 | } |
| 414 | |
| 415 | /* |
| 416 | * dml2_svp_drr_schedulable: Determine if SubVP + DRR config is schedulable |
| 417 | * |
| 418 | * High level algorithm: |
| 419 | * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe |
| 420 | * 2. Determine the frame time for the DRR display when adding required margin for MCLK switching |
| 421 | * (the margin is equal to the MALL region + DRR margin (500us)) |
| 422 | * 3.If (SubVP Active - Prefetch > Stretched DRR frame + max(MALL region, Stretched DRR frame)) |
| 423 | * then report the configuration as supported |
| 424 | * |
| 425 | * @dc: current dc state |
| 426 | * @context: new dc state |
| 427 | * @drr_pipe: DRR pipe_ctx for the SubVP + DRR config |
| 428 | * |
| 429 | * Return: |
| 430 | * bool - True if the SubVP + DRR config is schedulable, false otherwise |
| 431 | */ |
| 432 | bool dml2_svp_drr_schedulable(struct dml2_context *ctx, struct dc_state *context, struct dc_crtc_timing *drr_timing) |
| 433 | { |
| 434 | bool schedulable = false; |
| 435 | uint32_t i; |
| 436 | struct pipe_ctx *pipe = NULL; |
| 437 | struct dc_crtc_timing *main_timing = NULL; |
| 438 | struct dc_crtc_timing *phantom_timing = NULL; |
| 439 | struct dc_stream_state *phantom_stream; |
| 440 | int16_t prefetch_us = 0; |
| 441 | int16_t mall_region_us = 0; |
| 442 | int16_t drr_frame_us = 0; // nominal frame time |
| 443 | int16_t subvp_active_us = 0; |
| 444 | int16_t stretched_drr_us = 0; |
| 445 | int16_t drr_stretched_vblank_us = 0; |
| 446 | int16_t max_vblank_mallregion = 0; |
| 447 | |
| 448 | // Find SubVP pipe |
| 449 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 450 | pipe = &context->res_ctx.pipe_ctx[i]; |
| 451 | |
| 452 | // We check for master pipe, but it shouldn't matter since we only need |
| 453 | // the pipe for timing info (stream should be same for any pipe splits) |
| 454 | if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe) |
| 455 | continue; |
| 456 | |
| 457 | // Find the SubVP pipe |
| 458 | if (ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_MAIN) |
| 459 | break; |
| 460 | } |
| 461 | |
| 462 | phantom_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(context, pipe->stream); |
| 463 | main_timing = &pipe->stream->timing; |
| 464 | phantom_timing = &phantom_stream->timing; |
| 465 | prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total / |
| 466 | (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 + |
| 467 | ctx->config.svp_pstate.subvp_prefetch_end_to_mall_start_us; |
| 468 | subvp_active_us = main_timing->v_addressable * main_timing->h_total / |
| 469 | (double)(main_timing->pix_clk_100hz * 100) * 1000000; |
| 470 | drr_frame_us = drr_timing->v_total * drr_timing->h_total / |
| 471 | (double)(drr_timing->pix_clk_100hz * 100) * 1000000; |
| 472 | // P-State allow width and FW delays already included phantom_timing->v_addressable |
| 473 | mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total / |
| 474 | (double)(phantom_timing->pix_clk_100hz * 100) * 1000000; |
| 475 | stretched_drr_us = drr_frame_us + mall_region_us + SUBVP_DRR_MARGIN_US; |
| 476 | drr_stretched_vblank_us = (drr_timing->v_total - drr_timing->v_addressable) * drr_timing->h_total / |
| 477 | (double)(drr_timing->pix_clk_100hz * 100) * 1000000 + (stretched_drr_us - drr_frame_us); |
| 478 | max_vblank_mallregion = drr_stretched_vblank_us > mall_region_us ? drr_stretched_vblank_us : mall_region_us; |
| 479 | |
| 480 | /* We consider SubVP + DRR schedulable if the stretched frame duration of the DRR display (i.e. the |
| 481 | * highest refresh rate + margin that can support UCLK P-State switch) passes the static analysis |
| 482 | * for VBLANK: (VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time, |
| 483 | * and the max of (VBLANK blanking time, MALL region)). |
| 484 | */ |
| 485 | if (stretched_drr_us < (1 / (double)drr_timing->min_refresh_in_uhz) * 1000000 * 1000000 && |
| 486 | subvp_active_us - prefetch_us - stretched_drr_us - max_vblank_mallregion > 0) |
| 487 | schedulable = true; |
| 488 | |
| 489 | return schedulable; |
| 490 | } |
| 491 | |
| 492 | |
| 493 | /* |
| 494 | * subvp_vblank_schedulable: Determine if SubVP + VBLANK config is schedulable |
| 495 | * |
| 496 | * High level algorithm: |
| 497 | * 1. Get timing for SubVP pipe, phantom pipe, and VBLANK pipe |
| 498 | * 2. If (SubVP Active - Prefetch > Vblank Frame Time + max(MALL region, Vblank blanking time)) |
| 499 | * then report the configuration as supported |
| 500 | * 3. If the VBLANK display is DRR, then take the DRR static schedulability path |
| 501 | * |
| 502 | * @dc: current dc state |
| 503 | * @context: new dc state |
| 504 | * |
| 505 | * Return: |
| 506 | * bool - True if the SubVP + VBLANK/DRR config is schedulable, false otherwise |
| 507 | */ |
| 508 | static bool subvp_vblank_schedulable(struct dml2_context *ctx, struct dc_state *context) |
| 509 | { |
| 510 | struct pipe_ctx *pipe = NULL; |
| 511 | struct pipe_ctx *subvp_pipe = NULL; |
| 512 | bool found = false; |
| 513 | bool schedulable = false; |
| 514 | uint32_t i = 0; |
| 515 | uint8_t vblank_index = 0; |
| 516 | uint16_t prefetch_us = 0; |
| 517 | uint16_t mall_region_us = 0; |
| 518 | uint16_t vblank_frame_us = 0; |
| 519 | uint16_t subvp_active_us = 0; |
| 520 | uint16_t vblank_blank_us = 0; |
| 521 | uint16_t max_vblank_mallregion = 0; |
| 522 | struct dc_crtc_timing *main_timing = NULL; |
| 523 | struct dc_crtc_timing *phantom_timing = NULL; |
| 524 | struct dc_crtc_timing *vblank_timing = NULL; |
| 525 | struct dc_stream_state *phantom_stream; |
| 526 | enum mall_stream_type pipe_mall_type; |
| 527 | |
| 528 | /* For SubVP + VBLANK/DRR cases, we assume there can only be |
| 529 | * a single VBLANK/DRR display. If DML outputs SubVP + VBLANK |
| 530 | * is supported, it is either a single VBLANK case or two VBLANK |
| 531 | * displays which are synchronized (in which case they have identical |
| 532 | * timings). |
| 533 | */ |
| 534 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 535 | pipe = &context->res_ctx.pipe_ctx[i]; |
| 536 | pipe_mall_type = ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe); |
| 537 | |
| 538 | // We check for master pipe, but it shouldn't matter since we only need |
| 539 | // the pipe for timing info (stream should be same for any pipe splits) |
| 540 | if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe) |
| 541 | continue; |
| 542 | |
| 543 | if (!found && pipe_mall_type == SUBVP_NONE) { |
| 544 | // Found pipe which is not SubVP or Phantom (i.e. the VBLANK pipe). |
| 545 | vblank_index = i; |
| 546 | found = true; |
| 547 | } |
| 548 | |
| 549 | if (!subvp_pipe && pipe_mall_type == SUBVP_MAIN) |
| 550 | subvp_pipe = pipe; |
| 551 | } |
| 552 | // Use ignore_msa_timing_param flag to identify as DRR |
| 553 | if (found && context->res_ctx.pipe_ctx[vblank_index].stream->ignore_msa_timing_param) { |
| 554 | // SUBVP + DRR case |
| 555 | schedulable = dml2_svp_drr_schedulable(ctx, context, drr_timing: &context->res_ctx.pipe_ctx[vblank_index].stream->timing); |
| 556 | } else if (found) { |
| 557 | phantom_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(context, subvp_pipe->stream); |
| 558 | main_timing = &subvp_pipe->stream->timing; |
| 559 | phantom_timing = &phantom_stream->timing; |
| 560 | vblank_timing = &context->res_ctx.pipe_ctx[vblank_index].stream->timing; |
| 561 | // Prefetch time is equal to VACTIVE + BP + VSYNC of the phantom pipe |
| 562 | // Also include the prefetch end to mallstart delay time |
| 563 | prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total / |
| 564 | (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 + |
| 565 | ctx->config.svp_pstate.subvp_prefetch_end_to_mall_start_us; |
| 566 | // P-State allow width and FW delays already included phantom_timing->v_addressable |
| 567 | mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total / |
| 568 | (double)(phantom_timing->pix_clk_100hz * 100) * 1000000; |
| 569 | vblank_frame_us = vblank_timing->v_total * vblank_timing->h_total / |
| 570 | (double)(vblank_timing->pix_clk_100hz * 100) * 1000000; |
| 571 | vblank_blank_us = (vblank_timing->v_total - vblank_timing->v_addressable) * vblank_timing->h_total / |
| 572 | (double)(vblank_timing->pix_clk_100hz * 100) * 1000000; |
| 573 | subvp_active_us = main_timing->v_addressable * main_timing->h_total / |
| 574 | (double)(main_timing->pix_clk_100hz * 100) * 1000000; |
| 575 | max_vblank_mallregion = vblank_blank_us > mall_region_us ? vblank_blank_us : mall_region_us; |
| 576 | |
| 577 | // Schedulable if VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time, |
| 578 | // and the max of (VBLANK blanking time, MALL region) |
| 579 | // TODO: Possibly add some margin (i.e. the below conditions should be [...] > X instead of [...] > 0) |
| 580 | if (subvp_active_us - prefetch_us - vblank_frame_us - max_vblank_mallregion > 0) |
| 581 | schedulable = true; |
| 582 | } |
| 583 | return schedulable; |
| 584 | } |
| 585 | |
| 586 | /* |
| 587 | * subvp_validate_static_schedulability: Check which SubVP case is calculated and handle |
| 588 | * static analysis based on the case. |
| 589 | * |
| 590 | * Three cases: |
| 591 | * 1. SubVP + SubVP |
| 592 | * 2. SubVP + VBLANK (DRR checked internally) |
| 593 | * 3. SubVP + VACTIVE (currently unsupported) |
| 594 | * |
| 595 | * @dc: current dc state |
| 596 | * @context: new dc state |
| 597 | * @vlevel: Voltage level calculated by DML |
| 598 | * |
| 599 | * Return: |
| 600 | * bool - True if statically schedulable, false otherwise |
| 601 | */ |
| 602 | bool dml2_svp_validate_static_schedulability(struct dml2_context *ctx, struct dc_state *context, enum dml_dram_clock_change_support pstate_change_type) |
| 603 | { |
| 604 | bool schedulable = true; // true by default for single display case |
| 605 | struct vba_vars_st *vba = &context->bw_ctx.dml.vba; |
| 606 | uint32_t i, pipe_idx; |
| 607 | uint8_t subvp_count = 0; |
| 608 | uint8_t vactive_count = 0; |
| 609 | |
| 610 | for (i = 0, pipe_idx = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 611 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
| 612 | enum mall_stream_type pipe_mall_type = ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe); |
| 613 | |
| 614 | if (!pipe->stream) |
| 615 | continue; |
| 616 | |
| 617 | if (pipe->plane_state && !pipe->top_pipe && |
| 618 | pipe_mall_type == SUBVP_MAIN) |
| 619 | subvp_count++; |
| 620 | |
| 621 | // Count how many planes that aren't SubVP/phantom are capable of VACTIVE |
| 622 | // switching (SubVP + VACTIVE unsupported). In situations where we force |
| 623 | // SubVP for a VACTIVE plane, we don't want to increment the vactive_count. |
| 624 | if (vba->ActiveDRAMClockChangeLatencyMargin[vba->pipe_plane[pipe_idx]] > 0 && |
| 625 | pipe_mall_type == SUBVP_NONE) { |
| 626 | vactive_count++; |
| 627 | } |
| 628 | pipe_idx++; |
| 629 | } |
| 630 | |
| 631 | if (subvp_count == 2) { |
| 632 | // Static schedulability check for SubVP + SubVP case |
| 633 | schedulable = subvp_subvp_schedulable(ctx, context); |
| 634 | } else if (pstate_change_type == dml_dram_clock_change_vblank_w_mall_sub_vp) { |
| 635 | // Static schedulability check for SubVP + VBLANK case. Also handle the case where |
| 636 | // DML outputs SubVP + VBLANK + VACTIVE (DML will report as SubVP + VBLANK) |
| 637 | if (vactive_count > 0) |
| 638 | schedulable = false; |
| 639 | else |
| 640 | schedulable = subvp_vblank_schedulable(ctx, context); |
| 641 | } else if (pstate_change_type == dml_dram_clock_change_vactive_w_mall_sub_vp && |
| 642 | vactive_count > 0) { |
| 643 | // For single display SubVP cases, DML will output dm_dram_clock_change_vactive_w_mall_sub_vp by default. |
| 644 | // We tell the difference between SubVP vs. SubVP + VACTIVE by checking the vactive_count. |
| 645 | // SubVP + VACTIVE currently unsupported |
| 646 | schedulable = false; |
| 647 | } |
| 648 | return schedulable; |
| 649 | } |
| 650 | |
| 651 | static void set_phantom_stream_timing(struct dml2_context *ctx, struct dc_state *state, |
| 652 | struct pipe_ctx *ref_pipe, |
| 653 | struct dc_stream_state *phantom_stream, |
| 654 | unsigned int dc_pipe_idx, |
| 655 | unsigned int svp_height, |
| 656 | unsigned int svp_vstartup) |
| 657 | { |
| 658 | unsigned int i; |
| 659 | double line_time, fp_and_sync_width_time; |
| 660 | struct pipe_ctx *pipe; |
| 661 | uint32_t phantom_vactive, phantom_bp, pstate_width_fw_delay_lines; |
| 662 | static const double cvt_rb_vblank_max = ((double) 460 / (1000 * 1000)); |
| 663 | |
| 664 | // Find DML pipe index (pipe_idx) using dc_pipe_idx |
| 665 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 666 | pipe = &state->res_ctx.pipe_ctx[i]; |
| 667 | |
| 668 | if (!pipe->stream) |
| 669 | continue; |
| 670 | |
| 671 | if (i == dc_pipe_idx) |
| 672 | break; |
| 673 | } |
| 674 | |
| 675 | // Calculate lines required for pstate allow width and FW processing delays |
| 676 | pstate_width_fw_delay_lines = ((double)(ctx->config.svp_pstate.subvp_fw_processing_delay_us + |
| 677 | ctx->config.svp_pstate.subvp_pstate_allow_width_us) / 1000000) * |
| 678 | (ref_pipe->stream->timing.pix_clk_100hz * 100) / |
| 679 | (double)ref_pipe->stream->timing.h_total; |
| 680 | |
| 681 | // DML calculation for MALL region doesn't take into account FW delay |
| 682 | // and required pstate allow width for multi-display cases |
| 683 | /* Add 16 lines margin to the MALL REGION because SUB_VP_START_LINE must be aligned |
| 684 | * to 2 swaths (i.e. 16 lines) |
| 685 | */ |
| 686 | phantom_vactive = svp_height + pstate_width_fw_delay_lines + ctx->config.svp_pstate.subvp_swath_height_margin_lines; |
| 687 | |
| 688 | phantom_stream->timing.v_front_porch = 1; |
| 689 | |
| 690 | line_time = phantom_stream->timing.h_total / ((double)phantom_stream->timing.pix_clk_100hz * 100); |
| 691 | fp_and_sync_width_time = (phantom_stream->timing.v_front_porch + phantom_stream->timing.v_sync_width) * line_time; |
| 692 | |
| 693 | if ((svp_vstartup * line_time) + fp_and_sync_width_time > cvt_rb_vblank_max) { |
| 694 | svp_vstartup = (cvt_rb_vblank_max - fp_and_sync_width_time) / line_time; |
| 695 | } |
| 696 | |
| 697 | // For backporch of phantom pipe, use vstartup of the main pipe |
| 698 | phantom_bp = svp_vstartup; |
| 699 | |
| 700 | phantom_stream->dst.y = 0; |
| 701 | phantom_stream->dst.height = phantom_vactive; |
| 702 | phantom_stream->src.y = 0; |
| 703 | phantom_stream->src.height = phantom_vactive; |
| 704 | |
| 705 | phantom_stream->timing.v_addressable = phantom_vactive; |
| 706 | |
| 707 | phantom_stream->timing.v_total = phantom_stream->timing.v_addressable + |
| 708 | phantom_stream->timing.v_front_porch + |
| 709 | phantom_stream->timing.v_sync_width + |
| 710 | phantom_bp; |
| 711 | phantom_stream->timing.flags.DSC = 0; // Don't need DSC for phantom timing |
| 712 | } |
| 713 | |
| 714 | static struct dc_stream_state *enable_phantom_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int dc_pipe_idx, unsigned int svp_height, unsigned int vstartup) |
| 715 | { |
| 716 | struct pipe_ctx *ref_pipe = &state->res_ctx.pipe_ctx[dc_pipe_idx]; |
| 717 | struct dc_stream_state *phantom_stream = ctx->config.svp_pstate.callbacks.create_phantom_stream( |
| 718 | ctx->config.svp_pstate.callbacks.dc, |
| 719 | state, |
| 720 | ref_pipe->stream); |
| 721 | |
| 722 | /* stream has limited viewport and small timing */ |
| 723 | memcpy(&phantom_stream->timing, &ref_pipe->stream->timing, sizeof(phantom_stream->timing)); |
| 724 | memcpy(&phantom_stream->src, &ref_pipe->stream->src, sizeof(phantom_stream->src)); |
| 725 | memcpy(&phantom_stream->dst, &ref_pipe->stream->dst, sizeof(phantom_stream->dst)); |
| 726 | set_phantom_stream_timing(ctx, state, ref_pipe, phantom_stream, dc_pipe_idx, svp_height, svp_vstartup: vstartup); |
| 727 | |
| 728 | ctx->config.svp_pstate.callbacks.add_phantom_stream(ctx->config.svp_pstate.callbacks.dc, |
| 729 | state, |
| 730 | phantom_stream, |
| 731 | ref_pipe->stream); |
| 732 | return phantom_stream; |
| 733 | } |
| 734 | |
| 735 | static void enable_phantom_plane(struct dml2_context *ctx, |
| 736 | struct dc_state *state, |
| 737 | struct dc_stream_state *phantom_stream, |
| 738 | unsigned int dc_pipe_idx) |
| 739 | { |
| 740 | struct dc_plane_state *phantom_plane = NULL; |
| 741 | struct dc_plane_state *prev_phantom_plane = NULL; |
| 742 | struct pipe_ctx *curr_pipe = &state->res_ctx.pipe_ctx[dc_pipe_idx]; |
| 743 | |
| 744 | while (curr_pipe) { |
| 745 | if (curr_pipe->top_pipe && curr_pipe->top_pipe->plane_state == curr_pipe->plane_state) { |
| 746 | phantom_plane = prev_phantom_plane; |
| 747 | } else { |
| 748 | phantom_plane = ctx->config.svp_pstate.callbacks.create_phantom_plane( |
| 749 | ctx->config.svp_pstate.callbacks.dc, |
| 750 | state, |
| 751 | curr_pipe->plane_state); |
| 752 | if (!phantom_plane) |
| 753 | return; |
| 754 | } |
| 755 | |
| 756 | memcpy(&phantom_plane->address, &curr_pipe->plane_state->address, sizeof(phantom_plane->address)); |
| 757 | memcpy(&phantom_plane->scaling_quality, &curr_pipe->plane_state->scaling_quality, |
| 758 | sizeof(phantom_plane->scaling_quality)); |
| 759 | memcpy(&phantom_plane->src_rect, &curr_pipe->plane_state->src_rect, sizeof(phantom_plane->src_rect)); |
| 760 | memcpy(&phantom_plane->dst_rect, &curr_pipe->plane_state->dst_rect, sizeof(phantom_plane->dst_rect)); |
| 761 | memcpy(&phantom_plane->clip_rect, &curr_pipe->plane_state->clip_rect, sizeof(phantom_plane->clip_rect)); |
| 762 | memcpy(&phantom_plane->plane_size, &curr_pipe->plane_state->plane_size, |
| 763 | sizeof(phantom_plane->plane_size)); |
| 764 | memcpy(&phantom_plane->tiling_info, &curr_pipe->plane_state->tiling_info, |
| 765 | sizeof(phantom_plane->tiling_info)); |
| 766 | memcpy(&phantom_plane->dcc, &curr_pipe->plane_state->dcc, sizeof(phantom_plane->dcc)); |
| 767 | //phantom_plane->tiling_info.gfx10compatible.compat_level = curr_pipe->plane_state->tiling_info.gfx10compatible.compat_level; |
| 768 | phantom_plane->format = curr_pipe->plane_state->format; |
| 769 | phantom_plane->rotation = curr_pipe->plane_state->rotation; |
| 770 | phantom_plane->visible = curr_pipe->plane_state->visible; |
| 771 | |
| 772 | /* Shadow pipe has small viewport. */ |
| 773 | phantom_plane->clip_rect.y = 0; |
| 774 | phantom_plane->clip_rect.height = phantom_stream->timing.v_addressable; |
| 775 | |
| 776 | ctx->config.svp_pstate.callbacks.add_phantom_plane(ctx->config.svp_pstate.callbacks.dc, phantom_stream, phantom_plane, state); |
| 777 | |
| 778 | curr_pipe = curr_pipe->bottom_pipe; |
| 779 | prev_phantom_plane = phantom_plane; |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | static void add_phantom_pipes_for_main_pipe(struct dml2_context *ctx, struct dc_state *state, unsigned int main_pipe_idx, unsigned int svp_height, unsigned int vstartup) |
| 784 | { |
| 785 | struct dc_stream_state *phantom_stream = NULL; |
| 786 | unsigned int i; |
| 787 | |
| 788 | // The index of the DC pipe passed into this function is guarenteed to |
| 789 | // be a valid candidate for SubVP (i.e. has a plane, stream, doesn't |
| 790 | // already have phantom pipe assigned, etc.) by previous checks. |
| 791 | phantom_stream = enable_phantom_stream(ctx, state, dc_pipe_idx: main_pipe_idx, svp_height, vstartup); |
| 792 | enable_phantom_plane(ctx, state, phantom_stream, dc_pipe_idx: main_pipe_idx); |
| 793 | |
| 794 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 795 | struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i]; |
| 796 | |
| 797 | // Build scaling params for phantom pipes which were newly added. |
| 798 | // We determine which phantom pipes were added by comparing with |
| 799 | // the phantom stream. |
| 800 | if (pipe->plane_state && pipe->stream && pipe->stream == phantom_stream && |
| 801 | ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(state, pipe) == SUBVP_PHANTOM) { |
| 802 | pipe->stream->use_dynamic_meta = false; |
| 803 | pipe->plane_state->flip_immediate = false; |
| 804 | if (!ctx->config.svp_pstate.callbacks.build_scaling_params(pipe)) { |
| 805 | // Log / remove phantom pipes since failed to build scaling params |
| 806 | } |
| 807 | } |
| 808 | } |
| 809 | } |
| 810 | |
| 811 | static bool remove_all_phantom_planes_for_stream(struct dml2_context *ctx, struct dc_stream_state *stream, struct dc_state *context) |
| 812 | { |
| 813 | int i, old_plane_count; |
| 814 | struct dc_stream_status *stream_status = NULL; |
| 815 | struct dc_plane_state *del_planes[MAX_SURFACES] = { 0 }; |
| 816 | |
| 817 | for (i = 0; i < context->stream_count; i++) |
| 818 | if (context->streams[i] == stream) { |
| 819 | stream_status = &context->stream_status[i]; |
| 820 | break; |
| 821 | } |
| 822 | |
| 823 | if (stream_status == NULL) { |
| 824 | return false; |
| 825 | } |
| 826 | |
| 827 | old_plane_count = stream_status->plane_count; |
| 828 | |
| 829 | for (i = 0; i < old_plane_count; i++) |
| 830 | del_planes[i] = stream_status->plane_states[i]; |
| 831 | |
| 832 | for (i = 0; i < old_plane_count; i++) { |
| 833 | if (!ctx->config.svp_pstate.callbacks.remove_phantom_plane(ctx->config.svp_pstate.callbacks.dc, stream, del_planes[i], context)) |
| 834 | return false; |
| 835 | ctx->config.svp_pstate.callbacks.release_phantom_plane(ctx->config.svp_pstate.callbacks.dc, context, del_planes[i]); |
| 836 | } |
| 837 | |
| 838 | return true; |
| 839 | } |
| 840 | |
| 841 | bool dml2_svp_remove_all_phantom_pipes(struct dml2_context *ctx, struct dc_state *state) |
| 842 | { |
| 843 | int i; |
| 844 | bool removed_pipe = false; |
| 845 | struct dc_stream_state *phantom_stream = NULL; |
| 846 | |
| 847 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 848 | struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i]; |
| 849 | // build scaling params for phantom pipes |
| 850 | if (pipe->plane_state && pipe->stream && ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(state, pipe) == SUBVP_PHANTOM) { |
| 851 | phantom_stream = pipe->stream; |
| 852 | |
| 853 | remove_all_phantom_planes_for_stream(ctx, stream: phantom_stream, context: state); |
| 854 | ctx->config.svp_pstate.callbacks.remove_phantom_stream(ctx->config.svp_pstate.callbacks.dc, state, phantom_stream); |
| 855 | ctx->config.svp_pstate.callbacks.release_phantom_stream(ctx->config.svp_pstate.callbacks.dc, state, phantom_stream); |
| 856 | |
| 857 | removed_pipe = true; |
| 858 | } |
| 859 | |
| 860 | if (pipe->plane_state) { |
| 861 | pipe->plane_state->is_phantom = false; |
| 862 | } |
| 863 | } |
| 864 | return removed_pipe; |
| 865 | } |
| 866 | |
| 867 | |
| 868 | /* Conditions for setting up phantom pipes for SubVP: |
| 869 | * 1. Not force disable SubVP |
| 870 | * 2. Full update (i.e. DC_VALIDATE_MODE_AND_PROGRAMMING) |
| 871 | * 3. Enough pipes are available to support SubVP (TODO: Which pipes will use VACTIVE / VBLANK / SUBVP?) |
| 872 | * 4. Display configuration passes validation |
| 873 | * 5. (Config doesn't support MCLK in VACTIVE/VBLANK || dc->debug.force_subvp_mclk_switch) |
| 874 | */ |
| 875 | bool dml2_svp_add_phantom_pipe_to_dc_state(struct dml2_context *ctx, struct dc_state *state, struct dml_mode_support_info_st *mode_support_info) |
| 876 | { |
| 877 | unsigned int dc_pipe_idx, dml_pipe_idx; |
| 878 | unsigned int svp_height, vstartup; |
| 879 | |
| 880 | if (ctx->config.svp_pstate.force_disable_subvp) |
| 881 | return false; |
| 882 | |
| 883 | if (!all_pipes_have_stream_and_plane(ctx, context: state)) |
| 884 | return false; |
| 885 | |
| 886 | if (mpo_in_use(context: state)) |
| 887 | return false; |
| 888 | |
| 889 | merge_pipes_for_subvp(ctx, context: state); |
| 890 | // to re-initialize viewport after the pipe merge |
| 891 | for (int i = 0; i < ctx->config.dcn_pipe_count; i++) { |
| 892 | struct pipe_ctx *pipe_ctx = &state->res_ctx.pipe_ctx[i]; |
| 893 | |
| 894 | if (!pipe_ctx->plane_state || !pipe_ctx->stream) |
| 895 | continue; |
| 896 | |
| 897 | ctx->config.svp_pstate.callbacks.build_scaling_params(pipe_ctx); |
| 898 | } |
| 899 | |
| 900 | if (enough_pipes_for_subvp(ctx, state) && assign_subvp_pipe(ctx, context: state, index: &dc_pipe_idx)) { |
| 901 | dml_pipe_idx = dml2_helper_find_dml_pipe_idx_by_stream_id(ctx, stream_id: state->res_ctx.pipe_ctx[dc_pipe_idx].stream->stream_id); |
| 902 | svp_height = mode_support_info->SubViewportLinesNeededInMALL[dml_pipe_idx]; |
| 903 | vstartup = dml_get_vstartup_calculated(mode_lib: &ctx->v20.dml_core_ctx, surface_idx: dml_pipe_idx); |
| 904 | |
| 905 | add_phantom_pipes_for_main_pipe(ctx, state, main_pipe_idx: dc_pipe_idx, svp_height, vstartup); |
| 906 | |
| 907 | return true; |
| 908 | } |
| 909 | |
| 910 | return false; |
| 911 | } |
| 912 | |