audio: config audio for m8b
authorXing Wang <xing.wang@amlogic.com>
Wed, 26 Apr 2017 12:24:13 +0000 (20:24 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Wed, 26 Apr 2017 14:41:02 +0000 (07:41 -0700)
PD#141217: audio: config audio for m8b

Change-Id: If837cf19bf3da0e54830fefd2267fd14445ca6f1
Signed-off-by: Xing Wang <xing.wang@amlogic.com>
19 files changed:
MAINTAINERS
arch/arm/boot/dts/amlogic/meson8b.dtsi
arch/arm/boot/dts/amlogic/meson8b_m200.dts
arch/arm/configs/meson32_defconfig
arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts
arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts
arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts
arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts
arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts
arch/arm64/boot/dts/amlogic/gxm_skt.dts
drivers/amlogic/clk/m8b/clk_misc.c
drivers/amlogic/pinctrl/pinctrl-meson8b.c
include/dt-bindings/clock/meson8b-clkc.h
sound/soc/amlogic/aml_meson.c
sound/soc/amlogic/aml_pcm_dai.c
sound/soc/codecs/amlogic/Kconfig
sound/soc/codecs/amlogic/Makefile
sound/soc/codecs/amlogic/aml_pmu3.c [new file with mode: 0644]
sound/soc/codecs/amlogic/aml_pmu3.h [new file with mode: 0644]

index 91cbb7f..c34b94b 100644 (file)
@@ -13845,3 +13845,24 @@ AMLOGIC AXG ADD PXP DTS
 M: Yun Cai <yun.cai@amlogic.com>
 F: arch/arm64/boot/dts/amlogic/axg_pxp.dts
 F: arch/arm64/boot/dts/amlogic/mesonaxg.dtsi
+
+AMLOGIC S805 audio
+M: Xing Wang <xing.wang@amlogic.com>
+F: arch/arm/boot/dts/amlogic/meson8b.dtsi
+F: arch/arm/boot/dts/amlogic/meson8b_m200.dts
+F: arch/arm/configs/meson32_defconfig
+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/gxl_p400_2g.dts
+F: arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts
+F: arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts
+F: arch/arm64/boot/dts/amlogic/gxm_skt.dts
+F: drivers/amlogic/clk/m8b/clk_misc.c
+F: drivers/amlogic/pinctrl/pinctrl-meson8b.c
+F: include/dt-bindings/clock/meson8b-clkc.h
+F: sound/soc/amlogic/aml_meson.c
+F: sound/soc/amlogic/aml_pcm_dai.c
+F: sound/soc/codecs/amlogic/Kconfig
+F: sound/soc/codecs/amlogic/Makefile
+F: sound/soc/codecs/amlogic/aml_pmu3.c
+F: sound/soc/codecs/amlogic/aml_pmu3.h
index 0e2f788..f1aaaf6 100644 (file)
                                        function = "uart_b";
                                };
                        };
+
+                       audio_pcm_pins:audio_pcm {
+                               mux {
+                                       groups = "pcm_out_a",
+                                               "pcm_in_a",
+                                               "pcm_fs_a",
+                                               "pcm_clk_a";
+                                       function = "pcm_a";
+                               };
+                       };
                };
                pinctrl_aobus: pinctrl@c8100084 {
                        compatible = "amlogic,meson8b-aobus-pinctrl";
                                        function = "hdmi_cec";
                                };
                        };
+                       audio_i2s_pins:audio_i2s {
+                               mux {
+                                       groups = "i2s_am_clk_out",
+                                               "i2s_ao_clk_out",
+                                               "i2s_lr_clk_out",
+                                               "i2s_in_ch01",
+                                               "i2s_out_01";
+                                       function = "i2s";
+                               };
+                       };
+                       audio_spdif_pins:audio_spdif {
+                               mux {
+                                       groups = "spdif_out_2";
+                                       function = "spdif_2";
+                               };
+                       };
                };
 dwc2_b {
                compatible = "amlogic,dwc2";
index c13ce35..850ff06 100644 (file)
                        };
                };
        };
