Commit | Line | Data |
---|---|---|
e9474be4 JH |
1 | /* |
2 | * Samsung SoC DP (Display Port) 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/slab.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/clk.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/delay.h> | |
21 | ||
22 | #include <video/exynos_dp.h> | |
23 | ||
24 | #include <plat/cpu.h> | |
25 | ||
26 | #include "exynos_dp_core.h" | |
27 | ||
28 | static int exynos_dp_init_dp(struct exynos_dp_device *dp) | |
29 | { | |
30 | exynos_dp_reset(dp); | |
31 | ||
32 | /* SW defined function Normal operation */ | |
33 | exynos_dp_enable_sw_function(dp); | |
34 | ||
35 | exynos_dp_config_interrupt(dp); | |
36 | exynos_dp_init_analog_func(dp); | |
37 | ||
38 | exynos_dp_init_hpd(dp); | |
39 | exynos_dp_init_aux(dp); | |
40 | ||
41 | return 0; | |
42 | } | |
43 | ||
44 | static int exynos_dp_detect_hpd(struct exynos_dp_device *dp) | |
45 | { | |
46 | int timeout_loop = 0; | |
47 | ||
48 | exynos_dp_init_hpd(dp); | |
49 | ||
50 | udelay(200); | |
51 | ||
52 | while (exynos_dp_get_plug_in_status(dp) != 0) { | |
53 | timeout_loop++; | |
54 | if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { | |
55 | dev_err(dp->dev, "failed to get hpd plug status\n"); | |
56 | return -ETIMEDOUT; | |
57 | } | |
58 | udelay(10); | |
59 | } | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data) | |
65 | { | |
66 | int i; | |
67 | unsigned char sum = 0; | |
68 | ||
69 | for (i = 0; i < EDID_BLOCK_LENGTH; i++) | |
70 | sum = sum + edid_data[i]; | |
71 | ||
72 | return sum; | |
73 | } | |
74 | ||
75 | static int exynos_dp_read_edid(struct exynos_dp_device *dp) | |
76 | { | |
77 | unsigned char edid[EDID_BLOCK_LENGTH * 2]; | |
78 | unsigned int extend_block = 0; | |
79 | unsigned char sum; | |
80 | unsigned char test_vector; | |
81 | int retval; | |
82 | ||
83 | /* | |
84 | * EDID device address is 0x50. | |
85 | * However, if necessary, you must have set upper address | |
86 | * into E-EDID in I2C device, 0x30. | |
87 | */ | |
88 | ||
89 | /* Read Extension Flag, Number of 128-byte EDID extension blocks */ | |
90 | exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR, | |
91 | EDID_EXTENSION_FLAG, | |
92 | &extend_block); | |
93 | ||
94 | if (extend_block > 0) { | |
95 | dev_dbg(dp->dev, "EDID data includes a single extension!\n"); | |
96 | ||
97 | /* Read EDID data */ | |
98 | retval = exynos_dp_read_bytes_from_i2c(dp, I2C_EDID_DEVICE_ADDR, | |
99 | EDID_HEADER_PATTERN, | |
100 | EDID_BLOCK_LENGTH, | |
101 | &edid[EDID_HEADER_PATTERN]); | |
102 | if (retval != 0) { | |
103 | dev_err(dp->dev, "EDID Read failed!\n"); | |
104 | return -EIO; | |
105 | } | |
106 | sum = exynos_dp_calc_edid_check_sum(edid); | |
107 | if (sum != 0) { | |
108 | dev_err(dp->dev, "EDID bad checksum!\n"); | |
109 | return -EIO; | |
110 | } | |
111 | ||
112 | /* Read additional EDID data */ | |
113 | retval = exynos_dp_read_bytes_from_i2c(dp, | |
114 | I2C_EDID_DEVICE_ADDR, | |
115 | EDID_BLOCK_LENGTH, | |
116 | EDID_BLOCK_LENGTH, | |
117 | &edid[EDID_BLOCK_LENGTH]); | |
118 | if (retval != 0) { | |
119 | dev_err(dp->dev, "EDID Read failed!\n"); | |
120 | return -EIO; | |
121 | } | |
122 | sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); | |
123 | if (sum != 0) { | |
124 | dev_err(dp->dev, "EDID bad checksum!\n"); | |
125 | return -EIO; | |
126 | } | |
127 | ||
128 | exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TEST_REQUEST, | |
129 | &test_vector); | |
130 | if (test_vector & DPCD_TEST_EDID_READ) { | |
131 | exynos_dp_write_byte_to_dpcd(dp, | |
132 | DPCD_ADDR_TEST_EDID_CHECKSUM, | |
133 | edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); | |
134 | exynos_dp_write_byte_to_dpcd(dp, | |
135 | DPCD_ADDR_TEST_RESPONSE, | |
136 | DPCD_TEST_EDID_CHECKSUM_WRITE); | |
137 | } | |
138 | } else { | |
139 | dev_info(dp->dev, "EDID data does not include any extensions.\n"); | |
140 | ||
141 | /* Read EDID data */ | |
142 | retval = exynos_dp_read_bytes_from_i2c(dp, | |
143 | I2C_EDID_DEVICE_ADDR, | |
144 | EDID_HEADER_PATTERN, | |
145 | EDID_BLOCK_LENGTH, | |
146 | &edid[EDID_HEADER_PATTERN]); | |
147 | if (retval != 0) { | |
148 | dev_err(dp->dev, "EDID Read failed!\n"); | |
149 | return -EIO; | |
150 | } | |
151 | sum = exynos_dp_calc_edid_check_sum(edid); | |
152 | if (sum != 0) { | |
153 | dev_err(dp->dev, "EDID bad checksum!\n"); | |
154 | return -EIO; | |
155 | } | |
156 | ||
157 | exynos_dp_read_byte_from_dpcd(dp, | |
158 | DPCD_ADDR_TEST_REQUEST, | |
159 | &test_vector); | |
160 | if (test_vector & DPCD_TEST_EDID_READ) { | |
161 | exynos_dp_write_byte_to_dpcd(dp, | |
162 | DPCD_ADDR_TEST_EDID_CHECKSUM, | |
163 | edid[EDID_CHECKSUM]); | |
164 | exynos_dp_write_byte_to_dpcd(dp, | |
165 | DPCD_ADDR_TEST_RESPONSE, | |
166 | DPCD_TEST_EDID_CHECKSUM_WRITE); | |
167 | } | |
168 | } | |
169 | ||
170 | dev_err(dp->dev, "EDID Read success!\n"); | |
171 | return 0; | |
172 | } | |
173 | ||
174 | static int exynos_dp_handle_edid(struct exynos_dp_device *dp) | |
175 | { | |
176 | u8 buf[12]; | |
177 | int i; | |
178 | int retval; | |
179 | ||
180 | /* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */ | |
181 | exynos_dp_read_bytes_from_dpcd(dp, | |
182 | DPCD_ADDR_DPCD_REV, | |
183 | 12, buf); | |
184 | ||
185 | /* Read EDID */ | |
186 | for (i = 0; i < 3; i++) { | |
187 | retval = exynos_dp_read_edid(dp); | |
188 | if (retval == 0) | |
189 | break; | |
190 | } | |
191 | ||
192 | return retval; | |
193 | } | |
194 | ||
195 | static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp, | |
196 | bool enable) | |
197 | { | |
198 | u8 data; | |
199 | ||
200 | exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data); | |
201 | ||
202 | if (enable) | |
203 | exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, | |
204 | DPCD_ENHANCED_FRAME_EN | | |
205 | DPCD_LANE_COUNT_SET(data)); | |
206 | else | |
207 | exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, | |
208 | DPCD_LANE_COUNT_SET(data)); | |
209 | } | |
210 | ||
211 | static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp) | |
212 | { | |
213 | u8 data; | |
214 | int retval; | |
215 | ||
216 | exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); | |
217 | retval = DPCD_ENHANCED_FRAME_CAP(data); | |
218 | ||
219 | return retval; | |
220 | } | |
221 | ||
222 | static void exynos_dp_set_enhanced_mode(struct exynos_dp_device *dp) | |
223 | { | |
224 | u8 data; | |
225 | ||
226 | data = exynos_dp_is_enhanced_mode_available(dp); | |
227 | exynos_dp_enable_rx_to_enhanced_mode(dp, data); | |
228 | exynos_dp_enable_enhanced_mode(dp, data); | |
229 | } | |
230 | ||
231 | static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp) | |
232 | { | |
233 | exynos_dp_set_training_pattern(dp, DP_NONE); | |
234 | ||
235 | exynos_dp_write_byte_to_dpcd(dp, | |
236 | DPCD_ADDR_TRAINING_PATTERN_SET, | |
237 | DPCD_TRAINING_PATTERN_DISABLED); | |
238 | } | |
239 | ||
240 | static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp, | |
241 | int pre_emphasis, int lane) | |
242 | { | |
243 | switch (lane) { | |
244 | case 0: | |
245 | exynos_dp_set_lane0_pre_emphasis(dp, pre_emphasis); | |
246 | break; | |
247 | case 1: | |
248 | exynos_dp_set_lane1_pre_emphasis(dp, pre_emphasis); | |
249 | break; | |
250 | ||
251 | case 2: | |
252 | exynos_dp_set_lane2_pre_emphasis(dp, pre_emphasis); | |
253 | break; | |
254 | ||
255 | case 3: | |
256 | exynos_dp_set_lane3_pre_emphasis(dp, pre_emphasis); | |
257 | break; | |
258 | } | |
259 | } | |
260 | ||
261 | static void exynos_dp_link_start(struct exynos_dp_device *dp) | |
262 | { | |
263 | u8 buf[5]; | |
264 | int lane; | |
265 | int lane_count; | |
266 | ||
267 | lane_count = dp->link_train.lane_count; | |
268 | ||
269 | dp->link_train.lt_state = CLOCK_RECOVERY; | |
270 | dp->link_train.eq_loop = 0; | |
271 | ||
272 | for (lane = 0; lane < lane_count; lane++) | |
273 | dp->link_train.cr_loop[lane] = 0; | |
274 | ||
275 | /* Set sink to D0 (Sink Not Ready) mode. */ | |
276 | exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE, | |
277 | DPCD_SET_POWER_STATE_D0); | |
278 | ||
279 | /* Set link rate and count as you want to establish*/ | |
280 | exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate); | |
281 | exynos_dp_set_lane_count(dp, dp->link_train.lane_count); | |
282 | ||
283 | /* Setup RX configuration */ | |
284 | buf[0] = dp->link_train.link_rate; | |
285 | buf[1] = dp->link_train.lane_count; | |
286 | exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET, | |
287 | 2, buf); | |
288 | ||
289 | /* Set TX pre-emphasis to minimum */ | |
290 | for (lane = 0; lane < lane_count; lane++) | |
291 | exynos_dp_set_lane_lane_pre_emphasis(dp, | |
292 | PRE_EMPHASIS_LEVEL_0, lane); | |
293 | ||
294 | /* Set training pattern 1 */ | |
295 | exynos_dp_set_training_pattern(dp, TRAINING_PTN1); | |
296 | ||
297 | /* Set RX training pattern */ | |
298 | buf[0] = DPCD_SCRAMBLING_DISABLED | | |
299 | DPCD_TRAINING_PATTERN_1; | |
300 | exynos_dp_write_byte_to_dpcd(dp, | |
301 | DPCD_ADDR_TRAINING_PATTERN_SET, buf[0]); | |
302 | ||
303 | for (lane = 0; lane < lane_count; lane++) | |
304 | buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 | | |
305 | DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0; | |
306 | exynos_dp_write_bytes_to_dpcd(dp, | |
307 | DPCD_ADDR_TRAINING_PATTERN_SET, | |
308 | lane_count, buf); | |
309 | } | |
310 | ||
311 | static unsigned char exynos_dp_get_lane_status(u8 link_status[6], int lane) | |
312 | { | |
313 | int shift = (lane & 1) * 4; | |
314 | u8 link_value = link_status[lane>>1]; | |
315 | ||
316 | return (link_value >> shift) & 0xf; | |
317 | } | |
318 | ||
319 | static int exynos_dp_clock_recovery_ok(u8 link_status[6], int lane_count) | |
320 | { | |
321 | int lane; | |
322 | u8 lane_status; | |
323 | ||
324 | for (lane = 0; lane < lane_count; lane++) { | |
325 | lane_status = exynos_dp_get_lane_status(link_status, lane); | |
326 | if ((lane_status & DPCD_LANE_CR_DONE) == 0) | |
327 | return -EINVAL; | |
328 | } | |
329 | return 0; | |
330 | } | |
331 | ||
332 | static int exynos_dp_channel_eq_ok(u8 link_status[6], int lane_count) | |
333 | { | |
334 | int lane; | |
335 | u8 lane_align; | |
336 | u8 lane_status; | |
337 | ||
338 | lane_align = link_status[2]; | |
339 | if ((lane_align == DPCD_INTERLANE_ALIGN_DONE) == 0) | |
340 | return -EINVAL; | |
341 | ||
342 | for (lane = 0; lane < lane_count; lane++) { | |
343 | lane_status = exynos_dp_get_lane_status(link_status, lane); | |
344 | lane_status &= DPCD_CHANNEL_EQ_BITS; | |
345 | if (lane_status != DPCD_CHANNEL_EQ_BITS) | |
346 | return -EINVAL; | |
347 | } | |
348 | return 0; | |
349 | } | |
350 | ||
351 | static unsigned char exynos_dp_get_adjust_request_voltage(u8 adjust_request[2], | |
352 | int lane) | |
353 | { | |
354 | int shift = (lane & 1) * 4; | |
355 | u8 link_value = adjust_request[lane>>1]; | |
356 | ||
357 | return (link_value >> shift) & 0x3; | |
358 | } | |
359 | ||
360 | static unsigned char exynos_dp_get_adjust_request_pre_emphasis( | |
361 | u8 adjust_request[2], | |
362 | int lane) | |
363 | { | |
364 | int shift = (lane & 1) * 4; | |
365 | u8 link_value = adjust_request[lane>>1]; | |
366 | ||
367 | return ((link_value >> shift) & 0xc) >> 2; | |
368 | } | |
369 | ||
370 | static void exynos_dp_set_lane_link_training(struct exynos_dp_device *dp, | |
371 | u8 training_lane_set, int lane) | |
372 | { | |
373 | switch (lane) { | |
374 | case 0: | |
375 | exynos_dp_set_lane0_link_training(dp, training_lane_set); | |
376 | break; | |
377 | case 1: | |
378 | exynos_dp_set_lane1_link_training(dp, training_lane_set); | |
379 | break; | |
380 | ||
381 | case 2: | |
382 | exynos_dp_set_lane2_link_training(dp, training_lane_set); | |
383 | break; | |
384 | ||
385 | case 3: | |
386 | exynos_dp_set_lane3_link_training(dp, training_lane_set); | |
387 | break; | |
388 | } | |
389 | } | |
390 | ||
391 | static unsigned int exynos_dp_get_lane_link_training( | |
392 | struct exynos_dp_device *dp, | |
393 | int lane) | |
394 | { | |
395 | u32 reg; | |
396 | ||
397 | switch (lane) { | |
398 | case 0: | |
399 | reg = exynos_dp_get_lane0_link_training(dp); | |
400 | break; | |
401 | case 1: | |
402 | reg = exynos_dp_get_lane1_link_training(dp); | |
403 | break; | |
404 | case 2: | |
405 | reg = exynos_dp_get_lane2_link_training(dp); | |
406 | break; | |
407 | case 3: | |
408 | reg = exynos_dp_get_lane3_link_training(dp); | |
409 | break; | |
410 | } | |
411 | ||
412 | return reg; | |
413 | } | |
414 | ||
415 | static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp) | |
416 | { | |
417 | if (dp->link_train.link_rate == LINK_RATE_2_70GBPS) { | |
418 | /* set to reduced bit rate */ | |
419 | dp->link_train.link_rate = LINK_RATE_1_62GBPS; | |
420 | dev_err(dp->dev, "set to bandwidth %.2x\n", | |
421 | dp->link_train.link_rate); | |
422 | dp->link_train.lt_state = START; | |
423 | } else { | |
424 | exynos_dp_training_pattern_dis(dp); | |
425 | /* set enhanced mode if available */ | |
426 | exynos_dp_set_enhanced_mode(dp); | |
427 | dp->link_train.lt_state = FAILED; | |
428 | } | |
429 | } | |
430 | ||
431 | static void exynos_dp_get_adjust_train(struct exynos_dp_device *dp, | |
432 | u8 adjust_request[2]) | |
433 | { | |
434 | int lane; | |
435 | int lane_count; | |
436 | u8 voltage_swing; | |
437 | u8 pre_emphasis; | |
438 | u8 training_lane; | |
439 | ||
440 | lane_count = dp->link_train.lane_count; | |
441 | for (lane = 0; lane < lane_count; lane++) { | |
442 | voltage_swing = exynos_dp_get_adjust_request_voltage( | |
443 | adjust_request, lane); | |
444 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | |
445 | adjust_request, lane); | |
446 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | |
447 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | |
448 | ||
449 | if (voltage_swing == VOLTAGE_LEVEL_3 || | |
450 | pre_emphasis == PRE_EMPHASIS_LEVEL_3) { | |
451 | training_lane |= DPCD_MAX_SWING_REACHED; | |
452 | training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; | |
453 | } | |
454 | dp->link_train.training_lane[lane] = training_lane; | |
455 | } | |
456 | } | |
457 | ||
458 | static int exynos_dp_check_max_cr_loop(struct exynos_dp_device *dp, | |
459 | u8 voltage_swing) | |
460 | { | |
461 | int lane; | |
462 | int lane_count; | |
463 | ||
464 | lane_count = dp->link_train.lane_count; | |
465 | for (lane = 0; lane < lane_count; lane++) { | |
466 | if (voltage_swing == VOLTAGE_LEVEL_3 || | |
467 | dp->link_train.cr_loop[lane] == MAX_CR_LOOP) | |
468 | return -EINVAL; | |
469 | } | |
470 | return 0; | |
471 | } | |
472 | ||
473 | static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) | |
474 | { | |
475 | u8 data; | |
476 | u8 link_status[6]; | |
477 | int lane; | |
478 | int lane_count; | |
479 | u8 buf[5]; | |
480 | ||
481 | u8 *adjust_request; | |
482 | u8 voltage_swing; | |
483 | u8 pre_emphasis; | |
484 | u8 training_lane; | |
485 | ||
486 | udelay(100); | |
487 | ||
488 | exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, | |
489 | 6, link_status); | |
490 | lane_count = dp->link_train.lane_count; | |
491 | ||
492 | if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { | |
493 | /* set training pattern 2 for EQ */ | |
494 | exynos_dp_set_training_pattern(dp, TRAINING_PTN2); | |
495 | ||
496 | adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 | |
497 | - DPCD_ADDR_LANE0_1_STATUS); | |
498 | ||
499 | exynos_dp_get_adjust_train(dp, adjust_request); | |
500 | ||
501 | buf[0] = DPCD_SCRAMBLING_DISABLED | | |
502 | DPCD_TRAINING_PATTERN_2; | |
503 | exynos_dp_write_byte_to_dpcd(dp, | |
504 | DPCD_ADDR_TRAINING_LANE0_SET, | |
505 | buf[0]); | |
506 | ||
507 | for (lane = 0; lane < lane_count; lane++) { | |
508 | exynos_dp_set_lane_link_training(dp, | |
509 | dp->link_train.training_lane[lane], | |
510 | lane); | |
511 | buf[lane] = dp->link_train.training_lane[lane]; | |
512 | exynos_dp_write_byte_to_dpcd(dp, | |
513 | DPCD_ADDR_TRAINING_LANE0_SET + lane, | |
514 | buf[lane]); | |
515 | } | |
516 | dp->link_train.lt_state = EQUALIZER_TRAINING; | |
517 | } else { | |
518 | exynos_dp_read_byte_from_dpcd(dp, | |
519 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, | |
520 | &data); | |
521 | adjust_request[0] = data; | |
522 | ||
523 | exynos_dp_read_byte_from_dpcd(dp, | |
524 | DPCD_ADDR_ADJUST_REQUEST_LANE2_3, | |
525 | &data); | |
526 | adjust_request[1] = data; | |
527 | ||
528 | for (lane = 0; lane < lane_count; lane++) { | |
529 | training_lane = exynos_dp_get_lane_link_training( | |
530 | dp, lane); | |
531 | voltage_swing = exynos_dp_get_adjust_request_voltage( | |
532 | adjust_request, lane); | |
533 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | |
534 | adjust_request, lane); | |
535 | if ((DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing) && | |
536 | (DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis)) | |
537 | dp->link_train.cr_loop[lane]++; | |
538 | dp->link_train.training_lane[lane] = training_lane; | |
539 | } | |
540 | ||
541 | if (exynos_dp_check_max_cr_loop(dp, voltage_swing) != 0) { | |
542 | exynos_dp_reduce_link_rate(dp); | |
543 | } else { | |
544 | exynos_dp_get_adjust_train(dp, adjust_request); | |
545 | ||
546 | for (lane = 0; lane < lane_count; lane++) { | |
547 | exynos_dp_set_lane_link_training(dp, | |
548 | dp->link_train.training_lane[lane], | |
549 | lane); | |
550 | buf[lane] = dp->link_train.training_lane[lane]; | |
551 | exynos_dp_write_byte_to_dpcd(dp, | |
552 | DPCD_ADDR_TRAINING_LANE0_SET + lane, | |
553 | buf[lane]); | |
554 | } | |
555 | } | |
556 | } | |
557 | ||
558 | return 0; | |
559 | } | |
560 | ||
561 | static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) | |
562 | { | |
563 | u8 link_status[6]; | |
564 | int lane; | |
565 | int lane_count; | |
566 | u8 buf[5]; | |
567 | u32 reg; | |
568 | ||
569 | u8 *adjust_request; | |
570 | ||
571 | udelay(400); | |
572 | ||
573 | exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, | |
574 | 6, link_status); | |
575 | lane_count = dp->link_train.lane_count; | |
576 | ||
577 | if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { | |
578 | adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 | |
579 | - DPCD_ADDR_LANE0_1_STATUS); | |
580 | ||
581 | if (exynos_dp_channel_eq_ok(link_status, lane_count) == 0) { | |
582 | /* traing pattern Set to Normal */ | |
583 | exynos_dp_training_pattern_dis(dp); | |
584 | ||
585 | dev_info(dp->dev, "Link Training success!\n"); | |
586 | ||
587 | exynos_dp_get_link_bandwidth(dp, ®); | |
588 | dp->link_train.link_rate = reg; | |
589 | dev_dbg(dp->dev, "final bandwidth = %.2x\n", | |
590 | dp->link_train.link_rate); | |
591 | ||
592 | exynos_dp_get_lane_count(dp, ®); | |
593 | dp->link_train.lane_count = reg; | |
594 | dev_dbg(dp->dev, "final lane count = %.2x\n", | |
595 | dp->link_train.lane_count); | |
596 | /* set enhanced mode if available */ | |
597 | exynos_dp_set_enhanced_mode(dp); | |
598 | ||
599 | dp->link_train.lt_state = FINISHED; | |
600 | } else { | |
601 | /* not all locked */ | |
602 | dp->link_train.eq_loop++; | |
603 | ||
604 | if (dp->link_train.eq_loop > MAX_EQ_LOOP) { | |
605 | exynos_dp_reduce_link_rate(dp); | |
606 | } else { | |
607 | exynos_dp_get_adjust_train(dp, adjust_request); | |
608 | ||
609 | for (lane = 0; lane < lane_count; lane++) { | |
610 | exynos_dp_set_lane_link_training(dp, | |
611 | dp->link_train.training_lane[lane], | |
612 | lane); | |
613 | buf[lane] = dp->link_train.training_lane[lane]; | |
614 | exynos_dp_write_byte_to_dpcd(dp, | |
615 | DPCD_ADDR_TRAINING_LANE0_SET + lane, | |
616 | buf[lane]); | |
617 | } | |
618 | } | |
619 | } | |
620 | } else { | |
621 | exynos_dp_reduce_link_rate(dp); | |
622 | } | |
623 | ||
624 | return 0; | |
625 | } | |
626 | ||
627 | static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp, | |
628 | u8 *bandwidth) | |
629 | { | |
630 | u8 data; | |
631 | ||
632 | /* | |
633 | * For DP rev.1.1, Maximum link rate of Main Link lanes | |
634 | * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps | |
635 | */ | |
636 | exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data); | |
637 | *bandwidth = data; | |
638 | } | |
639 | ||
640 | static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp, | |
641 | u8 *lane_count) | |
642 | { | |
643 | u8 data; | |
644 | ||
645 | /* | |
646 | * For DP rev.1.1, Maximum number of Main Link lanes | |
647 | * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes | |
648 | */ | |
649 | exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); | |
650 | *lane_count = DPCD_MAX_LANE_COUNT(data); | |
651 | } | |
652 | ||
653 | static void exynos_dp_init_training(struct exynos_dp_device *dp, | |
654 | enum link_lane_count_type max_lane, | |
655 | enum link_rate_type max_rate) | |
656 | { | |
657 | /* | |
658 | * MACRO_RST must be applied after the PLL_LOCK to avoid | |
659 | * the DP inter pair skew issue for at least 10 us | |
660 | */ | |
661 | exynos_dp_reset_macro(dp); | |
662 | ||
663 | /* Initialize by reading RX's DPCD */ | |
664 | exynos_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate); | |
665 | exynos_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count); | |
666 | ||
667 | if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) && | |
668 | (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) { | |
669 | dev_err(dp->dev, "Rx Max Link Rate is abnormal :%x !\n", | |
670 | dp->link_train.link_rate); | |
671 | dp->link_train.link_rate = LINK_RATE_1_62GBPS; | |
672 | } | |
673 | ||
674 | if (dp->link_train.lane_count == 0) { | |
675 | dev_err(dp->dev, "Rx Max Lane count is abnormal :%x !\n", | |
676 | dp->link_train.lane_count); | |
677 | dp->link_train.lane_count = (u8)LANE_COUNT1; | |
678 | } | |
679 | ||
680 | /* Setup TX lane count & rate */ | |
681 | if (dp->link_train.lane_count > max_lane) | |
682 | dp->link_train.lane_count = max_lane; | |
683 | if (dp->link_train.link_rate > max_rate) | |
684 | dp->link_train.link_rate = max_rate; | |
685 | ||
686 | /* All DP analog module power up */ | |
687 | exynos_dp_set_analog_power_down(dp, POWER_ALL, 0); | |
688 | } | |
689 | ||
690 | static int exynos_dp_sw_link_training(struct exynos_dp_device *dp) | |
691 | { | |
692 | int retval = 0; | |
693 | int training_finished; | |
694 | ||
695 | /* Turn off unnecessary lane */ | |
696 | if (dp->link_train.lane_count == 1) | |
697 | exynos_dp_set_analog_power_down(dp, CH1_BLOCK, 1); | |
698 | ||
699 | training_finished = 0; | |
700 | ||
701 | dp->link_train.lt_state = START; | |
702 | ||
703 | /* Process here */ | |
704 | while (!training_finished) { | |
705 | switch (dp->link_train.lt_state) { | |
706 | case START: | |
707 | exynos_dp_link_start(dp); | |
708 | break; | |
709 | case CLOCK_RECOVERY: | |
710 | exynos_dp_process_clock_recovery(dp); | |
711 | break; | |
712 | case EQUALIZER_TRAINING: | |
713 | exynos_dp_process_equalizer_training(dp); | |
714 | break; | |
715 | case FINISHED: | |
716 | training_finished = 1; | |
717 | break; | |
718 | case FAILED: | |
719 | return -EREMOTEIO; | |
720 | } | |
721 | } | |
722 | ||
723 | return retval; | |
724 | } | |
725 | ||
726 | static int exynos_dp_set_link_train(struct exynos_dp_device *dp, | |
727 | u32 count, | |
728 | u32 bwtype) | |
729 | { | |
730 | int i; | |
731 | int retval; | |
732 | ||
733 | for (i = 0; i < DP_TIMEOUT_LOOP_COUNT; i++) { | |
734 | exynos_dp_init_training(dp, count, bwtype); | |
735 | retval = exynos_dp_sw_link_training(dp); | |
736 | if (retval == 0) | |
737 | break; | |
738 | ||
739 | udelay(100); | |
740 | } | |
741 | ||
742 | return retval; | |
743 | } | |
744 | ||
745 | static int exynos_dp_config_video(struct exynos_dp_device *dp, | |
746 | struct video_info *video_info) | |
747 | { | |
748 | int retval = 0; | |
749 | int timeout_loop = 0; | |
750 | int done_count = 0; | |
751 | ||
752 | exynos_dp_config_video_slave_mode(dp, video_info); | |
753 | ||
754 | exynos_dp_set_video_color_format(dp, video_info->color_depth, | |
755 | video_info->color_space, | |
756 | video_info->dynamic_range, | |
757 | video_info->ycbcr_coeff); | |
758 | ||
759 | if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { | |
760 | dev_err(dp->dev, "PLL is not locked yet.\n"); | |
761 | return -EINVAL; | |
762 | } | |
763 | ||
764 | for (;;) { | |
765 | timeout_loop++; | |
766 | if (exynos_dp_is_slave_video_stream_clock_on(dp) == 0) | |
767 | break; | |
768 | if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { | |
769 | dev_err(dp->dev, "Timeout of video streamclk ok\n"); | |
770 | return -ETIMEDOUT; | |
771 | } | |
772 | ||
773 | mdelay(100); | |
774 | } | |
775 | ||
776 | /* Set to use the register calculated M/N video */ | |
777 | exynos_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0); | |
778 | ||
779 | /* For video bist, Video timing must be generated by register */ | |
780 | exynos_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE); | |
781 | ||
782 | /* Disable video mute */ | |
783 | exynos_dp_enable_video_mute(dp, 0); | |
784 | ||
785 | /* Configure video slave mode */ | |
786 | exynos_dp_enable_video_master(dp, 0); | |
787 | ||
788 | /* Enable video */ | |
789 | exynos_dp_start_video(dp); | |
790 | ||
791 | timeout_loop = 0; | |
792 | ||
793 | for (;;) { | |
794 | timeout_loop++; | |
795 | if (exynos_dp_is_video_stream_on(dp) == 0) { | |
796 | done_count++; | |
797 | if (done_count > 10) | |
798 | break; | |
799 | } else if (done_count) { | |
800 | done_count = 0; | |
801 | } | |
802 | if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { | |
803 | dev_err(dp->dev, "Timeout of video streamclk ok\n"); | |
804 | return -ETIMEDOUT; | |
805 | } | |
806 | ||
807 | mdelay(100); | |
808 | } | |
809 | ||
810 | if (retval != 0) | |
811 | dev_err(dp->dev, "Video stream is not detected!\n"); | |
812 | ||
813 | return retval; | |
814 | } | |
815 | ||
816 | static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable) | |
817 | { | |
818 | u8 data; | |
819 | ||
820 | if (enable) { | |
821 | exynos_dp_enable_scrambling(dp); | |
822 | ||
823 | exynos_dp_read_byte_from_dpcd(dp, | |
824 | DPCD_ADDR_TRAINING_PATTERN_SET, | |
825 | &data); | |
826 | exynos_dp_write_byte_to_dpcd(dp, | |
827 | DPCD_ADDR_TRAINING_PATTERN_SET, | |
828 | (u8)(data & ~DPCD_SCRAMBLING_DISABLED)); | |
829 | } else { | |
830 | exynos_dp_disable_scrambling(dp); | |
831 | ||
832 | exynos_dp_read_byte_from_dpcd(dp, | |
833 | DPCD_ADDR_TRAINING_PATTERN_SET, | |
834 | &data); | |
835 | exynos_dp_write_byte_to_dpcd(dp, | |
836 | DPCD_ADDR_TRAINING_PATTERN_SET, | |
837 | (u8)(data | DPCD_SCRAMBLING_DISABLED)); | |
838 | } | |
839 | } | |
840 | ||
841 | static irqreturn_t exynos_dp_irq_handler(int irq, void *arg) | |
842 | { | |
843 | struct exynos_dp_device *dp = arg; | |
844 | ||
845 | dev_err(dp->dev, "exynos_dp_irq_handler\n"); | |
846 | return IRQ_HANDLED; | |
847 | } | |
848 | ||
849 | static int __devinit exynos_dp_probe(struct platform_device *pdev) | |
850 | { | |
851 | struct resource *res; | |
852 | struct exynos_dp_device *dp; | |
853 | struct exynos_dp_platdata *pdata; | |
854 | ||
855 | int ret = 0; | |
856 | ||
857 | pdata = pdev->dev.platform_data; | |
858 | if (!pdata) { | |
859 | dev_err(&pdev->dev, "no platform data\n"); | |
860 | return -EINVAL; | |
861 | } | |
862 | ||
863 | dp = kzalloc(sizeof(struct exynos_dp_device), GFP_KERNEL); | |
864 | if (!dp) { | |
865 | dev_err(&pdev->dev, "no memory for device data\n"); | |
866 | return -ENOMEM; | |
867 | } | |
868 | ||
869 | dp->dev = &pdev->dev; | |
870 | ||
871 | dp->clock = clk_get(&pdev->dev, "dp"); | |
872 | if (IS_ERR(dp->clock)) { | |
873 | dev_err(&pdev->dev, "failed to get clock\n"); | |
874 | ret = PTR_ERR(dp->clock); | |
875 | goto err_dp; | |
876 | } | |
877 | ||
878 | clk_enable(dp->clock); | |
879 | ||
880 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
881 | if (!res) { | |
882 | dev_err(&pdev->dev, "failed to get registers\n"); | |
883 | ret = -EINVAL; | |
884 | goto err_clock; | |
885 | } | |
886 | ||
887 | res = request_mem_region(res->start, resource_size(res), | |
888 | dev_name(&pdev->dev)); | |
889 | if (!res) { | |
890 | dev_err(&pdev->dev, "failed to request registers region\n"); | |
891 | ret = -EINVAL; | |
892 | goto err_clock; | |
893 | } | |
894 | ||
895 | dp->res = res; | |
896 | ||
897 | dp->reg_base = ioremap(res->start, resource_size(res)); | |
898 | if (!dp->reg_base) { | |
899 | dev_err(&pdev->dev, "failed to ioremap\n"); | |
900 | ret = -ENOMEM; | |
901 | goto err_req_region; | |
902 | } | |
903 | ||
904 | dp->irq = platform_get_irq(pdev, 0); | |
905 | if (!dp->irq) { | |
906 | dev_err(&pdev->dev, "failed to get irq\n"); | |
907 | ret = -ENODEV; | |
908 | goto err_ioremap; | |
909 | } | |
910 | ||
911 | ret = request_irq(dp->irq, exynos_dp_irq_handler, 0, | |
912 | "exynos-dp", dp); | |
913 | if (ret) { | |
914 | dev_err(&pdev->dev, "failed to request irq\n"); | |
915 | goto err_ioremap; | |
916 | } | |
917 | ||
918 | dp->video_info = pdata->video_info; | |
919 | if (pdata->phy_init) | |
920 | pdata->phy_init(); | |
921 | ||
922 | exynos_dp_init_dp(dp); | |
923 | ||
924 | ret = exynos_dp_detect_hpd(dp); | |
925 | if (ret) { | |
926 | dev_err(&pdev->dev, "unable to detect hpd\n"); | |
927 | goto err_irq; | |
928 | } | |
929 | ||
930 | exynos_dp_handle_edid(dp); | |
931 | ||
932 | ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count, | |
933 | dp->video_info->link_rate); | |
934 | if (ret) { | |
935 | dev_err(&pdev->dev, "unable to do link train\n"); | |
936 | goto err_irq; | |
937 | } | |
938 | ||
939 | exynos_dp_enable_scramble(dp, 1); | |
940 | exynos_dp_enable_rx_to_enhanced_mode(dp, 1); | |
941 | exynos_dp_enable_enhanced_mode(dp, 1); | |
942 | ||
943 | exynos_dp_set_lane_count(dp, dp->video_info->lane_count); | |
944 | exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); | |
945 | ||
946 | exynos_dp_init_video(dp); | |
947 | ret = exynos_dp_config_video(dp, dp->video_info); | |
948 | if (ret) { | |
949 | dev_err(&pdev->dev, "unable to config video\n"); | |
950 | goto err_irq; | |
951 | } | |
952 | ||
953 | platform_set_drvdata(pdev, dp); | |
954 | ||
955 | return 0; | |
956 | ||
957 | err_irq: | |
958 | free_irq(dp->irq, dp); | |
959 | err_ioremap: | |
960 | iounmap(dp->reg_base); | |
961 | err_req_region: | |
962 | release_mem_region(res->start, resource_size(res)); | |
963 | err_clock: | |
964 | clk_put(dp->clock); | |
965 | err_dp: | |
966 | kfree(dp); | |
967 | ||
968 | return ret; | |
969 | } | |
970 | ||
971 | static int __devexit exynos_dp_remove(struct platform_device *pdev) | |
972 | { | |
973 | struct exynos_dp_platdata *pdata = pdev->dev.platform_data; | |
974 | struct exynos_dp_device *dp = platform_get_drvdata(pdev); | |
975 | ||
976 | if (pdata && pdata->phy_exit) | |
977 | pdata->phy_exit(); | |
978 | ||
979 | free_irq(dp->irq, dp); | |
980 | iounmap(dp->reg_base); | |
981 | ||
982 | clk_disable(dp->clock); | |
983 | clk_put(dp->clock); | |
984 | ||
985 | release_mem_region(dp->res->start, resource_size(dp->res)); | |
986 | ||
987 | kfree(dp); | |
988 | ||
989 | return 0; | |
990 | } | |
991 | ||
992 | #ifdef CONFIG_PM_SLEEP | |
993 | static int exynos_dp_suspend(struct device *dev) | |
994 | { | |
995 | struct platform_device *pdev = to_platform_device(dev); | |
996 | struct exynos_dp_platdata *pdata = pdev->dev.platform_data; | |
997 | struct exynos_dp_device *dp = platform_get_drvdata(pdev); | |
998 | ||
999 | if (pdata && pdata->phy_exit) | |
1000 | pdata->phy_exit(); | |
1001 | ||
1002 | clk_disable(dp->clock); | |
1003 | ||
1004 | return 0; | |
1005 | } | |
1006 | ||
1007 | static int exynos_dp_resume(struct device *dev) | |
1008 | { | |
1009 | struct platform_device *pdev = to_platform_device(dev); | |
1010 | struct exynos_dp_platdata *pdata = pdev->dev.platform_data; | |
1011 | struct exynos_dp_device *dp = platform_get_drvdata(pdev); | |
1012 | ||
1013 | if (pdata && pdata->phy_init) | |
1014 | pdata->phy_init(); | |
1015 | ||
1016 | clk_enable(dp->clock); | |
1017 | ||
1018 | exynos_dp_init_dp(dp); | |
1019 | ||
1020 | exynos_dp_detect_hpd(dp); | |
1021 | exynos_dp_handle_edid(dp); | |
1022 | ||
1023 | exynos_dp_set_link_train(dp, dp->video_info->lane_count, | |
1024 | dp->video_info->link_rate); | |
1025 | ||
1026 | exynos_dp_enable_scramble(dp, 1); | |
1027 | exynos_dp_enable_rx_to_enhanced_mode(dp, 1); | |
1028 | exynos_dp_enable_enhanced_mode(dp, 1); | |
1029 | ||
1030 | exynos_dp_set_lane_count(dp, dp->video_info->lane_count); | |
1031 | exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); | |
1032 | ||
1033 | exynos_dp_init_video(dp); | |
1034 | exynos_dp_config_video(dp, dp->video_info); | |
1035 | ||
1036 | return 0; | |
1037 | } | |
1038 | #endif | |
1039 | ||
1040 | static const struct dev_pm_ops exynos_dp_pm_ops = { | |
1041 | SET_SYSTEM_SLEEP_PM_OPS(exynos_dp_suspend, exynos_dp_resume) | |
1042 | }; | |
1043 | ||
1044 | static struct platform_driver exynos_dp_driver = { | |
1045 | .probe = exynos_dp_probe, | |
1046 | .remove = __devexit_p(exynos_dp_remove), | |
1047 | .driver = { | |
1048 | .name = "exynos-dp", | |
1049 | .owner = THIS_MODULE, | |
1050 | .pm = &exynos_dp_pm_ops, | |
1051 | }, | |
1052 | }; | |
1053 | ||
1054 | module_platform_driver(exynos_dp_driver); | |
1055 | ||
1056 | MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); | |
1057 | MODULE_DESCRIPTION("Samsung SoC DP Driver"); | |
1058 | MODULE_LICENSE("GPL"); |