diff options
author | Dave Airlie <airlied@redhat.com> | 2016-09-12 20:28:17 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2016-09-12 20:28:17 -0400 |
commit | 8506912b969b60aacc733315eeeb46b014d920a4 (patch) | |
tree | 42a786a5e6d20470f9eec8cd2614d9e827505a9c | |
parent | b4eac5465b23a9bcb4a66376a5664086b4913288 (diff) | |
parent | df0bd1e8f3c508bf4c3445f94b12e38289b65f13 (diff) |
Merge branch 'drm-tda998x-devel' of git://git.armlinux.org.uk/~rmk/linux-arm into drm-next
This adds the ASoC codec interfaces for TDA998x HDMI audio from
Jyri Sarha.
* 'drm-tda998x-devel' of git://git.armlinux.org.uk/~rmk/linux-arm:
ARM: dts: am335x-boneblack: Add HDMI audio support
drm/i2c: tda998x: Register ASoC hdmi-codec and add audio DT binding
drm/i2c: tda998x: Improve tda998x_configure_audio() audio related pdata
-rw-r--r-- | Documentation/devicetree/bindings/display/bridge/tda998x.txt | 18 | ||||
-rw-r--r-- | arch/arm/boot/dts/am335x-boneblack.dts | 71 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/tda998x_drv.c | 297 | ||||
-rw-r--r-- | include/drm/i2c/tda998x.h | 29 | ||||
-rw-r--r-- | include/dt-bindings/display/tda998x.h | 7 |
6 files changed, 368 insertions, 55 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/tda998x.txt b/Documentation/devicetree/bindings/display/bridge/tda998x.txt index e178e6b9f9ee..24cc2466185a 100644 --- a/Documentation/devicetree/bindings/display/bridge/tda998x.txt +++ b/Documentation/devicetree/bindings/display/bridge/tda998x.txt | |||
@@ -21,8 +21,19 @@ Optional properties: | |||
21 | - video-ports: 24 bits value which defines how the video controller | 21 | - video-ports: 24 bits value which defines how the video controller |
22 | output is wired to the TDA998x input - default: <0x230145> | 22 | output is wired to the TDA998x input - default: <0x230145> |
23 | 23 | ||
24 | - audio-ports: array of 8-bit values, 2 values per one DAI[1]. | ||
25 | The first value defines the DAI type: TDA998x_SPDIF or TDA998x_I2S[2]. | ||
26 | The second value defines the tda998x AP_ENA reg content when the DAI | ||
27 | in question is used. The implementation allows one or two DAIs. If two | ||
28 | DAIs are defined, they must be of different type. | ||
29 | |||
30 | [1] Documentation/sound/alsa/soc/DAI.txt | ||
31 | [2] include/dt-bindings/display/tda998x.h | ||
32 | |||
24 | Example: | 33 | Example: |
25 | 34 | ||
35 | #include <dt-bindings/display/tda998x.h> | ||
36 | |||
26 | tda998x: hdmi-encoder { | 37 | tda998x: hdmi-encoder { |
27 | compatible = "nxp,tda998x"; | 38 | compatible = "nxp,tda998x"; |
28 | reg = <0x70>; | 39 | reg = <0x70>; |
@@ -30,4 +41,11 @@ Example: | |||
30 | interrupts = <27 2>; /* falling edge */ | 41 | interrupts = <27 2>; /* falling edge */ |
31 | pinctrl-0 = <&pmx_camera>; | 42 | pinctrl-0 = <&pmx_camera>; |
32 | pinctrl-names = "default"; | 43 | pinctrl-names = "default"; |
44 | video-ports = <0x230145>; | ||
45 | |||
46 | #sound-dai-cells = <2>; | ||
47 | /* DAI-format AP_ENA reg value */ | ||
48 | audio-ports = < TDA998x_SPDIF 0x04 | ||
49 | TDA998x_I2S 0x03>; | ||
50 | |||
33 | }; | 51 | }; |
diff --git a/arch/arm/boot/dts/am335x-boneblack.dts b/arch/arm/boot/dts/am335x-boneblack.dts index ca721670bd91..528559b33d8a 100644 --- a/arch/arm/boot/dts/am335x-boneblack.dts +++ b/arch/arm/boot/dts/am335x-boneblack.dts | |||
@@ -9,6 +9,7 @@ | |||
9 | 9 | ||
10 | #include "am33xx.dtsi" | 10 | #include "am33xx.dtsi" |
11 | #include "am335x-bone-common.dtsi" | 11 | #include "am335x-bone-common.dtsi" |
12 | #include <dt-bindings/display/tda998x.h> | ||
12 | 13 | ||
13 | / { | 14 | / { |
14 | model = "TI AM335x BeagleBone Black"; | 15 | model = "TI AM335x BeagleBone Black"; |
@@ -75,6 +76,16 @@ | |||
75 | AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ | 76 | AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ |
76 | >; | 77 | >; |
77 | }; | 78 | }; |
79 | |||
80 | mcasp0_pins: mcasp0_pins { | ||
81 | pinctrl-single,pins = < | ||
82 | AM33XX_IOPAD(0x9ac, PIN_INPUT_PULLUP | MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */ | ||
83 | AM33XX_IOPAD(0x99c, PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ | ||
84 | AM33XX_IOPAD(0x994, PIN_OUTPUT_PULLUP | MUX_MODE0) /* mcasp0_fsx.mcasp0_fsx */ | ||
85 | AM33XX_IOPAD(0x990, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mcasp0_aclkx.mcasp0_aclkx */ | ||
86 | AM33XX_IOPAD(0x86c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a11.GPIO1_27 */ | ||
87 | >; | ||
88 | }; | ||
78 | }; | 89 | }; |
79 | 90 | ||
80 | &lcdc { | 91 | &lcdc { |
@@ -87,16 +98,22 @@ | |||
87 | }; | 98 | }; |
88 | 99 | ||
89 | &i2c0 { | 100 | &i2c0 { |
90 | tda19988 { | 101 | tda19988: tda19988 { |
91 | compatible = "nxp,tda998x"; | 102 | compatible = "nxp,tda998x"; |
92 | reg = <0x70>; | 103 | reg = <0x70>; |
104 | |||
93 | pinctrl-names = "default", "off"; | 105 | pinctrl-names = "default", "off"; |
94 | pinctrl-0 = <&nxp_hdmi_bonelt_pins>; | 106 | pinctrl-0 = <&nxp_hdmi_bonelt_pins>; |
95 | pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; | 107 | pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; |
96 | 108 | ||
97 | port { | 109 | #sound-dai-cells = <0>; |
98 | hdmi_0: endpoint@0 { | 110 | audio-ports = < TDA998x_I2S 0x03>; |
99 | remote-endpoint = <&lcdc_0>; | 111 | |
112 | ports { | ||
113 | port@0 { | ||
114 | hdmi_0: endpoint@0 { | ||
115 | remote-endpoint = <&lcdc_0>; | ||
116 | }; | ||
100 | }; | 117 | }; |
101 | }; | 118 | }; |
102 | }; | 119 | }; |
@@ -105,3 +122,49 @@ | |||
105 | &rtc { | 122 | &rtc { |
106 | system-power-controller; | 123 | system-power-controller; |
107 | }; | 124 | }; |
125 | |||
126 | &mcasp0 { | ||
127 | #sound-dai-cells = <0>; | ||
128 | pinctrl-names = "default"; | ||
129 | pinctrl-0 = <&mcasp0_pins>; | ||
130 | status = "okay"; | ||
131 | op-mode = <0>; /* MCASP_IIS_MODE */ | ||
132 | tdm-slots = <2>; | ||
133 | serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ | ||
134 | 0 0 1 0 | ||
135 | >; | ||
136 | tx-num-evt = <32>; | ||
137 | rx-num-evt = <32>; | ||
138 | }; | ||
139 | |||
140 | / { | ||
141 | clk_mcasp0_fixed: clk_mcasp0_fixed { | ||
142 | #clock-cells = <0>; | ||
143 | compatible = "fixed-clock"; | ||
144 | clock-frequency = <24576000>; | ||
145 | }; | ||
146 | |||
147 | clk_mcasp0: clk_mcasp0 { | ||
148 | #clock-cells = <0>; | ||
149 | compatible = "gpio-gate-clock"; | ||
150 | clocks = <&clk_mcasp0_fixed>; | ||
151 | enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ | ||
152 | }; | ||
153 | |||
154 | sound { | ||
155 | compatible = "simple-audio-card"; | ||
156 | simple-audio-card,name = "TI BeagleBone Black"; | ||
157 | simple-audio-card,format = "i2s"; | ||
158 | simple-audio-card,bitclock-master = <&dailink0_master>; | ||
159 | simple-audio-card,frame-master = <&dailink0_master>; | ||
160 | |||
161 | dailink0_master: simple-audio-card,cpu { | ||
162 | sound-dai = <&mcasp0>; | ||
163 | clocks = <&clk_mcasp0>; | ||
164 | }; | ||
165 | |||
166 | simple-audio-card,codec { | ||
167 | sound-dai = <&tda19988>; | ||
168 | }; | ||
169 | }; | ||
170 | }; | ||
diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 4d341db462a2..a6c92beb410a 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig | |||
@@ -22,6 +22,7 @@ config DRM_I2C_SIL164 | |||
22 | config DRM_I2C_NXP_TDA998X | 22 | config DRM_I2C_NXP_TDA998X |
23 | tristate "NXP Semiconductors TDA998X HDMI encoder" | 23 | tristate "NXP Semiconductors TDA998X HDMI encoder" |
24 | default m if DRM_TILCDC | 24 | default m if DRM_TILCDC |
25 | select SND_SOC_HDMI_CODEC if SND_SOC | ||
25 | help | 26 | help |
26 | Support for NXP Semiconductors TDA998X HDMI encoders. | 27 | Support for NXP Semiconductors TDA998X HDMI encoders. |
27 | 28 | ||
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index f4315bc8d471..9798d400d817 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/irq.h> | 21 | #include <linux/irq.h> |
22 | #include <sound/asoundef.h> | 22 | #include <sound/asoundef.h> |
23 | #include <sound/hdmi-codec.h> | ||
23 | 24 | ||
24 | #include <drm/drmP.h> | 25 | #include <drm/drmP.h> |
25 | #include <drm/drm_atomic_helper.h> | 26 | #include <drm/drm_atomic_helper.h> |
@@ -30,6 +31,11 @@ | |||
30 | 31 | ||
31 | #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) | 32 | #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) |
32 | 33 | ||
34 | struct tda998x_audio_port { | ||
35 | u8 format; /* AFMT_xxx */ | ||
36 | u8 config; /* AP value */ | ||
37 | }; | ||
38 | |||
33 | struct tda998x_priv { | 39 | struct tda998x_priv { |
34 | struct i2c_client *cec; | 40 | struct i2c_client *cec; |
35 | struct i2c_client *hdmi; | 41 | struct i2c_client *hdmi; |
@@ -41,7 +47,10 @@ struct tda998x_priv { | |||
41 | u8 vip_cntrl_0; | 47 | u8 vip_cntrl_0; |
42 | u8 vip_cntrl_1; | 48 | u8 vip_cntrl_1; |
43 | u8 vip_cntrl_2; | 49 | u8 vip_cntrl_2; |
44 | struct tda998x_encoder_params params; | 50 | struct tda998x_audio_params audio_params; |
51 | |||
52 | struct platform_device *audio_pdev; | ||
53 | struct mutex audio_mutex; | ||
45 | 54 | ||
46 | wait_queue_head_t wq_edid; | 55 | wait_queue_head_t wq_edid; |
47 | volatile int wq_edid_wait; | 56 | volatile int wq_edid_wait; |
@@ -53,6 +62,8 @@ struct tda998x_priv { | |||
53 | 62 | ||
54 | struct drm_encoder encoder; | 63 | struct drm_encoder encoder; |
55 | struct drm_connector connector; | 64 | struct drm_connector connector; |
65 | |||
66 | struct tda998x_audio_port audio_port[2]; | ||
56 | }; | 67 | }; |
57 | 68 | ||
58 | #define conn_to_tda998x_priv(x) \ | 69 | #define conn_to_tda998x_priv(x) \ |
@@ -666,26 +677,16 @@ tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr, | |||
666 | reg_set(priv, REG_DIP_IF_FLAGS, bit); | 677 | reg_set(priv, REG_DIP_IF_FLAGS, bit); |
667 | } | 678 | } |
668 | 679 | ||
669 | static void | 680 | static int tda998x_write_aif(struct tda998x_priv *priv, |
670 | tda998x_write_aif(struct tda998x_priv *priv, struct tda998x_encoder_params *p) | 681 | struct hdmi_audio_infoframe *cea) |
671 | { | 682 | { |
672 | union hdmi_infoframe frame; | 683 | union hdmi_infoframe frame; |
673 | 684 | ||
674 | hdmi_audio_infoframe_init(&frame.audio); | 685 | frame.audio = *cea; |
675 | |||
676 | frame.audio.channels = p->audio_frame[1] & 0x07; | ||
677 | frame.audio.channel_allocation = p->audio_frame[4]; | ||
678 | frame.audio.level_shift_value = (p->audio_frame[5] & 0x78) >> 3; | ||
679 | frame.audio.downmix_inhibit = (p->audio_frame[5] & 0x80) >> 7; | ||
680 | |||
681 | /* | ||
682 | * L-PCM and IEC61937 compressed audio shall always set sample | ||
683 | * frequency to "refer to stream". For others, see the HDMI | ||
684 | * specification. | ||
685 | */ | ||
686 | frame.audio.sample_frequency = (p->audio_frame[2] & 0x1c) >> 2; | ||
687 | 686 | ||
688 | tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame); | 687 | tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame); |
688 | |||
689 | return 0; | ||
689 | } | 690 | } |
690 | 691 | ||
691 | static void | 692 | static void |
@@ -710,20 +711,21 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on) | |||
710 | } | 711 | } |
711 | } | 712 | } |
712 | 713 | ||
713 | static void | 714 | static int |
714 | tda998x_configure_audio(struct tda998x_priv *priv, | 715 | tda998x_configure_audio(struct tda998x_priv *priv, |
715 | struct drm_display_mode *mode, struct tda998x_encoder_params *p) | 716 | struct tda998x_audio_params *params, |
717 | unsigned mode_clock) | ||
716 | { | 718 | { |
717 | u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv; | 719 | u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv; |
718 | u32 n; | 720 | u32 n; |
719 | 721 | ||
720 | /* Enable audio ports */ | 722 | /* Enable audio ports */ |
721 | reg_write(priv, REG_ENA_AP, p->audio_cfg); | 723 | reg_write(priv, REG_ENA_AP, params->config); |
722 | reg_write(priv, REG_ENA_ACLK, p->audio_clk_cfg); | ||
723 | 724 | ||
724 | /* Set audio input source */ | 725 | /* Set audio input source */ |
725 | switch (p->audio_format) { | 726 | switch (params->format) { |
726 | case AFMT_SPDIF: | 727 | case AFMT_SPDIF: |
728 | reg_write(priv, REG_ENA_ACLK, 0); | ||
727 | reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF); | 729 | reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF); |
728 | clksel_aip = AIP_CLKSEL_AIP_SPDIF; | 730 | clksel_aip = AIP_CLKSEL_AIP_SPDIF; |
729 | clksel_fs = AIP_CLKSEL_FS_FS64SPDIF; | 731 | clksel_fs = AIP_CLKSEL_FS_FS64SPDIF; |
@@ -731,15 +733,29 @@ tda998x_configure_audio(struct tda998x_priv *priv, | |||
731 | break; | 733 | break; |
732 | 734 | ||
733 | case AFMT_I2S: | 735 | case AFMT_I2S: |
736 | reg_write(priv, REG_ENA_ACLK, 1); | ||
734 | reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S); | 737 | reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S); |
735 | clksel_aip = AIP_CLKSEL_AIP_I2S; | 738 | clksel_aip = AIP_CLKSEL_AIP_I2S; |
736 | clksel_fs = AIP_CLKSEL_FS_ACLK; | 739 | clksel_fs = AIP_CLKSEL_FS_ACLK; |
737 | cts_n = CTS_N_M(3) | CTS_N_K(3); | 740 | switch (params->sample_width) { |
741 | case 16: | ||
742 | cts_n = CTS_N_M(3) | CTS_N_K(1); | ||
743 | break; | ||
744 | case 18: | ||
745 | case 20: | ||
746 | case 24: | ||
747 | cts_n = CTS_N_M(3) | CTS_N_K(2); | ||
748 | break; | ||
749 | default: | ||
750 | case 32: | ||
751 | cts_n = CTS_N_M(3) | CTS_N_K(3); | ||
752 | break; | ||
753 | } | ||
738 | break; | 754 | break; |
739 | 755 | ||
740 | default: | 756 | default: |
741 | BUG(); | 757 | dev_err(&priv->hdmi->dev, "Unsupported I2S format\n"); |
742 | return; | 758 | return -EINVAL; |
743 | } | 759 | } |
744 | 760 | ||
745 | reg_write(priv, REG_AIP_CLKSEL, clksel_aip); | 761 | reg_write(priv, REG_AIP_CLKSEL, clksel_aip); |
@@ -755,11 +771,11 @@ tda998x_configure_audio(struct tda998x_priv *priv, | |||
755 | * assume 100MHz requires larger divider. | 771 | * assume 100MHz requires larger divider. |
756 | */ | 772 | */ |
757 | adiv = AUDIO_DIV_SERCLK_8; | 773 | adiv = AUDIO_DIV_SERCLK_8; |
758 | if (mode->clock > 100000) | 774 | if (mode_clock > 100000) |
759 | adiv++; /* AUDIO_DIV_SERCLK_16 */ | 775 | adiv++; /* AUDIO_DIV_SERCLK_16 */ |
760 | 776 | ||
761 | /* S/PDIF asks for a larger divider */ | 777 | /* S/PDIF asks for a larger divider */ |
762 | if (p->audio_format == AFMT_SPDIF) | 778 | if (params->format == AFMT_SPDIF) |
763 | adiv++; /* AUDIO_DIV_SERCLK_16 or _32 */ | 779 | adiv++; /* AUDIO_DIV_SERCLK_16 or _32 */ |
764 | 780 | ||
765 | reg_write(priv, REG_AUDIO_DIV, adiv); | 781 | reg_write(priv, REG_AUDIO_DIV, adiv); |
@@ -768,7 +784,7 @@ tda998x_configure_audio(struct tda998x_priv *priv, | |||
768 | * This is the approximate value of N, which happens to be | 784 | * This is the approximate value of N, which happens to be |
769 | * the recommended values for non-coherent clocks. | 785 | * the recommended values for non-coherent clocks. |
770 | */ | 786 | */ |
771 | n = 128 * p->audio_sample_rate / 1000; | 787 | n = 128 * params->sample_rate / 1000; |
772 | 788 | ||
773 | /* Write the CTS and N values */ | 789 | /* Write the CTS and N values */ |
774 | buf[0] = 0x44; | 790 | buf[0] = 0x44; |
@@ -786,20 +802,21 @@ tda998x_configure_audio(struct tda998x_priv *priv, | |||
786 | reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS); | 802 | reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS); |
787 | reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS); | 803 | reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS); |
788 | 804 | ||
789 | /* Write the channel status */ | 805 | /* Write the channel status |
790 | buf[0] = IEC958_AES0_CON_NOT_COPYRIGHT; | 806 | * The REG_CH_STAT_B-registers skip IEC958 AES2 byte, because |
791 | buf[1] = 0x00; | 807 | * there is a separate register for each I2S wire. |
792 | buf[2] = IEC958_AES3_CON_FS_NOTID; | 808 | */ |
793 | buf[3] = IEC958_AES4_CON_ORIGFS_NOTID | | 809 | buf[0] = params->status[0]; |
794 | IEC958_AES4_CON_MAX_WORDLEN_24; | 810 | buf[1] = params->status[1]; |
811 | buf[2] = params->status[3]; | ||
812 | buf[3] = params->status[4]; | ||
795 | reg_write_range(priv, REG_CH_STAT_B(0), buf, 4); | 813 | reg_write_range(priv, REG_CH_STAT_B(0), buf, 4); |
796 | 814 | ||
797 | tda998x_audio_mute(priv, true); | 815 | tda998x_audio_mute(priv, true); |
798 | msleep(20); | 816 | msleep(20); |
799 | tda998x_audio_mute(priv, false); | 817 | tda998x_audio_mute(priv, false); |
800 | 818 | ||
801 | /* Write the audio information packet */ | 819 | return tda998x_write_aif(priv, ¶ms->cea); |
802 | tda998x_write_aif(priv, p); | ||
803 | } | 820 | } |
804 | 821 | ||
805 | /* DRM encoder functions */ | 822 | /* DRM encoder functions */ |
@@ -820,7 +837,7 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv, | |||
820 | VIP_CNTRL_2_SWAP_F(p->swap_f) | | 837 | VIP_CNTRL_2_SWAP_F(p->swap_f) | |
821 | (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0); | 838 | (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0); |
822 | 839 | ||
823 | priv->params = *p; | 840 | priv->audio_params = p->audio_params; |
824 | } | 841 | } |
825 | 842 | ||
826 | static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) | 843 | static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) |
@@ -1057,9 +1074,13 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, | |||
1057 | 1074 | ||
1058 | tda998x_write_avi(priv, adjusted_mode); | 1075 | tda998x_write_avi(priv, adjusted_mode); |
1059 | 1076 | ||
1060 | if (priv->params.audio_cfg) | 1077 | if (priv->audio_params.format != AFMT_UNUSED) { |
1061 | tda998x_configure_audio(priv, adjusted_mode, | 1078 | mutex_lock(&priv->audio_mutex); |
1062 | &priv->params); | 1079 | tda998x_configure_audio(priv, |
1080 | &priv->audio_params, | ||
1081 | adjusted_mode->clock); | ||
1082 | mutex_unlock(&priv->audio_mutex); | ||
1083 | } | ||
1063 | } | 1084 | } |
1064 | } | 1085 | } |
1065 | 1086 | ||
@@ -1159,6 +1180,8 @@ static int tda998x_connector_get_modes(struct drm_connector *connector) | |||
1159 | drm_mode_connector_update_edid_property(connector, edid); | 1180 | drm_mode_connector_update_edid_property(connector, edid); |
1160 | n = drm_add_edid_modes(connector, edid); | 1181 | n = drm_add_edid_modes(connector, edid); |
1161 | priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid); | 1182 | priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid); |
1183 | drm_edid_to_eld(connector, edid); | ||
1184 | |||
1162 | kfree(edid); | 1185 | kfree(edid); |
1163 | 1186 | ||
1164 | return n; | 1187 | return n; |
@@ -1180,6 +1203,9 @@ static void tda998x_destroy(struct tda998x_priv *priv) | |||
1180 | cec_write(priv, REG_CEC_RXSHPDINTENA, 0); | 1203 | cec_write(priv, REG_CEC_RXSHPDINTENA, 0); |
1181 | reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); | 1204 | reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); |
1182 | 1205 | ||
1206 | if (priv->audio_pdev) | ||
1207 | platform_device_unregister(priv->audio_pdev); | ||
1208 | |||
1183 | if (priv->hdmi->irq) | 1209 | if (priv->hdmi->irq) |
1184 | free_irq(priv->hdmi->irq, priv); | 1210 | free_irq(priv->hdmi->irq, priv); |
1185 | 1211 | ||
@@ -1189,8 +1215,189 @@ static void tda998x_destroy(struct tda998x_priv *priv) | |||
1189 | i2c_unregister_device(priv->cec); | 1215 | i2c_unregister_device(priv->cec); |
1190 | } | 1216 | } |
1191 | 1217 | ||
1218 | static int tda998x_audio_hw_params(struct device *dev, void *data, | ||
1219 | struct hdmi_codec_daifmt *daifmt, | ||
1220 | struct hdmi_codec_params *params) | ||
1221 | { | ||
1222 | struct tda998x_priv *priv = dev_get_drvdata(dev); | ||
1223 | int i, ret; | ||
1224 | struct tda998x_audio_params audio = { | ||
1225 | .sample_width = params->sample_width, | ||
1226 | .sample_rate = params->sample_rate, | ||
1227 | .cea = params->cea, | ||
1228 | }; | ||
1229 | |||
1230 | if (!priv->encoder.crtc) | ||
1231 | return -ENODEV; | ||
1232 | |||
1233 | memcpy(audio.status, params->iec.status, | ||
1234 | min(sizeof(audio.status), sizeof(params->iec.status))); | ||
1235 | |||
1236 | switch (daifmt->fmt) { | ||
1237 | case HDMI_I2S: | ||
1238 | if (daifmt->bit_clk_inv || daifmt->frame_clk_inv || | ||
1239 | daifmt->bit_clk_master || daifmt->frame_clk_master) { | ||
1240 | dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__, | ||
1241 | daifmt->bit_clk_inv, daifmt->frame_clk_inv, | ||
1242 | daifmt->bit_clk_master, | ||
1243 | daifmt->frame_clk_master); | ||
1244 | return -EINVAL; | ||
1245 | } | ||
1246 | for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) | ||
1247 | if (priv->audio_port[i].format == AFMT_I2S) | ||
1248 | audio.config = priv->audio_port[i].config; | ||
1249 | audio.format = AFMT_I2S; | ||
1250 | break; | ||
1251 | case HDMI_SPDIF: | ||
1252 | for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) | ||
1253 | if (priv->audio_port[i].format == AFMT_SPDIF) | ||
1254 | audio.config = priv->audio_port[i].config; | ||
1255 | audio.format = AFMT_SPDIF; | ||
1256 | break; | ||
1257 | default: | ||
1258 | dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt); | ||
1259 | return -EINVAL; | ||
1260 | } | ||
1261 | |||
1262 | if (audio.config == 0) { | ||
1263 | dev_err(dev, "%s: No audio configutation found\n", __func__); | ||
1264 | return -EINVAL; | ||
1265 | } | ||
1266 | |||
1267 | mutex_lock(&priv->audio_mutex); | ||
1268 | ret = tda998x_configure_audio(priv, | ||
1269 | &audio, | ||
1270 | priv->encoder.crtc->hwmode.clock); | ||
1271 | |||
1272 | if (ret == 0) | ||
1273 | priv->audio_params = audio; | ||
1274 | mutex_unlock(&priv->audio_mutex); | ||
1275 | |||
1276 | return ret; | ||
1277 | } | ||
1278 | |||
1279 | static void tda998x_audio_shutdown(struct device *dev, void *data) | ||
1280 | { | ||
1281 | struct tda998x_priv *priv = dev_get_drvdata(dev); | ||
1282 | |||
1283 | mutex_lock(&priv->audio_mutex); | ||
1284 | |||
1285 | reg_write(priv, REG_ENA_AP, 0); | ||
1286 | |||
1287 | priv->audio_params.format = AFMT_UNUSED; | ||
1288 | |||
1289 | mutex_unlock(&priv->audio_mutex); | ||
1290 | } | ||
1291 | |||
1292 | int tda998x_audio_digital_mute(struct device *dev, void *data, bool enable) | ||
1293 | { | ||
1294 | struct tda998x_priv *priv = dev_get_drvdata(dev); | ||
1295 | |||
1296 | mutex_lock(&priv->audio_mutex); | ||
1297 | |||
1298 | tda998x_audio_mute(priv, enable); | ||
1299 | |||
1300 | mutex_unlock(&priv->audio_mutex); | ||
1301 | return 0; | ||
1302 | } | ||
1303 | |||
1304 | static int tda998x_audio_get_eld(struct device *dev, void *data, | ||
1305 | uint8_t *buf, size_t len) | ||
1306 | { | ||
1307 | struct tda998x_priv *priv = dev_get_drvdata(dev); | ||
1308 | struct drm_mode_config *config = &priv->encoder.dev->mode_config; | ||
1309 | struct drm_connector *connector; | ||
1310 | int ret = -ENODEV; | ||
1311 | |||
1312 | mutex_lock(&config->mutex); | ||
1313 | list_for_each_entry(connector, &config->connector_list, head) { | ||
1314 | if (&priv->encoder == connector->encoder) { | ||
1315 | memcpy(buf, connector->eld, | ||
1316 | min(sizeof(connector->eld), len)); | ||
1317 | ret = 0; | ||
1318 | } | ||
1319 | } | ||
1320 | mutex_unlock(&config->mutex); | ||
1321 | |||
1322 | return ret; | ||
1323 | } | ||
1324 | |||
1325 | static const struct hdmi_codec_ops audio_codec_ops = { | ||
1326 | .hw_params = tda998x_audio_hw_params, | ||
1327 | .audio_shutdown = tda998x_audio_shutdown, | ||
1328 | .digital_mute = tda998x_audio_digital_mute, | ||
1329 | .get_eld = tda998x_audio_get_eld, | ||
1330 | }; | ||
1331 | |||
1332 | static int tda998x_audio_codec_init(struct tda998x_priv *priv, | ||
1333 | struct device *dev) | ||
1334 | { | ||
1335 | struct hdmi_codec_pdata codec_data = { | ||
1336 | .ops = &audio_codec_ops, | ||
1337 | .max_i2s_channels = 2, | ||
1338 | }; | ||
1339 | int i; | ||
1340 | |||
1341 | for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) { | ||
1342 | if (priv->audio_port[i].format == AFMT_I2S && | ||
1343 | priv->audio_port[i].config != 0) | ||
1344 | codec_data.i2s = 1; | ||
1345 | if (priv->audio_port[i].format == AFMT_SPDIF && | ||
1346 | priv->audio_port[i].config != 0) | ||
1347 | codec_data.spdif = 1; | ||
1348 | } | ||
1349 | |||
1350 | priv->audio_pdev = platform_device_register_data( | ||
1351 | dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, | ||
1352 | &codec_data, sizeof(codec_data)); | ||
1353 | |||
1354 | return PTR_ERR_OR_ZERO(priv->audio_pdev); | ||
1355 | } | ||
1356 | |||
1192 | /* I2C driver functions */ | 1357 | /* I2C driver functions */ |
1193 | 1358 | ||
1359 | static int tda998x_get_audio_ports(struct tda998x_priv *priv, | ||
1360 | struct device_node *np) | ||
1361 | { | ||
1362 | const u32 *port_data; | ||
1363 | u32 size; | ||
1364 | int i; | ||
1365 | |||
1366 | port_data = of_get_property(np, "audio-ports", &size); | ||
1367 | if (!port_data) | ||
1368 | return 0; | ||
1369 | |||
1370 | size /= sizeof(u32); | ||
1371 | if (size > 2 * ARRAY_SIZE(priv->audio_port) || size % 2 != 0) { | ||
1372 | dev_err(&priv->hdmi->dev, | ||
1373 | "Bad number of elements in audio-ports dt-property\n"); | ||
1374 | return -EINVAL; | ||
1375 | } | ||
1376 | |||
1377 | size /= 2; | ||
1378 | |||
1379 | for (i = 0; i < size; i++) { | ||
1380 | u8 afmt = be32_to_cpup(&port_data[2*i]); | ||
1381 | u8 ena_ap = be32_to_cpup(&port_data[2*i+1]); | ||
1382 | |||
1383 | if (afmt != AFMT_SPDIF && afmt != AFMT_I2S) { | ||
1384 | dev_err(&priv->hdmi->dev, | ||
1385 | "Bad audio format %u\n", afmt); | ||
1386 | return -EINVAL; | ||
1387 | } | ||
1388 | |||
1389 | priv->audio_port[i].format = afmt; | ||
1390 | priv->audio_port[i].config = ena_ap; | ||
1391 | } | ||
1392 | |||
1393 | if (priv->audio_port[0].format == priv->audio_port[1].format) { | ||
1394 | dev_err(&priv->hdmi->dev, | ||
1395 | "There can only be on I2S port and one SPDIF port\n"); | ||
1396 | return -EINVAL; | ||
1397 | } | ||
1398 | return 0; | ||
1399 | } | ||
1400 | |||
1194 | static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) | 1401 | static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) |
1195 | { | 1402 | { |
1196 | struct device_node *np = client->dev.of_node; | 1403 | struct device_node *np = client->dev.of_node; |
@@ -1304,7 +1511,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) | |||
1304 | if (!np) | 1511 | if (!np) |
1305 | return 0; /* non-DT */ | 1512 | return 0; /* non-DT */ |
1306 | 1513 | ||
1307 | /* get the optional video properties */ | 1514 | /* get the device tree parameters */ |
1308 | ret = of_property_read_u32(np, "video-ports", &video); | 1515 | ret = of_property_read_u32(np, "video-ports", &video); |
1309 | if (ret == 0) { | 1516 | if (ret == 0) { |
1310 | priv->vip_cntrl_0 = video >> 16; | 1517 | priv->vip_cntrl_0 = video >> 16; |
@@ -1312,8 +1519,16 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) | |||
1312 | priv->vip_cntrl_2 = video; | 1519 | priv->vip_cntrl_2 = video; |
1313 | } | 1520 | } |
1314 | 1521 | ||
1315 | return 0; | 1522 | mutex_init(&priv->audio_mutex); /* Protect access from audio thread */ |
1316 | 1523 | ||
1524 | ret = tda998x_get_audio_ports(priv, np); | ||
1525 | if (ret) | ||
1526 | goto fail; | ||
1527 | |||
1528 | if (priv->audio_port[0].format != AFMT_UNUSED) | ||
1529 | tda998x_audio_codec_init(priv, &client->dev); | ||
1530 | |||
1531 | return 0; | ||
1317 | fail: | 1532 | fail: |
1318 | /* if encoder_init fails, the encoder slave is never registered, | 1533 | /* if encoder_init fails, the encoder slave is never registered, |
1319 | * so cleanup here: | 1534 | * so cleanup here: |
diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h index 3e419d92cf5a..a25483090cd5 100644 --- a/include/drm/i2c/tda998x.h +++ b/include/drm/i2c/tda998x.h | |||
@@ -1,6 +1,24 @@ | |||
1 | #ifndef __DRM_I2C_TDA998X_H__ | 1 | #ifndef __DRM_I2C_TDA998X_H__ |
2 | #define __DRM_I2C_TDA998X_H__ | 2 | #define __DRM_I2C_TDA998X_H__ |
3 | 3 | ||
4 | #include <linux/hdmi.h> | ||
5 | #include <dt-bindings/display/tda998x.h> | ||
6 | |||
7 | enum { | ||
8 | AFMT_UNUSED = 0, | ||
9 | AFMT_SPDIF = TDA998x_SPDIF, | ||
10 | AFMT_I2S = TDA998x_I2S, | ||
11 | }; | ||
12 | |||
13 | struct tda998x_audio_params { | ||
14 | u8 config; | ||
15 | u8 format; | ||
16 | unsigned sample_width; | ||
17 | unsigned sample_rate; | ||
18 | struct hdmi_audio_infoframe cea; | ||
19 | u8 status[5]; | ||
20 | }; | ||
21 | |||
4 | struct tda998x_encoder_params { | 22 | struct tda998x_encoder_params { |
5 | u8 swap_b:3; | 23 | u8 swap_b:3; |
6 | u8 mirr_b:1; | 24 | u8 mirr_b:1; |
@@ -15,16 +33,7 @@ struct tda998x_encoder_params { | |||
15 | u8 swap_e:3; | 33 | u8 swap_e:3; |
16 | u8 mirr_e:1; | 34 | u8 mirr_e:1; |
17 | 35 | ||
18 | u8 audio_cfg; | 36 | struct tda998x_audio_params audio_params; |
19 | u8 audio_clk_cfg; | ||
20 | u8 audio_frame[6]; | ||
21 | |||
22 | enum { | ||
23 | AFMT_SPDIF, | ||
24 | AFMT_I2S | ||
25 | } audio_format; | ||
26 | |||
27 | unsigned audio_sample_rate; | ||
28 | }; | 37 | }; |
29 | 38 | ||
30 | #endif | 39 | #endif |
diff --git a/include/dt-bindings/display/tda998x.h b/include/dt-bindings/display/tda998x.h new file mode 100644 index 000000000000..34757a3847ef --- /dev/null +++ b/include/dt-bindings/display/tda998x.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef _DT_BINDINGS_TDA998X_H | ||
2 | #define _DT_BINDINGS_TDA998X_H | ||
3 | |||
4 | #define TDA998x_SPDIF 1 | ||
5 | #define TDA998x_I2S 2 | ||
6 | |||
7 | #endif /*_DT_BINDINGS_TDA998X_H */ | ||