1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd |
4 | * Author: Chris Zhong <zyw@rock-chips.com> |
5 | */ |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/device.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/io.h> |
11 | #include <linux/iopoll.h> |
12 | #include <linux/reset.h> |
13 | |
14 | #include "cdn-dp-core.h" |
15 | #include "cdn-dp-reg.h" |
16 | |
17 | #define CDN_DP_SPDIF_CLK 200000000 |
18 | #define FW_ALIVE_TIMEOUT_US 1000000 |
19 | #define MAILBOX_RETRY_US 1000 |
20 | #define MAILBOX_TIMEOUT_US 5000000 |
21 | #define LINK_TRAINING_RETRY_MS 20 |
22 | #define LINK_TRAINING_TIMEOUT_MS 500 |
23 | |
24 | void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk) |
25 | { |
26 | writel(val: clk / 1000000, addr: dp->regs + SW_CLK_H); |
27 | } |
28 | |
29 | void cdn_dp_clock_reset(struct cdn_dp_device *dp) |
30 | { |
31 | u32 val; |
32 | |
33 | val = DPTX_FRMR_DATA_CLK_RSTN_EN | |
34 | DPTX_FRMR_DATA_CLK_EN | |
35 | DPTX_PHY_DATA_RSTN_EN | |
36 | DPTX_PHY_DATA_CLK_EN | |
37 | DPTX_PHY_CHAR_RSTN_EN | |
38 | DPTX_PHY_CHAR_CLK_EN | |
39 | SOURCE_AUX_SYS_CLK_RSTN_EN | |
40 | SOURCE_AUX_SYS_CLK_EN | |
41 | DPTX_SYS_CLK_RSTN_EN | |
42 | DPTX_SYS_CLK_EN | |
43 | CFG_DPTX_VIF_CLK_RSTN_EN | |
44 | CFG_DPTX_VIF_CLK_EN; |
45 | writel(val, addr: dp->regs + SOURCE_DPTX_CAR); |
46 | |
47 | val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN; |
48 | writel(val, addr: dp->regs + SOURCE_PHY_CAR); |
49 | |
50 | val = SOURCE_PKT_SYS_RSTN_EN | |
51 | SOURCE_PKT_SYS_CLK_EN | |
52 | SOURCE_PKT_DATA_RSTN_EN | |
53 | SOURCE_PKT_DATA_CLK_EN; |
54 | writel(val, addr: dp->regs + SOURCE_PKT_CAR); |
55 | |
56 | val = SPDIF_CDR_CLK_RSTN_EN | |
57 | SPDIF_CDR_CLK_EN | |
58 | SOURCE_AIF_SYS_RSTN_EN | |
59 | SOURCE_AIF_SYS_CLK_EN | |
60 | SOURCE_AIF_CLK_RSTN_EN | |
61 | SOURCE_AIF_CLK_EN; |
62 | writel(val, addr: dp->regs + SOURCE_AIF_CAR); |
63 | |
64 | val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN | |
65 | SOURCE_CIPHER_SYS_CLK_EN | |
66 | SOURCE_CIPHER_CHAR_CLK_RSTN_EN | |
67 | SOURCE_CIPHER_CHAR_CLK_EN; |
68 | writel(val, addr: dp->regs + SOURCE_CIPHER_CAR); |
69 | |
70 | val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN | |
71 | SOURCE_CRYPTO_SYS_CLK_EN; |
72 | writel(val, addr: dp->regs + SOURCE_CRYPTO_CAR); |
73 | |
74 | /* enable Mailbox and PIF interrupt */ |
75 | writel(val: 0, addr: dp->regs + APB_INT_MASK); |
76 | } |
77 | |
78 | static int cdn_dp_mailbox_read(struct cdn_dp_device *dp) |
79 | { |
80 | int val, ret; |
81 | |
82 | ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR, |
83 | val, !val, MAILBOX_RETRY_US, |
84 | MAILBOX_TIMEOUT_US); |
85 | if (ret < 0) |
86 | return ret; |
87 | |
88 | return readl(addr: dp->regs + MAILBOX0_RD_DATA) & 0xff; |
89 | } |
90 | |
91 | static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val) |
92 | { |
93 | int ret, full; |
94 | |
95 | ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR, |
96 | full, !full, MAILBOX_RETRY_US, |
97 | MAILBOX_TIMEOUT_US); |
98 | if (ret < 0) |
99 | return ret; |
100 | |
101 | writel(val, addr: dp->regs + MAILBOX0_WR_DATA); |
102 | |
103 | return 0; |
104 | } |
105 | |
106 | static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp, |
107 | u8 module_id, u8 opcode, |
108 | u16 req_size) |
109 | { |
110 | u32 mbox_size, i; |
111 | u8 [4]; |
112 | int ret; |
113 | |
114 | /* read the header of the message */ |
115 | for (i = 0; i < 4; i++) { |
116 | ret = cdn_dp_mailbox_read(dp); |
117 | if (ret < 0) |
118 | return ret; |
119 | |
120 | header[i] = ret; |
121 | } |
122 | |
123 | mbox_size = (header[2] << 8) | header[3]; |
124 | |
125 | if (opcode != header[0] || module_id != header[1] || |
126 | req_size != mbox_size) { |
127 | /* |
128 | * If the message in mailbox is not what we want, we need to |
129 | * clear the mailbox by reading its contents. |
130 | */ |
131 | for (i = 0; i < mbox_size; i++) |
132 | if (cdn_dp_mailbox_read(dp) < 0) |
133 | break; |
134 | |
135 | return -EINVAL; |
136 | } |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp, |
142 | u8 *buff, u16 buff_size) |
143 | { |
144 | u32 i; |
145 | int ret; |
146 | |
147 | for (i = 0; i < buff_size; i++) { |
148 | ret = cdn_dp_mailbox_read(dp); |
149 | if (ret < 0) |
150 | return ret; |
151 | |
152 | buff[i] = ret; |
153 | } |
154 | |
155 | return 0; |
156 | } |
157 | |
158 | static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id, |
159 | u8 opcode, u16 size, u8 *message) |
160 | { |
161 | u8 [4]; |
162 | int ret, i; |
163 | |
164 | header[0] = opcode; |
165 | header[1] = module_id; |
166 | header[2] = (size >> 8) & 0xff; |
167 | header[3] = size & 0xff; |
168 | |
169 | for (i = 0; i < 4; i++) { |
170 | ret = cdp_dp_mailbox_write(dp, val: header[i]); |
171 | if (ret) |
172 | return ret; |
173 | } |
174 | |
175 | for (i = 0; i < size; i++) { |
176 | ret = cdp_dp_mailbox_write(dp, val: message[i]); |
177 | if (ret) |
178 | return ret; |
179 | } |
180 | |
181 | return 0; |
182 | } |
183 | |
184 | static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val) |
185 | { |
186 | u8 msg[6]; |
187 | |
188 | msg[0] = (addr >> 8) & 0xff; |
189 | msg[1] = addr & 0xff; |
190 | msg[2] = (val >> 24) & 0xff; |
191 | msg[3] = (val >> 16) & 0xff; |
192 | msg[4] = (val >> 8) & 0xff; |
193 | msg[5] = val & 0xff; |
194 | return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER, |
195 | size: sizeof(msg), message: msg); |
196 | } |
197 | |
198 | static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, |
199 | u8 start_bit, u8 bits_no, u32 val) |
200 | { |
201 | u8 field[8]; |
202 | |
203 | field[0] = (addr >> 8) & 0xff; |
204 | field[1] = addr & 0xff; |
205 | field[2] = start_bit; |
206 | field[3] = bits_no; |
207 | field[4] = (val >> 24) & 0xff; |
208 | field[5] = (val >> 16) & 0xff; |
209 | field[6] = (val >> 8) & 0xff; |
210 | field[7] = val & 0xff; |
211 | |
212 | return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD, |
213 | size: sizeof(field), message: field); |
214 | } |
215 | |
216 | int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len) |
217 | { |
218 | u8 msg[5], reg[5]; |
219 | int ret; |
220 | |
221 | msg[0] = (len >> 8) & 0xff; |
222 | msg[1] = len & 0xff; |
223 | msg[2] = (addr >> 16) & 0xff; |
224 | msg[3] = (addr >> 8) & 0xff; |
225 | msg[4] = addr & 0xff; |
226 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD, |
227 | size: sizeof(msg), message: msg); |
228 | if (ret) |
229 | goto err_dpcd_read; |
230 | |
231 | ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, |
232 | DPTX_READ_DPCD, |
233 | req_size: sizeof(reg) + len); |
234 | if (ret) |
235 | goto err_dpcd_read; |
236 | |
237 | ret = cdn_dp_mailbox_read_receive(dp, buff: reg, buff_size: sizeof(reg)); |
238 | if (ret) |
239 | goto err_dpcd_read; |
240 | |
241 | ret = cdn_dp_mailbox_read_receive(dp, buff: data, buff_size: len); |
242 | |
243 | err_dpcd_read: |
244 | return ret; |
245 | } |
246 | |
247 | int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value) |
248 | { |
249 | u8 msg[6], reg[5]; |
250 | int ret; |
251 | |
252 | msg[0] = 0; |
253 | msg[1] = 1; |
254 | msg[2] = (addr >> 16) & 0xff; |
255 | msg[3] = (addr >> 8) & 0xff; |
256 | msg[4] = addr & 0xff; |
257 | msg[5] = value; |
258 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD, |
259 | size: sizeof(msg), message: msg); |
260 | if (ret) |
261 | goto err_dpcd_write; |
262 | |
263 | ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, |
264 | DPTX_WRITE_DPCD, req_size: sizeof(reg)); |
265 | if (ret) |
266 | goto err_dpcd_write; |
267 | |
268 | ret = cdn_dp_mailbox_read_receive(dp, buff: reg, buff_size: sizeof(reg)); |
269 | if (ret) |
270 | goto err_dpcd_write; |
271 | |
272 | if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) |
273 | ret = -EINVAL; |
274 | |
275 | err_dpcd_write: |
276 | if (ret) |
277 | DRM_DEV_ERROR(dp->dev, "dpcd write failed: %d\n" , ret); |
278 | return ret; |
279 | } |
280 | |
281 | int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, |
282 | u32 i_size, const u32 *d_mem, u32 d_size) |
283 | { |
284 | u32 reg; |
285 | int i, ret; |
286 | |
287 | /* reset ucpu before load firmware*/ |
288 | writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET, |
289 | addr: dp->regs + APB_CTRL); |
290 | |
291 | for (i = 0; i < i_size; i += 4) |
292 | writel(val: *i_mem++, addr: dp->regs + ADDR_IMEM + i); |
293 | |
294 | for (i = 0; i < d_size; i += 4) |
295 | writel(val: *d_mem++, addr: dp->regs + ADDR_DMEM + i); |
296 | |
297 | /* un-reset ucpu */ |
298 | writel(val: 0, addr: dp->regs + APB_CTRL); |
299 | |
300 | /* check the keep alive register to make sure fw working */ |
301 | ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE, |
302 | reg, reg, 2000, FW_ALIVE_TIMEOUT_US); |
303 | if (ret < 0) { |
304 | DRM_DEV_ERROR(dp->dev, "failed to loaded the FW reg = %x\n" , |
305 | reg); |
306 | return -EINVAL; |
307 | } |
308 | |
309 | reg = readl(addr: dp->regs + VER_L) & 0xff; |
310 | dp->fw_version = reg; |
311 | reg = readl(addr: dp->regs + VER_H) & 0xff; |
312 | dp->fw_version |= reg << 8; |
313 | reg = readl(addr: dp->regs + VER_LIB_L_ADDR) & 0xff; |
314 | dp->fw_version |= reg << 16; |
315 | reg = readl(addr: dp->regs + VER_LIB_H_ADDR) & 0xff; |
316 | dp->fw_version |= reg << 24; |
317 | |
318 | DRM_DEV_DEBUG(dp->dev, "firmware version: %x\n" , dp->fw_version); |
319 | |
320 | return 0; |
321 | } |
322 | |
323 | int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable) |
324 | { |
325 | u8 msg[5]; |
326 | int ret, i; |
327 | |
328 | msg[0] = GENERAL_MAIN_CONTROL; |
329 | msg[1] = MB_MODULE_ID_GENERAL; |
330 | msg[2] = 0; |
331 | msg[3] = 1; |
332 | msg[4] = enable ? FW_ACTIVE : FW_STANDBY; |
333 | |
334 | for (i = 0; i < sizeof(msg); i++) { |
335 | ret = cdp_dp_mailbox_write(dp, val: msg[i]); |
336 | if (ret) |
337 | goto err_set_firmware_active; |
338 | } |
339 | |
340 | /* read the firmware state */ |
341 | for (i = 0; i < sizeof(msg); i++) { |
342 | ret = cdn_dp_mailbox_read(dp); |
343 | if (ret < 0) |
344 | goto err_set_firmware_active; |
345 | |
346 | msg[i] = ret; |
347 | } |
348 | |
349 | ret = 0; |
350 | |
351 | err_set_firmware_active: |
352 | if (ret < 0) |
353 | DRM_DEV_ERROR(dp->dev, "set firmware active failed\n" ); |
354 | return ret; |
355 | } |
356 | |
357 | int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip) |
358 | { |
359 | u8 msg[8]; |
360 | int ret; |
361 | |
362 | msg[0] = CDN_DP_MAX_LINK_RATE; |
363 | msg[1] = lanes | SCRAMBLER_EN; |
364 | msg[2] = VOLTAGE_LEVEL_2; |
365 | msg[3] = PRE_EMPHASIS_LEVEL_3; |
366 | msg[4] = PTS1 | PTS2 | PTS3 | PTS4; |
367 | msg[5] = FAST_LT_NOT_SUPPORT; |
368 | msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL; |
369 | msg[7] = ENHANCED; |
370 | |
371 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, |
372 | DPTX_SET_HOST_CAPABILITIES, |
373 | size: sizeof(msg), message: msg); |
374 | if (ret) |
375 | goto err_set_host_cap; |
376 | |
377 | ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL, |
378 | AUX_HOST_INVERT); |
379 | |
380 | err_set_host_cap: |
381 | if (ret) |
382 | DRM_DEV_ERROR(dp->dev, "set host cap failed: %d\n" , ret); |
383 | return ret; |
384 | } |
385 | |
386 | int cdn_dp_event_config(struct cdn_dp_device *dp) |
387 | { |
388 | u8 msg[5]; |
389 | int ret; |
390 | |
391 | memset(msg, 0, sizeof(msg)); |
392 | |
393 | msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING; |
394 | |
395 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT, |
396 | size: sizeof(msg), message: msg); |
397 | if (ret) |
398 | DRM_DEV_ERROR(dp->dev, "set event config failed: %d\n" , ret); |
399 | |
400 | return ret; |
401 | } |
402 | |
403 | u32 cdn_dp_get_event(struct cdn_dp_device *dp) |
404 | { |
405 | return readl(addr: dp->regs + SW_EVENTS0); |
406 | } |
407 | |
408 | int cdn_dp_get_hpd_status(struct cdn_dp_device *dp) |
409 | { |
410 | u8 status; |
411 | int ret; |
412 | |
413 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE, |
414 | size: 0, NULL); |
415 | if (ret) |
416 | goto err_get_hpd; |
417 | |
418 | ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, |
419 | DPTX_HPD_STATE, req_size: sizeof(status)); |
420 | if (ret) |
421 | goto err_get_hpd; |
422 | |
423 | ret = cdn_dp_mailbox_read_receive(dp, buff: &status, buff_size: sizeof(status)); |
424 | if (ret) |
425 | goto err_get_hpd; |
426 | |
427 | return status; |
428 | |
429 | err_get_hpd: |
430 | DRM_DEV_ERROR(dp->dev, "get hpd status failed: %d\n" , ret); |
431 | return ret; |
432 | } |
433 | |
434 | int cdn_dp_get_edid_block(void *data, u8 *edid, |
435 | unsigned int block, size_t length) |
436 | { |
437 | struct cdn_dp_device *dp = data; |
438 | u8 msg[2], reg[2], i; |
439 | int ret; |
440 | |
441 | for (i = 0; i < 4; i++) { |
442 | msg[0] = block / 2; |
443 | msg[1] = block % 2; |
444 | |
445 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID, |
446 | size: sizeof(msg), message: msg); |
447 | if (ret) |
448 | continue; |
449 | |
450 | ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, |
451 | DPTX_GET_EDID, |
452 | req_size: sizeof(reg) + length); |
453 | if (ret) |
454 | continue; |
455 | |
456 | ret = cdn_dp_mailbox_read_receive(dp, buff: reg, buff_size: sizeof(reg)); |
457 | if (ret) |
458 | continue; |
459 | |
460 | ret = cdn_dp_mailbox_read_receive(dp, buff: edid, buff_size: length); |
461 | if (ret) |
462 | continue; |
463 | |
464 | if (reg[0] == length && reg[1] == block / 2) |
465 | break; |
466 | } |
467 | |
468 | if (ret) |
469 | DRM_DEV_ERROR(dp->dev, "get block[%d] edid failed: %d\n" , block, |
470 | ret); |
471 | |
472 | return ret; |
473 | } |
474 | |
475 | static int cdn_dp_training_start(struct cdn_dp_device *dp) |
476 | { |
477 | unsigned long timeout; |
478 | u8 msg, event[2]; |
479 | int ret; |
480 | |
481 | msg = LINK_TRAINING_RUN; |
482 | |
483 | /* start training */ |
484 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL, |
485 | size: sizeof(msg), message: &msg); |
486 | if (ret) |
487 | goto err_training_start; |
488 | |
489 | timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); |
490 | while (time_before(jiffies, timeout)) { |
491 | msleep(LINK_TRAINING_RETRY_MS); |
492 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, |
493 | DPTX_READ_EVENT, size: 0, NULL); |
494 | if (ret) |
495 | goto err_training_start; |
496 | |
497 | ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, |
498 | DPTX_READ_EVENT, |
499 | req_size: sizeof(event)); |
500 | if (ret) |
501 | goto err_training_start; |
502 | |
503 | ret = cdn_dp_mailbox_read_receive(dp, buff: event, buff_size: sizeof(event)); |
504 | if (ret) |
505 | goto err_training_start; |
506 | |
507 | if (event[1] & EQ_PHASE_FINISHED) |
508 | return 0; |
509 | } |
510 | |
511 | ret = -ETIMEDOUT; |
512 | |
513 | err_training_start: |
514 | DRM_DEV_ERROR(dp->dev, "training failed: %d\n" , ret); |
515 | return ret; |
516 | } |
517 | |
518 | static int cdn_dp_get_training_status(struct cdn_dp_device *dp) |
519 | { |
520 | u8 status[10]; |
521 | int ret; |
522 | |
523 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT, |
524 | size: 0, NULL); |
525 | if (ret) |
526 | goto err_get_training_status; |
527 | |
528 | ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, |
529 | DPTX_READ_LINK_STAT, |
530 | req_size: sizeof(status)); |
531 | if (ret) |
532 | goto err_get_training_status; |
533 | |
534 | ret = cdn_dp_mailbox_read_receive(dp, buff: status, buff_size: sizeof(status)); |
535 | if (ret) |
536 | goto err_get_training_status; |
537 | |
538 | dp->max_rate = drm_dp_bw_code_to_link_rate(link_bw: status[0]); |
539 | dp->max_lanes = status[1]; |
540 | |
541 | err_get_training_status: |
542 | if (ret) |
543 | DRM_DEV_ERROR(dp->dev, "get training status failed: %d\n" , ret); |
544 | return ret; |
545 | } |
546 | |
547 | int cdn_dp_train_link(struct cdn_dp_device *dp) |
548 | { |
549 | int ret; |
550 | |
551 | ret = cdn_dp_training_start(dp); |
552 | if (ret) { |
553 | DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n" , ret); |
554 | return ret; |
555 | } |
556 | |
557 | ret = cdn_dp_get_training_status(dp); |
558 | if (ret) { |
559 | DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n" , ret); |
560 | return ret; |
561 | } |
562 | |
563 | DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n" , dp->max_rate, |
564 | dp->max_lanes); |
565 | return ret; |
566 | } |
567 | |
568 | int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active) |
569 | { |
570 | u8 msg; |
571 | int ret; |
572 | |
573 | msg = !!active; |
574 | |
575 | ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO, |
576 | size: sizeof(msg), message: &msg); |
577 | if (ret) |
578 | DRM_DEV_ERROR(dp->dev, "set video status failed: %d\n" , ret); |
579 | |
580 | return ret; |
581 | } |
582 | |
583 | static int cdn_dp_get_msa_misc(struct video_info *video, |
584 | struct drm_display_mode *mode) |
585 | { |
586 | u32 msa_misc; |
587 | u8 val[2] = {0}; |
588 | |
589 | switch (video->color_fmt) { |
590 | case PXL_RGB: |
591 | case Y_ONLY: |
592 | val[0] = 0; |
593 | break; |
594 | /* set YUV default color space conversion to BT601 */ |
595 | case YCBCR_4_4_4: |
596 | val[0] = 6 + BT_601 * 8; |
597 | break; |
598 | case YCBCR_4_2_2: |
599 | val[0] = 5 + BT_601 * 8; |
600 | break; |
601 | case YCBCR_4_2_0: |
602 | val[0] = 5; |
603 | break; |
604 | } |
605 | |
606 | switch (video->color_depth) { |
607 | case 6: |
608 | val[1] = 0; |
609 | break; |
610 | case 8: |
611 | val[1] = 1; |
612 | break; |
613 | case 10: |
614 | val[1] = 2; |
615 | break; |
616 | case 12: |
617 | val[1] = 3; |
618 | break; |
619 | case 16: |
620 | val[1] = 4; |
621 | break; |
622 | } |
623 | |
624 | msa_misc = 2 * val[0] + 32 * val[1] + |
625 | ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0); |
626 | |
627 | return msa_misc; |
628 | } |
629 | |
630 | int cdn_dp_config_video(struct cdn_dp_device *dp) |
631 | { |
632 | struct video_info *video = &dp->video_info; |
633 | struct drm_display_mode *mode = &dp->mode; |
634 | u64 symbol; |
635 | u32 val, link_rate, rem; |
636 | u8 bit_per_pix, tu_size_reg = TU_SIZE; |
637 | int ret; |
638 | |
639 | bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ? |
640 | (video->color_depth * 2) : (video->color_depth * 3); |
641 | |
642 | link_rate = dp->max_rate / 1000; |
643 | |
644 | ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE); |
645 | if (ret) |
646 | goto err_config_video; |
647 | |
648 | ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, val: 0); |
649 | if (ret) |
650 | goto err_config_video; |
651 | |
652 | /* |
653 | * get a best tu_size and valid symbol: |
654 | * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32 |
655 | * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes) |
656 | * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set |
657 | * TU += 2 and repeat 2nd step. |
658 | */ |
659 | do { |
660 | tu_size_reg += 2; |
661 | symbol = (u64)tu_size_reg * mode->clock * bit_per_pix; |
662 | do_div(symbol, dp->max_lanes * link_rate * 8); |
663 | rem = do_div(symbol, 1000); |
664 | if (tu_size_reg > 64) { |
665 | ret = -EINVAL; |
666 | DRM_DEV_ERROR(dp->dev, |
667 | "tu error, clk:%d, lanes:%d, rate:%d\n" , |
668 | mode->clock, dp->max_lanes, link_rate); |
669 | goto err_config_video; |
670 | } |
671 | } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || |
672 | (rem > 850) || (rem < 100)); |
673 | |
674 | val = symbol + (tu_size_reg << 8); |
675 | val |= TU_CNT_RST_EN; |
676 | ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val); |
677 | if (ret) |
678 | goto err_config_video; |
679 | |
680 | /* set the FIFO Buffer size */ |
681 | val = div_u64(dividend: mode->clock * (symbol + 1), divisor: 1000) + link_rate; |
682 | val /= (dp->max_lanes * link_rate); |
683 | val = div_u64(dividend: 8 * (symbol + 1), divisor: bit_per_pix) - val; |
684 | val += 2; |
685 | ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val); |
686 | |
687 | switch (video->color_depth) { |
688 | case 6: |
689 | val = BCS_6; |
690 | break; |
691 | case 8: |
692 | val = BCS_8; |
693 | break; |
694 | case 10: |
695 | val = BCS_10; |
696 | break; |
697 | case 12: |
698 | val = BCS_12; |
699 | break; |
700 | case 16: |
701 | val = BCS_16; |
702 | break; |
703 | } |
704 | |
705 | val += video->color_fmt << 8; |
706 | ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val); |
707 | if (ret) |
708 | goto err_config_video; |
709 | |
710 | val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0; |
711 | val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0; |
712 | ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val); |
713 | if (ret) |
714 | goto err_config_video; |
715 | |
716 | val = (mode->hsync_start - mode->hdisplay) << 16; |
717 | val |= mode->htotal - mode->hsync_end; |
718 | ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val); |
719 | if (ret) |
720 | goto err_config_video; |
721 | |
722 | val = mode->hdisplay * bit_per_pix / 8; |
723 | ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val); |
724 | if (ret) |
725 | goto err_config_video; |
726 | |
727 | val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16); |
728 | ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val); |
729 | if (ret) |
730 | goto err_config_video; |
731 | |
732 | val = mode->hsync_end - mode->hsync_start; |
733 | val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15); |
734 | ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val); |
735 | if (ret) |
736 | goto err_config_video; |
737 | |
738 | val = mode->vtotal; |
739 | val |= (mode->vtotal - mode->vsync_start) << 16; |
740 | ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val); |
741 | if (ret) |
742 | goto err_config_video; |
743 | |
744 | val = mode->vsync_end - mode->vsync_start; |
745 | val |= (mode->vdisplay << 16) | (video->v_sync_polarity << 15); |
746 | ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val); |
747 | if (ret) |
748 | goto err_config_video; |
749 | |
750 | val = cdn_dp_get_msa_misc(video, mode); |
751 | ret = cdn_dp_reg_write(dp, MSA_MISC, val); |
752 | if (ret) |
753 | goto err_config_video; |
754 | |
755 | ret = cdn_dp_reg_write(dp, STREAM_CONFIG, val: 1); |
756 | if (ret) |
757 | goto err_config_video; |
758 | |
759 | val = mode->hsync_end - mode->hsync_start; |
760 | val |= mode->hdisplay << 16; |
761 | ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val); |
762 | if (ret) |
763 | goto err_config_video; |
764 | |
765 | val = mode->vdisplay; |
766 | val |= (mode->vtotal - mode->vsync_start) << 16; |
767 | ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val); |
768 | if (ret) |
769 | goto err_config_video; |
770 | |
771 | val = mode->vtotal; |
772 | ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val); |
773 | if (ret) |
774 | goto err_config_video; |
775 | |
776 | ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, start_bit: 2, bits_no: 1, val: 0); |
777 | |
778 | err_config_video: |
779 | if (ret) |
780 | DRM_DEV_ERROR(dp->dev, "config video failed: %d\n" , ret); |
781 | return ret; |
782 | } |
783 | |
784 | int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio) |
785 | { |
786 | int ret; |
787 | |
788 | ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, val: 0); |
789 | if (ret) { |
790 | DRM_DEV_ERROR(dp->dev, "audio stop failed: %d\n" , ret); |
791 | return ret; |
792 | } |
793 | |
794 | writel(val: 0, addr: dp->regs + SPDIF_CTRL_ADDR); |
795 | |
796 | /* clearn the audio config and reset */ |
797 | writel(val: 0, addr: dp->regs + AUDIO_SRC_CNTL); |
798 | writel(val: 0, addr: dp->regs + AUDIO_SRC_CNFG); |
799 | writel(AUDIO_SW_RST, addr: dp->regs + AUDIO_SRC_CNTL); |
800 | writel(val: 0, addr: dp->regs + AUDIO_SRC_CNTL); |
801 | |
802 | /* reset smpl2pckt component */ |
803 | writel(val: 0, addr: dp->regs + SMPL2PKT_CNTL); |
804 | writel(AUDIO_SW_RST, addr: dp->regs + SMPL2PKT_CNTL); |
805 | writel(val: 0, addr: dp->regs + SMPL2PKT_CNTL); |
806 | |
807 | /* reset FIFO */ |
808 | writel(AUDIO_SW_RST, addr: dp->regs + FIFO_CNTL); |
809 | writel(val: 0, addr: dp->regs + FIFO_CNTL); |
810 | |
811 | if (audio->format == AFMT_SPDIF) |
812 | clk_disable_unprepare(clk: dp->spdif_clk); |
813 | |
814 | return 0; |
815 | } |
816 | |
817 | int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable) |
818 | { |
819 | int ret; |
820 | |
821 | ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, start_bit: 4, bits_no: 1, val: enable); |
822 | if (ret) |
823 | DRM_DEV_ERROR(dp->dev, "audio mute failed: %d\n" , ret); |
824 | |
825 | return ret; |
826 | } |
827 | |
828 | static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, |
829 | struct audio_info *audio) |
830 | { |
831 | int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; |
832 | u32 val; |
833 | |
834 | if (audio->channels == 2) { |
835 | if (dp->max_lanes == 1) |
836 | sub_pckt_num = 2; |
837 | else |
838 | sub_pckt_num = 4; |
839 | |
840 | i2s_port_en_val = 1; |
841 | } else if (audio->channels == 4) { |
842 | i2s_port_en_val = 3; |
843 | } |
844 | |
845 | writel(val: 0x0, addr: dp->regs + SPDIF_CTRL_ADDR); |
846 | |
847 | writel(SYNC_WR_TO_CH_ZERO, addr: dp->regs + FIFO_CNTL); |
848 | |
849 | val = MAX_NUM_CH(audio->channels); |
850 | val |= NUM_OF_I2S_PORTS(audio->channels); |
851 | val |= AUDIO_TYPE_LPCM; |
852 | val |= CFG_SUB_PCKT_NUM(sub_pckt_num); |
853 | writel(val, addr: dp->regs + SMPL2PKT_CNFG); |
854 | |
855 | if (audio->sample_width == 16) |
856 | val = 0; |
857 | else if (audio->sample_width == 24) |
858 | val = 1 << 9; |
859 | else |
860 | val = 2 << 9; |
861 | |
862 | val |= AUDIO_CH_NUM(audio->channels); |
863 | val |= I2S_DEC_PORT_EN(i2s_port_en_val); |
864 | val |= TRANS_SMPL_WIDTH_32; |
865 | writel(val, addr: dp->regs + AUDIO_SRC_CNFG); |
866 | |
867 | for (i = 0; i < (audio->channels + 1) / 2; i++) { |
868 | if (audio->sample_width == 16) |
869 | val = (0x02 << 8) | (0x02 << 20); |
870 | else if (audio->sample_width == 24) |
871 | val = (0x0b << 8) | (0x0b << 20); |
872 | |
873 | val |= ((2 * i) << 4) | ((2 * i + 1) << 16); |
874 | writel(val, addr: dp->regs + STTS_BIT_CH(i)); |
875 | } |
876 | |
877 | switch (audio->sample_rate) { |
878 | case 32000: |
879 | val = SAMPLING_FREQ(3) | |
880 | ORIGINAL_SAMP_FREQ(0xc); |
881 | break; |
882 | case 44100: |
883 | val = SAMPLING_FREQ(0) | |
884 | ORIGINAL_SAMP_FREQ(0xf); |
885 | break; |
886 | case 48000: |
887 | val = SAMPLING_FREQ(2) | |
888 | ORIGINAL_SAMP_FREQ(0xd); |
889 | break; |
890 | case 88200: |
891 | val = SAMPLING_FREQ(8) | |
892 | ORIGINAL_SAMP_FREQ(0x7); |
893 | break; |
894 | case 96000: |
895 | val = SAMPLING_FREQ(0xa) | |
896 | ORIGINAL_SAMP_FREQ(5); |
897 | break; |
898 | case 176400: |
899 | val = SAMPLING_FREQ(0xc) | |
900 | ORIGINAL_SAMP_FREQ(3); |
901 | break; |
902 | case 192000: |
903 | val = SAMPLING_FREQ(0xe) | |
904 | ORIGINAL_SAMP_FREQ(1); |
905 | break; |
906 | } |
907 | val |= 4; |
908 | writel(val, addr: dp->regs + COM_CH_STTS_BITS); |
909 | |
910 | writel(SMPL2PKT_EN, addr: dp->regs + SMPL2PKT_CNTL); |
911 | writel(I2S_DEC_START, addr: dp->regs + AUDIO_SRC_CNTL); |
912 | } |
913 | |
914 | static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp) |
915 | { |
916 | u32 val; |
917 | |
918 | writel(SYNC_WR_TO_CH_ZERO, addr: dp->regs + FIFO_CNTL); |
919 | |
920 | val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); |
921 | writel(val, addr: dp->regs + SMPL2PKT_CNFG); |
922 | writel(SMPL2PKT_EN, addr: dp->regs + SMPL2PKT_CNTL); |
923 | |
924 | val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; |
925 | writel(val, addr: dp->regs + SPDIF_CTRL_ADDR); |
926 | |
927 | clk_prepare_enable(clk: dp->spdif_clk); |
928 | clk_set_rate(clk: dp->spdif_clk, CDN_DP_SPDIF_CLK); |
929 | } |
930 | |
931 | int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio) |
932 | { |
933 | int ret; |
934 | |
935 | /* reset the spdif clk before config */ |
936 | if (audio->format == AFMT_SPDIF) { |
937 | reset_control_assert(rstc: dp->spdif_rst); |
938 | reset_control_deassert(rstc: dp->spdif_rst); |
939 | } |
940 | |
941 | ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC); |
942 | if (ret) |
943 | goto err_audio_config; |
944 | |
945 | ret = cdn_dp_reg_write(dp, CM_CTRL, val: 0); |
946 | if (ret) |
947 | goto err_audio_config; |
948 | |
949 | if (audio->format == AFMT_I2S) |
950 | cdn_dp_audio_config_i2s(dp, audio); |
951 | else if (audio->format == AFMT_SPDIF) |
952 | cdn_dp_audio_config_spdif(dp); |
953 | |
954 | ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); |
955 | |
956 | err_audio_config: |
957 | if (ret) |
958 | DRM_DEV_ERROR(dp->dev, "audio config failed: %d\n" , ret); |
959 | return ret; |
960 | } |
961 | |