1/* Copyright 2021 Advanced Micro Devices, Inc. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a
4 * copy of this software and associated documentation files (the "Software"),
5 * to deal in the Software without restriction, including without limitation
6 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 * and/or sell copies of the Software, and to permit persons to whom the
8 * Software is furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
16 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
17 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19 * OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Authors: AMD
22 *
23 */
24
25#include "link_enc_cfg.h"
26#include "resource.h"
27#include "link.h"
28
29#define DC_LOGGER dc->ctx->logger
30
31/* Check whether stream is supported by DIG link encoders. */
32static bool is_dig_link_enc_stream(struct dc_stream_state *stream)
33{
34	bool is_dig_stream = false;
35	struct link_encoder *link_enc = NULL;
36	int i;
37
38	/* Loop over created link encoder objects. */
39	if (stream) {
40		for (i = 0; i < stream->ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) {
41			link_enc = stream->ctx->dc->res_pool->link_encoders[i];
42
43			/* Need to check link signal type rather than stream signal type which may not
44			 * yet match.
45			 */
46			if (link_enc && ((uint32_t)stream->link->connector_signal & link_enc->output_signals)) {
47				if (dc_is_dp_signal(stream->signal)) {
48					/* DIGs do not support DP2.0 streams with 128b/132b encoding. */
49					struct dc_link_settings link_settings = {0};
50
51					stream->ctx->dc->link_srv->dp_decide_link_settings(stream, &link_settings);
52					if ((link_settings.link_rate >= LINK_RATE_LOW) &&
53							link_settings.link_rate <= LINK_RATE_HIGH3) {
54						is_dig_stream = true;
55						break;
56					}
57				} else {
58					is_dig_stream = true;
59					break;
60				}
61			}
62		}
63	}
64	return is_dig_stream;
65}
66
67static struct link_enc_assignment get_assignment(struct dc *dc, int i)
68{
69	struct link_enc_assignment assignment;
70
71	if (dc->current_state->res_ctx.link_enc_cfg_ctx.mode == LINK_ENC_CFG_TRANSIENT)
72		assignment = dc->current_state->res_ctx.link_enc_cfg_ctx.transient_assignments[i];
73	else /* LINK_ENC_CFG_STEADY */
74		assignment = dc->current_state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
75
76	return assignment;
77}
78
79/* Return stream using DIG link encoder resource. NULL if unused. */
80static struct dc_stream_state *get_stream_using_link_enc(
81		struct dc_state *state,
82		enum engine_id eng_id)
83{
84	struct dc_stream_state *stream = NULL;
85	int i;
86
87	for (i = 0; i < state->stream_count; i++) {
88		struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
89
90		if ((assignment.valid == true) && (assignment.eng_id == eng_id)) {
91			stream = state->streams[i];
92			break;
93		}
94	}
95
96	return stream;
97}
98
99static void remove_link_enc_assignment(
100		struct dc_state *state,
101		struct dc_stream_state *stream,
102		enum engine_id eng_id)
103{
104	int eng_idx;
105	int i;
106
107	if (eng_id != ENGINE_ID_UNKNOWN) {
108		eng_idx = eng_id - ENGINE_ID_DIGA;
109
110		/* stream ptr of stream in dc_state used to update correct entry in
111		 * link_enc_assignments table.
112		 */
113		for (i = 0; i < MAX_PIPES; i++) {
114			struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
115
116			if (assignment.valid && assignment.stream == stream) {
117				state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].valid = false;
118				/* Only add link encoder back to availability pool if not being
119				 * used by any other stream (i.e. removing SST stream or last MST stream).
120				 */
121				if (get_stream_using_link_enc(state, eng_id) == NULL)
122					state->res_ctx.link_enc_cfg_ctx.link_enc_avail[eng_idx] = eng_id;
123
124				stream->link_enc = NULL;
125				state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].eng_id = ENGINE_ID_UNKNOWN;
126				state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].stream = NULL;
127				dc_stream_release(stream);
128				break;
129			}
130		}
131	}
132}
133
134static void add_link_enc_assignment(
135		struct dc_state *state,
136		struct dc_stream_state *stream,
137		enum engine_id eng_id)
138{
139	int eng_idx;
140	int i;
141
142	if (eng_id != ENGINE_ID_UNKNOWN) {
143		eng_idx = eng_id - ENGINE_ID_DIGA;
144
145		/* stream ptr of stream in dc_state used to update correct entry in
146		 * link_enc_assignments table.
147		 */
148		for (i = 0; i < state->stream_count; i++) {
149			if (stream == state->streams[i]) {
150				state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i] = (struct link_enc_assignment){
151					.valid = true,
152					.ep_id = (struct display_endpoint_id) {
153						.link_id = stream->link->link_id,
154						.ep_type = stream->link->ep_type},
155					.eng_id = eng_id,
156					.stream = stream};
157				dc_stream_retain(stream);
158				state->res_ctx.link_enc_cfg_ctx.link_enc_avail[eng_idx] = ENGINE_ID_UNKNOWN;
159				stream->link_enc = stream->ctx->dc->res_pool->link_encoders[eng_idx];
160				break;
161			}
162		}
163
164		/* Attempted to add an encoder assignment for a stream not in dc_state. */
165		ASSERT(i != state->stream_count);
166	}
167}
168
169/* Return first available DIG link encoder. */
170static enum engine_id find_first_avail_link_enc(
171		const struct dc_context *ctx,
172		const struct dc_state *state,
173		enum engine_id eng_id_requested)
174{
175	enum engine_id eng_id = ENGINE_ID_UNKNOWN;
176	int i;
177
178	if (eng_id_requested != ENGINE_ID_UNKNOWN) {
179
180		for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) {
181			eng_id = state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i];
182			if (eng_id == eng_id_requested)
183				return eng_id;
184		}
185	}
186
187	eng_id = ENGINE_ID_UNKNOWN;
188
189	for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) {
190		eng_id = state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i];
191		if (eng_id != ENGINE_ID_UNKNOWN)
192			break;
193	}
194
195	return eng_id;
196}
197
198/* Check for availability of link encoder eng_id. */
199static bool is_avail_link_enc(struct dc_state *state, enum engine_id eng_id, struct dc_stream_state *stream)
200{
201	bool is_avail = false;
202	int eng_idx = eng_id - ENGINE_ID_DIGA;
203
204	/* An encoder is available if it is still in the availability pool. */
205	if (eng_id != ENGINE_ID_UNKNOWN && state->res_ctx.link_enc_cfg_ctx.link_enc_avail[eng_idx] != ENGINE_ID_UNKNOWN) {
206		is_avail = true;
207	} else {
208		struct dc_stream_state *stream_assigned = NULL;
209
210		/* MST streams share the same link and should share the same encoder.
211		 * If a stream that has already been assigned a link encoder uses as the
212		 * same link as the stream checking for availability, it is an MST stream
213		 * and should use the same link encoder.
214		 */
215		stream_assigned = get_stream_using_link_enc(state, eng_id);
216		if (stream_assigned && stream != stream_assigned && stream->link == stream_assigned->link)
217			is_avail = true;
218	}
219
220	return is_avail;
221}
222
223/* Test for display_endpoint_id equality. */
224static bool are_ep_ids_equal(struct display_endpoint_id *lhs, struct display_endpoint_id *rhs)
225{
226	bool are_equal = false;
227
228	if (lhs->link_id.id == rhs->link_id.id &&
229			lhs->link_id.enum_id == rhs->link_id.enum_id &&
230			lhs->link_id.type == rhs->link_id.type &&
231			lhs->ep_type == rhs->ep_type)
232		are_equal = true;
233
234	return are_equal;
235}
236
237static struct link_encoder *get_link_enc_used_by_link(
238		struct dc_state *state,
239		const struct dc_link *link)
240{
241	struct link_encoder *link_enc = NULL;
242	struct display_endpoint_id ep_id;
243	int i;
244
245	ep_id = (struct display_endpoint_id) {
246		.link_id = link->link_id,
247		.ep_type = link->ep_type};
248
249	for (i = 0; i < MAX_PIPES; i++) {
250		struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
251
252		if (assignment.valid == true && are_ep_ids_equal(&assignment.ep_id, &ep_id))
253			link_enc = link->dc->res_pool->link_encoders[assignment.eng_id - ENGINE_ID_DIGA];
254	}
255
256	return link_enc;
257}
258/* Clear all link encoder assignments. */
259static void clear_enc_assignments(const struct dc *dc, struct dc_state *state)
260{
261	int i;
262
263	for (i = 0; i < MAX_PIPES; i++) {
264		state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].valid = false;
265		state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].eng_id = ENGINE_ID_UNKNOWN;
266		if (state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].stream != NULL) {
267			dc_stream_release(state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].stream);
268			state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].stream = NULL;
269		}
270	}
271
272	for (i = 0; i < dc->res_pool->res_cap->num_dig_link_enc; i++) {
273		if (dc->res_pool->link_encoders[i])
274			state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i] = (enum engine_id) i;
275		else
276			state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i] = ENGINE_ID_UNKNOWN;
277	}
278}
279
280void link_enc_cfg_init(
281		const struct dc *dc,
282		struct dc_state *state)
283{
284	clear_enc_assignments(dc, state);
285
286	state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_STEADY;
287}
288
289void link_enc_cfg_copy(const struct dc_state *src_ctx, struct dc_state *dst_ctx)
290{
291	memcpy(&dst_ctx->res_ctx.link_enc_cfg_ctx,
292	       &src_ctx->res_ctx.link_enc_cfg_ctx,
293	       sizeof(dst_ctx->res_ctx.link_enc_cfg_ctx));
294}
295
296void link_enc_cfg_link_encs_assign(
297		struct dc *dc,
298		struct dc_state *state,
299		struct dc_stream_state *streams[],
300		uint8_t stream_count)
301{
302	enum engine_id eng_id = ENGINE_ID_UNKNOWN, eng_id_req = ENGINE_ID_UNKNOWN;
303	int i;
304	int j;
305
306	ASSERT(state->stream_count == stream_count);
307	ASSERT(dc->current_state->res_ctx.link_enc_cfg_ctx.mode == LINK_ENC_CFG_STEADY);
308
309	/* Release DIG link encoder resources before running assignment algorithm. */
310	for (i = 0; i < dc->current_state->stream_count; i++)
311		dc->res_pool->funcs->link_enc_unassign(state, dc->current_state->streams[i]);
312
313	for (i = 0; i < MAX_PIPES; i++)
314		ASSERT(state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].valid == false);
315
316	/* (a) Assign DIG link encoders to physical (unmappable) endpoints first. */
317	for (i = 0; i < stream_count; i++) {
318		struct dc_stream_state *stream = streams[i];
319
320		/* skip it if the link is mappable endpoint. */
321		if (stream->link->is_dig_mapping_flexible)
322			continue;
323
324		/* Skip stream if not supported by DIG link encoder. */
325		if (!is_dig_link_enc_stream(stream))
326			continue;
327
328		/* Physical endpoints have a fixed mapping to DIG link encoders. */
329		eng_id = stream->link->eng_id;
330		add_link_enc_assignment(state, stream, eng_id);
331	}
332
333	/* (b) Retain previous assignments for mappable endpoints if encoders still available. */
334	eng_id = ENGINE_ID_UNKNOWN;
335
336	if (state != dc->current_state) {
337		struct dc_state *prev_state = dc->current_state;
338
339		for (i = 0; i < stream_count; i++) {
340			struct dc_stream_state *stream = state->streams[i];
341
342			/* Skip it if the link is NOT mappable endpoint. */
343			if (!stream->link->is_dig_mapping_flexible)
344				continue;
345
346			/* Skip stream if not supported by DIG link encoder. */
347			if (!is_dig_link_enc_stream(stream))
348				continue;
349
350			for (j = 0; j < prev_state->stream_count; j++) {
351				struct dc_stream_state *prev_stream = prev_state->streams[j];
352
353				if (stream == prev_stream && stream->link == prev_stream->link &&
354						prev_state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[j].valid) {
355					eng_id = prev_state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[j].eng_id;
356
357					if (is_avail_link_enc(state, eng_id, stream))
358						add_link_enc_assignment(state, stream, eng_id);
359				}
360			}
361		}
362	}
363
364	/* (c) Then assign encoders to remaining mappable endpoints. */
365	eng_id = ENGINE_ID_UNKNOWN;
366
367	for (i = 0; i < stream_count; i++) {
368		struct dc_stream_state *stream = streams[i];
369		struct link_encoder *link_enc = NULL;
370
371		/* Skip it if the link is NOT mappable endpoint. */
372		if (!stream->link->is_dig_mapping_flexible)
373			continue;
374
375		/* Skip if encoder assignment retained in step (b) above. */
376		if (stream->link_enc)
377			continue;
378
379		/* Skip stream if not supported by DIG link encoder. */
380		if (!is_dig_link_enc_stream(stream)) {
381			ASSERT(stream->link->is_dig_mapping_flexible != true);
382			continue;
383		}
384
385		/* Mappable endpoints have a flexible mapping to DIG link encoders. */
386
387		/* For MST, multiple streams will share the same link / display
388		 * endpoint. These streams should use the same link encoder
389		 * assigned to that endpoint.
390		 */
391		link_enc = get_link_enc_used_by_link(state, stream->link);
392		if (link_enc == NULL) {
393
394			if (stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
395					stream->link->dpia_preferred_eng_id != ENGINE_ID_UNKNOWN)
396				eng_id_req = stream->link->dpia_preferred_eng_id;
397
398			eng_id = find_first_avail_link_enc(stream->ctx, state, eng_id_req);
399		}
400		else
401			eng_id =  link_enc->preferred_engine;
402
403		add_link_enc_assignment(state, stream, eng_id);
404	}
405
406	link_enc_cfg_validate(dc, state);
407
408	/* Update transient assignments. */
409	for (i = 0; i < MAX_PIPES; i++) {
410		dc->current_state->res_ctx.link_enc_cfg_ctx.transient_assignments[i] =
411			state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
412	}
413
414	/* Log encoder assignments. */
415	for (i = 0; i < MAX_PIPES; i++) {
416		struct link_enc_assignment assignment =
417				dc->current_state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
418
419		if (assignment.valid)
420			DC_LOG_DEBUG("%s: CUR %s(%d) - enc_id(%d)\n",
421					__func__,
422					assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? "PHY" : "DPIA",
423					assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ?
424							assignment.ep_id.link_id.enum_id :
425							assignment.ep_id.link_id.enum_id - 1,
426					assignment.eng_id);
427	}
428	for (i = 0; i < MAX_PIPES; i++) {
429		struct link_enc_assignment assignment =
430				state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
431
432		if (assignment.valid)
433			DC_LOG_DEBUG("%s: NEW %s(%d) - enc_id(%d)\n",
434					__func__,
435					assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? "PHY" : "DPIA",
436					assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ?
437							assignment.ep_id.link_id.enum_id :
438							assignment.ep_id.link_id.enum_id - 1,
439					assignment.eng_id);
440	}
441
442	/* Current state mode will be set to steady once this state committed. */
443	state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_STEADY;
444}
445
446void link_enc_cfg_link_enc_unassign(
447		struct dc_state *state,
448		struct dc_stream_state *stream)
449{
450	enum engine_id eng_id = ENGINE_ID_UNKNOWN;
451
452	if (stream->link_enc)
453		eng_id = stream->link_enc->preferred_engine;
454
455	remove_link_enc_assignment(state, stream, eng_id);
456}
457
458bool link_enc_cfg_is_transmitter_mappable(
459		struct dc *dc,
460		struct link_encoder *link_enc)
461{
462	bool is_mappable = false;
463	enum engine_id eng_id = link_enc->preferred_engine;
464	struct dc_stream_state *stream = link_enc_cfg_get_stream_using_link_enc(dc, eng_id);
465
466	if (stream)
467		is_mappable = stream->link->is_dig_mapping_flexible;
468
469	return is_mappable;
470}
471
472struct dc_stream_state *link_enc_cfg_get_stream_using_link_enc(
473		struct dc *dc,
474		enum engine_id eng_id)
475{
476	struct dc_stream_state *stream = NULL;
477	int i;
478
479	for (i = 0; i < MAX_PIPES; i++) {
480		struct link_enc_assignment assignment = get_assignment(dc, i);
481
482		if ((assignment.valid == true) && (assignment.eng_id == eng_id)) {
483			stream = assignment.stream;
484			break;
485		}
486	}
487
488	return stream;
489}
490
491struct dc_link *link_enc_cfg_get_link_using_link_enc(
492		struct dc *dc,
493		enum engine_id eng_id)
494{
495	struct dc_link *link = NULL;
496	struct dc_stream_state *stream = NULL;
497
498	stream = link_enc_cfg_get_stream_using_link_enc(dc, eng_id);
499
500	if (stream)
501		link = stream->link;
502
503	return link;
504}
505
506struct link_encoder *link_enc_cfg_get_link_enc_used_by_link(
507		struct dc *dc,
508		const struct dc_link *link)
509{
510	struct link_encoder *link_enc = NULL;
511	struct display_endpoint_id ep_id;
512	int i;
513
514	ep_id = (struct display_endpoint_id) {
515		.link_id = link->link_id,
516		.ep_type = link->ep_type};
517
518	for (i = 0; i < MAX_PIPES; i++) {
519		struct link_enc_assignment assignment = get_assignment(dc, i);
520
521		if (assignment.valid == true && are_ep_ids_equal(&assignment.ep_id, &ep_id)) {
522			link_enc = link->dc->res_pool->link_encoders[assignment.eng_id - ENGINE_ID_DIGA];
523			break;
524		}
525	}
526
527	return link_enc;
528}
529
530struct link_encoder *link_enc_cfg_get_next_avail_link_enc(struct dc *dc)
531{
532	struct link_encoder *link_enc = NULL;
533	enum engine_id encs_assigned[MAX_DIG_LINK_ENCODERS];
534	int i;
535
536	for (i = 0; i < MAX_DIG_LINK_ENCODERS; i++)
537		encs_assigned[i] = ENGINE_ID_UNKNOWN;
538
539	/* Add assigned encoders to list. */
540	for (i = 0; i < MAX_PIPES; i++) {
541		struct link_enc_assignment assignment = get_assignment(dc, i);
542
543		if (assignment.valid)
544			encs_assigned[assignment.eng_id - ENGINE_ID_DIGA] = assignment.eng_id;
545	}
546
547	for (i = 0; i < dc->res_pool->res_cap->num_dig_link_enc; i++) {
548		if (encs_assigned[i] == ENGINE_ID_UNKNOWN &&
549				dc->res_pool->link_encoders[i] != NULL) {
550			link_enc = dc->res_pool->link_encoders[i];
551			break;
552		}
553	}
554
555	return link_enc;
556}
557
558struct link_encoder *link_enc_cfg_get_link_enc_used_by_stream(
559		struct dc *dc,
560		const struct dc_stream_state *stream)
561{
562	struct link_encoder *link_enc;
563
564	link_enc = link_enc_cfg_get_link_enc_used_by_link(dc, stream->link);
565
566	return link_enc;
567}
568
569struct link_encoder *link_enc_cfg_get_link_enc(
570		const struct dc_link *link)
571{
572	struct link_encoder *link_enc = NULL;
573
574	/* Links supporting dynamically assigned link encoder will be assigned next
575	 * available encoder if one not already assigned.
576	 */
577	if (link->is_dig_mapping_flexible &&
578	    link->dc->res_pool->funcs->link_encs_assign) {
579		link_enc = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
580		if (link_enc == NULL)
581			link_enc = link_enc_cfg_get_next_avail_link_enc(
582				link->ctx->dc);
583	} else
584		link_enc = link->link_enc;
585
586	return link_enc;
587}
588
589struct link_encoder *link_enc_cfg_get_link_enc_used_by_stream_current(
590		struct dc *dc,
591		const struct dc_stream_state *stream)
592{
593	struct link_encoder *link_enc = NULL;
594	struct display_endpoint_id ep_id;
595	int i;
596
597	ep_id = (struct display_endpoint_id) {
598		.link_id = stream->link->link_id,
599		.ep_type = stream->link->ep_type};
600
601	for (i = 0; i < MAX_PIPES; i++) {
602		struct link_enc_assignment assignment =
603			dc->current_state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
604
605		if (assignment.valid == true && are_ep_ids_equal(&assignment.ep_id, &ep_id)) {
606			link_enc = stream->link->dc->res_pool->link_encoders[assignment.eng_id - ENGINE_ID_DIGA];
607			break;
608		}
609	}
610
611	return link_enc;
612}
613
614bool link_enc_cfg_is_link_enc_avail(struct dc *dc, enum engine_id eng_id, struct dc_link *link)
615{
616	bool is_avail = true;
617	int i;
618
619	/* An encoder is not available if it has already been assigned to a different endpoint. */
620	for (i = 0; i < MAX_PIPES; i++) {
621		struct link_enc_assignment assignment = get_assignment(dc, i);
622		struct display_endpoint_id ep_id = (struct display_endpoint_id) {
623				.link_id = link->link_id,
624				.ep_type = link->ep_type};
625
626		if (assignment.valid && assignment.eng_id == eng_id && !are_ep_ids_equal(&ep_id, &assignment.ep_id)) {
627			is_avail = false;
628			break;
629		}
630	}
631
632	return is_avail;
633}
634
635bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state)
636{
637	bool is_valid = false;
638	bool valid_entries = true;
639	bool valid_stream_ptrs = true;
640	bool valid_uniqueness = true;
641	bool valid_avail = true;
642	bool valid_streams = true;
643	int i, j;
644	uint8_t valid_count = 0;
645	uint8_t dig_stream_count = 0;
646	int eng_ids_per_ep_id[MAX_PIPES] = {0};
647	int ep_ids_per_eng_id[MAX_PIPES] = {0};
648	int valid_bitmap = 0;
649
650	/* (1) No. valid entries same as stream count. */
651	for (i = 0; i < MAX_PIPES; i++) {
652		struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
653
654		if (assignment.valid)
655			valid_count++;
656
657		if (is_dig_link_enc_stream(state->streams[i]))
658			dig_stream_count++;
659	}
660	if (valid_count != dig_stream_count)
661		valid_entries = false;
662
663	/* (2) Matching stream ptrs. */
664	for (i = 0; i < MAX_PIPES; i++) {
665		struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
666
667		if (assignment.valid) {
668			if (assignment.stream != state->streams[i])
669				valid_stream_ptrs = false;
670		}
671	}
672
673	/* (3) Each endpoint assigned unique encoder. */
674	for (i = 0; i < MAX_PIPES; i++) {
675		struct link_enc_assignment assignment_i = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
676
677		if (assignment_i.valid) {
678			struct display_endpoint_id ep_id_i = assignment_i.ep_id;
679
680			eng_ids_per_ep_id[i]++;
681			ep_ids_per_eng_id[i]++;
682			for (j = 0; j < MAX_PIPES; j++) {
683				struct link_enc_assignment assignment_j =
684					state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[j];
685
686				if (j == i)
687					continue;
688
689				if (assignment_j.valid) {
690					struct display_endpoint_id ep_id_j = assignment_j.ep_id;
691
692					if (are_ep_ids_equal(&ep_id_i, &ep_id_j) &&
693							assignment_i.eng_id != assignment_j.eng_id) {
694						valid_uniqueness = false;
695						eng_ids_per_ep_id[i]++;
696					} else if (!are_ep_ids_equal(&ep_id_i, &ep_id_j) &&
697							assignment_i.eng_id == assignment_j.eng_id) {
698						valid_uniqueness = false;
699						ep_ids_per_eng_id[i]++;
700					}
701				}
702			}
703		}
704	}
705
706	/* (4) Assigned encoders not in available pool. */
707	for (i = 0; i < MAX_PIPES; i++) {
708		struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
709
710		if (assignment.valid) {
711			for (j = 0; j < dc->res_pool->res_cap->num_dig_link_enc; j++) {
712				if (state->res_ctx.link_enc_cfg_ctx.link_enc_avail[j] == assignment.eng_id) {
713					valid_avail = false;
714					break;
715				}
716			}
717		}
718	}
719
720	/* (5) All streams have valid link encoders. */
721	for (i = 0; i < state->stream_count; i++) {
722		struct dc_stream_state *stream = state->streams[i];
723
724		if (is_dig_link_enc_stream(stream) && stream->link_enc == NULL) {
725			valid_streams = false;
726			break;
727		}
728	}
729
730	is_valid = valid_entries && valid_stream_ptrs && valid_uniqueness && valid_avail && valid_streams;
731	ASSERT(is_valid);
732
733	if (is_valid == false) {
734		valid_bitmap =
735			(valid_entries & 0x1) |
736			((valid_stream_ptrs & 0x1) << 1) |
737			((valid_uniqueness & 0x1) << 2) |
738			((valid_avail & 0x1) << 3) |
739			((valid_streams & 0x1) << 4);
740		DC_LOG_ERROR("%s: Invalid link encoder assignments - 0x%x\n", __func__, valid_bitmap);
741	}
742
743	return is_valid;
744}
745
746void link_enc_cfg_set_transient_mode(struct dc *dc, struct dc_state *current_state, struct dc_state *new_state)
747{
748	int i = 0;
749	int num_transient_assignments = 0;
750
751	for (i = 0; i < MAX_PIPES; i++) {
752		if (current_state->res_ctx.link_enc_cfg_ctx.transient_assignments[i].valid)
753			num_transient_assignments++;
754	}
755
756	/* Only enter transient mode if the new encoder assignments are valid. */
757	if (new_state->stream_count == num_transient_assignments) {
758		current_state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_TRANSIENT;
759		DC_LOG_DEBUG("%s: current_state(%p) mode(%d)\n", __func__, current_state, LINK_ENC_CFG_TRANSIENT);
760	}
761}
762