2 * Copyright 2021 Advanced Micro Devices, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
26 #include "link_enc_cfg.h"
28 #include "dc_link_dp.h"
30 /* Check whether stream is supported by DIG link encoders. */
31 static bool is_dig_link_enc_stream(struct dc_stream_state
*stream
)
33 bool is_dig_stream
= false;
34 struct link_encoder
*link_enc
= NULL
;
37 /* Loop over created link encoder objects. */
39 for (i
= 0; i
< stream
->ctx
->dc
->res_pool
->res_cap
->num_dig_link_enc
; i
++) {
40 link_enc
= stream
->ctx
->dc
->res_pool
->link_encoders
[i
];
42 /* Need to check link signal type rather than stream signal type which may not
45 if (link_enc
&& ((uint32_t)stream
->link
->connector_signal
& link_enc
->output_signals
)) {
46 if (dc_is_dp_signal(stream
->signal
)) {
47 /* DIGs do not support DP2.0 streams with 128b/132b encoding. */
48 struct dc_link_settings link_settings
= {0};
50 decide_link_settings(stream
, &link_settings
);
51 if ((link_settings
.link_rate
>= LINK_RATE_LOW
) &&
52 link_settings
.link_rate
<= LINK_RATE_HIGH3
) {
66 static struct link_enc_assignment
get_assignment(struct dc
*dc
, int i
)
68 struct link_enc_assignment assignment
;
70 if (dc
->current_state
->res_ctx
.link_enc_cfg_ctx
.mode
== LINK_ENC_CFG_TRANSIENT
)
71 assignment
= dc
->current_state
->res_ctx
.link_enc_cfg_ctx
.transient_assignments
[i
];
72 else /* LINK_ENC_CFG_STEADY */
73 assignment
= dc
->current_state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
78 /* Return stream using DIG link encoder resource. NULL if unused. */
79 static struct dc_stream_state
*get_stream_using_link_enc(
80 struct dc_state
*state
,
81 enum engine_id eng_id
)
83 struct dc_stream_state
*stream
= NULL
;
86 for (i
= 0; i
< state
->stream_count
; i
++) {
87 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
89 if ((assignment
.valid
== true) && (assignment
.eng_id
== eng_id
)) {
90 stream
= state
->streams
[i
];
98 static void remove_link_enc_assignment(
99 struct dc_state
*state
,
100 struct dc_stream_state
*stream
,
101 enum engine_id eng_id
)
106 if (eng_id
!= ENGINE_ID_UNKNOWN
) {
107 eng_idx
= eng_id
- ENGINE_ID_DIGA
;
109 /* stream ptr of stream in dc_state used to update correct entry in
110 * link_enc_assignments table.
112 for (i
= 0; i
< MAX_PIPES
; i
++) {
113 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
115 if (assignment
.valid
&& assignment
.stream
== stream
) {
116 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
].valid
= false;
117 /* Only add link encoder back to availability pool if not being
118 * used by any other stream (i.e. removing SST stream or last MST stream).
120 if (get_stream_using_link_enc(state
, eng_id
) == NULL
)
121 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[eng_idx
] = eng_id
;
122 stream
->link_enc
= NULL
;
129 static void add_link_enc_assignment(
130 struct dc_state
*state
,
131 struct dc_stream_state
*stream
,
132 enum engine_id eng_id
)
137 if (eng_id
!= ENGINE_ID_UNKNOWN
) {
138 eng_idx
= eng_id
- ENGINE_ID_DIGA
;
140 /* stream ptr of stream in dc_state used to update correct entry in
141 * link_enc_assignments table.
143 for (i
= 0; i
< state
->stream_count
; i
++) {
144 if (stream
== state
->streams
[i
]) {
145 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
] = (struct link_enc_assignment
){
147 .ep_id
= (struct display_endpoint_id
) {
148 .link_id
= stream
->link
->link_id
,
149 .ep_type
= stream
->link
->ep_type
},
152 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[eng_idx
] = ENGINE_ID_UNKNOWN
;
153 stream
->link_enc
= stream
->ctx
->dc
->res_pool
->link_encoders
[eng_idx
];
158 /* Attempted to add an encoder assignment for a stream not in dc_state. */
159 ASSERT(i
!= state
->stream_count
);
163 /* Return first available DIG link encoder. */
164 static enum engine_id
find_first_avail_link_enc(
165 const struct dc_context
*ctx
,
166 const struct dc_state
*state
)
168 enum engine_id eng_id
= ENGINE_ID_UNKNOWN
;
171 for (i
= 0; i
< ctx
->dc
->res_pool
->res_cap
->num_dig_link_enc
; i
++) {
172 eng_id
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[i
];
173 if (eng_id
!= ENGINE_ID_UNKNOWN
)
180 static bool is_avail_link_enc(struct dc_state
*state
, enum engine_id eng_id
)
182 bool is_avail
= false;
183 int eng_idx
= eng_id
- ENGINE_ID_DIGA
;
185 if (eng_id
!= ENGINE_ID_UNKNOWN
&& state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[eng_idx
] != ENGINE_ID_UNKNOWN
)
191 /* Test for display_endpoint_id equality. */
192 static bool are_ep_ids_equal(struct display_endpoint_id
*lhs
, struct display_endpoint_id
*rhs
)
194 bool are_equal
= false;
196 if (lhs
->link_id
.id
== rhs
->link_id
.id
&&
197 lhs
->link_id
.enum_id
== rhs
->link_id
.enum_id
&&
198 lhs
->link_id
.type
== rhs
->link_id
.type
&&
199 lhs
->ep_type
== rhs
->ep_type
)
205 static struct link_encoder
*get_link_enc_used_by_link(
206 struct dc_state
*state
,
207 const struct dc_link
*link
)
209 struct link_encoder
*link_enc
= NULL
;
210 struct display_endpoint_id ep_id
;
213 ep_id
= (struct display_endpoint_id
) {
214 .link_id
= link
->link_id
,
215 .ep_type
= link
->ep_type
};
217 for (i
= 0; i
< state
->stream_count
; i
++) {
218 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
220 if (assignment
.valid
== true && are_ep_ids_equal(&assignment
.ep_id
, &ep_id
))
221 link_enc
= link
->dc
->res_pool
->link_encoders
[assignment
.eng_id
- ENGINE_ID_DIGA
];
227 void link_enc_cfg_init(
229 struct dc_state
*state
)
233 for (i
= 0; i
< dc
->res_pool
->res_cap
->num_dig_link_enc
; i
++) {
234 if (dc
->res_pool
->link_encoders
[i
])
235 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[i
] = (enum engine_id
) i
;
237 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[i
] = ENGINE_ID_UNKNOWN
;
240 state
->res_ctx
.link_enc_cfg_ctx
.mode
= LINK_ENC_CFG_STEADY
;
243 void link_enc_cfg_link_encs_assign(
245 struct dc_state
*state
,
246 struct dc_stream_state
*streams
[],
247 uint8_t stream_count
)
249 enum engine_id eng_id
= ENGINE_ID_UNKNOWN
;
253 ASSERT(state
->stream_count
== stream_count
);
255 /* Release DIG link encoder resources before running assignment algorithm. */
256 for (i
= 0; i
< stream_count
; i
++)
257 dc
->res_pool
->funcs
->link_enc_unassign(state
, streams
[i
]);
259 for (i
= 0; i
< MAX_PIPES
; i
++)
260 ASSERT(state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
].valid
== false);
262 /* (a) Assign DIG link encoders to physical (unmappable) endpoints first. */
263 for (i
= 0; i
< stream_count
; i
++) {
264 struct dc_stream_state
*stream
= streams
[i
];
266 /* Skip stream if not supported by DIG link encoder. */
267 if (!is_dig_link_enc_stream(stream
))
270 /* Physical endpoints have a fixed mapping to DIG link encoders. */
271 if (!stream
->link
->is_dig_mapping_flexible
) {
272 eng_id
= stream
->link
->eng_id
;
273 add_link_enc_assignment(state
, stream
, eng_id
);
277 /* (b) Retain previous assignments for mappable endpoints if encoders still available. */
278 eng_id
= ENGINE_ID_UNKNOWN
;
280 if (state
!= dc
->current_state
) {
281 struct dc_state
*prev_state
= dc
->current_state
;
283 for (i
= 0; i
< stream_count
; i
++) {
284 struct dc_stream_state
*stream
= state
->streams
[i
];
286 /* Skip stream if not supported by DIG link encoder. */
287 if (!is_dig_link_enc_stream(stream
))
290 if (!stream
->link
->is_dig_mapping_flexible
)
293 for (j
= 0; j
< prev_state
->stream_count
; j
++) {
294 struct dc_stream_state
*prev_stream
= prev_state
->streams
[j
];
296 if (stream
== prev_stream
&& stream
->link
== prev_stream
->link
&&
297 prev_state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[j
].valid
) {
298 eng_id
= prev_state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[j
].eng_id
;
299 if (is_avail_link_enc(state
, eng_id
))
300 add_link_enc_assignment(state
, stream
, eng_id
);
306 /* (c) Then assign encoders to remaining mappable endpoints. */
307 eng_id
= ENGINE_ID_UNKNOWN
;
309 for (i
= 0; i
< stream_count
; i
++) {
310 struct dc_stream_state
*stream
= streams
[i
];
312 /* Skip stream if not supported by DIG link encoder. */
313 if (!is_dig_link_enc_stream(stream
)) {
314 ASSERT(stream
->link
->is_dig_mapping_flexible
!= true);
318 /* Mappable endpoints have a flexible mapping to DIG link encoders. */
319 if (stream
->link
->is_dig_mapping_flexible
) {
320 struct link_encoder
*link_enc
= NULL
;
322 /* Skip if encoder assignment retained in step (b) above. */
323 if (stream
->link_enc
)
326 /* For MST, multiple streams will share the same link / display
327 * endpoint. These streams should use the same link encoder
328 * assigned to that endpoint.
330 link_enc
= get_link_enc_used_by_link(state
, stream
->link
);
331 if (link_enc
== NULL
)
332 eng_id
= find_first_avail_link_enc(stream
->ctx
, state
);
334 eng_id
= link_enc
->preferred_engine
;
335 add_link_enc_assignment(state
, stream
, eng_id
);
339 link_enc_cfg_validate(dc
, state
);
341 /* Update transient assignments. */
342 for (i
= 0; i
< MAX_PIPES
; i
++) {
343 dc
->current_state
->res_ctx
.link_enc_cfg_ctx
.transient_assignments
[i
] =
344 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
347 /* Current state mode will be set to steady once this state committed. */
348 state
->res_ctx
.link_enc_cfg_ctx
.mode
= LINK_ENC_CFG_STEADY
;
351 void link_enc_cfg_link_enc_unassign(
352 struct dc_state
*state
,
353 struct dc_stream_state
*stream
)
355 enum engine_id eng_id
= ENGINE_ID_UNKNOWN
;
357 /* Only DIG link encoders. */
358 if (!is_dig_link_enc_stream(stream
))
361 if (stream
->link_enc
)
362 eng_id
= stream
->link_enc
->preferred_engine
;
364 remove_link_enc_assignment(state
, stream
, eng_id
);
367 bool link_enc_cfg_is_transmitter_mappable(
369 struct link_encoder
*link_enc
)
371 bool is_mappable
= false;
372 enum engine_id eng_id
= link_enc
->preferred_engine
;
373 struct dc_stream_state
*stream
= link_enc_cfg_get_stream_using_link_enc(dc
, eng_id
);
376 is_mappable
= stream
->link
->is_dig_mapping_flexible
;
381 struct dc_stream_state
*link_enc_cfg_get_stream_using_link_enc(
383 enum engine_id eng_id
)
385 struct dc_stream_state
*stream
= NULL
;
388 for (i
= 0; i
< MAX_PIPES
; i
++) {
389 struct link_enc_assignment assignment
= get_assignment(dc
, i
);
391 if ((assignment
.valid
== true) && (assignment
.eng_id
== eng_id
)) {
392 stream
= assignment
.stream
;
400 struct dc_link
*link_enc_cfg_get_link_using_link_enc(
402 enum engine_id eng_id
)
404 struct dc_link
*link
= NULL
;
405 struct dc_stream_state
*stream
= NULL
;
407 stream
= link_enc_cfg_get_stream_using_link_enc(dc
, eng_id
);
412 // dm_output_to_console("%s: No link using DIG(%d).\n", __func__, eng_id);
416 struct link_encoder
*link_enc_cfg_get_link_enc_used_by_link(
418 const struct dc_link
*link
)
420 struct link_encoder
*link_enc
= NULL
;
421 struct display_endpoint_id ep_id
;
424 ep_id
= (struct display_endpoint_id
) {
425 .link_id
= link
->link_id
,
426 .ep_type
= link
->ep_type
};
428 for (i
= 0; i
< MAX_PIPES
; i
++) {
429 struct link_enc_assignment assignment
= get_assignment(dc
, i
);
431 if (assignment
.valid
== true && are_ep_ids_equal(&assignment
.ep_id
, &ep_id
)) {
432 link_enc
= link
->dc
->res_pool
->link_encoders
[assignment
.eng_id
- ENGINE_ID_DIGA
];
440 struct link_encoder
*link_enc_cfg_get_next_avail_link_enc(struct dc
*dc
)
442 struct link_encoder
*link_enc
= NULL
;
443 enum engine_id encs_assigned
[MAX_DIG_LINK_ENCODERS
];
446 for (i
= 0; i
< MAX_DIG_LINK_ENCODERS
; i
++)
447 encs_assigned
[i
] = ENGINE_ID_UNKNOWN
;
449 /* Add assigned encoders to list. */
450 for (i
= 0; i
< MAX_PIPES
; i
++) {
451 struct link_enc_assignment assignment
= get_assignment(dc
, i
);
453 if (assignment
.valid
)
454 encs_assigned
[assignment
.eng_id
- ENGINE_ID_DIGA
] = assignment
.eng_id
;
457 for (i
= 0; i
< dc
->res_pool
->res_cap
->num_dig_link_enc
; i
++) {
458 if (encs_assigned
[i
] == ENGINE_ID_UNKNOWN
) {
459 link_enc
= dc
->res_pool
->link_encoders
[i
];
467 struct link_encoder
*link_enc_cfg_get_link_enc_used_by_stream(
469 const struct dc_stream_state
*stream
)
471 struct link_encoder
*link_enc
;
473 link_enc
= link_enc_cfg_get_link_enc_used_by_link(dc
, stream
->link
);
478 bool link_enc_cfg_is_link_enc_avail(struct dc
*dc
, enum engine_id eng_id
)
480 bool is_avail
= true;
483 /* Add assigned encoders to list. */
484 for (i
= 0; i
< MAX_PIPES
; i
++) {
485 struct link_enc_assignment assignment
= get_assignment(dc
, i
);
487 if (assignment
.valid
&& assignment
.eng_id
== eng_id
) {
496 bool link_enc_cfg_validate(struct dc
*dc
, struct dc_state
*state
)
498 bool is_valid
= false;
499 bool valid_entries
= true;
500 bool valid_stream_ptrs
= true;
501 bool valid_uniqueness
= true;
502 bool valid_avail
= true;
503 bool valid_streams
= true;
505 uint8_t valid_count
= 0;
506 uint8_t dig_stream_count
= 0;
507 int matching_stream_ptrs
= 0;
508 int eng_ids_per_ep_id
[MAX_PIPES
] = {0};
510 /* (1) No. valid entries same as stream count. */
511 for (i
= 0; i
< MAX_PIPES
; i
++) {
512 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
514 if (assignment
.valid
)
517 if (is_dig_link_enc_stream(state
->streams
[i
]))
520 if (valid_count
!= dig_stream_count
)
521 valid_entries
= false;
523 /* (2) Matching stream ptrs. */
524 for (i
= 0; i
< MAX_PIPES
; i
++) {
525 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
527 if (assignment
.valid
) {
528 if (assignment
.stream
== state
->streams
[i
])
529 matching_stream_ptrs
++;
531 valid_stream_ptrs
= false;
535 /* (3) Each endpoint assigned unique encoder. */
536 for (i
= 0; i
< MAX_PIPES
; i
++) {
537 struct link_enc_assignment assignment_i
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
539 if (assignment_i
.valid
) {
540 struct display_endpoint_id ep_id_i
= assignment_i
.ep_id
;
542 eng_ids_per_ep_id
[i
]++;
543 for (j
= 0; j
< MAX_PIPES
; j
++) {
544 struct link_enc_assignment assignment_j
=
545 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[j
];
550 if (assignment_j
.valid
) {
551 struct display_endpoint_id ep_id_j
= assignment_j
.ep_id
;
553 if (are_ep_ids_equal(&ep_id_i
, &ep_id_j
) &&
554 assignment_i
.eng_id
!= assignment_j
.eng_id
) {
555 valid_uniqueness
= false;
556 eng_ids_per_ep_id
[i
]++;
563 /* (4) Assigned encoders not in available pool. */
564 for (i
= 0; i
< MAX_PIPES
; i
++) {
565 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
567 if (assignment
.valid
) {
568 for (j
= 0; j
< dc
->res_pool
->res_cap
->num_dig_link_enc
; j
++) {
569 if (state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[j
] == assignment
.eng_id
) {
577 /* (5) All streams have valid link encoders. */
578 for (i
= 0; i
< state
->stream_count
; i
++) {
579 struct dc_stream_state
*stream
= state
->streams
[i
];
581 if (is_dig_link_enc_stream(stream
) && stream
->link_enc
== NULL
) {
582 valid_streams
= false;
587 is_valid
= valid_entries
&& valid_stream_ptrs
&& valid_uniqueness
&& valid_avail
&& valid_streams
;