xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/tegra/dp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: MIT
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2013-2019 NVIDIA Corporation
4*4882a593Smuzhiyun  * Copyright (C) 2015 Rob Clark
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <drm/drm_crtc.h>
8*4882a593Smuzhiyun #include <drm/drm_dp_helper.h>
9*4882a593Smuzhiyun #include <drm/drm_print.h>
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include "dp.h"
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun static const u8 drm_dp_edp_revisions[] = { 0x11, 0x12, 0x13, 0x14 };
14*4882a593Smuzhiyun 
drm_dp_link_caps_reset(struct drm_dp_link_caps * caps)15*4882a593Smuzhiyun static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps)
16*4882a593Smuzhiyun {
17*4882a593Smuzhiyun 	caps->enhanced_framing = false;
18*4882a593Smuzhiyun 	caps->tps3_supported = false;
19*4882a593Smuzhiyun 	caps->fast_training = false;
20*4882a593Smuzhiyun 	caps->channel_coding = false;
21*4882a593Smuzhiyun 	caps->alternate_scrambler_reset = false;
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun 
drm_dp_link_caps_copy(struct drm_dp_link_caps * dest,const struct drm_dp_link_caps * src)24*4882a593Smuzhiyun void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
25*4882a593Smuzhiyun 			   const struct drm_dp_link_caps *src)
26*4882a593Smuzhiyun {
27*4882a593Smuzhiyun 	dest->enhanced_framing = src->enhanced_framing;
28*4882a593Smuzhiyun 	dest->tps3_supported = src->tps3_supported;
29*4882a593Smuzhiyun 	dest->fast_training = src->fast_training;
30*4882a593Smuzhiyun 	dest->channel_coding = src->channel_coding;
31*4882a593Smuzhiyun 	dest->alternate_scrambler_reset = src->alternate_scrambler_reset;
32*4882a593Smuzhiyun }
33*4882a593Smuzhiyun 
drm_dp_link_reset(struct drm_dp_link * link)34*4882a593Smuzhiyun static void drm_dp_link_reset(struct drm_dp_link *link)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	unsigned int i;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	if (!link)
39*4882a593Smuzhiyun 		return;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	link->revision = 0;
42*4882a593Smuzhiyun 	link->max_rate = 0;
43*4882a593Smuzhiyun 	link->max_lanes = 0;
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	drm_dp_link_caps_reset(&link->caps);
46*4882a593Smuzhiyun 	link->aux_rd_interval.cr = 0;
47*4882a593Smuzhiyun 	link->aux_rd_interval.ce = 0;
48*4882a593Smuzhiyun 	link->edp = 0;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	link->rate = 0;
51*4882a593Smuzhiyun 	link->lanes = 0;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	for (i = 0; i < DP_MAX_SUPPORTED_RATES; i++)
54*4882a593Smuzhiyun 		link->rates[i] = 0;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	link->num_rates = 0;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun /**
60*4882a593Smuzhiyun  * drm_dp_link_add_rate() - add a rate to the list of supported rates
61*4882a593Smuzhiyun  * @link: the link to add the rate to
62*4882a593Smuzhiyun  * @rate: the rate to add
63*4882a593Smuzhiyun  *
64*4882a593Smuzhiyun  * Add a link rate to the list of supported link rates.
65*4882a593Smuzhiyun  *
66*4882a593Smuzhiyun  * Returns:
67*4882a593Smuzhiyun  * 0 on success or one of the following negative error codes on failure:
68*4882a593Smuzhiyun  * - ENOSPC if the maximum number of supported rates has been reached
69*4882a593Smuzhiyun  * - EEXISTS if the link already supports this rate
70*4882a593Smuzhiyun  *
71*4882a593Smuzhiyun  * See also:
72*4882a593Smuzhiyun  * drm_dp_link_remove_rate()
73*4882a593Smuzhiyun  */
drm_dp_link_add_rate(struct drm_dp_link * link,unsigned long rate)74*4882a593Smuzhiyun int drm_dp_link_add_rate(struct drm_dp_link *link, unsigned long rate)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	unsigned int i, pivot;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	if (link->num_rates == DP_MAX_SUPPORTED_RATES)
79*4882a593Smuzhiyun 		return -ENOSPC;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	for (pivot = 0; pivot < link->num_rates; pivot++)
82*4882a593Smuzhiyun 		if (rate <= link->rates[pivot])
83*4882a593Smuzhiyun 			break;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	if (pivot != link->num_rates && rate == link->rates[pivot])
86*4882a593Smuzhiyun 		return -EEXIST;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	for (i = link->num_rates; i > pivot; i--)
89*4882a593Smuzhiyun 		link->rates[i] = link->rates[i - 1];
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	link->rates[pivot] = rate;
92*4882a593Smuzhiyun 	link->num_rates++;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	return 0;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun /**
98*4882a593Smuzhiyun  * drm_dp_link_remove_rate() - remove a rate from the list of supported rates
99*4882a593Smuzhiyun  * @link: the link from which to remove the rate
100*4882a593Smuzhiyun  * @rate: the rate to remove
101*4882a593Smuzhiyun  *
102*4882a593Smuzhiyun  * Removes a link rate from the list of supported link rates.
103*4882a593Smuzhiyun  *
104*4882a593Smuzhiyun  * Returns:
105*4882a593Smuzhiyun  * 0 on success or one of the following negative error codes on failure:
106*4882a593Smuzhiyun  * - EINVAL if the specified rate is not among the supported rates
107*4882a593Smuzhiyun  *
108*4882a593Smuzhiyun  * See also:
109*4882a593Smuzhiyun  * drm_dp_link_add_rate()
110*4882a593Smuzhiyun  */
drm_dp_link_remove_rate(struct drm_dp_link * link,unsigned long rate)111*4882a593Smuzhiyun int drm_dp_link_remove_rate(struct drm_dp_link *link, unsigned long rate)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	unsigned int i;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	for (i = 0; i < link->num_rates; i++)
116*4882a593Smuzhiyun 		if (rate == link->rates[i])
117*4882a593Smuzhiyun 			break;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	if (i == link->num_rates)
120*4882a593Smuzhiyun 		return -EINVAL;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	link->num_rates--;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	while (i < link->num_rates) {
125*4882a593Smuzhiyun 		link->rates[i] = link->rates[i + 1];
126*4882a593Smuzhiyun 		i++;
127*4882a593Smuzhiyun 	}
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	return 0;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun /**
133*4882a593Smuzhiyun  * drm_dp_link_update_rates() - normalize the supported link rates array
134*4882a593Smuzhiyun  * @link: the link for which to normalize the supported link rates
135*4882a593Smuzhiyun  *
136*4882a593Smuzhiyun  * Users should call this function after they've manually modified the array
137*4882a593Smuzhiyun  * of supported link rates. This function removes any stale entries, compacts
138*4882a593Smuzhiyun  * the array and updates the supported link rate count. Note that calling the
139*4882a593Smuzhiyun  * drm_dp_link_remove_rate() function already does this janitorial work.
140*4882a593Smuzhiyun  *
141*4882a593Smuzhiyun  * See also:
142*4882a593Smuzhiyun  * drm_dp_link_add_rate(), drm_dp_link_remove_rate()
143*4882a593Smuzhiyun  */
drm_dp_link_update_rates(struct drm_dp_link * link)144*4882a593Smuzhiyun void drm_dp_link_update_rates(struct drm_dp_link *link)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	unsigned int i, count = 0;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	for (i = 0; i < link->num_rates; i++) {
149*4882a593Smuzhiyun 		if (link->rates[i] != 0)
150*4882a593Smuzhiyun 			link->rates[count++] = link->rates[i];
151*4882a593Smuzhiyun 	}
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	for (i = count; i < link->num_rates; i++)
154*4882a593Smuzhiyun 		link->rates[i] = 0;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	link->num_rates = count;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun /**
160*4882a593Smuzhiyun  * drm_dp_link_probe() - probe a DisplayPort link for capabilities
161*4882a593Smuzhiyun  * @aux: DisplayPort AUX channel
162*4882a593Smuzhiyun  * @link: pointer to structure in which to return link capabilities
163*4882a593Smuzhiyun  *
164*4882a593Smuzhiyun  * The structure filled in by this function can usually be passed directly
165*4882a593Smuzhiyun  * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
166*4882a593Smuzhiyun  * configure the link based on the link's capabilities.
167*4882a593Smuzhiyun  *
168*4882a593Smuzhiyun  * Returns 0 on success or a negative error code on failure.
169*4882a593Smuzhiyun  */
drm_dp_link_probe(struct drm_dp_aux * aux,struct drm_dp_link * link)170*4882a593Smuzhiyun int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun 	u8 dpcd[DP_RECEIVER_CAP_SIZE], value;
173*4882a593Smuzhiyun 	unsigned int rd_interval;
174*4882a593Smuzhiyun 	int err;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	drm_dp_link_reset(link);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	err = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, sizeof(dpcd));
179*4882a593Smuzhiyun 	if (err < 0)
180*4882a593Smuzhiyun 		return err;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	link->revision = dpcd[DP_DPCD_REV];
183*4882a593Smuzhiyun 	link->max_rate = drm_dp_max_link_rate(dpcd);
184*4882a593Smuzhiyun 	link->max_lanes = drm_dp_max_lane_count(dpcd);
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	link->caps.enhanced_framing = drm_dp_enhanced_frame_cap(dpcd);
187*4882a593Smuzhiyun 	link->caps.tps3_supported = drm_dp_tps3_supported(dpcd);
188*4882a593Smuzhiyun 	link->caps.fast_training = drm_dp_fast_training_cap(dpcd);
189*4882a593Smuzhiyun 	link->caps.channel_coding = drm_dp_channel_coding_supported(dpcd);
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	if (drm_dp_alternate_scrambler_reset_cap(dpcd)) {
192*4882a593Smuzhiyun 		link->caps.alternate_scrambler_reset = true;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 		err = drm_dp_dpcd_readb(aux, DP_EDP_DPCD_REV, &value);
195*4882a593Smuzhiyun 		if (err < 0)
196*4882a593Smuzhiyun 			return err;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 		if (value >= ARRAY_SIZE(drm_dp_edp_revisions))
199*4882a593Smuzhiyun 			DRM_ERROR("unsupported eDP version: %02x\n", value);
200*4882a593Smuzhiyun 		else
201*4882a593Smuzhiyun 			link->edp = drm_dp_edp_revisions[value];
202*4882a593Smuzhiyun 	}
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	/*
205*4882a593Smuzhiyun 	 * The DPCD stores the AUX read interval in units of 4 ms. There are
206*4882a593Smuzhiyun 	 * two special cases:
207*4882a593Smuzhiyun 	 *
208*4882a593Smuzhiyun 	 *   1) if the TRAINING_AUX_RD_INTERVAL field is 0, the clock recovery
209*4882a593Smuzhiyun 	 *      and channel equalization should use 100 us or 400 us AUX read
210*4882a593Smuzhiyun 	 *      intervals, respectively
211*4882a593Smuzhiyun 	 *
212*4882a593Smuzhiyun 	 *   2) for DP v1.4 and above, clock recovery should always use 100 us
213*4882a593Smuzhiyun 	 *      AUX read intervals
214*4882a593Smuzhiyun 	 */
215*4882a593Smuzhiyun 	rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
216*4882a593Smuzhiyun 			   DP_TRAINING_AUX_RD_MASK;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	if (rd_interval > 4) {
219*4882a593Smuzhiyun 		DRM_DEBUG_KMS("AUX interval %u out of range (max. 4)\n",
220*4882a593Smuzhiyun 			      rd_interval);
221*4882a593Smuzhiyun 		rd_interval = 4;
222*4882a593Smuzhiyun 	}
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	rd_interval *= 4 * USEC_PER_MSEC;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	if (rd_interval == 0 || link->revision >= DP_DPCD_REV_14)
227*4882a593Smuzhiyun 		link->aux_rd_interval.cr = 100;
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	if (rd_interval == 0)
230*4882a593Smuzhiyun 		link->aux_rd_interval.ce = 400;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	link->rate = link->max_rate;
233*4882a593Smuzhiyun 	link->lanes = link->max_lanes;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	/* Parse SUPPORTED_LINK_RATES from eDP 1.4 */
236*4882a593Smuzhiyun 	if (link->edp >= 0x14) {
237*4882a593Smuzhiyun 		u8 supported_rates[DP_MAX_SUPPORTED_RATES * 2];
238*4882a593Smuzhiyun 		unsigned int i;
239*4882a593Smuzhiyun 		u16 rate;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 		err = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES,
242*4882a593Smuzhiyun 				       supported_rates,
243*4882a593Smuzhiyun 				       sizeof(supported_rates));
244*4882a593Smuzhiyun 		if (err < 0)
245*4882a593Smuzhiyun 			return err;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 		for (i = 0; i < DP_MAX_SUPPORTED_RATES; i++) {
248*4882a593Smuzhiyun 			rate = supported_rates[i * 2 + 1] << 8 |
249*4882a593Smuzhiyun 			       supported_rates[i * 2 + 0];
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 			drm_dp_link_add_rate(link, rate * 200);
252*4882a593Smuzhiyun 		}
253*4882a593Smuzhiyun 	}
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	return 0;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun /**
259*4882a593Smuzhiyun  * drm_dp_link_power_up() - power up a DisplayPort link
260*4882a593Smuzhiyun  * @aux: DisplayPort AUX channel
261*4882a593Smuzhiyun  * @link: pointer to a structure containing the link configuration
262*4882a593Smuzhiyun  *
263*4882a593Smuzhiyun  * Returns 0 on success or a negative error code on failure.
264*4882a593Smuzhiyun  */
drm_dp_link_power_up(struct drm_dp_aux * aux,struct drm_dp_link * link)265*4882a593Smuzhiyun int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun 	u8 value;
268*4882a593Smuzhiyun 	int err;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
271*4882a593Smuzhiyun 	if (link->revision < 0x11)
272*4882a593Smuzhiyun 		return 0;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
275*4882a593Smuzhiyun 	if (err < 0)
276*4882a593Smuzhiyun 		return err;
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	value &= ~DP_SET_POWER_MASK;
279*4882a593Smuzhiyun 	value |= DP_SET_POWER_D0;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
282*4882a593Smuzhiyun 	if (err < 0)
283*4882a593Smuzhiyun 		return err;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	/*
286*4882a593Smuzhiyun 	 * According to the DP 1.1 specification, a "Sink Device must exit the
287*4882a593Smuzhiyun 	 * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
288*4882a593Smuzhiyun 	 * Control Field" (register 0x600).
289*4882a593Smuzhiyun 	 */
290*4882a593Smuzhiyun 	usleep_range(1000, 2000);
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	return 0;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun /**
296*4882a593Smuzhiyun  * drm_dp_link_power_down() - power down a DisplayPort link
297*4882a593Smuzhiyun  * @aux: DisplayPort AUX channel
298*4882a593Smuzhiyun  * @link: pointer to a structure containing the link configuration
299*4882a593Smuzhiyun  *
300*4882a593Smuzhiyun  * Returns 0 on success or a negative error code on failure.
301*4882a593Smuzhiyun  */
drm_dp_link_power_down(struct drm_dp_aux * aux,struct drm_dp_link * link)302*4882a593Smuzhiyun int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link)
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun 	u8 value;
305*4882a593Smuzhiyun 	int err;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
308*4882a593Smuzhiyun 	if (link->revision < 0x11)
309*4882a593Smuzhiyun 		return 0;
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
312*4882a593Smuzhiyun 	if (err < 0)
313*4882a593Smuzhiyun 		return err;
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	value &= ~DP_SET_POWER_MASK;
316*4882a593Smuzhiyun 	value |= DP_SET_POWER_D3;
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
319*4882a593Smuzhiyun 	if (err < 0)
320*4882a593Smuzhiyun 		return err;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	return 0;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun /**
326*4882a593Smuzhiyun  * drm_dp_link_configure() - configure a DisplayPort link
327*4882a593Smuzhiyun  * @aux: DisplayPort AUX channel
328*4882a593Smuzhiyun  * @link: pointer to a structure containing the link configuration
329*4882a593Smuzhiyun  *
330*4882a593Smuzhiyun  * Returns 0 on success or a negative error code on failure.
331*4882a593Smuzhiyun  */
drm_dp_link_configure(struct drm_dp_aux * aux,struct drm_dp_link * link)332*4882a593Smuzhiyun int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun 	u8 values[2], value;
335*4882a593Smuzhiyun 	int err;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	if (link->ops && link->ops->configure) {
338*4882a593Smuzhiyun 		err = link->ops->configure(link);
339*4882a593Smuzhiyun 		if (err < 0) {
340*4882a593Smuzhiyun 			DRM_ERROR("failed to configure DP link: %d\n", err);
341*4882a593Smuzhiyun 			return err;
342*4882a593Smuzhiyun 		}
343*4882a593Smuzhiyun 	}
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	values[0] = drm_dp_link_rate_to_bw_code(link->rate);
346*4882a593Smuzhiyun 	values[1] = link->lanes;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	if (link->caps.enhanced_framing)
349*4882a593Smuzhiyun 		values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
352*4882a593Smuzhiyun 	if (err < 0)
353*4882a593Smuzhiyun 		return err;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	if (link->caps.channel_coding)
356*4882a593Smuzhiyun 		value = DP_SET_ANSI_8B10B;
357*4882a593Smuzhiyun 	else
358*4882a593Smuzhiyun 		value = 0;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	err = drm_dp_dpcd_writeb(aux, DP_MAIN_LINK_CHANNEL_CODING_SET, value);
361*4882a593Smuzhiyun 	if (err < 0)
362*4882a593Smuzhiyun 		return err;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	if (link->caps.alternate_scrambler_reset) {
365*4882a593Smuzhiyun 		err = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET,
366*4882a593Smuzhiyun 					 DP_ALTERNATE_SCRAMBLER_RESET_ENABLE);
367*4882a593Smuzhiyun 		if (err < 0)
368*4882a593Smuzhiyun 			return err;
369*4882a593Smuzhiyun 	}
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	return 0;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun /**
375*4882a593Smuzhiyun  * drm_dp_link_choose() - choose the lowest possible configuration for a mode
376*4882a593Smuzhiyun  * @link: DRM DP link object
377*4882a593Smuzhiyun  * @mode: DRM display mode
378*4882a593Smuzhiyun  * @info: DRM display information
379*4882a593Smuzhiyun  *
380*4882a593Smuzhiyun  * According to the eDP specification, a source should select a configuration
381*4882a593Smuzhiyun  * with the lowest number of lanes and the lowest possible link rate that can
382*4882a593Smuzhiyun  * match the bitrate requirements of a video mode. However it must ensure not
383*4882a593Smuzhiyun  * to exceed the capabilities of the sink.
384*4882a593Smuzhiyun  *
385*4882a593Smuzhiyun  * Returns: 0 on success or a negative error code on failure.
386*4882a593Smuzhiyun  */
drm_dp_link_choose(struct drm_dp_link * link,const struct drm_display_mode * mode,const struct drm_display_info * info)387*4882a593Smuzhiyun int drm_dp_link_choose(struct drm_dp_link *link,
388*4882a593Smuzhiyun 		       const struct drm_display_mode *mode,
389*4882a593Smuzhiyun 		       const struct drm_display_info *info)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun 	/* available link symbol clock rates */
392*4882a593Smuzhiyun 	static const unsigned int rates[3] = { 162000, 270000, 540000 };
393*4882a593Smuzhiyun 	/* available number of lanes */
394*4882a593Smuzhiyun 	static const unsigned int lanes[3] = { 1, 2, 4 };
395*4882a593Smuzhiyun 	unsigned long requirement, capacity;
396*4882a593Smuzhiyun 	unsigned int rate = link->max_rate;
397*4882a593Smuzhiyun 	unsigned int i, j;
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	/* bandwidth requirement */
400*4882a593Smuzhiyun 	requirement = mode->clock * info->bpc * 3;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(lanes) && lanes[i] <= link->max_lanes; i++) {
403*4882a593Smuzhiyun 		for (j = 0; j < ARRAY_SIZE(rates) && rates[j] <= rate; j++) {
404*4882a593Smuzhiyun 			/*
405*4882a593Smuzhiyun 			 * Capacity for this combination of lanes and rate,
406*4882a593Smuzhiyun 			 * factoring in the ANSI 8B/10B encoding.
407*4882a593Smuzhiyun 			 *
408*4882a593Smuzhiyun 			 * Link rates in the DRM DP helpers are really link
409*4882a593Smuzhiyun 			 * symbol frequencies, so a tenth of the actual rate
410*4882a593Smuzhiyun 			 * of the link.
411*4882a593Smuzhiyun 			 */
412*4882a593Smuzhiyun 			capacity = lanes[i] * (rates[j] * 10) * 8 / 10;
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 			if (capacity >= requirement) {
415*4882a593Smuzhiyun 				DRM_DEBUG_KMS("using %u lanes at %u kHz (%lu/%lu kbps)\n",
416*4882a593Smuzhiyun 					      lanes[i], rates[j], requirement,
417*4882a593Smuzhiyun 					      capacity);
418*4882a593Smuzhiyun 				link->lanes = lanes[i];
419*4882a593Smuzhiyun 				link->rate = rates[j];
420*4882a593Smuzhiyun 				return 0;
421*4882a593Smuzhiyun 			}
422*4882a593Smuzhiyun 		}
423*4882a593Smuzhiyun 	}
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	return -ERANGE;
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun /**
429*4882a593Smuzhiyun  * DOC: Link training
430*4882a593Smuzhiyun  *
431*4882a593Smuzhiyun  * These functions contain common logic and helpers to implement DisplayPort
432*4882a593Smuzhiyun  * link training.
433*4882a593Smuzhiyun  */
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun /**
436*4882a593Smuzhiyun  * drm_dp_link_train_init() - initialize DisplayPort link training state
437*4882a593Smuzhiyun  * @train: DisplayPort link training state
438*4882a593Smuzhiyun  */
drm_dp_link_train_init(struct drm_dp_link_train * train)439*4882a593Smuzhiyun void drm_dp_link_train_init(struct drm_dp_link_train *train)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun 	struct drm_dp_link_train_set *request = &train->request;
442*4882a593Smuzhiyun 	struct drm_dp_link_train_set *adjust = &train->adjust;
443*4882a593Smuzhiyun 	unsigned int i;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	for (i = 0; i < 4; i++) {
446*4882a593Smuzhiyun 		request->voltage_swing[i] = 0;
447*4882a593Smuzhiyun 		adjust->voltage_swing[i] = 0;
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 		request->pre_emphasis[i] = 0;
450*4882a593Smuzhiyun 		adjust->pre_emphasis[i] = 0;
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 		request->post_cursor[i] = 0;
453*4882a593Smuzhiyun 		adjust->post_cursor[i] = 0;
454*4882a593Smuzhiyun 	}
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	train->pattern = DP_TRAINING_PATTERN_DISABLE;
457*4882a593Smuzhiyun 	train->clock_recovered = false;
458*4882a593Smuzhiyun 	train->channel_equalized = false;
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun 
drm_dp_link_train_valid(const struct drm_dp_link_train * train)461*4882a593Smuzhiyun static bool drm_dp_link_train_valid(const struct drm_dp_link_train *train)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun 	return train->clock_recovered && train->channel_equalized;
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun 
drm_dp_link_apply_training(struct drm_dp_link * link)466*4882a593Smuzhiyun static int drm_dp_link_apply_training(struct drm_dp_link *link)
467*4882a593Smuzhiyun {
468*4882a593Smuzhiyun 	struct drm_dp_link_train_set *request = &link->train.request;
469*4882a593Smuzhiyun 	unsigned int lanes = link->lanes, *vs, *pe, *pc, i;
470*4882a593Smuzhiyun 	struct drm_dp_aux *aux = link->aux;
471*4882a593Smuzhiyun 	u8 values[4], pattern = 0;
472*4882a593Smuzhiyun 	int err;
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	err = link->ops->apply_training(link);
475*4882a593Smuzhiyun 	if (err < 0) {
476*4882a593Smuzhiyun 		DRM_ERROR("failed to apply link training: %d\n", err);
477*4882a593Smuzhiyun 		return err;
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	vs = request->voltage_swing;
481*4882a593Smuzhiyun 	pe = request->pre_emphasis;
482*4882a593Smuzhiyun 	pc = request->post_cursor;
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	/* write currently selected voltage-swing and pre-emphasis levels */
485*4882a593Smuzhiyun 	for (i = 0; i < lanes; i++)
486*4882a593Smuzhiyun 		values[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL(vs[i]) |
487*4882a593Smuzhiyun 			    DP_TRAIN_PRE_EMPHASIS_LEVEL(pe[i]);
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, values, lanes);
490*4882a593Smuzhiyun 	if (err < 0) {
491*4882a593Smuzhiyun 		DRM_ERROR("failed to set training parameters: %d\n", err);
492*4882a593Smuzhiyun 		return err;
493*4882a593Smuzhiyun 	}
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	/* write currently selected post-cursor level (if supported) */
496*4882a593Smuzhiyun 	if (link->revision >= 0x12 && link->rate == 540000) {
497*4882a593Smuzhiyun 		values[0] = values[1] = 0;
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 		for (i = 0; i < lanes; i++)
500*4882a593Smuzhiyun 			values[i / 2] |= DP_LANE_POST_CURSOR(i, pc[i]);
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 		err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_1_SET2, values,
503*4882a593Smuzhiyun 					DIV_ROUND_UP(lanes, 2));
504*4882a593Smuzhiyun 		if (err < 0) {
505*4882a593Smuzhiyun 			DRM_ERROR("failed to set post-cursor: %d\n", err);
506*4882a593Smuzhiyun 			return err;
507*4882a593Smuzhiyun 		}
508*4882a593Smuzhiyun 	}
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun 	/* write link pattern */
511*4882a593Smuzhiyun 	if (link->train.pattern != DP_TRAINING_PATTERN_DISABLE)
512*4882a593Smuzhiyun 		pattern |= DP_LINK_SCRAMBLING_DISABLE;
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	pattern |= link->train.pattern;
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, pattern);
517*4882a593Smuzhiyun 	if (err < 0) {
518*4882a593Smuzhiyun 		DRM_ERROR("failed to set training pattern: %d\n", err);
519*4882a593Smuzhiyun 		return err;
520*4882a593Smuzhiyun 	}
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	return 0;
523*4882a593Smuzhiyun }
524*4882a593Smuzhiyun 
drm_dp_link_train_wait(struct drm_dp_link * link)525*4882a593Smuzhiyun static void drm_dp_link_train_wait(struct drm_dp_link *link)
526*4882a593Smuzhiyun {
527*4882a593Smuzhiyun 	unsigned long min = 0;
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	switch (link->train.pattern) {
530*4882a593Smuzhiyun 	case DP_TRAINING_PATTERN_1:
531*4882a593Smuzhiyun 		min = link->aux_rd_interval.cr;
532*4882a593Smuzhiyun 		break;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	case DP_TRAINING_PATTERN_2:
535*4882a593Smuzhiyun 	case DP_TRAINING_PATTERN_3:
536*4882a593Smuzhiyun 		min = link->aux_rd_interval.ce;
537*4882a593Smuzhiyun 		break;
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	default:
540*4882a593Smuzhiyun 		break;
541*4882a593Smuzhiyun 	}
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	if (min > 0)
544*4882a593Smuzhiyun 		usleep_range(min, 2 * min);
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun 
drm_dp_link_get_adjustments(struct drm_dp_link * link,u8 status[DP_LINK_STATUS_SIZE])547*4882a593Smuzhiyun static void drm_dp_link_get_adjustments(struct drm_dp_link *link,
548*4882a593Smuzhiyun 					u8 status[DP_LINK_STATUS_SIZE])
549*4882a593Smuzhiyun {
550*4882a593Smuzhiyun 	struct drm_dp_link_train_set *adjust = &link->train.adjust;
551*4882a593Smuzhiyun 	unsigned int i;
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	for (i = 0; i < link->lanes; i++) {
554*4882a593Smuzhiyun 		adjust->voltage_swing[i] =
555*4882a593Smuzhiyun 			drm_dp_get_adjust_request_voltage(status, i) >>
556*4882a593Smuzhiyun 				DP_TRAIN_VOLTAGE_SWING_SHIFT;
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 		adjust->pre_emphasis[i] =
559*4882a593Smuzhiyun 			drm_dp_get_adjust_request_pre_emphasis(status, i) >>
560*4882a593Smuzhiyun 				DP_TRAIN_PRE_EMPHASIS_SHIFT;
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 		adjust->post_cursor[i] =
563*4882a593Smuzhiyun 			drm_dp_get_adjust_request_post_cursor(status, i);
564*4882a593Smuzhiyun 	}
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun 
drm_dp_link_train_adjust(struct drm_dp_link_train * train)567*4882a593Smuzhiyun static void drm_dp_link_train_adjust(struct drm_dp_link_train *train)
568*4882a593Smuzhiyun {
569*4882a593Smuzhiyun 	struct drm_dp_link_train_set *request = &train->request;
570*4882a593Smuzhiyun 	struct drm_dp_link_train_set *adjust = &train->adjust;
571*4882a593Smuzhiyun 	unsigned int i;
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	for (i = 0; i < 4; i++)
574*4882a593Smuzhiyun 		if (request->voltage_swing[i] != adjust->voltage_swing[i])
575*4882a593Smuzhiyun 			request->voltage_swing[i] = adjust->voltage_swing[i];
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	for (i = 0; i < 4; i++)
578*4882a593Smuzhiyun 		if (request->pre_emphasis[i] != adjust->pre_emphasis[i])
579*4882a593Smuzhiyun 			request->pre_emphasis[i] = adjust->pre_emphasis[i];
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	for (i = 0; i < 4; i++)
582*4882a593Smuzhiyun 		if (request->post_cursor[i] != adjust->post_cursor[i])
583*4882a593Smuzhiyun 			request->post_cursor[i] = adjust->post_cursor[i];
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun 
drm_dp_link_recover_clock(struct drm_dp_link * link)586*4882a593Smuzhiyun static int drm_dp_link_recover_clock(struct drm_dp_link *link)
587*4882a593Smuzhiyun {
588*4882a593Smuzhiyun 	u8 status[DP_LINK_STATUS_SIZE];
589*4882a593Smuzhiyun 	int err;
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	err = drm_dp_link_apply_training(link);
592*4882a593Smuzhiyun 	if (err < 0)
593*4882a593Smuzhiyun 		return err;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	drm_dp_link_train_wait(link);
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	err = drm_dp_dpcd_read_link_status(link->aux, status);
598*4882a593Smuzhiyun 	if (err < 0) {
599*4882a593Smuzhiyun 		DRM_ERROR("failed to read link status: %d\n", err);
600*4882a593Smuzhiyun 		return err;
601*4882a593Smuzhiyun 	}
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	if (!drm_dp_clock_recovery_ok(status, link->lanes))
604*4882a593Smuzhiyun 		drm_dp_link_get_adjustments(link, status);
605*4882a593Smuzhiyun 	else
606*4882a593Smuzhiyun 		link->train.clock_recovered = true;
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	return 0;
609*4882a593Smuzhiyun }
610*4882a593Smuzhiyun 
drm_dp_link_clock_recovery(struct drm_dp_link * link)611*4882a593Smuzhiyun static int drm_dp_link_clock_recovery(struct drm_dp_link *link)
612*4882a593Smuzhiyun {
613*4882a593Smuzhiyun 	unsigned int repeat;
614*4882a593Smuzhiyun 	int err;
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	/* start clock recovery using training pattern 1 */
617*4882a593Smuzhiyun 	link->train.pattern = DP_TRAINING_PATTERN_1;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	for (repeat = 1; repeat < 5; repeat++) {
620*4882a593Smuzhiyun 		err = drm_dp_link_recover_clock(link);
621*4882a593Smuzhiyun 		if (err < 0) {
622*4882a593Smuzhiyun 			DRM_ERROR("failed to recover clock: %d\n", err);
623*4882a593Smuzhiyun 			return err;
624*4882a593Smuzhiyun 		}
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 		if (link->train.clock_recovered)
627*4882a593Smuzhiyun 			break;
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 		drm_dp_link_train_adjust(&link->train);
630*4882a593Smuzhiyun 	}
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	return 0;
633*4882a593Smuzhiyun }
634*4882a593Smuzhiyun 
drm_dp_link_equalize_channel(struct drm_dp_link * link)635*4882a593Smuzhiyun static int drm_dp_link_equalize_channel(struct drm_dp_link *link)
636*4882a593Smuzhiyun {
637*4882a593Smuzhiyun 	struct drm_dp_aux *aux = link->aux;
638*4882a593Smuzhiyun 	u8 status[DP_LINK_STATUS_SIZE];
639*4882a593Smuzhiyun 	int err;
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun 	err = drm_dp_link_apply_training(link);
642*4882a593Smuzhiyun 	if (err < 0)
643*4882a593Smuzhiyun 		return err;
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 	drm_dp_link_train_wait(link);
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun 	err = drm_dp_dpcd_read_link_status(aux, status);
648*4882a593Smuzhiyun 	if (err < 0) {
649*4882a593Smuzhiyun 		DRM_ERROR("failed to read link status: %d\n", err);
650*4882a593Smuzhiyun 		return err;
651*4882a593Smuzhiyun 	}
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	if (!drm_dp_clock_recovery_ok(status, link->lanes)) {
654*4882a593Smuzhiyun 		DRM_ERROR("clock recovery lost while equalizing channel\n");
655*4882a593Smuzhiyun 		link->train.clock_recovered = false;
656*4882a593Smuzhiyun 		return 0;
657*4882a593Smuzhiyun 	}
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	if (!drm_dp_channel_eq_ok(status, link->lanes))
660*4882a593Smuzhiyun 		drm_dp_link_get_adjustments(link, status);
661*4882a593Smuzhiyun 	else
662*4882a593Smuzhiyun 		link->train.channel_equalized = true;
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	return 0;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun 
drm_dp_link_channel_equalization(struct drm_dp_link * link)667*4882a593Smuzhiyun static int drm_dp_link_channel_equalization(struct drm_dp_link *link)
668*4882a593Smuzhiyun {
669*4882a593Smuzhiyun 	unsigned int repeat;
670*4882a593Smuzhiyun 	int err;
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun 	/* start channel equalization using pattern 2 or 3 */
673*4882a593Smuzhiyun 	if (link->caps.tps3_supported)
674*4882a593Smuzhiyun 		link->train.pattern = DP_TRAINING_PATTERN_3;
675*4882a593Smuzhiyun 	else
676*4882a593Smuzhiyun 		link->train.pattern = DP_TRAINING_PATTERN_2;
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	for (repeat = 1; repeat < 5; repeat++) {
679*4882a593Smuzhiyun 		err = drm_dp_link_equalize_channel(link);
680*4882a593Smuzhiyun 		if (err < 0) {
681*4882a593Smuzhiyun 			DRM_ERROR("failed to equalize channel: %d\n", err);
682*4882a593Smuzhiyun 			return err;
683*4882a593Smuzhiyun 		}
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun 		if (link->train.channel_equalized)
686*4882a593Smuzhiyun 			break;
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 		drm_dp_link_train_adjust(&link->train);
689*4882a593Smuzhiyun 	}
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	return 0;
692*4882a593Smuzhiyun }
693*4882a593Smuzhiyun 
drm_dp_link_downgrade(struct drm_dp_link * link)694*4882a593Smuzhiyun static int drm_dp_link_downgrade(struct drm_dp_link *link)
695*4882a593Smuzhiyun {
696*4882a593Smuzhiyun 	switch (link->rate) {
697*4882a593Smuzhiyun 	case 162000:
698*4882a593Smuzhiyun 		return -EINVAL;
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	case 270000:
701*4882a593Smuzhiyun 		link->rate = 162000;
702*4882a593Smuzhiyun 		break;
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	case 540000:
705*4882a593Smuzhiyun 		link->rate = 270000;
706*4882a593Smuzhiyun 		return 0;
707*4882a593Smuzhiyun 	}
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun 	return 0;
710*4882a593Smuzhiyun }
711*4882a593Smuzhiyun 
drm_dp_link_train_disable(struct drm_dp_link * link)712*4882a593Smuzhiyun static void drm_dp_link_train_disable(struct drm_dp_link *link)
713*4882a593Smuzhiyun {
714*4882a593Smuzhiyun 	int err;
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	link->train.pattern = DP_TRAINING_PATTERN_DISABLE;
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun 	err = drm_dp_link_apply_training(link);
719*4882a593Smuzhiyun 	if (err < 0)
720*4882a593Smuzhiyun 		DRM_ERROR("failed to disable link training: %d\n", err);
721*4882a593Smuzhiyun }
722*4882a593Smuzhiyun 
drm_dp_link_train_full(struct drm_dp_link * link)723*4882a593Smuzhiyun static int drm_dp_link_train_full(struct drm_dp_link *link)
724*4882a593Smuzhiyun {
725*4882a593Smuzhiyun 	int err;
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun retry:
728*4882a593Smuzhiyun 	DRM_DEBUG_KMS("full-training link: %u lane%s at %u MHz\n",
729*4882a593Smuzhiyun 		      link->lanes, (link->lanes > 1) ? "s" : "",
730*4882a593Smuzhiyun 		      link->rate / 100);
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun 	err = drm_dp_link_configure(link->aux, link);
733*4882a593Smuzhiyun 	if (err < 0) {
734*4882a593Smuzhiyun 		DRM_ERROR("failed to configure DP link: %d\n", err);
735*4882a593Smuzhiyun 		return err;
736*4882a593Smuzhiyun 	}
737*4882a593Smuzhiyun 
738*4882a593Smuzhiyun 	err = drm_dp_link_clock_recovery(link);
739*4882a593Smuzhiyun 	if (err < 0) {
740*4882a593Smuzhiyun 		DRM_ERROR("clock recovery failed: %d\n", err);
741*4882a593Smuzhiyun 		goto out;
742*4882a593Smuzhiyun 	}
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	if (!link->train.clock_recovered) {
745*4882a593Smuzhiyun 		DRM_ERROR("clock recovery failed, downgrading link\n");
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun 		err = drm_dp_link_downgrade(link);
748*4882a593Smuzhiyun 		if (err < 0)
749*4882a593Smuzhiyun 			goto out;
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 		goto retry;
752*4882a593Smuzhiyun 	}
753*4882a593Smuzhiyun 
754*4882a593Smuzhiyun 	DRM_DEBUG_KMS("clock recovery succeeded\n");
755*4882a593Smuzhiyun 
756*4882a593Smuzhiyun 	err = drm_dp_link_channel_equalization(link);
757*4882a593Smuzhiyun 	if (err < 0) {
758*4882a593Smuzhiyun 		DRM_ERROR("channel equalization failed: %d\n", err);
759*4882a593Smuzhiyun 		goto out;
760*4882a593Smuzhiyun 	}
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 	if (!link->train.channel_equalized) {
763*4882a593Smuzhiyun 		DRM_ERROR("channel equalization failed, downgrading link\n");
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 		err = drm_dp_link_downgrade(link);
766*4882a593Smuzhiyun 		if (err < 0)
767*4882a593Smuzhiyun 			goto out;
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun 		goto retry;
770*4882a593Smuzhiyun 	}
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun 	DRM_DEBUG_KMS("channel equalization succeeded\n");
773*4882a593Smuzhiyun 
774*4882a593Smuzhiyun out:
775*4882a593Smuzhiyun 	drm_dp_link_train_disable(link);
776*4882a593Smuzhiyun 	return err;
777*4882a593Smuzhiyun }
778*4882a593Smuzhiyun 
drm_dp_link_train_fast(struct drm_dp_link * link)779*4882a593Smuzhiyun static int drm_dp_link_train_fast(struct drm_dp_link *link)
780*4882a593Smuzhiyun {
781*4882a593Smuzhiyun 	u8 status[DP_LINK_STATUS_SIZE];
782*4882a593Smuzhiyun 	int err;
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun 	DRM_DEBUG_KMS("fast-training link: %u lane%s at %u MHz\n",
785*4882a593Smuzhiyun 		      link->lanes, (link->lanes > 1) ? "s" : "",
786*4882a593Smuzhiyun 		      link->rate / 100);
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun 	err = drm_dp_link_configure(link->aux, link);
789*4882a593Smuzhiyun 	if (err < 0) {
790*4882a593Smuzhiyun 		DRM_ERROR("failed to configure DP link: %d\n", err);
791*4882a593Smuzhiyun 		return err;
792*4882a593Smuzhiyun 	}
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	/* transmit training pattern 1 for 500 microseconds */
795*4882a593Smuzhiyun 	link->train.pattern = DP_TRAINING_PATTERN_1;
796*4882a593Smuzhiyun 
797*4882a593Smuzhiyun 	err = drm_dp_link_apply_training(link);
798*4882a593Smuzhiyun 	if (err < 0)
799*4882a593Smuzhiyun 		goto out;
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 	usleep_range(500, 1000);
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 	/* transmit training pattern 2 or 3 for 500 microseconds */
804*4882a593Smuzhiyun 	if (link->caps.tps3_supported)
805*4882a593Smuzhiyun 		link->train.pattern = DP_TRAINING_PATTERN_3;
806*4882a593Smuzhiyun 	else
807*4882a593Smuzhiyun 		link->train.pattern = DP_TRAINING_PATTERN_2;
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun 	err = drm_dp_link_apply_training(link);
810*4882a593Smuzhiyun 	if (err < 0)
811*4882a593Smuzhiyun 		goto out;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	usleep_range(500, 1000);
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	err = drm_dp_dpcd_read_link_status(link->aux, status);
816*4882a593Smuzhiyun 	if (err < 0) {
817*4882a593Smuzhiyun 		DRM_ERROR("failed to read link status: %d\n", err);
818*4882a593Smuzhiyun 		goto out;
819*4882a593Smuzhiyun 	}
820*4882a593Smuzhiyun 
821*4882a593Smuzhiyun 	if (!drm_dp_clock_recovery_ok(status, link->lanes)) {
822*4882a593Smuzhiyun 		DRM_ERROR("clock recovery failed\n");
823*4882a593Smuzhiyun 		err = -EIO;
824*4882a593Smuzhiyun 	}
825*4882a593Smuzhiyun 
826*4882a593Smuzhiyun 	if (!drm_dp_channel_eq_ok(status, link->lanes)) {
827*4882a593Smuzhiyun 		DRM_ERROR("channel equalization failed\n");
828*4882a593Smuzhiyun 		err = -EIO;
829*4882a593Smuzhiyun 	}
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun out:
832*4882a593Smuzhiyun 	drm_dp_link_train_disable(link);
833*4882a593Smuzhiyun 	return err;
834*4882a593Smuzhiyun }
835*4882a593Smuzhiyun 
836*4882a593Smuzhiyun /**
837*4882a593Smuzhiyun  * drm_dp_link_train() - perform DisplayPort link training
838*4882a593Smuzhiyun  * @link: a DP link object
839*4882a593Smuzhiyun  *
840*4882a593Smuzhiyun  * Uses the context stored in the DP link object to perform link training. It
841*4882a593Smuzhiyun  * is expected that drivers will call drm_dp_link_probe() to obtain the link
842*4882a593Smuzhiyun  * capabilities before performing link training.
843*4882a593Smuzhiyun  *
844*4882a593Smuzhiyun  * If the sink supports fast link training (no AUX CH handshake) and valid
845*4882a593Smuzhiyun  * training settings are available, this function will try to perform fast
846*4882a593Smuzhiyun  * link training and fall back to full link training on failure.
847*4882a593Smuzhiyun  *
848*4882a593Smuzhiyun  * Returns: 0 on success or a negative error code on failure.
849*4882a593Smuzhiyun  */
drm_dp_link_train(struct drm_dp_link * link)850*4882a593Smuzhiyun int drm_dp_link_train(struct drm_dp_link *link)
851*4882a593Smuzhiyun {
852*4882a593Smuzhiyun 	int err;
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun 	drm_dp_link_train_init(&link->train);
855*4882a593Smuzhiyun 
856*4882a593Smuzhiyun 	if (link->caps.fast_training) {
857*4882a593Smuzhiyun 		if (drm_dp_link_train_valid(&link->train)) {
858*4882a593Smuzhiyun 			err = drm_dp_link_train_fast(link);
859*4882a593Smuzhiyun 			if (err < 0)
860*4882a593Smuzhiyun 				DRM_ERROR("fast link training failed: %d\n",
861*4882a593Smuzhiyun 					  err);
862*4882a593Smuzhiyun 			else
863*4882a593Smuzhiyun 				return 0;
864*4882a593Smuzhiyun 		} else {
865*4882a593Smuzhiyun 			DRM_DEBUG_KMS("training parameters not available\n");
866*4882a593Smuzhiyun 		}
867*4882a593Smuzhiyun 	} else {
868*4882a593Smuzhiyun 		DRM_DEBUG_KMS("fast link training not supported\n");
869*4882a593Smuzhiyun 	}
870*4882a593Smuzhiyun 
871*4882a593Smuzhiyun 	err = drm_dp_link_train_full(link);
872*4882a593Smuzhiyun 	if (err < 0)
873*4882a593Smuzhiyun 		DRM_ERROR("full link training failed: %d\n", err);
874*4882a593Smuzhiyun 
875*4882a593Smuzhiyun 	return err;
876*4882a593Smuzhiyun }
877