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 | #include "dml2_mall_phantom.h" |
28 | |
29 | #include "dml2_dc_types.h" |
30 | #include "dml2_internal_types.h" |
31 | #include "dml2_utils.h" |
32 | #include "dml2_dc_resource_mgmt.h" |
33 | |
34 | #define MAX_ODM_FACTOR 4 |
35 | #define MAX_MPCC_FACTOR 4 |
36 | |
37 | struct dc_plane_pipe_pool { |
38 | int pipes_assigned_to_plane[MAX_ODM_FACTOR][MAX_MPCC_FACTOR]; |
39 | bool pipe_used[MAX_ODM_FACTOR][MAX_MPCC_FACTOR]; |
40 | int num_pipes_assigned_to_plane_for_mpcc_combine; |
41 | int num_pipes_assigned_to_plane_for_odm_combine; |
42 | }; |
43 | |
44 | struct dc_pipe_mapping_scratch { |
45 | struct { |
46 | unsigned int odm_factor; |
47 | unsigned int odm_slice_end_x[MAX_PIPES]; |
48 | struct pipe_ctx *next_higher_pipe_for_odm_slice[MAX_PIPES]; |
49 | } odm_info; |
50 | struct { |
51 | unsigned int mpc_factor; |
52 | struct pipe_ctx *prev_odm_pipe; |
53 | } mpc_info; |
54 | |
55 | struct dc_plane_pipe_pool pipe_pool; |
56 | }; |
57 | |
58 | static bool get_plane_id(struct dml2_context *dml2, const struct dc_state *state, const struct dc_plane_state *plane, |
59 | unsigned int stream_id, unsigned int plane_index, unsigned int *plane_id) |
60 | { |
61 | int i, j; |
62 | bool is_plane_duplicate = dml2->v20.scratch.plane_duplicate_exists; |
63 | |
64 | if (!plane_id) |
65 | return false; |
66 | |
67 | for (i = 0; i < state->stream_count; i++) { |
68 | if (state->streams[i]->stream_id == stream_id) { |
69 | for (j = 0; j < state->stream_status[i].plane_count; j++) { |
70 | if (state->stream_status[i].plane_states[j] == plane && |
71 | (!is_plane_duplicate || (is_plane_duplicate && (j == plane_index)))) { |
72 | *plane_id = (i << 16) | j; |
73 | return true; |
74 | } |
75 | } |
76 | } |
77 | } |
78 | |
79 | return false; |
80 | } |
81 | |
82 | static int find_disp_cfg_idx_by_plane_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int plane_id) |
83 | { |
84 | int i; |
85 | |
86 | for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) { |
87 | if (mapping->disp_cfg_to_plane_id_valid[i] && mapping->disp_cfg_to_plane_id[i] == plane_id) |
88 | return i; |
89 | } |
90 | |
91 | return -1; |
92 | } |
93 | |
94 | static int find_disp_cfg_idx_by_stream_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int stream_id) |
95 | { |
96 | int i; |
97 | |
98 | for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) { |
99 | if (mapping->disp_cfg_to_stream_id_valid[i] && mapping->disp_cfg_to_stream_id[i] == stream_id) |
100 | return i; |
101 | } |
102 | |
103 | return -1; |
104 | } |
105 | |
106 | // The master pipe of a stream is defined as the top pipe in odm slice 0 |
107 | static struct pipe_ctx *find_master_pipe_of_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id) |
108 | { |
109 | int i; |
110 | |
111 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
112 | if (state->res_ctx.pipe_ctx[i].stream && state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) { |
113 | if (!state->res_ctx.pipe_ctx[i].prev_odm_pipe && !state->res_ctx.pipe_ctx[i].top_pipe) |
114 | return &state->res_ctx.pipe_ctx[i]; |
115 | } |
116 | } |
117 | |
118 | return NULL; |
119 | } |
120 | |
121 | static struct pipe_ctx *find_master_pipe_of_plane(struct dml2_context *ctx, |
122 | struct dc_state *state, unsigned int plane_id) |
123 | { |
124 | int i; |
125 | unsigned int plane_id_assigned_to_pipe; |
126 | |
127 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
128 | if (state->res_ctx.pipe_ctx[i].plane_state && get_plane_id(dml2: ctx, state, plane: state->res_ctx.pipe_ctx[i].plane_state, |
129 | stream_id: state->res_ctx.pipe_ctx[i].stream->stream_id, |
130 | plane_index: ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[state->res_ctx.pipe_ctx[i].pipe_idx], plane_id: &plane_id_assigned_to_pipe)) { |
131 | if (plane_id_assigned_to_pipe == plane_id) |
132 | return &state->res_ctx.pipe_ctx[i]; |
133 | } |
134 | } |
135 | |
136 | return NULL; |
137 | } |
138 | |
139 | static unsigned int find_pipes_assigned_to_plane(struct dml2_context *ctx, |
140 | struct dc_state *state, unsigned int plane_id, unsigned int *pipes) |
141 | { |
142 | int i; |
143 | unsigned int num_found = 0; |
144 | unsigned int plane_id_assigned_to_pipe = -1; |
145 | |
146 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
147 | struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i]; |
148 | |
149 | if (!pipe->plane_state || !pipe->stream) |
150 | continue; |
151 | |
152 | get_plane_id(dml2: ctx, state, plane: pipe->plane_state, stream_id: pipe->stream->stream_id, |
153 | plane_index: ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[pipe->pipe_idx], |
154 | plane_id: &plane_id_assigned_to_pipe); |
155 | if (plane_id_assigned_to_pipe == plane_id && !pipe->prev_odm_pipe |
156 | && (!pipe->top_pipe || pipe->top_pipe->plane_state != pipe->plane_state)) { |
157 | while (pipe) { |
158 | struct pipe_ctx *mpc_pipe = pipe; |
159 | |
160 | while (mpc_pipe) { |
161 | pipes[num_found++] = mpc_pipe->pipe_idx; |
162 | mpc_pipe = mpc_pipe->bottom_pipe; |
163 | if (!mpc_pipe) |
164 | break; |
165 | if (mpc_pipe->plane_state != pipe->plane_state) |
166 | mpc_pipe = NULL; |
167 | } |
168 | pipe = pipe->next_odm_pipe; |
169 | } |
170 | break; |
171 | } |
172 | } |
173 | |
174 | return num_found; |
175 | } |
176 | |
177 | static bool validate_pipe_assignment(const struct dml2_context *ctx, const struct dc_state *state, const struct dml_display_cfg_st *disp_cfg, const struct dml2_dml_to_dc_pipe_mapping *mapping) |
178 | { |
179 | // int i, j, k; |
180 | // |
181 | // unsigned int plane_id; |
182 | // |
183 | // unsigned int disp_cfg_index; |
184 | // |
185 | // unsigned int pipes_assigned_to_plane[MAX_PIPES]; |
186 | // unsigned int num_pipes_assigned_to_plane; |
187 | // |
188 | // struct pipe_ctx *top_pipe; |
189 | // |
190 | // for (i = 0; i < state->stream_count; i++) { |
191 | // for (j = 0; j < state->stream_status[i]->plane_count; j++) { |
192 | // if (get_plane_id(state, state->stream_status.plane_states[j], &plane_id)) { |
193 | // disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id); |
194 | // num_pipes_assigned_to_plane = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes_assigned_to_plane); |
195 | // |
196 | // if (disp_cfg_index >= 0 && num_pipes_assigned_to_plane > 0) { |
197 | // // Verify the number of pipes assigned matches |
198 | // if (disp_cfg->hw.DPPPerSurface != num_pipes_assigned_to_plane) |
199 | // return false; |
200 | // |
201 | // top_pipe = find_top_pipe_in_tree(state->res_ctx.pipe_ctx[pipes_assigned_to_plane[0]]); |
202 | // |
203 | // // Verify MPC and ODM combine |
204 | // if (disp_cfg->hw.ODMMode == dml_odm_mode_bypass) { |
205 | // verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, false); |
206 | // } else { |
207 | // verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, true); |
208 | // } |
209 | // |
210 | // // TODO: could also do additional verification that the pipes in tree are the same as |
211 | // // pipes_assigned_to_plane |
212 | // } else { |
213 | // ASSERT(false); |
214 | // return false; |
215 | // } |
216 | // } else { |
217 | // ASSERT(false); |
218 | // return false; |
219 | // } |
220 | // } |
221 | // } |
222 | return true; |
223 | } |
224 | |
225 | static bool is_plane_using_pipe(const struct pipe_ctx *pipe) |
226 | { |
227 | if (pipe->plane_state) |
228 | return true; |
229 | |
230 | return false; |
231 | } |
232 | |
233 | static bool is_pipe_free(const struct pipe_ctx *pipe) |
234 | { |
235 | if (!pipe->plane_state && !pipe->stream) |
236 | return true; |
237 | |
238 | return false; |
239 | } |
240 | |
241 | static unsigned int find_preferred_pipe_candidates(const struct dc_state *existing_state, |
242 | const int pipe_count, |
243 | const unsigned int stream_id, |
244 | unsigned int *preferred_pipe_candidates) |
245 | { |
246 | unsigned int num_preferred_candidates = 0; |
247 | int i; |
248 | |
249 | /* There is only one case which we consider for adding a pipe to the preferred |
250 | * pipe candidate array: |
251 | * |
252 | * 1. If the existing stream id of the pipe is equivalent to the stream id |
253 | * of the stream we are trying to achieve MPC/ODM combine for. This allows |
254 | * us to minimize the changes in pipe topology during the transition. |
255 | * |
256 | * However this condition comes with a caveat. We need to ignore pipes that will |
257 | * require a change in OPP but still have the same stream id. For example during |
258 | * an MPC to ODM transiton. |
259 | */ |
260 | if (existing_state) { |
261 | for (i = 0; i < pipe_count; i++) { |
262 | if (existing_state->res_ctx.pipe_ctx[i].stream && existing_state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) { |
263 | if (existing_state->res_ctx.pipe_ctx[i].plane_res.hubp && |
264 | existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i) |
265 | continue; |
266 | |
267 | preferred_pipe_candidates[num_preferred_candidates++] = i; |
268 | } |
269 | } |
270 | } |
271 | |
272 | return num_preferred_candidates; |
273 | } |
274 | |
275 | static unsigned int find_last_resort_pipe_candidates(const struct dc_state *existing_state, |
276 | const int pipe_count, |
277 | const unsigned int stream_id, |
278 | unsigned int *last_resort_pipe_candidates) |
279 | { |
280 | unsigned int num_last_resort_candidates = 0; |
281 | int i; |
282 | |
283 | /* There are two cases where we would like to add a given pipe into the last |
284 | * candidate array: |
285 | * |
286 | * 1. If the pipe requires a change in OPP, for example during an MPC |
287 | * to ODM transiton. |
288 | * |
289 | * 2. If the pipe already has an enabled OTG. |
290 | */ |
291 | if (existing_state) { |
292 | for (i = 0; i < pipe_count; i++) { |
293 | if ((existing_state->res_ctx.pipe_ctx[i].plane_res.hubp && |
294 | existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i) || |
295 | existing_state->res_ctx.pipe_ctx[i].stream_res.tg) |
296 | last_resort_pipe_candidates[num_last_resort_candidates++] = i; |
297 | } |
298 | } |
299 | |
300 | return num_last_resort_candidates; |
301 | } |
302 | |
303 | static bool is_pipe_in_candidate_array(const unsigned int pipe_idx, |
304 | const unsigned int *candidate_array, |
305 | const unsigned int candidate_array_size) |
306 | { |
307 | int i; |
308 | |
309 | for (i = 0; i < candidate_array_size; i++) { |
310 | if (candidate_array[i] == pipe_idx) |
311 | return true; |
312 | } |
313 | |
314 | return false; |
315 | } |
316 | |
317 | static bool find_more_pipes_for_stream(struct dml2_context *ctx, |
318 | struct dc_state *state, // The state we want to find a free mapping in |
319 | unsigned int stream_id, // The stream we want this pipe to drive |
320 | int *assigned_pipes, |
321 | int *assigned_pipe_count, |
322 | int pipes_needed, |
323 | const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to |
324 | { |
325 | struct pipe_ctx *pipe = NULL; |
326 | unsigned int preferred_pipe_candidates[MAX_PIPES] = {0}; |
327 | unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0}; |
328 | unsigned int num_preferred_candidates = 0; |
329 | unsigned int num_last_resort_candidates = 0; |
330 | int i; |
331 | |
332 | if (existing_state) { |
333 | num_preferred_candidates = |
334 | find_preferred_pipe_candidates(existing_state, pipe_count: ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates); |
335 | |
336 | num_last_resort_candidates = |
337 | find_last_resort_pipe_candidates(existing_state, pipe_count: ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates); |
338 | } |
339 | |
340 | // First see if any of the preferred are unmapped, and choose those instead |
341 | for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) { |
342 | pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]]; |
343 | if (!is_plane_using_pipe(pipe)) { |
344 | pipes_needed--; |
345 | // TODO: This doens't make sense really, pipe_idx should always be valid |
346 | pipe->pipe_idx = preferred_pipe_candidates[i]; |
347 | assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; |
348 | } |
349 | } |
350 | |
351 | // We like to pair pipes starting from the higher order indicies for combining |
352 | for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) { |
353 | // Ignore any pipes that are the preferred or last resort candidate |
354 | if (is_pipe_in_candidate_array(pipe_idx: i, candidate_array: preferred_pipe_candidates, candidate_array_size: num_preferred_candidates) || |
355 | is_pipe_in_candidate_array(pipe_idx: i, candidate_array: last_resort_pipe_candidates, candidate_array_size: num_last_resort_candidates)) |
356 | continue; |
357 | |
358 | pipe = &state->res_ctx.pipe_ctx[i]; |
359 | if (!is_plane_using_pipe(pipe)) { |
360 | pipes_needed--; |
361 | // TODO: This doens't make sense really, pipe_idx should always be valid |
362 | pipe->pipe_idx = i; |
363 | assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; |
364 | } |
365 | } |
366 | |
367 | // Only use the last resort pipe candidates as a last resort |
368 | for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) { |
369 | pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]]; |
370 | if (!is_plane_using_pipe(pipe)) { |
371 | pipes_needed--; |
372 | // TODO: This doens't make sense really, pipe_idx should always be valid |
373 | pipe->pipe_idx = last_resort_pipe_candidates[i]; |
374 | assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; |
375 | } |
376 | } |
377 | |
378 | ASSERT(pipes_needed <= 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available |
379 | |
380 | return pipes_needed <= 0; |
381 | } |
382 | |
383 | static bool find_more_free_pipes(struct dml2_context *ctx, |
384 | struct dc_state *state, // The state we want to find a free mapping in |
385 | unsigned int stream_id, // The stream we want this pipe to drive |
386 | int *assigned_pipes, |
387 | int *assigned_pipe_count, |
388 | int pipes_needed, |
389 | const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to |
390 | { |
391 | struct pipe_ctx *pipe = NULL; |
392 | unsigned int preferred_pipe_candidates[MAX_PIPES] = {0}; |
393 | unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0}; |
394 | unsigned int num_preferred_candidates = 0; |
395 | unsigned int num_last_resort_candidates = 0; |
396 | int i; |
397 | |
398 | if (existing_state) { |
399 | num_preferred_candidates = |
400 | find_preferred_pipe_candidates(existing_state, pipe_count: ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates); |
401 | |
402 | num_last_resort_candidates = |
403 | find_last_resort_pipe_candidates(existing_state, pipe_count: ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates); |
404 | } |
405 | |
406 | // First see if any of the preferred are unmapped, and choose those instead |
407 | for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) { |
408 | pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]]; |
409 | if (is_pipe_free(pipe)) { |
410 | pipes_needed--; |
411 | // TODO: This doens't make sense really, pipe_idx should always be valid |
412 | pipe->pipe_idx = preferred_pipe_candidates[i]; |
413 | assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; |
414 | } |
415 | } |
416 | |
417 | // We like to pair pipes starting from the higher order indicies for combining |
418 | for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) { |
419 | // Ignore any pipes that are the preferred or last resort candidate |
420 | if (is_pipe_in_candidate_array(pipe_idx: i, candidate_array: preferred_pipe_candidates, candidate_array_size: num_preferred_candidates) || |
421 | is_pipe_in_candidate_array(pipe_idx: i, candidate_array: last_resort_pipe_candidates, candidate_array_size: num_last_resort_candidates)) |
422 | continue; |
423 | |
424 | pipe = &state->res_ctx.pipe_ctx[i]; |
425 | if (is_pipe_free(pipe)) { |
426 | pipes_needed--; |
427 | // TODO: This doens't make sense really, pipe_idx should always be valid |
428 | pipe->pipe_idx = i; |
429 | assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; |
430 | } |
431 | } |
432 | |
433 | // Only use the last resort pipe candidates as a last resort |
434 | for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) { |
435 | pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]]; |
436 | if (is_pipe_free(pipe)) { |
437 | pipes_needed--; |
438 | // TODO: This doens't make sense really, pipe_idx should always be valid |
439 | pipe->pipe_idx = last_resort_pipe_candidates[i]; |
440 | assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; |
441 | } |
442 | } |
443 | |
444 | ASSERT(pipes_needed == 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available |
445 | |
446 | return pipes_needed == 0; |
447 | } |
448 | |
449 | static void sort_pipes_for_splitting(struct dc_plane_pipe_pool *pipes) |
450 | { |
451 | bool sorted, swapped; |
452 | unsigned int cur_index; |
453 | unsigned int temp; |
454 | int odm_slice_index; |
455 | |
456 | for (odm_slice_index = 0; odm_slice_index < pipes->num_pipes_assigned_to_plane_for_odm_combine; odm_slice_index++) { |
457 | // Sort each MPCC set |
458 | //Un-optimized bubble sort, but that's okay for array sizes <= 6 |
459 | |
460 | if (pipes->num_pipes_assigned_to_plane_for_mpcc_combine <= 1) |
461 | sorted = true; |
462 | else |
463 | sorted = false; |
464 | |
465 | cur_index = 0; |
466 | swapped = false; |
467 | while (!sorted) { |
468 | if (pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] > pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1]) { |
469 | temp = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index]; |
470 | pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1]; |
471 | pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1] = temp; |
472 | |
473 | swapped = true; |
474 | } |
475 | |
476 | cur_index++; |
477 | |
478 | if (cur_index == pipes->num_pipes_assigned_to_plane_for_mpcc_combine - 1) { |
479 | cur_index = 0; |
480 | |
481 | if (swapped) |
482 | sorted = false; |
483 | else |
484 | sorted = true; |
485 | |
486 | swapped = false; |
487 | } |
488 | |
489 | } |
490 | } |
491 | } |
492 | |
493 | // For example, 3840 x 2160, ODM2:1 has a slice array of [1919, 3839], meaning, slice0 spans h_pixels 0->1919, and slice1 spans 1920->3840 |
494 | static void calculate_odm_slices(const struct dc_stream_state *stream, unsigned int odm_factor, unsigned int *odm_slice_end_x) |
495 | { |
496 | unsigned int slice_size = 0; |
497 | int i; |
498 | |
499 | if (odm_factor < 1 || odm_factor > 4) { |
500 | ASSERT(false); |
501 | return; |
502 | } |
503 | |
504 | slice_size = stream->src.width / odm_factor; |
505 | |
506 | for (i = 0; i < odm_factor; i++) |
507 | odm_slice_end_x[i] = (slice_size * (i + 1)) - 1; |
508 | |
509 | odm_slice_end_x[odm_factor - 1] = stream->src.width - 1; |
510 | } |
511 | |
512 | static bool is_plane_in_odm_slice(const struct dc_plane_state *plane, unsigned int slice_index, unsigned int *odm_slice_end_x, unsigned int num_slices) |
513 | { |
514 | unsigned int slice_start_x, slice_end_x; |
515 | |
516 | if (slice_index == 0) |
517 | slice_start_x = 0; |
518 | else |
519 | slice_start_x = odm_slice_end_x[slice_index - 1] + 1; |
520 | |
521 | slice_end_x = odm_slice_end_x[slice_index]; |
522 | |
523 | if (plane->clip_rect.x + plane->clip_rect.width < slice_start_x) |
524 | return false; |
525 | |
526 | if (plane->clip_rect.x > slice_end_x) |
527 | return false; |
528 | |
529 | return true; |
530 | } |
531 | |
532 | static void add_odm_slice_to_odm_tree(struct dml2_context *ctx, |
533 | struct dc_state *state, |
534 | struct dc_pipe_mapping_scratch *scratch, |
535 | unsigned int odm_slice_index) |
536 | { |
537 | struct pipe_ctx *pipe = NULL; |
538 | int i; |
539 | |
540 | // MPCC Combine + ODM Combine is not supported, so there should never be a case where the current plane |
541 | // has more than 1 pipe mapped to it for a given slice. |
542 | ASSERT(scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine == 1 || scratch->pipe_pool.num_pipes_assigned_to_plane_for_odm_combine == 1); |
543 | |
544 | for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) { |
545 | pipe = &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]]; |
546 | |
547 | if (scratch->mpc_info.prev_odm_pipe) |
548 | scratch->mpc_info.prev_odm_pipe->next_odm_pipe = pipe; |
549 | |
550 | pipe->prev_odm_pipe = scratch->mpc_info.prev_odm_pipe; |
551 | pipe->next_odm_pipe = NULL; |
552 | } |
553 | scratch->mpc_info.prev_odm_pipe = pipe; |
554 | } |
555 | |
556 | static struct pipe_ctx *add_plane_to_blend_tree(struct dml2_context *ctx, |
557 | struct dc_state *state, |
558 | const struct dc_plane_state *plane, |
559 | struct dc_plane_pipe_pool *pipe_pool, |
560 | unsigned int odm_slice, |
561 | struct pipe_ctx *top_pipe) |
562 | { |
563 | int i; |
564 | |
565 | for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) { |
566 | if (top_pipe) |
567 | top_pipe->bottom_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]]; |
568 | |
569 | pipe_pool->pipe_used[odm_slice][i] = true; |
570 | |
571 | state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].top_pipe = top_pipe; |
572 | state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].bottom_pipe = NULL; |
573 | |
574 | top_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]]; |
575 | } |
576 | |
577 | // After running the above loop, the top pipe actually ends up pointing to the bottom of this MPCC combine tree, so we are actually |
578 | // returning the bottom pipe here |
579 | return top_pipe; |
580 | } |
581 | |
582 | static unsigned int find_pipes_assigned_to_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id, unsigned int *pipes) |
583 | { |
584 | int i; |
585 | unsigned int num_found = 0; |
586 | |
587 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
588 | struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i]; |
589 | |
590 | if (pipe->stream && pipe->stream->stream_id == stream_id && !pipe->top_pipe && !pipe->prev_odm_pipe) { |
591 | while (pipe) { |
592 | pipes[num_found++] = pipe->pipe_idx; |
593 | pipe = pipe->next_odm_pipe; |
594 | } |
595 | break; |
596 | } |
597 | } |
598 | |
599 | return num_found; |
600 | } |
601 | |
602 | static struct pipe_ctx *assign_pipes_to_stream(struct dml2_context *ctx, struct dc_state *state, |
603 | const struct dc_stream_state *stream, |
604 | int odm_factor, |
605 | struct dc_plane_pipe_pool *pipe_pool, |
606 | const struct dc_state *existing_state) |
607 | { |
608 | struct pipe_ctx *master_pipe; |
609 | unsigned int pipes_needed; |
610 | unsigned int pipes_assigned; |
611 | unsigned int pipes[MAX_PIPES] = {0}; |
612 | unsigned int next_pipe_to_assign; |
613 | int odm_slice; |
614 | |
615 | pipes_needed = odm_factor; |
616 | |
617 | master_pipe = find_master_pipe_of_stream(ctx, state, stream_id: stream->stream_id); |
618 | ASSERT(master_pipe); |
619 | |
620 | pipes_assigned = find_pipes_assigned_to_stream(ctx, state, stream_id: stream->stream_id, pipes); |
621 | |
622 | find_more_free_pipes(ctx, state, stream_id: stream->stream_id, assigned_pipes: pipes, assigned_pipe_count: &pipes_assigned, pipes_needed: pipes_needed - pipes_assigned, existing_state); |
623 | |
624 | ASSERT(pipes_assigned == pipes_needed); |
625 | |
626 | next_pipe_to_assign = 0; |
627 | for (odm_slice = 0; odm_slice < odm_factor; odm_slice++) |
628 | pipe_pool->pipes_assigned_to_plane[odm_slice][0] = pipes[next_pipe_to_assign++]; |
629 | |
630 | pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = 1; |
631 | pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor; |
632 | |
633 | return master_pipe; |
634 | } |
635 | |
636 | static struct pipe_ctx *assign_pipes_to_plane(struct dml2_context *ctx, struct dc_state *state, |
637 | const struct dc_stream_state *stream, |
638 | const struct dc_plane_state *plane, |
639 | int odm_factor, |
640 | int mpc_factor, |
641 | int plane_index, |
642 | struct dc_plane_pipe_pool *pipe_pool, |
643 | const struct dc_state *existing_state) |
644 | { |
645 | struct pipe_ctx *master_pipe = NULL; |
646 | unsigned int plane_id; |
647 | unsigned int pipes_needed; |
648 | unsigned int pipes_assigned; |
649 | unsigned int pipes[MAX_PIPES] = {0}; |
650 | unsigned int next_pipe_to_assign; |
651 | int odm_slice, mpc_slice; |
652 | |
653 | if (!get_plane_id(dml2: ctx, state, plane, stream_id: stream->stream_id, plane_index, plane_id: &plane_id)) { |
654 | ASSERT(false); |
655 | return master_pipe; |
656 | } |
657 | |
658 | pipes_needed = mpc_factor * odm_factor; |
659 | |
660 | master_pipe = find_master_pipe_of_plane(ctx, state, plane_id); |
661 | ASSERT(master_pipe); |
662 | |
663 | pipes_assigned = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes); |
664 | |
665 | find_more_pipes_for_stream(ctx, state, stream_id: stream->stream_id, assigned_pipes: pipes, assigned_pipe_count: &pipes_assigned, pipes_needed: pipes_needed - pipes_assigned, existing_state); |
666 | |
667 | ASSERT(pipes_assigned >= pipes_needed); |
668 | |
669 | next_pipe_to_assign = 0; |
670 | for (odm_slice = 0; odm_slice < odm_factor; odm_slice++) |
671 | for (mpc_slice = 0; mpc_slice < mpc_factor; mpc_slice++) |
672 | pipe_pool->pipes_assigned_to_plane[odm_slice][mpc_slice] = pipes[next_pipe_to_assign++]; |
673 | |
674 | pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = mpc_factor; |
675 | pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor; |
676 | |
677 | return master_pipe; |
678 | } |
679 | |
680 | static bool is_pipe_used(const struct dc_plane_pipe_pool *pool, unsigned int pipe_idx) |
681 | { |
682 | int i, j; |
683 | |
684 | for (i = 0; i < pool->num_pipes_assigned_to_plane_for_odm_combine; i++) { |
685 | for (j = 0; j < pool->num_pipes_assigned_to_plane_for_mpcc_combine; j++) { |
686 | if (pool->pipes_assigned_to_plane[i][j] == pipe_idx && pool->pipe_used[i][j]) |
687 | return true; |
688 | } |
689 | } |
690 | |
691 | return false; |
692 | } |
693 | |
694 | static void free_pipe(struct pipe_ctx *pipe) |
695 | { |
696 | memset(pipe, 0, sizeof(struct pipe_ctx)); |
697 | } |
698 | |
699 | static void free_unused_pipes_for_plane(struct dml2_context *ctx, struct dc_state *state, |
700 | const struct dc_plane_state *plane, const struct dc_plane_pipe_pool *pool, unsigned int stream_id, int plane_index) |
701 | { |
702 | int i; |
703 | bool is_plane_duplicate = ctx->v20.scratch.plane_duplicate_exists; |
704 | |
705 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
706 | if (state->res_ctx.pipe_ctx[i].plane_state == plane && |
707 | state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id && |
708 | (!is_plane_duplicate || (is_plane_duplicate && |
709 | ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[state->res_ctx.pipe_ctx[i].pipe_idx] == plane_index)) && |
710 | !is_pipe_used(pool, pipe_idx: state->res_ctx.pipe_ctx[i].pipe_idx)) { |
711 | free_pipe(pipe: &state->res_ctx.pipe_ctx[i]); |
712 | } |
713 | } |
714 | } |
715 | |
716 | static void remove_pipes_from_blend_trees(struct dml2_context *ctx, struct dc_state *state, struct dc_plane_pipe_pool *pipe_pool, unsigned int odm_slice) |
717 | { |
718 | struct pipe_ctx *pipe; |
719 | int i; |
720 | |
721 | for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) { |
722 | pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][0]]; |
723 | if (pipe->top_pipe) |
724 | pipe->top_pipe->bottom_pipe = pipe->bottom_pipe; |
725 | |
726 | if (pipe->bottom_pipe) |
727 | pipe->bottom_pipe = pipe->top_pipe; |
728 | |
729 | pipe_pool->pipe_used[odm_slice][i] = true; |
730 | } |
731 | } |
732 | |
733 | static void map_pipes_for_stream(struct dml2_context *ctx, struct dc_state *state, const struct dc_stream_state *stream, |
734 | struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state) |
735 | { |
736 | int odm_slice_index; |
737 | struct pipe_ctx *master_pipe = NULL; |
738 | |
739 | |
740 | master_pipe = assign_pipes_to_stream(ctx, state, stream, odm_factor: scratch->odm_info.odm_factor, pipe_pool: &scratch->pipe_pool, existing_state); |
741 | sort_pipes_for_splitting(pipes: &scratch->pipe_pool); |
742 | |
743 | for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) { |
744 | remove_pipes_from_blend_trees(ctx, state, pipe_pool: &scratch->pipe_pool, odm_slice: odm_slice_index); |
745 | |
746 | add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index); |
747 | |
748 | ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state, |
749 | master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][0]], true); |
750 | } |
751 | } |
752 | |
753 | static void map_pipes_for_plane(struct dml2_context *ctx, struct dc_state *state, const struct dc_stream_state *stream, const struct dc_plane_state *plane, |
754 | int plane_index, struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state) |
755 | { |
756 | int odm_slice_index; |
757 | unsigned int plane_id; |
758 | struct pipe_ctx *master_pipe = NULL; |
759 | int i; |
760 | |
761 | if (!get_plane_id(dml2: ctx, state, plane, stream_id: stream->stream_id, plane_index, plane_id: &plane_id)) { |
762 | ASSERT(false); |
763 | return; |
764 | } |
765 | |
766 | master_pipe = assign_pipes_to_plane(ctx, state, stream, plane, odm_factor: scratch->odm_info.odm_factor, |
767 | mpc_factor: scratch->mpc_info.mpc_factor, plane_index, pipe_pool: &scratch->pipe_pool, existing_state); |
768 | sort_pipes_for_splitting(pipes: &scratch->pipe_pool); |
769 | |
770 | for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) { |
771 | // We build the tree for one ODM slice at a time. |
772 | // Each ODM slice shares a common OPP |
773 | if (!is_plane_in_odm_slice(plane, slice_index: odm_slice_index, odm_slice_end_x: scratch->odm_info.odm_slice_end_x, num_slices: scratch->odm_info.odm_factor)) { |
774 | continue; |
775 | } |
776 | |
777 | // Now we have a list of all pipes to be used for this plane/stream, now setup the tree. |
778 | scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index] = add_plane_to_blend_tree(ctx, state, |
779 | plane, |
780 | pipe_pool: &scratch->pipe_pool, |
781 | odm_slice: odm_slice_index, |
782 | top_pipe: scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index]); |
783 | |
784 | add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index); |
785 | |
786 | for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) { |
787 | |
788 | ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state, |
789 | master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]], true); |
790 | } |
791 | } |
792 | |
793 | free_unused_pipes_for_plane(ctx, state, plane, pool: &scratch->pipe_pool, stream_id: stream->stream_id, plane_index); |
794 | } |
795 | |
796 | static unsigned int get_mpc_factor(struct dml2_context *ctx, |
797 | const struct dc_state *state, |
798 | const struct dml_display_cfg_st *disp_cfg, |
799 | struct dml2_dml_to_dc_pipe_mapping *mapping, |
800 | const struct dc_stream_status *status, |
801 | const struct dc_stream_state *stream, |
802 | int plane_idx) |
803 | { |
804 | unsigned int plane_id; |
805 | unsigned int cfg_idx; |
806 | unsigned int mpc_factor; |
807 | |
808 | get_plane_id(dml2: ctx, state, plane: status->plane_states[plane_idx], |
809 | stream_id: stream->stream_id, plane_index: plane_idx, plane_id: &plane_id); |
810 | cfg_idx = find_disp_cfg_idx_by_plane_id(mapping, plane_id); |
811 | if (ctx->architecture == dml2_architecture_20) { |
812 | mpc_factor = (unsigned int)disp_cfg->hw.DPPPerSurface[cfg_idx]; |
813 | } else { |
814 | mpc_factor = 1; |
815 | ASSERT(false); |
816 | } |
817 | |
818 | /* For stereo timings, we need to pipe split */ |
819 | if (dml2_is_stereo_timing(stream)) |
820 | mpc_factor = 2; |
821 | |
822 | return mpc_factor; |
823 | } |
824 | |
825 | static unsigned int get_odm_factor( |
826 | const struct dml2_context *ctx, |
827 | const struct dml_display_cfg_st *disp_cfg, |
828 | struct dml2_dml_to_dc_pipe_mapping *mapping, |
829 | const struct dc_stream_state *stream) |
830 | { |
831 | unsigned int cfg_idx = find_disp_cfg_idx_by_stream_id( |
832 | mapping, stream_id: stream->stream_id); |
833 | |
834 | if (ctx->architecture == dml2_architecture_20) |
835 | switch (disp_cfg->hw.ODMMode[cfg_idx]) { |
836 | case dml_odm_mode_bypass: |
837 | return 1; |
838 | case dml_odm_mode_combine_2to1: |
839 | return 2; |
840 | case dml_odm_mode_combine_4to1: |
841 | return 4; |
842 | default: |
843 | break; |
844 | } |
845 | ASSERT(false); |
846 | return 1; |
847 | } |
848 | |
849 | static void populate_mpc_factors_for_stream( |
850 | struct dml2_context *ctx, |
851 | const struct dml_display_cfg_st *disp_cfg, |
852 | struct dml2_dml_to_dc_pipe_mapping *mapping, |
853 | const struct dc_state *state, |
854 | unsigned int stream_idx, |
855 | unsigned int odm_factor, |
856 | unsigned int mpc_factors[MAX_PIPES]) |
857 | { |
858 | const struct dc_stream_status *status = &state->stream_status[stream_idx]; |
859 | int i; |
860 | |
861 | for (i = 0; i < status->plane_count; i++) |
862 | if (odm_factor == 1) |
863 | mpc_factors[i] = get_mpc_factor( |
864 | ctx, state, disp_cfg, mapping, status, |
865 | stream: state->streams[stream_idx], plane_idx: i); |
866 | else |
867 | mpc_factors[i] = 1; |
868 | } |
869 | |
870 | static void populate_odm_factors(const struct dml2_context *ctx, |
871 | const struct dml_display_cfg_st *disp_cfg, |
872 | struct dml2_dml_to_dc_pipe_mapping *mapping, |
873 | const struct dc_state *state, |
874 | unsigned int odm_factors[MAX_PIPES]) |
875 | { |
876 | int i; |
877 | |
878 | for (i = 0; i < state->stream_count; i++) |
879 | odm_factors[i] = get_odm_factor( |
880 | ctx, disp_cfg, mapping, stream: state->streams[i]); |
881 | } |
882 | |
883 | static bool map_dc_pipes_for_stream(struct dml2_context *ctx, |
884 | struct dc_state *state, |
885 | const struct dc_state *existing_state, |
886 | const struct dc_stream_state *stream, |
887 | const struct dc_stream_status *status, |
888 | unsigned int odm_factor, |
889 | unsigned int mpc_factors[MAX_PIPES]) |
890 | { |
891 | int plane_idx; |
892 | bool result = true; |
893 | |
894 | if (odm_factor == 1) |
895 | /* |
896 | * ODM and MPC combines are by DML design mutually exclusive. |
897 | * ODM factor of 1 means MPC factors may be greater than 1. |
898 | * In this case, we want to set ODM factor to 1 first to free up |
899 | * pipe resources from previous ODM configuration before setting |
900 | * up MPC combine to acquire more pipe resources. |
901 | */ |
902 | result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count( |
903 | state, |
904 | existing_state, |
905 | ctx->config.callbacks.dc->res_pool, |
906 | stream, |
907 | odm_factor); |
908 | for (plane_idx = 0; plane_idx < status->plane_count; plane_idx++) |
909 | result &= ctx->config.callbacks.update_pipes_for_plane_with_slice_count( |
910 | state, |
911 | existing_state, |
912 | ctx->config.callbacks.dc->res_pool, |
913 | status->plane_states[plane_idx], |
914 | mpc_factors[plane_idx]); |
915 | if (odm_factor > 1) |
916 | result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count( |
917 | state, |
918 | existing_state, |
919 | ctx->config.callbacks.dc->res_pool, |
920 | stream, |
921 | odm_factor); |
922 | return result; |
923 | } |
924 | |
925 | static bool map_dc_pipes_with_callbacks(struct dml2_context *ctx, |
926 | struct dc_state *state, |
927 | const struct dml_display_cfg_st *disp_cfg, |
928 | struct dml2_dml_to_dc_pipe_mapping *mapping, |
929 | const struct dc_state *existing_state) |
930 | { |
931 | unsigned int odm_factors[MAX_PIPES]; |
932 | unsigned int mpc_factors_for_stream[MAX_PIPES]; |
933 | int i; |
934 | bool result = true; |
935 | |
936 | populate_odm_factors(ctx, disp_cfg, mapping, state, odm_factors); |
937 | for (i = 0; i < state->stream_count; i++) { |
938 | populate_mpc_factors_for_stream(ctx, disp_cfg, mapping, state, |
939 | stream_idx: i, odm_factor: odm_factors[i], mpc_factors: mpc_factors_for_stream); |
940 | result &= map_dc_pipes_for_stream(ctx, state, existing_state, |
941 | stream: state->streams[i], |
942 | status: &state->stream_status[i], |
943 | odm_factor: odm_factors[i], mpc_factors: mpc_factors_for_stream); |
944 | } |
945 | return result; |
946 | } |
947 | |
948 | bool dml2_map_dc_pipes(struct dml2_context *ctx, struct dc_state *state, const struct dml_display_cfg_st *disp_cfg, struct dml2_dml_to_dc_pipe_mapping *mapping, const struct dc_state *existing_state) |
949 | { |
950 | int stream_index, plane_index, i; |
951 | |
952 | unsigned int stream_disp_cfg_index; |
953 | unsigned int plane_disp_cfg_index; |
954 | |
955 | unsigned int plane_id; |
956 | unsigned int stream_id; |
957 | |
958 | const unsigned int *ODMMode, *DPPPerSurface; |
959 | struct dc_pipe_mapping_scratch scratch; |
960 | |
961 | if (ctx->config.map_dc_pipes_with_callbacks) |
962 | return map_dc_pipes_with_callbacks( |
963 | ctx, state, disp_cfg, mapping, existing_state); |
964 | |
965 | ODMMode = (unsigned int *)disp_cfg->hw.ODMMode; |
966 | DPPPerSurface = disp_cfg->hw.DPPPerSurface; |
967 | |
968 | for (stream_index = 0; stream_index < state->stream_count; stream_index++) { |
969 | memset(&scratch, 0, sizeof(struct dc_pipe_mapping_scratch)); |
970 | |
971 | stream_id = state->streams[stream_index]->stream_id; |
972 | stream_disp_cfg_index = find_disp_cfg_idx_by_stream_id(mapping, stream_id); |
973 | |
974 | if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_bypass) { |
975 | scratch.odm_info.odm_factor = 1; |
976 | } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_2to1) { |
977 | scratch.odm_info.odm_factor = 2; |
978 | } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_4to1) { |
979 | scratch.odm_info.odm_factor = 4; |
980 | } else { |
981 | ASSERT(false); |
982 | scratch.odm_info.odm_factor = 1; |
983 | } |
984 | |
985 | calculate_odm_slices(stream: state->streams[stream_index], odm_factor: scratch.odm_info.odm_factor, odm_slice_end_x: scratch.odm_info.odm_slice_end_x); |
986 | |
987 | // If there are no planes, you still want to setup ODM... |
988 | if (state->stream_status[stream_index].plane_count == 0) { |
989 | map_pipes_for_stream(ctx, state, stream: state->streams[stream_index], scratch: &scratch, existing_state); |
990 | } |
991 | |
992 | for (plane_index = 0; plane_index < state->stream_status[stream_index].plane_count; plane_index++) { |
993 | // Planes are ordered top to bottom. |
994 | if (get_plane_id(dml2: ctx, state, plane: state->stream_status[stream_index].plane_states[plane_index], |
995 | stream_id, plane_index, plane_id: &plane_id)) { |
996 | plane_disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id); |
997 | |
998 | // Setup mpc_info for this plane |
999 | scratch.mpc_info.prev_odm_pipe = NULL; |
1000 | if (scratch.odm_info.odm_factor == 1) { |
1001 | // If ODM combine is not inuse, then the number of pipes |
1002 | // per plane is determined by MPC combine factor |
1003 | scratch.mpc_info.mpc_factor = DPPPerSurface[plane_disp_cfg_index]; |
1004 | |
1005 | //For stereo timings, we need to pipe split |
1006 | if (dml2_is_stereo_timing(stream: state->streams[stream_index])) |
1007 | scratch.mpc_info.mpc_factor = 2; |
1008 | } else { |
1009 | // If ODM combine is enabled, then we use at most 1 pipe per |
1010 | // odm slice per plane, i.e. MPC combine is never used |
1011 | scratch.mpc_info.mpc_factor = 1; |
1012 | } |
1013 | |
1014 | ASSERT(scratch.odm_info.odm_factor * scratch.mpc_info.mpc_factor > 0); |
1015 | |
1016 | // Clear the pool assignment scratch (which is per plane) |
1017 | memset(&scratch.pipe_pool, 0, sizeof(struct dc_plane_pipe_pool)); |
1018 | |
1019 | map_pipes_for_plane(ctx, state, stream: state->streams[stream_index], |
1020 | plane: state->stream_status[stream_index].plane_states[plane_index], plane_index, scratch: &scratch, existing_state); |
1021 | } else { |
1022 | // Plane ID cannot be generated, therefore no DML mapping can be performed. |
1023 | ASSERT(false); |
1024 | } |
1025 | } |
1026 | |
1027 | } |
1028 | |
1029 | if (!validate_pipe_assignment(ctx, state, disp_cfg, mapping)) |
1030 | ASSERT(false); |
1031 | |
1032 | for (i = 0; i < ctx->config.dcn_pipe_count; i++) { |
1033 | struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i]; |
1034 | |
1035 | if (pipe->plane_state) { |
1036 | if (!ctx->config.callbacks.build_scaling_params(pipe)) { |
1037 | ASSERT(false); |
1038 | } |
1039 | } |
1040 | } |
1041 | |
1042 | return true; |
1043 | } |
1044 | |