]>
Commit | Line | Data |
---|---|---|
d2a6982f DL |
1 | /* |
2 | * Copyright (C) 2012 Samsung Electronics | |
3 | * | |
4 | * Author: Donghwa Lee <[email protected]> | |
5 | * | |
3765b3e7 | 6 | * SPDX-License-Identifier: GPL-2.0+ |
d2a6982f DL |
7 | */ |
8 | ||
9 | #include <config.h> | |
bb5930d5 | 10 | #include <dm.h> |
d2a6982f | 11 | #include <common.h> |
bb5930d5 SG |
12 | #include <display.h> |
13 | #include <fdtdec.h> | |
14 | #include <libfdt.h> | |
d2a6982f | 15 | #include <malloc.h> |
bb5930d5 | 16 | #include <video_bridge.h> |
0c06db59 | 17 | #include <linux/compat.h> |
d2a6982f DL |
18 | #include <linux/err.h> |
19 | #include <asm/arch/clk.h> | |
20 | #include <asm/arch/cpu.h> | |
21 | #include <asm/arch/dp_info.h> | |
22 | #include <asm/arch/dp.h> | |
bb5930d5 | 23 | #include <asm/arch/pinmux.h> |
7eb860df | 24 | #include <asm/arch/power.h> |
d2a6982f DL |
25 | |
26 | #include "exynos_dp_lowlevel.h" | |
27 | ||
9947d13e AK |
28 | DECLARE_GLOBAL_DATA_PTR; |
29 | ||
d2a6982f DL |
30 | static void exynos_dp_disp_info(struct edp_disp_info *disp_info) |
31 | { | |
32 | disp_info->h_total = disp_info->h_res + disp_info->h_sync_width + | |
33 | disp_info->h_back_porch + disp_info->h_front_porch; | |
34 | disp_info->v_total = disp_info->v_res + disp_info->v_sync_width + | |
35 | disp_info->v_back_porch + disp_info->v_front_porch; | |
36 | ||
37 | return; | |
38 | } | |
39 | ||
8b449a66 | 40 | static int exynos_dp_init_dp(struct exynos_dp *regs) |
d2a6982f DL |
41 | { |
42 | int ret; | |
8b449a66 | 43 | exynos_dp_reset(regs); |
d2a6982f DL |
44 | |
45 | /* SW defined function Normal operation */ | |
8b449a66 | 46 | exynos_dp_enable_sw_func(regs, DP_ENABLE); |
d2a6982f | 47 | |
8b449a66 | 48 | ret = exynos_dp_init_analog_func(regs); |
d2a6982f DL |
49 | if (ret != EXYNOS_DP_SUCCESS) |
50 | return ret; | |
51 | ||
8b449a66 SG |
52 | exynos_dp_init_hpd(regs); |
53 | exynos_dp_init_aux(regs); | |
d2a6982f DL |
54 | |
55 | return ret; | |
56 | } | |
57 | ||
58 | static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data) | |
59 | { | |
60 | int i; | |
61 | unsigned char sum = 0; | |
62 | ||
63 | for (i = 0; i < EDID_BLOCK_LENGTH; i++) | |
64 | sum = sum + edid_data[i]; | |
65 | ||
66 | return sum; | |
67 | } | |
68 | ||
8b449a66 | 69 | static unsigned int exynos_dp_read_edid(struct exynos_dp *regs) |
d2a6982f DL |
70 | { |
71 | unsigned char edid[EDID_BLOCK_LENGTH * 2]; | |
72 | unsigned int extend_block = 0; | |
73 | unsigned char sum; | |
74 | unsigned char test_vector; | |
75 | int retval; | |
76 | ||
77 | /* | |
78 | * EDID device address is 0x50. | |
79 | * However, if necessary, you must have set upper address | |
80 | * into E-EDID in I2C device, 0x30. | |
81 | */ | |
82 | ||
83 | /* Read Extension Flag, Number of 128-byte EDID extension blocks */ | |
8b449a66 | 84 | exynos_dp_read_byte_from_i2c(regs, I2C_EDID_DEVICE_ADDR, |
8c9b8dc0 | 85 | EDID_EXTENSION_FLAG, &extend_block); |
d2a6982f DL |
86 | |
87 | if (extend_block > 0) { | |
88 | printf("DP EDID data includes a single extension!\n"); | |
89 | ||
90 | /* Read EDID data */ | |
8b449a66 | 91 | retval = exynos_dp_read_bytes_from_i2c(regs, |
8c9b8dc0 | 92 | I2C_EDID_DEVICE_ADDR, |
d2a6982f DL |
93 | EDID_HEADER_PATTERN, |
94 | EDID_BLOCK_LENGTH, | |
95 | &edid[EDID_HEADER_PATTERN]); | |
96 | if (retval != 0) { | |
97 | printf("DP EDID Read failed!\n"); | |
98 | return -1; | |
99 | } | |
100 | sum = exynos_dp_calc_edid_check_sum(edid); | |
101 | if (sum != 0) { | |
102 | printf("DP EDID bad checksum!\n"); | |
103 | return -1; | |
104 | } | |
105 | ||
106 | /* Read additional EDID data */ | |
8b449a66 | 107 | retval = exynos_dp_read_bytes_from_i2c(regs, |
8c9b8dc0 | 108 | I2C_EDID_DEVICE_ADDR, |
d2a6982f DL |
109 | EDID_BLOCK_LENGTH, |
110 | EDID_BLOCK_LENGTH, | |
111 | &edid[EDID_BLOCK_LENGTH]); | |
112 | if (retval != 0) { | |
113 | printf("DP EDID Read failed!\n"); | |
114 | return -1; | |
115 | } | |
116 | sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); | |
117 | if (sum != 0) { | |
118 | printf("DP EDID bad checksum!\n"); | |
119 | return -1; | |
120 | } | |
121 | ||
8b449a66 | 122 | exynos_dp_read_byte_from_dpcd(regs, DPCD_TEST_REQUEST, |
8c9b8dc0 | 123 | &test_vector); |
d2a6982f | 124 | if (test_vector & DPCD_TEST_EDID_READ) { |
8b449a66 | 125 | exynos_dp_write_byte_to_dpcd(regs, |
8c9b8dc0 | 126 | DPCD_TEST_EDID_CHECKSUM, |
d2a6982f | 127 | edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); |
8b449a66 | 128 | exynos_dp_write_byte_to_dpcd(regs, |
8c9b8dc0 | 129 | DPCD_TEST_RESPONSE, |
d2a6982f DL |
130 | DPCD_TEST_EDID_CHECKSUM_WRITE); |
131 | } | |
132 | } else { | |
133 | debug("DP EDID data does not include any extensions.\n"); | |
134 | ||
135 | /* Read EDID data */ | |
8b449a66 | 136 | retval = exynos_dp_read_bytes_from_i2c(regs, |
8c9b8dc0 | 137 | I2C_EDID_DEVICE_ADDR, |
d2a6982f DL |
138 | EDID_HEADER_PATTERN, |
139 | EDID_BLOCK_LENGTH, | |
140 | &edid[EDID_HEADER_PATTERN]); | |
141 | ||
142 | if (retval != 0) { | |
143 | printf("DP EDID Read failed!\n"); | |
144 | return -1; | |
145 | } | |
146 | sum = exynos_dp_calc_edid_check_sum(edid); | |
147 | if (sum != 0) { | |
148 | printf("DP EDID bad checksum!\n"); | |
149 | return -1; | |
150 | } | |
151 | ||
8b449a66 | 152 | exynos_dp_read_byte_from_dpcd(regs, DPCD_TEST_REQUEST, |
d2a6982f DL |
153 | &test_vector); |
154 | if (test_vector & DPCD_TEST_EDID_READ) { | |
8b449a66 | 155 | exynos_dp_write_byte_to_dpcd(regs, |
8c9b8dc0 | 156 | DPCD_TEST_EDID_CHECKSUM, edid[EDID_CHECKSUM]); |
8b449a66 | 157 | exynos_dp_write_byte_to_dpcd(regs, |
8c9b8dc0 | 158 | DPCD_TEST_RESPONSE, |
d2a6982f DL |
159 | DPCD_TEST_EDID_CHECKSUM_WRITE); |
160 | } | |
161 | } | |
162 | ||
163 | debug("DP EDID Read success!\n"); | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
8b449a66 SG |
168 | static unsigned int exynos_dp_handle_edid(struct exynos_dp *regs, |
169 | struct exynos_dp_priv *priv) | |
d2a6982f DL |
170 | { |
171 | unsigned char buf[12]; | |
172 | unsigned int ret; | |
173 | unsigned char temp; | |
174 | unsigned char retry_cnt; | |
175 | unsigned char dpcd_rev[16]; | |
176 | unsigned char lane_bw[16]; | |
177 | unsigned char lane_cnt[16]; | |
178 | ||
179 | memset(dpcd_rev, 0, 16); | |
180 | memset(lane_bw, 0, 16); | |
181 | memset(lane_cnt, 0, 16); | |
182 | memset(buf, 0, 12); | |
183 | ||
184 | retry_cnt = 5; | |
185 | while (retry_cnt) { | |
186 | /* Read DPCD 0x0000-0x000b */ | |
8b449a66 | 187 | ret = exynos_dp_read_bytes_from_dpcd(regs, DPCD_DPCD_REV, 12, |
8c9b8dc0 | 188 | buf); |
d2a6982f DL |
189 | if (ret != EXYNOS_DP_SUCCESS) { |
190 | if (retry_cnt == 0) { | |
191 | printf("DP read_byte_from_dpcd() failed\n"); | |
192 | return ret; | |
193 | } | |
194 | retry_cnt--; | |
195 | } else | |
196 | break; | |
197 | } | |
198 | ||
199 | /* */ | |
200 | temp = buf[DPCD_DPCD_REV]; | |
201 | if (temp == DP_DPCD_REV_10 || temp == DP_DPCD_REV_11) | |
8b449a66 | 202 | priv->dpcd_rev = temp; |
d2a6982f DL |
203 | else { |
204 | printf("DP Wrong DPCD Rev : %x\n", temp); | |
205 | return -ENODEV; | |
206 | } | |
207 | ||
208 | temp = buf[DPCD_MAX_LINK_RATE]; | |
209 | if (temp == DP_LANE_BW_1_62 || temp == DP_LANE_BW_2_70) | |
8b449a66 | 210 | priv->lane_bw = temp; |
d2a6982f DL |
211 | else { |
212 | printf("DP Wrong MAX LINK RATE : %x\n", temp); | |
213 | return -EINVAL; | |
214 | } | |
215 | ||
a418f7e8 | 216 | /* Refer VESA Display Port Standard Ver1.1a Page 120 */ |
8b449a66 | 217 | if (priv->dpcd_rev == DP_DPCD_REV_11) { |
d2a6982f DL |
218 | temp = buf[DPCD_MAX_LANE_COUNT] & 0x1f; |
219 | if (buf[DPCD_MAX_LANE_COUNT] & 0x80) | |
8b449a66 | 220 | priv->dpcd_efc = 1; |
d2a6982f | 221 | else |
8b449a66 | 222 | priv->dpcd_efc = 0; |
d2a6982f DL |
223 | } else { |
224 | temp = buf[DPCD_MAX_LANE_COUNT]; | |
8b449a66 | 225 | priv->dpcd_efc = 0; |
d2a6982f DL |
226 | } |
227 | ||
228 | if (temp == DP_LANE_CNT_1 || temp == DP_LANE_CNT_2 || | |
229 | temp == DP_LANE_CNT_4) { | |
8b449a66 | 230 | priv->lane_cnt = temp; |
d2a6982f DL |
231 | } else { |
232 | printf("DP Wrong MAX LANE COUNT : %x\n", temp); | |
233 | return -EINVAL; | |
234 | } | |
235 | ||
8b449a66 | 236 | ret = exynos_dp_read_edid(regs); |
d2a6982f DL |
237 | if (ret != EXYNOS_DP_SUCCESS) { |
238 | printf("DP exynos_dp_read_edid() failed\n"); | |
239 | return -EINVAL; | |
240 | } | |
241 | ||
242 | return ret; | |
243 | } | |
244 | ||
8b449a66 | 245 | static void exynos_dp_init_training(struct exynos_dp *regs) |
d2a6982f DL |
246 | { |
247 | /* | |
248 | * MACRO_RST must be applied after the PLL_LOCK to avoid | |
249 | * the DP inter pair skew issue for at least 10 us | |
250 | */ | |
8b449a66 | 251 | exynos_dp_reset_macro(regs); |
d2a6982f DL |
252 | |
253 | /* All DP analog module power up */ | |
8b449a66 | 254 | exynos_dp_set_analog_power_down(regs, POWER_ALL, 0); |
d2a6982f DL |
255 | } |
256 | ||
8b449a66 SG |
257 | static unsigned int exynos_dp_link_start(struct exynos_dp *regs, |
258 | struct exynos_dp_priv *priv) | |
d2a6982f DL |
259 | { |
260 | unsigned char buf[5]; | |
261 | unsigned int ret = 0; | |
262 | ||
263 | debug("DP: %s was called\n", __func__); | |
264 | ||
8b449a66 SG |
265 | priv->lt_info.lt_status = DP_LT_CR; |
266 | priv->lt_info.ep_loop = 0; | |
267 | priv->lt_info.cr_loop[0] = 0; | |
268 | priv->lt_info.cr_loop[1] = 0; | |
269 | priv->lt_info.cr_loop[2] = 0; | |
270 | priv->lt_info.cr_loop[3] = 0; | |
d2a6982f DL |
271 | |
272 | /* Set sink to D0 (Sink Not Ready) mode. */ | |
8b449a66 | 273 | ret = exynos_dp_write_byte_to_dpcd(regs, DPCD_SINK_POWER_STATE, |
8c9b8dc0 | 274 | DPCD_SET_POWER_STATE_D0); |
d2a6982f DL |
275 | if (ret != EXYNOS_DP_SUCCESS) { |
276 | printf("DP write_dpcd_byte failed\n"); | |
277 | return ret; | |
278 | } | |
279 | ||
a418f7e8 | 280 | /* Set link rate and count as you want to establish */ |
8b449a66 SG |
281 | exynos_dp_set_link_bandwidth(regs, priv->lane_bw); |
282 | exynos_dp_set_lane_count(regs, priv->lane_cnt); | |
d2a6982f DL |
283 | |
284 | /* Setup RX configuration */ | |
8b449a66 SG |
285 | buf[0] = priv->lane_bw; |
286 | buf[1] = priv->lane_cnt; | |
d2a6982f | 287 | |
8b449a66 | 288 | ret = exynos_dp_write_bytes_to_dpcd(regs, DPCD_LINK_BW_SET, 2, buf); |
d2a6982f DL |
289 | if (ret != EXYNOS_DP_SUCCESS) { |
290 | printf("DP write_dpcd_byte failed\n"); | |
291 | return ret; | |
292 | } | |
293 | ||
8b449a66 SG |
294 | exynos_dp_set_lane_pre_emphasis(regs, PRE_EMPHASIS_LEVEL_0, |
295 | priv->lane_cnt); | |
d2a6982f DL |
296 | |
297 | /* Set training pattern 1 */ | |
8b449a66 | 298 | exynos_dp_set_training_pattern(regs, TRAINING_PTN1); |
d2a6982f DL |
299 | |
300 | /* Set RX training pattern */ | |
301 | buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1; | |
302 | ||
303 | buf[1] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | | |
304 | DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; | |
305 | buf[2] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | | |
306 | DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; | |
307 | buf[3] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | | |
308 | DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; | |
309 | buf[4] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | | |
310 | DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; | |
311 | ||
8b449a66 | 312 | ret = exynos_dp_write_bytes_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET, |
8c9b8dc0 | 313 | 5, buf); |
d2a6982f DL |
314 | if (ret != EXYNOS_DP_SUCCESS) { |
315 | printf("DP write_dpcd_byte failed\n"); | |
316 | return ret; | |
317 | } | |
318 | ||
319 | return ret; | |
320 | } | |
321 | ||
8b449a66 | 322 | static unsigned int exynos_dp_training_pattern_dis(struct exynos_dp *regs) |
d2a6982f DL |
323 | { |
324 | unsigned int ret = EXYNOS_DP_SUCCESS; | |
325 | ||
8b449a66 | 326 | exynos_dp_set_training_pattern(regs, DP_NONE); |
d2a6982f | 327 | |
8b449a66 | 328 | ret = exynos_dp_write_byte_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET, |
8c9b8dc0 | 329 | DPCD_TRAINING_PATTERN_DISABLED); |
d2a6982f | 330 | if (ret != EXYNOS_DP_SUCCESS) { |
a418f7e8 | 331 | printf("DP request_link_training_req failed\n"); |
d2a6982f DL |
332 | return -EAGAIN; |
333 | } | |
334 | ||
335 | return ret; | |
336 | } | |
337 | ||
8c9b8dc0 | 338 | static unsigned int exynos_dp_enable_rx_to_enhanced_mode( |
8b449a66 | 339 | struct exynos_dp *regs, unsigned char enable) |
d2a6982f DL |
340 | { |
341 | unsigned char data; | |
342 | unsigned int ret = EXYNOS_DP_SUCCESS; | |
343 | ||
8b449a66 | 344 | ret = exynos_dp_read_byte_from_dpcd(regs, DPCD_LANE_COUNT_SET, |
8c9b8dc0 | 345 | &data); |
d2a6982f DL |
346 | if (ret != EXYNOS_DP_SUCCESS) { |
347 | printf("DP read_from_dpcd failed\n"); | |
348 | return -EAGAIN; | |
349 | } | |
350 | ||
351 | if (enable) | |
352 | data = DPCD_ENHANCED_FRAME_EN | DPCD_LN_COUNT_SET(data); | |
353 | else | |
354 | data = DPCD_LN_COUNT_SET(data); | |
355 | ||
8b449a66 | 356 | ret = exynos_dp_write_byte_to_dpcd(regs, DPCD_LANE_COUNT_SET, data); |
d2a6982f DL |
357 | if (ret != EXYNOS_DP_SUCCESS) { |
358 | printf("DP write_to_dpcd failed\n"); | |
359 | return -EAGAIN; | |
360 | ||
361 | } | |
362 | ||
363 | return ret; | |
364 | } | |
365 | ||
8b449a66 | 366 | static unsigned int exynos_dp_set_enhanced_mode(struct exynos_dp *regs, |
8c9b8dc0 | 367 | unsigned char enhance_mode) |
d2a6982f DL |
368 | { |
369 | unsigned int ret = EXYNOS_DP_SUCCESS; | |
370 | ||
8b449a66 | 371 | ret = exynos_dp_enable_rx_to_enhanced_mode(regs, enhance_mode); |
d2a6982f DL |
372 | if (ret != EXYNOS_DP_SUCCESS) { |
373 | printf("DP rx_enhance_mode failed\n"); | |
374 | return -EAGAIN; | |
375 | } | |
376 | ||
8b449a66 | 377 | exynos_dp_enable_enhanced_mode(regs, enhance_mode); |
d2a6982f DL |
378 | |
379 | return ret; | |
380 | } | |
381 | ||
8b449a66 SG |
382 | static int exynos_dp_read_dpcd_lane_stat(struct exynos_dp *regs, |
383 | struct exynos_dp_priv *priv, | |
8c9b8dc0 | 384 | unsigned char *status) |
d2a6982f DL |
385 | { |
386 | unsigned int ret, i; | |
387 | unsigned char buf[2]; | |
388 | unsigned char lane_stat[DP_LANE_CNT_4] = {0,}; | |
389 | unsigned char shift_val[DP_LANE_CNT_4] = {0,}; | |
390 | ||
391 | shift_val[0] = 0; | |
392 | shift_val[1] = 4; | |
393 | shift_val[2] = 0; | |
394 | shift_val[3] = 4; | |
395 | ||
8b449a66 | 396 | ret = exynos_dp_read_bytes_from_dpcd(regs, DPCD_LANE0_1_STATUS, 2, |
8c9b8dc0 | 397 | buf); |
d2a6982f DL |
398 | if (ret != EXYNOS_DP_SUCCESS) { |
399 | printf("DP read lane status failed\n"); | |
400 | return ret; | |
401 | } | |
402 | ||
8b449a66 | 403 | for (i = 0; i < priv->lane_cnt; i++) { |
d2a6982f DL |
404 | lane_stat[i] = (buf[(i / 2)] >> shift_val[i]) & 0x0f; |
405 | if (lane_stat[0] != lane_stat[i]) { | |
406 | printf("Wrong lane status\n"); | |
407 | return -EINVAL; | |
408 | } | |
409 | } | |
410 | ||
411 | *status = lane_stat[0]; | |
412 | ||
413 | return ret; | |
414 | } | |
415 | ||
8b449a66 | 416 | static unsigned int exynos_dp_read_dpcd_adj_req(struct exynos_dp *regs, |
8c9b8dc0 | 417 | unsigned char lane_num, unsigned char *sw, unsigned char *em) |
d2a6982f DL |
418 | { |
419 | unsigned int ret = EXYNOS_DP_SUCCESS; | |
420 | unsigned char buf; | |
421 | unsigned int dpcd_addr; | |
422 | unsigned char shift_val[DP_LANE_CNT_4] = {0, 4, 0, 4}; | |
423 | ||
a418f7e8 | 424 | /* lane_num value is used as array index, so this range 0 ~ 3 */ |
d2a6982f DL |
425 | dpcd_addr = DPCD_ADJUST_REQUEST_LANE0_1 + (lane_num / 2); |
426 | ||
8b449a66 | 427 | ret = exynos_dp_read_byte_from_dpcd(regs, dpcd_addr, &buf); |
d2a6982f DL |
428 | if (ret != EXYNOS_DP_SUCCESS) { |
429 | printf("DP read adjust request failed\n"); | |
430 | return -EAGAIN; | |
431 | } | |
432 | ||
433 | *sw = ((buf >> shift_val[lane_num]) & 0x03); | |
434 | *em = ((buf >> shift_val[lane_num]) & 0x0c) >> 2; | |
435 | ||
436 | return ret; | |
437 | } | |
438 | ||
8b449a66 SG |
439 | static int exynos_dp_equalizer_err_link(struct exynos_dp *regs, |
440 | struct exynos_dp_priv *priv) | |
d2a6982f DL |
441 | { |
442 | int ret; | |
443 | ||
8b449a66 | 444 | ret = exynos_dp_training_pattern_dis(regs); |
d2a6982f | 445 | if (ret != EXYNOS_DP_SUCCESS) { |
a418f7e8 | 446 | printf("DP training_pattern_disable() failed\n"); |
8b449a66 | 447 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
448 | } |
449 | ||
8b449a66 | 450 | ret = exynos_dp_set_enhanced_mode(regs, priv->dpcd_efc); |
d2a6982f DL |
451 | if (ret != EXYNOS_DP_SUCCESS) { |
452 | printf("DP set_enhanced_mode() failed\n"); | |
8b449a66 | 453 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
454 | } |
455 | ||
456 | return ret; | |
457 | } | |
458 | ||
8b449a66 SG |
459 | static int exynos_dp_reduce_link_rate(struct exynos_dp *regs, |
460 | struct exynos_dp_priv *priv) | |
d2a6982f DL |
461 | { |
462 | int ret; | |
463 | ||
8b449a66 SG |
464 | if (priv->lane_bw == DP_LANE_BW_2_70) { |
465 | priv->lane_bw = DP_LANE_BW_1_62; | |
d2a6982f | 466 | printf("DP Change lane bw to 1.62Gbps\n"); |
8b449a66 | 467 | priv->lt_info.lt_status = DP_LT_START; |
d2a6982f DL |
468 | ret = EXYNOS_DP_SUCCESS; |
469 | } else { | |
8b449a66 | 470 | ret = exynos_dp_training_pattern_dis(regs); |
d2a6982f DL |
471 | if (ret != EXYNOS_DP_SUCCESS) |
472 | printf("DP training_patter_disable() failed\n"); | |
473 | ||
8b449a66 | 474 | ret = exynos_dp_set_enhanced_mode(regs, priv->dpcd_efc); |
d2a6982f DL |
475 | if (ret != EXYNOS_DP_SUCCESS) |
476 | printf("DP set_enhanced_mode() failed\n"); | |
477 | ||
8b449a66 | 478 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
479 | } |
480 | ||
481 | return ret; | |
482 | } | |
483 | ||
8b449a66 SG |
484 | static unsigned int exynos_dp_process_clock_recovery(struct exynos_dp *regs, |
485 | struct exynos_dp_priv *priv) | |
d2a6982f DL |
486 | { |
487 | unsigned int ret = EXYNOS_DP_SUCCESS; | |
488 | unsigned char lane_stat; | |
489 | unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, }; | |
490 | unsigned int i; | |
491 | unsigned char adj_req_sw; | |
492 | unsigned char adj_req_em; | |
493 | unsigned char buf[5]; | |
494 | ||
495 | debug("DP: %s was called\n", __func__); | |
496 | mdelay(1); | |
497 | ||
8b449a66 | 498 | ret = exynos_dp_read_dpcd_lane_stat(regs, priv, &lane_stat); |
d2a6982f DL |
499 | if (ret != EXYNOS_DP_SUCCESS) { |
500 | printf("DP read lane status failed\n"); | |
8b449a66 | 501 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
502 | return ret; |
503 | } | |
504 | ||
505 | if (lane_stat & DP_LANE_STAT_CR_DONE) { | |
506 | debug("DP clock Recovery training succeed\n"); | |
8b449a66 | 507 | exynos_dp_set_training_pattern(regs, TRAINING_PTN2); |
d2a6982f | 508 | |
8b449a66 SG |
509 | for (i = 0; i < priv->lane_cnt; i++) { |
510 | ret = exynos_dp_read_dpcd_adj_req(regs, i, | |
8c9b8dc0 | 511 | &adj_req_sw, &adj_req_em); |
d2a6982f | 512 | if (ret != EXYNOS_DP_SUCCESS) { |
8b449a66 | 513 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
514 | return ret; |
515 | } | |
516 | ||
517 | lt_ctl_val[i] = 0; | |
518 | lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; | |
519 | ||
520 | if ((adj_req_sw == VOLTAGE_LEVEL_3) | |
521 | || (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { | |
522 | lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | | |
523 | MAX_PRE_EMPHASIS_REACH_3; | |
524 | } | |
8b449a66 | 525 | exynos_dp_set_lanex_pre_emphasis(regs, |
8c9b8dc0 | 526 | lt_ctl_val[i], i); |
d2a6982f DL |
527 | } |
528 | ||
529 | buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_2; | |
530 | buf[1] = lt_ctl_val[0]; | |
531 | buf[2] = lt_ctl_val[1]; | |
532 | buf[3] = lt_ctl_val[2]; | |
533 | buf[4] = lt_ctl_val[3]; | |
534 | ||
8b449a66 | 535 | ret = exynos_dp_write_bytes_to_dpcd(regs, |
d2a6982f DL |
536 | DPCD_TRAINING_PATTERN_SET, 5, buf); |
537 | if (ret != EXYNOS_DP_SUCCESS) { | |
a418f7e8 | 538 | printf("DP write training pattern1 failed\n"); |
8b449a66 | 539 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
540 | return ret; |
541 | } else | |
8b449a66 | 542 | priv->lt_info.lt_status = DP_LT_ET; |
d2a6982f | 543 | } else { |
8b449a66 | 544 | for (i = 0; i < priv->lane_cnt; i++) { |
8c9b8dc0 | 545 | lt_ctl_val[i] = exynos_dp_get_lanex_pre_emphasis( |
8b449a66 SG |
546 | regs, i); |
547 | ret = exynos_dp_read_dpcd_adj_req(regs, i, | |
d2a6982f DL |
548 | &adj_req_sw, &adj_req_em); |
549 | if (ret != EXYNOS_DP_SUCCESS) { | |
550 | printf("DP read adj req failed\n"); | |
8b449a66 | 551 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
552 | return ret; |
553 | } | |
554 | ||
555 | if ((adj_req_sw == VOLTAGE_LEVEL_3) || | |
556 | (adj_req_em == PRE_EMPHASIS_LEVEL_3)) | |
8b449a66 SG |
557 | ret = exynos_dp_reduce_link_rate(regs, |
558 | priv); | |
d2a6982f DL |
559 | |
560 | if ((DRIVE_CURRENT_SET_0_GET(lt_ctl_val[i]) == | |
561 | adj_req_sw) && | |
562 | (PRE_EMPHASIS_SET_0_GET(lt_ctl_val[i]) == | |
563 | adj_req_em)) { | |
8b449a66 SG |
564 | priv->lt_info.cr_loop[i]++; |
565 | if (priv->lt_info.cr_loop[i] == MAX_CR_LOOP) | |
d2a6982f | 566 | ret = exynos_dp_reduce_link_rate( |
8b449a66 | 567 | regs, priv); |
d2a6982f DL |
568 | } |
569 | ||
570 | lt_ctl_val[i] = 0; | |
571 | lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; | |
572 | ||
573 | if ((adj_req_sw == VOLTAGE_LEVEL_3) || | |
574 | (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { | |
575 | lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | | |
576 | MAX_PRE_EMPHASIS_REACH_3; | |
577 | } | |
8b449a66 | 578 | exynos_dp_set_lanex_pre_emphasis(regs, |
8c9b8dc0 | 579 | lt_ctl_val[i], i); |
d2a6982f DL |
580 | } |
581 | ||
8b449a66 | 582 | ret = exynos_dp_write_bytes_to_dpcd(regs, |
d2a6982f DL |
583 | DPCD_TRAINING_LANE0_SET, 4, lt_ctl_val); |
584 | if (ret != EXYNOS_DP_SUCCESS) { | |
a418f7e8 | 585 | printf("DP write training pattern2 failed\n"); |
8b449a66 | 586 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
587 | return ret; |
588 | } | |
589 | } | |
590 | ||
591 | return ret; | |
592 | } | |
593 | ||
8c9b8dc0 | 594 | static unsigned int exynos_dp_process_equalizer_training( |
8b449a66 | 595 | struct exynos_dp *regs, struct exynos_dp_priv *priv) |
d2a6982f DL |
596 | { |
597 | unsigned int ret = EXYNOS_DP_SUCCESS; | |
598 | unsigned char lane_stat, adj_req_sw, adj_req_em, i; | |
599 | unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,}; | |
600 | unsigned char interlane_aligned = 0; | |
601 | unsigned char f_bw; | |
602 | unsigned char f_lane_cnt; | |
603 | unsigned char sink_stat; | |
604 | ||
605 | mdelay(1); | |
606 | ||
8b449a66 | 607 | ret = exynos_dp_read_dpcd_lane_stat(regs, priv, &lane_stat); |
d2a6982f DL |
608 | if (ret != EXYNOS_DP_SUCCESS) { |
609 | printf("DP read lane status failed\n"); | |
8b449a66 | 610 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
611 | return ret; |
612 | } | |
613 | ||
614 | debug("DP lane stat : %x\n", lane_stat); | |
615 | ||
616 | if (lane_stat & DP_LANE_STAT_CR_DONE) { | |
8b449a66 | 617 | ret = exynos_dp_read_byte_from_dpcd(regs, |
8c9b8dc0 SG |
618 | DPCD_LN_ALIGN_UPDATED, |
619 | &sink_stat); | |
d2a6982f | 620 | if (ret != EXYNOS_DP_SUCCESS) { |
8b449a66 | 621 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
622 | |
623 | return ret; | |
624 | } | |
625 | ||
626 | interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE); | |
627 | ||
8b449a66 SG |
628 | for (i = 0; i < priv->lane_cnt; i++) { |
629 | ret = exynos_dp_read_dpcd_adj_req(regs, i, | |
d2a6982f DL |
630 | &adj_req_sw, &adj_req_em); |
631 | if (ret != EXYNOS_DP_SUCCESS) { | |
632 | printf("DP read adj req 1 failed\n"); | |
8b449a66 | 633 | priv->lt_info.lt_status = DP_LT_FAIL; |
d2a6982f DL |
634 | |
635 | return ret; | |
636 | } | |
637 | ||
638 | lt_ctl_val[i] = 0; | |
639 | lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; | |
640 | ||
641 | if ((adj_req_sw == VOLTAGE_LEVEL_3) || | |
642 | (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { | |
643 | lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3; | |
644 | lt_ctl_val[i] |= MAX_PRE_EMPHASIS_REACH_3; | |
645 | } | |
646 | } | |
647 | ||
648 | if (((lane_stat&DP_LANE_STAT_CE_DONE) && | |
649 | (lane_stat&DP_LANE_STAT_SYM_LOCK)) | |
650 | && (interlane_aligned == DPCD_INTERLANE_ALIGN_DONE)) { | |
651 | debug("DP Equalizer training succeed\n"); | |
652 | ||
8b449a66 SG |
653 | f_bw = exynos_dp_get_link_bandwidth(regs); |
654 | f_lane_cnt = exynos_dp_get_lane_count(regs); | |
d2a6982f DL |
655 | |
656 | debug("DP final BandWidth : %x\n", f_bw); | |
657 | debug("DP final Lane Count : %x\n", f_lane_cnt); | |
658 | ||
8b449a66 | 659 | priv->lt_info.lt_status = DP_LT_FINISHED; |
d2a6982f | 660 | |
8b449a66 | 661 | exynos_dp_equalizer_err_link(regs, priv); |
d2a6982f DL |
662 | |
663 | } else { | |
8b449a66 | 664 | priv->lt_info.ep_loop++; |
d2a6982f | 665 | |
8b449a66 SG |
666 | if (priv->lt_info.ep_loop > MAX_EQ_LOOP) { |
667 | if (priv->lane_bw == DP_LANE_BW_2_70) { | |
d2a6982f | 668 | ret = exynos_dp_reduce_link_rate( |
8b449a66 | 669 | regs, priv); |
d2a6982f | 670 | } else { |
8b449a66 | 671 | priv->lt_info.lt_status = |
d2a6982f | 672 | DP_LT_FAIL; |
8b449a66 SG |
673 | exynos_dp_equalizer_err_link(regs, |
674 | priv); | |
d2a6982f DL |
675 | } |
676 | } else { | |
8b449a66 | 677 | for (i = 0; i < priv->lane_cnt; i++) |
d2a6982f | 678 | exynos_dp_set_lanex_pre_emphasis( |
8b449a66 | 679 | regs, lt_ctl_val[i], i); |
d2a6982f | 680 | |
8b449a66 | 681 | ret = exynos_dp_write_bytes_to_dpcd(regs, |
8c9b8dc0 SG |
682 | DPCD_TRAINING_LANE0_SET, |
683 | 4, lt_ctl_val); | |
d2a6982f DL |
684 | if (ret != EXYNOS_DP_SUCCESS) { |
685 | printf("DP set lt pattern failed\n"); | |
8b449a66 | 686 | priv->lt_info.lt_status = |
d2a6982f | 687 | DP_LT_FAIL; |
8b449a66 SG |
688 | exynos_dp_equalizer_err_link(regs, |
689 | priv); | |
d2a6982f DL |
690 | } |
691 | } | |
692 | } | |
8b449a66 SG |
693 | } else if (priv->lane_bw == DP_LANE_BW_2_70) { |
694 | ret = exynos_dp_reduce_link_rate(regs, priv); | |
d2a6982f | 695 | } else { |
8b449a66 SG |
696 | priv->lt_info.lt_status = DP_LT_FAIL; |
697 | exynos_dp_equalizer_err_link(regs, priv); | |
d2a6982f DL |
698 | } |
699 | ||
700 | return ret; | |
701 | } | |
702 | ||
8b449a66 SG |
703 | static unsigned int exynos_dp_sw_link_training(struct exynos_dp *regs, |
704 | struct exynos_dp_priv *priv) | |
d2a6982f DL |
705 | { |
706 | unsigned int ret = 0; | |
707 | int training_finished; | |
708 | ||
709 | /* Turn off unnecessary lane */ | |
8b449a66 SG |
710 | if (priv->lane_cnt == 1) |
711 | exynos_dp_set_analog_power_down(regs, CH1_BLOCK, 1); | |
d2a6982f DL |
712 | |
713 | training_finished = 0; | |
714 | ||
8b449a66 | 715 | priv->lt_info.lt_status = DP_LT_START; |
d2a6982f DL |
716 | |
717 | /* Process here */ | |
718 | while (!training_finished) { | |
8b449a66 | 719 | switch (priv->lt_info.lt_status) { |
d2a6982f | 720 | case DP_LT_START: |
8b449a66 | 721 | ret = exynos_dp_link_start(regs, priv); |
d2a6982f DL |
722 | if (ret != EXYNOS_DP_SUCCESS) { |
723 | printf("DP LT:link start failed\n"); | |
724 | return ret; | |
725 | } | |
726 | break; | |
727 | case DP_LT_CR: | |
8b449a66 SG |
728 | ret = exynos_dp_process_clock_recovery(regs, |
729 | priv); | |
d2a6982f DL |
730 | if (ret != EXYNOS_DP_SUCCESS) { |
731 | printf("DP LT:clock recovery failed\n"); | |
732 | return ret; | |
733 | } | |
734 | break; | |
735 | case DP_LT_ET: | |
8b449a66 SG |
736 | ret = exynos_dp_process_equalizer_training(regs, |
737 | priv); | |
d2a6982f DL |
738 | if (ret != EXYNOS_DP_SUCCESS) { |
739 | printf("DP LT:equalizer training failed\n"); | |
740 | return ret; | |
741 | } | |
742 | break; | |
743 | case DP_LT_FINISHED: | |
744 | training_finished = 1; | |
745 | break; | |
746 | case DP_LT_FAIL: | |
747 | return -1; | |
748 | } | |
749 | } | |
750 | ||
751 | return ret; | |
752 | } | |
753 | ||
8b449a66 SG |
754 | static unsigned int exynos_dp_set_link_train(struct exynos_dp *regs, |
755 | struct exynos_dp_priv *priv) | |
d2a6982f DL |
756 | { |
757 | unsigned int ret; | |
758 | ||
8b449a66 | 759 | exynos_dp_init_training(regs); |
d2a6982f | 760 | |
8b449a66 | 761 | ret = exynos_dp_sw_link_training(regs, priv); |
d2a6982f | 762 | if (ret != EXYNOS_DP_SUCCESS) |
a418f7e8 | 763 | printf("DP dp_sw_link_training() failed\n"); |
d2a6982f DL |
764 | |
765 | return ret; | |
766 | } | |
767 | ||
8b449a66 | 768 | static void exynos_dp_enable_scramble(struct exynos_dp *regs, |
8c9b8dc0 | 769 | unsigned int enable) |
d2a6982f DL |
770 | { |
771 | unsigned char data; | |
772 | ||
773 | if (enable) { | |
8b449a66 | 774 | exynos_dp_enable_scrambling(regs, DP_ENABLE); |
d2a6982f | 775 | |
8b449a66 | 776 | exynos_dp_read_byte_from_dpcd(regs, |
8c9b8dc0 | 777 | DPCD_TRAINING_PATTERN_SET, &data); |
8b449a66 | 778 | exynos_dp_write_byte_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET, |
d2a6982f DL |
779 | (u8)(data & ~DPCD_SCRAMBLING_DISABLED)); |
780 | } else { | |
8b449a66 SG |
781 | exynos_dp_enable_scrambling(regs, DP_DISABLE); |
782 | exynos_dp_read_byte_from_dpcd(regs, | |
8c9b8dc0 | 783 | DPCD_TRAINING_PATTERN_SET, &data); |
8b449a66 | 784 | exynos_dp_write_byte_to_dpcd(regs, DPCD_TRAINING_PATTERN_SET, |
d2a6982f DL |
785 | (u8)(data | DPCD_SCRAMBLING_DISABLED)); |
786 | } | |
787 | } | |
788 | ||
8b449a66 SG |
789 | static unsigned int exynos_dp_config_video(struct exynos_dp *regs, |
790 | struct exynos_dp_priv *priv) | |
d2a6982f DL |
791 | { |
792 | unsigned int ret = 0; | |
793 | unsigned int retry_cnt; | |
794 | ||
795 | mdelay(1); | |
796 | ||
8b449a66 | 797 | if (priv->video_info.master_mode) { |
d2a6982f DL |
798 | printf("DP does not support master mode\n"); |
799 | return -ENODEV; | |
800 | } else { | |
801 | /* debug slave */ | |
8b449a66 SG |
802 | exynos_dp_config_video_slave_mode(regs, |
803 | &priv->video_info); | |
d2a6982f DL |
804 | } |
805 | ||
8b449a66 | 806 | exynos_dp_set_video_color_format(regs, &priv->video_info); |
d2a6982f | 807 | |
8b449a66 SG |
808 | if (priv->video_info.bist_mode) { |
809 | if (exynos_dp_config_video_bist(regs, priv) != 0) | |
d2a6982f DL |
810 | return -1; |
811 | } | |
812 | ||
8b449a66 | 813 | ret = exynos_dp_get_pll_lock_status(regs); |
d2a6982f DL |
814 | if (ret != PLL_LOCKED) { |
815 | printf("DP PLL is not locked yet\n"); | |
816 | return -EIO; | |
817 | } | |
818 | ||
8b449a66 | 819 | if (priv->video_info.master_mode == 0) { |
d2a6982f DL |
820 | retry_cnt = 10; |
821 | while (retry_cnt) { | |
8b449a66 | 822 | ret = exynos_dp_is_slave_video_stream_clock_on(regs); |
d2a6982f DL |
823 | if (ret != EXYNOS_DP_SUCCESS) { |
824 | if (retry_cnt == 0) { | |
825 | printf("DP stream_clock_on failed\n"); | |
826 | return ret; | |
827 | } | |
828 | retry_cnt--; | |
829 | mdelay(1); | |
830 | } else | |
831 | break; | |
832 | } | |
833 | } | |
834 | ||
835 | /* Set to use the register calculated M/N video */ | |
8b449a66 | 836 | exynos_dp_set_video_cr_mn(regs, CALCULATED_M, 0, 0); |
d2a6982f DL |
837 | |
838 | /* For video bist, Video timing must be generated by register */ | |
8b449a66 | 839 | exynos_dp_set_video_timing_mode(regs, VIDEO_TIMING_FROM_CAPTURE); |
d2a6982f DL |
840 | |
841 | /* Enable video bist */ | |
8b449a66 SG |
842 | if (priv->video_info.bist_pattern != COLOR_RAMP && |
843 | priv->video_info.bist_pattern != BALCK_WHITE_V_LINES && | |
844 | priv->video_info.bist_pattern != COLOR_SQUARE) | |
845 | exynos_dp_enable_video_bist(regs, | |
846 | priv->video_info.bist_mode); | |
d2a6982f | 847 | else |
8b449a66 | 848 | exynos_dp_enable_video_bist(regs, DP_DISABLE); |
d2a6982f DL |
849 | |
850 | /* Disable video mute */ | |
8b449a66 | 851 | exynos_dp_enable_video_mute(regs, DP_DISABLE); |
d2a6982f DL |
852 | |
853 | /* Configure video Master or Slave mode */ | |
8b449a66 SG |
854 | exynos_dp_enable_video_master(regs, |
855 | priv->video_info.master_mode); | |
d2a6982f DL |
856 | |
857 | /* Enable video */ | |
8b449a66 | 858 | exynos_dp_start_video(regs); |
d2a6982f | 859 | |
8b449a66 | 860 | if (priv->video_info.master_mode == 0) { |
d2a6982f DL |
861 | retry_cnt = 100; |
862 | while (retry_cnt) { | |
8b449a66 | 863 | ret = exynos_dp_is_video_stream_on(regs); |
d2a6982f DL |
864 | if (ret != EXYNOS_DP_SUCCESS) { |
865 | if (retry_cnt == 0) { | |
866 | printf("DP Timeout of video stream\n"); | |
867 | return ret; | |
868 | } | |
869 | retry_cnt--; | |
870 | mdelay(5); | |
871 | } else | |
872 | break; | |
873 | } | |
874 | } | |
875 | ||
876 | return ret; | |
877 | } | |
878 | ||
bb5930d5 | 879 | static int exynos_dp_ofdata_to_platdata(struct udevice *dev) |
9947d13e | 880 | { |
bb5930d5 SG |
881 | struct exynos_dp_priv *priv = dev_get_priv(dev); |
882 | const void *blob = gd->fdt_blob; | |
883 | unsigned int node = dev->of_offset; | |
884 | fdt_addr_t addr; | |
9947d13e | 885 | |
bb5930d5 SG |
886 | addr = dev_get_addr(dev); |
887 | if (addr == FDT_ADDR_T_NONE) { | |
888 | debug("Can't get the DP base address\n"); | |
889 | return -EINVAL; | |
890 | } | |
891 | priv->regs = (struct exynos_dp *)addr; | |
8b449a66 | 892 | priv->disp_info.h_res = fdtdec_get_int(blob, node, |
9947d13e | 893 | "samsung,h-res", 0); |
8b449a66 | 894 | priv->disp_info.h_sync_width = fdtdec_get_int(blob, node, |
9947d13e | 895 | "samsung,h-sync-width", 0); |
8b449a66 | 896 | priv->disp_info.h_back_porch = fdtdec_get_int(blob, node, |
9947d13e | 897 | "samsung,h-back-porch", 0); |
8b449a66 | 898 | priv->disp_info.h_front_porch = fdtdec_get_int(blob, node, |
9947d13e | 899 | "samsung,h-front-porch", 0); |
8b449a66 | 900 | priv->disp_info.v_res = fdtdec_get_int(blob, node, |
9947d13e | 901 | "samsung,v-res", 0); |
8b449a66 | 902 | priv->disp_info.v_sync_width = fdtdec_get_int(blob, node, |
9947d13e | 903 | "samsung,v-sync-width", 0); |
8b449a66 | 904 | priv->disp_info.v_back_porch = fdtdec_get_int(blob, node, |
9947d13e | 905 | "samsung,v-back-porch", 0); |
8b449a66 | 906 | priv->disp_info.v_front_porch = fdtdec_get_int(blob, node, |
9947d13e | 907 | "samsung,v-front-porch", 0); |
8b449a66 | 908 | priv->disp_info.v_sync_rate = fdtdec_get_int(blob, node, |
9947d13e AK |
909 | "samsung,v-sync-rate", 0); |
910 | ||
8b449a66 | 911 | priv->lt_info.lt_status = fdtdec_get_int(blob, node, |
9947d13e AK |
912 | "samsung,lt-status", 0); |
913 | ||
8b449a66 | 914 | priv->video_info.master_mode = fdtdec_get_int(blob, node, |
9947d13e | 915 | "samsung,master-mode", 0); |
8b449a66 | 916 | priv->video_info.bist_mode = fdtdec_get_int(blob, node, |
9947d13e | 917 | "samsung,bist-mode", 0); |
8b449a66 | 918 | priv->video_info.bist_pattern = fdtdec_get_int(blob, node, |
9947d13e | 919 | "samsung,bist-pattern", 0); |
8b449a66 | 920 | priv->video_info.h_sync_polarity = fdtdec_get_int(blob, node, |
9947d13e | 921 | "samsung,h-sync-polarity", 0); |
8b449a66 | 922 | priv->video_info.v_sync_polarity = fdtdec_get_int(blob, node, |
9947d13e | 923 | "samsung,v-sync-polarity", 0); |
8b449a66 | 924 | priv->video_info.interlaced = fdtdec_get_int(blob, node, |
9947d13e | 925 | "samsung,interlaced", 0); |
8b449a66 | 926 | priv->video_info.color_space = fdtdec_get_int(blob, node, |
9947d13e | 927 | "samsung,color-space", 0); |
8b449a66 | 928 | priv->video_info.dynamic_range = fdtdec_get_int(blob, node, |
9947d13e | 929 | "samsung,dynamic-range", 0); |
8b449a66 | 930 | priv->video_info.ycbcr_coeff = fdtdec_get_int(blob, node, |
9947d13e | 931 | "samsung,ycbcr-coeff", 0); |
8b449a66 | 932 | priv->video_info.color_depth = fdtdec_get_int(blob, node, |
9947d13e AK |
933 | "samsung,color-depth", 0); |
934 | return 0; | |
935 | } | |
9947d13e | 936 | |
bb5930d5 | 937 | static int exynos_dp_bridge_init(struct udevice *dev) |
d2a6982f | 938 | { |
bb5930d5 SG |
939 | const int max_tries = 10; |
940 | int num_tries; | |
941 | int ret; | |
d2a6982f | 942 | |
bb5930d5 SG |
943 | debug("%s\n", __func__); |
944 | ret = video_bridge_attach(dev); | |
945 | if (ret) { | |
946 | debug("video bridge init failed: %d\n", ret); | |
947 | return ret; | |
d2a6982f DL |
948 | } |
949 | ||
bb5930d5 SG |
950 | /* |
951 | * We need to wait for 90ms after bringing up the bridge since there | |
952 | * is a phantom "high" on the HPD chip during its bootup. The phantom | |
953 | * high comes within 7ms of de-asserting PD and persists for at least | |
954 | * 15ms. The real high comes roughly 50ms after PD is de-asserted. The | |
955 | * phantom high makes it hard for us to know when the NXP chip is up. | |
956 | */ | |
957 | mdelay(90); | |
d2a6982f | 958 | |
bb5930d5 SG |
959 | for (num_tries = 0; num_tries < max_tries; num_tries++) { |
960 | /* Check HPD. If it's high, or we don't have it, all is well */ | |
961 | ret = video_bridge_check_attached(dev); | |
962 | if (!ret || ret == -ENOENT) | |
963 | return 0; | |
8c9b8dc0 | 964 | |
bb5930d5 SG |
965 | debug("%s: eDP bridge failed to come up; try %d of %d\n", |
966 | __func__, num_tries, max_tries); | |
967 | } | |
968 | ||
969 | /* Immediately go into bridge reset if the hp line is not high */ | |
970 | return -EIO; | |
971 | } | |
972 | ||
973 | static int exynos_dp_bridge_setup(const void *blob) | |
974 | { | |
975 | const int max_tries = 2; | |
976 | int num_tries; | |
977 | struct udevice *dev; | |
978 | int ret; | |
979 | ||
980 | /* Configure I2C registers for Parade bridge */ | |
981 | ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &dev); | |
982 | if (ret) { | |
983 | debug("video bridge init failed: %d\n", ret); | |
984 | return ret; | |
985 | } | |
986 | ||
987 | if (strncmp(dev->driver->name, "parade", 6)) { | |
988 | /* Mux HPHPD to the special hotplug detect mode */ | |
989 | exynos_pinmux_config(PERIPH_ID_DPHPD, 0); | |
990 | } | |
991 | ||
992 | for (num_tries = 0; num_tries < max_tries; num_tries++) { | |
993 | ret = exynos_dp_bridge_init(dev); | |
994 | if (!ret) | |
995 | return 0; | |
996 | if (num_tries == max_tries - 1) | |
997 | break; | |
998 | ||
999 | /* | |
1000 | * If we're here, the bridge chip failed to initialise. | |
1001 | * Power down the bridge in an attempt to reset. | |
1002 | */ | |
1003 | video_bridge_set_active(dev, false); | |
1004 | ||
1005 | /* | |
1006 | * Arbitrarily wait 300ms here with DP_N low. Don't know for | |
1007 | * sure how long we should wait, but we're being paranoid. | |
1008 | */ | |
1009 | mdelay(300); | |
1010 | } | |
beded3d1 | 1011 | |
bb5930d5 SG |
1012 | return ret; |
1013 | } | |
1014 | int exynos_dp_enable(struct udevice *dev, int panel_bpp, | |
1015 | const struct display_timing *timing) | |
1016 | { | |
1017 | struct exynos_dp_priv *priv = dev_get_priv(dev); | |
1018 | struct exynos_dp *regs = priv->regs; | |
1019 | unsigned int ret; | |
1020 | ||
1021 | debug("%s: start\n", __func__); | |
8b449a66 | 1022 | exynos_dp_disp_info(&priv->disp_info); |
d2a6982f | 1023 | |
bb5930d5 SG |
1024 | ret = exynos_dp_bridge_setup(gd->fdt_blob); |
1025 | if (ret && ret != -ENODEV) | |
1026 | printf("LCD bridge failed to enable: %d\n", ret); | |
1027 | ||
7eb860df | 1028 | exynos_dp_phy_ctrl(1); |
d2a6982f | 1029 | |
8b449a66 | 1030 | ret = exynos_dp_init_dp(regs); |
d2a6982f DL |
1031 | if (ret != EXYNOS_DP_SUCCESS) { |
1032 | printf("DP exynos_dp_init_dp() failed\n"); | |
1033 | return ret; | |
1034 | } | |
1035 | ||
8b449a66 | 1036 | ret = exynos_dp_handle_edid(regs, priv); |
d2a6982f DL |
1037 | if (ret != EXYNOS_DP_SUCCESS) { |
1038 | printf("EDP handle_edid fail\n"); | |
1039 | return ret; | |
1040 | } | |
1041 | ||
8b449a66 | 1042 | ret = exynos_dp_set_link_train(regs, priv); |
d2a6982f DL |
1043 | if (ret != EXYNOS_DP_SUCCESS) { |
1044 | printf("DP link training fail\n"); | |
1045 | return ret; | |
1046 | } | |
1047 | ||
8b449a66 SG |
1048 | exynos_dp_enable_scramble(regs, DP_ENABLE); |
1049 | exynos_dp_enable_rx_to_enhanced_mode(regs, DP_ENABLE); | |
1050 | exynos_dp_enable_enhanced_mode(regs, DP_ENABLE); | |
d2a6982f | 1051 | |
8b449a66 SG |
1052 | exynos_dp_set_link_bandwidth(regs, priv->lane_bw); |
1053 | exynos_dp_set_lane_count(regs, priv->lane_cnt); | |
d2a6982f | 1054 | |
8b449a66 SG |
1055 | exynos_dp_init_video(regs); |
1056 | ret = exynos_dp_config_video(regs, priv); | |
d2a6982f DL |
1057 | if (ret != EXYNOS_DP_SUCCESS) { |
1058 | printf("Exynos DP init failed\n"); | |
1059 | return ret; | |
1060 | } | |
1061 | ||
129c942f | 1062 | debug("Exynos DP init done\n"); |
d2a6982f DL |
1063 | |
1064 | return ret; | |
1065 | } | |
bb5930d5 SG |
1066 | |
1067 | ||
1068 | static const struct dm_display_ops exynos_dp_ops = { | |
1069 | .enable = exynos_dp_enable, | |
1070 | }; | |
1071 | ||
1072 | static const struct udevice_id exynos_dp_ids[] = { | |
1073 | { .compatible = "samsung,exynos5-dp" }, | |
1074 | { } | |
1075 | }; | |
1076 | ||
1077 | U_BOOT_DRIVER(exynos_dp) = { | |
1078 | .name = "eexynos_dp", | |
1079 | .id = UCLASS_DISPLAY, | |
1080 | .of_match = exynos_dp_ids, | |
1081 | .ops = &exynos_dp_ops, | |
1082 | .ofdata_to_platdata = exynos_dp_ofdata_to_platdata, | |
1083 | .priv_auto_alloc_size = sizeof(struct exynos_dp_priv), | |
1084 | }; |