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