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