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
33unsigned 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
102static 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
147static 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
163static 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 */
186static 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 */
229static 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 */
310static 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 */
358static 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 */
432bool 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 */
508static 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 */
602bool 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
651static 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
714static 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
735static 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
783static 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
811static 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
841bool 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 */
875bool 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

source code of linux/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.c