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