1 /*
2 * Copyright 2021 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
26 #include "link_enc_cfg.h"
27 #include "resource.h"
28 #include "dc_link_dp.h"
29
30 /* Check whether stream is supported by DIG link encoders. */
is_dig_link_enc_stream(struct dc_stream_state * stream)31 static bool is_dig_link_enc_stream(struct dc_stream_state *stream)
32 {
33 bool is_dig_stream = false;
34 struct link_encoder *link_enc = NULL;
35 int i;
36
37 /* Loop over created link encoder objects. */
38 for (i = 0; i < stream->ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) {
39 link_enc = stream->ctx->dc->res_pool->link_encoders[i];
40
41 if (link_enc &&
42 ((uint32_t)stream->signal & link_enc->output_signals)) {
43 if (dc_is_dp_signal(stream->signal)) {
44 /* DIGs do not support DP2.0 streams with 128b/132b encoding. */
45 struct dc_link_settings link_settings = {0};
46
47 decide_link_settings(stream, &link_settings);
48 if ((link_settings.link_rate >= LINK_RATE_LOW) &&
49 link_settings.link_rate <= LINK_RATE_HIGH3) {
50 is_dig_stream = true;
51 break;
52 }
53 } else {
54 is_dig_stream = true;
55 break;
56 }
57 }
58 }
59
60 return is_dig_stream;
61 }
62
63 /* Update DIG link encoder resource tracking variables in dc_state. */
update_link_enc_assignment(struct dc_state * state,struct dc_stream_state * stream,enum engine_id eng_id,bool add_enc)64 static void update_link_enc_assignment(
65 struct dc_state *state,
66 struct dc_stream_state *stream,
67 enum engine_id eng_id,
68 bool add_enc)
69 {
70 int eng_idx;
71 int stream_idx;
72 int i;
73
74 if (eng_id != ENGINE_ID_UNKNOWN) {
75 eng_idx = eng_id - ENGINE_ID_DIGA;
76 stream_idx = -1;
77
78 /* Index of stream in dc_state used to update correct entry in
79 * link_enc_assignments table.
80 */
81 for (i = 0; i < state->stream_count; i++) {
82 if (stream == state->streams[i]) {
83 stream_idx = i;
84 break;
85 }
86 }
87
88 /* Update link encoder assignments table, link encoder availability
89 * pool and link encoder assigned to stream in state.
90 * Add/remove encoder resource to/from stream.
91 */
92 if (stream_idx != -1) {
93 if (add_enc) {
94 state->res_ctx.link_enc_assignments[stream_idx] = (struct link_enc_assignment){
95 .valid = true,
96 .ep_id = (struct display_endpoint_id) {
97 .link_id = stream->link->link_id,
98 .ep_type = stream->link->ep_type},
99 .eng_id = eng_id};
100 state->res_ctx.link_enc_avail[eng_idx] = ENGINE_ID_UNKNOWN;
101 stream->link_enc = stream->ctx->dc->res_pool->link_encoders[eng_idx];
102 } else {
103 state->res_ctx.link_enc_assignments[stream_idx].valid = false;
104 state->res_ctx.link_enc_avail[eng_idx] = eng_id;
105 stream->link_enc = NULL;
106 }
107 } else {
108 dm_output_to_console("%s: Stream not found in dc_state.\n", __func__);
109 }
110 }
111 }
112
113 /* Return first available DIG link encoder. */
find_first_avail_link_enc(const struct dc_context * ctx,const struct dc_state * state)114 static enum engine_id find_first_avail_link_enc(
115 const struct dc_context *ctx,
116 const struct dc_state *state)
117 {
118 enum engine_id eng_id = ENGINE_ID_UNKNOWN;
119 int i;
120
121 for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) {
122 eng_id = state->res_ctx.link_enc_avail[i];
123 if (eng_id != ENGINE_ID_UNKNOWN)
124 break;
125 }
126
127 return eng_id;
128 }
129
130 /* Return stream using DIG link encoder resource. NULL if unused. */
get_stream_using_link_enc(struct dc_state * state,enum engine_id eng_id)131 static struct dc_stream_state *get_stream_using_link_enc(
132 struct dc_state *state,
133 enum engine_id eng_id)
134 {
135 struct dc_stream_state *stream = NULL;
136 int stream_idx = -1;
137 int i;
138
139 for (i = 0; i < state->stream_count; i++) {
140 struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i];
141
142 if (assignment.valid && (assignment.eng_id == eng_id)) {
143 stream_idx = i;
144 break;
145 }
146 }
147
148 if (stream_idx != -1)
149 stream = state->streams[stream_idx];
150 else
151 dm_output_to_console("%s: No stream using DIG(%d).\n", __func__, eng_id);
152
153 return stream;
154 }
155
link_enc_cfg_init(struct dc * dc,struct dc_state * state)156 void link_enc_cfg_init(
157 struct dc *dc,
158 struct dc_state *state)
159 {
160 int i;
161
162 for (i = 0; i < dc->res_pool->res_cap->num_dig_link_enc; i++) {
163 if (dc->res_pool->link_encoders[i])
164 state->res_ctx.link_enc_avail[i] = (enum engine_id) i;
165 else
166 state->res_ctx.link_enc_avail[i] = ENGINE_ID_UNKNOWN;
167 }
168 }
169
link_enc_cfg_link_encs_assign(struct dc * dc,struct dc_state * state,struct dc_stream_state * streams[],uint8_t stream_count)170 void link_enc_cfg_link_encs_assign(
171 struct dc *dc,
172 struct dc_state *state,
173 struct dc_stream_state *streams[],
174 uint8_t stream_count)
175 {
176 enum engine_id eng_id = ENGINE_ID_UNKNOWN;
177 int i;
178
179 /* Release DIG link encoder resources before running assignment algorithm. */
180 for (i = 0; i < stream_count; i++)
181 dc->res_pool->funcs->link_enc_unassign(state, streams[i]);
182
183 /* (a) Assign DIG link encoders to physical (unmappable) endpoints first. */
184 for (i = 0; i < stream_count; i++) {
185 struct dc_stream_state *stream = streams[i];
186
187 /* Skip stream if not supported by DIG link encoder. */
188 if (!is_dig_link_enc_stream(stream))
189 continue;
190
191 /* Physical endpoints have a fixed mapping to DIG link encoders. */
192 if (!stream->link->is_dig_mapping_flexible) {
193 eng_id = stream->link->eng_id;
194 update_link_enc_assignment(state, stream, eng_id, true);
195 }
196 }
197
198 /* (b) Then assign encoders to mappable endpoints. */
199 eng_id = ENGINE_ID_UNKNOWN;
200
201 for (i = 0; i < stream_count; i++) {
202 struct dc_stream_state *stream = streams[i];
203
204 /* Skip stream if not supported by DIG link encoder. */
205 if (!is_dig_link_enc_stream(stream))
206 continue;
207
208 /* Mappable endpoints have a flexible mapping to DIG link encoders. */
209 if (stream->link->is_dig_mapping_flexible) {
210 eng_id = find_first_avail_link_enc(stream->ctx, state);
211 update_link_enc_assignment(state, stream, eng_id, true);
212 }
213 }
214 }
215
link_enc_cfg_link_enc_unassign(struct dc_state * state,struct dc_stream_state * stream)216 void link_enc_cfg_link_enc_unassign(
217 struct dc_state *state,
218 struct dc_stream_state *stream)
219 {
220 enum engine_id eng_id = ENGINE_ID_UNKNOWN;
221
222 /* Only DIG link encoders. */
223 if (!is_dig_link_enc_stream(stream))
224 return;
225
226 if (stream->link_enc)
227 eng_id = stream->link_enc->preferred_engine;
228
229 update_link_enc_assignment(state, stream, eng_id, false);
230 }
231
link_enc_cfg_is_transmitter_mappable(struct dc_state * state,struct link_encoder * link_enc)232 bool link_enc_cfg_is_transmitter_mappable(
233 struct dc_state *state,
234 struct link_encoder *link_enc)
235 {
236 bool is_mappable = false;
237 enum engine_id eng_id = link_enc->preferred_engine;
238 struct dc_stream_state *stream = get_stream_using_link_enc(state, eng_id);
239
240 if (stream)
241 is_mappable = stream->link->is_dig_mapping_flexible;
242
243 return is_mappable;
244 }
245
link_enc_cfg_get_link_using_link_enc(struct dc_state * state,enum engine_id eng_id)246 struct dc_link *link_enc_cfg_get_link_using_link_enc(
247 struct dc_state *state,
248 enum engine_id eng_id)
249 {
250 struct dc_link *link = NULL;
251 int stream_idx = -1;
252 int i;
253
254 for (i = 0; i < state->stream_count; i++) {
255 struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i];
256
257 if (assignment.valid && (assignment.eng_id == eng_id)) {
258 stream_idx = i;
259 break;
260 }
261 }
262
263 if (stream_idx != -1)
264 link = state->streams[stream_idx]->link;
265 else
266 dm_output_to_console("%s: No link using DIG(%d).\n", __func__, eng_id);
267
268 return link;
269 }
270
link_enc_cfg_get_link_enc_used_by_link(struct dc_state * state,const struct dc_link * link)271 struct link_encoder *link_enc_cfg_get_link_enc_used_by_link(
272 struct dc_state *state,
273 const struct dc_link *link)
274 {
275 struct link_encoder *link_enc = NULL;
276 struct display_endpoint_id ep_id;
277 int stream_idx = -1;
278 int i;
279
280 ep_id = (struct display_endpoint_id) {
281 .link_id = link->link_id,
282 .ep_type = link->ep_type};
283
284 for (i = 0; i < state->stream_count; i++) {
285 struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i];
286
287 if (assignment.valid &&
288 assignment.ep_id.link_id.id == ep_id.link_id.id &&
289 assignment.ep_id.link_id.enum_id == ep_id.link_id.enum_id &&
290 assignment.ep_id.link_id.type == ep_id.link_id.type &&
291 assignment.ep_id.ep_type == ep_id.ep_type) {
292 stream_idx = i;
293 break;
294 }
295 }
296
297 if (stream_idx != -1)
298 link_enc = state->streams[stream_idx]->link_enc;
299
300 return link_enc;
301 }
302
link_enc_cfg_get_next_avail_link_enc(const struct dc * dc,const struct dc_state * state)303 struct link_encoder *link_enc_cfg_get_next_avail_link_enc(
304 const struct dc *dc,
305 const struct dc_state *state)
306 {
307 struct link_encoder *link_enc = NULL;
308 enum engine_id eng_id;
309
310 eng_id = find_first_avail_link_enc(dc->ctx, state);
311 if (eng_id != ENGINE_ID_UNKNOWN)
312 link_enc = dc->res_pool->link_encoders[eng_id - ENGINE_ID_DIGA];
313
314 return link_enc;
315 }
316