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 dp 8b/10b link training software policies and
28 * sequences.
29 */
30 #include "link_dp_training_8b_10b.h"
31 #include "link_dpcd.h"
32 #include "link_dp_phy.h"
33 #include "link_dp_capability.h"
34
35 #define DC_LOGGER \
36 link->ctx->logger
37
get_default_8b_10b_lttpr_aux_rd_interval(union training_aux_rd_interval * training_rd_interval)38 static void get_default_8b_10b_lttpr_aux_rd_interval(
39 union training_aux_rd_interval *training_rd_interval)
40 {
41 /* LTTPR are required to program DPCD 0000Eh to 0x4 (16ms) upon AUX
42 * read reply to this register. Since old sinks with DPCD rev 1.1
43 * and earlier may not support this register, assume the mandatory
44 * value is programmed by the LTTPR to avoid AUX timeout issues.
45 */
46 training_rd_interval->raw = 0x4;
47 }
48
get_cr_training_aux_rd_interval(struct dc_link * link,const struct dc_link_settings * link_settings,enum lttpr_mode lttpr_mode)49 static int32_t get_cr_training_aux_rd_interval(struct dc_link *link,
50 const struct dc_link_settings *link_settings,
51 enum lttpr_mode lttpr_mode)
52 {
53 union training_aux_rd_interval training_rd_interval;
54 uint32_t wait_in_micro_secs = 100;
55
56 memset(&training_rd_interval, 0, sizeof(training_rd_interval));
57 if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) {
58 if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12)
59 core_link_read_dpcd(
60 link,
61 DP_TRAINING_AUX_RD_INTERVAL,
62 (uint8_t *)&training_rd_interval,
63 sizeof(training_rd_interval));
64 else if (dp_is_lttpr_present(link))
65 get_default_8b_10b_lttpr_aux_rd_interval(&training_rd_interval);
66
67 if (training_rd_interval.raw != 0) {
68 if (lttpr_mode != LTTPR_MODE_NON_TRANSPARENT)
69 wait_in_micro_secs = 400;
70 if (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL)
71 wait_in_micro_secs = training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000;
72 }
73 }
74 return wait_in_micro_secs;
75 }
76
get_eq_training_aux_rd_interval(struct dc_link * link,const struct dc_link_settings * link_settings)77 static uint32_t get_eq_training_aux_rd_interval(
78 struct dc_link *link,
79 const struct dc_link_settings *link_settings)
80 {
81 union training_aux_rd_interval training_rd_interval;
82
83 memset(&training_rd_interval, 0, sizeof(training_rd_interval));
84 if (link_dp_get_encoding_format(link_settings) == DP_128b_132b_ENCODING) {
85 core_link_read_dpcd(
86 link,
87 DP_128B132B_TRAINING_AUX_RD_INTERVAL,
88 (uint8_t *)&training_rd_interval,
89 sizeof(training_rd_interval));
90 } else if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) {
91 if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12)
92 core_link_read_dpcd(
93 link,
94 DP_TRAINING_AUX_RD_INTERVAL,
95 (uint8_t *)&training_rd_interval,
96 sizeof(training_rd_interval));
97 else if (dp_is_lttpr_present(link))
98 get_default_8b_10b_lttpr_aux_rd_interval(&training_rd_interval);
99 }
100
101 switch (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) {
102 case 0: return 400;
103 case 1: return 4000;
104 case 2: return 8000;
105 case 3: return 12000;
106 case 4: return 16000;
107 case 5: return 32000;
108 case 6: return 64000;
109 default: return 400;
110 }
111 }
112
decide_8b_10b_training_settings(struct dc_link * link,const struct link_resource * link_res,const struct dc_link_settings * link_setting,struct link_training_settings * lt_settings)113 void decide_8b_10b_training_settings(
114 struct dc_link *link,
115 const struct link_resource *link_res,
116 const struct dc_link_settings *link_setting,
117 struct link_training_settings *lt_settings)
118 {
119 memset(lt_settings, '\0', sizeof(struct link_training_settings));
120
121 /* Initialize link settings */
122 lt_settings->link_settings.use_link_rate_set = link_setting->use_link_rate_set;
123 lt_settings->link_settings.link_rate_set = link_setting->link_rate_set;
124 lt_settings->link_settings.link_rate = link_setting->link_rate;
125 lt_settings->link_settings.lane_count = link_setting->lane_count;
126 /* TODO hard coded to SS for now
127 * lt_settings.link_settings.link_spread =
128 * dal_display_path_is_ss_supported(
129 * path_mode->display_path) ?
130 * LINK_SPREAD_05_DOWNSPREAD_30KHZ :
131 * LINK_SPREAD_DISABLED;
132 */
133 lt_settings->link_settings.link_spread = link->dp_ss_off ?
134 LINK_SPREAD_DISABLED : LINK_SPREAD_05_DOWNSPREAD_30KHZ;
135 lt_settings->eq_pattern_time = get_eq_training_aux_rd_interval(link, link_setting);
136 lt_settings->pattern_for_cr = decide_cr_training_pattern(link_setting);
137 lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_res, link_setting);
138 lt_settings->enhanced_framing = 1;
139 lt_settings->should_set_fec_ready = true;
140 lt_settings->disallow_per_lane_settings = true;
141 lt_settings->always_match_dpcd_with_hw_lane_settings = true;
142 lt_settings->lttpr_mode = dp_decide_8b_10b_lttpr_mode(link);
143 lt_settings->cr_pattern_time = get_cr_training_aux_rd_interval(link, link_setting, lt_settings->lttpr_mode);
144 dp_hw_to_dpcd_lane_settings(lt_settings, lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
145 }
146
dp_decide_8b_10b_lttpr_mode(struct dc_link * link)147 enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link)
148 {
149 bool is_lttpr_present = dp_is_lttpr_present(link);
150 bool vbios_lttpr_force_non_transparent = link->dc->caps.vbios_lttpr_enable;
151 bool vbios_lttpr_aware = link->dc->caps.vbios_lttpr_aware;
152
153 if (!is_lttpr_present)
154 return LTTPR_MODE_NON_LTTPR;
155
156 if (vbios_lttpr_aware) {
157 if (vbios_lttpr_force_non_transparent) {
158 DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT due to VBIOS DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE set to 1.\n");
159 return LTTPR_MODE_NON_TRANSPARENT;
160 } else {
161 DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT by default due to VBIOS not set DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE set to 1.\n");
162 return LTTPR_MODE_TRANSPARENT;
163 }
164 }
165
166 if (link->dc->config.allow_lttpr_non_transparent_mode.bits.DP1_4A &&
167 link->dc->caps.extended_aux_timeout_support) {
168 DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT by default and dc->config.allow_lttpr_non_transparent_mode.bits.DP1_4A set to 1.\n");
169 return LTTPR_MODE_NON_TRANSPARENT;
170 }
171
172 DC_LOG_DC("chose LTTPR_MODE_NON_LTTPR.\n");
173 return LTTPR_MODE_NON_LTTPR;
174 }
175
perform_8b_10b_clock_recovery_sequence(struct dc_link * link,const struct link_resource * link_res,struct link_training_settings * lt_settings,uint32_t offset)176 enum link_training_result perform_8b_10b_clock_recovery_sequence(
177 struct dc_link *link,
178 const struct link_resource *link_res,
179 struct link_training_settings *lt_settings,
180 uint32_t offset)
181 {
182 enum dc_status status;
183 uint32_t retries_cr;
184 uint32_t retry_count;
185 uint32_t wait_time_microsec;
186 enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
187 union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX];
188 union lane_align_status_updated dpcd_lane_status_updated;
189 union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
190
191 retries_cr = 0;
192 retry_count = 0;
193
194 memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status));
195 memset(&dpcd_lane_status_updated, '\0',
196 sizeof(dpcd_lane_status_updated));
197
198 if (!link->ctx->dc->work_arounds.lt_early_cr_pattern)
199 dp_set_hw_training_pattern(link, link_res, lt_settings->pattern_for_cr, offset);
200
201 /* najeeb - The synaptics MST hub can put the LT in
202 * infinite loop by switching the VS
203 */
204 /* between level 0 and level 1 continuously, here
205 * we try for CR lock for LinkTrainingMaxCRRetry count*/
206 while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
207 (retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
208
209
210 /* 1. call HWSS to set lane settings*/
211 dp_set_hw_lane_settings(
212 link,
213 link_res,
214 lt_settings,
215 offset);
216
217 /* 2. update DPCD of the receiver*/
218 if (!retry_count)
219 /* EPR #361076 - write as a 5-byte burst,
220 * but only for the 1-st iteration.*/
221 dpcd_set_lt_pattern_and_lane_settings(
222 link,
223 lt_settings,
224 lt_settings->pattern_for_cr,
225 offset);
226 else
227 dpcd_set_lane_settings(
228 link,
229 lt_settings,
230 offset);
231
232 /* 3. wait receiver to lock-on*/
233 wait_time_microsec = lt_settings->cr_pattern_time;
234
235 dp_wait_for_training_aux_rd_interval(
236 link,
237 wait_time_microsec);
238
239 /* 4. Read lane status and requested drive
240 * settings as set by the sink
241 */
242 status = dp_get_lane_status_and_lane_adjust(
243 link,
244 lt_settings,
245 dpcd_lane_status,
246 &dpcd_lane_status_updated,
247 dpcd_lane_adjust,
248 offset);
249
250 if (dp_check_dpcd_reqeust_status(link, status))
251 return LINK_TRAINING_ABORT;
252
253 /* 5. check CR done*/
254 if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
255 DC_LOG_HW_LINK_TRAINING("%s: Clock recovery OK\n", __func__);
256 return LINK_TRAINING_SUCCESS;
257 }
258
259 /* 6. max VS reached*/
260 if ((link_dp_get_encoding_format(<_settings->link_settings) ==
261 DP_8b_10b_ENCODING) &&
262 dp_is_max_vs_reached(lt_settings))
263 break;
264
265 /* 7. same lane settings*/
266 /* Note: settings are the same for all lanes,
267 * so comparing first lane is sufficient*/
268 if ((link_dp_get_encoding_format(<_settings->link_settings) == DP_8b_10b_ENCODING) &&
269 lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
270 dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
271 retries_cr++;
272 else if ((link_dp_get_encoding_format(<_settings->link_settings) == DP_128b_132b_ENCODING) &&
273 lt_settings->dpcd_lane_settings[0].tx_ffe.PRESET_VALUE ==
274 dpcd_lane_adjust[0].tx_ffe.PRESET_VALUE)
275 retries_cr++;
276 else
277 retries_cr = 0;
278
279 /* 8. update VS/PE/PC2 in lt_settings*/
280 dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
281 lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
282 retry_count++;
283 }
284
285 if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) {
286 ASSERT(0);
287 DC_LOG_ERROR("%s: Link Training Error, could not get CR after %d tries. Possibly voltage swing issue",
288 __func__,
289 LINK_TRAINING_MAX_CR_RETRY);
290
291 }
292
293 return dp_get_cr_failure(lane_count, dpcd_lane_status);
294 }
295
perform_8b_10b_channel_equalization_sequence(struct dc_link * link,const struct link_resource * link_res,struct link_training_settings * lt_settings,uint32_t offset)296 enum link_training_result perform_8b_10b_channel_equalization_sequence(
297 struct dc_link *link,
298 const struct link_resource *link_res,
299 struct link_training_settings *lt_settings,
300 uint32_t offset)
301 {
302 enum dc_status status;
303 enum dc_dp_training_pattern tr_pattern;
304 uint32_t retries_ch_eq;
305 uint32_t wait_time_microsec;
306 enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
307 union lane_align_status_updated dpcd_lane_status_updated = {0};
308 union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
309 union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
310
311 /* Note: also check that TPS4 is a supported feature*/
312 tr_pattern = lt_settings->pattern_for_eq;
313
314 if (is_repeater(lt_settings, offset) && link_dp_get_encoding_format(<_settings->link_settings) == DP_8b_10b_ENCODING)
315 tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4;
316
317 dp_set_hw_training_pattern(link, link_res, tr_pattern, offset);
318
319 for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT;
320 retries_ch_eq++) {
321
322 dp_set_hw_lane_settings(link, link_res, lt_settings, offset);
323
324 /* 2. update DPCD*/
325 if (!retries_ch_eq)
326 /* EPR #361076 - write as a 5-byte burst,
327 * but only for the 1-st iteration
328 */
329
330 dpcd_set_lt_pattern_and_lane_settings(
331 link,
332 lt_settings,
333 tr_pattern, offset);
334 else
335 dpcd_set_lane_settings(link, lt_settings, offset);
336
337 /* 3. wait for receiver to lock-on*/
338 wait_time_microsec = dp_get_eq_aux_rd_interval(link, lt_settings, offset, retries_ch_eq);
339
340 dp_wait_for_training_aux_rd_interval(
341 link,
342 wait_time_microsec);
343
344 /* 4. Read lane status and requested
345 * drive settings as set by the sink*/
346
347 status = dp_get_lane_status_and_lane_adjust(
348 link,
349 lt_settings,
350 dpcd_lane_status,
351 &dpcd_lane_status_updated,
352 dpcd_lane_adjust,
353 offset);
354
355 if (dp_check_dpcd_reqeust_status(link, status))
356 return LINK_TRAINING_ABORT;
357
358 /* 5. check CR done*/
359 if (!dp_is_cr_done(lane_count, dpcd_lane_status))
360 return dpcd_lane_status[0].bits.CR_DONE_0 ?
361 LINK_TRAINING_EQ_FAIL_CR_PARTIAL :
362 LINK_TRAINING_EQ_FAIL_CR;
363
364 /* 6. check CHEQ done*/
365 if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) &&
366 dp_is_symbol_locked(lane_count, dpcd_lane_status) &&
367 dp_check_interlane_aligned(dpcd_lane_status_updated, link, retries_ch_eq))
368 return LINK_TRAINING_SUCCESS;
369
370 /* 7. update VS/PE/PC2 in lt_settings*/
371 dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
372 lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
373 }
374
375 return LINK_TRAINING_EQ_FAIL_EQ;
376
377 }
378
dp_perform_8b_10b_link_training(struct dc_link * link,const struct link_resource * link_res,struct link_training_settings * lt_settings)379 enum link_training_result dp_perform_8b_10b_link_training(
380 struct dc_link *link,
381 const struct link_resource *link_res,
382 struct link_training_settings *lt_settings)
383 {
384 enum link_training_result status = LINK_TRAINING_SUCCESS;
385
386 uint8_t repeater_cnt;
387 uint8_t repeater_id;
388 uint8_t lane = 0;
389
390 if (link->ctx->dc->work_arounds.lt_early_cr_pattern)
391 start_clock_recovery_pattern_early(link, link_res, lt_settings, DPRX);
392
393 /* 1. set link rate, lane count and spread. */
394 dpcd_set_link_settings(link, lt_settings);
395
396 if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
397
398 /* 2. perform link training (set link training done
399 * to false is done as well)
400 */
401 repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
402
403 for (repeater_id = repeater_cnt; (repeater_id > 0 && status == LINK_TRAINING_SUCCESS);
404 repeater_id--) {
405 status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, repeater_id);
406
407 if (status != LINK_TRAINING_SUCCESS) {
408 repeater_training_done(link, repeater_id);
409 break;
410 }
411
412 status = perform_8b_10b_channel_equalization_sequence(link,
413 link_res,
414 lt_settings,
415 repeater_id);
416 if (status == LINK_TRAINING_SUCCESS)
417 DC_LOG_HW_LINK_TRAINING("%s: Channel EQ done.\n", __func__);
418
419 repeater_training_done(link, repeater_id);
420
421 if (status != LINK_TRAINING_SUCCESS)
422 break;
423
424 for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) {
425 lt_settings->dpcd_lane_settings[lane].raw = 0;
426 lt_settings->hw_lane_settings[lane].VOLTAGE_SWING = 0;
427 lt_settings->hw_lane_settings[lane].PRE_EMPHASIS = 0;
428 }
429 }
430 }
431
432 if (status == LINK_TRAINING_SUCCESS) {
433 status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, DPRX);
434 if (status == LINK_TRAINING_SUCCESS) {
435 status = perform_8b_10b_channel_equalization_sequence(link,
436 link_res,
437 lt_settings,
438 DPRX);
439 if (status == LINK_TRAINING_SUCCESS)
440 DC_LOG_HW_LINK_TRAINING("%s: Channel EQ done.\n", __func__);
441 }
442 }
443
444 return status;
445 }
446