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