+
+       /* 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>,
+                       <&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 */
+       };
+       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_pcm";
+               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 = "disabled";
+       };
+       amlogic_codec:t9015 {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_codec_T9015";
+               reg = <0x0 0xc8832000 0x0 0x14>;
+               status = "disabled";
+       };
+       audio_pmu3_codec:aml_pmu3 {
+               #sound-dai-cells = <0>;
+               compatible = "amlogic, aml_pmu3_codec";
+               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 = "aml_audio_i2s";
+               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 = <&audio_pmu3_codec>;
+               };
+               codec1: codec1 {
+                       sound-dai = <&spdif_codec>;
+               };
+               codec2: codec2 {
+                       sound-dai = <&pcm_codec>;
+               };
+       };
+       /* END OF AUDIO board specific */
 };
 
 &uart_AO {
index 1de9fe0..c858d86 100644 (file)
@@ -126,6 +126,17 @@ CONFIG_FB_SIMPLE=y
 CONFIG_FRAMEBUFFER_CONSOLE=y
 CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
 CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_AMLOGIC_SND_SOC_CODECS=y
+CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC=y
+CONFIG_AMLOGIC_SND_CODEC_PCM2BT=y
+CONFIG_AMLOGIC_SND_CODEC_AMLT9015=y
+CONFIG_AMLOGIC_SND_CODEC_PMU3=y
+CONFIG_AMLOGIC_SND_SOC=y
+CONFIG_AMLOGIC_SND_SPLIT_MODE=y
 CONFIG_USB_HIDDEV=y
 CONFIG_USB=y
 CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
index ea844f4..7e3f4a7 100644 (file)
        pcm_dai: PCM {
                #sound-dai-cells = <0>;
                compatible = "amlogic, aml-pcm-dai";
-               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-names = "aml_audio_pcm";
                pinctrl-0 = <&audio_pcm_pins>;
                clocks =
                        <&clkc CLKID_MPLL0>,
                mute_inv;
                hp_disable;
                hp_paraments = <800 300 0 5 1>;
-               pinctrl-names = "audio_i2s_pins";
+               pinctrl-names = "aml_audio_i2s";
                pinctrl-0 = <&audio_i2s_pins>;
                cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
                codec_list = <&codec0 &codec1 &codec2>;
index 984da7b..a417a97 100644 (file)
        pcm_dai: PCM {
                #sound-dai-cells = <0>;
                compatible = "amlogic, aml-pcm-dai";
-               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-names = "aml_audio_pcm";
                pinctrl-0 = <&audio_pcm_pins>;
                clocks =
                        <&clkc CLKID_MPLL0>,
                mute_inv;
                hp_disable;
                hp_paraments = <800 300 0 5 1>;
-               pinctrl-names = "audio_i2s_pins";
+               pinctrl-names = "aml_audio_i2s";
                pinctrl-0 = <&audio_i2s_pins>;
                cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
                codec_list = <&codec0 &codec1 &codec2>;
index 32468bd..71af223 100644 (file)
        pcm_dai: PCM {
                #sound-dai-cells = <0>;
                compatible = "amlogic, aml-pcm-dai";
-               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-names = "aml_audio_pcm";
                pinctrl-0 = <&audio_pcm_pins>;
                clocks =
                        <&clkc CLKID_MPLL0>,
                mute_inv;
                hp_disable;
                hp_paraments = <800 300 0 5 1>;
-               pinctrl-names = "audio_i2s_pins";
+               pinctrl-names = "aml_audio_i2s";
                pinctrl-0 = <&audio_i2s_pins>;
                cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
                codec_list = <&codec0 &codec1 &codec2>;
index 0dc3a6c..5869480 100644 (file)
        pcm_dai: PCM {
                #sound-dai-cells = <0>;
                compatible = "amlogic, aml-pcm-dai";
-               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-names = "aml_audio_pcm";
                pinctrl-0 = <&audio_pcm_pins>;
                clocks =
                        <&clkc CLKID_MPLL0>,
                mute_inv;
                hp_disable;
                hp_paraments = <800 300 0 5 1>;
-               pinctrl-names = "audio_i2s_pins";
+               pinctrl-names = "aml_audio_i2s";
                pinctrl-0 = <&audio_i2s_pins>;
                cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
                codec_list = <&codec0 &codec1 &codec2>;
index ef4fa30..7893e3b 100644 (file)
        pcm_dai: PCM {
                #sound-dai-cells = <0>;
                compatible = "amlogic, aml-pcm-dai";
-               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-names = "aml_audio_pcm";
                pinctrl-0 = <&audio_pcm_pins>;
                clocks =
                        <&clkc CLKID_MPLL0>,
                mute_inv;
                hp_disable;
                hp_paraments = <800 300 0 5 1>;
-               pinctrl-names = "audio_i2s_pins";
+               pinctrl-names = "aml_audio_i2s";
                pinctrl-0 = <&audio_i2s_pins>;
                cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
                codec_list = <&codec0 &codec1 &codec2>;
index e2a657e..fcdada8 100644 (file)
        pcm_dai: PCM {
                #sound-dai-cells = <0>;
                compatible = "amlogic, aml-pcm-dai";
-               pinctrl-names = "aml_audio_btpcm";
+               pinctrl-names = "aml_audio_pcm";
                pinctrl-0 = <&audio_pcm_pins>;
                clocks =
                        <&clkc CLKID_MPLL0>,
                mute_inv;
                hp_disable;
                hp_paraments = <800 300 0 5 1>;
-               pinctrl-names = "audio_i2s_pins";
+               pinctrl-names = "aml_audio_i2s";
                pinctrl-0 = <&audio_i2s_pins>;
                cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
                codec_list = <&codec0 &codec1 &codec2>;
index 309dcaa..04d1768 100644 (file)
@@ -130,6 +130,7 @@ static struct clk_hw *amclk_hws[] = {
 
 /* cts_clk_i958 */
 const char *i958_parent_names[] = { "ddr_pll_clk", "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,
@@ -172,6 +173,20 @@ static struct clk_gate i958_gate = {
        },
 };
 
+static struct clk_mux i958_comp_spdif = {
+       .reg = (void *)HHI_AUD_CLK_CNTL2,
+       .mask = 0x1,
+       .shift = 27,
+       .lock = &clk_lock,
+       .hw.init = &(struct clk_init_data){
+               .name = "i958_comp_spdif",
+               .ops = &clk_mux_ops,
+               .parent_names = i958_ext_parent_names,
+               .num_parents = 2,
+               .flags = (CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED),
+       },
+};
+
 static struct clk_hw *i958_hws[] = {
 [CLKID_I958_MUX - CLKID_I958_MUX] = &i958_mux.hw,
 [CLKID_I958_DIV - CLKID_I958_MUX] = &i958_div.hw,
@@ -274,6 +289,8 @@ void amlogic_init_misc(void)
        i958_div.reg = clk_base + (u32)(i958_div.reg);
        i958_gate.reg = clk_base + (u32)(i958_gate.reg);
 
+       /*clk_i958 spdif*/
+       i958_comp_spdif.reg = clk_base + (u32)(i958_comp_spdif.reg);
        /* cts_pclk_mclk */
        pcm_mclk_mux.reg = clk_base + (u32)(pcm_mclk_mux.reg);
        pcm_mclk_div.reg = clk_base + (u32)(pcm_mclk_div.reg);
@@ -343,6 +360,9 @@ void amlogic_init_misc(void)
        clks[CLKID_PCM_SCLK_GATE] = clk_register(NULL, &pcm_sclk_gate.hw);
        WARN_ON(IS_ERR(clks[CLKID_PCM_SCLK_GATE]));
 
+       clks[CLKID_I958_COMP_SPDIF] = clk_register(NULL, &i958_comp_spdif.hw);
+       WARN_ON(IS_ERR(clks[CLKID_I958_COMP_SPDIF]));
+
        pr_info("%s: register meson misc clk\n", __func__);
 };
 
index d0e74e4..89e2b1a 100644 (file)
@@ -333,6 +333,7 @@ static const unsigned int uart_rts_ao_b_pins[]      = { PIN(GPIOAO_3, AO_OFF) };
 static const unsigned int uart_tx_ao_b1_pins[] = { PIN(GPIOAO_4, AO_OFF) };
 static const unsigned int uart_rx_ao_b1_pins[] = { PIN(GPIOAO_5, AO_OFF) };
 static const unsigned int spdif_out_1_pins[]   = { PIN(GPIOAO_6, AO_OFF) };
+static const unsigned int spdif_out_2_pins[]   = { PIN(GPIOAO_13, AO_OFF) };
 
 static const unsigned int i2s_in_ch01_pins[]   = { PIN(GPIOAO_6, AO_OFF) };
 static const unsigned int i2s_ao_clk_in_pins[] = { PIN(GPIOAO_9, AO_OFF) };
@@ -616,7 +617,8 @@ static struct meson_pmx_group meson8b_aobus_groups[] = {
        GROUP(uart_tx_ao_b1,    0,      24),
        GROUP(uart_rx_ao_b1,    0,      23),
        GROUP(spdif_out_1,      0,      16),
-       GROUP(i2s_in_ch01,      0,      13),
+       GROUP(spdif_out_2,      0,      3),
+       GROUP(i2s_in_ch01,      1,      13),
        GROUP(i2s_ao_clk_in,    0,      15),
        GROUP(i2s_lr_clk_in,    0,      14),
 };
@@ -808,6 +810,10 @@ static const char * const spdif_1_groups[] = {
        "spdif_out_1"
 };
 
+static const char * const spdif_2_groups[] = {
+       "spdif_out_2"
+};
+
 static const char * const i2s_groups[] = {
        "i2s_am_clk_out", "i2s_ao_clk_out", "i2s_lr_clk_out",
        "i2s_out_01", "i2s_in_ch01", "i2s_ao_clk_in",
@@ -902,6 +908,7 @@ static struct meson_pmx_func meson8b_aobus_functions[] = {
        FUNCTION(clk_32k),
        FUNCTION(pwm_c_ao),
        FUNCTION(spdif_1),
+       FUNCTION(spdif_2),
        FUNCTION(hdmi_cec),
 };
 
index 8d71d31..d507af3 100644 (file)
 #define CLKID_PCM_MCLK_COMP (CLKID_MISC_BASE + 15)
 #define CLKID_PCM_SCLK_DIV (CLKID_MISC_BASE + 16)
 #define CLKID_PCM_SCLK_GATE (CLKID_MISC_BASE + 17)
+#define CLKID_I958_COMP_SPDIF (CLKID_MISC_BASE + 18)
 
-#define NR_CLKS                164 /*96+4+9+36+18*/
+#define NR_CLKS                165 /*96+4+9+36+18*/
 
 
 #endif /* __MESON8B_CLKC_H */
index 20be66f..cf71284 100644 (file)
@@ -553,7 +553,7 @@ static void aml_pinmux_init(struct snd_soc_card *card)
 #endif
 
        p_aml_audio->pin_ctl = devm_pinctrl_get_select(
-               card->dev, "audio_i2s_pins");
+               card->dev, "aml_audio_i2s");
        if (IS_ERR(p_aml_audio->pin_ctl)) {
                pr_info("%s,aml_pinmux_init error!\n", __func__);
                return;
index 1416c6d..69223e6 100644 (file)
@@ -271,7 +271,7 @@ static int aml_pcm_dai_probe(struct platform_device *pdev)
 
        pr_debug("enter %s\n", __func__);
 
-       pin_ctl = devm_pinctrl_get_select(&pdev->dev, "aml_audio_btpcm");
+       pin_ctl = devm_pinctrl_get_select(&pdev->dev, "aml_audio_pcm");
        if (IS_ERR(pin_ctl)) {
                pin_ctl = NULL;
                pr_err("aml audio pcm dai pinmux set error!\n");
index 2e38b13..4dbe0cf 100644 (file)
@@ -38,4 +38,13 @@ config AMLOGIC_SND_CODEC_AMLT9015
                AMLT9015 codec,
                this codec is internal
 
-#endif #AMLOGIC_SND_SOC_CODECS
\ No newline at end of file
+config AMLOGIC_SND_CODEC_PMU3
+        bool "Amlogic Audio AML PMU3 codec"
+        depends on AMLOGIC_SND_SOC_CODECS
+    default n
+        help
+                Amlogic Audio codec,
+                AML PMU3 codec,
+                AML PMU3 codec,
+                this codec is internal
+#endif #AMLOGIC_SND_SOC_CODECS
index 817b59c..8496871 100644 (file)
@@ -2,8 +2,10 @@
 snd-soc-dummy_codec-objs := dummy_codec.o
 snd-soc-pcm2bt-objs  := pcm2bt.o
 snd-soc-aml_t9015-objs := aml_codec_t9015.o
+snd-soc-pmu3-objs := aml_pmu3.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
+obj-$(CONFIG_AMLOGIC_SND_CODEC_AMLT9015)       += snd-soc-aml_t9015.o
+obj-$(CONFIG_AMLOGIC_SND_CODEC_PMU3) += snd-soc-pmu3.o
diff --git a/sound/soc/codecs/amlogic/aml_pmu3.c b/sound/soc/codecs/amlogic/aml_pmu3.c
new file mode 100644 (file)
index 0000000..8b2b4b1
--- /dev/null
@@ -0,0 +1,984 @@
+/*
+ * aml_pmu3.c  --  AML_PMU3 ALSA Soc Codec driver
+ *
+ * Copyright 2013 AMLOGIC.
+ *
+ * Author: Shuai Li<shuai.li@amlogic.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <linux/amlogic/aml_pmu.h>
+#include "aml_pmu3.h"
+#include <linux/reboot.h>
+#include <linux/notifier.h>
+
+
+static u16 pmu3_reg_defaults[] = {
+       0x0000,    /* R00h      - SW Reset */
+       0x0000, /* R01h - Block Enable1 */
+       0x0000, /* R02h - Block Enable2 */
+       0x0000, /* R03h - PGAIN */
+       0x0000, /* R04h - MIXINL */
+       0x0000, /* R05h - MIXINR */
+       0x0000, /* R06h - MiXOUTL */
+       0x0000, /* R07h - MiXOUTR */
+       0x0000, /* R08h - RXV TO MIXOUT */
+       0x0000, /* R09h - Lineout&HP Driver */
+       0x0000, /* R0Ah - HP DC Offset */
+       0x2000, /* R0Bh - ADC & DAC */
+       0x00A8, /* R0Ch - HP & MIC Detect */
+       0x5050, /* R0Dh - ADC Digital Volume */
+       0xC0C0, /* R0Eh - DAC Digital Volume */
+       0xA000, /* R0Fh - Soft Mute and Unmute */
+       0x0000, /* R10h - Digital Sidetone & Mixing */
+       0x0000, /* R11h - DMIC & GPIO_AUDIO */
+       0x0000, /* R12h  - Monitor register */
+};
+
+static void pmu3_reset(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, PMU3_SOFTWARE_RESET, 0x500);
+       msleep(20);
+}
+
+static int pmu3_adc_event(struct snd_soc_dapm_widget *w,
+                          struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->dapm->component->codec;
+       u16 val = snd_soc_read(codec, PMU3_BLOCK_ENABLE_2);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               val |= 0x2;
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               val &= ~0x2;
+               break;
+
+       default:
+               break;
+       }
+
+       snd_soc_write(codec, PMU3_BLOCK_ENABLE_2, val);
+
+       return 0;
+}
+
+static int pmu3_dac_event(struct snd_soc_dapm_widget *w,
+                          struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->dapm->component->codec;
+       u16 val = snd_soc_read(codec, PMU3_BLOCK_ENABLE_2);
+       unsigned int mask = 1 << w->shift;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               val |= 0x1;
+               val |= mask;
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               /*val &= ~0x1;*/
+               break;
+
+       default:
+               break;
+       }
+
+       snd_soc_write(codec, PMU3_BLOCK_ENABLE_2, val);
+
+       return 0;
+}
+
+static int pmu3_hp_event(struct snd_soc_dapm_widget *w,
+                          struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->dapm->component->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               break;
+
+       case SND_SOC_DAPM_POST_PMU:
+               /* Enable the input stage of HP driver
+                * and start the HP DC offset cancellation process
+                */
+               snd_soc_update_bits(codec, PMU3_BLOCK_ENABLE_2, 0x1800, 0x1800);
+
+               msleep(20);
+
+               /* Finish the HP DC offset cancellation process */
+               snd_soc_update_bits(codec, PMU3_BLOCK_ENABLE_2, 0x1000, 0);
+
+               /* Remove shorting the HP driver
+                * output and enable its output stage
+                */
+               snd_soc_update_bits(codec, PMU3_BLOCK_ENABLE_2, 0x400, 0x400);
+               snd_soc_update_bits(codec, PMU3_LINEOUT_HP_DRV, 0x2, 0x2);
+
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, PMU3_LINEOUT_HP_DRV, 0x2, 0x0);
+               snd_soc_update_bits(codec, PMU3_BLOCK_ENABLE_2, 0x400, 0x0);
+               snd_soc_update_bits(codec, PMU3_BLOCK_ENABLE_2, 0x1800, 0x0);
+
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -4000, 200, 1);
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -3000, 37, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 37, 1);
+static const DECLARE_TLV_DB_SCALE(adc_svol_tlv, -3750, 250, 1);
+
+static const DECLARE_TLV_DB_SCALE(pga_in_tlv, -250, 250, 1);
+static const DECLARE_TLV_DB_SCALE(pga2mixin_tlv, -1600, 150, 1);
+static const DECLARE_TLV_DB_SCALE(xx2mixout_tlv, -5400, 200, 1);
+static const DECLARE_TLV_DB_SCALE(xx2mixin_tlv, -1600, 150, 1);
+
+static const char * const mic_bias_level_txt[] = {
+       "0.72*CPAVDD25", "0.85*CPAVDD25"
+};
+
+/* ADC HPF Mode*/
+static const char * const adc_hpf_mode_txt[] = {
+       "Voice", "Hi-fi" };
+
+static const char * const lr_txt[] = {
+       "Left", "Right" };
+
+static const char * const rl_txt[] = {
+       "Right", "Left" };
+
+static const char * const sidetone_txt[] = {
+       "Disabled", "Left ADC", "Right ADC" };
+
+static const char * const dac_mute_rate_txt[] = { "Fast", "Slow" };
+
+static const char * const pmu3_lol1_in_texts[] = {
+       "Off", "MIXOUTL", "MIXOUTR", "Inverted MIXOUTR"
+};
+
+static const char * const pmu3_lol2_in_texts[] = {
+       "Off", "MIXOUTL", "Inverted MIXOUTL", "Inverted MIXOUTR"
+};
+
+static const char * const pmu3_lor1_in_texts[] = {
+       "Off", "MIXOUTR", "MIXOUTL", "Inverted MIXOUTL"
+};
+
+static const char * const pmu3_lor2_in_texts[] = {
+       "Off", "MIXOUTR", "Inverted MIXOUTR", "Inverted MIXOUTL"
+};
+
+static const struct soc_enum pmu3_lineout_enum[] = {
+       SOC_ENUM_SINGLE(PMU3_LINEOUT_HP_DRV, 14,
+                       ARRAY_SIZE(pmu3_lol1_in_texts),
+                       pmu3_lol1_in_texts),
+       SOC_ENUM_SINGLE(PMU3_LINEOUT_HP_DRV, 12,
+                       ARRAY_SIZE(pmu3_lol2_in_texts),
+                       pmu3_lol2_in_texts),
+       SOC_ENUM_SINGLE(PMU3_LINEOUT_HP_DRV, 10,
+                       ARRAY_SIZE(pmu3_lor1_in_texts),
+                       pmu3_lor1_in_texts),
+       SOC_ENUM_SINGLE(PMU3_LINEOUT_HP_DRV, 8,
+                       ARRAY_SIZE(pmu3_lor2_in_texts),
+                       pmu3_lor2_in_texts),
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+       mic_bias1_enum, PMU3_HP_MIC_DET,
+       PMU3_MIC_BIAS1_SHIFT, mic_bias_level_txt);
+
+static const SOC_ENUM_SINGLE_DECL(
+       mic_bias2_enum, PMU3_HP_MIC_DET,
+       PMU3_MIC_BIAS2_SHIFT, mic_bias_level_txt);
+
+static const SOC_ENUM_SINGLE_DECL(
+       adc_hpf_mode, PMU3_ADC_DAC,
+       PMU3_ADC_HPF_MODE_SHIFT, adc_hpf_mode_txt);
+static const SOC_ENUM_SINGLE_DECL(
+       aifl_src, PMU3_SIDETONE_MIXING,
+       PMU3_ADCDATL_SRC_SHIFT, lr_txt);
+static const SOC_ENUM_SINGLE_DECL(
+       aifr_src, PMU3_SIDETONE_MIXING,
+       PMU3_ADCDATR_SRC_SHIFT, rl_txt);
+static const SOC_ENUM_SINGLE_DECL(
+       dacl_src, PMU3_SIDETONE_MIXING,
+       PMU3_DACDATL_SRC_SHIFT, lr_txt);
+static const SOC_ENUM_SINGLE_DECL(
+       dacr_src, PMU3_SIDETONE_MIXING,
+       PMU3_DACDATR_SRC_SHIFT, rl_txt);
+static const SOC_ENUM_SINGLE_DECL(
+       dacl_sidetone, PMU3_SIDETONE_MIXING,
+       PMU3_DACL_ST_SRC_SHIFT, sidetone_txt);
+static const SOC_ENUM_SINGLE_DECL(
+       dacr_sidetone, PMU3_SIDETONE_MIXING,
+       PMU3_DACR_ST_SRC_SHIFT, sidetone_txt);
+
+static const SOC_ENUM_SINGLE_DECL(
+       dac_mute_rate, PMU3_SOFT_MUTE,
+       PMU3_DAC_RAMP_RATE_SHIFT, dac_mute_rate_txt);
+
+static const struct snd_kcontrol_new pmu3_snd_controls[] = {
+       /* MIC */
+       SOC_ENUM("Mic Bias1 Level", mic_bias1_enum),
+       SOC_ENUM("Mic Bias2 Level", mic_bias2_enum),
+
+       SOC_SINGLE_TLV("PGAIN Left Gain", PMU3_PGA_IN, 12, 0xf, 0, pga_in_tlv),
+       SOC_SINGLE_TLV("PGAIN Right Gain", PMU3_PGA_IN, 4, 0xf, 0, pga_in_tlv),
+
+       /* ADC */
+       SOC_SINGLE_TLV("Left ADC Sidetone Volume",
+                               PMU3_SIDETONE_MIXING, 12, 0xf, 0,
+                               adc_svol_tlv),
+       SOC_SINGLE_TLV("Right ADC Sidetone Volume",
+                               PMU3_SIDETONE_MIXING, 8, 0xf, 0,
+                               adc_svol_tlv),
+
+       SOC_DOUBLE_TLV("Digital Capture Volume",
+                       PMU3_ADC_VOLUME_CTL, 8, 0, 0x7f, 0, adc_tlv),
+
+       SOC_SINGLE("ADC HPF Switch", PMU3_ADC_DAC, 11, 1, 0),
+       SOC_ENUM("ADC HPF Mode", adc_hpf_mode),
+
+       SOC_ENUM("Left Digital Audio Source", aifl_src),
+       SOC_ENUM("Right Digital Audio Source", aifr_src),
+       SOC_SINGLE_TLV("DACL to MIXOUTL Volume", PMU3_MIXOUT_L, 11, 0x1f, 0,
+                          xx2mixout_tlv),
+       SOC_SINGLE_TLV("DACR to MIXOUTR Volume", PMU3_MIXOUT_R, 11, 0x1f, 0,
+                          xx2mixout_tlv),
+
+       SOC_ENUM("DACL DATA Source", dacl_src),
+       SOC_ENUM("DACR DATA Source", dacr_src),
+       SOC_ENUM("DACL Sidetone Source", dacl_sidetone),
+       SOC_ENUM("DACR Sidetone Source", dacr_sidetone),
+       SOC_DOUBLE("DAC Invert Switch", PMU3_SOFT_MUTE, 8, 7, 1, 0),
+       SOC_DOUBLE("ADC Invert Switch", PMU3_SOFT_MUTE, 10, 9, 1, 0),
+
+       SOC_SINGLE("DAC Soft Mute Switch", PMU3_SOFT_MUTE, 15, 1, 0),
+       SOC_ENUM("DAC Mute Rate", dac_mute_rate),
+
+       SOC_DOUBLE_TLV("Digital Playback Volume",
+                        PMU3_DAC_VOLUME_CTL, 8, 0, 0xff, 0, dac_tlv),
+};
+
+static const struct snd_kcontrol_new pmu3_dapm_mixer_out_l_controls[] = {
+       SOC_DAPM_SINGLE_TLV("DACL to MIXOUTL Volume",
+               PMU3_MIXOUT_L, 11, 0x1f, 0, xx2mixout_tlv),
+       SOC_DAPM_SINGLE_TLV("PGAINL to MIXOUTL Volume",
+               PMU3_MIXOUT_L, 6, 0x1f, 0, xx2mixout_tlv),
+       SOC_DAPM_SINGLE_TLV("PGAINR to MIXOUTL Volume",
+               PMU3_MIXOUT_L, 1, 0x1f, 0, xx2mixout_tlv),
+       SOC_DAPM_SINGLE_TLV("RXV to MIXOUTL Volume",
+               PMU3_RXV_TO_MIXOUT, 11, 0x1f, 0, xx2mixout_tlv),
+       SOC_DAPM_SINGLE("DACR Mono Switch",
+               PMU3_SIDETONE_MIXING, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new pmu3_dapm_mixer_out_r_controls[] = {
+       SOC_DAPM_SINGLE_TLV("DACR to MIXOUTR Volume",
+               PMU3_MIXOUT_R, 11, 0x1f, 0, xx2mixout_tlv),
+       SOC_DAPM_SINGLE_TLV("PGAINR to MIXOUTR Volume",
+               PMU3_MIXOUT_R, 6, 0x1f, 0, xx2mixout_tlv),
+       SOC_DAPM_SINGLE_TLV("PGAINL to MIXOUTR Volume",
+               PMU3_MIXOUT_R, 1, 0x1f, 0, xx2mixout_tlv),
+       SOC_DAPM_SINGLE_TLV("RXV to MIXOUTR Volume",
+               PMU3_RXV_TO_MIXOUT, 6, 0x1f, 0, xx2mixout_tlv),
+       SOC_DAPM_SINGLE("DACL Mono Switch",
+               PMU3_SIDETONE_MIXING, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new pmu3_dapm_mixer_in_l_controls[] = {
+SOC_DAPM_SINGLE_TLV("PGAINL to MIXINL Volume", PMU3_MIXIN_L, 12, 0xf, 0,
+                          xx2mixin_tlv),
+SOC_DAPM_SINGLE_TLV("RXV to MIXINL Volume", PMU3_MIXIN_L, 8, 0xf, 0,
+                          xx2mixin_tlv),
+SOC_DAPM_SINGLE_TLV("RECL to MIXINL Volume", PMU3_MIXIN_L, 4, 0xf, 0,
+                          xx2mixin_tlv),
+};
+static const struct snd_kcontrol_new pmu3_dapm_mixer_in_r_controls[] = {
+SOC_DAPM_SINGLE_TLV("PGAINR to MIXINR Volume", PMU3_MIXIN_R, 12, 0xf, 0,
+                          xx2mixin_tlv),
+SOC_DAPM_SINGLE_TLV("RXV to MIXINR Volume", PMU3_MIXIN_R, 8, 0xf, 0,
+                          xx2mixin_tlv),
+SOC_DAPM_SINGLE_TLV("RECR to MIXINR Volume", PMU3_MIXIN_R, 4, 0xf, 0,
+                          xx2mixin_tlv),
+};
+
+static const struct snd_kcontrol_new lol1_mux_controls =
+       SOC_DAPM_ENUM("Route", pmu3_lineout_enum[0]);
+
+static const struct snd_kcontrol_new lol2_mux_controls =
+       SOC_DAPM_ENUM("Route", pmu3_lineout_enum[1]);
+
+static const struct snd_kcontrol_new lor1_mux_controls =
+       SOC_DAPM_ENUM("Route", pmu3_lineout_enum[2]);
+
+static const struct snd_kcontrol_new lor2_mux_controls =
+       SOC_DAPM_ENUM("Route", pmu3_lineout_enum[3]);
+
+static const struct snd_kcontrol_new pmu3_dapm_hpin_mixer_l_controls[] = {
+       SOC_DAPM_SINGLE("DACL Switch", PMU3_LINEOUT_HP_DRV, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIXOUTL Switch", PMU3_LINEOUT_HP_DRV, 6, 1, 0),
+       SOC_DAPM_SINGLE("MIXOUTR Switch", PMU3_LINEOUT_HP_DRV, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new pmu3_dapm_hpin_mixer_r_controls[] = {
+       SOC_DAPM_SINGLE("DACR Switch", PMU3_LINEOUT_HP_DRV, 4, 1, 0),
+       SOC_DAPM_SINGLE("MIXOUTR Switch", PMU3_LINEOUT_HP_DRV, 3, 1, 0),
+       SOC_DAPM_SINGLE("MIXOUTL Switch", PMU3_LINEOUT_HP_DRV, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new pgain_lp_switch_controls =
+       SOC_DAPM_SINGLE("Switch", PMU3_PGA_IN, 10, 1, 0);
+
+static const struct snd_kcontrol_new pgain_rp_switch_controls =
+       SOC_DAPM_SINGLE("Switch", PMU3_PGA_IN, 2, 1, 0);
+
+static const char * const plin_text[] = { "None", "AINL2", "AINL1" };
+static const struct soc_enum pgainl_enum =
+       SOC_ENUM_SINGLE(PMU3_PGA_IN, 8, ARRAY_SIZE(plin_text), plin_text);
+static const struct snd_kcontrol_new pgain_ln_mux[] = {
+       SOC_DAPM_ENUM("route", pgainl_enum),
+};
+static const char * const prin_text[] = { "None", "AINR2", "AINR1" };
+static const struct soc_enum pgainr_enum =
+       SOC_ENUM_SINGLE(PMU3_PGA_IN, 0, ARRAY_SIZE(prin_text), prin_text);
+static const struct snd_kcontrol_new pgain_rn_mux[] = {
+       SOC_DAPM_ENUM("route", pgainr_enum),
+};
+
+static const struct snd_soc_dapm_widget pmu3_dapm_widgets[] = {
+       /* Externally visible pins */
+       SND_SOC_DAPM_OUTPUT("LINEOUTL1"),
+       SND_SOC_DAPM_OUTPUT("LINEOUTR1"),
+       SND_SOC_DAPM_OUTPUT("LINEOUTL2"),
+       SND_SOC_DAPM_OUTPUT("LINEOUTR2"),
+       SND_SOC_DAPM_OUTPUT("HP_L"),
+       SND_SOC_DAPM_OUTPUT("HP_R"),
+       /* SND_SOC_DAPM_OUTPUT("AUXOUT"), */
+       /* SND_SOC_DAPM_INPUT("RXV"), */
+
+       SND_SOC_DAPM_INPUT("LINEINLP"),
+       SND_SOC_DAPM_INPUT("LINEINLN1"),
+       SND_SOC_DAPM_INPUT("LINEINLN2"),
+       SND_SOC_DAPM_INPUT("LINEINRP"),
+       SND_SOC_DAPM_INPUT("LINEINRN1"),
+       SND_SOC_DAPM_INPUT("LINEINRN2"),
+
+       /* Input */
+       SND_SOC_DAPM_PGA("PGAIN LEFT", PMU3_BLOCK_ENABLE_1, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("PGAIN RIGHT", PMU3_BLOCK_ENABLE_1, 4, 0, NULL, 0),
+       SND_SOC_DAPM_SWITCH("PGAIN LEFT Pos", SND_SOC_NOPM, 0, 0,
+                               &pgain_lp_switch_controls),
+       SND_SOC_DAPM_SWITCH("PGAIN RIGHT Pos", SND_SOC_NOPM, 0, 0,
+                               &pgain_rp_switch_controls),
+       SND_SOC_DAPM_MUX("PGAIN LEFT Neg", SND_SOC_NOPM, 0, 0, pgain_ln_mux),
+       SND_SOC_DAPM_MUX("PGAIN RIGHT Neg", SND_SOC_NOPM, 0, 0, pgain_rn_mux),
+
+       SND_SOC_DAPM_MIXER("MIXINL", PMU3_BLOCK_ENABLE_1, 7, 0,
+                          &pmu3_dapm_mixer_in_l_controls[0],
+                          ARRAY_SIZE(pmu3_dapm_mixer_in_l_controls)),
+       SND_SOC_DAPM_MIXER("MIXINR", PMU3_BLOCK_ENABLE_1, 6, 0,
+                          &pmu3_dapm_mixer_in_r_controls[0],
+                          ARRAY_SIZE(pmu3_dapm_mixer_in_r_controls)),
+
+       SND_SOC_DAPM_MICBIAS("Mic Bias1", PMU3_BLOCK_ENABLE_2, 15, 0),
+       SND_SOC_DAPM_MICBIAS("Mic Bias2", PMU3_BLOCK_ENABLE_2, 14, 0),
+
+       SND_SOC_DAPM_ADC_E("ADCL", "Capture", PMU3_BLOCK_ENABLE_2, 5, 0,
+                               pmu3_adc_event,
+                               SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_ADC_E("ADCR", "Capture", PMU3_BLOCK_ENABLE_2, 4, 0,
+                               pmu3_adc_event,
+                               SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+
+       /* Output */
+       SND_SOC_DAPM_DAC_E("DACL", "Playback", SND_SOC_NOPM, 3, 0,
+                               pmu3_dac_event,
+                               SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_DAC_E("DACR", "Playback", SND_SOC_NOPM, 2, 0,
+                               pmu3_dac_event,
+                               SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MIXER("MIXOUTL", PMU3_BLOCK_ENABLE_1, 3, 0,
+                          &pmu3_dapm_mixer_out_l_controls[0],
+                          ARRAY_SIZE(pmu3_dapm_mixer_out_l_controls)),
+       SND_SOC_DAPM_MIXER("MIXOUTR", PMU3_BLOCK_ENABLE_1, 2, 0,
+                          &pmu3_dapm_mixer_out_r_controls[0],
+                          ARRAY_SIZE(pmu3_dapm_mixer_out_r_controls)),
+
+       SND_SOC_DAPM_MUX("Lineout1 Left in",
+                               SND_SOC_NOPM, 0, 0, &lol1_mux_controls),
+       SND_SOC_DAPM_MUX("Lineout2 Left in",
+                               SND_SOC_NOPM, 0, 0, &lol2_mux_controls),
+       SND_SOC_DAPM_MUX("Lineout1 Right in",
+                               SND_SOC_NOPM, 0, 0, &lor1_mux_controls),
+       SND_SOC_DAPM_MUX("Lineout2 Right in",
+                               SND_SOC_NOPM, 0, 0, &lor2_mux_controls),
+
+       SND_SOC_DAPM_MIXER("HPINL Mixer", SND_SOC_NOPM, 0, 0,
+                          &pmu3_dapm_hpin_mixer_l_controls[0],
+                          ARRAY_SIZE(pmu3_dapm_hpin_mixer_l_controls)),
+
+       SND_SOC_DAPM_MIXER("HPINR Mixer", SND_SOC_NOPM, 0, 0,
+                          &pmu3_dapm_hpin_mixer_r_controls[0],
+                          ARRAY_SIZE(pmu3_dapm_hpin_mixer_r_controls)),
+
+       SND_SOC_DAPM_OUT_DRV("LINEOUT1 Left driver",
+               PMU3_BLOCK_ENABLE_1, 11, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LINEOUT2 Left driver",
+               PMU3_BLOCK_ENABLE_1, 10, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LINEOUT1 Right driver",
+               PMU3_BLOCK_ENABLE_1, 9, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("LINEOUT2 Right driver",
+               PMU3_BLOCK_ENABLE_1, 8, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA_E("Headphone Amplifier",
+               PMU3_BLOCK_ENABLE_2, 13, 0, NULL, 0,
+               pmu3_hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA("RXV", SND_SOC_NOPM, 8, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route pmu3_intercon[] = {
+       /* Inputs */
+       {"PGAIN LEFT Pos", "Switch", "LINEINLP"},
+       {"PGAIN LEFT Neg", "AINL1", "LINEINLN1"},
+       {"PGAIN LEFT Neg", "AINL2", "LINEINLN2"},
+
+       {"PGAIN RIGHT Pos", "Switch", "LINEINRP"},
+       {"PGAIN RIGHT Neg", "AINR1", "LINEINRN1"},
+       {"PGAIN RIGHT Neg", "AINR2", "LINEINRN2"},
+
+       {"PGAIN LEFT", NULL, "PGAIN LEFT Pos"},
+       {"PGAIN LEFT", NULL, "PGAIN LEFT Neg"},
+
+       {"PGAIN RIGHT", NULL, "PGAIN RIGHT Pos"},
+       {"PGAIN RIGHT", NULL, "PGAIN RIGHT Neg"},
+
+       {"RXV", NULL, "LINEINLN2"},
+       {"RXV", NULL, "LINEINRN2"},
+
+       {"MIXINL", "PGAINL to MIXINL Volume", "PGAIN LEFT"},
+       {"MIXINL", "RXV to MIXINL Volume", "RXV"},
+       {"MIXINL", "RECL to MIXINL Volume", "MIXOUTL"},
+
+       {"MIXINR", "PGAINR to MIXINR Volume", "PGAIN RIGHT"},
+       {"MIXINR", "RXV to MIXINR Volume", "RXV"},
+       {"MIXINR", "RECR to MIXINR Volume", "MIXOUTR"},
+
+       {"ADCL", NULL, "MIXINL"},
+       {"ADCR", NULL, "MIXINR"},
+
+       /* Outputs */
+       /* LINEOUTL1 */
+       {"LINEOUTL1", NULL, "LINEOUT1 Left driver"},
+       {"LINEOUT1 Left driver", NULL, "Lineout1 Left in"},
+
+       {"Lineout1 Left in", "MIXOUTL", "MIXOUTL"},
+       {"Lineout1 Left in", "MIXOUTR", "MIXOUTR"},
+       {"Lineout1 Left in", "Inverted MIXOUTR", "MIXOUTR"},
+
+       /* LINEOUTR1 */
+       {"LINEOUTR1", NULL, "LINEOUT1 Right driver"},
+       {"LINEOUT1 Right driver", NULL, "Lineout1 Right in"},
+
+       {"Lineout1 Right in", "MIXOUTR", "MIXOUTL"},
+       {"Lineout1 Right in", "MIXOUTL", "MIXOUTR"},
+       {"Lineout1 Right in", "Inverted MIXOUTL", "MIXOUTL"},
+
+       /* LINEOUTL2 */
+       {"LINEOUTL2", NULL, "LINEOUT2 Left driver"},
+       {"LINEOUT2 Left driver", NULL, "Lineout2 Left in"},
+
+       {"Lineout2 Left in", "MIXOUTL", "MIXOUTL"},
+       {"Lineout2 Left in", "Inverted MIXOUTL", "MIXOUTL"},
+       {"Lineout2 Left in", "Inverted MIXOUTR", "MIXOUTR"},
+
+       /* LINEOUTR2 */
+       {"LINEOUTR2", NULL, "LINEOUT2 Right driver"},
+       {"LINEOUT2 Right driver", NULL, "Lineout2 Right in"},
+
+       {"Lineout2 Right in", "MIXOUTR", "MIXOUTR"},
+       {"Lineout2 Right in", "Inverted MIXOUTR", "MIXOUTR"},
+       {"Lineout2 Right in", "Inverted MIXOUTL", "MIXOUTL"},
+
+       /* MIXOUT */
+       {"MIXOUTL", "DACL to MIXOUTL Volume", "DACL"},
+       {"MIXOUTL", "PGAINL to MIXOUTL Volume", "PGAIN LEFT"},
+       {"MIXOUTL", "PGAINR to MIXOUTL Volume", "PGAIN RIGHT"},
+       {"MIXOUTL", "RXV to MIXOUTL Volume", "RXV"},
+       {"MIXOUTL", "DACR Mono Switch", "DACR"},
+
+       {"MIXOUTR", "DACR to MIXOUTR Volume", "DACR"},
+       {"MIXOUTR", "PGAINR to MIXOUTR Volume", "PGAIN RIGHT"},
+       {"MIXOUTR", "PGAINL to MIXOUTR Volume", "PGAIN LEFT"},
+       {"MIXOUTR", "RXV to MIXOUTR Volume", "RXV"},
+       {"MIXOUTR", "DACL Mono Switch", "DACL"},
+
+       /* Headphone */
+
+       {"HPINL Mixer", "DACL Switch", "DACL"},
+       {"HPINL Mixer", "MIXOUTL Switch", "MIXOUTL"},
+       {"HPINL Mixer", "MIXOUTR Switch", "MIXOUTR"},
+
+       {"HPINR Mixer", "DACR Switch", "DACR"},
+       {"HPINR Mixer", "MIXOUTR Switch", "MIXOUTR"},
+       {"HPINR Mixer", "MIXOUTL Switch", "MIXOUTL"},
+
+       {"Headphone Amplifier", NULL, "HPINL Mixer"},
+       {"Headphone Amplifier", NULL, "HPINR Mixer"},
+       {"HP_L", NULL, "Headphone Amplifier"},
+       {"HP_R", NULL, "Headphone Amplifier"},
+};
+
+static int pmu3_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       int value = 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               if (codec->component.dapm.bias_level ==
+                       SND_SOC_BIAS_STANDBY) {
+                       value = snd_soc_read(codec,
+                               PMU3_SIDETONE_MIXING);
+                       if (value & 0x20)
+                               snd_soc_write(codec,
+                                       PMU3_BLOCK_ENABLE_1, 0xbc00);
+                       else
+                               snd_soc_write(codec,
+                                       PMU3_BLOCK_ENABLE_1, 0xbf00);
+               }
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->component.dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       /* VMID Gen & Bias Current & Refp Buf */
+                       snd_soc_update_bits(codec,
+                                       PMU3_BLOCK_ENABLE_1,
+                                       0xf000, 0xf000);
+                       msleep(200);
+                       /* Clear Fast Charge */
+                       snd_soc_update_bits(codec,
+                                       PMU3_BLOCK_ENABLE_1,
+                                       0x4000, 0x0);
+                       value = snd_soc_read(codec,
+                                       PMU3_SIDETONE_MIXING);
+                       if (value & 0x20)
+                               snd_soc_write(codec,
+                                       PMU3_BLOCK_ENABLE_1, 0xbc00);
+                       else
+                               snd_soc_write(codec,
+                                       PMU3_BLOCK_ENABLE_1, 0xbf00);
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_write(codec, PMU3_BLOCK_ENABLE_1, 0);
+
+               break;
+       }
+
+       codec->component.dapm.bias_level = level;
+
+       return 0;
+}
+
+static int pmu3_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                        int clk_id, unsigned int freq, int dir)
+{
+       return 0;
+}
+
+
+static int pmu3_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 iface = 0;
+
+       iface = snd_soc_read(codec, PMU3_ADC_DAC);
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               iface |= (0x1 << 14);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       }
+
+       snd_soc_write(codec, PMU3_ADC_DAC, iface);
+
+       return 0;
+}
+
+static int pmu3_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 reg;
+
+       reg = snd_soc_read(codec, PMU3_SOFT_MUTE);
+
+       if (mute)
+               reg |= 0x8000;
+       else
+               reg &= ~0x8000;
+
+       snd_soc_write(codec, PMU3_SOFT_MUTE, reg);
+
+       return 0;
+}
+
+static int pmu3_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, PMU3_SOFT_MUTE);
+               if (mute)
+                       reg |= 0x8000;
+               else
+                       reg &= ~0x8000;
+               snd_soc_write(codec, PMU3_SOFT_MUTE, reg);
+       }
+       if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+               if (mute)
+                       snd_soc_write(codec, PMU3_ADC_VOLUME_CTL, 0);
+               else {
+                       msleep(300);
+                       snd_soc_write(codec, PMU3_ADC_VOLUME_CTL, 0x6a6a);
+               }
+       }
+       return 0;
+}
+
+
+static struct {
+       int rate;
+       int value;
+} sample_rates[] = {
+       {  8000,  0 },
+       { 11025,  1 },
+       { 12000,  2 },
+       { 16000,  3 },
+       { 22050,  4 },
+       { 24000,  5 },
+       { 32000,  6 },
+       { 44100,  7 },
+       { 48000,  8 },
+};
+
+static int pmu3_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;
+       int fs = params_rate(params);
+       u16 reg = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(sample_rates); i++) {
+               if (sample_rates[i].rate == fs)
+                       reg = sample_rates[i].value;
+       }
+       pr_info("pmu3_hw_params(rate)%x\n", reg);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_soc_update_bits(codec, PMU3_ADC_DAC, 0xf0, reg << 4);
+       else
+               snd_soc_update_bits(codec, PMU3_ADC_DAC, 0xf, reg);
+
+       return 0;
+}
+
+#define AML_PMU3_PLAYBACK_RATES SNDRV_PCM_RATE_8000_48000
+#define AML_PMU3_CAPTURE_RATES SNDRV_PCM_RATE_8000_48000
+#define AML_PMU3_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+                               | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops pmu3_dai_ops = {
+       .hw_params      = pmu3_hw_params,
+       .digital_mute   = pmu3_digital_mute,
+       .mute_stream = pmu3_mute_stream,
+       .set_fmt        = pmu3_set_dai_fmt,
+       .set_sysclk     = pmu3_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver pmu3_dai = {
+       .name = "pmu3-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = AML_PMU3_PLAYBACK_RATES,
+               .formats = AML_PMU3_FORMATS,
+       },
+       .capture = {
+                .stream_name = "Capture",
+                .channels_min = 2,
+                .channels_max = 2,
+                .rates = AML_PMU3_CAPTURE_RATES,
+                .formats = AML_PMU3_FORMATS,
+        },
+       .ops = &pmu3_dai_ops,
+       .symmetric_rates = 1,
+};
+
+static int pmu3_suspend(struct snd_soc_codec *codec)
+{
+       pmu3_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int pmu3_resume(struct snd_soc_codec *codec)
+{
+       snd_soc_cache_sync(codec);
+
+       /* Bring the codec back up to standby
+        * first to minimise pop/clicks
+        */
+       pmu3_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+static int pmu3_audio_power_init(struct snd_soc_codec *codec)
+{
+       uint8_t val = 0;
+
+       // LDO1v8 for audio
+       aml1218_read(0x010e, &val);
+       val |= 0x1;
+       aml1218_write(0x010e, val);
+       // LDO2v5 for audio
+       aml1218_write(0x0139, 0x1f);
+       val = 0;
+       aml1218_read(0x010f, &val);
+       val |= 0x1;
+       aml1218_write(0x010f, val);
+
+       return 0;
+}
+
+static int pmu3_write(struct snd_soc_codec *codec, unsigned int reg,
+                                                       unsigned int value)
+{
+       uint32_t addr;
+
+       addr = PMU3_AUDIO_BASE + (reg<<1);
+       pr_info("pmu3_write,addr=0x%x, reg=0x%x, value=0x%x\n",
+               addr,
+               reg,
+               value);
+       aml1218_write16(addr, value);
+
+       return 0;
+}
+
+static unsigned int pmu3_read(struct snd_soc_codec *codec,
+                                                       unsigned int reg)
+{
+       uint32_t addr;
+       uint16_t val = 0;
+
+       addr = PMU3_AUDIO_BASE + (reg<<1);
+       aml1218_read16(addr, &val);
+
+       return val;
+}
+
+
+static int pmu3_probe(struct snd_soc_codec *codec)
+{
+       int ret = 0;
+
+       pr_info("aml pmu3 codec probe\n");
+       pmu3_audio_power_init(codec);
+       pmu3_reset(codec);
+       /* power on device */
+       pmu3_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Enable DAC soft mute by default */
+       snd_soc_update_bits(codec, PMU3_SOFT_MUTE, 0x7800, 0x7800);
+
+       /* Enable ZC */
+       snd_soc_update_bits(codec, PMU3_BLOCK_ENABLE_2, 0x3c0, 0x0);
+
+       snd_soc_add_codec_controls(codec, pmu3_snd_controls,
+                               ARRAY_SIZE(pmu3_snd_controls));
+       /* ADC high pass filter Hi-fi mode */
+       snd_soc_update_bits(codec, PMU3_ADC_DAC, 0xc00, 0xc00);
+
+       snd_soc_write(codec, PMU3_MIXOUT_L, 0xe000);
+       snd_soc_write(codec, PMU3_MIXOUT_R, 0xe000);
+       snd_soc_write(codec, PMU3_MIXIN_L, 0xf000);
+       snd_soc_write(codec, PMU3_MIXIN_R, 0xf000);
+       snd_soc_write(codec, PMU3_PGA_IN, 0x1616);
+
+
+       snd_soc_update_bits(codec, PMU3_BLOCK_ENABLE_1, 0xf0c, 0xf0c);
+       snd_soc_update_bits(codec, PMU3_BLOCK_ENABLE_2, 0xf, 0xf);
+
+       snd_soc_update_bits(codec, PMU3_LINEOUT_HP_DRV, 0x90, 0x90);
+       snd_soc_write(codec, PMU3_DAC_VOLUME_CTL, 0xb9b9);
+       snd_soc_write(codec, PMU3_ADC_VOLUME_CTL, 0x6a6a);
+
+       return ret;
+}
+
+/* power down chip */
+static int pmu3_remove(struct snd_soc_codec *codec)
+{
+       pmu3_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_pmu3 = {
+       .probe =        pmu3_probe,
+       .remove =       pmu3_remove,
+       .suspend =      pmu3_suspend,
+       .resume =       pmu3_resume,
+       .read = pmu3_read,
+       .write = pmu3_write,
+       .set_bias_level = pmu3_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(pmu3_reg_defaults),
+       .reg_word_size = sizeof(u16),
+       .reg_cache_step = 1,
+       .reg_cache_default = pmu3_reg_defaults,
+       .component_driver = {
+               .dapm_widgets = pmu3_dapm_widgets,
+               .num_dapm_widgets = ARRAY_SIZE(pmu3_dapm_widgets),
+               .dapm_routes = pmu3_intercon,
+               .num_dapm_routes = ARRAY_SIZE(pmu3_intercon),
+       }
+};
+
+
+static int pmu3_audio_codec_mute(void)
+{
+       uint32_t addr;
+       unsigned int value = 0x8000;
+       unsigned int reg = PMU3_SOFT_MUTE;
+
+       pr_info("pmu3_audio_codec_mute\n");
+
+       addr = PMU3_AUDIO_BASE + (reg<<1);
+
+       aml1218_write16(addr, value);
+
+       return 0;
+}
+
+
+
+static int aml_pmu3_audio_reboot_work(struct notifier_block *nb,
+       unsigned long state, void *cmd)
+{
+       pr_info("\n%s\n", __func__);
+
+       pmu3_audio_codec_mute();
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block aml_pmu3_audio_reboot_nb = {
+       .notifier_call    = aml_pmu3_audio_reboot_work,
+       .priority = 0,
+};
+
+static int aml_pmu3_codec_probe(struct platform_device *pdev)
+{
+       int ret = snd_soc_register_codec(&pdev->dev,
+               &soc_codec_dev_pmu3, &pmu3_dai, 1);
+
+       register_reboot_notifier(&aml_pmu3_audio_reboot_nb);
+
+       return ret;
+}
+
+
+
+static int aml_pmu3_codec_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_USE_OF
+static const struct of_device_id amlogic_pmu3_codec_dt_match[] = {
+       { .compatible = "amlogic, aml_pmu3_codec", },
+       {},
+};
+#else
+#define amlogic_audio_dt_match NULL
+#endif
+static struct platform_driver aml_pmu3_codec_platform_driver = {
+       .driver = {
+               .name = "aml_pmu3_codec",
+               .owner = THIS_MODULE,
+               .of_match_table = amlogic_pmu3_codec_dt_match,
+       },
+       .probe = aml_pmu3_codec_probe,
+       .remove = aml_pmu3_codec_remove,
+};
+
+static int __init aml_pmu3_modinit(void)
+{
+       int ret = 0;
+
+       ret = platform_driver_register(&aml_pmu3_codec_platform_driver);
+       if (ret != 0) {
+               pr_err("Failed to register AML PMU3 codec platform driver: %d\n",
+                      ret);
+       }
+
+       return ret;
+}
+module_init(aml_pmu3_modinit);
+
+static void __exit aml_pmu3_exit(void)
+{
+       platform_driver_unregister(&aml_pmu3_codec_platform_driver);
+}
+module_exit(aml_pmu3_exit);
+
+MODULE_DESCRIPTION("ASoC AML_PUM3 driver");
+MODULE_AUTHOR("Shuai Li <Shuai.li@amlogic.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/codecs/amlogic/aml_pmu3.h b/sound/soc/codecs/amlogic/aml_pmu3.h
new file mode 100644 (file)
index 0000000..77b8660
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * aml_pmu3.h  --  AML PMU3 Soc Audio codec driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _AML_PMU3_H
+#define _AML_PMU3_H
+
+#define PMU3_ADDR                              0x35
+#define PMU3_AUDIO_BASE                        0x220
+
+#define PMU3_SOFTWARE_RESET            0x0
+#define PMU3_BLOCK_ENABLE_1            0x1
+#define PMU3_BLOCK_ENABLE_2            0x2
+#define PMU3_PGA_IN                            0x3
+#define PMU3_MIXIN_L                   0x4
+#define PMU3_MIXIN_R                   0x5
+#define PMU3_MIXOUT_L                  0x6
+#define PMU3_MIXOUT_R                  0x7
+#define PMU3_RXV_TO_MIXOUT             0x8
+#define PMU3_LINEOUT_HP_DRV            0x9
+#define PMU3_HP_DC_OFFSET              0xA
+#define PMU3_ADC_DAC                   0xB
+#define PMU3_HP_MIC_DET                        0xC
+#define PMU3_ADC_VOLUME_CTL            0xD
+#define PMU3_DAC_VOLUME_CTL            0xE
+#define PMU3_SOFT_MUTE                 0xF
+#define PMU3_SIDETONE_MIXING   0x10
+#define PMU3_DMIC_GPIO                 0x11
+#define PMU3_MONITOR_REG               0x12
+
+/* R0Ch PMU3_HP_MIC_DET */
+#define PMU3_MIC_BIAS1_SHIFT           15
+#define PMU3_MIC_BIAS2_SHIFT           14
+
+/* R0Bh PMU3_ADC_DAC */
+#define PMU3_ADC_HPF_MODE_SHIFT                10
+
+/* R0FH PMU3_SOFT_MUTE */
+#define PMU3_DAC_RAMP_RATE_SHIFT               11
+
+/* R10h PMU3_SIDETONE_MIXING */
+#define PMU3_DACL_ST_SRC_SHIFT         7
+#define PMU3_DACR_ST_SRC_SHIFT         6
+#define PMU3_ADCDATL_SRC_SHIFT         4
+#define PMU3_ADCDATR_SRC_SHIFT         3
+#define PMU3_DACDATL_SRC_SHIFT         2
+#define PMU3_DACDATR_SRC_SHIFT         1
+
+#endif