diff options
-rw-r--r-- | drivers/gpu/drm/sti/sti_hdmi.c | 82 |
1 files changed, 73 insertions, 9 deletions
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index ff04ed2bb7a3..34e33a12984d 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c | |||
@@ -51,9 +51,18 @@ | |||
51 | #define HDMI_SW_DI_2_PKT_WORD4 0x0614 | 51 | #define HDMI_SW_DI_2_PKT_WORD4 0x0614 |
52 | #define HDMI_SW_DI_2_PKT_WORD5 0x0618 | 52 | #define HDMI_SW_DI_2_PKT_WORD5 0x0618 |
53 | #define HDMI_SW_DI_2_PKT_WORD6 0x061C | 53 | #define HDMI_SW_DI_2_PKT_WORD6 0x061C |
54 | #define HDMI_SW_DI_3_HEAD_WORD 0x0620 | ||
55 | #define HDMI_SW_DI_3_PKT_WORD0 0x0624 | ||
56 | #define HDMI_SW_DI_3_PKT_WORD1 0x0628 | ||
57 | #define HDMI_SW_DI_3_PKT_WORD2 0x062C | ||
58 | #define HDMI_SW_DI_3_PKT_WORD3 0x0630 | ||
59 | #define HDMI_SW_DI_3_PKT_WORD4 0x0634 | ||
60 | #define HDMI_SW_DI_3_PKT_WORD5 0x0638 | ||
61 | #define HDMI_SW_DI_3_PKT_WORD6 0x063C | ||
54 | 62 | ||
55 | #define HDMI_IFRAME_SLOT_AVI 1 | 63 | #define HDMI_IFRAME_SLOT_AVI 1 |
56 | #define HDMI_IFRAME_SLOT_AUDIO 2 | 64 | #define HDMI_IFRAME_SLOT_AUDIO 2 |
65 | #define HDMI_IFRAME_SLOT_VENDOR 3 | ||
57 | 66 | ||
58 | #define XCAT(prefix, x, suffix) prefix ## x ## suffix | 67 | #define XCAT(prefix, x, suffix) prefix ## x ## suffix |
59 | #define HDMI_SW_DI_N_HEAD_WORD(x) XCAT(HDMI_SW_DI_, x, _HEAD_WORD) | 68 | #define HDMI_SW_DI_N_HEAD_WORD(x) XCAT(HDMI_SW_DI_, x, _HEAD_WORD) |
@@ -264,6 +273,10 @@ static void hdmi_infoframe_reset(struct sti_hdmi *hdmi, | |||
264 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); | 273 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); |
265 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); | 274 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); |
266 | break; | 275 | break; |
276 | case HDMI_IFRAME_SLOT_VENDOR: | ||
277 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR); | ||
278 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR); | ||
279 | break; | ||
267 | default: | 280 | default: |
268 | DRM_ERROR("unsupported infoframe slot: %#x\n", slot); | 281 | DRM_ERROR("unsupported infoframe slot: %#x\n", slot); |
269 | return; | 282 | return; |
@@ -305,12 +318,13 @@ static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size) | |||
305 | * @data: infoframe to write | 318 | * @data: infoframe to write |
306 | * @size: size to write | 319 | * @size: size to write |
307 | */ | 320 | */ |
308 | static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | 321 | static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, |
322 | const u8 *data, | ||
323 | size_t size) | ||
309 | { | 324 | { |
310 | const u8 *ptr = data; | 325 | const u8 *ptr = data; |
311 | u32 val, slot, mode, i; | 326 | u32 val, slot, mode, i; |
312 | u32 head_offset, pack_offset; | 327 | u32 head_offset, pack_offset; |
313 | size_t size; | ||
314 | 328 | ||
315 | switch (*ptr) { | 329 | switch (*ptr) { |
316 | case HDMI_INFOFRAME_TYPE_AVI: | 330 | case HDMI_INFOFRAME_TYPE_AVI: |
@@ -318,17 +332,19 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | |||
318 | mode = HDMI_IFRAME_FIELD; | 332 | mode = HDMI_IFRAME_FIELD; |
319 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); | 333 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); |
320 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); | 334 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); |
321 | size = HDMI_AVI_INFOFRAME_SIZE; | ||
322 | break; | 335 | break; |
323 | |||
324 | case HDMI_INFOFRAME_TYPE_AUDIO: | 336 | case HDMI_INFOFRAME_TYPE_AUDIO: |
325 | slot = HDMI_IFRAME_SLOT_AUDIO; | 337 | slot = HDMI_IFRAME_SLOT_AUDIO; |
326 | mode = HDMI_IFRAME_FRAME; | 338 | mode = HDMI_IFRAME_FRAME; |
327 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); | 339 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); |
328 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); | 340 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); |
329 | size = HDMI_AUDIO_INFOFRAME_SIZE; | ||
330 | break; | 341 | break; |
331 | 342 | case HDMI_INFOFRAME_TYPE_VENDOR: | |
343 | slot = HDMI_IFRAME_SLOT_VENDOR; | ||
344 | mode = HDMI_IFRAME_FRAME; | ||
345 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR); | ||
346 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR); | ||
347 | break; | ||
332 | default: | 348 | default: |
333 | DRM_ERROR("unsupported infoframe type: %#x\n", *ptr); | 349 | DRM_ERROR("unsupported infoframe type: %#x\n", *ptr); |
334 | return; | 350 | return; |
@@ -347,8 +363,9 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | |||
347 | /* | 363 | /* |
348 | * Each subpack contains 4 bytes | 364 | * Each subpack contains 4 bytes |
349 | * The First Bytes of the first subpacket must contain the checksum | 365 | * The First Bytes of the first subpacket must contain the checksum |
350 | * Packet size in increase by one. | 366 | * Packet size is increase by one. |
351 | */ | 367 | */ |
368 | size = size - HDMI_INFOFRAME_HEADER_SIZE + 1; | ||
352 | for (i = 0; i < size; i += sizeof(u32)) { | 369 | for (i = 0; i < size; i += sizeof(u32)) { |
353 | size_t num; | 370 | size_t num; |
354 | 371 | ||
@@ -401,7 +418,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) | |||
401 | return ret; | 418 | return ret; |
402 | } | 419 | } |
403 | 420 | ||
404 | hdmi_infoframe_write_infopack(hdmi, buffer); | 421 | hdmi_infoframe_write_infopack(hdmi, buffer, ret); |
405 | 422 | ||
406 | return 0; | 423 | return 0; |
407 | } | 424 | } |
@@ -437,7 +454,49 @@ static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi) | |||
437 | return ret; | 454 | return ret; |
438 | } | 455 | } |
439 | 456 | ||
440 | hdmi_infoframe_write_infopack(hdmi, buffer); | 457 | hdmi_infoframe_write_infopack(hdmi, buffer, ret); |
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | /* | ||
463 | * Prepare and configure the VS infoframe | ||
464 | * | ||
465 | * Vendor Specific infoframe are transmitted once per frame and | ||
466 | * contains vendor specific information. | ||
467 | * | ||
468 | * @hdmi: pointer on the hdmi internal structure | ||
469 | * | ||
470 | * Return negative value if error occurs | ||
471 | */ | ||
472 | #define HDMI_VENDOR_INFOFRAME_MAX_SIZE 6 | ||
473 | static int hdmi_vendor_infoframe_config(struct sti_hdmi *hdmi) | ||
474 | { | ||
475 | struct drm_display_mode *mode = &hdmi->mode; | ||
476 | struct hdmi_vendor_infoframe infoframe; | ||
477 | u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_VENDOR_INFOFRAME_MAX_SIZE]; | ||
478 | int ret; | ||
479 | |||
480 | DRM_DEBUG_DRIVER("\n"); | ||
481 | |||
482 | ret = drm_hdmi_vendor_infoframe_from_display_mode(&infoframe, mode); | ||
483 | if (ret < 0) { | ||
484 | /* | ||
485 | * Going into that statement does not means vendor infoframe | ||
486 | * fails. It just informed us that vendor infoframe is not | ||
487 | * needed for the selected mode. Only 4k or stereoscopic 3D | ||
488 | * mode requires vendor infoframe. So just simply return 0. | ||
489 | */ | ||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | ret = hdmi_vendor_infoframe_pack(&infoframe, buffer, sizeof(buffer)); | ||
494 | if (ret < 0) { | ||
495 | DRM_ERROR("failed to pack VS infoframe: %d\n", ret); | ||
496 | return ret; | ||
497 | } | ||
498 | |||
499 | hdmi_infoframe_write_infopack(hdmi, buffer, ret); | ||
441 | 500 | ||
442 | return 0; | 501 | return 0; |
443 | } | 502 | } |
@@ -510,6 +569,7 @@ static void sti_hdmi_disable(struct drm_bridge *bridge) | |||
510 | /* Reset info frame transmission */ | 569 | /* Reset info frame transmission */ |
511 | hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AVI); | 570 | hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AVI); |
512 | hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AUDIO); | 571 | hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AUDIO); |
572 | hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_VENDOR); | ||
513 | 573 | ||
514 | /* Set the default channel data to be a dark red */ | 574 | /* Set the default channel data to be a dark red */ |
515 | hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT); | 575 | hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT); |
@@ -566,6 +626,10 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge) | |||
566 | if (hdmi_audio_infoframe_config(hdmi)) | 626 | if (hdmi_audio_infoframe_config(hdmi)) |
567 | DRM_ERROR("Unable to configure AUDIO infoframe\n"); | 627 | DRM_ERROR("Unable to configure AUDIO infoframe\n"); |
568 | 628 | ||
629 | /* Program VS infoframe */ | ||
630 | if (hdmi_vendor_infoframe_config(hdmi)) | ||
631 | DRM_ERROR("Unable to configure VS infoframe\n"); | ||
632 | |||
569 | /* Sw reset */ | 633 | /* Sw reset */ |
570 | hdmi_swreset(hdmi); | 634 | hdmi_swreset(hdmi); |
571 | } | 635 | } |