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 
dp_fixed_vs_pe_read_lane_adjust(struct dc_link * link,union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX])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(link->ddc,
56 			&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
57 
58 	link_query_fixed_vs_pe_retimer(link->ddc, &dprx_vs, 1);
59 
60 	link_configure_fixed_vs_pe_retimer(link->ddc,
61 			&vendor_lttpr_write_data_pe[0], sizeof(vendor_lttpr_write_data_pe));
62 
63 	link_query_fixed_vs_pe_retimer(link->ddc, &dprx_pe, 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 
dp_fixed_vs_pe_set_retimer_lane_settings(struct dc_link * link,const union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX],uint8_t lane_count)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(link->ddc,
91 			&vendor_lttpr_write_data_reset[0], sizeof(vendor_lttpr_write_data_reset));
92 
93 	link_configure_fixed_vs_pe_retimer(link->ddc,
94 			&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
95 
96 	link_configure_fixed_vs_pe_retimer(link->ddc,
97 			&vendor_lttpr_write_data_pe[0], sizeof(vendor_lttpr_write_data_pe));
98 }
99 
perform_fixed_vs_pe_nontransparent_training_sequence(struct dc_link * link,const struct link_resource * link_res,struct link_training_settings * lt_settings)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(&lt_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, 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 				&target_rate,
137 				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(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, repeater_id);
152 
153 			if (status != LINK_TRAINING_SUCCESS) {
154 				repeater_training_done(link, repeater_id);
155 				break;
156 			}
157 
158 			status = perform_8b_10b_channel_equalization_sequence(link,
159 					link_res,
160 					lt_settings,
161 					repeater_id);
162 
163 			repeater_training_done(link, 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, DPRX);
178 		if (status == LINK_TRAINING_SUCCESS) {
179 			status = perform_8b_10b_channel_equalization_sequence(link,
180 								       link_res,
181 								       lt_settings,
182 								       DPRX);
183 		}
184 	}
185 
186 	return status;
187 }
188 
dp_perform_fixed_vs_pe_training_sequence(struct dc_link * link,const struct link_resource * link_res,struct link_training_settings * lt_settings)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 			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(&lt_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(link->ddc,
239 			&vendor_lttpr_write_data_reset[0], sizeof(vendor_lttpr_write_data_reset));
240 	link_configure_fixed_vs_pe_retimer(link->ddc,
241 			&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
242 	link_configure_fixed_vs_pe_retimer(link->ddc,
243 			&vendor_lttpr_write_data_pe[0], sizeof(vendor_lttpr_write_data_pe));
244 
245 	/* Vendor specific: Enable intercept */
246 	link_configure_fixed_vs_pe_retimer(link->ddc,
247 			&vendor_lttpr_write_data_intercept_en[0], 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 		&downspread.raw, sizeof(downspread));
267 
268 	core_link_write_dpcd(link, DP_LANE_COUNT_SET,
269 		&lane_count_set.raw, 1);
270 
271 	rate = get_dpcd_link_rate(&lt_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 					&toggle_rate,
282 					1);
283 		}
284 
285 		link->vendor_specific_lttpr_link_rate_wa = rate;
286 	}
287 
288 	core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 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(link->ddc,
301 			&vendor_lttpr_write_data_dpmf[0],
302 			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(link->ddc,
306 				&vendor_lttpr_write_data_4lane_1[0], sizeof(vendor_lttpr_write_data_4lane_1));
307 		link_configure_fixed_vs_pe_retimer(link->ddc,
308 				&vendor_lttpr_write_data_4lane_2[0], sizeof(vendor_lttpr_write_data_4lane_2));
309 		link_configure_fixed_vs_pe_retimer(link->ddc,
310 				&vendor_lttpr_write_data_4lane_3[0], sizeof(vendor_lttpr_write_data_4lane_3));
311 		link_configure_fixed_vs_pe_retimer(link->ddc,
312 				&vendor_lttpr_write_data_4lane_4[0], sizeof(vendor_lttpr_write_data_4lane_4));
313 		link_configure_fixed_vs_pe_retimer(link->ddc,
314 				&vendor_lttpr_write_data_4lane_5[0], 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 					lt_settings,
347 					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 						lt_settings->pattern_for_cr,
358 						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(pre_disable_intercept_delay_ms);
363 					if (link_configure_fixed_vs_pe_retimer(link->ddc,
364 							&vendor_lttpr_write_data_intercept_dis[0],
365 							sizeof(vendor_lttpr_write_data_intercept_dis)))
366 						break;
367 
368 					link_configure_fixed_vs_pe_retimer(link->ddc,
369 							&vendor_lttpr_write_data_intercept_en[0],
370 							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(link->ddc,
385 						&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
386 				link_configure_fixed_vs_pe_retimer(link->ddc,
387 						&vendor_lttpr_write_data_pe[0], sizeof(vendor_lttpr_write_data_pe));
388 
389 				dpcd_set_lane_settings(
390 						link,
391 						lt_settings,
392 						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_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 					lt_settings,
408 					dpcd_lane_status,
409 					&dpcd_lane_status_updated,
410 					dpcd_lane_adjust,
411 					0);
412 
413 			/* 5. check CR done*/
414 			if (dp_is_cr_done(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, dpcd_lane_adjust,
435 					lt_settings->hw_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(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(link->ddc,
461 				&vendor_lttpr_write_data_adicora_eq1[0],
462 				sizeof(vendor_lttpr_write_data_adicora_eq1));
463 		link_configure_fixed_vs_pe_retimer(link->ddc,
464 				&vendor_lttpr_write_data_adicora_eq2[0],
465 				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, tr_pattern, 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, lt_settings, 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(link->ddc,
492 					&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
493 			link_configure_fixed_vs_pe_retimer(link->ddc,
494 					&vendor_lttpr_write_data_pe[0], 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 					tr_pattern, 0);
506 
507 				link_configure_fixed_vs_pe_retimer(link->ddc,
508 						&vendor_lttpr_write_data_adicora_eq3[0],
509 						sizeof(vendor_lttpr_write_data_adicora_eq3));
510 
511 			} else
512 				dpcd_set_lane_settings(link, lt_settings, 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_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 				lt_settings,
527 				dpcd_lane_status,
528 				&dpcd_lane_status_updated,
529 				dpcd_lane_adjust,
530 				0);
531 
532 			/* 5. check CR done*/
533 			if (!dp_is_cr_done(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(lane_count, dpcd_lane_status) &&
540 					dp_is_symbol_locked(lane_count, dpcd_lane_status) &&
541 					dp_is_interlane_aligned(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, dpcd_lane_adjust,
548 					lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
549 		}
550 	}
551 
552 	return status;
553 }
554