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