aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/tegra/hdmi.c
diff options
context:
space:
mode:
authorThierry Reding <thierry.reding@avionic-design.de>2012-11-23 09:14:00 -0500
committerThierry Reding <thierry.reding@avionic-design.de>2013-02-22 02:20:18 -0500
commitac24c2204a76e5b42aa103bf963ae0eda1b827f3 (patch)
tree73d668aaaebdc49e1b0a064715d9a21733559d9c /drivers/gpu/drm/tegra/hdmi.c
parent5e308591a887604ed4fca7e7fcd8fb18d8bdc459 (diff)
drm/tegra: Use generic HDMI infoframe helpers
Use the generic HDMI infoframe helpers to get rid of the NVIDIA Tegra reimplementation. Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de> Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/tegra/hdmi.c')
-rw-r--r--drivers/gpu/drm/tegra/hdmi.c226
1 files changed, 109 insertions, 117 deletions
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index e060c7e6434d..0daee8e2578b 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -10,6 +10,7 @@
10#include <linux/clk.h> 10#include <linux/clk.h>
11#include <linux/debugfs.h> 11#include <linux/debugfs.h>
12#include <linux/gpio.h> 12#include <linux/gpio.h>
13#include <linux/hdmi.h>
13#include <linux/module.h> 14#include <linux/module.h>
14#include <linux/of.h> 15#include <linux/of.h>
15#include <linux/platform_device.h> 16#include <linux/platform_device.h>
@@ -17,6 +18,8 @@
17 18
18#include <mach/clk.h> 19#include <mach/clk.h>
19 20
21#include <drm/drm_edid.h>
22
20#include "hdmi.h" 23#include "hdmi.h"
21#include "drm.h" 24#include "drm.h"
22#include "dc.h" 25#include "dc.h"
@@ -401,54 +404,65 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk)
401 return 0; 404 return 0;
402} 405}
403 406
404static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, 407static inline unsigned long tegra_hdmi_subpack(const u8 *ptr, size_t size)
405 unsigned int offset, u8 type,
406 u8 version, void *data, size_t size)
407{ 408{
408 unsigned long value; 409 unsigned long value = 0;
409 u8 *ptr = data;
410 u32 subpack[2];
411 size_t i; 410 size_t i;
412 u8 csum;
413 411
414 /* first byte of data is the checksum */ 412 for (i = size; i > 0; i--)
415 csum = type + version + size - 1; 413 value = (value << 8) | ptr[i - 1];
416 414
417 for (i = 1; i < size; i++) 415 return value;
418 csum += ptr[i]; 416}
419 417
420 ptr[0] = 0x100 - csum; 418static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data,
419 size_t size)
420{
421 const u8 *ptr = data;
422 unsigned long offset;
423 unsigned long value;
424 size_t i, j;
421 425
422 value = INFOFRAME_HEADER_TYPE(type) | 426 switch (ptr[0]) {
423 INFOFRAME_HEADER_VERSION(version) | 427 case HDMI_INFOFRAME_TYPE_AVI:
424 INFOFRAME_HEADER_LEN(size - 1); 428 offset = HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER;
425 tegra_hdmi_writel(hdmi, value, offset); 429 break;
426 430
427 /* The audio inforame only has one set of subpack registers. The hdmi 431 case HDMI_INFOFRAME_TYPE_AUDIO:
428 * block pads the rest of the data as per the spec so we have to fixup 432 offset = HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER;
429 * the length before filling in the subpacks. 433 break;
430 */
431 if (offset == HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER)
432 size = 6;
433 434
434 /* each subpack 7 bytes devided into: 435 case HDMI_INFOFRAME_TYPE_VENDOR:
435 * subpack_low - bytes 0 - 3 436 offset = HDMI_NV_PDISP_HDMI_GENERIC_HEADER;
436 * subpack_high - bytes 4 - 6 (with byte 7 padded to 0x00) 437 break;
437 */ 438
438 for (i = 0; i < size; i++) { 439 default:
439 size_t index = i % 7; 440 dev_err(hdmi->dev, "unsupported infoframe type: %02x\n",
441 ptr[0]);
442 return;
443 }
444
445 value = INFOFRAME_HEADER_TYPE(ptr[0]) |
446 INFOFRAME_HEADER_VERSION(ptr[1]) |
447 INFOFRAME_HEADER_LEN(ptr[2]);
448 tegra_hdmi_writel(hdmi, value, offset);
449 offset++;
440 450
441 if (index == 0) 451 /*
442 memset(subpack, 0x0, sizeof(subpack)); 452 * Each subpack contains 7 bytes, divided into:
453 * - subpack_low: bytes 0 - 3
454 * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00)
455 */
456 for (i = 3, j = 0; i < size; i += 7, j += 8) {
457 size_t rem = size - i, num = min_t(size_t, rem, 4);
443 458
444 ((u8 *)subpack)[index] = ptr[i]; 459 value = tegra_hdmi_subpack(&ptr[i], num);
460 tegra_hdmi_writel(hdmi, value, offset++);
445 461
446 if (index == 6 || (i + 1 == size)) { 462 num = min_t(size_t, rem - num, 3);
447 unsigned int reg = offset + 1 + (i / 7) * 2;
448 463
449 tegra_hdmi_writel(hdmi, subpack[0], reg); 464 value = tegra_hdmi_subpack(&ptr[i + 4], num);
450 tegra_hdmi_writel(hdmi, subpack[1], reg + 1); 465 tegra_hdmi_writel(hdmi, value, offset++);
451 }
452 } 466 }
453} 467}
454 468
@@ -456,9 +470,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
456 struct drm_display_mode *mode) 470 struct drm_display_mode *mode)
457{ 471{
458 struct hdmi_avi_infoframe frame; 472 struct hdmi_avi_infoframe frame;
459 unsigned int h_front_porch; 473 u8 buffer[17];
460 unsigned int hsize = 16; 474 ssize_t err;
461 unsigned int vsize = 9;
462 475
463 if (hdmi->dvi) { 476 if (hdmi->dvi) {
464 tegra_hdmi_writel(hdmi, 0, 477 tegra_hdmi_writel(hdmi, 0,
@@ -466,69 +479,19 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
466 return; 479 return;
467 } 480 }
468 481
469 h_front_porch = mode->hsync_start - mode->hdisplay; 482 err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
470 memset(&frame, 0, sizeof(frame)); 483 if (err < 0) {
471 frame.r = HDMI_AVI_R_SAME; 484 dev_err(hdmi->dev, "failed to setup AVI infoframe: %zd\n", err);
472 485 return;
473 switch (mode->vdisplay) { 486 }
474 case 480:
475 if (mode->hdisplay == 640) {
476 frame.m = HDMI_AVI_M_4_3;
477 frame.vic = 1;
478 } else {
479 frame.m = HDMI_AVI_M_16_9;
480 frame.vic = 3;
481 }
482 break;
483
484 case 576:
485 if (((hsize * 10) / vsize) > 14) {
486 frame.m = HDMI_AVI_M_16_9;
487 frame.vic = 18;
488 } else {
489 frame.m = HDMI_AVI_M_4_3;
490 frame.vic = 17;
491 }
492 break;
493
494 case 720:
495 case 1470: /* stereo mode */
496 frame.m = HDMI_AVI_M_16_9;
497
498 if (h_front_porch == 110)
499 frame.vic = 4;
500 else
501 frame.vic = 19;
502 break;
503
504 case 1080:
505 case 2205: /* stereo mode */
506 frame.m = HDMI_AVI_M_16_9;
507
508 switch (h_front_porch) {
509 case 88:
510 frame.vic = 16;
511 break;
512
513 case 528:
514 frame.vic = 31;
515 break;
516
517 default:
518 frame.vic = 32;
519 break;
520 }
521 break;
522 487
523 default: 488 err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
524 frame.m = HDMI_AVI_M_16_9; 489 if (err < 0) {
525 frame.vic = 0; 490 dev_err(hdmi->dev, "failed to pack AVI infoframe: %zd\n", err);
526 break; 491 return;
527 } 492 }
528 493
529 tegra_hdmi_write_infopack(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER, 494 tegra_hdmi_write_infopack(hdmi, buffer, err);
530 HDMI_INFOFRAME_TYPE_AVI, HDMI_AVI_VERSION,
531 &frame, sizeof(frame));
532 495
533 tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, 496 tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
534 HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); 497 HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
@@ -537,6 +500,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
537static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) 500static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
538{ 501{
539 struct hdmi_audio_infoframe frame; 502 struct hdmi_audio_infoframe frame;
503 u8 buffer[14];
504 ssize_t err;
540 505
541 if (hdmi->dvi) { 506 if (hdmi->dvi) {
542 tegra_hdmi_writel(hdmi, 0, 507 tegra_hdmi_writel(hdmi, 0,
@@ -544,14 +509,29 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
544 return; 509 return;
545 } 510 }
546 511
547 memset(&frame, 0, sizeof(frame)); 512 err = hdmi_audio_infoframe_init(&frame);
548 frame.cc = HDMI_AUDIO_CC_2; 513 if (err < 0) {
514 dev_err(hdmi->dev, "failed to initialize audio infoframe: %d\n",
515 err);
516 return;
517 }
518
519 frame.channels = 2;
520
521 err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
522 if (err < 0) {
523 dev_err(hdmi->dev, "failed to pack audio infoframe: %zd\n",
524 err);
525 return;
526 }
549 527
550 tegra_hdmi_write_infopack(hdmi, 528 /*
551 HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER, 529 * The audio infoframe has only one set of subpack registers, so the
552 HDMI_INFOFRAME_TYPE_AUDIO, 530 * infoframe needs to be truncated. One set of subpack registers can
553 HDMI_AUDIO_VERSION, 531 * contain 7 bytes. Including the 3 byte header only the first 10
554 &frame, sizeof(frame)); 532 * bytes can be programmed.
533 */
534 tegra_hdmi_write_infopack(hdmi, buffer, min(10, err));
555 535
556 tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, 536 tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
557 HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); 537 HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
@@ -559,8 +539,10 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
559 539
560static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) 540static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi)
561{ 541{
562 struct hdmi_stereo_infoframe frame; 542 struct hdmi_vendor_infoframe frame;
563 unsigned long value; 543 unsigned long value;
544 u8 buffer[10];
545 ssize_t err;
564 546
565 if (!hdmi->stereo) { 547 if (!hdmi->stereo) {
566 value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 548 value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
@@ -570,22 +552,32 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi)
570 } 552 }
571 553
572 memset(&frame, 0, sizeof(frame)); 554 memset(&frame, 0, sizeof(frame));
573 frame.regid0 = 0x03; 555
574 frame.regid1 = 0x0c; 556 frame.type = HDMI_INFOFRAME_TYPE_VENDOR;
575 frame.regid2 = 0x00; 557 frame.version = 0x01;
576 frame.hdmi_video_format = 2; 558 frame.length = 6;
559
560 frame.data[0] = 0x03; /* regid0 */
561 frame.data[1] = 0x0c; /* regid1 */
562 frame.data[2] = 0x00; /* regid2 */
563 frame.data[3] = 0x02 << 5; /* video format */
577 564
578 /* TODO: 74 MHz limit? */ 565 /* TODO: 74 MHz limit? */
579 if (1) { 566 if (1) {
580 frame._3d_structure = 0; 567 frame.data[4] = 0x00 << 4; /* 3D structure */
581 } else { 568 } else {
582 frame._3d_structure = 8; 569 frame.data[4] = 0x08 << 4; /* 3D structure */
583 frame._3d_ext_data = 0; 570 frame.data[5] = 0x00 << 4; /* 3D ext. data */
571 }
572
573 err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
574 if (err < 0) {
575 dev_err(hdmi->dev, "failed to pack vendor infoframe: %zd\n",
576 err);
577 return;
584 } 578 }
585 579
586 tegra_hdmi_write_infopack(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_HEADER, 580 tegra_hdmi_write_infopack(hdmi, buffer, err);
587 HDMI_INFOFRAME_TYPE_VENDOR,
588 HDMI_VENDOR_VERSION, &frame, 6);
589 581
590 value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 582 value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
591 value |= GENERIC_CTRL_ENABLE; 583 value |= GENERIC_CTRL_ENABLE;