1 | /* |
2 | * Copyright 2022 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 | /* FILE POLICY AND INTENDED USAGE: |
27 | * This file implements all generic dp link training helper functions and top |
28 | * level generic training sequence. All variations of dp link training sequence |
29 | * should be called inside the top level training functions in this file to |
30 | * ensure the integrity of our overall training procedure across different types |
31 | * of link encoding and back end hardware. |
32 | */ |
33 | #include "link_dp_training.h" |
34 | #include "link_dp_training_8b_10b.h" |
35 | #include "link_dp_training_128b_132b.h" |
36 | #include "link_dp_training_auxless.h" |
37 | #include "link_dp_training_dpia.h" |
38 | #include "link_dp_training_fixed_vs_pe_retimer.h" |
39 | #include "link_dpcd.h" |
40 | #include "link/accessories/link_dp_trace.h" |
41 | #include "link_dp_phy.h" |
42 | #include "link_dp_capability.h" |
43 | #include "link_edp_panel_control.h" |
44 | #include "link/link_detection.h" |
45 | #include "link/link_validation.h" |
46 | #include "atomfirmware.h" |
47 | #include "link_enc_cfg.h" |
48 | #include "resource.h" |
49 | #include "dm_helpers.h" |
50 | |
51 | #define DC_LOGGER \ |
52 | link->ctx->logger |
53 | |
54 | #define POST_LT_ADJ_REQ_LIMIT 6 |
55 | #define POST_LT_ADJ_REQ_TIMEOUT 200 |
56 | #define LINK_TRAINING_RETRY_DELAY 50 /* ms */ |
57 | |
58 | void dp_log_training_result( |
59 | struct dc_link *link, |
60 | const struct link_training_settings *lt_settings, |
61 | enum link_training_result status) |
62 | { |
63 | char *link_rate = "Unknown" ; |
64 | char *lt_result = "Unknown" ; |
65 | char *lt_spread = "Disabled" ; |
66 | |
67 | switch (lt_settings->link_settings.link_rate) { |
68 | case LINK_RATE_LOW: |
69 | link_rate = "RBR" ; |
70 | break; |
71 | case LINK_RATE_RATE_2: |
72 | link_rate = "R2" ; |
73 | break; |
74 | case LINK_RATE_RATE_3: |
75 | link_rate = "R3" ; |
76 | break; |
77 | case LINK_RATE_HIGH: |
78 | link_rate = "HBR" ; |
79 | break; |
80 | case LINK_RATE_RBR2: |
81 | link_rate = "RBR2" ; |
82 | break; |
83 | case LINK_RATE_RATE_6: |
84 | link_rate = "R6" ; |
85 | break; |
86 | case LINK_RATE_HIGH2: |
87 | link_rate = "HBR2" ; |
88 | break; |
89 | case LINK_RATE_RATE_8: |
90 | link_rate = "R8" ; |
91 | break; |
92 | case LINK_RATE_HIGH3: |
93 | link_rate = "HBR3" ; |
94 | break; |
95 | case LINK_RATE_UHBR10: |
96 | link_rate = "UHBR10" ; |
97 | break; |
98 | case LINK_RATE_UHBR13_5: |
99 | link_rate = "UHBR13.5" ; |
100 | break; |
101 | case LINK_RATE_UHBR20: |
102 | link_rate = "UHBR20" ; |
103 | break; |
104 | default: |
105 | break; |
106 | } |
107 | |
108 | switch (status) { |
109 | case LINK_TRAINING_SUCCESS: |
110 | lt_result = "pass" ; |
111 | break; |
112 | case LINK_TRAINING_CR_FAIL_LANE0: |
113 | lt_result = "CR failed lane0" ; |
114 | break; |
115 | case LINK_TRAINING_CR_FAIL_LANE1: |
116 | lt_result = "CR failed lane1" ; |
117 | break; |
118 | case LINK_TRAINING_CR_FAIL_LANE23: |
119 | lt_result = "CR failed lane23" ; |
120 | break; |
121 | case LINK_TRAINING_EQ_FAIL_CR: |
122 | lt_result = "CR failed in EQ" ; |
123 | break; |
124 | case LINK_TRAINING_EQ_FAIL_CR_PARTIAL: |
125 | lt_result = "CR failed in EQ partially" ; |
126 | break; |
127 | case LINK_TRAINING_EQ_FAIL_EQ: |
128 | lt_result = "EQ failed" ; |
129 | break; |
130 | case LINK_TRAINING_LQA_FAIL: |
131 | lt_result = "LQA failed" ; |
132 | break; |
133 | case LINK_TRAINING_LINK_LOSS: |
134 | lt_result = "Link loss" ; |
135 | break; |
136 | case DP_128b_132b_LT_FAILED: |
137 | lt_result = "LT_FAILED received" ; |
138 | break; |
139 | case DP_128b_132b_MAX_LOOP_COUNT_REACHED: |
140 | lt_result = "max loop count reached" ; |
141 | break; |
142 | case DP_128b_132b_CHANNEL_EQ_DONE_TIMEOUT: |
143 | lt_result = "channel EQ timeout" ; |
144 | break; |
145 | case DP_128b_132b_CDS_DONE_TIMEOUT: |
146 | lt_result = "CDS timeout" ; |
147 | break; |
148 | default: |
149 | break; |
150 | } |
151 | |
152 | switch (lt_settings->link_settings.link_spread) { |
153 | case LINK_SPREAD_DISABLED: |
154 | lt_spread = "Disabled" ; |
155 | break; |
156 | case LINK_SPREAD_05_DOWNSPREAD_30KHZ: |
157 | lt_spread = "0.5% 30KHz" ; |
158 | break; |
159 | case LINK_SPREAD_05_DOWNSPREAD_33KHZ: |
160 | lt_spread = "0.5% 33KHz" ; |
161 | break; |
162 | default: |
163 | break; |
164 | } |
165 | |
166 | /* Connectivity log: link training */ |
167 | |
168 | /* TODO - DP2.0 Log: add connectivity log for FFE PRESET */ |
169 | |
170 | CONN_MSG_LT(link, "%sx%d %s VS=%d, PE=%d, DS=%s" , |
171 | link_rate, |
172 | lt_settings->link_settings.lane_count, |
173 | lt_result, |
174 | lt_settings->hw_lane_settings[0].VOLTAGE_SWING, |
175 | lt_settings->hw_lane_settings[0].PRE_EMPHASIS, |
176 | lt_spread); |
177 | } |
178 | |
179 | uint8_t dp_initialize_scrambling_data_symbols( |
180 | struct dc_link *link, |
181 | enum dc_dp_training_pattern pattern) |
182 | { |
183 | uint8_t disable_scrabled_data_symbols = 0; |
184 | |
185 | switch (pattern) { |
186 | case DP_TRAINING_PATTERN_SEQUENCE_1: |
187 | case DP_TRAINING_PATTERN_SEQUENCE_2: |
188 | case DP_TRAINING_PATTERN_SEQUENCE_3: |
189 | disable_scrabled_data_symbols = 1; |
190 | break; |
191 | case DP_TRAINING_PATTERN_SEQUENCE_4: |
192 | case DP_128b_132b_TPS1: |
193 | case DP_128b_132b_TPS2: |
194 | disable_scrabled_data_symbols = 0; |
195 | break; |
196 | default: |
197 | ASSERT(0); |
198 | DC_LOG_HW_LINK_TRAINING("%s: Invalid HW Training pattern: %d\n" , |
199 | __func__, pattern); |
200 | break; |
201 | } |
202 | return disable_scrabled_data_symbols; |
203 | } |
204 | |
205 | enum dpcd_training_patterns |
206 | dp_training_pattern_to_dpcd_training_pattern( |
207 | struct dc_link *link, |
208 | enum dc_dp_training_pattern pattern) |
209 | { |
210 | enum dpcd_training_patterns dpcd_tr_pattern = |
211 | DPCD_TRAINING_PATTERN_VIDEOIDLE; |
212 | |
213 | switch (pattern) { |
214 | case DP_TRAINING_PATTERN_SEQUENCE_1: |
215 | DC_LOG_HW_LINK_TRAINING("%s: Using DP training pattern TPS1\n" , __func__); |
216 | dpcd_tr_pattern = DPCD_TRAINING_PATTERN_1; |
217 | break; |
218 | case DP_TRAINING_PATTERN_SEQUENCE_2: |
219 | DC_LOG_HW_LINK_TRAINING("%s: Using DP training pattern TPS2\n" , __func__); |
220 | dpcd_tr_pattern = DPCD_TRAINING_PATTERN_2; |
221 | break; |
222 | case DP_TRAINING_PATTERN_SEQUENCE_3: |
223 | DC_LOG_HW_LINK_TRAINING("%s: Using DP training pattern TPS3\n" , __func__); |
224 | dpcd_tr_pattern = DPCD_TRAINING_PATTERN_3; |
225 | break; |
226 | case DP_TRAINING_PATTERN_SEQUENCE_4: |
227 | DC_LOG_HW_LINK_TRAINING("%s: Using DP training pattern TPS4\n" , __func__); |
228 | dpcd_tr_pattern = DPCD_TRAINING_PATTERN_4; |
229 | break; |
230 | case DP_128b_132b_TPS1: |
231 | DC_LOG_HW_LINK_TRAINING("%s: Using DP 128b/132b training pattern TPS1\n" , __func__); |
232 | dpcd_tr_pattern = DPCD_128b_132b_TPS1; |
233 | break; |
234 | case DP_128b_132b_TPS2: |
235 | DC_LOG_HW_LINK_TRAINING("%s: Using DP 128b/132b training pattern TPS2\n" , __func__); |
236 | dpcd_tr_pattern = DPCD_128b_132b_TPS2; |
237 | break; |
238 | case DP_128b_132b_TPS2_CDS: |
239 | DC_LOG_HW_LINK_TRAINING("%s: Using DP 128b/132b training pattern TPS2 CDS\n" , |
240 | __func__); |
241 | dpcd_tr_pattern = DPCD_128b_132b_TPS2_CDS; |
242 | break; |
243 | case DP_TRAINING_PATTERN_VIDEOIDLE: |
244 | DC_LOG_HW_LINK_TRAINING("%s: Using DP training pattern videoidle\n" , __func__); |
245 | dpcd_tr_pattern = DPCD_TRAINING_PATTERN_VIDEOIDLE; |
246 | break; |
247 | default: |
248 | ASSERT(0); |
249 | DC_LOG_HW_LINK_TRAINING("%s: Invalid HW Training pattern: %d\n" , |
250 | __func__, pattern); |
251 | break; |
252 | } |
253 | |
254 | return dpcd_tr_pattern; |
255 | } |
256 | |
257 | uint8_t dp_get_nibble_at_index(const uint8_t *buf, |
258 | uint32_t index) |
259 | { |
260 | uint8_t nibble; |
261 | nibble = buf[index / 2]; |
262 | |
263 | if (index % 2) |
264 | nibble >>= 4; |
265 | else |
266 | nibble &= 0x0F; |
267 | |
268 | return nibble; |
269 | } |
270 | |
271 | void dp_wait_for_training_aux_rd_interval( |
272 | struct dc_link *link, |
273 | uint32_t wait_in_micro_secs) |
274 | { |
275 | fsleep(usecs: wait_in_micro_secs); |
276 | |
277 | DC_LOG_HW_LINK_TRAINING("%s:\n wait = %d\n" , |
278 | __func__, |
279 | wait_in_micro_secs); |
280 | } |
281 | |
282 | /* maximum pre emphasis level allowed for each voltage swing level*/ |
283 | static const enum dc_pre_emphasis voltage_swing_to_pre_emphasis[] = { |
284 | PRE_EMPHASIS_LEVEL3, |
285 | PRE_EMPHASIS_LEVEL2, |
286 | PRE_EMPHASIS_LEVEL1, |
287 | PRE_EMPHASIS_DISABLED }; |
288 | |
289 | static enum dc_pre_emphasis get_max_pre_emphasis_for_voltage_swing( |
290 | enum dc_voltage_swing voltage) |
291 | { |
292 | enum dc_pre_emphasis pre_emphasis; |
293 | pre_emphasis = PRE_EMPHASIS_MAX_LEVEL; |
294 | |
295 | if (voltage <= VOLTAGE_SWING_MAX_LEVEL) |
296 | pre_emphasis = voltage_swing_to_pre_emphasis[voltage]; |
297 | |
298 | return pre_emphasis; |
299 | |
300 | } |
301 | |
302 | static void maximize_lane_settings(const struct link_training_settings *lt_settings, |
303 | struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]) |
304 | { |
305 | uint32_t lane; |
306 | struct dc_lane_settings max_requested; |
307 | |
308 | max_requested.VOLTAGE_SWING = lane_settings[0].VOLTAGE_SWING; |
309 | max_requested.PRE_EMPHASIS = lane_settings[0].PRE_EMPHASIS; |
310 | max_requested.FFE_PRESET = lane_settings[0].FFE_PRESET; |
311 | |
312 | /* Determine what the maximum of the requested settings are*/ |
313 | for (lane = 1; lane < lt_settings->link_settings.lane_count; lane++) { |
314 | if (lane_settings[lane].VOLTAGE_SWING > max_requested.VOLTAGE_SWING) |
315 | max_requested.VOLTAGE_SWING = lane_settings[lane].VOLTAGE_SWING; |
316 | |
317 | if (lane_settings[lane].PRE_EMPHASIS > max_requested.PRE_EMPHASIS) |
318 | max_requested.PRE_EMPHASIS = lane_settings[lane].PRE_EMPHASIS; |
319 | if (lane_settings[lane].FFE_PRESET.settings.level > |
320 | max_requested.FFE_PRESET.settings.level) |
321 | max_requested.FFE_PRESET.settings.level = |
322 | lane_settings[lane].FFE_PRESET.settings.level; |
323 | } |
324 | |
325 | /* make sure the requested settings are |
326 | * not higher than maximum settings*/ |
327 | if (max_requested.VOLTAGE_SWING > VOLTAGE_SWING_MAX_LEVEL) |
328 | max_requested.VOLTAGE_SWING = VOLTAGE_SWING_MAX_LEVEL; |
329 | |
330 | if (max_requested.PRE_EMPHASIS > PRE_EMPHASIS_MAX_LEVEL) |
331 | max_requested.PRE_EMPHASIS = PRE_EMPHASIS_MAX_LEVEL; |
332 | if (max_requested.FFE_PRESET.settings.level > DP_FFE_PRESET_MAX_LEVEL) |
333 | max_requested.FFE_PRESET.settings.level = DP_FFE_PRESET_MAX_LEVEL; |
334 | |
335 | /* make sure the pre-emphasis matches the voltage swing*/ |
336 | if (max_requested.PRE_EMPHASIS > |
337 | get_max_pre_emphasis_for_voltage_swing( |
338 | voltage: max_requested.VOLTAGE_SWING)) |
339 | max_requested.PRE_EMPHASIS = |
340 | get_max_pre_emphasis_for_voltage_swing( |
341 | voltage: max_requested.VOLTAGE_SWING); |
342 | |
343 | for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { |
344 | lane_settings[lane].VOLTAGE_SWING = max_requested.VOLTAGE_SWING; |
345 | lane_settings[lane].PRE_EMPHASIS = max_requested.PRE_EMPHASIS; |
346 | lane_settings[lane].FFE_PRESET = max_requested.FFE_PRESET; |
347 | } |
348 | } |
349 | |
350 | void dp_hw_to_dpcd_lane_settings( |
351 | const struct link_training_settings *lt_settings, |
352 | const struct dc_lane_settings hw_lane_settings[LANE_COUNT_DP_MAX], |
353 | union dpcd_training_lane dpcd_lane_settings[LANE_COUNT_DP_MAX]) |
354 | { |
355 | uint8_t lane = 0; |
356 | |
357 | for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { |
358 | if (link_dp_get_encoding_format(link_settings: <_settings->link_settings) == |
359 | DP_8b_10b_ENCODING) { |
360 | dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET = |
361 | (uint8_t)(hw_lane_settings[lane].VOLTAGE_SWING); |
362 | dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET = |
363 | (uint8_t)(hw_lane_settings[lane].PRE_EMPHASIS); |
364 | dpcd_lane_settings[lane].bits.MAX_SWING_REACHED = |
365 | (hw_lane_settings[lane].VOLTAGE_SWING == |
366 | VOLTAGE_SWING_MAX_LEVEL ? 1 : 0); |
367 | dpcd_lane_settings[lane].bits.MAX_PRE_EMPHASIS_REACHED = |
368 | (hw_lane_settings[lane].PRE_EMPHASIS == |
369 | PRE_EMPHASIS_MAX_LEVEL ? 1 : 0); |
370 | } else if (link_dp_get_encoding_format(link_settings: <_settings->link_settings) == |
371 | DP_128b_132b_ENCODING) { |
372 | dpcd_lane_settings[lane].tx_ffe.PRESET_VALUE = |
373 | hw_lane_settings[lane].FFE_PRESET.settings.level; |
374 | } |
375 | } |
376 | } |
377 | |
378 | uint8_t get_dpcd_link_rate(const struct dc_link_settings *link_settings) |
379 | { |
380 | uint8_t link_rate = 0; |
381 | enum dp_link_encoding encoding = link_dp_get_encoding_format(link_settings); |
382 | |
383 | if (encoding == DP_128b_132b_ENCODING) |
384 | switch (link_settings->link_rate) { |
385 | case LINK_RATE_UHBR10: |
386 | link_rate = 0x1; |
387 | break; |
388 | case LINK_RATE_UHBR20: |
389 | link_rate = 0x2; |
390 | break; |
391 | case LINK_RATE_UHBR13_5: |
392 | link_rate = 0x4; |
393 | break; |
394 | default: |
395 | link_rate = 0; |
396 | break; |
397 | } |
398 | else if (encoding == DP_8b_10b_ENCODING) |
399 | link_rate = (uint8_t) link_settings->link_rate; |
400 | else |
401 | link_rate = 0; |
402 | |
403 | return link_rate; |
404 | } |
405 | |
406 | /* Only used for channel equalization */ |
407 | uint32_t dp_translate_training_aux_read_interval(uint32_t dpcd_aux_read_interval) |
408 | { |
409 | unsigned int aux_rd_interval_us = 400; |
410 | |
411 | switch (dpcd_aux_read_interval) { |
412 | case 0x01: |
413 | aux_rd_interval_us = 4000; |
414 | break; |
415 | case 0x02: |
416 | aux_rd_interval_us = 8000; |
417 | break; |
418 | case 0x03: |
419 | aux_rd_interval_us = 12000; |
420 | break; |
421 | case 0x04: |
422 | aux_rd_interval_us = 16000; |
423 | break; |
424 | case 0x05: |
425 | aux_rd_interval_us = 32000; |
426 | break; |
427 | case 0x06: |
428 | aux_rd_interval_us = 64000; |
429 | break; |
430 | default: |
431 | break; |
432 | } |
433 | |
434 | return aux_rd_interval_us; |
435 | } |
436 | |
437 | enum link_training_result dp_get_cr_failure(enum dc_lane_count ln_count, |
438 | union lane_status *dpcd_lane_status) |
439 | { |
440 | enum link_training_result result = LINK_TRAINING_SUCCESS; |
441 | |
442 | if (ln_count >= LANE_COUNT_ONE && !dpcd_lane_status[0].bits.CR_DONE_0) |
443 | result = LINK_TRAINING_CR_FAIL_LANE0; |
444 | else if (ln_count >= LANE_COUNT_TWO && !dpcd_lane_status[1].bits.CR_DONE_0) |
445 | result = LINK_TRAINING_CR_FAIL_LANE1; |
446 | else if (ln_count >= LANE_COUNT_FOUR && !dpcd_lane_status[2].bits.CR_DONE_0) |
447 | result = LINK_TRAINING_CR_FAIL_LANE23; |
448 | else if (ln_count >= LANE_COUNT_FOUR && !dpcd_lane_status[3].bits.CR_DONE_0) |
449 | result = LINK_TRAINING_CR_FAIL_LANE23; |
450 | return result; |
451 | } |
452 | |
453 | bool is_repeater(const struct link_training_settings *lt_settings, uint32_t offset) |
454 | { |
455 | return (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) && (offset != 0); |
456 | } |
457 | |
458 | bool dp_is_max_vs_reached( |
459 | const struct link_training_settings *lt_settings) |
460 | { |
461 | uint32_t lane; |
462 | for (lane = 0; lane < |
463 | (uint32_t)(lt_settings->link_settings.lane_count); |
464 | lane++) { |
465 | if (lt_settings->dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET |
466 | == VOLTAGE_SWING_MAX_LEVEL) |
467 | return true; |
468 | } |
469 | return false; |
470 | |
471 | } |
472 | |
473 | bool dp_is_cr_done(enum dc_lane_count ln_count, |
474 | union lane_status *dpcd_lane_status) |
475 | { |
476 | bool done = true; |
477 | uint32_t lane; |
478 | /*LANEx_CR_DONE bits All 1's?*/ |
479 | for (lane = 0; lane < (uint32_t)(ln_count); lane++) { |
480 | if (!dpcd_lane_status[lane].bits.CR_DONE_0) |
481 | done = false; |
482 | } |
483 | return done; |
484 | |
485 | } |
486 | |
487 | bool dp_is_ch_eq_done(enum dc_lane_count ln_count, |
488 | union lane_status *dpcd_lane_status) |
489 | { |
490 | bool done = true; |
491 | uint32_t lane; |
492 | for (lane = 0; lane < (uint32_t)(ln_count); lane++) |
493 | if (!dpcd_lane_status[lane].bits.CHANNEL_EQ_DONE_0) |
494 | done = false; |
495 | return done; |
496 | } |
497 | |
498 | bool dp_is_symbol_locked(enum dc_lane_count ln_count, |
499 | union lane_status *dpcd_lane_status) |
500 | { |
501 | bool locked = true; |
502 | uint32_t lane; |
503 | for (lane = 0; lane < (uint32_t)(ln_count); lane++) |
504 | if (!dpcd_lane_status[lane].bits.SYMBOL_LOCKED_0) |
505 | locked = false; |
506 | return locked; |
507 | } |
508 | |
509 | bool dp_is_interlane_aligned(union lane_align_status_updated align_status) |
510 | { |
511 | return align_status.bits.INTERLANE_ALIGN_DONE == 1; |
512 | } |
513 | |
514 | enum link_training_result dp_check_link_loss_status( |
515 | struct dc_link *link, |
516 | const struct link_training_settings *link_training_setting) |
517 | { |
518 | enum link_training_result status = LINK_TRAINING_SUCCESS; |
519 | union lane_status lane_status; |
520 | union lane_align_status_updated dpcd_lane_status_updated; |
521 | uint8_t dpcd_buf[6] = {0}; |
522 | uint32_t lane; |
523 | |
524 | core_link_read_dpcd( |
525 | link, |
526 | DP_SINK_COUNT, |
527 | data: (uint8_t *)(dpcd_buf), |
528 | size: sizeof(dpcd_buf)); |
529 | |
530 | /*parse lane status*/ |
531 | for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) { |
532 | /* |
533 | * check lanes status |
534 | */ |
535 | lane_status.raw = dp_get_nibble_at_index(buf: &dpcd_buf[2], index: lane); |
536 | dpcd_lane_status_updated.raw = dpcd_buf[4]; |
537 | |
538 | if (!lane_status.bits.CHANNEL_EQ_DONE_0 || |
539 | !lane_status.bits.CR_DONE_0 || |
540 | !lane_status.bits.SYMBOL_LOCKED_0 || |
541 | !dp_is_interlane_aligned(align_status: dpcd_lane_status_updated)) { |
542 | /* if one of the channel equalization, clock |
543 | * recovery or symbol lock is dropped |
544 | * consider it as (link has been |
545 | * dropped) dp sink status has changed |
546 | */ |
547 | status = LINK_TRAINING_LINK_LOSS; |
548 | break; |
549 | } |
550 | } |
551 | |
552 | return status; |
553 | } |
554 | |
555 | enum dc_status dp_get_lane_status_and_lane_adjust( |
556 | struct dc_link *link, |
557 | const struct link_training_settings *link_training_setting, |
558 | union lane_status ln_status[LANE_COUNT_DP_MAX], |
559 | union lane_align_status_updated *ln_align, |
560 | union lane_adjust ln_adjust[LANE_COUNT_DP_MAX], |
561 | uint32_t offset) |
562 | { |
563 | unsigned int lane01_status_address = DP_LANE0_1_STATUS; |
564 | uint8_t lane_adjust_offset = 4; |
565 | unsigned int lane01_adjust_address; |
566 | uint8_t dpcd_buf[6] = {0}; |
567 | uint32_t lane; |
568 | enum dc_status status; |
569 | |
570 | if (is_repeater(lt_settings: link_training_setting, offset)) { |
571 | lane01_status_address = |
572 | DP_LANE0_1_STATUS_PHY_REPEATER1 + |
573 | ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); |
574 | lane_adjust_offset = 3; |
575 | } |
576 | |
577 | status = core_link_read_dpcd( |
578 | link, |
579 | address: lane01_status_address, |
580 | data: (uint8_t *)(dpcd_buf), |
581 | size: sizeof(dpcd_buf)); |
582 | |
583 | if (status != DC_OK) { |
584 | DC_LOG_HW_LINK_TRAINING("%s:\n Failed to read from address 0x%X," |
585 | " keep current lane status and lane adjust unchanged" , |
586 | __func__, |
587 | lane01_status_address); |
588 | return status; |
589 | } |
590 | |
591 | for (lane = 0; lane < |
592 | (uint32_t)(link_training_setting->link_settings.lane_count); |
593 | lane++) { |
594 | |
595 | ln_status[lane].raw = |
596 | dp_get_nibble_at_index(buf: &dpcd_buf[0], index: lane); |
597 | ln_adjust[lane].raw = |
598 | dp_get_nibble_at_index(buf: &dpcd_buf[lane_adjust_offset], index: lane); |
599 | } |
600 | |
601 | ln_align->raw = dpcd_buf[2]; |
602 | |
603 | if (is_repeater(lt_settings: link_training_setting, offset)) { |
604 | DC_LOG_HW_LINK_TRAINING("%s:\n LTTPR Repeater ID: %d\n" |
605 | " 0x%X Lane01Status = %x\n 0x%X Lane23Status = %x\n " , |
606 | __func__, |
607 | offset, |
608 | lane01_status_address, dpcd_buf[0], |
609 | lane01_status_address + 1, dpcd_buf[1]); |
610 | |
611 | lane01_adjust_address = DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1 + |
612 | ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); |
613 | |
614 | DC_LOG_HW_LINK_TRAINING("%s:\n LTTPR Repeater ID: %d\n" |
615 | " 0x%X Lane01AdjustRequest = %x\n 0x%X Lane23AdjustRequest = %x\n" , |
616 | __func__, |
617 | offset, |
618 | lane01_adjust_address, |
619 | dpcd_buf[lane_adjust_offset], |
620 | lane01_adjust_address + 1, |
621 | dpcd_buf[lane_adjust_offset + 1]); |
622 | } else { |
623 | DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X Lane01Status = %x\n 0x%X Lane23Status = %x\n " , |
624 | __func__, |
625 | lane01_status_address, dpcd_buf[0], |
626 | lane01_status_address + 1, dpcd_buf[1]); |
627 | |
628 | lane01_adjust_address = DP_ADJUST_REQUEST_LANE0_1; |
629 | |
630 | DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X Lane01AdjustRequest = %x\n 0x%X Lane23AdjustRequest = %x\n" , |
631 | __func__, |
632 | lane01_adjust_address, |
633 | dpcd_buf[lane_adjust_offset], |
634 | lane01_adjust_address + 1, |
635 | dpcd_buf[lane_adjust_offset + 1]); |
636 | } |
637 | |
638 | return status; |
639 | } |
640 | |
641 | static void override_lane_settings(const struct link_training_settings *lt_settings, |
642 | struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]) |
643 | { |
644 | uint32_t lane; |
645 | |
646 | if (lt_settings->voltage_swing == NULL && |
647 | lt_settings->pre_emphasis == NULL && |
648 | lt_settings->ffe_preset == NULL && |
649 | lt_settings->post_cursor2 == NULL) |
650 | |
651 | return; |
652 | |
653 | for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { |
654 | if (lt_settings->voltage_swing) |
655 | lane_settings[lane].VOLTAGE_SWING = *lt_settings->voltage_swing; |
656 | if (lt_settings->pre_emphasis) |
657 | lane_settings[lane].PRE_EMPHASIS = *lt_settings->pre_emphasis; |
658 | if (lt_settings->post_cursor2) |
659 | lane_settings[lane].POST_CURSOR2 = *lt_settings->post_cursor2; |
660 | if (lt_settings->ffe_preset) |
661 | lane_settings[lane].FFE_PRESET = *lt_settings->ffe_preset; |
662 | } |
663 | } |
664 | |
665 | void dp_get_lttpr_mode_override(struct dc_link *link, enum lttpr_mode *override) |
666 | { |
667 | if (!dp_is_lttpr_present(link)) |
668 | return; |
669 | |
670 | if (link->dc->debug.lttpr_mode_override == LTTPR_MODE_TRANSPARENT) { |
671 | *override = LTTPR_MODE_TRANSPARENT; |
672 | } else if (link->dc->debug.lttpr_mode_override == LTTPR_MODE_NON_TRANSPARENT) { |
673 | *override = LTTPR_MODE_NON_TRANSPARENT; |
674 | } else if (link->dc->debug.lttpr_mode_override == LTTPR_MODE_NON_LTTPR) { |
675 | *override = LTTPR_MODE_NON_LTTPR; |
676 | } |
677 | DC_LOG_DC("lttpr_mode_override chose LTTPR_MODE = %d\n" , (uint8_t)(*override)); |
678 | } |
679 | |
680 | void override_training_settings( |
681 | struct dc_link *link, |
682 | const struct dc_link_training_overrides *overrides, |
683 | struct link_training_settings *lt_settings) |
684 | { |
685 | uint32_t lane; |
686 | |
687 | /* Override link spread */ |
688 | if (!link->dp_ss_off && overrides->downspread != NULL) |
689 | lt_settings->link_settings.link_spread = *overrides->downspread ? |
690 | LINK_SPREAD_05_DOWNSPREAD_30KHZ |
691 | : LINK_SPREAD_DISABLED; |
692 | |
693 | /* Override lane settings */ |
694 | if (overrides->voltage_swing != NULL) |
695 | lt_settings->voltage_swing = overrides->voltage_swing; |
696 | if (overrides->pre_emphasis != NULL) |
697 | lt_settings->pre_emphasis = overrides->pre_emphasis; |
698 | if (overrides->post_cursor2 != NULL) |
699 | lt_settings->post_cursor2 = overrides->post_cursor2; |
700 | if (overrides->ffe_preset != NULL) |
701 | lt_settings->ffe_preset = overrides->ffe_preset; |
702 | /* Override HW lane settings with BIOS forced values if present */ |
703 | if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && |
704 | lt_settings->lttpr_mode == LTTPR_MODE_TRANSPARENT) { |
705 | lt_settings->voltage_swing = &link->bios_forced_drive_settings.VOLTAGE_SWING; |
706 | lt_settings->pre_emphasis = &link->bios_forced_drive_settings.PRE_EMPHASIS; |
707 | lt_settings->always_match_dpcd_with_hw_lane_settings = false; |
708 | } |
709 | for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { |
710 | lt_settings->hw_lane_settings[lane].VOLTAGE_SWING = |
711 | lt_settings->voltage_swing != NULL ? |
712 | *lt_settings->voltage_swing : |
713 | VOLTAGE_SWING_LEVEL0; |
714 | lt_settings->hw_lane_settings[lane].PRE_EMPHASIS = |
715 | lt_settings->pre_emphasis != NULL ? |
716 | *lt_settings->pre_emphasis |
717 | : PRE_EMPHASIS_DISABLED; |
718 | lt_settings->hw_lane_settings[lane].POST_CURSOR2 = |
719 | lt_settings->post_cursor2 != NULL ? |
720 | *lt_settings->post_cursor2 |
721 | : POST_CURSOR2_DISABLED; |
722 | } |
723 | |
724 | if (lt_settings->always_match_dpcd_with_hw_lane_settings) |
725 | dp_hw_to_dpcd_lane_settings(lt_settings, |
726 | hw_lane_settings: lt_settings->hw_lane_settings, dpcd_lane_settings: lt_settings->dpcd_lane_settings); |
727 | |
728 | /* Override training timings */ |
729 | if (overrides->cr_pattern_time != NULL) |
730 | lt_settings->cr_pattern_time = *overrides->cr_pattern_time; |
731 | if (overrides->eq_pattern_time != NULL) |
732 | lt_settings->eq_pattern_time = *overrides->eq_pattern_time; |
733 | if (overrides->pattern_for_cr != NULL) |
734 | lt_settings->pattern_for_cr = *overrides->pattern_for_cr; |
735 | if (overrides->pattern_for_eq != NULL) |
736 | lt_settings->pattern_for_eq = *overrides->pattern_for_eq; |
737 | if (overrides->enhanced_framing != NULL) |
738 | lt_settings->enhanced_framing = *overrides->enhanced_framing; |
739 | if (link->preferred_training_settings.fec_enable != NULL) |
740 | lt_settings->should_set_fec_ready = *link->preferred_training_settings.fec_enable; |
741 | |
742 | /* Check DP tunnel LTTPR mode debug option. */ |
743 | if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->dc->debug.dpia_debug.bits.force_non_lttpr) |
744 | lt_settings->lttpr_mode = LTTPR_MODE_NON_LTTPR; |
745 | |
746 | dp_get_lttpr_mode_override(link, override: <_settings->lttpr_mode); |
747 | |
748 | } |
749 | |
750 | enum dc_dp_training_pattern decide_cr_training_pattern( |
751 | const struct dc_link_settings *link_settings) |
752 | { |
753 | switch (link_dp_get_encoding_format(link_settings)) { |
754 | case DP_8b_10b_ENCODING: |
755 | default: |
756 | return DP_TRAINING_PATTERN_SEQUENCE_1; |
757 | case DP_128b_132b_ENCODING: |
758 | return DP_128b_132b_TPS1; |
759 | } |
760 | } |
761 | |
762 | enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *link, |
763 | const struct dc_link_settings *link_settings) |
764 | { |
765 | struct link_encoder *link_enc; |
766 | struct encoder_feature_support *enc_caps; |
767 | struct dpcd_caps *rx_caps = &link->dpcd_caps; |
768 | enum dc_dp_training_pattern pattern = DP_TRAINING_PATTERN_SEQUENCE_2; |
769 | |
770 | link_enc = link_enc_cfg_get_link_enc(link); |
771 | ASSERT(link_enc); |
772 | enc_caps = &link_enc->features; |
773 | |
774 | switch (link_dp_get_encoding_format(link_settings)) { |
775 | case DP_8b_10b_ENCODING: |
776 | if (enc_caps->flags.bits.IS_TPS4_CAPABLE && |
777 | rx_caps->max_down_spread.bits.TPS4_SUPPORTED) |
778 | pattern = DP_TRAINING_PATTERN_SEQUENCE_4; |
779 | else if (enc_caps->flags.bits.IS_TPS3_CAPABLE && |
780 | rx_caps->max_ln_count.bits.TPS3_SUPPORTED) |
781 | pattern = DP_TRAINING_PATTERN_SEQUENCE_3; |
782 | else |
783 | pattern = DP_TRAINING_PATTERN_SEQUENCE_2; |
784 | break; |
785 | case DP_128b_132b_ENCODING: |
786 | pattern = DP_128b_132b_TPS2; |
787 | break; |
788 | default: |
789 | pattern = DP_TRAINING_PATTERN_SEQUENCE_2; |
790 | break; |
791 | } |
792 | return pattern; |
793 | } |
794 | |
795 | enum lttpr_mode dp_decide_lttpr_mode(struct dc_link *link, |
796 | struct dc_link_settings *link_setting) |
797 | { |
798 | enum dp_link_encoding encoding = link_dp_get_encoding_format(link_settings: link_setting); |
799 | |
800 | if (encoding == DP_8b_10b_ENCODING) |
801 | return dp_decide_8b_10b_lttpr_mode(link); |
802 | else if (encoding == DP_128b_132b_ENCODING) |
803 | return dp_decide_128b_132b_lttpr_mode(link); |
804 | |
805 | ASSERT(0); |
806 | return LTTPR_MODE_NON_LTTPR; |
807 | } |
808 | |
809 | void dp_decide_lane_settings( |
810 | const struct link_training_settings *lt_settings, |
811 | const union lane_adjust ln_adjust[LANE_COUNT_DP_MAX], |
812 | struct dc_lane_settings hw_lane_settings[LANE_COUNT_DP_MAX], |
813 | union dpcd_training_lane *dpcd_lane_settings) |
814 | { |
815 | uint32_t lane; |
816 | |
817 | for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { |
818 | if (link_dp_get_encoding_format(link_settings: <_settings->link_settings) == |
819 | DP_8b_10b_ENCODING) { |
820 | hw_lane_settings[lane].VOLTAGE_SWING = |
821 | (enum dc_voltage_swing)(ln_adjust[lane].bits. |
822 | VOLTAGE_SWING_LANE); |
823 | hw_lane_settings[lane].PRE_EMPHASIS = |
824 | (enum dc_pre_emphasis)(ln_adjust[lane].bits. |
825 | PRE_EMPHASIS_LANE); |
826 | } else if (link_dp_get_encoding_format(link_settings: <_settings->link_settings) == |
827 | DP_128b_132b_ENCODING) { |
828 | hw_lane_settings[lane].FFE_PRESET.raw = |
829 | ln_adjust[lane].tx_ffe.PRESET_VALUE; |
830 | } |
831 | } |
832 | dp_hw_to_dpcd_lane_settings(lt_settings, hw_lane_settings, dpcd_lane_settings); |
833 | |
834 | if (lt_settings->disallow_per_lane_settings) { |
835 | /* we find the maximum of the requested settings across all lanes*/ |
836 | /* and set this maximum for all lanes*/ |
837 | maximize_lane_settings(lt_settings, lane_settings: hw_lane_settings); |
838 | override_lane_settings(lt_settings, lane_settings: hw_lane_settings); |
839 | |
840 | if (lt_settings->always_match_dpcd_with_hw_lane_settings) |
841 | dp_hw_to_dpcd_lane_settings(lt_settings, hw_lane_settings, dpcd_lane_settings); |
842 | } |
843 | |
844 | } |
845 | |
846 | void dp_decide_training_settings( |
847 | struct dc_link *link, |
848 | const struct dc_link_settings *link_settings, |
849 | struct link_training_settings *lt_settings) |
850 | { |
851 | if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) |
852 | decide_8b_10b_training_settings(link, link_setting: link_settings, lt_settings); |
853 | else if (link_dp_get_encoding_format(link_settings) == DP_128b_132b_ENCODING) |
854 | decide_128b_132b_training_settings(link, link_settings, lt_settings); |
855 | } |
856 | |
857 | |
858 | enum dc_status configure_lttpr_mode_transparent(struct dc_link *link) |
859 | { |
860 | uint8_t repeater_mode = DP_PHY_REPEATER_MODE_TRANSPARENT; |
861 | |
862 | DC_LOG_HW_LINK_TRAINING("%s\n Set LTTPR to Transparent Mode\n" , __func__); |
863 | return core_link_write_dpcd(link, |
864 | DP_PHY_REPEATER_MODE, |
865 | data: (uint8_t *)&repeater_mode, |
866 | size: sizeof(repeater_mode)); |
867 | } |
868 | |
869 | static enum dc_status configure_lttpr_mode_non_transparent( |
870 | struct dc_link *link, |
871 | const struct link_training_settings *lt_settings) |
872 | { |
873 | /* aux timeout is already set to extended */ |
874 | /* RESET/SET lttpr mode to enable non transparent mode */ |
875 | uint8_t repeater_cnt; |
876 | uint32_t aux_interval_address; |
877 | uint8_t repeater_id; |
878 | enum dc_status result = DC_ERROR_UNEXPECTED; |
879 | uint8_t repeater_mode = DP_PHY_REPEATER_MODE_TRANSPARENT; |
880 | const struct dc *dc = link->dc; |
881 | |
882 | enum dp_link_encoding encoding = dc->link_srv->dp_get_encoding_format(<_settings->link_settings); |
883 | |
884 | if (encoding == DP_8b_10b_ENCODING) { |
885 | DC_LOG_HW_LINK_TRAINING("%s\n Set LTTPR to Transparent Mode\n" , __func__); |
886 | result = core_link_write_dpcd(link, |
887 | DP_PHY_REPEATER_MODE, |
888 | data: (uint8_t *)&repeater_mode, |
889 | size: sizeof(repeater_mode)); |
890 | |
891 | } |
892 | |
893 | if (result == DC_OK) { |
894 | link->dpcd_caps.lttpr_caps.mode = repeater_mode; |
895 | } |
896 | |
897 | if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) { |
898 | |
899 | DC_LOG_HW_LINK_TRAINING("%s\n Set LTTPR to Non Transparent Mode\n" , __func__); |
900 | |
901 | repeater_mode = DP_PHY_REPEATER_MODE_NON_TRANSPARENT; |
902 | result = core_link_write_dpcd(link, |
903 | DP_PHY_REPEATER_MODE, |
904 | data: (uint8_t *)&repeater_mode, |
905 | size: sizeof(repeater_mode)); |
906 | |
907 | if (result == DC_OK) { |
908 | link->dpcd_caps.lttpr_caps.mode = repeater_mode; |
909 | } |
910 | |
911 | if (encoding == DP_8b_10b_ENCODING) { |
912 | repeater_cnt = dp_parse_lttpr_repeater_count(lttpr_repeater_count: link->dpcd_caps.lttpr_caps.phy_repeater_cnt); |
913 | |
914 | /* Driver does not need to train the first hop. Skip DPCD read and clear |
915 | * AUX_RD_INTERVAL for DPTX-to-DPIA hop. |
916 | */ |
917 | if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) |
918 | link->dpcd_caps.lttpr_caps.aux_rd_interval[--repeater_cnt] = 0; |
919 | |
920 | for (repeater_id = repeater_cnt; repeater_id > 0; repeater_id--) { |
921 | aux_interval_address = DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 + |
922 | ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (repeater_id - 1)); |
923 | core_link_read_dpcd( |
924 | link, |
925 | address: aux_interval_address, |
926 | data: (uint8_t *)&link->dpcd_caps.lttpr_caps.aux_rd_interval[repeater_id - 1], |
927 | size: sizeof(link->dpcd_caps.lttpr_caps.aux_rd_interval[repeater_id - 1])); |
928 | link->dpcd_caps.lttpr_caps.aux_rd_interval[repeater_id - 1] &= 0x7F; |
929 | } |
930 | } |
931 | } |
932 | |
933 | return result; |
934 | } |
935 | |
936 | enum dc_status dpcd_configure_lttpr_mode(struct dc_link *link, struct link_training_settings *lt_settings) |
937 | { |
938 | enum dc_status status = DC_OK; |
939 | |
940 | if (lt_settings->lttpr_mode == LTTPR_MODE_TRANSPARENT) |
941 | status = configure_lttpr_mode_transparent(link); |
942 | |
943 | else if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) |
944 | status = configure_lttpr_mode_non_transparent(link, lt_settings); |
945 | |
946 | return status; |
947 | } |
948 | |
949 | void repeater_training_done(struct dc_link *link, uint32_t offset) |
950 | { |
951 | union dpcd_training_pattern dpcd_pattern = {0}; |
952 | |
953 | const uint32_t dpcd_base_lt_offset = |
954 | DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + |
955 | ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); |
956 | /* Set training not in progress*/ |
957 | dpcd_pattern.v1_4.TRAINING_PATTERN_SET = DPCD_TRAINING_PATTERN_VIDEOIDLE; |
958 | |
959 | core_link_write_dpcd( |
960 | link, |
961 | address: dpcd_base_lt_offset, |
962 | data: &dpcd_pattern.raw, |
963 | size: 1); |
964 | |
965 | DC_LOG_HW_LINK_TRAINING("%s\n LTTPR Id: %d 0x%X pattern = %x\n" , |
966 | __func__, |
967 | offset, |
968 | dpcd_base_lt_offset, |
969 | dpcd_pattern.v1_4.TRAINING_PATTERN_SET); |
970 | } |
971 | |
972 | static void dpcd_exit_training_mode(struct dc_link *link, enum dp_link_encoding encoding) |
973 | { |
974 | uint8_t sink_status = 0; |
975 | uint8_t i; |
976 | |
977 | /* clear training pattern set */ |
978 | dpcd_set_training_pattern(link, training_pattern: DP_TRAINING_PATTERN_VIDEOIDLE); |
979 | |
980 | if (encoding == DP_128b_132b_ENCODING) { |
981 | /* poll for intra-hop disable */ |
982 | for (i = 0; i < 10; i++) { |
983 | if ((core_link_read_dpcd(link, DP_SINK_STATUS, data: &sink_status, size: 1) == DC_OK) && |
984 | (sink_status & DP_INTRA_HOP_AUX_REPLY_INDICATION) == 0) |
985 | break; |
986 | fsleep(usecs: 1000); |
987 | } |
988 | } |
989 | } |
990 | |
991 | enum dc_status dpcd_configure_channel_coding(struct dc_link *link, |
992 | struct link_training_settings *lt_settings) |
993 | { |
994 | enum dp_link_encoding encoding = |
995 | link_dp_get_encoding_format( |
996 | link_settings: <_settings->link_settings); |
997 | enum dc_status status; |
998 | |
999 | status = core_link_write_dpcd( |
1000 | link, |
1001 | DP_MAIN_LINK_CHANNEL_CODING_SET, |
1002 | data: (uint8_t *) &encoding, |
1003 | size: 1); |
1004 | DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X MAIN_LINK_CHANNEL_CODING_SET = %x\n" , |
1005 | __func__, |
1006 | DP_MAIN_LINK_CHANNEL_CODING_SET, |
1007 | encoding); |
1008 | |
1009 | return status; |
1010 | } |
1011 | |
1012 | void dpcd_set_training_pattern( |
1013 | struct dc_link *link, |
1014 | enum dc_dp_training_pattern training_pattern) |
1015 | { |
1016 | union dpcd_training_pattern dpcd_pattern = {0}; |
1017 | |
1018 | dpcd_pattern.v1_4.TRAINING_PATTERN_SET = |
1019 | dp_training_pattern_to_dpcd_training_pattern( |
1020 | link, pattern: training_pattern); |
1021 | |
1022 | core_link_write_dpcd( |
1023 | link, |
1024 | DP_TRAINING_PATTERN_SET, |
1025 | data: &dpcd_pattern.raw, |
1026 | size: 1); |
1027 | |
1028 | DC_LOG_HW_LINK_TRAINING("%s\n %x pattern = %x\n" , |
1029 | __func__, |
1030 | DP_TRAINING_PATTERN_SET, |
1031 | dpcd_pattern.v1_4.TRAINING_PATTERN_SET); |
1032 | } |
1033 | |
1034 | enum dc_status dpcd_set_link_settings( |
1035 | struct dc_link *link, |
1036 | const struct link_training_settings *lt_settings) |
1037 | { |
1038 | uint8_t rate; |
1039 | enum dc_status status; |
1040 | |
1041 | union down_spread_ctrl downspread = {0}; |
1042 | union lane_count_set lane_count_set = {0}; |
1043 | |
1044 | downspread.raw = (uint8_t) |
1045 | (lt_settings->link_settings.link_spread); |
1046 | |
1047 | lane_count_set.bits.LANE_COUNT_SET = |
1048 | lt_settings->link_settings.lane_count; |
1049 | |
1050 | lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing; |
1051 | lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0; |
1052 | |
1053 | |
1054 | if (link->ep_type == DISPLAY_ENDPOINT_PHY && |
1055 | lt_settings->pattern_for_eq < DP_TRAINING_PATTERN_SEQUENCE_4) { |
1056 | lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = |
1057 | link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED; |
1058 | } |
1059 | |
1060 | status = core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, |
1061 | data: &downspread.raw, size: sizeof(downspread)); |
1062 | |
1063 | status = core_link_write_dpcd(link, DP_LANE_COUNT_SET, |
1064 | data: &lane_count_set.raw, size: 1); |
1065 | |
1066 | if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_13 && |
1067 | lt_settings->link_settings.use_link_rate_set == true) { |
1068 | rate = 0; |
1069 | /* WA for some MUX chips that will power down with eDP and lose supported |
1070 | * link rate set for eDP 1.4. Source reads DPCD 0x010 again to ensure |
1071 | * MUX chip gets link rate set back before link training. |
1072 | */ |
1073 | if (link->connector_signal == SIGNAL_TYPE_EDP) { |
1074 | uint8_t supported_link_rates[16]; |
1075 | |
1076 | core_link_read_dpcd(link, DP_SUPPORTED_LINK_RATES, |
1077 | data: supported_link_rates, size: sizeof(supported_link_rates)); |
1078 | } |
1079 | status = core_link_write_dpcd(link, DP_LINK_BW_SET, data: &rate, size: 1); |
1080 | status = core_link_write_dpcd(link, DP_LINK_RATE_SET, |
1081 | data: <_settings->link_settings.link_rate_set, size: 1); |
1082 | } else { |
1083 | rate = get_dpcd_link_rate(link_settings: <_settings->link_settings); |
1084 | |
1085 | status = core_link_write_dpcd(link, DP_LINK_BW_SET, data: &rate, size: 1); |
1086 | } |
1087 | |
1088 | if (rate) { |
1089 | DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x framing = %x\n %x spread = %x\n" , |
1090 | __func__, |
1091 | DP_LINK_BW_SET, |
1092 | lt_settings->link_settings.link_rate, |
1093 | DP_LANE_COUNT_SET, |
1094 | lt_settings->link_settings.lane_count, |
1095 | lt_settings->enhanced_framing, |
1096 | DP_DOWNSPREAD_CTRL, |
1097 | lt_settings->link_settings.link_spread); |
1098 | } else { |
1099 | DC_LOG_HW_LINK_TRAINING("%s\n %x rate set = %x\n %x lane = %x framing = %x\n %x spread = %x\n" , |
1100 | __func__, |
1101 | DP_LINK_RATE_SET, |
1102 | lt_settings->link_settings.link_rate_set, |
1103 | DP_LANE_COUNT_SET, |
1104 | lt_settings->link_settings.lane_count, |
1105 | lt_settings->enhanced_framing, |
1106 | DP_DOWNSPREAD_CTRL, |
1107 | lt_settings->link_settings.link_spread); |
1108 | } |
1109 | |
1110 | return status; |
1111 | } |
1112 | |
1113 | enum dc_status dpcd_set_lane_settings( |
1114 | struct dc_link *link, |
1115 | const struct link_training_settings *link_training_setting, |
1116 | uint32_t offset) |
1117 | { |
1118 | unsigned int lane0_set_address; |
1119 | enum dc_status status; |
1120 | lane0_set_address = DP_TRAINING_LANE0_SET; |
1121 | |
1122 | if (is_repeater(lt_settings: link_training_setting, offset)) |
1123 | lane0_set_address = DP_TRAINING_LANE0_SET_PHY_REPEATER1 + |
1124 | ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); |
1125 | |
1126 | status = core_link_write_dpcd(link, |
1127 | address: lane0_set_address, |
1128 | data: (uint8_t *)(link_training_setting->dpcd_lane_settings), |
1129 | size: link_training_setting->link_settings.lane_count); |
1130 | |
1131 | if (is_repeater(lt_settings: link_training_setting, offset)) { |
1132 | DC_LOG_HW_LINK_TRAINING("%s\n LTTPR Repeater ID: %d\n" |
1133 | " 0x%X VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n" , |
1134 | __func__, |
1135 | offset, |
1136 | lane0_set_address, |
1137 | link_training_setting->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET, |
1138 | link_training_setting->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET, |
1139 | link_training_setting->dpcd_lane_settings[0].bits.MAX_SWING_REACHED, |
1140 | link_training_setting->dpcd_lane_settings[0].bits.MAX_PRE_EMPHASIS_REACHED); |
1141 | |
1142 | } else { |
1143 | DC_LOG_HW_LINK_TRAINING("%s\n 0x%X VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n" , |
1144 | __func__, |
1145 | lane0_set_address, |
1146 | link_training_setting->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET, |
1147 | link_training_setting->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET, |
1148 | link_training_setting->dpcd_lane_settings[0].bits.MAX_SWING_REACHED, |
1149 | link_training_setting->dpcd_lane_settings[0].bits.MAX_PRE_EMPHASIS_REACHED); |
1150 | } |
1151 | |
1152 | return status; |
1153 | } |
1154 | |
1155 | void dpcd_set_lt_pattern_and_lane_settings( |
1156 | struct dc_link *link, |
1157 | const struct link_training_settings *lt_settings, |
1158 | enum dc_dp_training_pattern pattern, |
1159 | uint32_t offset) |
1160 | { |
1161 | uint32_t dpcd_base_lt_offset; |
1162 | uint8_t dpcd_lt_buffer[5] = {0}; |
1163 | union dpcd_training_pattern dpcd_pattern = {0}; |
1164 | uint32_t size_in_bytes; |
1165 | bool edp_workaround = false; /* TODO link_prop.INTERNAL */ |
1166 | dpcd_base_lt_offset = DP_TRAINING_PATTERN_SET; |
1167 | |
1168 | if (is_repeater(lt_settings, offset)) |
1169 | dpcd_base_lt_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + |
1170 | ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1)); |
1171 | |
1172 | /***************************************************************** |
1173 | * DpcdAddress_TrainingPatternSet |
1174 | *****************************************************************/ |
1175 | dpcd_pattern.v1_4.TRAINING_PATTERN_SET = |
1176 | dp_training_pattern_to_dpcd_training_pattern(link, pattern); |
1177 | |
1178 | dpcd_pattern.v1_4.SCRAMBLING_DISABLE = |
1179 | dp_initialize_scrambling_data_symbols(link, pattern); |
1180 | |
1181 | dpcd_lt_buffer[DP_TRAINING_PATTERN_SET - DP_TRAINING_PATTERN_SET] |
1182 | = dpcd_pattern.raw; |
1183 | |
1184 | if (is_repeater(lt_settings, offset)) { |
1185 | DC_LOG_HW_LINK_TRAINING("%s\n LTTPR Repeater ID: %d\n 0x%X pattern = %x\n" , |
1186 | __func__, |
1187 | offset, |
1188 | dpcd_base_lt_offset, |
1189 | dpcd_pattern.v1_4.TRAINING_PATTERN_SET); |
1190 | } else { |
1191 | DC_LOG_HW_LINK_TRAINING("%s\n 0x%X pattern = %x\n" , |
1192 | __func__, |
1193 | dpcd_base_lt_offset, |
1194 | dpcd_pattern.v1_4.TRAINING_PATTERN_SET); |
1195 | } |
1196 | |
1197 | /* concatenate everything into one buffer*/ |
1198 | size_in_bytes = lt_settings->link_settings.lane_count * |
1199 | sizeof(lt_settings->dpcd_lane_settings[0]); |
1200 | |
1201 | // 0x00103 - 0x00102 |
1202 | memmove( |
1203 | &dpcd_lt_buffer[DP_TRAINING_LANE0_SET - DP_TRAINING_PATTERN_SET], |
1204 | lt_settings->dpcd_lane_settings, |
1205 | size_in_bytes); |
1206 | |
1207 | if (is_repeater(lt_settings, offset)) { |
1208 | if (link_dp_get_encoding_format(link_settings: <_settings->link_settings) == |
1209 | DP_128b_132b_ENCODING) |
1210 | DC_LOG_HW_LINK_TRAINING("%s:\n LTTPR Repeater ID: %d\n" |
1211 | " 0x%X TX_FFE_PRESET_VALUE = %x\n" , |
1212 | __func__, |
1213 | offset, |
1214 | dpcd_base_lt_offset, |
1215 | lt_settings->dpcd_lane_settings[0].tx_ffe.PRESET_VALUE); |
1216 | else if (link_dp_get_encoding_format(link_settings: <_settings->link_settings) == |
1217 | DP_8b_10b_ENCODING) |
1218 | DC_LOG_HW_LINK_TRAINING("%s:\n LTTPR Repeater ID: %d\n" |
1219 | " 0x%X VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n" , |
1220 | __func__, |
1221 | offset, |
1222 | dpcd_base_lt_offset, |
1223 | lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET, |
1224 | lt_settings->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET, |
1225 | lt_settings->dpcd_lane_settings[0].bits.MAX_SWING_REACHED, |
1226 | lt_settings->dpcd_lane_settings[0].bits.MAX_PRE_EMPHASIS_REACHED); |
1227 | } else { |
1228 | if (link_dp_get_encoding_format(link_settings: <_settings->link_settings) == |
1229 | DP_128b_132b_ENCODING) |
1230 | DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X TX_FFE_PRESET_VALUE = %x\n" , |
1231 | __func__, |
1232 | dpcd_base_lt_offset, |
1233 | lt_settings->dpcd_lane_settings[0].tx_ffe.PRESET_VALUE); |
1234 | else if (link_dp_get_encoding_format(link_settings: <_settings->link_settings) == |
1235 | DP_8b_10b_ENCODING) |
1236 | DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X VS set = %x PE set = %x max VS Reached = %x max PE Reached = %x\n" , |
1237 | __func__, |
1238 | dpcd_base_lt_offset, |
1239 | lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET, |
1240 | lt_settings->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET, |
1241 | lt_settings->dpcd_lane_settings[0].bits.MAX_SWING_REACHED, |
1242 | lt_settings->dpcd_lane_settings[0].bits.MAX_PRE_EMPHASIS_REACHED); |
1243 | } |
1244 | if (edp_workaround) { |
1245 | /* for eDP write in 2 parts because the 5-byte burst is |
1246 | * causing issues on some eDP panels (EPR#366724) |
1247 | */ |
1248 | core_link_write_dpcd( |
1249 | link, |
1250 | DP_TRAINING_PATTERN_SET, |
1251 | data: &dpcd_pattern.raw, |
1252 | size: sizeof(dpcd_pattern.raw)); |
1253 | |
1254 | core_link_write_dpcd( |
1255 | link, |
1256 | DP_TRAINING_LANE0_SET, |
1257 | data: (uint8_t *)(lt_settings->dpcd_lane_settings), |
1258 | size: size_in_bytes); |
1259 | |
1260 | } else if (link_dp_get_encoding_format(link_settings: <_settings->link_settings) == |
1261 | DP_128b_132b_ENCODING) { |
1262 | core_link_write_dpcd( |
1263 | link, |
1264 | address: dpcd_base_lt_offset, |
1265 | data: dpcd_lt_buffer, |
1266 | size: sizeof(dpcd_lt_buffer)); |
1267 | } else |
1268 | /* write it all in (1 + number-of-lanes)-byte burst*/ |
1269 | core_link_write_dpcd( |
1270 | link, |
1271 | address: dpcd_base_lt_offset, |
1272 | data: dpcd_lt_buffer, |
1273 | size: size_in_bytes + sizeof(dpcd_pattern.raw)); |
1274 | } |
1275 | |
1276 | void start_clock_recovery_pattern_early(struct dc_link *link, |
1277 | const struct link_resource *link_res, |
1278 | struct link_training_settings *lt_settings, |
1279 | uint32_t offset) |
1280 | { |
1281 | DC_LOG_HW_LINK_TRAINING("%s\n GPU sends TPS1. Wait 400us.\n" , |
1282 | __func__); |
1283 | dp_set_hw_training_pattern(link, link_res, pattern: lt_settings->pattern_for_cr, offset); |
1284 | dp_set_hw_lane_settings(link, link_res, link_settings: lt_settings, offset); |
1285 | udelay(400); |
1286 | } |
1287 | |
1288 | void dp_set_hw_test_pattern( |
1289 | struct dc_link *link, |
1290 | const struct link_resource *link_res, |
1291 | enum dp_test_pattern test_pattern, |
1292 | uint8_t *custom_pattern, |
1293 | uint32_t custom_pattern_size) |
1294 | { |
1295 | const struct link_hwss *link_hwss = get_link_hwss(link, link_res); |
1296 | struct encoder_set_dp_phy_pattern_param pattern_param = {0}; |
1297 | |
1298 | pattern_param.dp_phy_pattern = test_pattern; |
1299 | pattern_param.custom_pattern = custom_pattern; |
1300 | pattern_param.custom_pattern_size = custom_pattern_size; |
1301 | pattern_param.dp_panel_mode = dp_get_panel_mode(link); |
1302 | |
1303 | if (link_hwss->ext.set_dp_link_test_pattern) |
1304 | link_hwss->ext.set_dp_link_test_pattern(link, link_res, &pattern_param); |
1305 | } |
1306 | |
1307 | bool dp_set_hw_training_pattern( |
1308 | struct dc_link *link, |
1309 | const struct link_resource *link_res, |
1310 | enum dc_dp_training_pattern pattern, |
1311 | uint32_t offset) |
1312 | { |
1313 | enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED; |
1314 | |
1315 | switch (pattern) { |
1316 | case DP_TRAINING_PATTERN_SEQUENCE_1: |
1317 | test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN1; |
1318 | break; |
1319 | case DP_TRAINING_PATTERN_SEQUENCE_2: |
1320 | test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN2; |
1321 | break; |
1322 | case DP_TRAINING_PATTERN_SEQUENCE_3: |
1323 | test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN3; |
1324 | break; |
1325 | case DP_TRAINING_PATTERN_SEQUENCE_4: |
1326 | test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN4; |
1327 | break; |
1328 | case DP_128b_132b_TPS1: |
1329 | test_pattern = DP_TEST_PATTERN_128b_132b_TPS1_TRAINING_MODE; |
1330 | break; |
1331 | case DP_128b_132b_TPS2: |
1332 | test_pattern = DP_TEST_PATTERN_128b_132b_TPS2_TRAINING_MODE; |
1333 | break; |
1334 | default: |
1335 | break; |
1336 | } |
1337 | |
1338 | dp_set_hw_test_pattern(link, link_res, test_pattern, NULL, custom_pattern_size: 0); |
1339 | |
1340 | return true; |
1341 | } |
1342 | |
1343 | static bool perform_post_lt_adj_req_sequence( |
1344 | struct dc_link *link, |
1345 | const struct link_resource *link_res, |
1346 | struct link_training_settings *lt_settings) |
1347 | { |
1348 | enum dc_lane_count lane_count = |
1349 | lt_settings->link_settings.lane_count; |
1350 | |
1351 | uint32_t adj_req_count; |
1352 | uint32_t adj_req_timer; |
1353 | bool req_drv_setting_changed; |
1354 | uint32_t lane; |
1355 | union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0}; |
1356 | union lane_align_status_updated dpcd_lane_status_updated = {0}; |
1357 | union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0}; |
1358 | |
1359 | req_drv_setting_changed = false; |
1360 | for (adj_req_count = 0; adj_req_count < POST_LT_ADJ_REQ_LIMIT; |
1361 | adj_req_count++) { |
1362 | |
1363 | req_drv_setting_changed = false; |
1364 | |
1365 | for (adj_req_timer = 0; |
1366 | adj_req_timer < POST_LT_ADJ_REQ_TIMEOUT; |
1367 | adj_req_timer++) { |
1368 | |
1369 | dp_get_lane_status_and_lane_adjust( |
1370 | link, |
1371 | link_training_setting: lt_settings, |
1372 | ln_status: dpcd_lane_status, |
1373 | ln_align: &dpcd_lane_status_updated, |
1374 | ln_adjust: dpcd_lane_adjust, |
1375 | offset: DPRX); |
1376 | |
1377 | if (dpcd_lane_status_updated.bits. |
1378 | POST_LT_ADJ_REQ_IN_PROGRESS == 0) |
1379 | return true; |
1380 | |
1381 | if (!dp_is_cr_done(ln_count: lane_count, dpcd_lane_status)) |
1382 | return false; |
1383 | |
1384 | if (!dp_is_ch_eq_done(ln_count: lane_count, dpcd_lane_status) || |
1385 | !dp_is_symbol_locked(ln_count: lane_count, dpcd_lane_status) || |
1386 | !dp_is_interlane_aligned(align_status: dpcd_lane_status_updated)) |
1387 | return false; |
1388 | |
1389 | for (lane = 0; lane < (uint32_t)(lane_count); lane++) { |
1390 | |
1391 | if (lt_settings-> |
1392 | dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET != |
1393 | dpcd_lane_adjust[lane].bits.VOLTAGE_SWING_LANE || |
1394 | lt_settings->dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET != |
1395 | dpcd_lane_adjust[lane].bits.PRE_EMPHASIS_LANE) { |
1396 | |
1397 | req_drv_setting_changed = true; |
1398 | break; |
1399 | } |
1400 | } |
1401 | |
1402 | if (req_drv_setting_changed) { |
1403 | dp_decide_lane_settings(lt_settings, ln_adjust: dpcd_lane_adjust, |
1404 | hw_lane_settings: lt_settings->hw_lane_settings, dpcd_lane_settings: lt_settings->dpcd_lane_settings); |
1405 | |
1406 | dp_set_drive_settings(link, |
1407 | link_res, |
1408 | lt_settings); |
1409 | break; |
1410 | } |
1411 | |
1412 | msleep(msecs: 1); |
1413 | } |
1414 | |
1415 | if (!req_drv_setting_changed) { |
1416 | DC_LOG_WARNING("%s: Post Link Training Adjust Request Timed out\n" , |
1417 | __func__); |
1418 | |
1419 | ASSERT(0); |
1420 | return true; |
1421 | } |
1422 | } |
1423 | DC_LOG_WARNING("%s: Post Link Training Adjust Request limit reached\n" , |
1424 | __func__); |
1425 | |
1426 | ASSERT(0); |
1427 | return true; |
1428 | |
1429 | } |
1430 | |
1431 | static enum link_training_result dp_transition_to_video_idle( |
1432 | struct dc_link *link, |
1433 | const struct link_resource *link_res, |
1434 | struct link_training_settings *lt_settings, |
1435 | enum link_training_result status) |
1436 | { |
1437 | union lane_count_set lane_count_set = {0}; |
1438 | |
1439 | /* 4. mainlink output idle pattern*/ |
1440 | dp_set_hw_test_pattern(link, link_res, test_pattern: DP_TEST_PATTERN_VIDEO_MODE, NULL, custom_pattern_size: 0); |
1441 | |
1442 | /* |
1443 | * 5. post training adjust if required |
1444 | * If the upstream DPTX and downstream DPRX both support TPS4, |
1445 | * TPS4 must be used instead of POST_LT_ADJ_REQ. |
1446 | */ |
1447 | if (link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED != 1 || |
1448 | lt_settings->pattern_for_eq >= DP_TRAINING_PATTERN_SEQUENCE_4) { |
1449 | /* delay 5ms after Main Link output idle pattern and then check |
1450 | * DPCD 0202h. |
1451 | */ |
1452 | if (link->connector_signal != SIGNAL_TYPE_EDP && status == LINK_TRAINING_SUCCESS) { |
1453 | msleep(msecs: 5); |
1454 | status = dp_check_link_loss_status(link, link_training_setting: lt_settings); |
1455 | } |
1456 | return status; |
1457 | } |
1458 | |
1459 | if (status == LINK_TRAINING_SUCCESS && |
1460 | perform_post_lt_adj_req_sequence(link, link_res, lt_settings) == false) |
1461 | status = LINK_TRAINING_LQA_FAIL; |
1462 | |
1463 | lane_count_set.bits.LANE_COUNT_SET = lt_settings->link_settings.lane_count; |
1464 | lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing; |
1465 | lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0; |
1466 | |
1467 | core_link_write_dpcd( |
1468 | link, |
1469 | DP_LANE_COUNT_SET, |
1470 | data: &lane_count_set.raw, |
1471 | size: sizeof(lane_count_set)); |
1472 | |
1473 | return status; |
1474 | } |
1475 | |
1476 | enum link_training_result dp_perform_link_training( |
1477 | struct dc_link *link, |
1478 | const struct link_resource *link_res, |
1479 | const struct dc_link_settings *link_settings, |
1480 | bool skip_video_pattern) |
1481 | { |
1482 | enum link_training_result status = LINK_TRAINING_SUCCESS; |
1483 | struct link_training_settings lt_settings = {0}; |
1484 | enum dp_link_encoding encoding = |
1485 | link_dp_get_encoding_format(link_settings); |
1486 | |
1487 | /* decide training settings */ |
1488 | dp_decide_training_settings( |
1489 | link, |
1490 | link_settings, |
1491 | lt_settings: <_settings); |
1492 | |
1493 | override_training_settings( |
1494 | link, |
1495 | overrides: &link->preferred_training_settings, |
1496 | lt_settings: <_settings); |
1497 | |
1498 | /* reset previous training states */ |
1499 | dpcd_exit_training_mode(link, encoding); |
1500 | |
1501 | /* configure link prior to entering training mode */ |
1502 | dpcd_configure_lttpr_mode(link, lt_settings: <_settings); |
1503 | dp_set_fec_ready(link, link_res, ready: lt_settings.should_set_fec_ready); |
1504 | dpcd_configure_channel_coding(link, lt_settings: <_settings); |
1505 | |
1506 | /* enter training mode: |
1507 | * Per DP specs starting from here, DPTX device shall not issue |
1508 | * Non-LT AUX transactions inside training mode. |
1509 | */ |
1510 | if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && encoding == DP_8b_10b_ENCODING) |
1511 | status = dp_perform_fixed_vs_pe_training_sequence(link, link_res, lt_settings: <_settings); |
1512 | else if (encoding == DP_8b_10b_ENCODING) |
1513 | status = dp_perform_8b_10b_link_training(link, link_res, lt_settings: <_settings); |
1514 | else if (encoding == DP_128b_132b_ENCODING) |
1515 | status = dp_perform_128b_132b_link_training(link, link_res, lt_settings: <_settings); |
1516 | else |
1517 | ASSERT(0); |
1518 | |
1519 | /* exit training mode */ |
1520 | dpcd_exit_training_mode(link, encoding); |
1521 | |
1522 | /* switch to video idle */ |
1523 | if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) |
1524 | status = dp_transition_to_video_idle(link, |
1525 | link_res, |
1526 | lt_settings: <_settings, |
1527 | status); |
1528 | |
1529 | /* dump debug data */ |
1530 | dp_log_training_result(link, lt_settings: <_settings, status); |
1531 | if (status != LINK_TRAINING_SUCCESS) |
1532 | link->ctx->dc->debug_data.ltFailCount++; |
1533 | return status; |
1534 | } |
1535 | |
1536 | bool perform_link_training_with_retries( |
1537 | const struct dc_link_settings *link_setting, |
1538 | bool skip_video_pattern, |
1539 | int attempts, |
1540 | struct pipe_ctx *pipe_ctx, |
1541 | enum signal_type signal, |
1542 | bool do_fallback) |
1543 | { |
1544 | int j; |
1545 | uint8_t delay_between_attempts = LINK_TRAINING_RETRY_DELAY; |
1546 | struct dc_stream_state *stream = pipe_ctx->stream; |
1547 | struct dc_link *link = stream->link; |
1548 | enum dp_panel_mode panel_mode = dp_get_panel_mode(link); |
1549 | enum link_training_result status = LINK_TRAINING_CR_FAIL_LANE0; |
1550 | struct dc_link_settings cur_link_settings = *link_setting; |
1551 | struct dc_link_settings max_link_settings = *link_setting; |
1552 | const struct link_hwss *link_hwss = get_link_hwss(link, link_res: &pipe_ctx->link_res); |
1553 | int fail_count = 0; |
1554 | bool is_link_bw_low = false; /* link bandwidth < stream bandwidth */ |
1555 | bool is_link_bw_min = /* RBR x 1 */ |
1556 | (cur_link_settings.link_rate <= LINK_RATE_LOW) && |
1557 | (cur_link_settings.lane_count <= LANE_COUNT_ONE); |
1558 | |
1559 | dp_trace_commit_lt_init(link); |
1560 | |
1561 | |
1562 | if (link_dp_get_encoding_format(link_settings: &cur_link_settings) == DP_8b_10b_ENCODING) |
1563 | /* We need to do this before the link training to ensure the idle |
1564 | * pattern in SST mode will be sent right after the link training |
1565 | */ |
1566 | link_hwss->setup_stream_encoder(pipe_ctx); |
1567 | |
1568 | dp_trace_set_lt_start_timestamp(link, in_detection: false); |
1569 | j = 0; |
1570 | while (j < attempts && fail_count < (attempts * 10)) { |
1571 | |
1572 | DC_LOG_HW_LINK_TRAINING("%s: Beginning link(%d) training attempt %u of %d @ rate(%d) x lane(%d) @ spread = %x\n" , |
1573 | __func__, link->link_index, (unsigned int)j + 1, attempts, |
1574 | cur_link_settings.link_rate, cur_link_settings.lane_count, |
1575 | cur_link_settings.link_spread); |
1576 | |
1577 | dp_enable_link_phy( |
1578 | link, |
1579 | link_res: &pipe_ctx->link_res, |
1580 | signal, |
1581 | clock_source: pipe_ctx->clock_source->id, |
1582 | link_settings: &cur_link_settings); |
1583 | |
1584 | if (stream->sink_patches.dppowerup_delay > 0) { |
1585 | int delay_dp_power_up_in_ms = stream->sink_patches.dppowerup_delay; |
1586 | |
1587 | msleep(msecs: delay_dp_power_up_in_ms); |
1588 | } |
1589 | |
1590 | if (panel_mode == DP_PANEL_MODE_EDP) { |
1591 | struct cp_psp *cp_psp = &stream->ctx->cp_psp; |
1592 | |
1593 | if (cp_psp && cp_psp->funcs.enable_assr) { |
1594 | /* ASSR is bound to fail with unsigned PSP |
1595 | * verstage used during devlopment phase. |
1596 | * Report and continue with eDP panel mode to |
1597 | * perform eDP link training with right settings |
1598 | */ |
1599 | bool result; |
1600 | result = cp_psp->funcs.enable_assr(cp_psp->handle, link); |
1601 | if (!result && link->panel_mode != DP_PANEL_MODE_EDP) |
1602 | panel_mode = DP_PANEL_MODE_DEFAULT; |
1603 | } |
1604 | } |
1605 | |
1606 | dp_set_panel_mode(link, panel_mode); |
1607 | |
1608 | if (link->aux_access_disabled) { |
1609 | dp_perform_link_training_skip_aux(link, link_res: &pipe_ctx->link_res, link_setting: &cur_link_settings); |
1610 | return true; |
1611 | } else { |
1612 | /** @todo Consolidate USB4 DP and DPx.x training. */ |
1613 | if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) { |
1614 | status = dpia_perform_link_training( |
1615 | link, |
1616 | link_res: &pipe_ctx->link_res, |
1617 | link_setting: &cur_link_settings, |
1618 | skip_video_pattern); |
1619 | |
1620 | /* Transmit idle pattern once training successful. */ |
1621 | if (status == LINK_TRAINING_SUCCESS && !is_link_bw_low) { |
1622 | dp_set_hw_test_pattern(link, link_res: &pipe_ctx->link_res, test_pattern: DP_TEST_PATTERN_VIDEO_MODE, NULL, custom_pattern_size: 0); |
1623 | // Update verified link settings to current one |
1624 | // Because DPIA LT might fallback to lower link setting. |
1625 | if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { |
1626 | link->verified_link_cap.link_rate = link->cur_link_settings.link_rate; |
1627 | link->verified_link_cap.lane_count = link->cur_link_settings.lane_count; |
1628 | dm_helpers_dp_mst_update_branch_bandwidth(ctx: link->ctx, link); |
1629 | } |
1630 | } |
1631 | } else { |
1632 | status = dp_perform_link_training( |
1633 | link, |
1634 | link_res: &pipe_ctx->link_res, |
1635 | link_settings: &cur_link_settings, |
1636 | skip_video_pattern); |
1637 | } |
1638 | |
1639 | dp_trace_lt_total_count_increment(link, in_detection: false); |
1640 | dp_trace_lt_result_update(link, result: status, in_detection: false); |
1641 | dp_trace_set_lt_end_timestamp(link, in_detection: false); |
1642 | if (status == LINK_TRAINING_SUCCESS && !is_link_bw_low) |
1643 | return true; |
1644 | } |
1645 | |
1646 | fail_count++; |
1647 | dp_trace_lt_fail_count_update(link, fail_count, in_detection: false); |
1648 | if (link->ep_type == DISPLAY_ENDPOINT_PHY) { |
1649 | /* latest link training still fail or link training is aborted |
1650 | * skip delay and keep PHY on |
1651 | */ |
1652 | if (j == (attempts - 1) || (status == LINK_TRAINING_ABORT)) |
1653 | break; |
1654 | } |
1655 | |
1656 | if (j == (attempts - 1)) { |
1657 | DC_LOG_WARNING( |
1658 | "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n" , |
1659 | __func__, link->link_index, (unsigned int)j + 1, attempts, |
1660 | cur_link_settings.link_rate, cur_link_settings.lane_count, |
1661 | cur_link_settings.link_spread, status); |
1662 | } else { |
1663 | DC_LOG_HW_LINK_TRAINING( |
1664 | "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n" , |
1665 | __func__, link->link_index, (unsigned int)j + 1, attempts, |
1666 | cur_link_settings.link_rate, cur_link_settings.lane_count, |
1667 | cur_link_settings.link_spread, status); |
1668 | } |
1669 | |
1670 | dp_disable_link_phy(link, link_res: &pipe_ctx->link_res, signal); |
1671 | |
1672 | /* Abort link training if failure due to sink being unplugged. */ |
1673 | if (status == LINK_TRAINING_ABORT) { |
1674 | enum dc_connection_type type = dc_connection_none; |
1675 | |
1676 | link_detect_connection_type(link, type: &type); |
1677 | if (type == dc_connection_none) { |
1678 | DC_LOG_HW_LINK_TRAINING("%s: Aborting training because sink unplugged\n" , __func__); |
1679 | break; |
1680 | } |
1681 | } |
1682 | |
1683 | /* Try to train again at original settings if: |
1684 | * - not falling back between training attempts; |
1685 | * - aborted previous attempt due to reasons other than sink unplug; |
1686 | * - successfully trained but at a link rate lower than that required by stream; |
1687 | * - reached minimum link bandwidth. |
1688 | */ |
1689 | if (!do_fallback || (status == LINK_TRAINING_ABORT) || |
1690 | (status == LINK_TRAINING_SUCCESS && is_link_bw_low) || |
1691 | is_link_bw_min) { |
1692 | j++; |
1693 | cur_link_settings = *link_setting; |
1694 | delay_between_attempts += LINK_TRAINING_RETRY_DELAY; |
1695 | is_link_bw_low = false; |
1696 | is_link_bw_min = (cur_link_settings.link_rate <= LINK_RATE_LOW) && |
1697 | (cur_link_settings.lane_count <= LANE_COUNT_ONE); |
1698 | |
1699 | } else if (do_fallback) { /* Try training at lower link bandwidth if doing fallback. */ |
1700 | uint32_t req_bw; |
1701 | uint32_t link_bw; |
1702 | enum dc_link_encoding_format link_encoding = DC_LINK_ENCODING_UNSPECIFIED; |
1703 | |
1704 | decide_fallback_link_setting(link, max: &max_link_settings, |
1705 | cur: &cur_link_settings, training_result: status); |
1706 | |
1707 | if (link_dp_get_encoding_format(link_settings: &cur_link_settings) == DP_8b_10b_ENCODING) |
1708 | link_encoding = DC_LINK_ENCODING_DP_8b_10b; |
1709 | else if (link_dp_get_encoding_format(link_settings: &cur_link_settings) == DP_128b_132b_ENCODING) |
1710 | link_encoding = DC_LINK_ENCODING_DP_128b_132b; |
1711 | |
1712 | /* Flag if reduced link bandwidth no longer meets stream requirements or fallen back to |
1713 | * minimum link bandwidth. |
1714 | */ |
1715 | req_bw = dc_bandwidth_in_kbps_from_timing(timing: &stream->timing, link_encoding); |
1716 | link_bw = dp_link_bandwidth_kbps(link, link_settings: &cur_link_settings); |
1717 | is_link_bw_low = (req_bw > link_bw); |
1718 | is_link_bw_min = ((cur_link_settings.link_rate <= LINK_RATE_LOW) && |
1719 | (cur_link_settings.lane_count <= LANE_COUNT_ONE)); |
1720 | |
1721 | if (is_link_bw_low) |
1722 | DC_LOG_WARNING( |
1723 | "%s: Link(%d) bandwidth too low after fallback req_bw(%d) > link_bw(%d)\n" , |
1724 | __func__, link->link_index, req_bw, link_bw); |
1725 | } |
1726 | |
1727 | msleep(msecs: delay_between_attempts); |
1728 | } |
1729 | |
1730 | return false; |
1731 | } |
1732 | |
1733 | |