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 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				(uint8_t *)&training_rd_interval,
51				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
58static 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				(uint8_t *)&training_rd_interval,
70				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				(uint8_t *)&training_rd_interval,
77				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
92void 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_setting);
114	lt_settings->eq_pattern_time = get_eq_training_aux_rd_interval(link, link_setting);
115	lt_settings->pattern_for_cr = decide_cr_training_pattern(link_setting);
116	lt_settings->pattern_for_eq = decide_eq_training_pattern(link, 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, lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
123}
124
125enum 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
154enum 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, 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				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					lt_settings->pattern_for_cr,
202					offset);
203		else
204			dpcd_set_lane_settings(
205					link,
206					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_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				lt_settings,
222				dpcd_lane_status,
223				&dpcd_lane_status_updated,
224				dpcd_lane_adjust,
225				offset);
226
227		/* 5. check CR done*/
228		if (dp_is_cr_done(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(&lt_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(&lt_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(&lt_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, dpcd_lane_adjust,
255				lt_settings->hw_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(lane_count, dpcd_lane_status);
268}
269
270enum 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(&lt_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, 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, 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				tr_pattern, offset);
307		else
308			dpcd_set_lane_settings(link, 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						link->dpcd_caps.lttpr_caps.aux_rd_interval[offset - 1]);
317
318		dp_wait_for_training_aux_rd_interval(
319				link,
320				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			lt_settings,
328			dpcd_lane_status,
329			&dpcd_lane_status_updated,
330			dpcd_lane_adjust,
331			offset);
332
333		/* 5. check CR done*/
334		if (!dp_is_cr_done(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(lane_count, dpcd_lane_status) &&
341				dp_is_symbol_locked(lane_count, dpcd_lane_status) &&
342				dp_is_interlane_aligned(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, dpcd_lane_adjust,
347				lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
348	}
349
350	return LINK_TRAINING_EQ_FAIL_EQ;
351
352}
353
354enum 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, 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(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, repeater_id);
381
382			if (status != LINK_TRAINING_SUCCESS) {
383				repeater_training_done(link, repeater_id);
384				break;
385			}
386
387			status = perform_8b_10b_channel_equalization_sequence(link,
388					link_res,
389					lt_settings,
390					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, 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, DPRX);
409		if (status == LINK_TRAINING_SUCCESS) {
410			status = perform_8b_10b_channel_equalization_sequence(link,
411					link_res,
412					lt_settings,
413					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