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