xref: /openbmc/linux/drivers/gpu/drm/msm/dp/dp_link.c (revision 554484e4)
1c943b494SChandan Uddaraju // SPDX-License-Identifier: GPL-2.0-only
2c943b494SChandan Uddaraju /*
3c943b494SChandan Uddaraju  * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
4c943b494SChandan Uddaraju  */
5c943b494SChandan Uddaraju 
6c943b494SChandan Uddaraju #define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
7c943b494SChandan Uddaraju 
8937f941cSStephen Boyd #include <drm/drm_print.h>
9937f941cSStephen Boyd 
101449c757SKuogee Hsieh #include "dp_reg.h"
11c943b494SChandan Uddaraju #include "dp_link.h"
12c943b494SChandan Uddaraju #include "dp_panel.h"
13c943b494SChandan Uddaraju 
14c943b494SChandan Uddaraju #define DP_TEST_REQUEST_MASK		0x7F
15c943b494SChandan Uddaraju 
16c943b494SChandan Uddaraju enum audio_sample_rate {
17c943b494SChandan Uddaraju 	AUDIO_SAMPLE_RATE_32_KHZ	= 0x00,
18c943b494SChandan Uddaraju 	AUDIO_SAMPLE_RATE_44_1_KHZ	= 0x01,
19c943b494SChandan Uddaraju 	AUDIO_SAMPLE_RATE_48_KHZ	= 0x02,
20c943b494SChandan Uddaraju 	AUDIO_SAMPLE_RATE_88_2_KHZ	= 0x03,
21c943b494SChandan Uddaraju 	AUDIO_SAMPLE_RATE_96_KHZ	= 0x04,
22c943b494SChandan Uddaraju 	AUDIO_SAMPLE_RATE_176_4_KHZ	= 0x05,
23c943b494SChandan Uddaraju 	AUDIO_SAMPLE_RATE_192_KHZ	= 0x06,
24c943b494SChandan Uddaraju };
25c943b494SChandan Uddaraju 
26c943b494SChandan Uddaraju enum audio_pattern_type {
27c943b494SChandan Uddaraju 	AUDIO_TEST_PATTERN_OPERATOR_DEFINED	= 0x00,
28c943b494SChandan Uddaraju 	AUDIO_TEST_PATTERN_SAWTOOTH		= 0x01,
29c943b494SChandan Uddaraju };
30c943b494SChandan Uddaraju 
31c943b494SChandan Uddaraju struct dp_link_request {
32c943b494SChandan Uddaraju 	u32 test_requested;
33c943b494SChandan Uddaraju 	u32 test_link_rate;
34c943b494SChandan Uddaraju 	u32 test_lane_count;
35c943b494SChandan Uddaraju };
36c943b494SChandan Uddaraju 
37c943b494SChandan Uddaraju struct dp_link_private {
38c943b494SChandan Uddaraju 	u32 prev_sink_count;
39c943b494SChandan Uddaraju 	struct device *dev;
40202aceacSKuogee Hsieh 	struct drm_device *drm_dev;
41c943b494SChandan Uddaraju 	struct drm_dp_aux *aux;
42c943b494SChandan Uddaraju 	struct dp_link dp_link;
43c943b494SChandan Uddaraju 
44c943b494SChandan Uddaraju 	struct dp_link_request request;
45c943b494SChandan Uddaraju 	struct mutex psm_mutex;
46c943b494SChandan Uddaraju 	u8 link_status[DP_LINK_STATUS_SIZE];
47c943b494SChandan Uddaraju };
48c943b494SChandan Uddaraju 
dp_aux_link_power_up(struct drm_dp_aux * aux,struct dp_link_info * link)49c943b494SChandan Uddaraju static int dp_aux_link_power_up(struct drm_dp_aux *aux,
50c943b494SChandan Uddaraju 					struct dp_link_info *link)
51c943b494SChandan Uddaraju {
52c943b494SChandan Uddaraju 	u8 value;
53d54c518aSKuogee Hsieh 	ssize_t len;
54af309c0cSKuogee Hsieh 	int i;
55c943b494SChandan Uddaraju 
56c943b494SChandan Uddaraju 	if (link->revision < 0x11)
57c943b494SChandan Uddaraju 		return 0;
58c943b494SChandan Uddaraju 
59d54c518aSKuogee Hsieh 	len = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
60d54c518aSKuogee Hsieh 	if (len < 0)
61d54c518aSKuogee Hsieh 		return len;
62c943b494SChandan Uddaraju 
63c943b494SChandan Uddaraju 	value &= ~DP_SET_POWER_MASK;
64c943b494SChandan Uddaraju 	value |= DP_SET_POWER_D0;
65c943b494SChandan Uddaraju 
66af309c0cSKuogee Hsieh 	/* retry for 1ms to give the sink time to wake up */
67af309c0cSKuogee Hsieh 	for (i = 0; i < 3; i++) {
68d54c518aSKuogee Hsieh 		len = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
69c943b494SChandan Uddaraju 		usleep_range(1000, 2000);
70af309c0cSKuogee Hsieh 		if (len == 1)
71af309c0cSKuogee Hsieh 			break;
72af309c0cSKuogee Hsieh 	}
73c943b494SChandan Uddaraju 
74c943b494SChandan Uddaraju 	return 0;
75c943b494SChandan Uddaraju }
76c943b494SChandan Uddaraju 
dp_aux_link_power_down(struct drm_dp_aux * aux,struct dp_link_info * link)77c943b494SChandan Uddaraju static int dp_aux_link_power_down(struct drm_dp_aux *aux,
78c943b494SChandan Uddaraju 					struct dp_link_info *link)
79c943b494SChandan Uddaraju {
80c943b494SChandan Uddaraju 	u8 value;
81c943b494SChandan Uddaraju 	int err;
82c943b494SChandan Uddaraju 
83c943b494SChandan Uddaraju 	if (link->revision < 0x11)
84c943b494SChandan Uddaraju 		return 0;
85c943b494SChandan Uddaraju 
86c943b494SChandan Uddaraju 	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
87c943b494SChandan Uddaraju 	if (err < 0)
88c943b494SChandan Uddaraju 		return err;
89c943b494SChandan Uddaraju 
90c943b494SChandan Uddaraju 	value &= ~DP_SET_POWER_MASK;
91c943b494SChandan Uddaraju 	value |= DP_SET_POWER_D3;
92c943b494SChandan Uddaraju 
93c943b494SChandan Uddaraju 	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
94c943b494SChandan Uddaraju 	if (err < 0)
95c943b494SChandan Uddaraju 		return err;
96c943b494SChandan Uddaraju 
97c943b494SChandan Uddaraju 	return 0;
98c943b494SChandan Uddaraju }
99c943b494SChandan Uddaraju 
dp_link_get_period(struct dp_link_private * link,int const addr)100c943b494SChandan Uddaraju static int dp_link_get_period(struct dp_link_private *link, int const addr)
101c943b494SChandan Uddaraju {
102c943b494SChandan Uddaraju 	int ret = 0;
103c943b494SChandan Uddaraju 	u8 data;
104c943b494SChandan Uddaraju 	u32 const max_audio_period = 0xA;
105c943b494SChandan Uddaraju 
106c943b494SChandan Uddaraju 	/* TEST_AUDIO_PERIOD_CH_XX */
107c943b494SChandan Uddaraju 	if (drm_dp_dpcd_readb(link->aux, addr, &data) < 0) {
108c943b494SChandan Uddaraju 		DRM_ERROR("failed to read test_audio_period (0x%x)\n", addr);
109c943b494SChandan Uddaraju 		ret = -EINVAL;
110c943b494SChandan Uddaraju 		goto exit;
111c943b494SChandan Uddaraju 	}
112c943b494SChandan Uddaraju 
113c943b494SChandan Uddaraju 	/* Period - Bits 3:0 */
114c943b494SChandan Uddaraju 	data = data & 0xF;
115c943b494SChandan Uddaraju 	if ((int)data > max_audio_period) {
116c943b494SChandan Uddaraju 		DRM_ERROR("invalid test_audio_period_ch_1 = 0x%x\n", data);
117c943b494SChandan Uddaraju 		ret = -EINVAL;
118c943b494SChandan Uddaraju 		goto exit;
119c943b494SChandan Uddaraju 	}
120c943b494SChandan Uddaraju 
121c943b494SChandan Uddaraju 	ret = data;
122c943b494SChandan Uddaraju exit:
123c943b494SChandan Uddaraju 	return ret;
124c943b494SChandan Uddaraju }
125c943b494SChandan Uddaraju 
dp_link_parse_audio_channel_period(struct dp_link_private * link)126c943b494SChandan Uddaraju static int dp_link_parse_audio_channel_period(struct dp_link_private *link)
127c943b494SChandan Uddaraju {
128c943b494SChandan Uddaraju 	int ret = 0;
129c943b494SChandan Uddaraju 	struct dp_link_test_audio *req = &link->dp_link.test_audio;
130c943b494SChandan Uddaraju 
131c943b494SChandan Uddaraju 	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH1);
132c943b494SChandan Uddaraju 	if (ret == -EINVAL)
133c943b494SChandan Uddaraju 		goto exit;
134c943b494SChandan Uddaraju 
135c943b494SChandan Uddaraju 	req->test_audio_period_ch_1 = ret;
136202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "test_audio_period_ch_1 = 0x%x\n", ret);
137c943b494SChandan Uddaraju 
138c943b494SChandan Uddaraju 	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH2);
139c943b494SChandan Uddaraju 	if (ret == -EINVAL)
140c943b494SChandan Uddaraju 		goto exit;
141c943b494SChandan Uddaraju 
142c943b494SChandan Uddaraju 	req->test_audio_period_ch_2 = ret;
143202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "test_audio_period_ch_2 = 0x%x\n", ret);
144c943b494SChandan Uddaraju 
145c943b494SChandan Uddaraju 	/* TEST_AUDIO_PERIOD_CH_3 (Byte 0x275) */
146c943b494SChandan Uddaraju 	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH3);
147c943b494SChandan Uddaraju 	if (ret == -EINVAL)
148c943b494SChandan Uddaraju 		goto exit;
149c943b494SChandan Uddaraju 
150c943b494SChandan Uddaraju 	req->test_audio_period_ch_3 = ret;
151202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "test_audio_period_ch_3 = 0x%x\n", ret);
152c943b494SChandan Uddaraju 
153c943b494SChandan Uddaraju 	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH4);
154c943b494SChandan Uddaraju 	if (ret == -EINVAL)
155c943b494SChandan Uddaraju 		goto exit;
156c943b494SChandan Uddaraju 
157c943b494SChandan Uddaraju 	req->test_audio_period_ch_4 = ret;
158202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "test_audio_period_ch_4 = 0x%x\n", ret);
159c943b494SChandan Uddaraju 
160c943b494SChandan Uddaraju 	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH5);
161c943b494SChandan Uddaraju 	if (ret == -EINVAL)
162c943b494SChandan Uddaraju 		goto exit;
163c943b494SChandan Uddaraju 
164c943b494SChandan Uddaraju 	req->test_audio_period_ch_5 = ret;
165202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "test_audio_period_ch_5 = 0x%x\n", ret);
166c943b494SChandan Uddaraju 
167c943b494SChandan Uddaraju 	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH6);
168c943b494SChandan Uddaraju 	if (ret == -EINVAL)
169c943b494SChandan Uddaraju 		goto exit;
170c943b494SChandan Uddaraju 
171c943b494SChandan Uddaraju 	req->test_audio_period_ch_6 = ret;
172202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "test_audio_period_ch_6 = 0x%x\n", ret);
173c943b494SChandan Uddaraju 
174c943b494SChandan Uddaraju 	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH7);
175c943b494SChandan Uddaraju 	if (ret == -EINVAL)
176c943b494SChandan Uddaraju 		goto exit;
177c943b494SChandan Uddaraju 
178c943b494SChandan Uddaraju 	req->test_audio_period_ch_7 = ret;
179202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "test_audio_period_ch_7 = 0x%x\n", ret);
180c943b494SChandan Uddaraju 
181c943b494SChandan Uddaraju 	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH8);
182c943b494SChandan Uddaraju 	if (ret == -EINVAL)
183c943b494SChandan Uddaraju 		goto exit;
184c943b494SChandan Uddaraju 
185c943b494SChandan Uddaraju 	req->test_audio_period_ch_8 = ret;
186202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "test_audio_period_ch_8 = 0x%x\n", ret);
187c943b494SChandan Uddaraju exit:
188c943b494SChandan Uddaraju 	return ret;
189c943b494SChandan Uddaraju }
190c943b494SChandan Uddaraju 
dp_link_parse_audio_pattern_type(struct dp_link_private * link)191c943b494SChandan Uddaraju static int dp_link_parse_audio_pattern_type(struct dp_link_private *link)
192c943b494SChandan Uddaraju {
193c943b494SChandan Uddaraju 	int ret = 0;
194c943b494SChandan Uddaraju 	u8 data;
195c943b494SChandan Uddaraju 	ssize_t rlen;
196c943b494SChandan Uddaraju 	int const max_audio_pattern_type = 0x1;
197c943b494SChandan Uddaraju 
198c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux,
199c943b494SChandan Uddaraju 				DP_TEST_AUDIO_PATTERN_TYPE, &data);
200c943b494SChandan Uddaraju 	if (rlen < 0) {
201c943b494SChandan Uddaraju 		DRM_ERROR("failed to read link audio mode. rlen=%zd\n", rlen);
202c943b494SChandan Uddaraju 		return rlen;
203c943b494SChandan Uddaraju 	}
204c943b494SChandan Uddaraju 
205c943b494SChandan Uddaraju 	/* Audio Pattern Type - Bits 7:0 */
206c943b494SChandan Uddaraju 	if ((int)data > max_audio_pattern_type) {
207c943b494SChandan Uddaraju 		DRM_ERROR("invalid audio pattern type = 0x%x\n", data);
208c943b494SChandan Uddaraju 		ret = -EINVAL;
209c943b494SChandan Uddaraju 		goto exit;
210c943b494SChandan Uddaraju 	}
211c943b494SChandan Uddaraju 
212c943b494SChandan Uddaraju 	link->dp_link.test_audio.test_audio_pattern_type = data;
213202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "audio pattern type = 0x%x\n", data);
214c943b494SChandan Uddaraju exit:
215c943b494SChandan Uddaraju 	return ret;
216c943b494SChandan Uddaraju }
217c943b494SChandan Uddaraju 
dp_link_parse_audio_mode(struct dp_link_private * link)218c943b494SChandan Uddaraju static int dp_link_parse_audio_mode(struct dp_link_private *link)
219c943b494SChandan Uddaraju {
220c943b494SChandan Uddaraju 	int ret = 0;
221c943b494SChandan Uddaraju 	u8 data;
222c943b494SChandan Uddaraju 	ssize_t rlen;
223c943b494SChandan Uddaraju 	int const max_audio_sampling_rate = 0x6;
224c943b494SChandan Uddaraju 	int const max_audio_channel_count = 0x8;
225c943b494SChandan Uddaraju 	int sampling_rate = 0x0;
226c943b494SChandan Uddaraju 	int channel_count = 0x0;
227c943b494SChandan Uddaraju 
228c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_AUDIO_MODE, &data);
229c943b494SChandan Uddaraju 	if (rlen < 0) {
230c943b494SChandan Uddaraju 		DRM_ERROR("failed to read link audio mode. rlen=%zd\n", rlen);
231c943b494SChandan Uddaraju 		return rlen;
232c943b494SChandan Uddaraju 	}
233c943b494SChandan Uddaraju 
234c943b494SChandan Uddaraju 	/* Sampling Rate - Bits 3:0 */
235c943b494SChandan Uddaraju 	sampling_rate = data & 0xF;
236c943b494SChandan Uddaraju 	if (sampling_rate > max_audio_sampling_rate) {
237c943b494SChandan Uddaraju 		DRM_ERROR("sampling rate (0x%x) greater than max (0x%x)\n",
238c943b494SChandan Uddaraju 				sampling_rate, max_audio_sampling_rate);
239c943b494SChandan Uddaraju 		ret = -EINVAL;
240c943b494SChandan Uddaraju 		goto exit;
241c943b494SChandan Uddaraju 	}
242c943b494SChandan Uddaraju 
243c943b494SChandan Uddaraju 	/* Channel Count - Bits 7:4 */
244c943b494SChandan Uddaraju 	channel_count = ((data & 0xF0) >> 4) + 1;
245c943b494SChandan Uddaraju 	if (channel_count > max_audio_channel_count) {
246c943b494SChandan Uddaraju 		DRM_ERROR("channel_count (0x%x) greater than max (0x%x)\n",
247c943b494SChandan Uddaraju 				channel_count, max_audio_channel_count);
248c943b494SChandan Uddaraju 		ret = -EINVAL;
249c943b494SChandan Uddaraju 		goto exit;
250c943b494SChandan Uddaraju 	}
251c943b494SChandan Uddaraju 
252c943b494SChandan Uddaraju 	link->dp_link.test_audio.test_audio_sampling_rate = sampling_rate;
253c943b494SChandan Uddaraju 	link->dp_link.test_audio.test_audio_channel_count = channel_count;
254202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev,
255202aceacSKuogee Hsieh 			"sampling_rate = 0x%x, channel_count = 0x%x\n",
256c943b494SChandan Uddaraju 			sampling_rate, channel_count);
257c943b494SChandan Uddaraju exit:
258c943b494SChandan Uddaraju 	return ret;
259c943b494SChandan Uddaraju }
260c943b494SChandan Uddaraju 
dp_link_parse_audio_pattern_params(struct dp_link_private * link)261c943b494SChandan Uddaraju static int dp_link_parse_audio_pattern_params(struct dp_link_private *link)
262c943b494SChandan Uddaraju {
263c943b494SChandan Uddaraju 	int ret = 0;
264c943b494SChandan Uddaraju 
265c943b494SChandan Uddaraju 	ret = dp_link_parse_audio_mode(link);
266c943b494SChandan Uddaraju 	if (ret)
267c943b494SChandan Uddaraju 		goto exit;
268c943b494SChandan Uddaraju 
269c943b494SChandan Uddaraju 	ret = dp_link_parse_audio_pattern_type(link);
270c943b494SChandan Uddaraju 	if (ret)
271c943b494SChandan Uddaraju 		goto exit;
272c943b494SChandan Uddaraju 
273c943b494SChandan Uddaraju 	ret = dp_link_parse_audio_channel_period(link);
274c943b494SChandan Uddaraju 
275c943b494SChandan Uddaraju exit:
276c943b494SChandan Uddaraju 	return ret;
277c943b494SChandan Uddaraju }
278c943b494SChandan Uddaraju 
dp_link_is_video_pattern_valid(u32 pattern)279c943b494SChandan Uddaraju static bool dp_link_is_video_pattern_valid(u32 pattern)
280c943b494SChandan Uddaraju {
281c943b494SChandan Uddaraju 	switch (pattern) {
282c943b494SChandan Uddaraju 	case DP_NO_TEST_PATTERN:
283c943b494SChandan Uddaraju 	case DP_COLOR_RAMP:
284c943b494SChandan Uddaraju 	case DP_BLACK_AND_WHITE_VERTICAL_LINES:
285c943b494SChandan Uddaraju 	case DP_COLOR_SQUARE:
286c943b494SChandan Uddaraju 		return true;
287c943b494SChandan Uddaraju 	default:
288c943b494SChandan Uddaraju 		return false;
289c943b494SChandan Uddaraju 	}
290c943b494SChandan Uddaraju }
291c943b494SChandan Uddaraju 
292c943b494SChandan Uddaraju /**
293c943b494SChandan Uddaraju  * dp_link_is_bit_depth_valid() - validates the bit depth requested
294c943b494SChandan Uddaraju  * @tbd: bit depth requested by the sink
295c943b494SChandan Uddaraju  *
296c943b494SChandan Uddaraju  * Returns true if the requested bit depth is supported.
297c943b494SChandan Uddaraju  */
dp_link_is_bit_depth_valid(u32 tbd)298c943b494SChandan Uddaraju static bool dp_link_is_bit_depth_valid(u32 tbd)
299c943b494SChandan Uddaraju {
300c943b494SChandan Uddaraju 	/* DP_TEST_VIDEO_PATTERN_NONE is treated as invalid */
301c943b494SChandan Uddaraju 	switch (tbd) {
302c943b494SChandan Uddaraju 	case DP_TEST_BIT_DEPTH_6:
303c943b494SChandan Uddaraju 	case DP_TEST_BIT_DEPTH_8:
304c943b494SChandan Uddaraju 	case DP_TEST_BIT_DEPTH_10:
305c943b494SChandan Uddaraju 		return true;
306c943b494SChandan Uddaraju 	default:
307c943b494SChandan Uddaraju 		return false;
308c943b494SChandan Uddaraju 	}
309c943b494SChandan Uddaraju }
310c943b494SChandan Uddaraju 
dp_link_parse_timing_params1(struct dp_link_private * link,int addr,int len,u32 * val)311c943b494SChandan Uddaraju static int dp_link_parse_timing_params1(struct dp_link_private *link,
312c943b494SChandan Uddaraju 					int addr, int len, u32 *val)
313c943b494SChandan Uddaraju {
314c943b494SChandan Uddaraju 	u8 bp[2];
315c943b494SChandan Uddaraju 	int rlen;
316c943b494SChandan Uddaraju 
317c943b494SChandan Uddaraju 	if (len != 2)
318c943b494SChandan Uddaraju 		return -EINVAL;
319c943b494SChandan Uddaraju 
320c943b494SChandan Uddaraju 	/* Read the requested video link pattern (Byte 0x221). */
321c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_read(link->aux, addr, bp, len);
322c943b494SChandan Uddaraju 	if (rlen < len) {
323c943b494SChandan Uddaraju 		DRM_ERROR("failed to read 0x%x\n", addr);
324c943b494SChandan Uddaraju 		return -EINVAL;
325c943b494SChandan Uddaraju 	}
326c943b494SChandan Uddaraju 
327c943b494SChandan Uddaraju 	*val = bp[1] | (bp[0] << 8);
328c943b494SChandan Uddaraju 
329c943b494SChandan Uddaraju 	return 0;
330c943b494SChandan Uddaraju }
331c943b494SChandan Uddaraju 
dp_link_parse_timing_params2(struct dp_link_private * link,int addr,int len,u32 * val1,u32 * val2)332c943b494SChandan Uddaraju static int dp_link_parse_timing_params2(struct dp_link_private *link,
333c943b494SChandan Uddaraju 					int addr, int len,
334c943b494SChandan Uddaraju 					u32 *val1, u32 *val2)
335c943b494SChandan Uddaraju {
336c943b494SChandan Uddaraju 	u8 bp[2];
337c943b494SChandan Uddaraju 	int rlen;
338c943b494SChandan Uddaraju 
339c943b494SChandan Uddaraju 	if (len != 2)
340c943b494SChandan Uddaraju 		return -EINVAL;
341c943b494SChandan Uddaraju 
342c943b494SChandan Uddaraju 	/* Read the requested video link pattern (Byte 0x221). */
343c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_read(link->aux, addr, bp, len);
344c943b494SChandan Uddaraju 	if (rlen < len) {
345c943b494SChandan Uddaraju 		DRM_ERROR("failed to read 0x%x\n", addr);
346c943b494SChandan Uddaraju 		return -EINVAL;
347c943b494SChandan Uddaraju 	}
348c943b494SChandan Uddaraju 
349c943b494SChandan Uddaraju 	*val1 = (bp[0] & BIT(7)) >> 7;
350c943b494SChandan Uddaraju 	*val2 = bp[1] | ((bp[0] & 0x7F) << 8);
351c943b494SChandan Uddaraju 
352c943b494SChandan Uddaraju 	return 0;
353c943b494SChandan Uddaraju }
354c943b494SChandan Uddaraju 
dp_link_parse_timing_params3(struct dp_link_private * link,int addr,u32 * val)355c943b494SChandan Uddaraju static int dp_link_parse_timing_params3(struct dp_link_private *link,
356c943b494SChandan Uddaraju 					int addr, u32 *val)
357c943b494SChandan Uddaraju {
358c943b494SChandan Uddaraju 	u8 bp;
359c943b494SChandan Uddaraju 	u32 len = 1;
360c943b494SChandan Uddaraju 	int rlen;
361c943b494SChandan Uddaraju 
362c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_read(link->aux, addr, &bp, len);
363c943b494SChandan Uddaraju 	if (rlen < 1) {
364c943b494SChandan Uddaraju 		DRM_ERROR("failed to read 0x%x\n", addr);
365c943b494SChandan Uddaraju 		return -EINVAL;
366c943b494SChandan Uddaraju 	}
367c943b494SChandan Uddaraju 	*val = bp;
368c943b494SChandan Uddaraju 
369c943b494SChandan Uddaraju 	return 0;
370c943b494SChandan Uddaraju }
371c943b494SChandan Uddaraju 
372c943b494SChandan Uddaraju /**
37344b4fcbcSLee Jones  * dp_link_parse_video_pattern_params() - parses video pattern parameters from DPCD
374c943b494SChandan Uddaraju  * @link: Display Port Driver data
375c943b494SChandan Uddaraju  *
376c943b494SChandan Uddaraju  * Returns 0 if it successfully parses the video link pattern and the link
377c943b494SChandan Uddaraju  * bit depth requested by the sink and, and if the values parsed are valid.
378c943b494SChandan Uddaraju  */
dp_link_parse_video_pattern_params(struct dp_link_private * link)379c943b494SChandan Uddaraju static int dp_link_parse_video_pattern_params(struct dp_link_private *link)
380c943b494SChandan Uddaraju {
381c943b494SChandan Uddaraju 	int ret = 0;
382c943b494SChandan Uddaraju 	ssize_t rlen;
383c943b494SChandan Uddaraju 	u8 bp;
384c943b494SChandan Uddaraju 
385c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_PATTERN, &bp);
386c943b494SChandan Uddaraju 	if (rlen < 0) {
387c943b494SChandan Uddaraju 		DRM_ERROR("failed to read link video pattern. rlen=%zd\n",
388c943b494SChandan Uddaraju 			rlen);
389c943b494SChandan Uddaraju 		return rlen;
390c943b494SChandan Uddaraju 	}
391c943b494SChandan Uddaraju 
392c943b494SChandan Uddaraju 	if (!dp_link_is_video_pattern_valid(bp)) {
393c943b494SChandan Uddaraju 		DRM_ERROR("invalid link video pattern = 0x%x\n", bp);
394c943b494SChandan Uddaraju 		ret = -EINVAL;
395c943b494SChandan Uddaraju 		return ret;
396c943b494SChandan Uddaraju 	}
397c943b494SChandan Uddaraju 
398c943b494SChandan Uddaraju 	link->dp_link.test_video.test_video_pattern = bp;
399c943b494SChandan Uddaraju 
400c943b494SChandan Uddaraju 	/* Read the requested color bit depth and dynamic range (Byte 0x232) */
401c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_MISC0, &bp);
402c943b494SChandan Uddaraju 	if (rlen < 0) {
403c943b494SChandan Uddaraju 		DRM_ERROR("failed to read link bit depth. rlen=%zd\n", rlen);
404c943b494SChandan Uddaraju 		return rlen;
405c943b494SChandan Uddaraju 	}
406c943b494SChandan Uddaraju 
407c943b494SChandan Uddaraju 	/* Dynamic Range */
408c943b494SChandan Uddaraju 	link->dp_link.test_video.test_dyn_range =
409c943b494SChandan Uddaraju 			(bp & DP_TEST_DYNAMIC_RANGE_CEA);
410c943b494SChandan Uddaraju 
411c943b494SChandan Uddaraju 	/* Color bit depth */
412c943b494SChandan Uddaraju 	bp &= DP_TEST_BIT_DEPTH_MASK;
413c943b494SChandan Uddaraju 	if (!dp_link_is_bit_depth_valid(bp)) {
414c943b494SChandan Uddaraju 		DRM_ERROR("invalid link bit depth = 0x%x\n", bp);
415c943b494SChandan Uddaraju 		ret = -EINVAL;
416c943b494SChandan Uddaraju 		return ret;
417c943b494SChandan Uddaraju 	}
418c943b494SChandan Uddaraju 
419c943b494SChandan Uddaraju 	link->dp_link.test_video.test_bit_depth = bp;
420c943b494SChandan Uddaraju 
421c943b494SChandan Uddaraju 	/* resolution timing params */
422c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params1(link, DP_TEST_H_TOTAL_HI, 2,
423c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_h_total);
424c943b494SChandan Uddaraju 	if (ret) {
425c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse test_htotal(DP_TEST_H_TOTAL_HI)\n");
426c943b494SChandan Uddaraju 		return ret;
427c943b494SChandan Uddaraju 	}
428c943b494SChandan Uddaraju 
429c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params1(link, DP_TEST_V_TOTAL_HI, 2,
430c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_v_total);
431c943b494SChandan Uddaraju 	if (ret) {
432c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse test_v_total(DP_TEST_V_TOTAL_HI)\n");
433c943b494SChandan Uddaraju 		return ret;
434c943b494SChandan Uddaraju 	}
435c943b494SChandan Uddaraju 
436c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params1(link, DP_TEST_H_START_HI, 2,
437c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_h_start);
438c943b494SChandan Uddaraju 	if (ret) {
439c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse test_h_start(DP_TEST_H_START_HI)\n");
440c943b494SChandan Uddaraju 		return ret;
441c943b494SChandan Uddaraju 	}
442c943b494SChandan Uddaraju 
443c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params1(link, DP_TEST_V_START_HI, 2,
444c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_v_start);
445c943b494SChandan Uddaraju 	if (ret) {
446c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse test_v_start(DP_TEST_V_START_HI)\n");
447c943b494SChandan Uddaraju 		return ret;
448c943b494SChandan Uddaraju 	}
449c943b494SChandan Uddaraju 
450c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params2(link, DP_TEST_HSYNC_HI, 2,
451c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_hsync_pol,
452c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_hsync_width);
453c943b494SChandan Uddaraju 	if (ret) {
454c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse (DP_TEST_HSYNC_HI)\n");
455c943b494SChandan Uddaraju 		return ret;
456c943b494SChandan Uddaraju 	}
457c943b494SChandan Uddaraju 
458c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params2(link, DP_TEST_VSYNC_HI, 2,
459c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_vsync_pol,
460c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_vsync_width);
461c943b494SChandan Uddaraju 	if (ret) {
462c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse (DP_TEST_VSYNC_HI)\n");
463c943b494SChandan Uddaraju 		return ret;
464c943b494SChandan Uddaraju 	}
465c943b494SChandan Uddaraju 
466c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params1(link, DP_TEST_H_WIDTH_HI, 2,
467c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_h_width);
468c943b494SChandan Uddaraju 	if (ret) {
469c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse test_h_width(DP_TEST_H_WIDTH_HI)\n");
470c943b494SChandan Uddaraju 		return ret;
471c943b494SChandan Uddaraju 	}
472c943b494SChandan Uddaraju 
473c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params1(link, DP_TEST_V_HEIGHT_HI, 2,
474c943b494SChandan Uddaraju 			&link->dp_link.test_video.test_v_height);
475c943b494SChandan Uddaraju 	if (ret) {
476c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse test_v_height\n");
477c943b494SChandan Uddaraju 		return ret;
478c943b494SChandan Uddaraju 	}
479c943b494SChandan Uddaraju 
480c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params3(link, DP_TEST_MISC1,
481c943b494SChandan Uddaraju 		&link->dp_link.test_video.test_rr_d);
482c943b494SChandan Uddaraju 	link->dp_link.test_video.test_rr_d &= DP_TEST_REFRESH_DENOMINATOR;
483c943b494SChandan Uddaraju 	if (ret) {
484c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse test_rr_d (DP_TEST_MISC1)\n");
485c943b494SChandan Uddaraju 		return ret;
486c943b494SChandan Uddaraju 	}
487c943b494SChandan Uddaraju 
488c943b494SChandan Uddaraju 	ret = dp_link_parse_timing_params3(link, DP_TEST_REFRESH_RATE_NUMERATOR,
489c943b494SChandan Uddaraju 		&link->dp_link.test_video.test_rr_n);
490c943b494SChandan Uddaraju 	if (ret) {
491c943b494SChandan Uddaraju 		DRM_ERROR("failed to parse test_rr_n\n");
492c943b494SChandan Uddaraju 		return ret;
493c943b494SChandan Uddaraju 	}
494c943b494SChandan Uddaraju 
495202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev,
496202aceacSKuogee Hsieh 		"link video pattern = 0x%x\n"
497c943b494SChandan Uddaraju 		"link dynamic range = 0x%x\n"
498c943b494SChandan Uddaraju 		"link bit depth = 0x%x\n"
499c943b494SChandan Uddaraju 		"TEST_H_TOTAL = %d, TEST_V_TOTAL = %d\n"
500c943b494SChandan Uddaraju 		"TEST_H_START = %d, TEST_V_START = %d\n"
501c943b494SChandan Uddaraju 		"TEST_HSYNC_POL = %d\n"
502c943b494SChandan Uddaraju 		"TEST_HSYNC_WIDTH = %d\n"
503c943b494SChandan Uddaraju 		"TEST_VSYNC_POL = %d\n"
504c943b494SChandan Uddaraju 		"TEST_VSYNC_WIDTH = %d\n"
505c943b494SChandan Uddaraju 		"TEST_H_WIDTH = %d\n"
506c943b494SChandan Uddaraju 		"TEST_V_HEIGHT = %d\n"
507c943b494SChandan Uddaraju 		"TEST_REFRESH_DENOMINATOR = %d\n"
508c943b494SChandan Uddaraju 		 "TEST_REFRESH_NUMERATOR = %d\n",
509c943b494SChandan Uddaraju 		link->dp_link.test_video.test_video_pattern,
510c943b494SChandan Uddaraju 		link->dp_link.test_video.test_dyn_range,
511c943b494SChandan Uddaraju 		link->dp_link.test_video.test_bit_depth,
512c943b494SChandan Uddaraju 		link->dp_link.test_video.test_h_total,
513c943b494SChandan Uddaraju 		link->dp_link.test_video.test_v_total,
514c943b494SChandan Uddaraju 		link->dp_link.test_video.test_h_start,
515c943b494SChandan Uddaraju 		link->dp_link.test_video.test_v_start,
516c943b494SChandan Uddaraju 		link->dp_link.test_video.test_hsync_pol,
517c943b494SChandan Uddaraju 		link->dp_link.test_video.test_hsync_width,
518c943b494SChandan Uddaraju 		link->dp_link.test_video.test_vsync_pol,
519c943b494SChandan Uddaraju 		link->dp_link.test_video.test_vsync_width,
520c943b494SChandan Uddaraju 		link->dp_link.test_video.test_h_width,
521c943b494SChandan Uddaraju 		link->dp_link.test_video.test_v_height,
522c943b494SChandan Uddaraju 		link->dp_link.test_video.test_rr_d,
523c943b494SChandan Uddaraju 		link->dp_link.test_video.test_rr_n);
524c943b494SChandan Uddaraju 
525c943b494SChandan Uddaraju 	return ret;
526c943b494SChandan Uddaraju }
527c943b494SChandan Uddaraju 
528c943b494SChandan Uddaraju /**
529c943b494SChandan Uddaraju  * dp_link_parse_link_training_params() - parses link training parameters from
530c943b494SChandan Uddaraju  * DPCD
531c943b494SChandan Uddaraju  * @link: Display Port Driver data
532c943b494SChandan Uddaraju  *
533c943b494SChandan Uddaraju  * Returns 0 if it successfully parses the link rate (Byte 0x219) and lane
534c943b494SChandan Uddaraju  * count (Byte 0x220), and if these values parse are valid.
535c943b494SChandan Uddaraju  */
dp_link_parse_link_training_params(struct dp_link_private * link)536c943b494SChandan Uddaraju static int dp_link_parse_link_training_params(struct dp_link_private *link)
537c943b494SChandan Uddaraju {
538c943b494SChandan Uddaraju 	u8 bp;
539c943b494SChandan Uddaraju 	ssize_t rlen;
540c943b494SChandan Uddaraju 
541c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_LINK_RATE,	&bp);
542c943b494SChandan Uddaraju 	if (rlen < 0) {
543c943b494SChandan Uddaraju 		DRM_ERROR("failed to read link rate. rlen=%zd\n", rlen);
544c943b494SChandan Uddaraju 		return rlen;
545c943b494SChandan Uddaraju 	}
546c943b494SChandan Uddaraju 
547c943b494SChandan Uddaraju 	if (!is_link_rate_valid(bp)) {
548c943b494SChandan Uddaraju 		DRM_ERROR("invalid link rate = 0x%x\n", bp);
549c943b494SChandan Uddaraju 		return -EINVAL;
550c943b494SChandan Uddaraju 	}
551c943b494SChandan Uddaraju 
552c943b494SChandan Uddaraju 	link->request.test_link_rate = bp;
553202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "link rate = 0x%x\n",
554202aceacSKuogee Hsieh 				link->request.test_link_rate);
555c943b494SChandan Uddaraju 
556c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_LANE_COUNT, &bp);
557c943b494SChandan Uddaraju 	if (rlen < 0) {
558c943b494SChandan Uddaraju 		DRM_ERROR("failed to read lane count. rlen=%zd\n", rlen);
559c943b494SChandan Uddaraju 		return rlen;
560c943b494SChandan Uddaraju 	}
561c943b494SChandan Uddaraju 	bp &= DP_MAX_LANE_COUNT_MASK;
562c943b494SChandan Uddaraju 
563c943b494SChandan Uddaraju 	if (!is_lane_count_valid(bp)) {
564c943b494SChandan Uddaraju 		DRM_ERROR("invalid lane count = 0x%x\n", bp);
565c943b494SChandan Uddaraju 		return -EINVAL;
566c943b494SChandan Uddaraju 	}
567c943b494SChandan Uddaraju 
568c943b494SChandan Uddaraju 	link->request.test_lane_count = bp;
569202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "lane count = 0x%x\n",
570202aceacSKuogee Hsieh 				link->request.test_lane_count);
571c943b494SChandan Uddaraju 	return 0;
572c943b494SChandan Uddaraju }
573c943b494SChandan Uddaraju 
574c943b494SChandan Uddaraju /**
57544b4fcbcSLee Jones  * dp_link_parse_phy_test_params() - parses the phy link parameters
576c943b494SChandan Uddaraju  * @link: Display Port Driver data
577c943b494SChandan Uddaraju  *
578c943b494SChandan Uddaraju  * Parses the DPCD (Byte 0x248) for the DP PHY link pattern that is being
579c943b494SChandan Uddaraju  * requested.
580c943b494SChandan Uddaraju  */
dp_link_parse_phy_test_params(struct dp_link_private * link)581c943b494SChandan Uddaraju static int dp_link_parse_phy_test_params(struct dp_link_private *link)
582c943b494SChandan Uddaraju {
583c943b494SChandan Uddaraju 	u8 data;
584c943b494SChandan Uddaraju 	ssize_t rlen;
585c943b494SChandan Uddaraju 
586c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux, DP_PHY_TEST_PATTERN,
587c943b494SChandan Uddaraju 					&data);
588c943b494SChandan Uddaraju 	if (rlen < 0) {
589c943b494SChandan Uddaraju 		DRM_ERROR("failed to read phy link pattern. rlen=%zd\n", rlen);
590c943b494SChandan Uddaraju 		return rlen;
591c943b494SChandan Uddaraju 	}
592c943b494SChandan Uddaraju 
5938ede2eccSKuogee Hsieh 	link->dp_link.phy_params.phy_test_pattern_sel = data & 0x07;
594c943b494SChandan Uddaraju 
595202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "phy_test_pattern_sel = 0x%x\n", data);
596c943b494SChandan Uddaraju 
597c943b494SChandan Uddaraju 	switch (data) {
5988ede2eccSKuogee Hsieh 	case DP_PHY_TEST_PATTERN_SEL_MASK:
5998ede2eccSKuogee Hsieh 	case DP_PHY_TEST_PATTERN_NONE:
6008ede2eccSKuogee Hsieh 	case DP_PHY_TEST_PATTERN_D10_2:
6018ede2eccSKuogee Hsieh 	case DP_PHY_TEST_PATTERN_ERROR_COUNT:
6028ede2eccSKuogee Hsieh 	case DP_PHY_TEST_PATTERN_PRBS7:
6038ede2eccSKuogee Hsieh 	case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
6048ede2eccSKuogee Hsieh 	case DP_PHY_TEST_PATTERN_CP2520:
605c943b494SChandan Uddaraju 		return 0;
606c943b494SChandan Uddaraju 	default:
607c943b494SChandan Uddaraju 		return -EINVAL;
608c943b494SChandan Uddaraju 	}
609c943b494SChandan Uddaraju }
610c943b494SChandan Uddaraju 
611c943b494SChandan Uddaraju /**
612c943b494SChandan Uddaraju  * dp_link_is_video_audio_test_requested() - checks for audio/video link request
613c943b494SChandan Uddaraju  * @link: link requested by the sink
614c943b494SChandan Uddaraju  *
615c943b494SChandan Uddaraju  * Returns true if the requested link is a permitted audio/video link.
616c943b494SChandan Uddaraju  */
dp_link_is_video_audio_test_requested(u32 link)617c943b494SChandan Uddaraju static bool dp_link_is_video_audio_test_requested(u32 link)
618c943b494SChandan Uddaraju {
619c943b494SChandan Uddaraju 	u8 video_audio_test = (DP_TEST_LINK_VIDEO_PATTERN |
620c943b494SChandan Uddaraju 				DP_TEST_LINK_AUDIO_PATTERN |
621c943b494SChandan Uddaraju 				DP_TEST_LINK_AUDIO_DISABLED_VIDEO);
622c943b494SChandan Uddaraju 
623c943b494SChandan Uddaraju 	return ((link & video_audio_test) &&
624c943b494SChandan Uddaraju 		!(link & ~video_audio_test));
625c943b494SChandan Uddaraju }
626c943b494SChandan Uddaraju 
627c943b494SChandan Uddaraju /**
628c943b494SChandan Uddaraju  * dp_link_parse_request() - parses link request parameters from sink
629c943b494SChandan Uddaraju  * @link: Display Port Driver data
630c943b494SChandan Uddaraju  *
631c943b494SChandan Uddaraju  * Parses the DPCD to check if an automated link is requested (Byte 0x201),
632c943b494SChandan Uddaraju  * and what type of link automation is being requested (Byte 0x218).
633c943b494SChandan Uddaraju  */
dp_link_parse_request(struct dp_link_private * link)634c943b494SChandan Uddaraju static int dp_link_parse_request(struct dp_link_private *link)
635c943b494SChandan Uddaraju {
636c943b494SChandan Uddaraju 	int ret = 0;
637c943b494SChandan Uddaraju 	u8 data;
638c943b494SChandan Uddaraju 	ssize_t rlen;
639c943b494SChandan Uddaraju 
640c943b494SChandan Uddaraju 	/**
641c943b494SChandan Uddaraju 	 * Read the device service IRQ vector (Byte 0x201) to determine
642c943b494SChandan Uddaraju 	 * whether an automated link has been requested by the sink.
643c943b494SChandan Uddaraju 	 */
644c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux,
645c943b494SChandan Uddaraju 				DP_DEVICE_SERVICE_IRQ_VECTOR, &data);
646c943b494SChandan Uddaraju 	if (rlen < 0) {
647c943b494SChandan Uddaraju 		DRM_ERROR("aux read failed. rlen=%zd\n", rlen);
648c943b494SChandan Uddaraju 		return rlen;
649c943b494SChandan Uddaraju 	}
650c943b494SChandan Uddaraju 
651202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "device service irq vector = 0x%x\n", data);
652c943b494SChandan Uddaraju 
653c943b494SChandan Uddaraju 	if (!(data & DP_AUTOMATED_TEST_REQUEST)) {
654202aceacSKuogee Hsieh 		drm_dbg_dp(link->drm_dev, "no test requested\n");
655c943b494SChandan Uddaraju 		return 0;
656c943b494SChandan Uddaraju 	}
657c943b494SChandan Uddaraju 
658c943b494SChandan Uddaraju 	/**
659c943b494SChandan Uddaraju 	 * Read the link request byte (Byte 0x218) to determine what type
660c943b494SChandan Uddaraju 	 * of automated link has been requested by the sink.
661c943b494SChandan Uddaraju 	 */
662c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_REQUEST, &data);
663c943b494SChandan Uddaraju 	if (rlen < 0) {
664c943b494SChandan Uddaraju 		DRM_ERROR("aux read failed. rlen=%zd\n", rlen);
665c943b494SChandan Uddaraju 		return rlen;
666c943b494SChandan Uddaraju 	}
667c943b494SChandan Uddaraju 
668c943b494SChandan Uddaraju 	if (!data || (data == DP_TEST_LINK_FAUX_PATTERN)) {
669202aceacSKuogee Hsieh 		drm_dbg_dp(link->drm_dev, "link 0x%x not supported\n", data);
670c943b494SChandan Uddaraju 		goto end;
671c943b494SChandan Uddaraju 	}
672c943b494SChandan Uddaraju 
673202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "Test:(0x%x) requested\n", data);
674c943b494SChandan Uddaraju 	link->request.test_requested = data;
675c943b494SChandan Uddaraju 	if (link->request.test_requested == DP_TEST_LINK_PHY_TEST_PATTERN) {
676c943b494SChandan Uddaraju 		ret = dp_link_parse_phy_test_params(link);
677c943b494SChandan Uddaraju 		if (ret)
678c943b494SChandan Uddaraju 			goto end;
679c943b494SChandan Uddaraju 		ret = dp_link_parse_link_training_params(link);
680c943b494SChandan Uddaraju 		if (ret)
681c943b494SChandan Uddaraju 			goto end;
682c943b494SChandan Uddaraju 	}
683c943b494SChandan Uddaraju 
684c943b494SChandan Uddaraju 	if (link->request.test_requested == DP_TEST_LINK_TRAINING) {
685c943b494SChandan Uddaraju 		ret = dp_link_parse_link_training_params(link);
686c943b494SChandan Uddaraju 		if (ret)
687c943b494SChandan Uddaraju 			goto end;
688c943b494SChandan Uddaraju 	}
689c943b494SChandan Uddaraju 
690c943b494SChandan Uddaraju 	if (dp_link_is_video_audio_test_requested(
691c943b494SChandan Uddaraju 			link->request.test_requested)) {
692c943b494SChandan Uddaraju 		ret = dp_link_parse_video_pattern_params(link);
693c943b494SChandan Uddaraju 		if (ret)
694c943b494SChandan Uddaraju 			goto end;
695c943b494SChandan Uddaraju 
696c943b494SChandan Uddaraju 		ret = dp_link_parse_audio_pattern_params(link);
697c943b494SChandan Uddaraju 	}
698c943b494SChandan Uddaraju end:
699c943b494SChandan Uddaraju 	/*
700c943b494SChandan Uddaraju 	 * Send a DP_TEST_ACK if all link parameters are valid, otherwise send
701c943b494SChandan Uddaraju 	 * a DP_TEST_NAK.
702c943b494SChandan Uddaraju 	 */
703c943b494SChandan Uddaraju 	if (ret) {
704c943b494SChandan Uddaraju 		link->dp_link.test_response = DP_TEST_NAK;
705c943b494SChandan Uddaraju 	} else {
706c943b494SChandan Uddaraju 		if (link->request.test_requested != DP_TEST_LINK_EDID_READ)
707c943b494SChandan Uddaraju 			link->dp_link.test_response = DP_TEST_ACK;
708c943b494SChandan Uddaraju 		else
709c943b494SChandan Uddaraju 			link->dp_link.test_response =
710c943b494SChandan Uddaraju 				DP_TEST_EDID_CHECKSUM_WRITE;
711c943b494SChandan Uddaraju 	}
712c943b494SChandan Uddaraju 
713c943b494SChandan Uddaraju 	return ret;
714c943b494SChandan Uddaraju }
715c943b494SChandan Uddaraju 
716c943b494SChandan Uddaraju /**
717c943b494SChandan Uddaraju  * dp_link_parse_sink_count() - parses the sink count
718c943b494SChandan Uddaraju  * @dp_link: pointer to link module data
719c943b494SChandan Uddaraju  *
720c943b494SChandan Uddaraju  * Parses the DPCD to check if there is an update to the sink count
721c943b494SChandan Uddaraju  * (Byte 0x200), and whether all the sink devices connected have Content
722c943b494SChandan Uddaraju  * Protection enabled.
723c943b494SChandan Uddaraju  */
dp_link_parse_sink_count(struct dp_link * dp_link)724c943b494SChandan Uddaraju static int dp_link_parse_sink_count(struct dp_link *dp_link)
725c943b494SChandan Uddaraju {
726c943b494SChandan Uddaraju 	ssize_t rlen;
727c943b494SChandan Uddaraju 	bool cp_ready;
728c943b494SChandan Uddaraju 
729c943b494SChandan Uddaraju 	struct dp_link_private *link = container_of(dp_link,
730c943b494SChandan Uddaraju 			struct dp_link_private, dp_link);
731c943b494SChandan Uddaraju 
732c943b494SChandan Uddaraju 	rlen = drm_dp_dpcd_readb(link->aux, DP_SINK_COUNT,
733c943b494SChandan Uddaraju 				 &link->dp_link.sink_count);
734c943b494SChandan Uddaraju 	if (rlen < 0) {
735c943b494SChandan Uddaraju 		DRM_ERROR("sink count read failed. rlen=%zd\n", rlen);
736c943b494SChandan Uddaraju 		return rlen;
737c943b494SChandan Uddaraju 	}
738c943b494SChandan Uddaraju 
739c943b494SChandan Uddaraju 	cp_ready = link->dp_link.sink_count & DP_SINK_CP_READY;
740c943b494SChandan Uddaraju 
741c943b494SChandan Uddaraju 	link->dp_link.sink_count =
742c943b494SChandan Uddaraju 		DP_GET_SINK_COUNT(link->dp_link.sink_count);
743c943b494SChandan Uddaraju 
744202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "sink_count = 0x%x, cp_ready = 0x%x\n",
745c943b494SChandan Uddaraju 				link->dp_link.sink_count, cp_ready);
746c943b494SChandan Uddaraju 	return 0;
747c943b494SChandan Uddaraju }
748c943b494SChandan Uddaraju 
dp_link_parse_sink_status_field(struct dp_link_private * link)749f61550b3SKuogee Hsieh static int dp_link_parse_sink_status_field(struct dp_link_private *link)
750c943b494SChandan Uddaraju {
751c943b494SChandan Uddaraju 	int len = 0;
752c943b494SChandan Uddaraju 
753c943b494SChandan Uddaraju 	link->prev_sink_count = link->dp_link.sink_count;
754f61550b3SKuogee Hsieh 	len = dp_link_parse_sink_count(&link->dp_link);
755f61550b3SKuogee Hsieh 	if (len < 0) {
756f61550b3SKuogee Hsieh 		DRM_ERROR("DP parse sink count failed\n");
757f61550b3SKuogee Hsieh 		return len;
758f61550b3SKuogee Hsieh 	}
759c943b494SChandan Uddaraju 
760c943b494SChandan Uddaraju 	len = drm_dp_dpcd_read_link_status(link->aux,
761c943b494SChandan Uddaraju 		link->link_status);
762f61550b3SKuogee Hsieh 	if (len < DP_LINK_STATUS_SIZE) {
763c943b494SChandan Uddaraju 		DRM_ERROR("DP link status read failed\n");
764f61550b3SKuogee Hsieh 		return len;
765f61550b3SKuogee Hsieh 	}
766f61550b3SKuogee Hsieh 
767f61550b3SKuogee Hsieh 	return dp_link_parse_request(link);
768c943b494SChandan Uddaraju }
769c943b494SChandan Uddaraju 
770c943b494SChandan Uddaraju /**
771c943b494SChandan Uddaraju  * dp_link_process_link_training_request() - processes new training requests
772c943b494SChandan Uddaraju  * @link: Display Port link data
773c943b494SChandan Uddaraju  *
774c943b494SChandan Uddaraju  * This function will handle new link training requests that are initiated by
775c943b494SChandan Uddaraju  * the sink. In particular, it will update the requested lane count and link
776c943b494SChandan Uddaraju  * rate, and then trigger the link retraining procedure.
777c943b494SChandan Uddaraju  *
778c943b494SChandan Uddaraju  * The function will return 0 if a link training request has been processed,
779c943b494SChandan Uddaraju  * otherwise it will return -EINVAL.
780c943b494SChandan Uddaraju  */
dp_link_process_link_training_request(struct dp_link_private * link)781c943b494SChandan Uddaraju static int dp_link_process_link_training_request(struct dp_link_private *link)
782c943b494SChandan Uddaraju {
783c943b494SChandan Uddaraju 	if (link->request.test_requested != DP_TEST_LINK_TRAINING)
784c943b494SChandan Uddaraju 		return -EINVAL;
785c943b494SChandan Uddaraju 
786202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev,
787202aceacSKuogee Hsieh 			"Test:0x%x link rate = 0x%x, lane count = 0x%x\n",
788c943b494SChandan Uddaraju 			DP_TEST_LINK_TRAINING,
789c943b494SChandan Uddaraju 			link->request.test_link_rate,
790c943b494SChandan Uddaraju 			link->request.test_lane_count);
791c943b494SChandan Uddaraju 
792c943b494SChandan Uddaraju 	link->dp_link.link_params.num_lanes = link->request.test_lane_count;
793ea530388SKuogee Hsieh 	link->dp_link.link_params.rate =
794ea530388SKuogee Hsieh 		drm_dp_bw_code_to_link_rate(link->request.test_link_rate);
795c943b494SChandan Uddaraju 
796c943b494SChandan Uddaraju 	return 0;
797c943b494SChandan Uddaraju }
798c943b494SChandan Uddaraju 
dp_link_send_test_response(struct dp_link * dp_link)799c943b494SChandan Uddaraju bool dp_link_send_test_response(struct dp_link *dp_link)
800c943b494SChandan Uddaraju {
801c943b494SChandan Uddaraju 	struct dp_link_private *link = NULL;
802c943b494SChandan Uddaraju 	int ret = 0;
803c943b494SChandan Uddaraju 
804c943b494SChandan Uddaraju 	if (!dp_link) {
805c943b494SChandan Uddaraju 		DRM_ERROR("invalid input\n");
806c943b494SChandan Uddaraju 		return false;
807c943b494SChandan Uddaraju 	}
808c943b494SChandan Uddaraju 
809c943b494SChandan Uddaraju 	link = container_of(dp_link, struct dp_link_private, dp_link);
810c943b494SChandan Uddaraju 
811c943b494SChandan Uddaraju 	ret = drm_dp_dpcd_writeb(link->aux, DP_TEST_RESPONSE,
812c943b494SChandan Uddaraju 			dp_link->test_response);
813c943b494SChandan Uddaraju 
814c943b494SChandan Uddaraju 	return ret == 1;
815c943b494SChandan Uddaraju }
816c943b494SChandan Uddaraju 
dp_link_psm_config(struct dp_link * dp_link,struct dp_link_info * link_info,bool enable)817c943b494SChandan Uddaraju int dp_link_psm_config(struct dp_link *dp_link,
818c943b494SChandan Uddaraju 			      struct dp_link_info *link_info, bool enable)
819c943b494SChandan Uddaraju {
820c943b494SChandan Uddaraju 	struct dp_link_private *link = NULL;
821c943b494SChandan Uddaraju 	int ret = 0;
822c943b494SChandan Uddaraju 
823c943b494SChandan Uddaraju 	if (!dp_link) {
824c943b494SChandan Uddaraju 		DRM_ERROR("invalid params\n");
825c943b494SChandan Uddaraju 		return -EINVAL;
826c943b494SChandan Uddaraju 	}
827c943b494SChandan Uddaraju 
828c943b494SChandan Uddaraju 	link = container_of(dp_link, struct dp_link_private, dp_link);
829c943b494SChandan Uddaraju 
830c943b494SChandan Uddaraju 	mutex_lock(&link->psm_mutex);
831c943b494SChandan Uddaraju 	if (enable)
832c943b494SChandan Uddaraju 		ret = dp_aux_link_power_down(link->aux, link_info);
833c943b494SChandan Uddaraju 	else
834c943b494SChandan Uddaraju 		ret = dp_aux_link_power_up(link->aux, link_info);
835c943b494SChandan Uddaraju 
836c943b494SChandan Uddaraju 	if (ret)
837c943b494SChandan Uddaraju 		DRM_ERROR("Failed to %s low power mode\n", enable ?
838c943b494SChandan Uddaraju 							"enter" : "exit");
839c943b494SChandan Uddaraju 	else
840c943b494SChandan Uddaraju 		dp_link->psm_enabled = enable;
841c943b494SChandan Uddaraju 
842c943b494SChandan Uddaraju 	mutex_unlock(&link->psm_mutex);
843c943b494SChandan Uddaraju 	return ret;
844c943b494SChandan Uddaraju }
845c943b494SChandan Uddaraju 
dp_link_send_edid_checksum(struct dp_link * dp_link,u8 checksum)846c943b494SChandan Uddaraju bool dp_link_send_edid_checksum(struct dp_link *dp_link, u8 checksum)
847c943b494SChandan Uddaraju {
848c943b494SChandan Uddaraju 	struct dp_link_private *link = NULL;
849c943b494SChandan Uddaraju 	int ret = 0;
850c943b494SChandan Uddaraju 
851c943b494SChandan Uddaraju 	if (!dp_link) {
852c943b494SChandan Uddaraju 		DRM_ERROR("invalid input\n");
853c943b494SChandan Uddaraju 		return false;
854c943b494SChandan Uddaraju 	}
855c943b494SChandan Uddaraju 
856c943b494SChandan Uddaraju 	link = container_of(dp_link, struct dp_link_private, dp_link);
857c943b494SChandan Uddaraju 
858c943b494SChandan Uddaraju 	ret = drm_dp_dpcd_writeb(link->aux, DP_TEST_EDID_CHECKSUM,
859c943b494SChandan Uddaraju 						checksum);
860c943b494SChandan Uddaraju 	return ret == 1;
861c943b494SChandan Uddaraju }
862c943b494SChandan Uddaraju 
dp_link_parse_vx_px(struct dp_link_private * link)8637d21fb8aSBernard Zhao static void dp_link_parse_vx_px(struct dp_link_private *link)
864c943b494SChandan Uddaraju {
865202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "vx: 0=%d, 1=%d, 2=%d, 3=%d\n",
866c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_voltage(link->link_status, 0),
867c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_voltage(link->link_status, 1),
868c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_voltage(link->link_status, 2),
869c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_voltage(link->link_status, 3));
870c943b494SChandan Uddaraju 
871202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "px: 0=%d, 1=%d, 2=%d, 3=%d\n",
872c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_pre_emphasis(link->link_status, 0),
873c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_pre_emphasis(link->link_status, 1),
874c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_pre_emphasis(link->link_status, 2),
875c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_pre_emphasis(link->link_status, 3));
876c943b494SChandan Uddaraju 
877c943b494SChandan Uddaraju 	/**
878c943b494SChandan Uddaraju 	 * Update the voltage and pre-emphasis levels as per DPCD request
879c943b494SChandan Uddaraju 	 * vector.
880c943b494SChandan Uddaraju 	 */
881202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev,
882202aceacSKuogee Hsieh 			 "Current: v_level = 0x%x, p_level = 0x%x\n",
883c943b494SChandan Uddaraju 			link->dp_link.phy_params.v_level,
884c943b494SChandan Uddaraju 			link->dp_link.phy_params.p_level);
885c943b494SChandan Uddaraju 	link->dp_link.phy_params.v_level =
886c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_voltage(link->link_status, 0);
887c943b494SChandan Uddaraju 	link->dp_link.phy_params.p_level =
888c943b494SChandan Uddaraju 		drm_dp_get_adjust_request_pre_emphasis(link->link_status, 0);
8896625e263STanmay Shah 
8906625e263STanmay Shah 	link->dp_link.phy_params.p_level >>= DP_TRAIN_PRE_EMPHASIS_SHIFT;
8916625e263STanmay Shah 
892202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev,
893202aceacSKuogee Hsieh 			"Requested: v_level = 0x%x, p_level = 0x%x\n",
894c943b494SChandan Uddaraju 			link->dp_link.phy_params.v_level,
895c943b494SChandan Uddaraju 			link->dp_link.phy_params.p_level);
896c943b494SChandan Uddaraju }
897c943b494SChandan Uddaraju 
898c943b494SChandan Uddaraju /**
899c943b494SChandan Uddaraju  * dp_link_process_phy_test_pattern_request() - process new phy link requests
900c943b494SChandan Uddaraju  * @link: Display Port Driver data
901c943b494SChandan Uddaraju  *
902c943b494SChandan Uddaraju  * This function will handle new phy link pattern requests that are initiated
903c943b494SChandan Uddaraju  * by the sink. The function will return 0 if a phy link pattern has been
904c943b494SChandan Uddaraju  * processed, otherwise it will return -EINVAL.
905c943b494SChandan Uddaraju  */
dp_link_process_phy_test_pattern_request(struct dp_link_private * link)906c943b494SChandan Uddaraju static int dp_link_process_phy_test_pattern_request(
907c943b494SChandan Uddaraju 		struct dp_link_private *link)
908c943b494SChandan Uddaraju {
909c943b494SChandan Uddaraju 	if (!(link->request.test_requested & DP_TEST_LINK_PHY_TEST_PATTERN)) {
910202aceacSKuogee Hsieh 		drm_dbg_dp(link->drm_dev, "no phy test\n");
911c943b494SChandan Uddaraju 		return -EINVAL;
912c943b494SChandan Uddaraju 	}
913c943b494SChandan Uddaraju 
914c943b494SChandan Uddaraju 	if (!is_link_rate_valid(link->request.test_link_rate) ||
915c943b494SChandan Uddaraju 		!is_lane_count_valid(link->request.test_lane_count)) {
916c943b494SChandan Uddaraju 		DRM_ERROR("Invalid: link rate = 0x%x,lane count = 0x%x\n",
917c943b494SChandan Uddaraju 				link->request.test_link_rate,
918c943b494SChandan Uddaraju 				link->request.test_lane_count);
919c943b494SChandan Uddaraju 		return -EINVAL;
920c943b494SChandan Uddaraju 	}
921c943b494SChandan Uddaraju 
922202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev,
923202aceacSKuogee Hsieh 			"Current: rate = 0x%x, lane count = 0x%x\n",
924c943b494SChandan Uddaraju 			link->dp_link.link_params.rate,
925c943b494SChandan Uddaraju 			link->dp_link.link_params.num_lanes);
926c943b494SChandan Uddaraju 
927202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev,
928202aceacSKuogee Hsieh 			"Requested: rate = 0x%x, lane count = 0x%x\n",
929c943b494SChandan Uddaraju 			link->request.test_link_rate,
930c943b494SChandan Uddaraju 			link->request.test_lane_count);
931c943b494SChandan Uddaraju 
932c943b494SChandan Uddaraju 	link->dp_link.link_params.num_lanes = link->request.test_lane_count;
9336625e263STanmay Shah 	link->dp_link.link_params.rate =
9346625e263STanmay Shah 		drm_dp_bw_code_to_link_rate(link->request.test_link_rate);
935c943b494SChandan Uddaraju 
9367d21fb8aSBernard Zhao 	dp_link_parse_vx_px(link);
937c943b494SChandan Uddaraju 
9387d21fb8aSBernard Zhao 	return 0;
939c943b494SChandan Uddaraju }
940c943b494SChandan Uddaraju 
dp_link_read_psr_error_status(struct dp_link_private * link)941cd779808SVinod Polimera static bool dp_link_read_psr_error_status(struct dp_link_private *link)
942cd779808SVinod Polimera {
943cd779808SVinod Polimera 	u8 status;
944cd779808SVinod Polimera 
945cd779808SVinod Polimera 	drm_dp_dpcd_read(link->aux, DP_PSR_ERROR_STATUS, &status, 1);
946cd779808SVinod Polimera 
947cd779808SVinod Polimera 	if (status & DP_PSR_LINK_CRC_ERROR)
948cd779808SVinod Polimera 		DRM_ERROR("PSR LINK CRC ERROR\n");
949cd779808SVinod Polimera 	else if (status & DP_PSR_RFB_STORAGE_ERROR)
950cd779808SVinod Polimera 		DRM_ERROR("PSR RFB STORAGE ERROR\n");
951cd779808SVinod Polimera 	else if (status & DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR)
952cd779808SVinod Polimera 		DRM_ERROR("PSR VSC SDP UNCORRECTABLE ERROR\n");
953cd779808SVinod Polimera 	else
954cd779808SVinod Polimera 		return false;
955cd779808SVinod Polimera 
956cd779808SVinod Polimera 	return true;
957cd779808SVinod Polimera }
958cd779808SVinod Polimera 
dp_link_psr_capability_changed(struct dp_link_private * link)959cd779808SVinod Polimera static bool dp_link_psr_capability_changed(struct dp_link_private *link)
960cd779808SVinod Polimera {
961cd779808SVinod Polimera 	u8 status;
962cd779808SVinod Polimera 
963cd779808SVinod Polimera 	drm_dp_dpcd_read(link->aux, DP_PSR_ESI, &status, 1);
964cd779808SVinod Polimera 
965cd779808SVinod Polimera 	if (status & DP_PSR_CAPS_CHANGE) {
966cd779808SVinod Polimera 		drm_dbg_dp(link->drm_dev, "PSR Capability Change\n");
967cd779808SVinod Polimera 		return true;
968cd779808SVinod Polimera 	}
969cd779808SVinod Polimera 
970cd779808SVinod Polimera 	return false;
971cd779808SVinod Polimera }
972cd779808SVinod Polimera 
get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE],int r)973c943b494SChandan Uddaraju static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
974c943b494SChandan Uddaraju {
975c943b494SChandan Uddaraju 	return link_status[r - DP_LANE0_1_STATUS];
976c943b494SChandan Uddaraju }
977c943b494SChandan Uddaraju 
978c943b494SChandan Uddaraju /**
979c943b494SChandan Uddaraju  * dp_link_process_link_status_update() - processes link status updates
980c943b494SChandan Uddaraju  * @link: Display Port link module data
981c943b494SChandan Uddaraju  *
982c943b494SChandan Uddaraju  * This function will check for changes in the link status, e.g. clock
983c943b494SChandan Uddaraju  * recovery done on all lanes, and trigger link training if there is a
984c943b494SChandan Uddaraju  * failure/error on the link.
985c943b494SChandan Uddaraju  *
986c943b494SChandan Uddaraju  * The function will return 0 if the a link status update has been processed,
987c943b494SChandan Uddaraju  * otherwise it will return -EINVAL.
988c943b494SChandan Uddaraju  */
dp_link_process_link_status_update(struct dp_link_private * link)989c943b494SChandan Uddaraju static int dp_link_process_link_status_update(struct dp_link_private *link)
990c943b494SChandan Uddaraju {
991ea530388SKuogee Hsieh 	bool channel_eq_done = drm_dp_channel_eq_ok(link->link_status,
992ea530388SKuogee Hsieh 			link->dp_link.link_params.num_lanes);
993ea530388SKuogee Hsieh 
994ea530388SKuogee Hsieh 	bool clock_recovery_done = drm_dp_clock_recovery_ok(link->link_status,
995ea530388SKuogee Hsieh 			link->dp_link.link_params.num_lanes);
996c943b494SChandan Uddaraju 
997202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev,
998202aceacSKuogee Hsieh 		       "channel_eq_done = %d, clock_recovery_done = %d\n",
999ea530388SKuogee Hsieh                         channel_eq_done, clock_recovery_done);
1000ea530388SKuogee Hsieh 
1001ea530388SKuogee Hsieh 	if (channel_eq_done && clock_recovery_done)
1002ea530388SKuogee Hsieh 		return -EINVAL;
1003ea530388SKuogee Hsieh 
1004c943b494SChandan Uddaraju 	return 0;
1005c943b494SChandan Uddaraju }
1006c943b494SChandan Uddaraju 
1007c943b494SChandan Uddaraju /**
100844b4fcbcSLee Jones  * dp_link_process_ds_port_status_change() - process port status changes
1009c943b494SChandan Uddaraju  * @link: Display Port Driver data
1010c943b494SChandan Uddaraju  *
1011c943b494SChandan Uddaraju  * This function will handle downstream port updates that are initiated by
1012c943b494SChandan Uddaraju  * the sink. If the downstream port status has changed, the EDID is read via
1013c943b494SChandan Uddaraju  * AUX.
1014c943b494SChandan Uddaraju  *
1015c943b494SChandan Uddaraju  * The function will return 0 if a downstream port update has been
1016c943b494SChandan Uddaraju  * processed, otherwise it will return -EINVAL.
1017c943b494SChandan Uddaraju  */
dp_link_process_ds_port_status_change(struct dp_link_private * link)1018c943b494SChandan Uddaraju static int dp_link_process_ds_port_status_change(struct dp_link_private *link)
1019c943b494SChandan Uddaraju {
1020c943b494SChandan Uddaraju 	if (get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
1021c943b494SChandan Uddaraju 					DP_DOWNSTREAM_PORT_STATUS_CHANGED)
1022c943b494SChandan Uddaraju 		goto reset;
1023c943b494SChandan Uddaraju 
1024c943b494SChandan Uddaraju 	if (link->prev_sink_count == link->dp_link.sink_count)
1025c943b494SChandan Uddaraju 		return -EINVAL;
1026c943b494SChandan Uddaraju 
1027c943b494SChandan Uddaraju reset:
1028c943b494SChandan Uddaraju 	/* reset prev_sink_count */
1029c943b494SChandan Uddaraju 	link->prev_sink_count = link->dp_link.sink_count;
1030c943b494SChandan Uddaraju 
1031c943b494SChandan Uddaraju 	return 0;
1032c943b494SChandan Uddaraju }
1033c943b494SChandan Uddaraju 
dp_link_is_video_pattern_requested(struct dp_link_private * link)1034c943b494SChandan Uddaraju static bool dp_link_is_video_pattern_requested(struct dp_link_private *link)
1035c943b494SChandan Uddaraju {
1036c943b494SChandan Uddaraju 	return (link->request.test_requested & DP_TEST_LINK_VIDEO_PATTERN)
1037c943b494SChandan Uddaraju 		&& !(link->request.test_requested &
1038c943b494SChandan Uddaraju 		DP_TEST_LINK_AUDIO_DISABLED_VIDEO);
1039c943b494SChandan Uddaraju }
1040c943b494SChandan Uddaraju 
dp_link_is_audio_pattern_requested(struct dp_link_private * link)1041c943b494SChandan Uddaraju static bool dp_link_is_audio_pattern_requested(struct dp_link_private *link)
1042c943b494SChandan Uddaraju {
1043c943b494SChandan Uddaraju 	return (link->request.test_requested & DP_TEST_LINK_AUDIO_PATTERN);
1044c943b494SChandan Uddaraju }
1045c943b494SChandan Uddaraju 
dp_link_reset_data(struct dp_link_private * link)1046c943b494SChandan Uddaraju static void dp_link_reset_data(struct dp_link_private *link)
1047c943b494SChandan Uddaraju {
1048c943b494SChandan Uddaraju 	link->request = (const struct dp_link_request){ 0 };
1049c943b494SChandan Uddaraju 	link->dp_link.test_video = (const struct dp_link_test_video){ 0 };
1050c943b494SChandan Uddaraju 	link->dp_link.test_video.test_bit_depth = DP_TEST_BIT_DEPTH_UNKNOWN;
1051c943b494SChandan Uddaraju 	link->dp_link.test_audio = (const struct dp_link_test_audio){ 0 };
1052c943b494SChandan Uddaraju 	link->dp_link.phy_params.phy_test_pattern_sel = 0;
1053c943b494SChandan Uddaraju 	link->dp_link.sink_request = 0;
1054c943b494SChandan Uddaraju 	link->dp_link.test_response = 0;
1055c943b494SChandan Uddaraju }
1056c943b494SChandan Uddaraju 
1057c943b494SChandan Uddaraju /**
1058c943b494SChandan Uddaraju  * dp_link_process_request() - handle HPD IRQ transition to HIGH
1059c943b494SChandan Uddaraju  * @dp_link: pointer to link module data
1060c943b494SChandan Uddaraju  *
1061c943b494SChandan Uddaraju  * This function will handle the HPD IRQ state transitions from LOW to HIGH
1062c943b494SChandan Uddaraju  * (including cases when there are back to back HPD IRQ HIGH) indicating
1063c943b494SChandan Uddaraju  * the start of a new link training request or sink status update.
1064c943b494SChandan Uddaraju  */
dp_link_process_request(struct dp_link * dp_link)1065c943b494SChandan Uddaraju int dp_link_process_request(struct dp_link *dp_link)
1066c943b494SChandan Uddaraju {
1067c943b494SChandan Uddaraju 	int ret = 0;
1068c943b494SChandan Uddaraju 	struct dp_link_private *link;
1069c943b494SChandan Uddaraju 
1070c943b494SChandan Uddaraju 	if (!dp_link) {
1071c943b494SChandan Uddaraju 		DRM_ERROR("invalid input\n");
1072c943b494SChandan Uddaraju 		return -EINVAL;
1073c943b494SChandan Uddaraju 	}
1074c943b494SChandan Uddaraju 
1075c943b494SChandan Uddaraju 	link = container_of(dp_link, struct dp_link_private, dp_link);
1076c943b494SChandan Uddaraju 
1077c943b494SChandan Uddaraju 	dp_link_reset_data(link);
1078c943b494SChandan Uddaraju 
1079f61550b3SKuogee Hsieh 	ret = dp_link_parse_sink_status_field(link);
1080f61550b3SKuogee Hsieh 	if (ret)
1081f61550b3SKuogee Hsieh 		return ret;
1082c943b494SChandan Uddaraju 
1083c943b494SChandan Uddaraju 	if (link->request.test_requested == DP_TEST_LINK_EDID_READ) {
1084c943b494SChandan Uddaraju 		dp_link->sink_request |= DP_TEST_LINK_EDID_READ;
1085601f0479SMaitreyee Rao 	} else if (!dp_link_process_ds_port_status_change(link)) {
1086c943b494SChandan Uddaraju 		dp_link->sink_request |= DS_PORT_STATUS_CHANGED;
1087601f0479SMaitreyee Rao 	} else if (!dp_link_process_link_training_request(link)) {
1088c943b494SChandan Uddaraju 		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
1089601f0479SMaitreyee Rao 	} else if (!dp_link_process_phy_test_pattern_request(link)) {
1090c943b494SChandan Uddaraju 		dp_link->sink_request |= DP_TEST_LINK_PHY_TEST_PATTERN;
1091cd779808SVinod Polimera 	} else if (dp_link_read_psr_error_status(link)) {
1092cd779808SVinod Polimera 		DRM_ERROR("PSR IRQ_HPD received\n");
1093cd779808SVinod Polimera 	} else if (dp_link_psr_capability_changed(link)) {
1094eba8c99aSStephen Boyd 		drm_dbg_dp(link->drm_dev, "PSR Capability changed\n");
1095601f0479SMaitreyee Rao 	} else {
1096c943b494SChandan Uddaraju 		ret = dp_link_process_link_status_update(link);
1097c943b494SChandan Uddaraju 		if (!ret) {
1098c943b494SChandan Uddaraju 			dp_link->sink_request |= DP_LINK_STATUS_UPDATED;
1099601f0479SMaitreyee Rao 		} else {
1100c943b494SChandan Uddaraju 			if (dp_link_is_video_pattern_requested(link)) {
1101ab205927SAbhinav Kumar 				ret = 0;
1102c943b494SChandan Uddaraju 				dp_link->sink_request |= DP_TEST_LINK_VIDEO_PATTERN;
1103c943b494SChandan Uddaraju 			}
1104c943b494SChandan Uddaraju 			if (dp_link_is_audio_pattern_requested(link)) {
1105c943b494SChandan Uddaraju 				dp_link->sink_request |= DP_TEST_LINK_AUDIO_PATTERN;
1106601f0479SMaitreyee Rao 				ret = -EINVAL;
1107601f0479SMaitreyee Rao 			}
1108601f0479SMaitreyee Rao 		}
1109c943b494SChandan Uddaraju 	}
1110c943b494SChandan Uddaraju 
1111eba8c99aSStephen Boyd 	drm_dbg_dp(link->drm_dev, "sink request=%#x\n",
1112202aceacSKuogee Hsieh 				dp_link->sink_request);
1113c943b494SChandan Uddaraju 	return ret;
1114c943b494SChandan Uddaraju }
1115c943b494SChandan Uddaraju 
dp_link_get_colorimetry_config(struct dp_link * dp_link)1116c943b494SChandan Uddaraju int dp_link_get_colorimetry_config(struct dp_link *dp_link)
1117c943b494SChandan Uddaraju {
11181449c757SKuogee Hsieh 	u32 cc = DP_MISC0_COLORIMERY_CFG_LEGACY_RGB;
1119c943b494SChandan Uddaraju 	struct dp_link_private *link;
1120c943b494SChandan Uddaraju 
1121c943b494SChandan Uddaraju 	if (!dp_link) {
1122c943b494SChandan Uddaraju 		DRM_ERROR("invalid input\n");
1123c943b494SChandan Uddaraju 		return -EINVAL;
1124c943b494SChandan Uddaraju 	}
1125c943b494SChandan Uddaraju 
1126c943b494SChandan Uddaraju 	link = container_of(dp_link, struct dp_link_private, dp_link);
1127c943b494SChandan Uddaraju 
1128c943b494SChandan Uddaraju 	/*
1129c943b494SChandan Uddaraju 	 * Unless a video pattern CTS test is ongoing, use RGB_VESA
1130c943b494SChandan Uddaraju 	 * Only RGB_VESA and RGB_CEA supported for now
1131c943b494SChandan Uddaraju 	 */
11321449c757SKuogee Hsieh 	if (dp_link_is_video_pattern_requested(link)) {
11331449c757SKuogee Hsieh 		if (link->dp_link.test_video.test_dyn_range &
11341449c757SKuogee Hsieh 					DP_TEST_DYNAMIC_RANGE_CEA)
11351449c757SKuogee Hsieh 			cc = DP_MISC0_COLORIMERY_CFG_CEA_RGB;
11361449c757SKuogee Hsieh 	}
1137c943b494SChandan Uddaraju 
1138c943b494SChandan Uddaraju 	return cc;
1139c943b494SChandan Uddaraju }
1140c943b494SChandan Uddaraju 
dp_link_adjust_levels(struct dp_link * dp_link,u8 * link_status)1141c943b494SChandan Uddaraju int dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status)
1142c943b494SChandan Uddaraju {
1143c943b494SChandan Uddaraju 	int i;
1144554484e4SDmitry Baryshkov 	u8 max_p_level;
1145c943b494SChandan Uddaraju 	int v_max = 0, p_max = 0;
1146202aceacSKuogee Hsieh 	struct dp_link_private *link;
1147c943b494SChandan Uddaraju 
1148c943b494SChandan Uddaraju 	if (!dp_link) {
1149c943b494SChandan Uddaraju 		DRM_ERROR("invalid input\n");
1150c943b494SChandan Uddaraju 		return -EINVAL;
1151c943b494SChandan Uddaraju 	}
1152c943b494SChandan Uddaraju 
1153202aceacSKuogee Hsieh 	link = container_of(dp_link, struct dp_link_private, dp_link);
1154202aceacSKuogee Hsieh 
1155c943b494SChandan Uddaraju 	/* use the max level across lanes */
1156c943b494SChandan Uddaraju 	for (i = 0; i < dp_link->link_params.num_lanes; i++) {
1157c943b494SChandan Uddaraju 		u8 data_v = drm_dp_get_adjust_request_voltage(link_status, i);
1158c943b494SChandan Uddaraju 		u8 data_p = drm_dp_get_adjust_request_pre_emphasis(link_status,
1159c943b494SChandan Uddaraju 									 i);
1160202aceacSKuogee Hsieh 		drm_dbg_dp(link->drm_dev,
1161202aceacSKuogee Hsieh 				"lane=%d req_vol_swing=%d req_pre_emphasis=%d\n",
1162c943b494SChandan Uddaraju 				i, data_v, data_p);
1163c943b494SChandan Uddaraju 		if (v_max < data_v)
1164c943b494SChandan Uddaraju 			v_max = data_v;
1165c943b494SChandan Uddaraju 		if (p_max < data_p)
1166c943b494SChandan Uddaraju 			p_max = data_p;
1167c943b494SChandan Uddaraju 	}
1168c943b494SChandan Uddaraju 
1169c943b494SChandan Uddaraju 	dp_link->phy_params.v_level = v_max >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
1170c943b494SChandan Uddaraju 	dp_link->phy_params.p_level = p_max >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
1171c943b494SChandan Uddaraju 
1172c943b494SChandan Uddaraju 	/**
1173c943b494SChandan Uddaraju 	 * Adjust the voltage swing and pre-emphasis level combination to within
1174c943b494SChandan Uddaraju 	 * the allowable range.
1175c943b494SChandan Uddaraju 	 */
1176554484e4SDmitry Baryshkov 	if (dp_link->phy_params.v_level > DP_TRAIN_LEVEL_MAX) {
1177202aceacSKuogee Hsieh 		drm_dbg_dp(link->drm_dev,
1178202aceacSKuogee Hsieh 			"Requested vSwingLevel=%d, change to %d\n",
1179c943b494SChandan Uddaraju 			dp_link->phy_params.v_level,
1180554484e4SDmitry Baryshkov 			DP_TRAIN_LEVEL_MAX);
1181554484e4SDmitry Baryshkov 		dp_link->phy_params.v_level = DP_TRAIN_LEVEL_MAX;
1182c943b494SChandan Uddaraju 	}
1183c943b494SChandan Uddaraju 
1184554484e4SDmitry Baryshkov 	if (dp_link->phy_params.p_level > DP_TRAIN_LEVEL_MAX) {
1185202aceacSKuogee Hsieh 		drm_dbg_dp(link->drm_dev,
1186202aceacSKuogee Hsieh 			"Requested preEmphasisLevel=%d, change to %d\n",
1187c943b494SChandan Uddaraju 			dp_link->phy_params.p_level,
1188554484e4SDmitry Baryshkov 			DP_TRAIN_LEVEL_MAX);
1189554484e4SDmitry Baryshkov 		dp_link->phy_params.p_level = DP_TRAIN_LEVEL_MAX;
1190c943b494SChandan Uddaraju 	}
1191c943b494SChandan Uddaraju 
1192554484e4SDmitry Baryshkov 	max_p_level = DP_TRAIN_LEVEL_MAX - dp_link->phy_params.v_level;
1193554484e4SDmitry Baryshkov 	if (dp_link->phy_params.p_level > max_p_level) {
1194202aceacSKuogee Hsieh 		drm_dbg_dp(link->drm_dev,
1195202aceacSKuogee Hsieh 			"Requested preEmphasisLevel=%d, change to %d\n",
1196c943b494SChandan Uddaraju 			dp_link->phy_params.p_level,
1197554484e4SDmitry Baryshkov 			max_p_level);
1198554484e4SDmitry Baryshkov 		dp_link->phy_params.p_level = max_p_level;
1199c943b494SChandan Uddaraju 	}
1200c943b494SChandan Uddaraju 
1201202aceacSKuogee Hsieh 	drm_dbg_dp(link->drm_dev, "adjusted: v_level=%d, p_level=%d\n",
1202c943b494SChandan Uddaraju 		dp_link->phy_params.v_level, dp_link->phy_params.p_level);
1203c943b494SChandan Uddaraju 
1204c943b494SChandan Uddaraju 	return 0;
1205c943b494SChandan Uddaraju }
1206c943b494SChandan Uddaraju 
dp_link_reset_phy_params_vx_px(struct dp_link * dp_link)12076625e263STanmay Shah void dp_link_reset_phy_params_vx_px(struct dp_link *dp_link)
12086625e263STanmay Shah {
12096625e263STanmay Shah 	dp_link->phy_params.v_level = 0;
12106625e263STanmay Shah 	dp_link->phy_params.p_level = 0;
12116625e263STanmay Shah }
12126625e263STanmay Shah 
dp_link_get_test_bits_depth(struct dp_link * dp_link,u32 bpp)1213c943b494SChandan Uddaraju u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp)
1214c943b494SChandan Uddaraju {
1215c943b494SChandan Uddaraju 	u32 tbd;
12161506145cSKuogee Hsieh 	struct dp_link_private *link;
12171506145cSKuogee Hsieh 
12181506145cSKuogee Hsieh 	link = container_of(dp_link, struct dp_link_private, dp_link);
1219c943b494SChandan Uddaraju 
1220c943b494SChandan Uddaraju 	/*
1221c943b494SChandan Uddaraju 	 * Few simplistic rules and assumptions made here:
1222c943b494SChandan Uddaraju 	 *    1. Test bit depth is bit depth per color component
1223c943b494SChandan Uddaraju 	 *    2. Assume 3 color components
1224c943b494SChandan Uddaraju 	 */
1225c943b494SChandan Uddaraju 	switch (bpp) {
1226c943b494SChandan Uddaraju 	case 18:
1227c943b494SChandan Uddaraju 		tbd = DP_TEST_BIT_DEPTH_6;
1228c943b494SChandan Uddaraju 		break;
1229c943b494SChandan Uddaraju 	case 24:
1230c943b494SChandan Uddaraju 		tbd = DP_TEST_BIT_DEPTH_8;
1231c943b494SChandan Uddaraju 		break;
1232c943b494SChandan Uddaraju 	case 30:
1233c943b494SChandan Uddaraju 		tbd = DP_TEST_BIT_DEPTH_10;
1234c943b494SChandan Uddaraju 		break;
1235c943b494SChandan Uddaraju 	default:
12361506145cSKuogee Hsieh 		drm_dbg_dp(link->drm_dev, "bpp=%d not supported, use bpc=8\n",
12371506145cSKuogee Hsieh 			   bpp);
12381506145cSKuogee Hsieh 		tbd = DP_TEST_BIT_DEPTH_8;
1239c943b494SChandan Uddaraju 		break;
1240c943b494SChandan Uddaraju 	}
1241c943b494SChandan Uddaraju 
1242c943b494SChandan Uddaraju 	tbd = (tbd >> DP_TEST_BIT_DEPTH_SHIFT);
1243c943b494SChandan Uddaraju 
1244c943b494SChandan Uddaraju 	return tbd;
1245c943b494SChandan Uddaraju }
1246c943b494SChandan Uddaraju 
dp_link_get(struct device * dev,struct drm_dp_aux * aux)1247c943b494SChandan Uddaraju struct dp_link *dp_link_get(struct device *dev, struct drm_dp_aux *aux)
1248c943b494SChandan Uddaraju {
1249c943b494SChandan Uddaraju 	struct dp_link_private *link;
1250c943b494SChandan Uddaraju 	struct dp_link *dp_link;
1251c943b494SChandan Uddaraju 
1252c943b494SChandan Uddaraju 	if (!dev || !aux) {
1253c943b494SChandan Uddaraju 		DRM_ERROR("invalid input\n");
1254c943b494SChandan Uddaraju 		return ERR_PTR(-EINVAL);
1255c943b494SChandan Uddaraju 	}
1256c943b494SChandan Uddaraju 
1257c943b494SChandan Uddaraju 	link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
1258c943b494SChandan Uddaraju 	if (!link)
1259c943b494SChandan Uddaraju 		return ERR_PTR(-ENOMEM);
1260c943b494SChandan Uddaraju 
1261c943b494SChandan Uddaraju 	link->dev   = dev;
1262c943b494SChandan Uddaraju 	link->aux   = aux;
1263c943b494SChandan Uddaraju 
1264c943b494SChandan Uddaraju 	mutex_init(&link->psm_mutex);
1265c943b494SChandan Uddaraju 	dp_link = &link->dp_link;
1266c943b494SChandan Uddaraju 
1267c943b494SChandan Uddaraju 	return dp_link;
1268c943b494SChandan Uddaraju }
1269