]>
Commit | Line | Data |
---|---|---|
3424e3a4 YY |
1 | /* |
2 | * Analogix DP (Display Port) core interface driver. | |
3 | * | |
4 | * Copyright (C) 2012 Samsung Electronics Co., Ltd. | |
5 | * Author: Jingoo Han <jg1.han@samsung.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. | |
11 | */ | |
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/err.h> | |
16 | #include <linux/clk.h> | |
17 | #include <linux/io.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/of.h> | |
20 | #include <linux/of_gpio.h> | |
21 | #include <linux/gpio.h> | |
22 | #include <linux/component.h> | |
23 | #include <linux/phy/phy.h> | |
24 | ||
25 | #include <drm/drmP.h> | |
26 | #include <drm/drm_atomic_helper.h> | |
27 | #include <drm/drm_crtc.h> | |
28 | #include <drm/drm_crtc_helper.h> | |
29 | #include <drm/drm_panel.h> | |
30 | ||
31 | #include <drm/bridge/analogix_dp.h> | |
32 | ||
33 | #include "analogix_dp_core.h" | |
0d97ad03 | 34 | #include "analogix_dp_reg.h" |
3424e3a4 YY |
35 | |
36 | #define to_dp(nm) container_of(nm, struct analogix_dp_device, nm) | |
37 | ||
38 | struct bridge_init { | |
39 | struct i2c_client *client; | |
40 | struct device_node *node; | |
41 | }; | |
42 | ||
43 | static void analogix_dp_init_dp(struct analogix_dp_device *dp) | |
44 | { | |
45 | analogix_dp_reset(dp); | |
46 | ||
47 | analogix_dp_swreset(dp); | |
48 | ||
49 | analogix_dp_init_analog_param(dp); | |
50 | analogix_dp_init_interrupt(dp); | |
51 | ||
52 | /* SW defined function Normal operation */ | |
53 | analogix_dp_enable_sw_function(dp); | |
54 | ||
55 | analogix_dp_config_interrupt(dp); | |
56 | analogix_dp_init_analog_func(dp); | |
57 | ||
58 | analogix_dp_init_hpd(dp); | |
59 | analogix_dp_init_aux(dp); | |
60 | } | |
61 | ||
62 | static int analogix_dp_detect_hpd(struct analogix_dp_device *dp) | |
63 | { | |
64 | int timeout_loop = 0; | |
65 | ||
5cff007c YY |
66 | while (timeout_loop < DP_TIMEOUT_LOOP_COUNT) { |
67 | if (analogix_dp_get_plug_in_status(dp) == 0) | |
68 | return 0; | |
69 | ||
3424e3a4 | 70 | timeout_loop++; |
3424e3a4 YY |
71 | usleep_range(10, 11); |
72 | } | |
73 | ||
5cff007c YY |
74 | /* |
75 | * Some edp screen do not have hpd signal, so we can't just | |
76 | * return failed when hpd plug in detect failed, DT property | |
77 | * "force-hpd" would indicate whether driver need this. | |
78 | */ | |
79 | if (!dp->force_hpd) | |
80 | return -ETIMEDOUT; | |
81 | ||
82 | /* | |
83 | * The eDP TRM indicate that if HPD_STATUS(RO) is 0, AUX CH | |
84 | * will not work, so we need to give a force hpd action to | |
85 | * set HPD_STATUS manually. | |
86 | */ | |
87 | dev_dbg(dp->dev, "failed to get hpd plug status, try to force hpd\n"); | |
88 | ||
89 | analogix_dp_force_hpd(dp); | |
90 | ||
91 | if (analogix_dp_get_plug_in_status(dp) != 0) { | |
92 | dev_err(dp->dev, "failed to get hpd plug in status\n"); | |
93 | return -EINVAL; | |
94 | } | |
95 | ||
96 | dev_dbg(dp->dev, "success to get plug in status after force hpd\n"); | |
97 | ||
3424e3a4 YY |
98 | return 0; |
99 | } | |
100 | ||
561b0690 TV |
101 | int analogix_dp_psr_supported(struct device *dev) |
102 | { | |
103 | struct analogix_dp_device *dp = dev_get_drvdata(dev); | |
104 | ||
105 | return dp->psr_support; | |
106 | } | |
107 | EXPORT_SYMBOL_GPL(analogix_dp_psr_supported); | |
108 | ||
5b3f84f2 YY |
109 | int analogix_dp_enable_psr(struct device *dev) |
110 | { | |
111 | struct analogix_dp_device *dp = dev_get_drvdata(dev); | |
112 | struct edp_vsc_psr psr_vsc; | |
113 | ||
114 | if (!dp->psr_support) | |
8f34a548 | 115 | return 0; |
5b3f84f2 YY |
116 | |
117 | /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ | |
118 | memset(&psr_vsc, 0, sizeof(psr_vsc)); | |
119 | psr_vsc.sdp_header.HB0 = 0; | |
120 | psr_vsc.sdp_header.HB1 = 0x7; | |
121 | psr_vsc.sdp_header.HB2 = 0x2; | |
122 | psr_vsc.sdp_header.HB3 = 0x8; | |
123 | ||
124 | psr_vsc.DB0 = 0; | |
125 | psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID; | |
126 | ||
127 | analogix_dp_send_psr_spd(dp, &psr_vsc); | |
128 | return 0; | |
129 | } | |
130 | EXPORT_SYMBOL_GPL(analogix_dp_enable_psr); | |
131 | ||
132 | int analogix_dp_disable_psr(struct device *dev) | |
133 | { | |
134 | struct analogix_dp_device *dp = dev_get_drvdata(dev); | |
135 | struct edp_vsc_psr psr_vsc; | |
2289b3e1 | 136 | int ret; |
5b3f84f2 YY |
137 | |
138 | if (!dp->psr_support) | |
8f34a548 | 139 | return 0; |
5b3f84f2 YY |
140 | |
141 | /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ | |
142 | memset(&psr_vsc, 0, sizeof(psr_vsc)); | |
143 | psr_vsc.sdp_header.HB0 = 0; | |
144 | psr_vsc.sdp_header.HB1 = 0x7; | |
145 | psr_vsc.sdp_header.HB2 = 0x2; | |
146 | psr_vsc.sdp_header.HB3 = 0x8; | |
147 | ||
148 | psr_vsc.DB0 = 0; | |
149 | psr_vsc.DB1 = 0; | |
150 | ||
2289b3e1 CW |
151 | ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0); |
152 | if (ret != 1) | |
153 | dev_err(dp->dev, "Failed to set DP Power0 %d\n", ret); | |
154 | ||
5b3f84f2 YY |
155 | analogix_dp_send_psr_spd(dp, &psr_vsc); |
156 | return 0; | |
157 | } | |
158 | EXPORT_SYMBOL_GPL(analogix_dp_disable_psr); | |
159 | ||
160 | static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp) | |
161 | { | |
162 | unsigned char psr_version; | |
163 | ||
0d97ad03 | 164 | drm_dp_dpcd_readb(&dp->aux, DP_PSR_SUPPORT, &psr_version); |
5b3f84f2 YY |
165 | dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version); |
166 | ||
167 | return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false; | |
168 | } | |
169 | ||
170 | static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp) | |
171 | { | |
172 | unsigned char psr_en; | |
173 | ||
174 | /* Disable psr function */ | |
0d97ad03 | 175 | drm_dp_dpcd_readb(&dp->aux, DP_PSR_EN_CFG, &psr_en); |
5b3f84f2 | 176 | psr_en &= ~DP_PSR_ENABLE; |
0d97ad03 | 177 | drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); |
5b3f84f2 YY |
178 | |
179 | /* Main-Link transmitter remains active during PSR active states */ | |
180 | psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION; | |
0d97ad03 | 181 | drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); |
5b3f84f2 YY |
182 | |
183 | /* Enable psr function */ | |
184 | psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE | | |
185 | DP_PSR_CRC_VERIFICATION; | |
0d97ad03 | 186 | drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); |
5b3f84f2 YY |
187 | |
188 | analogix_dp_enable_psr_crc(dp); | |
189 | } | |
190 | ||
bcbb7033 YY |
191 | static void |
192 | analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp, | |
193 | bool enable) | |
3424e3a4 YY |
194 | { |
195 | u8 data; | |
196 | ||
0d97ad03 | 197 | drm_dp_dpcd_readb(&dp->aux, DP_LANE_COUNT_SET, &data); |
3424e3a4 YY |
198 | |
199 | if (enable) | |
0d97ad03 TV |
200 | drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, |
201 | DP_LANE_COUNT_ENHANCED_FRAME_EN | | |
202 | DPCD_LANE_COUNT_SET(data)); | |
3424e3a4 | 203 | else |
0d97ad03 TV |
204 | drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, |
205 | DPCD_LANE_COUNT_SET(data)); | |
3424e3a4 YY |
206 | } |
207 | ||
208 | static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp) | |
209 | { | |
210 | u8 data; | |
211 | int retval; | |
212 | ||
0d97ad03 | 213 | drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data); |
3424e3a4 YY |
214 | retval = DPCD_ENHANCED_FRAME_CAP(data); |
215 | ||
216 | return retval; | |
217 | } | |
218 | ||
219 | static void analogix_dp_set_enhanced_mode(struct analogix_dp_device *dp) | |
220 | { | |
221 | u8 data; | |
222 | ||
223 | data = analogix_dp_is_enhanced_mode_available(dp); | |
224 | analogix_dp_enable_rx_to_enhanced_mode(dp, data); | |
225 | analogix_dp_enable_enhanced_mode(dp, data); | |
226 | } | |
227 | ||
228 | static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp) | |
229 | { | |
230 | analogix_dp_set_training_pattern(dp, DP_NONE); | |
231 | ||
0d97ad03 TV |
232 | drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, |
233 | DP_TRAINING_PATTERN_DISABLE); | |
3424e3a4 YY |
234 | } |
235 | ||
bcbb7033 YY |
236 | static void |
237 | analogix_dp_set_lane_lane_pre_emphasis(struct analogix_dp_device *dp, | |
238 | int pre_emphasis, int lane) | |
3424e3a4 YY |
239 | { |
240 | switch (lane) { | |
241 | case 0: | |
242 | analogix_dp_set_lane0_pre_emphasis(dp, pre_emphasis); | |
243 | break; | |
244 | case 1: | |
245 | analogix_dp_set_lane1_pre_emphasis(dp, pre_emphasis); | |
246 | break; | |
247 | ||
248 | case 2: | |
249 | analogix_dp_set_lane2_pre_emphasis(dp, pre_emphasis); | |
250 | break; | |
251 | ||
252 | case 3: | |
253 | analogix_dp_set_lane3_pre_emphasis(dp, pre_emphasis); | |
254 | break; | |
255 | } | |
256 | } | |
257 | ||
258 | static int analogix_dp_link_start(struct analogix_dp_device *dp) | |
259 | { | |
260 | u8 buf[4]; | |
261 | int lane, lane_count, pll_tries, retval; | |
262 | ||
263 | lane_count = dp->link_train.lane_count; | |
264 | ||
265 | dp->link_train.lt_state = CLOCK_RECOVERY; | |
266 | dp->link_train.eq_loop = 0; | |
267 | ||
268 | for (lane = 0; lane < lane_count; lane++) | |
269 | dp->link_train.cr_loop[lane] = 0; | |
270 | ||
271 | /* Set link rate and count as you want to establish*/ | |
272 | analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate); | |
273 | analogix_dp_set_lane_count(dp, dp->link_train.lane_count); | |
274 | ||
275 | /* Setup RX configuration */ | |
276 | buf[0] = dp->link_train.link_rate; | |
277 | buf[1] = dp->link_train.lane_count; | |
0d97ad03 TV |
278 | retval = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, 2); |
279 | if (retval < 0) | |
3424e3a4 YY |
280 | return retval; |
281 | ||
282 | /* Set TX pre-emphasis to minimum */ | |
283 | for (lane = 0; lane < lane_count; lane++) | |
284 | analogix_dp_set_lane_lane_pre_emphasis(dp, | |
285 | PRE_EMPHASIS_LEVEL_0, lane); | |
286 | ||
287 | /* Wait for PLL lock */ | |
288 | pll_tries = 0; | |
289 | while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { | |
290 | if (pll_tries == DP_TIMEOUT_LOOP_COUNT) { | |
291 | dev_err(dp->dev, "Wait for PLL lock timed out\n"); | |
292 | return -ETIMEDOUT; | |
293 | } | |
294 | ||
295 | pll_tries++; | |
296 | usleep_range(90, 120); | |
297 | } | |
298 | ||
299 | /* Set training pattern 1 */ | |
300 | analogix_dp_set_training_pattern(dp, TRAINING_PTN1); | |
301 | ||
302 | /* Set RX training pattern */ | |
0d97ad03 TV |
303 | retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, |
304 | DP_LINK_SCRAMBLING_DISABLE | | |
305 | DP_TRAINING_PATTERN_1); | |
306 | if (retval < 0) | |
3424e3a4 YY |
307 | return retval; |
308 | ||
309 | for (lane = 0; lane < lane_count; lane++) | |
310 | buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 | | |
311 | DP_TRAIN_VOLTAGE_SWING_LEVEL_0; | |
312 | ||
0d97ad03 TV |
313 | retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, buf, |
314 | lane_count); | |
315 | if (retval < 0) | |
316 | return retval; | |
3424e3a4 | 317 | |
0d97ad03 | 318 | return 0; |
3424e3a4 YY |
319 | } |
320 | ||
321 | static unsigned char analogix_dp_get_lane_status(u8 link_status[2], int lane) | |
322 | { | |
323 | int shift = (lane & 1) * 4; | |
bcbb7033 | 324 | u8 link_value = link_status[lane >> 1]; |
3424e3a4 YY |
325 | |
326 | return (link_value >> shift) & 0xf; | |
327 | } | |
328 | ||
329 | static int analogix_dp_clock_recovery_ok(u8 link_status[2], int lane_count) | |
330 | { | |
331 | int lane; | |
332 | u8 lane_status; | |
333 | ||
334 | for (lane = 0; lane < lane_count; lane++) { | |
335 | lane_status = analogix_dp_get_lane_status(link_status, lane); | |
336 | if ((lane_status & DP_LANE_CR_DONE) == 0) | |
337 | return -EINVAL; | |
338 | } | |
339 | return 0; | |
340 | } | |
341 | ||
342 | static int analogix_dp_channel_eq_ok(u8 link_status[2], u8 link_align, | |
bcbb7033 | 343 | int lane_count) |
3424e3a4 YY |
344 | { |
345 | int lane; | |
346 | u8 lane_status; | |
347 | ||
348 | if ((link_align & DP_INTERLANE_ALIGN_DONE) == 0) | |
349 | return -EINVAL; | |
350 | ||
351 | for (lane = 0; lane < lane_count; lane++) { | |
352 | lane_status = analogix_dp_get_lane_status(link_status, lane); | |
353 | lane_status &= DP_CHANNEL_EQ_BITS; | |
354 | if (lane_status != DP_CHANNEL_EQ_BITS) | |
355 | return -EINVAL; | |
356 | } | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
bcbb7033 YY |
361 | static unsigned char |
362 | analogix_dp_get_adjust_request_voltage(u8 adjust_request[2], int lane) | |
3424e3a4 YY |
363 | { |
364 | int shift = (lane & 1) * 4; | |
bcbb7033 | 365 | u8 link_value = adjust_request[lane >> 1]; |
3424e3a4 YY |
366 | |
367 | return (link_value >> shift) & 0x3; | |
368 | } | |
369 | ||
370 | static unsigned char analogix_dp_get_adjust_request_pre_emphasis( | |
371 | u8 adjust_request[2], | |
372 | int lane) | |
373 | { | |
374 | int shift = (lane & 1) * 4; | |
bcbb7033 | 375 | u8 link_value = adjust_request[lane >> 1]; |
3424e3a4 YY |
376 | |
377 | return ((link_value >> shift) & 0xc) >> 2; | |
378 | } | |
379 | ||
380 | static void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp, | |
bcbb7033 | 381 | u8 training_lane_set, int lane) |
3424e3a4 YY |
382 | { |
383 | switch (lane) { | |
384 | case 0: | |
385 | analogix_dp_set_lane0_link_training(dp, training_lane_set); | |
386 | break; | |
387 | case 1: | |
388 | analogix_dp_set_lane1_link_training(dp, training_lane_set); | |
389 | break; | |
390 | ||
391 | case 2: | |
392 | analogix_dp_set_lane2_link_training(dp, training_lane_set); | |
393 | break; | |
394 | ||
395 | case 3: | |
396 | analogix_dp_set_lane3_link_training(dp, training_lane_set); | |
397 | break; | |
398 | } | |
399 | } | |
400 | ||
bcbb7033 YY |
401 | static unsigned int |
402 | analogix_dp_get_lane_link_training(struct analogix_dp_device *dp, | |
403 | int lane) | |
3424e3a4 YY |
404 | { |
405 | u32 reg; | |
406 | ||
407 | switch (lane) { | |
408 | case 0: | |
409 | reg = analogix_dp_get_lane0_link_training(dp); | |
410 | break; | |
411 | case 1: | |
412 | reg = analogix_dp_get_lane1_link_training(dp); | |
413 | break; | |
414 | case 2: | |
415 | reg = analogix_dp_get_lane2_link_training(dp); | |
416 | break; | |
417 | case 3: | |
418 | reg = analogix_dp_get_lane3_link_training(dp); | |
419 | break; | |
420 | default: | |
421 | WARN_ON(1); | |
422 | return 0; | |
423 | } | |
424 | ||
425 | return reg; | |
426 | } | |
427 | ||
428 | static void analogix_dp_reduce_link_rate(struct analogix_dp_device *dp) | |
429 | { | |
430 | analogix_dp_training_pattern_dis(dp); | |
431 | analogix_dp_set_enhanced_mode(dp); | |
432 | ||
433 | dp->link_train.lt_state = FAILED; | |
434 | } | |
435 | ||
436 | static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp, | |
bcbb7033 | 437 | u8 adjust_request[2]) |
3424e3a4 YY |
438 | { |
439 | int lane, lane_count; | |
440 | u8 voltage_swing, pre_emphasis, training_lane; | |
441 | ||
442 | lane_count = dp->link_train.lane_count; | |
443 | for (lane = 0; lane < lane_count; lane++) { | |
444 | voltage_swing = analogix_dp_get_adjust_request_voltage( | |
445 | adjust_request, lane); | |
446 | pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis( | |
447 | adjust_request, lane); | |
448 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | |
449 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | |
450 | ||
451 | if (voltage_swing == VOLTAGE_LEVEL_3) | |
452 | training_lane |= DP_TRAIN_MAX_SWING_REACHED; | |
453 | if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) | |
454 | training_lane |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; | |
455 | ||
456 | dp->link_train.training_lane[lane] = training_lane; | |
457 | } | |
458 | } | |
459 | ||
460 | static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp) | |
461 | { | |
462 | int lane, lane_count, retval; | |
463 | u8 voltage_swing, pre_emphasis, training_lane; | |
464 | u8 link_status[2], adjust_request[2]; | |
465 | ||
466 | usleep_range(100, 101); | |
467 | ||
468 | lane_count = dp->link_train.lane_count; | |
469 | ||
0d97ad03 TV |
470 | retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2); |
471 | if (retval < 0) | |
3424e3a4 YY |
472 | return retval; |
473 | ||
0d97ad03 TV |
474 | retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1, |
475 | adjust_request, 2); | |
476 | if (retval < 0) | |
3424e3a4 YY |
477 | return retval; |
478 | ||
479 | if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) { | |
480 | /* set training pattern 2 for EQ */ | |
481 | analogix_dp_set_training_pattern(dp, TRAINING_PTN2); | |
482 | ||
0d97ad03 TV |
483 | retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, |
484 | DP_LINK_SCRAMBLING_DISABLE | | |
485 | DP_TRAINING_PATTERN_2); | |
486 | if (retval < 0) | |
3424e3a4 YY |
487 | return retval; |
488 | ||
489 | dev_info(dp->dev, "Link Training Clock Recovery success\n"); | |
490 | dp->link_train.lt_state = EQUALIZER_TRAINING; | |
491 | } else { | |
492 | for (lane = 0; lane < lane_count; lane++) { | |
493 | training_lane = analogix_dp_get_lane_link_training( | |
494 | dp, lane); | |
495 | voltage_swing = analogix_dp_get_adjust_request_voltage( | |
496 | adjust_request, lane); | |
497 | pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis( | |
498 | adjust_request, lane); | |
499 | ||
500 | if (DPCD_VOLTAGE_SWING_GET(training_lane) == | |
501 | voltage_swing && | |
502 | DPCD_PRE_EMPHASIS_GET(training_lane) == | |
503 | pre_emphasis) | |
504 | dp->link_train.cr_loop[lane]++; | |
505 | ||
506 | if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP || | |
507 | voltage_swing == VOLTAGE_LEVEL_3 || | |
508 | pre_emphasis == PRE_EMPHASIS_LEVEL_3) { | |
509 | dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n", | |
510 | dp->link_train.cr_loop[lane], | |
511 | voltage_swing, pre_emphasis); | |
512 | analogix_dp_reduce_link_rate(dp); | |
513 | return -EIO; | |
514 | } | |
515 | } | |
516 | } | |
517 | ||
518 | analogix_dp_get_adjust_training_lane(dp, adjust_request); | |
519 | ||
520 | for (lane = 0; lane < lane_count; lane++) | |
521 | analogix_dp_set_lane_link_training(dp, | |
522 | dp->link_train.training_lane[lane], lane); | |
523 | ||
0d97ad03 TV |
524 | retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, |
525 | dp->link_train.training_lane, lane_count); | |
526 | if (retval < 0) | |
3424e3a4 YY |
527 | return retval; |
528 | ||
0d97ad03 | 529 | return 0; |
3424e3a4 YY |
530 | } |
531 | ||
532 | static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp) | |
533 | { | |
534 | int lane, lane_count, retval; | |
535 | u32 reg; | |
536 | u8 link_align, link_status[2], adjust_request[2]; | |
537 | ||
538 | usleep_range(400, 401); | |
539 | ||
540 | lane_count = dp->link_train.lane_count; | |
541 | ||
0d97ad03 TV |
542 | retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2); |
543 | if (retval < 0) | |
3424e3a4 YY |
544 | return retval; |
545 | ||
546 | if (analogix_dp_clock_recovery_ok(link_status, lane_count)) { | |
547 | analogix_dp_reduce_link_rate(dp); | |
548 | return -EIO; | |
549 | } | |
550 | ||
0d97ad03 TV |
551 | retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1, |
552 | adjust_request, 2); | |
553 | if (retval < 0) | |
3424e3a4 YY |
554 | return retval; |
555 | ||
0d97ad03 TV |
556 | retval = drm_dp_dpcd_readb(&dp->aux, DP_LANE_ALIGN_STATUS_UPDATED, |
557 | &link_align); | |
558 | if (retval < 0) | |
3424e3a4 YY |
559 | return retval; |
560 | ||
561 | analogix_dp_get_adjust_training_lane(dp, adjust_request); | |
562 | ||
563 | if (!analogix_dp_channel_eq_ok(link_status, link_align, lane_count)) { | |
564 | /* traing pattern Set to Normal */ | |
565 | analogix_dp_training_pattern_dis(dp); | |
566 | ||
567 | dev_info(dp->dev, "Link Training success!\n"); | |
568 | ||
569 | analogix_dp_get_link_bandwidth(dp, ®); | |
570 | dp->link_train.link_rate = reg; | |
571 | dev_dbg(dp->dev, "final bandwidth = %.2x\n", | |
572 | dp->link_train.link_rate); | |
573 | ||
574 | analogix_dp_get_lane_count(dp, ®); | |
575 | dp->link_train.lane_count = reg; | |
576 | dev_dbg(dp->dev, "final lane count = %.2x\n", | |
577 | dp->link_train.lane_count); | |
578 | ||
579 | /* set enhanced mode if available */ | |
580 | analogix_dp_set_enhanced_mode(dp); | |
581 | dp->link_train.lt_state = FINISHED; | |
582 | ||
583 | return 0; | |
584 | } | |
585 | ||
586 | /* not all locked */ | |
587 | dp->link_train.eq_loop++; | |
588 | ||
589 | if (dp->link_train.eq_loop > MAX_EQ_LOOP) { | |
590 | dev_err(dp->dev, "EQ Max loop\n"); | |
591 | analogix_dp_reduce_link_rate(dp); | |
592 | return -EIO; | |
593 | } | |
594 | ||
595 | for (lane = 0; lane < lane_count; lane++) | |
596 | analogix_dp_set_lane_link_training(dp, | |
597 | dp->link_train.training_lane[lane], lane); | |
598 | ||
0d97ad03 TV |
599 | retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, |
600 | dp->link_train.training_lane, lane_count); | |
601 | if (retval < 0) | |
602 | return retval; | |
3424e3a4 | 603 | |
0d97ad03 | 604 | return 0; |
3424e3a4 YY |
605 | } |
606 | ||
607 | static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp, | |
bcbb7033 | 608 | u8 *bandwidth) |
3424e3a4 YY |
609 | { |
610 | u8 data; | |
611 | ||
612 | /* | |
613 | * For DP rev.1.1, Maximum link rate of Main Link lanes | |
614 | * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps | |
40fc7ce7 YY |
615 | * For DP rev.1.2, Maximum link rate of Main Link lanes |
616 | * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps, 0x14 = 5.4Gbps | |
3424e3a4 | 617 | */ |
0d97ad03 | 618 | drm_dp_dpcd_readb(&dp->aux, DP_MAX_LINK_RATE, &data); |
3424e3a4 YY |
619 | *bandwidth = data; |
620 | } | |
621 | ||
622 | static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp, | |
bcbb7033 | 623 | u8 *lane_count) |
3424e3a4 YY |
624 | { |
625 | u8 data; | |
626 | ||
627 | /* | |
628 | * For DP rev.1.1, Maximum number of Main Link lanes | |
629 | * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes | |
630 | */ | |
0d97ad03 | 631 | drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data); |
3424e3a4 YY |
632 | *lane_count = DPCD_MAX_LANE_COUNT(data); |
633 | } | |
634 | ||
635 | static void analogix_dp_init_training(struct analogix_dp_device *dp, | |
bcbb7033 | 636 | enum link_lane_count_type max_lane, |
40fc7ce7 | 637 | int max_rate) |
3424e3a4 YY |
638 | { |
639 | /* | |
640 | * MACRO_RST must be applied after the PLL_LOCK to avoid | |
641 | * the DP inter pair skew issue for at least 10 us | |
642 | */ | |
643 | analogix_dp_reset_macro(dp); | |
644 | ||
645 | /* Initialize by reading RX's DPCD */ | |
646 | analogix_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate); | |
647 | analogix_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count); | |
648 | ||
40fc7ce7 YY |
649 | if ((dp->link_train.link_rate != DP_LINK_BW_1_62) && |
650 | (dp->link_train.link_rate != DP_LINK_BW_2_7) && | |
651 | (dp->link_train.link_rate != DP_LINK_BW_5_4)) { | |
3424e3a4 YY |
652 | dev_err(dp->dev, "Rx Max Link Rate is abnormal :%x !\n", |
653 | dp->link_train.link_rate); | |
40fc7ce7 | 654 | dp->link_train.link_rate = DP_LINK_BW_1_62; |
3424e3a4 YY |
655 | } |
656 | ||
657 | if (dp->link_train.lane_count == 0) { | |
658 | dev_err(dp->dev, "Rx Max Lane count is abnormal :%x !\n", | |
659 | dp->link_train.lane_count); | |
660 | dp->link_train.lane_count = (u8)LANE_COUNT1; | |
661 | } | |
662 | ||
663 | /* Setup TX lane count & rate */ | |
664 | if (dp->link_train.lane_count > max_lane) | |
665 | dp->link_train.lane_count = max_lane; | |
666 | if (dp->link_train.link_rate > max_rate) | |
667 | dp->link_train.link_rate = max_rate; | |
668 | ||
669 | /* All DP analog module power up */ | |
670 | analogix_dp_set_analog_power_down(dp, POWER_ALL, 0); | |
671 | } | |
672 | ||
673 | static int analogix_dp_sw_link_training(struct analogix_dp_device *dp) | |
674 | { | |
675 | int retval = 0, training_finished = 0; | |
676 | ||
677 | dp->link_train.lt_state = START; | |
678 | ||
679 | /* Process here */ | |
680 | while (!retval && !training_finished) { | |
681 | switch (dp->link_train.lt_state) { | |
682 | case START: | |
683 | retval = analogix_dp_link_start(dp); | |
684 | if (retval) | |
685 | dev_err(dp->dev, "LT link start failed!\n"); | |
686 | break; | |
687 | case CLOCK_RECOVERY: | |
688 | retval = analogix_dp_process_clock_recovery(dp); | |
689 | if (retval) | |
690 | dev_err(dp->dev, "LT CR failed!\n"); | |
691 | break; | |
692 | case EQUALIZER_TRAINING: | |
693 | retval = analogix_dp_process_equalizer_training(dp); | |
694 | if (retval) | |
695 | dev_err(dp->dev, "LT EQ failed!\n"); | |
696 | break; | |
697 | case FINISHED: | |
698 | training_finished = 1; | |
699 | break; | |
700 | case FAILED: | |
701 | return -EREMOTEIO; | |
702 | } | |
703 | } | |
704 | if (retval) | |
705 | dev_err(dp->dev, "eDP link training failed (%d)\n", retval); | |
706 | ||
707 | return retval; | |
708 | } | |
709 | ||
710 | static int analogix_dp_set_link_train(struct analogix_dp_device *dp, | |
bcbb7033 | 711 | u32 count, u32 bwtype) |
3424e3a4 YY |
712 | { |
713 | int i; | |
714 | int retval; | |
715 | ||
716 | for (i = 0; i < DP_TIMEOUT_LOOP_COUNT; i++) { | |
717 | analogix_dp_init_training(dp, count, bwtype); | |
718 | retval = analogix_dp_sw_link_training(dp); | |
719 | if (retval == 0) | |
720 | break; | |
721 | ||
722 | usleep_range(100, 110); | |
723 | } | |
724 | ||
725 | return retval; | |
726 | } | |
727 | ||
728 | static int analogix_dp_config_video(struct analogix_dp_device *dp) | |
729 | { | |
730 | int retval = 0; | |
731 | int timeout_loop = 0; | |
732 | int done_count = 0; | |
733 | ||
734 | analogix_dp_config_video_slave_mode(dp); | |
735 | ||
736 | analogix_dp_set_video_color_format(dp); | |
737 | ||
738 | if (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { | |
739 | dev_err(dp->dev, "PLL is not locked yet.\n"); | |
740 | return -EINVAL; | |
741 | } | |
742 | ||
743 | for (;;) { | |
744 | timeout_loop++; | |
745 | if (analogix_dp_is_slave_video_stream_clock_on(dp) == 0) | |
746 | break; | |
bcbb7033 | 747 | if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) { |
3424e3a4 YY |
748 | dev_err(dp->dev, "Timeout of video streamclk ok\n"); |
749 | return -ETIMEDOUT; | |
750 | } | |
751 | ||
752 | usleep_range(1, 2); | |
753 | } | |
754 | ||
755 | /* Set to use the register calculated M/N video */ | |
756 | analogix_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0); | |
757 | ||
758 | /* For video bist, Video timing must be generated by register */ | |
759 | analogix_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE); | |
760 | ||
761 | /* Disable video mute */ | |
762 | analogix_dp_enable_video_mute(dp, 0); | |
763 | ||
764 | /* Configure video slave mode */ | |
765 | analogix_dp_enable_video_master(dp, 0); | |
766 | ||
767 | timeout_loop = 0; | |
768 | ||
769 | for (;;) { | |
770 | timeout_loop++; | |
771 | if (analogix_dp_is_video_stream_on(dp) == 0) { | |
772 | done_count++; | |
773 | if (done_count > 10) | |
774 | break; | |
775 | } else if (done_count) { | |
776 | done_count = 0; | |
777 | } | |
bcbb7033 | 778 | if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) { |
3424e3a4 YY |
779 | dev_err(dp->dev, "Timeout of video streamclk ok\n"); |
780 | return -ETIMEDOUT; | |
781 | } | |
782 | ||
783 | usleep_range(1000, 1001); | |
784 | } | |
785 | ||
786 | if (retval != 0) | |
787 | dev_err(dp->dev, "Video stream is not detected!\n"); | |
788 | ||
789 | return retval; | |
790 | } | |
791 | ||
bcbb7033 YY |
792 | static void analogix_dp_enable_scramble(struct analogix_dp_device *dp, |
793 | bool enable) | |
3424e3a4 YY |
794 | { |
795 | u8 data; | |
796 | ||
797 | if (enable) { | |
798 | analogix_dp_enable_scrambling(dp); | |
799 | ||
0d97ad03 TV |
800 | drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data); |
801 | drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, | |
802 | (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE)); | |
3424e3a4 YY |
803 | } else { |
804 | analogix_dp_disable_scrambling(dp); | |
805 | ||
0d97ad03 TV |
806 | drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data); |
807 | drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, | |
808 | (u8)(data | DP_LINK_SCRAMBLING_DISABLE)); | |
3424e3a4 YY |
809 | } |
810 | } | |
811 | ||
7b4b7a8d | 812 | static irqreturn_t analogix_dp_hardirq(int irq, void *arg) |
3424e3a4 YY |
813 | { |
814 | struct analogix_dp_device *dp = arg; | |
7b4b7a8d | 815 | irqreturn_t ret = IRQ_NONE; |
3424e3a4 YY |
816 | enum dp_irq_type irq_type; |
817 | ||
818 | irq_type = analogix_dp_get_irq_type(dp); | |
7b4b7a8d YY |
819 | if (irq_type != DP_IRQ_TYPE_UNKNOWN) { |
820 | analogix_dp_mute_hpd_interrupt(dp); | |
821 | ret = IRQ_WAKE_THREAD; | |
3424e3a4 | 822 | } |
7b4b7a8d YY |
823 | |
824 | return ret; | |
3424e3a4 YY |
825 | } |
826 | ||
7b4b7a8d | 827 | static irqreturn_t analogix_dp_irq_thread(int irq, void *arg) |
3424e3a4 | 828 | { |
7b4b7a8d YY |
829 | struct analogix_dp_device *dp = arg; |
830 | enum dp_irq_type irq_type; | |
831 | ||
832 | irq_type = analogix_dp_get_irq_type(dp); | |
833 | if (irq_type & DP_IRQ_TYPE_HP_CABLE_IN || | |
834 | irq_type & DP_IRQ_TYPE_HP_CABLE_OUT) { | |
835 | dev_dbg(dp->dev, "Detected cable status changed!\n"); | |
836 | if (dp->drm_dev) | |
837 | drm_helper_hpd_irq_event(dp->drm_dev); | |
838 | } | |
3424e3a4 | 839 | |
7b4b7a8d YY |
840 | if (irq_type != DP_IRQ_TYPE_UNKNOWN) { |
841 | analogix_dp_clear_hotplug_interrupts(dp); | |
842 | analogix_dp_unmute_hpd_interrupt(dp); | |
843 | } | |
3424e3a4 | 844 | |
7b4b7a8d | 845 | return IRQ_HANDLED; |
3424e3a4 YY |
846 | } |
847 | ||
848 | static void analogix_dp_commit(struct analogix_dp_device *dp) | |
849 | { | |
850 | int ret; | |
851 | ||
852 | /* Keep the panel disabled while we configure video */ | |
853 | if (dp->plat_data->panel) { | |
854 | if (drm_panel_disable(dp->plat_data->panel)) | |
855 | DRM_ERROR("failed to disable the panel\n"); | |
856 | } | |
857 | ||
0d0abd89 YY |
858 | ret = analogix_dp_set_link_train(dp, dp->video_info.max_lane_count, |
859 | dp->video_info.max_link_rate); | |
3424e3a4 YY |
860 | if (ret) { |
861 | dev_err(dp->dev, "unable to do link train\n"); | |
862 | return; | |
863 | } | |
864 | ||
865 | analogix_dp_enable_scramble(dp, 1); | |
866 | analogix_dp_enable_rx_to_enhanced_mode(dp, 1); | |
867 | analogix_dp_enable_enhanced_mode(dp, 1); | |
868 | ||
3424e3a4 YY |
869 | analogix_dp_init_video(dp); |
870 | ret = analogix_dp_config_video(dp); | |
871 | if (ret) | |
872 | dev_err(dp->dev, "unable to config video\n"); | |
873 | ||
874 | /* Safe to enable the panel now */ | |
875 | if (dp->plat_data->panel) { | |
876 | if (drm_panel_enable(dp->plat_data->panel)) | |
877 | DRM_ERROR("failed to enable the panel\n"); | |
878 | } | |
879 | ||
880 | /* Enable video */ | |
881 | analogix_dp_start_video(dp); | |
5b3f84f2 YY |
882 | |
883 | dp->psr_support = analogix_dp_detect_sink_psr(dp); | |
884 | if (dp->psr_support) | |
885 | analogix_dp_enable_sink_psr(dp); | |
3424e3a4 YY |
886 | } |
887 | ||
0b8b059a SP |
888 | /* |
889 | * This function is a bit of a catch-all for panel preparation, hopefully | |
890 | * simplifying the logic of functions that need to prepare/unprepare the panel | |
891 | * below. | |
892 | * | |
893 | * If @prepare is true, this function will prepare the panel. Conversely, if it | |
894 | * is false, the panel will be unprepared. | |
895 | * | |
896 | * If @is_modeset_prepare is true, the function will disregard the current state | |
897 | * of the panel and either prepare/unprepare the panel based on @prepare. Once | |
898 | * it finishes, it will update dp->panel_is_modeset to reflect the current state | |
899 | * of the panel. | |
900 | */ | |
901 | static int analogix_dp_prepare_panel(struct analogix_dp_device *dp, | |
902 | bool prepare, bool is_modeset_prepare) | |
903 | { | |
904 | int ret = 0; | |
905 | ||
906 | if (!dp->plat_data->panel) | |
907 | return 0; | |
908 | ||
909 | mutex_lock(&dp->panel_lock); | |
910 | ||
911 | /* | |
912 | * Exit early if this is a temporary prepare/unprepare and we're already | |
913 | * modeset (since we neither want to prepare twice or unprepare early). | |
914 | */ | |
915 | if (dp->panel_is_modeset && !is_modeset_prepare) | |
916 | goto out; | |
917 | ||
918 | if (prepare) | |
919 | ret = drm_panel_prepare(dp->plat_data->panel); | |
920 | else | |
921 | ret = drm_panel_unprepare(dp->plat_data->panel); | |
922 | ||
923 | if (ret) | |
924 | goto out; | |
925 | ||
926 | if (is_modeset_prepare) | |
927 | dp->panel_is_modeset = prepare; | |
928 | ||
929 | out: | |
930 | mutex_unlock(&dp->panel_lock); | |
931 | return ret; | |
932 | } | |
933 | ||
089cfdd9 | 934 | static int analogix_dp_get_modes(struct drm_connector *connector) |
3424e3a4 YY |
935 | { |
936 | struct analogix_dp_device *dp = to_dp(connector); | |
0d97ad03 | 937 | struct edid *edid; |
0b8b059a SP |
938 | int ret, num_modes = 0; |
939 | ||
8c77e2c2 | 940 | if (dp->plat_data->panel) { |
3424e3a4 | 941 | num_modes += drm_panel_get_modes(dp->plat_data->panel); |
8c77e2c2 | 942 | } else { |
f2600d08 SP |
943 | ret = analogix_dp_prepare_panel(dp, true, false); |
944 | if (ret) { | |
945 | DRM_ERROR("Failed to prepare panel (%d)\n", ret); | |
946 | return 0; | |
947 | } | |
948 | ||
510353a6 | 949 | pm_runtime_get_sync(dp->dev); |
8c77e2c2 | 950 | edid = drm_get_edid(connector, &dp->aux.ddc); |
510353a6 | 951 | pm_runtime_put(dp->dev); |
8c77e2c2 SP |
952 | if (edid) { |
953 | drm_mode_connector_update_edid_property(&dp->connector, | |
954 | edid); | |
955 | num_modes += drm_add_edid_modes(&dp->connector, edid); | |
956 | kfree(edid); | |
957 | } | |
f2600d08 SP |
958 | |
959 | ret = analogix_dp_prepare_panel(dp, false, false); | |
960 | if (ret) | |
961 | DRM_ERROR("Failed to unprepare panel (%d)\n", ret); | |
8c77e2c2 | 962 | } |
3424e3a4 YY |
963 | |
964 | if (dp->plat_data->get_modes) | |
fcc150c5 | 965 | num_modes += dp->plat_data->get_modes(dp->plat_data, connector); |
3424e3a4 YY |
966 | |
967 | return num_modes; | |
968 | } | |
969 | ||
970 | static struct drm_encoder * | |
971 | analogix_dp_best_encoder(struct drm_connector *connector) | |
972 | { | |
973 | struct analogix_dp_device *dp = to_dp(connector); | |
974 | ||
975 | return dp->encoder; | |
976 | } | |
977 | ||
978 | static const struct drm_connector_helper_funcs analogix_dp_connector_helper_funcs = { | |
979 | .get_modes = analogix_dp_get_modes, | |
980 | .best_encoder = analogix_dp_best_encoder, | |
981 | }; | |
982 | ||
089cfdd9 | 983 | static enum drm_connector_status |
3424e3a4 YY |
984 | analogix_dp_detect(struct drm_connector *connector, bool force) |
985 | { | |
2b77a291 | 986 | struct analogix_dp_device *dp = to_dp(connector); |
0b8b059a SP |
987 | enum drm_connector_status status = connector_status_disconnected; |
988 | int ret; | |
2b77a291 | 989 | |
f2600d08 SP |
990 | if (dp->plat_data->panel) |
991 | return connector_status_connected; | |
992 | ||
0b8b059a SP |
993 | ret = analogix_dp_prepare_panel(dp, true, false); |
994 | if (ret) { | |
995 | DRM_ERROR("Failed to prepare panel (%d)\n", ret); | |
2b77a291 | 996 | return connector_status_disconnected; |
0b8b059a SP |
997 | } |
998 | ||
999 | if (!analogix_dp_detect_hpd(dp)) | |
1000 | status = connector_status_connected; | |
2b77a291 | 1001 | |
0b8b059a SP |
1002 | ret = analogix_dp_prepare_panel(dp, false, false); |
1003 | if (ret) | |
1004 | DRM_ERROR("Failed to unprepare panel (%d)\n", ret); | |
1005 | ||
1006 | return status; | |
3424e3a4 YY |
1007 | } |
1008 | ||
3424e3a4 | 1009 | static const struct drm_connector_funcs analogix_dp_connector_funcs = { |
3424e3a4 YY |
1010 | .fill_modes = drm_helper_probe_single_connector_modes, |
1011 | .detect = analogix_dp_detect, | |
fdd8326a | 1012 | .destroy = drm_connector_cleanup, |
3424e3a4 YY |
1013 | .reset = drm_atomic_helper_connector_reset, |
1014 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
1015 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
1016 | }; | |
1017 | ||
1018 | static int analogix_dp_bridge_attach(struct drm_bridge *bridge) | |
1019 | { | |
1020 | struct analogix_dp_device *dp = bridge->driver_private; | |
1021 | struct drm_encoder *encoder = dp->encoder; | |
1022 | struct drm_connector *connector = &dp->connector; | |
1023 | int ret; | |
1024 | ||
1025 | if (!bridge->encoder) { | |
1026 | DRM_ERROR("Parent encoder object not found"); | |
1027 | return -ENODEV; | |
1028 | } | |
1029 | ||
1030 | connector->polled = DRM_CONNECTOR_POLL_HPD; | |
1031 | ||
1032 | ret = drm_connector_init(dp->drm_dev, connector, | |
1033 | &analogix_dp_connector_funcs, | |
1034 | DRM_MODE_CONNECTOR_eDP); | |
1035 | if (ret) { | |
1036 | DRM_ERROR("Failed to initialize connector with drm\n"); | |
1037 | return ret; | |
1038 | } | |
1039 | ||
1040 | drm_connector_helper_add(connector, | |
1041 | &analogix_dp_connector_helper_funcs); | |
1042 | drm_mode_connector_attach_encoder(connector, encoder); | |
1043 | ||
1044 | /* | |
1045 | * NOTE: the connector registration is implemented in analogix | |
1046 | * platform driver, that to say connector would be exist after | |
1047 | * plat_data->attch return, that's why we record the connector | |
1048 | * point after plat attached. | |
1049 | */ | |
1050 | if (dp->plat_data->attach) { | |
1051 | ret = dp->plat_data->attach(dp->plat_data, bridge, connector); | |
1052 | if (ret) { | |
1053 | DRM_ERROR("Failed at platform attch func\n"); | |
1054 | return ret; | |
1055 | } | |
1056 | } | |
1057 | ||
1058 | if (dp->plat_data->panel) { | |
1059 | ret = drm_panel_attach(dp->plat_data->panel, &dp->connector); | |
1060 | if (ret) { | |
1061 | DRM_ERROR("Failed to attach panel\n"); | |
1062 | return ret; | |
1063 | } | |
1064 | } | |
1065 | ||
1066 | return 0; | |
1067 | } | |
1068 | ||
0b8b059a SP |
1069 | static void analogix_dp_bridge_pre_enable(struct drm_bridge *bridge) |
1070 | { | |
1071 | struct analogix_dp_device *dp = bridge->driver_private; | |
1072 | int ret; | |
1073 | ||
1074 | ret = analogix_dp_prepare_panel(dp, true, true); | |
1075 | if (ret) | |
1076 | DRM_ERROR("failed to setup the panel ret = %d\n", ret); | |
1077 | } | |
1078 | ||
3424e3a4 YY |
1079 | static void analogix_dp_bridge_enable(struct drm_bridge *bridge) |
1080 | { | |
1081 | struct analogix_dp_device *dp = bridge->driver_private; | |
1082 | ||
1083 | if (dp->dpms_mode == DRM_MODE_DPMS_ON) | |
1084 | return; | |
1085 | ||
1086 | pm_runtime_get_sync(dp->dev); | |
1087 | ||
3424e3a4 YY |
1088 | if (dp->plat_data->power_on) |
1089 | dp->plat_data->power_on(dp->plat_data); | |
1090 | ||
1091 | phy_power_on(dp->phy); | |
1092 | analogix_dp_init_dp(dp); | |
1093 | enable_irq(dp->irq); | |
1094 | analogix_dp_commit(dp); | |
1095 | ||
1096 | dp->dpms_mode = DRM_MODE_DPMS_ON; | |
1097 | } | |
1098 | ||
1099 | static void analogix_dp_bridge_disable(struct drm_bridge *bridge) | |
1100 | { | |
1101 | struct analogix_dp_device *dp = bridge->driver_private; | |
0b8b059a | 1102 | int ret; |
3424e3a4 YY |
1103 | |
1104 | if (dp->dpms_mode != DRM_MODE_DPMS_ON) | |
1105 | return; | |
1106 | ||
1107 | if (dp->plat_data->panel) { | |
1108 | if (drm_panel_disable(dp->plat_data->panel)) { | |
1109 | DRM_ERROR("failed to disable the panel\n"); | |
1110 | return; | |
1111 | } | |
1112 | } | |
1113 | ||
1114 | disable_irq(dp->irq); | |
3424e3a4 YY |
1115 | phy_power_off(dp->phy); |
1116 | ||
1117 | if (dp->plat_data->power_off) | |
1118 | dp->plat_data->power_off(dp->plat_data); | |
1119 | ||
3424e3a4 YY |
1120 | pm_runtime_put_sync(dp->dev); |
1121 | ||
0b8b059a SP |
1122 | ret = analogix_dp_prepare_panel(dp, false, true); |
1123 | if (ret) | |
1124 | DRM_ERROR("failed to setup the panel ret = %d\n", ret); | |
1125 | ||
3424e3a4 YY |
1126 | dp->dpms_mode = DRM_MODE_DPMS_OFF; |
1127 | } | |
1128 | ||
793ce4eb YY |
1129 | static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge, |
1130 | struct drm_display_mode *orig_mode, | |
1131 | struct drm_display_mode *mode) | |
1132 | { | |
1133 | struct analogix_dp_device *dp = bridge->driver_private; | |
1134 | struct drm_display_info *display_info = &dp->connector.display_info; | |
1135 | struct video_info *video = &dp->video_info; | |
1136 | struct device_node *dp_node = dp->dev->of_node; | |
1137 | int vic; | |
1138 | ||
1139 | /* Input video interlaces & hsync pol & vsync pol */ | |
1140 | video->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); | |
1141 | video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); | |
1142 | video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); | |
1143 | ||
1144 | /* Input video dynamic_range & colorimetry */ | |
1145 | vic = drm_match_cea_mode(mode); | |
1146 | if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) || | |
1147 | (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) { | |
1148 | video->dynamic_range = CEA; | |
1149 | video->ycbcr_coeff = COLOR_YCBCR601; | |
1150 | } else if (vic) { | |
1151 | video->dynamic_range = CEA; | |
1152 | video->ycbcr_coeff = COLOR_YCBCR709; | |
1153 | } else { | |
1154 | video->dynamic_range = VESA; | |
1155 | video->ycbcr_coeff = COLOR_YCBCR709; | |
1156 | } | |
1157 | ||
1158 | /* Input vide bpc and color_formats */ | |
1159 | switch (display_info->bpc) { | |
1160 | case 12: | |
1161 | video->color_depth = COLOR_12; | |
1162 | break; | |
1163 | case 10: | |
1164 | video->color_depth = COLOR_10; | |
1165 | break; | |
1166 | case 8: | |
1167 | video->color_depth = COLOR_8; | |
1168 | break; | |
1169 | case 6: | |
1170 | video->color_depth = COLOR_6; | |
1171 | break; | |
1172 | default: | |
1173 | video->color_depth = COLOR_8; | |
1174 | break; | |
1175 | } | |
1176 | if (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB444) | |
1177 | video->color_space = COLOR_YCBCR444; | |
1178 | else if (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) | |
1179 | video->color_space = COLOR_YCBCR422; | |
1180 | else if (display_info->color_formats & DRM_COLOR_FORMAT_RGB444) | |
1181 | video->color_space = COLOR_RGB; | |
1182 | else | |
1183 | video->color_space = COLOR_RGB; | |
1184 | ||
1185 | /* | |
1186 | * NOTE: those property parsing code is used for providing backward | |
1187 | * compatibility for samsung platform. | |
1188 | * Due to we used the "of_property_read_u32" interfaces, when this | |
1189 | * property isn't present, the "video_info" can keep the original | |
1190 | * values and wouldn't be modified. | |
1191 | */ | |
1192 | of_property_read_u32(dp_node, "samsung,color-space", | |
1193 | &video->color_space); | |
1194 | of_property_read_u32(dp_node, "samsung,dynamic-range", | |
1195 | &video->dynamic_range); | |
1196 | of_property_read_u32(dp_node, "samsung,ycbcr-coeff", | |
1197 | &video->ycbcr_coeff); | |
1198 | of_property_read_u32(dp_node, "samsung,color-depth", | |
1199 | &video->color_depth); | |
1200 | if (of_property_read_bool(dp_node, "hsync-active-high")) | |
1201 | video->h_sync_polarity = true; | |
1202 | if (of_property_read_bool(dp_node, "vsync-active-high")) | |
1203 | video->v_sync_polarity = true; | |
1204 | if (of_property_read_bool(dp_node, "interlaced")) | |
1205 | video->interlaced = true; | |
1206 | } | |
1207 | ||
3424e3a4 YY |
1208 | static void analogix_dp_bridge_nop(struct drm_bridge *bridge) |
1209 | { | |
1210 | /* do nothing */ | |
1211 | } | |
1212 | ||
1213 | static const struct drm_bridge_funcs analogix_dp_bridge_funcs = { | |
0b8b059a | 1214 | .pre_enable = analogix_dp_bridge_pre_enable, |
3424e3a4 YY |
1215 | .enable = analogix_dp_bridge_enable, |
1216 | .disable = analogix_dp_bridge_disable, | |
3424e3a4 | 1217 | .post_disable = analogix_dp_bridge_nop, |
793ce4eb | 1218 | .mode_set = analogix_dp_bridge_mode_set, |
3424e3a4 YY |
1219 | .attach = analogix_dp_bridge_attach, |
1220 | }; | |
1221 | ||
1222 | static int analogix_dp_create_bridge(struct drm_device *drm_dev, | |
1223 | struct analogix_dp_device *dp) | |
1224 | { | |
1225 | struct drm_bridge *bridge; | |
1226 | int ret; | |
1227 | ||
1228 | bridge = devm_kzalloc(drm_dev->dev, sizeof(*bridge), GFP_KERNEL); | |
1229 | if (!bridge) { | |
1230 | DRM_ERROR("failed to allocate for drm bridge\n"); | |
1231 | return -ENOMEM; | |
1232 | } | |
1233 | ||
1234 | dp->bridge = bridge; | |
1235 | ||
3424e3a4 | 1236 | bridge->driver_private = dp; |
3424e3a4 YY |
1237 | bridge->funcs = &analogix_dp_bridge_funcs; |
1238 | ||
3bb80f24 | 1239 | ret = drm_bridge_attach(dp->encoder, bridge, NULL); |
3424e3a4 YY |
1240 | if (ret) { |
1241 | DRM_ERROR("failed to attach drm bridge\n"); | |
1242 | return -EINVAL; | |
1243 | } | |
1244 | ||
1245 | return 0; | |
1246 | } | |
1247 | ||
793ce4eb | 1248 | static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp) |
3424e3a4 | 1249 | { |
793ce4eb YY |
1250 | struct device_node *dp_node = dp->dev->of_node; |
1251 | struct video_info *video_info = &dp->video_info; | |
3424e3a4 | 1252 | |
0d0abd89 YY |
1253 | switch (dp->plat_data->dev_type) { |
1254 | case RK3288_DP: | |
82872e42 | 1255 | case RK3399_EDP: |
0d0abd89 YY |
1256 | /* |
1257 | * Like Rk3288 DisplayPort TRM indicate that "Main link | |
1258 | * containing 4 physical lanes of 2.7/1.62 Gbps/lane". | |
1259 | */ | |
1260 | video_info->max_link_rate = 0x0A; | |
1261 | video_info->max_lane_count = 0x04; | |
1262 | break; | |
1263 | case EXYNOS_DP: | |
1264 | /* | |
1265 | * NOTE: those property parseing code is used for | |
1266 | * providing backward compatibility for samsung platform. | |
1267 | */ | |
1268 | of_property_read_u32(dp_node, "samsung,link-rate", | |
1269 | &video_info->max_link_rate); | |
1270 | of_property_read_u32(dp_node, "samsung,lane-count", | |
1271 | &video_info->max_lane_count); | |
1272 | break; | |
3424e3a4 YY |
1273 | } |
1274 | ||
793ce4eb | 1275 | return 0; |
3424e3a4 YY |
1276 | } |
1277 | ||
0d97ad03 TV |
1278 | static ssize_t analogix_dpaux_transfer(struct drm_dp_aux *aux, |
1279 | struct drm_dp_aux_msg *msg) | |
1280 | { | |
1281 | struct analogix_dp_device *dp = to_dp(aux); | |
1282 | ||
1283 | return analogix_dp_transfer(dp, msg); | |
1284 | } | |
1285 | ||
3424e3a4 YY |
1286 | int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, |
1287 | struct analogix_dp_plat_data *plat_data) | |
1288 | { | |
1289 | struct platform_device *pdev = to_platform_device(dev); | |
1290 | struct analogix_dp_device *dp; | |
1291 | struct resource *res; | |
1292 | unsigned int irq_flags; | |
1293 | int ret; | |
1294 | ||
1295 | if (!plat_data) { | |
1296 | dev_err(dev, "Invalided input plat_data\n"); | |
1297 | return -EINVAL; | |
1298 | } | |
1299 | ||
1300 | dp = devm_kzalloc(dev, sizeof(struct analogix_dp_device), GFP_KERNEL); | |
1301 | if (!dp) | |
1302 | return -ENOMEM; | |
1303 | ||
1304 | dev_set_drvdata(dev, dp); | |
1305 | ||
1306 | dp->dev = &pdev->dev; | |
1307 | dp->dpms_mode = DRM_MODE_DPMS_OFF; | |
1308 | ||
0b8b059a SP |
1309 | mutex_init(&dp->panel_lock); |
1310 | dp->panel_is_modeset = false; | |
1311 | ||
3424e3a4 YY |
1312 | /* |
1313 | * platform dp driver need containor_of the plat_data to get | |
1314 | * the driver private data, so we need to store the point of | |
1315 | * plat_data, not the context of plat_data. | |
1316 | */ | |
1317 | dp->plat_data = plat_data; | |
1318 | ||
793ce4eb YY |
1319 | ret = analogix_dp_dt_parse_pdata(dp); |
1320 | if (ret) | |
1321 | return ret; | |
3424e3a4 YY |
1322 | |
1323 | dp->phy = devm_phy_get(dp->dev, "dp"); | |
1324 | if (IS_ERR(dp->phy)) { | |
1325 | dev_err(dp->dev, "no DP phy configured\n"); | |
1326 | ret = PTR_ERR(dp->phy); | |
1327 | if (ret) { | |
1328 | /* | |
1329 | * phy itself is not enabled, so we can move forward | |
1330 | * assigning NULL to phy pointer. | |
1331 | */ | |
1332 | if (ret == -ENOSYS || ret == -ENODEV) | |
1333 | dp->phy = NULL; | |
1334 | else | |
1335 | return ret; | |
1336 | } | |
1337 | } | |
1338 | ||
1339 | dp->clock = devm_clk_get(&pdev->dev, "dp"); | |
1340 | if (IS_ERR(dp->clock)) { | |
1341 | dev_err(&pdev->dev, "failed to get clock\n"); | |
1342 | return PTR_ERR(dp->clock); | |
1343 | } | |
1344 | ||
1345 | clk_prepare_enable(dp->clock); | |
1346 | ||
1347 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
1348 | ||
1349 | dp->reg_base = devm_ioremap_resource(&pdev->dev, res); | |
1350 | if (IS_ERR(dp->reg_base)) | |
1351 | return PTR_ERR(dp->reg_base); | |
1352 | ||
5cff007c YY |
1353 | dp->force_hpd = of_property_read_bool(dev->of_node, "force-hpd"); |
1354 | ||
3424e3a4 YY |
1355 | dp->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpios", 0); |
1356 | if (!gpio_is_valid(dp->hpd_gpio)) | |
1357 | dp->hpd_gpio = of_get_named_gpio(dev->of_node, | |
1358 | "samsung,hpd-gpio", 0); | |
1359 | ||
1360 | if (gpio_is_valid(dp->hpd_gpio)) { | |
1361 | /* | |
1362 | * Set up the hotplug GPIO from the device tree as an interrupt. | |
1363 | * Simply specifying a different interrupt in the device tree | |
1364 | * doesn't work since we handle hotplug rather differently when | |
1365 | * using a GPIO. We also need the actual GPIO specifier so | |
1366 | * that we can get the current state of the GPIO. | |
1367 | */ | |
1368 | ret = devm_gpio_request_one(&pdev->dev, dp->hpd_gpio, GPIOF_IN, | |
1369 | "hpd_gpio"); | |
1370 | if (ret) { | |
1371 | dev_err(&pdev->dev, "failed to get hpd gpio\n"); | |
1372 | return ret; | |
1373 | } | |
1374 | dp->irq = gpio_to_irq(dp->hpd_gpio); | |
1375 | irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; | |
1376 | } else { | |
1377 | dp->hpd_gpio = -ENODEV; | |
1378 | dp->irq = platform_get_irq(pdev, 0); | |
1379 | irq_flags = 0; | |
1380 | } | |
1381 | ||
1382 | if (dp->irq == -ENXIO) { | |
1383 | dev_err(&pdev->dev, "failed to get irq\n"); | |
1384 | return -ENODEV; | |
1385 | } | |
1386 | ||
3424e3a4 YY |
1387 | pm_runtime_enable(dev); |
1388 | ||
f0a8b49c | 1389 | pm_runtime_get_sync(dev); |
2b77a291 YY |
1390 | phy_power_on(dp->phy); |
1391 | ||
398a3995 YY |
1392 | analogix_dp_init_dp(dp); |
1393 | ||
7b4b7a8d YY |
1394 | ret = devm_request_threaded_irq(&pdev->dev, dp->irq, |
1395 | analogix_dp_hardirq, | |
1396 | analogix_dp_irq_thread, | |
1397 | irq_flags, "analogix-dp", dp); | |
3424e3a4 YY |
1398 | if (ret) { |
1399 | dev_err(&pdev->dev, "failed to request irq\n"); | |
1400 | goto err_disable_pm_runtime; | |
1401 | } | |
1402 | disable_irq(dp->irq); | |
1403 | ||
1404 | dp->drm_dev = drm_dev; | |
1405 | dp->encoder = dp->plat_data->encoder; | |
1406 | ||
0d97ad03 TV |
1407 | dp->aux.name = "DP-AUX"; |
1408 | dp->aux.transfer = analogix_dpaux_transfer; | |
1409 | dp->aux.dev = &pdev->dev; | |
1410 | ||
1411 | ret = drm_dp_aux_register(&dp->aux); | |
1412 | if (ret) | |
1413 | goto err_disable_pm_runtime; | |
1414 | ||
3424e3a4 YY |
1415 | ret = analogix_dp_create_bridge(drm_dev, dp); |
1416 | if (ret) { | |
1417 | DRM_ERROR("failed to create bridge (%d)\n", ret); | |
1418 | drm_encoder_cleanup(dp->encoder); | |
1419 | goto err_disable_pm_runtime; | |
1420 | } | |
1421 | ||
f0a8b49c MS |
1422 | phy_power_off(dp->phy); |
1423 | pm_runtime_put(dev); | |
1424 | ||
3424e3a4 YY |
1425 | return 0; |
1426 | ||
1427 | err_disable_pm_runtime: | |
f0a8b49c MS |
1428 | |
1429 | phy_power_off(dp->phy); | |
1430 | pm_runtime_put(dev); | |
3424e3a4 YY |
1431 | pm_runtime_disable(dev); |
1432 | ||
1433 | return ret; | |
1434 | } | |
1435 | EXPORT_SYMBOL_GPL(analogix_dp_bind); | |
1436 | ||
1437 | void analogix_dp_unbind(struct device *dev, struct device *master, | |
1438 | void *data) | |
1439 | { | |
1440 | struct analogix_dp_device *dp = dev_get_drvdata(dev); | |
1441 | ||
1442 | analogix_dp_bridge_disable(dp->bridge); | |
37e04877 JC |
1443 | dp->connector.funcs->destroy(&dp->connector); |
1444 | dp->encoder->funcs->destroy(dp->encoder); | |
2b77a291 YY |
1445 | |
1446 | if (dp->plat_data->panel) { | |
1447 | if (drm_panel_unprepare(dp->plat_data->panel)) | |
1448 | DRM_ERROR("failed to turnoff the panel\n"); | |
18aea7db JC |
1449 | if (drm_panel_detach(dp->plat_data->panel)) |
1450 | DRM_ERROR("failed to detach the panel\n"); | |
2b77a291 YY |
1451 | } |
1452 | ||
7b017a58 | 1453 | drm_dp_aux_unregister(&dp->aux); |
3424e3a4 | 1454 | pm_runtime_disable(dev); |
ede2fdf6 | 1455 | clk_disable_unprepare(dp->clock); |
3424e3a4 YY |
1456 | } |
1457 | EXPORT_SYMBOL_GPL(analogix_dp_unbind); | |
1458 | ||
1459 | #ifdef CONFIG_PM | |
1460 | int analogix_dp_suspend(struct device *dev) | |
1461 | { | |
1462 | struct analogix_dp_device *dp = dev_get_drvdata(dev); | |
1463 | ||
1464 | clk_disable_unprepare(dp->clock); | |
211f276e YY |
1465 | |
1466 | if (dp->plat_data->panel) { | |
1467 | if (drm_panel_unprepare(dp->plat_data->panel)) | |
1468 | DRM_ERROR("failed to turnoff the panel\n"); | |
1469 | } | |
1470 | ||
3424e3a4 YY |
1471 | return 0; |
1472 | } | |
1473 | EXPORT_SYMBOL_GPL(analogix_dp_suspend); | |
1474 | ||
1475 | int analogix_dp_resume(struct device *dev) | |
1476 | { | |
1477 | struct analogix_dp_device *dp = dev_get_drvdata(dev); | |
1478 | int ret; | |
1479 | ||
1480 | ret = clk_prepare_enable(dp->clock); | |
1481 | if (ret < 0) { | |
1482 | DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret); | |
1483 | return ret; | |
1484 | } | |
1485 | ||
211f276e YY |
1486 | if (dp->plat_data->panel) { |
1487 | if (drm_panel_prepare(dp->plat_data->panel)) { | |
1488 | DRM_ERROR("failed to setup the panel\n"); | |
1489 | return -EBUSY; | |
1490 | } | |
1491 | } | |
1492 | ||
3424e3a4 YY |
1493 | return 0; |
1494 | } | |
1495 | EXPORT_SYMBOL_GPL(analogix_dp_resume); | |
1496 | #endif | |
1497 | ||
737d6e33 TV |
1498 | int analogix_dp_start_crc(struct drm_connector *connector) |
1499 | { | |
1500 | struct analogix_dp_device *dp = to_dp(connector); | |
1501 | ||
1502 | if (!connector->state->crtc) { | |
1503 | DRM_ERROR("Connector %s doesn't currently have a CRTC.\n", | |
1504 | connector->name); | |
1505 | return -EINVAL; | |
1506 | } | |
1507 | ||
1508 | return drm_dp_start_crc(&dp->aux, connector->state->crtc); | |
1509 | } | |
1510 | EXPORT_SYMBOL_GPL(analogix_dp_start_crc); | |
1511 | ||
1512 | int analogix_dp_stop_crc(struct drm_connector *connector) | |
1513 | { | |
1514 | struct analogix_dp_device *dp = to_dp(connector); | |
1515 | ||
1516 | return drm_dp_stop_crc(&dp->aux); | |
1517 | } | |
1518 | EXPORT_SYMBOL_GPL(analogix_dp_stop_crc); | |
1519 | ||
3424e3a4 YY |
1520 | MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); |
1521 | MODULE_DESCRIPTION("Analogix DP Core Driver"); | |
1522 | MODULE_LICENSE("GPL v2"); |