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 8b/10b link training specially modified to support an |
28 | * embedded retimer chip. This retimer chip is referred as fixed vs pe retimer. |
29 | * Unlike native dp connection this chip requires a modified link training |
30 | * protocol based on 8b/10b link training. Since this is a non standard sequence |
31 | * and we must support this hardware, we decided to isolate it in its own |
32 | * training sequence inside its own file. |
33 | */ |
34 | #include "link_dp_training_fixed_vs_pe_retimer.h" |
35 | #include "link_dp_training_8b_10b.h" |
36 | #include "link_dpcd.h" |
37 | #include "link_dp_phy.h" |
38 | #include "link_dp_capability.h" |
39 | #include "link_ddc.h" |
40 | |
41 | #define DC_LOGGER \ |
42 | link->ctx->logger |
43 | |
44 | void dp_fixed_vs_pe_read_lane_adjust( |
45 | struct dc_link *link, |
46 | union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX]) |
47 | { |
48 | const uint8_t vendor_lttpr_write_data_vs[3] = {0x0, 0x53, 0x63}; |
49 | const uint8_t vendor_lttpr_write_data_pe[3] = {0x0, 0x54, 0x63}; |
50 | uint8_t dprx_vs = 0; |
51 | uint8_t dprx_pe = 0; |
52 | uint8_t lane; |
53 | |
54 | /* W/A to read lane settings requested by DPRX */ |
55 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
56 | data: &vendor_lttpr_write_data_vs[0], length: sizeof(vendor_lttpr_write_data_vs)); |
57 | |
58 | link_query_fixed_vs_pe_retimer(ddc: link->ddc, data: &dprx_vs, length: 1); |
59 | |
60 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
61 | data: &vendor_lttpr_write_data_pe[0], length: sizeof(vendor_lttpr_write_data_pe)); |
62 | |
63 | link_query_fixed_vs_pe_retimer(ddc: link->ddc, data: &dprx_pe, length: 1); |
64 | |
65 | for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { |
66 | dpcd_lane_adjust[lane].bits.VOLTAGE_SWING_SET = (dprx_vs >> (2 * lane)) & 0x3; |
67 | dpcd_lane_adjust[lane].bits.PRE_EMPHASIS_SET = (dprx_pe >> (2 * lane)) & 0x3; |
68 | } |
69 | } |
70 | |
71 | |
72 | void dp_fixed_vs_pe_set_retimer_lane_settings( |
73 | struct dc_link *link, |
74 | const union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX], |
75 | uint8_t lane_count) |
76 | { |
77 | const uint8_t vendor_lttpr_write_data_reset[4] = {0x1, 0x50, 0x63, 0xFF}; |
78 | uint8_t vendor_lttpr_write_data_vs[4] = {0x1, 0x51, 0x63, 0x0}; |
79 | uint8_t vendor_lttpr_write_data_pe[4] = {0x1, 0x52, 0x63, 0x0}; |
80 | uint8_t lane = 0; |
81 | |
82 | for (lane = 0; lane < lane_count; lane++) { |
83 | vendor_lttpr_write_data_vs[3] |= |
84 | dpcd_lane_adjust[lane].bits.VOLTAGE_SWING_SET << (2 * lane); |
85 | vendor_lttpr_write_data_pe[3] |= |
86 | dpcd_lane_adjust[lane].bits.PRE_EMPHASIS_SET << (2 * lane); |
87 | } |
88 | |
89 | /* Force LTTPR to output desired VS and PE */ |
90 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
91 | data: &vendor_lttpr_write_data_reset[0], length: sizeof(vendor_lttpr_write_data_reset)); |
92 | |
93 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
94 | data: &vendor_lttpr_write_data_vs[0], length: sizeof(vendor_lttpr_write_data_vs)); |
95 | |
96 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
97 | data: &vendor_lttpr_write_data_pe[0], length: sizeof(vendor_lttpr_write_data_pe)); |
98 | } |
99 | |
100 | static enum link_training_result perform_fixed_vs_pe_nontransparent_training_sequence( |
101 | struct dc_link *link, |
102 | const struct link_resource *link_res, |
103 | struct link_training_settings *lt_settings) |
104 | { |
105 | enum link_training_result status = LINK_TRAINING_SUCCESS; |
106 | uint8_t lane = 0; |
107 | uint8_t toggle_rate = 0x6; |
108 | uint8_t target_rate = 0x6; |
109 | bool apply_toggle_rate_wa = false; |
110 | uint8_t repeater_cnt; |
111 | uint8_t repeater_id; |
112 | |
113 | /* Fixed VS/PE specific: Force CR AUX RD Interval to at least 16ms */ |
114 | if (lt_settings->cr_pattern_time < 16000) |
115 | lt_settings->cr_pattern_time = 16000; |
116 | |
117 | /* Fixed VS/PE specific: Toggle link rate */ |
118 | apply_toggle_rate_wa = ((link->vendor_specific_lttpr_link_rate_wa == target_rate) || (link->vendor_specific_lttpr_link_rate_wa == 0)); |
119 | target_rate = get_dpcd_link_rate(link_settings: <_settings->link_settings); |
120 | toggle_rate = (target_rate == 0x6) ? 0xA : 0x6; |
121 | |
122 | if (apply_toggle_rate_wa) |
123 | lt_settings->link_settings.link_rate = toggle_rate; |
124 | |
125 | if (link->ctx->dc->work_arounds.lt_early_cr_pattern) |
126 | start_clock_recovery_pattern_early(link, link_res, lt_settings, offset: DPRX); |
127 | |
128 | /* 1. set link rate, lane count and spread. */ |
129 | dpcd_set_link_settings(link, lt_settings); |
130 | |
131 | /* Fixed VS/PE specific: Toggle link rate back*/ |
132 | if (apply_toggle_rate_wa) { |
133 | core_link_write_dpcd( |
134 | link, |
135 | DP_LINK_BW_SET, |
136 | data: &target_rate, |
137 | size: 1); |
138 | } |
139 | |
140 | link->vendor_specific_lttpr_link_rate_wa = target_rate; |
141 | |
142 | if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) { |
143 | |
144 | /* 2. perform link training (set link training done |
145 | * to false is done as well) |
146 | */ |
147 | repeater_cnt = dp_parse_lttpr_repeater_count(lttpr_repeater_count: link->dpcd_caps.lttpr_caps.phy_repeater_cnt); |
148 | |
149 | for (repeater_id = repeater_cnt; (repeater_id > 0 && status == LINK_TRAINING_SUCCESS); |
150 | repeater_id--) { |
151 | status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, offset: repeater_id); |
152 | |
153 | if (status != LINK_TRAINING_SUCCESS) { |
154 | repeater_training_done(link, offset: repeater_id); |
155 | break; |
156 | } |
157 | |
158 | status = perform_8b_10b_channel_equalization_sequence(link, |
159 | link_res, |
160 | lt_settings, |
161 | offset: repeater_id); |
162 | |
163 | repeater_training_done(link, offset: repeater_id); |
164 | |
165 | if (status != LINK_TRAINING_SUCCESS) |
166 | break; |
167 | |
168 | for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { |
169 | lt_settings->dpcd_lane_settings[lane].raw = 0; |
170 | lt_settings->hw_lane_settings[lane].VOLTAGE_SWING = 0; |
171 | lt_settings->hw_lane_settings[lane].PRE_EMPHASIS = 0; |
172 | } |
173 | } |
174 | } |
175 | |
176 | if (status == LINK_TRAINING_SUCCESS) { |
177 | status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, offset: DPRX); |
178 | if (status == LINK_TRAINING_SUCCESS) { |
179 | status = perform_8b_10b_channel_equalization_sequence(link, |
180 | link_res, |
181 | lt_settings, |
182 | offset: DPRX); |
183 | } |
184 | } |
185 | |
186 | return status; |
187 | } |
188 | |
189 | enum link_training_result dp_perform_fixed_vs_pe_training_sequence( |
190 | struct dc_link *link, |
191 | const struct link_resource *link_res, |
192 | struct link_training_settings *lt_settings) |
193 | { |
194 | const uint8_t vendor_lttpr_write_data_reset[4] = {0x1, 0x50, 0x63, 0xFF}; |
195 | const uint8_t offset = dp_parse_lttpr_repeater_count( |
196 | lttpr_repeater_count: link->dpcd_caps.lttpr_caps.phy_repeater_cnt); |
197 | const uint8_t vendor_lttpr_write_data_intercept_en[4] = {0x1, 0x55, 0x63, 0x0}; |
198 | const uint8_t vendor_lttpr_write_data_intercept_dis[4] = {0x1, 0x55, 0x63, 0x6E}; |
199 | const uint8_t vendor_lttpr_write_data_adicora_eq1[4] = {0x1, 0x55, 0x63, 0x2E}; |
200 | const uint8_t vendor_lttpr_write_data_adicora_eq2[4] = {0x1, 0x55, 0x63, 0x01}; |
201 | const uint8_t vendor_lttpr_write_data_adicora_eq3[4] = {0x1, 0x55, 0x63, 0x68}; |
202 | uint32_t pre_disable_intercept_delay_ms = 0; |
203 | uint8_t vendor_lttpr_write_data_vs[4] = {0x1, 0x51, 0x63, 0x0}; |
204 | uint8_t vendor_lttpr_write_data_pe[4] = {0x1, 0x52, 0x63, 0x0}; |
205 | const uint8_t vendor_lttpr_write_data_4lane_1[4] = {0x1, 0x6E, 0xF2, 0x19}; |
206 | const uint8_t vendor_lttpr_write_data_4lane_2[4] = {0x1, 0x6B, 0xF2, 0x01}; |
207 | const uint8_t vendor_lttpr_write_data_4lane_3[4] = {0x1, 0x6D, 0xF2, 0x18}; |
208 | const uint8_t vendor_lttpr_write_data_4lane_4[4] = {0x1, 0x6C, 0xF2, 0x03}; |
209 | const uint8_t vendor_lttpr_write_data_4lane_5[4] = {0x1, 0x03, 0xF3, 0x06}; |
210 | const uint8_t vendor_lttpr_write_data_dpmf[4] = {0x1, 0x6, 0x70, 0x87}; |
211 | enum link_training_result status = LINK_TRAINING_SUCCESS; |
212 | uint8_t lane = 0; |
213 | union down_spread_ctrl downspread = {0}; |
214 | union lane_count_set lane_count_set = {0}; |
215 | uint8_t toggle_rate; |
216 | uint8_t rate; |
217 | |
218 | /* Only 8b/10b is supported */ |
219 | ASSERT(link_dp_get_encoding_format(<_settings->link_settings) == |
220 | DP_8b_10b_ENCODING); |
221 | |
222 | if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) { |
223 | status = perform_fixed_vs_pe_nontransparent_training_sequence(link, link_res, lt_settings); |
224 | return status; |
225 | } |
226 | |
227 | if (offset != 0xFF) { |
228 | if (offset == 2) { |
229 | pre_disable_intercept_delay_ms = link->dc->debug.fixed_vs_aux_delay_config_wa; |
230 | |
231 | /* Certain display and cable configuration require extra delay */ |
232 | } else if (offset > 2) { |
233 | pre_disable_intercept_delay_ms = link->dc->debug.fixed_vs_aux_delay_config_wa * 2; |
234 | } |
235 | } |
236 | |
237 | /* Vendor specific: Reset lane settings */ |
238 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
239 | data: &vendor_lttpr_write_data_reset[0], length: sizeof(vendor_lttpr_write_data_reset)); |
240 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
241 | data: &vendor_lttpr_write_data_vs[0], length: sizeof(vendor_lttpr_write_data_vs)); |
242 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
243 | data: &vendor_lttpr_write_data_pe[0], length: sizeof(vendor_lttpr_write_data_pe)); |
244 | |
245 | /* Vendor specific: Enable intercept */ |
246 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
247 | data: &vendor_lttpr_write_data_intercept_en[0], length: sizeof(vendor_lttpr_write_data_intercept_en)); |
248 | |
249 | /* 1. set link rate, lane count and spread. */ |
250 | |
251 | downspread.raw = (uint8_t)(lt_settings->link_settings.link_spread); |
252 | |
253 | lane_count_set.bits.LANE_COUNT_SET = |
254 | lt_settings->link_settings.lane_count; |
255 | |
256 | lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing; |
257 | lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0; |
258 | |
259 | |
260 | if (lt_settings->pattern_for_eq < DP_TRAINING_PATTERN_SEQUENCE_4) { |
261 | lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = |
262 | link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED; |
263 | } |
264 | |
265 | core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, |
266 | data: &downspread.raw, size: sizeof(downspread)); |
267 | |
268 | core_link_write_dpcd(link, DP_LANE_COUNT_SET, |
269 | data: &lane_count_set.raw, size: 1); |
270 | |
271 | rate = get_dpcd_link_rate(link_settings: <_settings->link_settings); |
272 | |
273 | if (!link->dpcd_caps.lttpr_caps.main_link_channel_coding.bits.DP_128b_132b_SUPPORTED) { |
274 | /* Vendor specific: Toggle link rate */ |
275 | toggle_rate = (rate == 0x6) ? 0xA : 0x6; |
276 | |
277 | if (link->vendor_specific_lttpr_link_rate_wa == rate || link->vendor_specific_lttpr_link_rate_wa == 0) { |
278 | core_link_write_dpcd( |
279 | link, |
280 | DP_LINK_BW_SET, |
281 | data: &toggle_rate, |
282 | size: 1); |
283 | } |
284 | |
285 | link->vendor_specific_lttpr_link_rate_wa = rate; |
286 | } |
287 | |
288 | core_link_write_dpcd(link, DP_LINK_BW_SET, data: &rate, size: 1); |
289 | |
290 | DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x framing = %x\n %x spread = %x\n" , |
291 | __func__, |
292 | DP_LINK_BW_SET, |
293 | lt_settings->link_settings.link_rate, |
294 | DP_LANE_COUNT_SET, |
295 | lt_settings->link_settings.lane_count, |
296 | lt_settings->enhanced_framing, |
297 | DP_DOWNSPREAD_CTRL, |
298 | lt_settings->link_settings.link_spread); |
299 | |
300 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
301 | data: &vendor_lttpr_write_data_dpmf[0], |
302 | length: sizeof(vendor_lttpr_write_data_dpmf)); |
303 | |
304 | if (lt_settings->link_settings.lane_count == LANE_COUNT_FOUR) { |
305 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
306 | data: &vendor_lttpr_write_data_4lane_1[0], length: sizeof(vendor_lttpr_write_data_4lane_1)); |
307 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
308 | data: &vendor_lttpr_write_data_4lane_2[0], length: sizeof(vendor_lttpr_write_data_4lane_2)); |
309 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
310 | data: &vendor_lttpr_write_data_4lane_3[0], length: sizeof(vendor_lttpr_write_data_4lane_3)); |
311 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
312 | data: &vendor_lttpr_write_data_4lane_4[0], length: sizeof(vendor_lttpr_write_data_4lane_4)); |
313 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
314 | data: &vendor_lttpr_write_data_4lane_5[0], length: sizeof(vendor_lttpr_write_data_4lane_5)); |
315 | } |
316 | |
317 | /* 2. Perform link training */ |
318 | |
319 | /* Perform Clock Recovery Sequence */ |
320 | if (status == LINK_TRAINING_SUCCESS) { |
321 | const uint8_t max_vendor_dpcd_retries = 10; |
322 | uint32_t retries_cr; |
323 | uint32_t retry_count; |
324 | uint32_t wait_time_microsec; |
325 | enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; |
326 | union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX]; |
327 | union lane_align_status_updated dpcd_lane_status_updated; |
328 | union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0}; |
329 | uint8_t i = 0; |
330 | |
331 | retries_cr = 0; |
332 | retry_count = 0; |
333 | |
334 | memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status)); |
335 | memset(&dpcd_lane_status_updated, '\0', |
336 | sizeof(dpcd_lane_status_updated)); |
337 | |
338 | while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) && |
339 | (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { |
340 | |
341 | |
342 | /* 1. call HWSS to set lane settings */ |
343 | dp_set_hw_lane_settings( |
344 | link, |
345 | link_res, |
346 | link_settings: lt_settings, |
347 | offset: 0); |
348 | |
349 | /* 2. update DPCD of the receiver */ |
350 | if (!retry_count) { |
351 | /* EPR #361076 - write as a 5-byte burst, |
352 | * but only for the 1-st iteration. |
353 | */ |
354 | dpcd_set_lt_pattern_and_lane_settings( |
355 | link, |
356 | lt_settings, |
357 | pattern: lt_settings->pattern_for_cr, |
358 | offset: 0); |
359 | /* Vendor specific: Disable intercept */ |
360 | for (i = 0; i < max_vendor_dpcd_retries; i++) { |
361 | if (pre_disable_intercept_delay_ms != 0) |
362 | msleep(msecs: pre_disable_intercept_delay_ms); |
363 | if (link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
364 | data: &vendor_lttpr_write_data_intercept_dis[0], |
365 | length: sizeof(vendor_lttpr_write_data_intercept_dis))) |
366 | break; |
367 | |
368 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
369 | data: &vendor_lttpr_write_data_intercept_en[0], |
370 | length: sizeof(vendor_lttpr_write_data_intercept_en)); |
371 | } |
372 | } else { |
373 | vendor_lttpr_write_data_vs[3] = 0; |
374 | vendor_lttpr_write_data_pe[3] = 0; |
375 | |
376 | for (lane = 0; lane < lane_count; lane++) { |
377 | vendor_lttpr_write_data_vs[3] |= |
378 | lt_settings->dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET << (2 * lane); |
379 | vendor_lttpr_write_data_pe[3] |= |
380 | lt_settings->dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET << (2 * lane); |
381 | } |
382 | |
383 | /* Vendor specific: Update VS and PE to DPRX requested value */ |
384 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
385 | data: &vendor_lttpr_write_data_vs[0], length: sizeof(vendor_lttpr_write_data_vs)); |
386 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
387 | data: &vendor_lttpr_write_data_pe[0], length: sizeof(vendor_lttpr_write_data_pe)); |
388 | |
389 | dpcd_set_lane_settings( |
390 | link, |
391 | link_training_setting: lt_settings, |
392 | offset: 0); |
393 | } |
394 | |
395 | /* 3. wait receiver to lock-on*/ |
396 | wait_time_microsec = lt_settings->cr_pattern_time; |
397 | |
398 | dp_wait_for_training_aux_rd_interval( |
399 | link, |
400 | wait_in_micro_secs: wait_time_microsec); |
401 | |
402 | /* 4. Read lane status and requested drive |
403 | * settings as set by the sink |
404 | */ |
405 | dp_get_lane_status_and_lane_adjust( |
406 | link, |
407 | link_training_setting: lt_settings, |
408 | ln_status: dpcd_lane_status, |
409 | ln_align: &dpcd_lane_status_updated, |
410 | ln_adjust: dpcd_lane_adjust, |
411 | offset: 0); |
412 | |
413 | /* 5. check CR done*/ |
414 | if (dp_is_cr_done(ln_count: lane_count, dpcd_lane_status)) { |
415 | status = LINK_TRAINING_SUCCESS; |
416 | break; |
417 | } |
418 | |
419 | /* 6. max VS reached*/ |
420 | if (dp_is_max_vs_reached(lt_settings)) |
421 | break; |
422 | |
423 | /* 7. same lane settings */ |
424 | /* Note: settings are the same for all lanes, |
425 | * so comparing first lane is sufficient |
426 | */ |
427 | if (lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET == |
428 | dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE) |
429 | retries_cr++; |
430 | else |
431 | retries_cr = 0; |
432 | |
433 | /* 8. update VS/PE/PC2 in lt_settings*/ |
434 | dp_decide_lane_settings(lt_settings, ln_adjust: dpcd_lane_adjust, |
435 | hw_lane_settings: lt_settings->hw_lane_settings, dpcd_lane_settings: lt_settings->dpcd_lane_settings); |
436 | retry_count++; |
437 | } |
438 | |
439 | if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) { |
440 | ASSERT(0); |
441 | DC_LOG_ERROR("%s: Link Training Error, could not get CR after %d tries. Possibly voltage swing issue" , |
442 | __func__, |
443 | LINK_TRAINING_MAX_CR_RETRY); |
444 | |
445 | } |
446 | |
447 | status = dp_get_cr_failure(ln_count: lane_count, dpcd_lane_status); |
448 | } |
449 | |
450 | /* Perform Channel EQ Sequence */ |
451 | if (status == LINK_TRAINING_SUCCESS) { |
452 | enum dc_dp_training_pattern tr_pattern; |
453 | uint32_t retries_ch_eq; |
454 | uint32_t wait_time_microsec; |
455 | enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; |
456 | union lane_align_status_updated dpcd_lane_status_updated = {0}; |
457 | union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0}; |
458 | union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0}; |
459 | |
460 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
461 | data: &vendor_lttpr_write_data_adicora_eq1[0], |
462 | length: sizeof(vendor_lttpr_write_data_adicora_eq1)); |
463 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
464 | data: &vendor_lttpr_write_data_adicora_eq2[0], |
465 | length: sizeof(vendor_lttpr_write_data_adicora_eq2)); |
466 | |
467 | |
468 | /* Note: also check that TPS4 is a supported feature*/ |
469 | tr_pattern = lt_settings->pattern_for_eq; |
470 | |
471 | dp_set_hw_training_pattern(link, link_res, pattern: tr_pattern, offset: 0); |
472 | |
473 | status = LINK_TRAINING_EQ_FAIL_EQ; |
474 | |
475 | for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT; |
476 | retries_ch_eq++) { |
477 | |
478 | dp_set_hw_lane_settings(link, link_res, link_settings: lt_settings, offset: 0); |
479 | |
480 | vendor_lttpr_write_data_vs[3] = 0; |
481 | vendor_lttpr_write_data_pe[3] = 0; |
482 | |
483 | for (lane = 0; lane < lane_count; lane++) { |
484 | vendor_lttpr_write_data_vs[3] |= |
485 | lt_settings->dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET << (2 * lane); |
486 | vendor_lttpr_write_data_pe[3] |= |
487 | lt_settings->dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET << (2 * lane); |
488 | } |
489 | |
490 | /* Vendor specific: Update VS and PE to DPRX requested value */ |
491 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
492 | data: &vendor_lttpr_write_data_vs[0], length: sizeof(vendor_lttpr_write_data_vs)); |
493 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
494 | data: &vendor_lttpr_write_data_pe[0], length: sizeof(vendor_lttpr_write_data_pe)); |
495 | |
496 | /* 2. update DPCD*/ |
497 | if (!retries_ch_eq) { |
498 | /* EPR #361076 - write as a 5-byte burst, |
499 | * but only for the 1-st iteration |
500 | */ |
501 | |
502 | dpcd_set_lt_pattern_and_lane_settings( |
503 | link, |
504 | lt_settings, |
505 | pattern: tr_pattern, offset: 0); |
506 | |
507 | link_configure_fixed_vs_pe_retimer(ddc: link->ddc, |
508 | data: &vendor_lttpr_write_data_adicora_eq3[0], |
509 | length: sizeof(vendor_lttpr_write_data_adicora_eq3)); |
510 | |
511 | } else |
512 | dpcd_set_lane_settings(link, link_training_setting: lt_settings, offset: 0); |
513 | |
514 | /* 3. wait for receiver to lock-on*/ |
515 | wait_time_microsec = lt_settings->eq_pattern_time; |
516 | |
517 | dp_wait_for_training_aux_rd_interval( |
518 | link, |
519 | wait_in_micro_secs: wait_time_microsec); |
520 | |
521 | /* 4. Read lane status and requested |
522 | * drive settings as set by the sink |
523 | */ |
524 | dp_get_lane_status_and_lane_adjust( |
525 | link, |
526 | link_training_setting: lt_settings, |
527 | ln_status: dpcd_lane_status, |
528 | ln_align: &dpcd_lane_status_updated, |
529 | ln_adjust: dpcd_lane_adjust, |
530 | offset: 0); |
531 | |
532 | /* 5. check CR done*/ |
533 | if (!dp_is_cr_done(ln_count: lane_count, dpcd_lane_status)) { |
534 | status = LINK_TRAINING_EQ_FAIL_CR; |
535 | break; |
536 | } |
537 | |
538 | /* 6. check CHEQ done*/ |
539 | if (dp_is_ch_eq_done(ln_count: lane_count, dpcd_lane_status) && |
540 | dp_is_symbol_locked(ln_count: lane_count, dpcd_lane_status) && |
541 | dp_is_interlane_aligned(align_status: dpcd_lane_status_updated)) { |
542 | status = LINK_TRAINING_SUCCESS; |
543 | break; |
544 | } |
545 | |
546 | /* 7. update VS/PE/PC2 in lt_settings*/ |
547 | dp_decide_lane_settings(lt_settings, ln_adjust: dpcd_lane_adjust, |
548 | hw_lane_settings: lt_settings->hw_lane_settings, dpcd_lane_settings: lt_settings->dpcd_lane_settings); |
549 | } |
550 | } |
551 | |
552 | return status; |
553 | } |
554 | |