audio: add sound card
authorXing Wang <xing.wang@amlogic.com>
Mon, 27 Mar 2017 11:53:59 +0000 (19:53 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Tue, 28 Mar 2017 06:01:25 +0000 (22:01 -0800)
PD#138714: add sound card driver

Change-Id: I8c5cea4c62507976ab28ad76f6a3e42a9472cea4
Signed-off-by: Xing Wang <xing.wang@amlogic.com>
53 files changed:
MAINTAINERS
arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts
arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts
arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts
arch/arm64/boot/dts/amlogic/gxm_skt.dts
arch/arm64/boot/dts/amlogic/mesongxl.dtsi
arch/arm64/boot/dts/amlogic/mesongxm.dtsi
arch/arm64/configs/meson64_defconfig
drivers/amlogic/clk/clk-mpll.c
drivers/amlogic/clk/clk_misc.c
drivers/amlogic/clk/clkc.h
drivers/amlogic/clk/gxl.c
drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hdmi_tx_main.c
drivers/amlogic/pinctrl/pinctrl_gxl.c
include/dt-bindings/clock/amlogic,gxl-clkc.h
include/linux/amlogic/media/sound/audin_regs.h
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/amlogic/Kconfig [new file with mode: 0644]
sound/soc/amlogic/Makefile [new file with mode: 0644]
sound/soc/amlogic/aml_audio_hw.c [new file with mode: 0644]
sound/soc/amlogic/aml_audio_hw.h [new file with mode: 0644]
sound/soc/amlogic/aml_audio_hw_pcm.c [new file with mode: 0644]
sound/soc/amlogic/aml_audio_hw_pcm.h [new file with mode: 0644]
sound/soc/amlogic/aml_dmic.c [new file with mode: 0644]
sound/soc/amlogic/aml_i2s.c [new file with mode: 0644]
sound/soc/amlogic/aml_i2s.h [new file with mode: 0644]
sound/soc/amlogic/aml_i2s_dai.c [new file with mode: 0644]
sound/soc/amlogic/aml_i2s_dai.h [new file with mode: 0644]
sound/soc/amlogic/aml_meson.c [new file with mode: 0644]
sound/soc/amlogic/aml_meson.h [new file with mode: 0644]
sound/soc/amlogic/aml_notify.c [new file with mode: 0644]
sound/soc/amlogic/aml_pcm.c [new file with mode: 0644]
sound/soc/amlogic/aml_pcm.h [new file with mode: 0644]
sound/soc/amlogic/aml_pcm_dai.c [new file with mode: 0644]
sound/soc/amlogic/aml_pcm_dai.h [new file with mode: 0644]
sound/soc/amlogic/aml_spdif_codec.c [new file with mode: 0644]
sound/soc/amlogic/aml_spdif_dai.c [new file with mode: 0644]
sound/soc/amlogic/aml_spdif_dai.h [new file with mode: 0644]
sound/soc/amlogic/aml_tv.c [new file with mode: 0644]
sound/soc/amlogic/aml_tv.h [new file with mode: 0644]
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/amlogic/Kconfig [new file with mode: 0644]
sound/soc/codecs/amlogic/Makefile [new file with mode: 0644]
sound/soc/codecs/amlogic/aml_codec_t9015.c [new file with mode: 0644]
sound/soc/codecs/amlogic/aml_codec_t9015.h [new file with mode: 0644]
sound/soc/codecs/amlogic/aml_codec_t9015S.c [new file with mode: 0644]
sound/soc/codecs/amlogic/aml_codec_t9015S.h [new file with mode: 0644]
sound/soc/codecs/amlogic/aml_pmu4_codec.c [new file with mode: 0644]
sound/soc/codecs/amlogic/aml_pmu4_codec.h [new file with mode: 0644]
sound/soc/codecs/amlogic/dummy_codec.c [new file with mode: 0644]
sound/soc/codecs/amlogic/pcm2bt.c [new file with mode: 0644]

index b1d60d51aeac13a882359279e8292243a21efbcd..7dfb5df5005e453891dcf0a2f7e0eb4d826e19f3 100644 (file)
@@ -13663,6 +13663,7 @@ F: drivers/amlogic/mmc/emmc_key.c
 F: drivers/amlogic/mmc/emmc_key.h
 F: include/linux/amlogic/key_manage.h
 
+<<<<<<< HEAD
 AMLOGIC P400/P401 BSP
 M: Frank Chen <frank.chen@amlogic.com>
 F: arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts
@@ -13675,10 +13676,6 @@ F: drivers/staging/android/logger.h
 
 AMLOGIC AMLVIDEO2 DRIVER
 M: Guosong Zhou <guosong.zhou@amlogic.com>
-F: arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts
-F: arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts
-F: arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts
-F: arch/arm64/boot/dts/amlogic/gxm_skt.dts
 F: arch/arm64/configs/meson64_defconfig
 F: drivers/amlogic/media/Kconfig
 F: drivers/amlogic/media/Makefile
@@ -13692,3 +13689,27 @@ F: include/linux/amlogic/media/v4l_util/*
 AMLOGIC M8b
 M: Jianxin Pan <jianxin.pan@amlogic.com>
 F: arch/arm/boot/dts/amlogic>
+
+ANLOGIC AUDIO
+M: Xing Wang <xing.wang@amlogic.com>
+F: arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts
+F: arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts
+F: arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts
+F: arch/arm64/boot/dts/amlogic/gxm_skt.dts
+F: arch/arm64/boot/dts/amlogic/mesongxl.dtsi
+F: arch/arm64/boot/dts/amlogic/mesongxm.dtsi
+F: arch/arm64/configs/meson64_defconfig
+F: drivers/amlogic/clk/clk-mpll.c
+F: drivers/amlogic/clk/clk_misc.c
+F: drivers/amlogic/clk/clkc.h
+F: drivers/amlogic/clk/gxl.c
+F: drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hdmi_tx_main.c
+F: drivers/amlogic/pinctrl/pinctrl_gxl.c
+F: include/dt-bindings/clock/amlogic,gxl-clkc.h
+F: include/linux/amlogic/media/sound/audin_regs.h
+F: sound/soc/Kconfig
+F: sound/soc/Makefile
+F: sound/soc/amlogic/*
+F: sound/soc/codecs/Kconfig
+F: sound/soc/codecs/Makefile
+F: sound/soc/codecs/amlogic/*
\ No newline at end of file
index 15da8a70d9e9ec89e9568a8068345bf599923bb8..995efd2e6384b96678a86d5e574a5fa90694d447 100644 (file)
                        "clk_ge2d_gate";
        };
 
+
+       /* AUDIO MESON DEVICES */
+       i2s_dai: I2S {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-i2s-dai";
+               clocks =
+                       <&clkc CLKID_MPLL2>,
+                       <&clkc CLKID_AMCLK_COMP>,
+                       <&clkc CLKID_AIU_GLUE>,
+                       <&clkc CLKID_IEC958>,
+                       <&clkc CLKID_I2S_OUT>,
+                       <&clkc CLKID_AMCLK>,
+                       <&clkc CLKID_AIFIFO2>,
+                       <&clkc CLKID_MIXER>,
+                       <&clkc CLKID_MIXER_IFACE>,
+                       <&clkc CLKID_ADC>,
+                       <&clkc CLKID_AIU_TOP>,
+                       <&clkc CLKID_AOCLK_GATE>,
+                       <&clkc CLKID_I2S_SPDIF>;
+               clock-names =
+                       "mpll2",
+                       "mclk",
+                       "top_glue",
+                       "aud_buf",
+                       "i2s_out",
+                       "amclk_measure",
+                       "aififo2",
+                       "aud_mixer",
+                       "mixer_reg",
+                       "adc",
+                       "top_level",
+                       "aoclk",
+                       "aud_in";
+               /*DMIC;*/  /* I2s Mic or Dmic, default for I2S mic */
+       };
+       dmic:snd_dmic {
+               #sound-dai-cells = <0>;
+               compatible = "aml, aml_snd_dmic";
+               reg = <0x0 0xd0042000 0x0 0x2000>;
+               status = "okay";
+               resets = <
+                       &clkc CLKID_PDM_GATE
+               >;
+               reset-names =   "pdm";
+               pinctrl-names = "aml_dmic_pins";
+               pinctrl-0 = <&aml_dmic_pins>;
+               clocks = <&clkc CLKID_PDM_COMP>,
+                       <&clkc CLKID_AMCLK_COMP>;
+               clock-names = "pdm", "mclk";
+       };
+       spdif_dai: SPDIF {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-spdif-dai";
+               clocks =
+                       <&clkc CLKID_MPLL1>,
+                       <&clkc CLKID_I958_COMP>,
+                       <&clkc CLKID_AMCLK_COMP>,
+                       <&clkc CLKID_I958_COMP_SPDIF>,
+                       <&clkc CLKID_CLK81>,
+                       <&clkc CLKID_IEC958>,
+                       <&clkc CLKID_IEC958_GATE>;
+               clock-names =
+                       "mpll1",
+                       "i958",
+                       "mclk",
+                       "spdif",
+                       "clk_81",
+                       "iec958",
+                       "iec958_amclk";
+       };
+       pcm_dai: PCM {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-pcm-dai";
+               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-0 = <&audio_pcm_pins>;
+               clocks =
+                       <&clkc CLKID_MPLL0>,
+                       <&clkc CLKID_PCM_MCLK_COMP>,
+                       <&clkc CLKID_PCM_SCLK_GATE>;
+               clock-names =
+                       "mpll0",
+                       "pcm_mclk",
+                       "pcm_sclk";
+               pcm_mode = <1>; /* 0=slave mode, 1=master mode */
+       };
+       i2s_plat: i2s_platform {
+               compatible = "amlogic, aml-i2s";
+               interrupts = <0 29 1>;
+       };
+       pcm_plat: pcm_platform {
+               compatible = "amlogic, aml-pcm";
+       };
+       spdif_codec: spdif_codec{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-spdif-codec";
+               pinctrl-names = "aml_audio_spdif";
+               pinctrl-0 = <&audio_spdif_pins>;
+       };
+       pcm_codec: pcm_codec{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, pcm2BT-codec";
+       };
+       /* endof AUDIO MESON DEVICES */
+
+       /* AUDIO board specific */
+       dummy_codec:dummy{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_dummy_codec";
+               status = "disable";
+       };
+       amlogic_codec:t9015{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_codec_T9015";
+               reg = <0x0 0xc8832000 0x0 0x14>;
+               status = "okay";
+       };
+       aml_sound_meson {
+               compatible = "aml, meson-snd-card";
+               status = "okay";
+               aml-sound-card,format = "i2s";
+               aml_sound_card,name = "AML-MESONAUDIO";
+               aml,audio-routing =
+                               "Ext Spk","LOUTL",
+                               "Ext Spk","LOUTR";
+
+               mute_gpio-gpios = <&gpio GPIOH_5 0>;
+               mute_inv;
+               hp_disable;
+               hp_paraments = <800 300 0 5 1>;
+               pinctrl-names = "audio_i2s_pins";
+               pinctrl-0 = <&audio_i2s_pins>;
+               cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
+               codec_list = <&codec0 &codec1 &codec2>;
+               plat_list = <&i2s_plat &i2s_plat &pcm_plat>;
+               cpudai0: cpudai0 {
+                       sound-dai = <&i2s_dai>;
+               };
+               cpudai1: cpudai1 {
+                       sound-dai = <&spdif_dai>;
+               };
+               cpudai2: cpudai2 {
+                       sound-dai = <&pcm_dai>;
+               };
+               codec0: codec0 {
+                       sound-dai = <&amlogic_codec>;
+               };
+               codec1: codec1 {
+                       sound-dai = <&spdif_codec>;
+               };
+               codec2: codec2 {
+                       sound-dai = <&pcm_codec>;
+               };
+       };
+       /* END OF AUDIO board specific */
        rdma{
                compatible = "amlogic, meson, rdma";
                dev_name = "amlogic-rdma";
index b90d14a3694a4286eef1f763841a360ab83f5230..f416abdd42a3ed05609582e11c4803aaf1ed92b8 100644 (file)
                        "clk_ge2d_gate";
        };
 
+
+       /* AUDIO MESON DEVICES */
+       i2s_dai: I2S {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-i2s-dai";
+               clocks =
+                       <&clkc CLKID_MPLL2>,
+                       <&clkc CLKID_AMCLK_COMP>,
+                       <&clkc CLKID_AIU_GLUE>,
+                       <&clkc CLKID_IEC958>,
+                       <&clkc CLKID_I2S_OUT>,
+                       <&clkc CLKID_AMCLK>,
+                       <&clkc CLKID_AIFIFO2>,
+                       <&clkc CLKID_MIXER>,
+                       <&clkc CLKID_MIXER_IFACE>,
+                       <&clkc CLKID_ADC>,
+                       <&clkc CLKID_AIU_TOP>,
+                       <&clkc CLKID_AOCLK_GATE>,
+                       <&clkc CLKID_I2S_SPDIF>;
+               clock-names =
+                       "mpll2",
+                       "mclk",
+                       "top_glue",
+                       "aud_buf",
+                       "i2s_out",
+                       "amclk_measure",
+                       "aififo2",
+                       "aud_mixer",
+                       "mixer_reg",
+                       "adc",
+                       "top_level",
+                       "aoclk",
+                       "aud_in";
+               /*DMIC;*/  /* I2s Mic or Dmic, default for I2S mic */
+       };
+       dmic:snd_dmic {
+               #sound-dai-cells = <0>;
+               compatible = "aml, aml_snd_dmic";
+               reg = <0x0 0xd0042000 0x0 0x2000>;
+               status = "okay";
+               resets = <
+                       &clkc CLKID_PDM_GATE
+               >;
+               reset-names =   "pdm";
+               pinctrl-names = "aml_dmic_pins";
+               pinctrl-0 = <&aml_dmic_pins>;
+               clocks = <&clkc CLKID_PDM_COMP>,
+                       <&clkc CLKID_AMCLK_COMP>;
+               clock-names = "pdm", "mclk";
+       };
+       spdif_dai: SPDIF {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-spdif-dai";
+               clocks =
+                       <&clkc CLKID_MPLL1>,
+                       <&clkc CLKID_I958_COMP>,
+                       <&clkc CLKID_AMCLK_COMP>,
+                       <&clkc CLKID_I958_COMP_SPDIF>,
+                       <&clkc CLKID_CLK81>,
+                       <&clkc CLKID_IEC958>,
+                       <&clkc CLKID_IEC958_GATE>;
+               clock-names =
+                       "mpll1",
+                       "i958",
+                       "mclk",
+                       "spdif",
+                       "clk_81",
+                       "iec958",
+                       "iec958_amclk";
+       };
+       pcm_dai: PCM {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-pcm-dai";
+               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-0 = <&audio_pcm_pins>;
+               clocks =
+                       <&clkc CLKID_MPLL0>,
+                       <&clkc CLKID_PCM_MCLK_COMP>,
+                       <&clkc CLKID_PCM_SCLK_GATE>;
+               clock-names =
+                       "mpll0",
+                       "pcm_mclk",
+                       "pcm_sclk";
+               pcm_mode = <1>; /* 0=slave mode, 1=master mode */
+       };
+       i2s_plat: i2s_platform {
+               compatible = "amlogic, aml-i2s";
+               interrupts = <0 29 1>;
+       };
+       pcm_plat: pcm_platform {
+               compatible = "amlogic, aml-pcm";
+       };
+       spdif_codec: spdif_codec{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-spdif-codec";
+               pinctrl-names = "aml_audio_spdif";
+               pinctrl-0 = <&audio_spdif_pins>;
+       };
+       pcm_codec: pcm_codec{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, pcm2BT-codec";
+       };
+       /* endof AUDIO MESON DEVICES */
+
+       /* AUDIO board specific */
+       dummy_codec:dummy{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_dummy_codec";
+               status = "disable";
+       };
+       amlogic_codec:t9015{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_codec_T9015";
+               reg = <0x0 0xc8832000 0x0 0x14>;
+               status = "okay";
+       };
+       aml_sound_meson {
+               compatible = "aml, meson-snd-card";
+               status = "okay";
+               aml-sound-card,format = "i2s";
+               aml_sound_card,name = "AML-MESONAUDIO";
+               aml,audio-routing =
+                               "Ext Spk","LOUTL",
+                               "Ext Spk","LOUTR";
+
+               mute_gpio-gpios = <&gpio GPIOH_5 0>;
+               mute_inv;
+               hp_disable;
+               hp_paraments = <800 300 0 5 1>;
+               pinctrl-names = "audio_i2s_pins";
+               pinctrl-0 = <&audio_i2s_pins>;
+               cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
+               codec_list = <&codec0 &codec1 &codec2>;
+               plat_list = <&i2s_plat &i2s_plat &pcm_plat>;
+               cpudai0: cpudai0 {
+                       sound-dai = <&i2s_dai>;
+               };
+               cpudai1: cpudai1 {
+                       sound-dai = <&spdif_dai>;
+               };
+               cpudai2: cpudai2 {
+                       sound-dai = <&pcm_dai>;
+               };
+               codec0: codec0 {
+                       sound-dai = <&amlogic_codec>;
+               };
+               codec1: codec1 {
+                       sound-dai = <&spdif_codec>;
+               };
+               codec2: codec2 {
+                       sound-dai = <&pcm_codec>;
+               };
+       };
+       /* END OF AUDIO board specific */
        rdma{
                compatible = "amlogic, meson, rdma";
                dev_name = "amlogic-rdma";
index a4c90eec0064a3848d2c4d5d91f5c01f945849e1..ba8d3e097d9ee7b37097d853714045fa0b4609e3 100644 (file)
                        "clk_ge2d_gate";
        };
 
+
+       /* AUDIO MESON DEVICES */
+       i2s_dai: I2S {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-i2s-dai";
+               clocks =
+                       <&clkc CLKID_MPLL2>,
+                       <&clkc CLKID_AMCLK_COMP>,
+                       <&clkc CLKID_AIU_GLUE>,
+                       <&clkc CLKID_IEC958>,
+                       <&clkc CLKID_I2S_OUT>,
+                       <&clkc CLKID_AMCLK>,
+                       <&clkc CLKID_AIFIFO2>,
+                       <&clkc CLKID_MIXER>,
+                       <&clkc CLKID_MIXER_IFACE>,
+                       <&clkc CLKID_ADC>,
+                       <&clkc CLKID_AIU_TOP>,
+                       <&clkc CLKID_AOCLK_GATE>,
+                       <&clkc CLKID_I2S_SPDIF>;
+               clock-names =
+                       "mpll2",
+                       "mclk",
+                       "top_glue",
+                       "aud_buf",
+                       "i2s_out",
+                       "amclk_measure",
+                       "aififo2",
+                       "aud_mixer",
+                       "mixer_reg",
+                       "adc",
+                       "top_level",
+                       "aoclk",
+                       "aud_in";
+               /*DMIC;*/  /* I2s Mic or Dmic, default for I2S mic */
+       };
+       dmic:snd_dmic {
+               #sound-dai-cells = <0>;
+               compatible = "aml, aml_snd_dmic";
+               reg = <0x0 0xd0042000 0x0 0x2000>;
+               status = "okay";
+               resets = <
+                       &clkc CLKID_PDM_GATE
+               >;
+               reset-names =   "pdm";
+               pinctrl-names = "aml_dmic_pins";
+               pinctrl-0 = <&aml_dmic_pins>;
+               clocks = <&clkc CLKID_PDM_COMP>,
+                       <&clkc CLKID_AMCLK_COMP>;
+               clock-names = "pdm", "mclk";
+       };
+       spdif_dai: SPDIF {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-spdif-dai";
+               clocks =
+                       <&clkc CLKID_MPLL1>,
+                       <&clkc CLKID_I958_COMP>,
+                       <&clkc CLKID_AMCLK_COMP>,
+                       <&clkc CLKID_I958_COMP_SPDIF>,
+                       <&clkc CLKID_CLK81>,
+                       <&clkc CLKID_IEC958>,
+                       <&clkc CLKID_IEC958_GATE>;
+               clock-names =
+                       "mpll1",
+                       "i958",
+                       "mclk",
+                       "spdif",
+                       "clk_81",
+                       "iec958",
+                       "iec958_amclk";
+       };
+       pcm_dai: PCM {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-pcm-dai";
+               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-0 = <&audio_pcm_pins>;
+               clocks =
+                       <&clkc CLKID_MPLL0>,
+                       <&clkc CLKID_PCM_MCLK_COMP>,
+                       <&clkc CLKID_PCM_SCLK_GATE>;
+               clock-names =
+                       "mpll0",
+                       "pcm_mclk",
+                       "pcm_sclk";
+               pcm_mode = <1>; /* 0=slave mode, 1=master mode */
+       };
+       i2s_plat: i2s_platform {
+               compatible = "amlogic, aml-i2s";
+               interrupts = <0 29 1>;
+       };
+       pcm_plat: pcm_platform {
+               compatible = "amlogic, aml-pcm";
+       };
+       spdif_codec: spdif_codec{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-spdif-codec";
+               pinctrl-names = "aml_audio_spdif";
+               pinctrl-0 = <&audio_spdif_pins>;
+       };
+       pcm_codec: pcm_codec{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, pcm2BT-codec";
+       };
+       /* endof AUDIO MESON DEVICES */
+
+       /* AUDIO board specific */
+       dummy_codec:dummy{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_dummy_codec";
+               status = "disable";
+       };
+       amlogic_codec:t9015{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_codec_T9015";
+               reg = <0x0 0xc8832000 0x0 0x14>;
+               status = "okay";
+       };
+       aml_sound_meson {
+               compatible = "aml, meson-snd-card";
+               status = "okay";
+               aml-sound-card,format = "i2s";
+               aml_sound_card,name = "AML-MESONAUDIO";
+               aml,audio-routing =
+                               "Ext Spk","LOUTL",
+                               "Ext Spk","LOUTR";
+
+               mute_gpio-gpios = <&gpio GPIOH_5 0>;
+               mute_inv;
+               hp_disable;
+               hp_paraments = <800 300 0 5 1>;
+               pinctrl-names = "audio_i2s_pins";
+               pinctrl-0 = <&audio_i2s_pins>;
+               cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
+               codec_list = <&codec0 &codec1 &codec2>;
+               plat_list = <&i2s_plat &i2s_plat &pcm_plat>;
+               cpudai0: cpudai0 {
+                       sound-dai = <&i2s_dai>;
+               };
+               cpudai1: cpudai1 {
+                       sound-dai = <&spdif_dai>;
+               };
+               cpudai2: cpudai2 {
+                       sound-dai = <&pcm_dai>;
+               };
+               codec0: codec0 {
+                       sound-dai = <&amlogic_codec>;
+               };
+               codec1: codec1 {
+                       sound-dai = <&spdif_codec>;
+               };
+               codec2: codec2 {
+                       sound-dai = <&pcm_codec>;
+               };
+       };
+       /* END OF AUDIO board specific */
+
        rdma{
                compatible = "amlogic, meson, rdma";
                dev_name = "amlogic-rdma";
index 31921f2726efa170b3c8731a5bd55d35d8eeb27b..d548eb4df191e76e2e8f032545d5892d16ae32b5 100644 (file)
                        "clk_ge2d",
                        "clk_ge2d_gate";
        };
+
+
+       /* AUDIO MESON DEVICES */
+       i2s_dai: I2S {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-i2s-dai";
+               clocks =
+                       <&clkc CLKID_MPLL2>,
+                       <&clkc CLKID_AMCLK_COMP>,
+                       <&clkc CLKID_AIU_GLUE>,
+                       <&clkc CLKID_IEC958>,
+                       <&clkc CLKID_I2S_OUT>,
+                       <&clkc CLKID_AMCLK>,
+                       <&clkc CLKID_AIFIFO2>,
+                       <&clkc CLKID_MIXER>,
+                       <&clkc CLKID_MIXER_IFACE>,
+                       <&clkc CLKID_ADC>,
+                       <&clkc CLKID_AIU_TOP>,
+                       <&clkc CLKID_AOCLK_GATE>,
+                       <&clkc CLKID_I2S_SPDIF>;
+               clock-names =
+                       "mpll2",
+                       "mclk",
+                       "top_glue",
+                       "aud_buf",
+                       "i2s_out",
+                       "amclk_measure",
+                       "aififo2",
+                       "aud_mixer",
+                       "mixer_reg",
+                       "adc",
+                       "top_level",
+                       "aoclk",
+                       "aud_in";
+               /*DMIC;*/  /* I2s Mic or Dmic, default for I2S mic */
+       };
+       dmic:snd_dmic {
+               #sound-dai-cells = <0>;
+               compatible = "aml, aml_snd_dmic";
+               reg = <0x0 0xd0042000 0x0 0x2000>;
+               status = "okay";
+               resets = <
+                       &clkc CLKID_PDM_GATE
+               >;
+               reset-names =   "pdm";
+               pinctrl-names = "aml_dmic_pins";
+               pinctrl-0 = <&aml_dmic_pins>;
+               clocks = <&clkc CLKID_PDM_COMP>,
+                       <&clkc CLKID_AMCLK_COMP>;
+               clock-names = "pdm", "mclk";
+       };
+       spdif_dai: SPDIF {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-spdif-dai";
+               clocks =
+                       <&clkc CLKID_MPLL1>,
+                       <&clkc CLKID_I958_COMP>,
+                       <&clkc CLKID_AMCLK_COMP>,
+                       <&clkc CLKID_I958_COMP_SPDIF>,
+                       <&clkc CLKID_CLK81>,
+                       <&clkc CLKID_IEC958>,
+                       <&clkc CLKID_IEC958_GATE>;
+               clock-names =
+                       "mpll1",
+                       "i958",
+                       "mclk",
+                       "spdif",
+                       "clk_81",
+                       "iec958",
+                       "iec958_amclk";
+       };
+       pcm_dai: PCM {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-pcm-dai";
+               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-0 = <&audio_pcm_pins>;
+               clocks =
+                       <&clkc CLKID_MPLL0>,
+                       <&clkc CLKID_PCM_MCLK_COMP>,
+                       <&clkc CLKID_PCM_SCLK_GATE>;
+               clock-names =
+                       "mpll0",
+                       "pcm_mclk",
+                       "pcm_sclk";
+               pcm_mode = <1>; /* 0=slave mode, 1=master mode */
+       };
+       i2s_plat: i2s_platform {
+               compatible = "amlogic, aml-i2s";
+               interrupts = <0 29 1>;
+       };
+       pcm_plat: pcm_platform {
+               compatible = "amlogic, aml-pcm";
+       };
+       spdif_codec: spdif_codec{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml-spdif-codec";
+               pinctrl-names = "aml_audio_spdif";
+               pinctrl-0 = <&audio_spdif_pins>;
+       };
+       pcm_codec: pcm_codec{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, pcm2BT-codec";
+       };
+       /* endof AUDIO MESON DEVICES */
+
+       /* AUDIO board specific */
+       dummy_codec:dummy{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_dummy_codec";
+               status = "disable";
+       };
+       amlogic_codec:t9015{
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_codec_T9015";
+               reg = <0x0 0xc8832000 0x0 0x14>;
+               status = "okay";
+       };
+       aml_sound_meson {
+               compatible = "aml, meson-snd-card";
+               status = "okay";
+               aml-sound-card,format = "i2s";
+               aml_sound_card,name = "AML-MESONAUDIO";
+               aml,audio-routing =
+                               "Ext Spk","LOUTL",
+                               "Ext Spk","LOUTR";
+
+               mute_gpio-gpios = <&gpio GPIOH_5 0>;
+               mute_inv;
+               hp_disable;
+               hp_paraments = <800 300 0 5 1>;
+               pinctrl-names = "audio_i2s_pins";
+               pinctrl-0 = <&audio_i2s_pins>;
+               cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
+               codec_list = <&codec0 &codec1 &codec2>;
+               plat_list = <&i2s_plat &i2s_plat &pcm_plat>;
+               cpudai0: cpudai0 {
+                       sound-dai = <&i2s_dai>;
+               };
+               cpudai1: cpudai1 {
+                       sound-dai = <&spdif_dai>;
+               };
+               cpudai2: cpudai2 {
+                       sound-dai = <&pcm_dai>;
+               };
+               codec0: codec0 {
+                       sound-dai = <&amlogic_codec>;
+               };
+               codec1: codec1 {
+                       sound-dai = <&spdif_codec>;
+               };
+               codec2: codec2 {
+                       sound-dai = <&pcm_codec>;
+               };
+       };
+       /* END OF AUDIO board specific */
+
        rdma{
                compatible = "amlogic, meson, rdma";
                dev_name = "amlogic-rdma";
index c18a9d7cc70d2ba5d5b2d3002d1f931c55fa9f05..14b07c23fae9a0e73930584cd58ecc8d4cbc7b0e 100644 (file)
                        };
                };
 
-               audio_pins:audio_pin {
+               audio_i2s_pins:audio_i2s {
                        mux {
                                groups = "i2s_am_clk",
                                        "i2s_ao_clk_out",
                        };
                };
 
-               audio_spdif_pins:audio_pin1 {
+               audio_spdif_pins:audio_spdif {
                        mux {
                                groups = "spdif_out";
                                function = "spdif_out";
                        };
                };
 
-               audio_btpcm_pins:audio_btpcm_pins {
+               audio_pcm_pins:audio_pcm {
                        mux {
                                groups = "pcm_out_a",
                                        "pcm_in_a",
                                function = "pcm_a";
                        };
                };
+               aml_dmic_pins:audio_dmic {
+                       mux {
+                               groups = "pdm_in",
+                                               "pdm_clk";
+                               function = "pdm";
+                       };
+               };
+
        }; /* end of pinctrl_periphs */
 }; /* end of periphs */
 
index 41ece67c6feec54cebd242adae35aa9035325da0..7a3e60d4fe95165c01007f008477b47fbda44dd5 100644 (file)
                        };
                };
 
-               audio_pins:audio_pin {
+               audio_i2s_pins:audio_i2s {
                        mux {
                                groups = "i2s_am_clk",
                                        "i2s_ao_clk_out",
                        };
                };
 
-               audio_spdif_pins:audio_pin1 {
+               audio_spdif_pins:audio_spdif {
                        mux {
                                groups = "spdif_out";
                                function = "spdif_out";
                        };
                };
 
-               audio_btpcm_pins:audio_btpcm_pins {
+               audio_pcm_pins:audio_pcm {
                        mux {
                                groups = "pcm_out_a",
                                        "pcm_in_a",
                                function = "pcm_a";
                        };
                };
+               aml_dmic_pins:audio_dmic {
+                       mux {
+                               groups = "pdm_in",
+                                               "pdm_clk";
+                               function = "pdm";
+                       };
+               };
+
        }; /* end of pinctrl_periphs */
 }; /* end of periphs */
 
index 4333bc7be4ceb49efe0bd3e9c4555f5f0afe3ff6..e2d3cd4e211a5ae0dd9f08b4d37167eb193a52c3 100644 (file)
@@ -295,6 +295,13 @@ CONFIG_FB=y
 CONFIG_SOUND=y
 CONFIG_SND=y
 CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_AMLOGIC_SND_SOC_CODECS=y
+CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC=y
+CONFIG_AMLOGIC_SND_CODEC_PCM2BT=y
+CONFIG_AMLOGIC_SND_CODEC_AMLT9015=y
+CONFIG_AMLOGIC_SND_SOC=y
+CONFIG_AMLOGIC_SND_SPLIT_MODE=y
 CONFIG_UHID=y
 CONFIG_USB_HIDDEV=y
 CONFIG_USB_XHCI_HCD=y
index 46c441e4ff1f708f5dc07c816919ab528bd2bf37..744c1bf93907cbc36adea697582906ca6b511ff1 100644 (file)
@@ -29,6 +29,8 @@
 /* #undef pr_debug */
 /* #define pr_debug pr_info */
 #define SDM_MAX 16384
+#define MAX_RATE       500000000
+#define MIN_RATE       5000000
 
 #define to_meson_clk_mpll(_hw) container_of(_hw, struct meson_clk_mpll, hw)
 
@@ -56,7 +58,14 @@ static unsigned long mpll_recalc_rate(struct clk_hw *hw,
 static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
                                     unsigned long *parent_rate)
 {
-       return rate;
+       unsigned long rate_val = rate;
+
+       if (rate_val < MIN_RATE)
+               rate = MIN_RATE;
+       if (rate_val > MAX_RATE)
+               rate = MAX_RATE;
+
+       return rate_val;
 }
 
 
@@ -69,9 +78,9 @@ static int mpll_set_rate(struct clk_hw *hw, unsigned long rate,
        unsigned long reg, old_sdm, old_n2, sdm, n2;
        unsigned long flags = 0;
 
-       if ((rate > 500000000) || (rate < 250000000)) {
+       if ((rate > MAX_RATE) || (rate < MIN_RATE)) {
                pr_err("Err: can not set rate to %lu!\n", rate);
-               pr_err("Range[250000000 - 500000000]\n");
+               pr_err("Range[5000000 - 500000000]\n");
                return -1;
        }
 
@@ -105,7 +114,7 @@ static int mpll_set_rate(struct clk_hw *hw, unsigned long rate,
                p = &mpll->n2;
                reg = PARM_SET(p->width, p->shift, reg, n2);
                reg = PARM_SET(1, mpll->sdm_en, reg, 1);
-               reg = PARM_SET(1, mpll->en_dss, reg, 1);
+               reg = PARM_SET(1, mpll->en_dds, reg, 1);
                if (!strcmp(clk_hw_get_name(hw), "mpll3"))
                        /* MPLL_CNTL10 bit14 should be set together
                         * with MPLL3_CNTL0 bit0
index c7ea3e03a55cc52615c6cc0c0ab43a03ee4b557a..2fcd7e34ed49eea487f53032f5ba1b996c5fad86 100644 (file)
@@ -181,6 +181,7 @@ static struct clk_hw *pdm_hws[] = {
 
 /* cts_clk_i958 */
 const char *i958_parent_names[] = { "NULL", "mpll0", "mpll1", "mpll2"};
+const char *i958_ext_parent_names[] = {"amclk_composite", "i958_composite"};
 
 static struct clk_mux i958_mux = {
        .reg = (void *)HHI_AUD_CLK_CNTL2,
@@ -223,6 +224,20 @@ static struct clk_gate i958_gate = {
        },
 };
 
+static struct clk_mux i958_comp_spdif = {
+       .reg = (void *)HHI_AUD_CLK_CNTL2,
+       .mask = 0x1,
+       .shift = 27,
+       .lock = &clk_lock,
+       .hw.init = &(struct clk_init_data){
+               .name = "i958_comp_spdif",
+               .ops = &clk_mux_ops,
+               .parent_names = i958_ext_parent_names,
+               .num_parents = 2,
+               .flags = (CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED),
+       },
+};
+
 static struct clk_hw *i958_hws[] = {
 [CLKID_I958_MUX - CLKID_I958_MUX] = &i958_mux.hw,
 [CLKID_I958_DIV - CLKID_I958_MUX] = &i958_div.hw,
@@ -330,6 +345,9 @@ void amlogic_init_misc(void)
        i958_div.reg = clk_base + (u64)(i958_div.reg);
        i958_gate.reg = clk_base + (u64)(i958_gate.reg);
 
+       /*clk_i958 spdif*/
+       i958_comp_spdif.reg = clk_base + (u64)(i958_comp_spdif.reg);
+
        /* cts_pclk_mclk */
        pcm_mclk_mux.reg = clk_base + (u64)(pcm_mclk_mux.reg);
        pcm_mclk_div.reg = clk_base + (u64)(pcm_mclk_div.reg);
@@ -410,6 +428,9 @@ void amlogic_init_misc(void)
        clks[CLKID_PCM_SCLK_GATE] = clk_register(NULL, &pcm_sclk_gate.hw);
        WARN_ON(IS_ERR(clks[CLKID_PCM_SCLK_GATE]));
 
+       clks[CLKID_I958_COMP_SPDIF] = clk_register(NULL, &i958_comp_spdif.hw);
+       WARN_ON(IS_ERR(clks[CLKID_I958_COMP_SPDIF]));
+
        pr_info("%s: register meson misc clk\n", __func__);
 };
 
index c0b38c463ebed445613a0070bcb0ba7e53daf9bf..53d7114558eda73daba6e96ef006808f71e6f658 100644 (file)
@@ -101,7 +101,7 @@ struct meson_clk_mpll {
        struct parm n2;
        /* FIXME ssen gate control? */
        u8 sdm_en;
-       u8 en_dss;
+       u8 en_dds;
        spinlock_t *lock;
 };
 
index c9f85b7110fd05ed0708862dc212b2fd35c2ecd1..c94f58ee7632e6fd27952eb9406dbef473486d41 100644 (file)
@@ -419,10 +419,12 @@ static struct meson_clk_mpll gxl_mpll0 = {
                .shift   = 16,
                .width   = 9,
        },
+       .sdm_en = 15,
+       .en_dds = 14,
        .lock = &clk_lock,
        .hw.init = &(struct clk_init_data){
                .name = "mpll0",
-               .ops = &meson_clk_mpll_ro_ops,
+               .ops = &meson_clk_mpll_ops,
                .parent_names = (const char *[]){ "fixed_pll" },
                .num_parents = 1,
        },
@@ -439,10 +441,12 @@ static struct meson_clk_mpll gxl_mpll1 = {
                .shift   = 16,
                .width   = 9,
        },
+       .sdm_en = 15,
+       .en_dds = 14,
        .lock = &clk_lock,
        .hw.init = &(struct clk_init_data){
                .name = "mpll1",
-               .ops = &meson_clk_mpll_ro_ops,
+               .ops = &meson_clk_mpll_ops,
                .parent_names = (const char *[]){ "fixed_pll" },
                .num_parents = 1,
        },
@@ -459,10 +463,12 @@ static struct meson_clk_mpll gxl_mpll2 = {
                .shift   = 16,
                .width   = 9,
        },
+       .sdm_en = 15,
+       .en_dds = 14,
        .lock = &clk_lock,
        .hw.init = &(struct clk_init_data){
                .name = "mpll2",
-               .ops = &meson_clk_mpll_ro_ops,
+               .ops = &meson_clk_mpll_ops,
                .parent_names = (const char *[]){ "fixed_pll" },
                .num_parents = 1,
        },
@@ -480,7 +486,7 @@ static struct meson_clk_mpll gxl_mpll3 = {
                .width   = 9,
        },
        .sdm_en = 11,
-       .en_dss = 0,
+       .en_dds = 0,
        .lock = &clk_lock,
        .hw.init = &(struct clk_init_data){
                .name = "mpll3",
index 5aaa3a67342a56c835fd8c8de7460271b2889eea..eb9c10ed461af788ddbd799b55d73231e39dd8bb 100644 (file)
@@ -53,7 +53,9 @@
 #include <linux/amlogic/cpu_version.h>
 #include <linux/amlogic/media/vout/vinfo.h>
 #include <linux/amlogic/media/vout/vout_notify.h>
-/* #include <linux/amlogic/sound/aout_notify.h> */
+#ifdef CONFIG_AMLOGIC_SND_SOC
+#include <linux/amlogic/media/sound/aout_notify.h>
+#endif
 #include <linux/amlogic/media/vout/hdmi_tx/hdmi_info_global.h>
 #include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_ddc.h>
 #include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_module.h>
@@ -2684,7 +2686,7 @@ static int amhdmitx_probe(struct platform_device *pdev)
        ret = device_create_file(dev, &dev_attr_valid_mode);
 
        vout_register_server(&hdmitx_server);
-#ifdef CONFIG_SND_SOC
+#ifdef CONFIG_AMLOGIC_SND_SOC
        aout_register_client(&hdmitx_notifier_nb_a);
 #else
        r = r ? (long int)&hdmitx_notifier_nb_a :
@@ -2803,7 +2805,7 @@ static int amhdmitx_remove(struct platform_device *pdev)
        hdmitx_device.hpd_event = 0xff;
        kthread_stop(hdmitx_device.task);
        vout_unregister_server(&hdmitx_server);
-#ifdef CONFIG_SND_SOC
+#ifdef CONFIG_AMLOGIC_SND_SOC
        aout_unregister_client(&hdmitx_notifier_nb_a);
 #endif
 
index 2fc6812445580de670c5f45f59259e979054e8b8..8284843762ac5ee8b33fa07dee76c851abbbda35 100644 (file)
@@ -240,6 +240,10 @@ static const unsigned int uart_rts_b_pins[]        = { PIN(GPIODV_27, EE_OFF) };
 static const unsigned int i2c_sda_a_pins[]     = { PIN(GPIODV_24, EE_OFF) };
 static const unsigned int i2c_scl_a_pins[]     = { PIN(GPIODV_25, EE_OFF) };
 
+/*for dmic*/
+static const unsigned int pdm_in_pins[] = { PIN(GPIOZ_8, EE_OFF) };
+static const unsigned int pdm_clk_pins[] = { PIN(GPIOZ_9, EE_OFF)};
+
 static const unsigned int i2c_sda_b_pins[]     = { PIN(GPIODV_26, EE_OFF) };
 static const unsigned int i2c_scl_b_pins[]     = { PIN(GPIODV_27, EE_OFF) };
 
@@ -572,6 +576,9 @@ static struct meson_pmx_group meson_gxl_periphs_groups[] = {
        GROUP(i2c_sda_a,        1,      15), /*dv24*/
        GROUP(i2c_scl_a,        1,      14), /*dv25*/
 
+       GROUP(pdm_in,           2,      7), /* dv24 */
+       GROUP(pdm_clk,          2,      6), /* dv25 */
+
        GROUP(i2c_sda_b,        1,      13), /*dv26*/
        GROUP(i2c_scl_b,        1,      12), /*dv27*/
 
index 2f6741ff8d41c951c5ecf0449d53ea46850cb4b1..d0904a21557a80892e8643e951c6cba5ce02ad2d 100644 (file)
 #define CLKID_PCM_MCLK_COMP (CLKID_MISC_BASE + 19)
 #define CLKID_PCM_SCLK_DIV (CLKID_MISC_BASE + 20)
 #define CLKID_PCM_SCLK_GATE (CLKID_MISC_BASE + 21)
+#define CLKID_I958_COMP_SPDIF (CLKID_MISC_BASE + 22)
 
-#define NR_CLKS                                (OTHER_BASE + 101)
+#define NR_CLKS                                (OTHER_BASE + 102)
 
 #endif /* __GX_CLKC_H */
index 78c668c749bf958b4ab3fe1beb5fa0f0689e344b..c392390f2cab95831b494d7cc1d01bfdd557ec22 100644 (file)
 /* write 1 to load address to AUDIN_FIFO0. */
 #define AUDIN_FIFO0_LOAD       2
 
-#define AUDIN_FIFO0_DIN_SEL    3
-
-/* 0     spdifIN */
+enum data_source {
+       SPDIF_IN,
+       I2S_IN,
+       PCM_IN,
+       HDMI_IN,
+       PAO_IN
+};
 
-/* 1     i2Sin */
-
-/* 2     PCMIN */
-
-/* 3     HDMI in */
-
-/* 4     DEMODULATOR IN */
+#define AUDIN_FIFO0_DIN_SEL    3
+ /* MBOX platform*/
+               /* 0     spdifIN */
+               /* 1     i2Sin */
+               /* 2     PCMIN */
+               /* 3     Dmic in */
+ /* TV platform*/
+               /* 0     spdifIN */
+               /* 1     i2Sin */
+               /* 2     PCMIN */
+               /* 3     HDMI in */
+               /* 4     DEMODULATOR IN */
 
 /* 10:8   data endian control. */
 #define AUDIN_FIFO0_ENDIAN     8
index 182d92efc7c80166edea6374456df776aa8079e9..bab841fa58ae53afc303fee5fa3717e819bc2787 100644 (file)
@@ -76,5 +76,8 @@ source "sound/soc/codecs/Kconfig"
 # generic frame-work
 source "sound/soc/generic/Kconfig"
 
+# for Amlogic Soc
+source "sound/soc/amlogic/Kconfig"
+
 endif  # SND_SOC
 
index 9a30f21d16ee8efb2ddfcb84e757245e1b395bee..fa8269a6e2650bfb9eadb161511ac6bfa31a28bf 100644 (file)
@@ -49,3 +49,4 @@ obj-$(CONFIG_SND_SOC) += txx9/
 obj-$(CONFIG_SND_SOC)  += ux500/
 obj-$(CONFIG_SND_SOC)  += xtensa/
 obj-$(CONFIG_SND_SOC)  += zte/
+obj-$(CONFIG_SND_SOC)  += amlogic/
\ No newline at end of file
diff --git a/sound/soc/amlogic/Kconfig b/sound/soc/amlogic/Kconfig
new file mode 100644 (file)
index 0000000..58d5799
--- /dev/null
@@ -0,0 +1,27 @@
+menuconfig AMLOGIC_SND_SOC
+       bool "Amlogic Meson ASoC"
+       default n
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the Amlogic Asoc interface. You will also need
+         to select the audio interfaces to support below.
+
+if AMLOGIC_SND_SOC
+
+config AMLOGIC_SND_SPLIT_MODE
+       bool "AIU split mode, otherwise normal mode"
+       depends on AMLOGIC_SND_SOC
+       default n
+       help
+               Say 'Y' to enable AIU split mode. If not, it's normal mode.
+
+config AMLOGIC_SND_SPLIT_MODE_MMAP
+       bool "AIU split mode, mmap"
+       depends on AMLOGIC_SND_SPLIT_MODE
+       depends on AMLOGIC_SND_SOC
+       default n
+       help
+               Say 'Y' or 'N' to enable/disable AIU split mmap
+
+endif # AMLOGIC_SND_SOC
+
diff --git a/sound/soc/amlogic/Makefile b/sound/soc/amlogic/Makefile
new file mode 100644 (file)
index 0000000..a430b00
--- /dev/null
@@ -0,0 +1,31 @@
+# AML Platform Support
+snd-soc-aml-pcm-objs := aml_pcm.o
+snd-soc-aml-i2s-objs := aml_i2s.o
+snd-soc-aml-i2s-dai-objs := aml_i2s_dai.o
+snd-soc-aml-pcm-dai-objs := aml_pcm_dai.o
+snd-soc-aml-spdif-dai-objs  := aml_spdif_dai.o
+snd-soc-aml-hw-objs  := aml_audio_hw.o
+snd-soc-aml-hw-pcm2bt-objs  := aml_audio_hw_pcm.o
+snd-soc-aml-dmic-objs  := aml_dmic.o
+
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-pcm.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-i2s.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-i2s-dai.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-pcm-dai.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-hw.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += aml_notify.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-hw-pcm2bt.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-spdif-dai.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-dmic.o
+
+# AML spdif codec support
+snd-soc-aml-spdif-codec-objs := aml_spdif_codec.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-spdif-codec.o
+
+#AML M8 Machine support
+snd-soc-aml-meson-objs := aml_meson.o
+obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-meson.o
+
+#AML G9TV Machine support
+snd-soc-aml-tv-objs := aml_tv.o
+#obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-tv.o
diff --git a/sound/soc/amlogic/aml_audio_hw.c b/sound/soc/amlogic/aml_audio_hw.c
new file mode 100644 (file)
index 0000000..17e289e
--- /dev/null
@@ -0,0 +1,1013 @@
+/*
+ * sound/soc/amlogic/aml_audio_hw.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#define pr_fmt(fmt) "aml_audio_hw: " fmt
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+
+/* Amlogic headers */
+#include <linux/amlogic/iomap.h>
+#include <linux/amlogic/media/sound/aiu_regs.h>
+#include <linux/amlogic/media/sound/audin_regs.h>
+#include <linux/amlogic/cpu_version.h>
+#include "aml_audio_hw.h"
+
+/* i2s mode 0: master 1: slave */
+/* source: 0: linein; 1: ATV; 2: HDMI-in */
+unsigned int IEC958_MODE = AIU_958_MODE_PCM16;
+unsigned int I2S_MODE = AIU_I2S_MODE_PCM16;
+unsigned int audio_in_source;
+void set_i2s_source(unsigned int source)
+{
+       audio_in_source = source;
+}
+
+int audio_in_buf_ready;
+int audio_out_buf_ready;
+
+unsigned int IEC958_bpf = 0x7dd;
+EXPORT_SYMBOL(IEC958_bpf);
+unsigned int IEC958_brst = 0xc;
+EXPORT_SYMBOL(IEC958_brst);
+unsigned int IEC958_length = 0x7dd * 8;
+EXPORT_SYMBOL(IEC958_length);
+unsigned int IEC958_padsize = 0x8000;
+EXPORT_SYMBOL(IEC958_padsize);
+unsigned int IEC958_mode = 1;
+EXPORT_SYMBOL(IEC958_mode);
+unsigned int IEC958_syncword1 = 0x7ffe;
+EXPORT_SYMBOL(IEC958_syncword1);
+unsigned int IEC958_syncword2 = 0x8001;
+EXPORT_SYMBOL(IEC958_syncword2);
+unsigned int IEC958_syncword3;
+EXPORT_SYMBOL(IEC958_syncword3);
+unsigned int IEC958_syncword1_mask;
+EXPORT_SYMBOL(IEC958_syncword1_mask);
+unsigned int IEC958_syncword2_mask;
+EXPORT_SYMBOL(IEC958_syncword2_mask);
+unsigned int IEC958_syncword3_mask = 0xffff;
+EXPORT_SYMBOL(IEC958_syncword3_mask);
+unsigned int IEC958_chstat0_l = 0x1902;
+EXPORT_SYMBOL(IEC958_chstat0_l);
+unsigned int IEC958_chstat0_r = 0x1902;
+EXPORT_SYMBOL(IEC958_chstat0_r);
+unsigned int IEC958_chstat1_l = 0x200;
+EXPORT_SYMBOL(IEC958_chstat1_l);
+unsigned int IEC958_chstat1_r = 0x200;
+EXPORT_SYMBOL(IEC958_chstat1_r);
+unsigned int IEC958_mode_raw;
+EXPORT_SYMBOL(IEC958_mode_raw);
+
+/*
+ * bit 0:soc in slave mode for adc;
+ * bit 1:audio in data source from spdif in;
+ * bit 2:adc & spdif in work at the same time;
+ */
+unsigned int audioin_mode = I2SIN_MASTER_MODE;
+
+/* Bit 3:  mute constant
+ * 0 => 'h0000000
+ * 1 => 'h800000
+ */
+unsigned int dac_mute_const = 0x800000;
+
+void audio_set_aiubuf(u32 addr, u32 size, unsigned int channel)
+{
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+       aml_write_cbus(AIU_MEM_I2S_START_PTR, addr & 0xffffff00);
+       aml_write_cbus(AIU_MEM_I2S_RD_PTR, addr & 0xffffff00);
+#else
+       aml_write_cbus(AIU_MEM_I2S_START_PTR, addr & 0xffffffc0);
+       aml_write_cbus(AIU_MEM_I2S_RD_PTR, addr & 0xffffffc0);
+#endif
+
+       if (channel == 8) {
+               /*select cts_aoclkx2_int as AIU clk to hdmi_tx_audio_mster_clk*/
+               aml_cbus_update_bits(AIU_CLK_CTRL_MORE, 1 << 6, 1 << 6);
+               /*unmute all channels*/
+               aml_cbus_update_bits(AIU_I2S_MUTE_SWAP, 0xff << 8, 0 << 8);
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+               aml_write_cbus(AIU_MEM_I2S_END_PTR,
+                       (addr & 0xffffff00) + (size & 0xffffff00) - 256);
+#else
+               aml_write_cbus(AIU_MEM_I2S_END_PTR,
+                       (addr & 0xffffffc0) + (size & 0xffffffc0) - 256);
+#endif
+       } else {
+               /*select cts_clk_i958 as AIU clk to hdmi_tx_audio_mster_clk*/
+               aml_cbus_update_bits(AIU_CLK_CTRL_MORE, 1 << 6, 0 << 6);
+               /*unmute 0/1 channel*/
+               aml_cbus_update_bits(AIU_I2S_MUTE_SWAP, 0xff << 8, 0xfc << 8);
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+               aml_write_cbus(AIU_MEM_I2S_END_PTR,
+                       (addr & 0xffffff00) + (size & 0xffffff00) - 256);
+#else
+               aml_write_cbus(AIU_MEM_I2S_END_PTR,
+                       (addr & 0xffffffc0) + (size & 0xffffffc0) - 64);
+#endif
+       }
+       /* Hold I2S */
+       aml_write_cbus(AIU_I2S_MISC, 0x0004);
+       /* Release hold and force audio data to left or right */
+       aml_write_cbus(AIU_I2S_MISC, 0x0010);
+
+       if (channel == 8) {
+               pr_info("%s channel == 8\n", __func__);
+               /* [31:16] IRQ block. */
+               aml_write_cbus(AIU_MEM_I2S_MASKS, (24 << 16) |
+               /* [15: 8] chan_mem_mask.
+                *  Each bit indicates which channels exist in memory
+                */
+                                  (0xff << 8) |
+               /* [ 7: 0] chan_rd_mask.
+                *  Each bit indicates which channels are READ from memory
+                */
+                                  (0xff << 0));
+       } else {
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+               /* [31:16] IRQ block. */
+               aml_write_cbus(AIU_MEM_I2S_MASKS, (24 << 16) |
+                                       (0xff << 8) |
+                                       (0xff << 0));
+#else
+               /* [31:16] IRQ block. */
+               aml_write_cbus(AIU_MEM_I2S_MASKS, (24 << 16) |
+                                  (0x3 << 8) |
+                                  (0x3 << 0));
+#endif
+       }
+       /* 16 bit PCM mode */
+       /* aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1, 6, 1); */
+       /* Set init high then low to initialize the I2S memory logic */
+       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1, 1);
+       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1, 0);
+
+       aml_write_cbus(AIU_MEM_I2S_BUF_CNTL, 1 | (0 << 1));
+       aml_write_cbus(AIU_MEM_I2S_BUF_CNTL, 0 | (0 << 1));
+
+       audio_out_buf_ready = 1;
+}
+
+void audio_set_958outbuf(u32 addr, u32 size, int flag)
+{
+       aml_write_cbus(AIU_MEM_IEC958_START_PTR, addr & 0xffffffc0);
+       if (aml_read_cbus(AIU_MEM_IEC958_START_PTR) ==
+           aml_read_cbus(AIU_MEM_I2S_START_PTR)) {
+               aml_write_cbus(AIU_MEM_IEC958_RD_PTR,
+                              aml_read_cbus(AIU_MEM_I2S_RD_PTR));
+       } else
+               aml_write_cbus(AIU_MEM_IEC958_RD_PTR,
+                              addr & 0xffffffc0);
+       if (flag == 0) {
+               /* this is for 16bit 2 channel */
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+               aml_write_cbus(AIU_MEM_IEC958_END_PTR,
+                              (addr & 0xffffffc0) +
+                              (size & 0xffffffc0) - 8);
+#else
+               aml_write_cbus(AIU_MEM_IEC958_END_PTR,
+                              (addr & 0xffffffc0) +
+                              (size & 0xffffffc0) - 64);
+#endif
+       } else {
+               /* this is for RAW mode */
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+               aml_write_cbus(AIU_MEM_IEC958_END_PTR,
+                                       (addr & 0xffffffc0) +
+                                       (size & 0xffffffc0) - 8);
+#else
+               aml_write_cbus(AIU_MEM_IEC958_END_PTR,
+                              (addr & 0xffffffc0) +
+                              (size & 0xffffffc0) - 1);
+#endif
+       }
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+       aml_cbus_update_bits(AIU_MEM_IEC958_MASKS, 0xffff, 0xffff);
+#else
+       aml_cbus_update_bits(AIU_MEM_IEC958_MASKS, 0xffff, 0x303);
+#endif
+       aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1, 1);
+       aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1, 0);
+
+       aml_write_cbus(AIU_MEM_IEC958_BUF_CNTL, 1 | (0 << 1));
+       aml_write_cbus(AIU_MEM_IEC958_BUF_CNTL, 0 | (0 << 1));
+}
+
+/*
+ * i2s mode 0: master 1: slave
+ * din_sel 0:spdif 1:i2s 2:pcm 3: dmic
+ */
+static void i2sin_fifo0_set_buf(u32 addr, u32 size, u32 i2s_mode,
+               u32 i2s_sync, u32 din_sel, u32 ch)
+{
+       unsigned char mode = 0;
+       unsigned int sync_mode = 0, din_pos = 0;
+
+       if (i2s_sync)
+               sync_mode = i2s_sync;
+       if (i2s_mode & I2SIN_SLAVE_MODE)
+               mode = 1;
+       if (din_sel != 1)
+               din_pos = 1;
+       aml_write_cbus(AUDIN_FIFO0_START, addr & 0xffffffc0);
+       aml_write_cbus(AUDIN_FIFO0_PTR, (addr & 0xffffffc0));
+       aml_write_cbus(AUDIN_FIFO0_END,
+                      (addr & 0xffffffc0) + (size & 0xffffffc0) - 8);
+
+       aml_write_cbus(AUDIN_FIFO0_CTRL, (1 << AUDIN_FIFO0_EN)  /* FIFO0_EN */
+                      |(1 << AUDIN_FIFO0_LOAD) /* load start address */
+                      |(din_sel << AUDIN_FIFO0_DIN_SEL)
+
+                      /* DIN from i2sin */
+                      /* |(1<<6)    // 32 bits data in. */
+                      /* |(0<<7)    // put the 24bits data to  low 24 bits */
+                      | (4 << AUDIN_FIFO0_ENDIAN) /* AUDIN_FIFO0_ENDIAN */
+                      |((ch == 2?2:1) << AUDIN_FIFO0_CHAN) /* ch mode ctl */
+                      |(0 << 16)       /* to DDR */
+                      |(1 << AUDIN_FIFO0_UG)   /* Urgent request. */
+                      |(0 << 17)       /* Overflow Interrupt mask */
+                      |(0 << 18)
+                      /* Audio in INT */
+                      /* |(1<<19)    // hold 0 enable */
+                      | (0 << AUDIN_FIFO0_UG)  /* hold0 to aififo */
+           );
+
+       aml_write_cbus(AUDIN_FIFO0_CTRL1, 0 << 4        /* fifo0_dest_sel */
+                      | 2 << 2 /* fifo0_din_byte_num */
+                      | din_pos << 0); /* fifo0_din_pos */
+
+       if (audio_in_source == 0) {
+               aml_write_cbus(AUDIN_I2SIN_CTRL,
+                                  ((0xf>>(4 - ch/2)) << I2SIN_CHAN_EN)
+                                  | (3 << I2SIN_SIZE)
+                                  | (1 << I2SIN_LRCLK_INVT)
+                                  | (1 << I2SIN_LRCLK_SKEW)
+                                  | (sync_mode << I2SIN_POS_SYNC)
+                                  | (!mode << I2SIN_LRCLK_SEL)
+                                  | (!mode << I2SIN_CLK_SEL)
+                                  | (!mode << I2SIN_DIR));
+
+       } else if (audio_in_source == 1) {
+               aml_write_cbus(AUDIN_I2SIN_CTRL, (1 << I2SIN_CHAN_EN)
+                                  | (0 << I2SIN_SIZE)
+                                  | (0 << I2SIN_LRCLK_INVT)
+                                  | (0 << I2SIN_LRCLK_SKEW)
+                                  | (1 << I2SIN_POS_SYNC)
+                                  | (0 << I2SIN_LRCLK_SEL)
+                                  | (0 << I2SIN_CLK_SEL)
+                                  | (0 << I2SIN_DIR));
+       } else if (audio_in_source == 2) {
+               aml_write_cbus(AUDIN_I2SIN_CTRL, (1 << I2SIN_CHAN_EN)
+                                  | (3 << I2SIN_SIZE)
+                                  | (1 << I2SIN_LRCLK_INVT)
+                                  | (1 << I2SIN_LRCLK_SKEW)
+                                  | (1 << I2SIN_POS_SYNC)
+                                  | (1 << I2SIN_LRCLK_SEL)
+                                  | (1 << I2SIN_CLK_SEL)
+                                  | (1 << I2SIN_DIR));
+       }
+
+}
+
+static void spdifin_reg_set(void)
+{
+       /* get clk81 clk_rate */
+       unsigned int clk_rate = clk81;
+       u32 spdif_clk_time = 54;        /* 54us */
+       u32 spdif_mode_14bit = (u32)((clk_rate / 500000 + 1) >> 1)
+                                       * spdif_clk_time;
+       /* sysclk/32(bit)/2(ch)/2(bmc) */
+       u32 period_data = (u32)(clk_rate / 64000 + 1) >> 1;
+       u32 period_32k = (period_data + (1 << 4)) >> 5; /* 32k min period */
+       u32 period_44k = (period_data / 22 + 1) >> 1;   /* 44k min period */
+       u32 period_48k = (period_data / 24 + 1) >> 1;   /* 48k min period */
+       u32 period_96k = (period_data / 48 + 1) >> 1;   /* 96k min period */
+       u32 period_192k = (period_data / 96 + 1) >> 1;  /* 192k min period */
+
+       pr_info("spdifin_reg_set: clk_rate=%d\n", clk_rate);
+
+       aml_write_cbus(AUDIN_SPDIF_MODE,
+                      (aml_read_cbus(AUDIN_SPDIF_MODE) & 0x7fffc000) |
+                      (spdif_mode_14bit << 0));
+       aml_write_cbus(AUDIN_SPDIF_FS_CLK_RLTN,
+                      (period_32k << 0) |
+                      (period_44k << 6) | (period_48k << 12) |
+                      /* Spdif_fs_clk_rltn */
+                      (period_96k << 18) | (period_192k << 24));
+
+}
+
+static void spdifin_fifo1_set_buf(u32 addr, u32 size, u32 src)
+{
+       aml_write_cbus(AUDIN_SPDIF_MODE,
+                          aml_read_cbus(AUDIN_SPDIF_MODE) & 0x7fffffff);
+       /*set channel invert from old spdif in mode*/
+       aml_cbus_update_bits(AUDIN_SPDIF_MODE, (1 << 19), (1 << 19));
+       aml_write_cbus(AUDIN_FIFO1_START, addr & 0xffffffc0);
+       aml_write_cbus(AUDIN_FIFO1_PTR, (addr & 0xffffffc0));
+       aml_write_cbus(AUDIN_FIFO1_END,
+                      (addr & 0xffffffc0) + (size & 0xffffffc0) - 8);
+       aml_write_cbus(AUDIN_FIFO1_CTRL, (1 << AUDIN_FIFO1_EN)  /* FIFO0_EN */
+                      |(1 << AUDIN_FIFO1_LOAD) /* load start address. */
+                      |(src << AUDIN_FIFO1_DIN_SEL)
+
+                      /* DIN from i2sin. */
+                      /* |(1<<6)   // 32 bits data in. */
+                      /* |(0<<7)   // put the 24bits data to  low 24 bits */
+                      | (4 << AUDIN_FIFO1_ENDIAN)      /* AUDIN_FIFO0_ENDIAN */
+                      |(2 << AUDIN_FIFO1_CHAN) /* 2 channel */
+                      |(0 << 16)       /* to DDR */
+                      |(1 << AUDIN_FIFO1_UG)   /* Urgent request. */
+                      |(0 << 17)       /* Overflow Interrupt mask */
+                      |(0 << 18)
+                      /* Audio in INT */
+                      /* |(1<<19)   //hold 0 enable */
+                      | (0 << AUDIN_FIFO1_UG)  /* hold0 to aififo */
+           );
+
+       /*
+        *  according clk81 to set reg spdif_mode(0x2800)
+        *  the last 14 bit and reg Spdif_fs_clk_rltn(0x2801)
+        */
+       spdifin_reg_set();
+       /*3 byte mode, (23:0)*/
+       if (src == PAO_IN) {
+               aml_write_cbus(AUDIN_FIFO1_CTRL1, 0x08);
+       } else if (src == HDMI_IN) {
+               /* there are two inputs for HDMI_IN. New I2S:SPDIF */
+               aml_write_cbus(AUDIN_FIFO1_CTRL1, 0x08);
+               if (1) {
+                       /* new SPDIF in module */
+                       aml_write_cbus(AUDIN_DECODE_FORMAT, 1<<24);
+               } else {
+                       /* new I2S in module */
+                       aml_write_cbus(AUDIN_DECODE_FORMAT, 0x103ad);
+               }
+       } else
+               aml_write_cbus(AUDIN_FIFO1_CTRL1, 0x88);
+}
+
+void audio_in_i2s_set_buf(u32 addr, u32 size,
+       u32 i2s_mode, u32 i2s_sync, u32 din_sel, u32 ch)
+{
+       pr_info("i2sin_fifo0_set_buf din_sel:%d ch:%d\n", din_sel, ch);
+       i2sin_fifo0_set_buf(addr, size, i2s_mode, i2s_sync, din_sel, ch);
+       audio_in_buf_ready = 1;
+}
+
+void audio_in_spdif_set_buf(u32 addr, u32 size, u32 src)
+{
+       pr_info("spdifin_fifo1_set_buf, src = %d\n", src);
+       spdifin_fifo1_set_buf(addr, size, src);
+}
+
+/* extern void audio_in_enabled(int flag); */
+
+void audio_in_i2s_enable(int flag)
+{
+       int rd = 0, start = 0;
+
+       if (flag) {
+               /* reset only when start i2s input */
+ reset_again:
+               /* reset FIFO 0 */
+               aml_cbus_update_bits(AUDIN_FIFO0_CTRL, 0x2, 0x2);
+               aml_write_cbus(AUDIN_FIFO0_PTR, 0);
+               rd = aml_read_cbus(AUDIN_FIFO0_PTR);
+               start = aml_read_cbus(AUDIN_FIFO0_START);
+               if (rd != start) {
+                       pr_err("error %08x, %08x !\n",
+                              rd, start);
+                       goto reset_again;
+               }
+               aml_cbus_update_bits(AUDIN_I2SIN_CTRL, 1 << I2SIN_EN,
+                                    1 << I2SIN_EN);
+       } else {
+               aml_cbus_update_bits(AUDIN_I2SIN_CTRL, 1 << I2SIN_EN,
+                                    0 << I2SIN_EN);
+       }
+}
+
+void audio_in_spdif_enable(int flag)
+{
+       int rd = 0, start = 0;
+
+       if (flag) {
+ reset_again:
+               /* reset FIFO 0 */
+               aml_cbus_update_bits(AUDIN_FIFO1_CTRL, 0x2, 0x2);
+               aml_write_cbus(AUDIN_FIFO1_PTR, 0);
+               rd = aml_read_cbus(AUDIN_FIFO1_PTR);
+               start = aml_read_cbus(AUDIN_FIFO1_START);
+               if (rd != start) {
+                       pr_err("error %08x, %08x !\n", rd, start);
+                       goto reset_again;
+               }
+               aml_write_cbus(AUDIN_SPDIF_MODE,
+                              aml_read_cbus(AUDIN_SPDIF_MODE) | (1 << 31));
+       } else {
+               aml_write_cbus(AUDIN_SPDIF_MODE,
+                              aml_read_cbus(AUDIN_SPDIF_MODE) & ~(1 << 31));
+       }
+}
+
+int if_audio_in_i2s_enable(void)
+{
+       return aml_read_cbus(AUDIN_I2SIN_CTRL) & (1 << 15);
+}
+
+int if_audio_in_spdif_enable(void)
+{
+       return aml_read_cbus(AUDIN_SPDIF_MODE) & (1 << 31);
+}
+
+unsigned int audio_in_i2s_rd_ptr(void)
+{
+       unsigned int val;
+
+       val = aml_read_cbus(AUDIN_FIFO0_RDPTR);
+       pr_info("audio in i2s rd ptr: %x\n", val);
+       return val;
+}
+
+unsigned int audio_in_spdif_rd_ptr(void)
+{
+       unsigned int val;
+
+       val = aml_read_cbus(AUDIN_FIFO1_RDPTR);
+       pr_info("audio in spdif rd ptr: %x\n", val);
+       return val;
+}
+
+unsigned int audio_in_i2s_wr_ptr(void)
+{
+       unsigned int val;
+
+       aml_write_cbus(AUDIN_FIFO0_PTR, 1);
+       val = aml_read_cbus(AUDIN_FIFO0_PTR);
+       return (val) & (~0x3F);
+       /* return val&(~0x7); */
+}
+
+unsigned int audio_in_spdif_wr_ptr(void)
+{
+       unsigned int val;
+
+       aml_write_cbus(AUDIN_FIFO1_PTR, 1);
+       val = aml_read_cbus(AUDIN_FIFO1_PTR);
+       return (val) & (~0x3F);
+}
+
+void audio_in_i2s_set_wrptr(unsigned int val)
+{
+       aml_write_cbus(AUDIN_FIFO0_RDPTR, val);
+}
+
+void audio_in_spdif_set_wrptr(unsigned int val)
+{
+       aml_write_cbus(AUDIN_FIFO1_RDPTR, val);
+}
+
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+void audio_set_i2s_mode(u32 mode, unsigned int channel)
+{
+       aml_write_cbus(AIU_I2S_SOURCE_DESC, 0x800);
+
+       aml_cbus_update_bits(AIU_CLK_CTRL_MORE, 0x1f, 0);
+
+       if (channel == 8) {
+               aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 0, 1);
+
+               if (mode == AIU_I2S_MODE_PCM32) {
+                       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1 << 6, 0);
+
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 9,
+                                               1 << 9);
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 7 << 6,
+                                               7 << 6);
+               } else if (mode == AIU_I2S_MODE_PCM24) {
+                       /* todo: to verify it */
+                       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1 << 6, 0);
+
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 9,
+                                               1 << 9);
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 7 << 6,
+                                               7 << 6);
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 5,
+                                               1 << 5);
+
+               } else if (mode == AIU_I2S_MODE_PCM16) {
+                       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1 << 6,
+                                               1 << 6);
+
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 2 << 3,
+                                               2 << 3);
+
+                       aml_cbus_update_bits(AIU_CLK_CTRL_MORE, 0x1f, 0x5);
+               }
+       } else if (channel == 2) {
+               aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 0, 0);
+
+               if (mode == AIU_I2S_MODE_PCM16) {
+                       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1 << 6,
+                                               1 << 6);
+
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 2 << 3,
+                                               2 << 3);
+               } else if (mode == AIU_I2S_MODE_PCM24) {
+                       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1 << 6, 0);
+
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 5,
+                                               1 << 5);
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 2 << 3,
+                                               2 << 3);
+               } else if (mode == AIU_I2S_MODE_PCM32) {
+                       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1 << 6, 0);
+
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 9,
+                                               1 << 9);
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 7 << 6,
+                                               7 << 6);
+               }
+       }
+
+       /* In split mode, there are not mask control,
+        * so aiu_mem_i2s_mask[15:0] must set 8'hffff_ffff.
+        */
+       /* aml_write_cbus(AIU_MEM_I2S_MASKS,
+        *      (16 << 16) |
+        *      (0xff << 8) |
+        *      (0xff << 0));
+        */
+}
+#else
+void audio_set_i2s_mode(u32 mode)
+{
+       const unsigned short mask[4] = {
+               0x303,          /* 2x16 */
+               0x303,          /* 2x24 */
+               0x303,          /* 8x24 */
+               0x303,          /* 2x32 */
+       };
+
+       if (mode < sizeof(mask) / sizeof(unsigned short)) {
+               /* four two channels stream */
+               aml_write_cbus(AIU_I2S_SOURCE_DESC, 1);
+
+               if (mode == AIU_I2S_MODE_PCM16) {
+                       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1 << 6,
+                                            1 << 6);
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 5, 0);
+               } else if (mode == AIU_I2S_MODE_PCM32) {
+                       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1 << 6, 0);
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 5,
+                                            1 << 5);
+               } else if (mode == AIU_I2S_MODE_PCM24) {
+                       aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 1 << 6, 0);
+                       aml_cbus_update_bits(AIU_I2S_SOURCE_DESC, 1 << 5,
+                                            1 << 5);
+               }
+
+               aml_cbus_update_bits(AIU_MEM_I2S_MASKS, 0xffff, mask[mode]);
+       }
+}
+#endif
+
+/*
+ *  if normal clock, i2s clock is twice of 958 clock,
+ *  so the divisor for i2s is 8, but 4 for 958
+ *  if over clock, the devisor for i2s is 8, but for 958 should be 1,
+ *  because 958 should be 4 times speed according to i2s.
+ *  This is dolby digital plus's spec
+ */
+
+/* iec958 and i2s clock are separated after M6TV. */
+void audio_util_set_dac_958_format(unsigned int format)
+{
+       /* 958 divisor more, if true, divided by 2, 4, 6, 8 */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 1 << 12, 0);
+#if IEC958_OVERCLOCK == 1
+       /* 958 divisor: 0=no div; 1=div by 2; 2=div by 3; 3=div by 4. */
+/*     aml_cbus_update_bits(AIU_CLK_CTRL, 3 << 4, 1 << 4);  */
+#else
+       /* 958 divisor: 0=no div; 1=div by 2; 2=div by 3; 3=div by 4. */
+/*     aml_cbus_update_bits(AIU_CLK_CTRL, 3 << 4, 1 << 4);  */
+#endif
+       /* enable 958 divider */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 1 << 1, 1 << 1);
+}
+
+void audio_util_set_dac_i2s_format(unsigned int format)
+{
+       /* invert aoclk */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 1 << 6, 1 << 6);
+       /* invert lrclk */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 1 << 7, 1 << 7);
+       /* alrclk skew: 1=alrclk transitions on the cycle before msb is sent */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 0x3 << 8, 1 << 8);
+#if MCLKFS_RATIO == 512
+       /* i2s divisor: 0=no div; 1=div by 2; 2=div by 4; 3=div by 8. */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 0x3 << 2, 0x3 << 2);
+#elif MCLKFS_RATIO == 256
+       aml_cbus_update_bits(AIU_CLK_CTRL, 0x3 << 2, 0x2 << 2);
+#else
+       aml_cbus_update_bits(AIU_CLK_CTRL, 0x3 << 2, 0x1 << 2);
+#endif
+       /* enable I2S clock */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 1, 1);
+
+       if (format == AUDIO_ALGOUT_DAC_FORMAT_LEFT_JUSTIFY)
+               aml_cbus_update_bits(AIU_CLK_CTRL, 0x3 << 8, 0);
+
+       if (dac_mute_const == 0x800000)
+               aml_write_cbus(AIU_I2S_DAC_CFG, 0x000f);
+       else
+               /* Payload 24-bit, Msb first, alrclk = aoclk/64 */
+               aml_write_cbus(AIU_I2S_DAC_CFG, 0x0007);
+
+       /* four 2-channel */
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+       aml_write_cbus(AIU_I2S_SOURCE_DESC, (1 << 11));
+#else
+       aml_write_cbus(AIU_I2S_SOURCE_DESC, 0x0001);
+#endif
+}
+
+/* set sclk and lrclk, mclk = 256fs. */
+void audio_set_i2s_clk_div(void)
+{
+       /* aiclk source */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 1 << 10, 1 << 10);
+       /* Set mclk over sclk ratio */
+       aml_cbus_update_bits(AIU_CLK_CTRL_MORE, 0x3f << 8, (4 - 1) << 8);
+       /* set dac/adc lrclk ratio over sclk----64fs */
+       aml_cbus_update_bits(AIU_CODEC_DAC_LRCLK_CTRL, 0xfff, (64 - 1));
+       aml_cbus_update_bits(AIU_CODEC_ADC_LRCLK_CTRL, 0xfff, (64 - 1));
+       /* Enable sclk */
+       aml_cbus_update_bits(AIU_CLK_CTRL_MORE, 1 << 14, 1 << 14);
+}
+
+void audio_set_spdif_clk_div(uint div)
+{
+       uint val = 0;
+
+       if (div < 1 && div > 4)
+               return;
+
+       val = div - 1;
+       /* 958 divisor: 0=no div; 1=div by 2; 2=div by 3; 3=div by 4. */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 3 << 4, val << 4);
+       /* enable 958 divider */
+       aml_cbus_update_bits(AIU_CLK_CTRL, 1 << 1, 1 << 1);
+}
+
+void audio_enable_output(int flag)
+{
+       if (flag) {
+               aml_write_cbus(AIU_RST_SOFT, 0x05);
+               aml_read_cbus(AIU_I2S_SYNC);
+               aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 3 << 1, 3 << 1);
+
+               /* Maybe cause POP noise */
+               /* audio_i2s_unmute(); */
+       } else {
+               aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 3 << 1, 0);
+
+               /* Maybe cause POP noise */
+               /* audio_i2s_mute(); */
+       }
+       /* audio_out_enabled(flag); */
+}
+
+int if_audio_out_enable(void)
+{
+       return aml_read_cbus(AIU_MEM_I2S_CONTROL) & (0x3 << 1);
+}
+EXPORT_SYMBOL(if_audio_out_enable);
+
+int if_958_audio_out_enable(void)
+{
+       return aml_read_cbus(AIU_MEM_IEC958_CONTROL) & (0x3 << 1);
+}
+EXPORT_SYMBOL(if_958_audio_out_enable);
+
+unsigned int read_i2s_rd_ptr(void)
+{
+       unsigned int val;
+
+       val = aml_read_cbus(AIU_MEM_I2S_RD_PTR);
+       return val;
+}
+
+unsigned int read_iec958_rd_ptr(void)
+{
+       unsigned int val;
+
+       val = aml_read_cbus(AIU_MEM_IEC958_RD_PTR);
+       return val;
+}
+
+void aml_audio_i2s_unmute(void)
+{
+       aml_cbus_update_bits(AIU_I2S_MUTE_SWAP, 0xff << 8, 0);
+}
+
+void aml_audio_i2s_mute(void)
+{
+       aml_cbus_update_bits(AIU_I2S_MUTE_SWAP, 0xff << 8, 0xff << 8);
+}
+
+void audio_i2s_unmute(void)
+{
+       aml_cbus_update_bits(AIU_I2S_MUTE_SWAP, 0xff << 8, 0);
+       aml_cbus_update_bits(AIU_958_CTRL, 0x3 << 3, 0 << 3);
+}
+
+void audio_i2s_mute(void)
+{
+       aml_cbus_update_bits(AIU_I2S_MUTE_SWAP, 0xff << 8, 0xff << 8);
+       aml_cbus_update_bits(AIU_958_CTRL, 0x3 << 3, 0x3 << 3);
+}
+
+void audio_mute_left_right(unsigned int flag)
+{
+       if (flag == 0) {        /* right */
+               aml_cbus_update_bits(AIU_958_CTRL, 0x3 << 3, 0x1 << 3);
+       } else if (flag == 1) { /* left */
+               aml_cbus_update_bits(AIU_958_CTRL, 0x3 << 3, 0x2 << 3);
+       }
+}
+
+void audio_hw_958_reset(unsigned int slow_domain, unsigned int fast_domain)
+{
+       aml_write_cbus(AIU_958_DCU_FF_CTRL, 0);
+       aml_write_cbus(AIU_RST_SOFT, (slow_domain << 3) | (fast_domain << 2));
+}
+
+void audio_hw_958_raw(void)
+{
+       aml_write_cbus(AIU_958_MISC, 1);
+       /* raw */
+       aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 8, 1 << 8);
+       /* 8bit */
+       aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 7, 0);
+       /* endian */
+       aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 0x7 << 3, 1 << 3);
+
+       aml_write_cbus(AIU_958_BPF, IEC958_bpf);
+       aml_write_cbus(AIU_958_BRST, IEC958_brst);
+       aml_write_cbus(AIU_958_LENGTH, IEC958_length);
+       aml_write_cbus(AIU_958_PADDSIZE, IEC958_padsize);
+       /* disable int */
+       aml_cbus_update_bits(AIU_958_DCU_FF_CTRL, 0x3 << 2, 0);
+
+       if (IEC958_mode == 1) { /* search in byte */
+               aml_cbus_update_bits(AIU_958_DCU_FF_CTRL, 0x7 << 4, 0x7 << 4);
+       } else if (IEC958_mode == 2) {  /* search in word */
+               aml_cbus_update_bits(AIU_958_DCU_FF_CTRL, 0x7 << 4, 0x5 << 4);
+       } else {
+               aml_cbus_update_bits(AIU_958_DCU_FF_CTRL, 0x7 << 4, 0);
+       }
+       aml_write_cbus(AIU_958_CHSTAT_L0, IEC958_chstat0_l);
+       aml_write_cbus(AIU_958_CHSTAT_L1, IEC958_chstat1_l);
+       aml_write_cbus(AIU_958_CHSTAT_R0, IEC958_chstat0_r);
+       aml_write_cbus(AIU_958_CHSTAT_R1, IEC958_chstat1_r);
+
+       aml_write_cbus(AIU_958_SYNWORD1, IEC958_syncword1);
+       aml_write_cbus(AIU_958_SYNWORD2, IEC958_syncword2);
+       aml_write_cbus(AIU_958_SYNWORD3, IEC958_syncword3);
+       aml_write_cbus(AIU_958_SYNWORD1_MASK, IEC958_syncword1_mask);
+       aml_write_cbus(AIU_958_SYNWORD2_MASK, IEC958_syncword2_mask);
+       aml_write_cbus(AIU_958_SYNWORD3_MASK, IEC958_syncword3_mask);
+
+       pr_info("%s: %d\n", __func__, __LINE__);
+       pr_info("\tBPF: %x\n", IEC958_bpf);
+       pr_info("\tBRST: %x\n", IEC958_brst);
+       pr_info("\tLENGTH: %x\n", IEC958_length);
+       pr_info("\tPADDSIZE: %x\n", IEC958_length);
+       pr_info("\tsyncword: %x, %x, %x\n\n", IEC958_syncword1,
+               IEC958_syncword2, IEC958_syncword3);
+
+}
+
+void set_958_channel_status(struct _aiu_958_channel_status_t *set)
+{
+       if (set) {
+               aml_write_cbus(AIU_958_CHSTAT_L0, set->chstat0_l);
+               aml_write_cbus(AIU_958_CHSTAT_L1, set->chstat1_l);
+               aml_write_cbus(AIU_958_CHSTAT_R0, set->chstat0_r);
+               aml_write_cbus(AIU_958_CHSTAT_R1, set->chstat1_r);
+       }
+}
+
+static void audio_hw_set_958_pcm24(struct _aiu_958_raw_setting_t *set)
+{
+       /* in pcm mode, set bpf to 128 */
+       aml_write_cbus(AIU_958_BPF, 0x80);
+       set_958_channel_status(set->chan_stat);
+}
+
+void audio_set_958_mode(unsigned int mode, struct _aiu_958_raw_setting_t *set)
+{
+       if (mode == AIU_958_MODE_PCM_RAW) {
+               mode = AIU_958_MODE_PCM16;      /* use 958 raw pcm mode */
+               aml_write_cbus(AIU_958_VALID_CTRL, 3);
+       } else
+               aml_write_cbus(AIU_958_VALID_CTRL, 0);
+
+       if (mode == AIU_958_MODE_RAW) {
+
+               audio_hw_958_raw();
+               aml_write_cbus(AIU_958_MISC, 1);
+               /* raw */
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL,
+                                    1 << 8, 1 << 8);
+               /* 8bit */
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 7, 0);
+               /* endian */
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL,
+                                    0x7 << 3, 0x1 << 3);
+
+               pr_info("IEC958 RAW\n");
+       } else if (mode == AIU_958_MODE_PCM32) {
+               audio_hw_set_958_pcm24(set);
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+               aml_write_cbus(AIU_958_MISC, 0x3480);
+               /* pcm */
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 8,
+                                       1 << 8);
+#else
+               aml_write_cbus(AIU_958_MISC, 0x2020 | (1 << 7));
+               /* pcm */
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 8, 0);
+#endif
+               /* 16bit */
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 7, 0);
+               /* endian */
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL,
+                                    0x7 << 3, 0);
+
+               pr_info("IEC958 PCM32\n");
+       } else if (mode == AIU_958_MODE_PCM24) {
+               audio_hw_set_958_pcm24(set);
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+                       aml_write_cbus(AIU_958_MISC, 0x3480);
+                       /* pcm */
+                       aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 8,
+                                               1 << 8);
+#else
+                       aml_write_cbus(AIU_958_MISC, 0x2020 | (1 << 7));
+                       /* pcm */
+                       aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 8, 0);
+#endif
+                       /* 16bit */
+                       aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 7, 0);
+                       /* endian */
+                       aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL,
+                                            0x7 << 3, 0);
+
+               pr_info("IEC958 24bit\n");
+       } else if (mode == AIU_958_MODE_PCM16) {
+               audio_hw_set_958_pcm24(set);
+               aml_write_cbus(AIU_958_MISC, 0x2042);
+               /* pcm */
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 8,
+                                       1 << 8);
+#else
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 1 << 8, 0);
+#endif
+               /* 16bit */
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL,
+                                    1 << 7, 1 << 7);
+               /* endian */
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL,
+                                    0x7 << 3, 0);
+               pr_info("IEC958 16bit\n");
+       }
+
+       audio_hw_958_reset(0, 1);
+
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+       if (mode == AIU_958_MODE_PCM32)
+               aml_cbus_update_bits(AIU_958_DCU_FF_CTRL, 1 << 8, 1 << 8);
+#endif
+
+       aml_write_cbus(AIU_958_FORCE_LEFT, 1);
+}
+
+void audio_out_i2s_enable(unsigned int flag)
+{
+       if (flag) {
+               aml_write_cbus(AIU_RST_SOFT, 0x01);
+               aml_read_cbus(AIU_I2S_SYNC);
+               aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 0x3 << 1, 0x3 << 1);
+               /* Maybe cause POP noise */
+               /* audio_i2s_unmute(); */
+       } else {
+               aml_cbus_update_bits(AIU_MEM_I2S_CONTROL, 0x3 << 1, 0);
+
+               /* Maybe cause POP noise */
+               /* audio_i2s_mute(); */
+       }
+       /* audio_out_enabled(flag); */
+}
+
+void audio_hw_958_enable(unsigned int flag)
+{
+       if (flag) {
+               aml_write_cbus(AIU_RST_SOFT, 0x04);
+               aml_write_cbus(AIU_958_FORCE_LEFT, 0);
+               aml_cbus_update_bits(AIU_958_DCU_FF_CTRL, 1, 1);
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 0x3 << 1,
+                                    0x3 << 1);
+       } else {
+               aml_write_cbus(AIU_RST_SOFT, 0x04);
+               aml_write_cbus(AIU_958_FORCE_LEFT, 0);
+               aml_write_cbus(AIU_958_DCU_FF_CTRL, 0);
+               aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 0x3 << 1,
+                                    0);
+       }
+}
+
+unsigned int read_i2s_mute_swap_reg(void)
+{
+       unsigned int val;
+
+       val = aml_read_cbus(AIU_I2S_MUTE_SWAP);
+       return val;
+}
+
+void audio_i2s_swap_left_right(unsigned int flag)
+{
+    /*only LPCM output can set aiu hw channel swap*/
+       if (IEC958_mode_codec == 0 || IEC958_mode_codec == 9)
+               aml_cbus_update_bits(AIU_958_CTRL, 0x3 << 1, flag << 1);
+
+       aml_cbus_update_bits(AIU_I2S_MUTE_SWAP, 0x3, flag);
+       aml_cbus_update_bits(AIU_I2S_MUTE_SWAP, 0x3 << 2, flag << 2);
+}
+
+void audio_i2s_958_same_source(unsigned int same)
+{
+       aml_cbus_update_bits(AIU_I2S_MISC, 1 << 3, (!!same) << 3);
+}
+
+void set_hw_resample_source(int source)
+{
+       aml_cbus_update_bits(AUD_RESAMPLE_CTRL0, 1 << 29, source << 29);
+}
+EXPORT_SYMBOL(set_hw_resample_source);
+#if 0
+unsigned int audio_hdmi_init_ready(void)
+{
+       return READ_MPEG_REG_BITS(AIU_HDMI_CLK_DATA_CTRL, 0, 2);
+}
+
+/* power gate control for iec958 audio out */
+unsigned int audio_spdifout_pg_enable(unsigned char enable)
+{
+       if (enable) {
+               aml_cbus_update_bits(MPLL_958_CNTL, 1, 14, 1);
+               AUDIO_CLK_GATE_ON(AIU_IEC958);
+               AUDIO_CLK_GATE_ON(AIU_ICE958_AMCLK);
+       } else {
+               AUDIO_CLK_GATE_OFF(AIU_IEC958);
+               AUDIO_CLK_GATE_OFF(AIU_ICE958_AMCLK);
+               aml_cbus_update_bits(MPLL_958_CNTL, 0, 14, 1);
+       }
+       return 0;
+}
+
+/*
+ *    power gate control for normal aiu  domain including i2s in/out
+ *   TODO: move i2s out /adc related gate to i2s cpu dai driver
+ */
+unsigned int audio_aiu_pg_enable(unsigned char enable)
+{
+       if (enable)
+               switch_mod_gate_by_name("audio", 1);
+       else
+               switch_mod_gate_by_name("audio", 0);
+
+       return 0;
+}
+#endif
diff --git a/sound/soc/amlogic/aml_audio_hw.h b/sound/soc/amlogic/aml_audio_hw.h
new file mode 100644 (file)
index 0000000..c8ddea4
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * sound/soc/amlogic/aml_audio_hw.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AML_AUDIO_HW_H__
+#define __AML_AUDIO_HW_H__
+
+#define AUDIO_CLK_GATE_ON(a) CLK_GATE_ON(a)
+#define AUDIO_CLK_GATE_OFF(a) CLK_GATE_OFF(a)
+
+struct _aiu_clk_setting_t {
+       unsigned short pll;
+       unsigned short mux;
+       unsigned short devisor;
+};
+
+struct _aiu_958_channel_status_t {
+       unsigned short chstat0_l;
+       unsigned short chstat1_l;
+       unsigned short chstat0_r;
+       unsigned short chstat1_r;
+};
+
+struct audio_output_config_t {
+       /* audio clock */
+       unsigned short clock;
+       /* analog output */
+       unsigned short i2s_mode;
+       unsigned short i2s_dac_mode;
+       unsigned short i2s_preemphsis;
+       /* digital output */
+       unsigned short i958_buf_start_addr;
+       unsigned short i958_buf_blksize;
+       unsigned short i958_int_flag;
+       unsigned short i958_mode;
+       unsigned short i958_sync_mode;
+       unsigned short i958_preemphsis;
+       unsigned short i958_copyright;
+       unsigned short bpf;
+       unsigned short brst;
+       unsigned short length;
+       unsigned short paddsize;
+       struct _aiu_958_channel_status_t chan_status;
+};
+
+struct _aiu_958_raw_setting_t {
+       unsigned short int_flag;
+       unsigned short bpf;
+       unsigned short brst;
+       unsigned short length;
+       unsigned short paddsize;
+       struct _aiu_958_channel_status_t *chan_stat;
+};
+
+enum {
+       I2SIN_MASTER_MODE = 0,
+       I2SIN_SLAVE_MODE = 1 << 0,
+       SPDIFIN_MODE = 1 << 1,
+};
+enum {
+       AML_AUDIO_NA = 0,
+       AML_AUDIO_SPDIFIN = 1 << 0,
+       AML_AUDIO_SPDIFOUT = 1 << 1,
+       AML_AUDIO_I2SIN = 1 << 2,
+       AML_AUDIO_I2SOUT = 1 << 3,
+       AML_AUDIO_PCMIN = 1 << 4,
+       AML_AUDIO_PCMOUT = 1 << 5,
+};
+
+#define AUDIO_CLK_256FS             0
+#define AUDIO_CLK_384FS             1
+
+#define AUDIO_CLK_FREQ_192  0
+#define AUDIO_CLK_FREQ_1764 1
+#define AUDIO_CLK_FREQ_96   2
+#define AUDIO_CLK_FREQ_882  3
+#define AUDIO_CLK_FREQ_48   4
+#define AUDIO_CLK_FREQ_441  5
+#define AUDIO_CLK_FREQ_32   6
+
+#define AUDIO_CLK_FREQ_8               7
+#define AUDIO_CLK_FREQ_11              8
+#define AUDIO_CLK_FREQ_12              9
+#define AUDIO_CLK_FREQ_16              10
+#define AUDIO_CLK_FREQ_22              11
+#define AUDIO_CLK_FREQ_24              12
+
+#define AIU_958_MODE_RAW    0
+#define AIU_958_MODE_PCM16  1
+#define AIU_958_MODE_PCM24  2
+#define AIU_958_MODE_PCM32  3
+#define AIU_958_MODE_PCM_RAW  4
+
+#define AIU_I2S_MODE_PCM16   0
+#define AIU_I2S_MODE_PCM24   2
+#define AIU_I2S_MODE_PCM32   3
+
+#define AUDIO_ALGOUT_DAC_FORMAT_DSP             0
+#define AUDIO_ALGOUT_DAC_FORMAT_LEFT_JUSTIFY    1
+
+extern unsigned int IEC958_MODE;
+extern unsigned int I2S_MODE;
+extern unsigned int audio_in_source;
+
+void set_i2s_source(unsigned int source);
+void audio_set_aiubuf(u32 addr, u32 size, unsigned int channel);
+void audio_set_958outbuf(u32 addr, u32 size, int flag);
+void audio_in_i2s_set_buf(u32 addr, u32 size,
+       u32 i2s_mode, u32 i2s_sync, u32 din_sel, u32 ch);
+void audio_in_spdif_set_buf(u32 addr, u32 size, u32 src);
+void audio_in_i2s_enable(int flag);
+void audio_in_spdif_enable(int flag);
+unsigned int audio_in_i2s_rd_ptr(void);
+unsigned int audio_in_i2s_wr_ptr(void);
+unsigned int audio_in_spdif_wr_ptr(void);
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+void audio_set_i2s_mode(u32 mode, unsigned int channel);
+#else
+void audio_set_i2s_mode(u32 mode);
+#endif
+void audio_set_i2s_clk_div(void);
+void audio_set_spdif_clk_div(uint div);
+void audio_enable_output(int flag);
+unsigned int read_i2s_rd_ptr(void);
+void audio_i2s_unmute(void);
+void audio_i2s_mute(void);
+void aml_audio_i2s_unmute(void);
+void aml_audio_i2s_mute(void);
+void audio_util_set_dac_i2s_format(unsigned int format);
+void audio_util_set_dac_958_format(unsigned int format);
+void audio_set_958_mode(
+       unsigned int mode,
+       struct _aiu_958_raw_setting_t *set);
+unsigned int read_i2s_mute_swap_reg(void);
+void audio_i2s_swap_left_right(unsigned int flag);
+int if_audio_out_enable(void);
+int if_audio_in_i2s_enable(void);
+int if_audio_in_spdif_enable(void);
+void audio_out_i2s_enable(unsigned int flag);
+void audio_hw_958_enable(unsigned int flag);
+void audio_out_enabled(int flag);
+unsigned int audio_hdmi_init_ready(void);
+unsigned int read_iec958_rd_ptr(void);
+void audio_in_spdif_enable(int flag);
+unsigned int audio_spdifout_pg_enable(unsigned char enable);
+unsigned int audio_aiu_pg_enable(unsigned char enable);
+void audio_mute_left_right(unsigned int flag);
+void audio_i2s_958_same_source(unsigned int same);
+
+extern unsigned int IEC958_mode_codec;
+extern unsigned int clk81;
+
+/* OVERCLOCK == 1, our SOC privide 512fs mclk;
+ * DOWNCLOCK == 1, 128fs;
+ * normal mclk : 256fs
+ */
+#define OVERCLOCK 0
+#define DOWNCLOCK 0
+
+#define IEC958_OVERCLOCK 1
+
+#if (OVERCLOCK == 1)
+#define MCLKFS_RATIO 512
+#elif (DOWNCLOCK == 1)
+#define MCLKFS_RATIO 128
+#else
+#define MCLKFS_RATIO 256
+#endif
+
+#define DEFAULT_SAMPLERATE 48000
+#define DEFAULT_MCLK_RATIO_SR MCLKFS_RATIO
+
+#define I2S_PLL_SRC         1  /* MPLL0 */
+#define MPLL_I2S_CNTL          HHI_MPLL_MP0
+
+#define I958_PLL_SRC        2  /* MPLL1 */
+#define MPLL_958_CNTL          HHI_MPLL_MP1
+
+#endif
diff --git a/sound/soc/amlogic/aml_audio_hw_pcm.c b/sound/soc/amlogic/aml_audio_hw_pcm.c
new file mode 100644 (file)
index 0000000..c65ac0c
--- /dev/null
@@ -0,0 +1,726 @@
+/*
+ * sound/soc/amlogic/aml_audio_hw_pcm.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#define pr_fmt(fmt) "audio_pcm" fmt
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+
+#include <linux/amlogic/iomap.h>
+#include <linux/amlogic/media/sound/audin_regs.h>
+#include "aml_audio_hw_pcm.h"
+
+#include <linux/amlogic/media/sound/aiu_regs.h>
+
+static unsigned int pcmin_buffer_addr;
+static unsigned int pcmin_buffer_size;
+
+static unsigned int pcmout_buffer_addr;
+static unsigned int pcmout_buffer_size;
+
+int valid_channel[] = {
+       0x1,    /* slot number 1 */
+       0x3,    /* slot number 2 */
+       0x7,    /* slot number 3 */
+       0xf,    /* slot number 4 */
+       0x1f,    /* slot number 5 */
+       0x3f,    /* slot number 6 */
+       0x7f,    /* slot number 7 */
+       0xff,    /* slot number 8 */
+       0x1ff,    /* slot number 9 */
+       0x3ff,    /* slot number 10 */
+       0x7ff,    /* slot number 11 */
+       0xfff,    /* slot number 12 */
+       0x1fff,    /* slot number 13 */
+       0x2fff,    /* slot number 14 */
+       0x3fff,    /* slot number 15 */
+       0x7fff    /* slot number 16 */
+};
+
+static uint32_t aml_read_cbus_bits(uint32_t reg, const uint32_t start,
+                                  const uint32_t len)
+{
+       return (aml_read_cbus(reg) >> start) & ((1L << len) - 1);
+}
+
+static void pcm_in_register_show(void)
+{
+       pr_debug("PCMIN registers show:\n");
+       pr_debug("\tAUDIN_FIFO1_START(0x%04x): 0x%08x\n", AUDIN_FIFO1_START,
+                 aml_read_cbus(AUDIN_FIFO1_START));
+       pr_debug("\tAUDIN_FIFO1_END(0x%04x):   0x%08x\n", AUDIN_FIFO1_END,
+                 aml_read_cbus(AUDIN_FIFO1_END));
+       pr_debug("\tAUDIN_FIFO1_PTR(0x%04x):   0x%08x\n", AUDIN_FIFO1_PTR,
+                 aml_read_cbus(AUDIN_FIFO1_PTR));
+       pr_debug("\tAUDIN_FIFO1_RDPTR(0x%04x): 0x%08x\n", AUDIN_FIFO1_RDPTR,
+                 aml_read_cbus(AUDIN_FIFO1_RDPTR));
+       pr_debug("\tAUDIN_FIFO1_CTRL(0x%04x):  0x%08x\n", AUDIN_FIFO1_CTRL,
+                 aml_read_cbus(AUDIN_FIFO1_CTRL));
+       pr_debug("\tAUDIN_FIFO1_CTRL1(0x%04x): 0x%08x\n", AUDIN_FIFO1_CTRL1,
+                 aml_read_cbus(AUDIN_FIFO1_CTRL1));
+       pr_debug("\tPCMIN_CTRL0(0x%04x):       0x%08x\n", PCMIN_CTRL0,
+                 aml_read_cbus(PCMIN_CTRL0));
+       pr_debug("\tPCMIN_CTRL1(0x%04x):       0x%08x\n", PCMIN_CTRL1,
+                 aml_read_cbus(PCMIN_CTRL1));
+}
+
+void pcm_master_in_enable(struct snd_pcm_substream *substream, int flag)
+{
+       unsigned int dsp_mode = SND_SOC_DAIFMT_DSP_B;
+       unsigned int fs_offset;
+
+       if (dsp_mode == SND_SOC_DAIFMT_DSP_A)
+               fs_offset = 1;
+       else {
+               fs_offset = 0;
+
+               if (dsp_mode != SND_SOC_DAIFMT_DSP_B)
+                       pr_err("Unsupport DSP mode\n");
+       }
+
+       /* reset fifo */
+RESET_FIFO:
+       aml_cbus_update_bits(AUDIN_FIFO1_CTRL, 1 << 1, 1 << 1);
+       aml_write_cbus(AUDIN_FIFO1_PTR, 0);
+       if (aml_read_cbus(AUDIN_FIFO1_PTR) != aml_read_cbus(AUDIN_FIFO1_START))
+               goto RESET_FIFO;
+
+       aml_cbus_update_bits(AUDIN_FIFO1_CTRL, 1 << 1, 0 << 1);
+
+       /* reset pcmin */
+       aml_cbus_update_bits(PCMIN_CTRL0, 1 << 30, 1 << 30);
+       aml_cbus_update_bits(PCMIN_CTRL0, 1 << 30, 0 << 30);
+
+       /* disable fifo */
+       aml_cbus_update_bits(AUDIN_FIFO1_CTRL, 1, 0);
+
+       /* disable pcmin */
+       aml_cbus_update_bits(PCMIN_CTRL0, 1 << 31, 0 << 31);
+
+       if (flag) {
+               unsigned int pcm_mode = 1;
+               unsigned int valid_slot =
+                       valid_channel[substream->runtime->channels - 1];
+
+               switch (substream->runtime->format) {
+               case SNDRV_PCM_FORMAT_S32_LE:
+                       pcm_mode = 3;
+                       break;
+               case SNDRV_PCM_FORMAT_S24_LE:
+                       pcm_mode = 2;
+                       break;
+               case SNDRV_PCM_FORMAT_S16_LE:
+                       pcm_mode = 1;
+                       break;
+               case SNDRV_PCM_FORMAT_S8:
+                       pcm_mode = 0;
+                       break;
+               }
+
+               /* set buffer start ptr end */
+               aml_write_cbus(AUDIN_FIFO1_START, pcmin_buffer_addr);
+               aml_write_cbus(AUDIN_FIFO1_PTR, pcmin_buffer_addr);
+               aml_write_cbus(AUDIN_FIFO1_END,
+                       pcmin_buffer_addr + pcmin_buffer_size - 8);
+
+               /* fifo control */
+               aml_write_cbus(AUDIN_FIFO1_CTRL,
+                       (1 << 15) |    /* urgent request */
+                       (1 << 11) |    /* channel */
+                       (6 << 8) |     /* endian */
+                       (2 << 3) |     /* PCMIN input selection */
+                       (1 << 2) |     /* load address */
+                       (0 << 1) |     /* reset fifo */
+                       (1 << 0)       /* fifo enable */
+               );
+
+               /* fifo control1 */
+               aml_write_cbus(AUDIN_FIFO1_CTRL1,
+                       /* data destination DDR */
+                       (0 << 4) |
+                       /* fifo1 din byte num.  00 : 1 byte. 01: 2 bytes.
+                        * 10: 3 bytes. 11: 4 bytes
+                        */
+                       (pcm_mode << 2) |
+                       /* data position */
+                       (0 << 0)
+               );
+
+               /* pcmin control1 */
+               aml_write_cbus(PCMIN_CTRL1,
+                       /* pcmin SRC sel */
+                       (0 << 29) |
+                       /* pcmin clock sel */
+                       (1 << 28) |
+                       /* using negedge of PCM clock to latch the input data */
+                       (1 << 27) |
+                       /* max slot number in one frame */
+                       (0xF << 21) |
+                       /* data msb 16bits data */
+                       (0xF << 16) |
+                       /* slot valid */
+                       (valid_slot << 0)
+               );
+
+               /* pcmin control0 */
+               aml_write_cbus(PCMIN_CTRL0,
+                       /* pcmin enable */
+                       (1 << 31) |
+                       /* sync on clock posedge */
+                       (1 << 29) |
+                       /* FS SKEW */
+                       (fs_offset << 16) |
+                       /* waithing 1 system clock cycles
+                        * then sample the PCMIN singals
+                        */
+                       (0 << 4) |
+                       /* use clock counter to do the sample */
+                       (0 << 3) |
+                       /* fs inverted. */
+                       (0 << 2) |
+                       /* msb first */
+                       (1 << 1) |
+                       /* left justified */
+                       (1 << 0)
+               );
+
+               if (!pcm_out_is_enable()) {
+                       aml_write_cbus(PCMOUT_CTRL2,
+                               aml_read_cbus(PCMOUT_CTRL2) |
+                               /* pcmo max slot number in one frame*/
+                               (0xF << 22) |
+                               /* pcmo max bit number in one slot*/
+                               (0xF << 16) |
+                               (valid_slot << 0)
+                       );
+                       aml_write_cbus(PCMOUT_CTRL1,
+                               aml_read_cbus(PCMOUT_CTRL1) |
+                               /* use posedge of PCM clock to output data*/
+                               (0 << 28) |
+                               /* invert fs phase */
+                               (1 << 26) |
+                               /* invert the fs_o for master mode */
+                               (1 << 25) |
+                               /* fs_o start postion frame
+                                * slot counter number
+                                */
+                               (0 << 18) |
+                               /*fs_o start postion slot bit counter number*/
+                               (0 << 12) |
+                               /*fs_o end postion frame slot counter number.*/
+                               (0 << 6) |
+                               /* fs_o end postion slot bit counter number.*/
+                               (1 << 0)
+                       );
+                       aml_write_cbus(PCMOUT_CTRL0,
+                               aml_read_cbus(PCMOUT_CTRL0) |
+                               (1 << 31) |     /* enable */
+                               (1 << 29)     /* master */
+                       );
+               }
+       } else {
+               if (!pcm_out_is_enable()) {
+                       /* disable pcmout */
+                       aml_cbus_update_bits(PCMOUT_CTRL0, 1 << 31, 0 << 31);
+               }
+       }
+
+       pr_debug("PCMIN %s\n", flag ? "enable" : "disable");
+       pcm_in_register_show();
+}
+
+
+void pcm_in_enable(int flag)
+{
+       /* reset fifo */
+ RESET_FIFO:
+       aml_cbus_update_bits(AUDIN_FIFO1_CTRL, 1 << 1, 1 << 1);
+       aml_write_cbus(AUDIN_FIFO1_PTR, 0);
+       if (aml_read_cbus(AUDIN_FIFO1_PTR) != aml_read_cbus(AUDIN_FIFO1_START))
+               goto RESET_FIFO;
+       aml_cbus_update_bits(AUDIN_FIFO1_CTRL, 1 << 1, 0 << 1);
+
+       /* reset pcmin */
+       aml_cbus_update_bits(PCMIN_CTRL0, 1 << 30, 1 << 30);
+       aml_cbus_update_bits(PCMIN_CTRL0, 1 << 30, 0 << 30);
+
+       /* disable fifo */
+       aml_cbus_update_bits(AUDIN_FIFO1_CTRL, 1, 0);
+
+       /* disable pcmin */
+       aml_cbus_update_bits(PCMIN_CTRL0, 1 << 31, 0 << 31);
+
+       if (flag) {
+               /* set buffer start ptr end */
+               aml_write_cbus(AUDIN_FIFO1_START, pcmin_buffer_addr);
+               aml_write_cbus(AUDIN_FIFO1_PTR, pcmin_buffer_addr);
+               aml_write_cbus(AUDIN_FIFO1_END,
+                              pcmin_buffer_addr + pcmin_buffer_size - 8);
+
+               /* fifo control */
+               /* urgent request */
+               aml_write_cbus(AUDIN_FIFO1_CTRL, (1 << 15) |
+                              (1 << 11) |      /* channel */
+                              (6 << 8) |       /* endian */
+                              /* (0 << 8) |     // endian */
+                              (2 << 3) |       /* PCMIN input selection */
+                              (1 << 2) |       /* load address */
+                              (0 << 1) |       /* reset fifo */
+                              (1 << 0) /* fifo enable */
+                   );
+
+               /* fifo control1 */
+               /* data destination DDR */
+               aml_write_cbus(AUDIN_FIFO1_CTRL1, (0 << 4) |
+                              (1 << 2) |       /* 16bits */
+                              (0 << 0) /* data position */
+                   );
+
+               /* pcmin control1 */
+               aml_write_cbus(PCMIN_CTRL1, (0 << 29) | /* external chip */
+              (0 << 28) |      /* external chip */
+               /* using negedge of PCM clock to latch the input data */
+              (1 << 27) |
+              (15 << 21) |     /* slot bit msb 16 clocks per slot */
+              (15 << 16) |     /* data msb 16bits data */
+              (1 << 0) /* slot valid */
+                   );
+
+               /* pcmin control0 */
+               aml_write_cbus(PCMIN_CTRL0, (1 << 31) | /* pcmin enable */
+              (1 << 29) |      /* sync on clock posedge */
+              (0 << 16) |      /* FS SKEW */
+                       /* waithing 1 system clock cycles
+                        * then sample the PCMIN singals
+                        */
+              (0 << 4) |
+              (0 << 3) |       /* use clock counter to do the sample */
+              (0 << 2) |       /* fs not inverted. H = left, L = right */
+              (1 << 1) |       /* msb first */
+              (1 << 0));       /* left justified */
+       }
+
+       pr_debug("PCMIN %s\n", flag ? "enable" : "disable");
+       pcm_in_register_show();
+}
+
+void pcm_in_set_buf(unsigned int addr, unsigned int size)
+{
+       pcmin_buffer_addr = addr;
+       pcmin_buffer_size = size;
+
+       pr_debug("PCMIN buffer start: 0x%08x size: 0x%08x\n",
+                 pcmin_buffer_addr, pcmin_buffer_size);
+}
+
+int pcm_in_is_enable(void)
+{
+       int value = aml_read_cbus_bits(PCMIN_CTRL0, 31, 1);
+
+       return value;
+}
+
+unsigned int pcm_in_rd_ptr(void)
+{
+       unsigned int value = aml_read_cbus(AUDIN_FIFO1_RDPTR);
+
+       pr_debug("PCMIN AUDIN_FIFO1_RDPTR: 0x%08x\n", value);
+
+       return value;
+}
+
+unsigned int pcm_in_set_rd_ptr(unsigned int value)
+{
+       unsigned int old = aml_read_cbus(AUDIN_FIFO1_RDPTR);
+
+       aml_write_cbus(AUDIN_FIFO1_RDPTR, value);
+       pr_debug("PCMIN AUDIN_FIFO1_RDPTR: 0x%08x -> 0x%08x\n", old, value);
+
+       return old;
+}
+
+unsigned int pcm_in_wr_ptr(void)
+{
+       unsigned int writing = 0;
+       unsigned int written = 0;
+       unsigned int value = 0;
+
+       writing = aml_read_cbus(AUDIN_FIFO1_PTR);
+
+       aml_write_cbus(AUDIN_FIFO1_PTR, 1);
+       written = aml_read_cbus(AUDIN_FIFO1_PTR);
+       pr_debug("PCMIN AUDIN_FIFO1_PTR: 0x%08x (0x%08x)\n", written, writing);
+
+       /* value = written; */
+       value = written & (~0x07);
+       return value;
+}
+
+unsigned int pcm_in_fifo_int(void)
+{
+       unsigned int value = 0;
+
+       value = aml_read_cbus(AUDIN_FIFO_INT);
+       pr_debug("PCMIN AUDIN_FIFO_INT: 0x%08x\n", value);
+
+       return value;
+}
+
+static void pcm_out_register_show(void)
+{
+       pr_debug("PCMOUT registers show:\n");
+       pr_debug("\tAUDOUT_BUF0_STA(0x%04x):  0x%08x\n", AUDOUT_BUF0_STA,
+                 aml_read_cbus(AUDOUT_BUF0_STA));
+       pr_debug("\tAUDOUT_BUF0_EDA(0x%04x):  0x%08x\n", AUDOUT_BUF0_EDA,
+                 aml_read_cbus(AUDOUT_BUF0_EDA));
+       pr_debug("\tAUDOUT_BUF0_WPTR(0x%04x): 0x%08x\n", AUDOUT_BUF0_WPTR,
+                 aml_read_cbus(AUDOUT_BUF0_WPTR));
+       pr_debug("\tAUDOUT_FIFO_RPTR(0x%04x): 0x%08x\n", AUDOUT_FIFO_RPTR,
+                 aml_read_cbus(AUDOUT_FIFO_RPTR));
+       pr_debug("\tAUDOUT_CTRL(0x%04x):      0x%08x\n", AUDOUT_CTRL,
+                 aml_read_cbus(AUDOUT_CTRL));
+       pr_debug("\tAUDOUT_CTRL1(0x%04x):     0x%08x\n", AUDOUT_CTRL1,
+                 aml_read_cbus(AUDOUT_CTRL1));
+       pr_debug("\tPCMOUT_CTRL0(0x%04x):     0x%08x\n", PCMOUT_CTRL0,
+                 aml_read_cbus(PCMOUT_CTRL0));
+       pr_debug("\tPCMOUT_CTRL1(0x%04x):     0x%08x\n", PCMOUT_CTRL1,
+                 aml_read_cbus(PCMOUT_CTRL1));
+       pr_debug("\tPCMOUT_CTRL2(0x%04x):     0x%08x\n", PCMOUT_CTRL2,
+                 aml_read_cbus(PCMOUT_CTRL2));
+       pr_debug("\tPCMOUT_CTRL3(0x%04x):     0x%08x\n", PCMOUT_CTRL3,
+                 aml_read_cbus(PCMOUT_CTRL3));
+}
+
+void pcm_master_out_enable(struct snd_pcm_substream *substream, int flag)
+{
+       unsigned int pcm_mode = 1;
+       unsigned int valid_slot =
+               valid_channel[substream->runtime->channels - 1];
+       unsigned int dsp_mode = SND_SOC_DAIFMT_DSP_B;
+       unsigned int bit_offset_s, slot_offset_s, bit_offset_e, slot_offset_e;
+
+       if (dsp_mode == SND_SOC_DAIFMT_DSP_A) {
+               bit_offset_s = 0xF;
+               slot_offset_s = 0xF;
+               bit_offset_e = 0;
+               slot_offset_e = 0;
+       } else {
+               if (dsp_mode != SND_SOC_DAIFMT_DSP_B)
+                       pr_err("Unsupport DSP mode\n");
+
+               bit_offset_s = 0;
+               slot_offset_s = 0;
+               bit_offset_e = 0;
+               slot_offset_e = 1;
+       }
+
+       switch (substream->runtime->format) {
+       case SNDRV_PCM_FORMAT_S32_LE:
+               pcm_mode = 3;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               pcm_mode = 2;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               pcm_mode = 1;
+               break;
+       case SNDRV_PCM_FORMAT_S8:
+               pcm_mode = 0;
+               break;
+       }
+
+       /* reset fifo */
+       aml_cbus_update_bits(AUDOUT_CTRL, 1 << 30, 1 << 30);
+       aml_cbus_update_bits(AUDOUT_CTRL, 1 << 30, 1 << 30);
+       /* disable fifo */
+       aml_cbus_update_bits(AUDOUT_CTRL, 1 << 31, 0 << 31);
+
+       if (!pcm_in_is_enable()) {
+               /* reset pcmout */
+               aml_cbus_update_bits(PCMOUT_CTRL0, 1 << 30, 1 << 30);
+               aml_cbus_update_bits(PCMOUT_CTRL0, 1 << 30, 0 << 30);
+               /* disable pcmout */
+               aml_cbus_update_bits(PCMOUT_CTRL0, 1 << 31, 0 << 31);
+       }
+
+       if (flag) {
+               /* set buffer start ptr end */
+               aml_write_cbus(AUDOUT_BUF0_STA, pcmout_buffer_addr);
+               aml_write_cbus(AUDOUT_BUF0_WPTR, pcmout_buffer_addr);
+               aml_write_cbus(AUDOUT_BUF0_EDA,
+                       pcmout_buffer_addr + pcmout_buffer_size - 8);
+
+               /* fifo control */
+               aml_write_cbus(AUDOUT_CTRL, (0 << 31) |     /* fifo enable */
+                       (0 << 30) |     /* soft reset */
+                       (1 << 29) |     /* load address */
+                       /* use cbus AUDOUT BUFFER0 write pointer
+                        * as the AUDOUT FIFO write pointer
+                        */
+                       (0 << 22) |
+                       (52 << 15) |    /* data request size */
+                       (64 << 8) |     /* buffer level to keep */
+                       (0 << 7) |      /* buffer level control */
+                       (1 << 6) |      /* DMA mode */
+                       (1 << 5) |      /* circular buffer */
+                       (0 << 4) |      /* use register set 0 always */
+                       (1 << 3) |      /* urgent request */
+                       (6 << 0)        /* endian */
+               );
+
+               aml_write_cbus(AUDOUT_CTRL, (1 << 31) |/* fifo enable */
+                       (0 << 30) |     /* soft reset */
+                       (1 << 29) |     /* load address */
+                       /* use cbus AUDOUT BUFFER0 write pointer
+                        * as the AUDOUT FIFO write pointer
+                        */
+                       (1 << 22) |
+                       (56 << 15) |    /* data request size */
+                       (64 << 8) |     /* buffer level to keep */
+                       (1 << 7) |      /* buffer level control */
+                       (1 << 6) |      /* DMA mode */
+                       (1 << 5) |      /* circular buffer */
+                       (0 << 4) |      /* use register set 0 always */
+                       (1 << 3) |       /* urgent request */
+                       (6 << 0)         /* endian */
+               );
+
+               /* pcmout control3 */
+               aml_write_cbus(PCMOUT_CTRL3, 0); /* mute constant */
+
+               /* pcmout control2 */
+               /* FS * 16 * 16 = BCLK */
+               aml_write_cbus(PCMOUT_CTRL2,
+                       /* underrun use mute constant */
+                       (0 << 29) |
+                       /* pcmo max slot number in one frame */
+                       (0xF << 22) |
+                       /* pcmo max bit number in one slot */
+                       (0xF << 16) |
+                       /* pcmo valid slot. each bit for one slot */
+                       (valid_slot << 0)
+               );
+
+               /* pcmout control1 */
+               aml_write_cbus(PCMOUT_CTRL1,
+                       /* pcmo output data byte number.  00 : 8bits.
+                        * 01: 16bits. 10: 24bits. 11: 32bits
+                        */
+                       (pcm_mode << 30) |
+                       /* use posedge of PCM clock to output data */
+                       (0 << 28) |
+                       /* pcmo slave parts clock invert */
+                       (0 << 27) |
+                       /* invert fs phase */
+                       (1 << 26) |
+                       /* invert the fs_o for master mode */
+                       (1 << 25) |
+                       /* fs_o start postion frame slot counter number */
+                       (bit_offset_s << 18) |
+                       /* fs_o start postion slot bit counter number.*/
+                       (slot_offset_s << 12) |
+                       /* fs_o end postion frame slot counter number. */
+                       (bit_offset_e << 6) |
+                       /* fs_o end postion slot bit counter number. */
+                       (slot_offset_e << 0)
+               );
+
+               /* pcmout control0 */
+               aml_write_cbus(PCMOUT_CTRL0,
+                       (1 << 31) |     /* enable */
+                       (1 << 29) |     /* master */
+                       (1 << 28) |     /* sync on clock rising edge */
+                       /* system clock sync at clock edge of pcmout clock.
+                        * 0 = sync on clock counter.
+                        */
+                       (0 << 27) |
+                       /* system clock sync at counter number
+                        * if sync on clock counter
+                        */
+                       (0 << 15) |
+                       (1 << 14) |     /* msb first */
+                       (1 << 13) |     /* left justified */
+                       (0 << 12) |     /* data position */
+                       /*slave mode, sync fs with the slot bit counter.*/
+                       (0 << 6) |
+                       /*slave mode, sync fs with frame slot counter.*/
+                       (0 << 0)
+               );
+       }
+
+       pr_debug("PCMOUT %s\n", flag ? "enable" : "disable");
+       pcm_out_register_show();
+}
+
+void pcm_out_enable(int flag)
+{
+       /* reset fifo */
+       aml_cbus_update_bits(AUDOUT_CTRL, 1 << 30, 1 << 30);
+       aml_cbus_update_bits(AUDOUT_CTRL, 1 << 30, 1 << 30);
+
+       /* reset pcmout */
+       aml_cbus_update_bits(PCMOUT_CTRL0, 1 << 30, 1 << 30);
+       aml_cbus_update_bits(PCMOUT_CTRL0, 1 << 30, 0 << 30);
+
+       /* disable fifo */
+       aml_cbus_update_bits(AUDOUT_CTRL, 1 << 31, 0 << 31);
+
+       /* disable pcmout */
+       aml_cbus_update_bits(PCMOUT_CTRL0, 1 << 31, 0 << 31);
+
+       if (flag) {
+               /* set buffer start ptr end */
+               aml_write_cbus(AUDOUT_BUF0_STA, pcmout_buffer_addr);
+               aml_write_cbus(AUDOUT_BUF0_WPTR, pcmout_buffer_addr);
+               aml_write_cbus(AUDOUT_BUF0_EDA,
+                              pcmout_buffer_addr + pcmout_buffer_size - 8);
+
+               /* fifo control */
+               aml_write_cbus(AUDOUT_CTRL, (0 << 31) | /* fifo enable */
+              (0 << 30) |      /* soft reset */
+              (1 << 29) |      /* load address */
+                       /* use cbus AUDOUT BUFFER0 write pointer
+                        * as the AUDOUT FIFO write pointer
+                        */
+              (0 << 22) |
+              (52 << 15) |     /* data request size */
+              (64 << 8) |      /* buffer level to keep */
+              (0 << 7) |       /* buffer level control */
+              (1 << 6) |       /* DMA mode */
+              (1 << 5) |       /* circular buffer */
+              (0 << 4) |       /* use register set 0 always */
+              (1 << 3) |       /* urgent request */
+              (6 << 0));       /* endian */
+
+               aml_write_cbus(AUDOUT_CTRL, (1 << 31) | /* fifo enable */
+              (0 << 30) |      /* soft reset */
+              (0 << 29) |      /* load address */
+                       /* use cbus AUDOUT BUFFER0 write pointer
+                        * as the AUDOUT FIFO write pointer
+                        */
+                       (0 << 22) |
+                       (52 << 15) |    /* data request size */
+                       (64 << 8) |     /* buffer level to keep */
+                       (0 << 7) |      /* buffer level control */
+                       (1 << 6) |      /* DMA mode */
+                       (1 << 5) |      /* circular buffer */
+                       (0 << 4) |      /* use register set 0 always */
+                       (1 << 3) |      /* urgent request */
+                       (6 << 0));      /* endian */
+
+               /* pcmout control3 */
+               aml_write_cbus(PCMOUT_CTRL3, 0);        /* mute constant */
+
+               /* pcmout control2 */
+               /* 1 channel per frame */
+               aml_write_cbus(PCMOUT_CTRL2, (0 << 29) | (0 << 22) |
+                              (15 << 16) |     /* 16 bits per slot */
+                              (1 << 0) /* enable 1 slot */
+                   );
+
+               /* pcmout control1 */
+               /* use posedge of PCM clock to output data */
+               aml_write_cbus(PCMOUT_CTRL1, (1 << 30) | (0 << 28) |
+                       /* use negedge of pcm clock to check the fs */
+                       (1 << 27));
+
+               /* pcmout control0 */
+               /* slave */
+               aml_write_cbus(PCMOUT_CTRL0, (1 << 31) | (0 << 29) |
+                       /* sync on clock rising edge */
+                       (1 << 28) |
+                       /* data sample mode */
+                       (0 << 27) |
+                       /* sync on 4 system clock later ? */
+                       (1 << 15) |
+                       /* msb first */
+                       (1 << 14) |
+                       /* left justified */
+                       (1 << 13) |
+                       /* data position */
+                       (0 << 12) |
+                       /* sync fs with the slot bit counter. */
+                       (3 << 6) |
+                       /* sync fs with frame slot counter. */
+                       (0 << 0));
+       }
+
+       pr_debug("PCMOUT %s\n", flag ? "enable" : "disable");
+       pcm_out_register_show();
+}
+
+void pcm_out_mute(int flag)
+{
+       int value = flag ? 1 : 0;
+
+       aml_cbus_update_bits(PCMOUT_CTRL2, 1 << 31, value << 31);
+}
+
+void pcm_out_set_buf(unsigned int addr, unsigned int size)
+{
+       pcmout_buffer_addr = addr;
+       pcmout_buffer_size = size;
+
+       pr_debug("PCMOUT buffer addr: 0x%08x end: 0x%08x\n",
+                 pcmout_buffer_addr, pcmout_buffer_size);
+}
+
+int pcm_out_is_enable(void)
+{
+       int value = aml_read_cbus_bits(PCMOUT_CTRL0, 31, 1);
+
+       return value;
+}
+
+int pcm_out_is_mute(void)
+{
+       int value = aml_read_cbus_bits(PCMOUT_CTRL2, 31, 1);
+
+       return value;
+}
+
+unsigned int pcm_out_rd_ptr(void)
+{
+       unsigned int value = aml_read_cbus(AUDOUT_FIFO_RPTR);
+
+       pr_debug("PCMOUT read pointer: 0x%08x\n", value);
+
+       return value;
+}
+
+unsigned int pcm_out_wr_ptr(void)
+{
+       unsigned int value = 0;
+
+       value = aml_read_cbus(AUDOUT_BUF0_WPTR);
+       pr_debug("PCMOUT write pointer: 0x%08x\n", value);
+       return value;
+}
+
+unsigned int pcm_out_set_wr_ptr(unsigned int value)
+{
+       unsigned int old = aml_read_cbus(AUDOUT_BUF0_WPTR);
+
+       aml_write_cbus(AUDOUT_BUF0_WPTR, value);
+       pr_debug("PCMOUT write pointer: 0x%08x -> 0x%08x\n", old, value);
+
+       return old;
+}
diff --git a/sound/soc/amlogic/aml_audio_hw_pcm.h b/sound/soc/amlogic/aml_audio_hw_pcm.h
new file mode 100644 (file)
index 0000000..a7dbc30
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * sound/soc/amlogic/aml_audio_hw_pcm.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AML_PCM_HW_H__
+#define __AML_PCM_HW_H__
+
+#include "sound/asound.h"
+#include <sound/pcm.h>
+
+void pcm_in_enable(int flag);
+void pcm_in_set_buf(unsigned int addr, unsigned int size);
+int  pcm_in_is_enable(void);
+unsigned int pcm_in_rd_ptr(void);
+unsigned int pcm_in_wr_ptr(void);
+unsigned int pcm_in_set_rd_ptr(unsigned int value);
+unsigned int pcm_in_fifo_int(void);
+
+void pcm_out_enable(int flag);
+void pcm_out_mute(int flag);
+void pcm_out_set_buf(unsigned int addr, unsigned int size);
+int  pcm_out_is_enable(void);
+int  pcm_out_is_mute(void);
+unsigned int pcm_out_rd_ptr(void);
+unsigned int pcm_out_wr_ptr(void);
+unsigned int pcm_out_set_wr_ptr(unsigned int value);
+
+void pcm_master_in_enable(struct snd_pcm_substream *substream, int flag);
+void pcm_master_out_enable(struct snd_pcm_substream *substream, int flag);
+#endif
diff --git a/sound/soc/amlogic/aml_dmic.c b/sound/soc/amlogic/aml_dmic.c
new file mode 100644 (file)
index 0000000..dfb2f63
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * sound/soc/amlogic/aml_dmic.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <linux/reset.h>
+#include <linux/pinctrl/consumer.h>
+
+#define DRV_NAME "aml_snd_dmic"
+
+#define PDM_CTRL               0x40
+/* process_header_copy_only_on */
+#define CIC_DEC8OR16_SEL        2
+#define PDM_HPF_BYPASS          1
+#define PDM_ENABLE              0
+/* process_header_copy_only_off */
+#define PDM_IN_CTRL            0x42
+/* process_header_copy_only_on */
+#define PDMIN_REV              18
+#define PDMCLK_REV             17
+#define GET_STA_EN             16
+#define SAMPLE_CNT             8
+/* process_header_copy_only_off */
+#define PDM_VOL_CTRL   0x43
+/* process_header_copy_only_on */
+#define PDML_INV             1
+#define PDMR_INV             0
+/* process_header_copy_only_off */
+#define PDM_VOL_GAIN_L 0x44
+#define PDM_VOL_GAIN_R 0x45
+#define PDM_STATUS             0x50
+
+struct aml_dmic_priv {
+       void __iomem *pdm_base;
+       struct pinctrl *dmic_pins;
+       struct clk *clk_pdm;
+       struct clk *clk_mclk;
+};
+
+static int aml_dmic_codec_probe(struct snd_soc_codec *codec)
+{
+       return 0;
+}
+
+static struct snd_soc_dai_driver aml_dmic_dai = {
+       .name = "dmic-hifi",
+       .capture = {
+               .stream_name = "dmic Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+               .formats = SNDRV_PCM_FMTBIT_S32_LE
+                       | SNDRV_PCM_FMTBIT_S24_LE
+                       | SNDRV_PCM_FMTBIT_S16_LE,
+       },
+};
+
+static const struct snd_soc_dapm_widget aml_dmic_dapm_widgets[] = {
+       SND_SOC_DAPM_AIF_OUT("DMIC AIFIN", "dmic Capture", 0,
+                            SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_INPUT("DMIC IN"),
+};
+
+static const struct snd_soc_dapm_route dmic_intercon[] = {
+       {"DMIC AIFIN", NULL, "DMIC IN"},
+};
+
+static const struct snd_soc_codec_driver aml_dmic = {
+       .probe = aml_dmic_codec_probe,
+       .component_driver = {
+               .dapm_widgets = aml_dmic_dapm_widgets,
+               .num_dapm_widgets = ARRAY_SIZE(aml_dmic_dapm_widgets),
+               .dapm_routes = dmic_intercon,
+               .num_dapm_routes = ARRAY_SIZE(dmic_intercon),
+       }
+};
+
+static const char *const gate_names[] = {
+       "pdm",
+};
+
+static int aml_dmic_platform_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct clk *clk_gate;
+       struct aml_dmic_priv *dmic_priv;
+       unsigned int val;
+       int ret;
+
+       dev_info(&pdev->dev, "Dmic probe!\n");
+
+       clk_gate = devm_clk_get(&pdev->dev, gate_names[0]);
+       if (IS_ERR(clk_gate)) {
+               dev_err(&pdev->dev, "Can't get aml dmic gate\n");
+               return PTR_ERR(clk_gate);
+       }
+       clk_prepare_enable(clk_gate);
+
+       dmic_priv = devm_kzalloc(&pdev->dev,
+               sizeof(struct aml_dmic_priv), GFP_KERNEL);
+       if (dmic_priv == NULL)
+               return -ENOMEM;
+
+       dmic_priv->dmic_pins =
+               devm_pinctrl_get_select(&pdev->dev, "aml_dmic_pins");
+       if (IS_ERR(dmic_priv->dmic_pins)) {
+               dev_err(&pdev->dev, "pinctrls error!\n");
+               return -EINVAL;
+       }
+
+       dev_set_drvdata(&pdev->dev, dmic_priv);
+       dmic_priv->clk_mclk = devm_clk_get(&pdev->dev, "mclk");
+       if (IS_ERR(dmic_priv->clk_mclk)) {
+               dev_err(&pdev->dev, "Can't retrieve clk_mclk clock\n");
+               ret = PTR_ERR(dmic_priv->clk_mclk);
+               goto err;
+       }
+
+       dmic_priv->clk_pdm = devm_clk_get(&pdev->dev, "pdm");
+       if (IS_ERR(dmic_priv->clk_pdm)) {
+               dev_err(&pdev->dev, "Can't retrieve clk_pdm clock\n");
+               ret = PTR_ERR(dmic_priv->clk_pdm);
+               goto err;
+       }
+
+       ret = clk_set_parent(dmic_priv->clk_pdm, dmic_priv->clk_mclk);
+       if (ret) {
+               pr_err("Can't set dmic pdm clk parent err: %d\n", ret);
+               goto err;
+       }
+
+       ret = clk_set_rate(dmic_priv->clk_pdm,
+               clk_get_rate(dmic_priv->clk_mclk)/4);
+       if (ret) {
+               pr_err("Can't set dmic pdm clock rate, err: %d\n", ret);
+               goto err;
+       }
+
+       ret = clk_prepare_enable(dmic_priv->clk_pdm);
+       if (ret) {
+               pr_err("Can't enable dmic pdm clock: %d\n", ret);
+               goto err;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       dmic_priv->pdm_base = devm_ioremap_nocache(&pdev->dev, res->start,
+                                              resource_size(res));
+       if (IS_ERR(dmic_priv->pdm_base)) {
+               dev_err(&pdev->dev, "Unable to map pdm_base\n");
+               return PTR_ERR(dmic_priv->pdm_base);
+       }
+
+       writel(0x100000, dmic_priv->pdm_base + (PDM_VOL_GAIN_L<<2));
+       writel(0x100000, dmic_priv->pdm_base + (PDM_VOL_GAIN_R<<2));
+
+       val = readl(dmic_priv->pdm_base + (PDM_CTRL<<2));
+       writel(1, dmic_priv->pdm_base + (PDM_CTRL<<2));
+       val = readl(dmic_priv->pdm_base + (PDM_CTRL<<2));
+
+       return snd_soc_register_codec(&pdev->dev,
+                       &aml_dmic, &aml_dmic_dai, 1);
+err:
+       return ret;
+}
+
+static int aml_dmic_platform_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id amlogic_dmic_of_match[] = {
+       {.compatible = "aml, aml_snd_dmic"},
+       {}
+};
+
+static struct platform_driver aml_dmic_driver = {
+       .driver = {
+                  .name = DRV_NAME,
+                  .owner = THIS_MODULE,
+                  .of_match_table = amlogic_dmic_of_match,
+                  },
+       .probe = aml_dmic_platform_probe,
+       .remove = aml_dmic_platform_remove,
+};
+
+module_platform_driver(aml_dmic_driver);
+
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_DESCRIPTION("amlogic digital mic driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amlogic/aml_i2s.c b/sound/soc/amlogic/aml_i2s.c
new file mode 100644 (file)
index 0000000..33f2cb2
--- /dev/null
@@ -0,0 +1,1281 @@
+/*
+ * sound/soc/amlogic/aml_i2s.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/soundcard.h>
+#include <linux/timer.h>
+#include <linux/hrtimer.h>
+#include <linux/debugfs.h>
+#include <linux/major.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+/* Amlogic headers */
+#include <linux/amlogic/iomap.h>
+#include "aml_i2s.h"
+#include "aml_spdif_dai.h"
+#include "aml_audio_hw.h"
+#include <linux/amlogic/media/sound/aiu_regs.h>
+#include <linux/amlogic/media/sound/audin_regs.h>
+
+/*
+ * timer for i2s data update, hw timer, hrtimer, timer
+ * once select only one way to update
+ */
+/*#define USE_HW_TIMER*/
+/*#define USE_HRTIMER*/
+#ifdef USE_HW_TIMER
+#define XRUN_NUM 100 /*1ms*100=100ms timeout*/
+#else
+#define XRUN_NUM 10 /*10ms*10=100ms timeout*/
+#endif
+
+unsigned long aml_i2s_playback_start_addr;
+EXPORT_SYMBOL(aml_i2s_playback_start_addr);
+
+unsigned long aml_i2s_playback_phy_start_addr;
+EXPORT_SYMBOL(aml_i2s_playback_phy_start_addr);
+
+unsigned long aml_i2s_alsa_write_addr;
+EXPORT_SYMBOL(aml_i2s_alsa_write_addr);
+
+unsigned int aml_i2s_playback_channel = 2;
+EXPORT_SYMBOL(aml_i2s_playback_channel);
+
+unsigned int aml_i2s_playback_format = 16;
+EXPORT_SYMBOL(aml_i2s_playback_format);
+
+static int trigger_underrun;
+void aml_audio_hw_trigger(void)
+{
+       trigger_underrun = 1;
+}
+EXPORT_SYMBOL(aml_audio_hw_trigger);
+
+#ifndef USE_HRTIMER
+static void aml_i2s_timer_callback(unsigned long data);
+#endif
+
+/*--------------------------------------------------------------------------*
+ * Hardware definition
+ *--------------------------------------------------------------------------*
+ * TODO: These values were taken from the AML platform driver, check
+ *      them against real values for AML
+ */
+static const struct snd_pcm_hardware aml_i2s_hardware = {
+       .info =
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE_MMAP
+       SNDRV_PCM_INFO_MMAP |
+       SNDRV_PCM_INFO_MMAP_VALID |
+#endif
+       SNDRV_PCM_INFO_INTERLEAVED |
+           SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE,
+       .formats =
+           SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+           SNDRV_PCM_FMTBIT_S32_LE,
+
+       .period_bytes_min = 64,
+       .period_bytes_max = 32 * 1024 * 2,
+       .periods_min = 2,
+       .periods_max = 1024,
+       .buffer_bytes_max = 128 * 1024 * 2 * 2,
+
+       .rate_min = 8000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 8,
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE_MMAP
+       .fifo_size = 4,
+#else
+       .fifo_size = 0,
+#endif
+};
+
+static const struct snd_pcm_hardware aml_i2s_capture = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+           SNDRV_PCM_INFO_BLOCK_TRANSFER |
+           SNDRV_PCM_INFO_MMAP |
+           SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE,
+
+       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+           SNDRV_PCM_FMTBIT_S32_LE,
+       .period_bytes_min = 64,
+       .period_bytes_max = 32 * 1024,
+       .periods_min = 2,
+       .periods_max = 1024,
+       .buffer_bytes_max = 64 * 1024,
+
+       .rate_min = 8000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 8,
+       .fifo_size = 0,
+};
+
+static unsigned int period_sizes[] = {
+       64, 128, 256, 512, 1024, 2048, 4096, 8192,
+       16384, 32768, 65536, 65536 * 2, 65536 * 4
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
+       .count = ARRAY_SIZE(period_sizes),
+       .list = period_sizes,
+       .mask = 0
+};
+
+/*--------------------------------------------------------------------------
+ *--------------------------------------------------------------------------
+ * Helper functions
+ *--------------------------------------------------------------------------
+ */
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE_MMAP
+static int aml_i2s_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       size_t size = aml_i2s_hardware.buffer_bytes_max;
+
+       buf->dev.type = SNDRV_DMA_TYPE_DEV;
+       buf->dev.dev = pcm->card->dev;
+       buf->private_data = NULL;
+
+       buf->area = dmam_alloc_coherent(pcm->card->dev, size,
+                       &buf->addr, GFP_KERNEL);
+       dev_info(pcm->card->dev, "aml-pcm %d: playback preallocate_dma_buffer: area=%p, addr=%p, size=%ld\n",
+               stream, (void *) buf->area, (void *) buf->addr, size);
+       if (!buf->area)
+               return -ENOMEM;
+
+       buf->bytes = size;
+       return 0;
+}
+#else
+static int aml_i2s_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       struct aml_audio_buffer *tmp_buf = NULL;
+       size_t size = 0;
+
+       tmp_buf = kzalloc(sizeof(struct aml_audio_buffer), GFP_KERNEL);
+       if (tmp_buf == NULL) {
+               /*dev_err(&pcm->card->dev, "allocate tmp buffer error\n");*/
+               return -ENOMEM;
+       }
+       buf->private_data = tmp_buf;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* malloc DMA buffer */
+               size = aml_i2s_hardware.buffer_bytes_max;
+               buf->dev.type = SNDRV_DMA_TYPE_DEV;
+               buf->dev.dev = pcm->card->dev;
+               /* one size for i2s output, another for 958,
+                *and 128 for alignment
+                */
+               buf->area = dma_alloc_coherent(pcm->card->dev, size + 4096,
+                                              &buf->addr, GFP_KERNEL);
+               if (!buf->area) {
+                       dev_err(pcm->card->dev, "alloc playback DMA buffer error\n");
+                       kfree(tmp_buf);
+                       buf->private_data = NULL;
+                       return -ENOMEM;
+               }
+               buf->bytes = size;
+               /* malloc tmp buffer */
+               size = aml_i2s_hardware.buffer_bytes_max;
+               tmp_buf->buffer_start = kzalloc(size, GFP_KERNEL);
+               if (tmp_buf->buffer_start == NULL) {
+                       dev_err(pcm->card->dev, "alloc playback tmp buffer error\n");
+                       kfree(tmp_buf);
+                       buf->private_data = NULL;
+                       return -ENOMEM;
+               }
+               tmp_buf->buffer_size = size;
+
+       } else {
+               /* malloc DMA buffer */
+               size = aml_i2s_capture.buffer_bytes_max;
+               buf->dev.type = SNDRV_DMA_TYPE_DEV;
+               buf->dev.dev = pcm->card->dev;
+               buf->area = dma_alloc_coherent(pcm->card->dev, size * 2,
+                                              &buf->addr, GFP_KERNEL);
+
+               if (!buf->area) {
+                       dev_err(pcm->card->dev, "alloc capture DMA buffer error\n");
+                       kfree(tmp_buf);
+                       buf->private_data = NULL;
+                       return -ENOMEM;
+               }
+               buf->bytes = size;
+               /* malloc tmp buffer */
+               size = aml_i2s_capture.period_bytes_max;
+               tmp_buf->buffer_start = kzalloc(size, GFP_KERNEL);
+               if (tmp_buf->buffer_start == NULL) {
+                       dev_err(pcm->card->dev, "alloc capture tmp buffer error\n");
+                       kfree(tmp_buf);
+                       buf->private_data = NULL;
+                       return -ENOMEM;
+               }
+               tmp_buf->buffer_size = size;
+       }
+
+       return 0;
+
+}
+#endif
+/*--------------------------------------------------------------------------
+ * ISR
+ *--------------------------------------------------------------------------
+ *--------------------------------------------------------------------------
+ * i2s operations
+ *--------------------------------------------------------------------------
+ */
+static int aml_i2s_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_runtime_data *prtd = runtime->private_data;
+       struct audio_stream *s = &prtd->s;
+
+       /*
+        * this may get called several times by oss emulation
+        * with different params
+        */
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+       runtime->dma_bytes = params_buffer_bytes(params);
+       s->I2S_addr = runtime->dma_addr;
+
+       /*
+        * Both capture and playback need to reset the last ptr
+        * to the start address, playback and capture use
+        * different address calculate, so we reset the different
+        * start address to the last ptr
+        */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* s->last_ptr must initialized as dma buffer's start addr */
+               s->last_ptr = runtime->dma_addr;
+       } else {
+
+               s->last_ptr = 0;
+       }
+
+       return 0;
+}
+
+static int aml_i2s_hw_free(struct snd_pcm_substream *substream)
+{
+       return 0;
+}
+
+static int aml_i2s_prepare(struct snd_pcm_substream *substream)
+{
+
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_runtime_data *prtd = runtime->private_data;
+       struct audio_stream *s = &prtd->s;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       struct aml_audio_buffer *tmp_buf = buf->private_data;
+
+       if (s && s->device_type == AML_AUDIO_I2SOUT && trigger_underrun) {
+               dev_info(substream->pcm->card->dev, "clear i2s out trigger underrun\n");
+               trigger_underrun = 0;
+       }
+       if (s && s->device_type == AML_AUDIO_I2SOUT) {
+               aml_i2s_playback_channel = runtime->channels;
+               if (runtime->format == SNDRV_PCM_FORMAT_S16_LE)
+                       aml_i2s_playback_format = 16;
+               else if (runtime->format == SNDRV_PCM_FORMAT_S32_LE)
+                       aml_i2s_playback_format = 32;
+               else if (runtime->format == SNDRV_PCM_FORMAT_S24_LE)
+                       aml_i2s_playback_format = 24;
+       }
+       tmp_buf->cached_len = 0;
+       return 0;
+}
+
+#ifdef USE_HW_TIMER
+int hw_timer_init;
+static irqreturn_t audio_isr_handler(int irq, void *data)
+{
+       struct aml_runtime_data *prtd = data;
+       struct snd_pcm_substream *substream = prtd->substream;
+
+       aml_i2s_timer_callback((unsigned long)substream);
+       return IRQ_HANDLED;
+}
+
+static int snd_free_hw_timer_irq(void *data)
+{
+       free_irq(INT_TIMER_D, data);
+       return 0;
+}
+
+static int snd_request_hw_timer(void *data)
+{
+       int ret = 0;
+
+       if (hw_timer_init == 0) {
+               aml_write_cbus(ISA_TIMERD, TIMER_COUNT);
+               aml_cbus_update_bits(ISA_TIMER_MUX, 3 << 6,
+                                       TIMERD_RESOLUTION << 6);
+               aml_cbus_update_bits(ISA_TIMER_MUX, 1 << 15, TIMERD_MODE << 15);
+               aml_cbus_update_bits(ISA_TIMER_MUX, 1 << 19, 1 << 19);
+               hw_timer_init = 1;
+       }
+       ret = request_irq(INT_TIMER_D, audio_isr_handler,
+                               IRQF_SHARED, "timerd_irq", data);
+               if (ret < 0) {
+                       pr_err("audio hw interrupt register fail\n");
+                       return -1;
+               }
+       return 0;
+}
+#endif
+
+#ifdef USE_HRTIMER
+static void aml_i2s_hrtimer_set_rate(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_runtime_data *prtd = runtime->private_data;
+
+       prtd->wakeups_per_second = ktime_set(0, 1000000000 / (runtime->rate));
+}
+
+static enum hrtimer_restart aml_i2s_hrtimer_callback(struct hrtimer *timer)
+{
+       struct aml_runtime_data *prtd =  container_of(timer,
+                                       struct aml_runtime_data, hrtimer);
+       struct snd_pcm_substream *substream = prtd->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct audio_stream *s = &prtd->s;
+       unsigned int last_ptr, size;
+       unsigned long flags = 0;
+
+       if (prtd->active == 0) {
+               hrtimer_forward_now(timer, prtd->wakeups_per_second);
+               return HRTIMER_RESTART;
+       }
+
+       spin_lock_irqsave(&prtd->timer_lock, flags);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               last_ptr = read_i2s_rd_ptr();
+               if (last_ptr < s->last_ptr)
+                       size = runtime->dma_bytes + last_ptr - s->last_ptr;
+               else
+                       size = last_ptr - s->last_ptr;
+               s->last_ptr = last_ptr;
+               s->size += bytes_to_frames(substream->runtime, size);
+               if (s->size >= runtime->period_size) {
+                       s->size %= runtime->period_size;
+                       snd_pcm_period_elapsed(substream);
+               }
+       } else {
+               last_ptr = (audio_in_i2s_wr_ptr() - s->I2S_addr) / 2;
+               if (last_ptr < s->last_ptr)
+                       size = runtime->dma_bytes + last_ptr - s->last_ptr;
+               else
+                       size = last_ptr - s->last_ptr;
+
+               s->last_ptr = last_ptr;
+               s->size += bytes_to_frames(runtime, size);
+               if (s->size >= runtime->period_size) {
+                       s->size %= runtime->period_size;
+                       snd_pcm_period_elapsed(substream);
+               }
+       }
+       spin_unlock_irqrestore(&prtd->timer_lock, flags);
+       hrtimer_forward_now(timer, prtd->wakeups_per_second);
+
+       return HRTIMER_RESTART;
+}
+
+#endif
+
+static void start_timer(struct aml_runtime_data *prtd)
+{
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&prtd->timer_lock, flags);
+       if (!prtd->active) {
+#ifndef USE_HW_TIMER
+#ifdef USE_HRTIMER
+               hrtimer_start(&prtd->hrtimer, prtd->wakeups_per_second,
+                                 HRTIMER_MODE_REL);
+#else
+               prtd->timer.expires = jiffies + 1;
+               add_timer(&prtd->timer);
+#endif
+#endif
+               prtd->active = 1;
+               prtd->xrun_num = 0;
+       }
+       spin_unlock_irqrestore(&prtd->timer_lock, flags);
+
+}
+
+static void stop_timer(struct aml_runtime_data *prtd)
+{
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&prtd->timer_lock, flags);
+       if (prtd->active) {
+#ifndef USE_HW_TIMER
+#ifdef USE_HRTIMER
+               hrtimer_cancel(&prtd->hrtimer);
+#else
+               del_timer(&prtd->timer);
+#endif
+#endif
+               prtd->active = 0;
+               prtd->xrun_num = 0;
+       }
+       spin_unlock_irqrestore(&prtd->timer_lock, flags);
+}
+
+
+static int aml_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *rtd = substream->runtime;
+       struct aml_runtime_data *prtd = rtd->private_data;
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+#ifdef USE_HRTIMER
+               aml_i2s_hrtimer_set_rate(substream);
+#endif
+               start_timer(prtd);
+               break;          /* SNDRV_PCM_TRIGGER_START */
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_STOP:
+               stop_timer(prtd);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t aml_i2s_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_runtime_data *prtd = runtime->private_data;
+       struct audio_stream *s = &prtd->s;
+
+       unsigned int addr, ptr;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (s->device_type == AML_AUDIO_I2SOUT)
+                       ptr = read_i2s_rd_ptr();
+               else
+                       ptr = read_iec958_rd_ptr();
+               addr = ptr - s->I2S_addr;
+               return bytes_to_frames(runtime, addr);
+       }
+
+       {
+               if (s->device_type == AML_AUDIO_I2SIN)
+                       ptr = audio_in_i2s_wr_ptr();
+               else
+                       ptr = audio_in_spdif_wr_ptr();
+               addr = ptr - s->I2S_addr;
+               if (runtime->format == SNDRV_PCM_FORMAT_S16_LE)
+                       return bytes_to_frames(runtime, addr) >> 1;
+               else
+                       return bytes_to_frames(runtime, addr);
+       }
+
+       return 0;
+}
+
+#ifndef USE_HRTIMER
+static void aml_i2s_timer_callback(unsigned long data)
+{
+       struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_runtime_data *prtd = NULL;
+       struct audio_stream *s = NULL;
+       int elapsed = 0;
+       unsigned int last_ptr, size = 0;
+       unsigned long flags = 0;
+
+       if (runtime == NULL || runtime->private_data == NULL)
+               return;
+
+       prtd = runtime->private_data;
+       s = &prtd->s;
+
+       if (prtd->active == 0)
+               return;
+
+       spin_lock_irqsave(&prtd->timer_lock, flags);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (s->device_type == AML_AUDIO_I2SOUT)
+                       last_ptr = read_i2s_rd_ptr();
+               else
+                       last_ptr = read_iec958_rd_ptr();
+               if (last_ptr < s->last_ptr) {
+                       size =
+                               runtime->dma_bytes + last_ptr -
+                               (s->last_ptr);
+               } else {
+                       size = last_ptr - (s->last_ptr);
+               }
+               s->last_ptr = last_ptr;
+               s->size += bytes_to_frames(substream->runtime, size);
+               if (s->size >= runtime->period_size) {
+                       s->size %= runtime->period_size;
+                       elapsed = 1;
+               }
+       } else {
+               if (s->device_type == AML_AUDIO_I2SIN)
+                       last_ptr = audio_in_i2s_wr_ptr();
+               else
+                       last_ptr = audio_in_spdif_wr_ptr();
+
+               if (last_ptr < s->last_ptr) {
+                       if (runtime->format == SNDRV_PCM_FORMAT_S16_LE)
+                               size = runtime->dma_bytes +
+                                       (last_ptr - (s->last_ptr)) / 2;
+                       else
+                               size = runtime->dma_bytes +
+                                       (last_ptr - (s->last_ptr));
+                       prtd->xrun_num = 0;
+               } else if (last_ptr == s->last_ptr) {
+                       if (prtd->xrun_num++ > XRUN_NUM) {
+                               prtd->xrun_num = 0;
+                               s->size = runtime->period_size;
+                       }
+               } else {
+                       if (runtime->format == SNDRV_PCM_FORMAT_S16_LE)
+                               size = (last_ptr - (s->last_ptr)) / 2;
+                       else
+                               size = last_ptr - (s->last_ptr);
+                       prtd->xrun_num = 0;
+               }
+
+               s->last_ptr = last_ptr;
+               s->size += bytes_to_frames(substream->runtime, size);
+               if (s->size >= runtime->period_size) {
+                       s->size %= runtime->period_size;
+                       elapsed = 1;
+               }
+       }
+
+#ifndef USE_HW_TIMER
+       mod_timer(&prtd->timer, jiffies + 1);
+#endif
+
+       spin_unlock_irqrestore(&prtd->timer_lock, flags);
+       if (elapsed)
+               snd_pcm_period_elapsed(substream);
+}
+
+#endif
+
+static int aml_i2s_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_runtime_data *prtd = runtime->private_data;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       struct audio_stream *s = &prtd->s;
+       int ret = 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               snd_soc_set_runtime_hwparams(substream, &aml_i2s_hardware);
+               if (s->device_type == AML_AUDIO_I2SOUT) {
+                       aml_i2s_playback_start_addr = (unsigned long)buf->area;
+                       aml_i2s_playback_phy_start_addr = buf->addr;
+               }
+       } else {
+               snd_soc_set_runtime_hwparams(substream, &aml_i2s_capture);
+       }
+
+       /* ensure that period size is a multiple of 32bytes */
+       ret =
+           snd_pcm_hw_constraint_list(runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+                                      &hw_constraints_period_sizes);
+       if (ret < 0) {
+               dev_err(substream->pcm->card->dev,
+                       "set period bytes constraint error\n");
+               goto out;
+       }
+
+       /* ensure that buffer size is a multiple of period size */
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0) {
+               dev_err(substream->pcm->card->dev, "set period error\n");
+               goto out;
+       }
+       if (!prtd) {
+               prtd = kzalloc(sizeof(struct aml_runtime_data), GFP_KERNEL);
+               if (prtd == NULL) {
+                       dev_err(substream->pcm->card->dev, "alloc aml_runtime_data error\n");
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               prtd->substream = substream;
+               runtime->private_data = prtd;
+       }
+
+       spin_lock_init(&prtd->timer_lock);
+
+#if defined(USE_HW_TIMER)
+       ret = snd_request_hw_timer(prtd);
+       if (ret < 0) {
+               dev_err(substream->pcm->card->dev, "request audio hw timer failed\n");
+               goto out;
+       }
+#elif defined(USE_HRTIMER)
+       hrtimer_init(&prtd->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       prtd->hrtimer.function = aml_i2s_hrtimer_callback;
+       pr_info("hrtimer inited..\n");
+#else
+       init_timer(&prtd->timer);
+       prtd->timer.function = &aml_i2s_timer_callback;
+       prtd->timer.data = (unsigned long)substream;
+#endif
+
+ out:
+       return ret;
+}
+
+static int aml_i2s_close(struct snd_pcm_substream *substream)
+{
+       struct aml_runtime_data *prtd = substream->runtime->private_data;
+
+#ifdef USE_HW_TIMER
+       snd_free_hw_timer_irq(prtd);
+#endif
+       kfree(prtd);
+       prtd = NULL;
+
+       return 0;
+}
+
+#ifndef CONFIG_AMLOGIC_SND_SPLIT_MODE_MMAP
+static int aml_i2s_copy_playback(struct snd_pcm_runtime *runtime, int channel,
+                                snd_pcm_uframes_t pos,
+                                void __user *buf, snd_pcm_uframes_t count,
+                                struct snd_pcm_substream *substream)
+{
+       int res = 0;
+       int n;
+       unsigned long offset = frames_to_bytes(runtime, pos);
+       char *hwbuf = runtime->dma_area + offset;
+       struct aml_runtime_data *prtd = runtime->private_data;
+       struct snd_dma_buffer *buffer = &substream->dma_buffer;
+       struct aml_audio_buffer *tmp_buf = buffer->private_data;
+       void *ubuf = tmp_buf->buffer_start;
+       struct audio_stream *s = &prtd->s;
+       struct device *dev = substream->pcm->card->dev;
+#ifndef CONFIG_AMLOGIC_SND_SPLIT_MODE
+       int i = 0, j = 0;
+       int align = runtime->channels * 32;
+       int cached_len = tmp_buf->cached_len;
+       char *cache_buffer_bytes = tmp_buf->cache_buffer_bytes;
+#endif
+       n = frames_to_bytes(runtime, count);
+       if (n > tmp_buf->buffer_size) {
+               dev_err(dev, "FATAL_ERR:UserData/%d > buffer_size/%d\n",
+                               n, tmp_buf->buffer_size);
+               return -EFAULT;
+       }
+       res = copy_from_user(ubuf, buf, n);
+       if (res)
+               return -EFAULT;
+
+#ifndef CONFIG_AMLOGIC_SND_SPLIT_MODE
+       /*mask align byte(64 or 256)*/
+       if ((cached_len != 0 || (n % align) != 0)) {
+               int byte_size = n;
+               int total_len;
+               int output_len;
+               int next_cached_len;
+               char cache_buffer_bytes_tmp[256];
+
+               offset -= cached_len;
+               hwbuf = runtime->dma_area + offset;
+
+               total_len = byte_size + cached_len;
+               output_len = total_len & (~(align - 1));
+               next_cached_len = total_len - output_len;
+
+               if (next_cached_len)
+                       memcpy((void *)cache_buffer_bytes_tmp,
+                               (void *)((char *)ubuf +
+                               byte_size - next_cached_len),
+                               next_cached_len);
+               memmove((void *)((char *)ubuf + cached_len),
+                               (void *)ubuf, output_len - cached_len);
+               if (cached_len)
+                       memcpy((void *)ubuf,
+                               (void *)cache_buffer_bytes, cached_len);
+               if (next_cached_len)
+                       memcpy((void *)cache_buffer_bytes,
+                               (void *)cache_buffer_bytes_tmp,
+                               next_cached_len);
+
+               tmp_buf->cached_len = next_cached_len;
+               n = output_len;
+       }
+       /*end of mask*/
+#endif
+
+       if (s->device_type == AML_AUDIO_I2SOUT)
+               aml_i2s_alsa_write_addr = offset;
+
+       if (access_ok(VERIFY_READ, buf, frames_to_bytes(runtime, count))) {
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+               memcpy(hwbuf, ubuf, n);
+#else
+               if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
+                       int16_t *tfrom, *to;
+
+                       tfrom = (int16_t *) ubuf;
+                       to = (int16_t *) hwbuf;
+
+                       if (runtime->channels == 8) {
+                               int16_t *lf, *cf, *rf, *ls,
+                                               *rs, *lef, *sbl, *sbr;
+
+                               lf = to;
+                               cf = to + 16 * 1;
+                               rf = to + 16 * 2;
+                               ls = to + 16 * 3;
+                               rs = to + 16 * 4;
+                               lef = to + 16 * 5;
+                               sbl = to + 16 * 6;
+                               sbr = to + 16 * 7;
+                               for (j = 0; j < n; j += 256) {
+                                       for (i = 0; i < 16; i++) {
+                                               *lf++ = (*tfrom++);
+                                               *cf++ = (*tfrom++);
+                                               *rf++ = (*tfrom++);
+                                               *ls++ = (*tfrom++);
+                                               *rs++ = (*tfrom++);
+                                               *lef++ = (*tfrom++);
+                                               *sbl++ = (*tfrom++);
+                                               *sbr++ = (*tfrom++);
+                                       }
+                                       lf += 7 * 16;
+                                       cf += 7 * 16;
+                                       rf += 7 * 16;
+                                       ls += 7 * 16;
+                                       rs += 7 * 16;
+                                       lef += 7 * 16;
+                                       sbl += 7 * 16;
+                                       sbr += 7 * 16;
+                               }
+                       } else if (runtime->channels == 2) {
+                               int16_t *left, *right;
+
+                               left = to;
+                               right = to + 16;
+
+                               for (j = 0; j < n; j += 64) {
+                                       for (i = 0; i < 16; i++) {
+                                               *left++ = (*tfrom++);
+                                               *right++ = (*tfrom++);
+                                       }
+                                       left += 16;
+                                       right += 16;
+                               }
+                       }
+               } else if (runtime->format == SNDRV_PCM_FORMAT_S24_LE
+                          && I2S_MODE == AIU_I2S_MODE_PCM24) {
+                       int32_t *tfrom, *to;
+
+                       tfrom = (int32_t *) ubuf;
+                       to = (int32_t *) hwbuf;
+
+                       if (runtime->channels == 8) {
+                               int32_t *lf, *cf, *rf, *ls,
+                                       *rs, *lef, *sbl, *sbr;
+
+                               lf = to;
+                               cf = to + 8 * 1;
+                               rf = to + 8 * 2;
+                               ls = to + 8 * 3;
+                               rs = to + 8 * 4;
+                               lef = to + 8 * 5;
+                               sbl = to + 8 * 6;
+                               sbr = to + 8 * 7;
+                               for (j = 0; j < n; j += 256) {
+                                       for (i = 0; i < 8; i++) {
+                                               *lf++ = (*tfrom++);
+                                               *cf++ = (*tfrom++);
+                                               *rf++ = (*tfrom++);
+                                               *ls++ = (*tfrom++);
+                                               *rs++ = (*tfrom++);
+                                               *lef++ = (*tfrom++);
+                                               *sbl++ = (*tfrom++);
+                                               *sbr++ = (*tfrom++);
+                                       }
+                                       lf += 7 * 8;
+                                       cf += 7 * 8;
+                                       rf += 7 * 8;
+                                       ls += 7 * 8;
+                                       rs += 7 * 8;
+                                       lef += 7 * 8;
+                                       sbl += 7 * 8;
+                                       sbr += 7 * 8;
+                               }
+                       } else if (runtime->channels == 2) {
+                               int32_t *left, *right;
+
+                               left = to;
+                               right = to + 8;
+
+                               for (j = 0; j < n; j += 64) {
+                                       for (i = 0; i < 8; i++) {
+                                               *left++ = (*tfrom++);
+                                               *right++ = (*tfrom++);
+                                       }
+                                       left += 8;
+                                       right += 8;
+                               }
+                       }
+
+               } else if (runtime->format == SNDRV_PCM_FORMAT_S32_LE) {
+                       int32_t *tfrom, *to;
+
+                       tfrom = (int32_t *) ubuf;
+                       to = (int32_t *) hwbuf;
+
+                       if (runtime->channels == 8) {
+                               int32_t *lf, *cf, *rf, *ls,
+                                       *rs, *lef, *sbl, *sbr;
+
+                               lf = to;
+                               cf = to + 8 * 1;
+                               rf = to + 8 * 2;
+                               ls = to + 8 * 3;
+                               rs = to + 8 * 4;
+                               lef = to + 8 * 5;
+                               sbl = to + 8 * 6;
+                               sbr = to + 8 * 7;
+                               for (j = 0; j < n; j += 256) {
+                                       for (i = 0; i < 8; i++) {
+                                               *lf++ = (*tfrom++) >> 8;
+                                               *cf++ = (*tfrom++) >> 8;
+                                               *rf++ = (*tfrom++) >> 8;
+                                               *ls++ = (*tfrom++) >> 8;
+                                               *rs++ = (*tfrom++) >> 8;
+                                               *lef++ = (*tfrom++) >> 8;
+                                               *sbl++ = (*tfrom++) >> 8;
+                                               *sbr++ = (*tfrom++) >> 8;
+                                       }
+                                       lf += 7 * 8;
+                                       cf += 7 * 8;
+                                       rf += 7 * 8;
+                                       ls += 7 * 8;
+                                       rs += 7 * 8;
+                                       lef += 7 * 8;
+                                       sbl += 7 * 8;
+                                       sbr += 7 * 8;
+                               }
+                       } else if (runtime->channels == 2) {
+                               int32_t *left, *right;
+
+                               left = to;
+                               right = to + 8;
+
+                               for (j = 0; j < n; j += 64) {
+                                       for (i = 0; i < 8; i++) {
+                                               *left++ = (*tfrom++) >> 8;
+                                               *right++ = (*tfrom++) >> 8;
+                                       }
+                                       left += 8;
+                                       right += 8;
+                               }
+                       }
+               }
+#endif
+       } else {
+               res = -EFAULT;
+       }
+       return res;
+}
+
+static int aml_i2s_copy_capture(struct snd_pcm_runtime *runtime, int channel,
+                               snd_pcm_uframes_t pos,
+                               void __user *buf, snd_pcm_uframes_t count,
+                               struct snd_pcm_substream *substream)
+{
+       struct device *dev = substream->pcm->card->dev;
+       struct snd_dma_buffer *buffer = &substream->dma_buffer;
+       struct aml_audio_buffer *tmp_buf = buffer->private_data;
+       void *ubuf = tmp_buf->buffer_start;
+       unsigned long offset = frames_to_bytes(runtime, pos);
+       int res = 0, n = 0, i = 0, j = 0;
+       unsigned char r_shift = 8;
+
+       n = frames_to_bytes(runtime, count);
+
+       /*amlogic HW only supports 32bit mode by capture*/
+       if (access_ok(VERIFY_WRITE, buf, frames_to_bytes(runtime, count))) {
+               if (runtime->channels == 2) {
+                       if (pos % 8)
+                               dev_err(dev, "audio data unligned\n");
+
+                       if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
+                               char *hwbuf = runtime->dma_area + offset * 2;
+                               int32_t *tfrom = (int32_t *)hwbuf;
+                               int16_t *to = (int16_t *)ubuf;
+                               int32_t *left = tfrom;
+                               int32_t *right = tfrom + 8;
+
+                               if ((n * 2) % 64)
+                                       dev_err(dev, "audio data unaligned 64 bytes\n");
+
+                               for (j = 0; j < n * 2; j += 64) {
+                                       for (i = 0; i < 8; i++) {
+                                               *to++ = (int16_t)
+                                                       ((*left++) >> r_shift);
+                                               *to++ = (int16_t)
+                                                       ((*right++) >> r_shift);
+                                       }
+                                       left += 8;
+                                       right += 8;
+                               }
+                               /* clean hw buffer */
+                               memset(hwbuf, 0, n * 2);
+                       } else {
+                               char *hwbuf = runtime->dma_area + offset;
+                               int32_t *tfrom = (int32_t *)hwbuf;
+                               int32_t *to = (int32_t *)ubuf;
+                               int32_t *left = tfrom;
+                               int32_t *right = tfrom + 8;
+
+                               if (n % 64)
+                                       dev_err(dev, "audio data unaligned 64 bytes\n");
+
+                               if (runtime->format == SNDRV_PCM_FORMAT_S24_LE)
+                                       r_shift = 0;
+
+                               for (j = 0; j < n; j += 64) {
+                                       for (i = 0; i < 8; i++) {
+                                               *to++ = (int32_t)
+                                                       ((*left++) << r_shift);
+                                               *to++ = (int32_t)
+                                                       ((*right++) << r_shift);
+                                       }
+                                       left += 8;
+                                       right += 8;
+                               }
+                               memset(hwbuf, 0, n);
+                       }
+               }
+       }
+       res = copy_to_user(buf, ubuf, n);
+       return res;
+}
+
+static int aml_i2s_copy(struct snd_pcm_substream *substream, int channel,
+                       snd_pcm_uframes_t pos,
+                       void __user *buf, snd_pcm_uframes_t count)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int ret = 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               ret =
+                   aml_i2s_copy_playback(runtime, channel, pos, buf, count,
+                                         substream);
+       } else {
+               ret =
+                   aml_i2s_copy_capture(runtime, channel, pos, buf, count,
+                                        substream);
+       }
+       return ret;
+}
+#endif
+
+int aml_i2s_silence(struct snd_pcm_substream *substream, int channel,
+                   snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+{
+       char *ppos;
+       int n;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       n = frames_to_bytes(runtime, count);
+       ppos = runtime->dma_area + frames_to_bytes(runtime, pos);
+       memset(ppos, 0, n);
+       return 0;
+}
+
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE_MMAP
+static int aml_pcm_mmap(struct snd_pcm_substream *substream,
+       struct vm_area_struct *vma)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       dev_info(substream->pcm->card->dev,
+               "\narea=%p,addr=%ld,bytes=%ld,rate:%d, channels:%d, subformat:%d\n",
+               runtime->dma_area, (long)runtime->dma_addr, runtime->dma_bytes,
+               runtime->rate, runtime->channels, runtime->subformat);
+
+       return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+               runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+}
+#endif
+
+static struct snd_pcm_ops aml_i2s_ops = {
+       .open = aml_i2s_open,
+       .close = aml_i2s_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = aml_i2s_hw_params,
+       .hw_free = aml_i2s_hw_free,
+       .prepare = aml_i2s_prepare,
+       .trigger = aml_i2s_trigger,
+       .pointer = aml_i2s_pointer,
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE_MMAP
+       .mmap = aml_pcm_mmap,
+#else
+       .copy = aml_i2s_copy,
+#endif
+       .silence = aml_i2s_silence,
+};
+
+/*--------------------------------------------------------------------------
+ * ASoC platform driver
+ *--------------------------------------------------------------------------
+ */
+static u64 aml_i2s_dmamask = 0xffffffff;
+
+static int aml_i2s_new(struct snd_soc_pcm_runtime *rtd)
+{
+       int ret = 0;
+       struct snd_soc_card *card = rtd->card;
+       struct snd_pcm *pcm = rtd->pcm;
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &aml_i2s_dmamask;
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = 0xffffffff;
+
+       if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+               ret = aml_i2s_preallocate_dma_buffer(pcm,
+                                                    SNDRV_PCM_STREAM_PLAYBACK);
+               if (ret)
+                       goto out;
+       }
+
+       if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+               ret = aml_i2s_preallocate_dma_buffer(pcm,
+                                                    SNDRV_PCM_STREAM_CAPTURE);
+               if (ret)
+                       goto out;
+       }
+
+ out:
+       return ret;
+}
+
+static void aml_i2s_free_dma_buffers(struct snd_pcm *pcm)
+{
+       struct snd_pcm_substream *substream;
+       struct snd_dma_buffer *buf;
+       struct aml_audio_buffer *tmp_buf;
+       int stream;
+
+       for (stream = 0; stream < 2; stream++) {
+               substream = pcm->streams[stream].substream;
+               if (!substream)
+                       continue;
+
+               buf = &substream->dma_buffer;
+               if (!buf->area)
+                       continue;
+               dma_free_coherent(pcm->card->dev, buf->bytes,
+                                 buf->area, buf->addr);
+               buf->area = NULL;
+
+               tmp_buf = buf->private_data;
+               if (tmp_buf->buffer_start != NULL && tmp_buf != NULL)
+                       kfree(tmp_buf->buffer_start);
+               if (tmp_buf != NULL)
+                       kfree(tmp_buf);
+               buf->private_data = NULL;
+       }
+}
+
+static const char *const output_swap_texts[] = { "L/R", "L/L", "R/R", "R/L" };
+
+static const struct soc_enum output_swap_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(output_swap_texts),
+                       output_swap_texts);
+
+static int aml_output_swap_get_enum(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = read_i2s_mute_swap_reg();
+       return 0;
+}
+
+static int aml_output_swap_set_enum(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       audio_i2s_swap_left_right(ucontrol->value.enumerated.item[0]);
+       return 0;
+}
+
+bool aml_audio_i2s_mute_flag;
+static int aml_audio_set_i2s_mute(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       aml_audio_i2s_mute_flag = ucontrol->value.integer.value[0];
+       pr_info("aml_audio_i2s_mute_flag: flag=%d\n", aml_audio_i2s_mute_flag);
+       if (aml_audio_i2s_mute_flag)
+               aml_audio_i2s_mute();
+       else
+               aml_audio_i2s_unmute();
+       return 0;
+}
+
+static int aml_audio_get_i2s_mute(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = aml_audio_i2s_mute_flag;
+       return 0;
+}
+
+static const struct snd_kcontrol_new aml_i2s_controls[] = {
+       SOC_SINGLE_BOOL_EXT("Audio i2s mute",
+                               0, aml_audio_get_i2s_mute,
+                               aml_audio_set_i2s_mute),
+
+       SOC_ENUM_EXT("Output Swap",
+                               output_swap_enum,
+                               aml_output_swap_get_enum,
+                               aml_output_swap_set_enum),
+};
+
+static int aml_i2s_probe(struct snd_soc_platform *platform)
+{
+       return snd_soc_add_platform_controls(platform,
+                       aml_i2s_controls, ARRAY_SIZE(aml_i2s_controls));
+}
+
+struct snd_soc_platform_driver aml_soc_platform = {
+       .probe = aml_i2s_probe,
+       .ops = &aml_i2s_ops,
+       .pcm_new = aml_i2s_new,
+       .pcm_free = aml_i2s_free_dma_buffers,
+};
+
+static int aml_soc_platform_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_platform(&pdev->dev, &aml_soc_platform);
+}
+
+static int aml_soc_platform_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_platform(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_audio_dt_match[] = {
+       {.compatible = "amlogic, aml-i2s",
+        },
+       {},
+};
+#else
+#define amlogic_audio_dt_match NULL
+#endif
+
+#ifdef CONFIG_HIBERNATION
+static unsigned long isa_timerd_saved;
+static unsigned long isa_timerd_mux_saved;
+static int aml_i2s_freeze(struct device *dev)
+{
+       isa_timerd_saved = aml_read_cbus(ISA_TIMERD);
+       isa_timerd_mux_saved = aml_read_cbus(ISA_TIMER_MUX);
+       return 0;
+}
+
+static int aml_i2s_thaw(struct device *dev)
+{
+       return 0;
+}
+
+static int aml_i2s_restore(struct device *dev)
+{
+       aml_write_cbus(ISA_TIMERD, isa_timerd_saved);
+       aml_write_cbus(ISA_TIMER_MUX, isa_timerd_mux_saved);
+       return 0;
+}
+
+const struct dev_pm_ops aml_i2s_pm = {
+       .freeze         = aml_i2s_freeze,
+       .thaw           = aml_i2s_thaw,
+       .restore        = aml_i2s_restore,
+};
+#endif
+
+static struct platform_driver aml_i2s_driver = {
+       .driver = {
+               .name = "aml-i2s",
+               .owner = THIS_MODULE,
+               .of_match_table = amlogic_audio_dt_match,
+#ifdef CONFIG_HIBERNATION
+               .pm     = &aml_i2s_pm,
+#endif
+               },
+
+       .probe = aml_soc_platform_probe,
+       .remove = aml_soc_platform_remove,
+};
+
+static int __init aml_alsa_audio_init(void)
+{
+       return platform_driver_register(&aml_i2s_driver);
+}
+
+static void __exit aml_alsa_audio_exit(void)
+{
+       platform_driver_unregister(&aml_i2s_driver);
+}
+
+module_init(aml_alsa_audio_init);
+module_exit(aml_alsa_audio_exit);
+
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AML audio driver for ALSA");
diff --git a/sound/soc/amlogic/aml_i2s.h b/sound/soc/amlogic/aml_i2s.h
new file mode 100644 (file)
index 0000000..32e2ded
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * sound/soc/amlogic/aml_i2s.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AML_I2S_H__
+#define __AML_I2S_H__
+
+/* #define debug_printk */
+#ifdef debug_printk
+#define dug_printk(fmt, args...)  printk(fmt, ## args)
+#else
+#define dug_printk(fmt, args...)
+#endif
+
+/*hw timer, timer D*/
+#define BASE_IRQ                (32)
+#define AM_IRQ(reg)             (reg + BASE_IRQ)
+#define INT_TIMER_D             AM_IRQ(29)
+/* note: we use TIEMRD. MODE: 1: periodic, 0: one-shot*/
+#define TIMERD_MODE             1
+/* timerbase resolution: 00: 1us; 01: 10us; 10: 100us; 11: 1ms*/
+#define TIMERD_RESOLUTION       0x1
+/* timer count: 16bits*/
+#define TIMER_COUNT             100
+
+struct audio_stream {
+       int stream_id;
+       unsigned int last_ptr;
+       unsigned int size;
+       unsigned int sample_rate;
+       unsigned int I2S_addr;
+       spinlock_t lock;
+       struct snd_pcm_substream *stream;
+       unsigned int i2s_mode; /* 0:master, 1:slave, */
+       unsigned int device_type;
+};
+struct aml_audio_buffer {
+       void *buffer_start;
+       unsigned int buffer_size;
+       char cache_buffer_bytes[256];
+       int cached_len;
+};
+
+struct aml_i2s_dma_params {
+       char *name;                     /* stream identifier */
+       struct snd_pcm_substream *substream;
+       void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
+};
+struct aml_dai_info {
+       unsigned int i2s_mode; /* 0:master, 1:slave, */
+};
+enum {
+       I2S_MASTER_MODE = 0,
+       I2S_SLAVE_MODE,
+};
+/*--------------------------------------------------------------------------
+ * Data types
+ *--------------------------------------------------------------------------
+ */
+struct aml_runtime_data {
+       struct aml_i2s_dma_params *params;
+       dma_addr_t dma_buffer;          /* physical address of dma buffer */
+       dma_addr_t dma_buffer_end;      /* first address beyond DMA buffer */
+
+       struct snd_pcm *pcm;
+       struct snd_pcm_substream *substream;
+       struct audio_stream s;
+       struct timer_list timer;        /* timeer for playback and capture */
+       struct hrtimer hrtimer;
+       ktime_t wakeups_per_second;
+       spinlock_t timer_lock;
+       void *buf; /* tmp buffer for playback or capture */
+       int active;
+       unsigned int xrun_num;
+};
+
+#endif
diff --git a/sound/soc/amlogic/aml_i2s_dai.c b/sound/soc/amlogic/aml_i2s_dai.c
new file mode 100644 (file)
index 0000000..9568526
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * sound/soc/amlogic/aml_i2s_dai.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/soundcard.h>
+#include <linux/timer.h>
+#include <linux/debugfs.h>
+#include <linux/major.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "aml_i2s_dai.h"
+#include "aml_pcm.h"
+#include "aml_i2s.h"
+#include "aml_audio_hw.h"
+#include <linux/amlogic/media/sound/aout_notify.h>
+#include "aml_spdif_dai.h"
+
+struct aml_dai_info dai_info[3] = { {0} };
+
+static int i2s_pos_sync;
+
+/* extern int set_i2s_iec958_samesource(int enable);
+ *
+ * the I2S hw  and IEC958 PCM output initiation,958 initiation here,
+ * for the case that only use our ALSA driver for PCM s/pdif output.
+ */
+static void aml_hw_i2s_init(struct snd_pcm_runtime *runtime)
+{
+       unsigned int i2s_mode = AIU_I2S_MODE_PCM16;
+
+       switch (runtime->format) {
+       case SNDRV_PCM_FORMAT_S32_LE:
+               i2s_mode = AIU_I2S_MODE_PCM32;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               i2s_mode = AIU_I2S_MODE_PCM24;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               i2s_mode = AIU_I2S_MODE_PCM16;
+               break;
+       }
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+       audio_set_i2s_mode(i2s_mode, runtime->channels);
+#else
+       audio_set_i2s_mode(i2s_mode);
+#endif
+       audio_set_aiubuf(runtime->dma_addr, runtime->dma_bytes,
+                        runtime->channels);
+}
+
+static int aml_dai_i2s_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       int ret = 0;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_runtime_data *prtd =
+           (struct aml_runtime_data *)runtime->private_data;
+       struct audio_stream *s;
+
+       if (prtd == NULL) {
+               prtd =
+                   (struct aml_runtime_data *)
+                   kzalloc(sizeof(struct aml_runtime_data), GFP_KERNEL);
+               if (prtd == NULL) {
+                       dev_err(substream->pcm->card->dev, "alloc aml_runtime_data error\n");
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               prtd->substream = substream;
+               runtime->private_data = prtd;
+       }
+       s = &prtd->s;
+       if (substream->stream
+                       == SNDRV_PCM_STREAM_PLAYBACK) {
+               s->device_type = AML_AUDIO_I2SOUT;
+       } else {
+               s->device_type = AML_AUDIO_I2SIN;
+       }
+       return 0;
+ out:
+       return ret;
+}
+
+static void aml_dai_i2s_shutdown(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+       if (IEC958_mode_codec == 0)
+               aml_spdif_play(1);
+}
+
+#define AOUT_EVENT_IEC_60958_PCM 0x1
+static int aml_i2s_set_amclk(struct aml_i2s *i2s, unsigned long rate)
+{
+       int ret = 0;
+
+       ret = clk_set_rate(i2s->clk_mpll, rate * 10);
+       if (ret)
+               return ret;
+       ret = clk_set_parent(i2s->clk_mclk, i2s->clk_mpll);
+       if (ret)
+               return ret;
+
+       ret = clk_set_rate(i2s->clk_mclk, rate);
+       if (ret)
+               return ret;
+
+       audio_set_i2s_clk_div();
+
+       return 0;
+}
+
+static int aml_dai_i2s_prepare(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_runtime_data *prtd = runtime->private_data;
+       struct audio_stream *s = &prtd->s;
+       struct aml_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               s->i2s_mode = dai_info[dai->id].i2s_mode;
+               if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
+                       audio_in_i2s_set_buf(runtime->dma_addr,
+                                       runtime->dma_bytes * 2,
+                                       0, i2s_pos_sync, i2s->audin_fifo_src,
+                                       runtime->channels);
+                       memset((void *)runtime->dma_area, 0,
+                                       runtime->dma_bytes * 2);
+               } else {
+                       audio_in_i2s_set_buf(runtime->dma_addr,
+                                       runtime->dma_bytes,
+                                       0, i2s_pos_sync, i2s->audin_fifo_src,
+                                       runtime->channels);
+                       memset((void *)runtime->dma_area, 0,
+                                       runtime->dma_bytes);
+               }
+               s->device_type = AML_AUDIO_I2SIN;
+       } else {
+               s->device_type = AML_AUDIO_I2SOUT;
+               audio_out_i2s_enable(0);
+               audio_util_set_dac_i2s_format(AUDIO_ALGOUT_DAC_FORMAT_DSP);
+               aml_hw_i2s_init(runtime);
+               /* i2s/958 share the same audio hw buffer when PCM mode */
+               if (IEC958_mode_codec == 0) {
+                       aml_hw_iec958_init(substream, 1);
+                       /* use the hw same sync for i2s/958 */
+                       dev_info(substream->pcm->card->dev, "i2s/958 same source\n");
+               }
+               if (runtime->channels == 8) {
+                       dev_info(substream->pcm->card->dev,
+                               "8ch PCM output->notify HDMI\n");
+                       aout_notifier_call_chain(AOUT_EVENT_IEC_60958_PCM,
+                               substream);
+               }
+       }
+       return 0;
+}
+
+static int aml_dai_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
+{
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               /* TODO */
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       dev_info(substream->pcm->card->dev, "I2S playback enable\n");
+                       audio_out_i2s_enable(1);
+                       if (IEC958_mode_codec == 0) {
+                               dev_info(substream->pcm->card->dev, "IEC958 playback enable\n");
+                               audio_hw_958_enable(1);
+                       }
+               } else {
+                       audio_in_i2s_enable(1);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       dev_info(substream->pcm->card->dev, "I2S playback disable\n");
+                       audio_out_i2s_enable(0);
+                       if (IEC958_mode_codec == 0) {
+                               dev_info(substream->pcm->card->dev, "IEC958 playback disable\n");
+                               audio_hw_958_enable(0);
+                       }
+               } else {
+                       audio_in_i2s_enable(0);
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int aml_dai_i2s_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct aml_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+       int srate, mclk_rate;
+
+       srate = params_rate(params);
+       if (i2s->old_samplerate != srate) {
+               i2s->old_samplerate = srate;
+               mclk_rate = srate * DEFAULT_MCLK_RATIO_SR;
+               aml_i2s_set_amclk(i2s, mclk_rate);
+       }
+
+       return 0;
+}
+
+static int aml_dai_set_i2s_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       if (fmt & SND_SOC_DAIFMT_CBS_CFS)       /* slave mode */
+               dai_info[dai->id].i2s_mode = I2S_SLAVE_MODE;
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               i2s_pos_sync = 0;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               i2s_pos_sync = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int aml_dai_set_i2s_sysclk(struct snd_soc_dai *dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       return 0;
+}
+
+static int aml_dai_i2s_suspend(struct snd_soc_dai *dai)
+{
+       struct aml_i2s *i2s = dev_get_drvdata(dai->dev);
+
+       if (i2s && i2s->clk_mclk && !i2s->disable_clk_suspend)
+               clk_disable_unprepare(i2s->clk_mclk);
+
+       return 0;
+}
+
+static int aml_dai_i2s_resume(struct snd_soc_dai *dai)
+{
+       struct aml_i2s *i2s = dev_get_drvdata(dai->dev);
+
+       if (i2s && i2s->clk_mclk && !i2s->disable_clk_suspend)
+               clk_prepare_enable(i2s->clk_mclk);
+
+       return 0;
+}
+
+#define AML_DAI_I2S_RATES              (SNDRV_PCM_RATE_8000_192000)
+#define AML_DAI_I2S_FORMATS            (SNDRV_PCM_FMTBIT_S16_LE |\
+               SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops aml_dai_i2s_ops = {
+       .startup = aml_dai_i2s_startup,
+       .shutdown = aml_dai_i2s_shutdown,
+       .prepare = aml_dai_i2s_prepare,
+       .trigger = aml_dai_i2s_trigger,
+       .hw_params = aml_dai_i2s_hw_params,
+       .set_fmt = aml_dai_set_i2s_fmt,
+       .set_sysclk = aml_dai_set_i2s_sysclk,
+};
+
+struct snd_soc_dai_driver aml_i2s_dai[] = {
+       {
+        .id = 0,
+        .suspend = aml_dai_i2s_suspend,
+        .resume = aml_dai_i2s_resume,
+        .playback = {
+                     .channels_min = 1,
+                     .channels_max = 8,
+                     .rates = AML_DAI_I2S_RATES,
+                     .formats = AML_DAI_I2S_FORMATS,},
+        .capture = {
+                    .channels_min = 1,
+                    .channels_max = 8,
+                    .rates = AML_DAI_I2S_RATES,
+                    .formats = AML_DAI_I2S_FORMATS,},
+        .ops = &aml_dai_i2s_ops,
+        },
+};
+EXPORT_SYMBOL_GPL(aml_i2s_dai);
+
+static const struct snd_soc_component_driver aml_component = {
+       .name = "aml-i2s-dai",
+};
+
+static const char *const gate_names[] = {
+       "top_glue", "aud_buf", "i2s_out", "amclk_measure",
+       "aififo2", "aud_mixer", "mixer_reg", "adc",
+       "top_level", "aoclk", "aud_in"
+};
+
+static int aml_i2s_dai_probe(struct platform_device *pdev)
+{
+       struct aml_i2s *i2s = NULL;
+       struct device_node *pnode = pdev->dev.of_node;
+       int ret = 0, i;
+       struct clk *clk_gate;
+
+       /* enable AIU module power gate first */
+       for (i = 0; i < ARRAY_SIZE(gate_names); i++) {
+               clk_gate = devm_clk_get(&pdev->dev, gate_names[i]);
+               if (IS_ERR(clk_gate)) {
+                       dev_err(&pdev->dev, "Can't get aml audio gate\n");
+                       return PTR_ERR(clk_gate);
+               }
+               pr_info("clock source gate %s : %p\n", gate_names[i], clk_gate);
+               clk_prepare_enable(clk_gate);
+       }
+
+       i2s = devm_kzalloc(&pdev->dev, sizeof(struct aml_i2s), GFP_KERNEL);
+       if (!i2s) {
+               /*dev_err(&pdev->dev, "Can't allocate aml_i2s\n");*/
+               ret = -ENOMEM;
+               goto err;
+       }
+       dev_set_drvdata(&pdev->dev, i2s);
+
+       i2s->disable_clk_suspend =
+               of_property_read_bool(pnode, "disable_clk_suspend");
+
+       i2s->clk_mpll = devm_clk_get(&pdev->dev, "mpll2");
+       if (IS_ERR(i2s->clk_mpll)) {
+               dev_err(&pdev->dev, "Can't retrieve mpll2 clock\n");
+               ret = PTR_ERR(i2s->clk_mpll);
+               goto err;
+       }
+
+       i2s->clk_mclk = devm_clk_get(&pdev->dev, "mclk");
+       if (IS_ERR(i2s->clk_mclk)) {
+               dev_err(&pdev->dev, "Can't retrieve clk_mclk clock\n");
+               ret = PTR_ERR(i2s->clk_mclk);
+               goto err;
+       }
+
+       /* now only 256fs is supported */
+       ret = aml_i2s_set_amclk(i2s,
+                       DEFAULT_SAMPLERATE * DEFAULT_MCLK_RATIO_SR);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Can't set aml_i2s :%d\n", ret);
+               goto err;
+       }
+
+       ret = clk_prepare_enable(i2s->clk_mclk);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't enable I2S mclk clock: %d\n", ret);
+               goto err;
+       }
+
+       if (of_property_read_bool(pdev->dev.of_node, "DMIC")) {
+               i2s->audin_fifo_src = 3;
+               dev_info(&pdev->dev, "DMIC is in platform!\n");
+       } else {
+               i2s->audin_fifo_src = 1;
+               dev_info(&pdev->dev, "I2S Mic is in platform!\n");
+       }
+
+       ret = snd_soc_register_component(&pdev->dev, &aml_component,
+                                         aml_i2s_dai, ARRAY_SIZE(aml_i2s_dai));
+       if (ret) {
+               dev_err(&pdev->dev, "Can't register i2s dai: %d\n", ret);
+               goto err_clk_dis;
+       }
+       return 0;
+
+err_clk_dis:
+       clk_disable_unprepare(i2s->clk_mclk);
+err:
+       return ret;
+}
+
+static int aml_i2s_dai_remove(struct platform_device *pdev)
+{
+       struct aml_i2s *i2s = dev_get_drvdata(&pdev->dev);
+
+       snd_soc_unregister_component(&pdev->dev);
+       clk_disable_unprepare(i2s->clk_mclk);
+
+       return 0;
+}
+
+static void aml_i2s_dai_shutdown(struct platform_device *pdev)
+{
+       struct aml_i2s *i2s = dev_get_drvdata(&pdev->dev);
+
+       if (i2s && i2s->clk_mclk)
+               clk_disable_unprepare(i2s->clk_mclk);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_dai_dt_match[] = {
+       {.compatible = "amlogic, aml-i2s-dai",},
+       {},
+};
+#else
+#define amlogic_dai_dt_match NULL
+#endif
+
+static struct platform_driver aml_i2s_dai_driver = {
+       .driver = {
+                  .name = "aml-i2s-dai",
+                  .owner = THIS_MODULE,
+                  .of_match_table = amlogic_dai_dt_match,
+                  },
+
+       .probe = aml_i2s_dai_probe,
+       .remove = aml_i2s_dai_remove,
+       .shutdown = aml_i2s_dai_shutdown,
+};
+
+static int __init aml_i2s_dai_modinit(void)
+{
+       return platform_driver_register(&aml_i2s_dai_driver);
+}
+
+module_init(aml_i2s_dai_modinit);
+
+static void __exit aml_i2s_dai_modexit(void)
+{
+       platform_driver_unregister(&aml_i2s_dai_driver);
+}
+
+module_exit(aml_i2s_dai_modexit);
+
+/* Module information */
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_DESCRIPTION("AML DAI driver for ALSA");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/amlogic/aml_i2s_dai.h b/sound/soc/amlogic/aml_i2s_dai.h
new file mode 100644 (file)
index 0000000..eee31d2
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * sound/soc/amlogic/aml_i2s_dai.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AML_I2S_DAI_H__
+#define __AML_I2S_DAI_H__
+
+extern struct snd_soc_dai_driver aml_dai[];
+struct aml_i2s {
+       struct clk *clk_mpll;
+       struct clk *clk_mclk;
+       int old_samplerate;
+       bool disable_clk_suspend;
+       int audin_fifo_src;
+};
+
+#endif
diff --git a/sound/soc/amlogic/aml_meson.c b/sound/soc/amlogic/aml_meson.c
new file mode 100644 (file)
index 0000000..20be66f
--- /dev/null
@@ -0,0 +1,801 @@
+/*
+ * sound/soc/amlogic/aml_meson.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#define pr_fmt(fmt) "aml_snd_card: " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+/* #include <sound/soc-dapm.h> */
+#include <sound/jack.h>
+
+#if 0 /*tmp_mask_for_kernel_4_4*/
+#include <linux/switch.h>
+#include <linux/amlogic/jtag.h>
+#endif
+/* #include <linux/amlogic/saradc.h> */
+#include <linux/amlogic/iomap.h>
+
+#include "aml_i2s.h"
+#include "aml_meson.h"
+#include "aml_audio_hw.h"
+#include <linux/amlogic/media/sound/audin_regs.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+
+
+#define DRV_NAME "aml_meson_snd_card"
+
+static int i2sbuf[32 + 16];
+static void aml_i2s_play(void)
+{
+       audio_util_set_dac_i2s_format(AUDIO_ALGOUT_DAC_FORMAT_DSP);
+#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
+       audio_set_i2s_mode(AIU_I2S_MODE_PCM16, 2);
+#else
+       audio_set_i2s_mode(AIU_I2S_MODE_PCM16);
+#endif
+       memset(i2sbuf, 0, sizeof(i2sbuf));
+       audio_set_aiubuf((virt_to_phys(i2sbuf) + 63) & (~63), 128, 2);
+       audio_out_i2s_enable(1);
+
+}
+
+static void aml_audio_start_timer(struct aml_audio_private_data *p_aml_audio,
+                                 unsigned long delay)
+{
+       p_aml_audio->timer.expires = jiffies + delay;
+       p_aml_audio->timer.data = (unsigned long)p_aml_audio;
+       p_aml_audio->detect_flag = -1;
+       add_timer(&p_aml_audio->timer);
+       p_aml_audio->timer_en = 1;
+}
+
+static void aml_audio_stop_timer(struct aml_audio_private_data *p_aml_audio)
+{
+       del_timer_sync(&p_aml_audio->timer);
+       cancel_work_sync(&p_aml_audio->work);
+       p_aml_audio->timer_en = 0;
+       p_aml_audio->detect_flag = -1;
+}
+
+static int hp_det_adc_value(struct aml_audio_private_data *p_aml_audio)
+{
+       int ret, hp_value;
+       int hp_val_sum = 0;
+       int loop_num = 0;
+       unsigned int mic_ret = 0;
+
+       while (loop_num < 8) {
+               /* hp_value = get_adc_sample(p_aml_audio->hp_adc_ch); */
+               if (hp_value < 0) {
+                       pr_info("hp detect get error adc value!\n");
+                       return -1;      /* continue; */
+               }
+               hp_val_sum += hp_value;
+               loop_num++;
+               msleep_interruptible(15);
+       }
+       hp_val_sum = hp_val_sum >> 3;
+       /* pr_info("00000000000hp_val_sum = %hx\n",hp_val_sum); */
+       if (hp_val_sum >= p_aml_audio->hp_val_h) {
+               ret = 0;
+       } else if ((hp_val_sum < (p_aml_audio->hp_val_l)) && hp_val_sum >= 0) {
+               ret = 1;
+               if (p_aml_audio->mic_det) {
+                       if (hp_val_sum <= p_aml_audio->mic_val) {
+                               mic_ret = 8;
+                               ret |= mic_ret;
+                       }
+               }
+       } else {
+               ret = 2;
+               if (p_aml_audio->mic_det) {
+                       ret = 0;
+                       mic_ret = 8;
+                       ret |= mic_ret;
+               }
+
+       }
+
+       return ret;
+}
+
+static int aml_audio_hp_detect(struct aml_audio_private_data *p_aml_audio)
+{
+       int loop_num = 0;
+       int ret;
+
+       p_aml_audio->hp_det_status = false;
+       /* mutex_lock(&p_aml_audio->lock); */
+
+       while (loop_num < 3) {
+               ret = hp_det_adc_value(p_aml_audio);
+               if (p_aml_audio->hp_last_state != ret) {
+                       msleep_interruptible(50);
+                       if (ret < 0)
+                               ret = p_aml_audio->hp_last_state;
+                       else
+                               p_aml_audio->hp_last_state = ret;
+               } else
+                       msleep_interruptible(50);
+
+               loop_num = loop_num + 1;
+       }
+
+       /* mutex_unlock(&p_aml_audio->lock); */
+
+       return ret;
+}
+
+static void aml_asoc_work_func(struct work_struct *work)
+{
+       struct aml_audio_private_data *p_aml_audio = NULL;
+       struct snd_soc_card *card = NULL;
+       int flag = -1;
+       int status = SND_JACK_HEADPHONE;
+
+       p_aml_audio = container_of(work, struct aml_audio_private_data, work);
+       card = (struct snd_soc_card *)p_aml_audio->data;
+
+       flag = aml_audio_hp_detect(p_aml_audio);
+
+       if (p_aml_audio->detect_flag != flag) {
+
+               p_aml_audio->detect_flag = flag;
+
+               if (flag & 0x1) {
+                       /* 1 :have mic ;  2 no mic */
+#if 0 /*tmp_mask_for_kernel_4_4*/
+                       switch_set_state(&p_aml_audio->sdev, 2);
+#endif
+                       pr_info("aml aduio hp pluged 3 jack_type: %d\n",
+                              SND_JACK_HEADPHONE);
+                       snd_soc_jack_report(&p_aml_audio->jack, status,
+                                           SND_JACK_HEADPHONE);
+
+                       /* mic port detect */
+                       if (p_aml_audio->mic_det) {
+                               if (flag & 0x8) {
+#if 0 /*tmp_mask_for_kernel_4_4*/
+                                       switch_set_state(&p_aml_audio->mic_sdev,
+                                                        1);
+#endif
+                                       pr_info("aml aduio mic pluged jack_type: %d\n",
+                                              SND_JACK_MICROPHONE);
+                                       snd_soc_jack_report(&p_aml_audio->jack,
+                                               status, SND_JACK_HEADPHONE);
+                               }
+                       }
+
+               } else if (flag & 0x2) {
+                       /* 1 :have mic ;  2 no mic */
+#if 0 /*tmp_mask_for_kernel_4_4*/
+                       switch_set_state(&p_aml_audio->sdev, 1);
+#endif
+                       pr_info("aml aduio hp pluged 4 jack_type: %d\n",
+                              SND_JACK_HEADSET);
+                       snd_soc_jack_report(&p_aml_audio->jack, status,
+                                           SND_JACK_HEADPHONE);
+               } else {
+                       pr_info("aml audio hp unpluged\n");
+#if 0 /*tmp_mask_for_kernel_4_4*/
+                       switch_set_state(&p_aml_audio->sdev, 0);
+#endif
+                       snd_soc_jack_report(&p_aml_audio->jack, 0,
+                                           SND_JACK_HEADPHONE);
+
+                       /* mic port detect */
+                       if (p_aml_audio->mic_det) {
+                               if (flag & 0x8) {
+#if 0 /*tmp_mask_for_kernel_4_4*/
+                                       switch_set_state(&p_aml_audio->mic_sdev,
+                                                        1);
+#endif
+                                       pr_info("aml aduio mic pluged jack_type: %d\n",
+                                              SND_JACK_MICROPHONE);
+                                       snd_soc_jack_report(&p_aml_audio->jack,
+                                               status, SND_JACK_HEADPHONE);
+                               }
+                       }
+               }
+
+       }
+       p_aml_audio->hp_det_status = true;
+}
+
+static void aml_asoc_timer_func(unsigned long data)
+{
+       struct aml_audio_private_data *p_aml_audio =
+           (struct aml_audio_private_data *)data;
+       unsigned long delay = msecs_to_jiffies(150);
+
+       if (p_aml_audio->hp_det_status &&
+                       !p_aml_audio->suspended) {
+               schedule_work(&p_aml_audio->work);
+       }
+       mod_timer(&p_aml_audio->timer, jiffies + delay);
+}
+
+static struct aml_audio_private_data *p_audio;
+static int aml_spk_enabled;
+
+static int aml_set_spk(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       aml_spk_enabled = ucontrol->value.integer.value[0];
+       pr_info("aml_set_spk: aml_spk_enabled=%d\n",
+              aml_spk_enabled);
+
+       msleep_interruptible(10);
+       if (!IS_ERR(p_audio->mute_desc))
+               gpiod_direction_output(p_audio->mute_desc, aml_spk_enabled);
+
+       if (aml_spk_enabled == 1)
+               msleep_interruptible(100);
+
+       return 0;
+}
+
+static int aml_get_spk(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = aml_spk_enabled;
+       return 0;
+}
+
+static int aml_suspend_pre(struct snd_soc_card *card)
+{
+       struct aml_audio_private_data *p_aml_audio;
+       struct pinctrl_state *state;
+       int val = 0;
+
+       pr_info("enter %s\n", __func__);
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+       if (!p_aml_audio->hp_disable) {
+               /* stop timer */
+               mutex_lock(&p_aml_audio->lock);
+               p_aml_audio->suspended = true;
+               if (p_aml_audio->timer_en)
+                       aml_audio_stop_timer(p_aml_audio);
+
+               mutex_unlock(&p_aml_audio->lock);
+       }
+
+       if (IS_ERR_OR_NULL(p_aml_audio->pin_ctl)) {
+               pr_info("no audio pin_ctrl to suspend\n");
+               return 0;
+       }
+
+       state = pinctrl_lookup_state(p_aml_audio->pin_ctl, "aml_snd_suspend");
+       if (!IS_ERR(state)) {
+               pr_info("enter %s set pin_ctl suspend state\n", __func__);
+               pinctrl_select_state(p_aml_audio->pin_ctl, state);
+       }
+
+       if (!IS_ERR(p_aml_audio->mute_desc)) {
+               val = p_aml_audio->mute_inv ?
+                       GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
+               gpiod_direction_output(p_aml_audio->mute_desc, val);
+       };
+
+       return 0;
+}
+
+static int aml_suspend_post(struct snd_soc_card *card)
+{
+       pr_info("enter %s\n", __func__);
+       return 0;
+}
+
+static int aml_resume_pre(struct snd_soc_card *card)
+{
+       pr_info("enter %s\n", __func__);
+
+       return 0;
+}
+
+static int aml_resume_post(struct snd_soc_card *card)
+{
+       struct aml_audio_private_data *p_aml_audio;
+       struct pinctrl_state *state;
+       int val = 0;
+
+       pr_info("enter %s\n", __func__);
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+       if (!p_aml_audio->hp_disable) {
+               mutex_lock(&p_aml_audio->lock);
+               p_aml_audio->suspended = false;
+               if (!p_aml_audio->timer_en) {
+                       aml_audio_start_timer(p_aml_audio,
+                                             msecs_to_jiffies(100));
+               }
+               mutex_unlock(&p_aml_audio->lock);
+       }
+
+       if (IS_ERR_OR_NULL(p_aml_audio->pin_ctl)) {
+               pr_info("no audio pin_ctrl to resume\n");
+               return 0;
+       }
+
+       state = pinctrl_lookup_state(p_aml_audio->pin_ctl, "audio_i2s_pins");
+       if (!IS_ERR(state)) {
+               pr_info("enter %s set pin_ctl working state\n", __func__);
+               pinctrl_select_state(p_aml_audio->pin_ctl, state);
+       }
+
+       if (!IS_ERR(p_aml_audio->mute_desc)) {
+               if (p_aml_audio->sleep_time)
+                       msleep(p_aml_audio->sleep_time);
+               val = p_aml_audio->mute_inv ?
+                       GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+               gpiod_direction_output(p_aml_audio->mute_desc, val);
+       }
+       return 0;
+}
+
+static int speaker_events(struct snd_soc_dapm_widget *w,
+                         struct snd_kcontrol *kcontrol, int event)
+{
+       int val = 0;
+
+       if (IS_ERR(p_audio->mute_desc)) {
+               pr_info("no mute_gpio setting");
+               return 0;
+       }
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               pr_info("audio speaker on\n");
+               val = p_audio->mute_inv ?
+                       GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
+               gpiod_direction_output(p_audio->mute_desc, 1);
+               aml_spk_enabled = 1;
+               msleep(p_audio->sleep_time);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               pr_info("audio speaker off\n");
+               val = p_audio->mute_inv ?
+                       GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+               gpiod_direction_output(p_audio->mute_desc, val);
+               aml_spk_enabled = 0;
+               break;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget aml_asoc_dapm_widgets[] = {
+       SND_SOC_DAPM_SPK("Ext Spk", speaker_events),
+       SND_SOC_DAPM_HP("HP", NULL),
+       SND_SOC_DAPM_MIC("MAIN MIC", NULL),
+       SND_SOC_DAPM_MIC("HEADSET MIC", NULL),
+};
+
+static const struct snd_kcontrol_new aml_asoc_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static struct snd_soc_jack_pin jack_pins[] = {
+       {
+               .pin = "HP",
+               .mask = SND_JACK_HEADPHONE,
+       }
+};
+
+
+static const struct snd_kcontrol_new aml_controls[] = {
+       SOC_SINGLE_BOOL_EXT("Ext Spk Switch", 0,
+                           aml_get_spk,
+                           aml_set_spk),
+};
+
+static int aml_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_card *card = rtd->card;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_dapm_context *dapm = &codec->component.dapm;
+       struct aml_audio_private_data *p_aml_audio;
+       int ret = 0;
+       int hp_paraments[5];
+
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+       ret = snd_soc_add_card_controls(card, aml_controls,
+                                       ARRAY_SIZE(aml_controls));
+       if (ret)
+               return ret;
+       /* Add specific widgets */
+       snd_soc_dapm_new_controls(dapm, aml_asoc_dapm_widgets,
+                                 ARRAY_SIZE(aml_asoc_dapm_widgets));
+       p_aml_audio->hp_disable =
+           of_property_read_bool(card->dev->of_node, "hp_disable");
+
+       pr_info("headphone detection disable=%d\n", p_aml_audio->hp_disable);
+
+       if (!p_aml_audio->hp_disable) {
+               /* for report headphone to android */
+
+#if 0 /*tmp_mask_for_kernel_4_4*/
+               p_aml_audio->sdev.name = "h2w";
+               ret = switch_dev_register(&p_aml_audio->sdev);
+               if (ret < 0) {
+                       pr_err("ASoC: register hp switch dev failed\n");
+                       return ret;
+               }
+
+
+               /* for micphone detect */
+               p_aml_audio->mic_sdev.name = "mic_dev";
+               ret = switch_dev_register(&p_aml_audio->mic_sdev);
+               if (ret < 0) {
+                       pr_err("ASoC: register mic switch dev failed\n");
+                       return ret;
+               }
+#endif
+               ret = snd_soc_card_jack_new(card,
+                       "hp switch", SND_JACK_HEADPHONE,
+                       &p_aml_audio->jack, jack_pins, ARRAY_SIZE(jack_pins));
+               if (ret) {
+                       pr_info("Failed to alloc resource for hp switch\n");
+                       return ret;
+               }
+
+               p_aml_audio->hp_det_status = true;
+               p_aml_audio->mic_det =
+                   of_property_read_bool(card->dev->of_node, "mic_det");
+
+               pr_info("entern %s : mic_det=%d\n", __func__,
+                      p_aml_audio->mic_det);
+               ret =
+                   of_property_read_u32_array(card->dev->of_node,
+                                              "hp_paraments", &hp_paraments[0],
+                                              5);
+               if (ret)
+                       pr_info("falied to get hp detect paraments\n");
+               else {
+                       /* hp adc value higher base, hp unplugged */
+                       p_aml_audio->hp_val_h = hp_paraments[0];
+                       /* hp adc value low base, 3 section hp plugged. */
+                       p_aml_audio->hp_val_l = hp_paraments[1];
+                       /* hp adc value mic detect value. */
+                       p_aml_audio->mic_val = hp_paraments[2];
+                       /* hp adc value test toerance */
+                       p_aml_audio->hp_detal = hp_paraments[3];
+                       /* get adc value from which adc port for hp detect */
+                       p_aml_audio->hp_adc_ch = hp_paraments[4];
+
+                       pr_info("hp detect paraments: h=%d,     l=%d,mic=%d,det=%d,ch=%d\n",
+                               p_aml_audio->hp_val_h, p_aml_audio->hp_val_l,
+                               p_aml_audio->mic_val, p_aml_audio->hp_detal,
+                               p_aml_audio->hp_adc_ch);
+               }
+
+               init_timer(&p_aml_audio->timer);
+               p_aml_audio->timer.function = aml_asoc_timer_func;
+               p_aml_audio->timer.data = (unsigned long)p_aml_audio;
+               p_aml_audio->data = (void *)card;
+               p_aml_audio->suspended = false;
+
+               INIT_WORK(&p_aml_audio->work, aml_asoc_work_func);
+               mutex_init(&p_aml_audio->lock);
+
+               mutex_lock(&p_aml_audio->lock);
+               if (!p_aml_audio->timer_en) {
+                       aml_audio_start_timer(p_aml_audio,
+                                             msecs_to_jiffies(100));
+               }
+               mutex_unlock(&p_aml_audio->lock);
+       }
+
+       ret =
+           of_property_read_u32(card->dev->of_node, "sleep_time",
+                                &p_aml_audio->sleep_time);
+       if (ret)
+               pr_info("no spk event delay time set\n");
+
+       return 0;
+}
+
+static void aml_pinmux_init(struct snd_soc_card *card)
+{
+       struct aml_audio_private_data *p_aml_audio;
+       int val;
+
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+       p_aml_audio->mute_desc = gpiod_get(
+               card->dev, "mute_gpio", GPIOD_OUT_LOW);
+       p_aml_audio->mute_inv =
+           of_property_read_bool(card->dev->of_node, "mute_inv");
+       if (!IS_ERR(p_aml_audio->mute_desc)) {
+               if (p_aml_audio->sleep_time)
+                       msleep(p_aml_audio->sleep_time);
+               val = p_aml_audio->mute_inv ?
+                       GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+               gpiod_direction_output(p_aml_audio->mute_desc, val);
+       }
+
+#if 0 /*tmp_mask_for_kernel_4_4*/
+       if (is_jtag_apao())
+               return;
+
+       val = aml_read_sec_reg(0xda004004);
+       pr_info("audio use jtag pinmux as i2s output, read val =%x\n",
+               aml_read_sec_reg(0xda004004));
+       val = val & (~((1<<8) | (1<<1)));
+       aml_write_sec_reg(0xda004004, val);
+#endif
+
+       p_aml_audio->pin_ctl = devm_pinctrl_get_select(
+               card->dev, "audio_i2s_pins");
+       if (IS_ERR(p_aml_audio->pin_ctl)) {
+               pr_info("%s,aml_pinmux_init error!\n", __func__);
+               return;
+       }
+}
+
+static int aml_card_dai_parse_of(struct device *dev,
+                                struct snd_soc_dai_link *dai_link,
+                                int (*init)(struct snd_soc_pcm_runtime *rtd),
+                                struct device_node *cpu_node,
+                                struct device_node *codec_node,
+                                struct device_node *plat_node)
+{
+       int ret;
+
+       /* get cpu dai->name */
+       ret = snd_soc_of_get_dai_name(cpu_node, &dai_link->cpu_dai_name);
+       if (ret < 0)
+               goto parse_error;
+
+       /* get codec dai->name */
+       ret = snd_soc_of_get_dai_name(codec_node, &dai_link->codec_dai_name);
+       if (ret < 0)
+               goto parse_error;
+
+       dai_link->name = dai_link->stream_name = dai_link->cpu_dai_name;
+       dai_link->codec_of_node = of_parse_phandle(codec_node, "sound-dai", 0);
+       dai_link->platform_of_node = plat_node;
+       dai_link->init = init;
+
+       return 0;
+
+ parse_error:
+       return ret;
+}
+
+static int aml_card_dais_parse_of(struct snd_soc_card *card)
+{
+       struct device_node *np = card->dev->of_node;
+       struct device_node *cpu_node, *codec_node, *plat_node;
+       struct device *dev = card->dev;
+       struct snd_soc_dai_link *dai_links;
+       int num_dai_links, cpu_num, codec_num, plat_num;
+       int i, ret;
+       int (*init)(struct snd_soc_pcm_runtime *rtd);
+
+       ret = of_count_phandle_with_args(np, "cpu_list", NULL);
+       if (ret < 0) {
+               dev_err(dev, "AML sound card no cpu_list errno: %d\n", ret);
+               goto err;
+       } else {
+               cpu_num = ret;
+       }
+
+       ret = of_count_phandle_with_args(np, "codec_list", NULL);
+       if (ret < 0) {
+               dev_err(dev, "AML sound card no codec_list errno: %d\n", ret);
+               goto err;
+       } else {
+               codec_num = ret;
+       }
+
+       ret = of_count_phandle_with_args(np, "plat_list", NULL);
+       if (ret < 0) {
+               dev_err(dev, "AML sound card no plat_list errno: %d\n", ret);
+               goto err;
+       } else {
+               plat_num = ret;
+       }
+
+       if ((cpu_num == codec_num) && (cpu_num == plat_num)) {
+               num_dai_links = cpu_num;
+       } else {
+               dev_err(dev,
+                       "AML sound card cpu_dai num, codec_dai num, platform num don't match: %d\n",
+                       ret);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       dai_links =
+           devm_kzalloc(dev, num_dai_links * sizeof(struct snd_soc_dai_link),
+                        GFP_KERNEL);
+       if (!dai_links) {
+               dev_err(dev, "Can't allocate snd_soc_dai_links\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+       card->dai_link = dai_links;
+       card->num_links = num_dai_links;
+       for (i = 0; i < num_dai_links; i++) {
+               init = NULL;
+               /* CPU sub-node */
+               cpu_node = of_parse_phandle(np, "cpu_list", i);
+               if (cpu_node < 0) {
+                       dev_err(dev, "parse aml sound card cpu list error\n");
+                       return -EINVAL;
+               }
+               /* CODEC sub-node */
+               codec_node = of_parse_phandle(np, "codec_list", i);
+               if (codec_node < 0) {
+                       dev_err(dev, "parse aml sound card codec list error\n");
+                       return ret;
+               }
+               /* Platform sub-node */
+               plat_node = of_parse_phandle(np, "plat_list", i);
+               if (plat_node < 0) {
+                       dev_err(dev,
+                               "parse aml sound card platform list error\n");
+                       return ret;
+               }
+               if (i == 0)
+                       init = aml_asoc_init;
+
+               ret =
+                   aml_card_dai_parse_of(dev, &dai_links[i], init, cpu_node,
+                                         codec_node, plat_node);
+       }
+
+ err:
+       return ret;
+}
+
+static void aml_pinmux_work_func(struct work_struct *pinmux_work)
+{
+       struct aml_audio_private_data *p_aml_audio = NULL;
+       struct snd_soc_card *card = NULL;
+
+       p_aml_audio = container_of(pinmux_work,
+                                 struct aml_audio_private_data, pinmux_work);
+       card = (struct snd_soc_card *)p_aml_audio->data;
+
+       aml_pinmux_init(card);
+}
+
+static int aml_audio_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct snd_soc_card *card = NULL;
+       struct aml_audio_private_data *p_aml_audio;
+       int ret;
+
+       p_aml_audio =
+           devm_kzalloc(dev, sizeof(struct aml_audio_private_data),
+                        GFP_KERNEL);
+       if (!p_aml_audio) {
+               dev_err(&pdev->dev, "Can't allocate aml_audio_private_data\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+       p_audio = p_aml_audio;
+
+       card = devm_kzalloc(dev, sizeof(struct snd_soc_card), GFP_KERNEL);
+       if (!card) {
+               /*dev_err(dev, "Can't allocate snd_soc_card\n");*/
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       snd_soc_card_set_drvdata(card, p_aml_audio);
+       card->dev = dev;
+       platform_set_drvdata(pdev, card);
+       ret = snd_soc_of_parse_card_name(card, "aml_sound_card,name");
+       if (ret < 0) {
+               dev_err(dev, "no specific snd_soc_card name\n");
+               goto err;
+       }
+       /* DAPM routes */
+       if (of_property_read_bool(np, "aml,audio-routing")) {
+               ret = snd_soc_of_parse_audio_routing(card, "aml,audio-routing");
+               if (ret < 0) {
+                       dev_err(dev, "parse aml sound card routing error %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+       ret = aml_card_dais_parse_of(card);
+       if (ret < 0) {
+               dev_err(dev, "parse aml sound card routing error %d\n",
+                       ret);
+               goto err;
+       }
+
+       card->suspend_pre = aml_suspend_pre,
+       card->suspend_post = aml_suspend_post,
+       card->resume_pre = aml_resume_pre,
+       card->resume_post = aml_resume_post,
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret < 0) {
+               dev_err(dev, "register aml sound card error %d\n", ret);
+               goto err;
+       }
+
+       aml_i2s_play();
+
+       p_aml_audio->data = (void *)card;
+       INIT_WORK(&p_aml_audio->pinmux_work, aml_pinmux_work_func);
+       schedule_work(&p_aml_audio->pinmux_work);
+
+       /*aml_pinmux_init(card);*/
+       return 0;
+ err:
+       dev_err(dev, "Can't probe snd_soc_card\n");
+       return ret;
+}
+
+static void aml_audio_shutdown(struct platform_device *pdev)
+{
+       struct pinctrl_state *state;
+
+       if (IS_ERR_OR_NULL(p_audio->pin_ctl)) {
+               pr_info("no audio pin_ctrl to shutdown\n");
+               return;
+       }
+
+       state = pinctrl_lookup_state(p_audio->pin_ctl, "aml_snd_suspend");
+       if (!IS_ERR(state))
+               pinctrl_select_state(p_audio->pin_ctl, state);
+}
+
+static const struct of_device_id amlogic_audio_of_match[] = {
+       {.compatible = "aml, meson-snd-card",},
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, amlogic_audio_dt_match);
+
+static struct platform_driver aml_audio_driver = {
+       .driver = {
+                  .name = DRV_NAME,
+                  .owner = THIS_MODULE,
+                  .of_match_table = amlogic_audio_of_match,
+                  .pm = &snd_soc_pm_ops,
+                  },
+       .probe = aml_audio_probe,
+       .shutdown = aml_audio_shutdown,
+};
+
+module_platform_driver(aml_audio_driver);
+
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_DESCRIPTION("AML_MESON audio machine Asoc driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amlogic/aml_meson.h b/sound/soc/amlogic/aml_meson.h
new file mode 100644 (file)
index 0000000..11beaee
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * sound/soc/amlogic/aml_meson.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef AML_MESON_H
+#define AML_MESON_H
+
+#include <sound/soc.h>
+#include <linux/gpio/consumer.h>
+struct aml_audio_private_data {
+#if 0
+
+       const char *name;
+       const char *card;
+       const char *codec;
+       const char *platform;
+
+       unsigned int daifmt;
+       struct aml_audio_dai *cpu_dai;
+       struct aml_audio_dai *codec_dai;
+
+       struct snd_soc_dai_link *snd_link;
+       int num_links;
+       struct snd_soc_card snd_card;
+#endif
+       int bias_level;
+       int clock_en;
+       int gpio_hp_det;
+       bool det_pol_inv;
+       int sleep_time;
+       int gpio_mute;
+       int gpio_i2s_m;
+       int gpio_i2s_s;
+       int gpio_i2s_r;
+       int gpio_i2s_o;
+       bool mute_inv;
+       struct pinctrl *pin_ctl;
+       int hp_last_state;
+       bool hp_det_status;
+       unsigned int hp_val_h;
+       unsigned int hp_val_l;
+       unsigned int mic_val;
+       unsigned int hp_detal;
+       unsigned int hp_adc_ch;
+       bool suspended;
+       bool mic_det;
+       bool hp_disable;
+       int timer_en;
+       int detect_flag;
+       struct timer_list timer;
+       struct work_struct work;
+       struct mutex lock;
+       struct snd_soc_jack jack;
+       void *data;
+       struct gpio_desc *mute_desc;
+#if 0 /*tmp_mask_for_kernel_4_4*/
+       struct switch_dev sdev; /* for android */
+       struct switch_dev mic_sdev; /* for android */
+#endif
+       struct work_struct pinmux_work;
+};
+
+#endif
diff --git a/sound/soc/amlogic/aml_notify.c b/sound/soc/amlogic/aml_notify.c
new file mode 100644 (file)
index 0000000..4a87930
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * sound/soc/amlogic/aml_notify.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+
+static BLOCKING_NOTIFIER_HEAD(aout_notifier_list);
+/**
+ *     aout_register_client - register a client notifier
+ *     @nb: notifier block to callback on events
+ */
+int aout_register_client(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&aout_notifier_list, nb);
+}
+EXPORT_SYMBOL(aout_register_client);
+
+/**
+ *     aout_unregister_client - unregister a client notifier
+ *     @nb: notifier block to callback on events
+ */
+int aout_unregister_client(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&aout_notifier_list, nb);
+}
+EXPORT_SYMBOL(aout_unregister_client);
+
+/**
+ * aout_notifier_call_chain - notify clients of fb_events
+ *
+ */
+int aout_notifier_call_chain(unsigned long val, void *v)
+{
+       return blocking_notifier_call_chain(&aout_notifier_list, val, v);
+}
+EXPORT_SYMBOL_GPL(aout_notifier_call_chain);
+
+
diff --git a/sound/soc/amlogic/aml_pcm.c b/sound/soc/amlogic/aml_pcm.c
new file mode 100644 (file)
index 0000000..62e9ed4
--- /dev/null
@@ -0,0 +1,625 @@
+/*
+ * sound/soc/amlogic/aml_pcm.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#define pr_fmt(fmt) "aml_pcm: " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/soundcard.h>
+#include <linux/timer.h>
+#include <linux/debugfs.h>
+#include <linux/major.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include <linux/amlogic/media/sound/audin_regs.h>
+#include "aml_pcm.h"
+#include "aml_audio_hw_pcm.h"
+
+/*--------------------------------------------------------------------------
+ * Hardware definition
+ *--------------------------------------------------------------------------
+ * TODO: These values were taken from the AML platform driver, check
+ *      them against real values for AML
+ */
+static const struct snd_pcm_hardware aml_pcm_hardware = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+           SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE,
+       .formats =
+           SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+           SNDRV_PCM_FMTBIT_S32_LE,
+       .period_bytes_min = 32,
+       .period_bytes_max = 8 * 1024,
+       .periods_min = 2,
+       .periods_max = 1024,
+       .buffer_bytes_max = 64 * 1024,
+       .rate_min = 8000,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 8,
+};
+
+static const struct snd_pcm_hardware aml_pcm_capture = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+           SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE,
+
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .period_bytes_min = 64,
+       .period_bytes_max = 32 * 1024,
+       .periods_min = 2,
+       .periods_max = 1024,
+       .buffer_bytes_max = 64 * 1024,
+
+       .rate_min = 8000,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 8,
+       .fifo_size = 0,
+};
+
+
+static unsigned int period_sizes[] = { 64, 128, 256, 512,
+       1024, 2048, 4096, 8192
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
+       .count = ARRAY_SIZE(period_sizes),
+       .list = period_sizes,
+       .mask = 0
+};
+
+static unsigned int aml_pcm_offset_tx(struct aml_pcm_runtime_data *prtd)
+{
+       unsigned int value = 0;
+       signed int diff = 0;
+
+       value = pcm_out_rd_ptr();
+       diff = value - prtd->buffer_start;
+       if (diff < 0)
+               diff = 0;
+       else if (diff >= prtd->buffer_size)
+               diff = prtd->buffer_size;
+
+       pr_debug("%s value: 0x%08x offset: 0x%08x\n", __func__,
+                 value, diff);
+       return (unsigned int)diff;
+}
+
+static unsigned int aml_pcm_offset_rx(struct aml_pcm_runtime_data *prtd)
+{
+       unsigned int value = 0;
+       signed int diff = 0;
+
+       value = pcm_in_wr_ptr();
+       diff = value - prtd->buffer_start;
+       if (diff < 0)
+               diff = 0;
+       else if (diff >= prtd->buffer_size)
+               diff = prtd->buffer_size;
+
+       pr_debug("%s value: 0x%08x offset: 0x%08x\n", __func__,
+                 value, diff);
+       return (unsigned int)diff;
+}
+
+static void aml_pcm_timer_update(struct aml_pcm_runtime_data *prtd)
+{
+       struct snd_pcm_substream *substream = prtd->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int offset = 0;
+       unsigned int size = 0;
+
+       if (prtd->running && snd_pcm_running(substream)) {
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       offset = aml_pcm_offset_tx(prtd);
+                       if (offset < prtd->buffer_offset) {
+                               size =
+                                   prtd->buffer_size + offset -
+                                   prtd->buffer_offset;
+                       } else {
+                               size = offset - prtd->buffer_offset;
+                       }
+               } else {
+                       int rx_overflow = 0;
+
+                       offset = aml_pcm_offset_rx(prtd);
+                       if (offset < prtd->buffer_offset) {
+                               size =
+                                   prtd->buffer_size + offset -
+                                   prtd->buffer_offset;
+                       } else
+                               size = offset - prtd->buffer_offset;
+
+                       rx_overflow = pcm_in_fifo_int() & (1 << 2);
+                       if (rx_overflow) {
+                               pr_info("%s AUDIN_FIFO overflow !!\n",
+                                       __func__);
+                       }
+               }
+       }
+
+       prtd->buffer_offset = offset;
+       prtd->data_size += size;
+       if (prtd->data_size >= frames_to_bytes(runtime, runtime->period_size))
+               prtd->period_elapsed++;
+
+}
+
+static void aml_pcm_timer_rearm(struct aml_pcm_runtime_data *prtd)
+{
+       prtd->timer.expires = jiffies + prtd->timer_period;
+       add_timer(&prtd->timer);
+}
+
+static int aml_pcm_timer_start(struct aml_pcm_runtime_data *prtd)
+{
+       pr_info("%s\n", __func__);
+       spin_lock(&prtd->lock);
+       aml_pcm_timer_rearm(prtd);
+       prtd->running = 1;
+       spin_unlock(&prtd->lock);
+       return 0;
+}
+
+static int aml_pcm_timer_stop(struct aml_pcm_runtime_data *prtd)
+{
+       pr_info("%s\n", __func__);
+       spin_lock(&prtd->lock);
+       prtd->running = 0;
+       del_timer(&prtd->timer);
+       spin_unlock(&prtd->lock);
+       return 0;
+}
+
+static void aml_pcm_timer_callback(unsigned long data)
+{
+       struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_pcm_runtime_data *prtd = runtime->private_data;
+       unsigned long flags;
+       unsigned int elapsed = 0;
+       unsigned int datasize = 0;
+
+       spin_lock_irqsave(&prtd->lock, flags);
+       aml_pcm_timer_update(prtd);
+       aml_pcm_timer_rearm(prtd);
+       elapsed = prtd->period_elapsed;
+       datasize = prtd->data_size;
+       if (elapsed) {
+               prtd->period_elapsed--;
+               prtd->data_size -=
+                   frames_to_bytes(runtime, runtime->period_size);
+       }
+       spin_unlock_irqrestore(&prtd->lock, flags);
+       if (elapsed) {
+               if (elapsed > 1)
+                       pr_info("PCM timer callback not fast enough!\n");
+
+               snd_pcm_period_elapsed(prtd->substream);
+       }
+}
+
+static int aml_pcm_timer_create(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_pcm_runtime_data *prtd = runtime->private_data;
+
+       pr_info("%s\n", __func__);
+       init_timer(&prtd->timer);
+       prtd->timer_period = 1;
+       prtd->timer.data = (unsigned long)substream;
+       prtd->timer.function = aml_pcm_timer_callback;
+       prtd->running = 0;
+       return 0;
+}
+
+static int
+aml_pcm_hw_params(struct snd_pcm_substream *substream,
+                    struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_pcm_runtime_data *prtd = runtime->private_data;
+       size_t size = params_buffer_bytes(params);
+       int ret = 0;
+
+       pr_info("enter %s\n", __func__);
+
+       ret = snd_pcm_lib_malloc_pages(substream, size);
+       if (ret < 0)
+               pr_err("%s malloc_pages return: %d\n", __func__, ret);
+       else {
+               prtd->buffer_start = runtime->dma_addr;
+               prtd->buffer_size = runtime->dma_bytes;
+       }
+
+       return ret;
+}
+
+static int aml_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       pr_info("enter %s\n", __func__);
+       snd_pcm_lib_free_pages(substream);
+
+       return 0;
+}
+
+static int aml_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       pr_info("enter %s\n", __func__);
+
+       return 0;
+}
+
+static int aml_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_pcm_runtime_data *prtd = runtime->private_data;
+       int ret = 0;
+
+       pr_info("enter %s\n", __func__);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               aml_pcm_timer_start(prtd);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               aml_pcm_timer_stop(prtd);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t aml_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_pcm_runtime_data *prtd = runtime->private_data;
+       snd_pcm_uframes_t frames;
+
+       /* pr_info("enter %s\n", __func__); */
+       frames = bytes_to_frames(runtime, (ssize_t) prtd->buffer_offset);
+
+       return frames;
+}
+
+static int aml_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_pcm_runtime_data *prtd = NULL;
+       int ret;
+
+       pr_info("enter %s, stream:%d\n", __func__, substream->stream);
+
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_soc_set_runtime_hwparams(substream, &aml_pcm_hardware);
+       else
+               snd_soc_set_runtime_hwparams(substream, &aml_pcm_capture);
+
+       /* Ensure that period size is a multiple of 32bytes */
+       ret =
+           snd_pcm_hw_constraint_list(runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+                                      &hw_constraints_period_sizes);
+       if (ret < 0) {
+               pr_err("set period bytes constraint error\n");
+               goto out;
+       }
+
+       /* Ensure that buffer size is a multiple of period size */
+       ret =
+           snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0) {
+               pr_err("set periods constraint error\n");
+               goto out;
+       }
+
+       prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+       if (prtd == NULL) {
+               /*pr_err("out of memory\n");*/
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       runtime->private_data = prtd;
+       aml_pcm_timer_create(substream);
+       prtd->substream = substream;
+       spin_lock_init(&prtd->lock);
+
+       return 0;
+ out:
+       return ret;
+}
+
+static int aml_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_pcm_runtime_data *prtd = runtime->private_data;
+
+       pr_info("enter %s type: %d\n", __func__, substream->stream);
+       if (prtd)
+               kfree(runtime->private_data);
+
+       return 0;
+}
+
+static int
+aml_pcm_copy_playback(struct snd_pcm_runtime *runtime, int channel,
+                        snd_pcm_uframes_t pos,
+                        void __user *buf, snd_pcm_uframes_t count)
+{
+       struct aml_pcm_runtime_data *prtd = runtime->private_data;
+       unsigned char *hwbuf =
+           runtime->dma_area + frames_to_bytes(runtime, pos);
+       unsigned int wrptr = 0;
+       int ret = 0;
+
+/*     pr_info("enter %s channel: %d pos: %ld count: %ld\n",
+ *               __func__, channel, pos, count);
+ */
+       if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, count))) {
+               pr_err("%s copy from user failed!\n", __func__);
+               return -EFAULT;
+       }
+
+       {
+               wrptr = prtd->buffer_start + frames_to_bytes(runtime, pos)
+                       + frames_to_bytes(runtime, count);
+               if (wrptr >= (prtd->buffer_start + prtd->buffer_size))
+                       wrptr = prtd->buffer_start + prtd->buffer_size;
+
+               pcm_out_set_wr_ptr(wrptr);
+       }
+       return ret;
+}
+
+static int
+aml_pcm_copy_capture(struct snd_pcm_runtime *runtime, int channel,
+                       snd_pcm_uframes_t pos,
+                       void __user *buf, snd_pcm_uframes_t count)
+{
+       struct aml_pcm_runtime_data *prtd = runtime->private_data;
+       signed short *hwbuf =
+           (signed short *)(runtime->dma_area + frames_to_bytes(runtime, pos));
+       unsigned int rdptr = 0;
+       int ret = 0;
+
+/*     pr_info("enter %s channel: %d pos: %ld count: %ld\n",
+ *               __func__, channel, pos, count);
+ */
+       if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, count))) {
+               pr_err("%s copy to user failed!\n", __func__);
+               return -EFAULT;
+       }
+
+       {
+               /* memset(hwbuf, 0xff, frames_to_bytes(runtime, count)); */
+               rdptr =
+                   prtd->buffer_start + frames_to_bytes(runtime,
+                                                        pos) +
+                   frames_to_bytes(runtime, count);
+               if (rdptr >= (prtd->buffer_start + prtd->buffer_size))
+                       rdptr = prtd->buffer_start + prtd->buffer_size;
+
+               pcm_in_set_rd_ptr(rdptr);
+       }
+       return ret;
+}
+
+static int aml_pcm_copy(struct snd_pcm_substream *substream,
+       int channel, snd_pcm_uframes_t pos,
+       void __user *buf, snd_pcm_uframes_t count)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int ret = 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               ret =
+                   aml_pcm_copy_playback(runtime, channel, pos, buf, count);
+       } else {
+               ret =
+                   aml_pcm_copy_capture(runtime, channel, pos, buf, count);
+       }
+
+       return ret;
+}
+
+static int aml_pcm_silence(struct snd_pcm_substream *substream,
+       int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned char *ppos = NULL;
+       ssize_t n;
+
+       pr_info("enter %s\n", __func__);
+       n = frames_to_bytes(runtime, count);
+       ppos = runtime->dma_area + frames_to_bytes(runtime, pos);
+       memset(ppos, 0, n);
+
+       return 0;
+}
+
+static struct snd_pcm_ops aml_pcm_ops = {
+       .open = aml_pcm_open,
+       .close = aml_pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = aml_pcm_hw_params,
+       .hw_free = aml_pcm_hw_free,
+       .prepare = aml_pcm_prepare,
+       .trigger = aml_pcm_trigger,
+       .pointer = aml_pcm_pointer,
+       .copy = aml_pcm_copy,
+       .silence = aml_pcm_silence,
+};
+
+/* --------------------------------------------------------------------------
+ * ASoC platform driver
+ * --------------------------------------------------------------------------
+ */
+
+static u64 aml_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int aml_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       size_t size = aml_pcm_hardware.buffer_bytes_max;
+
+       buf->dev.type = SNDRV_DMA_TYPE_DEV;
+       buf->dev.dev = pcm->card->dev;
+       buf->private_data = NULL;
+       buf->area = dma_alloc_coherent(pcm->card->dev, size,
+                                      &buf->addr, GFP_KERNEL);
+       if (!buf->area) {
+               dev_err(pcm->card->dev, "aml_pcm alloc failed!\n");
+               return -ENOMEM;
+       }
+
+       buf->bytes = size;
+
+       return 0;
+}
+
+static int aml_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+       int ret = 0;
+       struct snd_soc_card *card = rtd->card;
+       struct snd_pcm *pcm = rtd->pcm;
+       struct snd_soc_dai *dai;
+
+       dai = rtd->cpu_dai;
+       pr_info("enter %s dai->name: %s dai->id: %d\n", __func__,
+                 dai->name, dai->id);
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &aml_pcm_dmamask;
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+       if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+               ret = aml_pcm_preallocate_dma_buffer(pcm,
+                               SNDRV_PCM_STREAM_PLAYBACK);
+               if (ret)
+                       goto out;
+       }
+
+       if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+               ret = aml_pcm_preallocate_dma_buffer(pcm,
+                               SNDRV_PCM_STREAM_CAPTURE);
+               if (ret)
+                       goto out;
+       }
+
+ out:
+       return ret;
+}
+
+static void aml_pcm_free(struct snd_pcm *pcm)
+{
+       struct snd_pcm_substream *substream;
+       struct snd_dma_buffer *buf;
+       int stream;
+
+       pr_info("enter %s\n", __func__);
+       for (stream = 0; stream < 2; stream++) {
+               substream = pcm->streams[stream].substream;
+               if (!substream)
+                       continue;
+
+               buf = &substream->dma_buffer;
+               if (!buf->area)
+                       continue;
+
+               dma_free_coherent(pcm->card->dev, buf->bytes, buf->area,
+                                 buf->addr);
+               buf->area = NULL;
+       }
+}
+
+struct snd_soc_platform_driver aml_soc_platform_pcm = {
+       .ops = &aml_pcm_ops,
+       .pcm_new = aml_pcm_new,
+       .pcm_free = aml_pcm_free,
+};
+EXPORT_SYMBOL_GPL(aml_soc_platform_pcm);
+
+static int aml_soc_platform_pcm_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_platform(&pdev->dev, &aml_soc_platform_pcm);
+}
+
+static int aml_soc_platform_pcm_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_platform(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_audio_dt_match[] = {
+       {
+               .compatible = "amlogic, aml-pcm",
+       },
+       {},
+};
+#else
+#define amlogic_audio_dt_match NULL
+#endif
+
+static struct platform_driver aml_platform_pcm_driver = {
+       .driver = {
+                  .name = "aml-pcm",
+                  .owner = THIS_MODULE,
+                  .of_match_table = amlogic_audio_dt_match,
+                  },
+
+       .probe = aml_soc_platform_pcm_probe,
+       .remove = aml_soc_platform_pcm_remove,
+};
+
+static int __init aml_alsa_pcm_init(void)
+{
+       return platform_driver_register(&aml_platform_pcm_driver);
+}
+
+static void __exit aml_alsa_pcm_exit(void)
+{
+       platform_driver_unregister(&aml_platform_pcm_driver);
+}
+
+module_init(aml_alsa_pcm_init);
+module_exit(aml_alsa_pcm_exit);
+
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AML audio driver for ALSA");
diff --git a/sound/soc/amlogic/aml_pcm.h b/sound/soc/amlogic/aml_pcm.h
new file mode 100644 (file)
index 0000000..8eb9dab
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * sound/soc/amlogic/aml_pcm.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AML_PCM_H__
+#define __AML_PCM_H__
+
+struct aml_pcm_runtime_data {
+       spinlock_t                      lock;
+       dma_addr_t          buffer_start;
+       unsigned int        buffer_size;
+       unsigned int        buffer_offset;
+       unsigned int        data_size;
+       unsigned int        running;
+       unsigned int        timer_period;
+       unsigned int        period_elapsed;
+
+       struct timer_list   timer;
+       struct snd_pcm_substream *substream;
+};
+
+#endif
diff --git a/sound/soc/amlogic/aml_pcm_dai.c b/sound/soc/amlogic/aml_pcm_dai.c
new file mode 100644 (file)
index 0000000..88960bb
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * sound/soc/amlogic/aml_pcm_dai.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#define pr_fmt(fmt) "aml_pcm_dai: " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pinctrl/consumer.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "aml_pcm_dai.h"
+#include "aml_pcm.h"
+#include "aml_i2s.h"
+#include "aml_audio_hw_pcm.h"
+
+#include <linux/of.h>
+
+#define DEV_NAME "aml-pcm-dai"
+
+#define AML_DAI_PCM_RATES              (SNDRV_PCM_RATE_8000_192000)
+#define AML_DAI_PCM_FORMATS            (SNDRV_PCM_FMTBIT_S16_LE |\
+       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define PCM_DEFAULT_SAMPLERATE 8000
+#define PCM_DEFAULT_MCLK_RATIO_SR 256
+
+static int aml_pcm_set_clk(struct aml_pcm *pcm, unsigned long rate)
+{
+       int ret = 0;
+
+       ret = clk_set_rate(pcm->clk_mpll, rate * 10);
+       if (ret) {
+               pr_debug("%s, line:%d, error:%d\n", __func__, __LINE__, ret);
+               return ret;
+       }
+       ret = clk_set_parent(pcm->clk_pcm_mclk, pcm->clk_mpll);
+       if (ret) {
+               pr_debug("%s line:%d, error:%d\n", __func__, __LINE__, ret);
+               return ret;
+       }
+
+       ret = clk_set_rate(pcm->clk_pcm_mclk, rate);
+       if (ret) {
+               pr_debug("%s, line:%d, error:%d\n", __func__, __LINE__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int aml_pcm_dai_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       pr_debug("***Entered %s\n", __func__);
+       return 0;
+}
+
+static void aml_pcm_dai_shutdown(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+       pr_debug("***Entered %s\n", __func__);
+}
+
+static int aml_pcm_dai_prepare(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_pcm_runtime_data *prtd = runtime->private_data;
+
+       pr_debug("***Entered %s\n", __func__);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               pr_info(
+                      "%s playback stream buffer start: %ld size: 0x%x\n",
+                      __func__, (long)prtd->buffer_start, prtd->buffer_size);
+               pcm_out_set_buf(prtd->buffer_start, prtd->buffer_size);
+       } else {
+               pr_info(
+                      "%s capture stream buffer start: %ld size: 0x%x\n",
+                      __func__, (long)prtd->buffer_start, prtd->buffer_size);
+               pcm_in_set_buf(prtd->buffer_start, prtd->buffer_size);
+       }
+
+       memset(runtime->dma_area, 0, runtime->dma_bytes);
+       prtd->buffer_offset = 0;
+       prtd->data_size = 0;
+       prtd->period_elapsed = 0;
+
+       return 0;
+}
+
+static int aml_pcm_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
+{
+       struct aml_pcm *pcm_p = dev_get_drvdata(dai->dev);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               /* TODO */
+               if (pcm_p && pcm_p->pcm_mode) {
+                       pr_info("aiu pcm master stream %d enable\n\n",
+                               substream->stream);
+                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               pcm_master_out_enable(substream, 1);
+                       else
+                               pcm_master_in_enable(substream, 1);
+               } else {
+                       pr_info("aiu slave pcm stream %d enable\n\n",
+                               substream->stream);
+                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               pcm_out_enable(1);
+                       else
+                               pcm_in_enable(1);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (pcm_p && pcm_p->pcm_mode) {
+                       pr_info("aiu master pcm stream %d disable\n\n",
+                               substream->stream);
+                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               pcm_master_out_enable(substream, 0);
+                       else
+                               pcm_master_in_enable(substream, 0);
+               } else {
+                       pr_info("aiu slave pcm stream %d disable\n\n",
+                               substream->stream);
+                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               pcm_out_enable(0);
+                       else
+                               pcm_in_enable(0);
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int aml_pcm_dai_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct aml_pcm *pcm = snd_soc_dai_get_drvdata(dai);
+       int srate, mclk_rate;
+
+       srate = params_rate(params);
+       if (pcm->old_samplerate != srate) {
+               pcm->old_samplerate = srate;
+               mclk_rate = srate * PCM_DEFAULT_MCLK_RATIO_SR;
+               aml_pcm_set_clk(pcm, mclk_rate);
+       }
+
+       pr_debug("***Entered %s:%s\n", __FILE__, __func__);
+       return 0;
+}
+
+static int aml_pcm_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       pr_debug("***Entered %s\n", __func__);
+       if (fmt & SND_SOC_DAIFMT_CBS_CFS)
+               snd_soc_dai_get_drvdata(dai);
+       return 0;
+}
+
+static int aml_pcm_dai_set_sysclk(struct snd_soc_dai *dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       pr_debug("***Entered %s\n", __func__);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int aml_pcm_dai_suspend(struct snd_soc_dai *dai)
+{
+
+       pr_debug("***Entered %s\n", __func__);
+       return 0;
+}
+
+static int aml_pcm_dai_resume(struct snd_soc_dai *dai)
+{
+       pr_debug("***Entered %s\n", __func__);
+       return 0;
+}
+
+#else                          /* CONFIG_PM */
+#define aml_pcm_dai_suspend    NULL
+#define aml_pcm_dai_resume     NULL
+#endif                         /* CONFIG_PM */
+
+
+static struct snd_soc_dai_ops aml_pcm_dai_ops = {
+       .startup = aml_pcm_dai_startup,
+       .shutdown = aml_pcm_dai_shutdown,
+       .prepare = aml_pcm_dai_prepare,
+       .trigger = aml_pcm_dai_trigger,
+       .hw_params = aml_pcm_dai_hw_params,
+       .set_fmt = aml_pcm_dai_set_fmt,
+       .set_sysclk = aml_pcm_dai_set_sysclk,
+};
+
+struct snd_soc_dai_driver aml_pcm_dai[] = {
+       {
+        .suspend = aml_pcm_dai_suspend,
+        .resume = aml_pcm_dai_resume,
+        .playback = {
+                     .channels_min = 1,
+                     .channels_max = 8,
+                     .rates = AML_DAI_PCM_RATES,
+                     .formats = AML_DAI_PCM_FORMATS,},
+        .capture = {
+                    .channels_min = 1,
+                    .channels_max = 8,
+                    .rates = AML_DAI_PCM_RATES,
+                    .formats = AML_DAI_PCM_FORMATS,},
+        .ops = &aml_pcm_dai_ops,
+        },
+
+};
+EXPORT_SYMBOL_GPL(aml_pcm_dai);
+
+static const struct snd_soc_component_driver aml_component = {
+       .name = DEV_NAME,
+};
+
+static int aml_pcm_dai_probe(struct platform_device *pdev)
+{
+       struct pinctrl *pin_ctl;
+       struct aml_pcm *pcm_p = NULL;
+       int ret;
+
+       pr_debug("enter %s\n", __func__);
+
+       pin_ctl = devm_pinctrl_get_select(&pdev->dev, "aml_audio_btpcm");
+       if (IS_ERR(pin_ctl)) {
+               pin_ctl = NULL;
+               pr_err("aml audio pcm dai pinmux set error!\n");
+       }
+
+       pcm_p = devm_kzalloc(&pdev->dev, sizeof(struct aml_pcm), GFP_KERNEL);
+       if (!pcm_p) {
+               /*dev_err(&pdev->dev, "Can't allocate pcm_p\n");*/
+               ret = -ENOMEM;
+               goto err;
+       }
+       dev_set_drvdata(&pdev->dev, pcm_p);
+
+       /* is PCM master? */
+       ret =
+           of_property_read_u32((&pdev->dev)->of_node, "pcm_mode",
+                                &pcm_p->pcm_mode);
+
+       pr_info("pcm mode detection =%d\n", pcm_p->pcm_mode);
+
+       if (pcm_p->pcm_mode) {
+               pcm_p->clk_mpll = devm_clk_get(&pdev->dev, "mpll0");
+               if (IS_ERR(pcm_p->clk_mpll)) {
+                       dev_err(&pdev->dev, "Can't retrieve mpll0 clock\n");
+                       ret = PTR_ERR(pcm_p->clk_mpll);
+                       goto err;
+               }
+
+               pcm_p->clk_pcm_mclk = devm_clk_get(&pdev->dev, "pcm_mclk");
+               if (IS_ERR(pcm_p->clk_pcm_mclk)) {
+                       dev_err(&pdev->dev, "Can't retrieve clk_pcm_mclk clock\n");
+                       ret = PTR_ERR(pcm_p->clk_pcm_mclk);
+                       goto err;
+               }
+               pcm_p->clk_pcm_sync = devm_clk_get(&pdev->dev, "pcm_sclk");
+               if (IS_ERR(pcm_p->clk_pcm_sync)) {
+                       dev_err(&pdev->dev, "Can't retrieve clk_pcm_sync clock\n");
+                       ret = PTR_ERR(pcm_p->clk_pcm_sync);
+                       goto err;
+               }
+
+               /* now only 256fs is supported */
+               ret = aml_pcm_set_clk(pcm_p,
+                       PCM_DEFAULT_SAMPLERATE * PCM_DEFAULT_MCLK_RATIO_SR);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Can't set aml_pcm clk :%d\n", ret);
+                       goto err;
+               }
+
+               ret = clk_prepare_enable(pcm_p->clk_pcm_mclk);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                       "Can't enable pcm clk_pcm_mclk clock: %d\n", ret);
+                       goto err;
+               }
+               ret = clk_prepare_enable(pcm_p->clk_pcm_sync);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                       "Can't enable pcm clk_pcm_sync clock: %d\n", ret);
+                       goto err;
+               }
+       }
+
+       ret = snd_soc_register_component(&pdev->dev, &aml_component,
+                                         aml_pcm_dai, ARRAY_SIZE(aml_pcm_dai));
+
+err:
+       return ret;
+
+}
+
+static int aml_pcm_dai_remove(struct platform_device *pdev)
+{
+       struct aml_pcm *pcm_priv = dev_get_drvdata(&pdev->dev);
+
+       clk_disable_unprepare(pcm_priv->clk_pcm_mclk);
+       clk_disable_unprepare(pcm_priv->clk_pcm_sync);
+
+       snd_soc_unregister_component(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_pcm_dai_match[] = {
+       {.compatible = "amlogic, aml-pcm-dai",
+        },
+       {},
+};
+#else
+#define amlogic_pcm_dai_match NULL
+#endif
+
+static struct platform_driver aml_pcm_dai_driver = {
+       .driver = {
+                  .name = DEV_NAME,
+                  .owner = THIS_MODULE,
+                  .of_match_table = amlogic_pcm_dai_match,
+                  },
+
+       .probe = aml_pcm_dai_probe,
+       .remove = aml_pcm_dai_remove,
+};
+
+static int __init aml_pcm_dai_modinit(void)
+{
+       return platform_driver_register(&aml_pcm_dai_driver);
+}
+
+module_init(aml_pcm_dai_modinit);
+
+static void __exit aml_pcm_dai_modexit(void)
+{
+       platform_driver_unregister(&aml_pcm_dai_driver);
+}
+
+module_exit(aml_pcm_dai_modexit);
+
+/* Module information */
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_DESCRIPTION("AML DAI driver for ALSA");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/amlogic/aml_pcm_dai.h b/sound/soc/amlogic/aml_pcm_dai.h
new file mode 100644 (file)
index 0000000..0a64f3b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * sound/soc/amlogic/aml_pcm_dai.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef AML_DAI_H
+#define AML_DAI_H
+
+struct aml_pcm {
+       struct clk *clk_mpll;
+       struct clk *clk_pcm_mclk;
+       struct clk *clk_pcm_sync;
+       int old_samplerate;
+       int pcm_mode;
+};
+
+void aml_hw_iec958_init(struct snd_pcm_substream *substream);
+extern struct snd_soc_dai_driver aml_dai[];
+
+#endif
diff --git a/sound/soc/amlogic/aml_spdif_codec.c b/sound/soc/amlogic/aml_spdif_codec.c
new file mode 100644 (file)
index 0000000..15f06c2
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * sound/soc/amlogic/aml_spdif_codec.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+
+#define DRV_NAME "spdif-dit"
+
+#define STUB_RATES     SNDRV_PCM_RATE_8000_192000
+#define STUB_FORMATS   (SNDRV_PCM_FMTBIT_S16_LE | \
+       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct pinctrl *pin_spdif_ctl;
+struct device *spdif_dev;
+static struct snd_soc_dai_driver dit_stub_dai = {
+       .name = "dit-hifi",
+       .playback = {
+                    .stream_name = "Playback",
+                    .channels_min = 1,
+                    .channels_max = 8,
+                    .rates = STUB_RATES,
+                    .formats = STUB_FORMATS,
+                    },
+       .capture = {
+                   .stream_name = "Capture",
+                   .channels_min = 1,
+                   .channels_max = 8,
+                   .rates = STUB_RATES,
+                   .formats = STUB_FORMATS,
+                   },
+};
+
+static unsigned int spdif_pinmux;
+void aml_spdif_pinmux_init(struct device *dev)
+{
+       if (!spdif_pinmux) {
+               spdif_pinmux = 1;
+               pin_spdif_ctl = devm_pinctrl_get_select(dev, "aml_audio_spdif");
+               if (IS_ERR(pin_spdif_ctl)) {
+                       pin_spdif_ctl = NULL;
+                       dev_err(dev, "aml_spdif_pinmux_init can't get pinctrl\n");
+               }
+       }
+}
+
+void aml_spdif_pinmux_deinit(struct device *dev)
+{
+       dev_dbg(dev, "aml_spdif_mute\n");
+       if (spdif_pinmux) {
+               spdif_pinmux = 0;
+               if (pin_spdif_ctl)
+                       devm_pinctrl_put(pin_spdif_ctl);
+       }
+}
+bool aml_audio_spdif_mute_flag;
+static int aml_audio_set_spdif_mute(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       aml_audio_spdif_mute_flag = ucontrol->value.integer.value[0];
+       pr_info("aml_audio_set_spdif_mute: flag=%d\n",
+               aml_audio_spdif_mute_flag);
+       if (aml_audio_spdif_mute_flag)
+               aml_spdif_pinmux_deinit(spdif_dev);
+       else
+               aml_spdif_pinmux_init(spdif_dev);
+       return 0;
+}
+
+static int aml_audio_get_spdif_mute(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = aml_audio_spdif_mute_flag;
+       return 0;
+}
+
+static const struct snd_kcontrol_new spdif_controls[] = {
+       SOC_SINGLE_BOOL_EXT("Audio spdif mute",
+                           0, aml_audio_get_spdif_mute,
+                           aml_audio_set_spdif_mute),
+};
+
+static int spdif_probe(struct snd_soc_codec *codec)
+{
+       return snd_soc_add_codec_controls(codec,
+                       spdif_controls, ARRAY_SIZE(spdif_controls));
+}
+static struct snd_soc_codec_driver soc_codec_spdif_dit = {
+       .probe =        spdif_probe,
+};
+
+static ssize_t spdif_mute_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       if (spdif_pinmux)
+               return sprintf(buf, "spdif_unmute\n");
+       else
+               return sprintf(buf, "spdif_mute\n");
+
+}
+
+static ssize_t spdif_mute_set(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       if (strncmp(buf, "spdif_mute", 10))
+               aml_spdif_pinmux_init(dev);
+       else if (strncmp(buf, "spdif_unmute", 12))
+               aml_spdif_pinmux_deinit(dev);
+       else
+               dev_err(dev, "spdif set the wrong value\n");
+
+       return count;
+}
+
+static DEVICE_ATTR(spdif_mute, 0660, spdif_mute_show, spdif_mute_set);
+
+static int spdif_dit_probe(struct platform_device *pdev)
+{
+       int ret = device_create_file(&pdev->dev, &dev_attr_spdif_mute);
+
+       spdif_dev = &pdev->dev;
+
+       aml_spdif_pinmux_init(&pdev->dev);
+       if (ret < 0)
+               dev_err(&pdev->dev,
+                       "spdif: failed to add spdif_mute sysfs: %d\n", ret);
+
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dit,
+                                     &dit_stub_dai, 1);
+}
+
+static int spdif_dit_remove(struct platform_device *pdev)
+{
+       aml_spdif_pinmux_deinit(&pdev->dev);
+       device_remove_file(&pdev->dev, &dev_attr_spdif_mute);
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_spdif_codec_dt_match[] = {
+       {.compatible = "amlogic, aml-spdif-codec",
+        },
+       {},
+};
+#else
+#define amlogic_spdif_codec_dt_match NULL
+#endif
+
+static struct platform_driver spdif_dit_driver = {
+       .probe = spdif_dit_probe,
+       .remove = spdif_dit_remove,
+       .driver = {
+                  .name = DRV_NAME,
+                  .owner = THIS_MODULE,
+                  .of_match_table = amlogic_spdif_codec_dt_match,
+                  },
+};
+
+static int __init spdif_codec_init(void)
+{
+       return platform_driver_register(&spdif_dit_driver);
+}
+
+static void __exit spdif_codec_exit(void)
+{
+       platform_driver_unregister(&spdif_dit_driver);
+}
+
+module_init(spdif_codec_init);
+module_exit(spdif_codec_exit);
+
+MODULE_AUTHOR("Steve Chen <schen@mvista.com>");
+MODULE_DESCRIPTION("SPDIF dummy codec driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amlogic/aml_spdif_dai.c b/sound/soc/amlogic/aml_spdif_dai.c
new file mode 100644 (file)
index 0000000..22b6a88
--- /dev/null
@@ -0,0 +1,794 @@
+/*
+ * sound/soc/amlogic/aml_spdif_dai.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#define pr_fmt(fmt) "aml_spdif_dai: " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/soundcard.h>
+#include <linux/timer.h>
+#include <linux/debugfs.h>
+#include <linux/major.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include <linux/amlogic/iomap.h>
+#include "aml_audio_hw.h"
+#include "aml_spdif_dai.h"
+#include "aml_i2s.h"
+#include <linux/amlogic/media/sound/aout_notify.h>
+#include <linux/amlogic/media/sound/aiu_regs.h>
+#include <linux/amlogic/media/sound/audin_regs.h>
+#include <linux/amlogic/cpu_version.h>
+
+/*
+ * 0 --  other formats except(DD,DD+,DTS)
+ * 1 --  DTS
+ * 2 --  DD
+ * 3 -- DTS with 958 PCM RAW package mode
+ * 4 -- DD+
+ */
+unsigned int IEC958_mode_codec;
+EXPORT_SYMBOL(IEC958_mode_codec);
+struct aml_spdif {
+       struct clk *clk_mpl1;
+       struct clk *clk_i958;
+       struct clk *clk_mclk;
+       struct clk *clk_spdif;
+       struct clk *clk_81;
+       int old_samplerate;
+       uint clk_div;
+       /* spdif dai data in source select.
+        * !Check this with chip spec.
+        */
+       uint src;
+};
+struct aml_spdif *spdif_p;
+unsigned int clk81;
+EXPORT_SYMBOL(clk81);
+
+static int old_samplerate = -1;
+static int flag_samesrc = -1;
+
+static void set_IEC958_clock_div(uint div)
+{
+       if (div == spdif_p->clk_div)
+               return;
+
+       if (div > 0 && div <= 4) {
+               pr_info("set 958 audio clk div %d\n", div);
+               audio_set_spdif_clk_div(div);
+               spdif_p->clk_div = div;
+       }
+}
+
+static inline bool is_meson_tv_chipset(void)
+{
+       return is_meson_gxtvbb_cpu() || is_meson_txl_cpu();
+}
+
+void aml_spdif_play(int samesrc)
+{
+       if (!is_meson_tv_chipset()) {
+               uint div = 0;
+               static int iec958buf[32 + 16];
+               struct _aiu_958_raw_setting_t set;
+               struct _aiu_958_channel_status_t chstat;
+               struct snd_pcm_substream substream;
+               struct snd_pcm_runtime runtime;
+
+               substream.runtime = &runtime;
+               runtime.rate = 48000;
+               runtime.format = SNDRV_PCM_FORMAT_S16_LE;
+               runtime.channels = 2;
+               runtime.sample_bits = 16;
+               memset((void *)(&set), 0, sizeof(set));
+               memset((void *)(&chstat), 0, sizeof(chstat));
+               set.chan_stat = &chstat;
+               set.chan_stat->chstat0_l = 0x0100;
+               set.chan_stat->chstat0_r = 0x0100;
+               set.chan_stat->chstat1_l = 0X200;
+               set.chan_stat->chstat1_r = 0X200;
+               audio_hw_958_enable(0);
+               if (old_samplerate != AUDIO_CLK_FREQ_48
+                               || samesrc != flag_samesrc) {
+                       pr_info("enterd %s,set_clock:%d,sample_rate=%d\n",
+                       __func__, old_samplerate, AUDIO_CLK_FREQ_48);
+                       old_samplerate = AUDIO_CLK_FREQ_48;
+                       flag_samesrc = samesrc;
+                       aml_set_spdif_clk(48000 * 512, samesrc);
+               }
+               if (IEC958_mode_codec == 4 || IEC958_mode_codec == 5 ||
+               IEC958_mode_codec == 7 || IEC958_mode_codec == 8) {
+                       pr_info("set 4x audio clk for 958\n");
+                       div = 1;
+               } else if (samesrc) {
+                       pr_info("share the same clock\n");
+                       div = 2;
+               } else {
+                       pr_info("set normal 512 fs /4 fs\n");
+                       div = 4;
+               }
+
+               set_IEC958_clock_div(div);
+               audio_util_set_dac_958_format(AUDIO_ALGOUT_DAC_FORMAT_DSP);
+               /*clear the same source function as new raw data output */
+               audio_i2s_958_same_source(samesrc);
+               memset(iec958buf, 0, sizeof(iec958buf));
+               audio_set_958outbuf((virt_to_phys(iec958buf) + 63) & (~63),
+                                       128, 0);
+               audio_set_958_mode(AIU_958_MODE_PCM16, &set);
+               aout_notifier_call_chain(AOUT_EVENT_IEC_60958_PCM, &substream);
+               audio_hw_958_enable(1);
+       }
+}
+
+static void aml_spdif_play_stop(void)
+{
+       audio_hw_958_enable(0);
+}
+
+static int aml_dai_spdif_set_sysclk(struct snd_soc_dai *cpu_dai,
+                                   int clk_id, unsigned int freq, int dir)
+{
+       return 0;
+}
+
+static int aml_dai_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+                                struct snd_soc_dai *dai)
+{
+
+       struct snd_soc_pcm_runtime *rtd = NULL;
+
+       rtd = (struct snd_soc_pcm_runtime *)substream->private_data;
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       pr_info("aiu 958 playback enable\n");
+                       audio_hw_958_enable(1);
+               } else {
+                       pr_info("spdif in capture enable\n");
+                       audio_in_spdif_enable(1);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       pr_info("aiu 958 playback disable\n");
+                       audio_hw_958_enable(0);
+               } else {
+                       pr_info("spdif in capture disable\n");
+                       audio_in_spdif_enable(0);
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+void aml_hw_iec958_init(struct snd_pcm_substream *substream, int samesrc)
+{
+       struct _aiu_958_raw_setting_t set;
+       struct _aiu_958_channel_status_t chstat;
+       unsigned int i2s_mode, iec958_mode;
+       unsigned int start, size;
+       int sample_rate;
+       int div;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       if (buf == NULL && runtime == NULL) {
+               pr_info("buf/%p runtime/%p\n", buf, runtime);
+               return;
+       }
+
+       i2s_mode = AIU_I2S_MODE_PCM16;
+       sample_rate = AUDIO_CLK_FREQ_48;
+       memset((void *)(&set), 0, sizeof(set));
+       memset((void *)(&chstat), 0, sizeof(chstat));
+       set.chan_stat = &chstat;
+       switch (runtime->rate) {
+       case 192000:
+               sample_rate = AUDIO_CLK_FREQ_192;
+               break;
+       case 176400:
+               sample_rate = AUDIO_CLK_FREQ_1764;
+               break;
+       case 96000:
+               sample_rate = AUDIO_CLK_FREQ_96;
+               break;
+       case 88200:
+               sample_rate = AUDIO_CLK_FREQ_882;
+               break;
+       case 48000:
+               sample_rate = AUDIO_CLK_FREQ_48;
+               break;
+       case 44100:
+               sample_rate = AUDIO_CLK_FREQ_441;
+               break;
+       case 32000:
+               sample_rate = AUDIO_CLK_FREQ_32;
+               break;
+       case 8000:
+               sample_rate = AUDIO_CLK_FREQ_8;
+               break;
+       case 11025:
+               sample_rate = AUDIO_CLK_FREQ_11;
+               break;
+       case 16000:
+               sample_rate = AUDIO_CLK_FREQ_16;
+               break;
+       case 22050:
+               sample_rate = AUDIO_CLK_FREQ_22;
+               break;
+       case 12000:
+               sample_rate = AUDIO_CLK_FREQ_12;
+               break;
+       case 24000:
+               sample_rate = AUDIO_CLK_FREQ_22;
+               break;
+       default:
+               sample_rate = AUDIO_CLK_FREQ_441;
+               break;
+       };
+       audio_hw_958_enable(0);
+       pr_info("aml_hw_iec958_init,runtime->rate=%d, same source mode(%d)\n",
+              runtime->rate, samesrc);
+
+       if (old_samplerate != sample_rate || samesrc != flag_samesrc) {
+               old_samplerate = sample_rate;
+               flag_samesrc = samesrc;
+               aml_set_spdif_clk(runtime->rate * 512, samesrc);
+       }
+
+       if (IEC958_mode_codec == 4 || IEC958_mode_codec == 5 ||
+       IEC958_mode_codec == 7 || IEC958_mode_codec == 8) {
+               pr_info("set 4x audio clk for 958\n");
+               div = 1;
+       } else if (samesrc) {
+               pr_info("share the same clock\n");
+               div = 2;
+       } else {
+               pr_info("set normal 512 fs /4 fs\n");
+               div = 4;
+       }
+       set_IEC958_clock_div(div);
+       audio_util_set_dac_958_format(AUDIO_ALGOUT_DAC_FORMAT_DSP);
+       /*clear the same source function as new raw data output */
+       audio_i2s_958_same_source(samesrc);
+
+       switch (runtime->format) {
+       case SNDRV_PCM_FORMAT_S32_LE:
+               i2s_mode = AIU_I2S_MODE_PCM32;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               i2s_mode = AIU_I2S_MODE_PCM24;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               i2s_mode = AIU_I2S_MODE_PCM16;
+               break;
+       }
+
+       /* audio_set_i2s_mode(i2s_mode); */
+       /* case 1,raw mode enabled */
+       if (IEC958_mode_codec && IEC958_mode_codec != 9) {
+               if (IEC958_mode_codec == 1) {
+                       /* dts, use raw sync-word mode */
+                       iec958_mode = AIU_958_MODE_RAW;
+                       pr_info("iec958 mode RAW\n");
+               } else {
+                       /* ac3,use the same pcm mode as i2s configuration */
+                       iec958_mode = AIU_958_MODE_PCM_RAW;
+                       pr_info("iec958 mode %s\n",
+                               (i2s_mode == AIU_I2S_MODE_PCM32) ? "PCM32_RAW"
+                               : ((I2S_MODE == AIU_I2S_MODE_PCM24) ?
+                               "PCM24_RAW"     : "PCM16_RAW"));
+               }
+       } else {
+               if (i2s_mode == AIU_I2S_MODE_PCM32)
+                       iec958_mode = AIU_958_MODE_PCM32;
+               else if (i2s_mode == AIU_I2S_MODE_PCM24)
+                       iec958_mode = AIU_958_MODE_PCM24;
+               else
+                       iec958_mode = AIU_958_MODE_PCM16;
+               pr_info("iec958 mode %s\n",
+                      (i2s_mode ==
+                       AIU_I2S_MODE_PCM32) ? "PCM32" : ((i2s_mode ==
+                                                         AIU_I2S_MODE_PCM24) ?
+                                                        "PCM24" : "PCM16"));
+       }
+       if (iec958_mode == AIU_958_MODE_PCM16
+           || iec958_mode == AIU_958_MODE_PCM24
+           || iec958_mode == AIU_958_MODE_PCM32
+           || IEC958_mode_codec == 9) {
+               set.chan_stat->chstat0_l = 0x0100;
+               set.chan_stat->chstat0_r = 0x0100;
+               set.chan_stat->chstat1_l = 0x200;
+               set.chan_stat->chstat1_r = 0x200;
+               if (sample_rate == AUDIO_CLK_FREQ_882) {
+                       pr_info("sample_rate==AUDIO_CLK_FREQ_882\n");
+                       set.chan_stat->chstat1_l = 0x800;
+                       set.chan_stat->chstat1_r = 0x800;
+               } else if (sample_rate == AUDIO_CLK_FREQ_96) {
+                       pr_info("sample_rate==AUDIO_CLK_FREQ_96\n");
+                       set.chan_stat->chstat1_l = 0xa00;
+                       set.chan_stat->chstat1_r = 0xa00;
+               } else if (sample_rate == AUDIO_CLK_FREQ_1764) {
+                       pr_info("sample_rate==AUDIO_CLK_FREQ_1764\n");
+                       set.chan_stat->chstat1_l = 0xc00;
+                       set.chan_stat->chstat1_r = 0xc00;
+               } else if (sample_rate == AUDIO_CLK_FREQ_192) {
+                       pr_info("sample_rate==AUDIO_CLK_FREQ_192\n");
+                       set.chan_stat->chstat1_l = 0xe00;
+                       set.chan_stat->chstat1_r = 0xe00;
+               }
+               start = buf->addr;
+               size = snd_pcm_lib_buffer_bytes(substream);
+               audio_set_958outbuf(start, size, 0);
+               /* audio_set_i2s_mode(AIU_I2S_MODE_PCM16); */
+               /* audio_set_aiubuf(start, size); */
+       } else {
+
+               set.chan_stat->chstat0_l = 0x1902;
+               set.chan_stat->chstat0_r = 0x1902;
+               if (IEC958_mode_codec == 4 || IEC958_mode_codec == 5) {
+                       /* DD+ */
+                       if (runtime->rate == 32000) {
+                               set.chan_stat->chstat1_l = 0x300;
+                               set.chan_stat->chstat1_r = 0x300;
+                       } else if (runtime->rate == 44100) {
+                               set.chan_stat->chstat1_l = 0xc00;
+                               set.chan_stat->chstat1_r = 0xc00;
+                       } else {
+                               set.chan_stat->chstat1_l = 0Xe00;
+                               set.chan_stat->chstat1_r = 0Xe00;
+                       }
+               } else {
+                       /* DTS,DD */
+                       if (runtime->rate == 32000) {
+                               set.chan_stat->chstat1_l = 0x300;
+                               set.chan_stat->chstat1_r = 0x300;
+                       } else if (runtime->rate == 44100) {
+                               set.chan_stat->chstat1_l = 0;
+                               set.chan_stat->chstat1_r = 0;
+                       } else {
+                               set.chan_stat->chstat1_l = 0x200;
+                               set.chan_stat->chstat1_r = 0x200;
+                       }
+               }
+               start = buf->addr;
+               size = snd_pcm_lib_buffer_bytes(substream);
+               audio_set_958outbuf(start, size,
+                                   (iec958_mode == AIU_958_MODE_RAW) ? 1 : 0);
+               memset((void *)buf->area, 0, size);
+       }
+       audio_set_958_mode(iec958_mode, &set);
+
+       if (IEC958_mode_codec == 2) {
+               aout_notifier_call_chain(AOUT_EVENT_RAWDATA_AC_3, substream);
+       } else if (IEC958_mode_codec == 3) {
+               aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DTS, substream);
+       } else if (IEC958_mode_codec == 4) {
+               aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DOBLY_DIGITAL_PLUS,
+                                        substream);
+       } else if (IEC958_mode_codec == 5) {
+               aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DTS_HD, substream);
+       } else if (IEC958_mode_codec == 7 || IEC958_mode_codec == 8) {
+               aml_write_cbus(AIU_958_CHSTAT_L0, 0x1902);
+               aml_write_cbus(AIU_958_CHSTAT_L1, 0x900);
+               aml_write_cbus(AIU_958_CHSTAT_R0, 0x1902);
+               aml_write_cbus(AIU_958_CHSTAT_R1, 0x900);
+               if (IEC958_mode_codec == 8)
+                       aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DTS_HD_MA,
+                       substream);
+               else
+                       aout_notifier_call_chain(AOUT_EVENT_RAWDATA_MAT_MLP,
+                       substream);
+       } else {
+               aout_notifier_call_chain(AOUT_EVENT_IEC_60958_PCM, substream);
+       }
+}
+
+/*
+ * special call by the audiodsp,add these code,
+ * as there are three cases for 958 s/pdif output
+ * 1)NONE-PCM  raw output ,only available when ac3/dts audio,
+ * when raw output mode is selected by user.
+ * 2)PCM  output for  all audio, when pcm mode is selected by user .
+ * 3)PCM  output for audios except ac3/dts,
+ * when raw output mode is selected by user
+ */
+
+void aml_alsa_hw_reprepare(void)
+{
+       /* M8 disable it */
+#if 0
+       /* disable 958 module before call initiation */
+
+       audio_hw_958_enable(0);
+       if (playback_substream_handle != 0)
+               aml_hw_iec958_init((struct snd_pcm_substream *)
+                                  playback_substream_handle);
+#endif
+}
+
+static int aml_dai_spdif_startup(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+
+       int ret = 0;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aml_runtime_data *prtd = runtime->private_data;
+       struct audio_stream *s;
+
+       if (!prtd) {
+               prtd =
+                   (struct aml_runtime_data *)
+                   kzalloc(sizeof(struct aml_runtime_data), GFP_KERNEL);
+               if (prtd == NULL) {
+                       pr_err("alloc aml_runtime_data error\n");
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               prtd->substream = substream;
+               runtime->private_data = prtd;
+       }
+       s = &prtd->s;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               s->device_type = AML_AUDIO_SPDIFOUT;
+               /* audio_spdifout_pg_enable(1); */
+               /*aml_spdif_play_stop(); */
+       } else {
+               s->device_type = AML_AUDIO_SPDIFIN;
+       }
+
+       return 0;
+ out:
+       return ret;
+}
+
+static void aml_dai_spdif_shutdown(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *dai)
+{
+
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       /* struct snd_dma_buffer *buf = &substream->dma_buffer; */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               memset((void *)runtime->dma_area, 0,
+                      snd_pcm_lib_buffer_bytes(substream));
+               if (IEC958_mode_codec == 6)
+                       pr_info("8chPCM output:disable aml_spdif_play\n");
+       }
+
+}
+
+static int aml_dai_spdif_prepare(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+
+       /* struct snd_soc_pcm_runtime *rtd = substream->private_data; */
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       /* struct aml_runtime_data *prtd = runtime->private_data; */
+       /* audio_stream_t *s = &prtd->s; */
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               aml_hw_iec958_init(substream, 0);
+       } else {
+               audio_in_spdif_set_buf(runtime->dma_addr,
+                                      runtime->dma_bytes * 2, spdif_p->src);
+               memset((void *)runtime->dma_area, 0, runtime->dma_bytes * 2);
+       }
+
+       return 0;
+}
+
+/* if src_i2s, then spdif parent clk is mclk, otherwise i958clk */
+int aml_set_spdif_clk(unsigned long rate, bool src_i2s)
+{
+       int ret = 0;
+
+       if (src_i2s) {
+               ret = clk_set_parent(spdif_p->clk_spdif, spdif_p->clk_mclk);
+               if (ret) {
+                       pr_err("Can't set spdif clk parent: %d\n", ret);
+                       return ret;
+               }
+       } else {
+               ret = clk_set_rate(spdif_p->clk_mpl1, rate * 4);
+               if (ret) {
+                       pr_err("Can't set spdif mpl1 clock rate: %d\n", ret);
+                       return ret;
+               }
+               ret = clk_set_parent(spdif_p->clk_i958, spdif_p->clk_mpl1);
+               if (ret) {
+                       pr_err("Can't set spdif clk parent: %d\n", ret);
+                       return ret;
+               }
+               ret = clk_set_rate(spdif_p->clk_i958, rate);
+               if (ret) {
+                       pr_err("Can't set spdif mpl1 clock rate: %d\n", ret);
+                       return ret;
+               }
+
+               ret = clk_set_parent(spdif_p->clk_spdif, spdif_p->clk_i958);
+               if (ret) {
+                       pr_err("Can't set spdif clk parent: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int aml_dai_spdif_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+#if 0
+       int srate;
+
+       srate = params_rate(params);
+       aml_set_spdif_clk(srate * 512, 0);
+#endif
+       return 0;
+}
+
+static int aml_dai_spdif_suspend(struct snd_soc_dai *cpu_dai)
+{
+       struct aml_spdif *spdif_priv = dev_get_drvdata(cpu_dai->dev);
+
+       aml_spdif_play_stop();
+       if (spdif_priv && spdif_priv->clk_spdif)
+               clk_disable_unprepare(spdif_priv->clk_spdif);
+
+       return 0;
+}
+
+static int aml_dai_spdif_resume(struct snd_soc_dai *cpu_dai)
+{
+       struct aml_spdif *spdif_priv = dev_get_drvdata(cpu_dai->dev);
+
+       /*aml_spdif_play();*/
+       if (spdif_priv && spdif_priv->clk_spdif)
+               clk_prepare_enable(spdif_priv->clk_spdif);
+
+       return 0;
+}
+
+static struct snd_soc_dai_ops spdif_dai_ops = {
+       .set_sysclk = aml_dai_spdif_set_sysclk,
+       .trigger = aml_dai_spdif_trigger,
+       .prepare = aml_dai_spdif_prepare,
+       .hw_params = aml_dai_spdif_hw_params,
+       .shutdown = aml_dai_spdif_shutdown,
+       .startup = aml_dai_spdif_startup,
+};
+
+static struct snd_soc_dai_driver aml_spdif_dai[] = {
+       {
+               .playback = {
+                       .stream_name = "S/PDIF Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = (SNDRV_PCM_RATE_32000 |
+                       SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 |
+                       SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000 |
+                       SNDRV_PCM_RATE_176400 |
+                       SNDRV_PCM_RATE_192000),
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_LE |
+                       SNDRV_PCM_FMTBIT_S32_LE),
+               },
+               .capture = {
+                       .stream_name = "S/PDIF Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = (SNDRV_PCM_RATE_32000 |
+                       SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 |
+                       SNDRV_PCM_RATE_96000),
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .ops = &spdif_dai_ops,
+               .suspend = aml_dai_spdif_suspend,
+               .resume = aml_dai_spdif_resume,
+       }
+};
+
+static const struct snd_soc_component_driver aml_component = {
+       .name = "aml-spdif-dai",
+};
+
+static const char *const gate_names[] = {
+       "iec958", "iec958_amclk"
+};
+
+static int aml_dai_spdif_probe(struct platform_device *pdev)
+{
+       int i, ret;
+       struct aml_spdif *spdif_priv = NULL;
+       struct clk *clk_gate;
+
+       /* enable spdif power gate first */
+       for (i = 0; i < ARRAY_SIZE(gate_names); i++) {
+               clk_gate = devm_clk_get(&pdev->dev, gate_names[i]);
+               if (IS_ERR(clk_gate)) {
+                       dev_err(&pdev->dev, "Can't get aml audio gate\n");
+                       return PTR_ERR(clk_gate);
+               }
+               clk_prepare_enable(clk_gate);
+       }
+
+       spdif_priv = devm_kzalloc(&pdev->dev,
+                       sizeof(struct aml_spdif), GFP_KERNEL);
+       if (!spdif_priv) {
+               /*dev_err(&pdev->dev, "Can't allocate spdif_priv\n");*/
+               ret = -ENOMEM;
+               goto err;
+       }
+       dev_set_drvdata(&pdev->dev, spdif_priv);
+       spdif_p = spdif_priv;
+
+       spdif_priv->clk_mpl1 = devm_clk_get(&pdev->dev, "mpll1");
+       if (IS_ERR(spdif_priv->clk_mpl1)) {
+               dev_err(&pdev->dev, "Can't retrieve mpll1 clock\n");
+               ret = PTR_ERR(spdif_priv->clk_mpl1);
+               goto err;
+       }
+
+       spdif_priv->clk_i958 = devm_clk_get(&pdev->dev, "i958");
+       if (IS_ERR(spdif_priv->clk_i958)) {
+               dev_err(&pdev->dev, "Can't retrieve spdif clk_i958 clock\n");
+               ret = PTR_ERR(spdif_priv->clk_i958);
+               goto err;
+       }
+
+       spdif_priv->clk_mclk = devm_clk_get(&pdev->dev, "mclk");
+       if (IS_ERR(spdif_priv->clk_mclk)) {
+               dev_err(&pdev->dev, "Can't retrieve spdif clk_mclk clock\n");
+               ret = PTR_ERR(spdif_priv->clk_mclk);
+               goto err;
+       }
+
+       spdif_priv->clk_spdif = devm_clk_get(&pdev->dev, "spdif");
+       if (IS_ERR(spdif_priv->clk_spdif)) {
+               dev_err(&pdev->dev, "Can't retrieve spdif clock\n");
+               ret = PTR_ERR(spdif_priv->clk_spdif);
+               goto err;
+       }
+       ret = clk_set_parent(spdif_priv->clk_i958, spdif_priv->clk_mpl1);
+       if (ret) {
+               pr_err("Can't set i958 clk parent: %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_set_parent(spdif_priv->clk_spdif, spdif_priv->clk_i958);
+       if (ret) {
+               pr_err("Can't set spdif clk parent: %d\n", ret);
+               return ret;
+       }
+       ret = clk_prepare_enable(spdif_priv->clk_spdif);
+       if (ret) {
+               pr_err("Can't enable spdif clock: %d\n", ret);
+               goto err;
+       }
+
+       spdif_priv->clk_81 = devm_clk_get(&pdev->dev, "clk_81");
+       if (IS_ERR(spdif_priv->clk_81)) {
+               dev_err(&pdev->dev, "Can't get clk81\n");
+               ret = PTR_ERR(spdif_priv->clk_81);
+               goto err;
+       }
+       clk81 = clk_get_rate(spdif_priv->clk_81);
+
+       /* In PAO mode, audio data from hdmi-rx has no channel order,
+        * set default mode from spdif-in
+        */
+       spdif_priv->src = SPDIF_IN;
+       aml_spdif_play(1);
+       ret = snd_soc_register_component(&pdev->dev, &aml_component,
+                                         aml_spdif_dai,
+                                         ARRAY_SIZE(aml_spdif_dai));
+       if (ret) {
+               pr_err("Can't register spdif dai: %d\n", ret);
+               goto err_clk_dis;
+       }
+       return 0;
+
+err_clk_dis:
+       clk_disable_unprepare(spdif_priv->clk_spdif);
+err:
+       return ret;
+}
+
+static int aml_dai_spdif_remove(struct platform_device *pdev)
+{
+       struct aml_spdif *spdif_priv = dev_get_drvdata(&pdev->dev);
+
+       snd_soc_unregister_component(&pdev->dev);
+       clk_disable_unprepare(spdif_priv->clk_spdif);
+       return 0;
+}
+
+static void aml_spdif_dai_shutdown(struct platform_device *pdev)
+{
+       struct aml_spdif *spdif_priv = dev_get_drvdata(&pdev->dev);
+
+       if (spdif_priv && spdif_priv->clk_spdif)
+               clk_disable_unprepare(spdif_priv->clk_spdif);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_spdif_dai_dt_match[] = {
+       {.compatible = "amlogic, aml-spdif-dai",
+        },
+       {},
+};
+#else
+#define amlogic_spdif_dai_dt_match NULL
+#endif
+
+static struct platform_driver aml_spdif_dai_driver = {
+       .probe = aml_dai_spdif_probe,
+       .remove = aml_dai_spdif_remove,
+       .shutdown = aml_spdif_dai_shutdown,
+       .driver = {
+                  .name = "aml-spdif-dai",
+                  .owner = THIS_MODULE,
+                  .of_match_table = amlogic_spdif_dai_dt_match,
+                  },
+};
+
+static int __init aml_dai_spdif_init(void)
+{
+       return platform_driver_register(&aml_spdif_dai_driver);
+}
+
+module_init(aml_dai_spdif_init);
+
+static void __exit aml_dai_spdif_exit(void)
+{
+       platform_driver_unregister(&aml_spdif_dai_driver);
+}
+
+module_exit(aml_dai_spdif_exit);
+
+MODULE_AUTHOR("jian.xu, <jian.xu@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic S/PDIF<HDMI> Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:aml-spdif");
diff --git a/sound/soc/amlogic/aml_spdif_dai.h b/sound/soc/amlogic/aml_spdif_dai.h
new file mode 100644 (file)
index 0000000..98b5b06
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * sound/soc/amlogic/aml_spdif_dai.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _AML_SPDIF_DAI_H
+#define _AML_SPDIF_DAI_H
+
+/* HDMI audio stream type ID */
+#define AOUT_EVENT_IEC_60958_PCM                0x1
+#define AOUT_EVENT_RAWDATA_AC_3                 0x2
+#define AOUT_EVENT_RAWDATA_MPEG1                0x3
+#define AOUT_EVENT_RAWDATA_MP3                  0x4
+#define AOUT_EVENT_RAWDATA_MPEG2                0x5
+#define AOUT_EVENT_RAWDATA_AAC                  0x6
+#define AOUT_EVENT_RAWDATA_DTS                  0x7
+#define AOUT_EVENT_RAWDATA_ATRAC                0x8
+#define AOUT_EVENT_RAWDATA_ONE_BIT_AUDIO        0x9
+#define AOUT_EVENT_RAWDATA_DOBLY_DIGITAL_PLUS   0xA
+#define AOUT_EVENT_RAWDATA_DTS_HD               0xB
+#define AOUT_EVENT_RAWDATA_MAT_MLP              0xC
+#define AOUT_EVENT_RAWDATA_DST                  0xD
+#define AOUT_EVENT_RAWDATA_WMA_PRO              0xE
+#define AOUT_EVENT_RAWDATA_DTS_HD_MA (AOUT_EVENT_RAWDATA_DTS_HD|(1<<8))
+extern unsigned int IEC958_mode_codec;
+
+/*
+ * special call by the audiodsp,add these code,
+ * as there are three cases for 958 s/pdif output
+ * 1)NONE-PCM  raw output ,only available when ac3/dts audio,
+ * when raw output mode is selected by user.
+ * 2)PCM  output for  all audio, when pcm mode is selected by user .
+ * 3)PCM  output for audios except ac3/dts,
+ * when raw output mode is selected by user
+ */
+void aml_hw_iec958_init(struct snd_pcm_substream *substream, int samesrc);
+int aml_set_spdif_clk(unsigned long rate, bool src_i2s);
+void aml_spdif_play(int samesrc);
+#endif  /* _AML_SPDIF_DAI_H */
diff --git a/sound/soc/amlogic/aml_tv.c b/sound/soc/amlogic/aml_tv.c
new file mode 100644 (file)
index 0000000..d5ec348
--- /dev/null
@@ -0,0 +1,1722 @@
+/*
+ * sound/soc/amlogic/aml_tv.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#define pr_fmt(fmt) "aml_tv: " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/amlogic/iomap.h>
+#include <linux/amlogic/media/sound/audin_regs.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/media/sound/aiu_regs.h>
+
+#ifdef CONFIG_SND_SOC_TAS5707
+#include <sound/tas57xx.h>
+#endif
+
+#include "aml_i2s.h"
+#include "aml_audio_hw.h"
+#include "aml_tv.h"
+
+#define DRV_NAME "aml_snd_card_tv"
+
+static int aml_audio_Hardware_resample;
+static int hardware_resample_locked_flag;
+unsigned int clk_rate;
+
+static u32 aml_EQ_param[20][5] = {
+       /*channel 1 param*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef0*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef1*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef2*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef3*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef4*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef5*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef6*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef7*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef8*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch1_coef9*/
+       /*channel 2 param*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef0*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef1*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef2*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef3*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef4*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef5*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef6*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef7*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef8*/
+       {0x800000, 0x00, 0x00, 0x00, 0x00}, /*eq_ch2_coef9*/
+};
+
+static u32 drc_table[3][2] = {
+       {0x800000, 0x00}, /*drc_ae && drc_ae_1m*/
+       {0x800000, 0x00}, /*drc_aa && drc_aa_1m*/
+       {0x800000, 0x00}, /*drc_ad && drc_ad_1m*/
+};
+
+static u32 drc_tko_table[2][3] = {
+       {0x0, 0xbf000000, 0x40000}, /*offset, thd, k*/
+       {0x0, 0x0, 0x40000}, /*offset, thd, k*/
+};
+
+static int DRC0_enable(int enable)
+{
+       if ((aml_read_cbus(AED_DRC_EN) & 1) == 1) {
+               if (enable == 1) {
+                       aml_write_cbus(AED_DRC_THD0, drc_tko_table[0][1]);
+                       aml_write_cbus(AED_DRC_K0, drc_tko_table[0][2]);
+               } else {
+                       aml_write_cbus(AED_DRC_THD0, 0xbf000000);
+                       aml_write_cbus(AED_DRC_K0, 0x40000);
+               }
+       }
+       return 0;
+}
+
+static const char *const audio_in_source_texts[] = { "LINEIN", "ATV", "HDMI"};
+
+static const struct soc_enum audio_in_source_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(audio_in_source_texts),
+                       audio_in_source_texts);
+
+static int aml_audio_get_in_source(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       int value = audio_in_source;
+
+       ucontrol->value.enumerated.item[0] = value;
+
+       return 0;
+}
+
+static int aml_audio_set_in_source(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       if (ucontrol->value.enumerated.item[0] == 0) {
+               if (is_meson_txl_cpu()) {
+                       /* select internal acodec output in TXL as I2S source */
+                       aml_write_cbus(AUDIN_SOURCE_SEL, 3);
+               } else
+                       /* select external codec output as I2S source */
+                       aml_write_cbus(AUDIN_SOURCE_SEL, 0);
+               audio_in_source = 0;
+               if (is_meson_txl_cpu())
+                       DRC0_enable(1);
+       } else if (ucontrol->value.enumerated.item[0] == 1) {
+               /* select ATV output as I2S source */
+               aml_write_cbus(AUDIN_SOURCE_SEL, 1);
+               audio_in_source = 1;
+               if (is_meson_txl_cpu())
+                       DRC0_enable(1);
+       } else if (ucontrol->value.enumerated.item[0] == 2) {
+               /* select HDMI-rx as I2S source */
+               /* [14:12]cntl_hdmirx_chsts_sel: */
+               /* 0=Report chan1 status; 1=Report chan2 status */
+               /* [11:8] cntl_hdmirx_chsts_en */
+               /* [5:4] spdif_src_sel:*/
+               /* 1=Select HDMIRX SPDIF output as AUDIN source */
+               /* [1:0] i2sin_src_sel: */
+               /*2=Select HDMIRX I2S output as AUDIN source */
+               aml_write_cbus(AUDIN_SOURCE_SEL, (0 << 12) |
+                              (0xf << 8) | (1 << 4) | (2 << 0));
+               audio_in_source = 2;
+               if (is_meson_txl_cpu())
+                       DRC0_enable(0);
+       }
+       set_i2s_source(audio_in_source);
+       return 0;
+}
+
+/* i2s audio format detect: LPCM or NONE-LPCM */
+static const char *const i2s_audio_type_texts[] = {
+       "LPCM", "NONE-LPCM", "UN-KNOWN"
+};
+static const struct soc_enum i2s_audio_type_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(i2s_audio_type_texts),
+                       i2s_audio_type_texts);
+
+static int aml_i2s_audio_type_get_enum(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       int ch_status = 0;
+
+       if ((aml_read_cbus(AUDIN_DECODE_CONTROL_STATUS) >> 24) & 0x1) {
+               ch_status = aml_read_cbus(AUDIN_DECODE_CHANNEL_STATUS_A_0);
+               if (ch_status & 2)
+                       ucontrol->value.enumerated.item[0] = 1;
+               else
+                       ucontrol->value.enumerated.item[0] = 0;
+       } else {
+               ucontrol->value.enumerated.item[0] = 2;
+       }
+       return 0;
+}
+
+/* spdif in audio format detect: LPCM or NONE-LPCM */
+struct sppdif_audio_info {
+       unsigned char aud_type;
+       /*IEC61937 package presamble Pc value*/
+       short pc;
+       char *aud_type_str;
+};
+static const char *const spdif_audio_type_texts[] = {
+       "LPCM",
+       "AC3",
+       "EAC3",
+       "DTS",
+       "DTS-HD",
+       "TRUEHD",
+};
+static const struct sppdif_audio_info type_texts[] = {
+       {0, 0, "LPCM"},
+       {1, 0x1, "AC3"},
+       {2, 0x15, "EAC3"},
+       {3, 0xb, "DTS-I"},
+       {3, 0x0c, "DTS-II"},
+       {3, 0x0d, "DTS-III"},
+       {3, 0x11, "DTS-IV"},
+       {4, 0, "DTS-HD"},
+       {5, 0x16, "TRUEHD"},
+};
+static const struct soc_enum spdif_audio_type_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(spdif_audio_type_texts),
+                       spdif_audio_type_texts);
+
+static int last_audio_type = -1;
+static int aml_spdif_audio_type_get_enum(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       int audio_type = 0;
+       int i;
+       int total_num = sizeof(type_texts)/sizeof(struct sppdif_audio_info);
+       int pc = aml_read_cbus(AUDIN_SPDIF_NPCM_PCPD)>>16;
+
+       pc = pc&0xff;
+       for (i = 0; i < total_num; i++) {
+               if (pc == type_texts[i].pc) {
+                       audio_type = type_texts[i].aud_type;
+                       break;
+               }
+       }
+       ucontrol->value.enumerated.item[0] = audio_type;
+       if (last_audio_type != audio_type) {
+               if (audio_type == 0) {
+                       /*In LPCM, use old spdif mode*/
+                       aml_cbus_update_bits(AUDIN_FIFO1_CTRL,
+                               (0x7 << AUDIN_FIFO1_DIN_SEL),
+                               (SPDIF_IN << AUDIN_FIFO1_DIN_SEL));
+                       /*spdif-in data fromat:(27:4)*/
+                       aml_write_cbus(AUDIN_FIFO1_CTRL1, 0x88);
+                       hardware_resample_locked_flag = 0;
+               } else {
+                       /*In RAW data, use PAO mode*/
+                       aml_cbus_update_bits(AUDIN_FIFO1_CTRL,
+                               (0x7 << AUDIN_FIFO1_DIN_SEL),
+                               (PAO_IN << AUDIN_FIFO1_DIN_SEL));
+                       /*spdif-in data fromat:(23:0)*/
+                       aml_write_cbus(AUDIN_FIFO1_CTRL1, 0x8);
+                       hardware_resample_locked_flag = 1;
+               }
+               last_audio_type = audio_type;
+       }
+       return 0;
+}
+
+static int aml_spdif_audio_type_set_enum(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       return 0;
+}
+
+#define RESAMPLE_BUFFER_SOURCE 1
+/*Cnt_ctrl = mclk/fs_out-1 ; fest 256fs */
+#define RESAMPLE_CNT_CONTROL 255
+
+static int hardware_resample_enable(int input_sr)
+{
+       u16 Avg_cnt_init = 0;
+       unsigned int clk_rate = clk81;
+
+       if (hardware_resample_locked_flag == 1) {
+               pr_info("HW resample is locked in RAW data.\n");
+               return 0;
+       }
+
+       if (input_sr < 8000 || input_sr > 48000) {
+               pr_err("Error input sample rate,input_sr = %d!\n", input_sr);
+               return -1;
+       }
+
+       Avg_cnt_init = (u16)(clk_rate * 4 / input_sr);
+       pr_info("clk_rate = %u, input_sr = %d, Avg_cnt_init = %u\n",
+               clk_rate, input_sr, Avg_cnt_init);
+
+       aml_write_cbus(AUD_RESAMPLE_CTRL0, (1 << 31));
+       aml_write_cbus(AUD_RESAMPLE_CTRL0, 0);
+       aml_write_cbus(AUD_RESAMPLE_CTRL0,
+                               (1 << 29)
+                               | (1 << 28)
+                               | (0 << 26)
+                               | (RESAMPLE_CNT_CONTROL << 16)
+                               | Avg_cnt_init);
+
+       return 0;
+}
+
+static int hardware_resample_disable(void)
+{
+       aml_write_cbus(AUD_RESAMPLE_CTRL0, 0);
+       return 0;
+}
+
+static const char *const hardware_resample_texts[] = {
+       "Disable",
+       "Enable:48K",
+       "Enable:44K",
+       "Enable:32K",
+       "Lock Resample",
+       "Unlock Resample"
+};
+
+static const struct soc_enum hardware_resample_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hardware_resample_texts),
+                       hardware_resample_texts);
+
+static int aml_hardware_resample_get_enum(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = aml_audio_Hardware_resample;
+       return 0;
+}
+
+static int aml_hardware_resample_set_enum(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       if (ucontrol->value.enumerated.item[0] == 0) {
+               hardware_resample_disable();
+               aml_audio_Hardware_resample = 0;
+       } else if (ucontrol->value.enumerated.item[0] == 1) {
+               hardware_resample_enable(48000);
+               aml_audio_Hardware_resample = 1;
+       } else if (ucontrol->value.enumerated.item[0] == 2) {
+               hardware_resample_enable(44100);
+               aml_audio_Hardware_resample = 2;
+       } else if (ucontrol->value.enumerated.item[0] == 3) {
+               hardware_resample_enable(32000);
+               aml_audio_Hardware_resample = 3;
+       } else if (ucontrol->value.enumerated.item[0] == 4) {
+               hardware_resample_disable();
+               aml_audio_Hardware_resample = 4;
+               hardware_resample_locked_flag = 1;
+       } else if (ucontrol->value.enumerated.item[0] == 5) {
+               hardware_resample_locked_flag = 0;
+               hardware_resample_enable(48000);
+               aml_audio_Hardware_resample = 5;
+       }
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget aml_asoc_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("LINEIN"),
+       SND_SOC_DAPM_OUTPUT("LINEOUT"),
+};
+
+int audio_in_GPIO;
+struct gpio_desc *av_source;
+static const char * const audio_in_switch_texts[] = { "AV", "Karaok"};
+
+static const struct soc_enum audio_in_switch_enum = SOC_ENUM_SINGLE(
+               SND_SOC_NOPM, 0, ARRAY_SIZE(audio_in_switch_texts),
+               audio_in_switch_texts);
+
+static int aml_get_audio_in_switch(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol) {
+
+       if (audio_in_GPIO == 0) {
+               ucontrol->value.enumerated.item[0] = 0;
+               pr_info("audio in source: AV\n");
+       } else if (audio_in_GPIO == 1) {
+               ucontrol->value.enumerated.item[0] = 1;
+               pr_info("audio in source: Karaok\n");
+       }
+       return 0;
+}
+
+static int aml_set_audio_in_switch(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol) {
+       if (ucontrol->value.enumerated.item[0] == 0) {
+               gpiod_direction_output(av_source,
+                                          GPIOF_OUT_INIT_LOW);
+               audio_in_GPIO = 0;
+               pr_info("Set audio in source: AV\n");
+       } else if (ucontrol->value.enumerated.item[0] == 1) {
+               gpiod_direction_output(av_source,
+                                          GPIOF_OUT_INIT_HIGH);
+               audio_in_GPIO = 1;
+               pr_info("Set audio in source: Karaok\n");
+       }
+       return 0;
+}
+
+static int init_EQ_DRC_module(void)
+{
+       aml_write_cbus(AED_TOP_CTL, (1 << 31)); /* fifo init */
+       aml_write_cbus(AED_ED_CTL, 1); /* soft reset*/
+       msleep(20);
+       aml_write_cbus(AED_ED_CTL, 0); /* soft reset*/
+       aml_write_cbus(AED_TOP_CTL, (0 << 1) /*i2s in sel*/
+                                               | (1 << 0)); /*module enable*/
+       aml_write_cbus(AED_NG_CTL, (3 << 30)); /* disable noise gate*/
+       return 0;
+}
+
+static int set_internal_EQ_volume(
+       unsigned int master_volume,
+       unsigned int channel_1_volume,
+       unsigned int channel_2_volume)
+{
+       aml_write_cbus(AED_EQ_VOLUME, (2 << 30) /* volume step: 0.5dB*/
+                       | (master_volume << 16) /* master volume: 0dB*/
+                       | (channel_1_volume << 8) /* channel 1 volume: 0dB*/
+                       | (channel_2_volume << 0) /* channel 2 volume: 0dB*/
+                       );
+       aml_write_cbus(AED_EQ_VOLUME_SLEW_CNT, 0x40);
+       aml_write_cbus(AED_MUTE, 0);
+       return 0;
+}
+
+static int Speaker_Channel_Mask;
+static const char *const Speaker_Channel_Mask_texts[] = {
+       "Channel0/1", "Channel2/3", "Channe4/5", "Channe6/7" };
+
+static const struct soc_enum Speaker_Channel_Mask_enum =
+       SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+                       ARRAY_SIZE(Speaker_Channel_Mask_texts),
+                       Speaker_Channel_Mask_texts);
+
+static int aml_Speaker_Channel_Mask_get_enum(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = Speaker_Channel_Mask;
+       return 0;
+}
+
+static int aml_Speaker_Channel_Mask_set_enum(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       Speaker_Channel_Mask = ucontrol->value.enumerated.item[0];
+       return 0;
+}
+
+static const struct snd_kcontrol_new av_controls[] = {
+       SOC_ENUM_EXT("AudioIn Switch",
+                        audio_in_switch_enum,
+                        aml_get_audio_in_switch,
+                        aml_set_audio_in_switch),
+};
+
+static const struct snd_kcontrol_new aml_tv_controls[] = {
+       SOC_ENUM_EXT("Audio In Source",
+                    audio_in_source_enum,
+                    aml_audio_get_in_source,
+                    aml_audio_set_in_source),
+
+       SOC_ENUM_EXT("I2SIN Audio Type",
+                    i2s_audio_type_enum,
+                    aml_i2s_audio_type_get_enum,
+                    NULL),
+
+       SOC_ENUM_EXT("SPDIFIN Audio Type",
+                    spdif_audio_type_enum,
+                    aml_spdif_audio_type_get_enum,
+                    aml_spdif_audio_type_set_enum),
+
+       SOC_ENUM_EXT("Hardware resample enable",
+                    hardware_resample_enum,
+                    aml_hardware_resample_get_enum,
+                    aml_hardware_resample_set_enum),
+
+       SOC_ENUM_EXT("Speaker Channel Mask",
+                    Speaker_Channel_Mask_enum,
+                    aml_Speaker_Channel_Mask_get_enum,
+                    aml_Speaker_Channel_Mask_set_enum),
+};
+
+static int set_HW_resample_pause_thd(unsigned int thd)
+{
+       aml_write_cbus(AUD_RESAMPLE_CTRL2,
+                       (1 << 24) /* enable HW_resample_pause*/
+                       | (thd << 11) /* set HW resample pause thd (sample)*/
+                       );
+       return 0;
+}
+
+static int aml_get_cbus_reg(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol) {
+
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int max = mc->max;
+       unsigned int invert = mc->invert;
+       unsigned int value =
+                       (((unsigned int)aml_read_cbus(reg)) >> shift) & max;
+
+       if (invert)
+               value = (~value) & max;
+       ucontrol->value.integer.value[0] = value;
+
+       return 0;
+}
+
+static int aml_set_cbus_reg(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol) {
+
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int max = mc->max;
+       unsigned int invert = mc->invert;
+       unsigned int value = ucontrol->value.integer.value[0];
+       unsigned int reg_value = (unsigned int)aml_read_cbus(reg);
+
+       if (invert)
+               value = (~value) & max;
+       max = ~(max << shift);
+       reg_value &= max;
+       reg_value |= (value << shift);
+       aml_write_cbus(reg, reg_value);
+
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12276, 12, 1);
+static const DECLARE_TLV_DB_SCALE(chvol_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new aml_EQ_DRC_controls[] = {
+       SOC_SINGLE_EXT_TLV("EQ master volume",
+                        AED_EQ_VOLUME, 16, 0x3FF, 1,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        mvol_tlv),
+
+       SOC_SINGLE_EXT_TLV("EQ ch1 volume",
+                        AED_EQ_VOLUME, 8, 0xFF, 1,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        chvol_tlv),
+
+       SOC_SINGLE_EXT_TLV("EQ ch2 volume",
+                        AED_EQ_VOLUME, 0, 0xFF, 1,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        chvol_tlv),
+
+       SOC_SINGLE_EXT_TLV("EQ master volume mute",
+                        AED_MUTE, 31, 0x1, 0,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        NULL),
+
+       SOC_SINGLE_EXT_TLV("EQ enable",
+                        AED_EQ_EN, 0, 0x1, 0,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        NULL),
+
+       SOC_SINGLE_EXT_TLV("DRC enable",
+                        AED_DRC_EN, 0, 0x1, 0,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        NULL),
+
+       SOC_SINGLE_EXT_TLV("NG enable",
+                        AED_NG_CTL, 0, 0x1, 0,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        NULL),
+
+       SOC_SINGLE_EXT_TLV("NG noise thd",
+                        AED_NG_THD0, 8, 0x7FFF, 0,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        NULL),
+
+       SOC_SINGLE_EXT_TLV("NG signal thd",
+                        AED_NG_THD1, 8, 0x7FFF, 0,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        NULL),
+
+       SOC_SINGLE_EXT_TLV("NG counter thd",
+                        AED_NG_CNT_THD, 0, 0xFFFF, 0,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        NULL),
+
+       SOC_SINGLE_EXT_TLV("Hw resample pause enable",
+                        AUD_RESAMPLE_CTRL2, 24, 0x1, 0,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        NULL),
+
+       SOC_SINGLE_EXT_TLV("Hw resample pause thd",
+                        AUD_RESAMPLE_CTRL2, 11, 0x1FFF, 0,
+                        aml_get_cbus_reg, aml_set_cbus_reg,
+                        NULL),
+};
+
+static void aml_audio_start_timer(struct aml_audio_private_data *p_aml_audio,
+                                 unsigned long delay)
+{
+       p_aml_audio->timer.expires = jiffies + delay;
+       p_aml_audio->timer.data = (unsigned long)p_aml_audio;
+       p_aml_audio->detect_flag = -1;
+       add_timer(&p_aml_audio->timer);
+       p_aml_audio->timer_en = 1;
+}
+
+static void aml_audio_stop_timer(struct aml_audio_private_data *p_aml_audio)
+{
+       del_timer_sync(&p_aml_audio->timer);
+       cancel_work_sync(&p_aml_audio->work);
+       p_aml_audio->timer_en = 0;
+       p_aml_audio->detect_flag = -1;
+}
+
+static int audio_hp_status;
+static int aml_get_audio_hp_status(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = audio_hp_status;
+       return 0;
+}
+
+static const char * const audio_hp_status_texts[] = {"Unpluged", "Pluged"};
+
+static const struct soc_enum audio_hp_status_enum = SOC_ENUM_SINGLE(
+                          SND_SOC_NOPM, 0, ARRAY_SIZE(audio_hp_status_texts),
+                          audio_hp_status_texts);
+
+static const struct snd_kcontrol_new hp_controls[] = {
+          SOC_ENUM_EXT("Hp Status",
+                          audio_hp_status_enum,
+                          aml_get_audio_hp_status,
+                          NULL),
+};
+
+static int hp_det_adc_value(struct aml_audio_private_data *p_aml_audio)
+{
+       int ret, hp_value;
+       int hp_val_sum = 0;
+       int loop_num = 0;
+
+       while (loop_num < 8) {
+               hp_value = gpiod_get_value(p_aml_audio->hp_det_desc);
+               if (hp_value < 0) {
+                       pr_info("hp detect get error adc value!\n");
+                       return -1;      /* continue; */
+               }
+               hp_val_sum += hp_value;
+               loop_num++;
+               msleep_interruptible(15);
+       }
+       hp_val_sum = hp_val_sum >> 3;
+
+       if (p_aml_audio->hp_det_inv) {
+               if (hp_val_sum > 0) {
+                       /* plug in */
+                       ret = 1;
+               } else {
+                       /* unplug */
+                       ret = 0;
+               }
+       } else {
+               if (hp_val_sum > 0) {
+                       /* unplug */
+                       ret = 0;
+               } else {
+                       /* plug in */
+                       ret = 1;
+               }
+       }
+
+       return ret;
+}
+
+static int aml_audio_hp_detect(struct aml_audio_private_data *p_aml_audio)
+{
+       int loop_num = 0;
+       int ret;
+
+       p_aml_audio->hp_det_status = false;
+
+       while (loop_num < 3) {
+               ret = hp_det_adc_value(p_aml_audio);
+               if (p_aml_audio->hp_last_state != ret) {
+                       msleep_interruptible(50);
+                       if (ret < 0)
+                               ret = p_aml_audio->hp_last_state;
+                       else
+                               p_aml_audio->hp_last_state = ret;
+               } else
+                       msleep_interruptible(50);
+
+               loop_num = loop_num + 1;
+       }
+
+       return ret;
+}
+
+/*mute: 1, ummute: 0*/
+static int aml_mute_unmute(struct snd_soc_card *card, int av_mute, int amp_mute)
+{
+       struct aml_audio_private_data *p_aml_audio;
+
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+
+       if (!IS_ERR(p_aml_audio->av_mute_desc)) {
+               if (p_aml_audio->av_mute_inv ^ av_mute) {
+                       gpiod_direction_output(
+                               p_aml_audio->av_mute_desc, GPIOF_OUT_INIT_LOW);
+                       pr_info("set av out GPIOF_OUT_INIT_LOW!\n");
+               } else {
+                       gpiod_direction_output(
+                               p_aml_audio->av_mute_desc, GPIOF_OUT_INIT_HIGH);
+                       pr_info("set av out GPIOF_OUT_INIT_HIGH!\n");
+               }
+       }
+
+       if (!IS_ERR(p_aml_audio->amp_mute_desc)) {
+               if (p_aml_audio->amp_mute_inv ^ amp_mute) {
+                       gpiod_direction_output(
+                               p_aml_audio->amp_mute_desc, GPIOF_OUT_INIT_LOW);
+                       pr_info("set amp out GPIOF_OUT_INIT_LOW!\n");
+               } else {
+                       gpiod_direction_output(
+                               p_aml_audio->amp_mute_desc,
+                               GPIOF_OUT_INIT_HIGH);
+                       pr_info("set amp out GPIOF_OUT_INIT_HIGH!\n");
+               }
+       }
+       return 0;
+}
+
+static void aml_asoc_work_func(struct work_struct *work)
+{
+       struct aml_audio_private_data *p_aml_audio = NULL;
+       struct snd_soc_card *card = NULL;
+       int flag = -1;
+
+       p_aml_audio = container_of(work, struct aml_audio_private_data, work);
+       card = (struct snd_soc_card *)p_aml_audio->data;
+
+       flag = aml_audio_hp_detect(p_aml_audio);
+
+       if (p_aml_audio->detect_flag != flag) {
+               p_aml_audio->detect_flag = flag;
+
+               if (flag & 0x1) {
+                       pr_info("aml aduio hp pluged\n");
+                       audio_hp_status = 1;
+                       aml_mute_unmute(card, 0, 1);
+               } else {
+                       pr_info("aml audio hp unpluged\n");
+                       audio_hp_status = 0;
+                       aml_mute_unmute(card, 1, 0);
+               }
+
+       }
+
+       p_aml_audio->hp_det_status = true;
+}
+
+static void aml_asoc_timer_func(unsigned long data)
+{
+       struct aml_audio_private_data *p_aml_audio =
+           (struct aml_audio_private_data *)data;
+       unsigned long delay = msecs_to_jiffies(150);
+
+       if (p_aml_audio->hp_det_status &&
+                       !p_aml_audio->suspended) {
+               schedule_work(&p_aml_audio->work);
+       }
+       mod_timer(&p_aml_audio->timer, jiffies + delay);
+}
+
+static int aml_suspend_pre(struct snd_soc_card *card)
+{
+       struct aml_audio_private_data *p_aml_audio;
+
+       pr_info("enter %s\n", __func__);
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+
+       if (p_aml_audio->av_hs_switch) {
+               /* stop timer */
+               mutex_lock(&p_aml_audio->lock);
+               p_aml_audio->suspended = true;
+               if (p_aml_audio->timer_en)
+                       aml_audio_stop_timer(p_aml_audio);
+
+               mutex_unlock(&p_aml_audio->lock);
+       }
+
+       aml_mute_unmute(card, 1, 1);
+       return 0;
+}
+
+static int aml_suspend_post(struct snd_soc_card *card)
+{
+       pr_info("enter %s\n", __func__);
+       return 0;
+}
+
+static int aml_resume_pre(struct snd_soc_card *card)
+{
+       pr_info("enter %s\n", __func__);
+       return 0;
+}
+
+static int aml_resume_post(struct snd_soc_card *card)
+{
+       struct aml_audio_private_data *p_aml_audio;
+
+       pr_info("enter %s\n", __func__);
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+
+       schedule_work(&p_aml_audio->pinmux_work);
+
+       return 0;
+}
+
+static int aml_asoc_hw_params(struct snd_pcm_substream *substream,
+                             struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       int ret;
+
+       /* set cpu DAI configuration */
+       if (is_meson_txl_cpu())
+               ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+                                 | SND_SOC_DAIFMT_CBM_CFM);
+       else
+               ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF
+                                 | SND_SOC_DAIFMT_CBM_CFM);
+
+       if (ret < 0) {
+               pr_err("%s: set cpu dai fmt failed!\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops aml_asoc_ops = {
+       .hw_params      = aml_asoc_hw_params,
+};
+
+static int aml_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_card *card = rtd->card;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_dapm_context *dapm = &codec->component.dapm;
+       struct aml_audio_private_data *p_aml_audio;
+       int ret = 0;
+
+       pr_info("enter %s\n", __func__);
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+
+       ret = snd_soc_add_card_controls(codec->component.card,
+                                       aml_tv_controls,
+                                       ARRAY_SIZE(aml_tv_controls));
+
+       /* Add specific widgets */
+       snd_soc_dapm_new_controls(dapm, aml_asoc_dapm_widgets,
+                                 ARRAY_SIZE(aml_asoc_dapm_widgets));
+
+       p_aml_audio->pin_ctl =
+               devm_pinctrl_get_select(card->dev, "aml_snd_tv");
+       if (IS_ERR(p_aml_audio->pin_ctl)) {
+               pr_info("%s, aml_tv_pinmux_init error!\n", __func__);
+               return 0;
+       }
+
+       /*read avmute pinmux from dts*/
+       p_aml_audio->av_mute_desc =
+               gpiod_get(card->dev,
+                               "mute_gpio",
+                               GPIOD_OUT_HIGH);
+       of_property_read_u32(card->dev->of_node, "av_mute_inv",
+               &p_aml_audio->av_mute_inv);
+       of_property_read_u32(card->dev->of_node, "sleep_time",
+               &p_aml_audio->sleep_time);
+
+       /*read amp mute pinmux from dts*/
+       p_aml_audio->amp_mute_desc =
+               gpiod_get(card->dev,
+                               "amp_mute_gpio",
+                               GPIOD_OUT_HIGH);
+       of_property_read_u32(card->dev->of_node, "amp_mute_inv",
+               &p_aml_audio->amp_mute_inv);
+
+       /*read headset pinmux from dts*/
+       of_property_read_u32(card->dev->of_node, "av_hs_switch",
+               &p_aml_audio->av_hs_switch);
+
+       if (p_aml_audio->av_hs_switch) {
+               /* headset dection gipo */
+               p_aml_audio->hp_det_desc = gpiod_get(card->dev,
+                       "hp_det", GPIOD_IN);
+               if (!IS_ERR(p_aml_audio->hp_det_desc))
+                       gpiod_direction_input(p_aml_audio->hp_det_desc);
+               else
+                       pr_err("ASoC: hp_det-gpio failed\n");
+
+               of_property_read_u32(card->dev->of_node,
+                       "hp_det_inv",
+                       &p_aml_audio->hp_det_inv);
+               pr_info("hp_det_inv:%d, %s\n",
+                       p_aml_audio->hp_det_inv,
+                       p_aml_audio->hp_det_inv ?
+                       "hs pluged, HP_DET:1; hs unpluged, HS_DET:0"
+                       :
+                       "hs pluged, HP_DET:0; hs unpluged, HS_DET:1");
+
+               p_aml_audio->hp_det_status = true;
+
+               init_timer(&p_aml_audio->timer);
+               p_aml_audio->timer.function = aml_asoc_timer_func;
+               p_aml_audio->timer.data = (unsigned long)p_aml_audio;
+               p_aml_audio->data = (void *)card;
+
+               INIT_WORK(&p_aml_audio->work, aml_asoc_work_func);
+               mutex_init(&p_aml_audio->lock);
+
+               ret = snd_soc_add_card_controls(codec->component.card,
+                                       hp_controls, ARRAY_SIZE(hp_controls));
+       }
+
+       /*It is used for KaraOK, */
+       av_source = gpiod_get(card->dev, "av_source", GPIOD_OUT_LOW);
+       if (!IS_ERR(av_source)) {
+               pr_info("%s, make av_source gpio low!\n", __func__);
+               gpiod_direction_output(av_source, GPIOF_OUT_INIT_LOW);
+               snd_soc_add_card_controls(card, av_controls,
+                                       ARRAY_SIZE(av_controls));
+       }
+
+       return 0;
+}
+
+static void aml_tv_pinmux_init(struct snd_soc_card *card)
+{
+       struct aml_audio_private_data *p_aml_audio;
+
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+
+       if (!p_aml_audio->av_hs_switch) {
+               if (p_aml_audio->sleep_time &&
+                               (!IS_ERR(p_aml_audio->av_mute_desc)))
+                       msleep(p_aml_audio->sleep_time);
+               aml_mute_unmute(card, 0, 0);
+               pr_info("av_mute_inv:%d, amp_mute_inv:%d, sleep %d ms\n",
+                       p_aml_audio->av_mute_inv, p_aml_audio->amp_mute_inv,
+                       p_aml_audio->sleep_time);
+       } else {
+               if (p_aml_audio->sleep_time &&
+                               (!IS_ERR(p_aml_audio->av_mute_desc)))
+                       msleep(p_aml_audio->sleep_time);
+               pr_info("aml audio hs detect enable!\n");
+               p_aml_audio->suspended = false;
+               mutex_lock(&p_aml_audio->lock);
+               if (!p_aml_audio->timer_en) {
+                       aml_audio_start_timer(p_aml_audio,
+                                                 msecs_to_jiffies(100));
+               }
+               mutex_unlock(&p_aml_audio->lock);
+       }
+}
+
+static int aml_card_dai_parse_of(struct device *dev,
+                                struct snd_soc_dai_link *dai_link,
+                                int (*init)(
+                                        struct snd_soc_pcm_runtime *rtd),
+                                struct device_node *cpu_node,
+                                struct device_node *codec_node,
+                                struct device_node *plat_node)
+{
+       int ret;
+
+       /* get cpu dai->name */
+       ret = snd_soc_of_get_dai_name(cpu_node, &dai_link->cpu_dai_name);
+       if (ret < 0)
+               goto parse_error;
+
+       /* get codec dai->name */
+       ret = snd_soc_of_get_dai_name(codec_node, &dai_link->codec_dai_name);
+       if (ret < 0)
+               goto parse_error;
+
+       dai_link->name = dai_link->stream_name = dai_link->cpu_dai_name;
+       dai_link->codec_of_node = of_parse_phandle(codec_node, "sound-dai", 0);
+       dai_link->platform_of_node = plat_node;
+       dai_link->init = init;
+
+       return 0;
+
+parse_error:
+       return ret;
+}
+
+struct snd_soc_aux_dev tv_audio_aux_dev;
+static struct snd_soc_codec_conf tv_audio_codec_conf[] = {
+       {
+               .name_prefix = "AMP",
+       },
+};
+static struct codec_probe_priv prob_priv;
+static struct codec_info codec_info_aux;
+
+static int get_audio_codec_i2c_info(struct device_node *p_node,
+                               struct aml_audio_codec_info *audio_codec_dev)
+{
+       const char *str;
+       int ret = 0;
+       unsigned int i2c_addr;
+
+       ret = of_property_read_string(p_node, "codec_name",
+                                     &audio_codec_dev->name);
+       if (ret) {
+               pr_info("get audio codec name failed!\n");
+               goto err_out;
+       }
+
+       ret = of_property_match_string(p_node, "status", "okay");
+       if (ret) {
+               pr_info("%s:this audio codec is disabled!\n",
+                       audio_codec_dev->name);
+               goto err_out;
+       }
+
+       pr_debug("use audio aux codec %s\n", audio_codec_dev->name);
+
+       ret = of_property_read_string(p_node, "i2c_bus", &str);
+       if (ret) {
+               pr_err("%s: failed to get i2c_bus str,use default i2c bus!\n",
+                      audio_codec_dev->name);
+               audio_codec_dev->i2c_bus_type = AML_I2C_BUS_D;
+       } else {
+               if (!strncmp(str, "i2c_bus_a", 9))
+                       audio_codec_dev->i2c_bus_type = AML_I2C_BUS_A;
+               else if (!strncmp(str, "i2c_bus_b", 9))
+                       audio_codec_dev->i2c_bus_type = AML_I2C_BUS_B;
+               else if (!strncmp(str, "i2c_bus_c", 9))
+                       audio_codec_dev->i2c_bus_type = AML_I2C_BUS_C;
+               else if (!strncmp(str, "i2c_bus_d", 9))
+                       audio_codec_dev->i2c_bus_type = AML_I2C_BUS_D;
+               else if (!strncmp(str, "i2c_bus_ao", 10))
+                       audio_codec_dev->i2c_bus_type = AML_I2C_BUS_AO;
+               else
+                       audio_codec_dev->i2c_bus_type = AML_I2C_BUS_D;
+       }
+
+       ret = of_property_read_u32(p_node, "i2c_addr", &i2c_addr);
+       if (!ret)
+               audio_codec_dev->i2c_addr = i2c_addr;
+       /* pr_info("audio aux codec addr: 0x%x, audio codec i2c bus: %d\n",
+        *      audio_codec_dev->i2c_addr, audio_codec_dev->i2c_bus_type);
+        */
+err_out:
+       return ret;
+}
+
+static char drc1_table[15] = "drc1_table_0";
+static char drc1_tko_table[20] = "drc1_tko_table_0";
+static char drc2_table[15] = "drc2_table_0";
+static char drc2_tko_table[20] = "drc2_tko_table_0";
+static int aml_drc_type_select(char *s)
+{
+       char *sel = s;
+
+       if (s != NULL) {
+               sprintf(drc1_table, "%s%s", "drc1_table_", sel);
+               sprintf(drc1_tko_table, "%s%s", "drc1_tko_table_", sel);
+               sprintf(drc2_table, "%s%s", "drc2_table_", sel);
+               sprintf(drc2_tko_table, "%s%s", "drc2_tko_table_", sel);
+               pr_info("select drc type: %s\n", sel);
+       }
+       return 0;
+}
+__setup("amp_drc_type=", aml_drc_type_select);
+
+static char table[10] = "table_0";
+static char wall[10] = "wall_0";
+static char sub_bq_table[20] = "sub_bq_table_0";
+static int aml_eq_type_select(char *s)
+{
+       char *sel = s;
+
+       if (s != NULL) {
+               sprintf(table, "%s%s", "table_", sel);
+               sprintf(wall, "%s%s", "wall_", sel);
+               sprintf(sub_bq_table, "%s%s", "sub_bq_table_", sel);
+               pr_info("select eq type: %s\n", sel);
+       }
+       return 0;
+}
+__setup("amp_eq_type=", aml_eq_type_select);
+
+static void *alloc_and_get_data_array(struct device_node *p_node, char *str,
+                                     int *lenp)
+{
+       int ret = 0, length = 0;
+       char *p = NULL;
+
+       if (of_find_property(p_node, str, &length) == NULL) {
+               pr_err("DTD of %s not found!\n", str);
+               goto exit;
+       }
+       pr_debug("prop name=%s,length=%d\n", str, length);
+       p = kzalloc((length * sizeof(char *)), GFP_KERNEL);
+       if (p == NULL) {
+               pr_err("ERROR, NO enough mem for %s!\n", str);
+               length = 0;
+               goto exit;
+       }
+
+       ret = of_property_read_u8_array(p_node, str, p, length);
+       if (ret) {
+               pr_err("no of property %s!\n", str);
+               kfree(p);
+               p = NULL;
+               goto exit;
+       }
+
+       *lenp = length;
+
+exit: return p;
+}
+#ifdef CONFIG_SND_SOC_TAS5707
+static int of_get_eq_pdata(struct tas57xx_platform_data *pdata,
+                          struct device_node *p_node)
+{
+       int length = 0;
+       char *regs = NULL;
+       int ret = 0;
+
+       ret = of_property_read_u32(p_node, "eq_enable", &pdata->eq_enable);
+       if (pdata->eq_enable == 0 || ret != 0) {
+               pr_err("Fail to get eq_enable node or EQ disable!\n");
+               return -2;
+       }
+
+       prob_priv.num_eq = 2;
+       pdata->num_eq_cfgs = prob_priv.num_eq;
+
+       prob_priv.eq_configs = kzalloc(
+               prob_priv.num_eq * sizeof(struct tas57xx_eq_cfg), GFP_KERNEL);
+
+       regs = alloc_and_get_data_array(p_node, table, &length);
+       if (regs == NULL) {
+               kfree(prob_priv.eq_configs);
+               return -2;
+       }
+       strncpy(prob_priv.eq_configs[0].name, table, NAME_SIZE);
+       prob_priv.eq_configs[0].regs = regs;
+       prob_priv.eq_configs[0].reg_bytes = length;
+
+       regs = alloc_and_get_data_array(p_node, wall, &length);
+       if (regs == NULL) {
+               kfree(prob_priv.eq_configs);
+               return -2;
+       }
+       strncpy(prob_priv.eq_configs[1].name, wall, NAME_SIZE);
+       prob_priv.eq_configs[1].regs = regs;
+       prob_priv.eq_configs[1].reg_bytes = length;
+
+       pdata->eq_cfgs = prob_priv.eq_configs;
+       return 0;
+}
+
+static int of_get_drc_pdata(struct tas57xx_platform_data *pdata,
+                           struct device_node *p_node)
+{
+       int length = 0;
+       char *pd = NULL;
+       int ret = 0;
+
+       ret = of_property_read_u32(p_node, "drc_enable", &pdata->drc_enable);
+       if (pdata->drc_enable == 0 || ret != 0) {
+               pr_err("Fail to get drc_enable node or DRC disable!\n");
+               return -2;
+       }
+
+       /* get drc1 table */
+       pd = alloc_and_get_data_array(p_node, drc1_table, &length);
+       if (pd == NULL)
+               return -2;
+       pdata->custom_drc1_table_len = length;
+       pdata->custom_drc1_table = pd;
+
+       /* get drc1 tko table */
+       length = 0;
+       pd = NULL;
+
+       pd = alloc_and_get_data_array(p_node, drc1_tko_table, &length);
+       if (pd == NULL)
+               return -2;
+       pdata->custom_drc1_tko_table_len = length;
+       pdata->custom_drc1_tko_table = pd;
+       pdata->enable_ch1_drc = 1;
+
+       /* get drc2 table */
+       length = 0;
+       pd = NULL;
+       pd = alloc_and_get_data_array(p_node, drc2_table, &length);
+       if (pd == NULL)
+               return -1;
+       pdata->custom_drc2_table_len = length;
+       pdata->custom_drc2_table = pd;
+
+       /* get drc2 tko table */
+       length = 0;
+       pd = NULL;
+       pd = alloc_and_get_data_array(p_node, drc2_tko_table, &length);
+       if (pd == NULL)
+               return -1;
+       pdata->custom_drc2_tko_table_len = length;
+       pdata->custom_drc2_tko_table = pd;
+       pdata->enable_ch2_drc = 1;
+
+       return 0;
+}
+
+static int of_get_init_pdata(struct tas57xx_platform_data *pdata,
+                            struct device_node *p_node)
+{
+       int length = 0;
+       char *pd = NULL;
+
+       pd = alloc_and_get_data_array(p_node, "input_mux_reg_buf", &length);
+       if (pd == NULL) {
+               pr_err("%s : can't get input_mux_reg_buf\n", __func__);
+               return -1;
+       }
+
+       /*Now only support 0x20 input mux init*/
+       pdata->num_init_regs = length;
+       pdata->init_regs = pd;
+
+       if (of_property_read_u32(p_node, "master_vol",
+                                &pdata->custom_master_vol)) {
+               pr_err("%s fail to get master volume\n", __func__);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int of_get_resetpin_pdata(struct tas57xx_platform_data *pdata,
+                                struct device_node *p_node)
+{
+       enum of_gpio_flags flags;
+       int reset_pin;
+
+       reset_pin = of_get_named_gpio_flags(p_node, "reset_pin", 0, &flags);
+       if (reset_pin < 0) {
+               pr_err("%s fail to get reset pin from dts!\n", __func__);
+               return reset_pin;
+       }
+       gpio_request(reset_pin, NULL);
+       gpio_direction_output(reset_pin, GPIOF_OUT_INIT_LOW);
+       pdata->reset_pin = reset_pin;
+       pr_info("%s pdata->reset_pin = %d!\n", __func__,
+               pdata->reset_pin);
+       return 0;
+}
+
+static int of_get_phonepin_pdata(struct tas57xx_platform_data *pdata,
+                                struct device_node *p_node)
+{
+       enum of_gpio_flags flags;
+       int phone_pin;
+
+       phone_pin = of_get_named_gpio_flags(p_node, "phone_pin", 0, &flags);
+       if (phone_pin < 0) {
+               pr_err("%s fail to get phone pin from dts!\n", __func__);
+               return phone_pin;
+       }
+
+       gpio_request(phone_pin, NULL);
+       gpio_direction_output(phone_pin, GPIOF_OUT_INIT_LOW);
+       pdata->phone_pin = phone_pin;
+       pr_info("%s pdata->phone_pin = %d!\n", __func__,
+               pdata->phone_pin);
+       return 0;
+}
+static int of_get_scanpin_pdata(struct tas57xx_platform_data *pdata,
+                                struct device_node *p_node)
+{
+       enum of_gpio_flags flags;
+       int scan_pin;
+
+       scan_pin = of_get_named_gpio_flags(p_node, "scan_pin", 0, &flags);
+       if (scan_pin < 0) {
+               pr_err("%s fail to get scan pin from dts!\n", __func__);
+               return scan_pin;
+       }
+       gpio_request(scan_pin, NULL);
+       gpio_direction_input(scan_pin);
+       pdata->scan_pin = scan_pin;
+       pr_info("%s pdata->scan_pin = %d!\n", __func__,
+               pdata->scan_pin);
+       return 0;
+}
+
+static int codec_get_of_pdata(struct tas57xx_platform_data *pdata,
+                             struct device_node *p_node)
+{
+       int ret = 0;
+
+       ret = of_get_resetpin_pdata(pdata, p_node);
+       if (ret)
+               pr_info("codec reset pin is not found in dts\n");
+       ret = of_get_phonepin_pdata(pdata, p_node);
+       if (ret)
+               pr_info("codec phone pin is not found in dtd\n");
+
+       ret = of_get_scanpin_pdata(pdata, p_node);
+       if (ret)
+               pr_info("codec scanp pin is not found in dtd\n");
+
+       ret = of_get_drc_pdata(pdata, p_node);
+       if (ret == -2)
+               pr_info("codec DRC configs are not found in dts\n");
+
+       ret = of_get_eq_pdata(pdata, p_node);
+       if (ret)
+               pr_info("codec EQ configs are not found in dts\n");
+
+       ret = of_get_init_pdata(pdata, p_node);
+       if (ret)
+               pr_info("codec init configs are not found in dts\n");
+       return ret;
+}
+#endif
+
+static int aml_aux_dev_parse_of(struct snd_soc_card *card)
+{
+       struct device_node *audio_codec_node = card->dev->of_node;
+       struct device_node *child;
+       struct i2c_board_info board_info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *client;
+       struct aml_audio_codec_info temp_audio_codec;
+#ifdef CONFIG_SND_SOC_TAS5707
+       struct tas57xx_platform_data *pdata;
+#endif
+       char tmp[I2C_NAME_SIZE];
+       const char *aux_dev;
+
+       if (of_property_read_string(audio_codec_node, "aux_dev", &aux_dev)) {
+               pr_info("no aux dev!\n");
+               return -ENODEV;
+       }
+       pr_info("aux name = %s\n", aux_dev);
+       child = of_get_child_by_name(audio_codec_node, aux_dev);
+       if (child == NULL) {
+               pr_info("error: failed to find aux dev node %s\n", aux_dev);
+               return -1;
+       }
+
+       memset(&temp_audio_codec, 0, sizeof(struct aml_audio_codec_info));
+       /*pr_info("%s, child name:%s\n", __func__, child->name);*/
+
+       if (get_audio_codec_i2c_info(child, &temp_audio_codec) == 0) {
+               memset(&board_info, 0, sizeof(board_info));
+               strncpy(board_info.type, temp_audio_codec.name, I2C_NAME_SIZE);
+               adapter = i2c_get_adapter(temp_audio_codec.i2c_bus_type);
+               board_info.addr = temp_audio_codec.i2c_addr;
+               board_info.platform_data = &temp_audio_codec;
+               client = i2c_new_device(adapter, &board_info);
+               snprintf(tmp, I2C_NAME_SIZE, "%s", temp_audio_codec.name);
+               strlcpy(codec_info_aux.name, tmp, I2C_NAME_SIZE);
+               snprintf(tmp, I2C_NAME_SIZE, "%s.%s", temp_audio_codec.name,
+                               dev_name(&client->dev));
+               strlcpy(codec_info_aux.name_bus, tmp, I2C_NAME_SIZE);
+
+               tv_audio_aux_dev.name = codec_info_aux.name;
+               tv_audio_aux_dev.codec_name = codec_info_aux.name_bus;
+               tv_audio_codec_conf[0].dev_name = codec_info_aux.name_bus;
+
+               card->aux_dev = &tv_audio_aux_dev,
+               card->num_aux_devs = 1,
+               card->codec_conf = tv_audio_codec_conf,
+               card->num_configs = ARRAY_SIZE(tv_audio_codec_conf),
+#ifdef CONFIG_SND_SOC_TAS5707
+               pdata =
+                       kzalloc(sizeof(struct tas57xx_platform_data),
+                               GFP_KERNEL);
+               if (!pdata) {
+                       pr_err("error: malloc tas57xx_platform_data!\n");
+                       return -ENOMEM;
+               }
+               codec_get_of_pdata(pdata, child);
+               client->dev.platform_data = pdata;
+#endif
+               Speaker_Channel_Mask = 1;
+       }
+       return 0;
+}
+static int aml_card_dais_parse_of(struct snd_soc_card *card)
+{
+       struct device_node *np = card->dev->of_node;
+       struct device_node *cpu_node, *codec_node, *plat_node;
+       struct device *dev = card->dev;
+       struct snd_soc_dai_link *dai_links;
+       int num_dai_links, cpu_num, codec_num, plat_num;
+       int i, ret;
+
+       int (*init)(struct snd_soc_pcm_runtime *rtd);
+
+       ret = of_count_phandle_with_args(np, "cpu_list", NULL);
+       if (ret < 0) {
+               dev_err(dev, "AML sound card no cpu_list errno: %d\n", ret);
+               goto err;
+       } else {
+               cpu_num = ret;
+       }
+       ret = of_count_phandle_with_args(np, "codec_list", NULL);
+       if (ret < 0) {
+               dev_err(dev, "AML sound card no codec_list errno: %d\n", ret);
+               goto err;
+       } else {
+               codec_num = ret;
+       }
+       ret = of_count_phandle_with_args(np, "plat_list", NULL);
+       if (ret < 0) {
+               dev_err(dev, "AML sound card no plat_list errno: %d\n", ret);
+               goto err;
+       } else {
+               plat_num = ret;
+       }
+       if ((cpu_num == codec_num) && (cpu_num == plat_num)) {
+               num_dai_links = cpu_num;
+       } else {
+               dev_err(dev,
+                       "AML sound card cpu_dai num, codec_dai num, platform num don't match: %d\n",
+                       ret);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       dai_links =
+               devm_kzalloc(dev,
+                            num_dai_links * sizeof(struct snd_soc_dai_link),
+                            GFP_KERNEL);
+       if (!dai_links) {
+               dev_err(dev, "Can't allocate snd_soc_dai_links\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+       card->dai_link = dai_links;
+       card->num_links = num_dai_links;
+       for (i = 0; i < num_dai_links; i++) {
+               init = NULL;
+               /* CPU sub-node */
+               cpu_node = of_parse_phandle(np, "cpu_list", i);
+               if (cpu_node < 0) {
+                       dev_err(dev, "parse aml sound card cpu list error\n");
+                       return -EINVAL;
+               }
+               /* CODEC sub-node */
+               codec_node = of_parse_phandle(np, "codec_list", i);
+               if (codec_node < 0) {
+                       dev_err(dev, "parse aml sound card codec list error\n");
+                       return ret;
+               }
+               /* Platform sub-node */
+               plat_node = of_parse_phandle(np, "plat_list", i);
+               if (plat_node < 0) {
+                       dev_err(dev,
+                               "parse aml sound card platform list error\n");
+                       return ret;
+               }
+               if (i == 0)
+                       init = aml_asoc_init;
+
+               ret =
+                       aml_card_dai_parse_of(dev, &dai_links[i], init,
+                                             cpu_node,
+                                             codec_node, plat_node);
+
+               dai_links[0].ops = &aml_asoc_ops;
+       }
+
+err:
+       return ret;
+}
+
+static int aml_EQ_DRC_parse_of(struct snd_soc_card *card)
+{
+       struct device_node *audio_codec_node = card->dev->of_node;
+       struct device_node *child;
+       struct aml_audio_private_data *p_aml_audio;
+       int length = 0;
+       int ret = 0;
+       int i = 0;
+       u32 *reg_ptr = &aml_EQ_param[0][0];
+
+       p_aml_audio = snd_soc_card_get_drvdata(card);
+
+       child = of_get_child_by_name(audio_codec_node, "aml_EQ_DRC");
+       if (child == NULL) {
+               pr_err("Error: failed to find node %s\n", "aml_EQ_DRC");
+               return -1;
+       }
+
+       if (of_find_property(child, "eq_table", &length) == NULL) {
+               pr_err("[%s] node not found!\n", "eq_table");
+       } else {
+               of_property_read_u32(child, "EQ_enable",
+                               &p_aml_audio->aml_EQ_enable);
+               /*read EQ value from dts*/
+               if (p_aml_audio->aml_EQ_enable) {
+                       ret = of_property_read_u32_array(child, "eq_table",
+                                       reg_ptr, 100);
+                       if (ret) {
+                               pr_err("Can't get EQ param [%s]!\n",
+                                       "eq_table");
+                       } else {
+                               for (i = 0; i < 100; i++) {
+                                       aml_write_cbus(AED_EQ_CH1_COEF00 + i,
+                                               *reg_ptr);
+                                       /* pr_info("EQ value[%d]: 0x%x\n",
+                                        * i, *reg_ptr);
+                                        */
+                                       reg_ptr++;
+                               }
+                               /*enable aml EQ*/
+                               aml_cbus_update_bits(AED_EQ_EN, 0x1, 0x1);
+                               pr_info("aml EQ enable!\n");
+                       }
+               }
+       }
+
+       if (of_find_property(child, "drc_table", &length) == NULL ||
+                       of_find_property(child, "drc_tko_table", &length)
+                       == NULL) {
+               pr_err("[%s or %s] not found!\n", "drc_table", "drc_tko_table");
+       } else {
+               /*read DRC value from dts*/
+               of_property_read_u32(child, "DRC_enable",
+                       &p_aml_audio->aml_DRC_enable);
+               if (p_aml_audio->aml_DRC_enable) {
+                       reg_ptr = &drc_table[0][0];
+                       ret = of_property_read_u32_array(child, "drc_table",
+                                       reg_ptr, 6);
+                       if (ret) {
+                               pr_err("Can't get drc param [%s]!\n",
+                                       "drc_table");
+                       } else {
+                               aml_write_cbus(AED_DRC_AE,
+                                       drc_table[0][0]);
+                               aml_write_cbus(AED_DRC_AA,
+                                       drc_table[1][0]);
+                               aml_write_cbus(AED_DRC_AD,
+                                       drc_table[2][0]);
+                               aml_write_cbus(AED_DRC_AE_1M,
+                                       drc_table[0][1]);
+                               aml_write_cbus(AED_DRC_AA_1M,
+                                       drc_table[1][1]);
+                               aml_write_cbus(AED_DRC_AD_1M,
+                                       drc_table[2][1]);
+                               /* pr_info("DRC table: 0x%x, 0x%x,"
+                                * "0x%x, 0x%x, 0x%x, 0x%x,\n",
+                                * drc_table[0][0], drc_table[0][1],
+                                * drc_table[1][0], drc_table[1][1],
+                                * drc_table[2][0], drc_table[2][1]);
+                                */
+                       }
+
+                       reg_ptr = &drc_tko_table[0][0];
+                       ret = of_property_read_u32_array(child, "drc_tko_table",
+                                               reg_ptr, 6);
+                       if (ret) {
+                               pr_err("Can't get drc param [%s]!\n",
+                                       "drc_tko_table");
+                       } else {
+                               aml_write_cbus(AED_DRC_OFFSET0,
+                                       drc_tko_table[0][0]);
+                               aml_write_cbus(AED_DRC_OFFSET1,
+                                       drc_tko_table[1][0]);
+                               aml_write_cbus(AED_DRC_THD0,
+                                       drc_tko_table[0][1]);
+                               aml_write_cbus(AED_DRC_THD1,
+                                       drc_tko_table[1][1]);
+                               aml_write_cbus(AED_DRC_K0,
+                                       drc_tko_table[0][2]);
+                               aml_write_cbus(AED_DRC_K1,
+                                       drc_tko_table[1][2]);
+                               /* pr_info("DRC tko: 0x%x, 0x%x,"
+                                * "0x%x, 0x%x, 0x%x, 0x%x,\n",
+                                * drc_tko_table[0][0], drc_tko_table[1][0],
+                                * drc_tko_table[0][1], drc_tko_table[1][1],
+                                * drc_tko_table[0][2], drc_tko_table[1][2]);
+                                */
+
+                               /*enable aml DRC*/
+                               aml_cbus_update_bits(AED_DRC_EN, 0x1, 0x1);
+                               pr_info("aml DRC enable!\n");
+                       }
+               }
+       }
+       return 0;
+}
+
+static void aml_pinmux_work_func(struct work_struct *pinmux_work)
+{
+       struct aml_audio_private_data *p_aml_audio = NULL;
+       struct snd_soc_card *card = NULL;
+
+       p_aml_audio = container_of(pinmux_work,
+                                 struct aml_audio_private_data, pinmux_work);
+       card = (struct snd_soc_card *)p_aml_audio->data;
+
+       aml_tv_pinmux_init(card);
+}
+
+static int aml_tv_audio_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct snd_soc_card *card;
+       struct aml_audio_private_data *p_aml_audio;
+       int ret;
+
+       p_aml_audio =
+               devm_kzalloc(dev, sizeof(struct aml_audio_private_data),
+                                GFP_KERNEL);
+       if (!p_aml_audio) {
+               dev_err(&pdev->dev, "Can't allocate aml_audio_private_data\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       card = devm_kzalloc(dev, sizeof(struct snd_soc_card), GFP_KERNEL);
+       if (!card) {
+               /*dev_err(&pdev->dev, "Can't allocate snd_soc_card\n");*/
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       snd_soc_card_set_drvdata(card, p_aml_audio);
+
+       card->dev = dev;
+       platform_set_drvdata(pdev, card);
+       ret = snd_soc_of_parse_card_name(card, "aml_sound_card,name");
+       if (ret < 0) {
+               dev_err(dev, "no specific snd_soc_card name\n");
+               goto err;
+       }
+
+       ret = aml_card_dais_parse_of(card);
+       if (ret < 0) {
+               dev_err(dev, "parse aml sound card dais error %d\n", ret);
+               goto err;
+       }
+       aml_aux_dev_parse_of(card);
+
+       card->suspend_pre = aml_suspend_pre,
+       card->suspend_post = aml_suspend_post,
+       card->resume_pre = aml_resume_pre,
+       card->resume_post = aml_resume_post,
+
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret < 0) {
+               dev_err(dev, "register aml sound card error %d\n", ret);
+               goto err;
+       }
+
+       if (is_meson_txl_cpu()) {
+               set_internal_EQ_volume(0xc0, 0x30, 0x30);
+               init_EQ_DRC_module();
+               snd_soc_add_card_controls(card, aml_EQ_DRC_controls,
+                                       ARRAY_SIZE(aml_EQ_DRC_controls));
+               aml_EQ_DRC_parse_of(card);
+               set_HW_resample_pause_thd(128);
+       }
+
+       p_aml_audio->data = (void *)card;
+       INIT_WORK(&p_aml_audio->pinmux_work, aml_pinmux_work_func);
+       schedule_work(&p_aml_audio->pinmux_work);
+
+       return 0;
+err:
+       dev_err(dev, "Can't probe snd_soc_card\n");
+       return ret;
+}
+
+static void aml_tv_audio_shutdown(struct platform_device *pdev)
+{
+       struct snd_soc_card *card;
+
+       card = platform_get_drvdata(pdev);
+       aml_suspend_pre(card);
+}
+
+
+static const struct of_device_id amlogic_audio_of_match[] = {
+       { .compatible = "aml, aml_snd_tv", },
+       {},
+};
+
+static struct platform_driver aml_tv_audio_driver = {
+       .driver                 = {
+               .name           = DRV_NAME,
+               .owner          = THIS_MODULE,
+               .of_match_table = amlogic_audio_of_match,
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe                  = aml_tv_audio_probe,
+       .shutdown               = aml_tv_audio_shutdown,
+};
+
+module_platform_driver(aml_tv_audio_driver);
+
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_DESCRIPTION("AML_TV audio machine Asoc driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amlogic/aml_tv.h b/sound/soc/amlogic/aml_tv.h
new file mode 100644 (file)
index 0000000..260c766
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * sound/soc/amlogic/aml_tv.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef AML_TV_H
+#define AML_TV_H
+
+#include <sound/soc.h>
+#include <linux/gpio/consumer.h>
+
+#define AML_I2C_BUS_AO 0
+#define AML_I2C_BUS_A 1
+#define AML_I2C_BUS_B 2
+#define AML_I2C_BUS_C 3
+#define AML_I2C_BUS_D 4
+
+struct aml_audio_private_data {
+       int clock_en;
+       bool suspended;
+       void *data;
+
+       int hp_last_state;
+       bool hp_det_status;
+       int av_hs_switch;
+       int hp_det_inv;
+       int timer_en;
+       int detect_flag;
+       struct work_struct work;
+       struct mutex lock;
+       struct gpio_desc *hp_det_desc;
+
+       struct pinctrl *pin_ctl;
+       struct timer_list timer;
+       struct gpio_desc *av_mute_desc;
+       int av_mute_inv;
+       struct gpio_desc *amp_mute_desc;
+       int amp_mute_inv;
+       struct clk *clk;
+       int sleep_time;
+       struct work_struct pinmux_work;
+       int aml_EQ_enable;
+       int aml_DRC_enable;
+};
+
+struct aml_audio_codec_info {
+       const char *name;
+       const char *status;
+       struct device_node *p_node;
+       unsigned int i2c_bus_type;
+       unsigned int i2c_addr;
+       unsigned int id_reg;
+       unsigned int id_val;
+       unsigned int capless;
+};
+
+struct codec_info {
+       char name[I2C_NAME_SIZE];
+       char name_bus[I2C_NAME_SIZE];
+};
+
+struct codec_probe_priv {
+       int num_eq;
+       struct tas57xx_eq_cfg *eq_configs;
+};
+
+#endif
index c67667bb970f1729db65b027dd8e963b9ee95b7b..218ec12314d6ea51a4de3155322073a7afd928c4 100644 (file)
@@ -1089,4 +1089,6 @@ config SND_SOC_TPA6130A2
        tristate "Texas Instruments TPA6130A2 headphone amplifier"
        depends on I2C
 
+# for Amlogic Codecs
+source "sound/soc/codecs/amlogic/Kconfig"
 endmenu
index 958cd4912fbc9820f965c0d2f38692857f410d3a..efad182254c9640839d72ff5ff16ce5e9a87ef16 100644 (file)
@@ -440,3 +440,5 @@ obj-$(CONFIG_SND_SOC_WM_HUBS)       += snd-soc-wm-hubs.o
 obj-$(CONFIG_SND_SOC_MAX9877)  += snd-soc-max9877.o
 obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o
 obj-$(CONFIG_SND_SOC_TPA6130A2)        += snd-soc-tpa6130a2.o
+# Amlogic
+obj-$(CONFIG_AMLOGIC_SND_SOC_CODECS) += amlogic/
diff --git a/sound/soc/codecs/amlogic/Kconfig b/sound/soc/codecs/amlogic/Kconfig
new file mode 100644 (file)
index 0000000..2e38b13
--- /dev/null
@@ -0,0 +1,41 @@
+menuconfig AMLOGIC_SND_SOC_CODECS
+       bool "AMLOGIC CODEC drivers"
+       default n
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the Amlogic Asoc interface. You will also need
+         to select the audio interfaces to support below.
+
+#if AMLOGIC_SND_SOC_CODECS
+
+config AMLOGIC_SND_CODEC_DUMMY_CODEC
+       bool "Amlogic Audio dummy codec"
+       depends on AMLOGIC_SND_SOC_CODECS
+    default n
+       help
+               Amlogic Audio codec,
+               dummy codec,
+               dummy codec,
+               this codec is internal
+
+config AMLOGIC_SND_CODEC_PCM2BT
+       bool "Amlogic Audio pcm2bt codec"
+       depends on AMLOGIC_SND_SOC_CODECS
+    default n
+       help
+               Amlogic Audio codec,
+               pcm2bt codec,
+               pcm2bt codec,
+               this codec is internal
+
+config AMLOGIC_SND_CODEC_AMLT9015
+       bool "Amlogic Audio AMLT9015 codec"
+       depends on AMLOGIC_SND_SOC_CODECS
+    default n
+       help
+               Amlogic Audio codec,
+               AMLT9015 codec,
+               AMLT9015 codec,
+               this codec is internal
+
+#endif #AMLOGIC_SND_SOC_CODECS
\ No newline at end of file
diff --git a/sound/soc/codecs/amlogic/Makefile b/sound/soc/codecs/amlogic/Makefile
new file mode 100644 (file)
index 0000000..817b59c
--- /dev/null
@@ -0,0 +1,9 @@
+#Amlogic
+snd-soc-dummy_codec-objs := dummy_codec.o
+snd-soc-pcm2bt-objs  := pcm2bt.o
+snd-soc-aml_t9015-objs := aml_codec_t9015.o
+
+# Amlogic
+obj-$(CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC)    += snd-soc-dummy_codec.o
+obj-$(CONFIG_AMLOGIC_SND_CODEC_PCM2BT) += snd-soc-pcm2bt.o
+obj-$(CONFIG_AMLOGIC_SND_CODEC_AMLT9015)       += snd-soc-aml_t9015.o
\ No newline at end of file
diff --git a/sound/soc/codecs/amlogic/aml_codec_t9015.c b/sound/soc/codecs/amlogic/aml_codec_t9015.c
new file mode 100644 (file)
index 0000000..c43ca2f
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * sound/soc/codecs/amlogic/aml_codec_t9015.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/regmap.h>
+
+#include <linux/amlogic/iomap.h>
+#include <linux/amlogic/media/sound/aiu_regs.h>
+
+#include "aml_codec_t9015.h"
+
+struct aml_T9015_audio_priv {
+       struct snd_soc_codec *codec;
+       struct snd_pcm_hw_params *params;
+};
+
+static const struct reg_default t9015_init_list[] = {
+       {AUDIO_CONFIG_BLOCK_ENABLE, 0x0000B00F},
+       {ADC_VOL_CTR_PGA_IN_CONFIG, 0x00000000},
+       {DAC_VOL_CTR_DAC_SOFT_MUTE, 0xFBFB0000},
+       {LINE_OUT_CONFIG, 0x00001111},
+       {POWER_CONFIG, 0x00010000},
+};
+
+static int aml_T9015_audio_reg_init(struct snd_soc_codec *codec)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(t9015_init_list); i++)
+               snd_soc_write(codec, t9015_init_list[i].reg,
+                               t9015_init_list[i].def);
+
+       return 0;
+}
+
+static int aml_DAC_Gain_get_enum(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u32 add, val, val1, val2;
+
+       if (codec == NULL)
+               return -1;
+
+       add = ADC_VOL_CTR_PGA_IN_CONFIG;
+       val = snd_soc_read(codec, add);
+       val1 = (val & (0x1 <<  DAC_GAIN_SEL_L)) >> DAC_GAIN_SEL_L;
+       val2 = (val & (0x1 <<  DAC_GAIN_SEL_H)) >> (DAC_GAIN_SEL_H - 1);
+
+       val = val1 | val2;
+       ucontrol->value.enumerated.item[0] = val;
+       return 0;
+}
+
+static int aml_DAC_Gain_set_enum(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u32 add = ADC_VOL_CTR_PGA_IN_CONFIG;
+       u32 val = snd_soc_read(codec, add);
+
+       if (ucontrol->value.enumerated.item[0] == 0) {
+               val &= ~(0x1 << DAC_GAIN_SEL_H);
+               val &= ~(0x1 << DAC_GAIN_SEL_L);
+       } else if (ucontrol->value.enumerated.item[0] == 1) {
+               val &= ~(0x1 << DAC_GAIN_SEL_H);
+               val |= (0x1 << DAC_GAIN_SEL_L);
+               pr_info("It has risk of distortion!\n");
+       } else if (ucontrol->value.enumerated.item[0] == 2) {
+               val |= (0x1 << DAC_GAIN_SEL_H);
+               val &= ~(0x1 << DAC_GAIN_SEL_L);
+               pr_info("It has risk of distortion!\n");
+       } else if (ucontrol->value.enumerated.item[0] == 3) {
+               val |= (0x1 << DAC_GAIN_SEL_H);
+               val |= (0x1 << DAC_GAIN_SEL_L);
+               pr_info("It has risk of distortion!\n");
+       }
+
+       snd_soc_write(codec, val, add);
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -95250, 375, 1);
+
+static const char *const DAC_Gain_texts[] = { "0dB", "6dB", "12dB", "18dB" };
+
+static const struct soc_enum DAC_Gain_enum = SOC_ENUM_SINGLE(
+                       SND_SOC_NOPM, 0, ARRAY_SIZE(DAC_Gain_texts),
+                       DAC_Gain_texts);
+
+static const struct snd_kcontrol_new T9015_audio_snd_controls[] = {
+
+       /*DAC Digital Volume control */
+       SOC_DOUBLE_TLV("DAC Digital Playback Volume",
+                          DAC_VOL_CTR_DAC_SOFT_MUTE,
+                          DACL_VC, DACR_VC,
+                          0xff, 0, dac_vol_tlv),
+
+    /*DAC extra Digital Gain control */
+       SOC_ENUM_EXT("DAC Extra Digital Gain",
+                          DAC_Gain_enum,
+                          aml_DAC_Gain_get_enum,
+                          aml_DAC_Gain_set_enum),
+
+};
+
+/*line out Left Positive mux */
+static const char * const T9015_out_lp_txt[] = {
+       "None", "LOLP_SEL_DACL", "LOLP_SEL_DACL_INV"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015_out_lp_enum, LINE_OUT_CONFIG,
+                                 LOLP_SEL_DACL, T9015_out_lp_txt);
+
+static const struct snd_kcontrol_new line_out_lp_mux =
+SOC_DAPM_ENUM("ROUTE_LP_OUT", T9015_out_lp_enum);
+
+/*line out Left Negative mux */
+static const char * const T9015_out_ln_txt[] = {
+       "None", "LOLN_SEL_DACL_INV", "LOLN_SEL_DACL"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015_out_ln_enum, LINE_OUT_CONFIG,
+                                 LOLN_SEL_DACL_INV, T9015_out_ln_txt);
+
+static const struct snd_kcontrol_new line_out_ln_mux =
+SOC_DAPM_ENUM("ROUTE_LN_OUT", T9015_out_ln_enum);
+
+/*line out Right Positive mux */
+static const char * const T9015_out_rp_txt[] = {
+       "None", "LORP_SEL_DACR", "LORP_SEL_DACR_INV"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015_out_rp_enum, LINE_OUT_CONFIG,
+                                 LORP_SEL_DACR, T9015_out_rp_txt);
+
+static const struct snd_kcontrol_new line_out_rp_mux =
+SOC_DAPM_ENUM("ROUTE_RP_OUT", T9015_out_rp_enum);
+
+/*line out Right Negative mux */
+static const char * const T9015_out_rn_txt[] = {
+       "None", "LORN_SEL_DACR_INV", "LORN_SEL_DACR"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015_out_rn_enum, LINE_OUT_CONFIG,
+                                 LORN_SEL_DACR_INV, T9015_out_rn_txt);
+
+static const struct snd_kcontrol_new line_out_rn_mux =
+SOC_DAPM_ENUM("ROUTE_RN_OUT", T9015_out_rn_enum);
+
+static const struct snd_soc_dapm_widget T9015_audio_dapm_widgets[] = {
+
+       /*Output */
+       SND_SOC_DAPM_OUTPUT("Lineout left N"),
+       SND_SOC_DAPM_OUTPUT("Lineout left P"),
+       SND_SOC_DAPM_OUTPUT("Lineout right N"),
+       SND_SOC_DAPM_OUTPUT("Lineout right P"),
+
+       /*DAC playback stream */
+       SND_SOC_DAPM_DAC("Left DAC", "HIFI Playback",
+                        AUDIO_CONFIG_BLOCK_ENABLE,
+                        DACL_EN, 0),
+       SND_SOC_DAPM_DAC("Right DAC", "HIFI Playback",
+                        AUDIO_CONFIG_BLOCK_ENABLE,
+                        DACR_EN, 0),
+
+       /*DRV output */
+       SND_SOC_DAPM_OUT_DRV("LOLP_OUT_EN", SND_SOC_NOPM,
+                            0, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LOLN_OUT_EN", SND_SOC_NOPM,
+                            0, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LORP_OUT_EN", SND_SOC_NOPM,
+                            0, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LORN_OUT_EN", SND_SOC_NOPM,
+                            0, 0, NULL, 0),
+
+       /*MUX output source select */
+       SND_SOC_DAPM_MUX("Lineout left P switch", SND_SOC_NOPM,
+                        0, 0, &line_out_lp_mux),
+       SND_SOC_DAPM_MUX("Lineout left N switch", SND_SOC_NOPM,
+                        0, 0, &line_out_ln_mux),
+       SND_SOC_DAPM_MUX("Lineout right P switch", SND_SOC_NOPM,
+                        0, 0, &line_out_rp_mux),
+       SND_SOC_DAPM_MUX("Lineout right N switch", SND_SOC_NOPM,
+                        0, 0, &line_out_rn_mux),
+};
+
+static const struct snd_soc_dapm_route T9015_audio_dapm_routes[] = {
+    /*Output path*/
+       {"Lineout left P switch", "LOLP_SEL_DACL", "Left DAC"},
+       {"Lineout left P switch", "LOLP_SEL_DACL_INV", "Left DAC"},
+
+       {"Lineout left N switch", "LOLN_SEL_DACL_INV", "Left DAC"},
+       {"Lineout left N switch", "LOLN_SEL_DACL", "Left DAC"},
+
+       {"Lineout right P switch", "LORP_SEL_DACR", "Right DAC"},
+       {"Lineout right P switch", "LORP_SEL_DACR_INV", "Right DAC"},
+
+       {"Lineout right N switch", "LORN_SEL_DACR_INV", "Right DAC"},
+       {"Lineout right N switch", "LORN_SEL_DACR", "Right DAC"},
+
+       {"LOLN_OUT_EN", NULL, "Lineout left N switch"},
+       {"LOLP_OUT_EN", NULL, "Lineout left P switch"},
+       {"LORN_OUT_EN", NULL, "Lineout right N switch"},
+       {"LORP_OUT_EN", NULL, "Lineout right P switch"},
+
+       {"Lineout left N", NULL, "LOLN_OUT_EN"},
+       {"Lineout left P", NULL, "LOLP_OUT_EN"},
+       {"Lineout right N", NULL, "LORN_OUT_EN"},
+       {"Lineout right P", NULL, "LORP_OUT_EN"},
+};
+
+static int aml_T9015_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               snd_soc_update_bits(codec, AUDIO_CONFIG_BLOCK_ENABLE,
+                                       I2S_MODE, 1);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               snd_soc_update_bits(codec, AUDIO_CONFIG_BLOCK_ENABLE,
+                                       I2S_MODE, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int aml_T9015_set_dai_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       return 0;
+}
+
+static int aml_T9015_hw_params(struct snd_pcm_substream *substream,
+                             struct snd_pcm_hw_params *params,
+                             struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct aml_T9015_audio_priv *T9015_audio =
+           snd_soc_codec_get_drvdata(codec);
+
+       T9015_audio->params = params;
+
+       return 0;
+}
+
+static int aml_T9015_audio_set_bias_level(struct snd_soc_codec *codec,
+                                        enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->component.dapm.bias_level == SND_SOC_BIAS_OFF) {
+#if 0 /*tmp_mask_for_kernel_4_4*/
+                       codec->cache_only = false;
+                       codec->cache_sync = 1;
+#endif
+                       snd_soc_cache_sync(codec);
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+
+               break;
+
+       default:
+               break;
+       }
+       codec->component.dapm.bias_level = level;
+
+       return 0;
+}
+
+static int aml_T9015_prepare(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
+{
+       /*struct snd_soc_codec *codec = dai->codec;*/
+       return 0;
+
+}
+
+static int aml_T9015_audio_reset(struct snd_soc_codec *codec)
+{
+       aml_cbus_update_bits(RESET1_REGISTER, (1 << ACODEC_RESET),
+                                       (1 << ACODEC_RESET));
+       udelay(1000);
+       return 0;
+}
+
+static int aml_T9015_audio_start_up(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, AUDIO_CONFIG_BLOCK_ENABLE, 0xF000);
+       msleep(200);
+       snd_soc_write(codec, AUDIO_CONFIG_BLOCK_ENABLE, 0xB000);
+       return 0;
+}
+
+static int aml_T9015_codec_mute_stream(struct snd_soc_dai *dai, int mute,
+                                     int stream)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u32 reg;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               reg = snd_soc_read(codec, DAC_VOL_CTR_DAC_SOFT_MUTE);
+               if (mute)
+                       reg |= 0x1 << DAC_SOFT_MUTE;
+               else
+                       reg &= ~(0x1 << DAC_SOFT_MUTE);
+
+               snd_soc_write(codec, DAC_VOL_CTR_DAC_SOFT_MUTE, reg);
+       }
+       return 0;
+}
+
+static int aml_T9015_audio_probe(struct snd_soc_codec *codec)
+{
+       struct aml_T9015_audio_priv *T9015_audio = NULL;
+
+       T9015_audio = kzalloc(sizeof(struct aml_T9015_audio_priv), GFP_KERNEL);
+       if (T9015_audio == NULL)
+               return -ENOMEM;
+       snd_soc_codec_set_drvdata(codec, T9015_audio);
+#if 0 /*tmp_mask_for_kernel_4_4*/
+       snd_soc_codec_set_cache_io(codec, 32, 32, SND_SOC_REGMAP);
+#endif
+       /*reset audio codec register*/
+       aml_T9015_audio_reset(codec);
+       aml_T9015_audio_start_up(codec);
+       aml_T9015_audio_reg_init(codec);
+
+       aml_write_cbus(AIU_ACODEC_CTRL, (1 << 4)
+                          |(1 << 6)
+                          |(1 << 11)
+                          |(1 << 15)
+                          |(2 << 2)
+       );
+
+       codec->component.dapm.bias_level = SND_SOC_BIAS_STANDBY;
+       T9015_audio->codec = codec;
+
+       return 0;
+}
+
+static int aml_T9015_audio_remove(struct snd_soc_codec *codec)
+{
+       aml_T9015_audio_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int aml_T9015_audio_suspend(struct snd_soc_codec *codec)
+{
+       pr_info("aml_T9015_audio_suspend!\n");
+       aml_T9015_audio_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       snd_soc_write(codec, AUDIO_CONFIG_BLOCK_ENABLE, 0);
+       return 0;
+}
+
+static int aml_T9015_audio_resume(struct snd_soc_codec *codec)
+{
+       pr_info("aml_T9015_audio_resume!\n");
+       aml_T9015_audio_reset(codec);
+       aml_T9015_audio_start_up(codec);
+       aml_T9015_audio_reg_init(codec);
+       codec->component.dapm.bias_level = SND_SOC_BIAS_STANDBY;
+       aml_T9015_audio_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       return 0;
+}
+
+#define T9015_AUDIO_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define T9015_AUDIO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+                       | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE \
+                       | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai_ops T9015_audio_aif_dai_ops = {
+       .hw_params = aml_T9015_hw_params,
+       .prepare = aml_T9015_prepare,
+       .set_fmt = aml_T9015_set_dai_fmt,
+       .set_sysclk = aml_T9015_set_dai_sysclk,
+       .mute_stream = aml_T9015_codec_mute_stream,
+};
+
+struct snd_soc_dai_driver aml_T9015_audio_dai[] = {
+       {
+        .name = "T9015-audio-hifi",
+        .id = 0,
+        .playback = {
+                     .stream_name = "HIFI Playback",
+                     .channels_min = 2,
+                     .channels_max = 8,
+                     .rates = T9015_AUDIO_STEREO_RATES,
+                     .formats = T9015_AUDIO_FORMATS,
+                     },
+        .capture = {
+                        .stream_name = "HIFI Capture",
+                        .channels_min = 2,
+                        .channels_max = 8,
+                        .rates = T9015_AUDIO_STEREO_RATES,
+                        .formats = T9015_AUDIO_FORMATS,
+                        },
+        .ops = &T9015_audio_aif_dai_ops,
+        },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_aml_T9015_audio = {
+       .probe = aml_T9015_audio_probe,
+       .remove = aml_T9015_audio_remove,
+       .suspend = aml_T9015_audio_suspend,
+       .resume = aml_T9015_audio_resume,
+       .set_bias_level = aml_T9015_audio_set_bias_level,
+       .component_driver = {
+               .controls = T9015_audio_snd_controls,
+               .num_controls = ARRAY_SIZE(T9015_audio_snd_controls),
+               .dapm_widgets = T9015_audio_dapm_widgets,
+               .num_dapm_widgets = ARRAY_SIZE(T9015_audio_dapm_widgets),
+               .dapm_routes = T9015_audio_dapm_routes,
+               .num_dapm_routes = ARRAY_SIZE(T9015_audio_dapm_routes),
+       }
+};
+
+static const struct regmap_config t9015_codec_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = 0x14,
+       .reg_defaults = t9015_init_list,
+       .num_reg_defaults = ARRAY_SIZE(t9015_init_list),
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static int aml_T9015_audio_codec_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct resource *res_mem;
+       struct device_node *np;
+       void __iomem *regs;
+       struct regmap *regmap;
+
+       dev_info(&pdev->dev, "aml_T9015_audio_codec_probe\n");
+
+       np = pdev->dev.of_node;
+
+       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res_mem)
+               return -ENODEV;
+
+       regs = devm_ioremap_resource(&pdev->dev, res_mem);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+                                           &t9015_codec_regmap_config);
+
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       ret = snd_soc_register_codec(&pdev->dev,
+                                    &soc_codec_dev_aml_T9015_audio,
+                                    &aml_T9015_audio_dai[0], 1);
+       return ret;
+}
+
+static int aml_T9015_audio_codec_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id aml_T9015_codec_dt_match[] = {
+       {.compatible = "amlogic, aml_codec_T9015",},
+       {},
+};
+
+static struct platform_driver aml_T9015_codec_platform_driver = {
+       .driver = {
+                  .name = "aml_codec_T9015",
+                  .owner = THIS_MODULE,
+                  .of_match_table = aml_T9015_codec_dt_match,
+                  },
+       .probe = aml_T9015_audio_codec_probe,
+       .remove = aml_T9015_audio_codec_remove,
+};
+
+static int __init aml_T9015_audio_modinit(void)
+{
+       int ret = 0;
+
+       ret = platform_driver_register(&aml_T9015_codec_platform_driver);
+       if (ret != 0) {
+               pr_err(
+                       "Failed to register AML T9015 codec platform driver: %d\n",
+                       ret);
+       }
+
+       return ret;
+}
+
+module_init(aml_T9015_audio_modinit);
+
+static void __exit aml_T9015_audio_exit(void)
+{
+       platform_driver_unregister(&aml_T9015_codec_platform_driver);
+}
+
+module_exit(aml_T9015_audio_exit);
+
+MODULE_DESCRIPTION("ASoC AML T9015 audio codec driver");
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/amlogic/aml_codec_t9015.h b/sound/soc/codecs/amlogic/aml_codec_t9015.h
new file mode 100644 (file)
index 0000000..ae908e6
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * sound/soc/codecs/amlogic/aml_codec_t9015.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#ifndef AML_T9015_H_
+#define AML_T9015_H_
+
+#define ACODEC_BASE_ADD    0xc8832000
+#define ACODEC_TOP_ADDR(x) (x)
+
+#define AUDIO_CONFIG_BLOCK_ENABLE       ACODEC_TOP_ADDR(0x00)
+#define MCLK_FREQ                   0x1F
+#define I2S_MODE                    0x1E
+#define DAC_CLK_TO_GPIO_EN          0x18
+#define DACL_DATA_SOURCE            0x17
+#define DACR_DATA_SOURCE            0x16
+#define DACL_INV                    0x15
+#define DACR_INV                    0x14
+#define VMID_GEN_EN                 0x0F
+#define VMID_GEN_FAST               0x0E
+#define BIAS_CURRENT_EN             0x0D
+#define REFP_BUF_EN                 0x0C
+#define DACL_EN                     0x05
+#define DACR_EN                     0x04
+#define LOLP_EN                     0x03
+#define LOLN_EN                     0x02
+#define LORP_EN                     0x01
+#define LORN_EN                     0x00
+
+#define ADC_VOL_CTR_PGA_IN_CONFIG       ACODEC_TOP_ADDR(0x04)
+#define DAC_GAIN_SEL_H              0x1F
+#define DAC_GAIN_SEL_L              0x17
+
+
+#define DAC_VOL_CTR_DAC_SOFT_MUTE       ACODEC_TOP_ADDR(0x08)
+#define DACL_VC                     0x18
+#define DACR_VC                     0x10
+#define DAC_SOFT_MUTE               0x0F
+#define DAC_UNMUTE_MODE             0x0E
+#define DAC_MUTE_MODE               0x0D
+#define DAC_VC_RAMP_MODE            0x0C
+#define DAC_RAMP_RATE               0x0A
+#define DAC_MONO                    0x08
+
+#define LINE_OUT_CONFIG                 ACODEC_TOP_ADDR(0x0c)
+#define LOLP_SEL_DACL_INV           0x0D
+#define LOLP_SEL_DACL               0x0C
+#define LOLN_SEL_DACL               0x09
+#define LOLN_SEL_DACL_INV           0x08
+#define LORP_SEL_DACR_INV           0x05
+#define LORP_SEL_DACR               0x04
+#define LORN_SEL_DACR               0x01
+#define LORN_SEL_DACR_INV           0x00
+
+#define POWER_CONFIG                    ACODEC_TOP_ADDR(0x10)
+#define MUTE_DAC_PD_EN              0x1F
+#define IB_CON                      0x10
+
+#endif
diff --git a/sound/soc/codecs/amlogic/aml_codec_t9015S.c b/sound/soc/codecs/amlogic/aml_codec_t9015S.c
new file mode 100644 (file)
index 0000000..cc28360
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ * sound/soc/codecs/amlogic/aml_codec_t9015S.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/regmap.h>
+
+#include <linux/amlogic/iomap.h>
+#include <linux/amlogic/media/sound/aiu_regs.h>
+#include <linux/amlogic/media/sound/audin_regs.h>
+
+#include "aml_codec_t9015S.h"
+
+struct aml_T9015S_audio_priv {
+       struct snd_soc_codec *codec;
+       struct snd_pcm_hw_params *params;
+};
+
+static const struct reg_default t9015s_init_list[] = {
+       {AUDIO_CONFIG_BLOCK_ENABLE, 0x3400BCFF},
+       {ADC_VOL_CTR_PGA_IN_CONFIG, 0x50502929},
+       {DAC_VOL_CTR_DAC_SOFT_MUTE, 0xFBFB0000},
+       {LINE_OUT_CONFIG, 0x00004444},
+       {POWER_CONFIG, 0x00010000},
+};
+
+static int aml_T9015S_audio_reg_init(struct snd_soc_codec *codec)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(t9015s_init_list); i++)
+               snd_soc_write(codec, t9015s_init_list[i].reg,
+                               t9015s_init_list[i].def);
+
+       return 0;
+}
+
+static int aml_DAC_Gain_get_enum(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u32 add = ADC_VOL_CTR_PGA_IN_CONFIG;
+       u32 val = snd_soc_read(codec, add);
+       u32 val1 = (val & (0x1 <<  DAC_GAIN_SEL_L)) >> DAC_GAIN_SEL_L;
+       u32 val2 = (val & (0x1 <<  DAC_GAIN_SEL_H)) >> (DAC_GAIN_SEL_H - 1);
+
+       val = val1 | val2;
+       ucontrol->value.enumerated.item[0] = val;
+       return 0;
+}
+
+static int aml_DAC_Gain_set_enum(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u32 add = ADC_VOL_CTR_PGA_IN_CONFIG;
+       u32 val = snd_soc_read(codec, add);
+
+       if (ucontrol->value.enumerated.item[0] == 0) {
+               val &= ~(0x1 << DAC_GAIN_SEL_H);
+               val &= ~(0x1 << DAC_GAIN_SEL_L);
+       } else if (ucontrol->value.enumerated.item[0] == 1) {
+               val &= ~(0x1 << DAC_GAIN_SEL_H);
+               val |= (0x1 << DAC_GAIN_SEL_L);
+               pr_info("It has risk of distortion!\n");
+       } else if (ucontrol->value.enumerated.item[0] == 2) {
+               val |= (0x1 << DAC_GAIN_SEL_H);
+               val &= ~(0x1 << DAC_GAIN_SEL_L);
+               pr_info("It has risk of distortion!\n");
+       } else if (ucontrol->value.enumerated.item[0] == 3) {
+               val |= (0x1 << DAC_GAIN_SEL_H);
+               val |= (0x1 << DAC_GAIN_SEL_L);
+               pr_info("It has risk of distortion!\n");
+       }
+
+       snd_soc_write(codec, val, add);
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(pga_in_tlv, -1200, 250, 1);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -29625, 375, 1);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -95250, 375, 1);
+
+static const char *const DAC_Gain_texts[] = { "0dB", "6dB", "12dB", "18dB" };
+
+static const struct soc_enum DAC_Gain_enum = SOC_ENUM_SINGLE(
+                       SND_SOC_NOPM, 0, ARRAY_SIZE(DAC_Gain_texts),
+                       DAC_Gain_texts);
+
+static const struct snd_kcontrol_new T9015S_audio_snd_controls[] = {
+       /*PGA_IN Gain */
+       SOC_DOUBLE_TLV("PGA IN Gain", ADC_VOL_CTR_PGA_IN_CONFIG,
+                      PGAL_IN_GAIN, PGAR_IN_GAIN,
+                      0x1f, 0, pga_in_tlv),
+
+       /*ADC Digital Volume control */
+       SOC_DOUBLE_TLV("ADC Digital Capture Volume", ADC_VOL_CTR_PGA_IN_CONFIG,
+                      ADCL_VC, ADCR_VC,
+                      0x7f, 0, adc_vol_tlv),
+
+       /*DAC Digital Volume control */
+       SOC_DOUBLE_TLV("DAC Digital Playback Volume",
+                          DAC_VOL_CTR_DAC_SOFT_MUTE,
+                          DACL_VC, DACR_VC,
+                          0xff, 0, dac_vol_tlv),
+
+    /*DAC extra Digital Gain control */
+       SOC_ENUM_EXT("DAC Extra Digital Gain",
+                          DAC_Gain_enum,
+                          aml_DAC_Gain_get_enum,
+                          aml_DAC_Gain_set_enum),
+
+};
+
+/*pgain Left Channel Input */
+static const char * const T9015S_pgain_left_txt[] = {
+       "None", "AIL1", "AIL2", "AIL3", "AIL4"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015S_pgain_left_enum,
+                                 ADC_VOL_CTR_PGA_IN_CONFIG,
+                                 PGAL_IN_SEL, T9015S_pgain_left_txt);
+
+static const struct snd_kcontrol_new pgain_ln_mux =
+SOC_DAPM_ENUM("ROUTE_L", T9015S_pgain_left_enum);
+
+/*pgain right Channel Input */
+static const char * const T9015S_pgain_right_txt[] = {
+       "None", "AIR1", "AIR2", "AIR3", "AIR4"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015S_pgain_right_enum,
+                                 ADC_VOL_CTR_PGA_IN_CONFIG,
+                                 PGAR_IN_SEL, T9015S_pgain_right_txt);
+
+static const struct snd_kcontrol_new pgain_rn_mux =
+SOC_DAPM_ENUM("ROUTE_R", T9015S_pgain_right_enum);
+
+/*line out Left Positive mux */
+static const char * const T9015S_out_lp_txt[] = {
+       "None", "LOLP_SEL_AIL_INV", "LOLP_SEL_AIL", "Reserved", "LOLP_SEL_DACL"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015S_out_lp_enum, LINE_OUT_CONFIG,
+                                 LOLP_SEL_AIL_INV, T9015S_out_lp_txt);
+
+static const struct snd_kcontrol_new line_out_lp_mux =
+SOC_DAPM_ENUM("ROUTE_LP_OUT", T9015S_out_lp_enum);
+
+/*line out Left Negative mux */
+static const char * const T9015S_out_ln_txt[] = {
+       "None", "LOLN_SEL_AIL", "LOLN_SEL_DACL", "Reserved", "LOLN_SEL_DACL_INV"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015S_out_ln_enum, LINE_OUT_CONFIG,
+                                 LOLN_SEL_AIL, T9015S_out_ln_txt);
+
+static const struct snd_kcontrol_new line_out_ln_mux =
+SOC_DAPM_ENUM("ROUTE_LN_OUT", T9015S_out_ln_enum);
+
+/*line out Right Positive mux */
+static const char * const T9015S_out_rp_txt[] = {
+       "None", "LORP_SEL_AIR_INV", "LORP_SEL_AIR", "Reserved", "LORP_SEL_DACR"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015S_out_rp_enum, LINE_OUT_CONFIG,
+                                 LORP_SEL_AIR_INV, T9015S_out_rp_txt);
+
+static const struct snd_kcontrol_new line_out_rp_mux =
+SOC_DAPM_ENUM("ROUTE_RP_OUT", T9015S_out_rp_enum);
+
+/*line out Right Negative mux */
+static const char * const T9015S_out_rn_txt[] = {
+       "None", "LORN_SEL_AIR", "LORN_SEL_DACR", "Reserved", "LORN_SEL_DACR_INV"
+};
+
+static const SOC_ENUM_SINGLE_DECL(T9015S_out_rn_enum, LINE_OUT_CONFIG,
+                                 LORN_SEL_AIR, T9015S_out_rn_txt);
+
+static const struct snd_kcontrol_new line_out_rn_mux =
+SOC_DAPM_ENUM("ROUTE_RN_OUT", T9015S_out_rn_enum);
+
+static const struct snd_soc_dapm_widget T9015S_audio_dapm_widgets[] = {
+
+       /* Input */
+       SND_SOC_DAPM_INPUT("Linein left 1"),
+       SND_SOC_DAPM_INPUT("Linein left 2"),
+       SND_SOC_DAPM_INPUT("Linein left 3"),
+       SND_SOC_DAPM_INPUT("Linein left 4"),
+
+       SND_SOC_DAPM_INPUT("Linein right 1"),
+       SND_SOC_DAPM_INPUT("Linein right 2"),
+       SND_SOC_DAPM_INPUT("Linein right 3"),
+       SND_SOC_DAPM_INPUT("Linein right 4"),
+
+       /*PGA input */
+       SND_SOC_DAPM_PGA("PGAL_IN_EN", AUDIO_CONFIG_BLOCK_ENABLE,
+                        PGAL_IN_EN, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("PGAR_IN_EN", AUDIO_CONFIG_BLOCK_ENABLE,
+                        PGAL_IN_EN, 0, NULL, 0),
+
+       /*PGA input source select */
+       SND_SOC_DAPM_MUX("Linein left switch", SND_SOC_NOPM,
+                        0, 0, &pgain_ln_mux),
+       SND_SOC_DAPM_MUX("Linein right switch", SND_SOC_NOPM,
+                        0, 0, &pgain_rn_mux),
+
+       /*ADC capture stream */
+       SND_SOC_DAPM_ADC("Left ADC", "HIFI Capture", AUDIO_CONFIG_BLOCK_ENABLE,
+                        ADCL_EN, 0),
+       SND_SOC_DAPM_ADC("Right ADC", "HIFI Capture", AUDIO_CONFIG_BLOCK_ENABLE,
+                        ADCR_EN, 0),
+
+       /*Output */
+       SND_SOC_DAPM_OUTPUT("Lineout left N"),
+       SND_SOC_DAPM_OUTPUT("Lineout left P"),
+       SND_SOC_DAPM_OUTPUT("Lineout right N"),
+       SND_SOC_DAPM_OUTPUT("Lineout right P"),
+
+       /*DAC playback stream */
+       SND_SOC_DAPM_DAC("Left DAC", "HIFI Playback",
+                        AUDIO_CONFIG_BLOCK_ENABLE,
+                        DACL_EN, 0),
+       SND_SOC_DAPM_DAC("Right DAC", "HIFI Playback",
+                        AUDIO_CONFIG_BLOCK_ENABLE,
+                        DACR_EN, 0),
+
+       /*DRV output */
+       SND_SOC_DAPM_OUT_DRV("LOLP_OUT_EN", SND_SOC_NOPM,
+                            0, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LOLN_OUT_EN", SND_SOC_NOPM,
+                            0, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LORP_OUT_EN", SND_SOC_NOPM,
+                            0, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LORN_OUT_EN", SND_SOC_NOPM,
+                            0, 0, NULL, 0),
+
+       /*MUX output source select */
+       SND_SOC_DAPM_MUX("Lineout left P switch", SND_SOC_NOPM,
+                        0, 0, &line_out_lp_mux),
+       SND_SOC_DAPM_MUX("Lineout left N switch", SND_SOC_NOPM,
+                        0, 0, &line_out_ln_mux),
+       SND_SOC_DAPM_MUX("Lineout right P switch", SND_SOC_NOPM,
+                        0, 0, &line_out_rp_mux),
+       SND_SOC_DAPM_MUX("Lineout right N switch", SND_SOC_NOPM,
+                        0, 0, &line_out_rn_mux),
+};
+
+static const struct snd_soc_dapm_route T9015S_audio_dapm_routes[] = {
+/* Input path */
+       {"Linein left switch", "AIL1", "Linein left 1"},
+       {"Linein left switch", "AIL2", "Linein left 2"},
+       {"Linein left switch", "AIL3", "Linein left 3"},
+       {"Linein left switch", "AIL4", "Linein left 4"},
+
+       {"Linein right switch", "AIR1", "Linein right 1"},
+       {"Linein right switch", "AIR2", "Linein right 2"},
+       {"Linein right switch", "AIR3", "Linein right 3"},
+       {"Linein right switch", "AIR4", "Linein right 4"},
+
+       {"PGAL_IN_EN", NULL, "Linein left switch"},
+       {"PGAR_IN_EN", NULL, "Linein right switch"},
+
+       {"Left ADC", NULL, "PGAL_IN_EN"},
+       {"Right ADC", NULL, "PGAR_IN_EN"},
+
+/*Output path*/
+       {"Lineout left P switch", "LOLP_SEL_DACL", "Left DAC"},
+       {"Lineout left P switch", "LOLP_SEL_AIL", "PGAL_IN_EN"},
+       {"Lineout left P switch", "LOLP_SEL_AIL_INV", "PGAL_IN_EN"},
+
+       {"Lineout left N switch", "LOLN_SEL_AIL", "PGAL_IN_EN"},
+       {"Lineout left N switch", "LOLN_SEL_DACL", "Left DAC"},
+       {"Lineout left N switch", "LOLN_SEL_DACL_INV", "Left DAC"},
+
+       {"Lineout right P switch", "LORP_SEL_DACR", "Right DAC"},
+       {"Lineout right P switch", "LORP_SEL_AIR", "PGAR_IN_EN"},
+       {"Lineout right P switch", "LORP_SEL_AIR_INV", "PGAR_IN_EN"},
+
+       {"Lineout right N switch", "LORN_SEL_AIR", "PGAR_IN_EN"},
+       {"Lineout right N switch", "LORN_SEL_DACR", "Right DAC"},
+       {"Lineout right N switch", "LORN_SEL_DACR_INV", "Right DAC"},
+
+       {"LOLN_OUT_EN", NULL, "Lineout left N switch"},
+       {"LOLP_OUT_EN", NULL, "Lineout left P switch"},
+       {"LORN_OUT_EN", NULL, "Lineout right N switch"},
+       {"LORP_OUT_EN", NULL, "Lineout right P switch"},
+
+       {"Lineout left N", NULL, "LOLN_OUT_EN"},
+       {"Lineout left P", NULL, "LOLP_OUT_EN"},
+       {"Lineout right N", NULL, "LORN_OUT_EN"},
+       {"Lineout right P", NULL, "LORP_OUT_EN"},
+};
+
+static int aml_T9015S_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               snd_soc_update_bits(codec, AUDIO_CONFIG_BLOCK_ENABLE,
+                                       I2S_MODE, 1);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               snd_soc_update_bits(codec, AUDIO_CONFIG_BLOCK_ENABLE,
+                                       I2S_MODE, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int aml_T9015S_set_dai_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       return 0;
+}
+
+static int aml_T9015S_hw_params(struct snd_pcm_substream *substream,
+                             struct snd_pcm_hw_params *params,
+                             struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct aml_T9015S_audio_priv *T9015S_audio =
+           snd_soc_codec_get_drvdata(codec);
+
+       T9015S_audio->params = params;
+
+       return 0;
+}
+
+static int aml_T9015S_audio_set_bias_level(struct snd_soc_codec *codec,
+                                        enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       codec->cache_only = false;
+                       codec->cache_sync = 1;
+                       snd_soc_cache_sync(codec);
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+
+               break;
+
+       default:
+               break;
+       }
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+static int aml_T9015S_prepare(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
+{
+       /*struct snd_soc_codec *codec = dai->codec;*/
+       return 0;
+
+}
+
+static int aml_T9015S_audio_reset(struct snd_soc_codec *codec)
+{
+       aml_cbus_update_bits(RESET1_REGISTER, (1 << ACODEC_RESET),
+                                       (1 << ACODEC_RESET));
+       udelay(1000);
+       return 0;
+}
+
+static int aml_T9015S_audio_start_up(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, AUDIO_CONFIG_BLOCK_ENABLE, 0xF000);
+       msleep(200);
+       snd_soc_write(codec, AUDIO_CONFIG_BLOCK_ENABLE, 0xB000);
+       return 0;
+}
+
+static int aml_T9015S_codec_mute_stream(struct snd_soc_dai *dai, int mute,
+                                     int stream)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u32 reg;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               reg = snd_soc_read(codec, DAC_VOL_CTR_DAC_SOFT_MUTE);
+               if (mute)
+                       reg |= 0x1 << DAC_SOFT_MUTE;
+               else
+                       reg &= ~(0x1 << DAC_SOFT_MUTE);
+
+               snd_soc_write(codec, DAC_VOL_CTR_DAC_SOFT_MUTE, reg);
+       }
+       return 0;
+}
+
+static int aml_T9015S_audio_probe(struct snd_soc_codec *codec)
+{
+       struct aml_T9015S_audio_priv *T9015S_audio = NULL;
+
+       T9015S_audio = kzalloc(sizeof(struct aml_T9015S_audio_priv),
+               GFP_KERNEL);
+       if (T9015S_audio == NULL)
+               return -ENOMEM;
+       snd_soc_codec_set_drvdata(codec, T9015S_audio);
+#if 0 /*tmp_mask_for_kernel_4_4*/
+       snd_soc_codec_set_cache_io(codec, 32, 32, SND_SOC_REGMAP);
+#endif
+       /*reset audio codec register*/
+       aml_T9015S_audio_reset(codec);
+       aml_T9015S_audio_start_up(codec);
+       aml_T9015S_audio_reg_init(codec);
+
+       aml_write_cbus(AIU_ACODEC_CTRL, (1 << 4)
+                          |(1 << 6)
+                          |(1 << 11)
+                          |(1 << 15)
+                          |(2 << 2)
+       );
+
+       aml_write_cbus(AUDIN_SOURCE_SEL, 3);
+       codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
+       T9015S_audio->codec = codec;
+
+       return 0;
+}
+
+static int aml_T9015S_audio_remove(struct snd_soc_codec *codec)
+{
+       aml_T9015S_audio_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int aml_T9015S_audio_suspend(struct snd_soc_codec *codec)
+{
+       pr_info("aml_T9015S_audio_suspend!\n");
+       aml_T9015S_audio_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       snd_soc_write(codec, AUDIO_CONFIG_BLOCK_ENABLE, 0);
+       return 0;
+}
+
+static int aml_T9015S_audio_resume(struct snd_soc_codec *codec)
+{
+       pr_info("aml_T9015S_audio_resume!\n");
+       aml_T9015S_audio_reset(codec);
+       aml_T9015S_audio_start_up(codec);
+       aml_T9015S_audio_reg_init(codec);
+       codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
+       aml_T9015S_audio_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       return 0;
+}
+
+#define T9015S_AUDIO_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define T9015S_AUDIO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+                       | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE \
+                       | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai_ops T9015S_audio_aif_dai_ops = {
+       .hw_params = aml_T9015S_hw_params,
+       .prepare = aml_T9015S_prepare,
+       .set_fmt = aml_T9015S_set_dai_fmt,
+       .set_sysclk = aml_T9015S_set_dai_sysclk,
+       .mute_stream = aml_T9015S_codec_mute_stream,
+};
+
+struct snd_soc_dai_driver aml_T9015S_audio_dai[] = {
+       {
+        .name = "T9015S-audio-hifi",
+        .id = 0,
+        .playback = {
+                     .stream_name = "HIFI Playback",
+                     .channels_min = 2,
+                     .channels_max = 8,
+                     .rates = T9015S_AUDIO_STEREO_RATES,
+                     .formats = T9015S_AUDIO_FORMATS,
+                     },
+        .capture = {
+                    .stream_name = "HIFI Capture",
+                    .channels_min = 1,
+                    .channels_max = 2,
+                    .rates = T9015S_AUDIO_STEREO_RATES,
+                    .formats = T9015S_AUDIO_FORMATS,
+                    },
+        .ops = &T9015S_audio_aif_dai_ops,
+        },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_aml_T9015S_audio = {
+       .probe = aml_T9015S_audio_probe,
+       .remove = aml_T9015S_audio_remove,
+       .suspend = aml_T9015S_audio_suspend,
+       .resume = aml_T9015S_audio_resume,
+       .set_bias_level = aml_T9015S_audio_set_bias_level,
+       .component_driver = {
+               .controls = T9015S_audio_snd_controls,
+               .num_controls = ARRAY_SIZE(T9015S_audio_snd_controls),
+               .dapm_widgets = T9015S_audio_dapm_widgets,
+               .num_dapm_widgets = ARRAY_SIZE(T9015S_audio_dapm_widgets),
+               .dapm_routes = T9015S_audio_dapm_routes,
+               .num_dapm_routes = ARRAY_SIZE(T9015S_audio_dapm_routes),
+       }
+};
+
+static const struct regmap_config t9015s_codec_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = 0x14,
+       .reg_defaults = t9015s_init_list,
+       .num_reg_defaults = ARRAY_SIZE(t9015s_init_list),
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static int aml_T9015S_audio_codec_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct resource *res_mem;
+       struct device_node *np;
+       void __iomem *regs;
+       struct regmap *regmap;
+
+       dev_info(&pdev->dev, "aml_T9015S_audio_codec_probe\n");
+
+       np = pdev->dev.of_node;
+
+       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res_mem)
+               return -ENODEV;
+
+       regs = devm_ioremap_resource(&pdev->dev, res_mem);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+                                           &t9015s_codec_regmap_config);
+
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       ret = snd_soc_register_codec(&pdev->dev,
+                                    &soc_codec_dev_aml_T9015S_audio,
+                                    &aml_T9015S_audio_dai[0], 1);
+       return ret;
+}
+
+static int aml_T9015S_audio_codec_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id aml_T9015S_codec_dt_match[] = {
+       {.compatible = "amlogic, aml_codec_T9015S",},
+       {},
+};
+
+static struct platform_driver aml_T9015S_codec_platform_driver = {
+       .driver = {
+                  .name = "aml_codec_T9015S",
+                  .owner = THIS_MODULE,
+                  .of_match_table = aml_T9015S_codec_dt_match,
+                  },
+       .probe = aml_T9015S_audio_codec_probe,
+       .remove = aml_T9015S_audio_codec_remove,
+};
+
+static int __init aml_T9015S_audio_modinit(void)
+{
+       int ret = 0;
+
+       ret = platform_driver_register(&aml_T9015S_codec_platform_driver);
+       if (ret != 0) {
+               pr_err(
+                       "Failed to register AML T9015S codec platform driver: %d\n",
+                       ret);
+       }
+
+       return ret;
+}
+
+module_init(aml_T9015S_audio_modinit);
+
+static void __exit aml_T9015S_audio_exit(void)
+{
+       platform_driver_unregister(&aml_T9015S_codec_platform_driver);
+}
+
+module_exit(aml_T9015S_audio_exit);
+
+MODULE_DESCRIPTION("ASoC AML T9015S audio codec driver");
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/amlogic/aml_codec_t9015S.h b/sound/soc/codecs/amlogic/aml_codec_t9015S.h
new file mode 100644 (file)
index 0000000..a144032
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * sound/soc/codecs/amlogic/aml_codec_t9015S.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef AML_T9015S_H_
+#define AML_T9015S_H_
+
+#define ACODEC_BASE_ADD    0xc8832000
+#define ACODEC_TOP_ADDR(x) (x)
+
+#define AUDIO_CONFIG_BLOCK_ENABLE       ACODEC_TOP_ADDR(0x00)
+#define MCLK_FREQ                   0x1F
+#define I2S_MODE                    0x1E
+#define ADC_HPF_EN                  0x1D
+#define ADC_HPF_MODE                0x1C
+#define ADC_OVERLOAD_DET_EN         0x1B
+#define ADC_DEM_EN                  0x1A
+#define ADC_CLK_TO_GPIO_EN          0x19
+#define DAC_CLK_TO_GPIO_EN          0x18
+#define DACL_DATA_SOURCE            0x17
+#define DACR_DATA_SOURCE            0x16
+#define DACL_INV                    0x15
+#define DACR_INV                    0x14
+#define ADCDATL_SOURCE              0x13
+#define ADCDATR_SOURCE              0x12
+#define ADCL_INV                    0x11
+#define ADCR_INV                    0x10
+#define VMID_GEN_EN                 0x0F
+#define VMID_GEN_FAST               0x0E
+#define BIAS_CURRENT_EN             0x0D
+#define REFP_BUF_EN                 0x0C
+#define PGAL_IN_EN                  0x0B
+#define PGAR_IN_EN                  0x0A
+#define PGAL_IN_ZC_EN               0x09
+#define PGAR_IN_ZC_EN               0x08
+#define ADCL_EN                     0x07
+#define ADCR_EN                     0x06
+#define DACL_EN                     0x05
+#define DACR_EN                     0x04
+#define LOLP_EN                     0x03
+#define LOLN_EN                     0x02
+#define LORP_EN                     0x01
+#define LORN_EN                     0x00
+
+#define ADC_VOL_CTR_PGA_IN_CONFIG       ACODEC_TOP_ADDR(0x04)
+#define DAC_GAIN_SEL_H              0x1F
+#define ADCL_VC                     0x18
+#define DAC_GAIN_SEL_L              0x17
+#define ADCR_VC                     0x10
+#define PGAL_IN_SEL                 0x0D
+#define PGAL_IN_GAIN                0x08
+#define PGAR_IN_SEL                 0x05
+#define PGAR_IN_GAIN                0x00
+
+#define DAC_VOL_CTR_DAC_SOFT_MUTE       ACODEC_TOP_ADDR(0x08)
+#define DACL_VC                     0x18
+#define DACR_VC                     0x10
+#define DAC_SOFT_MUTE               0x0F
+#define DAC_UNMUTE_MODE             0x0E
+#define DAC_MUTE_MODE               0x0D
+#define DAC_VC_RAMP_MODE            0x0C
+#define DAC_RAMP_RATE               0x0A
+#define DAC_MONO                    0x08
+
+#define LINE_OUT_CONFIG                 ACODEC_TOP_ADDR(0x0c)
+#define LOLP_SEL_DACL               0x0E
+#define LOLP_SEL_AIL                0x0D
+#define LOLP_SEL_AIL_INV            0x0C
+#define LOLN_SEL_DACL_INV           0x0A
+#define LOLN_SEL_DACL               0x09
+#define LOLN_SEL_AIL                0x08
+#define LORP_SEL_DACR               0x06
+#define LORP_SEL_AIR                0x05
+#define LORP_SEL_AIR_INV            0x04
+#define LORN_SEL_DACR_INV           0x02
+#define LORN_SEL_DACR               0x01
+#define LORN_SEL_AIR                0x00
+
+#define POWER_CONFIG                    ACODEC_TOP_ADDR(0x10)
+#define MUTE_DAC_PD_EN              0x1F
+#define IB_CON                      0x10
+
+#endif
diff --git a/sound/soc/codecs/amlogic/aml_pmu4_codec.c b/sound/soc/codecs/amlogic/aml_pmu4_codec.c
new file mode 100644 (file)
index 0000000..272988a
--- /dev/null
@@ -0,0 +1,587 @@
+/*
+ * sound/soc/codecs/amlogic/aml_pmu4_codec.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/amlogic/aml_pmu4.h>
+#include "aml_pmu4_codec.h"
+
+#ifdef CONFIG_USE_OF
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/of_gpio.h>
+#include <mach/pinmux.h>
+#include <plat/io.h>
+#endif
+
+struct aml_pmu4_audio_priv {
+       struct snd_soc_codec *codec;
+       struct snd_pcm_hw_params *params;
+};
+
+struct pmu4_audio_init_reg {
+       u8 reg;
+       u16 val;
+};
+
+#define AML1220_PMU_CTR_04 0x05
+
+static struct pmu4_audio_init_reg init_list[] = {
+       {PMU4_BLOCK_ENABLE, 0xBCF6},
+       {PMU4_AUDIO_CONFIG, 0x3400},
+       {PMU4_PGA_IN_CONFIG, 0x2929},
+       {PMU4_ADC_VOL_CTR, 0x5050},
+       {PMU4_DAC_SOFT_MUTE, 0x0000},
+       {PMU4_DAC_VOL_CTR, 0xFFFF},
+       {PMU4_LINE_OUT_CONFIG, 0x4242},
+
+};
+
+#define PMU4_AUDIO_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static int aml_pmu4_audio_reg_init(struct snd_soc_codec *codec)
+{
+       int i;
+
+       for (i = 0; i < PMU4_AUDIO_INIT_REG_LEN; i++)
+               snd_soc_write(codec, init_list[i].reg, init_list[i].val);
+
+       return 0;
+}
+
+static unsigned int aml_pmu4_audio_read(struct snd_soc_codec *codec,
+                                       unsigned int reg)
+{
+       u16 pmu4_audio_reg;
+       u16 val;
+
+       pmu4_audio_reg = PMU4_AUDIO_BASE + reg;
+       aml_pmu4_read16(pmu4_audio_reg, &val);
+
+       return val;
+
+}
+
+static int aml_pmu4_audio_write(struct snd_soc_codec *codec, unsigned int reg,
+                               unsigned int val)
+{
+       u16 pmu4_audio_reg;
+
+       pmu4_audio_reg = PMU4_AUDIO_BASE + reg;
+       aml_pmu4_write16(pmu4_audio_reg, val);
+
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(pga_in_tlv, -1200, 250, 1);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -29625, 375, 1);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -95250, 375, 1);
+
+static const struct snd_kcontrol_new pmu4_audio_snd_controls[] = {
+       /*PGA_IN Gain */
+       SOC_DOUBLE_TLV("PGA IN Gain", PMU4_PGA_IN_CONFIG,
+                      PMU4_PGAL_IN_GAIN, PMU4_PGAR_IN_GAIN,
+                      0x1f, 0, pga_in_tlv),
+
+       /*ADC Digital Volume control */
+       SOC_DOUBLE_TLV("ADC Digital Capture Volume", PMU4_ADC_VOL_CTR,
+                      PMU4_ADCL_VOL_CTR, PMU4_ADCR_VOL_CTR,
+                      0x7f, 0, adc_vol_tlv),
+
+       /*DAC Digital Volume control */
+       SOC_DOUBLE_TLV("DAC Digital Playback Volume", PMU4_DAC_VOL_CTR,
+                      PMU4_DACL_VOL_CTR, PMU4_DACR_VOL_CTR,
+                      0xff, 0, dac_vol_tlv),
+
+};
+
+/*pgain Left Channel Input */
+static const char * const pmu4_pgain_left_txt[] = {
+       "None", "AIL1", "AIL2", "AIL3", "AIL4"
+};
+
+static const SOC_ENUM_SINGLE_DECL(pmu4_pgain_left_enum, PMU4_PGA_IN_CONFIG,
+                                 PMU4_PGAL_IN_SEL, pmu4_pgain_left_txt);
+
+static const struct snd_kcontrol_new pgain_ln_mux =
+SOC_DAPM_ENUM("ROUTE_L", pmu4_pgain_left_enum);
+
+/*pgain right Channel Input */
+static const char * const pmu4_pgain_right_txt[] = {
+       "None", "AIR1", "AIR2", "AIR3", "AIR4"
+};
+
+static const SOC_ENUM_SINGLE_DECL(pmu4_pgain_right_enum, PMU4_PGA_IN_CONFIG,
+                                 PMU4_PGAR_IN_SEL, pmu4_pgain_right_txt);
+
+static const struct snd_kcontrol_new pgain_rn_mux =
+SOC_DAPM_ENUM("ROUTE_R", pmu4_pgain_right_enum);
+
+/*line out Left Positive mux */
+static const char * const pmu4_out_lp_txt[] = {
+       "None", "LOLP_SEL_AIL_INV", "LOLP_SEL_AIL", "Reserved", "LOLP_SEL_DACL"
+};
+
+static const SOC_ENUM_SINGLE_DECL(pmu4_out_lp_enum, PMU4_LINE_OUT_CONFIG,
+                                 PMU4_LOLP_SEL_SHIFT, pmu4_out_lp_txt);
+
+static const struct snd_kcontrol_new line_out_lp_mux =
+SOC_DAPM_ENUM("ROUTE_LP_OUT", pmu4_out_lp_enum);
+
+/*line out Left Negative mux */
+static const char * const pmu4_out_ln_txt[] = {
+       "None", "LOLN_SEL_AIL", "LOLN_SEL_DACL", "Reserved", "LOLN_SEL_DACL_INV"
+};
+
+static const SOC_ENUM_SINGLE_DECL(pmu4_out_ln_enum, PMU4_LINE_OUT_CONFIG,
+                                 PMU4_LOLN_SEL_SHIFT, pmu4_out_ln_txt);
+
+static const struct snd_kcontrol_new line_out_ln_mux =
+SOC_DAPM_ENUM("ROUTE_LN_OUT", pmu4_out_ln_enum);
+
+/*line out Right Positive mux */
+static const char * const pmu4_out_rp_txt[] = {
+       "None", "LORP_SEL_AIR_INV", "LORP_SEL_AIR", "Reserved", "LORP_SEL_DACR"
+};
+
+static const SOC_ENUM_SINGLE_DECL(pmu4_out_rp_enum, PMU4_LINE_OUT_CONFIG,
+                                 PMU4_LORP_SEL_SHIFT, pmu4_out_rp_txt);
+
+static const struct snd_kcontrol_new line_out_rp_mux =
+SOC_DAPM_ENUM("ROUTE_RP_OUT", pmu4_out_rp_enum);
+
+/*line out Right Negative mux */
+static const char * const pmu4_out_rn_txt[] = {
+       "None", "LORN_SEL_AIR", "LORN_SEL_DACR", "Reserved", "LORN_SEL_DACR_INV"
+};
+
+static const SOC_ENUM_SINGLE_DECL(pmu4_out_rn_enum, PMU4_LINE_OUT_CONFIG,
+                                 PMU4_LORN_SEL_SHIFT, pmu4_out_rn_txt);
+
+static const struct snd_kcontrol_new line_out_rn_mux =
+SOC_DAPM_ENUM("ROUTE_RN_OUT", pmu4_out_rn_enum);
+
+static const struct snd_soc_dapm_widget pmu4_audio_dapm_widgets[] = {
+
+       /* Input */
+       SND_SOC_DAPM_INPUT("Linein left 1"),
+       SND_SOC_DAPM_INPUT("Linein left 2"),
+       SND_SOC_DAPM_INPUT("Linein left 3"),
+       SND_SOC_DAPM_INPUT("Linein left 4"),
+
+       SND_SOC_DAPM_INPUT("Linein right 1"),
+       SND_SOC_DAPM_INPUT("Linein right 2"),
+       SND_SOC_DAPM_INPUT("Linein right 3"),
+       SND_SOC_DAPM_INPUT("Linein right 4"),
+
+       /*PGA input */
+       SND_SOC_DAPM_PGA("PGAL_IN_EN", PMU4_BLOCK_ENABLE,
+                        PMU4_PGAL_IN_EN, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("PGAR_IN_EN", PMU4_BLOCK_ENABLE,
+                        PMU4_PGAR_IN_EN, 0, NULL, 0),
+
+       /*PGA input source select */
+       SND_SOC_DAPM_MUX("Linein left switch", SND_SOC_NOPM,
+                        0, 0, &pgain_ln_mux),
+       SND_SOC_DAPM_MUX("Linein right switch", SND_SOC_NOPM,
+                        0, 0, &pgain_rn_mux),
+
+       /*ADC capture stream */
+       SND_SOC_DAPM_ADC("Left ADC", "HIFI Capture", PMU4_BLOCK_ENABLE,
+                        PMU4_ADCL_EN, 0),
+       SND_SOC_DAPM_ADC("Right ADC", "HIFI Capture", PMU4_BLOCK_ENABLE,
+                        PMU4_ADCR_EN, 0),
+
+       /*Output */
+       SND_SOC_DAPM_OUTPUT("Lineout left N"),
+       SND_SOC_DAPM_OUTPUT("Lineout left P"),
+       SND_SOC_DAPM_OUTPUT("Lineout right N"),
+       SND_SOC_DAPM_OUTPUT("Lineout right P"),
+
+       /*DAC playback stream */
+       SND_SOC_DAPM_DAC("Left DAC", "HIFI Playback", PMU4_BLOCK_ENABLE,
+                        PMU4_DACL_EN, 0),
+       SND_SOC_DAPM_DAC("Right DAC", "HIFI Playback", PMU4_BLOCK_ENABLE,
+                        PMU4_DACR_EN, 0),
+
+       /*DRV output */
+       SND_SOC_DAPM_OUT_DRV("LOLP_OUT_EN", PMU4_BLOCK_ENABLE,
+                            PMU4_LOLP_EN, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LOLN_OUT_EN", PMU4_BLOCK_ENABLE,
+                            PMU4_LOLN_EN, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LORP_OUT_EN", PMU4_BLOCK_ENABLE,
+                            PMU4_LORP_EN, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LORN_OUT_EN", PMU4_BLOCK_ENABLE,
+                            PMU4_LORN_EN, 0, NULL, 0),
+
+       /*MUX output source select */
+       SND_SOC_DAPM_MUX("Lineout left P switch", SND_SOC_NOPM,
+                        0, 0, &line_out_lp_mux),
+       SND_SOC_DAPM_MUX("Lineout left N switch", SND_SOC_NOPM,
+                        0, 0, &line_out_ln_mux),
+       SND_SOC_DAPM_MUX("Lineout right P switch", SND_SOC_NOPM,
+                        0, 0, &line_out_rp_mux),
+       SND_SOC_DAPM_MUX("Lineout right N switch", SND_SOC_NOPM,
+                        0, 0, &line_out_rn_mux),
+};
+
+static const struct snd_soc_dapm_route pmu4_audio_dapm_routes[] = {
+/* Input path */
+       {"Linein left switch", "AIL1", "Linein left 1"},
+       {"Linein left switch", "AIL2", "Linein left 2"},
+       {"Linein left switch", "AIL3", "Linein left 3"},
+       {"Linein left switch", "AIL4", "Linein left 4"},
+
+       {"Linein right switch", "AIR1", "Linein right 1"},
+       {"Linein right switch", "AIR2", "Linein right 2"},
+       {"Linein right switch", "AIR3", "Linein right 3"},
+       {"Linein right switch", "AIR4", "Linein right 4"},
+
+       {"PGAL_IN_EN", NULL, "Linein left switch"},
+       {"PGAR_IN_EN", NULL, "Linein right switch"},
+
+       {"Left ADC", NULL, "PGAL_IN_EN"},
+       {"Right ADC", NULL, "PGAR_IN_EN"},
+
+/*Output path*/
+       {"Lineout left P switch", "LOLP_SEL_DACL", "Left DAC"},
+       {"Lineout left P switch", "LOLP_SEL_AIL", "PGAL_IN_EN"},
+       {"Lineout left P switch", "LOLP_SEL_AIL_INV", "PGAL_IN_EN"},
+
+       {"Lineout left N switch", "LOLN_SEL_AIL", "PGAL_IN_EN"},
+       {"Lineout left N switch", "LOLN_SEL_DACL", "Left DAC"},
+       {"Lineout left N switch", "LOLN_SEL_DACL_INV", "Left DAC"},
+
+       {"Lineout right P switch", "LORP_SEL_DACR", "Right DAC"},
+       {"Lineout right P switch", "LORP_SEL_AIR", "PGAR_IN_EN"},
+       {"Lineout right P switch", "LORP_SEL_AIR_INV", "PGAR_IN_EN"},
+
+       {"Lineout right N switch", "LORN_SEL_AIR", "PGAR_IN_EN"},
+       {"Lineout right N switch", "LORN_SEL_DACR", "Right DAC"},
+       {"Lineout right N switch", "LORN_SEL_DACR_INV", "Right DAC"},
+
+       {"LOLN_OUT_EN", NULL, "Lineout left N switch"},
+       {"LOLP_OUT_EN", NULL, "Lineout left P switch"},
+       {"LORN_OUT_EN", NULL, "Lineout right N switch"},
+       {"LORP_OUT_EN", NULL, "Lineout right P switch"},
+
+       {"Lineout left N", NULL, "LOLN_OUT_EN"},
+       {"Lineout left P", NULL, "LOLP_OUT_EN"},
+       {"Lineout right N", NULL, "LORN_OUT_EN"},
+       {"Lineout right P", NULL, "LORP_OUT_EN"},
+};
+
+static int aml_pmu4_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               snd_soc_update_bits(codec, PMU4_AUDIO_CONFIG,
+                                   PMU4_I2S_MODE, PMU4_I2S_MODE);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               snd_soc_update_bits(codec, PMU4_AUDIO_CONFIG, PMU4_I2S_MODE, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int aml_pmu4_set_dai_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       return 0;
+}
+
+static int aml_pmu4_hw_params(struct snd_pcm_substream *substream,
+                             struct snd_pcm_hw_params *params,
+                             struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct aml_pmu4_audio_priv *pmu4_audio =
+           snd_soc_codec_get_drvdata(codec);
+
+       pmu4_audio->params = params;
+
+       return 0;
+}
+
+static int aml_pmu4_audio_set_bias_level(struct snd_soc_codec *codec,
+                                        enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->component.dapm.bias_level == SND_SOC_BIAS_OFF) {
+#if 0 /*xang_kernel44_tmp*/
+                       codec->cache_only = false;
+                       codec->cache_sync = 1;
+#endif
+                       snd_soc_cache_sync(codec);
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+
+               break;
+
+       default:
+               break;
+       }
+       codec->component.dapm.bias_level = level;
+
+       return 0;
+}
+
+static int aml_pmu4_prepare(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
+{
+       /*struct snd_soc_codec *codec = dai->codec;*/
+       return 0;
+
+}
+
+static int aml_pmu4_audio_reset(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, PMU4_SOFT_RESET, 0xF);
+       snd_soc_write(codec, PMU4_SOFT_RESET, 0x0);
+       udelay(10*1000);
+       return 0;
+}
+
+static int aml_pmu4_audio_start_up(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, PMU4_BLOCK_ENABLE, 0xF000);
+       msleep(200);
+       snd_soc_write(codec, PMU4_BLOCK_ENABLE, 0xB000);
+       return 0;
+}
+
+static int aml_pmu4_audio_power_init(void)
+{
+       uint8_t val = 0;
+
+       /*set audio ldo supply en in,reg:0x05,bit 0*/
+       aml_pmu4_read(AML1220_PMU_CTR_04, &val);
+       aml_pmu4_write(AML1220_PMU_CTR_04, val | 0x01);
+       return 0;
+
+}
+
+static int aml_pmu4_codec_mute_stream(struct snd_soc_dai *dai, int mute,
+                                     int stream)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 reg;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               reg = snd_soc_read(codec, PMU4_DAC_SOFT_MUTE);
+               if (mute)
+                       reg |= 0x8000;
+               else
+                       reg &= ~0x8000;
+
+               snd_soc_write(codec, PMU4_DAC_SOFT_MUTE, reg);
+       }
+       return 0;
+}
+
+static int aml_pmu4_audio_probe(struct snd_soc_codec *codec)
+{
+       struct aml_pmu4_audio_priv *pmu4_audio = NULL;
+
+       /*pr_info("enter %s\n", __func__);*/
+       pmu4_audio = kzalloc(sizeof(struct aml_pmu4_audio_priv), GFP_KERNEL);
+       if (pmu4_audio == NULL)
+               return -ENOMEM;
+       snd_soc_codec_set_drvdata(codec, pmu4_audio);
+
+       /*enable LDO1V8 for audio*/
+       aml_pmu4_audio_power_init();
+
+       /*reset audio codec register*/
+       aml_pmu4_audio_reset(codec);
+       aml_pmu4_audio_start_up(codec);
+       aml_pmu4_audio_reg_init(codec);
+
+       codec->component.dapm.bias_level = SND_SOC_BIAS_STANDBY;
+       pmu4_audio->codec = codec;
+
+       return 0;
+}
+
+static int aml_pmu4_audio_remove(struct snd_soc_codec *codec)
+{
+       aml_pmu4_audio_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int aml_pmu4_audio_suspend(struct snd_soc_codec *codec)
+{
+       aml_pmu4_audio_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int aml_pmu4_audio_resume(struct snd_soc_codec *codec)
+{
+       pr_info("enter %s\n", __func__);
+       aml_pmu4_audio_power_init();
+       aml_pmu4_audio_reset(codec);
+       aml_pmu4_audio_start_up(codec);
+       aml_pmu4_audio_reg_init(codec);
+       codec->component.dapm.bias_level = SND_SOC_BIAS_STANDBY;
+       aml_pmu4_audio_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       return 0;
+}
+
+#define PMU4_AUDIO_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define PMU4_AUDIO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+                       | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE \
+                       | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai_ops pmu4_audio_aif_dai_ops = {
+       .hw_params = aml_pmu4_hw_params,
+       .prepare = aml_pmu4_prepare,
+       .set_fmt = aml_pmu4_set_dai_fmt,
+       .set_sysclk = aml_pmu4_set_dai_sysclk,
+       .mute_stream = aml_pmu4_codec_mute_stream,
+};
+
+struct snd_soc_dai_driver aml_pmu4_audio_dai[] = {
+       {
+        .name = "pmu4-audio-hifi",
+        .id = 0,
+        .playback = {
+                     .stream_name = "HIFI Playback",
+                     .channels_min = 2,
+                     .channels_max = 8,
+                     .rates = PMU4_AUDIO_STEREO_RATES,
+                     .formats = PMU4_AUDIO_FORMATS,
+                     },
+        .capture = {
+                    .stream_name = "HIFI Capture",
+                    .channels_min = 1,
+                    .channels_max = 2,
+                    .rates = PMU4_AUDIO_STEREO_RATES,
+                    .formats = PMU4_AUDIO_FORMATS,
+                    },
+        .ops = &pmu4_audio_aif_dai_ops,
+        },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_aml_pmu4_audio = {
+       .probe = aml_pmu4_audio_probe,
+       .remove = aml_pmu4_audio_remove,
+       .suspend = aml_pmu4_audio_suspend,
+       .resume = aml_pmu4_audio_resume,
+       .read = aml_pmu4_audio_read,
+       .write = aml_pmu4_audio_write,
+       .set_bias_level = aml_pmu4_audio_set_bias_level,
+       .component_driver = {
+               .controls = pmu4_audio_snd_controls,
+               .num_controls = ARRAY_SIZE(pmu4_audio_snd_controls),
+               .dapm_widgets = pmu4_audio_dapm_widgets,
+               .num_dapm_widgets = ARRAY_SIZE(pmu4_audio_dapm_widgets),
+               .dapm_routes = pmu4_audio_dapm_routes,
+               .num_dapm_routes = ARRAY_SIZE(pmu4_audio_dapm_routes),
+       },
+       .reg_cache_size = 16,
+       .reg_word_size = sizeof(u16),
+       .reg_cache_step = 2,
+};
+
+static int aml_pmu4_audio_codec_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       dev_info(&pdev->dev, "aml_pmu4_audio_codec_probe\n");
+       ret = snd_soc_register_codec(&pdev->dev,
+                                    &soc_codec_dev_aml_pmu4_audio,
+                                    &aml_pmu4_audio_dai[0], 1);
+       return ret;
+}
+
+static int aml_pmu4_audio_codec_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id aml_pmu4_codec_dt_match[] = {
+       {.compatible = "amlogic, aml_pmu4_codec",},
+       {},
+};
+
+static struct platform_driver aml_pmu4_codec_platform_driver = {
+       .driver = {
+                  .name = "aml_pmu4_codec",
+                  .owner = THIS_MODULE,
+                  .of_match_table = aml_pmu4_codec_dt_match,
+                  },
+       .probe = aml_pmu4_audio_codec_probe,
+       .remove = aml_pmu4_audio_codec_remove,
+};
+
+static int __init aml_pmu4_audio_modinit(void)
+{
+       int ret = 0;
+
+       ret = platform_driver_register(&aml_pmu4_codec_platform_driver);
+       if (ret != 0) {
+               pr_info("Failed to register AML PMU4 codec platform driver: %d\n",
+                       ret);
+       }
+
+       return ret;
+}
+
+module_init(aml_pmu4_audio_modinit);
+
+static void __exit aml_pmu4_audio_exit(void)
+{
+       platform_driver_unregister(&aml_pmu4_codec_platform_driver);
+}
+
+module_exit(aml_pmu4_audio_exit);
+
+MODULE_DESCRIPTION("ASoC AML pmu4 audio codec driver");
+MODULE_AUTHOR("Chengshun Wang <chengshun.wang@amlogic.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/amlogic/aml_pmu4_codec.h b/sound/soc/codecs/amlogic/aml_pmu4_codec.h
new file mode 100644 (file)
index 0000000..f557d22
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * sound/soc/codecs/amlogic/aml_pmu4_codec.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef AML_PMU4_H_
+#define AML_PMU4_H_
+
+#define PMU4_AUDIO_BASE    0x40
+/*Info*/
+#define PMU4_SOFT_RESET                0x00
+#define PMU4_BLOCK_ENABLE              0x02
+#define PMU4_AUDIO_CONFIG              0x04
+#define PMU4_PGA_IN_CONFIG             0x06
+#define PMU4_ADC_VOL_CTR               0x08
+#define PMU4_DAC_SOFT_MUTE             0x0A
+#define PMU4_DAC_VOL_CTR               0x0C
+#define PMU4_LINE_OUT_CONFIG           0x0E
+
+/*Block Enable , Reg 0x02h*/
+#define PMU4_BIAS_CURRENT_EN    0xD
+#define PMU4_PGAL_IN_EN         0xB
+#define PMU4_PGAR_IN_EN         0xA
+#define PMU4_PGAL_IN_ZC_EN      0x9
+#define PMU4_PGAR_IN_ZC_EN      0x8
+#define PMU4_ADCL_EN            0x7
+#define PMU4_ADCR_EN            0x6
+#define PMU4_DACL_EN            0x5
+#define PMU4_DACR_EN            0x4
+#define PMU4_LOLP_EN            0x3
+#define PMU4_LOLN_EN            0x2
+#define PMU4_LORP_EN            0x1
+#define PMU4_LORN_EN            0x0
+
+/*Audio Config,Reg 0x04h*/
+#define PMU4_MCLK_FREQ          0xF
+#define PMU4_I2S_MODE           0xE
+#define PMU4_ADC_HPF_MODE       0xC
+#define PMU4_ADC_DEM_EN         0xA
+#define PMU4_ADC_CLK_TO_GPIO_EN 0x9
+#define PMU4_DAC_CLK_TO_GPIO_EN 0x8
+#define PMU4_DACL_DATA_SOURCE   0x7
+#define PMU4_DACR_DATA_SOURCE   0x6
+#define PMU4_DACL_INV           0x5
+#define PMU4_DACR_INV           0x4
+#define PMU4_ADCDATL_SOURCE     0x3
+#define PMU4_ADCDATR_SOURCE     0x2
+#define PMU4_ADCL_INV           0x1
+#define PMU4_ADCR_INV           0x0
+
+/*PGA_IN Config,  Reg 0x06h*/
+#define PMU4_PGAL_IN_SEL        0xD
+#define PMU4_PGAL_IN_GAIN       0x8
+#define PMU4_PGAR_IN_SEL        0x5
+#define PMU4_PGAR_IN_GAIN       0x0
+
+/*ADC_Volume_Control , Reg 0x08h*/
+#define PMU4_ADCL_VOL_CTR            0x8
+#define PMU4_ADCR_VOL_CTR            0x0
+
+/*DAC Soft Mute, Reg 0xA*/
+#define PMU4_DAC_SOFT_MUTE_BIT  0xF
+#define PMU4_DAC_UNMUTE_MODE    0xE
+#define PMU4_DAC_MUTE_MODE      0xD
+#define PMU4_DAC_VC_RAMP_MODE   0xC
+#define PMU4_DAC_RAMP_RATE      0xA
+#define PMU4_DAC_MONO           0x8
+#define PMU4_MUTE_DAC_PD_EN     0x7
+
+/*DAC_Volume_Control, Reg 0xC*/
+#define PMU4_DACL_VOL_CTR            0x8
+#define PMU4_DACR_VOL_CTR            0x0
+
+/*Line-Out Config, Reg 0xE*/
+#define PMU4_LOLP_SEL_DACL      0xE
+#define PMU4_LOLP_SEL_AIL       0xD
+#define PMU4_LOLP_SEL_SHIFT     0xC
+#define PMU4_LOLN_SEL_DACL_INV  0xA
+#define PMU4_LOLN_SEL_DACL      0x9
+#define PMU4_LOLN_SEL_SHIFT     0x8
+#define PMU4_LORP_SEL_DACR      0x6
+#define PMU4_LORP_SEL_AIR       0x5
+#define PMU4_LORP_SEL_SHIFT     0x4
+#define PMU4_LORN_SEL_DACR_INV  0x2
+#define PMU4_LORN_SEL_DACR      0x1
+#define PMU4_LORN_SEL_SHIFT     0x0
+
+#endif
diff --git a/sound/soc/codecs/amlogic/dummy_codec.c b/sound/soc/codecs/amlogic/dummy_codec.c
new file mode 100644 (file)
index 0000000..89b7974
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * sound/soc/codecs/amlogic/dummy_codec.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <linux/of.h>
+
+struct dummy_codec_private {
+       struct snd_soc_codec codec;
+};
+
+#define DUMMY_CODEC_RATES              (SNDRV_PCM_RATE_8000_192000)
+#define DUMMY_CODEC_FORMATS            (SNDRV_PCM_FMTBIT_S16_LE |\
+               SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int dummy_codec_pcm_hw_params(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_hw_params *params,
+                                    struct snd_soc_dai *dai)
+{
+       return 0;
+}
+
+static int dummy_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                                  unsigned int fmt)
+{
+       return 0;
+}
+
+static int dummy_codec_mute(struct snd_soc_dai *dai, int mute)
+{
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget dummy_codec_dapm_widgets[] = {
+       /* Output Side */
+       /* DACs */
+       SND_SOC_DAPM_DAC("Left DAC", "HIFI Playback",
+                        SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("Right DAC", "HIFI Playback",
+                        SND_SOC_NOPM, 7, 0),
+
+       /* Output Lines */
+       SND_SOC_DAPM_OUTPUT("LOUTL"),
+       SND_SOC_DAPM_OUTPUT("LOUTR"),
+
+};
+
+static const struct snd_soc_dapm_route dummy_codec_dapm_routes[] = {
+
+       {"LOUTL", NULL, "Left DAC"},
+       {"LOUTR", NULL, "Right DAC"},
+};
+
+static struct snd_soc_dai_ops dummy_codec_ops = {
+       .hw_params = dummy_codec_pcm_hw_params,
+       .set_fmt = dummy_codec_set_dai_fmt,
+       .digital_mute = dummy_codec_mute,
+};
+
+struct snd_soc_dai_driver dummy_codec_dai = {
+       .name = "dummy",
+       .id = 1,
+       .playback = {
+                    .stream_name = "HIFI Playback",
+                    .channels_min = 1,
+                    .channels_max = 8,
+                    .rates = DUMMY_CODEC_RATES,
+                    .formats = DUMMY_CODEC_FORMATS,
+                    },
+       .capture = {
+                   .stream_name = "HIFI Capture",
+                   .channels_min = 1,
+                   .channels_max = 2,
+                   .rates = DUMMY_CODEC_RATES,
+                   .formats = DUMMY_CODEC_FORMATS,
+                   },
+       .ops = &dummy_codec_ops,
+};
+
+static int dummy_codec_probe(struct snd_soc_codec *codec)
+{
+       return 0;
+}
+
+static int dummy_codec_remove(struct snd_soc_codec *codec)
+{
+       return 0;
+};
+
+struct snd_soc_codec_driver soc_codec_dev_dummy_codec = {
+       .probe = dummy_codec_probe,
+       .remove = dummy_codec_remove,
+       .component_driver = {
+               .dapm_widgets = dummy_codec_dapm_widgets,
+               .num_dapm_widgets = ARRAY_SIZE(dummy_codec_dapm_widgets),
+               .dapm_routes = dummy_codec_dapm_routes,
+               .num_dapm_routes = ARRAY_SIZE(dummy_codec_dapm_routes),
+       }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_codec_dt_match[] = {
+       {.compatible = "amlogic, aml_dummy_codec",
+        },
+       {},
+};
+#else
+#define amlogic_codec_dt_match NULL
+#endif
+
+static int dummy_codec_platform_probe(struct platform_device *pdev)
+{
+       struct dummy_codec_private *dummy_codec;
+       int ret;
+
+       dummy_codec = kzalloc(sizeof(struct dummy_codec_private), GFP_KERNEL);
+       if (dummy_codec == NULL)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, dummy_codec);
+       ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dummy_codec,
+                                    &dummy_codec_dai, 1);
+
+       if (ret < 0)
+               kfree(dummy_codec);
+
+       return ret;
+}
+
+static int dummy_codec_platform_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       kfree(platform_get_drvdata(pdev));
+       return 0;
+}
+
+static struct platform_driver dummy_codec_platform_driver = {
+       .driver = {
+                  .name = "dummy",
+                  .owner = THIS_MODULE,
+                  .of_match_table = amlogic_codec_dt_match,
+                  },
+       .probe = dummy_codec_platform_probe,
+       .remove = dummy_codec_platform_remove,
+};
+
+static int __init dummy_codec_init(void)
+{
+       return platform_driver_register(&dummy_codec_platform_driver);
+}
+
+static void __exit dummy_codec_exit(void)
+{
+       platform_driver_unregister(&dummy_codec_platform_driver);
+}
+
+module_init(dummy_codec_init);
+module_exit(dummy_codec_exit);
+
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_DESCRIPTION("ASoC dummy_codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/amlogic/pcm2bt.c b/sound/soc/codecs/amlogic/pcm2bt.c
new file mode 100644 (file)
index 0000000..fb2835a
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * sound/soc/codecs/amlogic/pcm2bt.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+struct pcm2bt_priv {
+       struct snd_soc_codec codec;
+};
+
+#define PCM2BT_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
+
+#define PCM2BT_FORMATS \
+       (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+       SNDRV_PCM_FMTBIT_S8)
+
+static int pcm2bt_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       return 0;
+}
+
+static int pcm2bt_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       return 0;
+}
+
+static int pcm2bt_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+                            unsigned int freq, int dir)
+{
+       return 0;
+}
+
+static int pcm2bt_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       return 0;
+}
+
+struct snd_soc_dai_ops pcm2bt_dai_ops = {
+       .hw_params = pcm2bt_hw_params,
+       .set_fmt = pcm2bt_set_fmt,
+       .set_sysclk = pcm2bt_set_sysclk,
+       .set_sysclk = pcm2bt_set_sysclk,
+};
+
+struct snd_soc_dai_driver pcm2bt_dai[] = {
+       {
+        .name = "pcm2bt-pcm",
+        .playback = {
+                     .stream_name = "PCM2BT Playback",
+                     .channels_min = 1,
+                     .channels_max = 1,
+                     .rates = PCM2BT_RATES,
+                     .formats = PCM2BT_FORMATS,
+                     },
+        .capture = {
+                    .stream_name = "BT2PCM Capture",
+                    .channels_min = 1,
+                    .channels_max = 1,
+                    .rates = PCM2BT_RATES,
+                    .formats = PCM2BT_FORMATS,
+                    },
+        .ops = &pcm2bt_dai_ops,
+        .symmetric_rates = 1,
+        },
+};
+
+static int pcm2bt_probe(struct snd_soc_codec *codec)
+{
+       return 0;
+}
+
+static int pcm2bt_suspend(struct snd_soc_codec *codec)
+{
+       return 0;
+}
+
+static int pcm2bt_resume(struct snd_soc_codec *codec)
+{
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm2bt = {
+       .probe = pcm2bt_probe,
+       .suspend = pcm2bt_suspend,
+       .resume = pcm2bt_resume,
+       .set_bias_level = pcm2bt_set_bias_level,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_pcm2bt);
+
+static int pcm2bt_platform_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       ret = snd_soc_register_codec(&pdev->dev,
+                                    &soc_codec_dev_pcm2bt, pcm2bt_dai,
+                                    ARRAY_SIZE(pcm2bt_dai));
+
+       return ret;
+
+}
+
+static int pcm2bt_platform_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_pcm2BT_codec_dt_match[] = {
+       {.compatible = "amlogic, pcm2BT-codec",},
+       {},
+};
+#else
+#define amlogic_pcm2BT_codec_dt_match NULL
+#endif
+
+static struct platform_driver pcm2bt_platform_driver = {
+       .driver = {
+                  .name = "pcm2bt",
+                  .owner = THIS_MODULE,
+                  .of_match_table = amlogic_pcm2BT_codec_dt_match,
+                  },
+       .probe = pcm2bt_platform_probe,
+       .remove = pcm2bt_platform_remove,
+};
+
+static int __init pcm_bt_init(void)
+{
+       return platform_driver_register(&pcm2bt_platform_driver);
+}
+
+module_init(pcm_bt_init);
+
+static void __exit pcm_bt_exit(void)
+{
+       platform_driver_unregister(&pcm2bt_platform_driver);
+}
+
+module_exit(pcm_bt_exit);
+
+MODULE_DESCRIPTION("ASoC pcm2bt driver");
+MODULE_AUTHOR("AMLogic, Inc.");
+MODULE_LICENSE("GPL");