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