1 | /* |
2 | * Copyright 2022 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | * Authors: AMD |
23 | * |
24 | */ |
25 | |
26 | // header file of functions being implemented |
27 | #include "dcn32_resource.h" |
28 | #include "dcn20/dcn20_resource.h" |
29 | #include "dml/dcn32/display_mode_vba_util_32.h" |
30 | #include "dml/dcn32/dcn32_fpu.h" |
31 | |
32 | static bool is_dual_plane(enum surface_pixel_format format) |
33 | { |
34 | return format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN || format == SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA; |
35 | } |
36 | |
37 | |
38 | uint32_t dcn32_helper_mall_bytes_to_ways( |
39 | struct dc *dc, |
40 | uint32_t total_size_in_mall_bytes) |
41 | { |
42 | uint32_t cache_lines_used, lines_per_way, total_cache_lines, num_ways; |
43 | |
44 | /* add 2 lines for worst case alignment */ |
45 | cache_lines_used = total_size_in_mall_bytes / dc->caps.cache_line_size + 2; |
46 | |
47 | total_cache_lines = dc->caps.max_cab_allocation_bytes / dc->caps.cache_line_size; |
48 | lines_per_way = total_cache_lines / dc->caps.cache_num_ways; |
49 | num_ways = cache_lines_used / lines_per_way; |
50 | if (cache_lines_used % lines_per_way > 0) |
51 | num_ways++; |
52 | |
53 | return num_ways; |
54 | } |
55 | |
56 | uint32_t dcn32_helper_calculate_mall_bytes_for_cursor( |
57 | struct dc *dc, |
58 | struct pipe_ctx *pipe_ctx, |
59 | bool ignore_cursor_buf) |
60 | { |
61 | struct hubp *hubp = pipe_ctx->plane_res.hubp; |
62 | uint32_t cursor_size = hubp->curs_attr.pitch * hubp->curs_attr.height; |
63 | uint32_t cursor_mall_size_bytes = 0; |
64 | |
65 | switch (pipe_ctx->stream->cursor_attributes.color_format) { |
66 | case CURSOR_MODE_MONO: |
67 | cursor_size /= 2; |
68 | break; |
69 | case CURSOR_MODE_COLOR_1BIT_AND: |
70 | case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA: |
71 | case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA: |
72 | cursor_size *= 4; |
73 | break; |
74 | |
75 | case CURSOR_MODE_COLOR_64BIT_FP_PRE_MULTIPLIED: |
76 | case CURSOR_MODE_COLOR_64BIT_FP_UN_PRE_MULTIPLIED: |
77 | cursor_size *= 8; |
78 | break; |
79 | } |
80 | |
81 | /* only count if cursor is enabled, and if additional allocation needed outside of the |
82 | * DCN cursor buffer |
83 | */ |
84 | if (pipe_ctx->stream->cursor_position.enable && (ignore_cursor_buf || |
85 | cursor_size > 16384)) { |
86 | /* cursor_num_mblk = CEILING(num_cursors*cursor_width*cursor_width*cursor_Bpe/mblk_bytes, 1) |
87 | * Note: add 1 mblk in case of cursor misalignment |
88 | */ |
89 | cursor_mall_size_bytes = ((cursor_size + DCN3_2_MALL_MBLK_SIZE_BYTES - 1) / |
90 | DCN3_2_MALL_MBLK_SIZE_BYTES + 1) * DCN3_2_MALL_MBLK_SIZE_BYTES; |
91 | } |
92 | |
93 | return cursor_mall_size_bytes; |
94 | } |
95 | |
96 | /** |
97 | * dcn32_helper_calculate_num_ways_for_subvp(): Calculate number of ways needed for SubVP |
98 | * |
99 | * Gets total allocation required for the phantom viewport calculated by DML in bytes and |
100 | * converts to number of cache ways. |
101 | * |
102 | * @dc: current dc state |
103 | * @context: new dc state |
104 | * |
105 | * Return: number of ways required for SubVP |
106 | */ |
107 | uint32_t dcn32_helper_calculate_num_ways_for_subvp( |
108 | struct dc *dc, |
109 | struct dc_state *context) |
110 | { |
111 | if (context->bw_ctx.bw.dcn.mall_subvp_size_bytes > 0) { |
112 | if (dc->debug.force_subvp_num_ways) { |
113 | return dc->debug.force_subvp_num_ways; |
114 | } else { |
115 | return dcn32_helper_mall_bytes_to_ways(dc, total_size_in_mall_bytes: context->bw_ctx.bw.dcn.mall_subvp_size_bytes); |
116 | } |
117 | } else { |
118 | return 0; |
119 | } |
120 | } |
121 | |
122 | void dcn32_merge_pipes_for_subvp(struct dc *dc, |
123 | struct dc_state *context) |
124 | { |
125 | uint32_t i; |
126 | |
127 | /* merge pipes if necessary */ |
128 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
129 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
130 | |
131 | // For now merge all pipes for SubVP since pipe split case isn't supported yet |
132 | |
133 | /* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */ |
134 | if (pipe->prev_odm_pipe) { |
135 | /*split off odm pipe*/ |
136 | pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe; |
137 | if (pipe->next_odm_pipe) |
138 | pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe; |
139 | |
140 | pipe->bottom_pipe = NULL; |
141 | pipe->next_odm_pipe = NULL; |
142 | pipe->plane_state = NULL; |
143 | pipe->stream = NULL; |
144 | pipe->top_pipe = NULL; |
145 | pipe->prev_odm_pipe = NULL; |
146 | if (pipe->stream_res.dsc) |
147 | dcn20_release_dsc(res_ctx: &context->res_ctx, pool: dc->res_pool, dsc: &pipe->stream_res.dsc); |
148 | memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); |
149 | memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); |
150 | } else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) { |
151 | struct pipe_ctx *top_pipe = pipe->top_pipe; |
152 | struct pipe_ctx *bottom_pipe = pipe->bottom_pipe; |
153 | |
154 | top_pipe->bottom_pipe = bottom_pipe; |
155 | if (bottom_pipe) |
156 | bottom_pipe->top_pipe = top_pipe; |
157 | |
158 | pipe->top_pipe = NULL; |
159 | pipe->bottom_pipe = NULL; |
160 | pipe->plane_state = NULL; |
161 | pipe->stream = NULL; |
162 | memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); |
163 | memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); |
164 | } |
165 | } |
166 | } |
167 | |
168 | bool dcn32_all_pipes_have_stream_and_plane(struct dc *dc, |
169 | struct dc_state *context) |
170 | { |
171 | uint32_t i; |
172 | |
173 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
174 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
175 | |
176 | if (!pipe->stream) |
177 | continue; |
178 | |
179 | if (!pipe->plane_state) |
180 | return false; |
181 | } |
182 | return true; |
183 | } |
184 | |
185 | bool dcn32_subvp_in_use(struct dc *dc, |
186 | struct dc_state *context) |
187 | { |
188 | uint32_t i; |
189 | |
190 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
191 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
192 | |
193 | if (pipe->stream && pipe->stream->mall_stream_config.type != SUBVP_NONE) |
194 | return true; |
195 | } |
196 | return false; |
197 | } |
198 | |
199 | bool dcn32_mpo_in_use(struct dc_state *context) |
200 | { |
201 | uint32_t i; |
202 | |
203 | for (i = 0; i < context->stream_count; i++) { |
204 | if (context->stream_status[i].plane_count > 1) |
205 | return true; |
206 | } |
207 | return false; |
208 | } |
209 | |
210 | |
211 | bool dcn32_any_surfaces_rotated(struct dc *dc, struct dc_state *context) |
212 | { |
213 | uint32_t i; |
214 | |
215 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
216 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
217 | |
218 | if (!pipe->stream) |
219 | continue; |
220 | |
221 | if (pipe->plane_state && pipe->plane_state->rotation != ROTATION_ANGLE_0) |
222 | return true; |
223 | } |
224 | return false; |
225 | } |
226 | |
227 | bool dcn32_is_center_timing(struct pipe_ctx *pipe) |
228 | { |
229 | bool is_center_timing = false; |
230 | |
231 | if (pipe->stream) { |
232 | if (pipe->stream->timing.v_addressable != pipe->stream->dst.height || |
233 | pipe->stream->timing.v_addressable != pipe->stream->src.height) { |
234 | is_center_timing = true; |
235 | } |
236 | } |
237 | |
238 | if (pipe->plane_state) { |
239 | if (pipe->stream->timing.v_addressable != pipe->plane_state->dst_rect.height && |
240 | pipe->stream->timing.v_addressable != pipe->plane_state->src_rect.height) { |
241 | is_center_timing = true; |
242 | } |
243 | } |
244 | |
245 | return is_center_timing; |
246 | } |
247 | |
248 | bool dcn32_is_psr_capable(struct pipe_ctx *pipe) |
249 | { |
250 | bool psr_capable = false; |
251 | |
252 | if (pipe->stream && pipe->stream->link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED) { |
253 | psr_capable = true; |
254 | } |
255 | return psr_capable; |
256 | } |
257 | |
258 | static void override_det_for_subvp(struct dc *dc, struct dc_state *context, uint8_t pipe_segments[]) |
259 | { |
260 | uint32_t i; |
261 | uint8_t fhd_count = 0; |
262 | uint8_t subvp_high_refresh_count = 0; |
263 | uint8_t stream_count = 0; |
264 | |
265 | // Do not override if a stream has multiple planes |
266 | for (i = 0; i < context->stream_count; i++) { |
267 | if (context->stream_status[i].plane_count > 1) { |
268 | return; |
269 | } |
270 | if (context->streams[i]->mall_stream_config.type != SUBVP_PHANTOM) { |
271 | stream_count++; |
272 | } |
273 | } |
274 | |
275 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
276 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
277 | |
278 | if (pipe_ctx->stream && pipe_ctx->plane_state && pipe_ctx->stream->mall_stream_config.type != SUBVP_PHANTOM) { |
279 | if (dcn32_allow_subvp_high_refresh_rate(dc, context, pipe: pipe_ctx)) { |
280 | |
281 | if (pipe_ctx->stream->timing.v_addressable == 1080 && pipe_ctx->stream->timing.h_addressable == 1920) { |
282 | fhd_count++; |
283 | } |
284 | subvp_high_refresh_count++; |
285 | } |
286 | } |
287 | } |
288 | |
289 | if (stream_count == 2 && subvp_high_refresh_count == 2 && fhd_count == 1) { |
290 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
291 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
292 | |
293 | if (pipe_ctx->stream && pipe_ctx->plane_state && pipe_ctx->stream->mall_stream_config.type != SUBVP_PHANTOM) { |
294 | if (pipe_ctx->stream->timing.v_addressable == 1080 && pipe_ctx->stream->timing.h_addressable == 1920) { |
295 | if (pipe_segments[i] > 4) |
296 | pipe_segments[i] = 4; |
297 | } |
298 | } |
299 | } |
300 | } |
301 | } |
302 | |
303 | /** |
304 | * dcn32_determine_det_override(): Determine DET allocation for each pipe |
305 | * |
306 | * This function determines how much DET to allocate for each pipe. The total number of |
307 | * DET segments will be split equally among each of the streams, and after that the DET |
308 | * segments per stream will be split equally among the planes for the given stream. |
309 | * |
310 | * If there is a plane that's driven by more than 1 pipe (i.e. pipe split), then the |
311 | * number of DET for that given plane will be split among the pipes driving that plane. |
312 | * |
313 | * |
314 | * High level algorithm: |
315 | * 1. Split total DET among number of streams |
316 | * 2. For each stream, split DET among the planes |
317 | * 3. For each plane, check if there is a pipe split. If yes, split the DET allocation |
318 | * among those pipes. |
319 | * 4. Assign the DET override to the DML pipes. |
320 | * |
321 | * @dc: Current DC state |
322 | * @context: New DC state to be programmed |
323 | * @pipes: Array of DML pipes |
324 | * |
325 | * Return: void |
326 | */ |
327 | void dcn32_determine_det_override(struct dc *dc, |
328 | struct dc_state *context, |
329 | display_e2e_pipe_params_st *pipes) |
330 | { |
331 | uint32_t i, j, k; |
332 | uint8_t pipe_plane_count, stream_segments, plane_segments, pipe_segments[MAX_PIPES] = {0}; |
333 | uint8_t pipe_counted[MAX_PIPES] = {0}; |
334 | uint8_t pipe_cnt = 0; |
335 | struct dc_plane_state *current_plane = NULL; |
336 | uint8_t stream_count = 0; |
337 | |
338 | for (i = 0; i < context->stream_count; i++) { |
339 | /* Don't count SubVP streams for DET allocation */ |
340 | if (context->streams[i]->mall_stream_config.type != SUBVP_PHANTOM) |
341 | stream_count++; |
342 | } |
343 | |
344 | if (stream_count > 0) { |
345 | stream_segments = 18 / stream_count; |
346 | for (i = 0; i < context->stream_count; i++) { |
347 | if (context->streams[i]->mall_stream_config.type == SUBVP_PHANTOM) |
348 | continue; |
349 | |
350 | if (context->stream_status[i].plane_count > 0) |
351 | plane_segments = stream_segments / context->stream_status[i].plane_count; |
352 | else |
353 | plane_segments = stream_segments; |
354 | for (j = 0; j < dc->res_pool->pipe_count; j++) { |
355 | pipe_plane_count = 0; |
356 | if (context->res_ctx.pipe_ctx[j].stream == context->streams[i] && |
357 | pipe_counted[j] != 1) { |
358 | /* Note: pipe_plane_count indicates the number of pipes to be used for a |
359 | * given plane. e.g. pipe_plane_count = 1 means single pipe (i.e. not split), |
360 | * pipe_plane_count = 2 means 2:1 split, etc. |
361 | */ |
362 | pipe_plane_count++; |
363 | pipe_counted[j] = 1; |
364 | current_plane = context->res_ctx.pipe_ctx[j].plane_state; |
365 | for (k = 0; k < dc->res_pool->pipe_count; k++) { |
366 | if (k != j && context->res_ctx.pipe_ctx[k].stream == context->streams[i] && |
367 | context->res_ctx.pipe_ctx[k].plane_state == current_plane) { |
368 | pipe_plane_count++; |
369 | pipe_counted[k] = 1; |
370 | } |
371 | } |
372 | |
373 | pipe_segments[j] = plane_segments / pipe_plane_count; |
374 | for (k = 0; k < dc->res_pool->pipe_count; k++) { |
375 | if (k != j && context->res_ctx.pipe_ctx[k].stream == context->streams[i] && |
376 | context->res_ctx.pipe_ctx[k].plane_state == current_plane) { |
377 | pipe_segments[k] = plane_segments / pipe_plane_count; |
378 | } |
379 | } |
380 | } |
381 | } |
382 | } |
383 | |
384 | override_det_for_subvp(dc, context, pipe_segments); |
385 | for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) { |
386 | if (!context->res_ctx.pipe_ctx[i].stream) |
387 | continue; |
388 | pipes[pipe_cnt].pipe.src.det_size_override = pipe_segments[i] * DCN3_2_DET_SEG_SIZE; |
389 | pipe_cnt++; |
390 | } |
391 | } else { |
392 | for (i = 0; i < dc->res_pool->pipe_count; i++) |
393 | pipes[i].pipe.src.det_size_override = 4 * DCN3_2_DET_SEG_SIZE; //DCN3_2_DEFAULT_DET_SIZE |
394 | } |
395 | } |
396 | |
397 | void dcn32_set_det_allocations(struct dc *dc, struct dc_state *context, |
398 | display_e2e_pipe_params_st *pipes) |
399 | { |
400 | int i, pipe_cnt; |
401 | struct resource_context *res_ctx = &context->res_ctx; |
402 | struct pipe_ctx *pipe; |
403 | bool disable_unbounded_requesting = dc->debug.disable_z9_mpc || dc->debug.disable_unbounded_requesting; |
404 | |
405 | for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) { |
406 | |
407 | if (!res_ctx->pipe_ctx[i].stream) |
408 | continue; |
409 | |
410 | pipe = &res_ctx->pipe_ctx[i]; |
411 | pipe_cnt++; |
412 | } |
413 | |
414 | /* For DET allocation, we don't want to use DML policy (not optimal for utilizing all |
415 | * the DET available for each pipe). Use the DET override input to maintain our driver |
416 | * policy. |
417 | */ |
418 | if (pipe_cnt == 1) { |
419 | pipes[0].pipe.src.det_size_override = DCN3_2_MAX_DET_SIZE; |
420 | if (pipe->plane_state && !disable_unbounded_requesting && pipe->plane_state->tiling_info.gfx9.swizzle != DC_SW_LINEAR) { |
421 | if (!is_dual_plane(format: pipe->plane_state->format)) { |
422 | pipes[0].pipe.src.det_size_override = DCN3_2_DEFAULT_DET_SIZE; |
423 | pipes[0].pipe.src.unbounded_req_mode = true; |
424 | if (pipe->plane_state->src_rect.width >= 5120 && |
425 | pipe->plane_state->src_rect.height >= 2880) |
426 | pipes[0].pipe.src.det_size_override = 320; // 5K or higher |
427 | } |
428 | } |
429 | } else |
430 | dcn32_determine_det_override(dc, context, pipes); |
431 | } |
432 | |
433 | /** |
434 | * dcn32_save_mall_state(): Save MALL (SubVP) state for fast validation cases |
435 | * |
436 | * This function saves the MALL (SubVP) case for fast validation cases. For fast validation, |
437 | * there are situations where a shallow copy of the dc->current_state is created for the |
438 | * validation. In this case we want to save and restore the mall config because we always |
439 | * teardown subvp at the beginning of validation (and don't attempt to add it back if it's |
440 | * fast validation). If we don't restore the subvp config in cases of fast validation + |
441 | * shallow copy of the dc->current_state, the dc->current_state will have a partially |
442 | * removed subvp state when we did not intend to remove it. |
443 | * |
444 | * NOTE: This function ONLY works if the streams are not moved to a different pipe in the |
445 | * validation. We don't expect this to happen in fast_validation=1 cases. |
446 | * |
447 | * @dc: Current DC state |
448 | * @context: New DC state to be programmed |
449 | * @temp_config: struct used to cache the existing MALL state |
450 | * |
451 | * Return: void |
452 | */ |
453 | void dcn32_save_mall_state(struct dc *dc, |
454 | struct dc_state *context, |
455 | struct mall_temp_config *temp_config) |
456 | { |
457 | uint32_t i; |
458 | |
459 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
460 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
461 | |
462 | if (pipe->stream) |
463 | temp_config->mall_stream_config[i] = pipe->stream->mall_stream_config; |
464 | |
465 | if (pipe->plane_state) |
466 | temp_config->is_phantom_plane[i] = pipe->plane_state->is_phantom; |
467 | } |
468 | } |
469 | |
470 | /** |
471 | * dcn32_restore_mall_state(): Restore MALL (SubVP) state for fast validation cases |
472 | * |
473 | * Restore the MALL state based on the previously saved state from dcn32_save_mall_state |
474 | * |
475 | * @dc: Current DC state |
476 | * @context: New DC state to be programmed, restore MALL state into here |
477 | * @temp_config: struct that has the cached MALL state |
478 | * |
479 | * Return: void |
480 | */ |
481 | void dcn32_restore_mall_state(struct dc *dc, |
482 | struct dc_state *context, |
483 | struct mall_temp_config *temp_config) |
484 | { |
485 | uint32_t i; |
486 | |
487 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
488 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
489 | |
490 | if (pipe->stream) |
491 | pipe->stream->mall_stream_config = temp_config->mall_stream_config[i]; |
492 | |
493 | if (pipe->plane_state) |
494 | pipe->plane_state->is_phantom = temp_config->is_phantom_plane[i]; |
495 | } |
496 | } |
497 | |
498 | #define MAX_STRETCHED_V_BLANK 1000 // in micro-seconds (must ensure to match value in FW) |
499 | /* |
500 | * Scaling factor for v_blank stretch calculations considering timing in |
501 | * micro-seconds and pixel clock in 100hz. |
502 | * Note: the parenthesis are necessary to ensure the correct order of |
503 | * operation where V_SCALE is used. |
504 | */ |
505 | #define V_SCALE (10000 / MAX_STRETCHED_V_BLANK) |
506 | |
507 | static int get_frame_rate_at_max_stretch_100hz( |
508 | struct dc_stream_state *fpo_candidate_stream, |
509 | uint32_t fpo_vactive_margin_us) |
510 | { |
511 | struct dc_crtc_timing *timing = NULL; |
512 | uint32_t sec_per_100_lines; |
513 | uint32_t max_v_blank; |
514 | uint32_t curr_v_blank; |
515 | uint32_t v_stretch_max; |
516 | uint32_t stretched_frame_pix_cnt; |
517 | uint32_t scaled_stretched_frame_pix_cnt; |
518 | uint32_t scaled_refresh_rate; |
519 | uint32_t v_scale; |
520 | |
521 | if (fpo_candidate_stream == NULL) |
522 | return 0; |
523 | |
524 | /* check if refresh rate at least 120hz */ |
525 | timing = &fpo_candidate_stream->timing; |
526 | if (timing == NULL) |
527 | return 0; |
528 | |
529 | v_scale = 10000 / (MAX_STRETCHED_V_BLANK + fpo_vactive_margin_us); |
530 | |
531 | sec_per_100_lines = timing->pix_clk_100hz / timing->h_total + 1; |
532 | max_v_blank = sec_per_100_lines / v_scale + 1; |
533 | curr_v_blank = timing->v_total - timing->v_addressable; |
534 | v_stretch_max = (max_v_blank > curr_v_blank) ? (max_v_blank - curr_v_blank) : (0); |
535 | stretched_frame_pix_cnt = (v_stretch_max + timing->v_total) * timing->h_total; |
536 | scaled_stretched_frame_pix_cnt = stretched_frame_pix_cnt / 10000; |
537 | scaled_refresh_rate = (timing->pix_clk_100hz) / scaled_stretched_frame_pix_cnt + 1; |
538 | |
539 | return scaled_refresh_rate; |
540 | |
541 | } |
542 | |
543 | static bool is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch( |
544 | struct dc_stream_state *fpo_candidate_stream, uint32_t fpo_vactive_margin_us) |
545 | { |
546 | int refresh_rate_max_stretch_100hz; |
547 | int min_refresh_100hz; |
548 | |
549 | if (fpo_candidate_stream == NULL) |
550 | return false; |
551 | |
552 | refresh_rate_max_stretch_100hz = get_frame_rate_at_max_stretch_100hz(fpo_candidate_stream, fpo_vactive_margin_us); |
553 | min_refresh_100hz = fpo_candidate_stream->timing.min_refresh_in_uhz / 10000; |
554 | |
555 | if (refresh_rate_max_stretch_100hz < min_refresh_100hz) |
556 | return false; |
557 | |
558 | return true; |
559 | } |
560 | |
561 | static int get_refresh_rate(struct dc_stream_state *fpo_candidate_stream) |
562 | { |
563 | int refresh_rate = 0; |
564 | int h_v_total = 0; |
565 | struct dc_crtc_timing *timing = NULL; |
566 | |
567 | if (fpo_candidate_stream == NULL) |
568 | return 0; |
569 | |
570 | /* check if refresh rate at least 120hz */ |
571 | timing = &fpo_candidate_stream->timing; |
572 | if (timing == NULL) |
573 | return 0; |
574 | |
575 | h_v_total = timing->h_total * timing->v_total; |
576 | if (h_v_total == 0) |
577 | return 0; |
578 | |
579 | refresh_rate = ((timing->pix_clk_100hz * 100) / (h_v_total)) + 1; |
580 | return refresh_rate; |
581 | } |
582 | |
583 | /** |
584 | * dcn32_can_support_mclk_switch_using_fw_based_vblank_stretch() - Determines if config can |
585 | * support FPO |
586 | * |
587 | * @dc: current dc state |
588 | * @context: new dc state |
589 | * |
590 | * Return: Pointer to FPO stream candidate if config can support FPO, otherwise NULL |
591 | */ |
592 | struct dc_stream_state *dcn32_can_support_mclk_switch_using_fw_based_vblank_stretch(struct dc *dc, const struct dc_state *context) |
593 | { |
594 | int refresh_rate = 0; |
595 | const int minimum_refreshrate_supported = 120; |
596 | struct dc_stream_state *fpo_candidate_stream = NULL; |
597 | bool is_fpo_vactive = false; |
598 | uint32_t fpo_vactive_margin_us = 0; |
599 | |
600 | if (context == NULL) |
601 | return NULL; |
602 | |
603 | if (dc->debug.disable_fams) |
604 | return NULL; |
605 | |
606 | if (!dc->caps.dmub_caps.mclk_sw) |
607 | return NULL; |
608 | |
609 | if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching_shut_down) |
610 | return NULL; |
611 | |
612 | /* For FPO we can support up to 2 display configs if: |
613 | * - first display uses FPO |
614 | * - Second display switches in VACTIVE */ |
615 | if (context->stream_count > 2) |
616 | return NULL; |
617 | else if (context->stream_count == 2) { |
618 | DC_FP_START(); |
619 | dcn32_assign_fpo_vactive_candidate(dc, context, fpo_candidate_stream: &fpo_candidate_stream); |
620 | DC_FP_END(); |
621 | |
622 | DC_FP_START(); |
623 | is_fpo_vactive = dcn32_find_vactive_pipe(dc, context, vactive_margin_req: dc->debug.fpo_vactive_min_active_margin_us); |
624 | DC_FP_END(); |
625 | if (!is_fpo_vactive || dc->debug.disable_fpo_vactive) |
626 | return NULL; |
627 | } else |
628 | fpo_candidate_stream = context->streams[0]; |
629 | |
630 | if (!fpo_candidate_stream) |
631 | return NULL; |
632 | |
633 | if (fpo_candidate_stream->sink->edid_caps.panel_patch.disable_fams) |
634 | return NULL; |
635 | |
636 | refresh_rate = get_refresh_rate(fpo_candidate_stream); |
637 | if (refresh_rate < minimum_refreshrate_supported) |
638 | return NULL; |
639 | |
640 | fpo_vactive_margin_us = is_fpo_vactive ? dc->debug.fpo_vactive_margin_us : 0; // For now hardcode the FPO + Vactive stretch margin to be 2000us |
641 | if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(fpo_candidate_stream, fpo_vactive_margin_us)) |
642 | return NULL; |
643 | |
644 | if (!fpo_candidate_stream->allow_freesync) |
645 | return NULL; |
646 | |
647 | if (fpo_candidate_stream->vrr_active_variable && dc->debug.disable_fams_gaming) |
648 | return NULL; |
649 | |
650 | return fpo_candidate_stream; |
651 | } |
652 | |
653 | bool dcn32_check_native_scaling_for_res(struct pipe_ctx *pipe, unsigned int width, unsigned int height) |
654 | { |
655 | bool is_native_scaling = false; |
656 | |
657 | if (pipe->stream->timing.h_addressable == width && |
658 | pipe->stream->timing.v_addressable == height && |
659 | pipe->plane_state->src_rect.width == width && |
660 | pipe->plane_state->src_rect.height == height && |
661 | pipe->plane_state->dst_rect.width == width && |
662 | pipe->plane_state->dst_rect.height == height) |
663 | is_native_scaling = true; |
664 | |
665 | return is_native_scaling; |
666 | } |
667 | |
668 | /** |
669 | * dcn32_subvp_drr_admissable() - Determine if SubVP + DRR config is admissible |
670 | * |
671 | * @dc: Current DC state |
672 | * @context: New DC state to be programmed |
673 | * |
674 | * SubVP + DRR is admissible under the following conditions: |
675 | * - Config must have 2 displays (i.e., 2 non-phantom master pipes) |
676 | * - One display is SubVP |
677 | * - Other display must have Freesync enabled |
678 | * - The potential DRR display must not be PSR capable |
679 | * |
680 | * Return: True if admissible, false otherwise |
681 | */ |
682 | bool dcn32_subvp_drr_admissable(struct dc *dc, struct dc_state *context) |
683 | { |
684 | bool result = false; |
685 | uint32_t i; |
686 | uint8_t subvp_count = 0; |
687 | uint8_t non_subvp_pipes = 0; |
688 | bool drr_pipe_found = false; |
689 | bool drr_psr_capable = false; |
690 | uint64_t refresh_rate = 0; |
691 | |
692 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
693 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
694 | |
695 | if (resource_is_pipe_type(pipe_ctx: pipe, type: OPP_HEAD) && |
696 | resource_is_pipe_type(pipe_ctx: pipe, type: DPP_PIPE)) { |
697 | if (pipe->stream->mall_stream_config.type == SUBVP_MAIN) { |
698 | subvp_count++; |
699 | |
700 | refresh_rate = (pipe->stream->timing.pix_clk_100hz * (uint64_t)100 + |
701 | pipe->stream->timing.v_total * pipe->stream->timing.h_total - (uint64_t)1); |
702 | refresh_rate = div_u64(dividend: refresh_rate, divisor: pipe->stream->timing.v_total); |
703 | refresh_rate = div_u64(dividend: refresh_rate, divisor: pipe->stream->timing.h_total); |
704 | } |
705 | if (pipe->stream->mall_stream_config.type == SUBVP_NONE) { |
706 | non_subvp_pipes++; |
707 | drr_psr_capable = (drr_psr_capable || dcn32_is_psr_capable(pipe)); |
708 | if (pipe->stream->ignore_msa_timing_param && |
709 | (pipe->stream->allow_freesync || pipe->stream->vrr_active_variable || pipe->stream->vrr_active_fixed)) { |
710 | drr_pipe_found = true; |
711 | } |
712 | } |
713 | } |
714 | } |
715 | |
716 | if (subvp_count == 1 && non_subvp_pipes == 1 && drr_pipe_found && !drr_psr_capable && |
717 | ((uint32_t)refresh_rate < 120)) |
718 | result = true; |
719 | |
720 | return result; |
721 | } |
722 | |
723 | /** |
724 | * dcn32_subvp_vblank_admissable() - Determine if SubVP + Vblank config is admissible |
725 | * |
726 | * @dc: Current DC state |
727 | * @context: New DC state to be programmed |
728 | * @vlevel: Voltage level calculated by DML |
729 | * |
730 | * SubVP + Vblank is admissible under the following conditions: |
731 | * - Config must have 2 displays (i.e., 2 non-phantom master pipes) |
732 | * - One display is SubVP |
733 | * - Other display must not have Freesync capability |
734 | * - DML must have output DRAM clock change support as SubVP + Vblank |
735 | * - The potential vblank display must not be PSR capable |
736 | * |
737 | * Return: True if admissible, false otherwise |
738 | */ |
739 | bool dcn32_subvp_vblank_admissable(struct dc *dc, struct dc_state *context, int vlevel) |
740 | { |
741 | bool result = false; |
742 | uint32_t i; |
743 | uint8_t subvp_count = 0; |
744 | uint8_t non_subvp_pipes = 0; |
745 | bool drr_pipe_found = false; |
746 | struct vba_vars_st *vba = &context->bw_ctx.dml.vba; |
747 | bool vblank_psr_capable = false; |
748 | uint64_t refresh_rate = 0; |
749 | |
750 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
751 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; |
752 | |
753 | if (resource_is_pipe_type(pipe_ctx: pipe, type: OPP_HEAD) && |
754 | resource_is_pipe_type(pipe_ctx: pipe, type: DPP_PIPE)) { |
755 | if (pipe->stream->mall_stream_config.type == SUBVP_MAIN) { |
756 | subvp_count++; |
757 | |
758 | refresh_rate = (pipe->stream->timing.pix_clk_100hz * (uint64_t)100 + |
759 | pipe->stream->timing.v_total * pipe->stream->timing.h_total - (uint64_t)1); |
760 | refresh_rate = div_u64(dividend: refresh_rate, divisor: pipe->stream->timing.v_total); |
761 | refresh_rate = div_u64(dividend: refresh_rate, divisor: pipe->stream->timing.h_total); |
762 | } |
763 | if (pipe->stream->mall_stream_config.type == SUBVP_NONE) { |
764 | non_subvp_pipes++; |
765 | vblank_psr_capable = (vblank_psr_capable || dcn32_is_psr_capable(pipe)); |
766 | if (pipe->stream->ignore_msa_timing_param && |
767 | (pipe->stream->allow_freesync || pipe->stream->vrr_active_variable || pipe->stream->vrr_active_fixed)) { |
768 | drr_pipe_found = true; |
769 | } |
770 | } |
771 | } |
772 | } |
773 | |
774 | if (subvp_count == 1 && non_subvp_pipes == 1 && !drr_pipe_found && !vblank_psr_capable && |
775 | ((uint32_t)refresh_rate < 120) && |
776 | vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vblank_w_mall_sub_vp) |
777 | result = true; |
778 | |
779 | return result; |
780 | } |
781 | |