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
;
123 stream
->link_enc
= NULL
;
124 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
].eng_id
= ENGINE_ID_UNKNOWN
;
125 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
].stream
= NULL
;
132 static void add_link_enc_assignment(
133 struct dc_state
*state
,
134 struct dc_stream_state
*stream
,
135 enum engine_id eng_id
)
140 if (eng_id
!= ENGINE_ID_UNKNOWN
) {
141 eng_idx
= eng_id
- ENGINE_ID_DIGA
;
143 /* stream ptr of stream in dc_state used to update correct entry in
144 * link_enc_assignments table.
146 for (i
= 0; i
< state
->stream_count
; i
++) {
147 if (stream
== state
->streams
[i
]) {
148 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
] = (struct link_enc_assignment
){
150 .ep_id
= (struct display_endpoint_id
) {
151 .link_id
= stream
->link
->link_id
,
152 .ep_type
= stream
->link
->ep_type
},
155 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[eng_idx
] = ENGINE_ID_UNKNOWN
;
156 stream
->link_enc
= stream
->ctx
->dc
->res_pool
->link_encoders
[eng_idx
];
161 /* Attempted to add an encoder assignment for a stream not in dc_state. */
162 ASSERT(i
!= state
->stream_count
);
166 /* Return first available DIG link encoder. */
167 static enum engine_id
find_first_avail_link_enc(
168 const struct dc_context
*ctx
,
169 const struct dc_state
*state
)
171 enum engine_id eng_id
= ENGINE_ID_UNKNOWN
;
174 for (i
= 0; i
< ctx
->dc
->res_pool
->res_cap
->num_dig_link_enc
; i
++) {
175 eng_id
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[i
];
176 if (eng_id
!= ENGINE_ID_UNKNOWN
)
183 static bool is_avail_link_enc(struct dc_state
*state
, enum engine_id eng_id
)
185 bool is_avail
= false;
186 int eng_idx
= eng_id
- ENGINE_ID_DIGA
;
188 if (eng_id
!= ENGINE_ID_UNKNOWN
&& state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[eng_idx
] != ENGINE_ID_UNKNOWN
)
194 /* Test for display_endpoint_id equality. */
195 static bool are_ep_ids_equal(struct display_endpoint_id
*lhs
, struct display_endpoint_id
*rhs
)
197 bool are_equal
= false;
199 if (lhs
->link_id
.id
== rhs
->link_id
.id
&&
200 lhs
->link_id
.enum_id
== rhs
->link_id
.enum_id
&&
201 lhs
->link_id
.type
== rhs
->link_id
.type
&&
202 lhs
->ep_type
== rhs
->ep_type
)
208 static struct link_encoder
*get_link_enc_used_by_link(
209 struct dc_state
*state
,
210 const struct dc_link
*link
)
212 struct link_encoder
*link_enc
= NULL
;
213 struct display_endpoint_id ep_id
;
216 ep_id
= (struct display_endpoint_id
) {
217 .link_id
= link
->link_id
,
218 .ep_type
= link
->ep_type
};
220 for (i
= 0; i
< state
->stream_count
; i
++) {
221 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
223 if (assignment
.valid
== true && are_ep_ids_equal(&assignment
.ep_id
, &ep_id
))
224 link_enc
= link
->dc
->res_pool
->link_encoders
[assignment
.eng_id
- ENGINE_ID_DIGA
];
229 /* Clear all link encoder assignments. */
230 static void clear_enc_assignments(struct dc
*dc
, struct dc_state
*state
)
234 for (i
= 0; i
< MAX_PIPES
; i
++) {
235 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
].valid
= false;
236 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
].eng_id
= ENGINE_ID_UNKNOWN
;
237 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
].stream
= NULL
;
240 for (i
= 0; i
< dc
->res_pool
->res_cap
->num_dig_link_enc
; i
++) {
241 if (dc
->res_pool
->link_encoders
[i
])
242 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[i
] = (enum engine_id
) i
;
244 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[i
] = ENGINE_ID_UNKNOWN
;
248 void link_enc_cfg_init(
250 struct dc_state
*state
)
252 clear_enc_assignments(dc
, state
);
254 state
->res_ctx
.link_enc_cfg_ctx
.mode
= LINK_ENC_CFG_STEADY
;
257 void link_enc_cfg_link_encs_assign(
259 struct dc_state
*state
,
260 struct dc_stream_state
*streams
[],
261 uint8_t stream_count
)
263 enum engine_id eng_id
= ENGINE_ID_UNKNOWN
;
267 ASSERT(state
->stream_count
== stream_count
);
269 /* Release DIG link encoder resources before running assignment algorithm. */
270 for (i
= 0; i
< dc
->current_state
->stream_count
; i
++)
271 dc
->res_pool
->funcs
->link_enc_unassign(state
, dc
->current_state
->streams
[i
]);
273 for (i
= 0; i
< MAX_PIPES
; i
++)
274 ASSERT(state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
].valid
== false);
276 /* (a) Assign DIG link encoders to physical (unmappable) endpoints first. */
277 for (i
= 0; i
< stream_count
; i
++) {
278 struct dc_stream_state
*stream
= streams
[i
];
280 /* Skip stream if not supported by DIG link encoder. */
281 if (!is_dig_link_enc_stream(stream
))
284 /* Physical endpoints have a fixed mapping to DIG link encoders. */
285 if (!stream
->link
->is_dig_mapping_flexible
) {
286 eng_id
= stream
->link
->eng_id
;
287 add_link_enc_assignment(state
, stream
, eng_id
);
291 /* (b) Retain previous assignments for mappable endpoints if encoders still available. */
292 eng_id
= ENGINE_ID_UNKNOWN
;
294 if (state
!= dc
->current_state
) {
295 struct dc_state
*prev_state
= dc
->current_state
;
297 for (i
= 0; i
< stream_count
; i
++) {
298 struct dc_stream_state
*stream
= state
->streams
[i
];
300 /* Skip stream if not supported by DIG link encoder. */
301 if (!is_dig_link_enc_stream(stream
))
304 if (!stream
->link
->is_dig_mapping_flexible
)
307 for (j
= 0; j
< prev_state
->stream_count
; j
++) {
308 struct dc_stream_state
*prev_stream
= prev_state
->streams
[j
];
310 if (stream
== prev_stream
&& stream
->link
== prev_stream
->link
&&
311 prev_state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[j
].valid
) {
312 eng_id
= prev_state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[j
].eng_id
;
313 if (is_avail_link_enc(state
, eng_id
))
314 add_link_enc_assignment(state
, stream
, eng_id
);
320 /* (c) Then assign encoders to remaining mappable endpoints. */
321 eng_id
= ENGINE_ID_UNKNOWN
;
323 for (i
= 0; i
< stream_count
; i
++) {
324 struct dc_stream_state
*stream
= streams
[i
];
326 /* Skip stream if not supported by DIG link encoder. */
327 if (!is_dig_link_enc_stream(stream
)) {
328 ASSERT(stream
->link
->is_dig_mapping_flexible
!= true);
332 /* Mappable endpoints have a flexible mapping to DIG link encoders. */
333 if (stream
->link
->is_dig_mapping_flexible
) {
334 struct link_encoder
*link_enc
= NULL
;
336 /* Skip if encoder assignment retained in step (b) above. */
337 if (stream
->link_enc
)
340 /* For MST, multiple streams will share the same link / display
341 * endpoint. These streams should use the same link encoder
342 * assigned to that endpoint.
344 link_enc
= get_link_enc_used_by_link(state
, stream
->link
);
345 if (link_enc
== NULL
)
346 eng_id
= find_first_avail_link_enc(stream
->ctx
, state
);
348 eng_id
= link_enc
->preferred_engine
;
349 add_link_enc_assignment(state
, stream
, eng_id
);
353 link_enc_cfg_validate(dc
, state
);
355 /* Update transient assignments. */
356 for (i
= 0; i
< MAX_PIPES
; i
++) {
357 dc
->current_state
->res_ctx
.link_enc_cfg_ctx
.transient_assignments
[i
] =
358 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
361 /* Current state mode will be set to steady once this state committed. */
362 state
->res_ctx
.link_enc_cfg_ctx
.mode
= LINK_ENC_CFG_STEADY
;
365 void link_enc_cfg_link_enc_unassign(
366 struct dc_state
*state
,
367 struct dc_stream_state
*stream
)
369 enum engine_id eng_id
= ENGINE_ID_UNKNOWN
;
371 /* Only DIG link encoders. */
372 if (!is_dig_link_enc_stream(stream
))
375 if (stream
->link_enc
)
376 eng_id
= stream
->link_enc
->preferred_engine
;
378 remove_link_enc_assignment(state
, stream
, eng_id
);
381 bool link_enc_cfg_is_transmitter_mappable(
383 struct link_encoder
*link_enc
)
385 bool is_mappable
= false;
386 enum engine_id eng_id
= link_enc
->preferred_engine
;
387 struct dc_stream_state
*stream
= link_enc_cfg_get_stream_using_link_enc(dc
, eng_id
);
390 is_mappable
= stream
->link
->is_dig_mapping_flexible
;
395 struct dc_stream_state
*link_enc_cfg_get_stream_using_link_enc(
397 enum engine_id eng_id
)
399 struct dc_stream_state
*stream
= NULL
;
402 for (i
= 0; i
< MAX_PIPES
; i
++) {
403 struct link_enc_assignment assignment
= get_assignment(dc
, i
);
405 if ((assignment
.valid
== true) && (assignment
.eng_id
== eng_id
)) {
406 stream
= assignment
.stream
;
414 struct dc_link
*link_enc_cfg_get_link_using_link_enc(
416 enum engine_id eng_id
)
418 struct dc_link
*link
= NULL
;
419 struct dc_stream_state
*stream
= NULL
;
421 stream
= link_enc_cfg_get_stream_using_link_enc(dc
, eng_id
);
426 // dm_output_to_console("%s: No link using DIG(%d).\n", __func__, eng_id);
430 struct link_encoder
*link_enc_cfg_get_link_enc_used_by_link(
432 const struct dc_link
*link
)
434 struct link_encoder
*link_enc
= NULL
;
435 struct display_endpoint_id ep_id
;
438 ep_id
= (struct display_endpoint_id
) {
439 .link_id
= link
->link_id
,
440 .ep_type
= link
->ep_type
};
442 for (i
= 0; i
< MAX_PIPES
; i
++) {
443 struct link_enc_assignment assignment
= get_assignment(dc
, i
);
445 if (assignment
.valid
== true && are_ep_ids_equal(&assignment
.ep_id
, &ep_id
)) {
446 link_enc
= link
->dc
->res_pool
->link_encoders
[assignment
.eng_id
- ENGINE_ID_DIGA
];
454 struct link_encoder
*link_enc_cfg_get_next_avail_link_enc(struct dc
*dc
)
456 struct link_encoder
*link_enc
= NULL
;
457 enum engine_id encs_assigned
[MAX_DIG_LINK_ENCODERS
];
460 for (i
= 0; i
< MAX_DIG_LINK_ENCODERS
; i
++)
461 encs_assigned
[i
] = ENGINE_ID_UNKNOWN
;
463 /* Add assigned encoders to list. */
464 for (i
= 0; i
< MAX_PIPES
; i
++) {
465 struct link_enc_assignment assignment
= get_assignment(dc
, i
);
467 if (assignment
.valid
)
468 encs_assigned
[assignment
.eng_id
- ENGINE_ID_DIGA
] = assignment
.eng_id
;
471 for (i
= 0; i
< dc
->res_pool
->res_cap
->num_dig_link_enc
; i
++) {
472 if (encs_assigned
[i
] == ENGINE_ID_UNKNOWN
) {
473 link_enc
= dc
->res_pool
->link_encoders
[i
];
481 struct link_encoder
*link_enc_cfg_get_link_enc_used_by_stream(
483 const struct dc_stream_state
*stream
)
485 struct link_encoder
*link_enc
;
487 link_enc
= link_enc_cfg_get_link_enc_used_by_link(dc
, stream
->link
);
492 bool link_enc_cfg_is_link_enc_avail(struct dc
*dc
, enum engine_id eng_id
)
494 bool is_avail
= true;
497 /* Add assigned encoders to list. */
498 for (i
= 0; i
< MAX_PIPES
; i
++) {
499 struct link_enc_assignment assignment
= get_assignment(dc
, i
);
501 if (assignment
.valid
&& assignment
.eng_id
== eng_id
) {
510 bool link_enc_cfg_validate(struct dc
*dc
, struct dc_state
*state
)
512 bool is_valid
= false;
513 bool valid_entries
= true;
514 bool valid_stream_ptrs
= true;
515 bool valid_uniqueness
= true;
516 bool valid_avail
= true;
517 bool valid_streams
= true;
519 uint8_t valid_count
= 0;
520 uint8_t dig_stream_count
= 0;
521 int matching_stream_ptrs
= 0;
522 int eng_ids_per_ep_id
[MAX_PIPES
] = {0};
524 /* (1) No. valid entries same as stream count. */
525 for (i
= 0; i
< MAX_PIPES
; i
++) {
526 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
528 if (assignment
.valid
)
531 if (is_dig_link_enc_stream(state
->streams
[i
]))
534 if (valid_count
!= dig_stream_count
)
535 valid_entries
= false;
537 /* (2) Matching stream ptrs. */
538 for (i
= 0; i
< MAX_PIPES
; i
++) {
539 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
541 if (assignment
.valid
) {
542 if (assignment
.stream
== state
->streams
[i
])
543 matching_stream_ptrs
++;
545 valid_stream_ptrs
= false;
549 /* (3) Each endpoint assigned unique encoder. */
550 for (i
= 0; i
< MAX_PIPES
; i
++) {
551 struct link_enc_assignment assignment_i
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
553 if (assignment_i
.valid
) {
554 struct display_endpoint_id ep_id_i
= assignment_i
.ep_id
;
556 eng_ids_per_ep_id
[i
]++;
557 for (j
= 0; j
< MAX_PIPES
; j
++) {
558 struct link_enc_assignment assignment_j
=
559 state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[j
];
564 if (assignment_j
.valid
) {
565 struct display_endpoint_id ep_id_j
= assignment_j
.ep_id
;
567 if (are_ep_ids_equal(&ep_id_i
, &ep_id_j
) &&
568 assignment_i
.eng_id
!= assignment_j
.eng_id
) {
569 valid_uniqueness
= false;
570 eng_ids_per_ep_id
[i
]++;
577 /* (4) Assigned encoders not in available pool. */
578 for (i
= 0; i
< MAX_PIPES
; i
++) {
579 struct link_enc_assignment assignment
= state
->res_ctx
.link_enc_cfg_ctx
.link_enc_assignments
[i
];
581 if (assignment
.valid
) {
582 for (j
= 0; j
< dc
->res_pool
->res_cap
->num_dig_link_enc
; j
++) {
583 if (state
->res_ctx
.link_enc_cfg_ctx
.link_enc_avail
[j
] == assignment
.eng_id
) {
591 /* (5) All streams have valid link encoders. */
592 for (i
= 0; i
< state
->stream_count
; i
++) {
593 struct dc_stream_state
*stream
= state
->streams
[i
];
595 if (is_dig_link_enc_stream(stream
) && stream
->link_enc
== NULL
) {
596 valid_streams
= false;
601 is_valid
= valid_entries
&& valid_stream_ptrs
&& valid_uniqueness
&& valid_avail
&& valid_streams
;