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