File Index
Files and directories
Sign in
FILE

link_dp_training_8b_10b.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c C
  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
 38static 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
 49static 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
 77static 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
113void 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	/* Some embedded LTTPRs rely on receiving TPS2 before LT to interop reliably with sensitive VGA dongles
147	 * This allows these LTTPRs to minimize freq/phase and skew variation during lock and deskew sequences
148	 */
149	if ((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) ==
150			AMD_EXT_DISPLAY_PATH_CAPS__DP_EARLY_8B10B_TPS2) {
151		lt_settings->lttpr_early_tps2 = true;
152	}
153}
154
155enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link)
156{
157	bool is_lttpr_present = dp_is_lttpr_present(link);
158	bool vbios_lttpr_force_non_transparent = link->dc->caps.vbios_lttpr_enable;
159	bool vbios_lttpr_aware = link->dc->caps.vbios_lttpr_aware;
160
161	if (!is_lttpr_present)
162		return LTTPR_MODE_NON_LTTPR;
163
164	if (vbios_lttpr_aware) {
165		if (vbios_lttpr_force_non_transparent) {
166			DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT due to VBIOS DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE set to 1.\n");
167			return LTTPR_MODE_NON_TRANSPARENT;
168		} else {
169			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");
170			return LTTPR_MODE_TRANSPARENT;
171		}
172	}
173
174	if (link->dc->config.allow_lttpr_non_transparent_mode.bits.DP1_4A &&
175			link->dc->caps.extended_aux_timeout_support) {
176		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");
177		return LTTPR_MODE_NON_TRANSPARENT;
178	}
179
180	DC_LOG_DC("chose LTTPR_MODE_NON_LTTPR.\n");
181	return LTTPR_MODE_NON_LTTPR;
182}
183
184static void set_link_settings_and_perform_early_tps2_retimer_pre_lt_sequence(struct dc_link *link,
185	const struct link_resource *link_res,
186	struct link_training_settings *lt_settings,
187	uint32_t lttpr_count)
188{
189	/* Vendor-specific LTTPR early TPS2 sequence:
190	* 1. Output TPS2
191	* 2. Wait 400us
192	* 3. Set link settings as usual
193	* 4. Write TPS1 to DP_TRAINING_PATTERN_SET_PHY_REPEATERx targeting LTTPR closest to host
194	* 5. Wait 1ms
195	* 6. Begin link training as usual
196	* */
197
198	uint32_t closest_lttpr_address_offset = dp_get_closest_lttpr_offset(lttpr_count);
199
200	union dpcd_training_pattern dpcd_pattern = {0};
201
202	dpcd_pattern.v1_4.TRAINING_PATTERN_SET = 1;
203	dpcd_pattern.v1_4.SCRAMBLING_DISABLE = 1;
204
205	DC_LOG_HW_LINK_TRAINING("%s\n GPU sends TPS2. Wait 400us.\n", __func__);
206
207	dp_set_hw_training_pattern(link, link_res, DP_TRAINING_PATTERN_SEQUENCE_2, DPRX);
208
209	dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX);
210
211	udelay(400);
212
213	dpcd_set_link_settings(link, lt_settings);
214
215	core_link_write_dpcd(link, DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + closest_lttpr_address_offset, &dpcd_pattern.raw, 1);
216
217	udelay(1000);
218	}
219
220enum link_training_result perform_8b_10b_clock_recovery_sequence(
221	struct dc_link *link,
222	const struct link_resource *link_res,
223	struct link_training_settings *lt_settings,
224	uint32_t offset)
225{
226	enum dc_status status;
227	uint32_t retries_cr;
228	uint32_t retry_count;
229	uint32_t wait_time_microsec;
230	enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
231	union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX];
232	union lane_align_status_updated dpcd_lane_status_updated;
233	union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
234
235	retries_cr = 0;
236	retry_count = 0;
237
238	memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status));
239	memset(&dpcd_lane_status_updated, '\0',
240	sizeof(dpcd_lane_status_updated));
241
242	if (!link->ctx->dc->work_arounds.lt_early_cr_pattern)
243		dp_set_hw_training_pattern(link, link_res, lt_settings->pattern_for_cr, offset);
244
245	/* najeeb - The synaptics MST hub can put the LT in
246	* infinite loop by switching the VS
247	*/
248	/* between level 0 and level 1 continuously, here
249	* we try for CR lock for LinkTrainingMaxCRRetry count*/
250	while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
251		(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
252
253
254		/* 1. call HWSS to set lane settings*/
255		dp_set_hw_lane_settings(
256				link,
257				link_res,
258				lt_settings,
259				offset);
260
261		/* 2. update DPCD of the receiver*/
262		if (!retry_count)
263			/* EPR #361076 - write as a 5-byte burst,
264			 * but only for the 1-st iteration.*/
265			dpcd_set_lt_pattern_and_lane_settings(
266					link,
267					lt_settings,
268					lt_settings->pattern_for_cr,
269					offset);
270		else
271			dpcd_set_lane_settings(
272					link,
273					lt_settings,
274					offset);
275
276		/* 3. wait receiver to lock-on*/
277		wait_time_microsec = lt_settings->cr_pattern_time;
278
279		dp_wait_for_training_aux_rd_interval(
280				link,
281				wait_time_microsec);
282
283		/* 4. Read lane status and requested drive
284		* settings as set by the sink
285		*/
286		status = dp_get_lane_status_and_lane_adjust(
287				link,
288				lt_settings,
289				dpcd_lane_status,
290				&dpcd_lane_status_updated,
291				dpcd_lane_adjust,
292				offset);
293
294		if (dp_check_dpcd_reqeust_status(link, status))
295			return LINK_TRAINING_ABORT;
296
297		/* 5. check CR done*/
298		if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
299			DC_LOG_HW_LINK_TRAINING("%s: Clock recovery OK\n", __func__);
300			return LINK_TRAINING_SUCCESS;
301		}
302
303		/* 6. max VS reached*/
304		if ((link_dp_get_encoding_format(&lt_settings->link_settings) ==
305				DP_8b_10b_ENCODING) &&
306				dp_is_max_vs_reached(lt_settings))
307			break;
308
309		/* 7. same lane settings*/
310		/* Note: settings are the same for all lanes,
311		 * so comparing first lane is sufficient*/
312		if ((link_dp_get_encoding_format(&lt_settings->link_settings) == DP_8b_10b_ENCODING) &&
313				lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
314						dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
315			retries_cr++;
316		else if ((link_dp_get_encoding_format(&lt_settings->link_settings) == DP_128b_132b_ENCODING) &&
317				lt_settings->dpcd_lane_settings[0].tx_ffe.PRESET_VALUE ==
318						dpcd_lane_adjust[0].tx_ffe.PRESET_VALUE)
319			retries_cr++;
320		else
321			retries_cr = 0;
322
323		/* 8. update VS/PE/PC2 in lt_settings*/
324		dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
325				lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
326		retry_count++;
327	}
328
329	if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) {
330		ASSERT(0);
331		DC_LOG_ERROR("%s: Link Training Error, could not get CR after %d tries. Possibly voltage swing issue",
332			__func__,
333			LINK_TRAINING_MAX_CR_RETRY);
334
335	}
336
337	return dp_get_cr_failure(lane_count, dpcd_lane_status);
338}
339
340enum link_training_result perform_8b_10b_channel_equalization_sequence(
341	struct dc_link *link,
342	const struct link_resource *link_res,
343	struct link_training_settings *lt_settings,
344	uint32_t offset)
345{
346	enum dc_status status;
347	enum dc_dp_training_pattern tr_pattern;
348	uint32_t retries_ch_eq;
349	uint32_t wait_time_microsec;
350	enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
351	union lane_align_status_updated dpcd_lane_status_updated = {0};
352	union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
353	union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
354
355	/* Note: also check that TPS4 is a supported feature*/
356	tr_pattern = lt_settings->pattern_for_eq;
357
358	if (is_repeater(lt_settings, offset) && link_dp_get_encoding_format(&lt_settings->link_settings) == DP_8b_10b_ENCODING)
359		tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4;
360
361	dp_set_hw_training_pattern(link, link_res, tr_pattern, offset);
362
363	for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT;
364		retries_ch_eq++) {
365
366		dp_set_hw_lane_settings(link, link_res, lt_settings, offset);
367
368		/* 2. update DPCD*/
369		if (!retries_ch_eq)
370			/* EPR #361076 - write as a 5-byte burst,
371			 * but only for the 1-st iteration
372			 */
373
374			dpcd_set_lt_pattern_and_lane_settings(
375				link,
376				lt_settings,
377				tr_pattern, offset);
378		else
379			dpcd_set_lane_settings(link, lt_settings, offset);
380
381		/* 3. wait for receiver to lock-on*/
382		wait_time_microsec = dp_get_eq_aux_rd_interval(link, lt_settings, offset, retries_ch_eq);
383
384		dp_wait_for_training_aux_rd_interval(
385				link,
386				wait_time_microsec);
387
388		/* 4. Read lane status and requested
389		 * drive settings as set by the sink*/
390
391		status = dp_get_lane_status_and_lane_adjust(
392			link,
393			lt_settings,
394			dpcd_lane_status,
395			&dpcd_lane_status_updated,
396			dpcd_lane_adjust,
397			offset);
398
399		if (dp_check_dpcd_reqeust_status(link, status))
400			return LINK_TRAINING_ABORT;
401
402		/* 5. check CR done*/
403		if (!dp_is_cr_done(lane_count, dpcd_lane_status))
404			return dpcd_lane_status[0].bits.CR_DONE_0 ?
405					LINK_TRAINING_EQ_FAIL_CR_PARTIAL :
406					LINK_TRAINING_EQ_FAIL_CR;
407
408		/* 6. check CHEQ done*/
409		if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) &&
410				dp_is_symbol_locked(lane_count, dpcd_lane_status) &&
411				dp_check_interlane_aligned(dpcd_lane_status_updated, link, retries_ch_eq))
412			return LINK_TRAINING_SUCCESS;
413
414		/* 7. update VS/PE/PC2 in lt_settings*/
415		dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
416				lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
417	}
418
419	return LINK_TRAINING_EQ_FAIL_EQ;
420
421}
422
423enum link_training_result dp_perform_8b_10b_link_training(
424		struct dc_link *link,
425		const struct link_resource *link_res,
426		struct link_training_settings *lt_settings)
427{
428	enum link_training_result status = LINK_TRAINING_SUCCESS;
429
430	uint8_t repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
431	uint8_t repeater_id;
432	uint8_t lane = 0;
433
434	if (link->ctx->dc->work_arounds.lt_early_cr_pattern)
435		start_clock_recovery_pattern_early(link, link_res, lt_settings, DPRX);
436
437	/* 1. set link rate, lane count and spread. */
438	if (lt_settings->lttpr_early_tps2)
439		set_link_settings_and_perform_early_tps2_retimer_pre_lt_sequence(link, link_res, lt_settings, repeater_cnt);
440	else
441		dpcd_set_link_settings(link, lt_settings);
442
443	if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
444
445		/* 2. perform link training (set link training done
446		 *  to false is done as well)
447		 */
448
449		for (repeater_id = repeater_cnt; (repeater_id > 0 && status == LINK_TRAINING_SUCCESS);
450				repeater_id--) {
451			status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, repeater_id);
452
453			if (status != LINK_TRAINING_SUCCESS) {
454				repeater_training_done(link, repeater_id);
455				break;
456			}
457
458			status = perform_8b_10b_channel_equalization_sequence(link,
459					link_res,
460					lt_settings,
461					repeater_id);
462			if (status == LINK_TRAINING_SUCCESS)
463				DC_LOG_HW_LINK_TRAINING("%s: Channel EQ done.\n", __func__);
464
465			repeater_training_done(link, repeater_id);
466
467			if (status != LINK_TRAINING_SUCCESS)
468				break;
469
470			for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) {
471				lt_settings->dpcd_lane_settings[lane].raw = 0;
472				lt_settings->hw_lane_settings[lane].VOLTAGE_SWING = 0;
473				lt_settings->hw_lane_settings[lane].PRE_EMPHASIS = 0;
474			}
475		}
476	}
477
478	if (status == LINK_TRAINING_SUCCESS) {
479		status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, DPRX);
480		if (status == LINK_TRAINING_SUCCESS) {
481			status = perform_8b_10b_channel_equalization_sequence(link,
482					link_res,
483					lt_settings,
484					DPRX);
485			if (status == LINK_TRAINING_SUCCESS)
486				DC_LOG_HW_LINK_TRAINING("%s: Channel EQ done.\n", __func__);
487		}
488	}
489
490	return status;
491}
~/ Read-only file preview.