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
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
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
"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";
"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";
"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";
"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";
};
};
- 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 */
};
};
- 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 */
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
/* #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)
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;
}
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;
}
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
/* 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,
},
};
+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,
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);
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__);
};
struct parm n2;
/* FIXME ssen gate control? */
u8 sdm_en;
- u8 en_dss;
+ u8 en_dds;
spinlock_t *lock;
};
.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,
},
.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,
},
.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,
},
.width = 9,
},
.sdm_en = 11,
- .en_dss = 0,
+ .en_dds = 0,
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
.name = "mpll3",
#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>
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 :
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
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) };
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*/
#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 */
/* 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
# generic frame-work
source "sound/soc/generic/Kconfig"
+# for Amlogic Soc
+source "sound/soc/amlogic/Kconfig"
+
endif # SND_SOC
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
--- /dev/null
+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
+
--- /dev/null
+# 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+
+
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
tristate "Texas Instruments TPA6130A2 headphone amplifier"
depends on I2C
+# for Amlogic Codecs
+source "sound/soc/codecs/amlogic/Kconfig"
endmenu
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/
--- /dev/null
+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
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");