diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/hdmi.c')
-rw-r--r-- | drivers/gpu/drm/tegra/hdmi.c | 507 |
1 files changed, 392 insertions, 115 deletions
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 2fdb8796443e..cda0491ed6bf 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c | |||
@@ -11,6 +11,7 @@ | |||
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/hdmi.h> |
14 | #include <linux/pm_runtime.h> | ||
14 | #include <linux/regulator/consumer.h> | 15 | #include <linux/regulator/consumer.h> |
15 | #include <linux/reset.h> | 16 | #include <linux/reset.h> |
16 | 17 | ||
@@ -18,10 +19,14 @@ | |||
18 | #include <drm/drm_crtc.h> | 19 | #include <drm/drm_crtc.h> |
19 | #include <drm/drm_crtc_helper.h> | 20 | #include <drm/drm_crtc_helper.h> |
20 | 21 | ||
22 | #include <sound/hda_verbs.h> | ||
23 | |||
21 | #include "hdmi.h" | 24 | #include "hdmi.h" |
22 | #include "drm.h" | 25 | #include "drm.h" |
23 | #include "dc.h" | 26 | #include "dc.h" |
24 | 27 | ||
28 | #define HDMI_ELD_BUFFER_SIZE 96 | ||
29 | |||
25 | struct tmds_config { | 30 | struct tmds_config { |
26 | unsigned int pclk; | 31 | unsigned int pclk; |
27 | u32 pll0; | 32 | u32 pll0; |
@@ -39,6 +44,8 @@ struct tegra_hdmi_config { | |||
39 | u32 fuse_override_value; | 44 | u32 fuse_override_value; |
40 | 45 | ||
41 | bool has_sor_io_peak_current; | 46 | bool has_sor_io_peak_current; |
47 | bool has_hda; | ||
48 | bool has_hbr; | ||
42 | }; | 49 | }; |
43 | 50 | ||
44 | struct tegra_hdmi { | 51 | struct tegra_hdmi { |
@@ -60,7 +67,10 @@ struct tegra_hdmi { | |||
60 | const struct tegra_hdmi_config *config; | 67 | const struct tegra_hdmi_config *config; |
61 | 68 | ||
62 | unsigned int audio_source; | 69 | unsigned int audio_source; |
63 | unsigned int audio_freq; | 70 | unsigned int audio_sample_rate; |
71 | unsigned int audio_channels; | ||
72 | |||
73 | unsigned int pixel_clock; | ||
64 | bool stereo; | 74 | bool stereo; |
65 | bool dvi; | 75 | bool dvi; |
66 | 76 | ||
@@ -402,11 +412,11 @@ static const struct tmds_config tegra124_tmds_config[] = { | |||
402 | }; | 412 | }; |
403 | 413 | ||
404 | static const struct tegra_hdmi_audio_config * | 414 | static const struct tegra_hdmi_audio_config * |
405 | tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pclk) | 415 | tegra_hdmi_get_audio_config(unsigned int sample_rate, unsigned int pclk) |
406 | { | 416 | { |
407 | const struct tegra_hdmi_audio_config *table; | 417 | const struct tegra_hdmi_audio_config *table; |
408 | 418 | ||
409 | switch (audio_freq) { | 419 | switch (sample_rate) { |
410 | case 32000: | 420 | case 32000: |
411 | table = tegra_hdmi_audio_32k; | 421 | table = tegra_hdmi_audio_32k; |
412 | break; | 422 | break; |
@@ -476,44 +486,114 @@ static void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi) | |||
476 | } | 486 | } |
477 | } | 487 | } |
478 | 488 | ||
479 | static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk) | 489 | static void tegra_hdmi_write_aval(struct tegra_hdmi *hdmi, u32 value) |
490 | { | ||
491 | static const struct { | ||
492 | unsigned int sample_rate; | ||
493 | unsigned int offset; | ||
494 | } regs[] = { | ||
495 | { 32000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320 }, | ||
496 | { 44100, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441 }, | ||
497 | { 48000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480 }, | ||
498 | { 88200, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882 }, | ||
499 | { 96000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960 }, | ||
500 | { 176400, HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764 }, | ||
501 | { 192000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920 }, | ||
502 | }; | ||
503 | unsigned int i; | ||
504 | |||
505 | for (i = 0; i < ARRAY_SIZE(regs); i++) { | ||
506 | if (regs[i].sample_rate == hdmi->audio_sample_rate) { | ||
507 | tegra_hdmi_writel(hdmi, value, regs[i].offset); | ||
508 | break; | ||
509 | } | ||
510 | } | ||
511 | } | ||
512 | |||
513 | static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) | ||
480 | { | 514 | { |
481 | struct device_node *node = hdmi->dev->of_node; | ||
482 | const struct tegra_hdmi_audio_config *config; | 515 | const struct tegra_hdmi_audio_config *config; |
483 | unsigned int offset = 0; | 516 | u32 source, value; |
484 | u32 value; | ||
485 | 517 | ||
486 | switch (hdmi->audio_source) { | 518 | switch (hdmi->audio_source) { |
487 | case HDA: | 519 | case HDA: |
488 | value = AUDIO_CNTRL0_SOURCE_SELECT_HDAL; | 520 | if (hdmi->config->has_hda) |
521 | source = SOR_AUDIO_CNTRL0_SOURCE_SELECT_HDAL; | ||
522 | else | ||
523 | return -EINVAL; | ||
524 | |||
489 | break; | 525 | break; |
490 | 526 | ||
491 | case SPDIF: | 527 | case SPDIF: |
492 | value = AUDIO_CNTRL0_SOURCE_SELECT_SPDIF; | 528 | if (hdmi->config->has_hda) |
529 | source = SOR_AUDIO_CNTRL0_SOURCE_SELECT_SPDIF; | ||
530 | else | ||
531 | source = AUDIO_CNTRL0_SOURCE_SELECT_SPDIF; | ||
493 | break; | 532 | break; |
494 | 533 | ||
495 | default: | 534 | default: |
496 | value = AUDIO_CNTRL0_SOURCE_SELECT_AUTO; | 535 | if (hdmi->config->has_hda) |
536 | source = SOR_AUDIO_CNTRL0_SOURCE_SELECT_AUTO; | ||
537 | else | ||
538 | source = AUDIO_CNTRL0_SOURCE_SELECT_AUTO; | ||
497 | break; | 539 | break; |
498 | } | 540 | } |
499 | 541 | ||
500 | if (of_device_is_compatible(node, "nvidia,tegra30-hdmi")) { | 542 | /* |
501 | value |= AUDIO_CNTRL0_ERROR_TOLERANCE(6) | | 543 | * Tegra30 and later use a slightly modified version of the register |
502 | AUDIO_CNTRL0_FRAMES_PER_BLOCK(0xc0); | 544 | * layout to accomodate for changes related to supporting HDA as the |
503 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_CNTRL0); | 545 | * audio input source for HDMI. The source select field has moved to |
504 | } else { | 546 | * the SOR_AUDIO_CNTRL0 register, but the error tolerance and frames |
505 | value |= AUDIO_CNTRL0_INJECT_NULLSMPL; | 547 | * per block fields remain in the AUDIO_CNTRL0 register. |
548 | */ | ||
549 | if (hdmi->config->has_hda) { | ||
550 | /* | ||
551 | * Inject null samples into the audio FIFO for every frame in | ||
552 | * which the codec did not receive any samples. This applies | ||
553 | * to stereo LPCM only. | ||
554 | * | ||
555 | * XXX: This seems to be a remnant of MCP days when this was | ||
556 | * used to work around issues with monitors not being able to | ||
557 | * play back system startup sounds early. It is possibly not | ||
558 | * needed on Linux at all. | ||
559 | */ | ||
560 | if (hdmi->audio_channels == 2) | ||
561 | value = SOR_AUDIO_CNTRL0_INJECT_NULLSMPL; | ||
562 | else | ||
563 | value = 0; | ||
564 | |||
565 | value |= source; | ||
566 | |||
506 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0); | 567 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0); |
568 | } | ||
507 | 569 | ||
508 | value = AUDIO_CNTRL0_ERROR_TOLERANCE(6) | | 570 | /* |
509 | AUDIO_CNTRL0_FRAMES_PER_BLOCK(0xc0); | 571 | * On Tegra20, HDA is not a supported audio source and the source |
510 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_CNTRL0); | 572 | * select field is part of the AUDIO_CNTRL0 register. |
573 | */ | ||
574 | value = AUDIO_CNTRL0_FRAMES_PER_BLOCK(0xc0) | | ||
575 | AUDIO_CNTRL0_ERROR_TOLERANCE(6); | ||
576 | |||
577 | if (!hdmi->config->has_hda) | ||
578 | value |= source; | ||
579 | |||
580 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_CNTRL0); | ||
581 | |||
582 | /* | ||
583 | * Advertise support for High Bit-Rate on Tegra114 and later. | ||
584 | */ | ||
585 | if (hdmi->config->has_hbr) { | ||
586 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); | ||
587 | value |= SOR_AUDIO_SPARE0_HBR_ENABLE; | ||
588 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); | ||
511 | } | 589 | } |
512 | 590 | ||
513 | config = tegra_hdmi_get_audio_config(hdmi->audio_freq, pclk); | 591 | config = tegra_hdmi_get_audio_config(hdmi->audio_sample_rate, |
592 | hdmi->pixel_clock); | ||
514 | if (!config) { | 593 | if (!config) { |
515 | dev_err(hdmi->dev, "cannot set audio to %u at %u pclk\n", | 594 | dev_err(hdmi->dev, |
516 | hdmi->audio_freq, pclk); | 595 | "cannot set audio to %u Hz at %u Hz pixel clock\n", |
596 | hdmi->audio_sample_rate, hdmi->pixel_clock); | ||
517 | return -EINVAL; | 597 | return -EINVAL; |
518 | } | 598 | } |
519 | 599 | ||
@@ -526,8 +606,8 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk) | |||
526 | tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config->n) | ACR_ENABLE, | 606 | tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config->n) | ACR_ENABLE, |
527 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); | 607 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); |
528 | 608 | ||
529 | value = ACR_SUBPACK_CTS(config->cts); | 609 | tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config->cts), |
530 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); | 610 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); |
531 | 611 | ||
532 | value = SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1); | 612 | value = SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1); |
533 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_SPARE); | 613 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_SPARE); |
@@ -536,43 +616,53 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk) | |||
536 | value &= ~AUDIO_N_RESETF; | 616 | value &= ~AUDIO_N_RESETF; |
537 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); | 617 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); |
538 | 618 | ||
539 | if (of_device_is_compatible(node, "nvidia,tegra30-hdmi")) { | 619 | if (hdmi->config->has_hda) |
540 | switch (hdmi->audio_freq) { | 620 | tegra_hdmi_write_aval(hdmi, config->aval); |
541 | case 32000: | ||
542 | offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320; | ||
543 | break; | ||
544 | 621 | ||
545 | case 44100: | 622 | tegra_hdmi_setup_audio_fs_tables(hdmi); |
546 | offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441; | ||
547 | break; | ||
548 | 623 | ||
549 | case 48000: | 624 | return 0; |
550 | offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480; | 625 | } |
551 | break; | ||
552 | 626 | ||
553 | case 88200: | 627 | static void tegra_hdmi_disable_audio(struct tegra_hdmi *hdmi) |
554 | offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882; | 628 | { |
555 | break; | 629 | u32 value; |
556 | 630 | ||
557 | case 96000: | 631 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); |
558 | offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960; | 632 | value &= ~GENERIC_CTRL_AUDIO; |
559 | break; | 633 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); |
634 | } | ||
560 | 635 | ||
561 | case 176400: | 636 | static void tegra_hdmi_enable_audio(struct tegra_hdmi *hdmi) |
562 | offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764; | 637 | { |
563 | break; | 638 | u32 value; |
564 | 639 | ||
565 | case 192000: | 640 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); |
566 | offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920; | 641 | value |= GENERIC_CTRL_AUDIO; |
567 | break; | 642 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); |
568 | } | 643 | } |
569 | 644 | ||
570 | tegra_hdmi_writel(hdmi, config->aval, offset); | 645 | static void tegra_hdmi_write_eld(struct tegra_hdmi *hdmi) |
571 | } | 646 | { |
647 | size_t length = drm_eld_size(hdmi->output.connector.eld), i; | ||
648 | u32 value; | ||
572 | 649 | ||
573 | tegra_hdmi_setup_audio_fs_tables(hdmi); | 650 | for (i = 0; i < length; i++) |
651 | tegra_hdmi_writel(hdmi, i << 8 | hdmi->output.connector.eld[i], | ||
652 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); | ||
574 | 653 | ||
575 | return 0; | 654 | /* |
655 | * The HDA codec will always report an ELD buffer size of 96 bytes and | ||
656 | * the HDA codec driver will check that each byte read from the buffer | ||
657 | * is valid. Therefore every byte must be written, even if no 96 bytes | ||
658 | * were parsed from EDID. | ||
659 | */ | ||
660 | for (i = length; i < HDMI_ELD_BUFFER_SIZE; i++) | ||
661 | tegra_hdmi_writel(hdmi, i << 8 | 0, | ||
662 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); | ||
663 | |||
664 | value = SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT; | ||
665 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); | ||
576 | } | 666 | } |
577 | 667 | ||
578 | static inline u32 tegra_hdmi_subpack(const u8 *ptr, size_t size) | 668 | static inline u32 tegra_hdmi_subpack(const u8 *ptr, size_t size) |
@@ -644,12 +734,6 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi, | |||
644 | u8 buffer[17]; | 734 | u8 buffer[17]; |
645 | ssize_t err; | 735 | ssize_t err; |
646 | 736 | ||
647 | if (hdmi->dvi) { | ||
648 | tegra_hdmi_writel(hdmi, 0, | ||
649 | HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); | ||
650 | return; | ||
651 | } | ||
652 | |||
653 | err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); | 737 | err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); |
654 | if (err < 0) { | 738 | if (err < 0) { |
655 | dev_err(hdmi->dev, "failed to setup AVI infoframe: %zd\n", err); | 739 | dev_err(hdmi->dev, "failed to setup AVI infoframe: %zd\n", err); |
@@ -663,9 +747,24 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi, | |||
663 | } | 747 | } |
664 | 748 | ||
665 | tegra_hdmi_write_infopack(hdmi, buffer, err); | 749 | tegra_hdmi_write_infopack(hdmi, buffer, err); |
750 | } | ||
751 | |||
752 | static void tegra_hdmi_disable_avi_infoframe(struct tegra_hdmi *hdmi) | ||
753 | { | ||
754 | u32 value; | ||
666 | 755 | ||
667 | tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, | 756 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); |
668 | HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); | 757 | value &= ~INFOFRAME_CTRL_ENABLE; |
758 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); | ||
759 | } | ||
760 | |||
761 | static void tegra_hdmi_enable_avi_infoframe(struct tegra_hdmi *hdmi) | ||
762 | { | ||
763 | u32 value; | ||
764 | |||
765 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); | ||
766 | value |= INFOFRAME_CTRL_ENABLE; | ||
767 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); | ||
669 | } | 768 | } |
670 | 769 | ||
671 | static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) | 770 | static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) |
@@ -674,12 +773,6 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) | |||
674 | u8 buffer[14]; | 773 | u8 buffer[14]; |
675 | ssize_t err; | 774 | ssize_t err; |
676 | 775 | ||
677 | if (hdmi->dvi) { | ||
678 | tegra_hdmi_writel(hdmi, 0, | ||
679 | HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); | ||
680 | return; | ||
681 | } | ||
682 | |||
683 | err = hdmi_audio_infoframe_init(&frame); | 776 | err = hdmi_audio_infoframe_init(&frame); |
684 | if (err < 0) { | 777 | if (err < 0) { |
685 | dev_err(hdmi->dev, "failed to setup audio infoframe: %zd\n", | 778 | dev_err(hdmi->dev, "failed to setup audio infoframe: %zd\n", |
@@ -687,7 +780,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) | |||
687 | return; | 780 | return; |
688 | } | 781 | } |
689 | 782 | ||
690 | frame.channels = 2; | 783 | frame.channels = hdmi->audio_channels; |
691 | 784 | ||
692 | err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); | 785 | err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); |
693 | if (err < 0) { | 786 | if (err < 0) { |
@@ -703,9 +796,24 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) | |||
703 | * bytes can be programmed. | 796 | * bytes can be programmed. |
704 | */ | 797 | */ |
705 | tegra_hdmi_write_infopack(hdmi, buffer, min_t(size_t, 10, err)); | 798 | tegra_hdmi_write_infopack(hdmi, buffer, min_t(size_t, 10, err)); |
799 | } | ||
706 | 800 | ||
707 | tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, | 801 | static void tegra_hdmi_disable_audio_infoframe(struct tegra_hdmi *hdmi) |
708 | HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); | 802 | { |
803 | u32 value; | ||
804 | |||
805 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); | ||
806 | value &= ~INFOFRAME_CTRL_ENABLE; | ||
807 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); | ||
808 | } | ||
809 | |||
810 | static void tegra_hdmi_enable_audio_infoframe(struct tegra_hdmi *hdmi) | ||
811 | { | ||
812 | u32 value; | ||
813 | |||
814 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); | ||
815 | value |= INFOFRAME_CTRL_ENABLE; | ||
816 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); | ||
709 | } | 817 | } |
710 | 818 | ||
711 | static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) | 819 | static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) |
@@ -713,14 +821,6 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) | |||
713 | struct hdmi_vendor_infoframe frame; | 821 | struct hdmi_vendor_infoframe frame; |
714 | u8 buffer[10]; | 822 | u8 buffer[10]; |
715 | ssize_t err; | 823 | ssize_t err; |
716 | u32 value; | ||
717 | |||
718 | if (!hdmi->stereo) { | ||
719 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
720 | value &= ~GENERIC_CTRL_ENABLE; | ||
721 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
722 | return; | ||
723 | } | ||
724 | 824 | ||
725 | hdmi_vendor_infoframe_init(&frame); | 825 | hdmi_vendor_infoframe_init(&frame); |
726 | frame.s3d_struct = HDMI_3D_STRUCTURE_FRAME_PACKING; | 826 | frame.s3d_struct = HDMI_3D_STRUCTURE_FRAME_PACKING; |
@@ -733,6 +833,20 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) | |||
733 | } | 833 | } |
734 | 834 | ||
735 | tegra_hdmi_write_infopack(hdmi, buffer, err); | 835 | tegra_hdmi_write_infopack(hdmi, buffer, err); |
836 | } | ||
837 | |||
838 | static void tegra_hdmi_disable_stereo_infoframe(struct tegra_hdmi *hdmi) | ||
839 | { | ||
840 | u32 value; | ||
841 | |||
842 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
843 | value &= ~GENERIC_CTRL_ENABLE; | ||
844 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
845 | } | ||
846 | |||
847 | static void tegra_hdmi_enable_stereo_infoframe(struct tegra_hdmi *hdmi) | ||
848 | { | ||
849 | u32 value; | ||
736 | 850 | ||
737 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | 851 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); |
738 | value |= GENERIC_CTRL_ENABLE; | 852 | value |= GENERIC_CTRL_ENABLE; |
@@ -772,10 +886,25 @@ static bool tegra_output_is_hdmi(struct tegra_output *output) | |||
772 | return drm_detect_hdmi_monitor(edid); | 886 | return drm_detect_hdmi_monitor(edid); |
773 | } | 887 | } |
774 | 888 | ||
889 | static enum drm_connector_status | ||
890 | tegra_hdmi_connector_detect(struct drm_connector *connector, bool force) | ||
891 | { | ||
892 | struct tegra_output *output = connector_to_output(connector); | ||
893 | struct tegra_hdmi *hdmi = to_hdmi(output); | ||
894 | enum drm_connector_status status; | ||
895 | |||
896 | status = tegra_output_connector_detect(connector, force); | ||
897 | if (status == connector_status_connected) | ||
898 | return status; | ||
899 | |||
900 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); | ||
901 | return status; | ||
902 | } | ||
903 | |||
775 | static const struct drm_connector_funcs tegra_hdmi_connector_funcs = { | 904 | static const struct drm_connector_funcs tegra_hdmi_connector_funcs = { |
776 | .dpms = drm_atomic_helper_connector_dpms, | 905 | .dpms = drm_atomic_helper_connector_dpms, |
777 | .reset = drm_atomic_helper_connector_reset, | 906 | .reset = drm_atomic_helper_connector_reset, |
778 | .detect = tegra_output_connector_detect, | 907 | .detect = tegra_hdmi_connector_detect, |
779 | .fill_modes = drm_helper_probe_single_connector_modes, | 908 | .fill_modes = drm_helper_probe_single_connector_modes, |
780 | .destroy = tegra_output_connector_destroy, | 909 | .destroy = tegra_output_connector_destroy, |
781 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | 910 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
@@ -814,7 +943,9 @@ static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = { | |||
814 | 943 | ||
815 | static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder) | 944 | static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder) |
816 | { | 945 | { |
946 | struct tegra_output *output = encoder_to_output(encoder); | ||
817 | struct tegra_dc *dc = to_tegra_dc(encoder->crtc); | 947 | struct tegra_dc *dc = to_tegra_dc(encoder->crtc); |
948 | struct tegra_hdmi *hdmi = to_hdmi(output); | ||
818 | u32 value; | 949 | u32 value; |
819 | 950 | ||
820 | /* | 951 | /* |
@@ -828,6 +959,20 @@ static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder) | |||
828 | 959 | ||
829 | tegra_dc_commit(dc); | 960 | tegra_dc_commit(dc); |
830 | } | 961 | } |
962 | |||
963 | if (!hdmi->dvi) { | ||
964 | if (hdmi->stereo) | ||
965 | tegra_hdmi_disable_stereo_infoframe(hdmi); | ||
966 | |||
967 | tegra_hdmi_disable_audio_infoframe(hdmi); | ||
968 | tegra_hdmi_disable_avi_infoframe(hdmi); | ||
969 | tegra_hdmi_disable_audio(hdmi); | ||
970 | } | ||
971 | |||
972 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_ENABLE); | ||
973 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_MASK); | ||
974 | |||
975 | pm_runtime_put(hdmi->dev); | ||
831 | } | 976 | } |
832 | 977 | ||
833 | static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) | 978 | static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) |
@@ -836,21 +981,28 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) | |||
836 | unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey; | 981 | unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey; |
837 | struct tegra_output *output = encoder_to_output(encoder); | 982 | struct tegra_output *output = encoder_to_output(encoder); |
838 | struct tegra_dc *dc = to_tegra_dc(encoder->crtc); | 983 | struct tegra_dc *dc = to_tegra_dc(encoder->crtc); |
839 | struct device_node *node = output->dev->of_node; | ||
840 | struct tegra_hdmi *hdmi = to_hdmi(output); | 984 | struct tegra_hdmi *hdmi = to_hdmi(output); |
841 | unsigned int pulse_start, div82, pclk; | 985 | unsigned int pulse_start, div82; |
842 | int retries = 1000; | 986 | int retries = 1000; |
843 | u32 value; | 987 | u32 value; |
844 | int err; | 988 | int err; |
845 | 989 | ||
846 | hdmi->dvi = !tegra_output_is_hdmi(output); | 990 | pm_runtime_get_sync(hdmi->dev); |
847 | 991 | ||
848 | pclk = mode->clock * 1000; | 992 | /* |
993 | * Enable and unmask the HDA codec SCRATCH0 register interrupt. This | ||
994 | * is used for interoperability between the HDA codec driver and the | ||
995 | * HDMI driver. | ||
996 | */ | ||
997 | tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_ENABLE); | ||
998 | tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_MASK); | ||
999 | |||
1000 | hdmi->pixel_clock = mode->clock * 1000; | ||
849 | h_sync_width = mode->hsync_end - mode->hsync_start; | 1001 | h_sync_width = mode->hsync_end - mode->hsync_start; |
850 | h_back_porch = mode->htotal - mode->hsync_end; | 1002 | h_back_porch = mode->htotal - mode->hsync_end; |
851 | h_front_porch = mode->hsync_start - mode->hdisplay; | 1003 | h_front_porch = mode->hsync_start - mode->hdisplay; |
852 | 1004 | ||
853 | err = clk_set_rate(hdmi->clk, pclk); | 1005 | err = clk_set_rate(hdmi->clk, hdmi->pixel_clock); |
854 | if (err < 0) { | 1006 | if (err < 0) { |
855 | dev_err(hdmi->dev, "failed to set HDMI clock frequency: %d\n", | 1007 | dev_err(hdmi->dev, "failed to set HDMI clock frequency: %d\n", |
856 | err); | 1008 | err); |
@@ -909,17 +1061,15 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) | |||
909 | value = SOR_REFCLK_DIV_INT(div82 >> 2) | SOR_REFCLK_DIV_FRAC(div82); | 1061 | value = SOR_REFCLK_DIV_INT(div82 >> 2) | SOR_REFCLK_DIV_FRAC(div82); |
910 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_REFCLK); | 1062 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_REFCLK); |
911 | 1063 | ||
1064 | hdmi->dvi = !tegra_output_is_hdmi(output); | ||
912 | if (!hdmi->dvi) { | 1065 | if (!hdmi->dvi) { |
913 | err = tegra_hdmi_setup_audio(hdmi, pclk); | 1066 | err = tegra_hdmi_setup_audio(hdmi); |
914 | if (err < 0) | 1067 | if (err < 0) |
915 | hdmi->dvi = true; | 1068 | hdmi->dvi = true; |
916 | } | 1069 | } |
917 | 1070 | ||
918 | if (of_device_is_compatible(node, "nvidia,tegra20-hdmi")) { | 1071 | if (hdmi->config->has_hda) |
919 | /* | 1072 | tegra_hdmi_write_eld(hdmi); |
920 | * TODO: add ELD support | ||
921 | */ | ||
922 | } | ||
923 | 1073 | ||
924 | rekey = HDMI_REKEY_DEFAULT; | 1074 | rekey = HDMI_REKEY_DEFAULT; |
925 | value = HDMI_CTRL_REKEY(rekey); | 1075 | value = HDMI_CTRL_REKEY(rekey); |
@@ -931,20 +1081,17 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) | |||
931 | 1081 | ||
932 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_CTRL); | 1082 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_CTRL); |
933 | 1083 | ||
934 | if (hdmi->dvi) | 1084 | if (!hdmi->dvi) { |
935 | tegra_hdmi_writel(hdmi, 0x0, | 1085 | tegra_hdmi_setup_avi_infoframe(hdmi, mode); |
936 | HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | 1086 | tegra_hdmi_setup_audio_infoframe(hdmi); |
937 | else | ||
938 | tegra_hdmi_writel(hdmi, GENERIC_CTRL_AUDIO, | ||
939 | HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
940 | 1087 | ||
941 | tegra_hdmi_setup_avi_infoframe(hdmi, mode); | 1088 | if (hdmi->stereo) |
942 | tegra_hdmi_setup_audio_infoframe(hdmi); | 1089 | tegra_hdmi_setup_stereo_infoframe(hdmi); |
943 | tegra_hdmi_setup_stereo_infoframe(hdmi); | 1090 | } |
944 | 1091 | ||
945 | /* TMDS CONFIG */ | 1092 | /* TMDS CONFIG */ |
946 | for (i = 0; i < hdmi->config->num_tmds; i++) { | 1093 | for (i = 0; i < hdmi->config->num_tmds; i++) { |
947 | if (pclk <= hdmi->config->tmds[i].pclk) { | 1094 | if (hdmi->pixel_clock <= hdmi->config->tmds[i].pclk) { |
948 | tegra_hdmi_setup_tmds(hdmi, &hdmi->config->tmds[i]); | 1095 | tegra_hdmi_setup_tmds(hdmi, &hdmi->config->tmds[i]); |
949 | break; | 1096 | break; |
950 | } | 1097 | } |
@@ -1031,6 +1178,15 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) | |||
1031 | 1178 | ||
1032 | tegra_dc_commit(dc); | 1179 | tegra_dc_commit(dc); |
1033 | 1180 | ||
1181 | if (!hdmi->dvi) { | ||
1182 | tegra_hdmi_enable_avi_infoframe(hdmi); | ||
1183 | tegra_hdmi_enable_audio_infoframe(hdmi); | ||
1184 | tegra_hdmi_enable_audio(hdmi); | ||
1185 | |||
1186 | if (hdmi->stereo) | ||
1187 | tegra_hdmi_enable_stereo_infoframe(hdmi); | ||
1188 | } | ||
1189 | |||
1034 | /* TODO: add HDCP support */ | 1190 | /* TODO: add HDCP support */ |
1035 | } | 1191 | } |
1036 | 1192 | ||
@@ -1235,8 +1391,14 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data) | |||
1235 | DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG); | 1391 | DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG); |
1236 | DUMP_REG(HDMI_NV_PDISP_KEY_SKEY_INDEX); | 1392 | DUMP_REG(HDMI_NV_PDISP_KEY_SKEY_INDEX); |
1237 | DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0); | 1393 | DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0); |
1394 | DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_SPARE0); | ||
1395 | DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); | ||
1396 | DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1); | ||
1238 | DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); | 1397 | DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); |
1239 | DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); | 1398 | DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); |
1399 | DUMP_REG(HDMI_NV_PDISP_INT_STATUS); | ||
1400 | DUMP_REG(HDMI_NV_PDISP_INT_MASK); | ||
1401 | DUMP_REG(HDMI_NV_PDISP_INT_ENABLE); | ||
1240 | DUMP_REG(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT); | 1402 | DUMP_REG(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT); |
1241 | 1403 | ||
1242 | #undef DUMP_REG | 1404 | #undef DUMP_REG |
@@ -1360,14 +1522,6 @@ static int tegra_hdmi_init(struct host1x_client *client) | |||
1360 | return err; | 1522 | return err; |
1361 | } | 1523 | } |
1362 | 1524 | ||
1363 | err = clk_prepare_enable(hdmi->clk); | ||
1364 | if (err < 0) { | ||
1365 | dev_err(hdmi->dev, "failed to enable clock: %d\n", err); | ||
1366 | return err; | ||
1367 | } | ||
1368 | |||
1369 | reset_control_deassert(hdmi->rst); | ||
1370 | |||
1371 | return 0; | 1525 | return 0; |
1372 | } | 1526 | } |
1373 | 1527 | ||
@@ -1377,9 +1531,6 @@ static int tegra_hdmi_exit(struct host1x_client *client) | |||
1377 | 1531 | ||
1378 | tegra_output_exit(&hdmi->output); | 1532 | tegra_output_exit(&hdmi->output); |
1379 | 1533 | ||
1380 | reset_control_assert(hdmi->rst); | ||
1381 | clk_disable_unprepare(hdmi->clk); | ||
1382 | |||
1383 | regulator_disable(hdmi->vdd); | 1534 | regulator_disable(hdmi->vdd); |
1384 | regulator_disable(hdmi->pll); | 1535 | regulator_disable(hdmi->pll); |
1385 | regulator_disable(hdmi->hdmi); | 1536 | regulator_disable(hdmi->hdmi); |
@@ -1401,6 +1552,8 @@ static const struct tegra_hdmi_config tegra20_hdmi_config = { | |||
1401 | .fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, | 1552 | .fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, |
1402 | .fuse_override_value = 1 << 31, | 1553 | .fuse_override_value = 1 << 31, |
1403 | .has_sor_io_peak_current = false, | 1554 | .has_sor_io_peak_current = false, |
1555 | .has_hda = false, | ||
1556 | .has_hbr = false, | ||
1404 | }; | 1557 | }; |
1405 | 1558 | ||
1406 | static const struct tegra_hdmi_config tegra30_hdmi_config = { | 1559 | static const struct tegra_hdmi_config tegra30_hdmi_config = { |
@@ -1409,6 +1562,8 @@ static const struct tegra_hdmi_config tegra30_hdmi_config = { | |||
1409 | .fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, | 1562 | .fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, |
1410 | .fuse_override_value = 1 << 31, | 1563 | .fuse_override_value = 1 << 31, |
1411 | .has_sor_io_peak_current = false, | 1564 | .has_sor_io_peak_current = false, |
1565 | .has_hda = true, | ||
1566 | .has_hbr = false, | ||
1412 | }; | 1567 | }; |
1413 | 1568 | ||
1414 | static const struct tegra_hdmi_config tegra114_hdmi_config = { | 1569 | static const struct tegra_hdmi_config tegra114_hdmi_config = { |
@@ -1417,6 +1572,8 @@ static const struct tegra_hdmi_config tegra114_hdmi_config = { | |||
1417 | .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0, | 1572 | .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0, |
1418 | .fuse_override_value = 1 << 31, | 1573 | .fuse_override_value = 1 << 31, |
1419 | .has_sor_io_peak_current = true, | 1574 | .has_sor_io_peak_current = true, |
1575 | .has_hda = true, | ||
1576 | .has_hbr = true, | ||
1420 | }; | 1577 | }; |
1421 | 1578 | ||
1422 | static const struct tegra_hdmi_config tegra124_hdmi_config = { | 1579 | static const struct tegra_hdmi_config tegra124_hdmi_config = { |
@@ -1425,6 +1582,8 @@ static const struct tegra_hdmi_config tegra124_hdmi_config = { | |||
1425 | .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0, | 1582 | .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0, |
1426 | .fuse_override_value = 1 << 31, | 1583 | .fuse_override_value = 1 << 31, |
1427 | .has_sor_io_peak_current = true, | 1584 | .has_sor_io_peak_current = true, |
1585 | .has_hda = true, | ||
1586 | .has_hbr = true, | ||
1428 | }; | 1587 | }; |
1429 | 1588 | ||
1430 | static const struct of_device_id tegra_hdmi_of_match[] = { | 1589 | static const struct of_device_id tegra_hdmi_of_match[] = { |
@@ -1436,6 +1595,67 @@ static const struct of_device_id tegra_hdmi_of_match[] = { | |||
1436 | }; | 1595 | }; |
1437 | MODULE_DEVICE_TABLE(of, tegra_hdmi_of_match); | 1596 | MODULE_DEVICE_TABLE(of, tegra_hdmi_of_match); |
1438 | 1597 | ||
1598 | static void hda_format_parse(unsigned int format, unsigned int *rate, | ||
1599 | unsigned int *channels) | ||
1600 | { | ||
1601 | unsigned int mul, div; | ||
1602 | |||
1603 | if (format & AC_FMT_BASE_44K) | ||
1604 | *rate = 44100; | ||
1605 | else | ||
1606 | *rate = 48000; | ||
1607 | |||
1608 | mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT; | ||
1609 | div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT; | ||
1610 | |||
1611 | *rate = *rate * (mul + 1) / (div + 1); | ||
1612 | |||
1613 | *channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT; | ||
1614 | } | ||
1615 | |||
1616 | static irqreturn_t tegra_hdmi_irq(int irq, void *data) | ||
1617 | { | ||
1618 | struct tegra_hdmi *hdmi = data; | ||
1619 | u32 value; | ||
1620 | int err; | ||
1621 | |||
1622 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_INT_STATUS); | ||
1623 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_INT_STATUS); | ||
1624 | |||
1625 | if (value & INT_CODEC_SCRATCH0) { | ||
1626 | unsigned int format; | ||
1627 | u32 value; | ||
1628 | |||
1629 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); | ||
1630 | |||
1631 | if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) { | ||
1632 | unsigned int sample_rate, channels; | ||
1633 | |||
1634 | format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK; | ||
1635 | |||
1636 | hda_format_parse(format, &sample_rate, &channels); | ||
1637 | |||
1638 | hdmi->audio_sample_rate = sample_rate; | ||
1639 | hdmi->audio_channels = channels; | ||
1640 | |||
1641 | err = tegra_hdmi_setup_audio(hdmi); | ||
1642 | if (err < 0) { | ||
1643 | tegra_hdmi_disable_audio_infoframe(hdmi); | ||
1644 | tegra_hdmi_disable_audio(hdmi); | ||
1645 | } else { | ||
1646 | tegra_hdmi_setup_audio_infoframe(hdmi); | ||
1647 | tegra_hdmi_enable_audio_infoframe(hdmi); | ||
1648 | tegra_hdmi_enable_audio(hdmi); | ||
1649 | } | ||
1650 | } else { | ||
1651 | tegra_hdmi_disable_audio_infoframe(hdmi); | ||
1652 | tegra_hdmi_disable_audio(hdmi); | ||
1653 | } | ||
1654 | } | ||
1655 | |||
1656 | return IRQ_HANDLED; | ||
1657 | } | ||
1658 | |||
1439 | static int tegra_hdmi_probe(struct platform_device *pdev) | 1659 | static int tegra_hdmi_probe(struct platform_device *pdev) |
1440 | { | 1660 | { |
1441 | const struct of_device_id *match; | 1661 | const struct of_device_id *match; |
@@ -1453,8 +1673,10 @@ static int tegra_hdmi_probe(struct platform_device *pdev) | |||
1453 | 1673 | ||
1454 | hdmi->config = match->data; | 1674 | hdmi->config = match->data; |
1455 | hdmi->dev = &pdev->dev; | 1675 | hdmi->dev = &pdev->dev; |
1676 | |||
1456 | hdmi->audio_source = AUTO; | 1677 | hdmi->audio_source = AUTO; |
1457 | hdmi->audio_freq = 44100; | 1678 | hdmi->audio_sample_rate = 48000; |
1679 | hdmi->audio_channels = 2; | ||
1458 | hdmi->stereo = false; | 1680 | hdmi->stereo = false; |
1459 | hdmi->dvi = false; | 1681 | hdmi->dvi = false; |
1460 | 1682 | ||
@@ -1515,6 +1737,17 @@ static int tegra_hdmi_probe(struct platform_device *pdev) | |||
1515 | 1737 | ||
1516 | hdmi->irq = err; | 1738 | hdmi->irq = err; |
1517 | 1739 | ||
1740 | err = devm_request_irq(hdmi->dev, hdmi->irq, tegra_hdmi_irq, 0, | ||
1741 | dev_name(hdmi->dev), hdmi); | ||
1742 | if (err < 0) { | ||
1743 | dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", | ||
1744 | hdmi->irq, err); | ||
1745 | return err; | ||
1746 | } | ||
1747 | |||
1748 | platform_set_drvdata(pdev, hdmi); | ||
1749 | pm_runtime_enable(&pdev->dev); | ||
1750 | |||
1518 | INIT_LIST_HEAD(&hdmi->client.list); | 1751 | INIT_LIST_HEAD(&hdmi->client.list); |
1519 | hdmi->client.ops = &hdmi_client_ops; | 1752 | hdmi->client.ops = &hdmi_client_ops; |
1520 | hdmi->client.dev = &pdev->dev; | 1753 | hdmi->client.dev = &pdev->dev; |
@@ -1526,8 +1759,6 @@ static int tegra_hdmi_probe(struct platform_device *pdev) | |||
1526 | return err; | 1759 | return err; |
1527 | } | 1760 | } |
1528 | 1761 | ||
1529 | platform_set_drvdata(pdev, hdmi); | ||
1530 | |||
1531 | return 0; | 1762 | return 0; |
1532 | } | 1763 | } |
1533 | 1764 | ||
@@ -1536,6 +1767,8 @@ static int tegra_hdmi_remove(struct platform_device *pdev) | |||
1536 | struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); | 1767 | struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); |
1537 | int err; | 1768 | int err; |
1538 | 1769 | ||
1770 | pm_runtime_disable(&pdev->dev); | ||
1771 | |||
1539 | err = host1x_client_unregister(&hdmi->client); | 1772 | err = host1x_client_unregister(&hdmi->client); |
1540 | if (err < 0) { | 1773 | if (err < 0) { |
1541 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | 1774 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", |
@@ -1545,17 +1778,61 @@ static int tegra_hdmi_remove(struct platform_device *pdev) | |||
1545 | 1778 | ||
1546 | tegra_output_remove(&hdmi->output); | 1779 | tegra_output_remove(&hdmi->output); |
1547 | 1780 | ||
1548 | clk_disable_unprepare(hdmi->clk_parent); | 1781 | return 0; |
1782 | } | ||
1783 | |||
1784 | #ifdef CONFIG_PM | ||
1785 | static int tegra_hdmi_suspend(struct device *dev) | ||
1786 | { | ||
1787 | struct tegra_hdmi *hdmi = dev_get_drvdata(dev); | ||
1788 | int err; | ||
1789 | |||
1790 | err = reset_control_assert(hdmi->rst); | ||
1791 | if (err < 0) { | ||
1792 | dev_err(dev, "failed to assert reset: %d\n", err); | ||
1793 | return err; | ||
1794 | } | ||
1795 | |||
1796 | usleep_range(1000, 2000); | ||
1797 | |||
1549 | clk_disable_unprepare(hdmi->clk); | 1798 | clk_disable_unprepare(hdmi->clk); |
1550 | 1799 | ||
1551 | return 0; | 1800 | return 0; |
1552 | } | 1801 | } |
1553 | 1802 | ||
1803 | static int tegra_hdmi_resume(struct device *dev) | ||
1804 | { | ||
1805 | struct tegra_hdmi *hdmi = dev_get_drvdata(dev); | ||
1806 | int err; | ||
1807 | |||
1808 | err = clk_prepare_enable(hdmi->clk); | ||
1809 | if (err < 0) { | ||
1810 | dev_err(dev, "failed to enable clock: %d\n", err); | ||
1811 | return err; | ||
1812 | } | ||
1813 | |||
1814 | usleep_range(1000, 2000); | ||
1815 | |||
1816 | err = reset_control_deassert(hdmi->rst); | ||
1817 | if (err < 0) { | ||
1818 | dev_err(dev, "failed to deassert reset: %d\n", err); | ||
1819 | clk_disable_unprepare(hdmi->clk); | ||
1820 | return err; | ||
1821 | } | ||
1822 | |||
1823 | return 0; | ||
1824 | } | ||
1825 | #endif | ||
1826 | |||
1827 | static const struct dev_pm_ops tegra_hdmi_pm_ops = { | ||
1828 | SET_RUNTIME_PM_OPS(tegra_hdmi_suspend, tegra_hdmi_resume, NULL) | ||
1829 | }; | ||
1830 | |||
1554 | struct platform_driver tegra_hdmi_driver = { | 1831 | struct platform_driver tegra_hdmi_driver = { |
1555 | .driver = { | 1832 | .driver = { |
1556 | .name = "tegra-hdmi", | 1833 | .name = "tegra-hdmi", |
1557 | .owner = THIS_MODULE, | ||
1558 | .of_match_table = tegra_hdmi_of_match, | 1834 | .of_match_table = tegra_hdmi_of_match, |
1835 | .pm = &tegra_hdmi_pm_ops, | ||
1559 | }, | 1836 | }, |
1560 | .probe = tegra_hdmi_probe, | 1837 | .probe = tegra_hdmi_probe, |
1561 | .remove = tegra_hdmi_remove, | 1838 | .remove = tegra_hdmi_remove, |