1/*
2 * Copyright 2023 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#include "dc_types.h"
26#include "core_types.h"
27#include "core_status.h"
28#include "dc_state.h"
29#include "dc_state_priv.h"
30#include "dc_stream_priv.h"
31#include "dc_plane_priv.h"
32
33#include "dm_services.h"
34#include "resource.h"
35#include "link_enc_cfg.h"
36
37#if defined(CONFIG_DRM_AMD_DC_FP)
38#include "dml2_0/dml2_wrapper.h"
39#include "dml2_0/dml2_internal_types.h"
40#endif
41
42#define DC_LOGGER \
43 dc->ctx->logger
44#define DC_LOGGER_INIT(logger)
45
46/* Private dc_state helper functions */
47static bool dc_state_track_phantom_stream(struct dc_state *state,
48 struct dc_stream_state *phantom_stream)
49{
50 if (state->phantom_stream_count >= MAX_PHANTOM_PIPES)
51 return false;
52
53 state->phantom_streams[state->phantom_stream_count++] = phantom_stream;
54
55 return true;
56}
57
58static bool dc_state_untrack_phantom_stream(struct dc_state *state, struct dc_stream_state *phantom_stream)
59{
60 bool res = false;
61 int i;
62
63 /* first find phantom stream in the dc_state */
64 for (i = 0; i < state->phantom_stream_count; i++) {
65 if (state->phantom_streams[i] == phantom_stream) {
66 state->phantom_streams[i] = NULL;
67 res = true;
68 break;
69 }
70 }
71
72 /* failed to find stream in state */
73 if (!res)
74 return res;
75
76 /* trim back phantom streams */
77 state->phantom_stream_count--;
78 for (; i < state->phantom_stream_count; i++)
79 state->phantom_streams[i] = state->phantom_streams[i + 1];
80
81 return res;
82}
83
84static bool dc_state_is_phantom_stream_tracked(struct dc_state *state, struct dc_stream_state *phantom_stream)
85{
86 int i;
87
88 for (i = 0; i < state->phantom_stream_count; i++) {
89 if (state->phantom_streams[i] == phantom_stream)
90 return true;
91 }
92
93 return false;
94}
95
96static bool dc_state_track_phantom_plane(struct dc_state *state,
97 struct dc_plane_state *phantom_plane)
98{
99 if (state->phantom_plane_count >= MAX_PHANTOM_PIPES)
100 return false;
101
102 state->phantom_planes[state->phantom_plane_count++] = phantom_plane;
103
104 return true;
105}
106
107static bool dc_state_untrack_phantom_plane(struct dc_state *state, struct dc_plane_state *phantom_plane)
108{
109 bool res = false;
110 int i;
111
112 /* first find phantom plane in the dc_state */
113 for (i = 0; i < state->phantom_plane_count; i++) {
114 if (state->phantom_planes[i] == phantom_plane) {
115 state->phantom_planes[i] = NULL;
116 res = true;
117 break;
118 }
119 }
120
121 /* failed to find plane in state */
122 if (!res)
123 return res;
124
125 /* trim back phantom planes */
126 state->phantom_plane_count--;
127 for (; i < state->phantom_plane_count; i++)
128 state->phantom_planes[i] = state->phantom_planes[i + 1];
129
130 return res;
131}
132
133static bool dc_state_is_phantom_plane_tracked(struct dc_state *state, struct dc_plane_state *phantom_plane)
134{
135 int i;
136
137 for (i = 0; i < state->phantom_plane_count; i++) {
138 if (state->phantom_planes[i] == phantom_plane)
139 return true;
140 }
141
142 return false;
143}
144
145static void dc_state_copy_internal(struct dc_state *dst_state, struct dc_state *src_state)
146{
147 int i, j;
148
149 memcpy(dst_state, src_state, sizeof(struct dc_state));
150
151 for (i = 0; i < MAX_PIPES; i++) {
152 struct pipe_ctx *cur_pipe = &dst_state->res_ctx.pipe_ctx[i];
153
154 if (cur_pipe->top_pipe)
155 cur_pipe->top_pipe = &dst_state->res_ctx.pipe_ctx[cur_pipe->top_pipe->pipe_idx];
156
157 if (cur_pipe->bottom_pipe)
158 cur_pipe->bottom_pipe = &dst_state->res_ctx.pipe_ctx[cur_pipe->bottom_pipe->pipe_idx];
159
160 if (cur_pipe->prev_odm_pipe)
161 cur_pipe->prev_odm_pipe = &dst_state->res_ctx.pipe_ctx[cur_pipe->prev_odm_pipe->pipe_idx];
162
163 if (cur_pipe->next_odm_pipe)
164 cur_pipe->next_odm_pipe = &dst_state->res_ctx.pipe_ctx[cur_pipe->next_odm_pipe->pipe_idx];
165 }
166
167 /* retain phantoms */
168 for (i = 0; i < dst_state->phantom_stream_count; i++)
169 dc_stream_retain(dc_stream: dst_state->phantom_streams[i]);
170
171 for (i = 0; i < dst_state->phantom_plane_count; i++)
172 dc_plane_state_retain(plane_state: dst_state->phantom_planes[i]);
173
174 /* retain streams and planes */
175 for (i = 0; i < dst_state->stream_count; i++) {
176 dc_stream_retain(dc_stream: dst_state->streams[i]);
177 for (j = 0; j < dst_state->stream_status[i].plane_count; j++)
178 dc_plane_state_retain(
179 plane_state: dst_state->stream_status[i].plane_states[j]);
180 }
181
182}
183
184static void init_state(struct dc *dc, struct dc_state *state)
185{
186 /* Each context must have their own instance of VBA and in order to
187 * initialize and obtain IP and SOC the base DML instance from DC is
188 * initially copied into every context
189 */
190 memcpy(&state->bw_ctx.dml, &dc->dml, sizeof(struct display_mode_lib));
191}
192
193/* Public dc_state functions */
194struct dc_state *dc_state_create(struct dc *dc, struct dc_state_create_params *params)
195{
196 struct dc_state *state;
197
198 state = kvzalloc(sizeof(struct dc_state), GFP_KERNEL);
199
200 if (!state)
201 return NULL;
202
203 init_state(dc, state);
204 dc_state_construct(dc, state);
205 state->power_source = params ? params->power_source : DC_POWER_SOURCE_AC;
206
207#ifdef CONFIG_DRM_AMD_DC_FP
208 if (dc->debug.using_dml2) {
209 if (!dml2_create(in_dc: dc, config: &dc->dml2_options, dml2: &state->bw_ctx.dml2)) {
210 dc_state_release(state);
211 return NULL;
212 }
213
214 if (dc->caps.dcmode_power_limits_present && !dml2_create(in_dc: dc, config: &dc->dml2_dc_power_options, dml2: &state->bw_ctx.dml2_dc_power_source)) {
215 dc_state_release(state);
216 return NULL;
217 }
218 }
219#endif
220
221 kref_init(kref: &state->refcount);
222
223 return state;
224}
225
226void dc_state_copy(struct dc_state *dst_state, struct dc_state *src_state)
227{
228 struct kref refcount = dst_state->refcount;
229#ifdef CONFIG_DRM_AMD_DC_FP
230 struct dml2_context *dst_dml2 = dst_state->bw_ctx.dml2;
231 struct dml2_context *dst_dml2_dc_power_source = dst_state->bw_ctx.dml2_dc_power_source;
232#endif
233
234 dc_state_copy_internal(dst_state, src_state);
235
236#ifdef CONFIG_DRM_AMD_DC_FP
237 dst_state->bw_ctx.dml2 = dst_dml2;
238 if (src_state->bw_ctx.dml2)
239 dml2_copy(dst_dml2: dst_state->bw_ctx.dml2, src_dml2: src_state->bw_ctx.dml2);
240
241 dst_state->bw_ctx.dml2_dc_power_source = dst_dml2_dc_power_source;
242 if (src_state->bw_ctx.dml2_dc_power_source)
243 dml2_copy(dst_dml2: dst_state->bw_ctx.dml2_dc_power_source, src_dml2: src_state->bw_ctx.dml2_dc_power_source);
244#endif
245
246 /* context refcount should not be overridden */
247 dst_state->refcount = refcount;
248}
249
250struct dc_state *dc_state_create_copy(struct dc_state *src_state)
251{
252 struct dc_state *new_state;
253
254 new_state = kvmalloc(sizeof(struct dc_state),
255 GFP_KERNEL);
256 if (!new_state)
257 return NULL;
258
259 dc_state_copy_internal(dst_state: new_state, src_state);
260
261#ifdef CONFIG_DRM_AMD_DC_FP
262 new_state->bw_ctx.dml2 = NULL;
263 new_state->bw_ctx.dml2_dc_power_source = NULL;
264
265 if (src_state->bw_ctx.dml2 &&
266 !dml2_create_copy(dst_dml2: &new_state->bw_ctx.dml2, src_dml2: src_state->bw_ctx.dml2)) {
267 dc_state_release(state: new_state);
268 return NULL;
269 }
270
271 if (src_state->bw_ctx.dml2_dc_power_source &&
272 !dml2_create_copy(dst_dml2: &new_state->bw_ctx.dml2_dc_power_source, src_dml2: src_state->bw_ctx.dml2_dc_power_source)) {
273 dc_state_release(state: new_state);
274 return NULL;
275 }
276#endif
277
278 kref_init(kref: &new_state->refcount);
279
280 return new_state;
281}
282
283void dc_state_copy_current(struct dc *dc, struct dc_state *dst_state)
284{
285 dc_state_copy(dst_state, src_state: dc->current_state);
286}
287
288struct dc_state *dc_state_create_current_copy(struct dc *dc)
289{
290 return dc_state_create_copy(src_state: dc->current_state);
291}
292
293void dc_state_construct(struct dc *dc, struct dc_state *state)
294{
295 state->clk_mgr = dc->clk_mgr;
296
297 /* Initialise DIG link encoder resource tracking variables. */
298 if (dc->res_pool)
299 link_enc_cfg_init(dc, state);
300}
301
302void dc_state_destruct(struct dc_state *state)
303{
304 int i, j;
305
306 for (i = 0; i < state->stream_count; i++) {
307 for (j = 0; j < state->stream_status[i].plane_count; j++)
308 dc_plane_state_release(
309 plane_state: state->stream_status[i].plane_states[j]);
310
311 state->stream_status[i].plane_count = 0;
312 dc_stream_release(dc_stream: state->streams[i]);
313 state->streams[i] = NULL;
314 }
315 state->stream_count = 0;
316
317 /* release tracked phantoms */
318 for (i = 0; i < state->phantom_stream_count; i++) {
319 dc_stream_release(dc_stream: state->phantom_streams[i]);
320 state->phantom_streams[i] = NULL;
321 }
322 state->phantom_stream_count = 0;
323
324 for (i = 0; i < state->phantom_plane_count; i++) {
325 dc_plane_state_release(plane_state: state->phantom_planes[i]);
326 state->phantom_planes[i] = NULL;
327 }
328 state->phantom_plane_count = 0;
329
330 state->stream_mask = 0;
331 memset(&state->res_ctx, 0, sizeof(state->res_ctx));
332 memset(&state->pp_display_cfg, 0, sizeof(state->pp_display_cfg));
333 memset(&state->dcn_bw_vars, 0, sizeof(state->dcn_bw_vars));
334 state->clk_mgr = NULL;
335 memset(&state->bw_ctx.bw, 0, sizeof(state->bw_ctx.bw));
336 memset(state->block_sequence, 0, sizeof(state->block_sequence));
337 state->block_sequence_steps = 0;
338 memset(state->dc_dmub_cmd, 0, sizeof(state->dc_dmub_cmd));
339 state->dmub_cmd_count = 0;
340 memset(&state->perf_params, 0, sizeof(state->perf_params));
341}
342
343void dc_state_retain(struct dc_state *state)
344{
345 kref_get(kref: &state->refcount);
346}
347
348static void dc_state_free(struct kref *kref)
349{
350 struct dc_state *state = container_of(kref, struct dc_state, refcount);
351
352 dc_state_destruct(state);
353
354#ifdef CONFIG_DRM_AMD_DC_FP
355 dml2_destroy(dml2: state->bw_ctx.dml2);
356 state->bw_ctx.dml2 = 0;
357
358 dml2_destroy(dml2: state->bw_ctx.dml2_dc_power_source);
359 state->bw_ctx.dml2_dc_power_source = 0;
360#endif
361
362 kvfree(addr: state);
363}
364
365void dc_state_release(struct dc_state *state)
366{
367 if (state != NULL)
368 kref_put(kref: &state->refcount, release: dc_state_free);
369}
370/*
371 * dc_state_add_stream() - Add a new dc_stream_state to a dc_state.
372 */
373enum dc_status dc_state_add_stream(
374 const struct dc *dc,
375 struct dc_state *state,
376 struct dc_stream_state *stream)
377{
378 enum dc_status res;
379
380 DC_LOGGER_INIT(dc->ctx->logger);
381
382 if (state->stream_count >= dc->res_pool->timing_generator_count) {
383 DC_LOG_WARNING("Max streams reached, can't add stream %p !\n", stream);
384 return DC_ERROR_UNEXPECTED;
385 }
386
387 state->streams[state->stream_count] = stream;
388 dc_stream_retain(dc_stream: stream);
389 state->stream_count++;
390
391 res = resource_add_otg_master_for_stream_output(
392 new_ctx: state, pool: dc->res_pool, stream);
393 if (res != DC_OK)
394 DC_LOG_WARNING("Adding stream %p to context failed with err %d!\n", stream, res);
395
396 return res;
397}
398
399/*
400 * dc_state_remove_stream() - Remove a stream from a dc_state.
401 */
402enum dc_status dc_state_remove_stream(
403 const struct dc *dc,
404 struct dc_state *state,
405 struct dc_stream_state *stream)
406{
407 int i;
408 struct pipe_ctx *del_pipe = resource_get_otg_master_for_stream(
409 res_ctx: &state->res_ctx, stream);
410
411 if (!del_pipe) {
412 dm_error("Pipe not found for stream %p !\n", stream);
413 return DC_ERROR_UNEXPECTED;
414 }
415
416 resource_update_pipes_for_stream_with_slice_count(new_ctx: state,
417 cur_ctx: dc->current_state, pool: dc->res_pool, stream, new_slice_count: 1);
418 resource_remove_otg_master_for_stream_output(
419 new_ctx: state, pool: dc->res_pool, stream);
420
421 for (i = 0; i < state->stream_count; i++)
422 if (state->streams[i] == stream)
423 break;
424
425 if (state->streams[i] != stream) {
426 dm_error("Context doesn't have stream %p !\n", stream);
427 return DC_ERROR_UNEXPECTED;
428 }
429
430 dc_stream_release_3dlut_for_stream(dc, stream);
431
432 dc_stream_release(dc_stream: state->streams[i]);
433 state->stream_count--;
434
435 /* Trim back arrays */
436 for (; i < state->stream_count; i++) {
437 state->streams[i] = state->streams[i + 1];
438 state->stream_status[i] = state->stream_status[i + 1];
439 }
440
441 state->streams[state->stream_count] = NULL;
442 memset(
443 &state->stream_status[state->stream_count],
444 0,
445 sizeof(state->stream_status[0]));
446
447 return DC_OK;
448}
449
450static void remove_mpc_combine_for_stream(const struct dc *dc,
451 struct dc_state *new_ctx,
452 const struct dc_state *cur_ctx,
453 struct dc_stream_status *status)
454{
455 int i;
456
457 for (i = 0; i < status->plane_count; i++)
458 resource_update_pipes_for_plane_with_slice_count(
459 new_ctx, cur_ctx, pool: dc->res_pool,
460 plane: status->plane_states[i], slice_count: 1);
461}
462
463bool dc_state_add_plane(
464 const struct dc *dc,
465 struct dc_stream_state *stream,
466 struct dc_plane_state *plane_state,
467 struct dc_state *state)
468{
469 struct resource_pool *pool = dc->res_pool;
470 struct pipe_ctx *otg_master_pipe;
471 struct dc_stream_status *stream_status = NULL;
472 bool added = false;
473 int odm_slice_count;
474 int i;
475
476 stream_status = dc_state_get_stream_status(state, stream);
477 otg_master_pipe = resource_get_otg_master_for_stream(
478 res_ctx: &state->res_ctx, stream);
479 if (stream_status == NULL) {
480 dm_error("Existing stream not found; failed to attach surface!\n");
481 goto out;
482 } else if (stream_status->plane_count == MAX_SURFACES) {
483 dm_error("Surface: can not attach plane_state %p! Maximum is: %d\n",
484 plane_state, MAX_SURFACES);
485 goto out;
486 } else if (!otg_master_pipe) {
487 goto out;
488 }
489
490 added = resource_append_dpp_pipes_for_plane_composition(new_ctx: state,
491 cur_ctx: dc->current_state, pool, otg_master_pipe, plane_state);
492
493 if (!added) {
494 /* try to remove MPC combine to free up pipes */
495 for (i = 0; i < state->stream_count; i++)
496 remove_mpc_combine_for_stream(dc, new_ctx: state,
497 cur_ctx: dc->current_state,
498 status: &state->stream_status[i]);
499 added = resource_append_dpp_pipes_for_plane_composition(new_ctx: state,
500 cur_ctx: dc->current_state, pool,
501 otg_master_pipe, plane_state);
502 }
503
504 if (!added) {
505 /* try to decrease ODM slice count gradually to free up pipes */
506 odm_slice_count = resource_get_odm_slice_count(pipe: otg_master_pipe);
507 for (i = odm_slice_count - 1; i > 0; i--) {
508 resource_update_pipes_for_stream_with_slice_count(new_ctx: state,
509 cur_ctx: dc->current_state, pool: dc->res_pool, stream,
510 new_slice_count: i);
511 added = resource_append_dpp_pipes_for_plane_composition(
512 new_ctx: state,
513 cur_ctx: dc->current_state, pool,
514 otg_master_pipe, plane_state);
515 if (added)
516 break;
517 }
518 }
519
520 if (added) {
521 stream_status->plane_states[stream_status->plane_count] =
522 plane_state;
523 stream_status->plane_count++;
524 dc_plane_state_retain(plane_state);
525 }
526
527out:
528 return added;
529}
530
531bool dc_state_remove_plane(
532 const struct dc *dc,
533 struct dc_stream_state *stream,
534 struct dc_plane_state *plane_state,
535 struct dc_state *state)
536{
537 int i;
538 struct dc_stream_status *stream_status = NULL;
539 struct resource_pool *pool = dc->res_pool;
540
541 if (!plane_state)
542 return true;
543
544 for (i = 0; i < state->stream_count; i++)
545 if (state->streams[i] == stream) {
546 stream_status = &state->stream_status[i];
547 break;
548 }
549
550 if (stream_status == NULL) {
551 dm_error("Existing stream not found; failed to remove plane.\n");
552 return false;
553 }
554
555 resource_remove_dpp_pipes_for_plane_composition(
556 context: state, pool, plane_state);
557
558 for (i = 0; i < stream_status->plane_count; i++) {
559 if (stream_status->plane_states[i] == plane_state) {
560 dc_plane_state_release(plane_state: stream_status->plane_states[i]);
561 break;
562 }
563 }
564
565 if (i == stream_status->plane_count) {
566 dm_error("Existing plane_state not found; failed to detach it!\n");
567 return false;
568 }
569
570 stream_status->plane_count--;
571
572 /* Start at the plane we've just released, and move all the planes one index forward to "trim" the array */
573 for (; i < stream_status->plane_count; i++)
574 stream_status->plane_states[i] = stream_status->plane_states[i + 1];
575
576 stream_status->plane_states[stream_status->plane_count] = NULL;
577
578 return true;
579}
580
581/**
582 * dc_state_rem_all_planes_for_stream - Remove planes attached to the target stream.
583 *
584 * @dc: Current dc state.
585 * @stream: Target stream, which we want to remove the attached plans.
586 * @state: context from which the planes are to be removed.
587 *
588 * Return:
589 * Return true if DC was able to remove all planes from the target
590 * stream, otherwise, return false.
591 */
592bool dc_state_rem_all_planes_for_stream(
593 const struct dc *dc,
594 struct dc_stream_state *stream,
595 struct dc_state *state)
596{
597 int i, old_plane_count;
598 struct dc_stream_status *stream_status = NULL;
599 struct dc_plane_state *del_planes[MAX_SURFACES] = { 0 };
600
601 for (i = 0; i < state->stream_count; i++)
602 if (state->streams[i] == stream) {
603 stream_status = &state->stream_status[i];
604 break;
605 }
606
607 if (stream_status == NULL) {
608 dm_error("Existing stream %p not found!\n", stream);
609 return false;
610 }
611
612 old_plane_count = stream_status->plane_count;
613
614 for (i = 0; i < old_plane_count; i++)
615 del_planes[i] = stream_status->plane_states[i];
616
617 for (i = 0; i < old_plane_count; i++)
618 if (!dc_state_remove_plane(dc, stream, plane_state: del_planes[i], state))
619 return false;
620
621 return true;
622}
623
624bool dc_state_add_all_planes_for_stream(
625 const struct dc *dc,
626 struct dc_stream_state *stream,
627 struct dc_plane_state * const *plane_states,
628 int plane_count,
629 struct dc_state *state)
630{
631 int i;
632 bool result = true;
633
634 for (i = 0; i < plane_count; i++)
635 if (!dc_state_add_plane(dc, stream, plane_state: plane_states[i], state)) {
636 result = false;
637 break;
638 }
639
640 return result;
641}
642
643/* Private dc_state functions */
644
645/**
646 * dc_state_get_stream_status - Get stream status from given dc state
647 * @state: DC state to find the stream status in
648 * @stream: The stream to get the stream status for
649 *
650 * The given stream is expected to exist in the given dc state. Otherwise, NULL
651 * will be returned.
652 */
653struct dc_stream_status *dc_state_get_stream_status(
654 struct dc_state *state,
655 const struct dc_stream_state *stream)
656{
657 uint8_t i;
658
659 if (state == NULL)
660 return NULL;
661
662 for (i = 0; i < state->stream_count; i++) {
663 if (stream == state->streams[i])
664 return &state->stream_status[i];
665 }
666
667 return NULL;
668}
669
670enum mall_stream_type dc_state_get_pipe_subvp_type(const struct dc_state *state,
671 const struct pipe_ctx *pipe_ctx)
672{
673 return dc_state_get_stream_subvp_type(state, stream: pipe_ctx->stream);
674}
675
676enum mall_stream_type dc_state_get_stream_subvp_type(const struct dc_state *state,
677 const struct dc_stream_state *stream)
678{
679 int i;
680
681 enum mall_stream_type type = SUBVP_NONE;
682
683 for (i = 0; i < state->stream_count; i++) {
684 if (state->streams[i] == stream) {
685 type = state->stream_status[i].mall_stream_config.type;
686 break;
687 }
688 }
689
690 return type;
691}
692
693struct dc_stream_state *dc_state_get_paired_subvp_stream(const struct dc_state *state,
694 const struct dc_stream_state *stream)
695{
696 int i;
697
698 struct dc_stream_state *paired_stream = NULL;
699
700 for (i = 0; i < state->stream_count; i++) {
701 if (state->streams[i] == stream) {
702 paired_stream = state->stream_status[i].mall_stream_config.paired_stream;
703 break;
704 }
705 }
706
707 return paired_stream;
708}
709
710struct dc_stream_state *dc_state_create_phantom_stream(const struct dc *dc,
711 struct dc_state *state,
712 struct dc_stream_state *main_stream)
713{
714 struct dc_stream_state *phantom_stream;
715
716 DC_LOGGER_INIT(dc->ctx->logger);
717
718 phantom_stream = dc_create_stream_for_sink(dc_sink: main_stream->sink);
719
720 if (!phantom_stream) {
721 DC_LOG_ERROR("Failed to allocate phantom stream.\n");
722 return NULL;
723 }
724
725 /* track phantom stream in dc_state */
726 dc_state_track_phantom_stream(state, phantom_stream);
727
728 phantom_stream->is_phantom = true;
729 phantom_stream->signal = SIGNAL_TYPE_VIRTUAL;
730 phantom_stream->dpms_off = true;
731
732 return phantom_stream;
733}
734
735void dc_state_release_phantom_stream(const struct dc *dc,
736 struct dc_state *state,
737 struct dc_stream_state *phantom_stream)
738{
739 DC_LOGGER_INIT(dc->ctx->logger);
740
741 if (!dc_state_untrack_phantom_stream(state, phantom_stream)) {
742 DC_LOG_ERROR("Failed to free phantom stream %p in dc state %p.\n", phantom_stream, state);
743 return;
744 }
745
746 dc_stream_release(dc_stream: phantom_stream);
747}
748
749struct dc_plane_state *dc_state_create_phantom_plane(const struct dc *dc,
750 struct dc_state *state,
751 struct dc_plane_state *main_plane)
752{
753 struct dc_plane_state *phantom_plane = dc_create_plane_state(dc);
754
755 DC_LOGGER_INIT(dc->ctx->logger);
756
757 if (!phantom_plane) {
758 DC_LOG_ERROR("Failed to allocate phantom plane.\n");
759 return NULL;
760 }
761
762 /* track phantom inside dc_state */
763 dc_state_track_phantom_plane(state, phantom_plane);
764
765 phantom_plane->is_phantom = true;
766
767 return phantom_plane;
768}
769
770void dc_state_release_phantom_plane(const struct dc *dc,
771 struct dc_state *state,
772 struct dc_plane_state *phantom_plane)
773{
774 DC_LOGGER_INIT(dc->ctx->logger);
775
776 if (!dc_state_untrack_phantom_plane(state, phantom_plane)) {
777 DC_LOG_ERROR("Failed to free phantom plane %p in dc state %p.\n", phantom_plane, state);
778 return;
779 }
780
781 dc_plane_state_release(plane_state: phantom_plane);
782}
783
784/* add phantom streams to context and generate correct meta inside dc_state */
785enum dc_status dc_state_add_phantom_stream(const struct dc *dc,
786 struct dc_state *state,
787 struct dc_stream_state *phantom_stream,
788 struct dc_stream_state *main_stream)
789{
790 struct dc_stream_status *main_stream_status;
791 struct dc_stream_status *phantom_stream_status;
792 enum dc_status res = dc_state_add_stream(dc, state, stream: phantom_stream);
793
794 /* check if stream is tracked */
795 if (res == DC_OK && !dc_state_is_phantom_stream_tracked(state, phantom_stream)) {
796 /* stream must be tracked if added to state */
797 dc_state_track_phantom_stream(state, phantom_stream);
798 }
799
800 /* setup subvp meta */
801 main_stream_status = dc_state_get_stream_status(state, stream: main_stream);
802 if (main_stream_status) {
803 main_stream_status->mall_stream_config.type = SUBVP_MAIN;
804 main_stream_status->mall_stream_config.paired_stream = phantom_stream;
805 }
806
807 phantom_stream_status = dc_state_get_stream_status(state, stream: phantom_stream);
808 if (phantom_stream_status) {
809 phantom_stream_status->mall_stream_config.type = SUBVP_PHANTOM;
810 phantom_stream_status->mall_stream_config.paired_stream = main_stream;
811 phantom_stream_status->mall_stream_config.subvp_limit_cursor_size = false;
812 phantom_stream_status->mall_stream_config.cursor_size_limit_subvp = false;
813 }
814
815 dc_state_set_stream_subvp_cursor_limit(stream: main_stream, state, limit: true);
816
817 return res;
818}
819
820enum dc_status dc_state_remove_phantom_stream(const struct dc *dc,
821 struct dc_state *state,
822 struct dc_stream_state *phantom_stream)
823{
824 struct dc_stream_status *main_stream_status = NULL;
825 struct dc_stream_status *phantom_stream_status;
826
827 /* reset subvp meta */
828 phantom_stream_status = dc_state_get_stream_status(state, stream: phantom_stream);
829 if (phantom_stream_status) {
830 main_stream_status = dc_state_get_stream_status(state, stream: phantom_stream_status->mall_stream_config.paired_stream);
831 phantom_stream_status->mall_stream_config.type = SUBVP_NONE;
832 phantom_stream_status->mall_stream_config.paired_stream = NULL;
833 }
834
835 if (main_stream_status) {
836 main_stream_status->mall_stream_config.type = SUBVP_NONE;
837 main_stream_status->mall_stream_config.paired_stream = NULL;
838 }
839
840 /* remove stream from state */
841 return dc_state_remove_stream(dc, state, stream: phantom_stream);
842}
843
844bool dc_state_add_phantom_plane(
845 const struct dc *dc,
846 struct dc_stream_state *phantom_stream,
847 struct dc_plane_state *phantom_plane,
848 struct dc_state *state)
849{
850 bool res = dc_state_add_plane(dc, stream: phantom_stream, plane_state: phantom_plane, state);
851
852 /* check if stream is tracked */
853 if (res && !dc_state_is_phantom_plane_tracked(state, phantom_plane)) {
854 /* stream must be tracked if added to state */
855 dc_state_track_phantom_plane(state, phantom_plane);
856 }
857
858 return res;
859}
860
861bool dc_state_remove_phantom_plane(
862 const struct dc *dc,
863 struct dc_stream_state *phantom_stream,
864 struct dc_plane_state *phantom_plane,
865 struct dc_state *state)
866{
867 return dc_state_remove_plane(dc, stream: phantom_stream, plane_state: phantom_plane, state);
868}
869
870bool dc_state_rem_all_phantom_planes_for_stream(
871 const struct dc *dc,
872 struct dc_stream_state *phantom_stream,
873 struct dc_state *state,
874 bool should_release_planes)
875{
876 int i, old_plane_count;
877 struct dc_stream_status *stream_status = NULL;
878 struct dc_plane_state *del_planes[MAX_SURFACES] = { 0 };
879
880 for (i = 0; i < state->stream_count; i++)
881 if (state->streams[i] == phantom_stream) {
882 stream_status = &state->stream_status[i];
883 break;
884 }
885
886 if (stream_status == NULL) {
887 dm_error("Existing stream %p not found!\n", phantom_stream);
888 return false;
889 }
890
891 old_plane_count = stream_status->plane_count;
892
893 for (i = 0; i < old_plane_count; i++)
894 del_planes[i] = stream_status->plane_states[i];
895
896 for (i = 0; i < old_plane_count; i++) {
897 if (!dc_state_remove_plane(dc, stream: phantom_stream, plane_state: del_planes[i], state))
898 return false;
899 if (should_release_planes)
900 dc_state_release_phantom_plane(dc, state, phantom_plane: del_planes[i]);
901 }
902
903 return true;
904}
905
906bool dc_state_add_all_phantom_planes_for_stream(
907 const struct dc *dc,
908 struct dc_stream_state *phantom_stream,
909 struct dc_plane_state * const *phantom_planes,
910 int plane_count,
911 struct dc_state *state)
912{
913 return dc_state_add_all_planes_for_stream(dc, stream: phantom_stream, plane_states: phantom_planes, plane_count, state);
914}
915
916bool dc_state_remove_phantom_streams_and_planes(
917 const struct dc *dc,
918 struct dc_state *state)
919{
920 int i;
921 bool removed_phantom = false;
922 struct dc_stream_state *phantom_stream = NULL;
923
924 for (i = 0; i < dc->res_pool->pipe_count; i++) {
925 struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
926
927 if (pipe->plane_state && pipe->stream && dc_state_get_pipe_subvp_type(state, pipe_ctx: pipe) == SUBVP_PHANTOM) {
928 phantom_stream = pipe->stream;
929
930 dc_state_rem_all_phantom_planes_for_stream(dc, phantom_stream, state, should_release_planes: false);
931 dc_state_remove_phantom_stream(dc, state, phantom_stream);
932 removed_phantom = true;
933 }
934 }
935 return removed_phantom;
936}
937
938void dc_state_release_phantom_streams_and_planes(
939 const struct dc *dc,
940 struct dc_state *state)
941{
942 unsigned int phantom_count;
943 struct dc_stream_state *phantom_streams[MAX_PHANTOM_PIPES];
944 struct dc_plane_state *phantom_planes[MAX_PHANTOM_PIPES];
945 int i;
946
947 phantom_count = state->phantom_stream_count;
948 memcpy(phantom_streams, state->phantom_streams, sizeof(struct dc_stream_state *) * MAX_PHANTOM_PIPES);
949 for (i = 0; i < phantom_count; i++)
950 dc_state_release_phantom_stream(dc, state, phantom_stream: phantom_streams[i]);
951
952 phantom_count = state->phantom_plane_count;
953 memcpy(phantom_planes, state->phantom_planes, sizeof(struct dc_plane_state *) * MAX_PHANTOM_PIPES);
954 for (i = 0; i < phantom_count; i++)
955 dc_state_release_phantom_plane(dc, state, phantom_plane: phantom_planes[i]);
956}
957
958struct dc_stream_state *dc_state_get_stream_from_id(const struct dc_state *state, unsigned int id)
959{
960 struct dc_stream_state *stream = NULL;
961 int i;
962
963 for (i = 0; i < state->stream_count; i++) {
964 if (state->streams[i] && state->streams[i]->stream_id == id) {
965 stream = state->streams[i];
966 break;
967 }
968 }
969
970 return stream;
971}
972
973bool dc_state_is_fams2_in_use(
974 const struct dc *dc,
975 const struct dc_state *state)
976{
977 bool is_fams2_in_use = false;
978
979 if (state)
980 is_fams2_in_use |= state->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable;
981
982 if (dc->current_state)
983 is_fams2_in_use |= dc->current_state->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable;
984
985 return is_fams2_in_use;
986}
987
988void dc_state_set_stream_subvp_cursor_limit(const struct dc_stream_state *stream,
989 struct dc_state *state,
990 bool limit)
991{
992 struct dc_stream_status *stream_status;
993
994 stream_status = dc_state_get_stream_status(state, stream);
995
996 if (stream_status) {
997 stream_status->mall_stream_config.subvp_limit_cursor_size = limit;
998 }
999}
1000
1001bool dc_state_get_stream_subvp_cursor_limit(const struct dc_stream_state *stream,
1002 struct dc_state *state)
1003{
1004 bool limit = false;
1005
1006 struct dc_stream_status *stream_status;
1007
1008 stream_status = dc_state_get_stream_status(state, stream);
1009
1010 if (stream_status) {
1011 limit = stream_status->mall_stream_config.subvp_limit_cursor_size;
1012 }
1013
1014 return limit;
1015}
1016
1017void dc_state_set_stream_cursor_subvp_limit(const struct dc_stream_state *stream,
1018 struct dc_state *state,
1019 bool limit)
1020{
1021 struct dc_stream_status *stream_status;
1022
1023 stream_status = dc_state_get_stream_status(state, stream);
1024
1025 if (stream_status) {
1026 stream_status->mall_stream_config.cursor_size_limit_subvp = limit;
1027 }
1028}
1029
1030bool dc_state_get_stream_cursor_subvp_limit(const struct dc_stream_state *stream,
1031 struct dc_state *state)
1032{
1033 bool limit = false;
1034
1035 struct dc_stream_status *stream_status;
1036
1037 stream_status = dc_state_get_stream_status(state, stream);
1038
1039 if (stream_status) {
1040 limit = stream_status->mall_stream_config.cursor_size_limit_subvp;
1041 }
1042
1043 return limit;
1044}
1045
1046bool dc_state_can_clear_stream_cursor_subvp_limit(const struct dc_stream_state *stream,
1047 struct dc_state *state)
1048{
1049 bool can_clear_limit = false;
1050
1051 struct dc_stream_status *stream_status;
1052
1053 stream_status = dc_state_get_stream_status(state, stream);
1054
1055 if (stream_status) {
1056 can_clear_limit = dc_state_get_stream_cursor_subvp_limit(stream, state) &&
1057 (stream_status->mall_stream_config.type == SUBVP_PHANTOM ||
1058 stream->hw_cursor_req ||
1059 !stream_status->mall_stream_config.subvp_limit_cursor_size ||
1060 !stream->cursor_position.enable ||
1061 dc_stream_check_cursor_attributes(stream, state, attributes: &stream->cursor_attributes));
1062 }
1063
1064 return can_clear_limit;
1065}
1066
1067bool dc_state_is_subvp_in_use(struct dc_state *state)
1068{
1069 uint32_t i;
1070
1071 for (i = 0; i < state->stream_count; i++) {
1072 if (dc_state_get_stream_subvp_type(state, stream: state->streams[i]) != SUBVP_NONE)
1073 return true;
1074 }
1075
1076 return false;
1077}
1078

source code of linux/drivers/gpu/drm/amd/display/dc/core/dc_state.c