[9610] ASoC: abox: Added abox driver for 4.14 kernel
authorShinHyung <s47.kang@samsung.com>
Fri, 18 May 2018 09:17:00 +0000 (18:17 +0900)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:22:29 +0000 (20:22 +0300)
Change-Id: I4dff3f2d9254e54f581ad6fd0d5567ed16aaa980
Signed-off-by: ShinHyung <s47.kang@samsung.com>
58 files changed:
arch/arm64/boot/dts/exynos/exynos9610-erd9610.dts
arch/arm64/boot/dts/exynos/exynos9610-pinctrl.dtsi
arch/arm64/boot/dts/exynos/exynos9610.dtsi
arch/arm64/configs/erd9610_defconfig
include/sound/compress_driver.h
include/sound/samsung/abox.h [new file with mode: 0644]
include/sound/samsung/abox_ipc.h [new file with mode: 0644]
include/sound/samsung/dp_ado.h [new file with mode: 0644]
include/sound/samsung/mailbox.h [new file with mode: 0644]
include/sound/samsung/vts.h [new file with mode: 0644]
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc-dpcm.h
include/sound/soc.h
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/abox/Kconfig [new file with mode: 0644]
sound/soc/samsung/abox/Makefile [new file with mode: 0644]
sound/soc/samsung/abox/abox.c [new file with mode: 0644]
sound/soc/samsung/abox/abox.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_bt.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_bt.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_dbg.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_dbg.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_dump.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_dump.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_effect.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_effect.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_failsafe.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_failsafe.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_gic.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_gic.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_if.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_if.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_log.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_log.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_rdma.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_util.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_util.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_vdma.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_vdma.h [new file with mode: 0644]
sound/soc/samsung/abox/abox_vss.c [new file with mode: 0644]
sound/soc/samsung/abox/abox_wdma.c [new file with mode: 0644]
sound/soc/samsung/exynos9610_madera.c [new file with mode: 0644]
sound/soc/samsung/vts/Kconfig [new file with mode: 0644]
sound/soc/samsung/vts/Makefile [new file with mode: 0644]
sound/soc/samsung/vts/mailbox.c [new file with mode: 0644]
sound/soc/samsung/vts/mailbox.h [new file with mode: 0644]
sound/soc/samsung/vts/vts.c [new file with mode: 0644]
sound/soc/samsung/vts/vts.h [new file with mode: 0644]
sound/soc/samsung/vts/vts_dma.c [new file with mode: 0644]
sound/soc/samsung/vts/vts_dump.c [new file with mode: 0644]
sound/soc/samsung/vts/vts_dump.h [new file with mode: 0644]
sound/soc/samsung/vts/vts_log.c [new file with mode: 0644]
sound/soc/samsung/vts/vts_log.h [new file with mode: 0644]
sound/soc/soc-compress.c
sound/soc/soc-dapm.c
sound/soc/soc-pcm.c

index a1eafa469dbcda9cc9d3b2816df0872ce7c7aca6..f17850834859ea857d3b7c121b8f2fb89b657590 100644 (file)
                interrupts = <0 460 0>;
        };
 
+       V_SYS: fixedregulator@0 {
+               compatible = "regulator-fixed";
+               regulator-name = "V_SYS";
+               regulator-min-microvolt = <4200000>;
+               regulator-max-microvolt = <4200000>;
+               regulator-boot-on;
+               regulator-always-on;
+       };
+
+       pinctrl@11850000 {
+               amp_irq: amp-irq {
+                       samsung,pins ="gpa0-2";
+                       samsung,pin-function = <0xf>;
+                       samsung,pin-pud = <0>;
+               };
+       };
+
+       pinctrl@139B0000 {
+               amp_reset: amp-reset {
+                       samsung,pins ="gpg3-3";
+                       samsung,pin-pud = <0>;
+                       samsung,pin-con-pdn =<3>;
+                       samsung,pin-pud-pdn = <0>;
+               };
+       };
+
+       i2c_3: i2c@13860000 {
+               status = "okay";
+               cs35l35_left: cs35l35@40 {
+                       #sound-dai-cells = <1>;
+                       compatible = "cirrus,cs35l35";
+                       reg = <0x40>;
+
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&amp_irq &amp_reset>;
+
+                       interrupt-parent = <&gpa0>;
+                       interrupts = <2 0 0>;
+
+                       reset-gpios = <&gpg3 3 0>;
+
+                       VA-supply = <&l42_reg>;
+                       VP-supply = <&V_SYS>;
+                       cirrus,audio-channel = <0x0>;
+                       cirrus,boost-ctl-millivolt = <7300>;
+                       cirrus,sp-drv-strength = <0x1>;
+                       cirrus,sp-drv-unused = <0x0>;
+                       cirrus,boost-ind-nanohenry = <1000>;
+
+                       cirrus,monitor-signal-format {
+                               cirrus,imon = /bits/ 8 <0x03 0x04 0x00 0x06>;
+                               cirrus,vmon = /bits/ 8 <0x03 0x00 0x00>;
+                               cirrus,vpmon = /bits/ 8 <0x00 0x00 0x00>;
+                               cirrus,vbstmon = /bits/ 8 <0x00 0x00 0x00>;
+                               cirrus,vpbrstat = /bits/ 8 <0x00 0x00 0x00>;
+                               cirrus,zerofill = /bits/ 8 <0x00 0x00 0x00>;
+                       };
+               };
+       };
+
+       pinctrl@139B0000 {
+               codec_reset: codec-reset {
+                       samsung,pins ="gpg3-2";
+                       samsung,pin-pud = <0>;
+                       samsung,pin-con-pdn =<3>;
+                       samsung,pin-pud-pdn = <0>;
+               };
+       };
+
+       spi_9: spi@13940000 {
+               pinctrl-names = "default";
+               pinctrl-0 = <&spi9_bus &spi9_cs_func>;
+               status = "okay";
+               cs47l35: cs47l35@0 {
+                       compatible = "cirrus,cs47l35";
+                       reg = <0x0>;
+
+                       spi-max-frequency = <11000000>;
+
+                       interrupts = <6 0 0>;
+                       interrupt-controller;
+                       #interrupt-cells = <2>;
+                       interrupt-parent = <&gpa0>;
+                       gpio-controller;
+                       #gpio-cells = <2>;
+                       #sound-dai-cells = <1>;
+
+                       AVDD-supply = <&l42_reg>;
+                       DBVDD1-supply = <&l42_reg>;
+                       DBVDD2-supply = <&l42_reg>;
+                       CPVDD1-supply = <&l42_reg>;
+                       CPVDD2-supply = <&l44_reg>;
+                       DCVDD-supply = <&l44_reg>;
+                       SPKVDD-supply = <&V_SYS>;
+
+                       reset-gpios = <&gpg3 2 0>;
+
+                       cirrus,dmic-ref = <0 0 0>;
+                       cirrus,inmode = <
+                               0 0 0 0 /* IN1 */
+                               0 0 0 0 /* IN2 */
+                       >;
+
+                       cirrus,gpsw = <3 0>;
+
+                       pinctrl-names = "probe", "active";
+                       pinctrl-0 = <&codec_reset>;
+                       pinctrl-1 = <&codec_reset &cs47l35_defaults>;
+
+                       madera_pinctrl: madera-pinctrl {
+                               compatible = "cirrus,madera-pinctrl";
+                               cs47l35_defaults: cs47l35-gpio-defaults {
+                                       aif1 {
+                                               groups = "aif1";
+                                               function = "aif1";
+                                               bias-bus-hold;
+                                       };
+
+                                       aif2 {
+                                               groups = "aif2";
+                                               function = "aif2";
+                                               bias-bus-hold;
+                                       };
+
+                                       aif3 {
+                                               groups = "aif3";
+                                               function = "aif3";
+                                               bias-bus-hold;
+                                       };
+
+                                       gpio6 { /* Amp Clock */
+                                               groups = "gpio6";
+                                               function = "opclk";
+                                               bias-pull-up;
+                                               output-low;
+                                       };
+
+                                       gpio5 { /* Mic Polarity Flip */
+                                               groups = "gpio5";
+                                               function = "io";
+                                       };
+                               };
+                       };
+
+                       micvdd {
+                               regulator-min-microvolt = <3000000>;
+                               regulator-max-microvolt = <3000000>;
+                       };
+
+                       MICBIAS1 {
+                               regulator-min-microvolt = <2800000>;
+                               regulator-max-microvolt = <2800000>;
+                               cirrus,ext-cap = <1>;
+                       };
+                       MICBIAS1A {
+                               regulator-active-discharge = <1>;
+                       };
+                       MICBIAS1B {
+                               regulator-active-discharge = <1>;
+                       };
+
+                       MICBIAS2 {
+                               regulator-min-microvolt = <2800000>;
+                               regulator-max-microvolt = <2800000>;
+                               cirrus,ext-cap = <1>;
+                       };
+                       MICBIAS2B {
+                               regulator-active-discharge = <1>;
+                       };
+
+                       cirrus,accdet {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               acc@1 {
+                                       reg = <1>;
+
+                                       cirrus,micd-configs = <
+                                               0 0 3 0 0
+                                       >;
+                                       cirrus,micd-bias-start-time = <8>;
+                                       cirrus,micd-rate = <6>;
+                                       cirrus,micd-pol-gpios = <&cs47l35 4 0>;
+                                       cirrus,micd-detect-debounce-ms = <500>;
+                                       cirrus,jd-use-jd2;
+                                       cirrus,micd-clamp-mode = <0x8>;
+                               };
+                       };
+
+                       controller-data {
+                               samsung,spi-feedback-delay = <1>;
+                               samsung,spi-chip-select-mode = <0>;
+                       };
+               };
+       };
+
+       dummy_audio_codec: audio_codec_dummy {
+               status = "okay";
+               compatible = "snd-soc-dummy";
+       };
+
+       dummy_audio_cpu: audio_cpu_dummy {
+               compatible = "samsung,dummy-cpu";
+               status = "okay";
+       };
+
+       sound {
+               status = "okay";
+               compatible = "samsung,exynos9610-madera";
+
+               clock-names = "xclkout";
+               clocks = <&clock OSC_AUD>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&xclkout0>;
+
+               cirrus,sysclk = <1 4 98304000>;
+               cirrus,dspclk = <8 4 147456000>;
+               cirrus,fll1-refclk = <1 0 26000000 98304000>;
+
+               cirrus,opclk = <3 0 12288000>;
+
+               samsung,routing =
+                       "HEADSETMIC", "MICBIAS2B",
+                       "MICBIAS2A", "MICBIAS2B",
+                       "IN1BL", "HEADSETMIC",
+                       "DMIC1", "MICBIAS1A",
+                       "IN1AL", "DMIC1",
+                       "DMIC2", "MICBIAS1B",
+                       "IN2L", "DMIC2",
+                       "RECEIVER", "EPOUTN",
+                       "RECEIVER", "EPOUTP",
+                       "HEADPHONE", "HPOUTL",
+                       "HEADPHONE", "HPOUTR",
+                       "AIF2 Playback", "OPCLK",
+                       "AIF2 Capture", "OPCLK",
+                       "VOUTPUT", "ABOX UAIF0 Playback",
+                       "SPEAKER", "Left SPK",
+                       "VOUTPUTCALL", "ABOX UAIF2 Playback",
+                       "ABOX UAIF2 Capture", "VINPUTCALL";
+
+               samsung,codec = <&abox &abox_uaif_0 &abox_uaif_1 &abox_uaif_2
+                       &abox_uaif_4 &abox_dsif &abox_spdy &cs35l35_left>;
+               samsung,prefix = "ABOX", "ABOX", "ABOX", "ABOX",
+                       "ABOX", "ABOX", "ABOX", "Left";
+               samsung,aux = <&abox_effect>;
+
+               rdma@0 {
+                       cpu {
+                               sound-dai = <&abox 0>;
+                       };
+                       platform {
+                               sound-dai = <&abox_rdma_0>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               rdma@1 {
+                       cpu {
+                               sound-dai = <&abox 1>;
+                       };
+                       platform {
+                               sound-dai = <&abox_rdma_1>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               rdma@2 {
+                       cpu {
+                               sound-dai = <&abox 2>;
+                       };
+                       platform {
+                               sound-dai = <&abox_rdma_2>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               rdma@3 {
+                       cpu {
+                               sound-dai = <&abox 3>;
+                       };
+                       platform {
+                               sound-dai = <&abox_rdma_3>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               rdma@4 {
+                       cpu {
+                               sound-dai = <&abox 4>;
+                       };
+                       platform {
+                               sound-dai = <&abox_rdma_4>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               rdma@5 {
+                       cpu {
+                               sound-dai = <&abox 5>;
+                       };
+                       platform {
+                               sound-dai = <&abox_rdma_5>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               rdma@6 {
+                       cpu {
+                               sound-dai = <&abox 6>;
+                       };
+                       platform {
+                               sound-dai = <&abox_rdma_6>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               rdma@7 {
+                       cpu {
+                               sound-dai = <&abox 7>;
+                       };
+                       platform {
+                               sound-dai = <&abox_rdma_7>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               wdma@0 {
+                       cpu {
+                               sound-dai = <&abox 8>;
+                       };
+                       platform {
+                               sound-dai = <&abox_wdma_0>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               wdma@1 {
+                       cpu {
+                               sound-dai = <&abox 9>;
+                       };
+                       platform {
+                               sound-dai = <&abox_wdma_1>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               wdma@2 {
+                       cpu {
+                               sound-dai = <&abox 10>;
+                       };
+                       platform {
+                               sound-dai = <&abox_wdma_2>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               wdma@3 {
+                       cpu {
+                               sound-dai = <&abox 11>;
+                       };
+                       platform {
+                               sound-dai = <&abox_wdma_3>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               wdma@4 {
+                       cpu {
+                               sound-dai = <&abox 12>;
+                       };
+                       platform {
+                               sound-dai = <&abox_wdma_4>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+/** ToDo: enable dp_audio link after enabling DP Audio
+ *             dp_audio@0 {
+ *                     cpu {
+ *                             sound-dai = <&dummy_audio_cpu>;
+ *                     };
+ *                     codec {
+ *                             sound-dai = <&dummy_audio_codec>;
+ *                     };
+ *             };
+ */
+               uaif@0 {
+                       format = "i2s";
+                       cpu {
+                               sound-dai = <&abox_uaif_0>;
+                       };
+                       codec {
+                               sound-dai = <&cs47l35 0>;
+                       };
+               };
+               uaif@1 {
+                       format = "i2s";
+                       cpu {
+                               sound-dai = <&abox_uaif_1>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               uaif@2 {
+                       format = "i2s";
+                       cpu {
+                               sound-dai = <&abox_uaif_2>;
+                       };
+                       codec {
+                               sound-dai = <&cs47l35 2>;
+                       };
+               };
+               uaif@4 {
+                       format = "i2s";
+                       bitclock-master;
+                       frame-master;
+                       cpu {
+                               sound-dai = <&abox_uaif_4>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               dsif@0 {
+                       format = "pdm";
+                       cpu {
+                               sound-dai = <&abox_dsif>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               spdy@0 {
+                       cpu {
+                               sound-dai = <&abox_spdy>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               sifs0@0 {
+                       cpu {
+                               sound-dai = <&abox 13>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               sifs1@0 {
+                       cpu {
+                               sound-dai = <&abox 14>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               sifs2@0 {
+                       cpu {
+                               sound-dai = <&abox 15>;
+                       };
+                       codec {
+                               sound-dai = <&dummy_audio_codec>;
+                       };
+               };
+               codec-left-amp@0 {
+                       format = "i2s";
+
+                       cpu {
+                               sound-dai = <&cs47l35 1>;
+                       };
+                       codec {
+                               sound-dai = <&cs35l35_left 0>;
+                       };
+               };
+
+       };
 };
 
 &pinctrl_0 {
index 1590b913d07fd3e62ea1f9d8a8e1e72ad713d42e..26743ebb2975b98991d803ba2fbfc5734831715f 100644 (file)
                        #interrupt-cells = <2>;
                };
 
+               xclkout0: xclkout0 {
+                       samsung,pins = "gpq0-1";
+                       samsung,pin-function = <2>;
+                       samsung,pin-pud = <0>;
+               };
+
                /* USI_PERIC0_UART_DBG */
                uart0_bus: uart0-bus {
                        samsung,pins = "gpq0-3", "gpq0-4";
                        interrupt-controller;
                        #interrupt-cells = <2>;
                };
+
+               aud_codec_mclk: aud-codec-mclk {
+                       samsung,pins = "gpb0-0";
+                       samsung,pin-function = <2>;
+                       samsung,pin-pud = <1>;
+               };
+
+               aud_codec_mclk_idle: aud-codec-mclk-idle {
+                       samsung,pins = "gpb0-0";
+                       samsung,pin-function = <0>;
+                       samsung,pin-pud = <1>;
+               };
+
+               aud_i2s0_bus: aud-i2s0-bus {
+                       samsung,pins = "gpb0-1", "gpb0-2", "gpb0-4";
+                       samsung,pin-function = <2>;
+                       samsung,pin-pud = <1>;
+                       samsung,pin-con-pdn =<2>;
+                       samsung,pin-pud-pdn = <1>;
+               };
+
+               aud_i2s0_sdo_bus: aud-i2s0-sdo-bus {
+                       samsung,pins = "gpb0-3";
+                       samsung,pin-function = <2>;
+                       samsung,pin-pud = <1>;
+                       samsung,pin-con-pdn =<2>;
+                       samsung,pin-pud-pdn = <1>;
+               };
+
+               aud_i2s0_idle: aud-i2s0-idle {
+                       samsung,pins = "gpb0-1", "gpb0-2", "gpb0-3", "gpb0-4";
+                       samsung,pin-function = <0>;
+                       samsung,pin-pud = <1>;
+               };
+
+               aud_i2s1_bus: aud-i2s1-bus {
+                       samsung,pins = "gpb1-0", "gpb1-1", "gpb1-3";
+                       samsung,pin-function = <2>;
+                       samsung,pin-pud = <1>;
+                       samsung,pin-con-pdn =<2>;
+                       samsung,pin-pud-pdn = <1>;
+               };
+
+               aud_i2s1_sdo_bus: aud-i2s1-sdo-bus {
+                       samsung,pins = "gpb1-2";
+                       samsung,pin-function = <2>;
+                       samsung,pin-pud = <1>;
+                       samsung,pin-con-pdn =<2>;
+                       samsung,pin-pud-pdn = <3>;
+               };
+
+               aud_i2s1_idle: aud-i2s1-idle {
+                       samsung,pins = "gpb1-0", "gpb1-1", "gpb1-2", "gpb1-3";
+                       samsung,pin-function = <0>;
+                       samsung,pin-pud = <1>;
+               };
+
+               aud_i2s2_bus: aud-i2s2-bus {
+                       samsung,pins = "gpb2-0", "gpb2-1", "gpb2-3";
+                       samsung,pin-function = <2>;
+                       samsung,pin-pud = <1>;
+                       samsung,pin-con-pdn =<2>;
+                       samsung,pin-pud-pdn = <1>;
+               };
+
+               aud_i2s2_sdo_bus: aud-i2s2-sdo-bus {
+                       samsung,pins = "gpb2-2";
+                       samsung,pin-function = <2>;
+                       samsung,pin-pud = <1>;
+                       samsung,pin-con-pdn =<2>;
+                       samsung,pin-pud-pdn = <3>;
+               };
+
+               aud_i2s2_idle: aud-i2s2-idle {
+                       samsung,pins = "gpb2-0", "gpb2-1", "gpb2-2", "gpb2-3";
+                       samsung,pin-function = <0>;
+                       samsung,pin-pud = <1>;
+               };
+
+               aud_dsd_bus: aud-dsd-bus {
+                       samsung,pins = "gpb2-0", "gpb2-1", "gpb2-2";
+                       samsung,pin-function = <3>;
+                       samsung,pin-pud = <1>;
+               };
+
+               aud_dsd_idle: aud-dsd-idle {
+                       samsung,pins = "gpb2-0", "gpb2-1", "gpb2-2";
+                       samsung,pin-function = <0>;
+                       samsung,pin-pud = <1>;
+               };
+
+               aud_fm_bus: aud-fm-bus {
+                       samsung,pins = "gpb2-4";
+                       samsung,pin-function = <2>;
+                       samsung,pin-pud = <1>;
+               };
+
+               aud_fm_idle: aud-fm-idle {
+                       samsung,pins = "gpb2-4";
+                       samsung,pin-function = <0>;
+                       samsung,pin-pud = <1>;
+               };
        };
 
        /* FSYS */
index d6fc9c93fc21fcef652f67be1bf2b59b15c787bd..543f9ecec4b6183865cac5eb385f91134eb57490 100644 (file)
                };
        };
 
+       abox_gic: abox_gic@0x14AF0000 {
+               compatible = "samsung,abox_gic";
+               status = "okay";
+               reg = <0x0 0x14AF1000 0x1000>, <0x0 0x14AF2000 0x1004>;
+               reg-names = "gicd", "gicc";
+               interrupts = <0 198 0>;
+       };
+
+       abox: abox@0x14A50000 {
+               compatible = "samsung,abox";
+               status = "okay";
+               reg = <0x0 0x14A50000 0x10000>, <0x0 0x14810000 0x3000>, <0x0 0x14B00000 0x35000>;
+               reg-names = "sfr", "sysreg", "sram";
+               #address-cells = <2>;
+               #size-cells = <1>;
+               ranges;
+               quirks = "try to asrc off", "off on suspend", "scsc bt";
+               #sound-dai-cells = <1>;
+               ipc_tx_offset = <0x22000>;
+               ipc_rx_offset = <0x22300>;
+               ipc_tx_ack_offset = <0x222FC>;
+               ipc_rx_ack_offset = <0x225FC>;
+               abox_gic = <&abox_gic>;
+               clocks = <&clock PLL_OUT_AUD>, <&clock GATE_ABOX_QCH_CPU>,
+                       <&clock DOUT_CLK_AUD_AUDIF>, <&clock DOUT_CLK_AUD_ACLK>;
+               clock-names = "pll", "cpu", "audif", "bus";
+               uaif_max_div = <512>;
+               iommus = <&sysmmu_abox>;
+               pm_qos_int = <0 0 0 0 0>;
+               pm_qos_aud = <1180000 800000 590000 394000 0>;
+
+               abox_rdma_0: abox_rdma@0x14A51000 {
+                       compatible = "samsung,abox-rdma";
+                       reg = <0x0 0x14A51000 0x100>;
+                       id = <0>;
+                       type = "normal";
+               };
+
+               abox_rdma_1: abox_rdma@0x14A51100 {
+                       compatible = "samsung,abox-rdma";
+                       reg = <0x0 0x14A51100 0x100>;
+                       id = <1>;
+                       type = "normal";
+               };
+
+               abox_rdma_2: abox_rdma@0x14A51200 {
+                       compatible = "samsung,abox-rdma";
+                       reg = <0x0 0x14A51200 0x100>;
+                       id = <2>;
+                       type = "normal";
+               };
+
+               abox_rdma_3: abox_rdma@0x14A51300 {
+                       compatible = "samsung,abox-rdma";
+                       reg = <0x0 0x14A51300 0x100>;
+                       id = <3>;
+                       type = "sync";
+               };
+
+               abox_rdma_4: abox_rdma@0x14A51400 {
+                       compatible = "samsung,abox-rdma";
+                       reg = <0x0 0x14A51400 0x100>;
+                       id = <4>;
+                       type = "call";
+               };
+
+               abox_rdma_5: abox_rdma@0x14A51500 {
+                       compatible = "samsung,abox-rdma";
+                       reg = <0x0 0x14A51500 0x100>, <0x0 0x14B22600 0x70>;
+                       id = <5>;
+                       type = "compress";
+               };
+
+               abox_rdma_6: abox_rdma@0x14A51600 {
+                       compatible = "samsung,abox-rdma";
+                       reg = <0x0 0x14A51600 0x100>;
+                       id = <6>;
+                       type = "realtime";
+                       scsc_bt;
+               };
+
+               abox_rdma_7: abox_rdma@0x14A51700 {
+                       compatible = "samsung,abox-rdma";
+                       reg = <0x0 0x14A51700 0x100>;
+                       id = <7>;
+                       type = "realtime";
+               };
+
+               abox_wdma_0: abox_wdma@0x14A52000 {
+                       compatible = "samsung,abox-wdma";
+                       reg = <0x0 0x14A52000 0x100>;
+                       id = <0>;
+                       type = "normal";
+                       scsc_bt;
+               };
+
+               abox_wdma_1: abox_wdma@0x14A52100 {
+                       compatible = "samsung,abox-wdma";
+                       reg = <0x0 0x14A52100 0x100>;
+                       id = <1>;
+                       type = "normal";
+               };
+
+               abox_wdma_2: abox_wdma@0x14A52200 {
+                       compatible = "samsung,abox-wdma";
+                       reg = <0x0 0x14A52200 0x100>;
+                       id = <2>;
+                       type = "call";
+               };
+
+               abox_wdma_3: abox_wdma@0x14A52300 {
+                       compatible = "samsung,abox-wdma";
+                       reg = <0x0 0x14A52300 0x100>;
+                       id = <3>;
+                       type = "normal";
+               };
+
+               abox_wdma_4: abox_wdma@0x14A52400 {
+                       compatible = "samsung,abox-wdma";
+                       reg = <0x0 0x14A52400 0x100>;
+                       id = <4>;
+                       type = "realtime";
+               };
+
+               abox_uaif_0: abox_uaif@0x14A50500 {
+                       compatible = "samsung,abox-uaif";
+                       reg = <0x0 0x14A50500 0x10>;
+                       id = <0>;
+                       clocks = <&clock DOUT_CLK_AUD_UAIF0>, <&clock GATE_ABOX_QCH_S_BCLK0>;
+                       clock-names = "bclk", "bclk_gate";
+                       pinctrl-names = "default", "sleep";
+                       pinctrl-0 = <&aud_i2s0_bus &aud_i2s0_sdo_bus &aud_codec_mclk>;
+                       pinctrl-1 = <&aud_i2s0_idle &aud_codec_mclk_idle>;
+                       #sound-dai-cells = <0>;
+               };
+
+               abox_uaif_1: abox_uaif@0x14A50510 {
+                       compatible = "samsung,abox-uaif";
+                       reg = <0x0 0x14A50510 0x10>;
+                       id = <1>;
+                       clocks = <&clock DOUT_CLK_AUD_UAIF1>, <&clock GATE_ABOX_QCH_S_BCLK1>;
+                       clock-names = "bclk", "bclk_gate";
+                       pinctrl-names = "default", "sleep";
+                       pinctrl-0 = <&aud_i2s1_bus &aud_i2s1_sdo_bus>;
+                       pinctrl-1 = <&aud_i2s1_idle>;
+                       #sound-dai-cells = <0>;
+               };
+
+               abox_uaif_2: abox_uaif@0x14A50520 {
+                       compatible = "samsung,abox-uaif";
+                       reg = <0x0 0x14A50520 0x10>;
+                       id = <2>;
+                       clocks = <&clock DOUT_CLK_AUD_UAIF2>, <&clock GATE_ABOX_QCH_S_BCLK2>;
+                       clock-names = "bclk", "bclk_gate";
+                       pinctrl-names = "default", "sleep";
+                       pinctrl-0 = <&aud_i2s2_bus &aud_i2s2_sdo_bus>;
+                       pinctrl-1 = <&aud_i2s2_idle>;
+                       #sound-dai-cells = <0>;
+               };
+
+               abox_uaif_4: abox_uaif@0x14A50540 {
+                       compatible = "samsung,abox-uaif";
+                       reg = <0x0 0x14A50540 0x10>;
+                       id = <4>;
+                       /* UAIF4 is connected to UAIF0 as slave */
+                       clocks = <&clock DOUT_CLK_AUD_UAIF0>, <&clock GATE_ABOX_QCH_S_BCLK0>;
+                       clock-names = "bclk", "bclk_gate";
+                       #sound-dai-cells = <0>;
+               };
+
+               abox_dsif: abox_dsif@0x14A50550 {
+                       compatible = "samsung,abox-dsif";
+                       reg = <0x0 0x14A50550 0x10>;
+                       id = <5>;
+                       clocks = <&clock DOUT_CLK_AUD_DSIF>, <&clock GATE_ABOX_QCH_S_BCLK_DSIF>;
+                       clock-names = "bclk", "bclk_gate";
+                       /* DSIF and UAIF2 shares GPIO
+                        * pinctrl-names = "default", "sleep";
+                        * pinctrl-0 = <&aud_dsd_bus>;
+                        * pinctrl-1 = <&aud_dsd_idle>;
+                        */
+                       #sound-dai-cells = <0>;
+               };
+
+               abox_spdy: abox_spdy@0x14A50560 {
+                       compatible = "samsung,abox-spdy";
+                       reg = <0x0 0x14A50560 0x10>;
+                       id = <6>;
+                       clocks = <&clock DOUT_CLK_AUD_FM>, <&clock GATE_ABOX_QCH_FM>;
+                       clock-names = "bclk", "bclk_gate";
+                       /* FM SPEEDY GPIO is controlled by FM radio driver
+                        * pinctrl-names = "default", "sleep";
+                        * pinctrl-0 = <&aud_fm_bus>;
+                        * pinctrl-1 = <&aud_fm_idle>;
+                        */
+                       #sound-dai-cells = <0>;
+               };
+
+               abox_effect: abox_effect@0x14B2E000 {
+                       compatible = "samsung,abox-effect";
+                       reg = <0x0 0x14B2E000 0x1000>;
+                       reg-names = "reg";
+                       abox = <&abox>;
+               };
+
+               abox_debug: abox_debug@0 {
+                       compatible = "samsung,abox-debug";
+                       memory-region = <&abox_rmem>;
+                       reg = <0x0 0x0 0x0>;
+               };
+
+               abox_vss: abox_vss@0 {
+                       compatible = "samsung,abox-vss";
+                       magic_offset = <0x600000>;
+                       reg = <0x0 0x0 0x0>;
+               };
+               /*
+                       *abox_bt: abox_bt@0 {
+                       *compatible = "samsung,abox-bt";
+                       *reg = <0x0 0x0 0x0>, <0x0 0x119D0000 0x1000>;
+                       *reg-names = "sfr", "mailbox";
+                       *};
+               */
+               ext_bin_0: ext_bin@0 {
+                       status = "enabled";
+                       samsung,name = "dsm.bin";
+                       samsung,area = <1>; /* 0:SRAM, 1:DRAM, 2:VSS */
+                       samsung,offset = <0x502000>;
+               };
+               ext_bin_1: ext_bin@1 {
+                       status = "disabled";
+                       samsung,name = "AP_AUDIO_SLSI.bin";
+                       samsung,area = <1>;
+                       samsung,offset = <0x7F0000>;
+               };
+               ext_bin_2: ext_bin@2 {
+                       status = "disabled";
+                       samsung,name = "APBargeIn_AUDIO_SLSI.bin";
+                       samsung,area = <1>;
+                       samsung,offset = <0x7EC000>;
+               };
+               ext_bin_3: ext_bin@3 {
+                       status = "disabled";
+                       samsung,name = "SoundBoosterParam.bin";
+                       samsung,area = <1>;
+                       samsung,offset = <0x4FC000>;
+               };
+               ext_bin_4: ext_bin@4 {
+                       status = "disabled";
+                       samsung,name = "dummy.bin";
+                       samsung,area = <1>;
+                       samsung,offset = <0x800000>;
+               };
+               ext_bin_5: ext_bin@5 {
+                       status = "disabled";
+                       samsung,name = "APBiBF_AUDIO_SLSI.bin";
+                       samsung,area = <1>;
+                       samsung,offset = <0x7EF000>;
+               };
+               ext_bin_6: ext_bin@6 {
+                       status = "disabled";
+                       samsung,name = "dummy.bin";
+                       samsung,area = <1>;
+                       samsung,offset = <0x800000>;
+               };
+               ext_bin_7: ext_bin@7 {
+                       status = "disabled";
+                       samsung,name = "dummy.bin";
+                       samsung,area = <1>;
+                       samsung,offset = <0x800000>;
+               };
+       };
+
        udc: usb@13200000 {
                compatible = "samsung,exynos-dwusb";
                clocks = <&clock GATE_USB30DRD_QCH_USB30>;
                #size-cells = <1>;
                ranges;
 
-               domain-clients = <>;
+               domain-clients = <&abox>;
        };
 
        iommu-domain_isp {
index d84449ace3cef000a11e287187608957e0bd1b7c..67b6acac691603a8c0350f5c244251740eaefc2a 100644 (file)
@@ -180,6 +180,7 @@ CONFIG_NET_ACT_MIRRED=y
 CONFIG_CFG80211=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_DEVTMPFS=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
 CONFIG_DMA_CMA=y
 CONFIG_CMA_SIZE_MBYTES=20
 CONFIG_BLK_DEV_LOOP=y
@@ -321,6 +322,11 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y
 # CONFIG_BACKLIGHT_GENERIC is not set
 CONFIG_SOUND=y
 CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_SAMSUNG=y
+CONFIG_SND_SOC_SAMSUNG_EXYNOS9610=y
+CONFIG_SND_SOC_SAMSUNG_EXYNOS9610_MADERA=y
 CONFIG_HIDRAW=y
 CONFIG_UHID=y
 CONFIG_HID_A4TECH=y
@@ -433,6 +439,7 @@ CONFIG_PM_DEVFREQ=y
 CONFIG_ARM_EXYNOS_DEVFREQ=y
 CONFIG_ARM_EXYNOS_DEVFREQ_DEBUG=y
 CONFIG_IIO=y
+CONFIG_EXTCON=y
 CONFIG_EXYNOS_ADC=y
 CONFIG_PWM=y
 CONFIG_PWM_SAMSUNG=y
index f58b8edf0371b6b466b7addfeab80348a5cbc25f..baa42fe598c2b558ea3aa7f633663e586702de88 100644 (file)
@@ -134,6 +134,8 @@ struct snd_compr_ops {
                        struct snd_compr_caps *caps);
        int (*get_codec_caps) (struct snd_compr_stream *stream,
                        struct snd_compr_codec_caps *codec);
+       int (*get_hw_params)(struct snd_compr_stream *stream,
+                        struct snd_pcm_hw_params *params);
 };
 
 /**
diff --git a/include/sound/samsung/abox.h b/include/sound/samsung/abox.h
new file mode 100644 (file)
index 0000000..ea95098
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * ALSA SoC - Samsung ABOX driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __ABOX_H
+#define __ABOX_H
+
+#include <linux/device.h>
+#include <linux/irqreturn.h>
+#include <sound/soc.h>
+#include <sound/samsung/abox_ipc.h>
+
+/**
+ * abox irq handler type definition
+ * @param[in]  ipc_id  id of ipc
+ * @param[in]  dev_id  dev_id from abox_register_irq_handler
+ * @param[in]  msg     message data
+ * @return     reference irqreturn_t
+ */
+typedef irqreturn_t (*abox_irq_handler_t)(int ipc_id, void *dev_id,
+               ABOX_IPC_MSG *msg);
+
+#ifdef CONFIG_SND_SOC_SAMSUNG_ABOX
+/**
+ * Check ABOX is on
+ * @return             true if A-Box is on, false on otherwise
+ */
+extern bool abox_is_on(void);
+
+/**
+ * Get INT frequency required by ABOX
+ * @return             INT frequency in kHz
+ */
+extern unsigned int abox_get_requiring_int_freq_in_khz(void);
+
+/**
+ * Get AUD frequency required by ABOX
+ * @return             AUD frequency in kHz
+ */
+extern unsigned int abox_get_requiring_aud_freq_in_khz(void);
+
+/**
+ * Start abox IPC
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  hw_irq          hardware IRQ number
+ * @param[in]  supplement      pointer to data
+ * @param[in]  size            size of data which are pointed by supplement
+ * @param[in]  atomic          1, if caller context is atomic. 0, if not.
+ * @param[in]  sync            1 to wait for ack. 0 if not.
+ * @return     error code if any
+ */
+extern int abox_request_ipc(struct device *dev,
+               int hw_irq, const void *supplement,
+               size_t size, int atomic, int sync);
+
+/**
+ * Start abox IPC
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  hw_irq          hardware IRQ number
+ * @param[in]  supplement      pointer to data
+ * @param[in]  size            size of data which are pointed by supplement
+ * @param[in]  atomic          1, if caller context is atomic. 0, if not.
+ * @param[in]  sync            1 to wait for ack. 0 if not.
+ * @return     error code if any
+ */
+static inline int abox_start_ipc_transaction(struct device *dev,
+               int hw_irq, const void *supplement,
+               size_t size, int atomic, int sync)
+{
+       return abox_request_ipc(dev, hw_irq, supplement, size, atomic, sync);
+}
+
+/**
+ * Register irq handler to abox
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  ipc_id          id of ipc
+ * @param[in]  irq_handler     abox irq handler to register
+ * @param[in]  dev_id          cookie which would be summitted with irq_handler
+ * @return     error code if any
+ */
+extern int abox_register_irq_handler(struct device *dev, int ipc_id,
+               abox_irq_handler_t irq_handler, void *dev_id);
+
+/**
+ * UAIF/DSIF hw params fixup helper
+ * @param[in]  rtd     snd_soc_pcm_runtime
+ * @param[out] params  snd_pcm_hw_params
+ * @param[in]  stream  SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
+ * @return             error code if any
+ */
+extern int abox_hw_params_fixup_helper(struct snd_soc_pcm_runtime *rtd,
+               struct snd_pcm_hw_params *params, int stream);
+/**
+ * iommu map for abox
+ * @param[in]  dev     pointer to abox device
+ * @param[in]  iova    device virtual address
+ * @param[in]  vaddr   kernel virtual address
+ * @param[in]  size    size of the mapping area
+ * @return     error code if any
+ */
+extern int abox_iommu_map(struct device *dev, unsigned long iova,
+               phys_addr_t paddr, size_t size);
+
+/**
+ * iommu unmap for abox
+ * @param[in]  dev     pointer to abox device
+ * @param[in]  iova    device virtual address
+ * @param[in]  vaddr   kernel virtual address
+ * @param[in]  size    size of the mapping area
+ * @return     error code if any
+ */
+extern int abox_iommu_unmap(struct device *dev, unsigned long iova,
+               phys_addr_t paddr, size_t size);
+
+/**
+ * query physical address from device virtual address
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  iova            device virtual address
+ * @return     physical address. 0 if not mapped
+ */
+extern phys_addr_t abox_iova_to_phys(struct device *dev, unsigned long iova);
+
+/**
+ * query virtual address from device virtual address
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  iova            device virtual address
+ * @return     virtual address. undefined if not mapped
+ */
+extern void *abox_iova_to_virt(struct device *dev, unsigned long iova);
+
+/**
+ * power off abox
+ */
+extern void abox_poweroff(void);
+
+#else /* !CONFIG_SND_SOC_SAMSUNG_ABOX */
+
+static inline bool abox_is_on(void)
+{
+       return false;
+}
+
+static inline unsigned int abox_get_requiring_int_freq_in_khz(void)
+{
+       return 0;
+}
+
+static inline int abox_request_ipc(struct device *dev,
+               int hw_irq, const void *supplement,
+               size_t size, int atomic, int sync)
+{
+       return -ENODEV;
+}
+
+static inline int abox_register_irq_handler(struct device *dev, int ipc_id,
+               abox_irq_handler_t irq_handler, void *dev_id)
+{
+       return -ENODEV;
+}
+
+static inline int abox_hw_params_fixup_helper(struct snd_soc_pcm_runtime *rtd,
+               struct snd_pcm_hw_params *params)
+{
+       return -ENODEV;
+}
+
+static inline int abox_iommu_map(struct device *dev, unsigned long iova,
+               phys_addr_t paddr, size_t size)
+{
+       return -ENODEV;
+}
+
+static inline int abox_iommu_unmap(struct device *dev, unsigned long iova,
+               phys_addr_t paddr, size_t size)
+{
+       return -ENODEV;
+}
+
+static inline phys_addr_t abox_iova_to_phys(struct device *dev,
+               unsigned long iova)
+{
+       return 0;
+}
+
+static inline void *abox_iova_to_virt(struct device *dev, unsigned long iova)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline void abox_poweroff(void) {}
+
+#endif /* !CONFIG_SND_SOC_SAMSUNG_ABOX */
+
+#endif /* __ABOX_H */
diff --git a/include/sound/samsung/abox_ipc.h b/include/sound/samsung/abox_ipc.h
new file mode 100644 (file)
index 0000000..37c8410
--- /dev/null
@@ -0,0 +1,355 @@
+/* sound/soc/samsung/abox/abox_ipc.h
+ *
+ * ALSA SoC - Samsung Abox driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_IPC_H
+#define __SND_SOC_ABOX_IPC_H
+
+/******** IPC signal ID *********************/
+enum SIGNAL_ID {
+       SIGID_SYSTEM = 1,
+       SIGID_PCMOUT_TASK0,
+       SIGID_PCMOUT_TASK1,
+       SIGID_PCMOUT_TASK2,
+       SIGID_PCMOUT_TASK3,
+       SIGID_PCMIN_TASK0,
+       SIGID_PCMIN_TASK1,
+       SIGID_OFFLOAD,
+       SIGID_ERAP,
+       SIGID_ASB,
+};
+
+/******** PCMTASK IPC ***********************/
+enum PCMMSG {
+       PCM_OPEN                = 1,
+       PCM_CLOSE               = 2,
+       PCM_CPUDAI_TRIGGER      = 3,
+       PCM_CPUDAI_HW_PARAMS    = 4,
+       PCM_CPUDAI_SET_FMT      = 5,
+       PCM_CPUDAI_SET_CLKDIV   = 6,
+       PCM_CPUDAI_SET_SYSCLK   = 7,
+       PCM_CPUDAI_STARTUP      = 8,
+       PCM_CPUDAI_SHUTDOWN     = 9,
+       PCM_CPUDAI_DELAY        = 10,
+       PCM_PLTDAI_OPEN         = 11,
+       PCM_PLTDAI_CLOSE        = 12,
+       PCM_PLTDAI_IOCTL        = 13,
+       PCM_PLTDAI_HW_PARAMS    = 14,
+       PCM_PLTDAI_HW_FREE      = 15,
+       PCM_PLTDAI_PREPARE      = 16,
+       PCM_PLTDAI_TRIGGER      = 17,
+       PCM_PLTDAI_POINTER      = 18,
+       PCM_PLTDAI_MMAP         = 19,
+       PCM_SET_BUFFER          = 20,
+       PCM_SYNCHRONIZE         = 21,
+       PCM_PLTDAI_ACK          = 22,
+       PCM_PLTDAI_REGISTER     = 50,
+};
+
+struct PCMTASK_HW_PARAMS {
+       int sample_rate;
+       int bit_depth;
+       int channels;
+};
+
+struct PCMTASK_SET_BUFFER {
+       int phyaddr;
+       int size;
+       int count;
+};
+
+struct PCMTASK_HARDWARE {
+       char name[32];                  /* name */
+       unsigned int addr;              /* buffer address */
+       unsigned int width_min;         /* min width */
+       unsigned int width_max;         /* max width */
+       unsigned int rate_min;          /* min rate */
+       unsigned int rate_max;          /* max rate */
+       unsigned int channels_min;      /* min channels */
+       unsigned int channels_max;      /* max channels */
+       unsigned int buffer_bytes_max;  /* max buffer size */
+       unsigned int period_bytes_min;  /* min period size */
+       unsigned int period_bytes_max;  /* max period size */
+       unsigned int periods_min;       /* min # of periods */
+       unsigned int periods_max;       /* max # of periods */
+};
+
+/* Channel id of the Virtual DMA should be started from the BASE */
+#define PCMTASK_VDMA_ID_BASE 100
+
+/* Parameter of the PCMTASK command */
+struct IPC_PCMTASK_MSG {
+       enum PCMMSG msgtype;
+       int channel_id;
+       union {
+               struct PCMTASK_HW_PARAMS hw_params;
+               struct PCMTASK_SET_BUFFER setbuff;
+               struct PCMTASK_HARDWARE hardware;
+               unsigned int pointer;
+               int trigger;
+               int synchronize;
+       } param;
+};
+
+/******** OFFLOAD IPC ***********************/
+enum OFFLOADMSG {
+       OFFLOAD_OPEN = 1,
+       OFFLOAD_CLOSE,
+       OFFLOAD_SETPARAM,
+       OFFLOAD_START,
+       OFFLOAD_WRITE,
+       OFFLOAD_PAUSE,
+       OFFLOAD_STOP,
+};
+
+/* The parameter of the set_param */
+struct OFFLOAD_SET_PARAM {
+       int sample_rate;
+       int chunk_size;
+};
+
+/* The parameter of the start */
+struct OFFLOAD_START {
+       int id;
+};
+
+/* The parameter of the write */
+struct OFFLOAD_WRITE {
+       int id;
+       int buff;
+       int size;
+};
+
+/* Parameter of the OFFLOADTASK command */
+struct IPC_OFFLOADTASK_MSG {
+       enum OFFLOADMSG msgtype;
+       int channel_id;
+       union {
+               struct OFFLOAD_SET_PARAM setparam;
+               struct OFFLOAD_START start;
+               struct OFFLOAD_WRITE write;
+       } param;
+};
+
+/******** ABOX_LOOPBACK IPC ***********************/
+enum ABOX_ERAP_MSG {
+       REALTIME_OPEN = 1,
+       REALTIME_CLOSE,
+       REALTIME_OUTPUT_SRATE,
+       REALTIME_INPUT_SRATE,
+       REALTIME_BIND_CHANNEL,
+       REALTIME_START,
+       REALTIME_STOP,
+       REALTIME_PREDSM_AUDIO,
+       REALTIME_PREDSM_VI_SENSE,
+       REALTIME_TONEGEN = 0x20,
+       REALTIME_EXTRA = 0xea,
+       REALTIME_USB = 0x30,
+};
+
+enum ABOX_ERAP_TYPE {
+       ERAP_ECHO_CANCEL,
+       ERAP_VI_SENSE,
+       ERAP_TYPE_COUNT,
+};
+
+enum ABOX_USB_MSG {
+       IPC_USB_PCM_OPEN,
+       IPC_USB_DESC,
+       IPC_USB_XHCI,
+       IPC_USB_L2,
+       IPC_USB_CONN,
+       IPC_USB_CTRL,
+       IPC_USB_SET_INTF,
+       IPC_USB_SAMPLE_RATE,
+       IPC_USB_PCM_BUF,
+};
+
+struct ERAP_ONOFF_PARAM {
+       enum ABOX_ERAP_TYPE type;
+       int channel_no;
+       int version;
+};
+
+struct ERAP_SET_SRATE_PARAM {
+       int channel_no;
+       int srate;
+};
+
+struct ERAP_BIND_CHANNEL_PARAM {
+       int input_channel;
+       int output_channel;
+};
+
+struct ERAP_RAW_PARAM {
+       unsigned int params[188];
+};
+
+struct ERAP_USB_AUDIO_PARAM {
+       enum ABOX_USB_MSG type;
+       int param1;
+       int param2;
+       int param3;
+       int param4;
+};
+
+struct IPC_ERAP_MSG {
+       enum ABOX_ERAP_MSG msgtype;
+       union {
+               struct ERAP_ONOFF_PARAM onoff;
+               struct ERAP_BIND_CHANNEL_PARAM bind;
+               struct ERAP_SET_SRATE_PARAM srate;
+               struct ERAP_RAW_PARAM raw;
+               struct ERAP_USB_AUDIO_PARAM usbaudio;
+       } param;
+};
+
+/******** ABOX_CONFIG IPC ***********************/
+enum ABOX_CONFIGMSG {
+       SET_MIXER_SAMPLE_RATE = 1,
+       SET_OUT1_SAMPLE_RATE,
+       SET_OUT2_SAMPLE_RATE,
+       SET_RECP_SAMPLE_RATE,
+       SET_INMUX0_SAMPLE_RATE,
+       SET_INMUX1_SAMPLE_RATE,
+       SET_INMUX2_SAMPLE_RATE,
+       SET_INMUX3_SAMPLE_RATE,
+       SET_INMUX4_SAMPLE_RATE,
+       SET_MIXER_FORMAT = 0x10,
+       SET_OUT1_FORMAT,
+       SET_OUT2_FORMAT,
+       SET_RECP_FORMAT,
+       SET_INMUX0_FORMAT,
+       SET_INMUX1_FORMAT,
+       SET_INMUX2_FORMAT,
+       SET_INMUX3_FORMAT,
+       SET_INMUX4_FORMAT,
+};
+
+struct IPC_ABOX_CONFIG_MSG {
+       enum ABOX_CONFIGMSG msgtype;
+       int param1;
+       int param2;
+};
+
+/******** ABOX_ASB_TEST IPC ***********************/
+enum ABOX_TEST_MSG {
+       ASB_MULTITX_SINGLERX = 1,
+       ASB_SINGLETX_MULTIRX,
+       ASB_MULTITX_MULTIRX,
+       ASB_FFT_TEST,
+};
+
+struct IPC_ABOX_TEST_MSG {
+       enum ABOX_TEST_MSG msgtype;
+       int param1;
+       int param2;
+};
+
+/******** IPC_ABOX_SYSTEM_MSG IPC ***********************/
+enum ABOX_SYSTEM_MSG {
+       ABOX_SUSPEND = 1,
+       ABOX_RESUME,
+       ABOX_BOOT_DONE,
+       ABOX_CHANGE_GEAR,
+       ABOX_START_L2C_CONTROL,
+       ABOX_END_L2C_CONTROL,
+       ABOX_REQUEST_SYSCLK,
+       ABOX_REQUEST_L2C,
+       ABOX_CHANGED_GEAR,
+       ABOX_REPORT_LOG = 0x10,
+       ABOX_FLUSH_LOG,
+       ABOX_REPORT_DUMP = 0x20,
+       ABOX_REQUEST_DUMP,
+       ABOX_FLUSH_DUMP,
+       ABOX_REPORT_SRAM = 0x40,
+       ABOX_START_CLAIM_SRAM,
+       ABOX_END_CLAIM_SRAM,
+       ABOX_START_RECLAIM_SRAM,
+       ABOX_END_RECLAIM_SRAM,
+       ABOX_REPORT_DRAM,
+       ABOX_SET_MODE = 0x50,
+       ABOX_SET_TYPE = 0x60,
+       ABOX_START_VSS = 0xA0,
+       ABOX_REPORT_COMPONENT = 0xC0,
+       ABOX_UPDATE_COMPONENT_CONTROL,
+       ABOX_REQUEST_COMPONENT_CONTROL,
+       ABOX_REPORT_COMPONENT_CONTROL,
+       ABOX_BT_SCO_ENABLE = 0xD0,
+       ABOX_REQUEST_DEBUG = 0xDE,
+       ABOX_REPORT_FAULT = 0xFA,
+};
+
+struct IPC_SYSTEM_MSG {
+       enum ABOX_SYSTEM_MSG msgtype;
+       int param1;
+       int param2;
+       int param3;
+       union {
+               int param_s32[0];
+               char param_bundle[740];
+       } bundle;
+};
+
+struct ABOX_LOG_BUFFER {
+       unsigned int index_writer;
+       unsigned int index_reader;
+       unsigned int size;
+       char buffer[0];
+};
+
+struct ABOX_COMPONENT_CONTROL {
+       unsigned int id;
+       char name[16];
+       unsigned int count;
+       int min, max;
+};
+
+struct ABOX_COMPONENT_DESCRIPTIOR {
+       unsigned int id;
+       char name[16];
+       unsigned int control_count;
+       struct ABOX_COMPONENT_CONTROL controls[];
+};
+
+/************ IPC DEFINITION ***************/
+/* Categorized IPC, */
+enum IPC_ID {
+       IPC_RECEIVED,
+       IPC_SYSTEM = 1,
+       IPC_PCMPLAYBACK,
+       IPC_PCMCAPTURE,
+       IPC_OFFLOAD,
+       IPC_ERAP,
+       WDMA0_BUF_FULL = 0x8,
+       WDMA1_BUF_FULL = 0x9,
+       IPC_ABOX_CONFIG = 0xA,
+       RDMA0_BUF_EMPTY = 0xB,
+       RDMA1_BUF_EMPTY = 0xC,
+       RDMA2_BUF_EMPTY = 0xD,
+       RDMA3_BUF_EMPTY = 0xE,
+       IPC_ASB_TEST = 0xF,
+       IPC_ID_COUNT,
+};
+
+typedef struct {
+       enum IPC_ID ipcid;
+       int task_id;
+       union IPC_MSG {
+               struct IPC_SYSTEM_MSG system;
+               struct IPC_PCMTASK_MSG pcmtask;
+               struct IPC_OFFLOADTASK_MSG offload;
+               struct IPC_ERAP_MSG erap;
+               struct IPC_ABOX_CONFIG_MSG config;
+               struct IPC_ABOX_TEST_MSG asb;
+       } msg;
+} ABOX_IPC_MSG;
+
+#endif /* __SND_SOC_ABOX_IPC_H */
diff --git a/include/sound/samsung/dp_ado.h b/include/sound/samsung/dp_ado.h
new file mode 100644 (file)
index 0000000..0fbea6c
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Samsung DP Audio driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+  *
+ * 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 __DPADO_H
+#define __DPADO_H
+
+#ifdef CONFIG_SOC_EXYNOS9810
+void dp_ado_switch_set_state(int state);
+#else
+#define dp_ado_set_state(state) do { } while (0)
+#endif
+
+#endif /* __DPADO_H */
diff --git a/include/sound/samsung/mailbox.h b/include/sound/samsung/mailbox.h
new file mode 100644 (file)
index 0000000..78f1314
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * ALSA SoC - Samsung Mailbox driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+  *
+ * 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 __VTS_MAILBOX_H
+#define __VTS_MAILBOX_H
+
+/**
+ * Mailbox interrupt generator
+ * @param[in]  pdev    pointer to struct platform_device of target mailbox device
+ * @param[in]  hw_irq  hardware irq number which is same with bit index of the irq
+ * @return             error code if any
+ */
+extern int mailbox_generate_interrupt(const struct platform_device *pdev, int hw_irq);
+
+/**
+ * Mailbox shared register writer
+ * @param[in]  pdev    pointer to struct platform_device of target mailbox device
+ * @param[in]  values  values to write
+ * @param[in]  start   start position to write. if 0, values are written from ISSR0
+ * @param[in]  count   count of value in values.
+ */
+extern void mailbox_write_shared_register(const struct platform_device *pdev,
+               const u32 *values, int start, int count);
+
+/**
+ * Mailbox shared register reader
+ * @param[in]  pdev    pointer to struct platform_device of target mailbox device
+ * @param[out] values  memory to write
+ * @param[in]  start   start position to read. if 1, values are read from ISSR1
+ * @param[in]  count   count of value in values.
+ */
+extern void mailbox_read_shared_register(const struct platform_device *pdev,
+               u32 *values, int start, int count);
+
+#endif /* __VTS_MAILBOX_H */
\ No newline at end of file
diff --git a/include/sound/samsung/vts.h b/include/sound/samsung/vts.h
new file mode 100644 (file)
index 0000000..cb87983
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * ALSA SoC - Samsung VTS driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+  *
+ * 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 __VTS_H
+#define __VTS_H
+
+#include <linux/platform_device.h>
+
+#ifdef CONFIG_SND_SOC_SAMSUNG_VTS
+/**
+ * Acquire SRAM in VTS
+ * @param[in]  pdev    pointer to struct platform_device of a VTS device
+ * @param[in]  vts     1, if requester is vts. 0 for others.
+ * @return             error code if any
+ */
+extern int vts_acquire_sram(struct platform_device *pdev, int vts);
+
+/**
+ * Release SRAM in VTS
+ * @param[in]  pdev    pointer to struct platform_device of a VTS device
+ * @param[in]  vts     1, if requester is vts. 0 for others.
+ * @return             error code if any
+ */
+extern int vts_release_sram(struct platform_device *pdev, int vts);
+
+/**
+ * Clear SRAM in VTS
+ * @param[in]  pdev    pointer to struct platform_device of a VTS device
+ * @return             error code if any
+ */
+extern int vts_clear_sram(struct platform_device *pdev);
+
+/**
+ * Check VTS is on
+ * @return             true if VTS is on, false on otherwise
+ */
+extern volatile bool vts_is_on(void);
+#else /* !CONFIG_SND_SOC_SAMSUNG_VTS */
+static inline int vts_acquire_sram(struct platform_device *pdev, int vts)
+{ return -ENODEV; }
+static inline int vts_release_sram(struct platform_device *pdev, int vts)
+{ return -ENODEV; }
+static inline int vts_clear_sram(struct platform_device *pdev) { return -ENODEV; }
+static inline bool vts_is_on(void) { return false; }
+#endif /* !CONFIG_SND_SOC_SAMSUNG_VTS */
+
+#endif /* __VTS_H */
index 58acd00cae1967bc6ebb7851aab9b12aea49f782..1e0b1698b0eb88aa7a434b92fc1e7dfd111201be 100644 (file)
@@ -292,8 +292,8 @@ struct snd_soc_dai {
        struct snd_soc_dai_driver *driver;
 
        /* DAI runtime info */
-       unsigned int capture_active:1;          /* stream is in use */
-       unsigned int playback_active:1;         /* stream is in use */
+       unsigned int capture_active:4;          /* stream is in use */
+       unsigned int playback_active:4;         /* stream is in use */
        unsigned int symmetric_rates:1;
        unsigned int symmetric_channels:1;
        unsigned int symmetric_samplebits:1;
@@ -312,6 +312,7 @@ struct snd_soc_dai {
        unsigned int rate;
        unsigned int channels;
        unsigned int sample_bits;
+       unsigned int sample_width;
 
        /* parent platform/codec */
        struct snd_soc_codec *codec;
index 14a35befad6c0e3eca8d0f64831173539d01f21c..2c105bdfdafb192b94e02222147ddbfa8ddfb92b 100644 (file)
@@ -172,6 +172,12 @@ struct device;
        SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .kcontrol_news = wcontrols, .num_kcontrols = 1, \
        .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_DEMUX_E(wname, wreg, wshift, winvert, wcontrols, \
+       wevent, wflags) \
+{      .id = snd_soc_dapm_demux, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = 1, \
+       .event = wevent, .event_flags = wflags}
 
 /* additional sequencing control within an event type */
 #define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
@@ -480,6 +486,12 @@ struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(
 int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm,
        enum snd_soc_bias_level level);
 
+int snd_soc_dapm_connected_output_ep(struct snd_soc_dapm_widget *widget,
+       struct list_head *list);
+
+int snd_soc_dapm_connected_input_ep(struct snd_soc_dapm_widget *widget,
+       struct list_head *list);
+
 /* dapm widget types */
 enum snd_soc_dapm_type {
        snd_soc_dapm_input = 0,         /* input pin */
index 806059052bfcca4aa9d46ff3b0f061442136e84f..aeb9a4099988ee2472ea9197b8fc9b264639dd0e 100644 (file)
@@ -62,6 +62,8 @@ enum snd_soc_dpcm_trigger {
        SND_SOC_DPCM_TRIGGER_PRE                = 0,
        SND_SOC_DPCM_TRIGGER_POST,
        SND_SOC_DPCM_TRIGGER_BESPOKE,
+       SND_SOC_DPCM_TRIGGER_PRE_POST, /* PRE on start, POST on stop */
+       SND_SOC_DPCM_TRIGGER_POST_PRE, /* POST on start, PRE on stop */
 };
 
 /*
index d22de9712c451d67a9ee068ab8001589f55901bd..a65a63e5eea622bf52a4e18d49858ae716c6d7db 100644 (file)
@@ -1044,7 +1044,7 @@ struct snd_soc_dai_link {
 
        /* optional hw_params re-writing for BE and FE sync */
        int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
-                       struct snd_pcm_hw_params *params);
+                       struct snd_pcm_hw_params *params, int stream);
 
        /* machine stream operations */
        const struct snd_soc_ops *ops;
index 0520f5afd7cca759ea6ac1dee55cc322e7533ddd..f268335b04309c18792777f9978faeaebde0ada7 100644 (file)
@@ -30,6 +30,9 @@ config SND_SAMSUNG_SPDIF
 config SND_SAMSUNG_I2S
        tristate "Samsung I2S interface support"
 
+config SND_SOC_SAMSUNG_DISPLAYPORT
+       tristate "Samsung display port audio interface support"
+
 config SND_SOC_SAMSUNG_NEO1973_WM8753
        tristate "Audio support for Openmoko Neo1973 Smartphones (GTA02)"
        depends on MACH_NEO1973_GTA02
@@ -193,6 +196,24 @@ config SND_SOC_ODROID
        help
          Say Y here to enable audio support for the Odroid XU3/XU4.
 
+config SND_SOC_SAMSUNG_EXYNOS9610
+       tristate "Audio support on exynos9610"
+       select SND_SOC_SAMSUNG_ABOX
+       #select SND_SOC_SAMSUNG_DISPLAYPORT
+       help
+               Say Y if you want to add support for audio on the Exynos9610.
+
+config SND_SOC_SAMSUNG_EXYNOS9610_MADERA
+       tristate "Madera audio codec support on exynos9610"
+       select SND_SOC_CS47L35
+       select MFD_MADERA_SPI
+       select MFD_CS47L35
+       select REGULATOR_ARIZONA_LDO1
+       select REGULATOR_ARIZONA_MICSUPP
+       select EXTCON_MADERA
+       select EXTCON_MADERA_INPUT_EVENT
+       select SND_SOC_CS35L35
+
 config SND_SOC_ARNDALE_RT5631_ALC5631
         tristate "Audio support for RT5631(ALC5631) on Arndale Board"
         depends on I2C
@@ -209,4 +230,7 @@ config SND_SOC_SAMSUNG_TM2_WM5110
        help
          Say Y if you want to add support for SoC audio on the TM2 board.
 
+source "sound/soc/samsung/abox/Kconfig"
+source "sound/soc/samsung/vts/Kconfig"
+
 endif #SND_SOC_SAMSUNG
index 030949e1e434229430eb66bfc132a3446bd73688..1ed6642cba4996913c04e961d495339d9e84641c 100644 (file)
@@ -8,6 +8,7 @@ snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
 snd-soc-samsung-spdif-objs := spdif.o
 snd-soc-pcm-objs := pcm.o
 snd-soc-i2s-objs := i2s.o
+snd-soc-dp_dma-objs := dp_dma.o dummy_cpu_dai.o
 
 obj-$(CONFIG_SND_SOC_SAMSUNG) += snd-soc-s3c-dma.o
 obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o
@@ -17,6 +18,9 @@ obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o
 obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o
 obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o
 obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_DISPLAYPORT) += snd-soc-dp_dma.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_VTS) += vts/
+obj-$(CONFIG_SND_SOC_SAMSUNG_ABOX) += abox/
 
 # S3C24XX Machine Support
 snd-soc-jive-wm8750-objs := jive_wm8750.o
@@ -67,3 +71,4 @@ obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
 obj-$(CONFIG_SND_SOC_ODROID) += snd-soc-odroid.o
 obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_EXYNOS9610_MADERA) += exynos9610_madera.o
\ No newline at end of file
diff --git a/sound/soc/samsung/abox/Kconfig b/sound/soc/samsung/abox/Kconfig
new file mode 100644 (file)
index 0000000..4a86343
--- /dev/null
@@ -0,0 +1,8 @@
+config SND_SOC_SAMSUNG_ABOX
+       tristate "ASoC support for Samsung ABOX Audio"
+       select REGMAP_MMIO
+       select SND_SOC_COMPRESS
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the Samsung SoC ABOX interface. You will also need to
+         select the audio interfaces to support below.
\ No newline at end of file
diff --git a/sound/soc/samsung/abox/Makefile b/sound/soc/samsung/abox/Makefile
new file mode 100644 (file)
index 0000000..79258f8
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_SND_SOC_SAMSUNG_ABOX) += abox_util.o abox_dbg.o abox_dump.o abox_log.o abox_gic.o abox.o abox_rdma.o abox_wdma.o abox_if.o abox_effect.o abox_vss.o abox_failsafe.o abox_vdma.o abox_bt.o
diff --git a/sound/soc/samsung/abox/abox.c b/sound/soc/samsung/abox/abox.c
new file mode 100644 (file)
index 0000000..f3bd43f
--- /dev/null
@@ -0,0 +1,6303 @@
+/* sound/soc/samsung/abox/abox.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/exynos_iovmm.h>
+#include <linux/workqueue.h>
+#include <linux/smc.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <linux/sched/clock.h>
+#include <linux/shm_ipc.h>
+#include <linux/modem_notifier.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/samsung/abox.h>
+#include <sound/samsung/vts.h>
+//#include <linux/exynos_iovmm.h>
+
+#include <soc/samsung/exynos-pmu.h>
+//#include <soc/samsung/exynos-itmon.h>
+#include "../../../../drivers/iommu/exynos-iommu.h"
+
+#include "abox_util.h"
+#include "abox_dbg.h"
+#include "abox_log.h"
+#include "abox_dump.h"
+#include "abox_gic.h"
+#include "abox_failsafe.h"
+#include "abox_if.h"
+#include "abox_vdma.h"
+#include "abox_effect.h"
+#ifdef CONFIG_SCSC_BT
+#include "abox_bt.h"
+#endif
+#include "abox.h"
+
+#undef EMULATOR
+#ifdef EMULATOR
+static void __iomem *pmu_alive;
+static void update_mask_value(void __iomem *sfr,
+               unsigned int mask, unsigned int value)
+{
+       unsigned int sfr_value = readl(sfr);
+
+       set_mask_value(sfr_value, mask, value);
+       writel(sfr_value, sfr);
+}
+#endif
+
+#if IS_ENABLED(CONFIG_SOC_EXYNOS8895)
+#define GPIO_MODE_ABOX_SYS_PWR_REG             (0x1308)
+#define PAD_RETENTION_ABOX_OPTION              (0x3048)
+#define ABOX_MAGIC                             (0x0814)
+#define ABOX_MAGIC_VALUE                       (0xAB0CAB0C)
+#define ABOX_CPU_CONFIGURATION                 (0x2520)
+#define ABOX_CPU_LOCAL_PWR_CFG                 (0x00000001)
+#define ABOX_CPU_STATUS                                (0x2524)
+#define ABOX_CPU_STATUS_STATUS_MASK            (0x00000001)
+#define ABOX_CPU_STANDBY                       ABOX_CPU_STATUS
+#define ABOX_CPU_STANDBY_WFE_MASK              (0x20000000)
+#define ABOX_CPU_STANDBY_WFI_MASK              (0x10000000)
+#define ABOX_CPU_OPTION                                (0x2528)
+#define ABOX_CPU_OPTION_USE_STANDBYWFE_MASK    (0x00020000)
+#define ABOX_CPU_OPTION_USE_STANDBYWFI_MASK    (0x00010000)
+#define ABOX_CPU_OPTION_ENABLE_CPU_MASK                (0x00008000)
+#elif IS_ENABLED(CONFIG_SOC_EXYNOS9810)
+#define GPIO_MODE_ABOX_SYS_PWR_REG             (0x1424)
+#define PAD_RETENTION_ABOX_OPTION              (0x4170)
+#define ABOX_MAGIC                             (0x0814)
+#define ABOX_MAGIC_VALUE                       (0xAB0CAB0C)
+#define ABOX_CPU_CONFIGURATION                 (0x415C)
+#define ABOX_CPU_LOCAL_PWR_CFG                 (0x00000001)
+#define ABOX_CPU_STATUS                                (0x4160)
+#define ABOX_CPU_STATUS_STATUS_MASK            (0x00000001)
+#define ABOX_CPU_STANDBY                       (0x3804)
+#define ABOX_CPU_STANDBY_WFE_MASK              (0x20000000)
+#define ABOX_CPU_STANDBY_WFI_MASK              (0x10000000)
+#define ABOX_CPU_OPTION                                (0x4164)
+#define ABOX_CPU_OPTION_ENABLE_CPU_MASK                (0x10000000)
+#elif IS_ENABLED(CONFIG_SOC_EXYNOS9610)
+#define GPIO_MODE_ABOX_SYS_PWR_REG             (0x1308)
+#define PAD_RETENTION_ABOX_OPTION              (0x3048)
+#define ABOX_MAGIC                             (0x0814)
+#define ABOX_MAGIC_VALUE                       (0xAB0CAB0C)
+#define ABOX_CPU_CONFIGURATION                 (0x2520)
+#define ABOX_CPU_LOCAL_PWR_CFG                 (0x00000001)
+#define ABOX_CPU_STATUS                                (0x2524)
+#define ABOX_CPU_STATUS_STATUS_MASK            (0x00000001)
+#define ABOX_CPU_STANDBY                       (0x2524)
+#define ABOX_CPU_STANDBY_WFE_MASK              (0x20000000)
+#define ABOX_CPU_STANDBY_WFI_MASK              (0x10000000)
+#define ABOX_CPU_OPTION                                (0x2528)
+#define ABOX_CPU_OPTION_ENABLE_CPU_MASK                (0x00008000)
+#endif
+
+#define DEFAULT_CPU_GEAR_ID            (0xAB0CDEFA)
+#define TEST_CPU_GEAR_ID               (DEFAULT_CPU_GEAR_ID + 1)
+#define DEFAULT_LIT_FREQ_ID            DEFAULT_CPU_GEAR_ID
+#define DEFAULT_BIG_FREQ_ID            DEFAULT_CPU_GEAR_ID
+#define DEFAULT_HMP_BOOST_ID           DEFAULT_CPU_GEAR_ID
+#define DEFAULT_INT_FREQ_ID            DEFAULT_CPU_GEAR_ID
+#define DEFAULT_MIF_FREQ_ID            DEFAULT_CPU_GEAR_ID
+#define AUD_PLL_RATE_KHZ               (1179648)
+#define AUD_PLL_RATE_HZ_BYPASS         (26000000)
+#define AUDIF_RATE_HZ                  (24576000)
+#define CALLIOPE_ENABLE_TIMEOUT_MS     (1000)
+#define IPC_TIMEOUT_US                 (10000)
+#define BOOT_DONE_TIMEOUT_MS           (10000)
+#define IPC_RETRY                      (10)
+
+#define ERAP(wname, wcontrols, event_fn, wparams) \
+{      .id = snd_soc_dapm_dai_link, .name = wname, \
+       .reg = SND_SOC_NOPM, .event = event_fn, \
+       .kcontrol_news = wcontrols, .num_kcontrols = 1, \
+       .params = wparams, .num_params = ARRAY_SIZE(wparams), \
+       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_WILL_PMD }
+
+/* For only external static functions */
+static struct abox_data *p_abox_data;
+
+struct abox_data *abox_get_abox_data(void)
+{
+       return p_abox_data;
+}
+
+static int abox_iommu_fault_handler(
+               struct iommu_domain *domain, struct device *dev,
+               unsigned long fault_addr, int fault_flags, void *token)
+{
+       struct abox_data *data = token;
+
+       abox_dbg_print_gpr(&data->pdev->dev, data);
+       return 0;
+}
+
+static void abox_cpu_power(bool on);
+static int abox_cpu_enable(bool enable);
+static int abox_cpu_pm_ipc(struct device *dev, bool resume);
+static void abox_boot_done(struct device *dev, unsigned int version);
+static int abox_enable(struct device *dev);
+
+static void exynos_abox_panic_handler(void)
+{
+       static bool has_run;
+       struct abox_data *data = p_abox_data;
+       struct device *dev = data ? (data->pdev ? &data->pdev->dev : NULL) :
+                       NULL;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (abox_is_on() && dev) {
+               if (has_run) {
+                       dev_info(dev, "already dumped\n");
+                       return;
+               }
+               has_run = true;
+
+               abox_dbg_dump_gpr(dev, data, ABOX_DBG_DUMP_KERNEL, "panic");
+               abox_cpu_pm_ipc(dev, false);
+               writel(0x504E4943, data->sram_base + data->sram_size -
+                               sizeof(u32));
+               abox_cpu_enable(false);
+               abox_cpu_power(false);
+               abox_cpu_power(true);
+               abox_cpu_enable(true);
+               mdelay(100);
+               abox_dbg_dump_mem(dev, data, ABOX_DBG_DUMP_KERNEL, "panic");
+       } else {
+               dev_info(dev, "%s: dump is skipped due to no power\n",
+                               __func__);
+       }
+}
+
+static int abox_panic_handler(struct notifier_block *nb,
+                              unsigned long action, void *data)
+{
+       exynos_abox_panic_handler();
+       return NOTIFY_OK;
+}
+
+static struct notifier_block abox_panic_notifier = {
+       .notifier_call  = abox_panic_handler,
+       .next           = NULL,
+       .priority       = 0     /* priority: INT_MAX >= x >= 0 */
+};
+
+static struct platform_driver samsung_abox_driver;
+static bool is_abox(struct device *dev)
+{
+       return (&samsung_abox_driver.driver) == dev->driver;
+}
+
+static void abox_probe_quirks(struct abox_data *data, struct device_node *np)
+{
+       #define QUIRKS "quirks"
+       #define DEC_MAP(id) {ABOX_QUIRK_STR_##id, ABOX_QUIRK_##id}
+
+       static const struct {
+               const char *str;
+               unsigned int bit;
+       } map[] = {
+               DEC_MAP(TRY_TO_ASRC_OFF),
+               DEC_MAP(SHARE_VTS_SRAM),
+               DEC_MAP(OFF_ON_SUSPEND),
+               DEC_MAP(SCSC_BT),
+               DEC_MAP(SCSC_BT_HACK),
+       };
+
+       int i, ret;
+
+       for (i = 0; i < ARRAY_SIZE(map); i++) {
+               ret = of_property_match_string(np, QUIRKS, map[i].str);
+               if (ret >= 0)
+                       data->quirks |= map[i].bit;
+       }
+}
+
+
+int abox_disable_qchannel(struct device *dev, struct abox_data *data,
+               enum qchannel clk, int disable)
+{
+       return regmap_update_bits(data->regmap, ABOX_QCHANNEL_DISABLE,
+                       ABOX_QCHANNEL_DISABLE_MASK(clk),
+                       !!disable << ABOX_QCHANNEL_DISABLE_L(clk));
+}
+
+phys_addr_t abox_addr_to_phys_addr(struct abox_data *data, unsigned int addr)
+{
+       phys_addr_t ret;
+
+       if (addr < IOVA_DRAM_FIRMWARE)
+               ret = data->sram_base_phys + addr;
+       else
+               ret = iommu_iova_to_phys(data->iommu_domain, addr);
+
+       return ret;
+}
+
+void *abox_addr_to_kernel_addr(struct abox_data *data, unsigned int addr)
+{
+       void *ret;
+
+       if (addr < IOVA_DRAM_FIRMWARE)
+               ret = data->sram_base + addr;
+       else if (addr >= IOVA_DRAM_FIRMWARE && addr < IOVA_IVA_FIRMWARE)
+               ret = data->dram_base + (addr - IOVA_DRAM_FIRMWARE);
+       else if (addr >= IOVA_IVA_FIRMWARE && addr < IOVA_VSS_FIRMWARE)
+               ret = data->iva_base + (addr - IOVA_IVA_FIRMWARE);
+       else if (addr >= IOVA_VSS_FIRMWARE && addr <  IOVA_DUMP_BUFFER)
+               ret = phys_to_virt(shm_get_vss_base() +
+                               (addr - IOVA_VSS_FIRMWARE));
+       else if (addr >= IOVA_DUMP_BUFFER)
+               ret = data->dump_base + (addr - IOVA_DUMP_BUFFER);
+       else
+               ret = phys_to_virt(abox_addr_to_phys_addr(data, addr));
+
+       return ret;
+}
+
+phys_addr_t abox_iova_to_phys(struct device *dev, unsigned long iova)
+{
+       return abox_addr_to_phys_addr(dev_get_drvdata(dev), iova);
+}
+EXPORT_SYMBOL(abox_iova_to_phys);
+
+void *abox_iova_to_virt(struct device *dev, unsigned long iova)
+{
+       return abox_addr_to_kernel_addr(dev_get_drvdata(dev), iova);
+}
+EXPORT_SYMBOL(abox_iova_to_virt);
+
+static int abox_sif_idx(enum ABOX_CONFIGMSG configmsg)
+{
+       return configmsg - ((configmsg < SET_MIXER_FORMAT) ?
+                       SET_MIXER_SAMPLE_RATE : SET_MIXER_FORMAT);
+}
+
+static unsigned int abox_get_sif_rate_min(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       return data->sif_rate_min[abox_sif_idx(configmsg)];
+}
+
+static void abox_set_sif_rate_min(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg, unsigned int val)
+{
+       data->sif_rate_min[abox_sif_idx(configmsg)] = val;
+}
+
+static snd_pcm_format_t abox_get_sif_format_min(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       return data->sif_format_min[abox_sif_idx(configmsg)];
+}
+
+static void abox_set_sif_format_min(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg, snd_pcm_format_t val)
+{
+       data->sif_format_min[abox_sif_idx(configmsg)] = val;
+}
+
+static int abox_get_sif_width_min(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       return snd_pcm_format_width(abox_get_sif_format_min(data, configmsg));
+}
+
+static void abox_set_sif_width_min(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg, int width)
+{
+       struct device *dev = &data->pdev->dev;
+       snd_pcm_format_t format = SNDRV_PCM_FORMAT_S16;
+
+       switch (width) {
+       case 16:
+               format = SNDRV_PCM_FORMAT_S16;
+               break;
+       case 24:
+               format = SNDRV_PCM_FORMAT_S24;
+               break;
+       case 32:
+               format = SNDRV_PCM_FORMAT_S32;
+               break;
+       default:
+               dev_warn(dev, "%s(%d): invalid argument\n", __func__, width);
+       }
+
+       abox_set_sif_format_min(data, configmsg, format);
+}
+
+static unsigned int abox_get_sif_channels_min(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       return data->sif_channels_min[abox_sif_idx(configmsg)];
+}
+
+static void __maybe_unused abox_set_sif_channels_min(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg, unsigned int val)
+{
+       data->sif_channels_min[abox_sif_idx(configmsg)] = val;
+}
+
+static bool abox_get_sif_auto_config(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       return data->sif_auto_config[abox_sif_idx(configmsg)];
+}
+
+static void abox_set_sif_auto_config(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg, bool val)
+{
+       data->sif_auto_config[abox_sif_idx(configmsg)] = val;
+}
+
+static unsigned int abox_get_sif_rate(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       unsigned int val = data->sif_rate[abox_sif_idx(configmsg)];
+       unsigned int min = abox_get_sif_rate_min(data, configmsg);
+
+       return (abox_get_sif_auto_config(data, configmsg) && (min > val)) ?
+                       min : val;
+}
+
+static void abox_set_sif_rate(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg, unsigned int val)
+{
+       data->sif_rate[abox_sif_idx(configmsg)] = val;
+}
+
+static snd_pcm_format_t abox_get_sif_format(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       snd_pcm_format_t val = data->sif_format[abox_sif_idx(configmsg)];
+       snd_pcm_format_t min = abox_get_sif_format_min(data, configmsg);
+
+       return (abox_get_sif_auto_config(data, configmsg) && (min > val)) ?
+                       min : val;
+}
+
+static void abox_set_sif_format(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg, snd_pcm_format_t val)
+{
+       data->sif_format[abox_sif_idx(configmsg)] = val;
+}
+
+static int abox_get_sif_physical_width(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       snd_pcm_format_t format = abox_get_sif_format(data, configmsg);
+
+       return snd_pcm_format_physical_width(format);
+}
+
+static int abox_get_sif_width(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       return snd_pcm_format_width(abox_get_sif_format(data, configmsg));
+}
+
+static void abox_set_sif_width(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg, int width)
+{
+       struct device *dev = &data->pdev->dev;
+       snd_pcm_format_t format = SNDRV_PCM_FORMAT_S16;
+
+       switch (width) {
+       case 16:
+               format = SNDRV_PCM_FORMAT_S16;
+               break;
+       case 24:
+               format = SNDRV_PCM_FORMAT_S24;
+               break;
+       case 32:
+               format = SNDRV_PCM_FORMAT_S32;
+               break;
+       default:
+               dev_err(dev, "%s(%d): invalid argument\n", __func__, width);
+       }
+
+       abox_set_sif_format(data, configmsg, format);
+}
+
+static unsigned int abox_get_sif_channels(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       unsigned int val = data->sif_channels[abox_sif_idx(configmsg)];
+       unsigned int min = abox_get_sif_channels_min(data, configmsg);
+
+       return (abox_get_sif_auto_config(data, configmsg) && (min > val)) ?
+                       min : val;
+}
+
+static void abox_set_sif_channels(struct abox_data *data,
+               enum ABOX_CONFIGMSG configmsg, unsigned int val)
+{
+       data->sif_channels[abox_sif_idx(configmsg)] = val;
+}
+
+static bool __abox_ipc_queue_empty(struct abox_data *data)
+{
+       return (data->ipc_queue_end == data->ipc_queue_start);
+}
+
+static bool __abox_ipc_queue_full(struct abox_data *data)
+{
+       size_t length = ARRAY_SIZE(data->ipc_queue);
+
+       return (((data->ipc_queue_end + 1) % length) == data->ipc_queue_start);
+}
+
+static int abox_ipc_queue_put(struct abox_data *data, struct device *dev,
+               int hw_irq, const void *supplement, size_t size)
+{
+       spinlock_t *lock = &data->ipc_queue_lock;
+       size_t length = ARRAY_SIZE(data->ipc_queue);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(lock, flags);
+       if (!__abox_ipc_queue_full(data)) {
+               struct abox_ipc *ipc;
+
+               ipc = &data->ipc_queue[data->ipc_queue_end];
+               ipc->dev = dev;
+               ipc->hw_irq = hw_irq;
+               ipc->put_time = sched_clock();
+               ipc->get_time = 0;
+               memcpy(&ipc->msg, supplement, size);
+               data->ipc_queue_end = (data->ipc_queue_end + 1) % length;
+
+               ret = 0;
+       } else {
+               ret = -EBUSY;
+       }
+       spin_unlock_irqrestore(lock, flags);
+
+       return ret;
+}
+
+static int abox_ipc_queue_get(struct abox_data *data, struct abox_ipc *ipc)
+{
+       spinlock_t *lock = &data->ipc_queue_lock;
+       size_t length = ARRAY_SIZE(data->ipc_queue);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(lock, flags);
+       if (!__abox_ipc_queue_empty(data)) {
+               struct abox_ipc *tmp;
+
+               tmp = &data->ipc_queue[data->ipc_queue_start];
+               tmp->get_time = sched_clock();
+               *ipc = *tmp;
+               data->ipc_queue_start = (data->ipc_queue_start + 1) % length;
+
+               ret = 0;
+       } else {
+               ret = -ENODATA;
+       }
+       spin_unlock_irqrestore(lock, flags);
+
+       return ret;
+}
+
+static bool abox_can_calliope_ipc(struct device *dev,
+               struct abox_data *data)
+{
+       bool ret = true;
+
+       switch (data->calliope_state) {
+       case CALLIOPE_DISABLING:
+       case CALLIOPE_ENABLED:
+               break;
+       case CALLIOPE_ENABLING:
+               wait_event_timeout(data->ipc_wait_queue,
+                               data->calliope_state == CALLIOPE_ENABLED,
+                               msecs_to_jiffies(CALLIOPE_ENABLE_TIMEOUT_MS));
+               if (data->calliope_state == CALLIOPE_ENABLED)
+                       break;
+               /* Fallthrough */
+       case CALLIOPE_DISABLED:
+       default:
+               dev_warn(dev, "Invalid calliope state: %d\n",
+                               data->calliope_state);
+               ret = false;
+       }
+
+       dev_dbg(dev, "%s: %d\n", __func__, ret);
+
+       return ret;
+}
+
+static int __abox_process_ipc(struct device *dev, struct abox_data *data,
+               int hw_irq, const ABOX_IPC_MSG *msg)
+{
+       static unsigned int tout_cnt;
+       static DEFINE_SPINLOCK(lock);
+
+       void __iomem *tx_base = data->sram_base + data->ipc_tx_offset;
+       void __iomem *tx_ack = data->sram_base + data->ipc_tx_ack_offset;
+       int ret, i;
+
+       dev_dbg(dev, "%s(%d, %d, %d)\n", __func__, hw_irq,
+                       msg->ipcid, msg->msg.system.msgtype);
+
+       do {
+               spin_lock(&lock);
+
+               memcpy_toio(tx_base, msg, sizeof(*msg));
+               writel(1, tx_ack);
+               abox_gic_generate_interrupt(data->dev_gic, hw_irq);
+               for (i = IPC_TIMEOUT_US; i && readl(tx_ack); i--)
+                       udelay(1);
+
+               if (readl(tx_ack)) {
+                       tout_cnt++;
+                       dev_warn_ratelimited(dev, "Transaction timeout(%d)\n",
+                                       tout_cnt);
+
+                       if (tout_cnt == 1)
+                               abox_dbg_dump_simple(dev, data,
+                                               "Transaction timeout");
+
+                       if ((tout_cnt % IPC_RETRY) == 0) {
+                               abox_failsafe_report(dev);
+                               writel(0, tx_ack);
+                       }
+
+                       ret = -EIO;
+               } else {
+                       tout_cnt = 0;
+                       ret = 0;
+               }
+               spin_unlock(&lock);
+       } while (readl(tx_ack));
+
+       return ret;
+}
+
+static void abox_process_ipc(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data, ipc_work);
+       struct device *dev = &data->pdev->dev;
+       struct abox_ipc ipc;
+
+       dev_dbg(dev, "%s: %d %d\n", __func__, data->ipc_queue_start,
+                       data->ipc_queue_end);
+
+       pm_runtime_get_sync(dev);
+
+       if (abox_can_calliope_ipc(dev, data)) {
+               while (abox_ipc_queue_get(data, &ipc) == 0) {
+                       struct device *dev = ipc.dev;
+                       int hw_irq = ipc.hw_irq;
+                       ABOX_IPC_MSG *msg = &ipc.msg;
+
+                       __abox_process_ipc(dev, data, hw_irq, msg);
+
+                       /* giving time to ABOX for processing */
+                       usleep_range(10, 100);
+               }
+       }
+
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+}
+
+static int abox_schedule_ipc(struct device *dev, struct abox_data *data,
+               int hw_irq, const void *supplement, size_t size,
+               bool atomic, bool sync)
+{
+       struct abox_ipc *ipc;
+       int retry = 0;
+       int ret;
+
+       dev_dbg(dev, "%s(%d, %p, %zu, %d, %d)\n", __func__, hw_irq, supplement,
+                       size, atomic, sync);
+
+       if (unlikely(sizeof(ipc->msg) < size)) {
+               dev_err(dev, "%s: too large supplement\n", __func__);
+               return -EINVAL;
+       }
+
+       do {
+               ret = abox_ipc_queue_put(data, dev, hw_irq, supplement, size);
+               queue_work(data->ipc_workqueue, &data->ipc_work);
+               if (!atomic && sync)
+                       flush_work(&data->ipc_work);
+               if (ret >= 0)
+                       break;
+
+               if (!atomic) {
+                       dev_info(dev, "%s: flush(%d)\n", __func__, retry);
+                       flush_work(&data->ipc_work);
+               } else {
+                       dev_info(dev, "%s: delay(%d)\n", __func__, retry);
+                       mdelay(10);
+               }
+       } while (retry++ < IPC_RETRY);
+
+       if (ret < 0) {
+               dev_err(dev, "%s(%d): ipc queue overflow\n", __func__, hw_irq);
+               abox_failsafe_report(dev);
+       }
+
+       return ret;
+}
+
+int abox_request_ipc(struct device *dev,
+               int hw_irq, const void *supplement,
+               size_t size, int atomic, int sync)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       int ret;
+
+       if (atomic && sync) {
+               ret = __abox_process_ipc(dev, data, hw_irq, supplement);
+       } else {
+               ret = abox_schedule_ipc(dev, data, hw_irq, supplement, size,
+                               !!atomic, !!sync);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(abox_request_ipc);
+
+bool abox_is_on(void)
+{
+       return p_abox_data && p_abox_data->enabled;
+}
+EXPORT_SYMBOL(abox_is_on);
+
+int abox_register_bclk_usage(struct device *dev, struct abox_data *data,
+               enum abox_dai dai_id, unsigned int rate, unsigned int channels,
+               unsigned int width)
+{
+       unsigned long target_pll, audif_rate;
+       int id = dai_id - ABOX_UAIF0;
+       int ret = 0;
+       int i;
+
+       dev_dbg(dev, "%s(%d, %d)\n", __func__, id, rate);
+
+       if (id < 0 || id >= ABOX_DAI_COUNT) {
+               dev_err(dev, "invalid dai_id: %d\n", dai_id);
+               return -EINVAL;
+       }
+
+       if (rate == 0) {
+               data->audif_rates[id] = 0;
+               return 0;
+       }
+
+       target_pll = ((rate % 44100) == 0) ? AUD_PLL_RATE_HZ_FOR_44100 :
+                       AUD_PLL_RATE_HZ_FOR_48000;
+       if (target_pll != clk_get_rate(data->clk_pll)) {
+               dev_info(dev, "Set AUD_PLL rate: %lu -> %lu\n",
+                       clk_get_rate(data->clk_pll), target_pll);
+               ret = clk_set_rate(data->clk_pll, target_pll);
+               if (ret < 0) {
+                       dev_err(dev, "AUD_PLL set error=%d\n", ret);
+                       return ret;
+               }
+       }
+
+       if (data->uaif_max_div <= 32) {
+               if ((rate % 44100) == 0)
+                       audif_rate = ((rate > 176400) ? 352800 : 176400) *
+                                       width * 2;
+               else
+                       audif_rate = ((rate > 192000) ? 384000 : 192000) *
+                                       width * 2;
+
+               while (audif_rate / rate / channels / width >
+                               data->uaif_max_div)
+                       audif_rate /= 2;
+       } else {
+               int clk_width = 96; /* LCM of 24 and 32 */
+               int clk_channels = 2;
+
+               if ((rate % 44100) == 0)
+                       audif_rate = 352800 * clk_width * clk_channels;
+               else
+                       audif_rate = 384000 * clk_width * clk_channels;
+
+               if (audif_rate < rate * width * channels)
+                       audif_rate = rate * width * channels;
+       }
+
+       data->audif_rates[id] = audif_rate;
+
+       for (i = 0; i < ARRAY_SIZE(data->audif_rates); i++) {
+               if (data->audif_rates[i] > 0 &&
+                               data->audif_rates[i] > audif_rate) {
+                       audif_rate = data->audif_rates[i];
+               }
+       }
+
+       ret = clk_set_rate(data->clk_audif, audif_rate);
+       if (ret < 0)
+               dev_err(dev, "Failed to set audif clock: %d\n", ret);
+
+       dev_info(dev, "audif clock: %lu\n", clk_get_rate(data->clk_audif));
+
+       return ret;
+}
+
+static int abox_sif_format_put_ipc(struct device *dev, snd_pcm_format_t format,
+               int channels, enum ABOX_CONFIGMSG configmsg)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       ABOX_IPC_MSG msg;
+       struct IPC_ABOX_CONFIG_MSG *abox_config_msg = &msg.msg.config;
+       int width = snd_pcm_format_width(format);
+       int ret;
+
+       dev_dbg(dev, "%s(%d, %d, %d)\n", __func__, width, channels, configmsg);
+
+       abox_set_sif_format(data, configmsg, format);
+       abox_set_sif_channels(data, configmsg, channels);
+
+       /* update manually for regmap cache sync */
+       switch (configmsg) {
+       case SET_MIXER_SAMPLE_RATE:
+       case SET_MIXER_FORMAT:
+               regmap_update_bits(data->regmap, ABOX_SPUS_CTRL1,
+                               ABOX_SPUS_MIXP_FORMAT_MASK,
+                               abox_get_format(width, channels) <<
+                               ABOX_SPUS_MIXP_FORMAT_L);
+               break;
+       case SET_RECP_SAMPLE_RATE:
+       case SET_RECP_FORMAT:
+               regmap_update_bits(data->regmap, ABOX_SPUM_CTRL1,
+                               ABOX_RECP_SRC_FORMAT_MASK,
+                               abox_get_format(width, channels) <<
+                               ABOX_RECP_SRC_FORMAT_L);
+               break;
+       default:
+               /* Nothing to do */
+               break;
+       }
+
+       msg.ipcid = IPC_ABOX_CONFIG;
+       abox_config_msg->param1 = abox_get_sif_width(data, configmsg);
+       abox_config_msg->param2 = abox_get_sif_channels(data, configmsg);
+       abox_config_msg->msgtype = configmsg;
+       ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
+       if (ret < 0)
+               dev_err(dev, "%d(%d bit, %d channels) failed: %d\n", configmsg,
+                               width, channels, ret);
+
+       return ret;
+}
+
+static unsigned int abox_sifsx_cnt_val(unsigned long aclk, unsigned int rate,
+               unsigned int physical_width, unsigned int channels)
+{
+       static const int correction = -2;
+       unsigned int n, d;
+
+       /* k = n / d */
+       d = channels;
+       n = 2 * (32 / physical_width);
+
+       return ((aclk * n) / d / rate) - 5 + correction;
+}
+
+static int abox_sifs_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *be = substream->private_data;
+       struct snd_soc_dpcm *dpcm;
+       int stream = substream->stream;
+       struct device *dev = dai->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
+       enum abox_dai id = dai->id;
+       unsigned int rate = params_rate(params);
+       unsigned int width = params_width(params);
+       unsigned int pwidth = params_physical_width(params);
+       unsigned int channels = params_channels(params);
+       unsigned long aclk;
+       unsigned int cnt_val;
+       bool skip = true;
+       int ret = 0;
+
+       if (stream != SNDRV_PCM_STREAM_CAPTURE)
+               goto out;
+
+       /* sifs count is needed only when SIFS is connected to NSRC */
+       list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
+               if (dpcm->fe->cpu_dai->id != ABOX_WDMA0) {
+                       skip = false;
+                       break;
+               }
+       }
+       if (skip)
+               goto out;
+
+       abox_request_cpu_gear_sync(dev, data, substream, ABOX_CPU_GEAR_MAX);
+       aclk = clk_get_rate(data->clk_bus);
+       cnt_val = abox_sifsx_cnt_val(aclk, rate, pwidth, channels);
+
+       dev_info(dev, "%s[%d](%ubit %uchannel %uHz at %luHz): %u\n",
+                       __func__, id, width, channels, rate, aclk, cnt_val);
+
+       switch (id) {
+       case ABOX_SIFS0:
+               ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
+                       ABOX_SIFS0_CNT_VAL_MASK,
+                       cnt_val << ABOX_SIFS0_CNT_VAL_L);
+               break;
+       case ABOX_SIFS1:
+               ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
+                       ABOX_SIFS1_CNT_VAL_MASK,
+                       cnt_val << ABOX_SIFS1_CNT_VAL_L);
+               break;
+       case ABOX_SIFS2:
+               ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT1,
+                       ABOX_SIFS2_CNT_VAL_MASK,
+                       cnt_val << ABOX_SIFS2_CNT_VAL_L);
+               break;
+       default:
+               dev_err(dev, "%s: invalid id(%d)\n", __func__, id);
+               ret = -EINVAL;
+               break;
+       }
+out:
+       return ret;
+}
+
+static int abox_sifs_hw_free(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
+       enum abox_dai id = dai->id;
+       int ret = 0;
+
+       if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
+               goto out;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       switch (id) {
+       case ABOX_SIFS0:
+               ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
+                       ABOX_SIFS0_CNT_VAL_MASK, 0);
+               break;
+       case ABOX_SIFS1:
+               ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
+                       ABOX_SIFS1_CNT_VAL_MASK, 0);
+               break;
+       case ABOX_SIFS2:
+               ret = regmap_update_bits(regmap, ABOX_SPUS_CTRL_SIFS_CNT1,
+                       ABOX_SIFS2_CNT_VAL_MASK, 0);
+               break;
+       default:
+               dev_err(dev, "%s: invalid id(%d)\n", __func__, id);
+               ret = -EINVAL;
+               break;
+       }
+
+       abox_request_cpu_gear(dev, data, substream, ABOX_CPU_GEAR_MIN);
+out:
+       return ret;
+}
+
+static const struct snd_soc_dai_ops abox_sifs_dai_ops = {
+       .hw_params      = abox_sifs_hw_params,
+       .hw_free        = abox_sifs_hw_free,
+};
+
+static struct snd_soc_dai_driver abox_dais[] = {
+       {
+               .name = "RDMA0",
+               .id = ABOX_RDMA0,
+               .playback = {
+                       .stream_name = "RDMA0 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "RDMA1",
+               .id = ABOX_RDMA1,
+               .playback = {
+                       .stream_name = "RDMA1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "RDMA2",
+               .id = ABOX_RDMA2,
+               .playback = {
+                       .stream_name = "RDMA2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "RDMA3",
+               .id = ABOX_RDMA3,
+               .playback = {
+                       .stream_name = "RDMA3 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "RDMA4",
+               .id = ABOX_RDMA4,
+               .playback = {
+                       .stream_name = "RDMA4 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "RDMA5",
+               .id = ABOX_RDMA5,
+               .playback = {
+                       .stream_name = "RDMA5 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+               .compress_new = snd_soc_new_compress,
+       },
+       {
+               .name = "RDMA6",
+               .id = ABOX_RDMA6,
+               .playback = {
+                       .stream_name = "RDMA6 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "RDMA7",
+               .id = ABOX_RDMA7,
+               .playback = {
+                       .stream_name = "RDMA7 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "WDMA0",
+               .id = ABOX_WDMA0,
+               .capture = {
+                       .stream_name = "WDMA0 Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_WDMA_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "WDMA1",
+               .id = ABOX_WDMA1,
+               .capture = {
+                       .stream_name = "WDMA1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_WDMA_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "WDMA2",
+               .id = ABOX_WDMA2,
+               .capture = {
+                       .stream_name = "WDMA2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_WDMA_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "WDMA3",
+               .id = ABOX_WDMA3,
+               .capture = {
+                       .stream_name = "WDMA3 Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_WDMA_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "WDMA4",
+               .id = ABOX_WDMA4,
+               .capture = {
+                       .stream_name = "WDMA4 Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_WDMA_SAMPLE_FORMATS,
+               },
+       },
+       {
+               .name = "SIFS0",
+               .id = ABOX_SIFS0,
+               .playback = {
+                       .stream_name = "SIFS0 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "SIFS0 Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+               .ops = &abox_sifs_dai_ops,
+               .symmetric_rates = 1,
+               .symmetric_channels = 1,
+               .symmetric_samplebits = 1,
+       },
+       {
+               .name = "SIFS1",
+               .id = ABOX_SIFS1,
+               .playback = {
+                       .stream_name = "SIFS1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "SIFS1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+               .ops = &abox_sifs_dai_ops,
+               .symmetric_rates = 1,
+               .symmetric_channels = 1,
+               .symmetric_samplebits = 1,
+       },
+       {
+               .name = "SIFS2",
+               .id = ABOX_SIFS2,
+               .playback = {
+                       .stream_name = "SIFS2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "SIFS2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = ABOX_SAMPLING_RATES,
+                       .rate_min = 8000,
+                       .rate_max = 384000,
+                       .formats = ABOX_SAMPLE_FORMATS,
+               },
+               .ops = &abox_sifs_dai_ops,
+               .symmetric_rates = 1,
+               .symmetric_channels = 1,
+               .symmetric_samplebits = 1,
+       },
+};
+
+static int abox_cmpnt_probe(struct snd_soc_component *component)
+{
+       struct device *dev = component->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+
+       dev_info(dev, "%s\n", __func__);
+
+       data->cmpnt = component;
+       snd_soc_component_update_bits(component, ABOX_SPUM_CTRL0,
+                       ABOX_FUNC_CHAIN_RSRC_RECP_MASK,
+                       ABOX_FUNC_CHAIN_RSRC_RECP_MASK);
+
+       return 0;
+}
+
+static void abox_cmpnt_remove(struct snd_soc_component *component)
+{
+       struct device *dev = component->dev;
+
+       dev_info(dev, "%s\n", __func__);
+
+       snd_soc_component_exit_regmap(component);
+}
+
+static int abox_sample_rate_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = abox_get_sif_rate(data, reg);
+
+       dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
+
+       ucontrol->value.integer.value[0] = val;
+
+       return 0;
+}
+
+static int abox_sample_rate_put_ipc(struct device *dev, unsigned int val,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       ABOX_IPC_MSG msg;
+       struct IPC_ABOX_CONFIG_MSG *abox_config_msg = &msg.msg.config;
+       int ret;
+
+       dev_dbg(dev, "%s(%u, 0x%08x)\n", __func__, val, configmsg);
+
+       abox_set_sif_rate(data, configmsg, val);
+
+       msg.ipcid = IPC_ABOX_CONFIG;
+       abox_config_msg->param1 = abox_get_sif_rate(data, configmsg);
+       abox_config_msg->msgtype = configmsg;
+       ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
+       if (ret < 0) {
+               dev_err(dev, "%s(%u, 0x%08x) failed: %d\n", __func__, val,
+                               configmsg, ret);
+       }
+
+       return ret;
+}
+
+static int abox_sample_rate_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
+
+       dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
+
+       return abox_sample_rate_put_ipc(dev, val, reg);
+}
+
+static int abox_bit_width_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = abox_get_sif_width(data, reg);
+
+       dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
+
+       ucontrol->value.integer.value[0] = val;
+
+       return 0;
+}
+
+static int abox_bit_width_put_ipc(struct device *dev, unsigned int val,
+               enum ABOX_CONFIGMSG configmsg)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       snd_pcm_format_t format = SNDRV_PCM_FORMAT_S16;
+       int channels = data->sif_channels[abox_sif_idx(configmsg)];
+
+       dev_dbg(dev, "%s(%u, 0x%08x)\n", __func__, val, configmsg);
+
+       switch (val) {
+       case 16:
+               format = SNDRV_PCM_FORMAT_S16;
+               break;
+       case 24:
+               format = SNDRV_PCM_FORMAT_S24;
+               break;
+       case 32:
+               format = SNDRV_PCM_FORMAT_S32;
+               break;
+       default:
+               dev_warn(dev, "%s(%u, 0x%08x) invalid argument\n", __func__,
+                               val, configmsg);
+               break;
+       }
+
+       return abox_sif_format_put_ipc(dev, format, channels, configmsg);
+}
+
+static int abox_bit_width_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
+
+       dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
+
+       return abox_bit_width_put_ipc(dev, val, reg);
+}
+
+static int abox_sample_rate_min_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = abox_get_sif_rate_min(data, reg);
+
+       dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
+
+       ucontrol->value.integer.value[0] = val;
+
+       return 0;
+}
+
+static int abox_sample_rate_min_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
+
+       dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
+
+       abox_set_sif_rate_min(data, reg, val);
+
+       return 0;
+}
+
+static int abox_bit_width_min_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = abox_get_sif_width_min(data, reg);
+
+       dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
+
+       ucontrol->value.integer.value[0] = val;
+
+       return 0;
+}
+
+static int abox_bit_width_min_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
+
+       dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
+
+       abox_set_sif_width_min(data, reg, val);
+
+       return 0;
+}
+
+static int abox_auto_config_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = abox_get_sif_auto_config(data, reg);
+
+       dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
+
+       ucontrol->value.integer.value[0] = val;
+
+       return 0;
+}
+
+static int abox_auto_config_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
+
+       dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
+
+       abox_set_sif_auto_config(data, reg, !!val);
+
+       return 0;
+}
+
+static int abox_erap_handler_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+                       (struct soc_mixer_control *)kcontrol->private_value;
+       enum ABOX_ERAP_TYPE type = (enum ABOX_ERAP_TYPE)mc->reg;
+
+       dev_dbg(dev, "%s(%d)\n", __func__, type);
+
+       ucontrol->value.integer.value[0] = data->erap_status[type];
+
+       return 0;
+}
+
+static int abox_erap_handler_put_ipc(struct device *dev,
+               enum ABOX_ERAP_TYPE type, unsigned int val)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       ABOX_IPC_MSG msg;
+       struct IPC_ERAP_MSG *erap_msg = &msg.msg.erap;
+       struct ERAP_ONOFF_PARAM *erap_param = &erap_msg->param.onoff;
+       int ret;
+
+       dev_dbg(dev, "%s(%u, %d)\n", __func__, val, type);
+
+       msg.ipcid = IPC_ERAP;
+       erap_msg->msgtype = val ? REALTIME_OPEN : REALTIME_CLOSE;
+       erap_param->type = type;
+       erap_param->channel_no = 0;
+       erap_param->version = val;
+       ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
+       if (ret < 0)
+               dev_err(dev, "erap control failed(type:%d, status:%d)\n",
+                               type, val);
+
+       data->erap_status[type] = val;
+
+       return ret;
+}
+
+static int abox_erap_handler_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct soc_mixer_control *mc =
+                       (struct soc_mixer_control *)kcontrol->private_value;
+       enum ABOX_ERAP_TYPE type = (enum ABOX_ERAP_TYPE)mc->reg;
+       unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
+
+       dev_info(dev, "%s(%u, %d)\n", __func__, val, type);
+
+       return abox_erap_handler_put_ipc(dev, type, val);
+}
+
+static int abox_audio_mode_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int item;
+
+       dev_dbg(dev, "%s: %u\n", __func__, data->audio_mode);
+
+       item = snd_soc_enum_val_to_item(e, data->audio_mode);
+       ucontrol->value.enumerated.item[0] = item;
+
+       return 0;
+}
+
+static int abox_audio_mode_put_ipc(struct device *dev, enum audio_mode mode)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+
+       dev_dbg(dev, "%s(%d)\n", __func__, mode);
+
+       msg.ipcid = IPC_SYSTEM;
+       system_msg->msgtype = ABOX_SET_MODE;
+       system_msg->param1 = data->audio_mode = mode;
+
+       return abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
+}
+
+static int abox_audio_mode_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int *item = ucontrol->value.enumerated.item;
+       enum audio_mode mode;
+
+       if (item[0] >= e->items)
+               return -EINVAL;
+
+       mode = snd_soc_enum_item_to_val(e, item[0]);
+       dev_info(dev, "%s(%u)\n", __func__, mode);
+
+       return abox_audio_mode_put_ipc(dev, mode);
+}
+
+static const char * const abox_audio_mode_enum_texts[] = {
+       "NORMAL",
+       "RINGTONE",
+       "IN_CALL",
+       "IN_COMMUNICATION",
+       "IN_VIDEOCALL",
+};
+static const unsigned int abox_audio_mode_enum_values[] = {
+       MODE_NORMAL,
+       MODE_RINGTONE,
+       MODE_IN_CALL,
+       MODE_IN_COMMUNICATION,
+       MODE_IN_VIDEOCALL,
+};
+SOC_VALUE_ENUM_SINGLE_DECL(abox_audio_mode_enum, SND_SOC_NOPM, 0, 0,
+               abox_audio_mode_enum_texts, abox_audio_mode_enum_values);
+
+static int abox_sound_type_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int item;
+
+       dev_dbg(dev, "%s: %u\n", __func__, data->sound_type);
+
+       item = snd_soc_enum_val_to_item(e, data->sound_type);
+       ucontrol->value.enumerated.item[0] = item;
+
+       return 0;
+}
+
+static int abox_sound_type_put_ipc(struct device *dev, enum sound_type type)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+
+       dev_dbg(dev, "%s(%d)\n", __func__, type);
+
+       msg.ipcid = IPC_SYSTEM;
+       system_msg->msgtype = ABOX_SET_TYPE;
+       system_msg->param1 = data->sound_type = type;
+
+       return abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
+}
+
+static int abox_sound_type_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int *item = ucontrol->value.enumerated.item;
+       enum sound_type type;
+
+       if (item[0] >= e->items)
+               return -EINVAL;
+
+       type = snd_soc_enum_item_to_val(e, item[0]);
+       dev_info(dev, "%s(%d)\n", __func__, type);
+
+       return abox_sound_type_put_ipc(dev, type);
+}
+static const char * const abox_sound_type_enum_texts[] = {
+       "VOICE",
+       "SPEAKER",
+       "HEADSET",
+       "BTVOICE",
+       "USB",
+};
+static const unsigned int abox_sound_type_enum_values[] = {
+       SOUND_TYPE_VOICE,
+       SOUND_TYPE_SPEAKER,
+       SOUND_TYPE_HEADSET,
+       SOUND_TYPE_BTVOICE,
+       SOUND_TYPE_USB,
+};
+SOC_VALUE_ENUM_SINGLE_DECL(abox_sound_type_enum, SND_SOC_NOPM, 0, 0,
+               abox_sound_type_enum_texts, abox_sound_type_enum_values);
+
+static int abox_tickle_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       ucontrol->value.integer.value[0] = data->enabled;
+
+       return 0;
+}
+
+
+static void abox_tickle_work_func(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct abox_data *data = container_of(dwork, struct abox_data,
+                       tickle_work);
+       struct device *dev = &data->pdev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       pm_runtime_put(dev);
+}
+static DECLARE_DELAYED_WORK(abox_tickle_work, abox_tickle_work_func);
+
+static int abox_tickle_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       long val = ucontrol->value.integer.value[0];
+
+       dev_dbg(dev, "%s(%ld)\n", __func__, val);
+
+       if (!!val) {
+               pm_runtime_get(dev);
+               schedule_delayed_work(&data->tickle_work, 1 * HZ);
+       }
+
+       return 0;
+}
+
+static const struct snd_kcontrol_new abox_cmpnt_controls[] = {
+       SOC_SINGLE_EXT("Sampling Rate Mixer", SET_MIXER_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_get, abox_sample_rate_put),
+       SOC_SINGLE_EXT("Sampling Rate Out1", SET_OUT1_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_get, abox_sample_rate_put),
+       SOC_SINGLE_EXT("Sampling Rate Out2", SET_OUT2_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_get, abox_sample_rate_put),
+       SOC_SINGLE_EXT("Sampling Rate Recp", SET_RECP_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_get, abox_sample_rate_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux0", SET_INMUX0_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_get, abox_sample_rate_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux1", SET_INMUX1_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_get, abox_sample_rate_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux2", SET_INMUX2_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_get, abox_sample_rate_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux3", SET_INMUX3_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_get, abox_sample_rate_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux4", SET_INMUX4_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_get, abox_sample_rate_put),
+       SOC_SINGLE_EXT("Bit Width Mixer", SET_MIXER_FORMAT, 16, 32, 0,
+                       abox_bit_width_get, abox_bit_width_put),
+       SOC_SINGLE_EXT("Bit Width Out1", SET_OUT1_FORMAT, 16, 32, 0,
+                       abox_bit_width_get, abox_bit_width_put),
+       SOC_SINGLE_EXT("Bit Width Out2", SET_OUT2_FORMAT, 16, 32, 0,
+                       abox_bit_width_get, abox_bit_width_put),
+       SOC_SINGLE_EXT("Bit Width Recp", SET_RECP_FORMAT, 16, 32, 0,
+                       abox_bit_width_get, abox_bit_width_put),
+       SOC_SINGLE_EXT("Bit Width Inmux0", SET_INMUX0_FORMAT, 16, 32, 0,
+                       abox_bit_width_get, abox_bit_width_put),
+       SOC_SINGLE_EXT("Bit Width Inmux1", SET_INMUX1_FORMAT, 16, 32, 0,
+                       abox_bit_width_get, abox_bit_width_put),
+       SOC_SINGLE_EXT("Bit Width Inmux2", SET_INMUX2_FORMAT, 16, 32, 0,
+                       abox_bit_width_get, abox_bit_width_put),
+       SOC_SINGLE_EXT("Bit Width Inmux3", SET_INMUX3_FORMAT, 16, 32, 0,
+                       abox_bit_width_get, abox_bit_width_put),
+       SOC_SINGLE_EXT("Bit Width Inmux4", SET_INMUX4_FORMAT, 16, 32, 0,
+                       abox_bit_width_get, abox_bit_width_put),
+       SOC_SINGLE_EXT("Sampling Rate Mixer Min", SET_MIXER_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_min_get, abox_sample_rate_min_put),
+       SOC_SINGLE_EXT("Sampling Rate Out1 Min", SET_OUT1_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_min_get, abox_sample_rate_min_put),
+       SOC_SINGLE_EXT("Sampling Rate Out2 Min", SET_OUT2_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_min_get, abox_sample_rate_min_put),
+       SOC_SINGLE_EXT("Sampling Rate Recp Min", SET_RECP_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_min_get, abox_sample_rate_min_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux0 Min", SET_INMUX0_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_min_get, abox_sample_rate_min_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux1 Min", SET_INMUX1_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_min_get, abox_sample_rate_min_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux2 Min", SET_INMUX2_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_min_get, abox_sample_rate_min_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux3 Min", SET_INMUX3_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_min_get, abox_sample_rate_min_put),
+       SOC_SINGLE_EXT("Sampling Rate Inmux4 Min", SET_INMUX4_SAMPLE_RATE,
+                       8000, 384000, 0,
+                       abox_sample_rate_min_get, abox_sample_rate_min_put),
+       SOC_SINGLE_EXT("Bit Width Mixer Min", SET_MIXER_FORMAT, 16, 32, 0,
+                       abox_bit_width_min_get, abox_bit_width_min_put),
+       SOC_SINGLE_EXT("Bit Width Out1 Min", SET_OUT1_FORMAT, 16, 32, 0,
+                       abox_bit_width_min_get, abox_bit_width_min_put),
+       SOC_SINGLE_EXT("Bit Width Out2 Min", SET_OUT2_FORMAT, 16, 32, 0,
+                       abox_bit_width_min_get, abox_bit_width_min_put),
+       SOC_SINGLE_EXT("Bit Width Recp Min", SET_RECP_FORMAT, 16, 32, 0,
+                       abox_bit_width_min_get, abox_bit_width_min_put),
+       SOC_SINGLE_EXT("Bit Width Inmux0 Min", SET_INMUX0_FORMAT, 16, 32, 0,
+                       abox_bit_width_min_get, abox_bit_width_min_put),
+       SOC_SINGLE_EXT("Bit Width Inmux1 Min", SET_INMUX1_FORMAT, 16, 32, 0,
+                       abox_bit_width_min_get, abox_bit_width_min_put),
+       SOC_SINGLE_EXT("Bit Width Inmux2 Min", SET_INMUX2_FORMAT, 16, 32, 0,
+                       abox_bit_width_min_get, abox_bit_width_min_put),
+       SOC_SINGLE_EXT("Bit Width Inmux3 Min", SET_INMUX3_FORMAT, 16, 32, 0,
+                       abox_bit_width_min_get, abox_bit_width_min_put),
+       SOC_SINGLE_EXT("Bit Width Inmux4 Min", SET_INMUX4_FORMAT, 16, 32, 0,
+                       abox_bit_width_min_get, abox_bit_width_min_put),
+       SOC_SINGLE_EXT("Auto Config Mixer", SET_MIXER_SAMPLE_RATE, 0, 1, 0,
+                       abox_auto_config_get, abox_auto_config_put),
+       SOC_SINGLE_EXT("Auto Config Out1", SET_OUT1_SAMPLE_RATE, 0, 1, 0,
+                       abox_auto_config_get, abox_auto_config_put),
+       SOC_SINGLE_EXT("Auto Config Out2", SET_OUT2_SAMPLE_RATE, 0, 1, 0,
+                       abox_auto_config_get, abox_auto_config_put),
+       SOC_SINGLE_EXT("Auto Config Recp", SET_RECP_SAMPLE_RATE, 0, 1, 0,
+                       abox_auto_config_get, abox_auto_config_put),
+       SOC_SINGLE_EXT("Auto Config Inmux0", SET_INMUX0_SAMPLE_RATE, 0, 1, 0,
+                       abox_auto_config_get, abox_auto_config_put),
+       SOC_SINGLE_EXT("Auto Config Inmux1", SET_INMUX1_SAMPLE_RATE, 0, 1, 0,
+                       abox_auto_config_get, abox_auto_config_put),
+       SOC_SINGLE_EXT("Auto Config Inmux2", SET_INMUX2_SAMPLE_RATE, 0, 1, 0,
+                       abox_auto_config_get, abox_auto_config_put),
+       SOC_SINGLE_EXT("Auto Config Inmux3", SET_INMUX3_SAMPLE_RATE, 0, 1, 0,
+                       abox_auto_config_get, abox_auto_config_put),
+       SOC_SINGLE_EXT("Auto Config Inmux4", SET_INMUX4_SAMPLE_RATE, 0, 1, 0,
+                       abox_auto_config_get, abox_auto_config_put),
+       SOC_SINGLE_EXT("Echo Cancellation", ERAP_ECHO_CANCEL, 0, 2, 0,
+                       abox_erap_handler_get, abox_erap_handler_put),
+       SOC_SINGLE_EXT("VI Sensing", ERAP_VI_SENSE, 0, 2, 0,
+                       abox_erap_handler_get, abox_erap_handler_put),
+       SOC_VALUE_ENUM_EXT("Audio Mode", abox_audio_mode_enum,
+                       abox_audio_mode_get, abox_audio_mode_put),
+       SOC_VALUE_ENUM_EXT("Sound Type", abox_sound_type_enum,
+                       abox_sound_type_get, abox_sound_type_put),
+       SOC_SINGLE_EXT("Tickle", 0, 0, 1, 0, abox_tickle_get, abox_tickle_put),
+};
+
+static const char * const spus_inx_texts[] = {"RDMA", "SIFSM"};
+static SOC_ENUM_SINGLE_DECL(spus_in0_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_IN_L(0), spus_inx_texts);
+static const struct snd_kcontrol_new spus_in0_controls[] = {
+       SOC_DAPM_ENUM("MUX", spus_in0_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_in1_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_IN_L(1), spus_inx_texts);
+static const struct snd_kcontrol_new spus_in1_controls[] = {
+       SOC_DAPM_ENUM("MUX", spus_in1_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_in2_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_IN_L(2), spus_inx_texts);
+static const struct snd_kcontrol_new spus_in2_controls[] = {
+       SOC_DAPM_ENUM("MUX", spus_in2_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_in3_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_IN_L(3), spus_inx_texts);
+static const struct snd_kcontrol_new spus_in3_controls[] = {
+       SOC_DAPM_ENUM("MUX", spus_in3_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_in4_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_IN_L(4), spus_inx_texts);
+static const struct snd_kcontrol_new spus_in4_controls[] = {
+       SOC_DAPM_ENUM("MUX", spus_in4_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_in5_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_IN_L(5), spus_inx_texts);
+static const struct snd_kcontrol_new spus_in5_controls[] = {
+       SOC_DAPM_ENUM("MUX", spus_in5_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_in6_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_IN_L(6), spus_inx_texts);
+static const struct snd_kcontrol_new spus_in6_controls[] = {
+       SOC_DAPM_ENUM("MUX", spus_in6_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_in7_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_IN_L(7), spus_inx_texts);
+static const struct snd_kcontrol_new spus_in7_controls[] = {
+       SOC_DAPM_ENUM("MUX", spus_in7_enum),
+};
+
+static const char * const spus_asrcx_texts[] = {"Off", "On"};
+static SOC_ENUM_SINGLE_DECL(spus_asrc0_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_ASRC_L(0), spus_asrcx_texts);
+static const struct snd_kcontrol_new spus_asrc0_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spus_asrc0_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_asrc1_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_ASRC_L(1), spus_asrcx_texts);
+static const struct snd_kcontrol_new spus_asrc1_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spus_asrc1_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_asrc2_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_ASRC_L(2), spus_asrcx_texts);
+static const struct snd_kcontrol_new spus_asrc2_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spus_asrc2_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_asrc3_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_ASRC_L(3), spus_asrcx_texts);
+static const struct snd_kcontrol_new spus_asrc3_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spus_asrc3_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_asrc4_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_ASRC_L(4), spus_asrcx_texts);
+static const struct snd_kcontrol_new spus_asrc4_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spus_asrc4_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_asrc5_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_ASRC_L(5), spus_asrcx_texts);
+static const struct snd_kcontrol_new spus_asrc5_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spus_asrc5_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_asrc6_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_ASRC_L(6), spus_asrcx_texts);
+static const struct snd_kcontrol_new spus_asrc6_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spus_asrc6_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_asrc7_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_ASRC_L(7), spus_asrcx_texts);
+static const struct snd_kcontrol_new spus_asrc7_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spus_asrc7_enum),
+};
+
+static const char * const spus_outx_texts[] = {"SIFS1", "SIFS0", "SIFS2"};
+static SOC_ENUM_SINGLE_DECL(spus_out0_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_OUT_L(0), spus_outx_texts);
+static const struct snd_kcontrol_new spus_out0_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", spus_out0_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_out1_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_OUT_L(1), spus_outx_texts);
+static const struct snd_kcontrol_new spus_out1_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", spus_out1_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_out2_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_OUT_L(2), spus_outx_texts);
+static const struct snd_kcontrol_new spus_out2_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", spus_out2_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_out3_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_OUT_L(3), spus_outx_texts);
+static const struct snd_kcontrol_new spus_out3_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", spus_out3_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_out4_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_OUT_L(4), spus_outx_texts);
+static const struct snd_kcontrol_new spus_out4_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", spus_out4_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_out5_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_OUT_L(5), spus_outx_texts);
+static const struct snd_kcontrol_new spus_out5_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", spus_out5_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_out6_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_OUT_L(6), spus_outx_texts);
+static const struct snd_kcontrol_new spus_out6_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", spus_out6_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spus_out7_enum, ABOX_SPUS_CTRL0,
+               ABOX_FUNC_CHAIN_SRC_OUT_L(7), spus_outx_texts);
+static const struct snd_kcontrol_new spus_out7_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", spus_out7_enum),
+};
+
+static const char * const spusm_texts[] = {
+       "RESERVED", "RESERVED", "RESERVED", "RESERVED",
+       "RESERVED", "RESERVED", "RESERVED", "RESERVED",
+       "UAIF0", "UAIF1", "UAIF2", "UAIF3", "UAIF4",
+       "RESERVED", "RESERVED", "SPDY",
+};
+static SOC_ENUM_SINGLE_DECL(spusm_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_SPUSM_L,
+               spusm_texts);
+static const struct snd_kcontrol_new spusm_controls[] = {
+       SOC_DAPM_ENUM("MUX", spusm_enum),
+};
+
+static int abox_flush_mixp(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct device *dev = cmpnt->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       dev_info(dev, "%s: flush\n", __func__);
+       snd_soc_component_update_bits(cmpnt, ABOX_SPUS_CTRL2,
+                       ABOX_SPUS_MIXP_FLUSH_MASK,
+                       1 << ABOX_SPUS_MIXP_FLUSH_L);
+
+       return 0;
+}
+
+static int abox_flush_sifm(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct device *dev = cmpnt->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!snd_soc_dapm_connected_input_ep(w, NULL)) {
+               dev_info(dev, "%s: flush\n", __func__);
+               snd_soc_component_update_bits(cmpnt, ABOX_SPUS_CTRL3,
+                               ABOX_SPUS_SIFM_FLUSH_MASK,
+                               1 << ABOX_SPUS_SIFM_FLUSH_L);
+       }
+
+       return 0;
+}
+
+static int abox_flush_sifs1(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct device *dev = cmpnt->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!snd_soc_dapm_connected_input_ep(w, NULL)) {
+               dev_info(dev, "%s: flush\n", __func__);
+               snd_soc_component_update_bits(cmpnt, ABOX_SPUS_CTRL3,
+                               ABOX_SPUS_SIFS1_FLUSH_MASK,
+                               1 << ABOX_SPUS_SIFS1_FLUSH_L);
+       }
+
+       return 0;
+}
+
+static int abox_flush_sifs2(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct device *dev = cmpnt->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!snd_soc_dapm_connected_input_ep(w, NULL)) {
+               dev_info(dev, "%s: flush\n", __func__);
+               snd_soc_component_update_bits(cmpnt, ABOX_SPUS_CTRL3,
+                               ABOX_SPUS_SIFS2_FLUSH_MASK,
+                               1 << ABOX_SPUS_SIFS2_FLUSH_L);
+       }
+
+       return 0;
+}
+
+static int abox_flush_recp(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct device *dev = cmpnt->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
+               dev_info(dev, "%s: flush\n", __func__);
+               snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL2,
+                               ABOX_SPUM_RECP_FLUSH_MASK,
+                               1 << ABOX_SPUM_RECP_FLUSH_L);
+       }
+
+       return 0;
+}
+
+static int abox_flush_sifm0(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct device *dev = cmpnt->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
+               dev_info(dev, "%s: flush\n", __func__);
+               snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL3,
+                               ABOX_SPUM_SIFM0_FLUSH_MASK,
+                               1 << ABOX_SPUM_SIFM0_FLUSH_L);
+       }
+
+       return 0;
+}
+
+static int abox_flush_sifm1(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct device *dev = cmpnt->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
+               dev_info(dev, "%s: flush\n", __func__);
+               snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL3,
+                               ABOX_SPUM_SIFM1_FLUSH_MASK,
+                               1 << ABOX_SPUM_SIFM1_FLUSH_L);
+       }
+
+       return 0;
+}
+
+static int abox_flush_sifm2(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct device *dev = cmpnt->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
+               dev_info(dev, "%s: flush\n", __func__);
+               snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL3,
+                               ABOX_SPUM_SIFM2_FLUSH_MASK,
+                               1 << ABOX_SPUM_SIFM2_FLUSH_L);
+       }
+
+       return 0;
+}
+
+static int abox_flush_sifm3(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct device *dev = cmpnt->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!snd_soc_dapm_connected_output_ep(w, NULL)) {
+               dev_info(dev, "%s: flush\n", __func__);
+               snd_soc_component_update_bits(cmpnt, ABOX_SPUM_CTRL3,
+                               ABOX_SPUM_SIFM3_FLUSH_MASK,
+                               1 << ABOX_SPUM_SIFM3_FLUSH_L);
+       }
+
+       return 0;
+}
+
+static const char * const sifsx_texts[] = {
+       "SPUS OUT0", "SPUS OUT1", "SPUS OUT2", "SPUS OUT3",
+       "SPUS OUT4", "SPUS OUT5", "SPUS OUT6", "SPUS OUT7",
+};
+static SOC_ENUM_SINGLE_DECL(sifs1_enum, ABOX_SPUS_CTRL1, ABOX_SIFS_OUT1_SEL_L,
+               sifsx_texts);
+static const struct snd_kcontrol_new sifs1_controls[] = {
+       SOC_DAPM_ENUM("MUX", sifs1_enum),
+};
+static SOC_ENUM_SINGLE_DECL(sifs2_enum, ABOX_SPUS_CTRL1, ABOX_SIFS_OUT2_SEL_L,
+               sifsx_texts);
+static const struct snd_kcontrol_new sifs2_controls[] = {
+       SOC_DAPM_ENUM("MUX", sifs2_enum),
+};
+
+static const char * const sifsm_texts[] = {
+       "SPUS IN0", "SPUS IN1", "SPUS IN2", "SPUS IN3",
+       "SPUS IN4", "SPUS IN5", "SPUS IN6", "SPUS IN7",
+};
+static SOC_ENUM_SINGLE_DECL(sifsm_enum, ABOX_SPUS_CTRL1, ABOX_SIFM_IN_SEL_L,
+               sifsm_texts);
+static const struct snd_kcontrol_new sifsm_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", sifsm_enum),
+};
+
+static const char * const uaif_spkx_texts[] = {
+       "RESERVED", "SIFS0", "SIFS1", "SIFS2",
+       "RESERVED", "RESERVED", "RESERVED", "RESERVED",
+       "RESERVED", "RESERVED", "RESERVED", "RESERVED",
+       "SIFMS",
+};
+static SOC_ENUM_SINGLE_DECL(uaif0_spk_enum, ABOX_ROUTE_CTRL0,
+               ABOX_ROUTE_UAIF_SPK_L(0), uaif_spkx_texts);
+static const struct snd_kcontrol_new uaif0_spk_controls[] = {
+       SOC_DAPM_ENUM("MUX", uaif0_spk_enum),
+};
+static SOC_ENUM_SINGLE_DECL(uaif1_spk_enum, ABOX_ROUTE_CTRL0,
+               ABOX_ROUTE_UAIF_SPK_L(1), uaif_spkx_texts);
+static const struct snd_kcontrol_new uaif1_spk_controls[] = {
+       SOC_DAPM_ENUM("MUX", uaif1_spk_enum),
+};
+static SOC_ENUM_SINGLE_DECL(uaif2_spk_enum, ABOX_ROUTE_CTRL0,
+               ABOX_ROUTE_UAIF_SPK_L(2), uaif_spkx_texts);
+static const struct snd_kcontrol_new uaif2_spk_controls[] = {
+       SOC_DAPM_ENUM("MUX", uaif2_spk_enum),
+};
+static SOC_ENUM_SINGLE_DECL(uaif3_spk_enum, ABOX_ROUTE_CTRL0,
+               ABOX_ROUTE_UAIF_SPK_L(3), uaif_spkx_texts);
+static const struct snd_kcontrol_new uaif3_spk_controls[] = {
+       SOC_DAPM_ENUM("MUX", uaif3_spk_enum),
+};
+static SOC_ENUM_SINGLE_DECL(uaif4_spk_enum, ABOX_ROUTE_CTRL0,
+               ABOX_ROUTE_UAIF_SPK_L(4), uaif_spkx_texts);
+static const struct snd_kcontrol_new uaif4_spk_controls[] = {
+       SOC_DAPM_ENUM("MUX", uaif4_spk_enum),
+};
+
+static const char * const dsif_spk_texts[] = {
+       "RESERVED", "RESERVED", "SIFS1", "SIFS2",
+};
+static SOC_ENUM_SINGLE_DECL(dsif_spk_enum, ABOX_ROUTE_CTRL0, 20,
+               dsif_spk_texts);
+static const struct snd_kcontrol_new dsif_spk_controls[] = {
+       SOC_DAPM_ENUM("MUX", dsif_spk_enum),
+};
+
+static const char * const rsrcx_texts[] = {
+       "RESERVED", "SIFS0", "SIFS1", "SIFS2",
+       "RESERVED", "RESERVED", "RESERVED", "RESERVED",
+       "NSRC0", "NSRC1", "NSRC2", "NSRC3",
+};
+static SOC_ENUM_SINGLE_DECL(rsrc0_enum, ABOX_ROUTE_CTRL2, ABOX_ROUTE_RSRC_L(0),
+               rsrcx_texts);
+static const struct snd_kcontrol_new rsrc0_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", rsrc0_enum),
+};
+static SOC_ENUM_SINGLE_DECL(rsrc1_enum, ABOX_ROUTE_CTRL2, ABOX_ROUTE_RSRC_L(1),
+               rsrcx_texts);
+static const struct snd_kcontrol_new rsrc1_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", rsrc1_enum),
+};
+
+static const char * const nsrcx_texts[] = {
+       "RESERVED", "SIFS0", "SIFS1", "SIFS2",
+       "RESERVED", "RESERVED", "RESERVED", "RESERVED",
+       "UAIF0", "UAIF1", "UAIF2", "UAIF3", "UAIF4",
+       "RESERVED", "RESERVED", "SPDY",
+};
+static SOC_ENUM_SINGLE_DECL(nsrc0_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_NSRC_L(0),
+               nsrcx_texts);
+static const struct snd_kcontrol_new nsrc0_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", nsrc0_enum),
+};
+static SOC_ENUM_SINGLE_DECL(nsrc1_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_NSRC_L(1),
+               nsrcx_texts);
+static const struct snd_kcontrol_new nsrc1_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", nsrc1_enum),
+};
+static SOC_ENUM_SINGLE_DECL(nsrc2_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_NSRC_L(2),
+               nsrcx_texts);
+static const struct snd_kcontrol_new nsrc2_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", nsrc2_enum),
+};
+static SOC_ENUM_SINGLE_DECL(nsrc3_enum, ABOX_ROUTE_CTRL1, ABOX_ROUTE_NSRC_L(3),
+               nsrcx_texts);
+static const struct snd_kcontrol_new nsrc3_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", nsrc3_enum),
+};
+
+static const struct snd_kcontrol_new recp_controls[] = {
+       SOC_DAPM_SINGLE("PIFS0", ABOX_SPUM_CTRL1, ABOX_RECP_SRC_VALID_L, 1, 0),
+       SOC_DAPM_SINGLE("PIFS1", ABOX_SPUM_CTRL1, ABOX_RECP_SRC_VALID_H, 1, 0),
+};
+
+static const char * const spum_asrcx_texts[] = {"Off", "On"};
+static SOC_ENUM_SINGLE_DECL(spum_asrc0_enum, ABOX_SPUM_CTRL0,
+               ABOX_FUNC_CHAIN_RSRC_ASRC_L, spum_asrcx_texts);
+static const struct snd_kcontrol_new spum_asrc0_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spum_asrc0_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spum_asrc1_enum, ABOX_SPUM_CTRL0,
+               ABOX_FUNC_CHAIN_NSRC_ASRC_L(0), spum_asrcx_texts);
+static const struct snd_kcontrol_new spum_asrc1_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spum_asrc1_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spum_asrc2_enum, ABOX_SPUM_CTRL0,
+               ABOX_FUNC_CHAIN_NSRC_ASRC_L(1), spum_asrcx_texts);
+static const struct snd_kcontrol_new spum_asrc2_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spum_asrc2_enum),
+};
+static SOC_ENUM_SINGLE_DECL(spum_asrc3_enum, ABOX_SPUM_CTRL0,
+               ABOX_FUNC_CHAIN_NSRC_ASRC_L(2), spum_asrcx_texts);
+static const struct snd_kcontrol_new spum_asrc3_controls[] = {
+       SOC_DAPM_ENUM("ASRC", spum_asrc3_enum),
+};
+
+static const char * const sifmx_texts[] = {
+       "WDMA", "SIFMS",
+};
+static SOC_ENUM_SINGLE_DECL(sifm0_enum, ABOX_SPUM_CTRL0,
+               ABOX_FUNC_CHAIN_NSRC_OUT_L(0), sifmx_texts);
+static const struct snd_kcontrol_new sifm0_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", sifm0_enum),
+};
+static SOC_ENUM_SINGLE_DECL(sifm1_enum, ABOX_SPUM_CTRL0,
+               ABOX_FUNC_CHAIN_NSRC_OUT_L(1), sifmx_texts);
+static const struct snd_kcontrol_new sifm1_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", sifm1_enum),
+};
+static SOC_ENUM_SINGLE_DECL(sifm2_enum, ABOX_SPUM_CTRL0,
+               ABOX_FUNC_CHAIN_NSRC_OUT_L(2), sifmx_texts);
+static const struct snd_kcontrol_new sifm2_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", sifm2_enum),
+};
+static SOC_ENUM_SINGLE_DECL(sifm3_enum, ABOX_SPUM_CTRL0,
+               ABOX_FUNC_CHAIN_NSRC_OUT_L(3), sifmx_texts);
+static const struct snd_kcontrol_new sifm3_controls[] = {
+       SOC_DAPM_ENUM("DEMUX", sifm3_enum),
+};
+
+static const char * const sifms_texts[] = {
+       "RESERVED", "SIFM0", "SIFM1", "SIFM2", "SIFM3",
+};
+static SOC_ENUM_SINGLE_DECL(sifms_enum, ABOX_SPUM_CTRL1, ABOX_SIFS_OUT_SEL_L,
+               sifms_texts);
+static const struct snd_kcontrol_new sifms_controls[] = {
+       SOC_DAPM_ENUM("MUX", sifms_enum),
+};
+
+static const struct snd_soc_dapm_widget abox_cmpnt_dapm_widgets[] = {
+       SND_SOC_DAPM_MUX("SPUSM", SND_SOC_NOPM, 0, 0, spusm_controls),
+       SND_SOC_DAPM_PGA("SIFSM-SPUS IN0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFSM-SPUS IN1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFSM-SPUS IN2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFSM-SPUS IN3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFSM-SPUS IN4", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFSM-SPUS IN5", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFSM-SPUS IN6", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFSM-SPUS IN7", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_DEMUX_E("SIFSM", SND_SOC_NOPM, 0, 0, sifsm_controls,
+                       abox_flush_sifm,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MUX("SPUS IN0", SND_SOC_NOPM, 0, 0, spus_in0_controls),
+       SND_SOC_DAPM_MUX("SPUS IN1", SND_SOC_NOPM, 0, 0, spus_in1_controls),
+       SND_SOC_DAPM_MUX("SPUS IN2", SND_SOC_NOPM, 0, 0, spus_in2_controls),
+       SND_SOC_DAPM_MUX("SPUS IN3", SND_SOC_NOPM, 0, 0, spus_in3_controls),
+       SND_SOC_DAPM_MUX("SPUS IN4", SND_SOC_NOPM, 0, 0, spus_in4_controls),
+       SND_SOC_DAPM_MUX("SPUS IN5", SND_SOC_NOPM, 0, 0, spus_in5_controls),
+       SND_SOC_DAPM_MUX("SPUS IN6", SND_SOC_NOPM, 0, 0, spus_in6_controls),
+       SND_SOC_DAPM_MUX("SPUS IN7", SND_SOC_NOPM, 0, 0, spus_in7_controls),
+       SND_SOC_DAPM_MUX("SPUS ASRC0", SND_SOC_NOPM, 0, 0, spus_asrc0_controls),
+       SND_SOC_DAPM_MUX("SPUS ASRC1", SND_SOC_NOPM, 0, 0, spus_asrc1_controls),
+       SND_SOC_DAPM_MUX("SPUS ASRC2", SND_SOC_NOPM, 0, 0, spus_asrc2_controls),
+       SND_SOC_DAPM_MUX("SPUS ASRC3", SND_SOC_NOPM, 0, 0, spus_asrc3_controls),
+       SND_SOC_DAPM_MUX("SPUS ASRC4", SND_SOC_NOPM, 0, 0, spus_asrc4_controls),
+       SND_SOC_DAPM_MUX("SPUS ASRC5", SND_SOC_NOPM, 0, 0, spus_asrc5_controls),
+       SND_SOC_DAPM_MUX("SPUS ASRC6", SND_SOC_NOPM, 0, 0, spus_asrc6_controls),
+       SND_SOC_DAPM_MUX("SPUS ASRC7", SND_SOC_NOPM, 0, 0, spus_asrc7_controls),
+       SND_SOC_DAPM_DEMUX("SPUS OUT0", SND_SOC_NOPM, 0, 0, spus_out0_controls),
+       SND_SOC_DAPM_DEMUX("SPUS OUT1", SND_SOC_NOPM, 0, 0, spus_out1_controls),
+       SND_SOC_DAPM_DEMUX("SPUS OUT2", SND_SOC_NOPM, 0, 0, spus_out2_controls),
+       SND_SOC_DAPM_DEMUX("SPUS OUT3", SND_SOC_NOPM, 0, 0, spus_out3_controls),
+       SND_SOC_DAPM_DEMUX("SPUS OUT4", SND_SOC_NOPM, 0, 0, spus_out4_controls),
+       SND_SOC_DAPM_DEMUX("SPUS OUT5", SND_SOC_NOPM, 0, 0, spus_out5_controls),
+       SND_SOC_DAPM_DEMUX("SPUS OUT6", SND_SOC_NOPM, 0, 0, spus_out6_controls),
+       SND_SOC_DAPM_DEMUX("SPUS OUT7", SND_SOC_NOPM, 0, 0, spus_out7_controls),
+
+       SND_SOC_DAPM_PGA("SPUS OUT0-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT1-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT2-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT3-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT4-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT5-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT6-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT7-SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT0-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT1-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT2-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT3-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT4-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT5-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT6-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT7-SIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT0-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT1-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT2-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT3-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT4-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT5-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT6-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPUS OUT7-SIFS2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER_E("SIFS0", SND_SOC_NOPM, 0, 0, NULL, 0,
+                       abox_flush_mixp,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MUX_E("SIFS1", SND_SOC_NOPM, 0, 0, sifs1_controls,
+                       abox_flush_sifs1,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MUX_E("SIFS2", SND_SOC_NOPM, 0, 0, sifs2_controls,
+                       abox_flush_sifs2,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MUX("UAIF0 SPK", SND_SOC_NOPM, 0, 0, uaif0_spk_controls),
+       SND_SOC_DAPM_MUX("UAIF1 SPK", SND_SOC_NOPM, 0, 0, uaif1_spk_controls),
+       SND_SOC_DAPM_MUX("UAIF2 SPK", SND_SOC_NOPM, 0, 0, uaif2_spk_controls),
+       SND_SOC_DAPM_MUX("UAIF3 SPK", SND_SOC_NOPM, 0, 0, uaif3_spk_controls),
+       SND_SOC_DAPM_MUX("UAIF4 SPK", SND_SOC_NOPM, 0, 0, uaif4_spk_controls),
+       SND_SOC_DAPM_MUX("DSIF SPK", SND_SOC_NOPM, 0, 0, dsif_spk_controls),
+
+       SND_SOC_DAPM_MUX("RSRC0", SND_SOC_NOPM, 0, 0, rsrc0_controls),
+       SND_SOC_DAPM_MUX("RSRC1", SND_SOC_NOPM, 0, 0, rsrc1_controls),
+       SND_SOC_DAPM_MUX("NSRC0", SND_SOC_NOPM, 0, 0, nsrc0_controls),
+       SND_SOC_DAPM_MUX("NSRC1", SND_SOC_NOPM, 0, 0, nsrc1_controls),
+       SND_SOC_DAPM_MUX("NSRC2", SND_SOC_NOPM, 0, 0, nsrc2_controls),
+       SND_SOC_DAPM_MUX("NSRC3", SND_SOC_NOPM, 0, 0, nsrc3_controls),
+
+       SND_SOC_DAPM_PGA("PIFS0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("PIFS1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SOC_MIXER_E_ARRAY("RECP", SND_SOC_NOPM, 0, 0, recp_controls,
+                       abox_flush_recp,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MUX("SPUM ASRC0", SND_SOC_NOPM, 0, 0, spum_asrc0_controls),
+       SND_SOC_DAPM_MUX("SPUM ASRC1", SND_SOC_NOPM, 0, 0, spum_asrc1_controls),
+       SND_SOC_DAPM_MUX("SPUM ASRC2", SND_SOC_NOPM, 0, 0, spum_asrc2_controls),
+       SND_SOC_DAPM_MUX("SPUM ASRC3", SND_SOC_NOPM, 0, 0, spum_asrc3_controls),
+       SND_SOC_DAPM_DEMUX_E("SIFM0", SND_SOC_NOPM, 0, 0, sifm0_controls,
+                       abox_flush_sifm0,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_DEMUX_E("SIFM1", SND_SOC_NOPM, 0, 0, sifm1_controls,
+                       abox_flush_sifm1,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_DEMUX_E("SIFM2", SND_SOC_NOPM, 0, 0, sifm2_controls,
+                       abox_flush_sifm2,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_DEMUX_E("SIFM3", SND_SOC_NOPM, 0, 0, sifm3_controls,
+                       abox_flush_sifm3,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA("SIFM0-SIFMS", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFM1-SIFMS", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFM2-SIFMS", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SIFM3-SIFMS", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MUX("SIFMS", SND_SOC_NOPM, 0, 0, sifms_controls),
+
+       SND_SOC_DAPM_AIF_IN("UAIF0IN", "UAIF0 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("UAIF1IN", "UAIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("UAIF2IN", "UAIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("UAIF3IN", "UAIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("UAIF4IN", "UAIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_AIF_OUT("UAIF0OUT", "UAIF0 Playback", 0, SND_SOC_NOPM,
+                       0, 0),
+       SND_SOC_DAPM_AIF_OUT("UAIF1OUT", "UAIF1 Playback", 0, SND_SOC_NOPM,
+                       0, 0),
+       SND_SOC_DAPM_AIF_OUT("UAIF2OUT", "UAIF2 Playback", 0, SND_SOC_NOPM,
+                       0, 0),
+       SND_SOC_DAPM_AIF_OUT("UAIF3OUT", "UAIF3 Playback", 0, SND_SOC_NOPM,
+                       0, 0),
+       SND_SOC_DAPM_AIF_OUT("UAIF4OUT", "UAIF4 Playback", 0, SND_SOC_NOPM,
+                       0, 0),
+       SND_SOC_DAPM_AIF_OUT("DSIFOUT", "DSIF Playback", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route abox_cmpnt_dapm_routes[] = {
+       /* sink, control, source */
+       {"SIFSM", NULL, "SPUSM"},
+       {"SIFSM-SPUS IN0", "SPUS IN0", "SIFSM"},
+       {"SIFSM-SPUS IN1", "SPUS IN1", "SIFSM"},
+       {"SIFSM-SPUS IN2", "SPUS IN2", "SIFSM"},
+       {"SIFSM-SPUS IN3", "SPUS IN3", "SIFSM"},
+       {"SIFSM-SPUS IN4", "SPUS IN4", "SIFSM"},
+       {"SIFSM-SPUS IN5", "SPUS IN5", "SIFSM"},
+       {"SIFSM-SPUS IN6", "SPUS IN6", "SIFSM"},
+       {"SIFSM-SPUS IN7", "SPUS IN7", "SIFSM"},
+
+       {"SPUS IN0", "RDMA", "RDMA0 Playback"},
+       {"SPUS IN0", "SIFSM", "SIFSM-SPUS IN0"},
+       {"SPUS IN1", "RDMA", "RDMA1 Playback"},
+       {"SPUS IN1", "SIFSM", "SIFSM-SPUS IN1"},
+       {"SPUS IN2", "RDMA", "RDMA2 Playback"},
+       {"SPUS IN2", "SIFSM", "SIFSM-SPUS IN2"},
+       {"SPUS IN3", "RDMA", "RDMA3 Playback"},
+       {"SPUS IN3", "SIFSM", "SIFSM-SPUS IN3"},
+       {"SPUS IN4", "RDMA", "RDMA4 Playback"},
+       {"SPUS IN4", "SIFSM", "SIFSM-SPUS IN4"},
+       {"SPUS IN5", "RDMA", "RDMA5 Playback"},
+       {"SPUS IN5", "SIFSM", "SIFSM-SPUS IN5"},
+       {"SPUS IN6", "RDMA", "RDMA6 Playback"},
+       {"SPUS IN6", "SIFSM", "SIFSM-SPUS IN6"},
+       {"SPUS IN7", "RDMA", "RDMA7 Playback"},
+       {"SPUS IN7", "SIFSM", "SIFSM-SPUS IN7"},
+
+       {"SPUS ASRC0", "On", "SPUS IN0"},
+       {"SPUS ASRC0", "Off", "SPUS IN0"},
+       {"SPUS ASRC1", "On", "SPUS IN1"},
+       {"SPUS ASRC1", "Off", "SPUS IN1"},
+       {"SPUS ASRC2", "On", "SPUS IN2"},
+       {"SPUS ASRC2", "Off", "SPUS IN2"},
+       {"SPUS ASRC3", "On", "SPUS IN3"},
+       {"SPUS ASRC3", "Off", "SPUS IN3"},
+       {"SPUS ASRC4", "On", "SPUS IN4"},
+       {"SPUS ASRC4", "Off", "SPUS IN4"},
+       {"SPUS ASRC5", "On", "SPUS IN5"},
+       {"SPUS ASRC5", "Off", "SPUS IN5"},
+       {"SPUS ASRC6", "On", "SPUS IN6"},
+       {"SPUS ASRC6", "Off", "SPUS IN6"},
+       {"SPUS ASRC7", "On", "SPUS IN7"},
+       {"SPUS ASRC7", "Off", "SPUS IN7"},
+
+       {"SPUS OUT0", NULL, "SPUS ASRC0"},
+       {"SPUS OUT1", NULL, "SPUS ASRC1"},
+       {"SPUS OUT2", NULL, "SPUS ASRC2"},
+       {"SPUS OUT3", NULL, "SPUS ASRC3"},
+       {"SPUS OUT4", NULL, "SPUS ASRC4"},
+       {"SPUS OUT5", NULL, "SPUS ASRC5"},
+       {"SPUS OUT6", NULL, "SPUS ASRC6"},
+       {"SPUS OUT7", NULL, "SPUS ASRC7"},
+
+       {"SPUS OUT0-SIFS0", "SIFS0", "SPUS OUT0"},
+       {"SPUS OUT1-SIFS0", "SIFS0", "SPUS OUT1"},
+       {"SPUS OUT2-SIFS0", "SIFS0", "SPUS OUT2"},
+       {"SPUS OUT3-SIFS0", "SIFS0", "SPUS OUT3"},
+       {"SPUS OUT4-SIFS0", "SIFS0", "SPUS OUT4"},
+       {"SPUS OUT5-SIFS0", "SIFS0", "SPUS OUT5"},
+       {"SPUS OUT6-SIFS0", "SIFS0", "SPUS OUT6"},
+       {"SPUS OUT7-SIFS0", "SIFS0", "SPUS OUT7"},
+       {"SPUS OUT0-SIFS1", "SIFS1", "SPUS OUT0"},
+       {"SPUS OUT1-SIFS1", "SIFS1", "SPUS OUT1"},
+       {"SPUS OUT2-SIFS1", "SIFS1", "SPUS OUT2"},
+       {"SPUS OUT3-SIFS1", "SIFS1", "SPUS OUT3"},
+       {"SPUS OUT4-SIFS1", "SIFS1", "SPUS OUT4"},
+       {"SPUS OUT5-SIFS1", "SIFS1", "SPUS OUT5"},
+       {"SPUS OUT6-SIFS1", "SIFS1", "SPUS OUT6"},
+       {"SPUS OUT7-SIFS1", "SIFS1", "SPUS OUT7"},
+       {"SPUS OUT0-SIFS2", "SIFS2", "SPUS OUT0"},
+       {"SPUS OUT1-SIFS2", "SIFS2", "SPUS OUT1"},
+       {"SPUS OUT2-SIFS2", "SIFS2", "SPUS OUT2"},
+       {"SPUS OUT3-SIFS2", "SIFS2", "SPUS OUT3"},
+       {"SPUS OUT4-SIFS2", "SIFS2", "SPUS OUT4"},
+       {"SPUS OUT5-SIFS2", "SIFS2", "SPUS OUT5"},
+       {"SPUS OUT6-SIFS2", "SIFS2", "SPUS OUT6"},
+       {"SPUS OUT7-SIFS2", "SIFS2", "SPUS OUT7"},
+
+       {"SIFS0", NULL, "SPUS OUT0-SIFS0"},
+       {"SIFS0", NULL, "SPUS OUT1-SIFS0"},
+       {"SIFS0", NULL, "SPUS OUT2-SIFS0"},
+       {"SIFS0", NULL, "SPUS OUT3-SIFS0"},
+       {"SIFS0", NULL, "SPUS OUT4-SIFS0"},
+       {"SIFS0", NULL, "SPUS OUT5-SIFS0"},
+       {"SIFS0", NULL, "SPUS OUT6-SIFS0"},
+       {"SIFS0", NULL, "SPUS OUT7-SIFS0"},
+       {"SIFS1", "SPUS OUT0", "SPUS OUT0-SIFS1"},
+       {"SIFS1", "SPUS OUT1", "SPUS OUT1-SIFS1"},
+       {"SIFS1", "SPUS OUT2", "SPUS OUT2-SIFS1"},
+       {"SIFS1", "SPUS OUT3", "SPUS OUT3-SIFS1"},
+       {"SIFS1", "SPUS OUT4", "SPUS OUT4-SIFS1"},
+       {"SIFS1", "SPUS OUT5", "SPUS OUT5-SIFS1"},
+       {"SIFS1", "SPUS OUT6", "SPUS OUT6-SIFS1"},
+       {"SIFS1", "SPUS OUT7", "SPUS OUT7-SIFS1"},
+       {"SIFS2", "SPUS OUT0", "SPUS OUT0-SIFS2"},
+       {"SIFS2", "SPUS OUT1", "SPUS OUT1-SIFS2"},
+       {"SIFS2", "SPUS OUT2", "SPUS OUT2-SIFS2"},
+       {"SIFS2", "SPUS OUT3", "SPUS OUT3-SIFS2"},
+       {"SIFS2", "SPUS OUT4", "SPUS OUT4-SIFS2"},
+       {"SIFS2", "SPUS OUT5", "SPUS OUT5-SIFS2"},
+       {"SIFS2", "SPUS OUT6", "SPUS OUT6-SIFS2"},
+       {"SIFS2", "SPUS OUT7", "SPUS OUT7-SIFS2"},
+
+       {"SIFS0 Playback", NULL, "SIFS0"},
+       {"SIFS1 Playback", NULL, "SIFS1"},
+       {"SIFS2 Playback", NULL, "SIFS2"},
+
+       {"UAIF0 SPK", "SIFS0", "SIFS0"},
+       {"UAIF0 SPK", "SIFS1", "SIFS1"},
+       {"UAIF0 SPK", "SIFS2", "SIFS2"},
+       {"UAIF0 SPK", "SIFMS", "SIFMS"},
+       {"UAIF1 SPK", "SIFS0", "SIFS0"},
+       {"UAIF1 SPK", "SIFS1", "SIFS1"},
+       {"UAIF1 SPK", "SIFS2", "SIFS2"},
+       {"UAIF1 SPK", "SIFMS", "SIFMS"},
+       {"UAIF2 SPK", "SIFS0", "SIFS0"},
+       {"UAIF2 SPK", "SIFS1", "SIFS1"},
+       {"UAIF2 SPK", "SIFS2", "SIFS2"},
+       {"UAIF2 SPK", "SIFMS", "SIFMS"},
+       {"UAIF3 SPK", "SIFS0", "SIFS0"},
+       {"UAIF3 SPK", "SIFS1", "SIFS1"},
+       {"UAIF3 SPK", "SIFS2", "SIFS2"},
+       {"UAIF3 SPK", "SIFMS", "SIFMS"},
+       {"UAIF4 SPK", "SIFS0", "SIFS0"},
+       {"UAIF4 SPK", "SIFS1", "SIFS1"},
+       {"UAIF4 SPK", "SIFS2", "SIFS2"},
+       {"UAIF4 SPK", "SIFMS", "SIFMS"},
+       {"DSIF SPK", "SIFS1", "SIFS1"},
+       {"DSIF SPK", "SIFS2", "SIFS2"},
+
+       {"RSRC0", "SIFS0", "SIFS0 Capture"},
+       {"RSRC0", "SIFS1", "SIFS1 Capture"},
+       {"RSRC0", "SIFS2", "SIFS2 Capture"},
+       {"RSRC0", "NSRC0", "NSRC0"},
+       {"RSRC0", "NSRC1", "NSRC1"},
+       {"RSRC0", "NSRC2", "NSRC2"},
+       {"RSRC0", "NSRC3", "NSRC3"},
+       {"RSRC1", "SIFS0", "SIFS0 Capture"},
+       {"RSRC1", "SIFS1", "SIFS1 Capture"},
+       {"RSRC1", "SIFS2", "SIFS2 Capture"},
+       {"RSRC1", "NSRC0", "NSRC0"},
+       {"RSRC1", "NSRC1", "NSRC1"},
+       {"RSRC1", "NSRC2", "NSRC2"},
+       {"RSRC1", "NSRC3", "NSRC3"},
+
+       {"NSRC0", "SIFS0", "SIFS0 Capture"},
+       {"NSRC0", "SIFS1", "SIFS1 Capture"},
+       {"NSRC0", "SIFS2", "SIFS2 Capture"},
+       {"NSRC1", "SIFS0", "SIFS0 Capture"},
+       {"NSRC1", "SIFS1", "SIFS1 Capture"},
+       {"NSRC1", "SIFS2", "SIFS2 Capture"},
+       {"NSRC2", "SIFS0", "SIFS0 Capture"},
+       {"NSRC2", "SIFS1", "SIFS1 Capture"},
+       {"NSRC2", "SIFS2", "SIFS2 Capture"},
+       {"NSRC3", "SIFS0", "SIFS0 Capture"},
+       {"NSRC3", "SIFS1", "SIFS1 Capture"},
+       {"NSRC3", "SIFS2", "SIFS2 Capture"},
+
+       {"PIFS0", NULL, "RSRC0"},
+       {"PIFS1", NULL, "RSRC1"},
+       {"RECP", "PIFS0", "PIFS0"},
+       {"RECP", "PIFS1", "PIFS1"},
+
+       {"SPUM ASRC0", "On", "RECP"},
+       {"SPUM ASRC0", "Off", "RECP"},
+       {"SPUM ASRC1", "On", "NSRC0"},
+       {"SPUM ASRC1", "Off", "NSRC0"},
+       {"SPUM ASRC2", "On", "NSRC1"},
+       {"SPUM ASRC2", "Off", "NSRC1"},
+       {"SPUM ASRC3", "On", "NSRC2"},
+       {"SPUM ASRC3", "Off", "NSRC2"},
+
+       {"SIFM0", NULL, "SPUM ASRC1"},
+       {"SIFM1", NULL, "SPUM ASRC2"},
+       {"SIFM2", NULL, "SPUM ASRC3"},
+       {"SIFM3", NULL, "NSRC3"},
+
+       {"SIFM0-SIFMS", "SIFMS", "SIFM0"},
+       {"SIFM1-SIFMS", "SIFMS", "SIFM1"},
+       {"SIFM2-SIFMS", "SIFMS", "SIFM2"},
+       {"SIFM3-SIFMS", "SIFMS", "SIFM3"},
+
+       {"SIFMS", "SIFM0", "SIFM0-SIFMS"},
+       {"SIFMS", "SIFM1", "SIFM1-SIFMS"},
+       {"SIFMS", "SIFM2", "SIFM2-SIFMS"},
+       {"SIFMS", "SIFM3", "SIFM3-SIFMS"},
+
+       {"WDMA0 Capture", NULL, "SPUM ASRC0"},
+       {"WDMA1 Capture", "WDMA", "SIFM0"},
+       {"WDMA2 Capture", "WDMA", "SIFM1"},
+       {"WDMA3 Capture", "WDMA", "SIFM2"},
+       {"WDMA4 Capture", "WDMA", "SIFM3"},
+};
+
+static bool abox_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ABOX_SYSPOWER_CTRL:
+       case ABOX_SYSPOWER_STATUS:
+       case ABOX_SPUS_CTRL2:
+       case ABOX_SPUS_CTRL3:
+       case ABOX_SPUM_CTRL2:
+       case ABOX_SPUM_CTRL3:
+       case ABOX_UAIF_STATUS(0):
+       case ABOX_UAIF_STATUS(1):
+       case ABOX_UAIF_STATUS(2):
+       case ABOX_UAIF_STATUS(3):
+       case ABOX_UAIF_STATUS(4):
+       case ABOX_DSIF_STATUS:
+       case ABOX_TIMER_CTRL0(0):
+       case ABOX_TIMER_CTRL1(0):
+       case ABOX_TIMER_CTRL0(1):
+       case ABOX_TIMER_CTRL1(1):
+       case ABOX_TIMER_CTRL0(2):
+       case ABOX_TIMER_CTRL1(2):
+       case ABOX_TIMER_CTRL0(3):
+       case ABOX_TIMER_CTRL1(3):
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool abox_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ABOX_IP_INDEX:
+       case ABOX_VERSION:
+       case ABOX_SYSPOWER_CTRL:
+       case ABOX_SYSPOWER_STATUS:
+       case ABOX_SYSTEM_CONFIG0:
+       case ABOX_REMAP_MASK:
+       case ABOX_REMAP_ADDR:
+       case ABOX_DYN_CLOCK_OFF:
+       case ABOX_QCHANNEL_DISABLE:
+       case ABOX_ROUTE_CTRL0:
+       case ABOX_ROUTE_CTRL1:
+       case ABOX_ROUTE_CTRL2:
+       case ABOX_TICK_DIV_RATIO:
+       case ABOX_SPUS_CTRL0:
+       case ABOX_SPUS_CTRL1:
+       case ABOX_SPUS_CTRL2:
+       case ABOX_SPUS_CTRL3:
+       case ABOX_SPUS_CTRL_SIFS_CNT0:
+       case ABOX_SPUS_CTRL_SIFS_CNT1:
+       case ABOX_SPUM_CTRL0:
+       case ABOX_SPUM_CTRL1:
+       case ABOX_SPUM_CTRL2:
+       case ABOX_SPUM_CTRL3:
+       case ABOX_UAIF_CTRL0(0):
+       case ABOX_UAIF_CTRL1(0):
+       case ABOX_UAIF_STATUS(0):
+       case ABOX_UAIF_CTRL0(1):
+       case ABOX_UAIF_CTRL1(1):
+       case ABOX_UAIF_STATUS(1):
+       case ABOX_UAIF_CTRL0(2):
+       case ABOX_UAIF_CTRL1(2):
+       case ABOX_UAIF_STATUS(2):
+       case ABOX_UAIF_CTRL0(3):
+       case ABOX_UAIF_CTRL1(3):
+       case ABOX_UAIF_STATUS(3):
+       case ABOX_UAIF_CTRL0(4):
+       case ABOX_UAIF_CTRL1(4):
+       case ABOX_UAIF_STATUS(4):
+       case ABOX_DSIF_CTRL:
+       case ABOX_DSIF_STATUS:
+       case ABOX_SPDYIF_CTRL:
+       case ABOX_TIMER_CTRL0(0):
+       case ABOX_TIMER_CTRL1(0):
+       case ABOX_TIMER_CTRL0(1):
+       case ABOX_TIMER_CTRL1(1):
+       case ABOX_TIMER_CTRL0(2):
+       case ABOX_TIMER_CTRL1(2):
+       case ABOX_TIMER_CTRL0(3):
+       case ABOX_TIMER_CTRL1(3):
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool abox_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ABOX_SYSPOWER_CTRL:
+       case ABOX_SYSTEM_CONFIG0:
+       case ABOX_REMAP_MASK:
+       case ABOX_REMAP_ADDR:
+       case ABOX_DYN_CLOCK_OFF:
+       case ABOX_QCHANNEL_DISABLE:
+       case ABOX_ROUTE_CTRL0:
+       case ABOX_ROUTE_CTRL1:
+       case ABOX_ROUTE_CTRL2:
+       case ABOX_SPUS_CTRL0:
+       case ABOX_SPUS_CTRL1:
+       case ABOX_SPUS_CTRL2:
+       case ABOX_SPUS_CTRL3:
+       case ABOX_SPUS_CTRL_SIFS_CNT0:
+       case ABOX_SPUS_CTRL_SIFS_CNT1:
+       case ABOX_SPUM_CTRL0:
+       case ABOX_SPUM_CTRL1:
+       case ABOX_SPUM_CTRL2:
+       case ABOX_SPUM_CTRL3:
+       case ABOX_UAIF_CTRL0(0):
+       case ABOX_UAIF_CTRL1(0):
+       case ABOX_UAIF_CTRL0(1):
+       case ABOX_UAIF_CTRL1(1):
+       case ABOX_UAIF_CTRL0(2):
+       case ABOX_UAIF_CTRL1(2):
+       case ABOX_UAIF_CTRL0(3):
+       case ABOX_UAIF_CTRL1(3):
+       case ABOX_UAIF_CTRL0(4):
+       case ABOX_UAIF_CTRL1(4):
+       case ABOX_DSIF_CTRL:
+       case ABOX_SPDYIF_CTRL:
+       case ABOX_TIMER_CTRL0(0):
+       case ABOX_TIMER_CTRL1(0):
+       case ABOX_TIMER_CTRL0(1):
+       case ABOX_TIMER_CTRL1(1):
+       case ABOX_TIMER_CTRL0(2):
+       case ABOX_TIMER_CTRL1(2):
+       case ABOX_TIMER_CTRL0(3):
+       case ABOX_TIMER_CTRL1(3):
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct reg_default abox_reg_defaults_8895[] = {
+       {0x0000, 0x41424F58},
+       {0x0010, 0x00000000},
+       {0x0014, 0x00000000},
+       {0x0020, 0x00000000},
+       {0x0024, 0xFFF00000},
+       {0x0028, 0x13F00000},
+       {0x0030, 0x7FFFFFFF},
+       {0x0040, 0x00000000},
+       {0x0044, 0x00000000},
+       {0x0048, 0x00000000},
+       {0x0200, 0x00000000},
+       {0x0204, 0x00000000},
+       {0x0208, 0x00000000},
+       {0x020C, 0x00000000},
+       {0x0220, 0x00000000},
+       {0x0224, 0x00000000},
+       {0x0228, 0x00000000},
+       {0x022C, 0x00000000},
+       {0x0230, 0x00000000},
+       {0x0234, 0x00000000},
+       {0x0238, 0x00000000},
+       {0x023C, 0x00000000},
+       {0x0240, 0x00000000},
+       {0x0260, 0x00000000},
+       {0x0300, 0x00000000},
+       {0x0304, 0x00000000},
+       {0x0308, 0x00000000},
+       {0x030C, 0x00000000},
+       {0x0320, 0x00000000},
+       {0x0324, 0x00000000},
+       {0x0328, 0x00000000},
+       {0x032C, 0x00000000},
+       {0x0330, 0x00000000},
+       {0x0334, 0x00000000},
+       {0x0338, 0x00000000},
+       {0x033C, 0x00000000},
+       {0x0340, 0x00000000},
+       {0x0344, 0x00000000},
+       {0x0348, 0x00000000},
+       {0x0500, 0x01000000},
+       {0x0504, 0x00000000},
+       {0x050C, 0x00000000},
+       {0x0510, 0x01000000},
+       {0x0514, 0x00000000},
+       {0x051C, 0x00000000},
+       {0x0520, 0x01000000},
+       {0x0524, 0x00000000},
+       {0x052C, 0x00000000},
+       {0x0530, 0x01000000},
+       {0x0534, 0x00000000},
+       {0x053C, 0x00000000},
+       {0x0540, 0x01000000},
+       {0x0544, 0x00000000},
+       {0x054C, 0x00000000},
+       {0x0550, 0x00000000},
+       {0x0554, 0x00000000},
+};
+
+static const struct reg_default abox_reg_defaults_9810[] = {
+       {0x0000, 0x41424F58},
+       {0x0010, 0x00000000},
+       {0x0014, 0x00000000},
+       {0x0020, 0x00004444},
+       {0x0024, 0xFFF00000},
+       {0x0028, 0x17D00000},
+       {0x0030, 0x7FFFFFFF},
+       {0x0038, 0x00000000},
+       {0x0040, 0x00000000},
+       {0x0044, 0x00000000},
+       {0x0048, 0x00000000},
+       {0x0200, 0x00000000},
+       {0x0204, 0x00000000},
+       {0x0208, 0x00000000},
+       {0x020C, 0x00000000},
+       {0x0220, 0x00000000},
+       {0x0224, 0x00000000},
+       {0x0228, 0x00000000},
+       {0x022C, 0x00000000},
+       {0x0230, 0x00000000},
+       {0x0234, 0x00000000},
+       {0x0238, 0x00000000},
+       {0x023C, 0x00000000},
+       {0x0240, 0x00000000},
+       {0x0260, 0x00000000},
+       {0x0280, 0x00000000},
+       {0x0284, 0x00000000},
+       {0x0300, 0x00000000},
+       {0x0304, 0x00000000},
+       {0x0308, 0x00000000},
+       {0x030C, 0x00000000},
+       {0x0320, 0x00000000},
+       {0x0324, 0x00000000},
+       {0x0328, 0x00000000},
+       {0x032C, 0x00000000},
+       {0x0330, 0x00000000},
+       {0x0334, 0x00000000},
+       {0x0338, 0x00000000},
+       {0x033C, 0x00000000},
+       {0x0340, 0x00000000},
+       {0x0344, 0x00000000},
+       {0x0348, 0x00000000},
+       {0x0500, 0x01000010},
+       {0x0504, 0x00000000},
+       {0x050C, 0x00000000},
+       {0x0510, 0x01000010},
+       {0x0514, 0x00000000},
+       {0x051C, 0x00000000},
+       {0x0520, 0x01000010},
+       {0x0524, 0x00000000},
+       {0x052C, 0x00000000},
+       {0x0530, 0x01000010},
+       {0x0534, 0x00000000},
+       {0x053C, 0x00000000},
+       {0x0540, 0x01000010},
+       {0x0544, 0x00000000},
+       {0x054C, 0x00000000},
+       {0x0550, 0x00000000},
+       {0x0554, 0x00000000},
+};
+
+static const struct reg_default abox_reg_defaults_9610[] = {
+       {0x0000, 0x41424F58},
+       {0x0010, 0x00000000},
+       {0x0014, 0x00000000},
+       {0x0020, 0x00000000},
+       {0x0024, 0xFFF00000},
+       {0x0028, 0x14B00000},
+       {0x0030, 0x7FFFFFFF},
+       {0x0038, 0x00000000},
+       {0x0040, 0x00000000},
+       {0x0044, 0x00000000},
+       {0x0048, 0x00000000},
+       {0x0050, 0x000004E1},
+       {0x0200, 0x00000000},
+       {0x0204, 0x00000000},
+       {0x0208, 0x00000000},
+       {0x020C, 0x00000000},
+       {0x0220, 0x00000000},
+       {0x0224, 0x00000000},
+       {0x0228, 0x00000000},
+       {0x022C, 0x00000000},
+       {0x0230, 0x00000000},
+       {0x0234, 0x00000000},
+       {0x0238, 0x00000000},
+       {0x023C, 0x00000000},
+       {0x0240, 0x00000000},
+       {0x0244, 0x00000000},
+       {0x0248, 0x00000000},
+       {0x024C, 0x00000000},
+       {0x0250, 0x00000000},
+       {0x0254, 0x00000000},
+       {0x0258, 0x00000000},
+       {0x025C, 0x00000000},
+       {0x0260, 0x00000000},
+       {0x0280, 0x00000000},
+       {0x0284, 0x00000000},
+       {0x0300, 0x00000000},
+       {0x0304, 0x00000000},
+       {0x0308, 0x00000000},
+       {0x030C, 0x00000000},
+       {0x0320, 0x00000000},
+       {0x0324, 0x00000000},
+       {0x0328, 0x00000000},
+       {0x032C, 0x00000000},
+       {0x0330, 0x00000000},
+       {0x0334, 0x00000000},
+       {0x0338, 0x00000000},
+       {0x033C, 0x00000000},
+       {0x0340, 0x00000000},
+       {0x0344, 0x00000000},
+       {0x0348, 0x00000000},
+       {0x0500, 0x01000010},
+       {0x0504, 0x00000000},
+       {0x050C, 0x00000000},
+       {0x0510, 0x01000010},
+       {0x0514, 0x00000000},
+       {0x051C, 0x00000000},
+       {0x0520, 0x01000010},
+       {0x0524, 0x00000000},
+       {0x052C, 0x00000000},
+       {0x0530, 0x01000010},
+       {0x0534, 0x00000000},
+       {0x053C, 0x00000000},
+       {0x0540, 0x01000010},
+       {0x0544, 0x00000000},
+       {0x054C, 0x00000000},
+       {0x0550, 0x00000000},
+       {0x0554, 0x00000000},
+       {0x0560, 0x00000000},
+};
+
+static struct regmap_config abox_regmap_config = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .max_register = ABOX_MAX_REGISTERS,
+       .volatile_reg = abox_volatile_reg,
+       .readable_reg = abox_readable_reg,
+       .writeable_reg = abox_writeable_reg,
+       .cache_type = REGCACHE_RBTREE,
+       .fast_io = true,
+};
+
+static const struct snd_soc_component_driver abox_cmpnt = {
+       .probe                  = abox_cmpnt_probe,
+       .remove                 = abox_cmpnt_remove,
+       .controls               = abox_cmpnt_controls,
+       .num_controls           = ARRAY_SIZE(abox_cmpnt_controls),
+       .dapm_widgets           = abox_cmpnt_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(abox_cmpnt_dapm_widgets),
+       .dapm_routes            = abox_cmpnt_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(abox_cmpnt_dapm_routes),
+       .probe_order            = SND_SOC_COMP_ORDER_FIRST,
+};
+
+struct abox_name_rate_format {
+       const char *name;
+       int stream;
+       const enum ABOX_CONFIGMSG rate;
+       const enum ABOX_CONFIGMSG format;
+
+};
+
+static const struct abox_name_rate_format abox_nrf[] = {
+       {"SIFS0", SNDRV_PCM_STREAM_PLAYBACK, SET_MIXER_SAMPLE_RATE,
+                       SET_MIXER_FORMAT},
+       {"SIFS1", SNDRV_PCM_STREAM_PLAYBACK, SET_OUT1_SAMPLE_RATE,
+                       SET_OUT1_FORMAT},
+       {"SIFS2", SNDRV_PCM_STREAM_PLAYBACK, SET_OUT2_SAMPLE_RATE,
+                       SET_OUT2_FORMAT},
+       {"RECP", SNDRV_PCM_STREAM_CAPTURE, SET_RECP_SAMPLE_RATE,
+                       SET_RECP_FORMAT},
+       {"SIFM0", SNDRV_PCM_STREAM_CAPTURE, SET_INMUX0_SAMPLE_RATE,
+                       SET_INMUX0_FORMAT},
+       {"SIFM1", SNDRV_PCM_STREAM_CAPTURE, SET_INMUX1_SAMPLE_RATE,
+                       SET_INMUX1_FORMAT},
+       {"SIFM2", SNDRV_PCM_STREAM_CAPTURE, SET_INMUX2_SAMPLE_RATE,
+                       SET_INMUX2_FORMAT},
+       {"SIFM3", SNDRV_PCM_STREAM_CAPTURE, SET_INMUX3_SAMPLE_RATE,
+                       SET_INMUX3_FORMAT},
+};
+
+static bool abox_find_nrf_stream(const struct snd_soc_dapm_widget *w,
+               int stream, enum ABOX_CONFIGMSG *rate,
+               enum ABOX_CONFIGMSG *format)
+{
+       struct snd_soc_component *cmpnt = w->dapm->component;
+       const char *name_prefix = cmpnt ? cmpnt->name_prefix : NULL;
+       size_t prefix_len = name_prefix ? strlen(name_prefix) + 1 : 0;
+       const char *name = w->name + prefix_len;
+       const struct abox_name_rate_format *nrf;
+
+       for (nrf = abox_nrf; nrf - abox_nrf < ARRAY_SIZE(abox_nrf); nrf++) {
+               if ((nrf->stream == stream) && (strcmp(nrf->name, name) == 0)) {
+                       *rate = nrf->rate;
+                       *format = nrf->format;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool __maybe_unused abox_find_nrf(const struct snd_soc_dapm_widget *w,
+               enum ABOX_CONFIGMSG *rate, enum ABOX_CONFIGMSG *format)
+{
+       struct snd_soc_component *cmpnt = w->dapm->component;
+       const char *name_prefix = cmpnt ? cmpnt->name_prefix : NULL;
+       size_t prefix_len = name_prefix ? strlen(name_prefix) + 1 : 0;
+       const char *name = w->name + prefix_len;
+       const struct abox_name_rate_format *nrf;
+
+       for (nrf = abox_nrf; nrf - abox_nrf < ARRAY_SIZE(abox_nrf); nrf++) {
+               if (strcmp(nrf->name, name) == 0) {
+                       *rate = nrf->rate;
+                       *format = nrf->format;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+int abox_hw_params_fixup_helper(struct snd_soc_pcm_runtime *rtd,
+               struct snd_pcm_hw_params *params, int stream)
+{
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_soc_component *cmpnt = dai->component;
+       struct device *dev = is_abox(dai->dev) ? dai->dev : dai->dev->parent;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct snd_soc_dapm_widget *w, *w_tgt = NULL;
+       struct snd_soc_dapm_path *path;
+       LIST_HEAD(widget_list);
+       enum ABOX_CONFIGMSG msg_rate, msg_format;
+       unsigned int rate, channels, width;
+       snd_pcm_format_t format;
+
+       dev_info(dev, "%s[%s](%d)\n", __func__, dai->name, stream);
+
+       if (params_channels(params) < 1) {
+               dev_info(dev, "channel is fixed from %d to 2\n",
+                               params_channels(params));
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min = 2;
+       }
+
+       if (params_width(params) < 16) {
+               dev_info(dev, "width is fixed from %d to 16\n",
+                               params_width(params));
+               params_set_format(params, SNDRV_PCM_FORMAT_S16);
+       }
+
+       snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(cmpnt));
+
+       /*
+        * For snd_soc_dapm_connected_{output,input}_ep fully discover the graph
+        * we need to reset the cached number of inputs and outputs.
+        */
+       list_for_each_entry(w, &cmpnt->card->widgets, list) {
+               w->endpoints[SND_SOC_DAPM_DIR_IN] = -1;
+               w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1;
+       }
+
+       if (!dai->playback_widget)
+               goto skip_playback;
+       snd_soc_dapm_widget_for_each_source_path(dai->playback_widget, path) {
+               if (path->connect) {
+                       w = path->node[SND_SOC_DAPM_DIR_IN];
+                       snd_soc_dapm_connected_input_ep(w, &widget_list);
+               }
+       }
+skip_playback:
+       if (!dai->capture_widget)
+               goto skip_capture;
+       snd_soc_dapm_widget_for_each_sink_path(dai->capture_widget, path) {
+               if (path->connect) {
+                       w = path->node[SND_SOC_DAPM_DIR_OUT];
+                       snd_soc_dapm_connected_output_ep(w, &widget_list);
+               }
+       }
+skip_capture:
+
+       /* find current params */
+       list_for_each_entry(w, &widget_list, work_list) {
+               dev_dbg(dev, "%s\n", w->name);
+               if (!abox_find_nrf_stream(w, stream, &msg_rate, &msg_format))
+                       continue;
+
+               format = abox_get_sif_format(data, msg_format);
+               width = snd_pcm_format_width(format);
+               rate = abox_get_sif_rate(data, msg_rate);
+               channels = abox_get_sif_channels(data, msg_format);
+               dev_dbg(dev, "%s: %s: find %d bit, %u channel, %uHz\n",
+                               __func__, w->name, width, channels, rate);
+               w_tgt = w;
+               break;
+       }
+
+       if (!w_tgt)
+               goto unlock;
+
+       /* channel mixing isn't supported */
+       abox_set_sif_channels(data, msg_format, params_channels(params));
+
+       /* override formats to UAIF format, if it is connected */
+       if (abox_if_hw_params_fixup(rtd, params, stream) >= 0)
+               abox_set_sif_auto_config(data, msg_rate, true);
+
+       if (!abox_get_sif_auto_config(data, msg_rate))
+               goto unlock;
+
+       format = params_format(params);
+       width = params_width(params);
+       rate = params_rate(params);
+       channels = params_channels(params);
+
+       if (dai->sample_width && dai->sample_width != width) {
+               width = dai->sample_width;
+               abox_set_sif_width(data, msg_format, dai->sample_width);
+               format = abox_get_sif_format(data, msg_format);
+       }
+
+       if (dai->channels && dai->channels != channels)
+               channels = dai->channels;
+
+       if (dai->rate && dai->rate != rate)
+               rate = dai->rate;
+
+       dev_dbg(dev, "%s: set to %u bit, %u channel, %uHz\n", __func__,
+                       width, channels, rate);
+unlock:
+       snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(cmpnt));
+
+       if (!w_tgt)
+               goto out;
+       abox_sample_rate_put_ipc(dev, rate, msg_rate);
+       abox_sif_format_put_ipc(dev, format, channels, msg_format);
+       hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min =
+                       abox_get_sif_rate(data, msg_rate);
+       hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min =
+                       abox_get_sif_channels(data, msg_format);
+       snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT));
+       snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+                       abox_get_sif_format(data, msg_format));
+       dev_info(dev, "%s: %s: %d bit, %u channel, %uHz\n",
+                       __func__, w_tgt->name,
+                       abox_get_sif_width(data, msg_format),
+                       abox_get_sif_channels(data, msg_format),
+                       abox_get_sif_rate(data, msg_rate));
+out:
+       return 0;
+}
+EXPORT_SYMBOL(abox_hw_params_fixup_helper);
+
+static struct pm_qos_request abox_pm_qos_aud;
+static struct pm_qos_request abox_pm_qos_int;
+static struct pm_qos_request abox_pm_qos_mif;
+static struct pm_qos_request abox_pm_qos_lit;
+static struct pm_qos_request abox_pm_qos_big;
+
+unsigned int abox_get_requiring_int_freq_in_khz(void)
+{
+       struct abox_data *data = p_abox_data;
+       unsigned int gear;
+       unsigned int int_freq;
+
+       if (data == NULL)
+               return 0;
+
+       gear = data->cpu_gear;
+
+       if (gear <= ARRAY_SIZE(data->pm_qos_int))
+               int_freq = data->pm_qos_int[gear - 1];
+       else
+               int_freq = 0;
+
+       return int_freq;
+}
+EXPORT_SYMBOL(abox_get_requiring_int_freq_in_khz);
+
+unsigned int abox_get_requiring_aud_freq_in_khz(void)
+{
+       struct abox_data *data = p_abox_data;
+       unsigned int gear;
+       unsigned int aud_freq;
+
+       if (data == NULL)
+               return 0;
+
+       gear = data->cpu_gear;
+
+       if (gear <= ARRAY_SIZE(data->pm_qos_aud))
+               aud_freq = data->pm_qos_aud[gear - 1];
+       else
+               aud_freq = 0;
+
+       return aud_freq;
+}
+EXPORT_SYMBOL(abox_get_requiring_aud_freq_in_khz);
+
+bool abox_cpu_gear_idle(struct device *dev, struct abox_data *data, void *id)
+{
+       struct abox_qos_request *request;
+
+       dev_dbg(dev, "%s(%p)\n", __func__, id);
+
+       for (request = data->cpu_gear_requests;
+                       request - data->cpu_gear_requests <
+                       ARRAY_SIZE(data->cpu_gear_requests)
+                       && request->id;
+                       request++) {
+               if (id == request->id) {
+                       if (request->value >= ABOX_CPU_GEAR_MIN)
+                               return true;
+                       else
+                               return false;
+               }
+       }
+
+       return true;
+}
+
+static void abox_check_cpu_gear(struct device *dev,
+               struct abox_data *data,
+               const void *old_id, unsigned int old_gear,
+               const void *id, unsigned int gear)
+{
+       struct device *dev_abox = &data->pdev->dev;
+
+       if (id != (void *)ABOX_CPU_GEAR_BOOT)
+               return;
+
+       if (data->calliope_state == CALLIOPE_ENABLING)
+               abox_boot_done(dev_abox, data->calliope_version);
+
+       if (old_id != id) {
+               if (gear < ABOX_CPU_GEAR_MIN) {
+                       /* new */
+                       dev_dbg(dev, "%s(%p): new\n", __func__, id);
+                       pm_wakeup_event(dev_abox, BOOT_DONE_TIMEOUT_MS);
+               }
+       } else {
+               if ((old_gear >= ABOX_CPU_GEAR_MIN) &&
+                               (gear < ABOX_CPU_GEAR_MIN)) {
+                       /* on */
+                       dev_dbg(dev, "%s(%p): on\n", __func__, id);
+                       pm_wakeup_event(dev_abox, BOOT_DONE_TIMEOUT_MS);
+               } else if ((old_gear < ABOX_CPU_GEAR_MIN) &&
+                               (gear >= ABOX_CPU_GEAR_MIN)) {
+                       /* off */
+                       dev_dbg(dev, "%s(%p): off\n", __func__, id);
+                       pm_relax(dev);
+               }
+       }
+}
+
+static void abox_notify_cpu_gear(struct abox_data *data, unsigned int freq)
+{
+       struct device *dev = &data->pdev->dev;
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+       unsigned long long time = sched_clock();
+       unsigned long rem = do_div(time, NSEC_PER_SEC);
+
+       switch (data->calliope_state) {
+       case CALLIOPE_ENABLING:
+       case CALLIOPE_ENABLED:
+               dev_dbg(dev, "%s\n", __func__);
+
+               msg.ipcid = IPC_SYSTEM;
+               system_msg->msgtype = ABOX_CHANGED_GEAR;
+               system_msg->param1 = (int)freq;
+               system_msg->param2 = (int)time; /* SEC */
+               system_msg->param3 = (int)rem; /* NSEC */
+               abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
+               break;
+       case CALLIOPE_DISABLING:
+       case CALLIOPE_DISABLED:
+       default:
+               /* notification to passing by context is not needed */
+               break;
+       }
+}
+
+static void abox_change_cpu_gear_legacy(struct device *dev,
+               struct abox_data *data)
+{
+       struct abox_qos_request *request;
+       unsigned int gear = UINT_MAX;
+       int ret;
+       bool increasing;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (request = data->cpu_gear_requests;
+                       request - data->cpu_gear_requests <
+                       ARRAY_SIZE(data->cpu_gear_requests)
+                       && request->id;
+                       request++) {
+               if (gear > request->value)
+                       gear = request->value;
+
+               dev_dbg(dev, "id=%p, value=%u, gear=%u\n", request->id,
+                               request->value, gear);
+       }
+
+       if (data->cpu_gear == gear)
+               goto skip;
+
+       increasing = (gear < data->cpu_gear);
+       data->cpu_gear = gear;
+
+       if (increasing) {
+               if (gear <= ARRAY_SIZE(data->pm_qos_int))
+                       pm_qos_update_request(&abox_pm_qos_int,
+                                       data->pm_qos_int[gear - 1]);
+               else
+                       pm_qos_update_request(&abox_pm_qos_int, 0);
+       }
+
+       if (gear >= ABOX_CPU_GEAR_MIN) {
+               ret = clk_set_rate(data->clk_pll, 0);
+               if (ret < 0)
+                       dev_warn(dev, "setting pll clock to 0 is failed: %d\n",
+                                       ret);
+               dev_info(dev, "pll clock: %lu\n", clk_get_rate(data->clk_pll));
+
+               ret = clk_set_rate(data->clk_cpu, AUD_PLL_RATE_KHZ);
+               if (ret < 0)
+                       dev_warn(dev, "setting cpu clock gear to %d is failed: %d\n",
+                                       gear, ret);
+       } else {
+               ret = clk_set_rate(data->clk_cpu, AUD_PLL_RATE_KHZ / gear);
+               if (ret < 0)
+                       dev_warn(dev, "setting cpu clock gear to %d is failed: %d\n",
+                                       gear, ret);
+
+               if (clk_get_rate(data->clk_pll) <= AUD_PLL_RATE_HZ_BYPASS) {
+                       ret = clk_set_rate(data->clk_pll,
+                                       AUD_PLL_RATE_HZ_FOR_48000);
+                       if (ret < 0)
+                               dev_warn(dev, "setting pll clock to 0 is failed: %d\n",
+                                               ret);
+                       dev_info(dev, "pll clock: %lu\n",
+                                       clk_get_rate(data->clk_pll));
+               }
+       }
+       dev_info(dev, "cpu clock: %lukHz\n", clk_get_rate(data->clk_cpu));
+
+       if (!increasing) {
+               if (gear <= ARRAY_SIZE(data->pm_qos_int))
+                       pm_qos_update_request(&abox_pm_qos_int,
+                                       data->pm_qos_int[gear - 1]);
+               else
+                       pm_qos_update_request(&abox_pm_qos_int, 0);
+       }
+skip:
+       abox_notify_cpu_gear(data, clk_get_rate(data->clk_cpu) * 1000);
+}
+
+static void abox_change_cpu_gear(struct device *dev, struct abox_data *data)
+{
+       struct abox_qos_request *request;
+       unsigned int gear = UINT_MAX;
+       s32 freq;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (request = data->cpu_gear_requests;
+                       request - data->cpu_gear_requests <
+                       ARRAY_SIZE(data->cpu_gear_requests)
+                       && request->id;
+                       request++) {
+               if (gear > request->value)
+                       gear = request->value;
+
+               dev_dbg(dev, "id=%p, value=%u, gear=%u\n", request->id,
+                               request->value, gear);
+       }
+
+       if ((data->cpu_gear >= ABOX_CPU_GEAR_MIN) &&
+                       (gear < ABOX_CPU_GEAR_MIN)) {
+               /* first cpu gear request */
+               clk_set_rate(data->clk_pll, AUD_PLL_RATE_HZ_FOR_48000);
+               dev_info(dev, "pll clock: %lu\n", clk_get_rate(data->clk_pll));
+               pm_runtime_get(dev);
+       }
+
+       if (data->cpu_gear != gear) {
+               freq = (gear <= ARRAY_SIZE(data->pm_qos_aud)) ?
+                               data->pm_qos_aud[gear - 1] : 0;
+               pm_qos_update_request(&abox_pm_qos_aud, freq);
+       }
+
+       dev_info(dev, "pm qos request aud: req=%dkHz ret=%dkHz\n", freq,
+                       pm_qos_request(abox_pm_qos_aud.pm_qos_class));
+       abox_notify_cpu_gear(data,
+                       pm_qos_request(abox_pm_qos_aud.pm_qos_class) * 1000);
+
+       if ((data->cpu_gear < ABOX_CPU_GEAR_MIN) &&
+                       (gear >= ABOX_CPU_GEAR_MIN)) {
+               /* no more cpu gear request */
+               pm_runtime_mark_last_busy(dev);
+               pm_runtime_put_autosuspend(dev);
+               clk_set_rate(data->clk_pll, 0);
+               dev_info(dev, "pll clock: %lu\n", clk_get_rate(data->clk_pll));
+       }
+
+       data->cpu_gear = gear;
+}
+
+static void abox_change_cpu_gear_work_func(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data,
+                       change_cpu_gear_work);
+
+       if (IS_ENABLED(CONFIG_SOC_EXYNOS8895))
+               abox_change_cpu_gear_legacy(&data->pdev->dev, data);
+       else
+               abox_change_cpu_gear(&data->pdev->dev, data);
+}
+
+int abox_request_cpu_gear(struct device *dev, struct abox_data *data,
+               const void *id, unsigned int gear)
+{
+       struct abox_qos_request *request;
+       const void *old_id;
+       unsigned int old_gear;
+
+       dev_info(dev, "%s(%p, %u)\n", __func__, id, gear);
+
+       for (request = data->cpu_gear_requests;
+                       request - data->cpu_gear_requests <
+                       ARRAY_SIZE(data->cpu_gear_requests)
+                       && request->id && request->id != id;
+                       request++) {
+       }
+
+       old_id = request->id;
+       old_gear = request->value;
+       request->value = gear;
+       wmb(); /* value is read after id in reading function */
+       request->id = id;
+
+       if (request - data->cpu_gear_requests >=
+                       ARRAY_SIZE(data->cpu_gear_requests)) {
+               dev_err(dev, "%s: out of index. id=%p, gear=%u\n", __func__,
+                               id, gear);
+               return -ENOMEM;
+       }
+
+       queue_work(data->gear_workqueue, &data->change_cpu_gear_work);
+       abox_check_cpu_gear(dev, data, old_id, old_gear, id, gear);
+
+       return 0;
+}
+
+void abox_cpu_gear_barrier(struct abox_data *data)
+{
+       flush_work(&data->change_cpu_gear_work);
+}
+
+int abox_request_cpu_gear_sync(struct device *dev, struct abox_data *data,
+               const void *id, unsigned int gear)
+{
+       int ret = abox_request_cpu_gear(dev, data, id, gear);
+
+       abox_cpu_gear_barrier(data);
+       return ret;
+}
+
+void abox_clear_cpu_gear_requests(struct device *dev, struct abox_data *data)
+{
+       struct abox_qos_request *req;
+       size_t len = ARRAY_SIZE(data->cpu_gear_requests);
+
+       dev_info(dev, "%s\n", __func__);
+
+       for (req = data->cpu_gear_requests; req - data->cpu_gear_requests < len
+                       && req->id; req++) {
+               if (req->value < ABOX_CPU_GEAR_MIN)
+                       abox_request_cpu_gear(dev, data, req->id,
+                                       ABOX_CPU_GEAR_MIN);
+       }
+}
+
+static void abox_change_int_freq_work_func(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data,
+                       change_int_freq_work);
+       struct device *dev = &data->pdev->dev;
+       struct abox_qos_request *request;
+       unsigned int freq = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (request = data->int_requests; request - data->int_requests <
+                       ARRAY_SIZE(data->int_requests) && request->id;
+                       request++) {
+               if (freq < request->value)
+                       freq = request->value;
+
+               dev_dbg(dev, "id=%p, value=%u, freq=%u\n", request->id,
+                               request->value, freq);
+       }
+
+       data->int_freq = freq;
+       pm_qos_update_request(&abox_pm_qos_int, data->int_freq);
+
+       dev_info(dev, "pm qos request int: %dHz\n", pm_qos_request(
+                       abox_pm_qos_int.pm_qos_class));
+}
+
+static int abox_request_int_freq(struct device *dev, struct abox_data *data,
+               void *id, unsigned int int_freq)
+{
+       struct abox_qos_request *request;
+
+       dev_info(dev, "%s(%p, %u)\n", __func__, id, int_freq);
+
+       if (!id)
+               id = (void *)DEFAULT_INT_FREQ_ID;
+
+       for (request = data->int_requests; request - data->int_requests <
+                       ARRAY_SIZE(data->int_requests) && request->id &&
+                       request->id != id; request++) {
+       }
+
+       request->value = int_freq;
+       wmb(); /* value is read after id in reading function */
+       request->id = id;
+
+       if (request - data->int_requests >= ARRAY_SIZE(data->int_requests)) {
+               dev_err(dev, "%s: out of index. id=%p, int_freq=%u\n", __func__,
+                               id, int_freq);
+               return -ENOMEM;
+       }
+
+       schedule_work(&data->change_int_freq_work);
+
+       return 0;
+}
+
+static void abox_change_mif_freq_work_func(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data,
+                       change_mif_freq_work);
+       struct device *dev = &data->pdev->dev;
+       struct abox_qos_request *request;
+       unsigned int freq = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (request = data->mif_requests; request - data->mif_requests <
+                       ARRAY_SIZE(data->mif_requests) && request->id;
+                       request++) {
+               if (freq < request->value)
+                       freq = request->value;
+
+               dev_dbg(dev, "id=%p, value=%u, freq=%u\n", request->id,
+                               request->value, freq);
+       }
+
+       data->mif_freq = freq;
+       pm_qos_update_request(&abox_pm_qos_mif, data->mif_freq);
+
+       dev_info(dev, "pm qos request mif: %dHz\n", pm_qos_request(
+                       abox_pm_qos_mif.pm_qos_class));
+}
+
+static int abox_request_mif_freq(struct device *dev, struct abox_data *data,
+               void *id, unsigned int mif_freq)
+{
+       struct abox_qos_request *request;
+
+       dev_info(dev, "%s(%p, %u)\n", __func__, id, mif_freq);
+
+       if (!id)
+               id = (void *)DEFAULT_MIF_FREQ_ID;
+
+       for (request = data->mif_requests; request - data->mif_requests <
+                       ARRAY_SIZE(data->mif_requests) && request->id &&
+                       request->id != id; request++) {
+       }
+
+       request->value = mif_freq;
+       wmb(); /* value is read after id in reading function */
+       request->id = id;
+
+       if (request - data->mif_requests >= ARRAY_SIZE(data->mif_requests)) {
+               dev_err(dev, "%s: out of index. id=%p, mif_freq=%u\n", __func__,
+                               id, mif_freq);
+               return -ENOMEM;
+       }
+
+       schedule_work(&data->change_mif_freq_work);
+
+       return 0;
+}
+
+static void abox_change_lit_freq_work_func(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data,
+                       change_lit_freq_work);
+       struct device *dev = &data->pdev->dev;
+       size_t array_size = ARRAY_SIZE(data->lit_requests);
+       struct abox_qos_request *request;
+       unsigned int freq = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (request = data->lit_requests;
+                       request - data->lit_requests < array_size &&
+                       request->id; request++) {
+               if (freq < request->value)
+                       freq = request->value;
+
+               dev_dbg(dev, "id=%p, value=%u, freq=%u\n", request->id,
+                               request->value, freq);
+       }
+
+       data->lit_freq = freq;
+       pm_qos_update_request(&abox_pm_qos_lit, data->lit_freq);
+
+       dev_info(dev, "pm qos request little: %dkHz\n",
+                       pm_qos_request(abox_pm_qos_lit.pm_qos_class));
+}
+
+int abox_request_lit_freq(struct device *dev, struct abox_data *data,
+               void *id, unsigned int freq)
+{
+       size_t array_size = ARRAY_SIZE(data->lit_requests);
+       struct abox_qos_request *request;
+
+       if (!id)
+               id = (void *)DEFAULT_LIT_FREQ_ID;
+
+       for (request = data->lit_requests;
+                       request - data->lit_requests < array_size &&
+                       request->id && request->id != id; request++) {
+       }
+
+       if ((request->id == id) && (request->value == freq))
+               return 0;
+
+       request->value = freq;
+       wmb(); /* value is read after id in reading function */
+       request->id = id;
+
+       dev_info(dev, "%s(%p, %u)\n", __func__, id, freq);
+
+       if (request - data->lit_requests >= ARRAY_SIZE(data->lit_requests)) {
+               dev_err(dev, "%s: out of index. id=%p, freq=%u\n",
+                               __func__, id, freq);
+               return -ENOMEM;
+       }
+
+       schedule_work(&data->change_lit_freq_work);
+
+       return 0;
+}
+
+static void abox_change_big_freq_work_func(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data,
+                       change_big_freq_work);
+       struct device *dev = &data->pdev->dev;
+       size_t array_size = ARRAY_SIZE(data->big_requests);
+       struct abox_qos_request *request;
+       unsigned int freq = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (request = data->big_requests;
+                       request - data->big_requests < array_size &&
+                       request->id; request++) {
+               if (freq < request->value)
+                       freq = request->value;
+
+               dev_dbg(dev, "id=%p, value=%u, freq=%u\n", request->id,
+                               request->value, freq);
+       }
+
+       data->big_freq = freq;
+       pm_qos_update_request(&abox_pm_qos_big, data->big_freq);
+
+       dev_info(dev, "pm qos request big: %dkHz\n",
+                       pm_qos_request(abox_pm_qos_big.pm_qos_class));
+}
+
+int abox_request_big_freq(struct device *dev, struct abox_data *data,
+               void *id, unsigned int freq)
+{
+       size_t array_size = ARRAY_SIZE(data->big_requests);
+       struct abox_qos_request *request;
+
+       if (!id)
+               id = (void *)DEFAULT_BIG_FREQ_ID;
+
+       for (request = data->big_requests;
+                       request - data->big_requests < array_size &&
+                       request->id && request->id != id; request++) {
+       }
+
+       if ((request->id == id) && (request->value == freq))
+               return 0;
+
+       dev_info(dev, "%s(%p, %u)\n", __func__, id, freq);
+
+       request->value = freq;
+       wmb(); /* value is read after id in reading function */
+       request->id = id;
+
+       if (request - data->big_requests >= ARRAY_SIZE(data->big_requests)) {
+               dev_err(dev, "%s: out of index. id=%p, freq=%u\n",
+                               __func__, id, freq);
+               return -ENOMEM;
+       }
+
+       schedule_work(&data->change_big_freq_work);
+
+       return 0;
+}
+
+static void abox_change_hmp_boost_work_func(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data,
+                       change_hmp_boost_work);
+       struct device *dev = &data->pdev->dev;
+       size_t array_size = ARRAY_SIZE(data->hmp_requests);
+       struct abox_qos_request *request;
+       unsigned int on = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (request = data->hmp_requests;
+                       request - data->hmp_requests < array_size &&
+                       request->id; request++) {
+               if (request->value)
+                       on = request->value;
+
+               dev_dbg(dev, "id=%p, value=%u, on=%u\n", request->id,
+                               request->value, on);
+       }
+
+       if (data->hmp_boost != on) {
+               dev_info(dev, "request hmp boost: %d\n", on);
+
+               data->hmp_boost = on;
+#ifdef CONFIG_SCHED_HMP
+               set_hmp_boost(on);
+#endif
+       }
+}
+
+int abox_request_hmp_boost(struct device *dev, struct abox_data *data,
+               void *id, unsigned int on)
+{
+       size_t array_size = ARRAY_SIZE(data->hmp_requests);
+       struct abox_qos_request *request;
+
+       if (!id)
+               id = (void *)DEFAULT_HMP_BOOST_ID;
+
+       for (request = data->hmp_requests;
+                       request - data->hmp_requests < array_size &&
+                       request->id && request->id != id; request++) {
+       }
+
+       if ((request->id == id) && (request->value == on))
+               return 0;
+
+       dev_info(dev, "%s(%p, %u)\n", __func__, id, on);
+
+       request->value = on;
+       wmb(); /* value is read after id in reading function */
+       request->id = id;
+
+       if (request - data->hmp_requests >= ARRAY_SIZE(data->hmp_requests)) {
+               dev_err(dev, "%s: out of index. id=%p, on=%u\n",
+                               __func__, id, on);
+               return -ENOMEM;
+       }
+
+       schedule_work(&data->change_hmp_boost_work);
+
+       return 0;
+}
+
+void abox_request_dram_on(struct platform_device *pdev_abox, void *id, bool on)
+{
+       struct device *dev = &pdev_abox->dev;
+       struct abox_data *data = platform_get_drvdata(pdev_abox);
+       struct abox_dram_request *request;
+       unsigned int val = 0x0;
+
+       dev_dbg(dev, "%s(%d)\n", __func__, on);
+
+       for (request = data->dram_requests; request - data->dram_requests <
+                       ARRAY_SIZE(data->dram_requests) && request->id &&
+                       request->id != id; request++) {
+       }
+
+       request->on = on;
+       wmb(); /* on is read after id in reading function */
+       request->id = id;
+
+       for (request = data->dram_requests; request - data->dram_requests <
+                       ARRAY_SIZE(data->dram_requests) && request->id;
+                       request++) {
+               if (request->on) {
+                       val = ABOX_SYSPOWER_CTRL_MASK;
+                       break;
+               }
+       }
+
+       regmap_write(data->regmap, ABOX_SYSPOWER_CTRL, val);
+       dev_dbg(dev, "%s: SYSPOWER_CTRL=%08x\n", __func__,
+                       ({regmap_read(data->regmap, ABOX_SYSPOWER_CTRL, &val);
+                       val; }));
+}
+
+int abox_iommu_map(struct device *dev, unsigned long iova,
+               phys_addr_t paddr, size_t size)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       int ret;
+
+       dev_info(dev, "%s(%lx, %pa, %zx)\n", __func__, iova, &paddr, size);
+
+       ret = iommu_map(data->iommu_domain, iova, paddr, size, 0);
+       if (ret < 0) {
+               dev_err(dev, "Failed to iommu_map: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(abox_iommu_map);
+
+int abox_iommu_unmap(struct device *dev, unsigned long iova,
+               phys_addr_t paddr, size_t size)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       int ret;
+
+       dev_info(dev, "%s(%lx, %pa, %zx)\n", __func__, iova, &paddr, size);
+
+       ret = iommu_unmap(data->iommu_domain, iova, size);
+       if (ret < 0) {
+               dev_err(dev, "Failed to iommu_unmap: %d\n", ret);
+               return ret;
+       }
+
+       exynos_sysmmu_tlb_invalidate(data->iommu_domain, iova, size);
+
+       return 0;
+}
+EXPORT_SYMBOL(abox_iommu_unmap);
+
+int abox_register_irq_handler(struct device *dev, int ipc_id,
+               abox_irq_handler_t irq_handler, void *dev_id)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct abox_irq_action *irq_action = NULL;
+       bool new_handler = true;
+
+       if (ipc_id >= IPC_ID_COUNT)
+               return -EINVAL;
+
+       list_for_each_entry(irq_action, &data->irq_actions, list) {
+               if (irq_action->irq == ipc_id && irq_action->dev_id == dev_id) {
+                       new_handler = false;
+                       break;
+               }
+       }
+
+       if (new_handler) {
+               irq_action = devm_kzalloc(dev, sizeof(struct abox_irq_action),
+                               GFP_KERNEL);
+               if (IS_ERR_OR_NULL(irq_action)) {
+                       dev_err(dev, "%s: kmalloc fail\n", __func__);
+                       return -ENOMEM;
+               }
+               irq_action->irq = ipc_id;
+               irq_action->dev_id = dev_id;
+               list_add_tail(&irq_action->list, &data->irq_actions);
+       }
+
+       irq_action->irq_handler = irq_handler;
+
+       return 0;
+}
+EXPORT_SYMBOL(abox_register_irq_handler);
+
+static int abox_control_asrc(struct snd_soc_dapm_widget *w, bool on)
+{
+       struct snd_kcontrol *kcontrol;
+       struct snd_soc_component *cmpnt;
+       struct soc_enum *e;
+       unsigned int reg, mask, val;
+
+       if (!w || !w->name || !w->num_kcontrols)
+               return -EINVAL;
+
+       kcontrol = w->kcontrols[0];
+       cmpnt = w->dapm->component;
+       e = (struct soc_enum *)kcontrol->private_value;
+       reg = e->reg;
+       mask = e->mask << e->shift_l;
+       val = (on ? 1 : 0) << e->shift_l;
+
+       return snd_soc_component_update_bits(cmpnt, reg, mask, val);
+}
+
+static bool abox_is_asrc_widget(struct snd_soc_dapm_widget *w)
+{
+       return w->name && !!strstr(w->name, "ASRC");
+}
+
+int abox_try_to_asrc_off(struct device *dev, struct abox_data *data,
+               struct snd_soc_pcm_runtime *fe, int stream)
+{
+       struct snd_soc_dai *dai = fe->cpu_dai;
+       struct snd_soc_component *cmpnt = dai->component;
+       struct snd_soc_dapm_widget *w, *w_asrc = NULL;
+       LIST_HEAD(widget_list);
+       enum ABOX_CONFIGMSG rate, format;
+       unsigned int out_rate = 0, out_width = 0;
+       unsigned int pcm_rate, pcm_width;
+
+       if (!abox_test_quirk(data, ABOX_QUIRK_TRY_TO_ASRC_OFF))
+               return 0;
+
+       dev_dbg(dev, "%s(%s)\n", __func__, dai->name);
+
+       pcm_rate = params_rate(&fe->dpcm[stream].hw_params);
+       pcm_width = params_width(&fe->dpcm[stream].hw_params);
+
+       snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(cmpnt));
+       /*
+        * For snd_soc_dapm_connected_{output,input}_ep fully discover the graph
+        * we need to reset the cached number of inputs and outputs.
+        */
+       list_for_each_entry(w, &cmpnt->card->widgets, list) {
+               w->endpoints[SND_SOC_DAPM_DIR_IN] = -1;
+               w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1;
+       }
+       if (dai->playback_widget)
+               snd_soc_dapm_connected_output_ep(dai->playback_widget,
+                               &widget_list);
+       if (dai->capture_widget)
+               snd_soc_dapm_connected_input_ep(dai->capture_widget,
+                               &widget_list);
+
+       list_for_each_entry(w, &widget_list, work_list) {
+               dev_dbg(dev, "%s", w->name);
+
+               if (abox_find_nrf_stream(w, stream, &rate, &format)) {
+                       out_rate = abox_get_sif_rate(data, rate);
+                       out_width = abox_get_sif_width(data, format);
+                       dev_dbg(dev, "%s: rate=%u, width=%u\n",
+                                       w->name, out_rate, out_width);
+               }
+
+               if (abox_is_asrc_widget(w)) {
+                       w_asrc = w;
+                       dev_dbg(dev, "%s is asrc\n", w->name);
+               }
+
+               if (w_asrc && out_rate && out_width)
+                       break;
+       }
+       snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(cmpnt));
+
+       if (!w_asrc || !out_rate || !out_width) {
+               dev_warn(dev, "%s: incomplete path: w_asrc=%p, out_rate=%u, out_width=%u",
+                               __func__, w_asrc, out_rate, out_width);
+               return -EINVAL;
+       }
+
+       return abox_control_asrc(w_asrc, (pcm_rate != out_rate) ||
+                       (pcm_width != out_width));
+}
+
+static int abox_register_if_routes(struct device *dev,
+               const struct snd_soc_dapm_route *route_base, int num,
+               struct snd_soc_dapm_context *dapm, const char *name)
+{
+       struct snd_soc_dapm_route *route;
+       int i;
+
+       route = devm_kmemdup(dev, route_base, sizeof(*route_base) * num,
+                       GFP_KERNEL);
+       if (!route) {
+               dev_err(dev, "%s: insufficient memory\n", __func__);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < num; i++) {
+               if (route[i].sink)
+                       route[i].sink = devm_kasprintf(dev, GFP_KERNEL,
+                                       route[i].sink, name);
+               if (route[i].control)
+                       route[i].control = devm_kasprintf(dev, GFP_KERNEL,
+                                       route[i].control, name);
+               if (route[i].source)
+                       route[i].source = devm_kasprintf(dev, GFP_KERNEL,
+                                       route[i].source, name);
+       }
+
+       snd_soc_dapm_add_routes(dapm, route, num);
+       devm_kfree(dev, route);
+
+       return 0;
+}
+
+int abox_register_if(struct platform_device *pdev_abox,
+               struct platform_device *pdev_if, unsigned int id,
+               struct snd_soc_dapm_context *dapm, const char *name,
+               bool playback, bool capture)
+{
+       struct device *dev = &pdev_if->dev;
+       struct abox_data *data = platform_get_drvdata(pdev_abox);
+       int ret;
+
+       static const struct snd_soc_dapm_route route_base_pla[] = {
+               /* sink, control, source */
+               {"%s Playback", NULL, "%s SPK"},
+       };
+
+       static const struct snd_soc_dapm_route route_base_cap[] = {
+               /* sink, control, source */
+               {"SPUSM", "%s", "%s Capture"},
+               {"NSRC0", "%s", "%s Capture"},
+               {"NSRC1", "%s", "%s Capture"},
+               {"NSRC2", "%s", "%s Capture"},
+               {"NSRC3", "%s", "%s Capture"},
+       };
+
+       if (id >= ARRAY_SIZE(data->pdev_if)) {
+               dev_err(dev, "%s: invalid id(%u)\n", __func__, id);
+               return -EINVAL;
+       }
+
+       if (data->cmpnt->name_prefix && dapm->component->name_prefix &&
+                       strcmp(data->cmpnt->name_prefix,
+                       dapm->component->name_prefix)) {
+               dev_err(dev, "%s: name prefix is different: %s != %s\n",
+                               __func__, data->cmpnt->name_prefix,
+                               dapm->component->name_prefix);
+               return -EINVAL;
+       }
+
+       data->pdev_if[id] = pdev_if;
+       if (id > data->if_count)
+               data->if_count = id + 1;
+
+       if (playback) {
+               ret = abox_register_if_routes(dev, route_base_pla,
+                               ARRAY_SIZE(route_base_pla), dapm, name);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (capture) {
+               ret = abox_register_if_routes(dev, route_base_cap,
+                               ARRAY_SIZE(route_base_cap), dapm, name);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int abox_register_rdma(struct platform_device *pdev_abox,
+               struct platform_device *pdev_rdma, unsigned int id)
+{
+       struct abox_data *data = platform_get_drvdata(pdev_abox);
+
+       if (id < ARRAY_SIZE(data->pdev_rdma)) {
+               data->pdev_rdma[id] = pdev_rdma;
+               if (id > data->rdma_count)
+                       data->rdma_count = id + 1;
+       } else {
+               dev_err(&data->pdev->dev, "%s: invalid id(%u)\n", __func__, id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int abox_register_wdma(struct platform_device *pdev_abox,
+               struct platform_device *pdev_wdma, unsigned int id)
+{
+       struct abox_data *data = platform_get_drvdata(pdev_abox);
+
+       if (id < ARRAY_SIZE(data->pdev_wdma)) {
+               data->pdev_wdma[id] = pdev_wdma;
+               if (id > data->wdma_count)
+                       data->wdma_count = id + 1;
+       } else {
+               dev_err(&data->pdev->dev, "%s: invalid id(%u)\n", __func__, id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int abox_component_control_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_component_kcontrol_value *value =
+                       (void *)kcontrol->private_value;
+
+       dev_dbg(dev, "%s(%s)\n", __func__, kcontrol->id.name);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = value->control->count;
+       uinfo->value.integer.min = value->control->min;
+       uinfo->value.integer.max = value->control->max;
+       return 0;
+}
+
+static ABOX_IPC_MSG abox_component_control_get_msg;
+
+static int abox_component_control_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct abox_component_kcontrol_value *value =
+                       (void *)kcontrol->private_value;
+       ABOX_IPC_MSG *msg = &abox_component_control_get_msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg->msg.system;
+       int i, ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (value->cache_only) {
+               for (i = 0; i < value->control->count; i++)
+                       ucontrol->value.integer.value[i] = value->cache[i];
+               return 0;
+       }
+
+       msg->ipcid = IPC_SYSTEM;
+       system_msg->msgtype = ABOX_REQUEST_COMPONENT_CONTROL;
+       system_msg->param1 = value->desc->id;
+       system_msg->param2 = value->control->id;
+       ret = abox_request_ipc(dev, msg->ipcid, msg, sizeof(*msg), 0, 1);
+       if (ret < 0)
+               return ret;
+
+       ret = wait_event_timeout(data->ipc_wait_queue,
+                       system_msg->msgtype == ABOX_REPORT_COMPONENT_CONTROL,
+                       msecs_to_jiffies(1000));
+       if (system_msg->msgtype != ABOX_REPORT_COMPONENT_CONTROL)
+               return -ETIME;
+
+       for (i = 0; i < value->control->count; i++) {
+               long val = (long)system_msg->bundle.param_s32[i];
+
+               ucontrol->value.integer.value[i] = val;
+       }
+
+       return 0;
+}
+
+static int abox_component_control_put_ipc(struct device *dev,
+               struct abox_component_kcontrol_value *value)
+{
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+       int i;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (i = 0; i < value->control->count; i++) {
+               int val = value->cache[i];
+               char *name = value->control->name;
+
+               system_msg->bundle.param_s32[i] = val;
+               dev_dbg(dev, "%s: %s[%d] <= %d", __func__, name, i, val);
+       }
+
+       msg.ipcid = IPC_SYSTEM;
+       system_msg->msgtype = ABOX_UPDATE_COMPONENT_CONTROL;
+       system_msg->param1 = value->desc->id;
+       system_msg->param2 = value->control->id;
+
+       return abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
+}
+
+static int abox_component_control_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_component_kcontrol_value *value =
+                       (void *)kcontrol->private_value;
+       int i;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (i = 0; i < value->control->count; i++) {
+               int val = (int)ucontrol->value.integer.value[i];
+               char *name = kcontrol->id.name;
+
+               value->cache[i] = val;
+               dev_dbg(dev, "%s: %s[%d] <= %d", __func__, name, i, val);
+       }
+
+       return abox_component_control_put_ipc(dev, value);
+}
+
+#define ABOX_COMPONENT_KCONTROL(xname, xdesc, xcontrol)        \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .info = abox_component_control_info, \
+       .get = abox_component_control_get, \
+       .put = abox_component_control_put, \
+       .private_value = \
+               (unsigned long)&(struct abox_component_kcontrol_value) \
+               {.desc = xdesc, .control = xcontrol} }
+
+struct snd_kcontrol_new abox_component_kcontrols[] = {
+       ABOX_COMPONENT_KCONTROL(NULL, NULL, NULL),
+};
+
+static void abox_register_component_work_func(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data,
+                       register_component_work);
+       struct device *dev = &data->pdev->dev;
+       struct abox_component *component;
+       int i;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (component = data->components; ((component - data->components) <
+                       ARRAY_SIZE(data->components)); component++) {
+               struct ABOX_COMPONENT_DESCRIPTIOR *desc = component->desc;
+
+               if (!component->desc || component->registered)
+                       continue;
+
+               for (i = 0; i < desc->control_count; i++) {
+                       struct ABOX_COMPONENT_CONTROL *control =
+                                       &desc->controls[i];
+                       struct abox_component_kcontrol_value *value;
+                       char kcontrol_name[64];
+
+                       value = devm_kzalloc(dev, sizeof(*value) +
+                                       (control->count *
+                                       sizeof(value->cache[0])), GFP_KERNEL);
+                       if (IS_ERR_OR_NULL(value)) {
+                               dev_err(dev, "%s: kmalloc fail\n", __func__);
+                               continue;
+                       }
+                       value->desc = desc;
+                       value->control = control;
+                       list_add_tail(&value->list, &component->value_list);
+
+                       snprintf(kcontrol_name, sizeof(kcontrol_name), "%s %s",
+                                       desc->name, control->name);
+
+                       abox_component_kcontrols[0].name = devm_kstrdup(dev,
+                                       kcontrol_name, GFP_KERNEL);
+                       abox_component_kcontrols[0].private_value =
+                                       (unsigned long)value;
+                       if (data->cmpnt) {
+                               snd_soc_add_component_controls(data->cmpnt,
+                                               abox_component_kcontrols, 1);
+                       }
+               }
+               component->registered = true;
+       }
+}
+
+
+static int abox_register_component(struct device *dev,
+               struct ABOX_COMPONENT_DESCRIPTIOR *desc)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct abox_component *component;
+
+       dev_dbg(dev, "%s(%d, %s)\n", __func__, desc->id, desc->name);
+
+       for (component = data->components;
+                       ((component - data->components) <
+                       ARRAY_SIZE(data->components)) &&
+                       component->desc && component->desc != desc;
+                       component++) {
+       }
+
+       if (!component->desc) {
+               component->desc = desc;
+               INIT_LIST_HEAD(&component->value_list);
+               schedule_work(&data->register_component_work);
+       }
+
+       return 0;
+}
+
+static void abox_restore_components(struct device *dev, struct abox_data *data)
+{
+       struct abox_component *component;
+       struct abox_component_kcontrol_value *value;
+       size_t len = ARRAY_SIZE(data->components);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (component = data->components;
+                       (component - data->components) < len &&
+                       component->registered; component++) {
+               list_for_each_entry(value, &component->value_list, list) {
+                       abox_component_control_put_ipc(dev, value);
+                       value->cache_only = false;
+               }
+       }
+}
+
+static void abox_cache_components(struct device *dev, struct abox_data *data)
+{
+       struct abox_component *component;
+       struct abox_component_kcontrol_value *value;
+       size_t len = ARRAY_SIZE(data->components);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (component = data->components;
+                       (component - data->components) < len &&
+                       component->registered; component++) {
+               list_for_each_entry(value, &component->value_list, list) {
+                       value->cache_only = true;
+               }
+       }
+}
+
+static bool abox_is_calliope_incompatible(struct device *dev)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+
+       memcpy(&msg, data->sram_base + 0x30040, 0x3C);
+
+       return ((system_msg->param3 >> 24) == 'A');
+}
+
+static void abox_restore_data(struct device *dev)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       int i;
+
+       dev_info(dev, "%s\n", __func__);
+
+       for (i = SET_MIXER_SAMPLE_RATE; i <= SET_INMUX4_SAMPLE_RATE; i++)
+               abox_sample_rate_put_ipc(dev,
+                               data->sif_rate[abox_sif_idx(i)], i);
+       for (i = SET_MIXER_FORMAT; i <= SET_INMUX4_FORMAT; i++)
+               abox_sif_format_put_ipc(dev,
+                               data->sif_format[abox_sif_idx(i)],
+                               data->sif_channels[abox_sif_idx(i)], i);
+       abox_erap_handler_put_ipc(dev, ERAP_ECHO_CANCEL,
+                       data->erap_status[ERAP_ECHO_CANCEL]);
+       abox_erap_handler_put_ipc(dev, ERAP_VI_SENSE,
+                       data->erap_status[ERAP_VI_SENSE]);
+       abox_audio_mode_put_ipc(dev, data->audio_mode);
+       abox_sound_type_put_ipc(dev, data->sound_type);
+       abox_restore_components(dev, data);
+       abox_effect_restore();
+}
+
+static void abox_boot_done_work_func(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data,
+                       boot_done_work);
+       struct platform_device *pdev = data->pdev;
+       struct device *dev = &pdev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       abox_cpu_pm_ipc(dev, true);
+       abox_restore_data(dev);
+       abox_request_cpu_gear(dev, data, (void *)DEFAULT_CPU_GEAR_ID,
+                       ABOX_CPU_GEAR_MIN);
+       abox_request_dram_on(pdev, dev, false);
+}
+
+static void abox_boot_done(struct device *dev, unsigned int version)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       char ver_char[4];
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       data->calliope_version = version;
+       memcpy(ver_char, &version, sizeof(ver_char));
+       dev_info(dev, "Calliope is ready to sing (version:%c%c%c%c)\n",
+                       ver_char[3], ver_char[2], ver_char[1], ver_char[0]);
+       data->calliope_state = CALLIOPE_ENABLED;
+       schedule_work(&data->boot_done_work);
+
+       wake_up(&data->ipc_wait_queue);
+}
+
+static irqreturn_t abox_dma_irq_handler(int irq, struct abox_data *data)
+{
+       struct device *dev = &data->pdev->dev;
+       int id;
+       struct platform_device **pdev_dma;
+       struct abox_platform_data *platform_data;
+
+       dev_dbg(dev, "%s(%d)\n", __func__, irq);
+
+       switch (irq) {
+       case RDMA0_BUF_EMPTY:
+               id = 0;
+               pdev_dma = data->pdev_rdma;
+               break;
+       case RDMA1_BUF_EMPTY:
+               id = 1;
+               pdev_dma = data->pdev_rdma;
+               break;
+       case RDMA2_BUF_EMPTY:
+               id = 2;
+               pdev_dma = data->pdev_rdma;
+               break;
+       case RDMA3_BUF_EMPTY:
+               id = 3;
+               pdev_dma = data->pdev_rdma;
+               break;
+       case WDMA0_BUF_FULL:
+               id = 0;
+               pdev_dma = data->pdev_wdma;
+               break;
+       case WDMA1_BUF_FULL:
+               id = 1;
+               pdev_dma = data->pdev_wdma;
+               break;
+       default:
+               return IRQ_NONE;
+       }
+
+       if (unlikely(!pdev_dma[id])) {
+               dev_err(dev, "spurious dma irq: irq=%d id=%d\n", irq, id);
+               return IRQ_HANDLED;
+       }
+
+       platform_data = platform_get_drvdata(pdev_dma[id]);
+       if (unlikely(!platform_data)) {
+               dev_err(dev, "dma irq with null data: irq=%d id=%d\n", irq, id);
+               return IRQ_HANDLED;
+       }
+
+       platform_data->pointer = 0;
+       snd_pcm_period_elapsed(platform_data->substream);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t abox_registered_ipc_handler(struct device *dev, int irq,
+               struct abox_data *data, ABOX_IPC_MSG *msg, bool broadcast)
+{
+       struct abox_irq_action *action;
+       irqreturn_t ret = IRQ_NONE;
+
+       dev_dbg(dev, "%s: irq=%d\n", __func__, irq);
+
+       list_for_each_entry(action, &data->irq_actions, list) {
+               if (action->irq != irq)
+                       continue;
+
+               ret = action->irq_handler(irq, action->dev_id, msg);
+               if (!broadcast && ret == IRQ_HANDLED)
+                       break;
+       }
+
+       return ret;
+}
+
+static void abox_system_ipc_handler(struct device *dev,
+               struct abox_data *data, ABOX_IPC_MSG *msg)
+{
+       struct IPC_SYSTEM_MSG *system_msg = &msg->msg.system;
+       int ret;
+
+       dev_dbg(dev, "msgtype=%d\n", system_msg->msgtype);
+
+       switch (system_msg->msgtype) {
+       case ABOX_BOOT_DONE:
+               if (abox_is_calliope_incompatible(dev))
+                       dev_err(dev, "Calliope is not compatible with the driver\n");
+
+               abox_boot_done(dev, system_msg->param3);
+               abox_registered_ipc_handler(dev, IPC_SYSTEM, data, msg, true);
+               break;
+       case ABOX_CHANGE_GEAR:
+               abox_request_cpu_gear(dev, data,
+                               (void *)(long)system_msg->param2,
+                               system_msg->param1);
+               break;
+       case ABOX_END_L2C_CONTROL:
+               data->l2c_controlled = true;
+               wake_up(&data->ipc_wait_queue);
+               break;
+       case ABOX_REQUEST_L2C:
+       {
+               void *id = (void *)(long)system_msg->param2;
+               bool on = !!system_msg->param1;
+
+               abox_request_l2c(dev, data, id, on);
+               break;
+       }
+       case ABOX_REQUEST_SYSCLK:
+               switch (system_msg->param2) {
+               default:
+                       /* fall through */
+               case 0:
+                       abox_request_mif_freq(dev, data,
+                                       (void *)(long)system_msg->param3,
+                                       system_msg->param1);
+                       break;
+               case 1:
+                       abox_request_int_freq(dev, data,
+                                       (void *)(long)system_msg->param3,
+                                       system_msg->param1);
+                       break;
+               }
+               break;
+       case ABOX_REPORT_LOG:
+               ret = abox_log_register_buffer(dev, system_msg->param1,
+                               abox_addr_to_kernel_addr(data,
+                               system_msg->param2));
+               if (ret < 0) {
+                       dev_err(dev, "log buffer registration failed: %u, %u\n",
+                                       system_msg->param1, system_msg->param2);
+               }
+               break;
+       case ABOX_FLUSH_LOG:
+               break;
+       case ABOX_REPORT_DUMP:
+               ret = abox_dump_register_buffer(dev, system_msg->param1,
+                               system_msg->bundle.param_bundle,
+                               abox_addr_to_kernel_addr(data,
+                               system_msg->param2),
+                               abox_addr_to_phys_addr(data,
+                               system_msg->param2),
+                               system_msg->param3);
+               if (ret < 0) {
+                       dev_err(dev, "dump buffer registration failed: %u, %u\n",
+                                       system_msg->param1, system_msg->param2);
+               }
+               break;
+       case ABOX_FLUSH_DUMP:
+               abox_dump_period_elapsed(system_msg->param1,
+                               system_msg->param2);
+               break;
+       case ABOX_END_CLAIM_SRAM:
+               data->ima_claimed = true;
+               wake_up(&data->ipc_wait_queue);
+               break;
+       case ABOX_END_RECLAIM_SRAM:
+               data->ima_claimed = false;
+               wake_up(&data->ipc_wait_queue);
+               break;
+       case ABOX_REPORT_COMPONENT:
+               abox_register_component(dev,
+                               abox_addr_to_kernel_addr(data,
+                               system_msg->param1));
+               break;
+       case ABOX_REPORT_COMPONENT_CONTROL:
+               abox_component_control_get_msg = *msg;
+               wake_up(&data->ipc_wait_queue);
+               break;
+       case ABOX_REPORT_FAULT:
+       {
+               const char *type;
+
+               switch (system_msg->param1) {
+               case 1:
+                       type = "data abort";
+                       break;
+               case 2:
+                       type = "prefetch abort";
+                       break;
+               case 3:
+                       type = "os error";
+                       break;
+               case 4:
+                       type = "vss error";
+                       break;
+               case 5:
+                       type = "undefined exception";
+                       break;
+               default:
+                       type = "unknown error";
+                       break;
+               }
+               dev_err(dev, "%s(%08X, %08X, %08X) is reported from calliope\n",
+                               type, system_msg->param1, system_msg->param2,
+                               system_msg->param3);
+
+               switch (system_msg->param1) {
+               case 1:
+               case 2:
+                       abox_dbg_print_gpr_from_addr(dev, data,
+                                       abox_addr_to_kernel_addr(data,
+                                       system_msg->bundle.param_s32[0]));
+                       abox_dbg_dump_gpr_from_addr(dev,
+                                       abox_addr_to_kernel_addr(data,
+                                       system_msg->bundle.param_s32[0]),
+                                       ABOX_DBG_DUMP_FIRMWARE, type);
+                       abox_dbg_dump_mem(dev, data, ABOX_DBG_DUMP_FIRMWARE,
+                                       type);
+                       break;
+               case 4:
+                       abox_dbg_print_gpr(dev, data);
+                       abox_dbg_dump_gpr(dev, data, ABOX_DBG_DUMP_VSS, type);
+                       abox_dbg_dump_mem(dev, data, ABOX_DBG_DUMP_VSS, type);
+                       break;
+               default:
+                       abox_dbg_print_gpr(dev, data);
+                       abox_dbg_dump_gpr(dev, data, ABOX_DBG_DUMP_FIRMWARE,
+                                       type);
+                       abox_dbg_dump_mem(dev, data, ABOX_DBG_DUMP_FIRMWARE,
+                                       type);
+                       break;
+               }
+               abox_failsafe_report(dev);
+               break;
+       }
+       default:
+               dev_warn(dev, "Redundant system message: %d(%d, %d, %d)\n",
+                               system_msg->msgtype, system_msg->param1,
+                               system_msg->param2, system_msg->param3);
+               break;
+       }
+}
+
+static void abox_playback_ipc_handler(struct device *dev,
+               struct abox_data *data, ABOX_IPC_MSG *msg)
+{
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg->msg.pcmtask;
+       struct abox_platform_data *platform_data;
+       int id = pcmtask_msg->channel_id;
+
+       dev_dbg(dev, "msgtype=%d\n", pcmtask_msg->msgtype);
+
+       if ((id >= ARRAY_SIZE(data->pdev_rdma)) || !data->pdev_rdma[id]) {
+               irqreturn_t ret;
+
+               ret = abox_registered_ipc_handler(dev, IPC_PCMPLAYBACK, data,
+                               msg, false);
+               if (ret != IRQ_HANDLED)
+                       dev_err(dev, "pcm playback irq: id=%d\n", id);
+               return;
+       }
+
+       platform_data = platform_get_drvdata(data->pdev_rdma[id]);
+
+       switch (pcmtask_msg->msgtype) {
+       case PCM_PLTDAI_POINTER:
+               platform_data->pointer = pcmtask_msg->param.pointer;
+               snd_pcm_period_elapsed(platform_data->substream);
+               break;
+       case PCM_PLTDAI_ACK:
+               platform_data->ack_enabled = !!pcmtask_msg->param.trigger;
+               break;
+       default:
+               dev_warn(dev, "Redundant pcmtask message: %d\n",
+                               pcmtask_msg->msgtype);
+               break;
+       }
+}
+
+static void abox_capture_ipc_handler(struct device *dev,
+               struct abox_data *data, ABOX_IPC_MSG *msg)
+{
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg->msg.pcmtask;
+       struct abox_platform_data *platform_data;
+       int id = pcmtask_msg->channel_id;
+
+       dev_dbg(dev, "msgtype=%d\n", pcmtask_msg->msgtype);
+
+       if ((id >= ARRAY_SIZE(data->pdev_wdma)) || (!data->pdev_wdma[id])) {
+               irqreturn_t ret;
+
+               ret = abox_registered_ipc_handler(dev, IPC_PCMCAPTURE, data,
+                               msg, false);
+               if (ret != IRQ_HANDLED)
+                       dev_err(dev, "pcm capture irq: id=%d\n", id);
+               return;
+       }
+
+       platform_data = platform_get_drvdata(data->pdev_wdma[id]);
+
+       switch (pcmtask_msg->msgtype) {
+       case PCM_PLTDAI_POINTER:
+               platform_data->pointer = pcmtask_msg->param.pointer;
+               snd_pcm_period_elapsed(platform_data->substream);
+               break;
+       case PCM_PLTDAI_ACK:
+               platform_data->ack_enabled = !!pcmtask_msg->param.trigger;
+               break;
+       default:
+               dev_warn(dev, "Redundant pcmtask message: %d\n",
+                               pcmtask_msg->msgtype);
+               break;
+       }
+}
+
+static void abox_offload_ipc_handler(struct device *dev,
+               struct abox_data *data, ABOX_IPC_MSG *msg)
+{
+       struct IPC_OFFLOADTASK_MSG *offloadtask_msg = &msg->msg.offload;
+       int id = offloadtask_msg->channel_id;
+       struct abox_platform_data *platform_data;
+
+       if (id != 5) {
+               dev_warn(dev, "%s: unknown channel id(%d)\n", __func__, id);
+               id = 5;
+       }
+       platform_data = platform_get_drvdata(data->pdev_rdma[id]);
+
+       if (platform_data->compr_data.isr_handler)
+               platform_data->compr_data.isr_handler(data->pdev_rdma[id]);
+       else
+               dev_warn(dev, "Redundant offload message on rdma[%d]", id);
+}
+
+static irqreturn_t abox_irq_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct abox_data *data = platform_get_drvdata(pdev);
+       ABOX_IPC_MSG msg;
+
+       if (abox_dma_irq_handler(irq, data) == IRQ_HANDLED)
+               goto out;
+
+       memcpy(&msg, data->sram_base + data->ipc_rx_offset, sizeof(msg));
+       writel(0, data->sram_base + data->ipc_rx_ack_offset);
+
+       dev_dbg(dev, "%s: irq=%d, ipcid=%d\n", __func__, irq, msg.ipcid);
+
+       switch (irq) {
+       case IPC_SYSTEM:
+               abox_system_ipc_handler(dev, data, &msg);
+               break;
+       case IPC_PCMPLAYBACK:
+               abox_playback_ipc_handler(dev, data, &msg);
+               break;
+       case IPC_PCMCAPTURE:
+               abox_capture_ipc_handler(dev, data, &msg);
+               break;
+       case IPC_OFFLOAD:
+               abox_offload_ipc_handler(dev, data, &msg);
+               break;
+       default:
+               abox_registered_ipc_handler(dev, irq, data, &msg, false);
+               break;
+       }
+out:
+       abox_log_schedule_flush_all(dev);
+
+       dev_dbg(dev, "%s: exit\n", __func__);
+       return IRQ_HANDLED;
+}
+
+static int abox_cpu_pm_ipc(struct device *dev, bool resume)
+{
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system = &msg.msg.system;
+       int ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       msg.ipcid = IPC_SYSTEM;
+       system->msgtype = resume ? ABOX_RESUME : ABOX_SUSPEND;
+       ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg),
+                       1, 1);
+       if (!resume) {
+               int i = 1000;
+               unsigned int val;
+
+               do {
+                       exynos_pmu_read(ABOX_CPU_STANDBY, &val);
+               } while (--i && !(val & ABOX_CPU_STANDBY_WFI_MASK));
+
+               if (!(val & ABOX_CPU_STANDBY_WFI_MASK)) {
+                       dev_warn(dev, "calliope suspend time out\n");
+                       ret = -ETIME;
+               }
+       }
+
+       return ret;
+}
+
+static void abox_pad_retention(bool retention)
+{
+       if (retention) {
+#ifndef EMULATOR
+               exynos_pmu_update(GPIO_MODE_ABOX_SYS_PWR_REG, 0x1, 0x1);
+#else
+               update_mask_value(pmu_alive + GPIO_MODE_ABOX_SYS_PWR_REG,
+                               0x1, 0x1);
+#endif
+       } else {
+#ifndef EMULATOR
+               exynos_pmu_update(PAD_RETENTION_ABOX_OPTION,
+                               0x10000000, 0x10000000);
+               exynos_pmu_update(GPIO_MODE_ABOX_SYS_PWR_REG, 0x1, 0x1);
+#else
+               update_mask_value(pmu_alive + PAD_RETENTION_ABOX_OPTION,
+                               0x10000000, 0x10000000);
+               update_mask_value(pmu_alive + GPIO_MODE_ABOX_SYS_PWR_REG,
+                               0x1, 0x1);
+#endif
+       }
+}
+
+static void abox_cpu_power(bool on)
+{
+       pr_info("%s(%d)\n", __func__, on);
+
+#ifndef EMULATOR
+       exynos_pmu_update(ABOX_CPU_CONFIGURATION, ABOX_CPU_LOCAL_PWR_CFG,
+                       on ? ABOX_CPU_LOCAL_PWR_CFG : 0);
+#else
+       update_mask_value(pmu_alive + ABOX_CPU_CONFIGURATION,
+                       ABOX_CPU_LOCAL_PWR_CFG,
+                       on ? ABOX_CPU_LOCAL_PWR_CFG : 0);
+#endif
+}
+
+static int abox_cpu_enable(bool enable)
+{
+       unsigned int mask = ABOX_CPU_OPTION_ENABLE_CPU_MASK;
+       unsigned int val = (enable ? mask : 0);
+       unsigned int status = 0;
+       unsigned long after;
+
+
+       pr_info("%s(%d)\n", __func__, enable);
+
+#ifndef EMULATOR
+       exynos_pmu_update(ABOX_CPU_OPTION, mask, val);
+#else
+       update_mask_value(pmu_alive + ABOX_CPU_OPTION, mask, val);
+#endif
+       if (enable) {
+               after = jiffies + LIMIT_IN_JIFFIES;
+               do {
+#ifndef EMULATOR
+                       exynos_pmu_read(ABOX_CPU_STATUS, &status);
+#else
+                       status = readl(pmu_alive + ABOX_CPU_STATUS);
+#endif
+               } while (((status & ABOX_CPU_STATUS_STATUS_MASK)
+                               != ABOX_CPU_STATUS_STATUS_MASK)
+                               && time_is_after_eq_jiffies(after));
+               if (time_is_before_jiffies(after)) {
+                       pr_err("abox cpu enable timeout\n");
+                       return -ETIME;
+               }
+       }
+
+       return 0;
+
+}
+
+static void abox_save_register(struct abox_data *data)
+{
+       regcache_cache_only(data->regmap, true);
+       regcache_mark_dirty(data->regmap);
+}
+
+static void abox_restore_register(struct abox_data *data)
+{
+       regcache_cache_only(data->regmap, false);
+       regcache_sync(data->regmap);
+}
+
+static void abox_reload_extra_firmware(struct abox_data *data, const char *name)
+{
+       struct platform_device *pdev = data->pdev;
+       struct device *dev = &pdev->dev;
+       struct abox_extra_firmware *ext_fw;
+       int ret;
+
+       dev_dbg(dev, "%s(%s)\n", __func__, name);
+
+       for (ext_fw = data->firmware_extra; ext_fw - data->firmware_extra <
+                       ARRAY_SIZE(data->firmware_extra); ext_fw++) {
+               if (!ext_fw->name || strcmp(ext_fw->name, name))
+                       continue;
+
+               release_firmware(ext_fw->firmware);
+               ret = request_firmware(&ext_fw->firmware, ext_fw->name, dev);
+               if (ret < 0) {
+                       dev_err(dev, "%s: %s request failed\n", __func__,
+                                       ext_fw->name);
+                       break;
+               }
+               dev_info(dev, "%s is reloaded at %p (%zu)\n", name,
+                               ext_fw->firmware->data,
+                               ext_fw->firmware->size);
+       }
+}
+
+static void abox_request_extra_firmware(struct abox_data *data)
+{
+       struct platform_device *pdev = data->pdev;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *child_np;
+       struct abox_extra_firmware *ext_fw;
+       int ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       ext_fw = data->firmware_extra;
+       for_each_child_of_node(np, child_np) {
+               const char *status;
+
+               status = of_get_property(child_np, "status", NULL);
+               if (status && strcmp("okay", status) && strcmp("ok", status))
+                       continue;
+
+               ret = of_property_read_string(child_np, "samsung,name",
+                               &ext_fw->name);
+               if (ret < 0)
+                       continue;
+
+               ret = of_property_read_u32(child_np, "samsung,area",
+                               &ext_fw->area);
+               if (ret < 0)
+                       continue;
+
+               ret = of_property_read_u32(child_np, "samsung,offset",
+                               &ext_fw->offset);
+               if (ret < 0)
+                       continue;
+
+               dev_dbg(dev, "%s: name=%s, area=%u, offset=%u\n", __func__,
+                               ext_fw->name, ext_fw->area, ext_fw->offset);
+
+               if (!ext_fw->firmware) {
+                       dev_dbg(dev, "%s: request %s\n", __func__,
+                                       ext_fw->name);
+                       ret = request_firmware(&ext_fw->firmware,
+                                       ext_fw->name, dev);
+                       if (ret < 0)
+                               dev_err(dev, "%s: %s request failed\n",
+                                               __func__, ext_fw->name);
+               }
+               ext_fw++;
+       }
+
+}
+
+static void abox_download_extra_firmware(struct abox_data *data)
+{
+       struct device *dev = &data->pdev->dev;
+       struct abox_extra_firmware *ext_fw;
+       void __iomem *base;
+       size_t size;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (ext_fw = data->firmware_extra; ext_fw - data->firmware_extra <
+                       ARRAY_SIZE(data->firmware_extra); ext_fw++) {
+               if (!ext_fw->firmware)
+                       continue;
+
+               switch (ext_fw->area) {
+               case 0:
+                       base = data->sram_base;
+                       size = data->sram_size;
+                       break;
+               case 1:
+                       base = data->dram_base;
+                       size = DRAM_FIRMWARE_SIZE;
+                       break;
+               case 2:
+                       base = phys_to_virt(shm_get_vss_base());
+                       size = shm_get_vss_size();
+                       break;
+               default:
+                       dev_err(dev, "%s: area is invalid name=%s, area=%u, offset=%u\n",
+                                       __func__, ext_fw->name, ext_fw->area,
+                                       ext_fw->offset);
+                       continue;
+               }
+
+               if (ext_fw->offset + ext_fw->firmware->size > size) {
+                       dev_err(dev, "%s: firmware is too large name=%s, area=%u, offset=%u\n",
+                                       __func__, ext_fw->name, ext_fw->area,
+                                       ext_fw->offset);
+                       continue;
+               }
+
+               memcpy(base + ext_fw->offset, ext_fw->firmware->data,
+                               ext_fw->firmware->size);
+               dev_info(dev, "%s is downloaded at area %u offset %u\n",
+                               ext_fw->name, ext_fw->area, ext_fw->offset);
+       }
+}
+
+static int abox_request_firmware(struct device *dev,
+               const struct firmware **fw, const char *name)
+{
+       int ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       release_firmware(*fw);
+       ret = request_firmware(fw, name, dev);
+       if (ret < 0) {
+               dev_err(dev, "%s: %s request failed\n", __func__, name);
+       } else {
+               dev_info(dev, "%s is loaded at %p (%zu)\n", name,
+                               (*fw)->data, (*fw)->size);
+       }
+
+       return ret;
+}
+
+static void abox_complete_sram_firmware_request(const struct firmware *fw,
+               void *context)
+{
+       struct platform_device *pdev = context;
+       struct device *dev = &pdev->dev;
+       struct abox_data *data = platform_get_drvdata(pdev);
+
+       if (!fw) {
+               dev_err(dev, "Failed to request firmware\n");
+               return;
+       }
+
+       if (data->firmware_sram)
+               release_firmware(data->firmware_sram);
+
+       data->firmware_sram = fw;
+
+       dev_info(dev, "SRAM firmware loaded at %p (%zu)\n", fw->data, fw->size);
+
+       abox_request_firmware(dev, &data->firmware_dram, "calliope_dram.bin");
+       abox_request_firmware(dev, &data->firmware_iva, "calliope_iva.bin");
+       abox_request_extra_firmware(data);
+
+       if (abox_test_quirk(data, ABOX_QUIRK_OFF_ON_SUSPEND))
+               if (pm_runtime_active(dev))
+                       abox_enable(dev);
+}
+
+static int abox_download_firmware(struct platform_device *pdev)
+{
+       struct abox_data *data = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+
+       dev_info(dev, "%s\n", __func__);
+
+       if (unlikely(!data->firmware_sram)) {
+               request_firmware_nowait(THIS_MODULE,
+                       FW_ACTION_HOTPLUG,
+                       "calliope_sram.bin",
+                       dev,
+                       GFP_KERNEL,
+                       pdev,
+                       abox_complete_sram_firmware_request);
+               dev_warn(dev, "SRAM firmware downloading is deferred\n");
+               return -EAGAIN;
+       }
+       memcpy_toio(data->sram_base, data->firmware_sram->data,
+                       data->firmware_sram->size);
+       memset_io(data->sram_base + data->firmware_sram->size, 0,
+                       data->sram_size - data->firmware_sram->size);
+
+       if (unlikely(!data->firmware_dram)) {
+               dev_warn(dev, "DRAM firmware downloading is defferred\n");
+               return -EAGAIN;
+       }
+       memcpy(data->dram_base, data->firmware_dram->data,
+                       data->firmware_dram->size);
+       memset(data->dram_base + data->firmware_dram->size, 0,
+                       DRAM_FIRMWARE_SIZE - data->firmware_dram->size);
+
+       if (unlikely(!data->firmware_iva)) {
+               dev_warn(dev, "IVA firmware is not loaded\n");
+       } else {
+               memcpy(data->iva_base, data->firmware_iva->data,
+                               data->firmware_iva->size);
+               memset(data->iva_base + data->firmware_iva->size, 0,
+                               IVA_FIRMWARE_SIZE - data->firmware_iva->size);
+       }
+
+       abox_download_extra_firmware(data);
+
+       return 0;
+}
+
+static void abox_cfg_gpio(struct device *dev, const char *name)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       struct pinctrl_state *pin_state;
+       int ret;
+
+       dev_info(dev, "%s(%s)\n", __func__, name);
+
+       if (!data->pinctrl)
+               return;
+
+       pin_state = pinctrl_lookup_state(data->pinctrl, name);
+       if (IS_ERR(pin_state)) {
+               dev_err(dev, "Couldn't find pinctrl %s\n", name);
+       } else {
+               ret = pinctrl_select_state(data->pinctrl, pin_state);
+               if (ret < 0)
+                       dev_err(dev, "Unable to configure pinctrl %s\n", name);
+       }
+}
+
+#undef MANUAL_SECURITY_CHANGE
+#ifdef MANUAL_SECURITY_CHANGE
+static void work_temp_function(struct work_struct *work)
+{
+       exynos_smc(0x82000701, 0, 0, 0);
+       pr_err("%s: ABOX_CA7 security changed!!!\n", __func__);
+}
+static DECLARE_DELAYED_WORK(work_temp, work_temp_function);
+#endif
+
+#undef IVA_SRAM_SHARING
+#ifdef IVA_SRAM_SHARING
+#include <misc/exynos_ima.h>
+
+int abox_ima_claim(struct device *dev, struct abox_data *data,
+               phys_addr_t *addr)
+{
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+       phys_addr_t paddr;
+       int ret;
+
+       dev_info(dev, "%s\n", __func__);
+
+       mutex_lock(&data->ima_lock);
+
+       if (data->ima_claimed) {
+               mutex_unlock(&data->ima_lock);
+               return 0;
+       }
+
+       data->ima_vaddr = ima_alloc(data->ima_client, IVA_FIRMWARE_SIZE, 0);
+       if (IS_ERR_OR_NULL(data->ima_vaddr)) {
+               dev_err(dev, "%s: ima_alloc failed: %ld\n", __func__,
+                               PTR_ERR(data->ima_vaddr));
+               ret = data->ima_vaddr ? PTR_ERR(data->ima_vaddr) : -ENOMEM;
+               goto error;
+       }
+       paddr = ima_get_dma_addr(data->ima_client, data->ima_vaddr);
+       if (addr)
+               *addr = paddr;
+
+       ret = iommu_map(data->iommu_domain,
+                       IOVA_IVA(ABOX_IVA_MEMORY_PREPARE), paddr,
+                       IVA_FIRMWARE_SIZE, 0);
+       if (ret < 0) {
+               dev_err(dev, "%s: iommu mapping failed(%d)\n", __func__,
+                               ret);
+               goto error;
+       }
+
+       msg.ipcid = IPC_SYSTEM;
+       system_msg->msgtype = ABOX_START_CLAIM_SRAM;
+       system_msg->param1 = ABOX_IVA_MEMORY_PREPARE;
+       system_msg->param2 = IOVA_IVA(ABOX_IVA_MEMORY_PREPARE);
+       system_msg->param3 = IVA_FIRMWARE_SIZE;
+       ret = abox_request_ipc(&data->pdev->dev, msg.ipcid, &msg,
+                       sizeof(msg), 0, 0);
+       if (ret < 0)
+               goto error;
+
+       ret = wait_event_timeout(data->ipc_wait_queue,
+                       data->ima_claimed, msecs_to_jiffies(1000));
+       if (data->ima_claimed) {
+               ret = 0;
+       } else {
+               dev_err(dev, "IVA memory claim failed\n");
+               ret = -ETIME;
+               goto error;
+       }
+
+       iommu_unmap(data->iommu_domain, IOVA_IVA(ABOX_IVA_MEMORY),
+                       IVA_FIRMWARE_SIZE);
+       iommu_unmap(data->iommu_domain, IOVA_IVA(ABOX_IVA_MEMORY_PREPARE),
+                       IVA_FIRMWARE_SIZE);
+       exynos_sysmmu_tlb_invalidate(data->iommu_domain,
+                       IOVA_IVA(ABOX_IVA_MEMORY), IVA_FIRMWARE_SIZE);
+       exynos_sysmmu_tlb_invalidate(data->iommu_domain,
+                       IOVA_IVA(ABOX_IVA_MEMORY_PREPARE), IVA_FIRMWARE_SIZE);
+
+       ret = iommu_map(data->iommu_domain, IOVA_IVA(ABOX_IVA_MEMORY),
+                       paddr, IVA_FIRMWARE_SIZE, 0);
+       if (ret < 0) {
+               dev_err(dev, "%s: iommu mapping failed(%d)\n", __func__,
+                               ret);
+               goto error;
+       }
+       ret = iommu_map(data->iommu_domain,
+                       IOVA_IVA(ABOX_IVA_MEMORY_PREPARE),
+                       data->iva_base_phys, IVA_FIRMWARE_SIZE, 0);
+       if (ret < 0) {
+               dev_err(dev, "%s: iommu mapping failed(%d)\n", __func__,
+                               ret);
+               goto error;
+       }
+
+       system_msg->msgtype = ABOX_REPORT_SRAM;
+       system_msg->param1 = ABOX_IVA_MEMORY;
+       system_msg->param2 = IOVA_IVA(ABOX_IVA_MEMORY);
+       system_msg->param3 = IVA_FIRMWARE_SIZE;
+       ret = abox_request_ipc(&data->pdev->dev, msg.ipcid, &msg,
+                       sizeof(msg), 0, 1);
+       if (ret < 0)
+               goto error;
+
+       mutex_unlock(&data->ima_lock);
+       return ret;
+
+error:
+       iommu_unmap(data->iommu_domain, IOVA_IVA(ABOX_IVA_MEMORY),
+                       IVA_FIRMWARE_SIZE);
+       iommu_unmap(data->iommu_domain, IOVA_IVA(ABOX_IVA_MEMORY_PREPARE),
+                       IVA_FIRMWARE_SIZE);
+       exynos_sysmmu_tlb_invalidate(data->iommu_domain,
+                       IOVA_IVA(ABOX_IVA_MEMORY), IVA_FIRMWARE_SIZE);
+       exynos_sysmmu_tlb_invalidate(data->iommu_domain,
+                       IOVA_IVA(ABOX_IVA_MEMORY_PREPARE), IVA_FIRMWARE_SIZE);
+       iommu_map(data->iommu_domain, IOVA_IVA(ABOX_IVA_MEMORY),
+                       data->iva_base_phys, IVA_FIRMWARE_SIZE, 0);
+       ima_free(data->ima_client, data->ima_vaddr);
+       mutex_unlock(&data->ima_lock);
+       return ret;
+}
+
+static int abox_ima_reclaim(struct ima_client *client, struct device *dev,
+               void *priv)
+{
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+       struct abox_data *data = priv;
+       long ret;
+
+       dev_info(dev, "%s\n", __func__);
+
+       mutex_lock(&data->ima_lock);
+
+       if (!data->ima_claimed) {
+               ret = 0;
+               goto error;
+       }
+
+       msg.ipcid = IPC_SYSTEM;
+       system_msg->msgtype = ABOX_START_RECLAIM_SRAM;
+       system_msg->param1 = ABOX_IVA_MEMORY;
+       system_msg->param2 = IOVA_IVA(ABOX_IVA_MEMORY_PREPARE);
+       system_msg->param3 = IVA_FIRMWARE_SIZE;
+
+       abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
+
+       ret = wait_event_timeout(data->ipc_wait_queue,
+                       !data->ima_claimed, msecs_to_jiffies(1000));
+       if (!data->ima_claimed) {
+               ret = 0;
+       } else {
+               dev_err(dev, "IVA memory reclamation failed\n");
+               ret = -ETIME;
+       }
+
+       iommu_unmap(data->iommu_domain, IOVA_IVA(ABOX_IVA_MEMORY),
+                       IVA_FIRMWARE_SIZE);
+       iommu_unmap(data->iommu_domain, IOVA_IVA(ABOX_IVA_MEMORY_PREPARE),
+                       IVA_FIRMWARE_SIZE);
+       exynos_sysmmu_tlb_invalidate(data->iommu_domain,
+                       IOVA_IVA(ABOX_IVA_MEMORY), IVA_FIRMWARE_SIZE);
+       exynos_sysmmu_tlb_invalidate(data->iommu_domain,
+                       IOVA_IVA(ABOX_IVA_MEMORY_PREPARE), IVA_FIRMWARE_SIZE);
+
+       ret = iommu_map(data->iommu_domain, IOVA_IVA(ABOX_IVA_MEMORY),
+                       data->iva_base_phys, IVA_FIRMWARE_SIZE, 0);
+       if (ret < 0) {
+               dev_err(dev, "%s: iommu mapping failed(%ld)\n", __func__,
+                               ret);
+               goto error;
+       }
+
+       ima_free(data->ima_client, data->ima_vaddr);
+
+       system_msg->msgtype = ABOX_REPORT_DRAM;
+       system_msg->param1 = ABOX_IVA_MEMORY;
+       system_msg->param2 = IOVA_IVA(ABOX_IVA_MEMORY);
+       system_msg->param3 = IVA_FIRMWARE_SIZE;
+       ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 1);
+       if (ret < 0)
+               goto error;
+
+error:
+       mutex_unlock(&data->ima_lock);
+       return (int)ret;
+}
+
+static int abox_ima_init(struct device *dev, struct abox_data *data)
+{
+       dev_dbg(dev, "%s\n", __func__);
+
+       mutex_init(&data->ima_lock);
+       data->ima_client = ima_create_client(dev, abox_ima_reclaim, data);
+       if (IS_ERR(data->ima_client)) {
+               dev_err(dev, "ima_create_client failed: %ld\n",
+                               PTR_ERR(data->ima_client));
+               return PTR_ERR(data->ima_client);
+       }
+
+       return 0;
+}
+#else
+int abox_ima_claim(struct device *dev, struct abox_data *data,
+               phys_addr_t *addr)
+{
+       return 0;
+}
+
+static int abox_ima_reclaim(struct ima_client *client, struct device *dev,
+               void *priv)
+{
+       return 0;
+}
+
+static int abox_ima_init(struct device *dev, struct abox_data *data)
+{
+       return 0;
+}
+#endif
+static void __abox_control_l2c(struct abox_data *data, bool enable)
+{
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+       struct device *dev = &data->pdev->dev;
+
+       if (data->l2c_enabled == enable)
+               return;
+
+       dev_info(dev, "%s(%d)\n", __func__, enable);
+
+       data->l2c_controlled = false;
+
+       msg.ipcid = IPC_SYSTEM;
+       system_msg->msgtype = ABOX_START_L2C_CONTROL;
+       system_msg->param1 = enable ? 1 : 0;
+
+       if (enable) {
+               vts_acquire_sram(data->pdev_vts, 0);
+
+               abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 1, 0);
+               wait_event_timeout(data->ipc_wait_queue,
+                               data->l2c_controlled, LIMIT_IN_JIFFIES);
+               if (!data->l2c_controlled)
+                       dev_err(dev, "l2c enable failed\n");
+       } else {
+               abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 1, 0);
+               wait_event_timeout(data->ipc_wait_queue,
+                               data->l2c_controlled, LIMIT_IN_JIFFIES);
+               if (!data->l2c_controlled)
+                       dev_err(dev, "l2c disable failed\n");
+
+               vts_release_sram(data->pdev_vts, 0);
+       }
+
+       data->l2c_enabled = enable;
+}
+
+static void abox_l2c_work_func(struct work_struct *work)
+{
+       struct abox_data *data = container_of(work, struct abox_data, l2c_work);
+       struct platform_device *pdev = data->pdev;
+       struct device *dev = &pdev->dev;
+       size_t length = ARRAY_SIZE(data->l2c_requests);
+       struct abox_l2c_request *request;
+       bool enable = false;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       for (request = data->l2c_requests;
+                       request - data->l2c_requests < length
+                       && request->id;
+                       request++) {
+               if (request->on) {
+                       enable = true;
+                       break;
+               }
+       }
+
+       __abox_control_l2c(data, enable);
+}
+
+int abox_request_l2c(struct device *dev, struct abox_data *data,
+               void *id, bool on)
+{
+       struct abox_l2c_request *request;
+       size_t length = ARRAY_SIZE(data->l2c_requests);
+
+       if (!abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM))
+               return 0;
+
+       dev_info(dev, "%s(%p, %d)\n", __func__, id, on);
+
+       for (request = data->l2c_requests;
+                       request - data->l2c_requests < length
+                       && request->id && request->id != id;
+                       request++) {
+       }
+
+       request->on = on;
+       wmb(); /* on is read after id in reading function */
+       request->id = id;
+
+       if (request - data->l2c_requests >= ARRAY_SIZE(data->l2c_requests)) {
+               dev_err(dev, "%s: out of index. id=%p, on=%d\n",
+                               __func__, id, on);
+               return -ENOMEM;
+       }
+
+       schedule_work(&data->l2c_work);
+
+       return 0;
+}
+
+int abox_request_l2c_sync(struct device *dev, struct abox_data *data,
+               void *id, bool on)
+{
+       if (!abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM))
+               return 0;
+
+       abox_request_l2c(dev, data, id, on);
+       flush_work(&data->l2c_work);
+       return 0;
+}
+
+static void abox_clear_l2c_requests(struct device *dev, struct abox_data *data)
+{
+       struct abox_l2c_request *req;
+       size_t len = ARRAY_SIZE(data->l2c_requests);
+
+       if (!abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM))
+               return;
+
+       dev_info(dev, "%s\n", __func__);
+
+       for (req = data->l2c_requests; req - data->l2c_requests < len &&
+                       req->id; req++) {
+               req->on = false;
+       }
+
+       __abox_control_l2c(data, false);
+}
+
+static bool abox_is_timer_set(struct abox_data *data)
+{
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(data->regmap, ABOX_TIMER_CTRL1(0), &val);
+       if (ret < 0)
+               val = 0;
+
+       return !!val;
+}
+
+static void abox_start_timer(struct abox_data *data)
+{
+       struct regmap *regmap = data->regmap;
+
+       regmap_write(regmap, ABOX_TIMER_CTRL0(0), 1 << ABOX_TIMER_START_L);
+       regmap_write(regmap, ABOX_TIMER_CTRL0(1), 1 << ABOX_TIMER_START_L);
+       regmap_write(regmap, ABOX_TIMER_CTRL0(2), 1 << ABOX_TIMER_START_L);
+       regmap_write(regmap, ABOX_TIMER_CTRL0(3), 1 << ABOX_TIMER_START_L);
+}
+
+static int abox_enable(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct abox_data *data = dev_get_drvdata(dev);
+       unsigned int i, value;
+       bool has_reset;
+       int ret = 0;
+
+       dev_info(dev, "%s\n", __func__);
+
+       abox_gic_enable_irq(data->dev_gic);
+
+       abox_request_cpu_gear_sync(dev, data, (void *)DEFAULT_CPU_GEAR_ID,
+                       ABOX_CPU_GEAR_MAX);
+
+       if (is_secure_gic()) {
+               exynos_pmu_write(ABOX_MAGIC, 0);
+               ret = exynos_smc(0x82000501, 0, 0, 0);
+               dev_dbg(dev, "%s: smc ret=%d\n", __func__, ret);
+
+               for (i = 1000; i; i--) {
+                       exynos_pmu_read(ABOX_MAGIC, &value);
+                       if (value == ABOX_MAGIC_VALUE)
+                               break;
+               }
+               if (value != ABOX_MAGIC_VALUE)
+                       dev_warn(dev, "%s: abox magic timeout\n", __func__);
+               abox_cpu_enable(false);
+               abox_cpu_power(false);
+       }
+
+       if (abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM)) {
+               writel(0x1, data->sysreg_base + ABOX_SYSREG_MISC_CON);
+               writel(0x1, data->sysreg_base + ABOX_SYSREG_L2_CACHE_CON);
+       }
+
+       ret = clk_enable(data->clk_cpu);
+       if (ret < 0) {
+               dev_err(dev, "Failed to enable cpu clock: %d\n", ret);
+               goto error;
+       }
+
+       ret = clk_set_rate(data->clk_audif, AUDIF_RATE_HZ);
+       if (ret < 0) {
+               dev_err(dev, "Failed to set audif clock: %d\n", ret);
+               goto error;
+       }
+       dev_info(dev, "audif clock: %lu\n", clk_get_rate(data->clk_audif));
+
+       ret = clk_enable(data->clk_audif);
+       if (ret < 0) {
+               dev_err(dev, "Failed to enable audif clock: %d\n", ret);
+               goto error;
+       }
+
+       abox_cfg_gpio(dev, "default");
+
+       abox_restore_register(data);
+       has_reset = !abox_is_timer_set(data);
+       if (!has_reset) {
+               dev_info(dev, "wakeup from WFI\n");
+               abox_start_timer(data);
+       } else {
+               abox_gic_init_gic(data->dev_gic);
+
+               ret = abox_download_firmware(pdev);
+               if (ret < 0) {
+                       if (ret != -EAGAIN)
+                               dev_err(dev, "Failed to download firmware\n");
+                       else
+                               ret = 0;
+
+                       abox_request_cpu_gear(dev, data,
+                                       (void *)DEFAULT_CPU_GEAR_ID,
+                                       ABOX_CPU_GEAR_MIN);
+                       goto error;
+               }
+       }
+
+       abox_request_dram_on(pdev, dev, true);
+       if (has_reset) {
+               abox_cpu_power(true);
+               abox_cpu_enable(true);
+       }
+       data->calliope_state = CALLIOPE_ENABLING;
+
+       abox_pad_retention(false);
+#ifdef MANUAL_SECURITY_CHANGE
+       schedule_delayed_work(&work_temp, msecs_to_jiffies(3000));
+#endif
+
+       data->enabled = true;
+
+       if (has_reset)
+               pm_wakeup_event(dev, BOOT_DONE_TIMEOUT_MS);
+       else
+               abox_boot_done(dev, data->calliope_version);
+
+error:
+       return ret;
+}
+
+static int abox_disable(struct device *dev)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       enum calliope_state state = data->calliope_state;
+
+       dev_info(dev, "%s\n", __func__);
+
+       /* AUD_PLL must be normal during suspend */
+       clk_set_rate(data->clk_pll, AUD_PLL_RATE_HZ_FOR_48000);
+
+       data->calliope_state = CALLIOPE_DISABLING;
+       abox_cache_components(dev, data);
+       abox_ima_reclaim(data->ima_client, dev, data);
+       abox_clear_l2c_requests(dev, data);
+       flush_work(&data->boot_done_work);
+       flush_work(&data->l2c_work);
+       if (state != CALLIOPE_DISABLED)
+               abox_cpu_pm_ipc(dev, false);
+       data->calliope_state = CALLIOPE_DISABLED;
+       abox_log_drain_all(dev);
+
+       abox_save_register(data);
+       abox_cfg_gpio(dev, "idle");
+       abox_pad_retention(true);
+       data->enabled = false;
+       clk_disable(data->clk_cpu);
+       abox_gic_disable_irq(data->dev_gic);
+       abox_failsafe_report_reset(dev);
+
+       return 0;
+}
+
+void abox_poweroff(void)
+{
+       struct platform_device *pdev = p_abox_data->pdev;
+       struct device *dev = &pdev->dev;
+       struct abox_data *data = dev_get_drvdata(dev);
+
+       if (data->calliope_state == CALLIOPE_DISABLED) {
+               dev_dbg(dev, "already disabled\n");
+               return;
+       }
+       dev_info(dev, "%s\n", __func__);
+
+       abox_disable(dev);
+
+       //exynos_sysmmu_control(dev, false);
+}
+
+static int abox_runtime_suspend(struct device *dev)
+{
+       dev_dbg(dev, "%s\n", __func__);
+
+       //p_abox_data->enabled = false;
+       abox_disable(dev);
+
+       return 0;
+}
+
+static int abox_runtime_resume(struct device *dev)
+{
+       dev_dbg(dev, "%s\n", __func__);
+
+       //exynos_sysmmu_control(dev, true);
+
+       return abox_enable(dev);
+}
+
+static int abox_suspend(struct device *dev)
+{
+       dev_dbg(dev, "%s\n", __func__);
+       /* nothing to do */
+       return 0;
+}
+
+static int abox_resume(struct device *dev)
+{
+       dev_dbg(dev, "%s\n", __func__);
+       /* nothing to do */
+       return 0;
+}
+
+static int abox_qos_notifier(struct notifier_block *nb,
+               unsigned long action, void *nb_data)
+{
+       struct abox_data *data = container_of(nb, struct abox_data, qos_nb);
+       struct device *dev = &data->pdev->dev;
+       long value = (long)action;
+       long qos_class = (long)nb_data;
+       unsigned long aclk = clk_get_rate(data->clk_bus);
+       unsigned int sifs_cnt0, sifs_cnt1, cnt_val, rate, pwidth, channels;
+       unsigned long sifs0_cnt, sifs1_cnt, sifs2_cnt;
+       int ret;
+
+       dev_dbg(dev, "%s(%ldkHz, %ld)\n", __func__, value, qos_class);
+
+       ret = regmap_read(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT0, &sifs_cnt0);
+       if (ret < 0) {
+               dev_err(dev, "%s: SPUS_CTRL_SIFS_CNT0 read fail: %d\n",
+                               __func__, ret);
+               goto out;
+       }
+       ret = regmap_read(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT1, &sifs_cnt1);
+       if (ret < 0) {
+               dev_err(dev, "%s: SPUS_CTRL_SIFS_CNT1 read fail: %d\n",
+                               __func__, ret);
+               goto out;
+       }
+
+       sifs0_cnt = (sifs_cnt0 & ABOX_SIFS0_CNT_VAL_MASK) >>
+                       ABOX_SIFS0_CNT_VAL_L;
+       sifs1_cnt = (sifs_cnt0 & ABOX_SIFS1_CNT_VAL_MASK) >>
+                       ABOX_SIFS1_CNT_VAL_L;
+       sifs2_cnt = (sifs_cnt1 & ABOX_SIFS2_CNT_VAL_MASK) >>
+                       ABOX_SIFS2_CNT_VAL_L;
+
+       if (sifs0_cnt) {
+               rate = abox_get_sif_rate(data, SET_MIXER_SAMPLE_RATE);
+               pwidth = abox_get_sif_physical_width(data, SET_MIXER_FORMAT);
+               channels = abox_get_sif_channels(data, SET_MIXER_FORMAT);
+               cnt_val = abox_sifsx_cnt_val(aclk, rate, pwidth, channels);
+               dev_info(dev, "%s: %s <= %u\n", __func__, "SIFS0_CNT_VAL",
+                               cnt_val);
+               ret = regmap_update_bits(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
+                       ABOX_SIFS0_CNT_VAL_MASK,
+                       (unsigned int)cnt_val << ABOX_SIFS0_CNT_VAL_L);
+               if (ret < 0)
+                       dev_err(dev, "regmap update failed: %d\n", ret);
+       }
+       if (sifs1_cnt) {
+               rate = abox_get_sif_rate(data, SET_OUT1_SAMPLE_RATE);
+               pwidth = abox_get_sif_physical_width(data, SET_OUT1_FORMAT);
+               channels = abox_get_sif_channels(data, SET_OUT1_FORMAT);
+               cnt_val = abox_sifsx_cnt_val(aclk, rate, pwidth, channels);
+               dev_info(dev, "%s: %s <= %u\n", __func__, "SIFS0_CNT_VAL",
+                               cnt_val);
+               ret = regmap_update_bits(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT0,
+                       ABOX_SIFS1_CNT_VAL_MASK,
+                       (unsigned int)cnt_val << ABOX_SIFS1_CNT_VAL_L);
+               if (ret < 0)
+                       dev_err(dev, "regmap update failed: %d\n", ret);
+       }
+       if (sifs2_cnt) {
+               rate = abox_get_sif_rate(data, SET_OUT2_SAMPLE_RATE);
+               pwidth = abox_get_sif_physical_width(data, SET_OUT2_FORMAT);
+               channels = abox_get_sif_channels(data, SET_OUT2_FORMAT);
+               cnt_val = abox_sifsx_cnt_val(aclk, rate, pwidth, channels);
+               dev_info(dev, "%s: %s <= %u\n", __func__, "SIFS0_CNT_VAL",
+                               cnt_val);
+               ret = regmap_update_bits(data->regmap, ABOX_SPUS_CTRL_SIFS_CNT1,
+                       ABOX_SIFS2_CNT_VAL_MASK,
+                       (unsigned int)cnt_val << ABOX_SIFS2_CNT_VAL_L);
+               if (ret < 0)
+                       dev_err(dev, "regmap update failed: %d\n", ret);
+       }
+out:
+       return NOTIFY_DONE;
+}
+
+static int abox_pm_notifier(struct notifier_block *nb,
+               unsigned long action, void *nb_data)
+{
+       struct abox_data *data = container_of(nb, struct abox_data, pm_nb);
+       struct device *dev = &data->pdev->dev;
+       int ret;
+
+       dev_dbg(dev, "%s(%lu)\n", __func__, action);
+
+       switch (action) {
+       case PM_SUSPEND_PREPARE:
+               if (data->audio_mode != MODE_IN_CALL) {
+                       enum calliope_state state;
+
+                       pm_runtime_barrier(dev);
+                       state = data->calliope_state;
+                       if (state == CALLIOPE_ENABLING) {
+                               dev_info(dev, "calliope state: %d\n", state);
+                               return NOTIFY_BAD;
+                       }
+                       /* clear cpu gears to abox power off */
+                       abox_clear_cpu_gear_requests(dev, data);
+                       abox_cpu_gear_barrier(data);
+                       flush_workqueue(data->ipc_workqueue);
+                       if (abox_test_quirk(data, ABOX_QUIRK_OFF_ON_SUSPEND)) {
+                               ret = pm_runtime_put_sync_suspend(dev);
+                               if (ret < 0) {
+                                       pm_runtime_get(dev);
+                                       dev_info(dev, "runtime suspend: %d\n",
+                                                       ret);
+                                       return NOTIFY_BAD;
+                               }
+                       } else {
+                               ret = pm_runtime_suspend(dev);
+                               if (ret < 0) {
+                                       dev_info(dev, "runtime suspend: %d\n",
+                                                       ret);
+                                       return NOTIFY_BAD;
+                               }
+                       }
+               }
+               break;
+       case PM_POST_SUSPEND:
+               if (abox_test_quirk(data, ABOX_QUIRK_OFF_ON_SUSPEND))
+                       pm_runtime_get_sync(&data->pdev->dev);
+               break;
+       default:
+               /* Nothing to do */
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+static int abox_modem_notifier(struct notifier_block *nb,
+               unsigned long action, void *nb_data)
+{
+       struct abox_data *data = container_of(nb, struct abox_data, modem_nb);
+       struct device *dev = &data->pdev->dev;
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+
+       dev_info(&data->pdev->dev, "%s(%lu)\n", __func__, action);
+
+       switch (action) {
+       case MODEM_EVENT_ONLINE:
+               msg.ipcid = IPC_SYSTEM;
+               system_msg->msgtype = ABOX_START_VSS;
+               abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 1, 0);
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+#ifdef CONFIG_EXYNOS_ITMON
+static int abox_itmon_notifier(struct notifier_block *nb,
+               unsigned long action, void *nb_data)
+{
+       struct abox_data *data = container_of(nb, struct abox_data, itmon_nb);
+       struct device *dev = &data->pdev->dev;
+       struct itmon_notifier *itmon_data = nb_data;
+
+       if (itmon_data && itmon_data->dest && (strncmp("ABOX", itmon_data->dest,
+                       sizeof("ABOX") - 1) == 0)) {
+               dev_info(dev, "%s(%lu)\n", __func__, action);
+               data->enabled = false;
+               return NOTIFY_OK;
+       }
+
+       return NOTIFY_DONE;
+}
+#endif
+
+static ssize_t calliope_version_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+       unsigned int version = be32_to_cpu(data->calliope_version);
+
+       memcpy(buf, &version, sizeof(version));
+       buf[4] = '\n';
+       buf[5] = '\0';
+
+       return 6;
+}
+
+static ssize_t calliope_debug_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       ABOX_IPC_MSG msg = {0,};
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+       int ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       msg.ipcid = IPC_SYSTEM;
+       system_msg->msgtype = ABOX_REQUEST_DEBUG;
+       ret = sscanf(buf, "%10d,%10d,%10d,%739s", &system_msg->param1,
+                       &system_msg->param2, &system_msg->param3,
+                       system_msg->bundle.param_bundle);
+       if (ret < 0)
+               return ret;
+
+       ret = abox_request_ipc(dev, msg.ipcid, &msg, sizeof(msg), 0, 0);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t calliope_cmd_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       static const char cmd_reload_ext_bin[] = "RELOAD EXT BIN";
+       static const char cmd_failsafe[] = "FAILSAFE";
+       static const char cmd_cpu_gear[] = "CPU GEAR";
+       struct abox_data *data = dev_get_drvdata(dev);
+       char name[80];
+
+       dev_dbg(dev, "%s(%s)\n", __func__, buf);
+       if (!strncmp(cmd_reload_ext_bin, buf, sizeof(cmd_reload_ext_bin) - 1)) {
+               dev_dbg(dev, "reload ext bin\n");
+               if (sscanf(buf, "RELOAD EXT BIN:%63s", name) == 1)
+                       abox_reload_extra_firmware(data, name);
+       } else if (!strncmp(cmd_failsafe, buf, sizeof(cmd_failsafe) - 1)) {
+               dev_dbg(dev, "failsafe\n");
+               abox_failsafe_report(dev);
+       } else if (!strncmp(cmd_cpu_gear, buf, sizeof(cmd_cpu_gear) - 1)) {
+               unsigned int gear;
+               int ret;
+
+               dev_info(dev, "set clk\n");
+               ret = kstrtouint(buf + sizeof(cmd_cpu_gear), 10, &gear);
+               if (!ret) {
+                       dev_info(dev, "gear = %u\n", gear);
+                       pm_runtime_get_sync(dev);
+                       abox_request_cpu_gear(dev, data,
+                                       (void *)TEST_CPU_GEAR_ID, gear);
+                       dev_info(dev, "bus clk = %lu\n", clk_get_rate(data->clk_bus));
+                       pm_runtime_mark_last_busy(dev);
+                       pm_runtime_put_autosuspend(dev);
+               }
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR_RO(calliope_version);
+static DEVICE_ATTR_WO(calliope_debug);
+static DEVICE_ATTR_WO(calliope_cmd);
+
+static int samsung_abox_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *np_tmp;
+       struct platform_device *pdev_tmp;
+       struct abox_data *data;
+       phys_addr_t paddr;
+       int ret, i;
+
+       dev_info(dev, "%s\n", __func__);
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, data);
+       data->pdev = pdev;
+       p_abox_data = data;
+
+       abox_probe_quirks(data, np);
+       init_waitqueue_head(&data->ipc_wait_queue);
+       spin_lock_init(&data->ipc_queue_lock);
+       device_init_wakeup(dev, true);
+       data->cpu_gear = ABOX_CPU_GEAR_MIN;
+       data->cpu_gear_min = 3; /* default value from kangchen */
+       for (i = 0; i < ARRAY_SIZE(data->sif_rate); i++) {
+               data->sif_rate_min[i] = data->sif_rate[i] = 48000;
+               data->sif_format_min[i] = data->sif_format[i] =
+                               SNDRV_PCM_FORMAT_S16;
+               data->sif_channels_min[i] = data->sif_channels[i] = 2;
+       }
+       INIT_WORK(&data->ipc_work, abox_process_ipc);
+       INIT_WORK(&data->change_cpu_gear_work, abox_change_cpu_gear_work_func);
+       INIT_WORK(&data->change_int_freq_work, abox_change_int_freq_work_func);
+       INIT_WORK(&data->change_mif_freq_work, abox_change_mif_freq_work_func);
+       INIT_WORK(&data->change_lit_freq_work, abox_change_lit_freq_work_func);
+       INIT_WORK(&data->change_big_freq_work, abox_change_big_freq_work_func);
+       INIT_WORK(&data->change_hmp_boost_work,
+                       abox_change_hmp_boost_work_func);
+       INIT_WORK(&data->register_component_work,
+                       abox_register_component_work_func);
+       INIT_WORK(&data->boot_done_work, abox_boot_done_work_func);
+       INIT_WORK(&data->l2c_work, abox_l2c_work_func);
+       INIT_DELAYED_WORK(&data->tickle_work, abox_tickle_work_func);
+       INIT_LIST_HEAD(&data->irq_actions);
+
+       data->gear_workqueue = alloc_ordered_workqueue("abox_gear",
+                       WQ_FREEZABLE | WQ_MEM_RECLAIM);
+       if (!data->gear_workqueue) {
+               dev_err(dev, "Couldn't create workqueue %s\n", "abox_gear");
+               return -ENOMEM;
+       }
+
+       data->ipc_workqueue = alloc_ordered_workqueue("abox_ipc",
+                       WQ_MEM_RECLAIM);
+       if (!data->ipc_workqueue) {
+               dev_err(dev, "Couldn't create workqueue %s\n", "abox_ipc");
+               return -ENOMEM;
+       }
+
+       data->pinctrl = devm_pinctrl_get(dev);
+       if (IS_ERR(data->pinctrl)) {
+               dev_err(dev, "Couldn't get pins (%li)\n",
+                               PTR_ERR(data->pinctrl));
+               data->pinctrl = NULL;
+       }
+
+       data->sfr_base = devm_request_and_map_byname(pdev, "sfr",
+                       NULL, NULL);
+       if (IS_ERR(data->sfr_base))
+               return PTR_ERR(data->sfr_base);
+
+       data->sysreg_base = devm_request_and_map_byname(pdev, "sysreg",
+                       NULL, NULL);
+       if (IS_ERR(data->sysreg_base))
+               return PTR_ERR(data->sysreg_base);
+
+       data->sram_base = devm_request_and_map_byname(pdev, "sram",
+                       &data->sram_base_phys, &data->sram_size);
+       if (IS_ERR(data->sram_base))
+               return PTR_ERR(data->sram_base);
+
+       data->iommu_domain = get_domain_from_dev(dev);
+       if (IS_ERR(data->iommu_domain)) {
+               dev_err(dev, "Unable to get iommu domain\n");
+               return PTR_ERR(data->iommu_domain);
+       }
+
+       ret = iommu_attach_device(data->iommu_domain, dev);
+       if (ret < 0) {
+               dev_err(dev, "Unable to attach device to iommu (%d)\n", ret);
+               return ret;
+       }
+
+       data->dram_base = dmam_alloc_coherent(dev, DRAM_FIRMWARE_SIZE,
+                       &data->dram_base_phys, GFP_KERNEL);
+       if (IS_ERR_OR_NULL(data->dram_base)) {
+               dev_err(dev, "Failed to allocate coherent memory: %ld\n",
+                               PTR_ERR(data->dram_base));
+               return PTR_ERR(data->dram_base);
+       }
+       dev_info(&pdev->dev, "%s(%pa) is mapped on %p with size of %d\n",
+                       "dram firmware", &data->dram_base_phys, data->dram_base,
+                       DRAM_FIRMWARE_SIZE);
+       iommu_map(data->iommu_domain, IOVA_DRAM_FIRMWARE, data->dram_base_phys,
+                       DRAM_FIRMWARE_SIZE, 0);
+
+       data->iva_base = dmam_alloc_coherent(dev, IVA_FIRMWARE_SIZE,
+                       &data->iva_base_phys, GFP_KERNEL);
+       if (IS_ERR_OR_NULL(data->iva_base)) {
+               dev_err(dev, "Failed to allocate coherent memory: %ld\n",
+                               PTR_ERR(data->iva_base));
+               return PTR_ERR(data->iva_base);
+       }
+       dev_info(&pdev->dev, "%s(%pa) is mapped on %p with size of %d\n",
+                       "iva firmware", &data->iva_base_phys, data->iva_base,
+                       IVA_FIRMWARE_SIZE);
+       iommu_map(data->iommu_domain, IOVA_IVA_FIRMWARE, data->iva_base_phys,
+                       IVA_FIRMWARE_SIZE, 0);
+
+       paddr = shm_get_vss_base();
+       dev_info(&pdev->dev, "%s(%pa) is mapped on %p with size of %d\n",
+                       "vss firmware", &paddr, phys_to_virt(paddr),
+                       shm_get_vss_size());
+       iommu_map(data->iommu_domain, IOVA_VSS_FIRMWARE, paddr,
+                       shm_get_vss_size(), 0);
+
+       paddr = shm_get_vparam_base();
+       dev_info(&pdev->dev, "%s(%pa) is mapped on %p with size of %d\n",
+                       "vss parameter", &paddr, phys_to_virt(paddr),
+                       shm_get_vparam_size());
+       iommu_map(data->iommu_domain, IOVA_VSS_PARAMETER, paddr,
+                       shm_get_vparam_size(), 0);
+
+       iommu_map(data->iommu_domain, 0x10000000, 0x10000000, PAGE_SIZE, 0);
+       iovmm_set_fault_handler(&pdev->dev, abox_iommu_fault_handler, data);
+
+       data->clk_pll = devm_clk_get_and_prepare(pdev, "pll");
+       if (IS_ERR(data->clk_pll))
+               return PTR_ERR(data->clk_pll);
+
+       data->clk_audif = devm_clk_get_and_prepare(pdev, "audif");
+       if (IS_ERR(data->clk_audif))
+               return PTR_ERR(data->clk_audif);
+
+       data->clk_cpu = devm_clk_get_and_prepare(pdev, "cpu");
+       if (IS_ERR(data->clk_cpu))
+               return PTR_ERR(data->clk_cpu);
+
+       data->clk_bus = devm_clk_get_and_prepare(pdev, "bus");
+       if (IS_ERR(data->clk_bus))
+               return PTR_ERR(data->clk_bus);
+
+       ret = of_property_read_u32(np, "uaif_max_div", &data->uaif_max_div);
+       if (ret < 0) {
+               dev_warn(dev, "Failed to read %s: %d\n", "uaif_max_div", ret);
+               data->uaif_max_div = 32;
+       }
+
+       ret = of_property_read_u32(np, "ipc_tx_offset", &data->ipc_tx_offset);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read %s: %d\n", "ipc_tx_offset", ret);
+               return ret;
+       }
+
+       ret = of_property_read_u32(np, "ipc_rx_offset", &data->ipc_rx_offset);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read %s: %d\n", "ipc_rx_offset", ret);
+               return ret;
+       }
+
+       ret = of_property_read_u32(np, "ipc_tx_ack_offset",
+                       &data->ipc_tx_ack_offset);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read %s: %d\n", "ipc_tx_ack_offset",
+                               ret);
+               return ret;
+       }
+
+       ret = of_property_read_u32(np, "ipc_rx_ack_offset",
+                       &data->ipc_rx_ack_offset);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read %s: %d\n", "ipc_rx_ack_offset",
+                               ret);
+               return ret;
+       }
+
+       ret = of_property_read_u32_array(np, "pm_qos_int", data->pm_qos_int,
+                       ARRAY_SIZE(data->pm_qos_int));
+       if (ret < 0)
+               dev_warn(dev, "Failed to read %s: %d\n", "pm_qos_int", ret);
+
+       ret = of_property_read_u32_array(np, "pm_qos_aud", data->pm_qos_aud,
+                       ARRAY_SIZE(data->pm_qos_aud));
+       if (ret < 0) {
+               dev_warn(dev, "Failed to read %s: %d\n", "pm_qos_aud", ret);
+       } else {
+               for (i = 0; i < ARRAY_SIZE(data->pm_qos_aud); i++) {
+                       if (!data->pm_qos_aud[i]) {
+                               data->cpu_gear_min = i;
+                               break;
+                       }
+               }
+       }
+
+       np_tmp = of_parse_phandle(np, "abox_gic", 0);
+       if (!np_tmp) {
+               dev_err(dev, "Failed to get abox_gic device node\n");
+               return -EPROBE_DEFER;
+       }
+       pdev_tmp = of_find_device_by_node(np_tmp);
+       if (!pdev_tmp) {
+               dev_err(dev, "Failed to get abox_gic platform device\n");
+               return -EPROBE_DEFER;
+       }
+       data->dev_gic = &pdev_tmp->dev;
+
+       if (abox_test_quirk(data, ABOX_QUIRK_SHARE_VTS_SRAM)) {
+               np_tmp = of_parse_phandle(np, "vts", 0);
+               if (!np_tmp) {
+                       dev_err(dev, "Failed to get vts device node\n");
+                       return -EPROBE_DEFER;
+               }
+               data->pdev_vts = of_find_device_by_node(np_tmp);
+               if (!data->pdev_vts) {
+                       dev_err(dev, "Failed to get vts platform device\n");
+                       return -EPROBE_DEFER;
+               }
+       }
+
+#ifdef EMULATOR
+       pmu_alive = ioremap(0x16480000, 0x10000);
+#endif
+       pm_qos_add_request(&abox_pm_qos_aud, PM_QOS_AUD_THROUGHPUT, 0);
+       pm_qos_add_request(&abox_pm_qos_int, PM_QOS_DEVICE_THROUGHPUT, 0);
+       pm_qos_add_request(&abox_pm_qos_mif, PM_QOS_BUS_THROUGHPUT, 0);
+       pm_qos_add_request(&abox_pm_qos_lit, PM_QOS_CLUSTER0_FREQ_MIN, 0);
+       pm_qos_add_request(&abox_pm_qos_big, PM_QOS_CLUSTER1_FREQ_MIN, 0);
+
+       for (i = 0; i < ABOX_GIC_IRQ_COUNT; i++)
+               abox_gic_register_irq_handler(data->dev_gic, i,
+                               abox_irq_handler, pdev);
+
+       if (IS_ENABLED(CONFIG_SOC_EXYNOS8895)) {
+               abox_regmap_config.reg_defaults = abox_reg_defaults_8895;
+               abox_regmap_config.num_reg_defaults =
+                               ARRAY_SIZE(abox_reg_defaults_8895);
+       } else if (IS_ENABLED(CONFIG_SOC_EXYNOS9810)) {
+               abox_regmap_config.reg_defaults = abox_reg_defaults_9810;
+               abox_regmap_config.num_reg_defaults =
+                               ARRAY_SIZE(abox_reg_defaults_9810);
+       } else if (IS_ENABLED(CONFIG_SOC_EXYNOS9610)) {
+               abox_regmap_config.reg_defaults = abox_reg_defaults_9610;
+               abox_regmap_config.num_reg_defaults =
+                               ARRAY_SIZE(abox_reg_defaults_9610);
+       }
+       data->regmap = devm_regmap_init_mmio(dev,
+                       data->sfr_base,
+                       &abox_regmap_config);
+
+       pm_runtime_enable(dev);
+       pm_runtime_set_autosuspend_delay(dev, 1);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_get(dev);
+
+       data->qos_nb.notifier_call = abox_qos_notifier;
+       pm_qos_add_notifier(PM_QOS_AUD_THROUGHPUT, &data->qos_nb);
+
+       data->pm_nb.notifier_call = abox_pm_notifier;
+       register_pm_notifier(&data->pm_nb);
+
+       data->modem_nb.notifier_call = abox_modem_notifier;
+       register_modem_event_notifier(&data->modem_nb);
+
+#ifdef CONFIG_EXYNOS_ITMON
+       data->itmon_nb.notifier_call = abox_itmon_notifier;
+       itmon_notifier_chain_register(&data->itmon_nb);
+#endif
+
+       abox_ima_init(dev, data);
+       abox_failsafe_init(dev);
+
+       ret = device_create_file(dev, &dev_attr_calliope_version);
+       if (ret < 0)
+               dev_warn(dev, "Failed to create file: %s\n", "version");
+
+       ret = device_create_file(dev, &dev_attr_calliope_debug);
+       if (ret < 0)
+               dev_warn(dev, "Failed to create file: %s\n", "debug");
+
+       ret = device_create_file(dev, &dev_attr_calliope_cmd);
+       if (ret < 0)
+               dev_warn(dev, "Failed to create file: %s\n", "cmd");
+
+       atomic_notifier_chain_register(&panic_notifier_list,
+                       &abox_panic_notifier);
+
+       ret = snd_soc_register_component(dev, &abox_cmpnt, abox_dais,
+                       ARRAY_SIZE(abox_dais));
+       if (ret < 0)
+               dev_err(dev, "component register failed:%d\n", ret);
+
+       dev_info(dev, "%s: probe complete\n", __func__);
+
+       of_platform_populate(np, NULL, NULL, dev);
+
+       return 0;
+}
+
+static int samsung_abox_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct abox_data *data = platform_get_drvdata(pdev);
+
+       dev_info(dev, "%s\n", __func__);
+
+       pm_runtime_disable(dev);
+#ifndef CONFIG_PM
+       abox_runtime_suspend(dev);
+#endif
+       device_init_wakeup(dev, false);
+       destroy_workqueue(data->ipc_workqueue);
+       pm_qos_remove_request(&abox_pm_qos_aud);
+       pm_qos_remove_request(&abox_pm_qos_int);
+       pm_qos_remove_request(&abox_pm_qos_mif);
+       pm_qos_remove_request(&abox_pm_qos_lit);
+       pm_qos_remove_request(&abox_pm_qos_big);
+       snd_soc_unregister_component(dev);
+       iommu_unmap(data->iommu_domain, IOVA_DRAM_FIRMWARE, DRAM_FIRMWARE_SIZE);
+#ifdef EMULATOR
+       iounmap(pmu_alive);
+#endif
+       return 0;
+}
+
+static void samsung_abox_shutdown(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       dev_info(dev, "%s\n", __func__);
+       pm_runtime_disable(dev);
+}
+
+static const struct of_device_id samsung_abox_match[] = {
+       {
+               .compatible = "samsung,abox",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_abox_match);
+
+static const struct dev_pm_ops samsung_abox_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(abox_suspend, abox_resume)
+       SET_RUNTIME_PM_OPS(abox_runtime_suspend, abox_runtime_resume, NULL)
+};
+
+static struct platform_driver samsung_abox_driver = {
+       .probe  = samsung_abox_probe,
+       .remove = samsung_abox_remove,
+       .shutdown = samsung_abox_shutdown,
+       .driver = {
+               .name = "samsung-abox",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_abox_match),
+               .pm = &samsung_abox_pm,
+       },
+};
+
+module_platform_driver(samsung_abox_driver);
+
+static int __init samsung_abox_late_initcall(void)
+{
+       pr_info("%s\n", __func__);
+
+       if (p_abox_data && p_abox_data->pdev) {
+               if (!abox_test_quirk(p_abox_data, ABOX_QUIRK_OFF_ON_SUSPEND))
+                       pm_runtime_put(&p_abox_data->pdev->dev);
+       } else {
+               pr_err("%s: p_abox_data or pdev is null", __func__);
+       }
+
+       return 0;
+}
+late_initcall(samsung_abox_late_initcall);
+
+/* Module information */
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box Driver");
+MODULE_ALIAS("platform:samsung-abox");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox.h b/sound/soc/samsung/abox/abox.h
new file mode 100644 (file)
index 0000000..4b1eefc
--- /dev/null
@@ -0,0 +1,1048 @@
+/* sound/soc/samsung/abox/abox.h
+ *
+ * ALSA SoC - Samsung Abox driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_H
+#define __SND_SOC_ABOX_H
+
+#include <linux/pm_wakeup.h>
+#include <sound/samsung/abox.h>
+
+#define ABOX_MASK(name) (GENMASK(ABOX_##name##_H, ABOX_##name##_L))
+#define ABOX_MASK_ARG(name, x) (GENMASK(ABOX_##name##_H(x), ABOX_##name##_L(x)))
+#define ABOX_IDX_ARG(name, o, x) (ABOX_##name##_BASE + \
+               (x * ABOX_##name##_INTERVAL) + o)
+#define ABOX_L_ARG(name, o, x) ABOX_IDX_ARG(name, o, x)
+#define ABOX_H_ARG(name, o, x) ABOX_IDX_ARG(name, o, x)
+
+/* System */
+#define ABOX_IP_INDEX                  (0x0000)
+#define ABOX_VERSION                   (0x0004)
+#define ABOX_SYSPOWER_CTRL             (0x0010)
+#define ABOX_SYSPOWER_STATUS           (0x0014)
+#define ABOX_SYSTEM_CONFIG0            (0x0020)
+#define ABOX_REMAP_MASK                        (0x0024)
+#define ABOX_REMAP_ADDR                        (0x0028)
+#define ABOX_DYN_CLOCK_OFF             (0x0030)
+#define ABOX_QCHANNEL_DISABLE          (0x0038)
+#define ABOX_ROUTE_CTRL0               (0x0040)
+#define ABOX_ROUTE_CTRL1               (0x0044)
+#define ABOX_ROUTE_CTRL2               (0x0048)
+#define ABOX_TICK_DIV_RATIO            (0x0050)
+/* ABOX_SYSPOWER_CTRL */
+#define ABOX_SYSPOWER_CTRL_L           (0)
+#define ABOX_SYSPOWER_CTRL_H           (0)
+#define ABOX_SYSPOWER_CTRL_MASK                ABOX_MASK(SYSPOWER_CTRL)
+/* ABOX_SYSPOWER_STATUS */
+#define ABOX_SYSPOWER_STATUS_L         (0)
+#define ABOX_SYSPOWER_STATUS_H         (0)
+#define ABOX_SYSPOWER_STATUS_MASK      ABOX_MASK(SYSPOWER_STATUS)
+/* ABOX_DYN_CLOCK_OFF */
+#define ABOX_DYN_CLOCK_OFF_L           (0)
+#define ABOX_DYN_CLOCK_OFF_H           (30)
+#define ABOX_DYN_CLOCK_OFF_MASK                ABOX_MASK(DYN_CLOCK_OFF)
+/* ABOX_QCHANNEL_DISABLE */
+#define ABOX_QCHANNEL_DISABLE_BASE     (0)
+#define ABOX_QCHANNEL_DISABLE_INTERVAL (1)
+#define ABOX_QCHANNEL_DISABLE_L(x)     ABOX_L_ARG(QCHANNEL_DISABLE, 0, x)
+#define ABOX_QCHANNEL_DISABLE_H(x)     ABOX_H_ARG(QCHANNEL_DISABLE, 0, x)
+#define ABOX_QCHANNEL_DISABLE_MASK(x)  ABOX_MASK_ARG(QCHANNEL_DISABLE, x)
+/* ABOX_ROUTE_CTRL0 */
+#define ABOX_ROUTE_DSIF_L              (20)
+#define ABOX_ROUTE_DSIF_H              (23)
+#define ABOX_ROUTE_DSIF_MASK           ABOX_MASK(ROUTE_DSIF)
+#define ABOX_ROUTE_UAIF_SPK_BASE       (0)
+#define ABOX_ROUTE_UAIF_SPK_INTERVAL   (4)
+#define ABOX_ROUTE_UAIF_SPK_L(x)       ABOX_L_ARG(ROUTE_UAIF_SPK, 0, x)
+#define ABOX_ROUTE_UAIF_SPK_H(x)       ABOX_H_ARG(ROUTE_UAIF_SPK, 3, x)
+#define ABOX_ROUTE_UAIF_SPK_MASK(x)    ABOX_MASK_ARG(ROUTE_UAIF_SPK, x)
+/* ABOX_ROUTE_CTRL1 */
+#define ABOX_ROUTE_SPUSM_L             (16)
+#define ABOX_ROUTE_SPUSM_H             (19)
+#define ABOX_ROUTE_SPUSM_MASK          ABOX_MASK(ROUTE_SPUSM)
+#define ABOX_ROUTE_NSRC_BASE           (0)
+#define ABOX_ROUTE_NSRC_INTERVAL       (4)
+#define ABOX_ROUTE_NSRC_L(x)           ABOX_L_ARG(ROUTE_NSRC, 0, x)
+#define ABOX_ROUTE_NSRC_H(x)           ABOX_H_ARG(ROUTE_NSRC, 3, x)
+#define ABOX_ROUTE_NSRC_MASK(x)                ABOX_MASK_ARG(ROUTE_NSRC, x)
+/* ABOX_ROUTE_CTRL2 */
+#define ABOX_ROUTE_RSRC_BASE           (0)
+#define ABOX_ROUTE_RSRC_INTERVAL       (4)
+#define ABOX_ROUTE_RSRC_L(x)           ABOX_L_ARG(ROUTE_RSRC, 0, x)
+#define ABOX_ROUTE_RSRC_H(x)           ABOX_H_ARG(ROUTE_RSRC, 3, x)
+#define ABOX_ROUTE_RSRC_MASK(x)                ABOX_MASK_ARG(ROUTE_RSRC, x)
+
+/* SPUS */
+#define ABOX_SPUS_CTRL0                        (0x0200)
+#define ABOX_SPUS_CTRL1                        (0x0204)
+#define ABOX_SPUS_CTRL2                        (0x0208)
+#define ABOX_SPUS_CTRL3                        (0x020C)
+#define ABOX_SPUS_CTRL_SIFS_CNT0       (0x0280)
+#define ABOX_SPUS_CTRL_SIFS_CNT1       (0x0284)
+/* ABOX_SPUS_CTRL0 */
+#define ABOX_FUNC_CHAIN_SRC_BASE       (0)
+#define ABOX_FUNC_CHAIN_SRC_INTERVAL   (4)
+#define ABOX_FUNC_CHAIN_SRC_IN_L(x)    ABOX_L_ARG(FUNC_CHAIN_SRC, 3, x)
+#define ABOX_FUNC_CHAIN_SRC_IN_H(x)    ABOX_H_ARG(FUNC_CHAIN_SRC, 3, x)
+#define ABOX_FUNC_CHAIN_SRC_IN_MASK(x) ABOX_MASK_ARG(FUNC_CHAIN_SRC_IN, x)
+#define ABOX_FUNC_CHAIN_SRC_OUT_L(x)   ABOX_L_ARG(FUNC_CHAIN_SRC, 1, x)
+#define ABOX_FUNC_CHAIN_SRC_OUT_H(x)   ABOX_H_ARG(FUNC_CHAIN_SRC, 2, x)
+#define ABOX_FUNC_CHAIN_SRC_OUT_MASK(x)        ABOX_MASK_ARG(FUNC_CHAIN_SRC_OUT, x)
+#define ABOX_FUNC_CHAIN_SRC_ASRC_L(x)  ABOX_L_ARG(FUNC_CHAIN_SRC, 0, x)
+#define ABOX_FUNC_CHAIN_SRC_ASRC_H(x)  ABOX_H_ARG(FUNC_CHAIN_SRC, 0, x)
+#define ABOX_FUNC_CHAIN_SRC_ASRC_MASK(x)       ABOX_MASK_ARG(\
+               FUNC_CHAIN_SRC_ASRC, x)
+/* ABOX_SPUS_CTRL1 */
+#define ABOX_SIFM_IN_SEL_L             (22)
+#define ABOX_SIFM_IN_SEL_H             (24)
+#define ABOX_SIFM_IN_SEL_MASK          (ABOX_MASK(SIFM_IN_SEL))
+#define ABOX_SIFS_OUT2_SEL_L           (19)
+#define ABOX_SIFS_OUT2_SEL_H           (21)
+#define ABOX_SIFS_OUT2_SEL_MASK                (ABOX_MASK(SIFS_OUT2_SEL))
+#define ABOX_SIFS_OUT1_SEL_L           (16)
+#define ABOX_SIFS_OUT1_SEL_H           (18)
+#define ABOX_SIFS_OUT1_SEL_MASK                (ABOX_MASK(SIFS_OUT1_SEL))
+#define ABOX_SPUS_MIXP_FORMAT_L                (0)
+#define ABOX_SPUS_MIXP_FORMAT_H                (4)
+#define ABOX_SPUS_MIXP_FORMAT_MASK     (ABOX_MASK(SPUS_MIXP_FORMAT))
+/* ABOX_SPUS_CTRL2 */
+#define ABOX_SPUS_MIXP_FLUSH_L         (0)
+#define ABOX_SPUS_MIXP_FLUSH_H         (0)
+#define ABOX_SPUS_MIXP_FLUSH_MASK      (ABOX_MASK(SPUS_MIXP_FLUSH))
+/* ABOX_SPUS_CTRL3 */
+#define ABOX_SPUS_SIFM_FLUSH_L         (2)
+#define ABOX_SPUS_SIFM_FLUSH_H         (2)
+#define ABOX_SPUS_SIFM_FLUSH_MASK      (ABOX_MASK(SPUS_SIFM_FLUSH))
+#define ABOX_SPUS_SIFS2_FLUSH_L                (1)
+#define ABOX_SPUS_SIFS2_FLUSH_H                (1)
+#define ABOX_SPUS_SIFS2_FLUSH_MASK     (ABOX_MASK(SPUS_SIFS2_FLUSH))
+#define ABOX_SPUS_SIFS1_FLUSH_L                (0)
+#define ABOX_SPUS_SIFS1_FLUSH_H                (0)
+#define ABOX_SPUS_SIFS1_FLUSH_MASK     (ABOX_MASK(SPUS_SIFS1_FLUSH))
+/* ABOX_SPUS_CTRL_SIFS_CNT0 */
+#define ABOX_SIFS1_CNT_VAL_L           (16)
+#define ABOX_SIFS1_CNT_VAL_H           (31)
+#define ABOX_SIFS1_CNT_VAL_MASK                (ABOX_MASK(SIFS1_CNT_VAL))
+#define ABOX_SIFS0_CNT_VAL_L           (0)
+#define ABOX_SIFS0_CNT_VAL_H           (15)
+#define ABOX_SIFS0_CNT_VAL_MASK                (ABOX_MASK(SIFS0_CNT_VAL))
+/* ABOX_SPUS_CTRL_SIFS_CNT1 */
+#define ABOX_SIFS2_CNT_VAL_L           (0)
+#define ABOX_SIFS2_CNT_VAL_H           (15)
+#define ABOX_SIFS2_CNT_VAL_MASK                (ABOX_MASK(SIFS2_CNT_VAL))
+
+/* SPUM */
+#define ABOX_SPUM_CTRL0                        (0x0300)
+#define ABOX_SPUM_CTRL1                        (0x0304)
+#define ABOX_SPUM_CTRL2                        (0x0308)
+#define ABOX_SPUM_CTRL3                        (0x030C)
+
+/* ABOX_SPUM_CTRL0 */
+#define ABOX_FUNC_CHAIN_NSRC_BASE      (4)
+#define ABOX_FUNC_CHAIN_NSRC_INTERVAL  (4)
+#define ABOX_FUNC_CHAIN_NSRC_OUT_L(x)  ABOX_L_ARG(FUNC_CHAIN_NSRC, 3, x)
+#define ABOX_FUNC_CHAIN_NSRC_OUT_H(x)  ABOX_H_ARG(FUNC_CHAIN_NSRC, 3, x)
+#define ABOX_FUNC_CHAIN_NSRC_OUT_MASK(x)       ABOX_MASK_ARG(\
+               FUNC_CHAIN_NSRC_OUT, x)
+#define ABOX_FUNC_CHAIN_NSRC_ASRC_L(x) ABOX_L_ARG(FUNC_CHAIN_NSRC, 0, x)
+#define ABOX_FUNC_CHAIN_NSRC_ASRC_H(x) ABOX_H_ARG(FUNC_CHAIN_NSRC, 0, x)
+#define ABOX_FUNC_CHAIN_NSRC_ASRC_MASK(x)      ABOX_MASK_ARG(\
+               FUNC_CHAIN_NSRC_ASRC, x)
+#define ABOX_FUNC_CHAIN_RSRC_RECP_L    (1)
+#define ABOX_FUNC_CHAIN_RSRC_RECP_H    (1)
+#define ABOX_FUNC_CHAIN_RSRC_RECP_MASK ABOX_MASK(FUNC_CHAIN_RSRC_RECP)
+#define ABOX_FUNC_CHAIN_RSRC_ASRC_L    (0)
+#define ABOX_FUNC_CHAIN_RSRC_ASRC_H    (0)
+#define ABOX_FUNC_CHAIN_RSRC_ASRC_MASK ABOX_MASK(FUNC_CHAIN_RSRC_ASRC)
+/* ABOX_SPUM_CTRL1 */
+#define ABOX_SIFS_OUT_SEL_L            (16)
+#define ABOX_SIFS_OUT_SEL_H            (18)
+#define ABOX_SIFS_OUT_SEL_MASK         (ABOX_MASK(SIFS_OUT_SEL))
+#define ABOX_RECP_SRC_FORMAT_L         (8)
+#define ABOX_RECP_SRC_FORMAT_H         (12)
+#define ABOX_RECP_SRC_FORMAT_MASK      (ABOX_MASK(RECP_SRC_FORMAT))
+#define ABOX_RECP_SRC_VALID_L          (0)
+#define ABOX_RECP_SRC_VALID_H          (1)
+#define ABOX_RECP_SRC_VALID_MASK       (ABOX_MASK(RECP_SRC_VALID))
+/* ABOX_SPUM_CTRL2 */
+#define ABOX_SPUM_RECP_FLUSH_L         (0)
+#define ABOX_SPUM_RECP_FLUSH_H         (0)
+#define ABOX_SPUM_RECP_FLUSH_MASK      (ABOX_MASK(SPUM_RECP_FLUSH))
+/* ABOX_SPUM_CTRL3 */
+#define ABOX_SPUM_SIFM3_FLUSH_L                (3)
+#define ABOX_SPUM_SIFM3_FLUSH_H                (3)
+#define ABOX_SPUM_SIFM3_FLUSH_MASK     (ABOX_MASK(SPUM_SIFM3_FLUSH))
+#define ABOX_SPUM_SIFM2_FLUSH_L                (2)
+#define ABOX_SPUM_SIFM2_FLUSH_H                (2)
+#define ABOX_SPUM_SIFM2_FLUSH_MASK     (ABOX_MASK(SPUM_SIFM2_FLUSH))
+#define ABOX_SPUM_SIFM1_FLUSH_L                (1)
+#define ABOX_SPUM_SIFM1_FLUSH_H                (1)
+#define ABOX_SPUM_SIFM1_FLUSH_MASK     (ABOX_MASK(SPUM_SIFM1_FLUSH))
+#define ABOX_SPUM_SIFM0_FLUSH_L                (0)
+#define ABOX_SPUM_SIFM0_FLUSH_H                (0)
+#define ABOX_SPUM_SIFM0_FLUSH_MASK     (ABOX_MASK(SPUM_SIFM0_FLUSH))
+
+
+/* UAIF */
+#define ABOX_UAIF_BASE                 (0x0500)
+#define ABOX_UAIF_INTERVAL             (0x0010)
+#define ABOX_UAIF_CTRL0(x)             ABOX_IDX_ARG(UAIF, 0x0, x)
+#define ABOX_UAIF_CTRL1(x)             ABOX_IDX_ARG(UAIF, 0x4, x)
+#define ABOX_UAIF_STATUS(x)            ABOX_IDX_ARG(UAIF, 0xC, x)
+/* ABOX_UAIF?_CTRL0 */
+#define ABOX_START_FIFO_DIFF_MIC_L     (28)
+#define ABOX_START_FIFO_DIFF_MIC_H     (31)
+#define ABOX_START_FIFO_DIFF_MIC_MASK  (ABOX_MASK(START_FIFO_DIFF_MIC))
+#define ABOX_START_FIFO_DIFF_SPK_L     (24)
+#define ABOX_START_FIFO_DIFF_SPK_H     (27)
+#define ABOX_START_FIFO_DIFF_SPK_MASK  (ABOX_MASK(START_FIFO_DIFF_SPK))
+#define ABOX_DATA_MODE_L               (4)
+#define ABOX_DATA_MODE_H               (4)
+#define ABOX_DATA_MODE_MASK            (ABOX_MASK(DATA_MODE))
+#define ABOX_IRQ_MODE_L                        (3)
+#define ABOX_IRQ_MODE_H                        (3)
+#define ABOX_IRQ_MODE_MASK             (ABOX_MASK(IRQ_MODE))
+#define ABOX_MODE_L                    (2)
+#define ABOX_MODE_H                    (2)
+#define ABOX_MODE_MASK                 (ABOX_MASK(MODE))
+#define ABOX_MIC_ENABLE_L              (1)
+#define ABOX_MIC_ENABLE_H              (1)
+#define ABOX_MIC_ENABLE_MASK           (ABOX_MASK(MIC_ENABLE))
+#define ABOX_SPK_ENABLE_L              (0)
+#define ABOX_SPK_ENABLE_H              (0)
+#define ABOX_SPK_ENABLE_MASK           (ABOX_MASK(SPK_ENABLE))
+/* ABOX_UAIF?_CTRL1 */
+#define ABOX_FORMAT_L                  (24)
+#define ABOX_FORMAT_H                  (28)
+#define ABOX_FORMAT_MASK               (ABOX_MASK(FORMAT))
+#define ABOX_BCLK_POLARITY_L           (23)
+#define ABOX_BCLK_POLARITY_H           (23)
+#define ABOX_BCLK_POLARITY_MASK                (ABOX_MASK(BCLK_POLARITY))
+#define ABOX_WS_MODE_L                 (22)
+#define ABOX_WS_MODE_H                 (22)
+#define ABOX_WS_MODE_MASK              (ABOX_MASK(WS_MODE))
+#define ABOX_WS_POLAR_L                        (21)
+#define ABOX_WS_POLAR_H                        (21)
+#define ABOX_WS_POLAR_MASK             (ABOX_MASK(WS_POLAR))
+#define ABOX_SLOT_MAX_L                        (18)
+#define ABOX_SLOT_MAX_H                        (20)
+#define ABOX_SLOT_MAX_MASK             (ABOX_MASK(SLOT_MAX))
+#define ABOX_SBIT_MAX_L                        (12)
+#define ABOX_SBIT_MAX_H                        (17)
+#define ABOX_SBIT_MAX_MASK             (ABOX_MASK(SBIT_MAX))
+#define ABOX_VALID_STR_L               (6)
+#define ABOX_VALID_STR_H               (11)
+#define ABOX_VALID_STR_MASK            (ABOX_MASK(VALID_STR))
+#define ABOX_VALID_END_L               (0)
+#define ABOX_VALID_END_H               (5)
+#define ABOX_VALID_END_MASK            (ABOX_MASK(VALID_END))
+/* ABOX_UAIF?_STATUS */
+#define ABOX_ERROR_OF_MIC_L            (1)
+#define ABOX_ERROR_OF_MIC_H            (1)
+#define ABOX_ERROR_OF_MIC_MASK         (ABOX_MASK(ERROR_OF_MIC))
+#define ABOX_ERROR_OF_SPK_L            (0)
+#define ABOX_ERROR_OF_SPK_H            (0)
+#define ABOX_ERROR_OF_SPK_MASK         (ABOX_MASK(ERROR_OF_SPK))
+
+/* DSIF */
+#define ABOX_DSIF_CTRL                 (0x0550)
+#define ABOX_DSIF_STATUS               (0x0554)
+/* ABOX_DSIF_CTRL */
+#define ABOX_DSIF_BCLK_POLARITY_L      (2)
+#define ABOX_DSIF_BCLK_POLARITY_H      (2)
+#define ABOX_DSIF_BCLK_POLARITY_MASK   (ABOX_MASK(DSIF_BCLK_POLARITY))
+#define ABOX_ORDER_L                   (1)
+#define ABOX_ORDER_H                   (1)
+#define ABOX_ORDER_MASK                        (ABOX_MASK(ORDER))
+#define ABOX_ENABLE_L                  (0)
+#define ABOX_ENABLE_H                  (0)
+#define ABOX_ENABLE_MASK               (ABOX_MASK(ENABLE))
+/* ABOX_DSIF_STATUS */
+#define ABOX_ERROR_L                   (0)
+#define ABOX_ERROR_H                   (0)
+#define ABOX_ERROR_MASK                        (ABOX_MASK(ERROR))
+
+/* SPDY */
+#define ABOX_SPDYIF_CTRL               (0x0560)
+/* ABOX_SPDYIF_CTRL */
+#define ABOX_START_FIFO_DIFF_L         (1)
+#define ABOX_START_FIFO_DIFF_H         (4)
+#define ABOX_START_FIFO_DIFF_MASK      (ABOX_MASK(START_FIFO_DIFF))
+/* same with DSIF
+ * #define ABOX_ENABLE_L               (0)
+ * #define ABOX_ENABLE_H               (0)
+ * #define ABOX_ENABLE_MASK            (ABOX_MASK(ENABLE))
+ */
+
+/* TIMER */
+#define ABOX_TIMER_BASE                        (0x0600)
+#define ABOX_TIMER_INTERVAL            (0x0020)
+#define ABOX_TIMER_CTRL0(x)            ABOX_IDX_ARG(TIMER, 0x0, x)
+#define ABOX_TIMER_CTRL1(x)            ABOX_IDX_ARG(TIMER, 0x4, x)
+/* ABOX_TIMER?_CTRL0 */
+#define ABOX_TIMER_FLUSH_L             (1)
+#define ABOX_TIMER_FLUSH_H             (1)
+#define ABOX_TIMER_FLUSH_MASK          (ABOX_MASK(TIMER_FLUSH))
+#define ABOX_TIMER_START_L             (0)
+#define ABOX_TIMER_START_H             (0)
+#define ABOX_TIMER_START_MASK          (ABOX_MASK(TIMER_START))
+/* ABOX_TIMER?_CTRL1 */
+#define ABOX_TIMER_MODE_L              (0)
+#define ABOX_TIMER_MODE_H              (0)
+#define ABOX_TIMER_MODE_MASK           (ABOX_MASK(TIMER_MODE))
+
+/* RDMA */
+#define ABOX_RDMA_BASE                 (0x1000)
+#define ABOX_RDMA_INTERVAL             (0x0100)
+#define ABOX_RDMA_CTRL0                        (0x00)
+#define ABOX_RDMA_CTRL1                        (0x04)
+#define ABOX_RDMA_BUF_STR              (0x08)
+#define ABOX_RDMA_BUF_END              (0x0C)
+#define ABOX_RDMA_BUF_OFFSET           (0x10)
+#define ABOX_RDMA_STR_POINT            (0x14)
+#define ABOX_RDMA_VOL_FACTOR           (0x18)
+#define ABOX_RDMA_VOL_CHANGE           (0x1C)
+#ifdef CONFIG_SOC_EXYNOS8895
+#define ABOX_RDMA_STATUS               (0x20)
+#else
+#define ABOX_RDMA_SBACK_LIMIT          (0x20)
+#define ABOX_RDMA_STATUS               (0x30)
+#endif
+/* ABOX_RDMA_CTRL0 */
+#define ABOX_RDMA_ENABLE_L             (0)
+#define ABOX_RDMA_ENABLE_H             (0)
+#define ABOX_RDMA_ENABLE_MASK          (ABOX_MASK(RDMA_ENABLE))
+/* ABOX_RDMA_STATUS */
+#define ABOX_RDMA_PROGRESS_L           (31)
+#define ABOX_RDMA_PROGRESS_H           (31)
+#define ABOX_RDMA_PROGRESS_MASK                (ABOX_MASK(RDMA_PROGRESS))
+#define ABOX_RDMA_RBUF_OFFSET_L                (16)
+#define ABOX_RDMA_RBUF_OFFSET_H                (28)
+#define ABOX_RDMA_RBUF_OFFSET_MASK     (ABOX_MASK(RDMA_RBUF_OFFSET))
+#define ABOX_RDMA_RBUF_CNT_L           (0)
+#define ABOX_RDMA_RBUF_CNT_H           (12)
+#define ABOX_RDMA_RBUF_CNT_MASK                (ABOX_MASK(RDMA_RBUF_CNT))
+
+/* WDMA */
+#define ABOX_WDMA_BASE                 (0x2000)
+#define ABOX_WDMA_INTERVAL             (0x0100)
+#define ABOX_WDMA_CTRL                 (0x00)
+#define ABOX_WDMA_BUF_STR              (0x08)
+#define ABOX_WDMA_BUF_END              (0x0C)
+#define ABOX_WDMA_BUF_OFFSET           (0x10)
+#define ABOX_WDMA_STR_POINT            (0x14)
+#define ABOX_WDMA_VOL_FACTOR           (0x18)
+#define ABOX_WDMA_VOL_CHANGE           (0x1C)
+#ifdef CONFIG_SOC_EXYNOS8895
+#define ABOX_WDMA_STATUS               (0x20)
+#else
+#define ABOX_WDMA_SBACK_LIMIT          (0x20)
+#define ABOX_WDMA_STATUS               (0x30)
+#endif
+/* ABOX_WDMA_CTRL */
+#define ABOX_WDMA_ENABLE_L             (0)
+#define ABOX_WDMA_ENABLE_H             (0)
+#define ABOX_WDMA_ENABLE_MASK          (ABOX_MASK(WDMA_ENABLE))
+/* ABOX_WDMA_STATUS */
+#define ABOX_WDMA_PROGRESS_L           (31)
+#define ABOX_WDMA_PROGRESS_H           (31)
+#define ABOX_WDMA_PROGRESS_MASK                (ABOX_MASK(WDMA_PROGRESS))
+#define ABOX_WDMA_RBUF_OFFSET_L                (16)
+#define ABOX_WDMA_RBUF_OFFSET_H                (28)
+#define ABOX_WDMA_RBUF_OFFSET_MASK     (ABOX_MASK(WDMA_RBUF_OFFSET))
+#define ABOX_WDMA_RBUF_CNT_L           (0)
+#define ABOX_WDMA_RBUF_CNT_H           (12)
+#define ABOX_WDMA_RBUF_CNT_MASK                (ABOX_MASK(WDMA_RBUF_CNT))
+
+/* CA7 */
+#define ABOX_CPU_R(x)                  (0x2C00 + (x * 0x4))
+#define ABOX_CPU_PC                    (0x2C3C)
+#define ABOX_CPU_L2C_STATUS            (0x2C40)
+
+#define ABOX_MAX_REGISTERS             (0x2D0C)
+
+/* SYSREG */
+#define ABOX_SYSREG_L2_CACHE_CON       (0x0328)
+#define ABOX_SYSREG_MISC_CON           (0x032C)
+
+#define BUFFER_BYTES_MAX               (SZ_128K)
+#define PERIOD_BYTES_MIN               (SZ_128)
+#define PERIOD_BYTES_MAX               (BUFFER_BYTES_MAX / 2)
+
+#define DRAM_FIRMWARE_SIZE             (SZ_8M + SZ_4M)
+#define IOVA_DRAM_FIRMWARE             (0x80000000)
+#define IOVA_RDMA_BUFFER_BASE          (0x81000000)
+#define IOVA_RDMA_BUFFER(x)            (IOVA_RDMA_BUFFER_BASE + (SZ_1M * x))
+#define IOVA_WDMA_BUFFER_BASE          (0x82000000)
+#define IOVA_WDMA_BUFFER(x)            (IOVA_WDMA_BUFFER_BASE + (SZ_1M * x))
+#define IOVA_COMPR_BUFFER_BASE         (0x83000000)
+#define IOVA_COMPR_BUFFER(x)           (IOVA_COMPR_BUFFER_BASE + (SZ_1M * x))
+#define IOVA_VDMA_BUFFER_BASE          (0x84000000)
+#define IOVA_VDMA_BUFFER(x)            (IOVA_VDMA_BUFFER_BASE + (SZ_1M * x))
+#define IVA_FIRMWARE_SIZE              (SZ_512K)
+#define IOVA_IVA_FIRMWARE              (0x90000000)
+#define IOVA_IVA_BASE                  (IOVA_IVA_FIRMWARE)
+#define IOVA_IVA(x)                    (IOVA_IVA_BASE + (SZ_16M * x))
+#define IOVA_VSS_FIRMWARE              (0xA0000000)
+#define IOVA_VSS_PARAMETER             (0xA1000000)
+#define IOVA_BLUETOOTH                 (0xB0000000)
+#define IOVA_DUMP_BUFFER               (0xD0000000)
+#define PHSY_VSS_FIRMWARE              (0xFEE00000)
+#define PHSY_VSS_SIZE                  (SZ_4M + SZ_2M)
+
+#define AUD_PLL_RATE_HZ_FOR_48000      (1179648040)
+#define AUD_PLL_RATE_HZ_FOR_44100      (1083801600)
+
+#define LIMIT_IN_JIFFIES               (msecs_to_jiffies(1000))
+
+#define ABOX_CPU_GEAR_CALL_VSS         (0xCA11)
+#define ABOX_CPU_GEAR_CALL_KERNEL      (0xCA12)
+#define ABOX_CPU_GEAR_CALL             ABOX_CPU_GEAR_CALL_VSS
+#define ABOX_CPU_GEAR_BOOT             (0xB00D)
+#define ABOX_CPU_GEAR_MAX              (1)
+#define ABOX_CPU_GEAR_MIN              (12)
+
+#define ABOX_DMA_TIMEOUT_NS            (40000000)
+
+#define ABOX_SAMPLING_RATES (SNDRV_PCM_RATE_KNOT)
+#define ABOX_SAMPLE_FORMATS (SNDRV_PCM_FMTBIT_S16\
+               | SNDRV_PCM_FMTBIT_S24\
+               | SNDRV_PCM_FMTBIT_S32)
+#define ABOX_WDMA_SAMPLE_FORMATS (SNDRV_PCM_FMTBIT_S16\
+               | SNDRV_PCM_FMTBIT_S24\
+               | SNDRV_PCM_FMTBIT_S32)
+
+#define set_mask_value(id, mask, value) \
+               {id = (typeof(id))((id & ~mask) | (value & mask)); }
+
+#define set_value_by_name(id, name, value) \
+               set_mask_value(id, name##_MASK, value << name##_L)
+
+#define ABOX_SUPPLEMENT_SIZE (SZ_128)
+#define ABOX_IPC_QUEUE_SIZE (SZ_64)
+
+#define CALLIOPE_VERSION(class, year, month, minor) \
+               ((class << 24) | \
+               ((year - 1 + 'A') << 16) | \
+               ((month - 1 + 'A') << 8) | \
+               ((minor + '0') << 0))
+
+enum abox_sram {
+       ABOX_IVA_MEMORY,
+       ABOX_IVA_MEMORY_PREPARE,
+};
+
+enum abox_dai {
+       ABOX_RDMA0,
+       ABOX_RDMA1,
+       ABOX_RDMA2,
+       ABOX_RDMA3,
+       ABOX_RDMA4,
+       ABOX_RDMA5,
+       ABOX_RDMA6,
+       ABOX_RDMA7,
+       ABOX_WDMA0,
+       ABOX_WDMA1,
+       ABOX_WDMA2,
+       ABOX_WDMA3,
+       ABOX_WDMA4,
+       ABOX_UAIF0,
+       ABOX_UAIF1,
+       ABOX_UAIF2,
+       ABOX_UAIF3,
+       ABOX_UAIF4,
+       ABOX_DSIF,
+       ABOX_SPDY,
+       ABOX_SIFS0, /* Virtual DAI */
+       ABOX_SIFS1, /* Virtual DAI */
+       ABOX_SIFS2, /* Virtual DAI */
+};
+
+#define ABOX_DAI_COUNT (ABOX_DSIF - ABOX_UAIF0 + 1)
+
+enum calliope_state {
+       CALLIOPE_DISABLED,
+       CALLIOPE_DISABLING,
+       CALLIOPE_ENABLING,
+       CALLIOPE_ENABLED,
+       CALLIOPE_STATE_COUNT,
+};
+
+enum audio_mode {
+       MODE_NORMAL,
+       MODE_RINGTONE,
+       MODE_IN_CALL,
+       MODE_IN_COMMUNICATION,
+       MODE_IN_VIDEOCALL,
+};
+
+enum sound_type {
+       SOUND_TYPE_VOICE,
+       SOUND_TYPE_SPEAKER,
+       SOUND_TYPE_HEADSET,
+       SOUND_TYPE_BTVOICE,
+       SOUND_TYPE_USB,
+};
+
+enum qchannel {
+       ABOX_CCLK_CA7,
+       ABOX_ACLK,
+       ABOX_BCLK_UAIF0,
+       ABOX_BCLK_UAIF1,
+       ABOX_BCLK_UAIF2,
+       ABOX_BCLK_UAIF3,
+       ABOX_BCLK_DSIF,
+       ABOX_CCLK_ATB,
+       ABOX_CCLK_ASB,
+};
+
+
+#define ABOX_QUIRK_TRY_TO_ASRC_OFF     (1 << 0)
+#define ABOX_QUIRK_SHARE_VTS_SRAM      (1 << 1)
+#define ABOX_QUIRK_OFF_ON_SUSPEND      (1 << 2)
+#define ABOX_QUIRK_SCSC_BT             (1 << 3)
+#define ABOX_QUIRK_SCSC_BT_HACK                (1 << 4)
+#define ABOX_QUIRK_STR_TRY_TO_ASRC_OFF "try to asrc off"
+#define ABOX_QUIRK_STR_SHARE_VTS_SRAM  "share vts sram"
+#define ABOX_QUIRK_STR_OFF_ON_SUSPEND  "off on suspend"
+#define ABOX_QUIRK_STR_SCSC_BT         "scsc bt"
+#define ABOX_QUIRK_STR_SCSC_BT_HACK    "scsc bt hack"
+
+struct abox_ipc {
+       struct device *dev;
+       int hw_irq;
+       unsigned long long put_time;
+       unsigned long long get_time;
+       ABOX_IPC_MSG msg;
+};
+
+struct abox_irq_action {
+       struct list_head list;
+       int irq;
+       abox_irq_handler_t irq_handler;
+       void *dev_id;
+};
+
+struct abox_qos_request {
+       const void *id;
+       unsigned int value;
+};
+
+struct abox_dram_request {
+       void *id;
+       bool on;
+};
+
+struct abox_l2c_request {
+       void *id;
+       bool on;
+};
+
+struct abox_extra_firmware {
+       const struct firmware *firmware;
+       const char *name;
+       u32 area;
+       u32 offset;
+};
+
+struct abox_component {
+       struct ABOX_COMPONENT_DESCRIPTIOR *desc;
+       bool registered;
+       struct list_head value_list;
+};
+
+struct abox_component_kcontrol_value {
+       struct ABOX_COMPONENT_DESCRIPTIOR *desc;
+       struct ABOX_COMPONENT_CONTROL *control;
+       struct list_head list;
+       bool cache_only;
+       int cache[];
+};
+
+struct abox_data {
+       struct platform_device *pdev;
+       struct snd_soc_component *cmpnt;
+       struct regmap *regmap;
+       void __iomem *sfr_base;
+       void __iomem *sysreg_base;
+       void __iomem *sram_base;
+       phys_addr_t sram_base_phys;
+       size_t sram_size;
+       void *dram_base;
+       dma_addr_t dram_base_phys;
+       void *iva_base;
+       dma_addr_t iva_base_phys;
+       void *dump_base;
+       phys_addr_t dump_base_phys;
+       struct iommu_domain *iommu_domain;
+       unsigned int ipc_tx_offset;
+       unsigned int ipc_rx_offset;
+       unsigned int ipc_tx_ack_offset;
+       unsigned int ipc_rx_ack_offset;
+       unsigned int mailbox_offset;
+       unsigned int if_count;
+       unsigned int rdma_count;
+       unsigned int wdma_count;
+       unsigned int calliope_version;
+       const struct firmware *firmware_sram;
+       const struct firmware *firmware_dram;
+       const struct firmware *firmware_iva;
+       struct abox_extra_firmware firmware_extra[8];
+       struct device *dev_gic;
+       struct device *dev_bt;
+       struct platform_device *pdev_if[8];
+       struct platform_device *pdev_rdma[8];
+       struct platform_device *pdev_wdma[5];
+       struct platform_device *pdev_vts;
+       struct workqueue_struct *gear_workqueue;
+       struct workqueue_struct *ipc_workqueue;
+       struct work_struct ipc_work;
+       struct abox_ipc ipc_queue[ABOX_IPC_QUEUE_SIZE];
+       int ipc_queue_start;
+       int ipc_queue_end;
+       spinlock_t ipc_queue_lock;
+       wait_queue_head_t ipc_wait_queue;
+       struct clk *clk_pll;
+       struct clk *clk_audif;
+       struct clk *clk_cpu;
+       struct clk *clk_bus;
+       unsigned int uaif_max_div;
+       struct pinctrl *pinctrl;
+       unsigned long quirks;
+       unsigned int cpu_gear;
+       unsigned int cpu_gear_min;
+       struct abox_qos_request cpu_gear_requests[32];
+       struct work_struct change_cpu_gear_work;
+       unsigned int int_freq;
+       struct abox_qos_request int_requests[16];
+       struct work_struct change_int_freq_work;
+       unsigned int mif_freq;
+       struct abox_qos_request mif_requests[16];
+       struct work_struct change_mif_freq_work;
+       unsigned int lit_freq;
+       struct abox_qos_request lit_requests[16];
+       struct work_struct change_lit_freq_work;
+       unsigned int big_freq;
+       struct abox_qos_request big_requests[16];
+       struct work_struct change_big_freq_work;
+       unsigned int hmp_boost;
+       struct abox_qos_request hmp_requests[16];
+       struct work_struct change_hmp_boost_work;
+       struct abox_dram_request dram_requests[16];
+       unsigned long audif_rates[ABOX_DAI_COUNT];
+       unsigned int sif_rate[SET_INMUX4_SAMPLE_RATE -
+                       SET_MIXER_SAMPLE_RATE + 1];
+       snd_pcm_format_t sif_format[SET_INMUX4_FORMAT -
+                       SET_MIXER_FORMAT + 1];
+       unsigned int sif_channels[SET_INMUX4_FORMAT -
+                       SET_MIXER_FORMAT + 1];
+       unsigned int sif_rate_min[SET_INMUX4_SAMPLE_RATE -
+                       SET_MIXER_SAMPLE_RATE + 1];
+       snd_pcm_format_t sif_format_min[SET_INMUX4_FORMAT -
+                       SET_MIXER_FORMAT + 1];
+       unsigned int sif_channels_min[SET_INMUX4_FORMAT -
+                       SET_MIXER_FORMAT + 1];
+       bool sif_auto_config[SET_INMUX4_SAMPLE_RATE -
+                       SET_MIXER_SAMPLE_RATE + 1];
+       unsigned int erap_status[ERAP_TYPE_COUNT];
+       struct work_struct register_component_work;
+       struct abox_component components[16];
+       struct list_head irq_actions;
+       bool enabled;
+       enum calliope_state calliope_state;
+       bool l2c_controlled;
+       bool l2c_enabled;
+       struct abox_l2c_request l2c_requests[8];
+       struct work_struct l2c_work;
+       struct notifier_block qos_nb;
+       struct notifier_block pm_nb;
+       struct notifier_block modem_nb;
+       struct notifier_block itmon_nb;
+       int pm_qos_int[5];
+       int pm_qos_aud[5];
+       struct ima_client *ima_client;
+       void *ima_vaddr;
+       bool ima_claimed;
+       struct mutex ima_lock;
+       struct work_struct boot_done_work;
+       struct delayed_work tickle_work;
+       enum audio_mode audio_mode;
+       enum sound_type sound_type;
+};
+
+struct abox_compr_data {
+       /* compress offload */
+       struct snd_compr_stream *cstream;
+
+       void *dma_area;
+       size_t dma_size;
+       dma_addr_t dma_addr;
+
+       unsigned int block_num;
+       unsigned int handle_id;
+       unsigned int codec_id;
+       unsigned int channels;
+       unsigned int sample_rate;
+
+       unsigned int byte_offset;
+       u64 copied_total;
+       u64 received_total;
+
+       bool start;
+       bool eos;
+       bool created;
+
+       bool effect_on;
+
+       wait_queue_head_t flush_wait;
+       wait_queue_head_t exit_wait;
+       wait_queue_head_t ipc_wait;
+
+       uint32_t stop_ack;
+       uint32_t exit_ack;
+
+       spinlock_t lock;
+       spinlock_t cmd_lock;
+
+       int (*isr_handler)(void *data);
+
+       struct snd_compr_params codec_param;
+
+       /* effect offload */
+       unsigned int out_sample_rate;
+};
+
+enum abox_platform_type {
+       PLATFORM_NORMAL,
+       PLATFORM_CALL,
+       PLATFORM_COMPRESS,
+       PLATFORM_REALTIME,
+       PLATFORM_VI_SENSING,
+       PLATFORM_SYNC,
+};
+
+enum abox_rate {
+       RATE_SUHQA,
+       RATE_UHQA,
+       RATE_NORMAL,
+       RATE_COUNT,
+};
+
+static inline bool abox_test_quirk(struct abox_data *data, unsigned long quirk)
+{
+       return !!(data->quirks & quirk);
+}
+
+/**
+ * Get sampling rate type
+ * @param[in]  rate            sampling rate in Hz
+ * @return     rate type in enum abox_rate
+ */
+static inline enum abox_rate abox_get_rate_type(unsigned int rate)
+{
+       if (rate < 176400)
+               return RATE_NORMAL;
+       else if (rate >= 176400 && rate <= 192000)
+               return RATE_UHQA;
+       else
+               return RATE_SUHQA;
+}
+
+/**
+ * Get SFR of sample format
+ * @param[in]  bit_depth       count of bit in sample
+ * @param[in]  channels        count of channel
+ * @return     SFR of sample format
+ */
+static inline u32 abox_get_format(int bit_depth, int channels)
+{
+       u32 ret = (channels - 1);
+
+       switch (bit_depth) {
+       case 16:
+               ret |= 1 << 3;
+               break;
+       case 24:
+               ret |= 2 << 3;
+               break;
+       case 32:
+               ret |= 3 << 3;
+               break;
+       default:
+               break;
+       }
+
+       pr_debug("%s(%d, %d): %u\n", __func__, bit_depth, channels, ret);
+
+       return ret;
+}
+
+/**
+ * Get enum IPC_ID from SNDRV_PCM_STREAM_*
+ * @param[in]  stream  SNDRV_PCM_STREAM_*
+ * @return     IPC_PCMPLAYBACK or IPC_PCMCAPTURE
+ */
+static inline enum IPC_ID abox_stream_to_ipcid(int stream)
+{
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               return IPC_PCMPLAYBACK;
+       else if (stream == SNDRV_PCM_STREAM_CAPTURE)
+               return IPC_PCMCAPTURE;
+       else
+               return -EINVAL;
+}
+
+/**
+ * Get SNDRV_PCM_STREAM_* from enum IPC_ID
+ * @param[in]  ipcid   IPC_PCMPLAYBACK or IPC_PCMCAPTURE
+ * @return     SNDRV_PCM_STREAM_*
+ */
+static inline int abox_ipcid_to_stream(enum IPC_ID ipcid)
+{
+       if (ipcid == IPC_PCMPLAYBACK)
+               return SNDRV_PCM_STREAM_PLAYBACK;
+       else if (ipcid == IPC_PCMCAPTURE)
+               return SNDRV_PCM_STREAM_CAPTURE;
+       else
+               return -EINVAL;
+}
+
+struct abox_platform_data {
+       void __iomem *sfr_base;
+       void __iomem *mailbox_base;
+       unsigned int id;
+       unsigned int pointer;
+       int pm_qos_lit[RATE_COUNT];
+       int pm_qos_big[RATE_COUNT];
+       int pm_qos_hmp[RATE_COUNT];
+       struct platform_device *pdev_abox;
+       struct abox_data *abox_data;
+       struct snd_pcm_substream *substream;
+       enum abox_platform_type type;
+       bool ack_enabled;
+       struct abox_compr_data compr_data;
+       struct regmap *mailbox;
+       bool scsc_bt;
+};
+
+/**
+ * get pointer to abox_data (internal use only)
+ * @return     pointer to abox_data
+ */
+extern struct abox_data *abox_get_abox_data(void);
+
+/**
+ * get physical address from abox virtual address
+ * @param[in]  data    pointer to abox_data structure
+ * @param[in]  addr    abox virtual address
+ * @return     physical address
+ */
+extern phys_addr_t abox_addr_to_phys_addr(struct abox_data *data,
+               unsigned int addr);
+
+/**
+ * get kernel address from abox virtual address
+ * @param[in]  data    pointer to abox_data structure
+ * @param[in]  addr    abox virtual address
+ * @return     kernel address
+ */
+extern void *abox_addr_to_kernel_addr(struct abox_data *data,
+               unsigned int addr);
+
+/**
+ * check specific cpu gear request is idle
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  id              key which is used as unique handle
+ * @return     true if it is idle or not has been requested, false on otherwise
+ */
+extern bool abox_cpu_gear_idle(struct device *dev, struct abox_data *data,
+               void *id);
+
+/**
+ * Request abox cpu clock level
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  id              key which is used as unique handle
+ * @param[in]  gear            gear level (cpu clock = aud pll rate / gear)
+ * @return     error code if any
+ */
+extern int abox_request_cpu_gear(struct device *dev, struct abox_data *data,
+               const void *id, unsigned int gear);
+
+/**
+ * Wait for pending cpu gear change
+ * @param[in]  data            pointer to abox_data structure
+ */
+extern void abox_cpu_gear_barrier(struct abox_data *data);
+
+/**
+ * Request abox cpu clock level synchronously
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  id              key which is used as unique handle
+ * @param[in]  gear            gear level (cpu clock = aud pll rate / gear)
+ * @return     error code if any
+ */
+extern int abox_request_cpu_gear_sync(struct device *dev,
+               struct abox_data *data, const void *id, unsigned int gear);
+
+/**
+ * Clear abox cpu clock requests
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ */
+extern void abox_clear_cpu_gear_requests(struct device *dev,
+               struct abox_data *data);
+
+/**
+ * Request LITTLE cluster clock level
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  id              key which is used as unique handle
+ * @param[in]  freq            frequency in kHz
+ * @return     error code if any
+ */
+extern int abox_request_lit_freq(struct device *dev, struct abox_data *data,
+               void *id, unsigned int freq);
+
+/**
+ * Request big cluster clock level
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  id              key which is used as unique handle
+ * @param[in]  freq            frequency in kHz
+ * @return     error code if any
+ */
+extern int abox_request_big_freq(struct device *dev, struct abox_data *data,
+               void *id, unsigned int freq);
+
+/**
+ * Request hmp boost
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  id              key which is used as unique handle
+ * @param[in]  on              1 on boost, 0 on otherwise.
+ * @return     error code if any
+ */
+extern int abox_request_hmp_boost(struct device *dev, struct abox_data *data,
+               void *id, unsigned int on);
+/**
+ * Register uaif or dsif to abox
+ * @param[in]  pdev_abox       pointer to abox platform device
+ * @param[in]  pdev_xif        pointer to abox uaif or dsif device
+ * @param[in]  id              number
+ * @param[in]  dapm            dapm context of the uaif or dsif
+ * @param[in]  name            dai name
+ * @param[in]  playback        true if dai has playback capability
+ * @param[in]  capture         true if dai has capture capability
+ * @return     error code if any
+ */
+extern int abox_register_if(struct platform_device *pdev_abox,
+               struct platform_device *pdev_if, unsigned int id,
+               struct snd_soc_dapm_context *dapm, const char *name,
+               bool playback, bool capture);
+
+/**
+ * Try to turn off ASRC when sampling rate auto control is enabled
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  fe              pointer to snd_soc_pcm_runtime
+ * @param[in]  stream          SNDRV_PCM_STREAM_PLAYBACK or CAPUTURE
+ * @return     error code if any
+ */
+extern int abox_try_to_asrc_off(struct device *dev, struct abox_data *data,
+               struct snd_soc_pcm_runtime *fe, int stream);
+
+/**
+ * Register rdma to abox
+ * @param[in]  pdev_abox       pointer to abox platform device
+ * @param[in]  pdev_rdma       pointer to abox rdma platform device
+ * @param[in]  id              number
+ * @return     error code if any
+ */
+extern int abox_register_rdma(struct platform_device *pdev_abox,
+               struct platform_device *pdev_rdma, unsigned int id);
+
+/**
+ * Register wdma to abox
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  ipcid           id of ipc
+ * @param[in]  irq_handler     irq handler to register
+ * @param[in]  dev_id          cookie which would be summitted with irq_handler
+ * @return     error code if any
+ */
+extern int abox_register_wdma(struct platform_device *pdev_abox,
+               struct platform_device *pdev_wdma, unsigned int id);
+
+/**
+ * Register uaif to abox
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  id              id of the uaif
+ * @param[in]  rate            sampling rate
+ * @param[in]  channels        number of channels
+ * @param[in]  width           number of bit in sample
+ * @return     error code if any
+ */
+extern int abox_register_bclk_usage(struct device *dev, struct abox_data *data,
+               enum abox_dai id, unsigned int rate, unsigned int channels,
+               unsigned int width);
+
+/**
+ * Request or release l2 cache
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  id              key which is used as unique handle
+ * @param[in]  on              true for requesting, false on otherwise
+ * @return     error code if any
+ */
+extern int abox_request_l2c(struct device *dev, struct abox_data *data,
+               void *id, bool on);
+
+/**
+ * Request or release l2 cache synchronously
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  id              key which is used as unique handle
+ * @param[in]  on              true for requesting, false on otherwise
+ * @return     error code if any
+ */
+extern int abox_request_l2c_sync(struct device *dev, struct abox_data *data,
+               void *id, bool on);
+
+/**
+ * Request or release dram during cpuidle (count based API)
+ * @param[in]  pdev_abox       pointer to abox platform device
+ * @param[in]  id              key which is used as unique handle
+ * @param[in]  on              true for requesting, false on otherwise
+ */
+extern void abox_request_dram_on(struct platform_device *pdev_abox, void *id,
+               bool on);
+
+/**
+ * claim IVA memory
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[out] addr            optional argument to get physical address
+ */
+extern int abox_ima_claim(struct device *dev, struct abox_data *data,
+               phys_addr_t *addr);
+
+/**
+ * disable or enable qchannel of a clock
+ * @param[in]  dev             pointer to struct dev which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  clk             clock id
+ * @param[in]  disable         disable or enable
+ */
+extern int abox_disable_qchannel(struct device *dev, struct abox_data *data,
+               enum qchannel clk, int disable);
+#endif /* __SND_SOC_ABOX_H */
diff --git a/sound/soc/samsung/abox/abox_bt.c b/sound/soc/samsung/abox/abox_bt.c
new file mode 100644 (file)
index 0000000..1559903
--- /dev/null
@@ -0,0 +1,230 @@
+/* sound/soc/samsung/abox/abox_bt.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox SCSC B/T driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/shm_ipc.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+
+#include <scsc/api/bt_audio.h>
+#include "../../../../drivers/iommu/exynos-iommu.h"
+
+#include "abox.h"
+#include "abox_bt.h"
+
+static int abox_bt_sco_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_bt_data *data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+                       (struct soc_mixer_control *)kcontrol->private_value;
+       enum bt_sco dir = (enum bt_sco)mc->reg;
+
+       dev_dbg(dev, "%s(%d)\n", __func__, dir);
+
+       ucontrol->value.integer.value[0] = data->active[dir];
+       return 0;
+}
+
+static int abox_bt_sco_put_ipc(struct device *dev,
+               enum bt_sco dir, unsigned int val)
+{
+       struct device *dev_abox = dev->parent;
+       struct abox_bt_data *data = dev_get_drvdata(dev);
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system_msg = &msg.msg.system;
+       int ret;
+
+       dev_dbg(dev, "%s(%d, %u)\n", __func__, dir, val);
+
+       if (!data->paddr_bt) {
+               dev_warn(dev, "B/T SCO isn't ready yet\n");
+               return -EIO;
+       }
+
+       msg.ipcid = IPC_SYSTEM;
+       system_msg->msgtype = ABOX_BT_SCO_ENABLE;
+       system_msg->param1 = val;
+       system_msg->param2 = IOVA_BLUETOOTH;
+       system_msg->param3 = dir;
+       ret = abox_request_ipc(dev_abox, msg.ipcid, &msg, sizeof(msg), 0, 0);
+
+       data->active[dir] = val;
+
+       return ret;
+}
+
+static int abox_bt_sco_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct soc_mixer_control *mc =
+                       (struct soc_mixer_control *)kcontrol->private_value;
+       enum bt_sco dir = (enum bt_sco)mc->reg;
+       unsigned int val = (unsigned int)ucontrol->value.integer.value[0];
+
+       dev_info(dev, "%s(%d, %u)\n", __func__, dir, val);
+
+       return abox_bt_sco_put_ipc(dev, dir, val);
+}
+
+static const struct snd_kcontrol_new abox_bt_controls[] = {
+       SOC_SINGLE_EXT("BT SCO SPK Enable", BT_SCO_SPK, 0, 1, 0,
+               abox_bt_sco_get, abox_bt_sco_put),
+       SOC_SINGLE_EXT("BT SCO MIC Enable", BT_SCO_MIC, 0, 1, 0,
+               abox_bt_sco_get, abox_bt_sco_put),
+};
+
+static const struct snd_soc_component_driver abox_bt_cmpnt = {
+       .controls       = abox_bt_controls,
+       .num_controls   = ARRAY_SIZE(abox_bt_controls),
+};
+
+unsigned int abox_bt_get_rate(struct device *dev)
+{
+       dev_dbg(dev, "%s\n", __func__);
+
+       return scsc_bt_audio_get_rate(0);
+}
+
+unsigned int abox_bt_get_buf_iova(struct device *dev, int stream)
+{
+       struct abox_bt_data *data = dev_get_drvdata(dev);
+       bool tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+       phys_addr_t paddr_buf = scsc_bt_audio_get_paddr_buf(tx);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (paddr_buf && data->paddr_bt)
+               return IOVA_BLUETOOTH + (paddr_buf - data->paddr_bt);
+       else
+               return 0;
+}
+
+bool abox_bt_active(struct device *dev, int stream)
+{
+       struct abox_bt_data *data = dev_get_drvdata(dev);
+       enum bt_sco dir;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dir = BT_SCO_MIC;
+       else
+               dir = BT_SCO_SPK;
+
+       return data->active[dir];
+}
+
+static int abox_bt_iommu_map(struct device *dev, phys_addr_t paddr, size_t size)
+{
+       struct abox_bt_data *data = dev_get_drvdata(dev);
+       struct iommu_domain *domain = data->abox_data->iommu_domain;
+
+       dev_info(dev, "%s(%pa, %#zx)\n", __func__, &paddr, size);
+
+       data->paddr_bt = paddr;
+       return iommu_map(domain, IOVA_BLUETOOTH, paddr, size, 0);
+}
+
+static void abox_bt_iommu_unmap(struct device *dev, size_t size)
+{
+       struct abox_bt_data *data = dev_get_drvdata(dev);
+       struct iommu_domain *domain = data->abox_data->iommu_domain;
+
+       dev_info(dev, "%s(%#zx)\n", __func__, size);
+
+       iommu_unmap(domain, IOVA_BLUETOOTH, size);
+       exynos_sysmmu_tlb_invalidate(domain, IOVA_BLUETOOTH, size);
+}
+
+static int samsung_abox_bt_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device *dev_abox = dev->parent;
+       struct abox_data *abox_data = dev_get_drvdata(dev_abox);
+       struct abox_bt_data *data;
+       struct resource *res;
+       int ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, data);
+       data->dev = dev;
+       data->abox_data = abox_data;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mailbox");
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(&pdev->dev, "Failed to get %s\n", "mailbox");
+               return -EINVAL;
+       }
+       abox_iommu_map(dev_abox, res->start, res->start, resource_size(res));
+
+       ret = scsc_bt_audio_register(dev, abox_bt_iommu_map,
+                       abox_bt_iommu_unmap);
+       if (ret < 0)
+               return -EPROBE_DEFER;
+
+       ret = devm_snd_soc_register_component(dev, &abox_bt_cmpnt, NULL, 0);
+       if (ret < 0) {
+               dev_err(dev, "component register failed:%d\n", ret);
+               return ret;
+       }
+
+       abox_data->dev_bt = dev;
+
+       return ret;
+}
+
+static int samsung_abox_bt_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       scsc_bt_audio_unregister(dev);
+
+       return 0;
+}
+
+static const struct of_device_id samsung_abox_bt_match[] = {
+       {
+               .compatible = "samsung,abox-bt",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_abox_bt_match);
+
+static struct platform_driver samsung_abox_bt_driver = {
+       .probe  = samsung_abox_bt_probe,
+       .remove = samsung_abox_bt_remove,
+       .driver = {
+               .name = "samsung-abox-bt",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_abox_bt_match),
+       },
+};
+
+module_platform_driver(samsung_abox_bt_driver);
+
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box SCSC Bluetooth Driver");
+MODULE_ALIAS("platform:samsung-abox-bt");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox_bt.h b/sound/soc/samsung/abox/abox_bt.h
new file mode 100644 (file)
index 0000000..573c95c
--- /dev/null
@@ -0,0 +1,54 @@
+/* sound/soc/samsung/abox/abox_bt.h
+ *
+ * ALSA SoC Audio Layer - Samsung Abox SCSC B/T driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_BT_H
+#define __SND_SOC_ABOX_BT_H
+
+#include <linux/device.h>
+
+enum bt_sco {
+       BT_SCO_ALL,
+       BT_SCO_MIC,
+       BT_SCO_SPK,
+       BT_SCO_COUNT,
+};
+
+struct abox_bt_data {
+       struct device *dev;
+       struct abox_data *abox_data;
+       phys_addr_t paddr_bt;
+       bool active[BT_SCO_COUNT];
+};
+
+/**
+ * Get rate of bluetooth audio interface
+ * @param[in]  dev     pointer to abox_bt device
+ * @return     sample rate or 0
+ */
+extern unsigned int abox_bt_get_rate(struct device *dev);
+
+/**
+ * Get IO virtual address of bluetooth tx buffer
+ * @param[in]  dev     pointer to abox_bt device
+ * @param[in]  stream  SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
+ * @return     IO virtual address or 0
+ */
+extern unsigned int abox_bt_get_buf_iova(struct device *dev, int stream);
+
+/**
+ * Returns true when the bt stream is active
+ * @param[in]  dev     pointer to abox_bt device
+ * @param[in]  stream  SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
+ * @return     true or false
+ */
+extern bool abox_bt_active(struct device *dev, int stream);
+
+#endif /* __SND_SOC_ABOX_BT_H */
diff --git a/sound/soc/samsung/abox/abox_dbg.c b/sound/soc/samsung/abox/abox_dbg.c
new file mode 100644 (file)
index 0000000..1d99ea6
--- /dev/null
@@ -0,0 +1,461 @@
+/* sound/soc/samsung/abox/abox_dbg.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox Debug driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/iommu.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched/clock.h>
+#include "abox_dbg.h"
+#include "abox_gic.h"
+
+static struct dentry *abox_dbg_root_dir __read_mostly;
+
+struct dentry *abox_dbg_get_root_dir(void)
+{
+       pr_debug("%s\n", __func__);
+
+       if (abox_dbg_root_dir == NULL)
+               abox_dbg_root_dir = debugfs_create_dir("abox", NULL);
+
+       return abox_dbg_root_dir;
+}
+
+void abox_dbg_print_gpr_from_addr(struct device *dev, struct abox_data *data,
+               unsigned int *addr)
+{
+       int i;
+       char version[4];
+
+       memcpy(version, &data->calliope_version, sizeof(version));
+
+       dev_info(dev, "========================================\n");
+       dev_info(dev, "A-Box CPU register dump (%c%c%c%c)\n",
+                       version[3], version[2], version[1], version[0]);
+       dev_info(dev, "----------------------------------------\n");
+       for (i = 0; i <= 14; i++)
+               dev_info(dev, "CA7_R%02d        : %08x\n", i, *addr++);
+       dev_info(dev, "CA7_PC         : %08x\n", *addr++);
+       dev_info(dev, "========================================\n");
+}
+
+void abox_dbg_print_gpr(struct device *dev, struct abox_data *data)
+{
+       int i;
+       char version[4];
+
+       memcpy(version, &data->calliope_version, sizeof(version));
+
+       dev_info(dev, "========================================\n");
+       dev_info(dev, "A-Box CPU register dump (%c%c%c%c)\n",
+                       version[3], version[2], version[1], version[0]);
+       dev_info(dev, "----------------------------------------\n");
+       for (i = 0; i <= 14; i++) {
+               dev_info(dev, "CA7_R%02d        : %08x\n", i,
+                               readl(data->sfr_base + ABOX_CPU_R(i)));
+       }
+       dev_info(dev, "CA7_PC         : %08x\n",
+                       readl(data->sfr_base + ABOX_CPU_PC));
+       dev_info(dev, "CA7_L2C_STATUS : %08x\n",
+                       readl(data->sfr_base + ABOX_CPU_L2C_STATUS));
+       dev_info(dev, "========================================\n");
+}
+
+struct abox_dbg_dump {
+       char sram[SZ_512K];
+       char iva[IVA_FIRMWARE_SIZE];
+       char dram[DRAM_FIRMWARE_SIZE];
+       u32 sfr[SZ_64K / sizeof(u32)];
+       u32 sfr_gic_gicd[SZ_4K / sizeof(u32)];
+       unsigned int gpr[17];
+       long long time;
+       char reason[SZ_32];
+};
+
+struct abox_dbg_dump_min {
+       char sram[SZ_512K];
+       char iva[IVA_FIRMWARE_SIZE];
+       u32 sfr[SZ_64K / sizeof(u32)];
+       u32 sfr_gic_gicd[SZ_4K / sizeof(u32)];
+       unsigned int gpr[17];
+       long long time;
+       char reason[SZ_32];
+};
+
+static struct abox_dbg_dump (*p_abox_dbg_dump)[ABOX_DBG_DUMP_COUNT];
+static struct abox_dbg_dump_min (*p_abox_dbg_dump_min)[ABOX_DBG_DUMP_COUNT];
+static struct reserved_mem *abox_rmem;
+
+static int __init abox_rmem_setup(struct reserved_mem *rmem)
+{
+       pr_info("%s: base=%pa, size=%pa\n", __func__, &rmem->base, &rmem->size);
+
+       abox_rmem = rmem;
+       if (sizeof(*p_abox_dbg_dump) <= abox_rmem->size)
+               p_abox_dbg_dump = phys_to_virt(abox_rmem->base);
+       else if (sizeof(*p_abox_dbg_dump_min) <= abox_rmem->size)
+               p_abox_dbg_dump_min = phys_to_virt(abox_rmem->base);
+
+       return 0;
+}
+
+RESERVEDMEM_OF_DECLARE(abox_rmem, "exynos,abox_rmem", abox_rmem_setup);
+
+void abox_dbg_dump_gpr_from_addr(struct device *dev, unsigned int *addr,
+               enum abox_dbg_dump_src src, const char *reason)
+{
+       int i;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!abox_is_on()) {
+               dev_info(dev, "%s is skipped due to no power\n", __func__);
+               return;
+       }
+
+       if (p_abox_dbg_dump) {
+               struct abox_dbg_dump *p_dump = &(*p_abox_dbg_dump)[src];
+
+               p_dump->time = sched_clock();
+               strncpy(p_dump->reason, reason, sizeof(p_dump->reason) - 1);
+               for (i = 0; i <= 14; i++)
+                       p_dump->gpr[i] = *addr++;
+               p_dump->gpr[i++] = *addr++;
+       } else if (p_abox_dbg_dump_min) {
+               struct abox_dbg_dump_min *p_dump = &(*p_abox_dbg_dump_min)[src];
+
+               p_dump->time = sched_clock();
+               strncpy(p_dump->reason, reason, sizeof(p_dump->reason) - 1);
+               for (i = 0; i <= 14; i++)
+                       p_dump->gpr[i] = *addr++;
+               p_dump->gpr[i++] = *addr++;
+       }
+}
+
+void abox_dbg_dump_gpr(struct device *dev, struct abox_data *data,
+               enum abox_dbg_dump_src src, const char *reason)
+{
+       int i;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!abox_is_on()) {
+               dev_info(dev, "%s is skipped due to no power\n", __func__);
+               return;
+       }
+
+       if (p_abox_dbg_dump) {
+               struct abox_dbg_dump *p_dump = &(*p_abox_dbg_dump)[src];
+
+               p_dump->time = sched_clock();
+               strncpy(p_dump->reason, reason, sizeof(p_dump->reason) - 1);
+               for (i = 0; i <= 14; i++)
+                       p_dump->gpr[i] = readl(data->sfr_base + ABOX_CPU_R(i));
+               p_dump->gpr[i++] = readl(data->sfr_base + ABOX_CPU_PC);
+               p_dump->gpr[i++] = readl(data->sfr_base + ABOX_CPU_L2C_STATUS);
+       } else if (p_abox_dbg_dump_min) {
+               struct abox_dbg_dump_min *p_dump = &(*p_abox_dbg_dump_min)[src];
+
+               p_dump->time = sched_clock();
+               strncpy(p_dump->reason, reason, sizeof(p_dump->reason) - 1);
+               for (i = 0; i <= 14; i++)
+                       p_dump->gpr[i] = readl(data->sfr_base + ABOX_CPU_R(i));
+               p_dump->gpr[i++] = readl(data->sfr_base + ABOX_CPU_PC);
+               p_dump->gpr[i++] = readl(data->sfr_base + ABOX_CPU_L2C_STATUS);
+       }
+}
+
+void abox_dbg_dump_mem(struct device *dev, struct abox_data *data,
+               enum abox_dbg_dump_src src, const char *reason)
+{
+       struct abox_gic_data *gic_data = dev_get_drvdata(data->dev_gic);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!abox_is_on()) {
+               dev_info(dev, "%s is skipped due to no power\n", __func__);
+               return;
+       }
+
+       if (p_abox_dbg_dump) {
+               struct abox_dbg_dump *p_dump = &(*p_abox_dbg_dump)[src];
+
+               p_dump->time = sched_clock();
+               strncpy(p_dump->reason, reason, sizeof(p_dump->reason) - 1);
+               memcpy_fromio(p_dump->sram, data->sram_base, data->sram_size);
+               if (data->ima_claimed)
+                       memcpy_fromio(p_dump->iva, data->ima_vaddr,
+                                       sizeof(p_dump->iva));
+               else
+                       memcpy(p_dump->iva, data->iva_base,
+                                       sizeof(p_dump->iva));
+               memcpy(p_dump->dram, data->dram_base, sizeof(p_dump->dram));
+               memcpy_fromio(p_dump->sfr, data->sfr_base, sizeof(p_dump->sfr));
+               memcpy_fromio(p_dump->sfr_gic_gicd, gic_data->gicd_base,
+                               sizeof(p_dump->sfr_gic_gicd));
+       } else if (p_abox_dbg_dump_min) {
+               struct abox_dbg_dump_min *p_dump = &(*p_abox_dbg_dump_min)[src];
+
+               p_dump->time = sched_clock();
+               strncpy(p_dump->reason, reason, sizeof(p_dump->reason) - 1);
+               memcpy_fromio(p_dump->sram, data->sram_base, data->sram_size);
+               if (data->ima_claimed)
+                       memcpy_fromio(p_dump->iva, data->ima_vaddr,
+                                       sizeof(p_dump->iva));
+               else
+                       memcpy(p_dump->iva, data->iva_base,
+                                       sizeof(p_dump->iva));
+               memcpy_fromio(p_dump->sfr, data->sfr_base, sizeof(p_dump->sfr));
+               memcpy_fromio(p_dump->sfr_gic_gicd, gic_data->gicd_base,
+                               sizeof(p_dump->sfr_gic_gicd));
+       }
+}
+
+void abox_dbg_dump_gpr_mem(struct device *dev, struct abox_data *data,
+               enum abox_dbg_dump_src src, const char *reason)
+{
+       abox_dbg_dump_gpr(dev, data, src, reason);
+       abox_dbg_dump_mem(dev, data, src, reason);
+}
+
+struct abox_dbg_dump_simple {
+       char sram[SZ_512K];
+       char iva[IVA_FIRMWARE_SIZE];
+       u32 sfr[SZ_64K / sizeof(u32)];
+       u32 sfr_gic_gicd[SZ_4K / sizeof(u32)];
+       unsigned int gpr[17];
+       long long time;
+       char reason[SZ_32];
+};
+
+static struct abox_dbg_dump_simple abox_dump_simple;
+
+void abox_dbg_dump_simple(struct device *dev, struct abox_data *data,
+               const char *reason)
+{
+       struct abox_gic_data *gic_data = dev_get_drvdata(data->dev_gic);
+       int i;
+
+       dev_info(dev, "%s\n", __func__);
+
+       if (!abox_is_on()) {
+               dev_info(dev, "%s is skipped due to no power\n", __func__);
+               return;
+       }
+
+       abox_dump_simple.time = sched_clock();
+       strncpy(abox_dump_simple.reason, reason,
+                       sizeof(abox_dump_simple.reason) - 1);
+       for (i = 0; i <= 14; i++)
+               abox_dump_simple.gpr[i] = readl(data->sfr_base + ABOX_CPU_R(i));
+       abox_dump_simple.gpr[i++] = readl(data->sfr_base + ABOX_CPU_PC);
+       abox_dump_simple.gpr[i++] = readl(data->sfr_base + ABOX_CPU_L2C_STATUS);
+       memcpy_fromio(abox_dump_simple.sram, data->sram_base, data->sram_size);
+       if (data->ima_claimed) {
+               memcpy_fromio(abox_dump_simple.iva, data->ima_vaddr,
+                               sizeof(abox_dump_simple.iva));
+       } else {
+               memcpy(abox_dump_simple.iva, data->iva_base,
+                               sizeof(abox_dump_simple.iva));
+       }
+       memcpy_fromio(abox_dump_simple.sfr, data->sfr_base,
+                       sizeof(abox_dump_simple.sfr));
+       memcpy_fromio(abox_dump_simple.sfr_gic_gicd, gic_data->gicd_base,
+                       sizeof(abox_dump_simple.sfr_gic_gicd));
+}
+
+static atomic_t abox_error_count = ATOMIC_INIT(0);
+
+void abox_dbg_report_status(struct device *dev, bool ok)
+{
+       char env[32] = {0,};
+       char *envp[2] = {env, NULL};
+
+       dev_info(dev, "%s\n", __func__);
+
+       if (ok)
+               atomic_set(&abox_error_count, 0);
+       else
+               atomic_inc(&abox_error_count);
+
+       snprintf(env, sizeof(env), "ERR_CNT=%d",
+                       atomic_read(&abox_error_count));
+       kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+}
+
+int abox_dbg_get_error_count(struct device *dev)
+{
+       int count = atomic_read(&abox_error_count);
+
+       dev_dbg(dev, "%s: %d\n", __func__, count);
+
+       return count;
+}
+
+static ssize_t calliope_sram_read(struct file *file, struct kobject *kobj,
+               struct bin_attribute *battr, char *buf,
+               loff_t off, size_t size)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct device *dev_abox = dev->parent;
+
+       dev_dbg(dev, "%s(%lld, %zu)\n", __func__, off, size);
+
+       if (pm_runtime_get_if_in_use(dev_abox) > 0) {
+               memcpy_fromio(buf, battr->private + off, size);
+               pm_runtime_put(dev_abox);
+       } else {
+               memset(buf, 0x0, size);
+       }
+
+       return size;
+}
+
+static ssize_t calliope_iva_read(struct file *file, struct kobject *kobj,
+               struct bin_attribute *battr, char *buf,
+               loff_t off, size_t size)
+{
+       struct device *dev = kobj_to_dev(kobj);
+
+       dev_dbg(dev, "%s(%lld, %zu)\n", __func__, off, size);
+
+       memcpy(buf, battr->private + off, size);
+       return size;
+}
+
+static ssize_t calliope_dram_read(struct file *file, struct kobject *kobj,
+               struct bin_attribute *battr, char *buf,
+               loff_t off, size_t size)
+{
+       struct device *dev = kobj_to_dev(kobj);
+
+       dev_dbg(dev, "%s(%lld, %zu)\n", __func__, off, size);
+
+       memcpy(buf, battr->private + off, size);
+       return size;
+}
+
+/* size will be updated later */
+static BIN_ATTR_RO(calliope_sram, 0);
+static BIN_ATTR_RO(calliope_iva, IVA_FIRMWARE_SIZE);
+static BIN_ATTR_RO(calliope_dram, DRAM_FIRMWARE_SIZE);
+static struct bin_attribute *calliope_bin_attrs[] = {
+       &bin_attr_calliope_sram,
+       &bin_attr_calliope_iva,
+       &bin_attr_calliope_dram,
+};
+
+static ssize_t gpr_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct abox_data *data = dev_get_drvdata(dev->parent);
+       char version[4];
+       char *pbuf = buf;
+       int i;
+
+       if (!abox_is_on()) {
+               dev_info(dev, "%s is skipped due to no power\n", __func__);
+               return -EFAULT;
+       }
+
+       memcpy(version, &data->calliope_version, sizeof(version));
+
+       pbuf += sprintf(pbuf, "========================================\n");
+       pbuf += sprintf(pbuf, "A-Box CPU register dump (%c%c%c%c)\n",
+                       version[3], version[2], version[1], version[0]);
+       pbuf += sprintf(pbuf, "----------------------------------------\n");
+       for (i = 0; i <= 14; i++) {
+               pbuf += sprintf(pbuf, "CA7_R%02d        : %08x\n", i,
+                               readl(data->sfr_base + ABOX_CPU_R(i)));
+       }
+       pbuf += sprintf(pbuf, "CA7_PC         : %08x\n",
+                       readl(data->sfr_base + ABOX_CPU_PC));
+       pbuf += sprintf(pbuf, "CA7_L2C_STATUS : %08x\n",
+                       readl(data->sfr_base + ABOX_CPU_L2C_STATUS));
+       pbuf += sprintf(pbuf, "========================================\n");
+
+       return pbuf - buf;
+}
+
+static DEVICE_ATTR_RO(gpr);
+
+static int samsung_abox_debug_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device *abox_dev = dev->parent;
+       struct abox_data *data = dev_get_drvdata(abox_dev);
+       int i, ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (abox_rmem == NULL)
+               return -ENOMEM;
+
+       dev_info(dev, "%s(%pa) is mapped on %p with size of %pa\n",
+                       "dump buffer", &abox_rmem->base,
+                       phys_to_virt(abox_rmem->base), &abox_rmem->size);
+       iommu_map(data->iommu_domain, IOVA_DUMP_BUFFER, abox_rmem->base,
+                       abox_rmem->size, 0);
+       data->dump_base = phys_to_virt(abox_rmem->base);
+       data->dump_base_phys = abox_rmem->base;
+       ret = device_create_file(dev, &dev_attr_gpr);
+       bin_attr_calliope_sram.size = data->sram_size;
+       bin_attr_calliope_sram.private = data->sram_base;
+       bin_attr_calliope_iva.private = data->iva_base;
+       bin_attr_calliope_dram.private = data->dram_base;
+       for (i = 0; i < ARRAY_SIZE(calliope_bin_attrs); i++) {
+               struct bin_attribute *battr = calliope_bin_attrs[i];
+
+               ret = device_create_bin_file(dev, battr);
+               if (ret < 0)
+                       dev_warn(dev, "Failed to create file: %s\n",
+                                       battr->attr.name);
+       }
+
+       return ret;
+}
+
+static int samsung_abox_debug_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       return 0;
+}
+
+static const struct of_device_id samsung_abox_debug_match[] = {
+       {
+               .compatible = "samsung,abox-debug",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_abox_debug_match);
+
+static struct platform_driver samsung_abox_debug_driver = {
+       .probe  = samsung_abox_debug_probe,
+       .remove = samsung_abox_debug_remove,
+       .driver = {
+               .name = "samsung-abox-debug",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_abox_debug_match),
+       },
+};
+
+module_platform_driver(samsung_abox_debug_driver);
+
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box Debug Driver");
+MODULE_ALIAS("platform:samsung-abox-debug");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox_dbg.h b/sound/soc/samsung/abox/abox_dbg.h
new file mode 100644 (file)
index 0000000..152f1c4
--- /dev/null
@@ -0,0 +1,102 @@
+/* sound/soc/samsung/abox/abox_dbg.h
+ *
+ * ALSA SoC - Samsung Abox Debug driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_DEBUG_H
+#define __SND_SOC_ABOX_DEBUG_H
+
+#include "abox.h"
+
+enum abox_dbg_dump_src {
+       ABOX_DBG_DUMP_KERNEL,
+       ABOX_DBG_DUMP_FIRMWARE,
+       ABOX_DBG_DUMP_VSS,
+       ABOX_DBG_DUMP_COUNT,
+};
+
+/**
+ * Initialize abox debug driver
+ * @return     dentry of abox debugfs root directory
+ */
+extern struct dentry *abox_dbg_get_root_dir(void);
+
+/**
+ * print gpr into the kmsg from memory
+ * @param[in]  dev             pointer to device which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  addr            pointer to gpr dump
+ */
+extern void abox_dbg_print_gpr_from_addr(struct device *dev,
+               struct abox_data *data, unsigned int *addr);
+
+/**
+ * print gpr into the kmsg
+ * @param[in]  dev             pointer to device which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ */
+extern void abox_dbg_print_gpr(struct device *dev, struct abox_data *data);
+
+/**
+ * dump gpr from memory
+ * @param[in]  dev             pointer to device which invokes this API
+ * @param[in]  addr            pointer to gpr dump
+ * @param[in]  src             source of the dump request
+ * @param[in]  reason          reason description
+ */
+extern void abox_dbg_dump_gpr_from_addr(struct device *dev, unsigned int *addr,
+               enum abox_dbg_dump_src src, const char *reason);
+
+/**
+ * dump gpr
+ * @param[in]  dev             pointer to device which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  src             source of the dump request
+ * @param[in]  reason          reason description
+ */
+extern void abox_dbg_dump_gpr(struct device *dev, struct abox_data *data,
+               enum abox_dbg_dump_src src, const char *reason);
+
+/**
+ * dump memory
+ * @param[in]  dev             pointer to device which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  src             source of the dump request
+ * @param[in]  reason          reason description
+ */
+extern void abox_dbg_dump_mem(struct device *dev, struct abox_data *data,
+               enum abox_dbg_dump_src src, const char *reason);
+
+/**
+ * dump gpr and memory
+ * @param[in]  dev             pointer to device which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  src             source of the dump request
+ * @param[in]  reason          reason description
+ */
+extern void abox_dbg_dump_gpr_mem(struct device *dev, struct abox_data *data,
+               enum abox_dbg_dump_src src, const char *reason);
+
+/**
+ * dump gpr and memory except DRAM
+ * @param[in]  dev             pointer to device which invokes this API
+ * @param[in]  data            pointer to abox_data structure
+ * @param[in]  reason          reason description
+ */
+extern void abox_dbg_dump_simple(struct device *dev, struct abox_data *data,
+               const char *reason);
+
+/**
+ * Push status of the abox
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  ok              true for okay, false on otherwise
+ */
+extern void abox_dbg_report_status(struct device *dev, bool ok);
+
+#endif /* __SND_SOC_ABOX_DEBUG_H */
diff --git a/sound/soc/samsung/abox/abox_dump.c b/sound/soc/samsung/abox/abox_dump.c
new file mode 100644 (file)
index 0000000..8513ad3
--- /dev/null
@@ -0,0 +1,674 @@
+/* sound/soc/samsung/abox/abox_dump.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox Internal Buffer Dumping driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/spinlock.h>
+#include <linux/vmalloc.h>
+#include <linux/pm_runtime.h>
+#include <sound/samsung/abox.h>
+
+#include "abox_util.h"
+#include "abox.h"
+#include "abox_dbg.h"
+#include "abox_log.h"
+
+#define BUFFER_MAX (SZ_32)
+#define NAME_LENGTH (SZ_32)
+
+struct abox_dump_buffer_info {
+       struct device *dev;
+       struct list_head list;
+       int id;
+       char name[NAME_LENGTH];
+       struct mutex lock;
+       struct snd_dma_buffer buffer;
+       struct snd_pcm_substream *substream;
+       size_t pointer;
+       bool started;
+       bool auto_started;
+       bool file_created;
+       struct file *filp;
+       ssize_t auto_pointer;
+       struct work_struct auto_work;
+};
+
+static struct device *abox_dump_dev_abox;
+static struct abox_dump_buffer_info abox_dump_list[BUFFER_MAX];
+static LIST_HEAD(abox_dump_list_head);
+
+static struct abox_dump_buffer_info *abox_dump_get_buffer_info(int id)
+{
+       struct abox_dump_buffer_info *info;
+
+       list_for_each_entry(info, &abox_dump_list_head, list) {
+               if (info->id == id)
+                       return info;
+       }
+
+       return NULL;
+}
+
+static struct abox_dump_buffer_info *abox_dump_get_buffer_info_by_name(
+               const char *name)
+{
+       struct abox_dump_buffer_info *info;
+
+       list_for_each_entry(info, &abox_dump_list_head, list) {
+               if (strncmp(info->name, name, sizeof(info->name)) == 0)
+                       return info;
+       }
+
+       return NULL;
+}
+
+static void abox_dump_request_dump(int id)
+{
+       struct abox_dump_buffer_info *info = abox_dump_get_buffer_info(id);
+       ABOX_IPC_MSG msg;
+       struct IPC_SYSTEM_MSG *system = &msg.msg.system;
+       bool start = info->started || info->auto_started;
+
+       dev_dbg(abox_dump_dev_abox, "%s(%d)\n", __func__, id);
+
+       msg.ipcid = IPC_SYSTEM;
+       system->msgtype = ABOX_REQUEST_DUMP;
+       system->param1 = id;
+       system->param2 = start ? 1 : 0;
+       abox_request_ipc(abox_dump_dev_abox, msg.ipcid, &msg, sizeof(msg),
+                       0, 0);
+}
+
+static ssize_t abox_dump_auto_read(struct file *file, char __user *data,
+               size_t count, loff_t *ppos, bool enable)
+{
+       struct abox_dump_buffer_info *info;
+       char buffer[SZ_256] = {0,}, *buffer_p = buffer;
+
+       dev_dbg(abox_dump_dev_abox, "%s(%zu, %lld, %d)\n", __func__, count,
+                       *ppos, enable);
+
+       list_for_each_entry(info, &abox_dump_list_head, list) {
+               if (info->auto_started == enable) {
+                       buffer_p += snprintf(buffer_p, sizeof(buffer) -
+                                       (buffer_p - buffer),
+                                       "%d(%s) ", info->id, info->name);
+               }
+       }
+       snprintf(buffer_p, 2, "\n");
+
+       return simple_read_from_buffer(data, count, ppos, buffer,
+                       buffer_p - buffer);
+}
+
+static ssize_t abox_dump_auto_write(struct file *file, const char __user *data,
+               size_t count, loff_t *ppos, bool enable)
+{
+       char buffer[SZ_256] = {0,}, name[NAME_LENGTH];
+       char *p_buffer = buffer, *token = NULL;
+       unsigned int id;
+       struct abox_dump_buffer_info *info;
+       ssize_t ret;
+
+       dev_dbg(abox_dump_dev_abox, "%s(%zu, %lld, %d)\n", __func__, count,
+                       *ppos, enable);
+
+       ret = simple_write_to_buffer(buffer, sizeof(buffer), ppos, data, count);
+       if (ret < 0)
+               return ret;
+
+       while ((token = strsep(&p_buffer, " ")) != NULL) {
+               if (sscanf(token, "%11u", &id) == 1)
+                       info = abox_dump_get_buffer_info(id);
+               else if (sscanf(token, "%31s", name) == 1)
+                       info = abox_dump_get_buffer_info_by_name(name);
+               else
+                       info = NULL;
+
+               if (IS_ERR_OR_NULL(info)) {
+                       dev_err(abox_dump_dev_abox, "invalid argument\n");
+                       continue;
+               }
+
+               if (info->auto_started != enable) {
+                       struct device *dev = info->dev;
+                       struct platform_device *pdev_abox;
+
+                       pdev_abox = to_platform_device(abox_dump_dev_abox);
+                       if (enable) {
+                               abox_request_dram_on(pdev_abox, dev, true);
+                               pm_runtime_get(dev);
+                       } else {
+                               pm_runtime_put(dev);
+                               abox_request_dram_on(pdev_abox, dev, false);
+                       }
+               }
+
+               info->auto_started = enable;
+               if (enable) {
+                       info->file_created = false;
+                       info->auto_pointer = -1;
+               }
+
+               abox_dump_request_dump(info->id);
+       }
+
+       return count;
+}
+
+static ssize_t abox_dump_auto_start_read(struct file *file,
+               char __user *data, size_t count, loff_t *ppos)
+{
+       return abox_dump_auto_read(file, data, count, ppos, true);
+}
+
+static ssize_t abox_dump_auto_start_write(struct file *file,
+               const char __user *data, size_t count, loff_t *ppos)
+{
+       return abox_dump_auto_write(file, data, count, ppos, true);
+}
+
+static ssize_t abox_dump_auto_stop_read(struct file *file,
+               char __user *data, size_t count, loff_t *ppos)
+{
+       return abox_dump_auto_read(file, data, count, ppos, false);
+}
+
+static ssize_t abox_dump_auto_stop_write(struct file *file,
+               const char __user *data, size_t count, loff_t *ppos)
+{
+       return abox_dump_auto_write(file, data, count, ppos, false);
+}
+
+static const struct file_operations abox_dump_auto_start_fops = {
+       .read = abox_dump_auto_start_read,
+       .write = abox_dump_auto_start_write,
+};
+
+static const struct file_operations abox_dump_auto_stop_fops = {
+       .read = abox_dump_auto_stop_read,
+       .write = abox_dump_auto_stop_write,
+};
+
+static int __init samsung_abox_dump_late_initcall(void)
+{
+       pr_info("%s\n", __func__);
+
+       debugfs_create_file("dump_auto_start", 0660, abox_dbg_get_root_dir(),
+                       NULL, &abox_dump_auto_start_fops);
+       debugfs_create_file("dump_auto_stop", 0660, abox_dbg_get_root_dir(),
+                       NULL, &abox_dump_auto_stop_fops);
+
+       return 0;
+}
+late_initcall(samsung_abox_dump_late_initcall);
+
+static struct snd_soc_dai_link abox_dump_dai_links[BUFFER_MAX];
+
+static struct snd_soc_card abox_dump_card = {
+       .name = "abox_dump",
+       .owner = THIS_MODULE,
+       .dai_link = abox_dump_dai_links,
+       .num_links = 0,
+};
+
+static void abox_dump_auto_dump_work_func(struct work_struct *work)
+{
+       struct abox_dump_buffer_info *info = container_of(work,
+                       struct abox_dump_buffer_info, auto_work);
+       struct device *dev = info->dev;
+       int id = info->id;
+
+       if (info->auto_started) {
+               mm_segment_t old_fs;
+               char filename[SZ_64];
+               struct file *filp;
+
+               sprintf(filename, "/data/abox_dump-%d.raw", id);
+
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               if (likely(info->file_created)) {
+                       filp = filp_open(filename, O_RDWR | O_APPEND | O_CREAT,
+                                       0660);
+                       dev_dbg(dev, "appended\n");
+               } else {
+                       filp = filp_open(filename, O_RDWR | O_TRUNC | O_CREAT,
+                                       0660);
+                       info->file_created = true;
+                       dev_dbg(dev, "created\n");
+               }
+               if (!IS_ERR_OR_NULL(filp)) {
+                       void *area = info->buffer.area;
+                       size_t bytes = info->buffer.bytes;
+                       size_t pointer = info->pointer;
+                       bool first = false;
+
+                       if (unlikely(info->auto_pointer < 0)) {
+                               info->auto_pointer = pointer;
+                               first = true;
+                       }
+                       dev_dbg(dev, "%pad, %p, %zx, %zx)\n",
+                                       &info->buffer.addr, area, bytes,
+                                       info->auto_pointer);
+                       if (pointer < info->auto_pointer || first) {
+                               vfs_write(filp, area + info->auto_pointer,
+                                               bytes - info->auto_pointer,
+                                               &filp->f_pos);
+                               dev_dbg(dev, "vfs_write(%p, %zx)\n",
+                                               area + info->auto_pointer,
+                                               bytes - info->auto_pointer);
+                               info->auto_pointer = 0;
+                       }
+                       vfs_write(filp, area + info->auto_pointer,
+                                       pointer - info->auto_pointer,
+                                       &filp->f_pos);
+                       dev_dbg(dev, "vfs_write(%p, %zx)\n",
+                                       area + info->auto_pointer,
+                                       pointer - info->auto_pointer);
+                       info->auto_pointer = pointer;
+
+                       vfs_fsync(filp, 1);
+                       filp_close(filp, NULL);
+               } else {
+                       dev_err(dev, "dump file %d open error: %ld\n", id,
+                                       PTR_ERR(filp));
+               }
+
+               set_fs(old_fs);
+       }
+}
+
+void abox_dump_register_buffer_work_func(struct work_struct *work)
+{
+       int id;
+       struct abox_dump_buffer_info *info;
+
+       if (!abox_dump_card.dev) {
+               platform_device_register_data(abox_dump_dev_abox,
+                               "samsung-abox-dump", -1, NULL, 0);
+       }
+
+       dev_dbg(abox_dump_card.dev, "%s\n", __func__);
+
+       for (info = &abox_dump_list[0]; (info - &abox_dump_list[0]) <
+                       ARRAY_SIZE(abox_dump_list); info++) {
+               id = info->id;
+               if (info->dev && !abox_dump_get_buffer_info(id)) {
+                       dev_info(info->dev, "%s[%d]\n", __func__, id);
+                       list_add_tail(&info->list, &abox_dump_list_head);
+                       platform_device_register_data(info->dev,
+                                       "samsung-abox-dump", id, NULL, 0);
+               }
+       }
+}
+
+static DECLARE_WORK(abox_dump_register_buffer_work,
+               abox_dump_register_buffer_work_func);
+
+int abox_dump_register_buffer(struct device *dev, int id, const char *name,
+               void *area, phys_addr_t addr, size_t bytes)
+{
+       struct abox_dump_buffer_info *info;
+
+       dev_dbg(dev, "%s[%d] %p(%pa)\n", __func__, id, area, &addr);
+
+       if (id < 0 || id >= ARRAY_SIZE(abox_dump_list)) {
+               dev_err(dev, "invalid id: %d\n", id);
+               return -EINVAL;
+       }
+
+       if (abox_dump_get_buffer_info(id)) {
+               dev_dbg(dev, "already registered dump: %d\n", id);
+               return 0;
+       }
+
+       info = &abox_dump_list[id];
+       mutex_init(&info->lock);
+       info->id = id;
+       strncpy(info->name, name, sizeof(info->name) - 1);
+       info->buffer.area = area;
+       info->buffer.addr = addr;
+       info->buffer.bytes = bytes;
+       INIT_WORK(&info->auto_work, abox_dump_auto_dump_work_func);
+       abox_dump_dev_abox = info->dev = dev;
+       schedule_work(&abox_dump_register_buffer_work);
+
+       return 0;
+}
+EXPORT_SYMBOL(abox_dump_register_buffer);
+
+static struct snd_pcm_hardware abox_dump_hardware = {
+       .info           = SNDRV_PCM_INFO_INTERLEAVED
+                       | SNDRV_PCM_INFO_BLOCK_TRANSFER
+                       | SNDRV_PCM_INFO_MMAP
+                       | SNDRV_PCM_INFO_MMAP_VALID,
+       .formats        = ABOX_SAMPLE_FORMATS,
+       .rates          = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT,
+       .rate_min       = 8000,
+       .rate_max       = 384000,
+       .channels_min   = 1,
+       .channels_max   = 8,
+       .periods_min    = 2,
+       .periods_max    = 32,
+};
+
+static int abox_dump_platform_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_dump_buffer_info *info = abox_dump_get_buffer_info(id);
+       struct snd_dma_buffer *dmab = &substream->dma_buffer;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       abox_dump_hardware.buffer_bytes_max = dmab->bytes;
+       abox_dump_hardware.period_bytes_min = dmab->bytes /
+                       abox_dump_hardware.periods_max;
+       abox_dump_hardware.period_bytes_max = dmab->bytes /
+                       abox_dump_hardware.periods_min;
+
+       snd_soc_set_runtime_hwparams(substream, &abox_dump_hardware);
+
+       info->substream = substream;
+
+       return 0;
+}
+
+static int abox_dump_platform_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_dump_buffer_info *info = abox_dump_get_buffer_info(id);
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       info->substream = NULL;
+
+       return 0;
+}
+
+static int abox_dump_platform_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_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+}
+
+static int abox_dump_platform_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int abox_dump_platform_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_dump_buffer_info *info = abox_dump_get_buffer_info(id);
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       info->pointer = 0;
+
+       return 0;
+}
+
+static int abox_dump_platform_trigger(struct snd_pcm_substream *substream,
+               int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_dump_buffer_info *info = abox_dump_get_buffer_info(id);
+
+       dev_dbg(dev, "%s[%d](%d)\n", __func__, id, cmd);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               info->started = true;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               info->started = false;
+               break;
+       default:
+               dev_err(dev, "invalid command: %d\n", cmd);
+               return -EINVAL;
+       }
+
+       abox_dump_request_dump(id);
+
+       return 0;
+}
+
+void abox_dump_period_elapsed(int id, size_t pointer)
+{
+       struct abox_dump_buffer_info *info = abox_dump_get_buffer_info(id);
+       struct device *dev = info->dev;
+
+       dev_dbg(dev, "%s[%d](%zx)\n", __func__, id, pointer);
+
+       info->pointer = pointer;
+       schedule_work(&info->auto_work);
+       snd_pcm_period_elapsed(info->substream);
+}
+EXPORT_SYMBOL(abox_dump_period_elapsed);
+
+static snd_pcm_uframes_t abox_dump_platform_pointer(
+               struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_dump_buffer_info *info = abox_dump_get_buffer_info(id);
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       return bytes_to_frames(substream->runtime, info->pointer);
+}
+
+static struct snd_pcm_ops abox_dump_platform_ops = {
+       .open           = abox_dump_platform_open,
+       .close          = abox_dump_platform_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = abox_dump_platform_hw_params,
+       .hw_free        = abox_dump_platform_hw_free,
+       .prepare        = abox_dump_platform_prepare,
+       .trigger        = abox_dump_platform_trigger,
+       .pointer        = abox_dump_platform_pointer,
+};
+
+static void abox_dump_register_card_work_func(struct work_struct *work)
+{
+       int i;
+
+       pr_debug("%s\n", __func__);
+
+       snd_soc_unregister_card(&abox_dump_card);
+       for (i = 0; i < abox_dump_card.num_links; i++) {
+               struct snd_soc_dai_link *link = &abox_dump_card.dai_link[i];
+
+               if (link->name)
+                       continue;
+
+               link->name = link->stream_name =
+                               kasprintf(GFP_KERNEL, "dummy%d", i);
+               link->cpu_name = "snd-soc-dummy";
+               link->cpu_dai_name = "snd-soc-dummy-dai";
+               link->codec_name = "snd-soc-dummy";
+               link->codec_dai_name = "snd-soc-dummy-dai";
+               link->no_pcm = 1;
+       }
+       snd_soc_register_card(&abox_dump_card);
+}
+
+DECLARE_DELAYED_WORK(abox_dump_register_card_work,
+               abox_dump_register_card_work_func);
+
+static void abox_dump_add_dai_link(struct device *dev)
+{
+       int id = to_platform_device(dev)->id;
+       struct abox_dump_buffer_info *info = abox_dump_get_buffer_info(id);
+       struct snd_soc_dai_link *link = &abox_dump_dai_links[id];
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       if (id > ARRAY_SIZE(abox_dump_dai_links)) {
+               dev_err(dev, "Too many dump request\n");
+               return;
+       }
+
+       cancel_delayed_work_sync(&abox_dump_register_card_work);
+       info->dev = dev;
+       kfree(link->name);
+       link->name = link->stream_name = kstrdup(info->name, GFP_KERNEL);
+       link->cpu_name = "snd-soc-dummy";
+       link->cpu_dai_name = "snd-soc-dummy-dai";
+       link->platform_name = dev_name(dev);
+       link->codec_name = "snd-soc-dummy";
+       link->codec_dai_name = "snd-soc-dummy-dai";
+       link->ignore_suspend = 1;
+       link->ignore_pmdown_time = 1;
+       link->no_pcm = 0;
+       link->capture_only = true;
+
+       if (abox_dump_card.num_links <= id)
+               abox_dump_card.num_links = id + 1;
+
+       schedule_delayed_work(&abox_dump_register_card_work, HZ);
+}
+
+static int abox_dump_platform_probe(struct snd_soc_platform *platform)
+{
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       return 0;
+}
+
+static int abox_dump_platform_new(struct snd_soc_pcm_runtime *runtime)
+{
+       struct device *dev = runtime->platform->dev;
+       struct snd_pcm *pcm = runtime->pcm;
+       struct snd_pcm_str *stream = &pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
+       struct snd_pcm_substream *substream = stream->substream;
+       struct snd_dma_buffer *dmab = &substream->dma_buffer;
+       int id = to_platform_device(dev)->id;
+       struct abox_dump_buffer_info *info = abox_dump_get_buffer_info(id);
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       dmab->dev.type = SNDRV_DMA_TYPE_DEV;
+       dmab->dev.dev = dev;
+       dmab->area = info->buffer.area;
+       dmab->addr = info->buffer.addr;
+       dmab->bytes = info->buffer.bytes;
+
+       return 0;
+}
+
+static void abox_dump_platform_free(struct snd_pcm *pcm)
+{
+       struct snd_soc_pcm_runtime *runtime = pcm->private_data;
+       struct device *dev = runtime->platform->dev;
+       int id = to_platform_device(dev)->id;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+}
+
+struct snd_soc_platform_driver abox_dump_platform = {
+       .probe          = abox_dump_platform_probe,
+       .ops            = &abox_dump_platform_ops,
+       .pcm_new        = abox_dump_platform_new,
+       .pcm_free       = abox_dump_platform_free,
+};
+
+static int samsung_abox_dump_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int id = to_platform_device(dev)->id;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       if (id >= 0) {
+               pm_runtime_no_callbacks(dev);
+               pm_runtime_enable(dev);
+
+               devm_snd_soc_register_platform(dev, &abox_dump_platform);
+               abox_dump_add_dai_link(dev);
+       } else {
+               abox_dump_card.dev = &pdev->dev;
+       }
+
+       return 0;
+}
+
+static int samsung_abox_dump_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int id = to_platform_device(dev)->id;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       return 0;
+}
+
+static const struct platform_device_id samsung_abox_dump_driver_ids[] = {
+       {
+               .name = "samsung-abox-dump",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(platform, samsung_abox_dump_driver_ids);
+
+static struct platform_driver samsung_abox_dump_driver = {
+       .probe  = samsung_abox_dump_probe,
+       .remove = samsung_abox_dump_remove,
+       .driver = {
+               .name = "samsung-abox-dump",
+               .owner = THIS_MODULE,
+       },
+       .id_table = samsung_abox_dump_driver_ids,
+};
+
+module_platform_driver(samsung_abox_dump_driver);
+
+/* Module information */
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box Internal Buffer Dumping Driver");
+MODULE_ALIAS("platform:samsung-abox-dump");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox_dump.h b/sound/soc/samsung/abox/abox_dump.h
new file mode 100644 (file)
index 0000000..08346b5
--- /dev/null
@@ -0,0 +1,38 @@
+/* sound/soc/samsung/abox/abox_dump.h
+ *
+ * ALSA SoC Audio Layer - Samsung Abox Internal Buffer Dumping driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_DUMP_H
+#define __SND_SOC_ABOX_DUMP_H
+
+#include <linux/device.h>
+#include <sound/samsung/abox.h>
+
+/**
+ * Report dump data written
+ * @param[in]  id              unique buffer id
+ * @param[in]  pointer         byte index of the written data
+ */
+extern void abox_dump_period_elapsed(int id, size_t pointer);
+
+/**
+ * Register abox dump buffer
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  id              unique buffer id
+ * @param[in]  name            unique buffer name
+ * @param[in]  area            virtual address of the buffer
+ * @param[in]  addr            pysical address of the buffer
+ * @param[in]  bytes           buffer size in bytes
+ * @return     error code if any
+ */
+extern int abox_dump_register_buffer(struct device *dev, int id,
+               const char *name, void *area, phys_addr_t addr, size_t bytes);
+
+#endif /* __SND_SOC_ABOX_DUMP_H */
diff --git a/sound/soc/samsung/abox/abox_effect.c b/sound/soc/samsung/abox/abox_effect.c
new file mode 100644 (file)
index 0000000..09c0826
--- /dev/null
@@ -0,0 +1,321 @@
+/* sound/soc/samsung/abox/abox_effect.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox Effect driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/samsung/abox.h>
+
+#include "abox.h"
+#include "abox_util.h"
+#include "abox_effect.h"
+
+struct abox_ctl_eq_switch {
+       unsigned int base;
+       unsigned int count;
+       unsigned int min;
+       unsigned int max;
+};
+
+static int abox_ctl_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_ctl_eq_switch *params = (void *)kcontrol->private_value;
+
+       dev_dbg(dev, "%s: %s\n", __func__, kcontrol->id.name);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = params->count;
+       uinfo->value.integer.min = params->min;
+       uinfo->value.integer.max = params->max;
+       return 0;
+}
+
+static int abox_ctl_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_ctl_eq_switch *params = (void *)kcontrol->private_value;
+       int i;
+       int ret = 0;
+
+       dev_dbg(dev, "%s: %s\n", __func__, kcontrol->id.name);
+
+       pm_runtime_get_sync(dev);
+       for (i = 0; i < params->count; i++) {
+               unsigned int reg, val;
+
+               reg = (unsigned int)(params->base + PARAM_OFFSET +
+                               (i * sizeof(u32)));
+               ret = snd_soc_component_read(cmpnt, reg, &val);
+               if (ret < 0) {
+                       dev_err(dev, "reading fail at 0x%08X\n", reg);
+                       break;
+               }
+               ucontrol->value.integer.value[i] = val;
+               dev_dbg(dev, "%s[%d] = %u\n", kcontrol->id.name, i, val);
+       }
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       return ret;
+}
+
+static int abox_ctl_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_ctl_eq_switch *params = (void *)kcontrol->private_value;
+       int i;
+
+       dev_dbg(dev, "%s: %s\n", __func__, kcontrol->id.name);
+
+       pm_runtime_get_sync(dev);
+       for (i = 0; i < params->count; i++) {
+               unsigned int reg, val;
+
+               reg = (unsigned int)(params->base + PARAM_OFFSET +
+                               (i * sizeof(u32)));
+               val = (unsigned int)ucontrol->value.integer.value[i];
+               snd_soc_component_write(cmpnt, reg, val);
+               dev_dbg(dev, "%s[%d] <= %u\n", kcontrol->id.name, i, val);
+       }
+       snd_soc_component_write(cmpnt, params->base, CHANGE_BIT);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       return 0;
+}
+
+#define ABOX_CTL_EQ_SWITCH(xname, xbase, xcount, xmin, xmax)   \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),   \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,              \
+       .info = abox_ctl_info, .get = abox_ctl_get,             \
+       .put = abox_ctl_put, .private_value =                   \
+               ((unsigned long)&(struct abox_ctl_eq_switch)    \
+               {.base = xbase, .count = xcount,                \
+               .min = xmin, .max = xmax}) }
+
+#define DECLARE_ABOX_CTL_EQ_SWITCH(name, prefix)       \
+               ABOX_CTL_EQ_SWITCH(name, prefix##_BASE, \
+               prefix##_MAX_COUNT, prefix##_VALUE_MIN, \
+               prefix##_VALUE_MAX)
+
+static const struct snd_kcontrol_new abox_effect_controls[] = {
+       DECLARE_ABOX_CTL_EQ_SWITCH("SA data", SA),
+       DECLARE_ABOX_CTL_EQ_SWITCH("Audio DHA data", MYSOUND),
+       DECLARE_ABOX_CTL_EQ_SWITCH("VSP data", VSP),
+       DECLARE_ABOX_CTL_EQ_SWITCH("LRSM data", LRSM),
+       DECLARE_ABOX_CTL_EQ_SWITCH("MSP data", MYSPACE),
+       DECLARE_ABOX_CTL_EQ_SWITCH("ESA BBoost data", BB),
+       DECLARE_ABOX_CTL_EQ_SWITCH("ESA EQ data", EQ),
+       DECLARE_ABOX_CTL_EQ_SWITCH("NXP BDL data", NXPBDL),
+       DECLARE_ABOX_CTL_EQ_SWITCH("NXP RVB ctx data", NXPRVB_CTX),
+       DECLARE_ABOX_CTL_EQ_SWITCH("NXP RVB param data", NXPRVB_PARAM),
+       DECLARE_ABOX_CTL_EQ_SWITCH("SB rotation", SB),
+       DECLARE_ABOX_CTL_EQ_SWITCH("UPSCALER", UPSCALER),
+};
+
+#define ABOX_EFFECT_ACCESSIABLE_REG(name, reg) \
+               (reg >= name##_BASE && reg <= name##_BASE + PARAM_OFFSET + \
+               (name##_MAX_COUNT * sizeof(u32)))
+
+static bool abox_effect_accessible_reg(struct device *dev, unsigned int reg)
+{
+       return ABOX_EFFECT_ACCESSIABLE_REG(SA, reg)                     ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(MYSOUND, reg)       ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(VSP, reg)           ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(LRSM, reg)          ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(MYSPACE, reg)       ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(BB, reg)            ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(EQ, reg)            ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(ELPE, reg)          ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(NXPBDL, reg)        ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(NXPRVB_CTX, reg)    ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(NXPRVB_PARAM, reg)  ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(SB, reg)            ||
+                       ABOX_EFFECT_ACCESSIABLE_REG(UPSCALER, reg);
+}
+
+#define ABOX_EFFECT_VOLATILE_REG(name, reg) (reg == name##_BASE)
+
+static bool abox_effect_volatile_reg(struct device *dev, unsigned int reg)
+{
+       return ABOX_EFFECT_VOLATILE_REG(SA, reg)                        ||
+                       ABOX_EFFECT_VOLATILE_REG(MYSOUND, reg)          ||
+                       ABOX_EFFECT_VOLATILE_REG(VSP, reg)              ||
+                       ABOX_EFFECT_VOLATILE_REG(LRSM, reg)             ||
+                       ABOX_EFFECT_VOLATILE_REG(MYSPACE, reg)          ||
+                       ABOX_EFFECT_VOLATILE_REG(BB, reg)               ||
+                       ABOX_EFFECT_VOLATILE_REG(EQ, reg)               ||
+                       ABOX_EFFECT_VOLATILE_REG(ELPE, reg)             ||
+                       ABOX_EFFECT_VOLATILE_REG(NXPBDL, reg)           ||
+                       ABOX_EFFECT_VOLATILE_REG(NXPRVB_CTX, reg)       ||
+                       ABOX_EFFECT_VOLATILE_REG(NXPRVB_PARAM, reg)     ||
+                       ABOX_EFFECT_VOLATILE_REG(SB, reg)               ||
+                       ABOX_EFFECT_VOLATILE_REG(UPSCALER, reg);
+}
+
+static const struct regmap_config abox_effect_regmap_config = {
+       .reg_bits               = 32,
+       .val_bits               = 32,
+       .reg_stride             = 4,
+       .max_register           = ABOX_EFFECT_MAX_REGISTERS,
+       .writeable_reg          = abox_effect_accessible_reg,
+       .readable_reg           = abox_effect_accessible_reg,
+       .volatile_reg           = abox_effect_volatile_reg,
+       .cache_type             = REGCACHE_FLAT,
+};
+
+static const struct snd_soc_component_driver abox_effect = {
+       .controls               = abox_effect_controls,
+       .num_controls           = ARRAY_SIZE(abox_effect_controls),
+};
+
+static struct abox_effect_data *p_abox_effect_data;
+
+void abox_effect_restore(void)
+{
+       if (p_abox_effect_data && p_abox_effect_data->pdev) {
+               struct device *dev = &p_abox_effect_data->pdev->dev;
+
+               pm_runtime_get(dev);
+               pm_runtime_put_autosuspend(dev);
+       }
+}
+
+static int abox_effect_runtime_suspend(struct device *dev)
+{
+       struct abox_effect_data *data = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       regcache_cache_only(data->regmap, true);
+       regcache_mark_dirty(data->regmap);
+
+       return 0;
+}
+
+static int abox_effect_runtime_resume(struct device *dev)
+{
+       struct abox_effect_data *data = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       regcache_cache_only(data->regmap, false);
+       regcache_sync(data->regmap);
+
+       return 0;
+}
+
+const struct dev_pm_ops samsung_abox_effect_pm = {
+       SET_RUNTIME_PM_OPS(abox_effect_runtime_suspend,
+                       abox_effect_runtime_resume, NULL)
+};
+
+static int samsung_abox_effect_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *np_tmp;
+       struct abox_effect_data *data;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, data);
+       p_abox_effect_data = data;
+
+       np_tmp = of_parse_phandle(np, "abox", 0);
+       if (!np_tmp) {
+               dev_err(dev, "Failed to get abox device node\n");
+               return -EPROBE_DEFER;
+       }
+       data->pdev_abox = of_find_device_by_node(np_tmp);
+       if (!data->pdev_abox) {
+               dev_err(dev, "Failed to get abox platform device\n");
+               return -EPROBE_DEFER;
+       }
+
+       data->pdev = pdev;
+
+       data->base = devm_not_request_and_map(pdev, "reg", 0, NULL, NULL);
+       if (IS_ERR(data->base)) {
+               dev_err(dev, "base address request failed: %ld\n",
+                               PTR_ERR(data->base));
+               return PTR_ERR(data->base);
+       }
+
+       data->regmap = devm_regmap_init_mmio(dev, data->base,
+                       &abox_effect_regmap_config);
+       if (IS_ERR(data->regmap)) {
+               dev_err(dev, "regmap init failed: %ld\n",
+                               PTR_ERR(data->regmap));
+               return PTR_ERR(data->regmap);
+       }
+
+       pm_runtime_enable(dev);
+       pm_runtime_set_autosuspend_delay(dev, 100);
+       pm_runtime_use_autosuspend(dev);
+
+       return devm_snd_soc_register_component(&pdev->dev, &abox_effect, NULL,
+                       0);
+}
+
+static int samsung_abox_effect_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       pm_runtime_disable(dev);
+
+       return 0;
+}
+
+static const struct of_device_id samsung_abox_effect_match[] = {
+       {
+               .compatible = "samsung,abox-effect",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_abox_effect_match);
+
+static struct platform_driver samsung_abox_effect_driver = {
+       .probe  = samsung_abox_effect_probe,
+       .remove = samsung_abox_effect_remove,
+       .driver = {
+               .name = "samsung-abox-effect",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_abox_effect_match),
+               .pm = &samsung_abox_effect_pm,
+       },
+};
+
+module_platform_driver(samsung_abox_effect_driver);
+
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box Effect Driver");
+MODULE_ALIAS("platform:samsung-abox-effect");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox_effect.h b/sound/soc/samsung/abox/abox_effect.h
new file mode 100644 (file)
index 0000000..9c67e4a
--- /dev/null
@@ -0,0 +1,176 @@
+/* sound/soc/samsung/abox/abox_effect.h
+ *
+ * ALSA SoC Audio Layer - Samsung Abox Effect driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_EFFECT_H
+#define __SND_SOC_ABOX_EFFECT_H
+enum {
+       SOUNDALIVE = 0,
+       MYSOUND,
+       PLAYSPEED,
+       SOUNDBALANCE,
+       MYSPACE,
+       BASSBOOST,
+       EQUALIZER,
+       NXPBUNDLE, /* includes BB,EQ, virtualizer, volume */
+       NXPREVERB_CTX, /* Reverb context inforamtion */
+       NXPREVERB_PARAM, /* Reverb effect parameters */
+       SOUNDBOOSTER,
+};
+
+/* Effect offset */
+#define SA_BASE                        (0x000)
+#define SA_CHANGE_BIT          (0x000)
+#define SA_OUT_DEVICE          (0x010)
+#define        SA_PRESET               (0x014)
+#define SA_EQ_BEGIN            (0x018)
+#define SA_EQ_END              (0x038)
+#define SA_3D_LEVEL            (0x03C)
+#define SA_BE_LEVEL            (0x040)
+#define SA_REVERB              (0x044)
+#define SA_ROOMSIZE            (0x048)
+#define SA_CLA_LEVEL           (0x04C)
+#define SA_VOLUME_LEVEL                (0x050)
+#define SA_SQUARE_ROW          (0x054)
+#define SA_SQUARE_COLUMN       (0x058)
+#define SA_TAB_INFO            (0x05C)
+#define SA_NEW_UI              (0x060)
+#define SA_3D_ON               (0x064)
+#define SA_3D_ANGLE_L          (0x068)
+#define SA_3D_ANGLE_R          (0x06C)
+#define SA_3D_GAIN_L           (0x070)
+#define SA_3D_GAIN_R           (0x074)
+#define SA_MAX_COUNT           (26)
+#define SA_VALUE_MIN           (0)
+#define SA_VALUE_MAX           (100)
+
+#define MYSOUND_BASE           (0x100)
+#define MYSOUND_CHANGE_BIT     (0x100)
+#define MYSOUND_DHA_ENABLE     (0x110)
+#define MYSOUND_GAIN_BEGIN     (0x114)
+#define MYSOUND_OUT_DEVICE     (0x148)
+#define MYSOUND_MAX_COUNT      (14)
+#define MYSOUND_VALUE_MIN      (0)
+#define MYSOUND_VALUE_MAX      (100)
+
+#define VSP_BASE               (0x200)
+#define VSP_CHANGE_BIT         (0x200)
+#define VSP_INDEX              (0x210)
+#define VSP_MAX_COUNT          (2)
+#define VSP_VALUE_MIN          (0)
+#define VSP_VALUE_MAX          (1000)
+
+#define LRSM_BASE              (0x300)
+#define LRSM_CHANGE_BIT                (0x300)
+#define LRSM_INDEX0            (0x310)
+#define LRSM_INDEX1            (0x320)
+#define LRSM_MAX_COUNT         (3)
+#define LRSM_VALUE_MIN         (-50)
+#define LRSM_VALUE_MAX         (50)
+
+#define MYSPACE_BASE           (0x340)
+#define MYSPACE_CHANGE_BIT     (0x340)
+#define MYSPACE_PRESET         (0x350)
+#define MYSPACE_MAX_COUNT      (2)
+#define MYSPACE_VALUE_MIN      (0)
+#define MYSPACE_VALUE_MAX      (5)
+
+#define BB_BASE                        (0x400)
+#define BB_CHANGE_BIT          (0x400)
+#define BB_STATUS              (0x410)
+#define BB_STRENGTH            (0x414)
+#define BB_MAX_COUNT           (2)
+#define BB_VALUE_MIN           (0)
+#define BB_VALUE_MAX           (100)
+
+#define EQ_BASE                        (0x500)
+#define EQ_CHANGE_BIT          (0x500)
+#define EQ_STATUS              (0x510)
+#define EQ_PRESET              (0x514)
+#define EQ_NBAND               (0x518)
+#define EQ_NBAND_LEVEL         (0x51c) /* 10 nband levels */
+#define EQ_NBAND_FREQ          (0x544) /* 10 nband frequencies */
+#define EQ_MAX_COUNT           (23)
+#define EQ_VALUE_MIN           (0)
+#define EQ_VALUE_MAX           (14000)
+
+/* CommBox ELPE Parameter */
+#define ELPE_BASE              (0x600)
+#define ELPE_CMD               (0x600)
+#define ELPE_ARG0              (0x604)
+#define ELPE_ARG1              (0x608)
+#define ELPE_ARG2              (0x60C)
+#define ELPE_ARG3              (0x610)
+#define ELPE_ARG4              (0x614)
+#define ELPE_ARG5              (0x618)
+#define ELPE_ARG6              (0x61C)
+#define ELPE_ARG7              (0x620)
+#define ELPE_ARG8              (0x624)
+#define ELPE_ARG9              (0x628)
+#define ELPE_ARG10             (0x62C)
+#define ELPE_ARG11             (0x630)
+#define ELPE_ARG12             (0x634)
+#define ELPE_RET               (0x638)
+#define ELPE_DONE              (0x63C)
+#define ELPE_MAX_COUNT         (11)
+
+/* NXP Bundle control parameters */
+#define NXPBDL_BASE            (0x700)
+#define NXPBDL_CHANGE_BIT      (0x700)
+#define NXPBDL_MAX_COUNT       (35) /* bundle common struct param count */
+#define NXPBDL_VALUE_MIN       (0)
+#define NXPBDL_VALUE_MAX       (192000)
+
+/* NXP Reverb control parameters */
+#define NXPRVB_PARAM_BASE      (0x800)
+#define NXPRVB_PARAM_CHANGE_BIT        (0x800)
+#define NXPRVB_PARAM_MAX_COUNT (10) /* reverb common struct param count */
+#define NXPRVB_PARAM_VALUE_MIN (0)
+#define NXPRVB_PARAM_VALUE_MAX (192000)
+
+/* NXP reverb context parameters */
+#define NXPRVB_CTX_BASE                (0x880)
+#define NXPRVB_CTX_CHANGE_BIT  (0x880)
+#define NXPRVB_CTX_MAX_COUNT   (7) /* reverb context param count */
+#define NXPRVB_CTX_VALUE_MIN   (0)
+#define NXPRVB_CTX_VALUE_MAX   (192000)
+
+#define SB_BASE                        (0x900)
+#define SB_CHANGE_BIT          (0x900)
+#define SB_ROTATION            (0x910)
+#define SB_MAX_COUNT           (1)
+#define SB_VALUE_MIN           (0)
+#define SB_VALUE_MAX           (3)
+
+#define UPSCALER_BASE          (0xA00)
+#define UPSCALER_CHANGE_BIT    (0xA00)
+#define UPSCALER_ROTATION      (0xA10)
+#define UPSCALER_MAX_COUNT     (1)
+#define UPSCALER_VALUE_MIN     (0)
+#define UPSCALER_VALUE_MAX     (2)
+
+#define ABOX_EFFECT_MAX_REGISTERS      (0xB00)
+
+#define PARAM_OFFSET           (0x10)
+#define CHANGE_BIT             (1)
+
+struct abox_effect_data {
+       struct platform_device *pdev;
+       struct platform_device *pdev_abox;
+       void __iomem *base;
+       struct regmap *regmap;
+};
+
+/**
+ * Restore abox effect register map
+ */
+extern void abox_effect_restore(void);
+
+#endif /* __SND_SOC_ABOX_EFFECT_H */
diff --git a/sound/soc/samsung/abox/abox_failsafe.c b/sound/soc/samsung/abox/abox_failsafe.c
new file mode 100644 (file)
index 0000000..3abebe5
--- /dev/null
@@ -0,0 +1,160 @@
+/* sound/soc/samsung/abox/abox_failsafe.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox Failsafe driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include <linux/vmalloc.h>
+#include <linux/pm_runtime.h>
+#include <sound/samsung/abox.h>
+
+#include "abox_util.h"
+#include "abox.h"
+#include "abox_log.h"
+
+#define SMART_FAILSAFE
+#define WAKE_LOCK_TIMEOUT_MS (4000)
+
+static int abox_failsafe_reset_count;
+static struct device *abox_failsafe_dev;
+static atomic_t abox_failsafe_reported;
+static bool abox_failsafe_service;
+
+static int abox_failsafe_start(struct device *dev, struct abox_data *data)
+{
+       int ret = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (abox_failsafe_service)
+               pm_runtime_put(dev);
+       if (!atomic_cmpxchg(&abox_failsafe_reported, 0, 1)) {
+               dev_info(dev, "%s\n", __func__);
+               /* prevent sleep during abox recovery */
+               pm_wakeup_event(dev, WAKE_LOCK_TIMEOUT_MS);
+               abox_clear_cpu_gear_requests(dev, data);
+       }
+
+       return ret;
+}
+
+static int abox_failsafe_end(struct device *dev, struct abox_data *data)
+{
+       int ret = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (atomic_cmpxchg(&abox_failsafe_reported, 1, 0)) {
+               dev_info(dev, "%s\n", __func__);
+               if (abox_test_quirk(data, ABOX_QUIRK_OFF_ON_SUSPEND))
+                       pm_runtime_get(dev);
+               pm_relax(dev);
+       }
+
+       return ret;
+}
+
+static void abox_failsafe_report_work_func(struct work_struct *work)
+{
+       struct device *dev = abox_failsafe_dev;
+       char env[32] = {0,};
+       char *envp[2] = {env, NULL};
+
+       dev_dbg(dev, "%s\n", __func__);
+       pm_runtime_barrier(dev);
+       snprintf(env, sizeof(env), "COUNT=%d", abox_failsafe_reset_count);
+       kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+}
+
+DECLARE_WORK(abox_failsafe_report_work, abox_failsafe_report_work_func);
+
+#ifdef SMART_FAILSAFE
+void abox_failsafe_report(struct device *dev)
+{
+       dev_dbg(dev, "%s\n", __func__);
+
+       abox_failsafe_dev = dev;
+       abox_failsafe_reset_count++;
+       if (abox_failsafe_service)
+               pm_runtime_get(dev);
+       schedule_work(&abox_failsafe_report_work);
+}
+#else
+/* TODO: Use SMART_FAILSAFE.
+ * SMART_FAILSAFE needs support from user space.
+ */
+void abox_failsafe_report(struct device *dev)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       abox_failsafe_start(dev, data);
+}
+#endif
+void abox_failsafe_report_reset(struct device *dev)
+{
+       struct abox_data *data = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       abox_failsafe_end(dev, data);
+}
+
+static int abox_failsafe_reset(struct device *dev, struct abox_data *data)
+{
+       struct device *dev_abox = &data->pdev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       return abox_failsafe_start(dev_abox, data);
+}
+
+static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       static const char code[] = "CALLIOPE";
+       struct abox_data *data = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(dev, "%s(%zu)\n", __func__, count);
+
+       if (!strncmp(code, buf, min(sizeof(code) - 1, count))) {
+               ret = abox_failsafe_reset(dev, data);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR_WO(reset);
+static DEVICE_BOOL_ATTR(service, 0660, abox_failsafe_service);
+static DEVICE_INT_ATTR(reset_count, 0660, abox_failsafe_reset_count);
+
+void abox_failsafe_init(struct device *dev)
+{
+       int ret;
+
+       ret = device_create_file(dev, &dev_attr_reset);
+       if (ret < 0)
+               dev_warn(dev, "%s: %s file creation failed: %d\n",
+                               __func__, "reset", ret);
+       ret = device_create_file(dev, &dev_attr_service.attr);
+       if (ret < 0)
+               dev_warn(dev, "%s: %s file creation failed: %d\n",
+                               __func__, "service", ret);
+       ret = device_create_file(dev, &dev_attr_reset_count.attr);
+       if (ret < 0)
+               dev_warn(dev, "%s: %s file creation failed: %d\n",
+                               __func__, "reset_count", ret);
+}
diff --git a/sound/soc/samsung/abox/abox_failsafe.h b/sound/soc/samsung/abox/abox_failsafe.h
new file mode 100644 (file)
index 0000000..0c05f05
--- /dev/null
@@ -0,0 +1,36 @@
+/* sound/soc/samsung/abox/abox_failsafe.h
+ *
+ * ALSA SoC - Samsung Abox Fail-safe driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_FAILSAFE_H
+#define __SND_SOC_ABOX_FAILSAFE_H
+
+#include <linux/device.h>
+#include <sound/samsung/abox.h>
+
+/**
+ * Report failure to user space
+ * @param[in]  dev             pointer to abox device
+ */
+extern void abox_failsafe_report(struct device *dev);
+
+/**
+ * Report reset
+ * @param[in]  dev             pointer to abox device
+ */
+extern void abox_failsafe_report_reset(struct device *dev);
+
+/**
+ * Register abox fail-safe
+ * @param[in]  dev             pointer to abox device
+ */
+extern void abox_failsafe_init(struct device *dev);
+
+#endif /* __SND_SOC_ABOX_FAILSAFE_H */
diff --git a/sound/soc/samsung/abox/abox_gic.c b/sound/soc/samsung/abox/abox_gic.c
new file mode 100644 (file)
index 0000000..3205fdc
--- /dev/null
@@ -0,0 +1,295 @@
+/* sound/soc/samsung/abox/abox_gic.c
+ *
+ * ALSA SoC Audio Layer - Samsung ABOX GIC driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/smc.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/delay.h>
+
+#include "abox_util.h"
+#include "abox_gic.h"
+
+#define GIC_IS_SECURE_FREE
+
+void abox_gic_generate_interrupt(struct device *dev, unsigned int irq)
+{
+#ifdef GIC_IS_SECURE_FREE
+       struct abox_gic_data *data = dev_get_drvdata(dev);
+#endif
+       dev_dbg(dev, "%s(%d)\n", __func__, irq);
+#ifdef GIC_IS_SECURE_FREE
+       writel((0x1 << 16) | (irq & 0xF),
+                       data->gicd_base + GIC_DIST_SOFTINT);
+#else
+       dev_dbg(dev, "exynos_smc() is called\n");
+       exynos_smc(SMC_CMD_REG,
+                       SMC_REG_ID_SFR_W(0x13EF1000 + GIC_DIST_SOFTINT),
+                       (0x1 << 16) | (hw_irq & 0xF), 0);
+#endif
+}
+EXPORT_SYMBOL(abox_gic_generate_interrupt);
+
+int abox_gic_register_irq_handler(struct device *dev, unsigned int irq,
+               irq_handler_t handler, void *dev_id)
+{
+       struct abox_gic_data *data = dev_get_drvdata(dev);
+
+       dev_info(dev, "%s(%u, %p, %p)\n", __func__, irq, handler, dev_id);
+
+       if (irq >= ARRAY_SIZE(data->handler)) {
+               dev_err(dev, "invalid irq: %d\n", irq);
+               return -EINVAL;
+       }
+
+       data->handler[irq].handler = handler;
+       data->handler[irq].dev_id = dev_id;
+
+       return 0;
+}
+EXPORT_SYMBOL(abox_gic_register_irq_handler);
+
+int abox_gic_unregister_irq_handler(struct device *dev, unsigned int irq)
+{
+       struct abox_gic_data *data = dev_get_drvdata(dev);
+
+       dev_info(dev, "%s(%u)\n", __func__, irq);
+
+       if (irq >= ARRAY_SIZE(data->handler)) {
+               dev_err(dev, "invalid irq: %d\n", irq);
+               return -EINVAL;
+       }
+
+       data->handler[irq].handler = NULL;
+       data->handler[irq].dev_id = NULL;
+
+       return 0;
+}
+EXPORT_SYMBOL(abox_gic_unregister_irq_handler);
+
+static irqreturn_t __abox_gic_irq_handler(struct abox_gic_data *data, u32 irqnr)
+{
+       struct abox_gic_irq_handler_t *handler;
+
+       if (irqnr >= ARRAY_SIZE(data->handler))
+               return IRQ_NONE;
+
+       handler = &data->handler[irqnr];
+       if (!handler->handler)
+               return IRQ_NONE;
+
+       return handler->handler(irqnr, handler->dev_id);
+}
+
+static irqreturn_t abox_gic_irq_handler(int irq, void *dev_id)
+{
+       struct device *dev = dev_id;
+       struct abox_gic_data *data = dev_get_drvdata(dev);
+       irqreturn_t ret = IRQ_NONE;
+       u32 irqstat, irqnr;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       do {
+               irqstat = readl(data->gicc_base + GIC_CPU_INTACK);
+               irqnr = irqstat & GICC_IAR_INT_ID_MASK;
+               dev_dbg(dev, "IAR: %08X\n", irqstat);
+
+               if (likely(irqnr < 16)) {
+                       writel(irqstat, data->gicc_base + GIC_CPU_EOI);
+                       writel(irqstat, data->gicc_base + GIC_CPU_DEACTIVATE);
+                       ret |= __abox_gic_irq_handler(data, irqnr);
+                       continue;
+               } else if (unlikely(irqnr > 15 && irqnr < 1021)) {
+                       writel(irqstat, data->gicc_base + GIC_CPU_EOI);
+                       ret |= __abox_gic_irq_handler(data, irqnr);
+                       continue;
+               }
+               break;
+       } while (1);
+
+       return ret;
+}
+
+static void abox_gicd_enable(struct device *dev, bool en)
+{
+       struct abox_gic_data *data = dev_get_drvdata(dev);
+       void __iomem *gicd_base = data->gicd_base;
+
+       if (en) {
+               writel(0x1, gicd_base + GIC_DIST_CTRL);
+               writel(0x0, gicd_base + GIC_DIST_IGROUP + 0x0);
+               writel(0x0, gicd_base + GIC_DIST_IGROUP + 0x4);
+               writel(0x0, gicd_base + GIC_DIST_IGROUP + 0x8);
+               writel(0x0, gicd_base + GIC_DIST_IGROUP + 0xC);
+               /* Todo: check whether it is really needed
+                * writel(0xc, gicd_base + GIC_DIST_ENABLE_SET + 0x4);
+                */
+               dev_dbg(dev, "[WRITE]GICD_ISENABLE:en   : 0x%x\n",
+                               readl(gicd_base + GIC_DIST_ENABLE_SET + 0x4));
+       } else {
+               writel(0x0, gicd_base + GIC_DIST_CTRL);
+               /* Todo: check whether it is really needed
+                * writel(0xc, gicd_base + GIC_DIST_ENABLE_CLEAR + 0x4);
+                */
+               dev_dbg(dev, "[WRITE]GICD_ISENABLE:dis  : 0x%x\n",
+                               readl(gicd_base + GIC_DIST_ENABLE_SET + 0x4));
+       }
+}
+
+void abox_gic_init_gic(struct device *dev)
+{
+       struct abox_gic_data *data = dev_get_drvdata(dev);
+       unsigned long arg;
+       int i, ret;
+
+       dev_info(dev, "%s\n", __func__);
+
+#ifdef GIC_IS_SECURE_FREE
+       writel(0x000000FF, data->gicc_base + GIC_CPU_PRIMASK);
+       writel(0x3, data->gicd_base + GIC_DIST_CTRL);
+#else
+       arg = SMC_REG_ID_SFR_W(data->gicc_base_phys + GIC_CPU_PRIMASK);
+       ret = exynos_smc(SMC_CMD_REG, arg, 0x000000FF, 0);
+
+       arg = SMC_REG_ID_SFR_W(data->gicd_base_phys + GIC_DIST_CTRL);
+       ret = exynos_smc(SMC_CMD_REG, arg, 0x3, 0);
+#endif
+       if (is_secure_gic()) {
+               for (i = 0; i < 1; i++) {
+                       arg = SMC_REG_ID_SFR_W(data->gicd_base_phys +
+                                       GIC_DIST_IGROUP + (i * 4));
+                       ret = exynos_smc(SMC_CMD_REG, arg, 0xFFFFFFFF, 0);
+               }
+       }
+       for (i = 0; i < 40; i++) {
+#ifdef GIC_IS_SECURE_FREE
+               writel(0x10101010, data->gicd_base + GIC_DIST_PRI + (i * 4));
+#else
+               arg = SMC_REG_ID_SFR_W(data->gicd_base_phys +
+                               GIC_DIST_PRI + (i * 4));
+               ret = exynos_smc(SMC_CMD_REG, arg, 0x10101010, 0);
+#endif
+       }
+
+       writel(0x3, data->gicc_base + GIC_CPU_CTRL);
+}
+EXPORT_SYMBOL(abox_gic_init_gic);
+
+int abox_gic_enable_irq(struct device *dev)
+{
+       struct abox_gic_data *data = dev_get_drvdata(dev);
+
+       if (likely(data->disabled)) {
+               dev_info(dev, "%s\n", __func__);
+
+               data->disabled = false;
+               enable_irq(data->irq);
+               abox_gicd_enable(dev, true);
+       }
+       return 0;
+}
+
+int abox_gic_disable_irq(struct device *dev)
+{
+       struct abox_gic_data *data = dev_get_drvdata(dev);
+
+       if (likely(!data->disabled)) {
+               dev_info(dev, "%s\n", __func__);
+
+               data->disabled = true;
+               disable_irq(data->irq);
+       }
+
+       return 0;
+}
+
+static int samsung_abox_gic_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct abox_gic_data *data;
+       int ret;
+
+       dev_info(dev, "%s\n", __func__);
+
+       data = devm_kzalloc(dev, sizeof(struct abox_gic_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, data);
+
+       data->gicd_base = devm_request_and_map_byname(pdev, "gicd",
+                       &data->gicd_base_phys, NULL);
+       if (IS_ERR(data->gicd_base))
+               return PTR_ERR(data->gicd_base);
+
+       data->gicc_base = devm_request_and_map_byname(pdev, "gicc",
+                       &data->gicc_base_phys, NULL);
+       if (IS_ERR(data->gicc_base))
+               return PTR_ERR(data->gicc_base);
+
+       data->irq = platform_get_irq(pdev, 0);
+       if (data->irq < 0) {
+               dev_err(dev, "Failed to get irq\n");
+               return data->irq;
+       }
+
+       ret = devm_request_irq(dev, data->irq, abox_gic_irq_handler,
+               IRQF_TRIGGER_RISING, pdev->name, dev);
+       if (ret < 0) {
+               dev_err(dev, "Failed to request irq\n");
+               return ret;
+       }
+
+#ifndef CONFIG_PM
+       abox_gic_resume(dev);
+#endif
+       dev_info(dev, "%s: probe complete\n", __func__);
+
+       return 0;
+}
+
+static int samsung_abox_gic_remove(struct platform_device *pdev)
+{
+       dev_info(&pdev->dev, "%s\n", __func__);
+
+       return 0;
+}
+
+static const struct of_device_id samsung_abox_gic_of_match[] = {
+       {
+               .compatible = "samsung,abox_gic",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_abox_gic_of_match);
+
+static struct platform_driver samsung_abox_gic_driver = {
+       .probe  = samsung_abox_gic_probe,
+       .remove = samsung_abox_gic_remove,
+       .driver = {
+               .name = "samsung-abox-gic",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_abox_gic_of_match),
+       },
+};
+
+module_platform_driver(samsung_abox_gic_driver);
+
+/* Module information */
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box GIC Driver");
+MODULE_ALIAS("platform:samsung-abox-gic");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox_gic.h b/sound/soc/samsung/abox/abox_gic.h
new file mode 100644 (file)
index 0000000..f68d3ef
--- /dev/null
@@ -0,0 +1,78 @@
+/* sound/soc/samsung/abox/abox_gic.h
+ *
+ * ALSA SoC Audio Layer - Samsung Abox GIC driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_GIC_H
+#define __SND_SOC_ABOX_GIC_H
+
+#define ABOX_GIC_IRQ_COUNT 16
+
+struct abox_gic_irq_handler_t {
+       irq_handler_t handler;
+       void *dev_id;
+};
+
+struct abox_gic_data {
+       void __iomem *gicd_base;
+       void __iomem *gicc_base;
+       phys_addr_t gicd_base_phys;
+       phys_addr_t gicc_base_phys;
+       int irq;
+       struct abox_gic_irq_handler_t handler[ABOX_GIC_IRQ_COUNT];
+       bool disabled;
+
+};
+
+/**
+ * Generate interrupt
+ * @param[in]  dev     pointer to abox_gic device
+ * @param[in]  irq     irq number
+ */
+extern void abox_gic_generate_interrupt(struct device *dev, unsigned int irq);
+
+/**
+ * Register interrupt handler
+ * @param[in]  dev     pointer to abox_gic device
+ * @param[in]  irq     irq number
+ * @param[in]  handler function to be called on interrupt
+ * @param[in]  dev_id  cookie for interrupt.
+ * @return     error code or 0
+ */
+extern int abox_gic_register_irq_handler(struct device *dev,
+               unsigned int irq, irq_handler_t handler, void *dev_id);
+
+/**
+ * Unregister interrupt handler
+ * @param[in]  dev     pointer to abox_gic device
+ * @param[in]  irq     irq number
+ * @return     error code or 0
+ */
+extern int abox_gic_unregister_irq_handler(struct device *dev,
+               unsigned int irq);
+
+/**
+ * Enable abox gic irq
+ * @param[in]  dev     pointer to abox_gic device
+ */
+extern int abox_gic_enable_irq(struct device *dev);
+
+/**
+ * Disable abox gic irq
+ * @param[in]  dev     pointer to abox_gic device
+ */
+extern int abox_gic_disable_irq(struct device *dev);
+
+/**
+ * Initialize abox gic
+ * @param[in]  dev     pointer to abox_gic device
+ */
+extern void abox_gic_init_gic(struct device *dev);
+
+#endif /* __SND_SOC_ABOX_GIC_H */
diff --git a/sound/soc/samsung/abox/abox_if.c b/sound/soc/samsung/abox/abox_if.c
new file mode 100644 (file)
index 0000000..1ecb3fe
--- /dev/null
@@ -0,0 +1,964 @@
+/* sound/soc/samsung/abox/abox_uaif.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox UAIF driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm_params.h>
+#include <sound/samsung/abox.h>
+
+#include "abox_util.h"
+#include "abox.h"
+#include "abox_if.h"
+
+static int abox_if_startup(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct abox_data *abox_data = data->abox_data;
+       int ret;
+
+       dev_info(dev, "%s[%c]\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P');
+
+       abox_request_cpu_gear(dev, abox_data, dai, abox_data->cpu_gear_min);
+       ret = clk_enable(data->clk_bclk);
+       if (ret < 0) {
+               dev_err(dev, "Failed to enable bclk: %d\n", ret);
+               goto err;
+       }
+       ret = clk_enable(data->clk_bclk_gate);
+       if (ret < 0) {
+               dev_err(dev, "Failed to enable bclk_gate: %d\n", ret);
+               goto err;
+       }
+err:
+       return ret;
+}
+
+static void abox_if_shutdown(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct abox_data *abox_data = data->abox_data;
+
+       dev_info(dev, "%s[%c]\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P');
+
+       clk_disable(data->clk_bclk_gate);
+       clk_disable(data->clk_bclk);
+       abox_request_cpu_gear(dev, abox_data, dai, ABOX_CPU_GEAR_MIN);
+}
+
+static int abox_if_hw_free(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct abox_data *abox_data = data->abox_data;
+
+       dev_info(dev, "%s[%c]\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P');
+
+       abox_register_bclk_usage(dev, abox_data, dai->id, 0, 0, 0);
+
+       return 0;
+}
+
+static int abox_spdy_trigger(struct snd_pcm_substream *substream,
+               int trigger, struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_component *cmpnt = data->cmpnt;
+       int ret = 0;
+
+       dev_info(dev, "%s[%c](%d)\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P', trigger);
+
+       switch (trigger) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = snd_soc_component_update_bits(cmpnt, ABOX_SPDYIF_CTRL,
+                               ABOX_ENABLE_MASK, 1 << ABOX_ENABLE_L);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ret = snd_soc_component_update_bits(cmpnt, ABOX_SPDYIF_CTRL,
+                               ABOX_ENABLE_MASK, 0 << ABOX_ENABLE_L);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       if (ret < 0)
+               dev_err(dev, "sfr access failed: %d\n", ret);
+
+       return ret;
+}
+
+static int abox_dsif_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct abox_data *abox_data = data->abox_data;
+       unsigned int rate;
+       int ret;
+
+       dev_info(dev, "%s\n", __func__);
+
+       rate = dai->rate;
+
+       ret = abox_register_bclk_usage(dev, abox_data, dai->id, rate, 1, ratio);
+       if (ret < 0)
+               dev_err(dev, "Unable to register bclk usage: %d\n", ret);
+
+       ret = clk_set_rate(data->clk_bclk, rate * ratio);
+       if (ret < 0) {
+               dev_err(dev, "bclk set error=%d\n", ret);
+       } else {
+               dev_info(dev, "rate=%u, bclk=%lu\n", rate,
+                               clk_get_rate(data->clk_bclk));
+       }
+
+       return ret;
+}
+
+static int abox_dsif_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_component *cmpnt = data->cmpnt;
+       unsigned int ctrl;
+       int ret = 0;
+
+       dev_info(dev, "%s(0x%08x)\n", __func__, fmt);
+
+       pm_runtime_get_sync(dev);
+
+       snd_soc_component_read(cmpnt, ABOX_DSIF_CTRL, &ctrl);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_PDM:
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+       case SND_SOC_DAIFMT_NB_IF:
+               set_value_by_name(ctrl, ABOX_DSIF_BCLK_POLARITY, 1);
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+       case SND_SOC_DAIFMT_IB_IF:
+               set_value_by_name(ctrl, ABOX_DSIF_BCLK_POLARITY, 0);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       snd_soc_component_write(cmpnt, ABOX_DSIF_CTRL, ctrl);
+
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       return ret;
+}
+
+static int abox_dsif_set_channel_map(struct snd_soc_dai *dai,
+               unsigned int tx_num, unsigned int *tx_slot,
+               unsigned int rx_num, unsigned int *rx_slot)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_component *cmpnt = data->cmpnt;
+
+       dev_info(dev, "%s\n", __func__);
+
+       snd_soc_component_update_bits(cmpnt, ABOX_DSIF_CTRL, ABOX_ORDER_MASK,
+                       (tx_slot[0] ? 1 : 0) << ABOX_ORDER_L);
+
+       return 0;
+}
+
+static int abox_dsif_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       unsigned int channels, rate, width;
+
+       dev_info(dev, "%s[%c]\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P');
+
+       channels = params_channels(hw_params);
+       rate = params_rate(hw_params);
+       width = params_width(hw_params);
+
+       dev_info(dev, "rate=%u, width=%d, channel=%u, bclk=%lu\n",
+                       rate,
+                       width,
+                       channels,
+                       clk_get_rate(data->clk_bclk));
+
+       switch (params_format(hw_params)) {
+       case SNDRV_PCM_FORMAT_S32:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (channels) {
+       case 2:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int abox_dsif_trigger(struct snd_pcm_substream *substream,
+               int trigger, struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_component *cmpnt = data->cmpnt;
+
+       dev_info(dev, "%s[%c](%d)\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P', trigger);
+
+       switch (trigger) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               snd_soc_component_update_bits(cmpnt, ABOX_DSIF_CTRL,
+                               ABOX_ENABLE_MASK, 1 << ABOX_ENABLE_L);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               snd_soc_component_update_bits(cmpnt, ABOX_DSIF_CTRL,
+                               ABOX_ENABLE_MASK, 0 << ABOX_ENABLE_L);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int abox_uaif_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_component *cmpnt = data->cmpnt;
+       int id = data->id;
+       unsigned int ctrl0, ctrl1;
+       int ret = 0;
+
+       dev_info(dev, "%s(0x%08x)\n", __func__, fmt);
+
+       pm_runtime_get_sync(dev);
+
+       snd_soc_component_read(cmpnt, ABOX_UAIF_CTRL0(id), &ctrl0);
+       snd_soc_component_read(cmpnt, ABOX_UAIF_CTRL1(id), &ctrl1);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               set_value_by_name(ctrl1, ABOX_WS_MODE, 0);
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               set_value_by_name(ctrl1, ABOX_WS_MODE, 1);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               set_value_by_name(ctrl1, ABOX_BCLK_POLARITY, 1);
+               set_value_by_name(ctrl1, ABOX_WS_POLAR, 0);
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               set_value_by_name(ctrl1, ABOX_BCLK_POLARITY, 1);
+               set_value_by_name(ctrl1, ABOX_WS_POLAR, 1);
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               set_value_by_name(ctrl1, ABOX_BCLK_POLARITY, 0);
+               set_value_by_name(ctrl1, ABOX_WS_POLAR, 0);
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               set_value_by_name(ctrl1, ABOX_BCLK_POLARITY, 0);
+               set_value_by_name(ctrl1, ABOX_WS_POLAR, 1);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               set_value_by_name(ctrl0, ABOX_MODE, 0);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               set_value_by_name(ctrl0, ABOX_MODE, 1);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       snd_soc_component_write(cmpnt, ABOX_UAIF_CTRL0(id), ctrl0);
+       snd_soc_component_write(cmpnt, ABOX_UAIF_CTRL1(id), ctrl1);
+
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       return ret;
+}
+
+static int abox_uaif_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct abox_data *abox_data = data->abox_data;
+       int id = data->id;
+       enum qchannel clk;
+
+       dev_info(dev, "%s(%d)\n", __func__, tristate);
+
+       switch (id) {
+       case 0:
+               clk = ABOX_BCLK_UAIF0;
+               break;
+       case 1:
+               clk = ABOX_BCLK_UAIF1;
+               break;
+       case 2:
+               clk = ABOX_BCLK_UAIF2;
+               break;
+       case 3:
+               clk = ABOX_BCLK_UAIF3;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return abox_disable_qchannel(dev, abox_data, clk, !tristate);
+}
+
+static int abox_uaif_startup(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_component *cmpnt = data->cmpnt;
+       int ret;
+
+       dev_dbg(dev, "%s[%c]\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P');
+
+       ret = abox_if_startup(substream, dai);
+       if (ret < 0)
+               goto err;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               snd_soc_component_update_bits(cmpnt, ABOX_UAIF_CTRL0(data->id),
+                               ABOX_DATA_MODE_MASK | ABOX_IRQ_MODE_MASK,
+                               (1 << ABOX_DATA_MODE_L) |
+                               (0 << ABOX_IRQ_MODE_L));
+err:
+       return ret;
+}
+
+static int abox_uaif_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct abox_data *abox_data = data->abox_data;
+       struct snd_soc_component *cmpnt = data->cmpnt;
+       int id = data->id;
+       unsigned int ctrl1;
+       unsigned int channels, rate, width;
+       int ret;
+
+       dev_info(dev, "%s[%c]\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P');
+
+       channels = params_channels(hw_params);
+       rate = params_rate(hw_params);
+       width = params_width(hw_params);
+
+       ret = abox_register_bclk_usage(dev, abox_data, dai->id, rate, channels,
+                       width);
+       if (ret < 0)
+               dev_err(dev, "Unable to register bclk usage: %d\n", ret);
+
+       ret = clk_set_rate(data->clk_bclk, rate * channels * width);
+       if (ret < 0) {
+               dev_err(dev, "bclk set error=%d\n", ret);
+               return ret;
+       }
+
+       dev_info(dev, "rate=%u, width=%d, channel=%u, bclk=%lu\n",
+                       rate, width, channels, clk_get_rate(data->clk_bclk));
+
+       ret = snd_soc_component_read(cmpnt, ABOX_UAIF_CTRL1(id), &ctrl1);
+       if (ret < 0)
+               dev_err(dev, "sfr access failed: %d\n", ret);
+
+       switch (params_format(hw_params)) {
+       case SNDRV_PCM_FORMAT_S16:
+       case SNDRV_PCM_FORMAT_S24:
+       case SNDRV_PCM_FORMAT_S32:
+               set_value_by_name(ctrl1, ABOX_SBIT_MAX, (width - 1));
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (channels) {
+       case 2:
+               set_value_by_name(ctrl1, ABOX_VALID_STR, 0);
+               set_value_by_name(ctrl1, ABOX_VALID_END, 0);
+               break;
+       case 1:
+       case 4:
+       case 6:
+       case 8:
+               set_value_by_name(ctrl1, ABOX_VALID_STR, (width - 1));
+               set_value_by_name(ctrl1, ABOX_VALID_END, (width - 1));
+               break;
+       default:
+               return -EINVAL;
+       }
+       set_value_by_name(ctrl1, ABOX_SLOT_MAX, (channels - 1));
+       set_value_by_name(ctrl1, ABOX_FORMAT, abox_get_format(width, channels));
+
+       ret = snd_soc_component_write(cmpnt, ABOX_UAIF_CTRL1(id), ctrl1);
+       if (ret < 0)
+               dev_err(dev, "sfr access failed: %d\n", ret);
+
+       return 0;
+}
+
+static int abox_uaif_trigger(struct snd_pcm_substream *substream,
+               int trigger, struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_component *cmpnt = data->cmpnt;
+       int id = data->id;
+       unsigned long mask, shift;
+       int ret = 0;
+
+       dev_info(dev, "%s[%c](%d)\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P', trigger);
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               mask = ABOX_MIC_ENABLE_MASK;
+               shift = ABOX_MIC_ENABLE_L;
+       } else {
+               mask = ABOX_SPK_ENABLE_MASK;
+               shift = ABOX_SPK_ENABLE_L;
+       }
+
+       switch (trigger) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = snd_soc_component_update_bits(cmpnt, ABOX_UAIF_CTRL0(id),
+                               mask, 1 << shift);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+                       ret = snd_soc_component_update_bits(cmpnt,
+                                       ABOX_UAIF_CTRL0(id), mask, 0 << shift);
+               }
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       if (ret < 0)
+               dev_err(dev, "sfr access failed: %d\n", ret);
+
+       return ret;
+}
+
+static void abox_uaif_shutdown(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_component *cmpnt = data->cmpnt;
+       int id = data->id;
+
+       dev_dbg(dev, "%s[%c]\n", __func__,
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       'C' : 'P');
+
+       /* dpcm_be_dai_trigger doesn't call trigger stop on paused stream. */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_soc_component_update_bits(cmpnt, ABOX_UAIF_CTRL0(id),
+                               ABOX_SPK_ENABLE_MASK, 0 << ABOX_SPK_ENABLE_L);
+       abox_if_shutdown(substream, dai);
+}
+
+static const struct snd_soc_dai_ops abox_spdy_dai_ops = {
+       .startup        = abox_if_startup,
+       .shutdown       = abox_if_shutdown,
+       .hw_free        = abox_if_hw_free,
+       .trigger        = abox_spdy_trigger,
+};
+
+static struct snd_soc_dai_driver abox_spdy_dai_drv = {
+       .capture = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = ABOX_SAMPLING_RATES,
+               .rate_min = 40000,
+               .rate_max = 40000,
+               .formats = SNDRV_PCM_FMTBIT_S16,
+       },
+       .ops = &abox_spdy_dai_ops,
+};
+
+static const struct snd_soc_dai_ops abox_dsif_dai_ops = {
+       .set_bclk_ratio = abox_dsif_set_bclk_ratio,
+       .set_fmt        = abox_dsif_set_fmt,
+       .set_channel_map        = abox_dsif_set_channel_map,
+       .startup        = abox_if_startup,
+       .shutdown       = abox_if_shutdown,
+       .hw_params      = abox_dsif_hw_params,
+       .hw_free        = abox_if_hw_free,
+       .trigger        = abox_dsif_trigger,
+};
+
+static struct snd_soc_dai_driver abox_dsif_dai_drv = {
+       .playback = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = ABOX_SAMPLING_RATES,
+               .rate_min = 8000,
+               .rate_max = 384000,
+               .formats = SNDRV_PCM_FMTBIT_S32,
+       },
+       .ops = &abox_dsif_dai_ops,
+};
+
+static const struct snd_soc_dai_ops abox_uaif_dai_ops = {
+       .set_fmt        = abox_uaif_set_fmt,
+       .set_tristate   = abox_uaif_set_tristate,
+       .startup        = abox_uaif_startup,
+       .shutdown       = abox_uaif_shutdown,
+       .hw_params      = abox_uaif_hw_params,
+       .hw_free        = abox_if_hw_free,
+       .trigger        = abox_uaif_trigger,
+};
+
+static struct snd_soc_dai_driver abox_uaif_dai_drv = {
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 8,
+               .rates = ABOX_SAMPLING_RATES,
+               .rate_min = 8000,
+               .rate_max = 384000,
+               .formats = ABOX_SAMPLE_FORMATS,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 8,
+               .rates = ABOX_SAMPLING_RATES,
+               .rate_min = 8000,
+               .rate_max = 384000,
+               .formats = ABOX_SAMPLE_FORMATS,
+       },
+       .ops = &abox_uaif_dai_ops,
+       .symmetric_rates = 1,
+       .symmetric_channels = 1,
+       .symmetric_samplebits = 1,
+};
+
+static int abox_if_config_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_if_data *data = snd_soc_component_get_drvdata(cmpnt);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = data->config[reg];
+
+       dev_dbg(dev, "%s(0x%08x): %u\n", __func__, reg, val);
+
+       ucontrol->value.integer.value[0] = val;
+
+       return 0;
+}
+
+static int abox_if_config_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = cmpnt->dev;
+       struct abox_if_data *data = snd_soc_component_get_drvdata(cmpnt);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int val = ucontrol->value.integer.value[0];
+
+       dev_info(dev, "%s(0x%08x, %u)\n", __func__, reg, val);
+
+       data->config[reg] = val;
+
+       return 0;
+}
+
+static const struct snd_kcontrol_new abox_if_controls[] = {
+       SOC_SINGLE_EXT("%s width", ABOX_IF_WIDTH, 0, 32, 0,
+                       abox_if_config_get, abox_if_config_put),
+       SOC_SINGLE_EXT("%s channel", ABOX_IF_CHANNEL, 0, 8, 0,
+                       abox_if_config_get, abox_if_config_put),
+       SOC_SINGLE_EXT("%s rate", ABOX_IF_RATE, 0, 384000, 0,
+                       abox_if_config_get, abox_if_config_put),
+};
+
+static int abox_if_cmpnt_probe(struct snd_soc_component *cmpnt)
+{
+       struct device *dev = cmpnt->dev;
+       struct abox_if_data *data = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_kcontrol_new (*controls)[3];
+       struct snd_kcontrol_new *control;
+       int i;
+
+       dev_info(dev, "%s\n", __func__);
+
+       controls = devm_kmemdup(dev, abox_if_controls,
+                       sizeof(abox_if_controls), GFP_KERNEL);
+       if (!controls)
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(*controls); i++) {
+               control = &(*controls)[i];
+               control->name = devm_kasprintf(dev, GFP_KERNEL, control->name,
+                               data->of_data->get_dai_name(data->id));
+       }
+       snd_soc_add_component_controls(cmpnt, *controls, ARRAY_SIZE(*controls));
+
+       data->cmpnt = cmpnt;
+       snd_soc_component_init_regmap(cmpnt, data->abox_data->regmap);
+       abox_register_if(data->abox_data->pdev, to_platform_device(dev),
+                       data->id, snd_soc_component_get_dapm(cmpnt),
+                       data->of_data->get_dai_name(data->id),
+                       !!data->dai_drv->playback.formats,
+                       !!data->dai_drv->capture.formats);
+
+       return 0;
+}
+
+static void abox_if_cmpnt_remove(struct snd_soc_component *cmpnt)
+{
+       struct device *dev = cmpnt->dev;
+
+       dev_info(dev, "%s\n", __func__);
+}
+
+static const struct snd_soc_component_driver abox_if_cmpnt = {
+       .probe                  = abox_if_cmpnt_probe,
+       .remove                 = abox_if_cmpnt_remove,
+};
+
+enum abox_dai abox_spdy_get_dai_id(int id)
+{
+       return ABOX_SPDY;
+}
+
+const char *abox_spdy_get_dai_name(int id)
+{
+       return "SPDY";
+}
+
+const char *abox_spdy_get_str_name(int id, int stream)
+{
+       return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                       "SPDY Playback" : "SPDY Capture";
+}
+
+enum abox_dai abox_dsif_get_dai_id(int id)
+{
+       return ABOX_DSIF;
+}
+
+const char *abox_dsif_get_dai_name(int id)
+{
+       return "DSIF";
+}
+
+const char *abox_dsif_get_str_name(int id, int stream)
+{
+       return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                       "DSIF Playback" : "DSIF Capture";
+}
+
+enum abox_dai abox_uaif_get_dai_id(int id)
+{
+       return ABOX_UAIF0 + id;
+}
+
+const char *abox_uaif_get_dai_name(int id)
+{
+       static const char * const names[] = {
+               "UAIF0", "UAIF1", "UAIF2", "UAIF3", "UAIF4",
+               "UAIF5", "UAIF6", "UAIF7", "UAIF8", "UAIF9",
+       };
+
+       return (id < ARRAY_SIZE(names)) ? names[id] : ERR_PTR(-EINVAL);
+}
+
+const char *abox_uaif_get_str_name(int id, int stream)
+{
+       static const char * const names_pla[] = {
+               "UAIF0 Playback", "UAIF1 Playback", "UAIF2 Playback",
+               "UAIF3 Playback", "UAIF4 Playback", "UAIF5 Playback",
+               "UAIF6 Playback", "UAIF7 Playback", "UAIF8 Playback",
+               "UAIF9 Playback",
+       };
+       static const char * const names_cap[] = {
+               "UAIF0 Capture", "UAIF1 Capture", "UAIF2 Capture",
+               "UAIF3 Capture", "UAIF4 Capture", "UAIF5 Capture",
+               "UAIF6 Capture", "UAIF7 Capture", "UAIF8 Capture",
+               "UAIF9 Capture",
+       };
+       const char *ret;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK && id < ARRAY_SIZE(names_pla))
+               ret = names_pla[id];
+       else if (stream == SNDRV_PCM_STREAM_CAPTURE &&
+                       id < ARRAY_SIZE(names_cap))
+               ret = names_cap[id];
+       else
+               ret = ERR_PTR(-EINVAL);
+
+       return ret;
+}
+
+static const struct of_device_id samsung_abox_if_match[] = {
+       {
+               .compatible = "samsung,abox-uaif",
+               .data = (void *)&(struct abox_if_of_data){
+                       .get_dai_id = abox_uaif_get_dai_id,
+                       .get_dai_name = abox_uaif_get_dai_name,
+                       .get_str_name = abox_uaif_get_str_name,
+                       .base_dai_drv = &abox_uaif_dai_drv,
+               },
+       },
+       {
+               .compatible = "samsung,abox-dsif",
+               .data = (void *)&(struct abox_if_of_data){
+                       .get_dai_id = abox_dsif_get_dai_id,
+                       .get_dai_name = abox_dsif_get_dai_name,
+                       .get_str_name = abox_dsif_get_str_name,
+                       .base_dai_drv = &abox_dsif_dai_drv,
+               },
+       },
+       {
+               .compatible = "samsung,abox-spdy",
+               .data = (void *)&(struct abox_if_of_data){
+                       .get_dai_id = abox_spdy_get_dai_id,
+                       .get_dai_name = abox_spdy_get_dai_name,
+                       .get_str_name = abox_spdy_get_str_name,
+                       .base_dai_drv = &abox_spdy_dai_drv,
+               },
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_abox_if_match);
+
+static int samsung_abox_if_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device *dev_abox = dev->parent;
+       struct device_node *np = dev->of_node;
+       struct abox_if_data *data;
+       int ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, data);
+
+       data->sfr_base = devm_not_request_and_map(pdev, "sfr", 0, NULL, NULL);
+       if (IS_ERR(data->sfr_base))
+               return PTR_ERR(data->sfr_base);
+
+       ret = of_property_read_u32_index(np, "id", 0, &data->id);
+       if (ret < 0) {
+               dev_err(dev, "id property reading fail\n");
+               return ret;
+       }
+
+       data->clk_bclk = devm_clk_get_and_prepare(pdev, "bclk");
+       if (IS_ERR(data->clk_bclk))
+               return PTR_ERR(data->clk_bclk);
+
+       data->clk_bclk_gate = devm_clk_get_and_prepare(pdev, "bclk_gate");
+       if (IS_ERR(data->clk_bclk_gate))
+               return PTR_ERR(data->clk_bclk_gate);
+
+       data->of_data = of_match_node(samsung_abox_if_match,
+                       pdev->dev.of_node)->data;
+       data->abox_data = dev_get_drvdata(dev_abox);
+       data->dai_drv = devm_kzalloc(dev, sizeof(struct snd_soc_dai_driver),
+                       GFP_KERNEL);
+       if (!data->dai_drv)
+               return -ENOMEM;
+       memcpy(data->dai_drv, data->of_data->base_dai_drv,
+                       sizeof(struct snd_soc_dai_driver));
+       data->dai_drv->id = data->of_data->get_dai_id(data->id);
+       data->dai_drv->name = data->of_data->get_dai_name(data->id);
+       if (data->dai_drv->capture.formats)
+               data->dai_drv->capture.stream_name =
+                               data->of_data->get_str_name(data->id,
+                               SNDRV_PCM_STREAM_CAPTURE);
+       if (data->dai_drv->playback.formats)
+               data->dai_drv->playback.stream_name =
+                               data->of_data->get_str_name(data->id,
+                               SNDRV_PCM_STREAM_PLAYBACK);
+
+       ret = devm_snd_soc_register_component(dev, &abox_if_cmpnt,
+                       data->dai_drv, 1);
+       if (ret < 0)
+               return ret;
+
+       pm_runtime_enable(dev);
+       pm_runtime_no_callbacks(dev);
+
+       return ret;
+}
+
+static int samsung_abox_if_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       return 0;
+}
+
+static struct platform_driver samsung_abox_if_driver = {
+       .probe  = samsung_abox_if_probe,
+       .remove = samsung_abox_if_remove,
+       .driver = {
+               .name = "samsung-abox-if",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_abox_if_match),
+       },
+};
+
+module_platform_driver(samsung_abox_if_driver);
+
+int abox_if_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+               struct snd_pcm_hw_params *params, int stream)
+{
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct device *dev = dai->dev;
+       struct abox_if_data *data = dev_get_drvdata(dev);
+       unsigned int rate, channels, width;
+       int ret = 0;
+
+       if (dev->driver != &samsung_abox_if_driver.driver)
+               return -EINVAL;
+
+       dev_dbg(dev, "%s[%s](%d)\n", __func__, dai->name, stream);
+
+       rate = data->config[ABOX_IF_RATE];
+       channels = data->config[ABOX_IF_CHANNEL];
+       width = data->config[ABOX_IF_WIDTH];
+
+       if (rate)
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min = rate;
+
+       if (channels)
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min =
+                               channels;
+
+       if (width) {
+               unsigned int format = 0;
+
+               switch (width) {
+               case 8:
+                       format = SNDRV_PCM_FORMAT_S8;
+                       break;
+               case 16:
+                       format = SNDRV_PCM_FORMAT_S16;
+                       break;
+               case 24:
+                       format = SNDRV_PCM_FORMAT_S24;
+                       break;
+               case 32:
+                       format = SNDRV_PCM_FORMAT_S32;
+                       break;
+               default:
+                       width = format = 0;
+                       break;
+               }
+
+               if (format) {
+                       struct snd_mask *mask;
+
+                       mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+                       snd_mask_none(mask);
+                       snd_mask_set(mask, format);
+               }
+       }
+
+       if (rate || channels || width)
+               dev_info(dev, "%s: %s: %d bit, %u channel, %uHz\n",
+                               __func__, dai->name, width, channels, rate);
+       else
+               ret = -EINVAL;
+
+       return ret;
+}
+
+/* Module information */
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box UAIF/DSIF Driver");
+MODULE_ALIAS("platform:samsung-abox-if");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox_if.h b/sound/soc/samsung/abox/abox_if.h
new file mode 100644 (file)
index 0000000..4f1205d
--- /dev/null
@@ -0,0 +1,53 @@
+/* sound/soc/samsung/abox/abox_if.h
+ *
+ * ALSA SoC - Samsung Abox UAIF/DSIF driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_IF_H
+#define __SND_SOC_ABOX_IF_H
+
+#include "abox.h"
+
+enum abox_if_config {
+       ABOX_IF_WIDTH,
+       ABOX_IF_CHANNEL,
+       ABOX_IF_RATE,
+       ABOX_IF_FMT_COUNT,
+};
+
+struct abox_if_of_data {
+       enum abox_dai (*get_dai_id)(int id);
+       const char *(*get_dai_name)(int id);
+       const char *(*get_str_name)(int id, int stream);
+       struct snd_soc_dai_driver *base_dai_drv;
+};
+
+struct abox_if_data {
+       int id;
+       void __iomem *sfr_base;
+       struct clk *clk_bclk;
+       struct clk *clk_bclk_gate;
+       struct snd_soc_component *cmpnt;
+       struct snd_soc_dai_driver *dai_drv;
+       struct abox_data *abox_data;
+       const struct abox_if_of_data *of_data;
+       unsigned int config[ABOX_IF_FMT_COUNT];
+};
+
+/**
+ * UAIF/DSIF hw params fixup helper
+ * @param[in]  rtd     snd_soc_pcm_runtime
+ * @param[out] params  snd_pcm_hw_params
+ * @param[in]  stream  SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
+ * @return             error code if any
+ */
+extern int abox_if_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+               struct snd_pcm_hw_params *params, int stream);
+
+#endif /* __SND_SOC_ABOX_IF_H */
diff --git a/sound/soc/samsung/abox/abox_log.c b/sound/soc/samsung/abox/abox_log.c
new file mode 100644 (file)
index 0000000..ae6942b
--- /dev/null
@@ -0,0 +1,426 @@
+/* sound/soc/samsung/abox/abox_log.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox Log driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/vmalloc.h>
+#include <sound/samsung/abox.h>
+
+#include "abox_util.h"
+#include "abox.h"
+#include "abox_dbg.h"
+#include "abox_log.h"
+
+#undef VERBOSE_LOG
+
+#undef TEST
+#ifdef TEST
+#define SIZE_OF_BUFFER (SZ_128)
+#else
+#define SIZE_OF_BUFFER (SZ_2M)
+#endif
+
+#define S_IRWUG (0660)
+
+struct abox_log_kernel_buffer {
+       char *buffer;
+       unsigned int index;
+       bool wrap;
+       bool updated;
+       wait_queue_head_t wq;
+};
+
+struct abox_log_buffer_info {
+       struct list_head list;
+       struct device *dev;
+       int id;
+       bool file_created;
+       ssize_t file_index;
+       struct mutex lock;
+       struct ABOX_LOG_BUFFER *log_buffer;
+       struct abox_log_kernel_buffer kernel_buffer;
+};
+
+static LIST_HEAD(abox_log_list_head);
+static u32 abox_log_auto_save;
+
+static void abox_log_memcpy(struct device *dev,
+       struct abox_log_kernel_buffer *kernel_buffer,
+       const char *src, size_t size)
+{
+       size_t left_size = SIZE_OF_BUFFER - kernel_buffer->index;
+
+       dev_dbg(dev, "%s(%zu)\n", __func__, size);
+
+       if (left_size < size) {
+#ifdef VERBOSE_LOG
+               dev_dbg(dev, "0: %s\n", src);
+#endif
+               memcpy(kernel_buffer->buffer + kernel_buffer->index, src,
+                               left_size);
+               src += left_size;
+               size -= left_size;
+               kernel_buffer->index = 0;
+               kernel_buffer->wrap = true;
+       }
+#ifdef VERBOSE_LOG
+       dev_dbg(dev, "1: %s\n", src);
+#endif
+       memcpy(kernel_buffer->buffer + kernel_buffer->index, src, size);
+       kernel_buffer->index += (unsigned int)size;
+}
+
+static void abox_log_file_name(struct device *dev,
+               struct abox_log_buffer_info *info, char *name, size_t size)
+{
+       snprintf(name, size, "/data/calliope-%02d.log", info->id);
+}
+
+static void abox_log_file_save(struct device *dev,
+               struct abox_log_buffer_info *info)
+{
+       struct ABOX_LOG_BUFFER *log_buffer = info->log_buffer;
+       unsigned int index_writer = log_buffer->index_writer;
+       char name[32];
+       struct file *filp;
+       mm_segment_t old_fs;
+
+       dev_dbg(dev, "%s(%d)\n", __func__, info->id);
+
+       abox_log_file_name(dev, info, name, sizeof(name));
+
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+       if (likely(info->file_created)) {
+               filp = filp_open(name, O_RDWR | O_APPEND | O_CREAT, S_IRWUG);
+               dev_dbg(dev, "appended\n");
+       } else {
+               filp = filp_open(name, O_RDWR | O_TRUNC | O_CREAT, S_IRWUG);
+               info->file_created = true;
+               dev_dbg(dev, "created\n");
+       }
+       if (IS_ERR(filp)) {
+               dev_warn(dev, "%s: saving log fail\n", __func__);
+               goto out;
+       }
+
+
+       if (log_buffer->index_reader > index_writer) {
+               vfs_write(filp, log_buffer->buffer + log_buffer->index_reader,
+                       log_buffer->size - log_buffer->index_reader,
+                       &filp->f_pos);
+               vfs_write(filp, log_buffer->buffer, index_writer, &filp->f_pos);
+       } else {
+               vfs_write(filp, log_buffer->buffer + log_buffer->index_reader,
+                       index_writer - log_buffer->index_reader, &filp->f_pos);
+       }
+
+       vfs_fsync(filp, 0);
+       filp_close(filp, NULL);
+out:
+       set_fs(old_fs);
+
+}
+
+static void abox_log_flush(struct device *dev,
+               struct abox_log_buffer_info *info)
+{
+       struct ABOX_LOG_BUFFER *log_buffer = info->log_buffer;
+       unsigned int index_writer = log_buffer->index_writer;
+       struct abox_log_kernel_buffer *kernel_buffer = &info->kernel_buffer;
+
+       if (log_buffer->index_reader == index_writer)
+               return;
+
+       dev_dbg(dev, "%s(%d): index_writer=%u, index_reader=%u, size=%u\n",
+                       __func__, info->id, index_writer,
+                       log_buffer->index_reader, log_buffer->size);
+
+       mutex_lock(&info->lock);
+
+       if (abox_log_auto_save)
+               abox_log_file_save(dev, info);
+
+       if (log_buffer->index_reader > index_writer) {
+               abox_log_memcpy(info->dev, kernel_buffer,
+                               log_buffer->buffer + log_buffer->index_reader,
+                               log_buffer->size - log_buffer->index_reader);
+               log_buffer->index_reader = 0;
+       }
+       abox_log_memcpy(info->dev, kernel_buffer,
+                       log_buffer->buffer + log_buffer->index_reader,
+                       index_writer - log_buffer->index_reader);
+       log_buffer->index_reader = index_writer;
+       mutex_unlock(&info->lock);
+
+       kernel_buffer->updated = true;
+       wake_up_interruptible(&kernel_buffer->wq);
+
+#ifdef TEST
+       dev_dbg(dev, "shared_buffer: %s\n", log_buffer->buffer);
+       dev_dbg(dev, "kernel_buffer: %s\n", info->kernel_buffer.buffer);
+#endif
+}
+
+void abox_log_flush_all(struct device *dev)
+{
+       struct abox_log_buffer_info *info;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       list_for_each_entry(info, &abox_log_list_head, list) {
+               abox_log_flush(info->dev, info);
+       }
+}
+EXPORT_SYMBOL(abox_log_flush_all);
+
+static unsigned long abox_log_flush_all_work_rearm_self;
+static void abox_log_flush_all_work_func(struct work_struct *work);
+static DECLARE_DEFERRABLE_WORK(abox_log_flush_all_work,
+               abox_log_flush_all_work_func);
+
+static void abox_log_flush_all_work_func(struct work_struct *work)
+{
+       abox_log_flush_all(NULL);
+       schedule_delayed_work(&abox_log_flush_all_work, msecs_to_jiffies(3000));
+       set_bit(0, &abox_log_flush_all_work_rearm_self);
+}
+
+void abox_log_schedule_flush_all(struct device *dev)
+{
+       if (test_and_clear_bit(0, &abox_log_flush_all_work_rearm_self))
+               cancel_delayed_work(&abox_log_flush_all_work);
+       schedule_delayed_work(&abox_log_flush_all_work, msecs_to_jiffies(100));
+}
+EXPORT_SYMBOL(abox_log_schedule_flush_all);
+
+void abox_log_drain_all(struct device *dev)
+{
+       cancel_delayed_work(&abox_log_flush_all_work);
+       abox_log_flush_all(dev);
+}
+EXPORT_SYMBOL(abox_log_drain_all);
+
+static int abox_log_file_open(struct inode *inode, struct  file *file)
+{
+       struct abox_log_buffer_info *info = inode->i_private;
+
+       dev_dbg(info->dev, "%s\n", __func__);
+
+       info->file_index = -1;
+       file->private_data = info;
+
+       return 0;
+}
+
+static ssize_t abox_log_file_read(struct file *file, char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct abox_log_buffer_info *info = file->private_data;
+       struct abox_log_kernel_buffer *kernel_buffer = &info->kernel_buffer;
+       unsigned int index;
+       size_t end, size;
+       bool first = (info->file_index < 0);
+       int ret;
+
+       dev_dbg(info->dev, "%s(%zu, %lld)\n", __func__, count, *ppos);
+
+       mutex_lock(&info->lock);
+
+       if (first) {
+               info->file_index = likely(kernel_buffer->wrap) ?
+                               kernel_buffer->index : 0;
+       }
+
+       do {
+               index = kernel_buffer->index;
+               end = ((info->file_index < index) ||
+                               ((info->file_index == index) && !first)) ?
+                               index : SIZE_OF_BUFFER;
+               size = min(end - info->file_index, count);
+               if (size == 0) {
+                       mutex_unlock(&info->lock);
+                       if (file->f_flags & O_NONBLOCK) {
+                               dev_dbg(info->dev, "non block\n");
+                               return -EAGAIN;
+                       }
+                       kernel_buffer->updated = false;
+
+                       ret = wait_event_interruptible(kernel_buffer->wq,
+                                       kernel_buffer->updated);
+                       if (ret != 0) {
+                               dev_dbg(info->dev, "interrupted\n");
+                               return ret;
+                       }
+                       mutex_lock(&info->lock);
+               }
+#ifdef VERBOSE_LOG
+               dev_dbg(info->dev, "loop %zu, %zu, %zd, %zu\n", size, end,
+                               info->file_index, count);
+#endif
+       } while (size == 0);
+
+       dev_dbg(info->dev, "start=%zd, end=%zd size=%zd\n", info->file_index,
+                       end, size);
+       if (copy_to_user(buf, kernel_buffer->buffer + info->file_index,
+                       size)) {
+               mutex_unlock(&info->lock);
+               return -EFAULT;
+       }
+
+       info->file_index += size;
+       if (info->file_index >= SIZE_OF_BUFFER)
+               info->file_index = 0;
+
+       mutex_unlock(&info->lock);
+
+       dev_dbg(info->dev, "%s: size = %zd\n", __func__, size);
+
+       return size;
+}
+
+static unsigned int abox_log_file_poll(struct file *file, poll_table *wait)
+{
+       struct abox_log_buffer_info *info = file->private_data;
+       struct abox_log_kernel_buffer *kernel_buffer = &info->kernel_buffer;
+
+       dev_dbg(info->dev, "%s\n", __func__);
+
+       poll_wait(file, &kernel_buffer->wq, wait);
+       return POLLIN | POLLRDNORM;
+}
+
+static const struct file_operations abox_log_fops = {
+       .open = abox_log_file_open,
+       .read = abox_log_file_read,
+       .poll = abox_log_file_poll,
+       .llseek = generic_file_llseek,
+       .owner = THIS_MODULE,
+};
+
+static struct abox_log_buffer_info abox_log_buffer_info_new;
+
+void abox_log_register_buffer_work_func(struct work_struct *work)
+{
+       struct device *dev;
+       int id;
+       struct ABOX_LOG_BUFFER *buffer;
+       struct abox_log_buffer_info *info;
+       char name[16];
+
+       dev = abox_log_buffer_info_new.dev;
+       id = abox_log_buffer_info_new.id;
+       buffer = abox_log_buffer_info_new.log_buffer;
+       abox_log_buffer_info_new.dev = NULL;
+       abox_log_buffer_info_new.id = 0;
+       abox_log_buffer_info_new.log_buffer = NULL;
+
+       dev_info(dev, "%s(%p, %d, %p)\n", __func__, dev, id, buffer);
+
+       info = vmalloc(sizeof(*info));
+       mutex_init(&info->lock);
+       info->id = id;
+       info->file_created = false;
+       info->kernel_buffer.buffer = vzalloc(SIZE_OF_BUFFER);
+       info->kernel_buffer.index = 0;
+       info->kernel_buffer.wrap = false;
+       init_waitqueue_head(&info->kernel_buffer.wq);
+       info->dev = dev;
+       info->log_buffer = buffer;
+       list_add_tail(&info->list, &abox_log_list_head);
+
+       snprintf(name, sizeof(name), "log-%02d", id);
+       debugfs_create_file(name, 0664, abox_dbg_get_root_dir(), info,
+                       &abox_log_fops);
+}
+
+static DECLARE_WORK(abox_log_register_buffer_work,
+               abox_log_register_buffer_work_func);
+
+int abox_log_register_buffer(struct device *dev, int id,
+               struct ABOX_LOG_BUFFER *buffer)
+{
+       struct abox_log_buffer_info *info;
+
+       dev_dbg(dev, "%s(%d, %p)\n", __func__, id, buffer);
+
+       if (abox_log_buffer_info_new.dev != NULL ||
+                       abox_log_buffer_info_new.id > 0 ||
+                       abox_log_buffer_info_new.log_buffer != NULL) {
+               return -EBUSY;
+       }
+
+       list_for_each_entry(info, &abox_log_list_head, list) {
+               if (info->id == id) {
+                       dev_dbg(dev, "already registered log: %d\n", id);
+                       return 0;
+               }
+       }
+
+       abox_log_buffer_info_new.dev = dev;
+       abox_log_buffer_info_new.id = id;
+       abox_log_buffer_info_new.log_buffer = buffer;
+       schedule_work(&abox_log_register_buffer_work);
+
+       return 0;
+}
+EXPORT_SYMBOL(abox_log_register_buffer);
+
+#ifdef TEST
+static struct ABOX_LOG_BUFFER *abox_log_test_buffer;
+static void abox_log_test_work_func(struct work_struct *work);
+DECLARE_DELAYED_WORK(abox_log_test_work, abox_log_test_work_func);
+static void abox_log_test_work_func(struct work_struct *work)
+{
+       struct ABOX_LOG_BUFFER *log = abox_log_test_buffer;
+       static unsigned int i;
+       char buffer[32];
+       char *buffer_index = buffer;
+       int size, left;
+
+       pr_debug("%s: %d\n", __func__, i);
+
+       size = snprintf(buffer, sizeof(buffer), "%d ", i++);
+
+       if (log->index_writer + size > log->size) {
+               left = log->size - log->index_writer;
+               memcpy(&log->buffer[log->index_writer], buffer_index, left);
+               log->index_writer = 0;
+               buffer_index += left;
+       }
+
+       left = size - (buffer_index - buffer);
+       memcpy(&log->buffer[log->index_writer], buffer_index, left);
+       log->index_writer += left;
+
+       abox_log_flush_all(NULL);
+
+       schedule_delayed_work(&abox_log_test_work, msecs_to_jiffies(1000));
+}
+#endif
+
+static int __init samsung_abox_log_late_initcall(void)
+{
+       pr_info("%s\n", __func__);
+
+       debugfs_create_u32("log_auto_save", S_IRWUG, abox_dbg_get_root_dir(),
+                       &abox_log_auto_save);
+
+#ifdef TEST
+       abox_log_test_buffer = vzalloc(SZ_128);
+       abox_log_test_buffer->size = SZ_64;
+       abox_log_register_buffer(NULL, 0, abox_log_test_buffer);
+       schedule_delayed_work(&abox_log_test_work, msecs_to_jiffies(1000));
+#endif
+
+       return 0;
+}
+late_initcall(samsung_abox_log_late_initcall);
diff --git a/sound/soc/samsung/abox/abox_log.h b/sound/soc/samsung/abox/abox_log.h
new file mode 100644 (file)
index 0000000..3d8e789
--- /dev/null
@@ -0,0 +1,53 @@
+/* sound/soc/samsung/abox/abox_log.h
+ *
+ * ALSA SoC - Samsung Abox Log driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_LOG_H
+#define __SND_SOC_ABOX_LOG_H
+
+#include <linux/device.h>
+#include <sound/samsung/abox.h>
+
+/**
+ * Flush log from all shared memories to kernel memory
+ * @param[in]  dev             pointer to abox device
+ */
+extern void abox_log_flush_all(struct device *dev);
+
+/**
+ * Schedule log flush from all shared memories to kernel memory
+ * @param[in]  dev             pointer to abox device
+ */
+extern void abox_log_schedule_flush_all(struct device *dev);
+
+/**
+ * drain log and stop scheduling log flush
+ * @param[in]  dev             pointer to abox device
+ */
+extern void abox_log_drain_all(struct device *dev);
+
+/**
+ * Flush log from specific shared memory to kernel memory
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  id              unique buffer id
+ */
+extern void abox_log_flush_by_id(struct device *dev, int id);
+
+/**
+ * Register abox log buffer
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  id              unique buffer id
+ * @param[in]  buffer          pointer to shared buffer
+ * @return     error code if any
+ */
+extern int abox_log_register_buffer(struct device *dev, int id,
+               struct ABOX_LOG_BUFFER *buffer);
+
+#endif /* __SND_SOC_ABOX_LOG_H */
diff --git a/sound/soc/samsung/abox/abox_rdma.c b/sound/soc/samsung/abox/abox_rdma.c
new file mode 100644 (file)
index 0000000..049b5d5
--- /dev/null
@@ -0,0 +1,1979 @@
+/* sound/soc/samsung/abox/abox_rdma.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox RDMA driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/iommu.h>
+#include <linux/delay.h>
+#include <linux/memblock.h>
+#include <linux/sched/clock.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "../../../../drivers/iommu/exynos-iommu.h"
+#include <sound/samsung/abox.h>
+#include "abox_util.h"
+#include "abox_gic.h"
+#include "abox_dbg.h"
+#ifdef CONFIG_SCSC_BT
+#include "abox_bt.h"
+#endif
+#include "abox.h"
+
+#define COMPR_USE_COPY
+#define COMPR_USE_FIXED_MEMORY
+#define USE_FIXED_MEMORY
+
+/* Mailbox between driver and firmware for offload */
+#define COMPR_CMD_CODE         (0x0004)
+#define COMPR_HANDLE_ID                (0x0008)
+#define COMPR_IP_TYPE          (0x000C)
+#define COMPR_SIZE_OF_FRAGMENT (0x0010)
+#define COMPR_PHY_ADDR_INBUF   (0x0014)
+#define COMPR_SIZE_OF_INBUF    (0x0018)
+#define COMPR_LEFT_VOL         (0x001C)
+#define COMPR_RIGHT_VOL                (0x0020)
+#define EFFECT_EXT_ON          (0x0024)
+#define COMPR_ALPA_NOTI                (0x0028)
+#define COMPR_PARAM_RATE       (0x0034)
+#define COMPR_PARAM_SAMPLE     (0x0038)
+#define COMPR_PARAM_CH         (0x003C)
+#define COMPR_RENDERED_PCM_SIZE        (0x004C)
+#define COMPR_RETURN_CMD       (0x0040)
+#define COMPR_IP_ID            (0x0044)
+#define COMPR_SIZE_OUT_DATA    (0x0048)
+#define COMPR_UPSCALE          (0x0050)
+#define COMPR_CPU_LOCK_LV      (0x0054)
+#define COMPR_CHECK_CMD                (0x0058)
+#define COMPR_CHECK_RUNNING    (0x005C)
+#define COMPR_ACK              (0x0060)
+#define COMPR_INTR_ACK         (0x0064)
+#define COMPR_INTR_DMA_ACK     (0x0068)
+#define COMPR_MAX              COMPR_INTR_DMA_ACK
+
+/* COMPR_UPSCALE */
+#define COMPR_BIT_SHIFT                (0)
+#define COMPR_BIT_MASK         (0xFF)
+#define COMPR_CH_SHIFT         (8)
+#define COMPR_CH_MASK          (0xF)
+#define COMPR_RATE_SHIFT       (12)
+#define COMPR_RATE_MASK                (0xFFFFF)
+
+/* Interrupt type */
+#define INTR_WAKEUP            (0x0)
+#define INTR_READY             (0x1000)
+#define INTR_DMA               (0x2000)
+#define INTR_CREATED           (0x3000)
+#define INTR_DECODED           (0x4000)
+#define INTR_RENDERED          (0x5000)
+#define INTR_FLUSH             (0x6000)
+#define INTR_PAUSED            (0x6001)
+#define INTR_EOS               (0x7000)
+#define INTR_DESTROY           (0x8000)
+#define INTR_FX_EXT            (0x9000)
+#define INTR_EFF_REQUEST       (0xA000)
+#define INTR_SET_CPU_LOCK      (0xC000)
+#define INTR_FW_LOG            (0xFFFF)
+
+#define COMPRESSED_LR_VOL_MAX_STEPS     0x2000
+
+enum SEIREN_CMDTYPE {
+       /* OFFLOAD */
+       CMD_COMPR_CREATE = 0x50,
+       CMD_COMPR_DESTROY,
+       CMD_COMPR_SET_PARAM,
+       CMD_COMPR_WRITE,
+       CMD_COMPR_READ,
+       CMD_COMPR_START,
+       CMD_COMPR_STOP,
+       CMD_COMPR_PAUSE,
+       CMD_COMPR_EOS,
+       CMD_COMPR_GET_VOLUME,
+       CMD_COMPR_SET_VOLUME,
+       CMD_COMPR_CA5_WAKEUP,
+       CMD_COMPR_HPDET_NOTIFY,
+};
+
+enum OFFLOAD_IPTYPE {
+       COMPR_MP3 = 0x0,
+       COMPR_AAC = 0x1,
+       COMPR_FLAC = 0x2,
+};
+
+static const struct snd_compr_caps abox_rdma_compr_caps = {
+       .direction              = SND_COMPRESS_PLAYBACK,
+       .min_fragment_size      = SZ_4K,
+       .max_fragment_size      = SZ_32K,
+       .min_fragments          = 1,
+       .max_fragments          = 5,
+       .num_codecs             = 3,
+       .codecs                 = {
+               SND_AUDIOCODEC_MP3,
+               SND_AUDIOCODEC_AAC,
+               SND_AUDIOCODEC_FLAC
+       },
+};
+
+static void abox_rdma_mailbox_write(struct device *dev, u32 index, u32 value)
+{
+       struct regmap *regmap = dev_get_regmap(dev, NULL);
+       int ret;
+
+       dev_dbg(dev, "%s(0x%x, 0x%x)\n", __func__, index, value);
+
+       if (!regmap) {
+               dev_err(dev, "%s: regmap is null\n", __func__);
+               return;
+       }
+
+       pm_runtime_get(dev);
+       ret = regmap_write(regmap, index, value);
+       if (ret < 0)
+               dev_warn(dev, "%s(%u) failed: %d\n", __func__, index, ret);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+}
+
+static u32 abox_rdma_mailbox_read(struct device *dev, u32 index)
+{
+       struct regmap *regmap = dev_get_regmap(dev, NULL);
+       int ret;
+       u32 val = 0;
+
+       dev_dbg(dev, "%s(0x%x)\n", __func__, index);
+
+       if (!regmap) {
+               dev_err(dev, "%s: regmap is null\n", __func__);
+               return 0;
+       }
+
+       pm_runtime_get(dev);
+       ret = regmap_read(regmap, index, &val);
+       if (ret < 0)
+               dev_warn(dev, "%s(%u) failed: %d\n", __func__, index, ret);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       return val;
+}
+
+static void abox_mailbox_save(struct device *dev)
+{
+       struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+       if (regmap) {
+               regcache_cache_only(regmap, true);
+               regcache_mark_dirty(regmap);
+       }
+}
+
+static void abox_mailbox_restore(struct device *dev)
+{
+       struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+       if (regmap) {
+               regcache_cache_only(regmap, false);
+               regcache_sync(regmap);
+       }
+}
+
+static bool abox_mailbox_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case COMPR_ACK:
+       case COMPR_INTR_ACK:
+       case COMPR_INTR_DMA_ACK:
+       case COMPR_RETURN_CMD:
+       case COMPR_SIZE_OUT_DATA:
+       case COMPR_IP_ID:
+       case COMPR_RENDERED_PCM_SIZE:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool abox_mailbox_rw_reg(struct device *dev, unsigned int reg)
+{
+       return true;
+}
+
+static const struct regmap_config abox_mailbox_config = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .max_register = COMPR_MAX,
+       .volatile_reg = abox_mailbox_volatile_reg,
+       .readable_reg = abox_mailbox_rw_reg,
+       .writeable_reg = abox_mailbox_rw_reg,
+       .cache_type = REGCACHE_FLAT,
+       .fast_io = true,
+};
+
+static int abox_rdma_request_ipc(struct abox_platform_data *data,
+               ABOX_IPC_MSG *msg, int atomic, int sync)
+{
+       struct device *dev_abox = &data->pdev_abox->dev;
+
+       return abox_request_ipc(dev_abox, msg->ipcid, msg, sizeof(*msg),
+                       atomic, sync);
+}
+
+static int abox_rdma_mailbox_send_cmd(struct device *dev, unsigned int cmd)
+{
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct device *dev_abox = &platform_data->pdev_abox->dev;
+       struct abox_compr_data *data = &platform_data->compr_data;
+       ABOX_IPC_MSG ipc;
+       int ret, n, ack;
+
+       dev_dbg(dev, "%s(%x)\n", __func__, cmd);
+
+       switch (cmd) {
+       case CMD_COMPR_CREATE:
+               dev_dbg(dev, "%s: CMD_COMPR_CREATE %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_DESTROY:
+               dev_dbg(dev, "%s: CMD_COMPR_DESTROY %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_SET_PARAM:
+               dev_dbg(dev, "%s: CMD_COMPR_SET_PARAM %d\n", __func__, cmd);
+#ifdef CONFIG_SND_ESA_SA_EFFECT
+               abox_rdma_mailbox_write(dev, abox_data, COMPR_PARAM_RATE,
+                               data->out_sample_rate);
+#endif
+               break;
+       case CMD_COMPR_WRITE:
+               dev_dbg(dev, "%s: CMD_COMPR_WRITE %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_READ:
+               dev_dbg(dev, "%s: CMD_COMPR_READ %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_START:
+               dev_dbg(dev, "%s: CMD_COMPR_START %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_STOP:
+               dev_dbg(dev, "%s: CMD_COMPR_STOP %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_PAUSE:
+               dev_dbg(dev, "%s: CMD_COMPR_PAUSE %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_EOS:
+               dev_dbg(dev, "%s: CMD_COMPR_EOS %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_GET_VOLUME:
+               dev_dbg(dev, "%s: CMD_COMPR_GET_VOLUME %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_SET_VOLUME:
+               dev_dbg(dev, "%s: CMD_COMPR_SET_VOLUME %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_CA5_WAKEUP:
+               dev_dbg(dev, "%s: CMD_COMPR_CA5_WAKEUP %d\n", __func__, cmd);
+               break;
+       case CMD_COMPR_HPDET_NOTIFY:
+               dev_dbg(dev, "%s: CMD_COMPR_HPDET_NOTIFY %d\n", __func__, cmd);
+               break;
+       default:
+               dev_err(dev, "%s: unknown cmd %d\n", __func__, cmd);
+               return -EINVAL;
+       }
+
+       spin_lock(&data->cmd_lock);
+
+       abox_rdma_mailbox_write(dev, COMPR_HANDLE_ID, data->handle_id);
+       abox_rdma_mailbox_write(dev, COMPR_CMD_CODE, cmd);
+       ret = abox_request_ipc(dev_abox, IPC_OFFLOAD, &ipc, 0, 1, 1);
+
+       for (n = 0, ack = 0; n < 2000; n++) {
+               /* Wait for ACK */
+               if (abox_rdma_mailbox_read(dev, COMPR_ACK)) {
+                       ack = 1;
+                       break;
+               }
+               udelay(100);
+       }
+       /* clear ACK */
+       abox_rdma_mailbox_write(dev, COMPR_ACK, 0);
+
+       spin_unlock(&data->cmd_lock);
+
+       if (!ack) {
+               dev_err(dev, "%s: No ack error!(%x)", __func__, cmd);
+               ret = -EFAULT;
+       }
+
+       return ret;
+}
+
+static void abox_rdma_compr_clear_intr_ack(struct device *dev)
+{
+       abox_rdma_mailbox_write(dev, COMPR_INTR_ACK, 0);
+}
+
+static int abox_rdma_compr_isr_handler(void *priv)
+{
+       struct platform_device *pdev = priv;
+       struct device *dev = &pdev->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct abox_compr_data *data = &platform_data->compr_data;
+       int id = platform_data->id;
+       unsigned long flags;
+       u32 val, fw_stat;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       val = abox_rdma_mailbox_read(dev, COMPR_RETURN_CMD);
+
+       if (val == 1)
+               dev_err(dev, "%s: There is possibility of firmware CMD fail %u\n",
+                                               __func__, val);
+       fw_stat = val >> 16;
+       dev_dbg(dev, "fw_stat(%08x), val(%08x)\n", fw_stat, val);
+
+       switch (fw_stat) {
+       case INTR_CREATED:
+               dev_info(dev, "INTR_CREATED\n");
+               abox_rdma_compr_clear_intr_ack(dev);
+               data->created = true;
+               break;
+       case INTR_DECODED:
+               /* check the error */
+               val &= 0xFF;
+               if (val) {
+                       dev_err(dev, "INTR_DECODED err(%d)\n", val);
+               } else if (data->cstream && data->cstream->runtime) {
+                       /* update copied total bytes */
+                       u32 size = abox_rdma_mailbox_read(dev,
+                                       COMPR_SIZE_OUT_DATA);
+                       struct snd_compr_stream *cstream = data->cstream;
+                       struct snd_compr_runtime *runtime = cstream->runtime;
+
+                       dev_dbg(dev, "INTR_DECODED(%d)\n", size);
+
+                       spin_lock_irqsave(&data->lock, flags);
+                       /* update copied total bytes */
+                       data->copied_total += size;
+                       data->byte_offset += size;
+                       if (data->byte_offset >= runtime->buffer_size)
+                               data->byte_offset -= runtime->buffer_size;
+                       spin_unlock_irqrestore(&data->lock, flags);
+
+                       snd_compr_fragment_elapsed(data->cstream);
+
+                       if (!data->start &&
+                               runtime->state != SNDRV_PCM_STATE_PAUSED) {
+                               /* Writes must be restarted from _copy() */
+                               dev_err(dev, "%s: write_done received while not started(%d)",
+                                       __func__, runtime->state);
+                       } else {
+                               u64 bytes_available = data->received_total -
+                                               data->copied_total;
+
+                               dev_dbg(dev, "%s: current free bufsize(%llu)\n",
+                                               __func__, runtime->buffer_size -
+                                               bytes_available);
+
+                               if (bytes_available < runtime->fragment_size) {
+                                       dev_dbg(dev, "%s: WRITE_DONE Insufficient data to send.(avail:%llu)\n",
+                                               __func__, bytes_available);
+                               }
+                       }
+               } else {
+                       dev_dbg(dev, "%s: INTR_DECODED after compress offload end\n",
+                                       __func__);
+               }
+               abox_rdma_compr_clear_intr_ack(dev);
+               break;
+       case INTR_FLUSH:
+               /* check the error */
+               val &= 0xFF;
+               if (val) {
+                       dev_err(dev, "INTR_FLUSH err(%d)\n", val);
+               } else {
+                       /* flush done */
+                       data->stop_ack = 1;
+                       wake_up_interruptible(&data->flush_wait);
+               }
+               abox_rdma_compr_clear_intr_ack(dev);
+               break;
+       case INTR_PAUSED:
+               /* check the error */
+               val &= 0xFF;
+               if (val)
+                       dev_err(dev, "INTR_PAUSED err(%d)\n", val);
+
+               abox_rdma_compr_clear_intr_ack(dev);
+               break;
+       case INTR_EOS:
+               if (data->eos) {
+                       if (data->copied_total != data->received_total)
+                               dev_err(dev, "%s: EOS is not sync!(%llu/%llu)\n",
+                                               __func__, data->copied_total,
+                                               data->received_total);
+
+                       /* ALSA Framework callback to notify drain complete */
+                       snd_compr_drain_notify(data->cstream);
+                       data->eos = 0;
+                       dev_info(dev, "%s: DATA_CMD_EOS wake up\n", __func__);
+               }
+               abox_rdma_compr_clear_intr_ack(dev);
+               break;
+       case INTR_DESTROY:
+               /* check the error */
+               val &= 0xFF;
+               if (val) {
+                       dev_err(dev, "INTR_DESTROY err(%d)\n", val);
+               } else {
+                       /* destroied */
+                       data->exit_ack = 1;
+                       wake_up_interruptible(&data->exit_wait);
+               }
+               abox_rdma_compr_clear_intr_ack(dev);
+               break;
+       case INTR_FX_EXT:
+               /* To Do */
+               abox_rdma_compr_clear_intr_ack(dev);
+               break;
+#ifdef CONFIG_SND_SAMSUNG_ELPE
+       case INTR_EFF_REQUEST:
+               /*To Do */
+               abox_rdma_compr_clear_intr_ack(dev);
+               break;
+#endif
+       }
+
+       wake_up_interruptible(&data->ipc_wait);
+
+       return IRQ_HANDLED;
+}
+
+static int abox_rdma_compr_set_param(struct platform_device *pdev,
+               struct snd_compr_runtime *runtime)
+{
+       struct device *dev = &pdev->dev;
+       struct abox_platform_data *platform_data = platform_get_drvdata(pdev);
+       struct abox_compr_data *data = &platform_data->compr_data;
+       int id = platform_data->id;
+       int ret;
+
+       dev_info(dev, "%s[%d] buffer: %p(%llu)\n", __func__, id,
+                       runtime->buffer, runtime->buffer_size);
+
+#ifdef COMPR_USE_FIXED_MEMORY
+       /* free memory allocated by ALSA */
+       kfree(runtime->buffer);
+
+       runtime->buffer = data->dma_area;
+       if (runtime->buffer_size > data->dma_size) {
+               dev_err(dev, "allocated buffer size is smaller than requested(%llu > %zu)\n",
+                               runtime->buffer_size, data->dma_size);
+               ret = -ENOMEM;
+               goto error;
+       }
+#else
+#ifdef COMPR_USE_COPY
+       runtime->buffer = dma_alloc_coherent(dev, runtime->buffer_size,
+                       &data->dma_addr, GFP_KERNEL);
+       if (!runtime->buffer) {
+               dev_err(dev, "dma memory allocation failed (size=%llu)\n",
+                               runtime->buffer_size);
+               ret = -ENOMEM;
+               goto error;
+       }
+#else
+       data->dma_addr = dma_map_single(dev, runtime->buffer,
+                       runtime->buffer_size, DMA_TO_DEVICE);
+       ret = dma_mapping_error(dev, data->dma_addr);
+       if (ret) {
+               dev_err(dev, "dma memory mapping failed(%d)\n", ret);
+               goto error;
+       }
+#endif
+       ret = iommu_map(platform_data->abox_data->iommu_domain,
+                       IOVA_COMPR_BUFFER(id), virt_to_phys(runtime->buffer),
+                       round_up(runtime->buffer_size, PAGE_SIZE), 0);
+       if (ret < 0) {
+               dev_err(dev, "iommu mapping failed(%d)\n", ret);
+               goto error;
+       }
+#endif
+       /* set buffer information at mailbox */
+       abox_rdma_mailbox_write(dev, COMPR_SIZE_OF_INBUF, runtime->buffer_size);
+       abox_rdma_mailbox_write(dev, COMPR_PHY_ADDR_INBUF,
+                       IOVA_COMPR_BUFFER(id));
+       abox_rdma_mailbox_write(dev, COMPR_PARAM_SAMPLE, data->sample_rate);
+       abox_rdma_mailbox_write(dev, COMPR_PARAM_CH, data->channels);
+       abox_rdma_mailbox_write(dev, COMPR_IP_TYPE, data->codec_id << 16);
+       data->created = false;
+       ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_SET_PARAM);
+       if (ret < 0) {
+               dev_err(dev, "CMD_COMPR_SET_PARAM failed(%d)\n", ret);
+               goto error;
+       }
+
+       /* wait until the parameter is set up */
+       ret = wait_event_interruptible_timeout(data->ipc_wait,
+                       data->created, msecs_to_jiffies(1000));
+       if (!ret) {
+               dev_err(dev, "%s: compress set param timed out!!! (%d)\n",
+                       __func__, ret);
+               abox_rdma_mailbox_write(dev, COMPR_INTR_ACK, 0);
+               ret = -EBUSY;
+               goto error;
+       }
+
+       /* created instance */
+       data->handle_id = abox_rdma_mailbox_read(dev, COMPR_IP_ID);
+       dev_info(dev, "%s: codec id:0x%x, ret_val:0x%x, handle_id:0x%x\n",
+               __func__, data->codec_id,
+               abox_rdma_mailbox_read(dev, COMPR_RETURN_CMD),
+               data->handle_id);
+
+       dev_info(dev, "%s: allocated buffer address (0x%pad), size(0x%llx)\n",
+               __func__, &data->dma_addr, runtime->buffer_size);
+#ifdef CONFIG_SND_ESA_SA_EFFECT
+       data->effect_on = false;
+#endif
+
+       return 0;
+
+error:
+       return ret;
+}
+
+static int abox_rdma_compr_open(struct snd_compr_stream *stream)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct abox_compr_data *data = &platform_data->compr_data;
+       struct abox_data *abox_data = platform_data->abox_data;
+       int id = platform_data->id;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       /* init runtime data */
+       data->cstream = stream;
+       data->byte_offset = 0;
+       data->copied_total = 0;
+       data->received_total = 0;
+       data->sample_rate = 44100;
+       data->channels = 0x3; /* stereo channel mask */
+
+       data->eos = false;
+       data->start = false;
+       data->created = false;
+
+       pm_runtime_get_sync(dev);
+       abox_request_cpu_gear(dev, abox_data, dev, abox_data->cpu_gear_min);
+
+       return 0;
+}
+
+static int abox_rdma_compr_free(struct snd_compr_stream *stream)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct abox_compr_data *data = &platform_data->compr_data;
+       struct abox_data *abox_data = platform_data->abox_data;
+       int id = platform_data->id;
+       int ret = 0;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       if (data->eos) {
+               /* ALSA Framework callback to notify drain complete */
+               snd_compr_drain_notify(stream);
+               data->eos = 0;
+               dev_dbg(dev, "%s Call Drain notify to wakeup\n", __func__);
+       }
+
+       if (data->created) {
+               data->created = false;
+               data->exit_ack = 0;
+
+               ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_DESTROY);
+               if (ret) {
+                       dev_err(dev, "%s: can't send CMD_COMPR_DESTROY (%d)\n",
+                                       __func__, ret);
+               } else {
+                       ret = wait_event_interruptible_timeout(data->exit_wait,
+                                       data->exit_ack, msecs_to_jiffies(1000));
+                       if (!ret)
+                               dev_err(dev, "%s: CMD_DESTROY timed out!!!\n",
+                                               __func__);
+               }
+       }
+
+#ifdef COMPR_USE_FIXED_MEMORY
+       /* prevent kfree in ALSA */
+       stream->runtime->buffer = NULL;
+#else
+{
+       struct snd_compr_runtime *runtime = stream->runtime;
+
+       iommu_unmap(abox_data->iommu_domain, IOVA_COMPR_BUFFER(id),
+                       round_up(runtime->buffer_size, PAGE_SIZE));
+       exynos_sysmmu_tlb_invalidate(abox_data->iommu_domain,
+                       (dma_addr_t)IOVA_COMPR_BUFFER(id),
+                       round_up(runtime->buffer_size, PAGE_SIZE));
+
+#ifdef COMPR_USE_COPY
+       dma_free_coherent(dev, runtime->buffer_size, runtime->buffer,
+                       data->dma_addr);
+       runtime->buffer = NULL;
+#else
+       dma_unmap_single(dev, data->dma_addr, runtime->buffer_size,
+                       DMA_TO_DEVICE);
+#endif
+}
+#endif
+
+       abox_request_cpu_gear(dev, abox_data, dev, ABOX_CPU_GEAR_MIN);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put(dev);
+
+       return ret;
+}
+
+static int abox_rdma_compr_set_params(struct snd_compr_stream *stream,
+                           struct snd_compr_params *params)
+{
+       struct snd_compr_runtime *runtime = stream->runtime;
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct abox_compr_data *data = &platform_data->compr_data;
+       int id = platform_data->id;
+       int ret = 0;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       /* COMPR set_params */
+       memcpy(&data->codec_param, params, sizeof(data->codec_param));
+
+       data->byte_offset = 0;
+       data->copied_total = 0;
+       data->channels = data->codec_param.codec.ch_in;
+       data->sample_rate = data->codec_param.codec.sample_rate;
+
+       if (data->sample_rate == 0 ||
+               data->channels == 0) {
+               dev_err(dev, "%s: invalid parameters: sample(%u), ch(%u)\n",
+                       __func__, data->sample_rate, data->channels);
+               return -EINVAL;
+       }
+
+       switch (params->codec.id) {
+       case SND_AUDIOCODEC_MP3:
+               data->codec_id = COMPR_MP3;
+               break;
+       case SND_AUDIOCODEC_AAC:
+               data->codec_id = COMPR_AAC;
+               break;
+       case SND_AUDIOCODEC_FLAC:
+               data->codec_id = COMPR_FLAC;
+               break;
+       default:
+               dev_err(dev, "%s: unknown codec id %d\n", __func__,
+                               params->codec.id);
+               break;
+       }
+
+       ret = abox_rdma_compr_set_param(pdev, runtime);
+       if (ret) {
+               dev_err(dev, "%s: esa_compr_set_param fail(%d)\n", __func__,
+                               ret);
+               return ret;
+       }
+
+       dev_info(dev, "%s: sample rate:%u, channels:%u\n", __func__,
+               data->sample_rate, data->channels);
+       return 0;
+}
+
+static int abox_rdma_compr_set_metadata(struct snd_compr_stream *stream,
+                             struct snd_compr_metadata *metadata)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       int id = platform_data->id;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       if (!metadata)
+               return -EINVAL;
+
+       if (metadata->key == SNDRV_COMPRESS_ENCODER_PADDING)
+               dev_dbg(dev, "%s: got encoder padding %u", __func__,
+                               metadata->value[0]);
+       else if (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY)
+               dev_dbg(dev, "%s: got encoder delay %u", __func__,
+                               metadata->value[0]);
+
+       return 0;
+}
+
+static int abox_rdma_compr_trigger(struct snd_compr_stream *stream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct abox_compr_data *data = &platform_data->compr_data;
+       int id = platform_data->id;
+       int ret = 0;
+
+       dev_info(dev, "%s[%d](%d)\n", __func__, id, cmd);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               dev_info(dev, "SNDRV_PCM_TRIGGER_PAUSE_PUSH\n");
+               ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_PAUSE);
+               if (ret < 0)
+                       dev_err(dev, "%s: pause cmd failed(%d)\n", __func__,
+                                       ret);
+
+               abox_request_dram_on(platform_data->pdev_abox, dev, false);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               dev_info(dev, "SNDRV_PCM_TRIGGER_STOP\n");
+
+               if (data->eos) {
+                       /* ALSA Framework callback to notify drain complete */
+                       snd_compr_drain_notify(stream);
+                       data->eos = 0;
+                       dev_dbg(dev, "interrupt drain and eos wait queues\n");
+               }
+
+               data->stop_ack = 0;
+               ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_STOP);
+               if (ret < 0)
+                       dev_err(dev, "%s: stop cmd failed (%d)\n",
+                               __func__, ret);
+
+               ret = wait_event_interruptible_timeout(data->flush_wait,
+                       data->stop_ack, msecs_to_jiffies(1000));
+               if (!ret) {
+                       dev_err(dev, "CMD_STOP cmd timeout(%d)\n", ret);
+                       ret = -ETIMEDOUT;
+               } else {
+                       ret = 0;
+               }
+
+               data->start = false;
+
+               /* reset */
+               data->stop_ack = 0;
+               data->byte_offset = 0;
+               data->copied_total = 0;
+               data->received_total = 0;
+
+               abox_request_dram_on(platform_data->pdev_abox, dev, false);
+               break;
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               dev_info(dev, "%s: %s", __func__,
+                               (cmd == SNDRV_PCM_TRIGGER_START) ?
+                               "SNDRV_PCM_TRIGGER_START" :
+                               "SNDRV_PCM_TRIGGER_PAUSE_RELEASE");
+
+               abox_request_dram_on(platform_data->pdev_abox, dev, true);
+
+               data->start = 1;
+               ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_START);
+               if (ret < 0)
+                       dev_err(dev, "%s: start cmd failed\n", __func__);
+
+               break;
+       case SND_COMPR_TRIGGER_NEXT_TRACK:
+               pr_info("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__);
+               break;
+       case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+       case SND_COMPR_TRIGGER_DRAIN:
+               dev_info(dev, "%s: %s", __func__,
+                               (cmd == SND_COMPR_TRIGGER_DRAIN) ?
+                               "SND_COMPR_TRIGGER_DRAIN" :
+                               "SND_COMPR_TRIGGER_PARTIAL_DRAIN");
+               /* Make sure all the data is sent to F/W before sending EOS */
+               if (!data->start) {
+                       dev_err(dev, "%s: stream is not in started state\n",
+                               __func__);
+                       ret = -EPERM;
+                       break;
+               }
+
+               data->eos = 1;
+               dev_dbg(dev, "%s: CMD_EOS\n", __func__);
+               ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_EOS);
+               if (ret < 0)
+                       dev_err(dev, "%s: can't send eos (%d)\n", __func__,
+                                       ret);
+               else
+                       pr_info("%s: Out of %s Drain", __func__,
+                                       (cmd == SND_COMPR_TRIGGER_DRAIN ?
+                                       "Full" : "Partial"));
+
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int abox_rdma_compr_pointer(struct snd_compr_stream *stream,
+                        struct snd_compr_tstamp *tstamp)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct abox_compr_data *data = &platform_data->compr_data;
+       int id = platform_data->id;
+       unsigned int num_channel;
+       u32 pcm_size;
+       unsigned long flags;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       spin_lock_irqsave(&data->lock, flags);
+       tstamp->sampling_rate = data->sample_rate;
+       tstamp->byte_offset = data->byte_offset;
+       tstamp->copied_total = data->copied_total;
+       spin_unlock_irqrestore(&data->lock, flags);
+
+       pcm_size = abox_rdma_mailbox_read(dev, COMPR_RENDERED_PCM_SIZE);
+
+       /* set the number of channels */
+       num_channel = hweight32(data->channels);
+
+       if (pcm_size) {
+               tstamp->pcm_io_frames = pcm_size / (2 * num_channel);
+               dev_dbg(dev, "%s: pcm_size(%u), frame_count(%u), copied_total(%u)\n",
+                               __func__, pcm_size, tstamp->pcm_io_frames,
+                               tstamp->copied_total);
+
+       }
+
+       return 0;
+}
+
+static int abox_rdma_compr_mmap(struct snd_compr_stream *stream,
+               struct vm_area_struct *vma)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       int id = platform_data->id;
+       struct snd_compr_runtime *runtime = stream->runtime;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       return dma_mmap_writecombine(dev, vma,
+                       runtime->buffer,
+                       virt_to_phys(runtime->buffer),
+                       runtime->buffer_size);
+}
+
+static int abox_rdma_compr_ack(struct snd_compr_stream *stream, size_t bytes)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct abox_compr_data *data = &platform_data->compr_data;
+       int id = platform_data->id;
+       int ret;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       /* write mp3 data to firmware */
+       data->received_total += bytes;
+       abox_rdma_mailbox_write(dev, COMPR_SIZE_OF_FRAGMENT, bytes);
+       ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_WRITE);
+
+       return ret;
+}
+
+#ifdef COMPR_USE_COPY
+static int abox_compr_write_data(struct snd_compr_stream *stream,
+              const char __user *buf, size_t count)
+{
+       void *dstn;
+       size_t copy;
+       struct snd_compr_runtime *runtime = stream->runtime;
+       /* 64-bit Modulus */
+       u64 app_pointer = div64_u64(runtime->total_bytes_available,
+                                   runtime->buffer_size);
+       app_pointer = runtime->total_bytes_available -
+                     (app_pointer * runtime->buffer_size);
+       dstn = runtime->buffer + app_pointer;
+
+       pr_debug("copying %ld at %lld\n",
+                       (unsigned long)count, app_pointer);
+
+       if (count < runtime->buffer_size - app_pointer) {
+               if (copy_from_user(dstn, buf, count))
+                       return -EFAULT;
+       } else {
+               copy = runtime->buffer_size - app_pointer;
+               if (copy_from_user(dstn, buf, copy))
+                       return -EFAULT;
+               if (copy_from_user(runtime->buffer, buf + copy, count - copy))
+                       return -EFAULT;
+       }
+       abox_rdma_compr_ack(stream, count);
+
+       return count;
+}
+
+static int abox_rdma_compr_copy(struct snd_compr_stream *stream,
+               char __user *buf, size_t count)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       int id = platform_data->id;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       return abox_compr_write_data(stream, buf, count);
+}
+#endif
+
+static int abox_rdma_compr_get_caps(struct snd_compr_stream *stream,
+               struct snd_compr_caps *caps)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       int id = platform_data->id;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       memcpy(caps, &abox_rdma_compr_caps, sizeof(*caps));
+
+       return 0;
+}
+
+static int abox_rdma_compr_get_codec_caps(struct snd_compr_stream *stream,
+               struct snd_compr_codec_caps *codec)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       int id = platform_data->id;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       return 0;
+}
+
+static void __abox_rdma_compr_get_hw_params_legacy(struct device *dev,
+               struct snd_pcm_hw_params *params, unsigned int upscale)
+{
+       struct snd_mask *format_mask;
+       struct snd_interval *rate_interval;
+
+       rate_interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       format_mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       switch (upscale) {
+       default:
+               /* fallback */
+       case 0:
+               /* 48kHz 16bit */
+               rate_interval->min = 48000;
+               snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S16);
+               dev_info(dev, "%s: 48kHz 16bit\n", __func__);
+               break;
+       case 1:
+               /* 192kHz 24bit */
+               rate_interval->min = 192000;
+               snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S24);
+               dev_info(dev, "%s: 192kHz 24bit\n", __func__);
+               break;
+       case 2:
+               /* 48kHz 24bit */
+               rate_interval->min = 48000;
+               snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S24);
+               dev_info(dev, "%s: 48kHz 24bit\n", __func__);
+               break;
+       case 3:
+               /* 384kHz 32bit */
+               rate_interval->min = 384000;
+               snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S32);
+               dev_info(dev, "%s: 384kHz 32bit\n", __func__);
+               break;
+       }
+}
+
+static void __abox_rdma_compr_get_hw_params(struct device *dev,
+               struct snd_pcm_hw_params *params, unsigned int upscale)
+{
+       unsigned int bit, ch, rate;
+       struct snd_mask *format_mask;
+       struct snd_interval *rate_interval, *ch_interval;
+
+       format_mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       ch_interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       rate_interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       bit = (upscale >> COMPR_BIT_SHIFT) & COMPR_BIT_MASK;
+       ch = (upscale >> COMPR_CH_SHIFT) & COMPR_CH_MASK;
+       rate = (upscale >> COMPR_RATE_SHIFT) & COMPR_RATE_MASK;
+
+       switch (bit) {
+       default:
+               /* fallback */
+       case 16:
+               snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S16);
+               break;
+       case 24:
+               snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S24);
+               break;
+       case 32:
+               snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S32);
+               break;
+       }
+       ch_interval->min = ch ? ch : 2;
+       rate_interval->min = rate ? rate : 48000;
+
+       dev_info(dev, "%s: %ubit %uch %uHz\n", __func__, bit, ch, rate);
+}
+
+static int abox_rdma_compr_get_hw_params(struct snd_compr_stream *stream,
+               struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       int id = platform_data->id;
+       unsigned int upscale;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       upscale = abox_rdma_mailbox_read(dev, COMPR_UPSCALE);
+       if (upscale <= 0xF)
+               __abox_rdma_compr_get_hw_params_legacy(dev, params, upscale);
+       else
+               __abox_rdma_compr_get_hw_params(dev, params, upscale);
+
+       return 0;
+}
+
+static struct snd_compr_ops abox_rdma_compr_ops = {
+       .open           = abox_rdma_compr_open,
+       .free           = abox_rdma_compr_free,
+       .set_params     = abox_rdma_compr_set_params,
+       .set_metadata   = abox_rdma_compr_set_metadata,
+       .trigger        = abox_rdma_compr_trigger,
+       .pointer        = abox_rdma_compr_pointer,
+#ifdef COMPR_USE_COPY
+       .copy           = abox_rdma_compr_copy,
+#endif
+       .mmap           = abox_rdma_compr_mmap,
+       .ack            = abox_rdma_compr_ack,
+       .get_caps       = abox_rdma_compr_get_caps,
+       .get_codec_caps = abox_rdma_compr_get_codec_caps,
+       .get_hw_params  = abox_rdma_compr_get_hw_params,
+};
+
+static int abox_rdma_compr_vol_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+                       (struct soc_mixer_control *)kcontrol->private_value;
+       int id = platform_data->id;
+       unsigned int volumes[2];
+
+       volumes[0] = (unsigned int)ucontrol->value.integer.value[0];
+       volumes[1] = (unsigned int)ucontrol->value.integer.value[1];
+       dev_dbg(dev, "%s[%d]: left_vol=%d right_vol=%d\n",
+                       __func__, id, volumes[0], volumes[1]);
+
+       abox_rdma_mailbox_write(dev, mc->reg, volumes[0]);
+       abox_rdma_mailbox_write(dev, mc->rreg, volumes[1]);
+
+       return 0;
+}
+
+static int abox_rdma_compr_vol_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct soc_mixer_control *mc =
+                       (struct soc_mixer_control *)kcontrol->private_value;
+       int id = platform_data->id;
+       unsigned int volumes[2];
+
+       volumes[0] = abox_rdma_mailbox_read(dev, mc->reg);
+       volumes[1] = abox_rdma_mailbox_read(dev, mc->rreg);
+       dev_dbg(dev, "%s[%d]: left_vol=%d right_vol=%d\n",
+                       __func__, id, volumes[0], volumes[1]);
+
+       ucontrol->value.integer.value[0] = volumes[0];
+       ucontrol->value.integer.value[1] = volumes[1];
+
+       return 0;
+}
+
+static const DECLARE_TLV_DB_LINEAR(abox_rdma_compr_vol_gain, 0,
+               COMPRESSED_LR_VOL_MAX_STEPS);
+
+static int abox_rdma_compr_format_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int id = platform_data->id;
+       unsigned int upscale;
+
+       dev_warn(dev, "%s is deprecated\n", kcontrol->id.name);
+
+       upscale = ucontrol->value.enumerated.item[0];
+       dev_dbg(dev, "%s[%d]: scale=%u\n", __func__, id, upscale);
+
+       abox_rdma_mailbox_write(dev, e->reg, upscale);
+
+       return 0;
+}
+
+static int abox_rdma_compr_format_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct device *dev = platform->dev;
+       struct abox_platform_data *platform_data = dev_get_drvdata(dev);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int id = platform_data->id;
+       unsigned int upscale;
+
+       dev_warn(dev, "%s is deprecated\n", kcontrol->id.name);
+
+       upscale = abox_rdma_mailbox_read(dev, e->reg);
+       dev_dbg(dev, "%s[%d]: upscale=%u\n", __func__, id, upscale);
+
+       if (upscale >= e->items) {
+               unsigned int bit, rate;
+
+               bit = (upscale >> COMPR_BIT_SHIFT) & COMPR_BIT_MASK ;
+               rate = (upscale >> COMPR_RATE_SHIFT) & COMPR_RATE_MASK;
+
+               if (rate == 384000) {
+                       upscale = 3;
+               } else if (rate == 192000) {
+                       upscale = 1;
+               } else if (rate == 48000) {
+                       if (bit == 24)
+                               upscale = 2;
+                       else
+                               upscale = 0;
+               } else {
+                       upscale = 0;
+               }
+       }
+
+       ucontrol->value.enumerated.item[0] = upscale;
+
+       return 0;
+}
+
+static const char * const abox_rdma_compr_format_text[] = {
+       "48kHz 16bit",
+       "192kHz 24bit",
+       "48kHz 24bit",
+       "384kHz 32bit",
+};
+
+static SOC_ENUM_SINGLE_DECL(abox_rdma_compr_format,
+               COMPR_UPSCALE, 0,
+               abox_rdma_compr_format_text);
+
+static const struct snd_kcontrol_new abox_rdma_compr_controls[] = {
+       SOC_DOUBLE_R_EXT_TLV("ComprTx0 Volume", COMPR_LEFT_VOL, COMPR_RIGHT_VOL,
+                       0, COMPRESSED_LR_VOL_MAX_STEPS, 0,
+                       abox_rdma_compr_vol_get, abox_rdma_compr_vol_put,
+                       abox_rdma_compr_vol_gain),
+       SOC_ENUM_EXT("ComprTx0 Format", abox_rdma_compr_format,
+                       abox_rdma_compr_format_get, abox_rdma_compr_format_put),
+       SOC_SINGLE("ComprTx0 Bit", COMPR_UPSCALE, COMPR_BIT_SHIFT,
+                       COMPR_BIT_MASK, 0),
+       SOC_SINGLE("ComprTx0 Ch", COMPR_UPSCALE, COMPR_CH_SHIFT,
+                       COMPR_CH_MASK, 0),
+       SOC_SINGLE("ComprTx0 Rate", COMPR_UPSCALE, COMPR_RATE_SHIFT,
+                       COMPR_RATE_MASK, 0),
+};
+
+static const struct snd_pcm_hardware abox_rdma_hardware = {
+       .info                   = SNDRV_PCM_INFO_INTERLEAVED
+                               | SNDRV_PCM_INFO_BLOCK_TRANSFER
+                               | SNDRV_PCM_INFO_MMAP
+                               | SNDRV_PCM_INFO_MMAP_VALID,
+       .formats                = ABOX_SAMPLE_FORMATS,
+       .channels_min           = 1,
+       .channels_max           = 8,
+       .buffer_bytes_max       = BUFFER_BYTES_MAX,
+       .period_bytes_min       = PERIOD_BYTES_MIN,
+       .period_bytes_max       = PERIOD_BYTES_MAX,
+       .periods_min            = BUFFER_BYTES_MAX / PERIOD_BYTES_MAX,
+       .periods_max            = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+};
+
+static irqreturn_t abox_rdma_irq_handler(int irq, void *dev_id,
+               ABOX_IPC_MSG *msg)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct abox_platform_data *data = platform_get_drvdata(pdev);
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg->msg.pcmtask;
+       int id = data->id;
+
+       if (id != pcmtask_msg->channel_id)
+               return IRQ_NONE;
+
+       dev_dbg(dev, "%s[%d]: ipcid=%d, msgtype=%d\n", __func__, id, msg->ipcid,
+                       pcmtask_msg->msgtype);
+
+       switch (pcmtask_msg->msgtype) {
+       case PCM_PLTDAI_POINTER:
+               snd_pcm_period_elapsed(data->substream);
+               break;
+       default:
+               dev_warn(dev, "Unknown pcmtask message: %d\n",
+                               pcmtask_msg->msgtype);
+               break;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int abox_rdma_enabled(struct abox_platform_data *data)
+{
+       return readl(data->sfr_base + ABOX_RDMA_CTRL0) & ABOX_RDMA_ENABLE_MASK;
+}
+
+static void abox_rdma_disable_barrier(struct device *dev,
+               struct abox_platform_data *data)
+{
+       int id = data->id;
+       struct abox_data *abox_data = data->abox_data;
+       u64 timeout = local_clock() + ABOX_DMA_TIMEOUT_NS;
+
+       while (abox_rdma_enabled(data)) {
+               if (local_clock() <= timeout) {
+                       cond_resched();
+                       continue;
+               }
+               dev_warn_ratelimited(dev, "RDMA disable timeout[%d]\n", id);
+               abox_dbg_dump_simple(dev, abox_data, "RDMA disable timeout");
+               break;
+       }
+}
+
+static int abox_rdma_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_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct abox_data *abox_data = data->abox_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int id = data->id;
+       unsigned int lit_freq, big_freq, hmp_boost;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+       if (ret < 0) {
+               dev_err(dev, "Memory allocation failed (size:%u)\n",
+                               params_buffer_bytes(params));
+               return ret;
+       }
+
+       pcmtask_msg->channel_id = id;
+#ifndef USE_FIXED_MEMORY
+       ret = iommu_map(data->abox_data->iommu_domain, IOVA_RDMA_BUFFER(id),
+                       runtime->dma_addr, round_up(runtime->dma_bytes,
+                       PAGE_SIZE), 0);
+       if (ret < 0) {
+               dev_err(dev, "dma buffer iommu map failed\n");
+               return ret;
+       }
+#endif
+       msg.ipcid = IPC_PCMPLAYBACK;
+       msg.task_id = pcmtask_msg->channel_id = id;
+
+       pcmtask_msg->msgtype = PCM_SET_BUFFER;
+       pcmtask_msg->param.setbuff.phyaddr = IOVA_RDMA_BUFFER(id);
+#ifdef CONFIG_SCSC_BT  //if (IS_ENABLED(CONFIG_SCSC_BT))
+       if (abox_test_quirk(abox_data, ABOX_QUIRK_SCSC_BT) && data->scsc_bt) {
+               struct device *dev_bt = abox_data->dev_bt;
+               int stream = substream->stream;
+               unsigned int iova = abox_bt_get_buf_iova(dev_bt, stream);
+
+               if (abox_bt_active(dev_bt, stream) && iova)
+                       pcmtask_msg->param.setbuff.phyaddr = iova;
+       }
+#endif
+       pcmtask_msg->param.setbuff.size = params_period_bytes(params);
+       pcmtask_msg->param.setbuff.count = params_periods(params);
+       ret = abox_rdma_request_ipc(data, &msg, 0, 0);
+       if (ret < 0)
+               return ret;
+
+       pcmtask_msg->msgtype = PCM_PLTDAI_HW_PARAMS;
+       pcmtask_msg->param.hw_params.sample_rate = params_rate(params);
+#ifdef CONFIG_SCSC_BT  //if (IS_ENABLED(CONFIG_SCSC_BT))
+       if (abox_test_quirk(abox_data, ABOX_QUIRK_SCSC_BT_HACK) &&
+                       data->scsc_bt) {
+               struct device *dev_bt = abox_data->dev_bt;
+               int stream = substream->stream;
+               unsigned int rate = abox_bt_get_rate(dev_bt);
+
+               if (abox_bt_active(dev_bt, stream) && rate)
+                       pcmtask_msg->param.hw_params.sample_rate = rate;
+       }
+#endif
+       pcmtask_msg->param.hw_params.bit_depth = params_width(params);
+       pcmtask_msg->param.hw_params.channels = params_channels(params);
+       ret = abox_rdma_request_ipc(data, &msg, 0, 0);
+       if (ret < 0)
+               return ret;
+
+       if (params_rate(params) > 48000)
+               abox_request_cpu_gear(dev, abox_data, dev,
+                               abox_data->cpu_gear_min - 1);
+
+       lit_freq = data->pm_qos_lit[abox_get_rate_type(params_rate(params))];
+       big_freq = data->pm_qos_big[abox_get_rate_type(params_rate(params))];
+       hmp_boost = data->pm_qos_hmp[abox_get_rate_type(params_rate(params))];
+       abox_request_lit_freq(dev, data->abox_data, dev, lit_freq);
+       abox_request_big_freq(dev, data->abox_data, dev, big_freq);
+       abox_request_hmp_boost(dev, data->abox_data, dev, hmp_boost);
+
+       dev_info(dev, "%s:DmaAddr=%pad Total=%zu PrdSz=%u(%u) #Prds=%u dma_area=%p rate=%u, width=%d, channels=%u\n",
+                       snd_pcm_stream_str(substream), &runtime->dma_addr,
+                       runtime->dma_bytes, params_period_size(params),
+                       params_period_bytes(params), params_periods(params),
+                       runtime->dma_area, params_rate(params),
+                       snd_pcm_format_width(params_format(params)),
+                       params_channels(params));
+
+       return 0;
+}
+
+static int abox_rdma_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       msg.ipcid = IPC_PCMPLAYBACK;
+       pcmtask_msg->msgtype = PCM_PLTDAI_HW_FREE;
+       msg.task_id = pcmtask_msg->channel_id = id;
+       abox_rdma_request_ipc(data, &msg, 0, 0);
+#ifndef USE_FIXED_MEMORY
+       iommu_unmap(data->abox_data->iommu_domain, IOVA_RDMA_BUFFER(id),
+                       round_up(substream->runtime->dma_bytes, PAGE_SIZE));
+       exynos_sysmmu_tlb_invalidate(data->abox_data->iommu_domain,
+                       (dma_addr_t)IOVA_RDMA_BUFFER(id),
+                       round_up(substream->runtime->dma_bytes, PAGE_SIZE));
+#endif
+       abox_request_lit_freq(dev, data->abox_data, dev, 0);
+       abox_request_big_freq(dev, data->abox_data, dev, 0);
+       abox_request_hmp_boost(dev, data->abox_data, dev, 0);
+
+       switch (data->type) {
+       default:
+               abox_rdma_disable_barrier(dev, data);
+               break;
+       }
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int abox_rdma_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       data->pointer = IOVA_RDMA_BUFFER(id);
+
+       switch (data->type) {
+       case PLATFORM_CALL:
+               break;
+       default:
+               ret = abox_try_to_asrc_off(dev, data->abox_data, rtd,
+                       SNDRV_PCM_STREAM_PLAYBACK);
+               if (ret < 0)
+                       dev_warn(dev, "abox_try_to_asrc_off: %d\n", ret);
+               break;
+       }
+
+       msg.ipcid = IPC_PCMPLAYBACK;
+       pcmtask_msg->msgtype = PCM_PLTDAI_PREPARE;
+       msg.task_id = pcmtask_msg->channel_id = id;
+       ret = abox_rdma_request_ipc(data, &msg, 0, 0);
+
+       return ret;
+}
+
+static int abox_rdma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int id = data->id;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_info(dev, "%s[%d](%d)\n", __func__, id, cmd);
+
+       msg.ipcid = IPC_PCMPLAYBACK;
+       pcmtask_msg->msgtype = PCM_PLTDAI_TRIGGER;
+       msg.task_id = pcmtask_msg->channel_id = id;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (memblock_is_memory(runtime->dma_addr))
+                       abox_request_dram_on(data->pdev_abox, dev, true);
+
+               pcmtask_msg->param.trigger = 1;
+               ret = abox_rdma_request_ipc(data, &msg, 1, 0);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               pcmtask_msg->param.trigger = 0;
+               ret = abox_rdma_request_ipc(data, &msg, 1, 0);
+               switch (data->type) {
+               case PLATFORM_REALTIME:
+                       msg.ipcid = IPC_ERAP;
+                       msg.msg.erap.msgtype = REALTIME_STOP;
+                       ret = abox_rdma_request_ipc(data, &msg, 1, 0);
+                       break;
+               default:
+                       break;
+               }
+
+               if (memblock_is_memory(runtime->dma_addr))
+                       abox_request_dram_on(data->pdev_abox, dev, false);
+
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t abox_rdma_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int id = data->id;
+       ssize_t pointer;
+       u32 status = readl(data->sfr_base + ABOX_RDMA_STATUS);
+       bool progress = (status & ABOX_RDMA_PROGRESS_MASK) ? true : false;
+
+       if (data->pointer >= IOVA_RDMA_BUFFER(id)) {
+               pointer = data->pointer - IOVA_RDMA_BUFFER(id);
+       } else if (((data->type == PLATFORM_NORMAL) ||
+                       (data->type == PLATFORM_SYNC)) && progress) {
+               ssize_t offset, count;
+               ssize_t buffer_bytes, period_bytes;
+
+               buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+               period_bytes = snd_pcm_lib_period_bytes(substream);
+
+               offset = (((status & ABOX_RDMA_RBUF_OFFSET_MASK) >>
+                               ABOX_RDMA_RBUF_OFFSET_L) << 4);
+               count = (status & ABOX_RDMA_RBUF_CNT_MASK);
+
+               while ((offset % period_bytes) && (buffer_bytes >= 0)) {
+                       buffer_bytes -= period_bytes;
+                       if ((buffer_bytes & offset) == offset)
+                               offset = buffer_bytes;
+               }
+
+               pointer = offset + count;
+       } else {
+               pointer = 0;
+       }
+
+       dev_dbg(dev, "%s[%d]: pointer=%08zx\n", __func__, id, pointer);
+
+       return bytes_to_frames(runtime, pointer);
+}
+
+static int abox_rdma_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct abox_data *abox_data = data->abox_data;
+       int id = data->id;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       if (data->type == PLATFORM_CALL) {
+               if (abox_cpu_gear_idle(dev, abox_data,
+                               (void *)ABOX_CPU_GEAR_CALL_VSS))
+                       abox_request_cpu_gear_sync(dev, abox_data,
+                                       (void *)ABOX_CPU_GEAR_CALL_KERNEL,
+                                       ABOX_CPU_GEAR_MAX);
+               ret = abox_request_l2c_sync(dev, data->abox_data, dev, true);
+               if (ret < 0)
+                       return ret;
+       }
+       abox_request_cpu_gear(dev, abox_data, dev, abox_data->cpu_gear_min);
+
+       snd_soc_set_runtime_hwparams(substream, &abox_rdma_hardware);
+
+       data->substream = substream;
+
+       msg.ipcid = IPC_PCMPLAYBACK;
+       pcmtask_msg->msgtype = PCM_PLTDAI_OPEN;
+       msg.task_id = pcmtask_msg->channel_id = id;
+       ret = abox_rdma_request_ipc(data, &msg, 0, 0);
+
+       return ret;
+}
+
+static int abox_rdma_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct abox_data *abox_data = data->abox_data;
+       int id = data->id;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       data->substream = NULL;
+
+       msg.ipcid = IPC_PCMPLAYBACK;
+       pcmtask_msg->msgtype = PCM_PLTDAI_CLOSE;
+       msg.task_id = pcmtask_msg->channel_id = id;
+       ret = abox_rdma_request_ipc(data, &msg, 0, 1);
+
+       abox_request_cpu_gear(dev, abox_data, dev, ABOX_CPU_GEAR_MIN);
+       if (data->type == PLATFORM_CALL) {
+               abox_request_cpu_gear(dev, abox_data,
+                               (void *)ABOX_CPU_GEAR_CALL_KERNEL,
+                               ABOX_CPU_GEAR_MIN);
+               ret = abox_request_l2c(dev, data->abox_data, dev, false);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static int abox_rdma_mmap(struct snd_pcm_substream *substream,
+               struct vm_area_struct *vma)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       return dma_mmap_writecombine(dev, vma,
+                       runtime->dma_area,
+                       runtime->dma_addr,
+                       runtime->dma_bytes);
+}
+
+static int abox_rdma_ack(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+       snd_pcm_uframes_t appl_ofs = appl_ptr % runtime->buffer_size;
+       ssize_t appl_bytes = frames_to_bytes(runtime, appl_ofs);
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       if (!data->ack_enabled)
+               return 0;
+
+       dev_dbg(dev, "%s[%d]: %zd\n", __func__, id, appl_bytes);
+
+       msg.ipcid = IPC_PCMPLAYBACK;
+       pcmtask_msg->msgtype = PCM_PLTDAI_ACK;
+       pcmtask_msg->param.pointer = (unsigned int)appl_bytes;
+       msg.task_id = pcmtask_msg->channel_id = id;
+
+       return abox_rdma_request_ipc(data, &msg, 0, 0);
+}
+
+static struct snd_pcm_ops abox_rdma_ops = {
+       .open           = abox_rdma_open,
+       .close          = abox_rdma_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = abox_rdma_hw_params,
+       .hw_free        = abox_rdma_hw_free,
+       .prepare        = abox_rdma_prepare,
+       .trigger        = abox_rdma_trigger,
+       .pointer        = abox_rdma_pointer,
+       .mmap           = abox_rdma_mmap,
+       .ack            = abox_rdma_ack,
+};
+
+static int abox_rdma_new(struct snd_soc_pcm_runtime *runtime)
+{
+       struct snd_pcm *pcm = runtime->pcm;
+       struct snd_pcm_str *stream = &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
+       struct snd_pcm_substream *substream = stream->substream;
+       struct snd_soc_platform *platform = runtime->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct iommu_domain *iommu_domain = data->abox_data->iommu_domain;
+       int id = data->id;
+       size_t buffer_bytes;
+       int ret;
+
+       switch (data->type) {
+       case PLATFORM_NORMAL:
+               buffer_bytes = BUFFER_BYTES_MAX;
+               break;
+       default:
+               buffer_bytes = BUFFER_BYTES_MAX >> 2;
+               break;
+       }
+
+       ret = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
+                       runtime->cpu_dai->dev, buffer_bytes, buffer_bytes);
+       if (ret < 0)
+               return ret;
+
+#ifdef USE_FIXED_MEMORY
+       iommu_map(iommu_domain, IOVA_RDMA_BUFFER(id),
+                       substream->dma_buffer.addr, BUFFER_BYTES_MAX, 0);
+#endif
+
+       return ret;
+}
+
+static void abox_rdma_free(struct snd_pcm *pcm)
+{
+#ifdef USE_FIXED_MEMORY
+       struct snd_pcm_str *stream = &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
+       struct snd_pcm_substream *substream = stream->substream;
+       struct snd_soc_pcm_runtime *runtime = substream->private_data;
+       struct snd_soc_platform *platform = runtime->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct iommu_domain *iommu_domain = data->abox_data->iommu_domain;
+       int id = data->id;
+
+       iommu_unmap(iommu_domain, IOVA_RDMA_BUFFER(id), BUFFER_BYTES_MAX);
+#endif
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int abox_rdma_probe(struct snd_soc_platform *platform)
+{
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data =
+                       snd_soc_platform_get_drvdata(platform);
+       int ret;
+
+       if (data->type == PLATFORM_COMPRESS) {
+               struct abox_compr_data *compr_data = &data->compr_data;
+
+               ret = snd_soc_add_platform_controls(platform,
+                               abox_rdma_compr_controls,
+                               ARRAY_SIZE(abox_rdma_compr_controls));
+               if (ret < 0) {
+                       dev_err(dev, "add platform control failed: %d\n", ret);
+                       return ret;
+               }
+#ifdef COMPR_USE_FIXED_MEMORY
+               compr_data->dma_size = abox_rdma_compr_caps.max_fragments *
+                               abox_rdma_compr_caps.max_fragment_size;
+               compr_data->dma_area = dmam_alloc_coherent(dev,
+                               compr_data->dma_size, &compr_data->dma_addr,
+                               GFP_KERNEL);
+               if (compr_data->dma_area == NULL) {
+                       dev_err(dev, "dma memory allocation failed: %lu\n",
+                                       PTR_ERR(compr_data->dma_area));
+                       return -ENOMEM;
+               }
+               ret = iommu_map(data->abox_data->iommu_domain,
+                               IOVA_COMPR_BUFFER(data->id),
+                               compr_data->dma_addr,
+                               round_up(compr_data->dma_size, PAGE_SIZE), 0);
+               if (ret < 0) {
+                       dev_err(dev, "dma memory iommu map failed: %d\n", ret);
+                       return ret;
+               }
+#endif
+       }
+
+       return 0;
+}
+
+static int abox_rdma_remove(struct snd_soc_platform *platform)
+{
+       struct abox_platform_data *data =
+                       snd_soc_platform_get_drvdata(platform);
+
+       if (data->type == PLATFORM_COMPRESS) {
+               struct abox_compr_data *compr_data = &data->compr_data;
+
+               iommu_unmap(data->abox_data->iommu_domain,
+                               IOVA_RDMA_BUFFER(data->id),
+                               round_up(compr_data->dma_size, PAGE_SIZE));
+       }
+
+       return 0;
+}
+
+struct snd_soc_platform_driver abox_rdma = {
+       .probe          = abox_rdma_probe,
+       .remove         = abox_rdma_remove,
+       .compr_ops      = &abox_rdma_compr_ops,
+       .ops            = &abox_rdma_ops,
+       .pcm_new        = abox_rdma_new,
+       .pcm_free       = abox_rdma_free,
+};
+
+static int abox_rdma_runtime_suspend(struct device *dev)
+{
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       abox_mailbox_save(dev);
+       return 0;
+}
+
+static int abox_rdma_runtime_resume(struct device *dev)
+{
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       abox_mailbox_restore(dev);
+       return 0;
+}
+
+static int samsung_abox_rdma_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct abox_platform_data *data;
+       int ret;
+       const char *type;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, data);
+
+       data->sfr_base = devm_not_request_and_map(pdev, "sfr", 0, NULL, NULL);
+       if (IS_ERR(data->sfr_base))
+               return PTR_ERR(data->sfr_base);
+
+       data->pdev_abox = to_platform_device(pdev->dev.parent);
+       if (!data->pdev_abox) {
+               dev_err(dev, "Failed to get abox platform device\n");
+               return -EPROBE_DEFER;
+       }
+       data->abox_data = platform_get_drvdata(data->pdev_abox);
+
+       spin_lock_init(&data->compr_data.lock);
+       spin_lock_init(&data->compr_data.cmd_lock);
+       init_waitqueue_head(&data->compr_data.flush_wait);
+       init_waitqueue_head(&data->compr_data.exit_wait);
+       init_waitqueue_head(&data->compr_data.ipc_wait);
+       data->compr_data.isr_handler = abox_rdma_compr_isr_handler;
+
+       abox_register_irq_handler(&data->pdev_abox->dev, IPC_PCMPLAYBACK,
+                       abox_rdma_irq_handler, pdev);
+
+       ret = of_property_read_u32_index(np, "id", 0, &data->id);
+       if (ret < 0) {
+               dev_err(dev, "id property reading fail\n");
+               return ret;
+       }
+
+       ret = of_property_read_string(np, "type", &type);
+       if (ret < 0)
+               return ret;
+
+       if (!strncmp(type, "call", sizeof("call")))
+               data->type = PLATFORM_CALL;
+       else if (!strncmp(type, "compress", sizeof("compress")))
+               data->type = PLATFORM_COMPRESS;
+       else if (!strncmp(type, "realtime", sizeof("realtime")))
+               data->type = PLATFORM_REALTIME;
+       else if (!strncmp(type, "vi-sensing", sizeof("vi-sensing")))
+               data->type = PLATFORM_VI_SENSING;
+       else if (!strncmp(type, "sync", sizeof("sync")))
+               data->type = PLATFORM_SYNC;
+       else
+               data->type = PLATFORM_NORMAL;
+
+       data->scsc_bt = !!of_find_property(np, "scsc_bt", NULL);
+
+       ret = of_property_read_u32_array(np, "pm_qos_lit", data->pm_qos_lit,
+                       ARRAY_SIZE(data->pm_qos_lit));
+       if (ret < 0)
+               dev_dbg(dev, "Failed to read %s: %d\n", "pm_qos_lit", ret);
+
+       ret = of_property_read_u32_array(np, "pm_qos_big", data->pm_qos_big,
+                       ARRAY_SIZE(data->pm_qos_big));
+       if (ret < 0)
+               dev_dbg(dev, "Failed to read %s: %d\n", "pm_qos_big", ret);
+
+       ret = of_property_read_u32_array(np, "pm_qos_hmp", data->pm_qos_hmp,
+                       ARRAY_SIZE(data->pm_qos_hmp));
+       if (ret < 0)
+               dev_dbg(dev, "Failed to read %s: %d\n", "pm_qos_hmp", ret);
+
+       abox_register_rdma(data->abox_data->pdev, pdev, data->id);
+
+       if (data->type == PLATFORM_COMPRESS) {
+               data->mailbox_base = devm_not_request_and_map(pdev, "mailbox",
+                               1, NULL, NULL);
+               if (IS_ERR(data->mailbox_base))
+                       return PTR_ERR(data->mailbox_base);
+
+               data->mailbox = devm_regmap_init_mmio(dev,
+                               data->mailbox_base,
+                               &abox_mailbox_config);
+               if (IS_ERR(data->mailbox))
+                       return PTR_ERR(data->mailbox);
+
+               pm_runtime_set_autosuspend_delay(dev, 1);
+               pm_runtime_use_autosuspend(dev);
+       } else {
+               pm_runtime_no_callbacks(dev);
+       }
+       pm_runtime_enable(dev);
+
+       return snd_soc_register_platform(&pdev->dev, &abox_rdma);
+}
+
+static int samsung_abox_rdma_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_platform(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id samsung_abox_rdma_match[] = {
+       {
+               .compatible = "samsung,abox-rdma",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_abox_rdma_match);
+
+static const struct dev_pm_ops samsung_abox_rdma_pm = {
+       SET_RUNTIME_PM_OPS(abox_rdma_runtime_suspend,
+                       abox_rdma_runtime_resume, NULL)
+};
+
+static struct platform_driver samsung_abox_rdma_driver = {
+       .probe  = samsung_abox_rdma_probe,
+       .remove = samsung_abox_rdma_remove,
+       .driver = {
+               .name = "samsung-abox-rdma",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_abox_rdma_match),
+               .pm = &samsung_abox_rdma_pm,
+       },
+};
+
+module_platform_driver(samsung_abox_rdma_driver);
+
+/* Module information */
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box RDMA Driver");
+MODULE_ALIAS("platform:samsung-abox-rdma");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox_util.c b/sound/soc/samsung/abox/abox_util.c
new file mode 100644 (file)
index 0000000..4875035
--- /dev/null
@@ -0,0 +1,189 @@
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <sound/pcm.h>
+
+#include "abox_util.h"
+
+void __iomem *devm_not_request_and_map(struct platform_device *pdev,
+               const char *name, unsigned int num, phys_addr_t *phys_addr,
+               size_t *size)
+{
+       struct resource *res;
+       void __iomem *ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, num);
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(&pdev->dev, "Failed to get %s\n", name);
+               return ERR_PTR(-EINVAL);
+       }
+       if (phys_addr)
+               *phys_addr = res->start;
+       if (size)
+               *size = resource_size(res);
+
+       ret = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (IS_ERR_OR_NULL(ret)) {
+               dev_err(&pdev->dev, "Failed to map %s\n", name);
+               return ERR_PTR(-EFAULT);
+       }
+
+       dev_dbg(&pdev->dev, "%s: %s(%p) is mapped on %p with size of %zu",
+                       __func__, name, (void *)res->start, ret,
+                       (size_t)resource_size(res));
+
+       return ret;
+}
+
+void __iomem *devm_request_and_map(struct platform_device *pdev,
+               const char *name, unsigned int num, phys_addr_t *phys_addr,
+               size_t *size)
+{
+       struct resource *res;
+       void __iomem *ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, num);
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(&pdev->dev, "Failed to get %s\n", name);
+               return ERR_PTR(-EINVAL);
+       }
+       if (phys_addr)
+               *phys_addr = res->start;
+       if (size)
+               *size = resource_size(res);
+
+       res = devm_request_mem_region(&pdev->dev, res->start,
+                       resource_size(res), name);
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(&pdev->dev, "Failed to request %s\n", name);
+               return ERR_PTR(-EFAULT);
+       }
+
+       ret = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (IS_ERR_OR_NULL(ret)) {
+               dev_err(&pdev->dev, "Failed to map %s\n", name);
+               return ERR_PTR(-EFAULT);
+       }
+
+       dev_dbg(&pdev->dev, "%s: %s(%p) is mapped on %p with size of %zu",
+                       __func__, name, (void *)res->start, ret,
+                       (size_t)resource_size(res));
+
+       return ret;
+}
+
+void __iomem *devm_request_and_map_byname(struct platform_device *pdev,
+               const char *name, phys_addr_t *phys_addr, size_t *size)
+{
+       struct resource *res;
+       void __iomem *ret;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(&pdev->dev, "Failed to get %s\n", name);
+               return ERR_PTR(-EINVAL);
+       }
+       if (phys_addr)
+               *phys_addr = res->start;
+       if (size)
+               *size = resource_size(res);
+
+       res = devm_request_mem_region(&pdev->dev, res->start,
+                       resource_size(res), name);
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(&pdev->dev, "Failed to request %s\n", name);
+               return ERR_PTR(-EFAULT);
+       }
+
+       ret = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (IS_ERR_OR_NULL(ret)) {
+               dev_err(&pdev->dev, "Failed to map %s\n", name);
+               return ERR_PTR(-EFAULT);
+       }
+
+       dev_dbg(&pdev->dev, "%s: %s(%p) is mapped on %p with size of %zu",
+                       __func__, name, (void *)res->start, ret,
+                       (size_t)resource_size(res));
+
+       return ret;
+}
+
+struct clk *devm_clk_get_and_prepare(struct platform_device *pdev,
+               const char *name)
+{
+       struct device *dev = &pdev->dev;
+       struct clk *clk;
+       int ret;
+
+       clk = devm_clk_get(dev, name);
+       if (IS_ERR(clk)) {
+               dev_err(dev, "Failed to get clock %s\n", name);
+               goto error;
+       }
+
+       ret = clk_prepare(clk);
+       if (ret < 0) {
+               dev_err(dev, "Failed to prepare clock %s\n", name);
+               goto error;
+       }
+
+error:
+       return clk;
+}
+
+u32 readl_phys(phys_addr_t addr)
+{
+       u32 ret;
+       void __iomem *virt = ioremap(addr, 0x4);
+
+       ret = readl(virt);
+       pr_debug("%pa = %08x\n", &addr, ret);
+       iounmap(virt);
+
+       return ret;
+}
+
+void writel_phys(unsigned int val, phys_addr_t addr)
+{
+       void __iomem *virt = ioremap(addr, 0x4);
+
+       writel(val, virt);
+       pr_debug("%pa <= %08x\n", &addr, val);
+       iounmap(virt);
+}
+
+bool is_secure_gic(void)
+{
+       pr_debug("%s: %08x, %08x\n", __func__, readl_phys(0x10000000),
+                       readl_phys(0x10000010));
+       return (readl_phys(0x10000000) == 0xE8895000) &&
+                       (readl_phys(0x10000010) == 0x0);
+}
+
+u64 width_range_to_bits(unsigned int width_min, unsigned int width_max)
+{
+       static const struct {
+               unsigned int width;
+               u64 format;
+       } map[] = {
+               {  8, SNDRV_PCM_FMTBIT_S8  },
+               { 16, SNDRV_PCM_FMTBIT_S16 },
+               { 24, SNDRV_PCM_FMTBIT_S24 },
+               { 32, SNDRV_PCM_FMTBIT_S32 },
+       };
+
+       int i;
+       u64 fmt = 0;
+
+       for (i = 0; i < ARRAY_SIZE(map); i++) {
+               if (map[i].width >= width_min && map[i].width <= width_max)
+                       fmt |= map[i].format;
+       }
+
+       return fmt;
+}
+
+char substream_to_char(struct snd_pcm_substream *substream)
+{
+       return (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 'p' : 'c';
+}
\ No newline at end of file
diff --git a/sound/soc/samsung/abox/abox_util.h b/sound/soc/samsung/abox/abox_util.h
new file mode 100644 (file)
index 0000000..cecd95a
--- /dev/null
@@ -0,0 +1,137 @@
+/* sound/soc/samsung/abox/abox_util.h
+ *
+ * ALSA SoC - Samsung Abox utility
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_UTIL_H
+#define __SND_SOC_ABOX_UTIL_H
+
+#include <sound/pcm.h>
+
+/**
+ * ioremap to virtual address but not request
+ * @param[in]  pdev            pointer to platform device structure
+ * @param[in]  name            name of resource
+ * @param[in]  num             index of resource
+ * @param[out] phys_addr       physical address of the resource
+ * @param[out] size            size of the resource
+ * @return     virtual address
+ */
+extern void __iomem *devm_not_request_and_map(struct platform_device *pdev,
+               const char *name, unsigned int num, phys_addr_t *phys_addr,
+               size_t *size);
+
+/**
+ * Request memory resource and map to virtual address
+ * @param[in]  pdev            pointer to platform device structure
+ * @param[in]  name            name of resource
+ * @param[in]  num             index of resource
+ * @param[out] phys_addr       physical address of the resource
+ * @param[out] size            size of the resource
+ * @return     virtual address
+ */
+extern void __iomem *devm_request_and_map(struct platform_device *pdev,
+               const char *name, unsigned int num, phys_addr_t *phys_addr,
+               size_t *size);
+
+/**
+ * Request memory resource and map to virtual address
+ * @param[in]  pdev            pointer to platform device structure
+ * @param[in]  name            name of resource
+ * @param[out] phys_addr       physical address of the resource
+ * @param[out] size            size of the resource
+ * @return     virtual address
+ */
+extern void __iomem *devm_request_and_map_byname(struct platform_device *pdev,
+               const char *name, phys_addr_t *phys_addr, size_t *size);
+
+/**
+ * Request clock and prepare
+ * @param[in]  pdev            pointer to platform device structure
+ * @param[in]  name            name of clock
+ * @return     pointer to clock
+ */
+extern struct clk *devm_clk_get_and_prepare(struct platform_device *pdev,
+               const char *name);
+
+/**
+ * Read single long physical address (sleeping function)
+ * @param[in]  addr            physical address
+ * @return     value of the physical address
+ */
+extern u32 readl_phys(phys_addr_t addr);
+
+/**
+ * Write single long physical address (sleeping function)
+ * @param[in]  val             value
+ * @param[in]  addr            physical address
+ */
+extern void writel_phys(unsigned int val, phys_addr_t addr);
+
+/**
+ * Atomically increments @v, if @v was @r, set to 0.
+ * @param[in]  v               pointer of type atomic_t
+ * @param[in]  r               maximum range of @v.
+ * @return     Returns old value
+ */
+static inline int atomic_inc_unless_in_range(atomic_t *v, int r)
+{
+       int ret;
+
+       while ((ret = __atomic_add_unless(v, 1, r)) == r) {
+               ret = atomic_cmpxchg(v, r, 0);
+               if (ret == r)
+                       break;
+       }
+
+       return ret;
+}
+
+/**
+ * Atomically decrements @v, if @v was 0, set to @r.
+ * @param[in]  v               pointer of type atomic_t
+ * @param[in]  r               maximum range of @v.
+ * @return     Returns old value
+ */
+static inline int atomic_dec_unless_in_range(atomic_t *v, int r)
+{
+       int ret;
+
+       while ((ret = __atomic_add_unless(v, -1, 0)) == 0) {
+               ret = atomic_cmpxchg(v, 0, r);
+               if (ret == 0)
+                       break;
+       }
+
+       return ret;
+}
+
+/**
+ * Check whether the GIC is secure (sleeping function)
+ * @return     true if the GIC is secure, false on otherwise
+ */
+extern bool is_secure_gic(void);
+
+/**
+ * Get SNDRV_PCM_FMTBIT_* within width_min and width_max.
+ * @param[in]  width_min       minimum bit width
+ * @param[in]  width_max       maximum bit width
+ * @return     Bitwise and of SNDRV_PCM_FMTBIT_*
+ */
+extern u64 width_range_to_bits(unsigned int width_min,
+               unsigned int width_max);
+
+/**
+ * Get character from substream direction
+ * @param[in]  substream       substream
+ * @return     'p' if direction is playback. 'c' if not.
+ */
+extern char substream_to_char(struct snd_pcm_substream *substream);
+
+#endif /* __SND_SOC_ABOX_UTIL_H */
diff --git a/sound/soc/samsung/abox/abox_vdma.c b/sound/soc/samsung/abox/abox_vdma.c
new file mode 100644 (file)
index 0000000..9befb44
--- /dev/null
@@ -0,0 +1,739 @@
+/* sound/soc/samsung/abox/abox_vdma.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox Virtual DMA driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+#undef DEBUG
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/memblock.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "abox_util.h"
+#include "abox.h"
+#include "abox_vdma.h"
+
+#undef TEST
+#define VDMA_COUNT_MAX SZ_32
+#define NAME_LENGTH SZ_32
+
+
+struct abox_vdma_rtd {
+       struct snd_dma_buffer buffer;
+       struct snd_pcm_hardware hardware;
+       struct snd_pcm_substream *substream;
+       unsigned long iova;
+       size_t pointer;
+       bool ack_enabled;
+};
+
+struct abox_vdma_info {
+       struct device *dev;
+       int id;
+       char name[NAME_LENGTH];
+       struct abox_vdma_rtd rtd[SNDRV_PCM_STREAM_LAST + 1];
+};
+
+static struct device *abox_vdma_dev_abox;
+static struct abox_vdma_info abox_vdma_list[VDMA_COUNT_MAX];
+
+static int abox_vdma_get_idx(int id)
+{
+       return id - PCMTASK_VDMA_ID_BASE;
+}
+
+static unsigned long abox_vdma_get_iova(int id, int stream)
+{
+       int idx = abox_vdma_get_idx(id);
+       long ret;
+
+       switch (stream) {
+       case SNDRV_PCM_STREAM_PLAYBACK:
+       case SNDRV_PCM_STREAM_CAPTURE:
+               ret = IOVA_VDMA_BUFFER(idx) + (SZ_512K * stream);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static struct abox_vdma_info *abox_vdma_get_info(int id)
+{
+       int idx = abox_vdma_get_idx(id);
+
+       if (idx < 0 || idx >= ARRAY_SIZE(abox_vdma_list))
+               return NULL;
+
+       return &abox_vdma_list[idx];
+}
+
+static struct abox_vdma_rtd *abox_vdma_get_rtd(struct abox_vdma_info *info,
+               int stream)
+{
+       if (!info || stream < 0 || stream >= ARRAY_SIZE(info->rtd))
+               return NULL;
+
+       return &info->rtd[stream];
+}
+
+static int abox_vdma_request_ipc(ABOX_IPC_MSG *msg, int atomic, int sync)
+{
+       return abox_request_ipc(abox_vdma_dev_abox, msg->ipcid, msg,
+                       sizeof(*msg), atomic, sync);
+}
+
+int abox_vdma_period_elapsed(struct abox_vdma_info *info,
+               struct abox_vdma_rtd *rtd, size_t pointer)
+{
+       dev_dbg(info->dev, "%s[%d:%c](%zx)\n", __func__, info->id,
+                       substream_to_char(rtd->substream), pointer);
+
+       rtd->pointer = pointer - rtd->iova;
+       snd_pcm_period_elapsed(rtd->substream);
+
+       return 0;
+}
+
+static int abox_vdma_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
+       struct snd_soc_platform *platform = soc_rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_vdma_info *info = snd_soc_platform_get_drvdata(platform);
+       struct abox_vdma_rtd *rtd = abox_vdma_get_rtd(info, substream->stream);
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d:%c]\n", __func__, id, substream_to_char(substream));
+
+       snd_soc_set_runtime_hwparams(substream, &rtd->hardware);
+
+       msg.ipcid = abox_stream_to_ipcid(substream->stream);
+       msg.task_id = pcmtask_msg->channel_id = id;
+       pcmtask_msg->msgtype = PCM_PLTDAI_OPEN;
+
+       return abox_vdma_request_ipc(&msg, 0, 0);
+}
+
+static int abox_vdma_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
+       struct snd_soc_platform *platform = soc_rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d:%c]\n", __func__, id, substream_to_char(substream));
+
+       msg.ipcid = abox_stream_to_ipcid(substream->stream);
+       msg.task_id = pcmtask_msg->channel_id = id;
+       pcmtask_msg->msgtype = PCM_PLTDAI_CLOSE;
+       return abox_vdma_request_ipc(&msg, 0, 0);
+}
+
+static int abox_vdma_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
+       struct snd_soc_platform *platform = soc_rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_vdma_info *info = snd_soc_platform_get_drvdata(platform);
+       struct abox_vdma_rtd *rtd = abox_vdma_get_rtd(info, substream->stream);
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+       int ret;
+
+       dev_dbg(dev, "%s[%d:%c]\n", __func__, id, substream_to_char(substream));
+
+       ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+       if (ret < 0)
+               return ret;
+
+       msg.ipcid = abox_stream_to_ipcid(substream->stream);
+       msg.task_id = pcmtask_msg->channel_id = id;
+
+       pcmtask_msg->msgtype = PCM_SET_BUFFER;
+       pcmtask_msg->param.setbuff.phyaddr = rtd->iova;
+       pcmtask_msg->param.setbuff.size = params_period_bytes(params);
+       pcmtask_msg->param.setbuff.count = params_periods(params);
+       ret = abox_vdma_request_ipc(&msg, 0, 0);
+       if (ret < 0)
+               return ret;
+
+       pcmtask_msg->msgtype = PCM_PLTDAI_HW_PARAMS;
+       pcmtask_msg->param.hw_params.sample_rate = params_rate(params);
+       pcmtask_msg->param.hw_params.bit_depth = params_width(params);
+       pcmtask_msg->param.hw_params.channels = params_channels(params);
+       ret = abox_vdma_request_ipc(&msg, 0, 0);
+       if (ret < 0)
+               return ret;
+
+       return ret;
+}
+
+static int abox_vdma_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
+       struct snd_soc_platform *platform = soc_rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d:%c]\n", __func__, id, substream_to_char(substream));
+
+       msg.ipcid = abox_stream_to_ipcid(substream->stream);
+       msg.task_id = pcmtask_msg->channel_id = id;
+       pcmtask_msg->msgtype = PCM_PLTDAI_HW_FREE;
+       abox_vdma_request_ipc(&msg, 0, 0);
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int abox_vdma_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
+       struct snd_soc_platform *platform = soc_rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_vdma_info *info = snd_soc_platform_get_drvdata(platform);
+       struct abox_vdma_rtd *rtd = abox_vdma_get_rtd(info, substream->stream);
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d:%c]\n", __func__, id, substream_to_char(substream));
+
+       rtd->pointer = 0;
+
+       msg.ipcid = abox_stream_to_ipcid(substream->stream);
+       msg.task_id = pcmtask_msg->channel_id = id;
+       pcmtask_msg->msgtype = PCM_PLTDAI_PREPARE;
+       return abox_vdma_request_ipc(&msg, 0, 0);
+}
+
+static int abox_vdma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
+       struct snd_soc_platform *platform = soc_rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+       struct platform_device *pdev_abox;
+       int ret;
+
+       dev_info(dev, "%s[%d:%c](%d)\n", __func__, id,
+                       substream_to_char(substream), cmd);
+
+       pdev_abox = to_platform_device(abox_vdma_dev_abox);
+
+       msg.ipcid = abox_stream_to_ipcid(substream->stream);
+       msg.task_id = pcmtask_msg->channel_id = id;
+       pcmtask_msg->msgtype = PCM_PLTDAI_TRIGGER;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (memblock_is_memory(substream->runtime->dma_addr))
+                       abox_request_dram_on(pdev_abox, dev, true);
+               pcmtask_msg->param.trigger = 1;
+               ret = abox_vdma_request_ipc(&msg, 1, 0);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               pcmtask_msg->param.trigger = 0;
+               ret = abox_vdma_request_ipc(&msg, 1, 0);
+               if (memblock_is_memory(substream->runtime->dma_addr))
+                       abox_request_dram_on(pdev_abox, dev, false);
+               break;
+       default:
+               dev_err(dev, "invalid command: %d\n", cmd);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t abox_vdma_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
+       struct snd_soc_platform *platform = soc_rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_vdma_info *info = snd_soc_platform_get_drvdata(platform);
+       struct abox_vdma_rtd *rtd = abox_vdma_get_rtd(info, substream->stream);
+
+       dev_dbg(dev, "%s[%d:%c]\n", __func__, id, substream_to_char(substream));
+
+       return bytes_to_frames(substream->runtime, rtd->pointer);
+}
+
+static int abox_vdma_ack(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *pcm_rtd = substream->runtime;
+       struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
+       struct snd_soc_platform *platform = soc_rtd->platform;
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_vdma_info *info = snd_soc_platform_get_drvdata(platform);
+       struct abox_vdma_rtd *rtd = abox_vdma_get_rtd(info, substream->stream);
+       snd_pcm_uframes_t appl_ptr = pcm_rtd->control->appl_ptr;
+       snd_pcm_uframes_t appl_ofs = appl_ptr % pcm_rtd->buffer_size;
+       ssize_t appl_bytes = frames_to_bytes(pcm_rtd, appl_ofs);
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       if (!rtd->ack_enabled)
+               return 0;
+
+       dev_dbg(dev, "%s[%d:%c]: %zd\n", __func__, id,
+                       substream_to_char(substream), appl_bytes);
+
+       msg.ipcid = abox_stream_to_ipcid(substream->stream);
+       msg.task_id = pcmtask_msg->channel_id = id;
+       pcmtask_msg->msgtype = PCM_PLTDAI_ACK;
+       pcmtask_msg->param.pointer = (unsigned int)appl_bytes;
+
+       return abox_vdma_request_ipc(&msg, 0, 0);
+}
+
+static struct snd_pcm_ops abox_vdma_platform_ops = {
+       .open           = abox_vdma_open,
+       .close          = abox_vdma_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = abox_vdma_hw_params,
+       .hw_free        = abox_vdma_hw_free,
+       .prepare        = abox_vdma_prepare,
+       .trigger        = abox_vdma_trigger,
+       .pointer        = abox_vdma_pointer,
+       .ack            = abox_vdma_ack,
+};
+
+static int abox_vdma_platform_probe(struct snd_soc_platform *platform)
+{
+       struct device *dev = platform->dev;
+       int id = to_platform_device(dev)->id;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       snd_soc_platform_set_drvdata(platform, abox_vdma_get_info(id));
+       return 0;
+}
+
+static int abox_vdma_platform_new(struct snd_soc_pcm_runtime *soc_rtd)
+{
+       struct device *dev = soc_rtd->platform->dev;
+       struct device *dev_abox = abox_vdma_dev_abox;
+       struct snd_pcm *pcm = soc_rtd->pcm;
+       int id = to_platform_device(dev)->id;
+       struct abox_vdma_info *info = abox_vdma_get_info(id);
+       int i, ret;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) {
+               struct snd_pcm_substream *substream = pcm->streams[i].substream;
+
+               if (!substream)
+                       continue;
+
+               if (info->rtd[i].iova == 0)
+                       info->rtd[i].iova = abox_vdma_get_iova(id, i);
+
+               if (info->rtd[i].buffer.bytes == 0)
+                       info->rtd[i].buffer.bytes = BUFFER_BYTES_MAX;
+
+               if (info->rtd[i].buffer.addr) {
+                       substream->dma_buffer = info->rtd[i].buffer;
+               } else {
+                       size_t size = info->rtd[i].buffer.bytes;
+
+                       ret = snd_pcm_lib_preallocate_pages(substream,
+                                       SNDRV_DMA_TYPE_DEV, dev_abox,
+                                       size, size);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               if (abox_iova_to_phys(dev_abox, info->rtd[i].iova) == 0) {
+                       ret = abox_iommu_map(dev_abox, info->rtd[i].iova,
+                                       substream->dma_buffer.addr,
+                                       substream->dma_buffer.bytes);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               info->rtd[i].substream = substream;
+       }
+
+       return 0;
+}
+
+static void abox_vdma_platform_free(struct snd_pcm *pcm)
+{
+       struct snd_soc_pcm_runtime *soc_rtd = pcm->private_data;
+       struct device *dev = soc_rtd->platform->dev;
+       struct device *dev_abox = abox_vdma_dev_abox;
+       int id = to_platform_device(dev)->id;
+       struct abox_vdma_info *info = abox_vdma_get_info(id);
+       int i;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) {
+               struct snd_pcm_substream *substream = pcm->streams[i].substream;
+
+               if (!substream)
+                       continue;
+
+               info->rtd[i].substream = NULL;
+
+               if (!info->rtd[i].buffer.addr) {
+                       abox_iommu_unmap(dev_abox, info->rtd[i].iova,
+                                       substream->dma_buffer.addr,
+                                       substream->dma_buffer.bytes);
+                       snd_pcm_lib_preallocate_free(substream);
+               }
+       }
+}
+
+struct snd_soc_platform_driver abox_vdma_platform = {
+       .probe          = abox_vdma_platform_probe,
+       .ops            = &abox_vdma_platform_ops,
+       .pcm_new        = abox_vdma_platform_new,
+       .pcm_free       = abox_vdma_platform_free,
+};
+
+static irqreturn_t abox_vdma_irq_handler(int ipc_id, void *dev_id,
+               ABOX_IPC_MSG *msg)
+{
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg->msg.pcmtask;
+       int id = pcmtask_msg->channel_id;
+       int stream = abox_ipcid_to_stream(ipc_id);
+       struct abox_vdma_info *info = abox_vdma_get_info(id);
+       struct abox_vdma_rtd *rtd = abox_vdma_get_rtd(info, stream);
+
+       if (!info || !rtd)
+               return IRQ_NONE;
+
+       switch (pcmtask_msg->msgtype) {
+       case PCM_PLTDAI_POINTER:
+               abox_vdma_period_elapsed(info, rtd, pcmtask_msg->param.pointer);
+               break;
+       case PCM_PLTDAI_ACK:
+               rtd->ack_enabled = !!pcmtask_msg->param.trigger;
+               break;
+       case PCM_PLTDAI_REGISTER:
+       {
+               struct PCMTASK_HARDWARE *hardware;
+               struct device *dev_abox = dev_id;
+               struct abox_data *data = dev_get_drvdata(dev_abox);
+
+               hardware = &pcmtask_msg->param.hardware;
+               abox_vdma_register(dev_abox, id, stream,
+                               abox_addr_to_kernel_addr(data, hardware->addr),
+                               abox_addr_to_phys_addr(data, hardware->addr),
+                               hardware);
+               break;
+       }
+       default:
+               return IRQ_NONE;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct snd_soc_dai_link abox_vdma_dai_links[VDMA_COUNT_MAX];
+static struct snd_soc_card abox_vdma_card = {
+       .name = "abox_vdma",
+       .owner = THIS_MODULE,
+       .dai_link = abox_vdma_dai_links,
+       .num_links = 0,
+};
+
+void abox_vdma_register_work_func(struct work_struct *work)
+{
+       int id;
+       struct abox_vdma_info *info;
+
+       dev_dbg(abox_vdma_dev_abox, "%s\n", __func__);
+
+       if (!abox_vdma_card.dev) {
+               platform_device_register_data(abox_vdma_dev_abox,
+                               "samsung-abox-vdma", -1, NULL, 0);
+       }
+
+       for (info = abox_vdma_list; (info - abox_vdma_list) <
+                       ARRAY_SIZE(abox_vdma_list); info++) {
+               id = info->id;
+               if (info->dev == abox_vdma_dev_abox) {
+                       dev_dbg(info->dev, "%s[%d]\n", __func__, id);
+                       platform_device_register_data(info->dev,
+                                       "samsung-abox-vdma", id, NULL, 0);
+               }
+       }
+}
+
+static DECLARE_WORK(abox_vdma_register_work, abox_vdma_register_work_func);
+
+int abox_vdma_register(struct device *dev, int id, int stream,
+               void *area, phys_addr_t addr,
+               const struct PCMTASK_HARDWARE *pcm_hardware)
+{
+       struct abox_vdma_info *info = abox_vdma_get_info(id);
+       struct abox_vdma_rtd *rtd = abox_vdma_get_rtd(info, stream);
+       struct snd_dma_buffer *buffer = &rtd->buffer;
+       struct snd_pcm_hardware *hardware = &rtd->hardware;
+
+       if (!info || !rtd)
+               return -EINVAL;
+
+       if (info->dev && rtd->iova)
+               return -EEXIST;
+
+       dev_info(dev, "%s(%d, %s, %d, %p, %pa, %u)\n", __func__,
+                       id, pcm_hardware->name, stream, area, &addr,
+                       pcm_hardware->buffer_bytes_max);
+
+       info->id = id;
+       strncpy(info->name, pcm_hardware->name, sizeof(info->name) - 1);
+
+       rtd->iova = pcm_hardware->addr;
+
+       buffer->dev.type = SNDRV_DMA_TYPE_DEV;
+       buffer->dev.dev = dev;
+       buffer->area = area;
+       buffer->addr = addr;
+       buffer->bytes = pcm_hardware->buffer_bytes_max;
+
+       hardware->info = SNDRV_PCM_INFO_INTERLEAVED
+                       | SNDRV_PCM_INFO_BLOCK_TRANSFER
+                       | SNDRV_PCM_INFO_MMAP
+                       | SNDRV_PCM_INFO_MMAP_VALID;
+       hardware->formats = width_range_to_bits(pcm_hardware->width_min,
+                       pcm_hardware->width_max);
+       hardware->rates = (pcm_hardware->rate_max > 192000) ?
+                       SNDRV_PCM_RATE_KNOT : snd_pcm_rate_range_to_bits(
+                       pcm_hardware->rate_min, pcm_hardware->rate_max);
+       hardware->rate_min = pcm_hardware->rate_min;
+       hardware->rate_max = pcm_hardware->rate_max;
+       hardware->channels_min = pcm_hardware->channels_min;
+       hardware->channels_max = pcm_hardware->channels_max;
+       hardware->buffer_bytes_max = pcm_hardware->buffer_bytes_max;
+       hardware->period_bytes_min = pcm_hardware->period_bytes_min;
+       hardware->period_bytes_max = pcm_hardware->period_bytes_max;
+       hardware->periods_min = pcm_hardware->periods_min;
+       hardware->periods_max = pcm_hardware->periods_max;
+
+       abox_vdma_dev_abox = info->dev = dev;
+       schedule_work(&abox_vdma_register_work);
+
+       return 0;
+}
+
+static void abox_vdma_register_card_work_func(struct work_struct *work)
+{
+       int i;
+
+       dev_dbg(abox_vdma_dev_abox, "%s\n", __func__);
+
+       snd_soc_unregister_card(&abox_vdma_card);
+
+       for (i = 0; i < abox_vdma_card.num_links; i++) {
+               struct snd_soc_dai_link *link = &abox_vdma_card.dai_link[i];
+
+               if (link->name)
+                       continue;
+
+               link->name = link->stream_name =
+                               kasprintf(GFP_KERNEL, "dummy%d", i);
+               link->stream_name = link->name;
+               link->cpu_name = "snd-soc-dummy";
+               link->cpu_dai_name = "snd-soc-dummy-dai";
+               link->codec_name = "snd-soc-dummy";
+               link->codec_dai_name = "snd-soc-dummy-dai";
+               link->no_pcm = 1;
+       }
+
+       snd_soc_register_card(&abox_vdma_card);
+}
+
+DECLARE_DELAYED_WORK(abox_vdma_register_card_work,
+               abox_vdma_register_card_work_func);
+
+static int abox_vdma_add_dai_link(struct device *dev)
+{
+       int id = to_platform_device(dev)->id;
+       int idx = abox_vdma_get_idx(id);
+       struct abox_vdma_info *info = abox_vdma_get_info(id);
+       struct snd_soc_dai_link *link = &abox_vdma_dai_links[idx];
+       struct abox_vdma_rtd *playback, *capture;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       if (idx > ARRAY_SIZE(abox_vdma_dai_links)) {
+               dev_err(dev, "Too many request\n");
+               return -ENOMEM;
+       }
+
+       cancel_delayed_work_sync(&abox_vdma_register_card_work);
+
+       kfree(link->name);
+       link->name = link->stream_name = kstrdup(info->name, GFP_KERNEL);
+       link->cpu_name = "snd-soc-dummy";
+       link->cpu_dai_name = "snd-soc-dummy-dai";
+       link->platform_name = dev_name(dev);
+       link->codec_name = "snd-soc-dummy";
+       link->codec_dai_name = "snd-soc-dummy-dai";
+       link->ignore_suspend = 1;
+       link->ignore_pmdown_time = 1;
+       link->no_pcm = 0;
+       playback = &info->rtd[SNDRV_PCM_STREAM_PLAYBACK];
+       capture = &info->rtd[SNDRV_PCM_STREAM_CAPTURE];
+       link->playback_only = playback->buffer.area && !capture->buffer.area;
+       link->capture_only = !playback->buffer.area && capture->buffer.area;
+
+       if (abox_vdma_card.num_links <= idx)
+               abox_vdma_card.num_links = idx + 1;
+
+       schedule_delayed_work(&abox_vdma_register_card_work, HZ);
+
+       return 0;
+}
+
+static int samsung_abox_vdma_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int id = to_platform_device(dev)->id;
+       struct abox_vdma_info *info = abox_vdma_get_info(id);
+       int ret = 0;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       if (id <= 0) {
+               abox_vdma_card.dev = &pdev->dev;
+       } else {
+               info->dev = dev;
+               pm_runtime_no_callbacks(dev);
+               pm_runtime_enable(dev);
+               devm_snd_soc_register_platform(dev, &abox_vdma_platform);
+               ret = abox_vdma_add_dai_link(dev);
+       }
+
+       return ret;
+}
+
+static int samsung_abox_vdma_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int id = to_platform_device(dev)->id;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       return 0;
+}
+
+static const struct platform_device_id samsung_abox_vdma_driver_ids[] = {
+       {
+               .name = "samsung-abox-vdma",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(platform, samsung_abox_vdma_driver_ids);
+
+static struct platform_driver samsung_abox_vdma_driver = {
+       .probe  = samsung_abox_vdma_probe,
+       .remove = samsung_abox_vdma_remove,
+       .driver = {
+               .name = "samsung-abox-vdma",
+               .owner = THIS_MODULE,
+       },
+       .id_table = samsung_abox_vdma_driver_ids,
+};
+
+module_platform_driver(samsung_abox_vdma_driver);
+
+#ifdef TEST
+static unsigned char test_buf[4096];
+
+static void test_work_func(struct work_struct *work)
+{
+       struct abox_vdma_info *info = &abox_vdma_list[2];
+       static unsigned char i;
+       int j;
+
+       pr_debug("%s: %d\n", __func__, i);
+
+       for (j = 0; j < 1024; j++, i++) {
+               test_buf[i % ARRAY_SIZE(test_buf)] = i;
+       }
+       abox_vdma_period_elapsed(info, &info->rtd[0], i % ARRAY_SIZE(test_buf));
+       abox_vdma_period_elapsed(info, &info->rtd[1], i % ARRAY_SIZE(test_buf));
+       schedule_delayed_work(to_delayed_work(work), msecs_to_jiffies(1000));
+}
+DECLARE_DELAYED_WORK(test_work, test_work_func);
+
+static const struct PCMTASK_HARDWARE test_hardware = {
+       .name = "test01",
+       .addr = 0x12345678,
+       .width_min = 16,
+       .width_max = 24,
+       .rate_min = 48000,
+       .rate_max = 192000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = 4096,
+       .period_bytes_min = 1024,
+       .period_bytes_max = 2048,
+       .periods_min = 2,
+       .periods_max = 4,
+};
+#endif
+
+static int __init samsung_abox_vdma_initcall(void)
+{
+       struct abox_data *data = abox_get_abox_data();
+       struct device *dev_abox;
+
+       if (!data)
+               return 0;
+
+       pr_info("%s\n", __func__);
+
+       dev_abox = &abox_get_abox_data()->pdev->dev;
+       abox_register_irq_handler(dev_abox, IPC_PCMPLAYBACK,
+                       abox_vdma_irq_handler, dev_abox);
+       abox_register_irq_handler(dev_abox, IPC_PCMCAPTURE,
+                       abox_vdma_irq_handler, dev_abox);
+#ifdef TEST
+       abox_vdma_register(dev_abox, 102, SNDRV_PCM_STREAM_PLAYBACK, test_buf,
+                       virt_to_phys(test_buf), &test_hardware);
+       abox_vdma_register(dev_abox, 102, SNDRV_PCM_STREAM_CAPTURE, test_buf,
+                       virt_to_phys(test_buf), &test_hardware);
+       schedule_delayed_work(&test_work, HZ * 5);
+#endif
+       return 0;
+}
+late_initcall(samsung_abox_vdma_initcall);
+
+/* Module information */
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box Virtual DMA Driver");
+MODULE_ALIAS("platform:samsung-abox-vdma");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox_vdma.h b/sound/soc/samsung/abox/abox_vdma.h
new file mode 100644 (file)
index 0000000..807fd0f
--- /dev/null
@@ -0,0 +1,29 @@
+/* sound/soc/samsung/abox/abox_vdma.h
+ *
+ * ALSA SoC Audio Layer - Samsung Abox Virtual DMA driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_ABOX_VDMA_H
+#define __SND_SOC_ABOX_VDMA_H
+
+/**
+ * Register abox dump buffer
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  id              unique buffer id
+ * @param[in]  stream          SNDRV_PCM_STREAM_*
+ * @param[in]  area            virtual address of the buffer
+ * @param[in]  addr            pysical address of the buffer
+ * @param[in]  pcm_hardware    hardware information of virtual DMA
+ * @return     error code if any
+ */
+extern int abox_vdma_register(struct device *dev, int id, int stream,
+               void *area, phys_addr_t addr,
+               const struct PCMTASK_HARDWARE *pcm_hardware);
+
+#endif /* __SND_SOC_ABOX_VDMA_H */
diff --git a/sound/soc/samsung/abox/abox_vss.c b/sound/soc/samsung/abox/abox_vss.c
new file mode 100644 (file)
index 0000000..c56dad5
--- /dev/null
@@ -0,0 +1,68 @@
+/* sound/soc/samsung/abox/abox_vss.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox VSS driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/shm_ipc.h>
+#include <linux/io.h>
+
+#include "abox.h"
+
+static unsigned int VSS_MAGIC_OFFSET = 0x500000;
+
+static int samsung_abox_vss_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       void __iomem *magic_addr;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       of_property_read_u32(np, "magic_offset", &VSS_MAGIC_OFFSET);
+       dev_info(dev, "magic_offset = 0x%08X\n", VSS_MAGIC_OFFSET);
+       magic_addr = phys_to_virt(shm_get_vss_base() + VSS_MAGIC_OFFSET);
+       writel(0, magic_addr);
+       return 0;
+}
+
+static int samsung_abox_vss_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+       return 0;
+}
+
+static const struct of_device_id samsung_abox_vss_match[] = {
+       {
+               .compatible = "samsung,abox-vss",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_abox_vss_match);
+
+static struct platform_driver samsung_abox_vss_driver = {
+       .probe  = samsung_abox_vss_probe,
+       .remove = samsung_abox_vss_remove,
+       .driver = {
+               .name = "samsung-abox-vss",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_abox_vss_match),
+       },
+};
+
+module_platform_driver(samsung_abox_vss_driver);
+
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box VSS Driver");
+MODULE_ALIAS("platform:samsung-abox-vss");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/abox/abox_wdma.c b/sound/soc/samsung/abox/abox_wdma.c
new file mode 100644 (file)
index 0000000..e7e1932
--- /dev/null
@@ -0,0 +1,653 @@
+/* sound/soc/samsung/abox/abox_wdma.c
+ *
+ * ALSA SoC Audio Layer - Samsung Abox WDMA driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/iommu.h>
+#include <linux/delay.h>
+#include <linux/memblock.h>
+#include <linux/sched/clock.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../../../../drivers/iommu/exynos-iommu.h"
+#include <sound/samsung/abox.h>
+#include "abox_util.h"
+#include "abox_gic.h"
+#include "abox_dbg.h"
+#ifdef CONFIG_SCSC_BT
+#include "abox_bt.h"
+#endif
+#include "abox.h"
+
+#define USE_FIXED_MEMORY
+
+static const struct snd_pcm_hardware abox_wdma_hardware = {
+       .info                   = SNDRV_PCM_INFO_INTERLEAVED
+                               | SNDRV_PCM_INFO_BLOCK_TRANSFER
+                               | SNDRV_PCM_INFO_MMAP
+                               | SNDRV_PCM_INFO_MMAP_VALID,
+       .formats                = ABOX_WDMA_SAMPLE_FORMATS,
+       .channels_min           = 1,
+       .channels_max           = 8,
+       .buffer_bytes_max       = BUFFER_BYTES_MAX,
+       .period_bytes_min       = PERIOD_BYTES_MIN,
+       .period_bytes_max       = PERIOD_BYTES_MAX,
+       .periods_min            = BUFFER_BYTES_MAX / PERIOD_BYTES_MAX,
+       .periods_max            = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+};
+
+static int abox_wdma_request_ipc(struct abox_platform_data *data,
+               ABOX_IPC_MSG *msg, int atomic, int sync)
+{
+       struct device *dev_abox = &data->pdev_abox->dev;
+
+       return abox_request_ipc(dev_abox, msg->ipcid, msg, sizeof(*msg),
+                       atomic, sync);
+}
+
+static irqreturn_t abox_wdma_irq_handler(int irq, void *dev_id,
+               ABOX_IPC_MSG *msg)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct abox_platform_data *data = platform_get_drvdata(pdev);
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg->msg.pcmtask;
+       int id = data->id;
+
+       if (id != pcmtask_msg->channel_id)
+               return IRQ_NONE;
+
+       dev_dbg(dev, "%s[%d]: ipcid=%d, msgtype=%d\n", __func__, id,
+                       msg->ipcid, pcmtask_msg->msgtype);
+
+       switch (pcmtask_msg->msgtype) {
+       case PCM_PLTDAI_POINTER:
+               snd_pcm_period_elapsed(data->substream);
+               break;
+       default:
+               dev_warn(dev, "Unknown pcmtask message: %d\n",
+                               pcmtask_msg->msgtype);
+               break;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int abox_wdma_enabled(struct abox_platform_data *data)
+{
+       return readl(data->sfr_base + ABOX_WDMA_CTRL) & ABOX_WDMA_ENABLE_MASK;
+}
+
+static void abox_wdma_disable_barrier(struct device *dev,
+               struct abox_platform_data *data)
+{
+       int id = data->id;
+       struct abox_data *abox_data = data->abox_data;
+       u64 timeout = local_clock() + ABOX_DMA_TIMEOUT_NS;
+
+       while (abox_wdma_enabled(data)) {
+               if (local_clock() <= timeout) {
+                       cond_resched();
+                       continue;
+               }
+               dev_warn_ratelimited(dev, "WDMA disable timeout[%d]\n", id);
+               abox_dbg_dump_simple(dev, abox_data, "WDMA disable timeout");
+               break;
+       }
+}
+
+static int abox_wdma_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_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct abox_data *abox_data = data->abox_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int id = data->id;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+       if (ret < 0) {
+               dev_err(dev, "Memory allocation failed (size:%u)\n",
+                               params_buffer_bytes(params));
+               return ret;
+       }
+
+       pcmtask_msg->channel_id = id;
+#ifndef USE_FIXED_MEMORY
+       ret = iommu_map(abox_data->iommu_domain, IOVA_WDMA_BUFFER(id),
+                       runtime->dma_addr, round_up(runtime->dma_bytes,
+                       PAGE_SIZE), 0);
+       if (ret < 0) {
+               dev_err(dev, "dma buffer iommu map failed\n");
+               return ret;
+       }
+#endif
+       msg.ipcid = IPC_PCMCAPTURE;
+       msg.task_id = pcmtask_msg->channel_id = id;
+
+       pcmtask_msg->msgtype = PCM_SET_BUFFER;
+       pcmtask_msg->param.setbuff.phyaddr = IOVA_WDMA_BUFFER(id);
+#ifdef CONFIG_SCSC_BT  //if (IS_ENABLED(CONFIG_SCSC_BT))
+               if (abox_test_quirk(abox_data, ABOX_QUIRK_SCSC_BT) && data->scsc_bt) {
+                       struct device *dev_bt = abox_data->dev_bt;
+                       int stream = substream->stream;
+                       unsigned int iova = abox_bt_get_buf_iova(dev_bt, stream);
+
+                       if (abox_bt_active(dev_bt, stream) && iova)
+                               pcmtask_msg->param.setbuff.phyaddr = iova;
+               }
+#endif
+       pcmtask_msg->param.setbuff.size = params_period_bytes(params);
+       pcmtask_msg->param.setbuff.count = params_periods(params);
+       ret = abox_wdma_request_ipc(data, &msg, 0, 0);
+       if (ret < 0)
+               return ret;
+
+       pcmtask_msg->msgtype = PCM_PLTDAI_HW_PARAMS;
+       pcmtask_msg->param.hw_params.sample_rate = params_rate(params);
+#ifdef CONFIG_SCSC_BT  //if (IS_ENABLED(CONFIG_SCSC_BT))
+       if (abox_test_quirk(abox_data, ABOX_QUIRK_SCSC_BT_HACK) &&
+                       data->scsc_bt) {
+               struct device *dev_bt = abox_data->dev_bt;
+               int stream = substream->stream;
+               unsigned int rate = abox_bt_get_rate(dev_bt);
+
+               if (abox_bt_active(dev_bt, stream) && rate)
+                       pcmtask_msg->param.hw_params.sample_rate = rate;
+       }
+#endif
+       pcmtask_msg->param.hw_params.bit_depth = params_width(params);
+       pcmtask_msg->param.hw_params.channels = params_channels(params);
+       ret = abox_wdma_request_ipc(data, &msg, 0, 0);
+       if (ret < 0)
+               return ret;
+
+       if (params_rate(params) > 48000)
+               abox_request_cpu_gear(dev, abox_data, dev,
+                               abox_data->cpu_gear_min - 1);
+
+       dev_info(dev, "%s:DmaAddr=%pad Total=%zu PrdSz=%u(%u) #Prds=%u dma_area=%p rate=%u, width=%d, channels=%u\n",
+                       snd_pcm_stream_str(substream), &runtime->dma_addr,
+                       runtime->dma_bytes, params_period_size(params),
+                       params_period_bytes(params), params_periods(params),
+                       runtime->dma_area, params_rate(params),
+                       snd_pcm_format_width(params_format(params)),
+                       params_channels(params));
+
+       return 0;
+}
+
+static int abox_wdma_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       msg.ipcid = IPC_PCMCAPTURE;
+       pcmtask_msg->msgtype = PCM_PLTDAI_HW_FREE;
+       msg.task_id = pcmtask_msg->channel_id = id;
+       abox_wdma_request_ipc(data, &msg, 0, 0);
+#ifndef USE_FIXED_MEMORY
+       iommu_unmap(data->abox_data->iommu_domain, IOVA_WDMA_BUFFER(id),
+                       round_up(substream->runtime->dma_bytes, PAGE_SIZE));
+       exynos_sysmmu_tlb_invalidate(data->abox_data->iommu_domain,
+                       (dma_addr_t)IOVA_WDMA_BUFFER(id),
+                       round_up(substream->runtime->dma_bytes, PAGE_SIZE));
+#endif
+
+       switch (data->type) {
+       default:
+               abox_wdma_disable_barrier(dev, data);
+               break;
+       }
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int abox_wdma_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       data->pointer = IOVA_WDMA_BUFFER(id);
+
+       switch (data->type) {
+       case PLATFORM_CALL:
+               break;
+       default:
+               ret = abox_try_to_asrc_off(dev, data->abox_data, rtd,
+                       SNDRV_PCM_STREAM_CAPTURE);
+               if (ret < 0)
+                       dev_warn(dev, "abox_try_to_asrc_off: %d\n", ret);
+               break;
+       }
+
+       msg.ipcid = IPC_PCMCAPTURE;
+       pcmtask_msg->msgtype = PCM_PLTDAI_PREPARE;
+       msg.task_id = pcmtask_msg->channel_id = id;
+       ret = abox_wdma_request_ipc(data, &msg, 0, 0);
+
+       return ret;
+}
+
+static int abox_wdma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_info(dev, "%s[%d](%d)\n", __func__, id, cmd);
+
+       msg.ipcid = IPC_PCMCAPTURE;
+       pcmtask_msg->msgtype = PCM_PLTDAI_TRIGGER;
+       msg.task_id = pcmtask_msg->channel_id = id;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (memblock_is_memory(substream->runtime->dma_addr))
+                       abox_request_dram_on(data->pdev_abox, dev, true);
+
+               pcmtask_msg->param.trigger = 1;
+               ret = abox_wdma_request_ipc(data, &msg, 1, 0);
+               switch (data->type) {
+               case PLATFORM_REALTIME:
+                       msg.ipcid = IPC_ERAP;
+                       msg.msg.erap.msgtype = REALTIME_START;
+                       ret = abox_wdma_request_ipc(data, &msg, 1, 0);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               pcmtask_msg->param.trigger = 0;
+               ret = abox_wdma_request_ipc(data, &msg, 1, 0);
+               switch (data->type) {
+               case PLATFORM_REALTIME:
+                       msg.ipcid = IPC_ERAP;
+                       msg.msg.erap.msgtype = REALTIME_STOP;
+                       ret = abox_wdma_request_ipc(data, &msg, 1, 0);
+                       break;
+               default:
+                       break;
+               }
+
+               if (memblock_is_memory(substream->runtime->dma_addr))
+                       abox_request_dram_on(data->pdev_abox, dev, false);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t abox_wdma_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int id = data->id;
+       ssize_t pointer;
+       u32 status = readl(data->sfr_base + ABOX_WDMA_STATUS);
+       bool progress = (status & ABOX_WDMA_PROGRESS_MASK) ? true : false;
+
+       if (data->pointer >= IOVA_WDMA_BUFFER(id)) {
+               pointer = data->pointer - IOVA_WDMA_BUFFER(id);
+       } else if (((data->type == PLATFORM_NORMAL) ||
+                       (data->type == PLATFORM_SYNC)) && progress) {
+               ssize_t offset, count;
+               ssize_t buffer_bytes, period_bytes;
+
+               buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+               period_bytes = snd_pcm_lib_period_bytes(substream);
+
+               offset = (((status & ABOX_WDMA_RBUF_OFFSET_MASK) >>
+                               ABOX_WDMA_RBUF_OFFSET_L) << 4);
+               count = (status & ABOX_WDMA_RBUF_CNT_MASK);
+
+               while ((offset % period_bytes) && (buffer_bytes >= 0)) {
+                       buffer_bytes -= period_bytes;
+                       if ((buffer_bytes & offset) == offset)
+                               offset = buffer_bytes;
+               }
+
+               pointer = offset + count;
+       } else {
+               pointer = 0;
+       }
+
+       dev_dbg(dev, "%s[%d]: pointer=%08zx\n", __func__, id, pointer);
+
+       return bytes_to_frames(runtime, pointer);
+}
+
+static int abox_wdma_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct abox_data *abox_data = data->abox_data;
+       int id = data->id;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       if (data->type == PLATFORM_CALL) {
+               if (abox_cpu_gear_idle(dev, abox_data,
+                               (void *)ABOX_CPU_GEAR_CALL_VSS))
+                       abox_request_cpu_gear_sync(dev, abox_data,
+                                       (void *)ABOX_CPU_GEAR_CALL_KERNEL,
+                                       ABOX_CPU_GEAR_MAX);
+               ret = abox_request_l2c_sync(dev, abox_data, dev, true);
+               if (ret < 0)
+                       return ret;
+       }
+       abox_request_cpu_gear(dev, abox_data, dev, abox_data->cpu_gear_min);
+
+       snd_soc_set_runtime_hwparams(substream, &abox_wdma_hardware);
+
+       data->substream = substream;
+
+       msg.ipcid = IPC_PCMCAPTURE;
+       pcmtask_msg->msgtype = PCM_PLTDAI_OPEN;
+       msg.task_id = pcmtask_msg->channel_id = id;
+       ret = abox_wdma_request_ipc(data, &msg, 0, 0);
+
+       return ret;
+}
+
+static int abox_wdma_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       struct abox_data *abox_data = data->abox_data;
+       int id = data->id;
+       int ret;
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       dev_dbg(dev, "%s[%d]\n", __func__, id);
+
+       data->substream = NULL;
+
+       msg.ipcid = IPC_PCMCAPTURE;
+       pcmtask_msg->msgtype = PCM_PLTDAI_CLOSE;
+       msg.task_id = pcmtask_msg->channel_id = id;
+       ret = abox_wdma_request_ipc(data, &msg, 0, 1);
+
+       abox_request_cpu_gear(dev, abox_data, dev, ABOX_CPU_GEAR_MIN);
+       if (data->type == PLATFORM_CALL) {
+               abox_request_cpu_gear(dev, abox_data,
+                               (void *)ABOX_CPU_GEAR_CALL_KERNEL,
+                               ABOX_CPU_GEAR_MIN);
+               ret = abox_request_l2c(dev, abox_data, dev, false);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static int abox_wdma_mmap(struct snd_pcm_substream *substream,
+               struct vm_area_struct *vma)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       dev_info(dev, "%s[%d]\n", __func__, id);
+
+       return dma_mmap_writecombine(dev, vma,
+                       runtime->dma_area,
+                       runtime->dma_addr,
+                       runtime->dma_bytes);
+}
+
+static int abox_wdma_ack(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+       snd_pcm_uframes_t appl_ofs = appl_ptr % runtime->buffer_size;
+       ssize_t appl_bytes = frames_to_bytes(runtime, appl_ofs);
+       ABOX_IPC_MSG msg;
+       struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
+
+       if (!data->ack_enabled)
+               return 0;
+
+       dev_dbg(dev, "%s[%d]: %zd\n", __func__, id, appl_bytes);
+
+       msg.ipcid = IPC_PCMCAPTURE;
+       pcmtask_msg->msgtype = PCM_PLTDAI_ACK;
+       pcmtask_msg->param.pointer = (unsigned int)appl_bytes;
+       msg.task_id = pcmtask_msg->channel_id = id;
+
+       return abox_wdma_request_ipc(data, &msg, 0, 0);
+}
+
+static struct snd_pcm_ops abox_wdma_ops = {
+       .open           = abox_wdma_open,
+       .close          = abox_wdma_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = abox_wdma_hw_params,
+       .hw_free        = abox_wdma_hw_free,
+       .prepare        = abox_wdma_prepare,
+       .trigger        = abox_wdma_trigger,
+       .pointer        = abox_wdma_pointer,
+       .mmap           = abox_wdma_mmap,
+       .ack            = abox_wdma_ack,
+};
+
+static int abox_wdma_new(struct snd_soc_pcm_runtime *runtime)
+{
+       struct snd_pcm *pcm = runtime->pcm;
+       struct snd_pcm_str *stream = &pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
+       struct snd_pcm_substream *substream = stream->substream;
+       struct snd_soc_platform *platform = runtime->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+       size_t buffer_bytes;
+       int ret;
+
+       switch (data->type) {
+       case PLATFORM_NORMAL:
+               buffer_bytes = BUFFER_BYTES_MAX;
+               break;
+       default:
+               buffer_bytes = BUFFER_BYTES_MAX >> 2;
+               break;
+       }
+
+       ret = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
+                       runtime->cpu_dai->dev, buffer_bytes, buffer_bytes);
+       if (ret < 0)
+               return ret;
+
+#ifdef USE_FIXED_MEMORY
+       ret = iommu_map(data->abox_data->iommu_domain, IOVA_WDMA_BUFFER(id),
+                       substream->dma_buffer.addr, BUFFER_BYTES_MAX, 0);
+#endif
+       return ret;
+}
+
+static void abox_wdma_free(struct snd_pcm *pcm)
+{
+#ifdef USE_FIXED_MEMORY
+       struct snd_pcm_substream *substream =
+                       pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+       struct snd_soc_pcm_runtime *runtime = substream->private_data;
+       struct snd_soc_platform *platform = runtime->platform;
+       struct device *dev = platform->dev;
+       struct abox_platform_data *data = dev_get_drvdata(dev);
+       int id = data->id;
+
+       iommu_unmap(data->abox_data->iommu_domain, IOVA_WDMA_BUFFER(id),
+                       BUFFER_BYTES_MAX);
+#endif
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static struct snd_soc_platform_driver abox_wdma = {
+       .ops            = &abox_wdma_ops,
+       .pcm_new        = abox_wdma_new,
+       .pcm_free       = abox_wdma_free,
+};
+
+static int samsung_abox_wdma_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct abox_platform_data *data;
+       int ret;
+       const char *type;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, data);
+
+       data->sfr_base = devm_not_request_and_map(pdev, "sfr", 0, NULL, NULL);
+       if (IS_ERR(data->sfr_base))
+               return PTR_ERR(data->sfr_base);
+
+       data->pdev_abox = to_platform_device(pdev->dev.parent);
+       if (!data->pdev_abox) {
+               dev_err(dev, "Failed to get abox platform device\n");
+               return -EPROBE_DEFER;
+       }
+       data->abox_data = platform_get_drvdata(data->pdev_abox);
+
+       abox_register_irq_handler(&data->pdev_abox->dev, IPC_PCMCAPTURE,
+                       abox_wdma_irq_handler, pdev);
+
+       ret = of_property_read_u32_index(np, "id", 0, &data->id);
+       if (ret < 0) {
+               dev_err(dev, "id property reading fail\n");
+               return ret;
+       }
+
+       ret = of_property_read_string(np, "type", &type);
+       if (ret < 0) {
+               dev_err(dev, "type property reading fail\n");
+               return ret;
+       }
+       if (!strncmp(type, "call", sizeof("call")))
+               data->type = PLATFORM_CALL;
+       else if (!strncmp(type, "compress", sizeof("compress")))
+               data->type = PLATFORM_COMPRESS;
+       else if (!strncmp(type, "realtime", sizeof("realtime")))
+               data->type = PLATFORM_REALTIME;
+       else if (!strncmp(type, "vi-sensing", sizeof("vi-sensing")))
+               data->type = PLATFORM_VI_SENSING;
+       else if (!strncmp(type, "sync", sizeof("sync")))
+               data->type = PLATFORM_SYNC;
+       else
+               data->type = PLATFORM_NORMAL;
+
+       data->scsc_bt = !!of_find_property(np, "scsc_bt", NULL);
+
+       abox_register_wdma(data->abox_data->pdev, pdev, data->id);
+
+       pm_runtime_no_callbacks(dev);
+       pm_runtime_enable(dev);
+
+       return snd_soc_register_platform(&pdev->dev, &abox_wdma);
+}
+
+static int samsung_abox_wdma_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_platform(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id samsung_abox_wdma_match[] = {
+       {
+               .compatible = "samsung,abox-wdma",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_abox_wdma_match);
+
+static struct platform_driver samsung_abox_wdma_driver = {
+       .probe  = samsung_abox_wdma_probe,
+       .remove = samsung_abox_wdma_remove,
+       .driver = {
+               .name = "samsung-abox-wdma",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_abox_wdma_match),
+       },
+};
+
+module_platform_driver(samsung_abox_wdma_driver);
+
+/* Module information */
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung ASoC A-Box WDMA Driver");
+MODULE_ALIAS("platform:samsung-abox-wdma");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/exynos9610_madera.c b/sound/soc/samsung/exynos9610_madera.c
new file mode 100644 (file)
index 0000000..820a7af
--- /dev/null
@@ -0,0 +1,1358 @@
+/*
+ *  Driver for Madera CODECs on Exynos9610
+ *
+ *  Copyright 2013 Wolfson Microelectronics
+ *  Copyright 2016 Cirrus Logic
+ *
+ *  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.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/extcon/extcon-madera.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <soc/samsung/exynos-pmu.h>
+#include <sound/samsung/abox.h>
+
+#if IS_ENABLED(CONFIG_SND_SOC_MADERA)
+#include <linux/mfd/madera/core.h>
+#include <linux/extcon/extcon-madera.h>
+#include "../codecs/madera.h"
+#endif
+
+#define MADERA_BASECLK_48K     49152000
+#define MADERA_BASECLK_44K1    45158400
+
+#define MADERA_AMP_RATE        48000
+#define MADERA_AMP_BCLK        (MADERA_AMP_RATE * 16 * 4)
+
+#define EXYNOS_PMU_PMU_DEBUG_OFFSET    0x0A00
+#define MADERA_DAI_ID                  0x4735
+#define MADERA_CODEC_MAX               10
+#define MADERA_AUX_MAX                 2
+
+static unsigned int baserate = MADERA_BASECLK_48K;
+
+enum FLL_ID { FLL1, FLL2, FLL3, FLLAO };
+enum CLK_ID { SYSCLK, ASYNCCLK, DSPCLK, OPCLK, OUTCLK };
+
+/* Used for debugging and test automation */
+static u32 voice_trigger_count;
+
+/* Debugfs value overrides, default to 0 */
+static unsigned int forced_mclk1;
+static unsigned int forced_sysclk;
+static unsigned int forced_dspclk;
+
+struct clk_conf {
+       int id;
+       const char *name;
+       int source;
+       int rate;
+       int fout;
+
+       bool valid;
+};
+
+#define MADERA_MAX_CLOCKS 10
+
+struct madera_drvdata {
+       struct device *dev;
+
+       struct clk_conf fll1_refclk;
+       struct clk_conf fll2_refclk;
+       struct clk_conf fllao_refclk;
+       struct clk_conf sysclk;
+       struct clk_conf asyncclk;
+       struct clk_conf dspclk;
+       struct clk_conf opclk;
+       struct clk_conf outclk;
+
+       struct notifier_block nb;
+
+       int left_amp_dai;
+       int right_amp_dai;
+       struct clk *clk[MADERA_MAX_CLOCKS];
+};
+
+static struct madera_drvdata exynos9610_drvdata;
+
+static int map_fllid_with_name(const char *name)
+{
+       if (!strcmp(name, "fll1-refclk"))
+               return FLL1;
+       else if (!strcmp(name, "fll2-refclk"))
+               return FLL2;
+       else if (!strcmp(name, "fll3-refclk"))
+               return FLL3;
+       else if (!strcmp(name, "fllao-refclk"))
+               return FLLAO;
+       else
+               return -1;
+}
+
+static int map_clkid_with_name(const char *name)
+{
+       if (!strcmp(name, "sysclk"))
+               return SYSCLK;
+       else if (!strcmp(name, "asyncclk"))
+               return ASYNCCLK;
+       else if (!strcmp(name, "dspclk"))
+               return DSPCLK;
+       else if (!strcmp(name, "opclk"))
+               return OPCLK;
+       else if (!strcmp(name, "outclk"))
+               return OUTCLK;
+       else
+               return -1;
+}
+
+static struct snd_soc_pcm_runtime *madera_get_rtd(struct snd_soc_card *card,
+               int id)
+{
+       struct snd_soc_dai_link *dai_link;
+       struct snd_soc_pcm_runtime *rtd = NULL;
+
+       for (dai_link = card->dai_link;
+                       dai_link - card->dai_link < card->num_links;
+                       dai_link++) {
+               if (id == dai_link->id) {
+                       rtd = snd_soc_get_pcm_runtime(card, dai_link->name);
+                       break;
+               }
+       }
+
+       return rtd;
+}
+
+static int madera_start_fll(struct snd_soc_card *card,
+                               struct clk_conf *config)
+{
+       struct snd_soc_dai *codec_dai;
+       struct snd_soc_codec *codec;
+       unsigned int fsrc = 0, fin = 0, fout = 0, pll_id;
+       int ret;
+
+       if (!config->valid)
+               return 0;
+
+       codec_dai = madera_get_rtd(card, MADERA_DAI_ID)->codec_dai;
+       codec = codec_dai->codec;
+
+       pll_id = map_fllid_with_name(config->name);
+       switch (pll_id) {
+       case FLL1:
+               if (forced_mclk1) {
+                       /* use 32kHz input to avoid overclocking the FLL when
+                        * forcing a specific MCLK frequency into the codec
+                        * FLL calculations
+                        */
+#if IS_ENABLED(CONFIG_SND_SOC_MADERA)
+                       fsrc = MADERA_FLL_SRC_MCLK2;
+#endif
+                       fin = forced_mclk1;
+               } else {
+                       fsrc = config->source;
+                       fin = config->rate;
+               }
+
+               if (forced_sysclk)
+                       fout = forced_sysclk;
+               else
+                       fout = config->fout;
+               break;
+       case FLL2:
+       case FLLAO:
+               fsrc = config->source;
+               fin = config->rate;
+               fout = config->fout;
+               break;
+       default:
+               dev_err(card->dev, "Unknown FLLID for %s\n", config->name);
+       }
+
+       dev_dbg(card->dev, "Setting %s fsrc=%d fin=%uHz fout=%uHz\n",
+               config->name, fsrc, fin, fout);
+
+       ret = snd_soc_codec_set_pll(codec, config->id, fsrc, fin, fout);
+       if (ret)
+               dev_err(card->dev, "Failed to start %s\n", config->name);
+
+       return ret;
+}
+
+static int madera_stop_fll(struct snd_soc_card *card,
+                               struct clk_conf *config)
+{
+       struct snd_soc_dai *codec_dai;
+       struct snd_soc_codec *codec;
+       int ret;
+
+       if (!config->valid)
+               return 0;
+
+       codec_dai = madera_get_rtd(card, MADERA_DAI_ID)->codec_dai;
+       codec = codec_dai->codec;
+
+       ret = snd_soc_codec_set_pll(codec, config->id, 0, 0, 0);
+       if (ret)
+               dev_err(card->dev, "Failed to stop %s\n", config->name);
+
+       return ret;
+}
+
+static int madera_set_clock(struct snd_soc_card *card,
+                               struct clk_conf *config)
+{
+       struct snd_soc_dai *aif_dai;
+       struct snd_soc_codec *codec;
+       unsigned int freq = 0, clk_id;
+       int ret;
+       int dir = SND_SOC_CLOCK_IN;
+
+       if (!config->valid)
+               return 0;
+
+       aif_dai = madera_get_rtd(card, MADERA_DAI_ID)->codec_dai;
+       codec = aif_dai->codec;
+
+       clk_id = map_clkid_with_name(config->name);
+       switch (clk_id) {
+       case  SYSCLK:
+               if (forced_sysclk)
+                       freq = forced_sysclk;
+               else
+                       if (config->rate)
+                               freq = config->rate;
+                       else
+                               freq = baserate * 2;
+               break;
+       case ASYNCCLK:
+               freq = config->rate;
+               break;
+       case DSPCLK:
+               if (forced_dspclk)
+                       freq = forced_dspclk;
+               else
+                       if (config->rate)
+                               freq = config->rate;
+                       else
+                               freq = baserate * 3;
+               break;
+       case OPCLK:
+               freq = config->rate;
+               dir = SND_SOC_CLOCK_OUT;
+               break;
+       case OUTCLK:
+               freq = config->rate;
+               break;
+       default:
+               dev_err(card->dev, "Unknown Clock ID for %s\n", config->name);
+       }
+
+       dev_dbg(card->dev, "Setting %s freq to %u Hz\n", config->name, freq);
+
+       ret = snd_soc_codec_set_sysclk(codec, config->id,
+                                      config->source, freq, dir);
+       if (ret)
+               dev_err(card->dev, "Failed to set %s to %u Hz\n",
+                       config->name, freq);
+
+       return ret;
+}
+
+static int madera_stop_clock(struct snd_soc_card *card,
+                               struct clk_conf *config)
+{
+       struct snd_soc_dai *aif_dai;
+       struct snd_soc_codec *codec;
+       int ret;
+
+       if (!config->valid)
+               return 0;
+
+       aif_dai = madera_get_rtd(card, MADERA_DAI_ID)->codec_dai;
+       codec = aif_dai->codec;
+
+       ret = snd_soc_codec_set_sysclk(codec, config->id, 0, 0, 0);
+       if (ret)
+               dev_err(card->dev, "Failed to stop %s\n", config->name);
+
+       return ret;
+}
+
+static int madera_set_clocking(struct snd_soc_card *card,
+                                 struct madera_drvdata *drvdata)
+{
+       int ret;
+
+       ret = madera_start_fll(card, &drvdata->fll1_refclk);
+       if (ret)
+               return ret;
+
+       if (!drvdata->sysclk.rate) {
+               ret = madera_set_clock(card, &drvdata->sysclk);
+               if (ret)
+                       return ret;
+       }
+
+       if (!drvdata->dspclk.rate) {
+               ret = madera_set_clock(card, &drvdata->dspclk);
+               if (ret)
+                       return ret;
+       }
+
+       ret = madera_set_clock(card, &drvdata->opclk);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static const struct snd_soc_ops rdma_ops = {
+};
+
+static const struct snd_soc_ops wdma_ops = {
+};
+
+static int madera_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_card *card = rtd->card;
+       struct madera_drvdata *drvdata = card->drvdata;
+       unsigned int rate = params_rate(params);
+       int ret;
+
+       /* Treat sysclk rate zero as automatic mode */
+       if (!drvdata->sysclk.rate) {
+               if (rate % 4000)
+                       baserate = MADERA_BASECLK_44K1;
+               else
+                       baserate = MADERA_BASECLK_48K;
+       }
+
+       dev_dbg(card->dev, "Requesting Rate: %dHz, FLL: %dHz\n", rate,
+               drvdata->sysclk.rate ? drvdata->sysclk.rate : baserate * 2);
+
+       /* Ensure we can't race against set_bias_level */
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       ret = madera_set_clocking(card, drvdata);
+       mutex_unlock(&card->dapm_mutex);
+
+       return ret;
+}
+
+static int madera_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+       return snd_soc_dai_set_tristate(rtd->cpu_dai, 0);
+}
+
+static void madera_shutdown(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+       snd_soc_dai_set_tristate(rtd->cpu_dai, 1);
+}
+
+static const struct snd_soc_ops uaif0_ops = {
+       .hw_params = madera_hw_params,
+       .prepare = madera_prepare,
+       .shutdown = madera_shutdown,
+};
+
+static const struct snd_soc_ops uaif_ops = {
+};
+
+static int dsif_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       int tx_slot[] = {0, 1};
+
+       /* bclk ratio 64 for DSD64, 128 for DSD128 */
+       snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
+
+       /* channel map 0 1 if left is first, 1 0 if right is first */
+       snd_soc_dai_set_channel_map(cpu_dai, 2, tx_slot, 0, NULL);
+       return 0;
+}
+
+static const struct snd_soc_ops dsif_ops = {
+       .hw_params = dsif_hw_params,
+};
+
+static int exynos9610_set_bias_level(struct snd_soc_card *card,
+                                 struct snd_soc_dapm_context *dapm,
+                                 enum snd_soc_bias_level level)
+{
+       struct snd_soc_dai *codec_dai;
+       struct madera_drvdata *drvdata = card->drvdata;
+       int ret;
+
+       codec_dai = madera_get_rtd(card, MADERA_DAI_ID)->codec_dai;
+
+       if (dapm->dev != codec_dai->dev)
+               return 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_STANDBY:
+               if (dapm->bias_level != SND_SOC_BIAS_OFF)
+                       break;
+
+               ret = madera_set_clocking(card, drvdata);
+               if (ret)
+                       return ret;
+
+               ret = madera_start_fll(card, &drvdata->fll2_refclk);
+               if (ret)
+                       return ret;
+
+               ret = madera_start_fll(card, &drvdata->fllao_refclk);
+               if (ret)
+                       return ret;
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int exynos9610_set_bias_level_post(struct snd_soc_card *card,
+                                      struct snd_soc_dapm_context *dapm,
+                                      enum snd_soc_bias_level level)
+{
+       struct snd_soc_dai *codec_dai;
+       struct madera_drvdata *drvdata = card->drvdata;
+       int ret;
+
+       codec_dai = madera_get_rtd(card, MADERA_DAI_ID)->codec_dai;
+
+       if (dapm->dev != codec_dai->dev)
+               return 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_OFF:
+               ret = madera_stop_fll(card, &drvdata->fll1_refclk);
+               if (ret)
+                       return ret;
+
+               ret = madera_stop_fll(card, &drvdata->fll2_refclk);
+               if (ret)
+                       return ret;
+
+               ret = madera_stop_fll(card, &drvdata->fllao_refclk);
+               if (ret)
+                       return ret;
+
+               if (!drvdata->sysclk.rate) {
+                       ret = madera_stop_clock(card, &drvdata->sysclk);
+                       if (ret)
+                               return ret;
+               }
+
+               if (!drvdata->dspclk.rate) {
+                       ret = madera_stop_clock(card, &drvdata->dspclk);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_MADERA)
+static int madera_notify(struct notifier_block *nb,
+                                  unsigned long event, void *data)
+{
+       const struct madera_hpdet_notify_data *hp_inf;
+       const struct madera_micdet_notify_data *md_inf;
+       const struct madera_voice_trigger_info *vt_inf;
+       const struct madera_drvdata *drvdata =
+               container_of(nb, struct madera_drvdata, nb);
+
+       switch (event) {
+       case MADERA_NOTIFY_VOICE_TRIGGER:
+               vt_inf = data;
+               dev_info(drvdata->dev, "Voice Triggered (core_num=%d)\n",
+                        vt_inf->core_num);
+               ++voice_trigger_count;
+               break;
+       case MADERA_NOTIFY_HPDET:
+               hp_inf = data;
+               dev_info(drvdata->dev, "HPDET val=%d.%02d ohms\n",
+                        hp_inf->impedance_x100 / 100,
+                        hp_inf->impedance_x100 % 100);
+               break;
+       case MADERA_NOTIFY_MICDET:
+               md_inf = data;
+               dev_info(drvdata->dev, "MICDET present=%c val=%d.%02d ohms\n",
+                        md_inf->present ? 'Y' : 'N',
+                        md_inf->impedance_x100 / 100,
+                        md_inf->impedance_x100 % 100);
+               break;
+       default:
+               dev_info(drvdata->dev, "notifier event=0x%lx data=0x%p\n",
+                        event, data);
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int madera_force_fll1_enable_write(void *data, u64 val)
+{
+       struct snd_soc_card *card = data;
+       struct madera_drvdata *drvdata = card->drvdata;
+       int ret;
+
+       if (val == 0)
+               ret = madera_stop_fll(card, &drvdata->fll1_refclk);
+       else
+               ret = madera_start_fll(card, &drvdata->fll1_refclk);
+
+       return ret;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(madera_force_fll1_enable_fops, NULL,
+                       madera_force_fll1_enable_write, "%llu\n");
+
+static void madera_init_debugfs(struct snd_soc_card *card)
+{
+       struct dentry *root;
+
+       if (!card->debugfs_card_root) {
+               dev_warn(card->dev, "No card debugfs root\n");
+               return;
+       }
+
+       root = debugfs_create_dir("test-automation", card->debugfs_card_root);
+       if (!root) {
+               dev_warn(card->dev, "Failed to create debugfs dir\n");
+               return;
+       }
+
+       debugfs_create_u32("voice_trigger_count", S_IRUGO, root,
+                          &voice_trigger_count);
+
+       debugfs_create_u32("forced_mclk1", S_IRUGO | S_IWUGO, root,
+                          &forced_mclk1);
+       debugfs_create_u32("forced_sysclk", S_IRUGO | S_IWUGO, root,
+                          &forced_sysclk);
+       debugfs_create_u32("forced_dspclk", S_IRUGO | S_IWUGO, root,
+                          &forced_dspclk);
+
+       debugfs_create_file("force_fll1_enable", S_IWUSR | S_IWGRP, root, card,
+                           &madera_force_fll1_enable_fops);
+}
+#else
+static void madera_init_debugfs(struct snd_soc_card *card)
+{
+}
+#endif
+
+static int madera_amp_late_probe(struct snd_soc_card *card, int dai)
+{
+       struct madera_drvdata *drvdata = card->drvdata;
+       struct snd_soc_pcm_runtime *rtd;
+       struct snd_soc_dai *amp_dai;
+       struct snd_soc_codec *amp;
+       int ret;
+
+       if (!dai || !card->dai_link[dai].name)
+               return 0;
+
+       if (!drvdata->opclk.valid) {
+               dev_err(card->dev, "OPCLK required to use speaker amp\n");
+               return -ENOENT;
+       }
+
+       rtd = snd_soc_get_pcm_runtime(card, card->dai_link[dai].name);
+
+       amp_dai = rtd->codec_dai;
+       amp = amp_dai->codec;
+
+       ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 4, 16);
+       if (ret)
+               dev_err(card->dev, "Failed to set TDM: %d\n", ret);
+
+       ret = snd_soc_codec_set_sysclk(amp, 0, 0, drvdata->opclk.rate,
+                                      SND_SOC_CLOCK_IN);
+       if (ret != 0) {
+               dev_err(card->dev, "Failed to set amp SYSCLK: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_sysclk(amp_dai, 0, MADERA_AMP_BCLK,
+                                    SND_SOC_CLOCK_IN);
+       if (ret != 0) {
+               dev_err(card->dev, "Failed to set amp DAI clock: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int exynos9610_late_probe(struct snd_soc_card *card)
+{
+       struct madera_drvdata *drvdata = card->drvdata;
+       struct snd_soc_dai *aif_dai;
+       struct snd_soc_codec *codec;
+       struct snd_soc_component *cpu;
+       int ret;
+
+       aif_dai = madera_get_rtd(card, 0)->cpu_dai;
+       cpu = aif_dai->component;
+
+       aif_dai = madera_get_rtd(card, MADERA_DAI_ID)->codec_dai;
+       codec = aif_dai->codec;
+
+       ret = snd_soc_dai_set_sysclk(aif_dai, drvdata->sysclk.id, 0, 0);
+       if (ret != 0) {
+               dev_err(drvdata->dev, "Failed to set AIF1 clock: %d\n", ret);
+               return ret;
+       }
+
+       if (drvdata->sysclk.rate) {
+               ret = madera_set_clock(card, &drvdata->sysclk);
+               if (ret)
+                       return ret;
+       }
+
+       if (drvdata->dspclk.rate) {
+               ret = madera_set_clock(card, &drvdata->dspclk);
+               if (ret)
+                       return ret;
+       }
+
+       ret = madera_set_clock(card, &drvdata->asyncclk);
+       if (ret)
+               return ret;
+
+       ret = madera_set_clock(card, &drvdata->outclk);
+       if (ret)
+               return ret;
+
+       ret = madera_amp_late_probe(card, drvdata->left_amp_dai);
+       if (ret)
+               return ret;
+
+       ret = madera_amp_late_probe(card, drvdata->right_amp_dai);
+       if (ret)
+               return ret;
+
+       snd_soc_dapm_ignore_suspend(&card->dapm, "VOUTPUT");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "VINPUT1");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "VINPUT2");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "HEADSETMIC");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "RECEIVER");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "HEADPHONE");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "SPEAKER");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "DMIC1");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "DMIC2");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "DMIC3");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "VTS Virtual Output");
+       snd_soc_dapm_sync(&card->dapm);
+
+       snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "AIF1 Playback");
+       snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "AIF1 Capture");
+       snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX RDMA0 Playback");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX RDMA1 Playback");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX RDMA2 Playback");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX RDMA3 Playback");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX RDMA4 Playback");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX RDMA5 Playback");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX RDMA6 Playback");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX RDMA7 Playback");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX WDMA0 Capture");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX WDMA1 Capture");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX WDMA2 Capture");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX WDMA3 Capture");
+       snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(cpu), "ABOX WDMA4 Capture");
+       snd_soc_dapm_sync(snd_soc_component_get_dapm(cpu));
+
+       madera_init_debugfs(card);
+
+#if IS_ENABLED(CONFIG_SND_SOC_MADERA)
+       drvdata->nb.notifier_call = madera_notify;
+       madera_register_notifier(codec, &drvdata->nb);
+#endif
+
+       return 0;
+}
+
+static struct snd_soc_pcm_stream madera_amp_params[] = {
+       {
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               .rate_min = MADERA_AMP_RATE,
+               .rate_max = MADERA_AMP_RATE,
+               .channels_min = 1,
+               .channels_max = 1,
+       },
+};
+
+static struct snd_soc_dai_link exynos9610_dai[] = {
+       {
+               .name = "RDMA0",
+               .stream_name = "RDMA0",
+               .platform_name = "14a51000.abox_rdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &rdma_ops,
+               .dpcm_playback = 1,
+       },
+       {
+               .name = "RDMA1",
+               .stream_name = "RDMA1",
+               .platform_name = "14a51100.abox_rdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &rdma_ops,
+               .dpcm_playback = 1,
+       },
+       {
+               .name = "RDMA2",
+               .stream_name = "RDMA2",
+               .platform_name = "14a51200.abox_rdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &rdma_ops,
+               .dpcm_playback = 1,
+       },
+       {
+               .name = "RDMA3",
+               .stream_name = "RDMA3",
+               .platform_name = "14a51300.abox_rdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &rdma_ops,
+               .dpcm_playback = 1,
+       },
+       {
+               .name = "RDMA4",
+               .stream_name = "RDMA4",
+               .platform_name = "14a51400.abox_rdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &rdma_ops,
+               .dpcm_playback = 1,
+       },
+       {
+               .name = "RDMA5",
+               .stream_name = "RDMA5",
+               .platform_name = "14a51500.abox_rdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &rdma_ops,
+               .dpcm_playback = 1,
+       },
+       {
+               .name = "RDMA6",
+               .stream_name = "RDMA6",
+               .platform_name = "14a51600.abox_rdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &rdma_ops,
+               .dpcm_playback = 1,
+       },
+       {
+               .name = "RDMA7",
+               .stream_name = "RDMA7",
+               .platform_name = "14a51700.abox_rdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &rdma_ops,
+               .dpcm_playback = 1,
+       },
+       {
+               .name = "WDMA0",
+               .stream_name = "WDMA0",
+               .platform_name = "14a52000.abox_wdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &wdma_ops,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "WDMA1",
+               .stream_name = "WDMA1",
+               .platform_name = "14a52100.abox_wdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &wdma_ops,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "WDMA2",
+               .stream_name = "WDMA2",
+               .platform_name = "14a52200.abox_wdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &wdma_ops,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "WDMA3",
+               .stream_name = "WDMA3",
+               .platform_name = "14a52300.abox_wdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &wdma_ops,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "WDMA4",
+               .stream_name = "WDMA4",
+               .platform_name = "14a52400.abox_wdma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dynamic = 1,
+               .ignore_suspend = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST},
+               .ops = &wdma_ops,
+               .dpcm_capture = 1,
+       },
+#if IS_ENABLED(SND_SOC_SAMSUNG_DISPLAYPORT)
+       {
+               .name = "DP Audio",
+               .stream_name = "DP Audio",
+               .cpu_dai_name = "audio_cpu_dummy",
+               .platform_name = "dp_dma",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+       },
+#endif
+       {
+               .name = "UAIF0",
+               .stream_name = "UAIF0",
+               .platform_name = "snd-soc-dummy",
+               .id = MADERA_DAI_ID,
+               .no_pcm = 1,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = abox_hw_params_fixup_helper,
+               .ops = &uaif0_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "UAIF1",
+               .stream_name = "UAIF1",
+               .platform_name = "snd-soc-dummy",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .no_pcm = 1,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = abox_hw_params_fixup_helper,
+               .ops = &uaif_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "UAIF2",
+               .stream_name = "UAIF2",
+               .platform_name = "snd-soc-dummy",
+               .no_pcm = 1,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = abox_hw_params_fixup_helper,
+               .ops = &uaif_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "UAIF4",
+               .stream_name = "UAIF4",
+               .platform_name = "snd-soc-dummy",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .no_pcm = 1,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = abox_hw_params_fixup_helper,
+               .ops = &uaif_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "DSIF",
+               .stream_name = "DSIF",
+               .cpu_dai_name = "DSIF",
+               .platform_name = "snd-soc-dummy",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .no_pcm = 1,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = abox_hw_params_fixup_helper,
+               .ops = &dsif_ops,
+               .dpcm_playback = 1,
+       },
+       {
+               .name = "SPDY",
+               .stream_name = "SPDY",
+               .cpu_dai_name = "SPDY",
+               .platform_name = "snd-soc-dummy",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .no_pcm = 1,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = abox_hw_params_fixup_helper,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "SIFS0",
+               .stream_name = "SIFS0",
+               .cpu_dai_name = "SIFS0",
+               .platform_name = "snd-soc-dummy",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .no_pcm = 1,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = abox_hw_params_fixup_helper,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "SIFS1",
+               .stream_name = "SIFS1",
+               .cpu_dai_name = "SIFS1",
+               .platform_name = "snd-soc-dummy",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .no_pcm = 1,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = abox_hw_params_fixup_helper,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "SIFS2",
+               .stream_name = "SIFS2",
+               .cpu_dai_name = "SIFS2",
+               .platform_name = "snd-soc-dummy",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .no_pcm = 1,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = abox_hw_params_fixup_helper,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "codec-left-amp",
+               .params = madera_amp_params,
+       },
+
+};
+
+static const char * const vts_output_texts[] = {
+        "None",
+        "DMIC1",
+};
+
+static const struct snd_kcontrol_new exynos9610_controls[] = {
+       SOC_DAPM_PIN_SWITCH("DMIC1"),
+       SOC_DAPM_PIN_SWITCH("DMIC2"),
+       SOC_DAPM_PIN_SWITCH("DMIC3"),
+};
+
+static struct snd_soc_dapm_widget exynos9610_widgets[] = {
+       SND_SOC_DAPM_OUTPUT("VOUTPUT"),
+       SND_SOC_DAPM_INPUT("VINPUT1"),
+       SND_SOC_DAPM_INPUT("VINPUT2"),
+       SND_SOC_DAPM_OUTPUT("VOUTPUTCALL"),
+       SND_SOC_DAPM_INPUT("VINPUTCALL"),
+       SND_SOC_DAPM_MIC("DMIC1", NULL),
+       SND_SOC_DAPM_MIC("DMIC2", NULL),
+       SND_SOC_DAPM_MIC("DMIC3", NULL),
+       SND_SOC_DAPM_MIC("HEADSETMIC", NULL),
+       SND_SOC_DAPM_SPK("RECEIVER", NULL),
+       SND_SOC_DAPM_HP("HEADPHONE", NULL),
+       SND_SOC_DAPM_SPK("SPEAKER", NULL),
+       SND_SOC_DAPM_MIC("BLUETOOTH MIC", NULL),
+       SND_SOC_DAPM_SPK("BLUETOOTH SPK", NULL),
+};
+
+static struct snd_soc_codec_conf codec_conf[MADERA_CODEC_MAX];
+
+static struct snd_soc_aux_dev aux_dev[MADERA_AUX_MAX];
+
+static struct snd_soc_card exynos9610_madera = {
+       .name = "Exynos9610-Madera",
+       .owner = THIS_MODULE,
+       .dai_link = exynos9610_dai,
+       .num_links = ARRAY_SIZE(exynos9610_dai),
+
+       .late_probe = exynos9610_late_probe,
+
+       .controls = exynos9610_controls,
+       .num_controls = ARRAY_SIZE(exynos9610_controls),
+       .dapm_widgets = exynos9610_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(exynos9610_widgets),
+
+       .set_bias_level = exynos9610_set_bias_level,
+       .set_bias_level_post = exynos9610_set_bias_level_post,
+
+       .drvdata = (void *)&exynos9610_drvdata,
+
+       .codec_conf = codec_conf,
+       .num_configs = ARRAY_SIZE(codec_conf),
+
+       .aux_dev = aux_dev,
+       .num_aux_devs = ARRAY_SIZE(aux_dev),
+};
+
+static int read_clk_conf(struct device_node *np,
+                                  const char * const prop,
+                                  struct clk_conf *conf,
+                                  bool is_fll)
+{
+       u32 tmp;
+       int ret;
+
+       /*Truncate "cirrus," from prop_name to fetch clk_name*/
+       conf->name = &prop[7];
+
+       ret = of_property_read_u32_index(np, prop, 0, &tmp);
+       if (ret)
+               return ret;
+
+       conf->id = tmp;
+
+       ret = of_property_read_u32_index(np, prop, 1, &tmp);
+       if (ret)
+               return ret;
+
+       if (tmp < 0xffff)
+               conf->source = tmp;
+       else
+               conf->source = -1;
+
+       ret = of_property_read_u32_index(np, prop, 2, &tmp);
+       if (ret)
+               return ret;
+
+       conf->rate = tmp;
+
+       if (is_fll) {
+               ret = of_property_read_u32_index(np, prop, 3, &tmp);
+               if (ret)
+                       return ret;
+               conf->fout = tmp;
+       }
+
+       conf->valid = true;
+
+       return 0;
+}
+
+static int read_dai(struct device_node *np, const char * const prop,
+                             struct device_node **dai, const char **name)
+{
+       int ret = 0;
+
+       np = of_get_child_by_name(np, prop);
+       if (!np)
+               return -ENOENT;
+
+       *dai = of_parse_phandle(np, "sound-dai", 0);
+       if (!*dai) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       if (*name == NULL) {
+               /* Ignoring the return as we don't register DAIs to the platform */
+               ret = snd_soc_of_get_dai_name(np, name);
+               if (ret && !*name)
+                       return ret;
+       }
+out:
+       of_node_put(np);
+
+       return ret;
+}
+
+static struct clk *xclkout;
+
+static void control_xclkout(bool on)
+{
+       if (on) {
+               clk_prepare_enable(xclkout);
+       } else {
+               clk_disable_unprepare(xclkout);
+       }
+}
+
+static int exynos9610_audio_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &exynos9610_madera;
+       struct madera_drvdata *drvdata = card->drvdata;
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *dai;
+       int nlink = 0;
+       int i, rc, ret;
+       const char *cur = NULL;
+       struct property *p;
+
+       card->dev = &pdev->dev;
+       drvdata->dev = card->dev;
+
+       snd_soc_card_set_drvdata(card, drvdata);
+
+       xclkout = devm_clk_get(&pdev->dev, "xclkout");
+       if (IS_ERR(xclkout)) {
+               dev_err(&pdev->dev, "xclkout get failed\n");
+               xclkout = NULL;
+       }
+       control_xclkout(true);
+       dev_info(&pdev->dev, "xclkout is enabled\n");
+
+       i = 0;
+       p = of_find_property(np, "clock-names", NULL);
+       if (p) {
+               while ((cur = of_prop_next_string(p, cur)) != NULL) {
+                       drvdata->clk[i] = devm_clk_get(drvdata->dev, cur);
+                       if (IS_ERR(drvdata->clk[i])) {
+                               dev_info(drvdata->dev, "Failed to get %s: %ld\n",
+                                        cur, PTR_ERR(drvdata->clk[i]));
+                               drvdata->clk[i] = NULL;
+                               break;
+                       }
+
+                       clk_prepare_enable(drvdata->clk[i]);
+
+                       if (++i == MADERA_MAX_CLOCKS)
+                               break;
+               }
+       }
+
+       ret = read_clk_conf(np, "cirrus,sysclk",
+                       &drvdata->sysclk, false);
+       if (ret) {
+               dev_err(card->dev, "Failed to parse sysclk: %d\n", ret);
+               return ret;
+       }
+       ret = read_clk_conf(np, "cirrus,asyncclk",
+                       &drvdata->asyncclk, false);
+       if (ret)
+               dev_info(card->dev, "Failed to parse asyncclk: %d\n", ret);
+
+       ret = read_clk_conf(np, "cirrus,dspclk",
+                       &drvdata->dspclk, false);
+       if (ret) {
+               dev_info(card->dev, "Failed to parse dspclk: %d\n", ret);
+       }
+
+       ret = read_clk_conf(np, "cirrus,opclk",
+                       &drvdata->opclk, false);
+       if (ret)
+               dev_info(card->dev, "Failed to parse opclk: %d\n", ret);
+
+       ret = read_clk_conf(np, "cirrus,fll1-refclk",
+                       &drvdata->fll1_refclk, true);
+       if (ret)
+               dev_info(card->dev, "Failed to parse fll1-refclk: %d\n", ret);
+
+       ret = read_clk_conf(np, "cirrus,fll2-refclk",
+                       &drvdata->fll2_refclk, true);
+       if (ret)
+               dev_info(card->dev, "Failed to parse fll2-refclk: %d\n", ret);
+
+       ret = read_clk_conf(np, "cirrus,fllao-refclk",
+                       &drvdata->fllao_refclk, true);
+       if (ret)
+               dev_info(card->dev, "Failed to parse fllao-refclk: %d\n", ret);
+
+       ret = read_clk_conf(np, "cirrus,outclk",
+                       &drvdata->outclk, false);
+       if (ret)
+               dev_info(card->dev, "Failed to parse outclk: %d\n", ret);
+
+       for_each_child_of_node(np, dai) {
+               if (!exynos9610_dai[nlink].name)
+                       exynos9610_dai[nlink].name = dai->name;
+               if (!exynos9610_dai[nlink].stream_name)
+                       exynos9610_dai[nlink].stream_name = dai->name;
+
+               if (!exynos9610_dai[nlink].cpu_name) {
+                       ret = read_dai(dai, "cpu",
+                                       &exynos9610_dai[nlink].cpu_of_node,
+                                       &exynos9610_dai[nlink].cpu_dai_name);
+                       if (ret) {
+                               dev_err(card->dev,
+                                       "Failed to parse cpu DAI for %s: %d\n",
+                                       dai->name, ret);
+                               return ret;
+                       }
+               }
+
+               if (!exynos9610_dai[nlink].platform_name) {
+                       ret = read_dai(dai, "platform",
+                               &exynos9610_dai[nlink].platform_of_node,
+                               &exynos9610_dai[nlink].platform_name);
+                       if (ret) {
+                               exynos9610_dai[nlink].platform_of_node =
+                                       exynos9610_dai[nlink].cpu_of_node;
+                               dev_info(card->dev,
+                                       "Cpu node is used as platform for %s: %d\n",
+                                       dai->name, ret);
+                       }
+               }
+
+               if (!exynos9610_dai[nlink].codec_name) {
+                       ret = read_dai(dai, "codec",
+                                       &exynos9610_dai[nlink].codec_of_node,
+                                       &exynos9610_dai[nlink].codec_dai_name);
+                       if (ret) {
+                               dev_err(card->dev,
+                                       "Failed to parse codec DAI for %s: %d\n",
+                                       dai->name, ret);
+                               return ret;
+                       }
+               }
+
+               if (strstr(dai->name, "left-amp")) {
+                       exynos9610_dai[nlink].params = madera_amp_params;
+                       drvdata->left_amp_dai = nlink;
+               } else if (strstr(dai->name, "right-amp")) {
+                       exynos9610_dai[nlink].params = madera_amp_params;
+                       drvdata->right_amp_dai = nlink;
+               }
+
+               exynos9610_dai[nlink].dai_fmt =
+                               snd_soc_of_parse_daifmt(dai, NULL, NULL, NULL);
+
+               if (++nlink == card->num_links)
+                       break;
+       }
+
+       if (!nlink) {
+               dev_err(card->dev, "No DAIs specified\n");
+               return -EINVAL;
+       }
+
+       if (of_property_read_bool(np, "samsung,routing")) {
+               ret = snd_soc_of_parse_audio_routing(card, "samsung,routing");
+               if (ret)
+                       return ret;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(codec_conf); i++) {
+               codec_conf[i].of_node = of_parse_phandle(np, "samsung,codec", i);
+               if (IS_ERR_OR_NULL(codec_conf[i].of_node)) {
+                       exynos9610_madera.num_configs = i;
+                       break;
+               }
+
+               rc = of_property_read_string_index(np, "samsung,prefix", i,
+                               &codec_conf[i].name_prefix);
+               if (rc < 0)
+                       codec_conf[i].name_prefix = "";
+       }
+
+       for (i = 0; i < ARRAY_SIZE(aux_dev); i++) {
+               aux_dev[i].codec_of_node = of_parse_phandle(np, "samsung,aux", i);
+               if (IS_ERR_OR_NULL(aux_dev[i].codec_of_node)) {
+                       exynos9610_madera.num_aux_devs = i;
+                       break;
+               }
+       }
+
+       ret = devm_snd_soc_register_card(card->dev, card);
+       if (ret)
+               dev_err(card->dev, "snd_soc_register_card() failed:%d\n", ret);
+
+       return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id exynos9610_of_match[] = {
+       { .compatible = "samsung,exynos9610-madera", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos9610_of_match);
+#endif /* CONFIG_OF */
+
+static struct platform_driver exynos9610_audio_driver = {
+       .driver         = {
+               .name   = "exynos9610-madera",
+               .owner  = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = of_match_ptr(exynos9610_of_match),
+       },
+
+       .probe          = exynos9610_audio_probe,
+};
+
+module_platform_driver(exynos9610_audio_driver);
+
+MODULE_DESCRIPTION("ALSA SoC Exynos9610 Madera Driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_AUTHOR("Gyeongtaek Lee <gt82.lee@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos9610-madera");
diff --git a/sound/soc/samsung/vts/Kconfig b/sound/soc/samsung/vts/Kconfig
new file mode 100644 (file)
index 0000000..abc1a37
--- /dev/null
@@ -0,0 +1,11 @@
+config SND_SOC_SAMSUNG_MAILBOX
+       bool "Samsung MAILBOX"
+       select GENERIC_IRQ_CHIP
+       help
+         Say Y if you want to use mailbox for voice trigger system.
+
+config SND_SOC_SAMSUNG_VTS
+       bool "Samsung VTS"
+       depends on SND_SOC_SAMSUNG_MAILBOX
+       help
+         Say Y if you want to use voice trigger system.
diff --git a/sound/soc/samsung/vts/Makefile b/sound/soc/samsung/vts/Makefile
new file mode 100644 (file)
index 0000000..8d869d3
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SND_SOC_SAMSUNG_MAILBOX) += mailbox.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_VTS) += vts.o vts_dma.o vts_log.o vts_dump.o
diff --git a/sound/soc/samsung/vts/mailbox.c b/sound/soc/samsung/vts/mailbox.c
new file mode 100644 (file)
index 0000000..f37dbbd
--- /dev/null
@@ -0,0 +1,255 @@
+/* sound/soc/samsung/vts/mailbox.c
+ *
+ * ALSA SoC - Samsung Mailbox driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+  *
+ * 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.
+ */
+//#define DEBUG
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <sound/samsung/vts.h>
+
+#include "mailbox.h"
+
+#define DEVICE_NAME "samsung-mailbox-asoc"
+
+struct mailbox_data {
+       void __iomem *sfr_base;
+       struct irq_domain *irq_domain;
+       int irq;
+};
+
+int mailbox_generate_interrupt(const struct platform_device *pdev, int hw_irq)
+{
+       struct mailbox_data *data = platform_get_drvdata(pdev);
+
+       writel(BIT(hw_irq), data->sfr_base + MAILBOX_INTGR0);
+       dev_dbg(&pdev->dev, "%s: writel(%lx, %lx)", __func__,
+                       BIT(hw_irq), (unsigned long)virt_to_phys(data->sfr_base + MAILBOX_INTGR0));
+
+       return 0;
+}
+EXPORT_SYMBOL(mailbox_generate_interrupt);
+
+void mailbox_write_shared_register(const struct platform_device *pdev,
+               const u32 *values, int start, int count)
+{
+       struct mailbox_data *data = platform_get_drvdata(pdev);
+       u32 __iomem *sfr = data->sfr_base + MAILBOX_ISSR0 + (start * sizeof(u32));
+
+       dev_dbg(&pdev->dev, "%s(%p, %p, %d, %d)", __func__, pdev, values, start, count);
+       while (count--) {
+               writel_relaxed(*values++, sfr++);
+       }
+}
+EXPORT_SYMBOL(mailbox_write_shared_register);
+
+void mailbox_read_shared_register(const struct platform_device *pdev,
+               u32 *values, int start, int count)
+{
+       struct mailbox_data *data = platform_get_drvdata(pdev);
+       u32 __iomem *sfr = data->sfr_base + MAILBOX_ISSR0 + (start * sizeof(u32));
+
+       dev_dbg(&pdev->dev, "%s(%p, %p, %d, %d)", __func__, pdev, values, start, count);
+       while (count--) {
+               *values++ = readl_relaxed(sfr++);
+               dev_dbg(&pdev->dev, "value=%d\n", *(values - 1));
+       }
+}
+EXPORT_SYMBOL(mailbox_read_shared_register);
+
+static void mailbox_handle_irq(struct irq_desc *desc)
+{
+       struct platform_device *pdev = irq_desc_get_handler_data(desc);
+       struct mailbox_data *data = platform_get_drvdata(pdev);
+       struct irq_domain *irq_domain = data->irq_domain;
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+
+       struct irq_chip_generic *gc = irq_get_domain_generic_chip(irq_domain, 0);
+       u32 stat = readl_relaxed(gc->reg_base + MAILBOX_INTSR1);
+
+       dev_dbg(&pdev->dev, "%s: stat=%08x\n", __func__, stat);
+
+       chained_irq_enter(chip, desc);
+
+       while (stat) {
+               u32 hwirq = __fls(stat);
+               generic_handle_irq(irq_find_mapping(irq_domain, gc->irq_base + hwirq));
+               stat &= ~(1 << hwirq);
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static int mailbox_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int mailbox_resume(struct device *dev)
+{
+       return 0;
+}
+
+static const struct of_device_id samsung_mailbox_of_match[] = {
+       {
+               .compatible = "samsung,mailbox-asoc",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_mailbox_of_match);
+
+SIMPLE_DEV_PM_OPS(samsung_mailbox_pm, mailbox_suspend, mailbox_resume);
+
+static void mailbox_irq_enable(struct irq_data *data)
+{
+       if (vts_is_on()) {
+               irq_gc_mask_clr_bit(data);
+       } else {
+               pr_debug("%s(%d): vts is already off\n",
+                               __func__, data->irq);
+       }
+       return;
+}
+
+static void mailbox_irq_disable(struct irq_data *data)
+{
+       if (vts_is_on()) {
+               irq_gc_mask_set_bit(data);
+       } else {
+               pr_debug("%s(%d): vts is already off\n",
+                               __func__, data->irq);
+       }
+       return;
+}
+
+static void __iomem *devm_request_and_ioremap(struct platform_device *pdev, const char *name)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       void __iomem *result;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(dev, "Failed to get %s\n", name);
+               return ERR_PTR(-EINVAL);
+       }
+
+       res = devm_request_mem_region(dev, res->start, resource_size(res), name);
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(dev, "Failed to request %s\n", name);
+               return ERR_PTR(-EFAULT);
+       }
+
+       result = devm_ioremap(dev, res->start, resource_size(res));
+       if (IS_ERR_OR_NULL(result)) {
+               dev_err(dev, "Failed to map %s\n", name);
+               return ERR_PTR(-EFAULT);
+       }
+
+       return result;
+}
+
+static int samsung_mailbox_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct mailbox_data *data;
+       struct irq_chip_generic *gc;
+       int result;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (IS_ERR_OR_NULL(data)) {
+               dev_err(dev, "Failed to allocate memory\n");
+               return -ENOMEM;
+       }
+       platform_set_drvdata(pdev, data);
+
+       data->sfr_base = devm_request_and_ioremap(pdev, "sfr");
+       if (IS_ERR(data->sfr_base)) {
+               return PTR_ERR(data->sfr_base);
+       }
+
+       result = platform_get_irq(pdev, 0);
+       if (result < 0) {
+               dev_err(dev, "Failed to get irq resource\n");
+               return result;
+       }
+       data->irq = result;
+
+       data->irq_domain = irq_domain_add_linear(np, MAILBOX_INT1_OFFSET + MAILBOX_INT1_SIZE,
+                       &irq_generic_chip_ops, NULL);
+       if (IS_ERR_OR_NULL(data->irq_domain)) {
+               dev_err(dev, "Failed to add irq domain\n");
+               return -ENOMEM;
+       }
+
+       result = irq_alloc_domain_generic_chips(data->irq_domain, MAILBOX_INT1_OFFSET + MAILBOX_INT1_SIZE,
+                       1, "mailbox_irq_chip", handle_level_irq, 0, 0, IRQ_GC_INIT_MASK_CACHE);
+       if (result < 0) {
+               dev_err(dev, "Failed to allocation generic irq chips\n");
+               return result;
+       }
+
+       gc = irq_get_domain_generic_chip(data->irq_domain, 0);
+       gc->reg_base                            = data->sfr_base;
+       gc->chip_types[0].chip.irq_enable       = mailbox_irq_enable;
+       gc->chip_types[0].chip.irq_disable      = mailbox_irq_disable;
+       gc->chip_types[0].chip.irq_ack          = irq_gc_ack_set_bit;
+       gc->chip_types[0].chip.irq_mask         = irq_gc_mask_set_bit;
+       gc->chip_types[0].chip.irq_unmask       = irq_gc_mask_clr_bit;
+       gc->chip_types[0].regs.mask             = MAILBOX_INTMR1;
+       gc->chip_types[0].regs.ack              = MAILBOX_INTCR1;
+       gc->wake_enabled                        = 0x0000FFFF;
+       gc->chip_types[0].chip.irq_set_wake     = irq_gc_set_wake;
+
+       irq_set_handler_data(data->irq, pdev);
+       irq_set_chained_handler(data->irq, mailbox_handle_irq);
+
+       dev_info(dev, "Probed successfully\n");
+
+       return 0;
+}
+
+static int samsung_mailbox_remove(struct platform_device *pdev)
+{
+       struct mailbox_data *mailbox_data = platform_get_drvdata(pdev);
+
+       dev_info(&pdev->dev, "%s\n", __func__);
+
+       irq_remove_generic_chip(irq_get_domain_generic_chip(mailbox_data->irq_domain, 0),
+                       IRQ_MSK(MAILBOX_INT1_SIZE), 0, 0);
+       irq_domain_remove(mailbox_data->irq_domain);
+
+       return 0;
+}
+
+static struct platform_driver samsung_mailbox_driver = {
+       .probe  = samsung_mailbox_probe,
+       .remove = samsung_mailbox_remove,
+       .driver = {
+               .name = DEVICE_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_mailbox_of_match),
+               .pm = &samsung_mailbox_pm,
+       },
+};
+
+module_platform_driver(samsung_mailbox_driver);
+
+/* Module information */
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_DESCRIPTION("Samsung Mailbox");
+MODULE_ALIAS("platform:samsung-mailbox");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/vts/mailbox.h b/sound/soc/samsung/vts/mailbox.h
new file mode 100644 (file)
index 0000000..7606546
--- /dev/null
@@ -0,0 +1,88 @@
+/* sound/soc/samsung/vts/mailbox.h
+ *
+ * ALSA SoC - Samsung Mailbox driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+  *
+ * 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 __MAILBOX_H
+#define __MAILBOX_H
+
+/* MAILBOX */
+#ifdef CONFIG_SOC_EXYNOS8895
+#define MAILBOX_MCUCTRL                        (0x0000)
+#define MAILBOX_INTGR0                 (0x0008)
+#define MAILBOX_INTCR0                 (0x000C)
+#define MAILBOX_INTMR0                 (0x0010)
+#define MAILBOX_INTSR0                 (0x0014)
+#define MAILBOX_INTMSR0                        (0x0018)
+#define MAILBOX_INTGR1                 (0x001C)
+#define MAILBOX_INTCR1                 (0x0020)
+#define MAILBOX_INTMR1                 (0x0024)
+#define MAILBOX_INTSR1                 (0x0028)
+#define MAILBOX_INTMSR1                        (0x002C)
+#define MAILBOX_MIF_INIT               (0x004c)
+#define MAILBOX_IS_VERSION             (0x0050)
+#define MAILBOX_ISSR0                  (0x0080)
+#define MAILBOX_ISSR1                  (0x0084)
+#define MAILBOX_ISSR2                  (0x0088)
+#define MAILBOX_ISSR3                  (0x008C)
+#define MAILBOX_SEMAPHORE0             (0x0180)
+#define MAILBOX_SEMAPHORE1             (0x0184)
+#define MAILBOX_SEMAPHORE2             (0x0188)
+#define MAILBOX_SEMAPHORE3             (0x018C)
+#define MAILBOX_SEMA0CON               (0x01C0)
+#define MAILBOX_SEMA0STATE             (0x01C8)
+#define MAILBOX_SEMA1CON               (0x01E0)
+#define MAILBOX_SEMA1STATE             (0x01E8)
+#elif defined(CONFIG_SOC_EXYNOS9810)
+#define MAILBOX_MCUCTRL                        (0x0000)
+#define MAILBOX_INTGR0                 (0x001C)
+#define MAILBOX_INTCR0                 (0x0020)
+#define MAILBOX_INTMR0                 (0x0024)
+#define MAILBOX_INTSR0                 (0x0028)
+#define MAILBOX_INTMSR0                        (0x002C)
+#define MAILBOX_INTGR1                 (0x0008)
+#define MAILBOX_INTCR1                 (0x000C)
+#define MAILBOX_INTMR1                 (0x0010)
+#define MAILBOX_INTSR1                 (0x0014)
+#define MAILBOX_INTMSR1                        (0x0018)
+#define MAILBOX_MIF_INIT               (0x004C)
+#define MAILBOX_IS_VERSION             (0x0050)
+#define MAILBOX_ISSR0                  (0x0080)
+#define MAILBOX_ISSR1                  (0x0084)
+#define MAILBOX_ISSR2                  (0x0088)
+#define MAILBOX_ISSR3                  (0x008C)
+#define MAILBOX_ISSR4                  (0x0090)
+#define MAILBOX_ISSR5                  (0x0094)
+#define MAILBOX_SEMAPHORE0             (0x0180)
+#define MAILBOX_SEMAPHORE1             (0x0184)
+#define MAILBOX_SEMAPHORE2             (0x0188)
+#define MAILBOX_SEMAPHORE3             (0x018C)
+#define MAILBOX_SEMA0CON               (0x01C0)
+#define MAILBOX_SEMA0STATE             (0x01C8)
+#define MAILBOX_SEMA1CON               (0x01E0)
+#define MAILBOX_SEMA1STATE             (0x01E8)
+#endif
+
+/* MAILBOX_MCUCTRL */
+#define MAILBOX_MSWRST_OFFSET          (0)
+#define MAILBOX_MSWRST_SIZE            (1)
+/* MAILBOX_INT*R0 */
+#define MAILBOX_INT0_OFFSET            (0)
+#define MAILBOX_INT0_SIZE              (16)
+/* MAILBOX_INT*R1 */
+#define MAILBOX_INT1_OFFSET            (16)
+#define MAILBOX_INT1_SIZE              (16)
+/* MAILBOX_SEMA?CON */
+#define MAILBOX_INT_EN_OFFSET          (3)
+#define MAILBOX_INT_EN_SIZE            (1)
+/* MAILBOX_SEMA?STATE */
+#define MAILBOX_LOCK_OFFSET            (0)
+#define MAILBOX_LOCK_SIZE              (1)
+
+#endif /* __MAILBOX_H */
diff --git a/sound/soc/samsung/vts/vts.c b/sound/soc/samsung/vts/vts.c
new file mode 100644 (file)
index 0000000..ee5608f
--- /dev/null
@@ -0,0 +1,2655 @@
+/* sound/soc/samsung/vts/vts.c
+ *
+ * ALSA SoC - Samsung VTS driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+//#define DEBUG
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/wakelock.h>
+
+#include <asm-generic/delay.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include <sound/samsung/mailbox.h>
+#include <sound/samsung/vts.h>
+#include <soc/samsung/exynos-pmu.h>
+
+#include "vts.h"
+#include "vts_log.h"
+#include "vts_dump.h"
+
+#undef EMULATOR
+#ifdef EMULATOR
+static void __iomem *pmu_alive;
+#define set_mask_value(id, mask, value) do {id = ((id & ~mask) | (value & mask));} while(0);
+static void update_mask_value(volatile void __iomem *sfr,
+               unsigned int mask, unsigned int value)
+{
+       unsigned int sfr_value = readl(sfr);
+       set_mask_value(sfr_value, mask, value);
+       writel(sfr_value, sfr);
+}
+#endif
+
+
+#ifdef CONFIG_SOC_EXYNOS8895
+#define PAD_RETENTION_VTS_OPTION               (0x3148)
+#define VTS_CPU_CONFIGURATION                  (0x24E0)
+#define VTS_CPU_LOCAL_PWR_CFG                  (0x00000001)
+#define VTS_CPU_STATUS                         (0x24E4)
+#define VTS_CPU_STATUS_STATUS_MASK             (0x00000001)
+#define VTS_CPU_STANDBY                                VTS_CPU_STATUS
+#define VTS_CPU_STANDBY_STANDBYWFI_MASK                (0x10000000)
+#define VTS_CPU_OPTION                         (0x24E8)
+#define VTS_CPU_OPTION_USE_STANDBYWFI_MASK     (0x00010000)
+#define VTS_CPU_RESET_OPTION                   VTS_CPU_OPTION
+#define VTS_CPU_RESET_OPTION_ENABLE_CPU_MASK   (0x00008000)
+#elif defined(CONFIG_SOC_EXYNOS9810)
+#define PAD_RETENTION_VTS_OPTION               (0x4AD8)
+#define VTS_CPU_CONFIGURATION                  (0x4AC4)
+#define VTS_CPU_LOCAL_PWR_CFG                  (0x00000001)
+#define VTS_CPU_STATUS                         (0x4AC8)
+#define VTS_CPU_STATUS_STATUS_MASK             (0x00000001)
+#define VTS_CPU_STANDBY                                (0x3814)
+#define VTS_CPU_STANDBY_STANDBYWFI_MASK                (0x10000000)
+#define VTS_CPU_OPTION                         (0x3818)
+#define VTS_CPU_OPTION_USE_STANDBYWFI_MASK     (0x00010000)
+#define VTS_CPU_RESET_OPTION                   (0x4ACC)
+#define VTS_CPU_RESET_OPTION_ENABLE_CPU_MASK   (0x10000000)
+#endif
+
+#define LIMIT_IN_JIFFIES (msecs_to_jiffies(1000))
+#define DMIC_CLK_RATE (768000)
+#define VTS_TRIGGERED_TIMEOUT_MS (5000)
+
+/* For only external static functions */
+static struct vts_data *p_vts_data;
+
+static int vts_start_ipc_transaction_atomic(struct device *dev, struct vts_data *data, int msg, u32 (*values)[3], int sync)
+{
+       long result = 0;
+       u32 ack_value = 0;
+       volatile enum ipc_state *state = &data->ipc_state_ap;
+
+       dev_info(dev, "%s:++ msg:%d, values: 0x%08x, 0x%08x, 0x%08x\n",
+                                       __func__, msg, (*values)[0],
+                                       (*values)[1], (*values)[2]);
+
+       if (pm_runtime_suspended(dev)) {
+               dev_warn(dev, "%s: VTS IP is in suspended state, IPC cann't be processed \n", __func__);
+               return -EINVAL;
+       }
+
+       if (!data->vts_ready) {
+               dev_warn(dev, "%s: VTS Firmware Not running\n", __func__);
+               return -EINVAL;
+       }
+
+       spin_lock(&data->ipc_spinlock);
+
+       *state = SEND_MSG;
+       mailbox_write_shared_register(data->pdev_mailbox, *values, 0, 3);
+       mailbox_generate_interrupt(data->pdev_mailbox, msg);
+       data->running_ipc = msg;
+
+       if (sync) {
+               int i;
+               for (i = 1000; i && (*state != SEND_MSG_OK) &&
+                               (*state != SEND_MSG_FAIL) &&
+                               (ack_value != (0x1 << msg)); i--) {
+                       mailbox_read_shared_register(data->pdev_mailbox, &ack_value, 3, 1);
+                       dev_dbg(dev, "%s ACK-value: 0x%08x\n", __func__, ack_value);
+                       udelay(50);
+               }
+               if (!i) {
+                       dev_warn(dev, "Transaction timeout!! Ack_value:0x%x\n",
+                                       ack_value);
+               }
+               if (*state == SEND_MSG_OK || ack_value == (0x1 << msg)) {
+                       dev_dbg(dev, "Transaction success Ack_value:0x%x\n",
+                                       ack_value);
+                       if (ack_value == (0x1 << VTS_IRQ_AP_TEST_COMMAND) &&
+                               ((*values)[0] == VTS_ENABLE_DEBUGLOG ||
+                               (*values)[0] == VTS_ENABLE_AUDIODUMP ||
+                               (*values)[0] == VTS_ENABLE_LOGDUMP)) {
+                               u32 ackvalues[3] = {0, 0, 0};
+
+                               mailbox_read_shared_register(data->pdev_mailbox,
+                                       ackvalues, 0, 2);
+                               dev_info(dev, "%s: offset: 0x%x size:0x%x\n",
+                                       __func__, ackvalues[0], ackvalues[1]);
+                               if ((*values)[0] == VTS_ENABLE_DEBUGLOG) {
+                                       /* Register debug log buffer */
+                                       vts_register_log_buffer(dev,
+                                                       ackvalues[0],
+                                                       ackvalues[1]);
+                                       dev_dbg(dev, "%s: Log buffer\n",
+                                                       __func__);
+                               } else {
+                                       u32 dumpmode =
+                                       ((*values)[0] == VTS_ENABLE_LOGDUMP ?
+                                       VTS_LOG_DUMP : VTS_AUDIO_DUMP);
+                                       /* register dump offset & size */
+                                       vts_dump_addr_register(dev,
+                                                       ackvalues[0],
+                                                       ackvalues[1],
+                                                       dumpmode);
+                                       dev_dbg(dev, "%s: Dump buffer\n",
+                                                       __func__);
+                               }
+
+                       }
+               } else {
+                       dev_err(dev, "Transaction failed\n");
+               }
+               result = (*state == SEND_MSG_OK || ack_value) ? 0 : -EIO;
+       }
+
+       /* Clear running IPC & ACK value */
+       ack_value = 0x0;
+       mailbox_write_shared_register(data->pdev_mailbox, &ack_value, 3, 1);
+       data->running_ipc = 0;
+       *state = IDLE;
+
+       spin_unlock(&data->ipc_spinlock);
+       dev_info(dev, "%s:-- msg:%d \n", __func__, msg);
+
+       return (int)result;
+}
+
+int vts_start_ipc_transaction(struct device *dev, struct vts_data *data,
+               int msg, u32 (*values)[3], int atomic, int sync)
+{
+       return vts_start_ipc_transaction_atomic(dev, data, msg, values, sync);
+}
+
+static int vts_ipc_ack(struct vts_data *data, u32 result)
+{
+       if (!data->vts_ready)
+               return 0;
+
+       pr_debug("%s(%p, %u)\n", __func__, data, result);
+       mailbox_write_shared_register(data->pdev_mailbox, &result, 0, 1);
+       mailbox_generate_interrupt(data->pdev_mailbox, VTS_IRQ_AP_IPC_RECEIVED);
+       return 0;
+}
+
+int vts_send_ipc_ack(struct vts_data *data, u32 result)
+{
+       return vts_ipc_ack(data, result);
+}
+
+static void vts_cpu_power(bool on)
+{
+       pr_info("%s(%d)\n", __func__, on);
+
+#ifndef EMULATOR
+       exynos_pmu_update(VTS_CPU_CONFIGURATION, VTS_CPU_LOCAL_PWR_CFG,
+                       on ? VTS_CPU_LOCAL_PWR_CFG : 0);
+#else
+       update_mask_value(pmu_alive + VTS_CPU_CONFIGURATION, VTS_CPU_LOCAL_PWR_CFG,
+                       on ? VTS_CPU_LOCAL_PWR_CFG : 0);
+#endif
+
+       if (!on) {
+#ifndef EMULATOR
+               exynos_pmu_update(VTS_CPU_OPTION,
+                               VTS_CPU_OPTION_USE_STANDBYWFI_MASK,
+                               VTS_CPU_OPTION_USE_STANDBYWFI_MASK);
+#else
+               update_mask_value(pmu_alive + VTS_CPU_OPTION,
+                               VTS_CPU_OPTION_USE_STANDBYWFI_MASK,
+                               VTS_CPU_OPTION_USE_STANDBYWFI_MASK);
+#endif
+       }
+}
+
+static int vts_cpu_enable(bool enable)
+{
+       unsigned int mask = VTS_CPU_RESET_OPTION_ENABLE_CPU_MASK;
+       unsigned int val = (enable ? mask : 0);
+       unsigned int status = 0;
+       unsigned long after;
+
+       pr_info("%s(%d)\n", __func__, enable);
+
+#ifndef EMULATOR
+       exynos_pmu_update(VTS_CPU_RESET_OPTION, mask, val);
+#else
+       update_mask_value(pmu_alive + VTS_CPU_RESET_OPTION, mask, val);
+#endif
+       if (enable) {
+               after = jiffies + LIMIT_IN_JIFFIES;
+               do {
+                       schedule();
+#ifndef EMULATOR
+                       exynos_pmu_read(VTS_CPU_STATUS, &status);
+#else
+                       status = readl(pmu_alive + VTS_CPU_STATUS);
+#endif
+               } while (((status & VTS_CPU_STATUS_STATUS_MASK)
+                               != VTS_CPU_STATUS_STATUS_MASK)
+                               && time_is_after_eq_jiffies(after));
+               if (time_is_before_jiffies(after)) {
+                       pr_err("vts cpu enable timeout\n");
+                       return -ETIME;
+               }
+       }
+
+       return 0;
+}
+
+static void vts_reset_cpu(void)
+{
+#ifndef EMULATOR
+       vts_cpu_enable(false);
+       vts_cpu_power(false);
+       vts_cpu_power(true);
+       vts_cpu_enable(true);
+#endif
+}
+
+static int vts_download_firmware(struct platform_device *pdev)
+{
+       struct vts_data *data = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+       dev_info(dev, "%s\n", __func__);
+
+       if (!data->firmware) {
+               dev_err(dev, "firmware is not loaded\n");
+               return -EAGAIN;
+       }
+
+       memcpy(data->sram_base, data->firmware->data, data->firmware->size);
+       dev_info(dev, "firmware is downloaded to %p (size=%zu)\n", data->sram_base, data->firmware->size);
+
+       return 0;
+}
+
+static int vts_wait_for_fw_ready(struct device *dev)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+       int result;
+
+       result = wait_event_timeout(data->ipc_wait_queue,
+                       data->vts_ready, msecs_to_jiffies(3000));
+       if (data->vts_ready) {
+               result = 0;
+       } else {
+               dev_err(dev, "VTS Firmware is not ready\n");
+               result = -ETIME;
+       }
+
+       return result;
+}
+
+static void vts_pad_retention(bool retention)
+{
+       if (!retention) {
+               exynos_pmu_update(PAD_RETENTION_VTS_OPTION, 0x10000000, 0x10000000);
+       }
+}
+
+static void vts_cfg_gpio(struct device *dev, const char *name)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+       struct pinctrl_state *pin_state;
+       int ret;
+
+       dev_info(dev, "%s(%s)\n", __func__, name);
+
+       pin_state = pinctrl_lookup_state(data->pinctrl, name);
+       if (IS_ERR(pin_state)) {
+               dev_err(dev, "Couldn't find pinctrl %s\n", name);
+       } else {
+               ret = pinctrl_select_state(data->pinctrl, pin_state);
+               if (ret < 0)
+                       dev_err(dev, "Unable to configure pinctrl %s\n", name);
+       }
+}
+
+#ifdef CONFIG_SOC_EXYNOS9810
+static u32 vts_set_baaw(void __iomem *sfr_base, u64 base, u32 size)
+{
+       u32 aligned_size = round_up(size, SZ_4M);
+       u64 aligned_base = round_down(base, aligned_size);
+
+       writel(VTS_BAAW_BASE / SZ_4K, sfr_base + VTS_BAAW_SRC_START_ADDRESS);
+       writel((VTS_BAAW_BASE + aligned_size) / SZ_4K, sfr_base + VTS_BAAW_SRC_END_ADDRESS);
+       writel(aligned_base / SZ_4K, sfr_base + VTS_BAAW_REMAPPED_ADDRESS);
+       writel(0x80000003, sfr_base + VTS_BAAW_INIT_DONE);
+
+       return base - aligned_base + VTS_BAAW_BASE;
+}
+#endif
+
+static int vts_clk_set_rate(struct device *dev, unsigned long combination)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+       unsigned long dmic_rate, dmic_sync, dmic_if;
+       int result;
+
+       dev_info(dev, "%s(%lu)\n", __func__, combination);
+
+       switch (combination) {
+       case 2:
+               dmic_rate = 384000;
+               dmic_sync = 384000;
+               dmic_if = 768000;
+               break;
+       case 0:
+               dmic_rate = 512000;
+               dmic_sync = 512000;
+               dmic_if = 1024000;
+               break;
+       case 1:
+               dmic_rate = 768000;
+               dmic_sync = 768000;
+               dmic_if = 1536000;
+               break;
+       case 3:
+               dmic_rate = 4096000;
+               dmic_sync = 2048000;
+               dmic_if = 4096000;
+               break;
+       default:
+               result = -EINVAL;
+               goto out;
+       }
+
+
+       result = clk_set_rate(data->clk_dmic_if, dmic_if);
+       if (result < 0) {
+               dev_err(dev, "Failed to set rate of the clock %s\n", "dmic_if");
+               goto out;
+       }
+       dev_info(dev, "DMIC IF clock rate: %lu\n", clk_get_rate(data->clk_dmic_if));
+
+       result = clk_set_rate(data->clk_dmic_sync, dmic_sync);
+       if (result < 0) {
+               dev_err(dev, "Failed to set rate of the clock %s\n", "dmic_sync");
+               goto out;
+       }
+       dev_info(dev, "DMIC SYNC clock rate: %lu\n", clk_get_rate(data->clk_dmic_sync));
+
+       result = clk_set_rate(data->clk_dmic, dmic_rate);
+       if (result < 0) {
+               dev_err(dev, "Failed to set rate of the clock %s\n", "dmic");
+               goto out;
+       }
+       dev_info(dev, "DMIC clock rate: %lu\n", clk_get_rate(data->clk_dmic));
+
+out:
+       return result;
+}
+
+int vts_acquire_sram(struct platform_device *pdev, int vts)
+{
+#ifdef CONFIG_SOC_EXYNOS8895
+       struct vts_data *data = platform_get_drvdata(pdev);
+       int previous;
+
+       if (IS_ENABLED(CONFIG_SOC_EXYNOS8895)) {
+               dev_info(&pdev->dev, "%s(%d)\n", __func__, vts);
+
+               if (!vts) {
+                       while(pm_runtime_active(&pdev->dev)) {
+                               dev_warn(&pdev->dev, "%s Clear existing active states\n", __func__);
+                               pm_runtime_put_sync(&pdev->dev);
+                       }
+               }
+               previous = test_and_set_bit(0, &data->sram_acquired);
+               if (previous) {
+                       dev_err(&pdev->dev, "vts sram acquisition failed\n");
+                       return -EBUSY;
+               }
+
+               if (!vts) {
+                       pm_runtime_get_sync(&pdev->dev);
+                       data->voicecall_enabled = true;
+                       data->vts_state = VTS_STATE_VOICECALL;
+               }
+
+               writel((vts ? 0 : 1) << VTS_MEM_SEL_OFFSET, data->sfr_base + VTS_SHARED_MEM_CTRL);
+       }
+#endif
+
+       return 0;
+}
+EXPORT_SYMBOL(vts_acquire_sram);
+
+int vts_release_sram(struct platform_device *pdev, int vts)
+{
+#ifdef CONFIG_SOC_EXYNOS8895
+       struct vts_data *data = platform_get_drvdata(pdev);
+
+       dev_info(&pdev->dev, "%s(%d)\n", __func__, vts);
+
+       if (IS_ENABLED(CONFIG_SOC_EXYNOS8895)) {
+               if (test_bit(0, &data->sram_acquired) &&
+                               (data->voicecall_enabled || vts)) {
+                       writel(0 << VTS_MEM_SEL_OFFSET,
+                                       data->sfr_base + VTS_SHARED_MEM_CTRL);
+                       clear_bit(0, &data->sram_acquired);
+
+                       if (!vts) {
+                               pm_runtime_put_sync(&pdev->dev);
+                               data->voicecall_enabled = false;
+                       }
+                       dev_info(&pdev->dev, "%s(%d) completed\n",
+                                       __func__, vts);
+               } else
+                       dev_warn(&pdev->dev, "%s(%d) already released\n",
+                                       __func__, vts);
+       }
+
+#endif
+       return 0;
+}
+EXPORT_SYMBOL(vts_release_sram);
+
+int vts_clear_sram(struct platform_device *pdev)
+{
+       struct vts_data *data = platform_get_drvdata(pdev);
+
+       pr_info("%s\n", __func__);
+
+       memset(data->sram_base, 0, data->sram_size);
+
+       return 0;
+}
+EXPORT_SYMBOL(vts_clear_sram);
+
+volatile bool vts_is_on(void)
+{
+       return p_vts_data && p_vts_data->enabled;
+}
+EXPORT_SYMBOL(vts_is_on);
+
+volatile bool vts_is_recognitionrunning(void)
+{
+       return p_vts_data && p_vts_data->running;
+}
+EXPORT_SYMBOL(vts_is_recognitionrunning);
+
+static struct snd_soc_dai_driver vts_dai[] = {
+       {
+               .name = "vts-tri",
+               .capture = {
+                       .stream_name = "VTS Trigger Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_16000,
+                       .formats = SNDRV_PCM_FMTBIT_S16,
+                       .sig_bits = 16,
+                },
+       },
+       {
+               .name = "vts-rec",
+               .capture = {
+                       .stream_name = "VTS Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_16000,
+                       .formats = SNDRV_PCM_FMTBIT_S16,
+                       .sig_bits = 16,
+                },
+       },
+};
+
+static const char *vts_hpf_sel_texts[] = {"120Hz", "40Hz"};
+static SOC_ENUM_SINGLE_DECL(vts_hpf_sel, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_HPF_SEL_OFFSET, vts_hpf_sel_texts);
+
+static const char *vts_cps_sel_texts[] = {"normal", "absolute"};
+static SOC_ENUM_SINGLE_DECL(vts_cps_sel, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_CPS_SEL_OFFSET, vts_cps_sel_texts);
+
+static const DECLARE_TLV_DB_SCALE(vts_gain_tlv_array, 0, 6, 0);
+
+static const char *vts_sys_sel_texts[] = {"512kHz", "768kHz", "384kHz", "2048kHz"};
+static SOC_ENUM_SINGLE_DECL(vts_sys_sel, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_SYS_SEL_OFFSET, vts_sys_sel_texts);
+
+static int vts_sys_sel_put_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = component->dev;
+       unsigned int *item = ucontrol->value.enumerated.item;
+       struct vts_data *data = p_vts_data;
+
+       dev_dbg(dev, "%s(%u)\n", __func__, item[0]);
+
+       data->syssel_rate = item[0];
+
+       return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static const char *vts_polarity_clk_texts[] = {"rising edge of clock", "falling edge of clock"};
+static SOC_ENUM_SINGLE_DECL(vts_polarity_clk, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_POLARITY_CLK_OFFSET, vts_polarity_clk_texts);
+
+static const char *vts_polarity_output_texts[] = {"right first", "left first"};
+static SOC_ENUM_SINGLE_DECL(vts_polarity_output, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_POLARITY_OUTPUT_OFFSET, vts_polarity_output_texts);
+
+static const char *vts_polarity_input_texts[] = {"left PDM on CLK high", "left PDM on CLK low"};
+static SOC_ENUM_SINGLE_DECL(vts_polarity_input, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_POLARITY_INPUT_OFFSET, vts_polarity_input_texts);
+
+static const char *vts_ovfw_ctrl_texts[] = {"limit", "reset"};
+static SOC_ENUM_SINGLE_DECL(vts_ovfw_ctrl, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_OVFW_CTRL_OFFSET, vts_ovfw_ctrl_texts);
+
+static const char *vts_cic_sel_texts[] = {"Off", "On"};
+static SOC_ENUM_SINGLE_DECL(vts_cic_sel, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_CIC_SEL_OFFSET, vts_cic_sel_texts);
+
+static const char * const vtsvcrecog_mode_text[] = {
+       "OFF", "VOICE_TRIGGER_MODE_ON", "SOUND_DETECT_MODE_ON",
+        "VT_ALWAYS_ON_MODE_ON", "GOOGLE_TRIGGER_MODE_ON",
+        "SENSORY_TRIGGER_MODE_ON", "VOICE_TRIGGER_MODE_OFF",
+        "SOUND_DETECT_MODE_OFF", "VT_ALWAYS_ON_MODE_OFF",
+        "GOOGLE_TRIGGER_MODE_OFF", "SENSORY_TRIGGER_MODE_OFF"
+};
+/* Keyphrases svoice: "Hi Galaxy", Google:"Okay Google" Sensory: "Hi Blue Genie" */
+static const char * const vtsactive_phrase_text[] = {
+       "SVOICE", "GOOGLE", "SENSORY"
+};
+static const char * const vtsforce_reset_text[] = {
+       "NONE", "RESET"
+};
+static SOC_ENUM_SINGLE_EXT_DECL(vtsvcrecog_mode_enum, vtsvcrecog_mode_text);
+static SOC_ENUM_SINGLE_EXT_DECL(vtsactive_phrase_enum, vtsactive_phrase_text);
+static SOC_ENUM_SINGLE_EXT_DECL(vtsforce_reset_enum, vtsforce_reset_text);
+
+static int vts_start_recognization(struct device *dev, int start)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+       int active_trigger = data->active_trigger;
+       int result;
+       u32 values[3];
+
+       dev_info(dev, "%s for %s\n", __func__, vtsactive_phrase_text[active_trigger]);
+
+       start = !!start;
+       if (start) {
+               dev_info(dev, "%s for %s G-loaded:%d s-loaded: %d\n", __func__,
+                                vtsactive_phrase_text[active_trigger],
+                                data->google_info.loaded,
+                                data->svoice_info.loaded);
+               dev_info(dev, "%s exec_mode %d active_trig :%d\n", __func__,
+                                data->exec_mode, active_trigger);
+               if (!(data->exec_mode & (0x1 << VTS_SOUND_DETECT_MODE))) {
+                       if (active_trigger == TRIGGER_SVOICE &&
+                                data->svoice_info.loaded) {
+                               /*
+                                * load svoice model.bin @ offset 0x2A800
+                                * file before starting recognition
+                                */
+                               memcpy(data->sram_base + 0x2A800, data->svoice_info.data,
+                                       data->svoice_info.actual_sz);
+                               dev_info(dev, "svoice.bin Binary uploaded size=%zu\n",
+                                               data->svoice_info.actual_sz);
+
+                       } else if (active_trigger == TRIGGER_GOOGLE &&
+                               data->google_info.loaded) {
+                               /*
+                                * load google model.bin @ offset 0x32B00
+                                * file before starting recognition
+                                */
+                               memcpy(data->sram_base + 0x32B00, data->google_info.data,
+                                       data->google_info.actual_sz);
+                               dev_info(dev, "google.bin Binary uploaded size=%zu\n",
+                                               data->google_info.actual_sz);
+                       } else {
+                               dev_err(dev, "%s Model Binary File not Loaded\n", __func__);
+                               return -EINVAL;
+                       }
+               }
+
+               result = vts_set_dmicctrl(data->pdev,
+                               ((active_trigger == TRIGGER_SVOICE) ?
+                               VTS_MICCONF_FOR_TRIGGER
+                               : VTS_MICCONF_FOR_GOOGLE), true);
+               if (result < 0) {
+                       dev_err(dev, "%s: MIC control failed\n",
+                                                __func__);
+                       return result;
+               }
+
+               if (data->exec_mode & (0x1 << VTS_SOUND_DETECT_MODE)) {
+                       int ctrl_dmicif;
+                       /* set VTS Gain for LPSD mode */
+                       ctrl_dmicif = readl(data->dmic_base +
+                                        VTS_DMIC_CONTROL_DMIC_IF);
+                       ctrl_dmicif &= ~(0x7 << VTS_DMIC_GAIN_OFFSET);
+                       writel((ctrl_dmicif |
+                                (data->lpsdgain << VTS_DMIC_GAIN_OFFSET)),
+                               data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF);
+               }
+
+               /* Send Start recognition IPC command to VTS */
+               values[0] = 1 << active_trigger;
+               values[1] = 0;
+               values[2] = 0;
+               result = vts_start_ipc_transaction(dev, data,
+                               VTS_IRQ_AP_START_RECOGNITION,
+                               &values, 0, 1);
+               if (result < 0) {
+                       dev_err(dev, "vts ipc VTS_IRQ_AP_START_RECOGNITION failed: %d\n", result);
+                       return result;
+               }
+
+               data->vts_state = VTS_STATE_RECOG_STARTED;
+               dev_info(dev, "%s start=%d, active_trigger=%d\n", __func__, start, active_trigger);
+
+       } else if (!start) {
+               values[0] = 1 << active_trigger;
+               values[1] = 0;
+               values[2] = 0;
+               result = vts_start_ipc_transaction(dev, data,
+                               VTS_IRQ_AP_STOP_RECOGNITION,
+                               &values, 0, 1);
+               if (result < 0) {
+                       dev_err(dev, "vts ipc VTS_IRQ_AP_STOP_RECOGNITION failed: %d\n", result);
+                       return result;
+               }
+
+               data->vts_state = VTS_STATE_RECOG_STOPPED;
+               dev_info(dev, "%s start=%d, active_trigger=%d\n", __func__, start, active_trigger);
+
+               result = vts_set_dmicctrl(data->pdev,
+                               ((active_trigger == TRIGGER_SVOICE) ?
+                               VTS_MICCONF_FOR_TRIGGER
+                               : VTS_MICCONF_FOR_GOOGLE), false);
+               if (result < 0) {
+                       dev_err(dev, "%s: MIC control failed\n",
+                                                __func__);
+                       return result;
+               }
+       }
+
+
+       return 0;
+}
+
+static int get_vtsvoicerecognize_mode(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct vts_data *data = p_vts_data;
+
+       ucontrol->value.integer.value[0] = data->exec_mode;
+
+       dev_dbg(component->dev, "GET VTS Execution mode: %d\n",
+                        data->exec_mode);
+
+       return 0;
+}
+
+static int set_vtsvoicerecognize_mode(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct vts_data *data = p_vts_data;
+       struct device *dev = component->dev;
+       u32 values[3];
+       int result = 0;
+       int vcrecognize_mode = 0;
+       int vcrecognize_start = 0;
+
+       u32 keyword_type = 1;
+       char env[100] = {0,};
+       char *envp[2] = {env, NULL};
+       int loopcnt = 10;
+
+       pm_runtime_barrier(component->dev);
+
+       while (data->voicecall_enabled) {
+               dev_warn(dev, "%s voicecall (%d)\n", __func__, data->voicecall_enabled);
+
+               if (loopcnt <= 0) {
+                       dev_warn(dev, "%s VTS SRAM is Used for CP call\n", __func__);
+
+                       keyword_type = -EBUSY;
+                       snprintf(env, sizeof(env),
+                                "VOICE_WAKEUP_WORD_ID=%x",
+                                keyword_type);
+
+                       kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+                       return -EBUSY;
+               }
+
+               loopcnt--;
+               usleep_range(10000, 10000);
+       }
+
+       dev_warn(dev, "%s voicecall (%d) (End)\n", __func__, data->voicecall_enabled);
+
+       vcrecognize_mode = ucontrol->value.integer.value[0];
+
+       if (vcrecognize_mode < VTS_VOICE_TRIGGER_MODE ||
+               vcrecognize_mode >= VTS_MODE_COUNT) {
+               dev_err(dev,
+               "Invalid voice control mode =%d", vcrecognize_mode);
+               return 0;
+       } else {
+               dev_info(dev, "%s Current: %d requested %s\n",
+                                __func__, data->exec_mode,
+                                vtsvcrecog_mode_text[vcrecognize_mode]);
+               if ((vcrecognize_mode < VTS_VOICE_TRIGGER_MODE_OFF &&
+                       data->exec_mode & (0x1 << vcrecognize_mode)) ||
+                       (vcrecognize_mode >= VTS_VOICE_TRIGGER_MODE_OFF &&
+                       !(data->exec_mode & (0x1 << (vcrecognize_mode -
+                               VTS_SENSORY_TRIGGER_MODE))))) {
+                       dev_err(dev,
+                       "Requested Recognition mode=%d already completed",
+                        vcrecognize_mode);
+                       return 0;
+               }
+
+               if (vcrecognize_mode <= VTS_SENSORY_TRIGGER_MODE) {
+                       pm_runtime_get_sync(dev);
+                       vts_clk_set_rate(dev, data->syssel_rate);
+                       vcrecognize_start = true;
+               } else
+                       vcrecognize_start = false;
+
+               if (!pm_runtime_active(dev)) {
+                       dev_warn(dev, "%s wrong state %d req: %d\n",
+                                       __func__, data->exec_mode,
+                                       vcrecognize_mode);
+                       return 0;
+               }
+
+               values[0] = vcrecognize_mode;
+               values[1] = 0;
+               values[2] = 0;
+               result = vts_start_ipc_transaction(dev,
+                                data, VTS_IRQ_AP_SET_MODE,
+                                &values, 0, 1);
+               if (result < 0) {
+                       dev_err(dev, "%s IPC transaction Failed\n",
+                               vtsvcrecog_mode_text[vcrecognize_mode]);
+                       goto err_ipcmode;
+               }
+
+               if (vcrecognize_start)
+                       data->exec_mode |= (0x1 << vcrecognize_mode);
+               else
+                       data->exec_mode &= ~(0x1 << (vcrecognize_mode -
+                                               VTS_SENSORY_TRIGGER_MODE));
+
+               /* Start/stop the request Voice recognization mode */
+               result = vts_start_recognization(dev,
+                                               vcrecognize_start);
+               if (result < 0) {
+                       dev_err(dev, "Start Recognization Failed: %d\n",
+                                result);
+                       goto err_ipcmode;
+               }
+
+               if (vcrecognize_start)
+                       data->voicerecog_start |= (0x1 << data->active_trigger);
+               else
+                       data->voicerecog_start &= ~(0x1 << data->active_trigger);
+
+               dev_info(dev, "%s Configured: [%d] %s started\n",
+                        __func__, data->exec_mode,
+                        vtsvcrecog_mode_text[vcrecognize_mode]);
+
+               if (!vcrecognize_start &&
+                               pm_runtime_active(dev)) {
+                       pm_runtime_put_sync(dev);
+               }
+       }
+
+       return  0;
+
+err_ipcmode:
+       if (pm_runtime_active(dev) && (vcrecognize_start ||
+               data->exec_mode & (0x1 << vcrecognize_mode) ||
+               data->voicerecog_start & (0x1 << data->active_trigger))) {
+               pm_runtime_put_sync(dev);
+       }
+
+       if (!vcrecognize_start) {
+               data->exec_mode &= ~(0x1 << (vcrecognize_mode -
+                                       VTS_SENSORY_TRIGGER_MODE));
+               data->voicerecog_start &= ~(0x1 << data->active_trigger);
+       }
+
+       return result;
+}
+
+#ifdef CONFIG_SOC_EXYNOS8895
+static int set_vtsexec_mode(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct vts_data *data = p_vts_data;
+       u32 values[3];
+       int result = 0;
+       int vtsexecution_mode;
+
+       u32 keyword_type = 1;
+       char env[100] = {0,};
+       char *envp[2] = {env, NULL};
+       struct device *dev = &data->pdev->dev;
+       int loopcnt = 10;
+
+       pm_runtime_barrier(component->dev);
+
+       while (data->voicecall_enabled) {
+               dev_warn(component->dev, "%s voicecall (%d)\n", __func__, data->voicecall_enabled);
+
+               if (loopcnt <= 0) {
+                       dev_warn(component->dev, "%s VTS SRAM is Used for CP call\n", __func__);
+
+                       keyword_type = -EBUSY;
+                       snprintf(env, sizeof(env),
+                                "VOICE_WAKEUP_WORD_ID=%x",
+                                keyword_type);
+
+                       kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+                       return -EBUSY;
+               }
+
+               loopcnt--;
+               usleep_range(10000, 10000);
+       }
+
+       dev_warn(component->dev, "%s voicecall (%d) (End)\n", __func__, data->voicecall_enabled);
+
+       vtsexecution_mode = ucontrol->value.integer.value[0];
+
+       if (vtsexecution_mode >= VTS_MODE_COUNT) {
+               dev_err(component->dev,
+               "Invalid voice control mode =%d", vtsexecution_mode);
+               return 0;
+       } else {
+               dev_info(component->dev, "%s Current: %d requested %s\n",
+                                __func__, data->exec_mode,
+                                vtsexec_mode_text[vtsexecution_mode]);
+               if (data->exec_mode == VTS_OFF_MODE &&
+                        vtsexecution_mode != VTS_OFF_MODE) {
+                       pm_runtime_get_sync(component->dev);
+                       vts_clk_set_rate(component->dev, data->syssel_rate);
+               }
+
+               if (pm_runtime_active(component->dev)) {
+                       values[0] = vtsexecution_mode;
+                       values[1] = 0;
+                       values[2] = 0;
+                       result = vts_start_ipc_transaction(component->dev,
+                                        data, VTS_IRQ_AP_SET_MODE,
+                                        &values, 0, 1);
+                       if (result < 0) {
+                               dev_err(component->dev, "%s SET_MODE IPC transaction Failed\n",
+                                               vtsexec_mode_text[vtsexecution_mode]);
+                               if (data->exec_mode == VTS_OFF_MODE &&
+                                        vtsexecution_mode != VTS_OFF_MODE)
+                                       pm_runtime_put_sync(component->dev);
+                               return result;
+                       }
+               }
+               if (vtsexecution_mode <= VTS_SENSORY_TRIGGER_MODE)
+                       data->exec_mode |= (0x1 << vtsexecution_mode);
+               else
+                       data->exec_mode &= ~(0x1 << (vtsexecution_mode -
+                                               VTS_SENSORY_TRIGGER_MODE));
+               dev_info(component->dev, "%s Configured: [%d] %s\n",
+                        __func__, data->exec_mode,
+                        vtsexec_mode_text[vtsexecution_mode]);
+
+               if (data->exec_mode == VTS_OFF_MODE &&
+                        pm_runtime_active(component->dev)) {
+                       pm_runtime_put_sync(component->dev);
+               }
+       }
+
+       return  0;
+}
+#endif
+
+static int get_vtsactive_phrase(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct vts_data *data = p_vts_data;
+
+       ucontrol->value.integer.value[0] = data->active_trigger;
+
+       dev_dbg(component->dev, "GET VTS Active Phrase: %s \n",
+                       vtsactive_phrase_text[data->active_trigger]);
+
+       return 0;
+}
+
+static int set_vtsactive_phrase(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct vts_data *data = p_vts_data;
+       int vtsactive_phrase;
+
+       vtsactive_phrase = ucontrol->value.integer.value[0];
+
+       if (vtsactive_phrase > 2) {
+               dev_err(component->dev,
+               "Invalid VTS Trigger Key phrase =%d", vtsactive_phrase);
+               return 0;
+       } else {
+               data->active_trigger = vtsactive_phrase;
+               dev_info(component->dev, "VTS Active phrase: %s \n",
+                       vtsactive_phrase_text[vtsactive_phrase]);
+       }
+
+       return  0;
+}
+
+static int get_voicetrigger_value(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct vts_data *data = p_vts_data;
+
+       ucontrol->value.integer.value[0] = data->target_size;
+
+       dev_info(component->dev, "GET Voice Trigger Value: %d \n",
+                       data->target_size);
+
+       return 0;
+}
+
+static int set_voicetrigger_value(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct vts_data *data = p_vts_data;
+       int active_trigger = data->active_trigger;
+       u32 values[3];
+       int result = 0;
+       int trig_ms;
+
+       pm_runtime_barrier(component->dev);
+
+       if (data->voicecall_enabled) {
+               u32 keyword_type = 1;
+               char env[100] = {0,};
+               char *envp[2] = {env, NULL};
+               struct device *dev = &data->pdev->dev;
+
+               dev_warn(component->dev, "%s VTS SRAM is Used for CP call\n", __func__);
+               keyword_type = -EBUSY;
+               snprintf(env, sizeof(env),
+                        "VOICE_WAKEUP_WORD_ID=%x",
+                        keyword_type);
+
+               kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+               return -EBUSY;
+       }
+
+       trig_ms = ucontrol->value.integer.value[0];
+
+       if (trig_ms > 2000 || trig_ms < 0) {
+               dev_err(component->dev,
+               "Invalid Voice Trigger Value = %d (valid range 0~2000ms)", trig_ms);
+               return 0;
+       } else {
+               /* Configure VTS target size */
+               values[0] = trig_ms * 32; /* 1ms requires (16KHz,16bit,Mono) = 16samples * 2 bytes = 32 bytes*/
+               values[1] = 1 << active_trigger;
+               values[2] = 0;
+               result = vts_start_ipc_transaction(component->dev, data, VTS_IRQ_AP_TARGET_SIZE, &values, 0, 1);
+               if (result < 0) {
+                       dev_err(component->dev, "Voice Trigger Value setting IPC Transaction Failed: %d\n", result);
+                       return result;
+               }
+
+               data->target_size = trig_ms;
+               dev_info(component->dev, "SET Voice Trigger Value: %dms\n",
+                       data->target_size);
+       }
+
+       return  0;
+}
+
+static int get_vtsforce_reset(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct vts_data *data = p_vts_data;
+
+       ucontrol->value.integer.value[0] = data->running;
+
+       dev_dbg(component->dev, "GET VTS Force Reset: %s\n",
+                       (data->running ? "VTS Running" :
+                       "VTS Not Running"));
+
+       return 0;
+}
+
+static int set_vtsforce_reset(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+       struct device *dev = component->dev;
+       struct vts_data *data = p_vts_data;
+
+       dev_dbg(dev, "VTS RESET: %s\n", __func__);
+
+       while (data->running && pm_runtime_active(dev)) {
+               dev_warn(dev, "%s Clear active models\n", __func__);
+               pm_runtime_put_sync(dev);
+       }
+
+       return  0;
+}
+
+static const struct snd_kcontrol_new vts_controls[] = {
+       SOC_SINGLE("PERIOD DATA2REQ", VTS_DMIC_ENABLE_DMIC_IF, VTS_DMIC_PERIOD_DATA2REQ_OFFSET, 3, 0),
+       SOC_SINGLE("HPF EN", VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_HPF_EN_OFFSET, 1, 0),
+       SOC_ENUM("HPF SEL", vts_hpf_sel),
+       SOC_ENUM("CPS SEL", vts_cps_sel),
+       SOC_SINGLE_TLV("GAIN", VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_GAIN_OFFSET, 4, 0, vts_gain_tlv_array),
+       SOC_ENUM_EXT("SYS SEL", vts_sys_sel, snd_soc_get_enum_double, vts_sys_sel_put_enum),
+       SOC_ENUM("POLARITY CLK", vts_polarity_clk),
+       SOC_ENUM("POLARITY OUTPUT", vts_polarity_output),
+       SOC_ENUM("POLARITY INPUT", vts_polarity_input),
+       SOC_ENUM("OVFW CTRL", vts_ovfw_ctrl),
+       SOC_ENUM("CIC SEL", vts_cic_sel),
+       SOC_ENUM_EXT("VoiceRecognization Mode", vtsvcrecog_mode_enum,
+               get_vtsvoicerecognize_mode, set_vtsvoicerecognize_mode),
+       SOC_ENUM_EXT("Active Keyphrase", vtsactive_phrase_enum,
+               get_vtsactive_phrase, set_vtsactive_phrase),
+       SOC_SINGLE_EXT("VoiceTrigger Value",
+               SND_SOC_NOPM,
+               0, 2000, 0,
+               get_voicetrigger_value, set_voicetrigger_value),
+       SOC_ENUM_EXT("Force Reset", vtsforce_reset_enum,
+               get_vtsforce_reset, set_vtsforce_reset),
+};
+
+static const char *dmic_sel_texts[] = {"DPDM", "APDM"};
+static SOC_ENUM_SINGLE_DECL(dmic_sel_enum, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_DMIC_SEL_OFFSET, dmic_sel_texts);
+static const struct snd_kcontrol_new dmic_sel_controls[] = {
+       SOC_DAPM_ENUM("MUX", dmic_sel_enum),
+};
+
+static const struct snd_kcontrol_new dmic_if_controls[] = {
+       SOC_DAPM_SINGLE("RCH EN", VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_RCH_EN_OFFSET, 1, 0),
+       SOC_DAPM_SINGLE("LCH EN", VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_LCH_EN_OFFSET, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget vts_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("PAD APDM"),
+       SND_SOC_DAPM_INPUT("PAD DPDM"),
+       SND_SOC_DAPM_MUX("DMIC SEL", SND_SOC_NOPM, 0, 0, dmic_sel_controls),
+       SOC_MIXER_ARRAY("DMIC IF", SND_SOC_NOPM, 0, 0, dmic_if_controls),
+};
+
+static const struct snd_soc_dapm_route vts_dapm_routes[] = {
+       // sink, control, source
+       {"DMIC SEL", "APDM", "PAD APDM"},
+       {"DMIC SEL", "DPDM", "PAD DPDM"},
+       {"DMIC IF", "RCH EN", "DMIC SEL"},
+       {"DMIC IF", "LCH EN", "DMIC SEL"},
+       {"VTS Capture", NULL, "DMIC IF"},
+};
+
+
+static int vts_component_probe(struct snd_soc_component *component)
+{
+       struct device *dev = component->dev;
+       struct vts_data *data = dev_get_drvdata(dev);
+
+       dev_info(dev, "%s\n", __func__);
+
+       data->cmpnt = component;
+       vts_clk_set_rate(component->dev, 0);
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver vts_component = {
+       .probe = vts_component_probe,
+       .controls = vts_controls,
+       .num_controls = ARRAY_SIZE(vts_controls),
+       .dapm_widgets = vts_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(vts_dapm_widgets),
+       .dapm_routes = vts_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(vts_dapm_routes),
+};
+
+int vts_set_dmicctrl(struct platform_device *pdev, int micconf_type, bool enable)
+{
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+       int dmic_clkctrl = 0;
+       int ctrl_dmicif = 0;
+       int select_dmicclk = 0;
+
+       dev_dbg(dev, "%s-- flag: %d mictype: %d micusagecnt: %d\n",
+                __func__, enable, micconf_type, data->micclk_init_cnt);
+       if (!data->vts_ready) {
+               dev_warn(dev, "%s: VTS Firmware Not running\n", __func__);
+               return -EINVAL;
+       }
+
+       if (enable) {
+               if (!data->micclk_init_cnt) {
+                       ctrl_dmicif = readl(data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF);
+
+                       if (ctrl_dmicif & (0x1 << VTS_DMIC_DMIC_SEL_OFFSET)) {
+                               vts_cfg_gpio(dev, "amic_default");
+                               select_dmicclk = ((0x1 << VTS_ENABLE_CLK_GEN_OFFSET) |
+                                       (0x1 << VTS_SEL_EXT_DMIC_CLK_OFFSET) |
+                                       (0x1 << VTS_ENABLE_CLK_CLK_GEN_OFFSET));
+                               writel(select_dmicclk, data->sfr_base + VTS_DMIC_CLK_CON);
+                               /* Set AMIC VTS Gain */
+                               writel((ctrl_dmicif |
+                                        (data->amicgain << VTS_DMIC_GAIN_OFFSET)),
+                                        data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF);
+
+                       } else {
+                               vts_cfg_gpio(dev, "dmic_default");
+                               select_dmicclk = ((0x0 << VTS_ENABLE_CLK_GEN_OFFSET) |
+                                       (0x0 << VTS_SEL_EXT_DMIC_CLK_OFFSET) |
+                                       (0x0 << VTS_ENABLE_CLK_CLK_GEN_OFFSET));
+                               writel(select_dmicclk, data->sfr_base + VTS_DMIC_CLK_CON);
+                               /* Set DMIC VTS Gain */
+                               writel((ctrl_dmicif |
+                                        (data->dmicgain << VTS_DMIC_GAIN_OFFSET)),
+                                        data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF);
+                       }
+
+                       dmic_clkctrl = readl(data->sfr_base + VTS_DMIC_CLK_CTRL);
+                       writel(dmic_clkctrl | (0x1 << VTS_CLK_ENABLE_OFFSET),
+                               data->sfr_base + VTS_DMIC_CLK_CTRL);
+                       dev_info(dev, "%s Micclk setting ENABLED\n", __func__);
+               }
+
+               /* check whether Mic is already configure or not based on VTS
+                  option type for MIC configuration book keeping */
+               if ((!(data->mic_ready & (0x1 << VTS_MICCONF_FOR_TRIGGER)) ||
+                       !(data->mic_ready & (0x1 << VTS_MICCONF_FOR_GOOGLE))) &&
+                       (micconf_type == VTS_MICCONF_FOR_TRIGGER ||
+                       micconf_type == VTS_MICCONF_FOR_GOOGLE)) {
+                       data->micclk_init_cnt++;
+                       data->mic_ready |= (0x1 << micconf_type);
+                       dev_info(dev, "%s Micclk ENABLED for TRIGGER ++ %d\n",
+                                __func__, data->mic_ready);
+               } else if (!(data->mic_ready & (0x1 << VTS_MICCONF_FOR_RECORD)) &&
+                       micconf_type == VTS_MICCONF_FOR_RECORD) {
+                       data->micclk_init_cnt++;
+                       data->mic_ready |= (0x1 << micconf_type);
+                       dev_info(dev, "%s Micclk ENABLED for RECORD ++ %d\n",
+                                __func__, data->mic_ready);
+               }
+       } else {
+               if (data->micclk_init_cnt)
+                       data->micclk_init_cnt--;
+               if (!data->micclk_init_cnt) {
+                       vts_cfg_gpio(dev, "idle");
+
+                       dmic_clkctrl = readl(data->sfr_base + VTS_DMIC_CLK_CTRL);
+                       writel(dmic_clkctrl & ~(0x1 << VTS_CLK_ENABLE_OFFSET),
+                               data->sfr_base + VTS_DMIC_CLK_CTRL);
+                       writel(0x0, data->sfr_base + VTS_DMIC_CLK_CON);
+                       /* reset VTS Gain to default */
+                       writel((ctrl_dmicif & (~(0x7 << VTS_DMIC_GAIN_OFFSET))),
+                                data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF);
+                       dev_info(dev, "%s Micclk setting DISABLED\n", __func__);
+               }
+
+               /* MIC configuration book keeping */
+               if (((data->mic_ready & (0x1 << VTS_MICCONF_FOR_TRIGGER)) ||
+                       (data->mic_ready & (0x1 << VTS_MICCONF_FOR_GOOGLE))) &&
+                       (micconf_type == VTS_MICCONF_FOR_TRIGGER ||
+                       micconf_type == VTS_MICCONF_FOR_GOOGLE)) {
+                       data->mic_ready &= ~(0x1 << micconf_type);
+                       dev_info(dev, "%s Micclk DISABLED for TRIGGER -- %d\n",
+                                __func__, data->mic_ready);
+               } else if ((data->mic_ready & (0x1 << VTS_MICCONF_FOR_RECORD)) &&
+                       micconf_type == VTS_MICCONF_FOR_RECORD) {
+                       data->mic_ready &= ~(0x1 << micconf_type);
+                       dev_info(dev, "%s Micclk DISABLED for RECORD -- %d\n",
+                                __func__, data->mic_ready);
+               }
+       }
+
+       return 0;
+}
+
+static irqreturn_t vts_error_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+       u32 error_code;
+
+       mailbox_read_shared_register(data->pdev_mailbox, &error_code, 3, 1);
+       vts_ipc_ack(data, 1);
+
+       dev_err(dev, "Error occurred on VTS: 0x%x\n", (int)error_code);
+       vts_reset_cpu();
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t vts_boot_completed_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+
+       data->vts_ready = 1;
+
+       vts_ipc_ack(data, 1);
+       wake_up(&data->ipc_wait_queue);
+
+       dev_info(dev, "VTS boot completed\n");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t vts_ipc_received_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+       u32 result;
+
+       mailbox_read_shared_register(data->pdev_mailbox, &result, 3, 1);
+       dev_info(dev, "VTS received IPC: 0x%x\n", result);
+
+       switch (data->ipc_state_ap) {
+       case SEND_MSG:
+               if (result  == (0x1 << data->running_ipc)) {
+                       dev_dbg(dev, "IPC transaction completed\n");
+                       data->ipc_state_ap = SEND_MSG_OK;
+               } else {
+                       dev_err(dev, "IPC transaction error\n");
+                       data->ipc_state_ap = SEND_MSG_FAIL;
+               }
+               break;
+       default:
+               dev_warn(dev, "State fault: %d Ack_value:0x%x\n",
+                               data->ipc_state_ap, result);
+               break;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t vts_voice_triggered_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+       u32 id, score, frame_count;
+       u32 keyword_type = 1;
+       char env[100] = {0,};
+       char *envp[2] = {env, NULL};
+
+       if (data->mic_ready & (0x1 << VTS_MICCONF_FOR_TRIGGER) ||
+               data->mic_ready & (0x1 << VTS_MICCONF_FOR_GOOGLE)) {
+               mailbox_read_shared_register(data->pdev_mailbox, &id,
+                                                3, 1);
+               vts_ipc_ack(data, 1);
+
+               frame_count = (u32)(id & GENMASK(15, 0));
+               score = (u32)((id & GENMASK(27, 16)) >> 16);
+               id >>= 28;
+
+               dev_info(dev, "VTS triggered: id = %u, score = %u\n",
+                               id, score);
+               dev_info(dev, "VTS triggered: frame_count = %u\n",
+                                frame_count);
+
+               if (!(data->exec_mode & (0x1 << VTS_SOUND_DETECT_MODE))) {
+                       keyword_type = id;
+                       snprintf(env, sizeof(env),
+                                        "VOICE_WAKEUP_WORD_ID=%x",
+                                        keyword_type);
+               } else if (data->exec_mode & (0x1 << VTS_SOUND_DETECT_MODE)) {
+                       snprintf(env, sizeof(env),
+                                "VOICE_WAKEUP_WORD_ID=LPSD");
+               } else {
+                       dev_warn(dev, "Unknown VTS Execution Mode!!\n");
+               }
+
+               kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+               wake_lock_timeout(&data->wake_lock,
+                               VTS_TRIGGERED_TIMEOUT_MS);
+               data->vts_state = VTS_STATE_RECOG_TRIGGERED;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t vts_trigger_period_elapsed_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+       struct vts_platform_data *platform_data = platform_get_drvdata(data->pdev_vtsdma[0]);
+       u32 pointer;
+
+       if (data->mic_ready & (0x1 << VTS_MICCONF_FOR_TRIGGER) ||
+               data->mic_ready & (0x1 << VTS_MICCONF_FOR_GOOGLE)) {
+               mailbox_read_shared_register(data->pdev_mailbox,
+                                        &pointer, 2, 1);
+               dev_dbg(dev, "%s:[%s] Base: %08x pointer:%08x\n",
+                        __func__, (platform_data->id ? "VTS-RECORD" :
+                        "VTS-TRIGGER"), data->dma_area_vts, pointer);
+
+               if (pointer)
+                       platform_data->pointer = (pointer -
+                                        data->dma_area_vts);
+               vts_ipc_ack(data, 1);
+
+
+               snd_pcm_period_elapsed(platform_data->substream);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t vts_record_period_elapsed_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+       struct vts_platform_data *platform_data = platform_get_drvdata(data->pdev_vtsdma[1]);
+       u32 pointer;
+
+       if (data->mic_ready & (0x1 << VTS_MICCONF_FOR_RECORD)) {
+               mailbox_read_shared_register(data->pdev_mailbox,
+                                                &pointer, 1, 1);
+               dev_dbg(dev, "%s:[%s] Base: %08x pointer:%08x\n",
+                        __func__,
+                        (platform_data->id ? "VTS-RECORD" : "VTS-TRIGGER"),
+                        (data->dma_area_vts + BUFFER_BYTES_MAX/2), pointer);
+
+               if (pointer)
+                       platform_data->pointer = (pointer -
+                               (data->dma_area_vts + BUFFER_BYTES_MAX/2));
+               vts_ipc_ack(data, 1);
+
+               snd_pcm_period_elapsed(platform_data->substream);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t vts_debuglog_bufzero_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+
+       dev_dbg(dev, "%s LogBuffer Index: %d\n", __func__, 0);
+
+       /* schedule log dump */
+       vts_log_schedule_flush(dev, 0);
+
+       vts_ipc_ack(data, 1);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t vts_debuglog_bufone_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+
+       dev_dbg(dev, "%s LogBuffer Index: %d\n", __func__, 1);
+
+       /* schedule log dump */
+       vts_log_schedule_flush(dev, 1);
+
+       vts_ipc_ack(data, 1);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t vts_audiodump_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+
+       dev_info(dev, "%s\n", __func__);
+
+       if (data->vts_ready && data->audiodump_enabled) {
+               u32 ackvalues[3] = {0, 0, 0};
+
+               mailbox_read_shared_register(data->pdev_mailbox,
+                       ackvalues, 0, 2);
+               dev_info(dev, "%sDump offset: 0x%x size:0x%x\n",
+                       __func__, ackvalues[0], ackvalues[1]);
+               /* register audio dump offset & size */
+               vts_dump_addr_register(dev, ackvalues[0],
+                               ackvalues[1], VTS_AUDIO_DUMP);
+               /* schedule pcm dump */
+               vts_audiodump_schedule_flush(dev);
+               /* vts_ipc_ack should be sent once dump is completed */
+       } else {
+               vts_ipc_ack(data, 1);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t vts_logdump_handler(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+
+       dev_info(dev, "%s\n", __func__);
+
+       if (data->vts_ready && data->logdump_enabled) {
+               /* schedule pcm dump */
+               vts_logdump_schedule_flush(dev);
+               /* vts_ipc_ack should be sent once dump is completed */
+       } else {
+               vts_ipc_ack(data, 1);
+       }
+
+       return IRQ_HANDLED;
+}
+
+void vts_register_dma(struct platform_device *pdev_vts,
+               struct platform_device *pdev_vts_dma, unsigned int id)
+{
+       struct vts_data *data = platform_get_drvdata(pdev_vts);
+
+       if (id < ARRAY_SIZE(data->pdev_vtsdma)) {
+               data->pdev_vtsdma[id] = pdev_vts_dma;
+               if (id > data->vtsdma_count) {
+                       data->vtsdma_count = id + 1;
+               }
+               dev_info(&data->pdev->dev, "%s: VTS-DMA id(%u)Registered \n", __func__, id);
+       } else {
+               dev_err(&data->pdev->dev, "%s: invalid id(%u)\n", __func__, id);
+       }
+}
+
+static int vts_suspend(struct device *dev)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+       u32 values[3] = {0,0,0};
+       int result = 0;
+
+       if (data->vts_ready) {
+               if (data->running &&
+                       data->vts_state == VTS_STATE_RECOG_TRIGGERED) {
+                       result = vts_start_ipc_transaction(dev, data,
+                                       VTS_IRQ_AP_RESTART_RECOGNITION,
+                                       &values, 0, 1);
+                       if (result < 0) {
+                               dev_err(dev, "%s restarted trigger failed\n",
+                                       __func__);
+                               goto error_ipc;
+                       }
+                       data->vts_state = VTS_STATE_RECOG_STARTED;
+               }
+
+               /* enable vts wakeup source interrupts */
+               enable_irq_wake(data->irq[VTS_IRQ_VTS_VOICE_TRIGGERED]);
+               enable_irq_wake(data->irq[VTS_IRQ_VTS_ERROR]);
+               if (data->audiodump_enabled)
+                       enable_irq_wake(data->irq[VTS_IRQ_VTS_AUDIO_DUMP]);
+               if (data->logdump_enabled)
+                       enable_irq_wake(data->irq[VTS_IRQ_VTS_LOG_DUMP]);
+
+               dev_info(dev, "%s: Enable VTS Wakeup source irqs\n", __func__);
+       }
+
+error_ipc:
+       return result;
+}
+
+static int vts_resume(struct device *dev)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+
+       if (data->vts_ready) {
+               /* disable vts wakeup source interrupts */
+               disable_irq_wake(data->irq[VTS_IRQ_VTS_VOICE_TRIGGERED]);
+               disable_irq_wake(data->irq[VTS_IRQ_VTS_ERROR]);
+               if (data->audiodump_enabled)
+                       disable_irq_wake(data->irq[VTS_IRQ_VTS_AUDIO_DUMP]);
+               if (data->logdump_enabled)
+                       disable_irq_wake(data->irq[VTS_IRQ_VTS_LOG_DUMP]);
+
+               dev_info(dev, "%s: Disable VTS Wakeup source irqs\n", __func__);
+       }
+       return 0;
+}
+
+static void vts_irq_enable(struct platform_device *pdev, bool enable)
+{
+       struct vts_data *data = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+       int irqidx;
+
+       dev_info(dev, "%s IRQ Enable: [%s]\n", __func__,
+                       (enable ? "TRUE" : "FALSE"));
+
+       for (irqidx = 0; irqidx < VTS_IRQ_COUNT; irqidx++) {
+               if (enable)
+                       enable_irq(data->irq[irqidx]);
+               else
+                       disable_irq(data->irq[irqidx]);
+       }
+}
+
+static void vts_save_register(struct vts_data *data)
+{
+       regcache_cache_only(data->regmap_dmic, true);
+       regcache_mark_dirty(data->regmap_dmic);
+}
+
+static void vts_restore_register(struct vts_data *data)
+{
+       regcache_cache_only(data->regmap_dmic, false);
+       regcache_sync(data->regmap_dmic);
+}
+
+void vts_dbg_print_gpr(struct device *dev, struct vts_data *data)
+{
+       int i;
+       char buf[10];
+       unsigned int version = data->vtsfw_version;
+
+       buf[0] = (((version >> 24) & 0xFF) + '0');
+       buf[1] = '.';
+       buf[2] = (((version >> 16) & 0xFF) + '0');
+       buf[3] = '.';
+       buf[4] = (((version >> 8) & 0xFF) + '0');
+       buf[5] = '.';
+       buf[6] = ((version & 0xFF) + '0');
+       buf[7] = '\0';
+
+       dev_info(dev, "========================================\n");
+       dev_info(dev, "VTS CM4 register dump (%s)\n", buf);
+       dev_info(dev, "----------------------------------------\n");
+       for (i = 0; i <= 14; i++) {
+               dev_info(dev, "CM4_R%02d        : %08x\n", i,
+                               readl(data->gpr_base + VTS_CM4_R(i)));
+       }
+       dev_info(dev, "CM4_PC         : %08x\n",
+                       readl(data->gpr_base + VTS_CM4_PC));
+       dev_info(dev, "========================================\n");
+}
+
+void vts_dbg_dump_log_gpr(
+       struct device *dev,
+       struct vts_data *data,
+       unsigned int dbg_type,
+       const char *reason)
+{
+       struct vts_dbg_dump *p_dump = NULL;
+       int i;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!vts_is_on() || !data->running) {
+               dev_info(dev, "%s is skipped due to no power\n", __func__);
+               return;
+       }
+
+       if (dbg_type == RUNTIME_SUSPEND_DUMP)
+               p_dump = (struct vts_dbg_dump *)data->dmab_log.area;
+       else
+               p_dump = (struct vts_dbg_dump *)(data->dmab_log.area +
+                                       (LOG_BUFFER_BYTES_MAX/2));
+
+       /* Get VTS firmware log msgs */
+       memcpy_fromio(p_dump->sram, data->sramlog_baseaddr, SZ_2K);
+
+       p_dump->time = sched_clock();
+       strncpy(p_dump->reason, reason, sizeof(p_dump->reason) - 1);
+       for (i = 0; i <= 14; i++)
+               p_dump->gpr[i] = readl(data->gpr_base + VTS_CM4_R(i));
+
+       p_dump->gpr[i++] = readl(data->gpr_base + VTS_CM4_PC);
+}
+
+static void exynos_vts_panic_handler(void)
+{
+       static bool has_run;
+       struct vts_data *data = p_vts_data;
+       struct device *dev = data ? (data->pdev ? &data->pdev->dev : NULL) : NULL;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (vts_is_on() && dev) {
+               if (has_run) {
+                       dev_info(dev, "already dumped\n");
+                       return;
+               }
+               has_run = true;
+
+               /* Dump VTS GPR register & Log messages */
+               vts_dbg_dump_log_gpr(dev, data, KERNEL_PANIC_DUMP,
+                                               "panic");
+       } else {
+               dev_info(dev, "%s: dump is skipped due to no power\n", __func__);
+       }
+}
+
+static int vts_panic_handler(struct notifier_block *nb,
+                              unsigned long action, void *data)
+{
+       exynos_vts_panic_handler();
+       return NOTIFY_OK;
+}
+
+static struct notifier_block vts_panic_notifier = {
+       .notifier_call  = vts_panic_handler,
+       .next           = NULL,
+       .priority       = 0     /* priority: INT_MAX >= x >= 0 */
+};
+
+static int vts_runtime_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct vts_data *data = dev_get_drvdata(dev);
+       int i = 1000;
+       unsigned int status = 0;
+       u32 values[3] = {0,0,0};
+       int result = 0;
+
+       dev_info(dev, "%s \n", __func__);
+
+       vts_save_register(data);
+
+       if (data->running) {
+               if (data->audiodump_enabled) {
+                       values[0] = VTS_DISABLE_AUDIODUMP;
+                       values[1] = 0;
+                       values[2] = 0;
+                       result = vts_start_ipc_transaction(dev, data,
+                                               VTS_IRQ_AP_TEST_COMMAND,
+                                               &values, 0, 1);
+                       if (result < 0)
+                               dev_warn(dev, "Disable_AudioDump ipc failed\n");
+                       /* reset audio dump offset & size */
+                       vts_dump_addr_register(dev, 0, 0, VTS_AUDIO_DUMP);
+               }
+
+               if (data->logdump_enabled) {
+                       values[0] = VTS_DISABLE_LOGDUMP;
+                       values[1] = 0;
+                       values[2] = 0;
+                       result = vts_start_ipc_transaction(dev, data,
+                                               VTS_IRQ_AP_TEST_COMMAND,
+                                               &values, 0, 1);
+                       if (result < 0)
+                               dev_warn(dev, "Disable_LogDump ipc failed\n");
+                       /* reset audio dump offset & size */
+                       vts_dump_addr_register(dev, 0, 0, VTS_LOG_DUMP);
+               }
+
+               if (data->vtslog_enabled) {
+                       values[0] = VTS_DISABLE_DEBUGLOG;
+                       values[1] = 0;
+                       values[2] = 0;
+                       result = vts_start_ipc_transaction(dev, data,
+                                       VTS_IRQ_AP_TEST_COMMAND,
+                                       &values, 0, 1);
+                       if (result < 0)
+                               dev_warn(dev, "Disable_debuglog ipc transaction failed\n");
+                       /* reset VTS SRAM debug log buffer */
+                       vts_register_log_buffer(dev, 0, 0);
+               }
+
+               values[0] = 0;
+               values[1] = 0;
+               values[2] = 0;
+               result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_POWER_DOWN, &values, 0, 1);
+               if (result < 0) {
+                       dev_warn(dev, "POWER_DOWN IPC transaction Failed\n");
+               }
+
+               /* Dump VTS GPR register & Log messages */
+               vts_dbg_print_gpr(dev, data);
+               vts_dbg_dump_log_gpr(dev, data, RUNTIME_SUSPEND_DUMP,
+                                               "runtime_suspend");
+
+               /* wait for VTS STANDBYWFI in STATUS SFR */
+               do {
+                       exynos_pmu_read(VTS_CPU_STANDBY, &status);
+                       dev_dbg(dev, "%s Read VTS_CPU_STANDBY for STANDBYWFI status\n", __func__);
+               } while (i-- && !(status & VTS_CPU_STANDBY_STANDBYWFI_MASK));
+
+               if (!i) {
+                       dev_warn(dev, "VTS IP entering WFI time out\n");
+               }
+
+               if (data->irq_state) {
+                       vts_irq_enable(pdev, false);
+                       data->irq_state = false;
+               }
+               clk_disable(data->clk_dmic);
+               vts_cpu_enable(false);
+               vts_cpu_power(false);
+               data->running = false;
+       }
+
+       data->enabled = false;
+       data->exec_mode = VTS_OFF_MODE;
+       data->active_trigger = TRIGGER_SVOICE;
+       data->voicerecog_start = 0;
+       data->target_size = 0;
+       /* reset micbias setting count */
+       data->micclk_init_cnt = 0;
+       data->mic_ready = 0;
+       data->vts_ready = 0;
+       data->vts_state = VTS_STATE_NONE;
+       dev_info(dev, "%s Exit \n", __func__);
+       return 0;
+}
+
+static int vts_runtime_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct vts_data *data = dev_get_drvdata(dev);
+       u32 values[3];
+       int result;
+
+       dev_info(dev, "%s \n", __func__);
+
+       data->enabled = true;
+
+       vts_restore_register(data);
+
+       vts_cfg_gpio(dev, "dmic_default");
+       vts_cfg_gpio(dev, "idle");
+       vts_pad_retention(false);
+
+       if (!data->irq_state) {
+               vts_irq_enable(pdev, true);
+               data->irq_state = true;
+       }
+
+       result = clk_enable(data->clk_dmic);
+       if (result < 0) {
+               dev_err(dev, "Failed to enable the clock\n");
+               goto error_clk;
+       }
+       dev_info(dev, "dmic clock rate:%lu\n", clk_get_rate(data->clk_dmic));
+
+       vts_cpu_power(true);
+
+       result = vts_download_firmware(pdev);
+       if (result < 0) {
+               dev_err(dev, "Failed to download firmware\n");
+               goto error_firmware;
+       }
+
+       vts_cpu_enable(true);
+
+       vts_wait_for_fw_ready(dev);
+
+       /* Configure select sys clock rate */
+       vts_clk_set_rate(dev, data->syssel_rate);
+
+#if 1
+       data->dma_area_vts= vts_set_baaw(data->baaw_base,
+                       data->dmab.addr, BUFFER_BYTES_MAX);
+#endif
+       values[0] = data->dma_area_vts;
+       values[1] = 0x140;
+       values[2] = 0x800;
+       result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_SET_DRAM_BUFFER, &values, 0, 1);
+       if (result < 0) {
+               dev_err(dev, "DRAM_BUFFER Setting IPC transaction Failed\n");
+               goto error_firmware;
+       }
+
+       values[0] = VTS_OFF_MODE;
+       values[1] = 0;
+       values[2] = 0;
+       result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_SET_MODE, &values, 0, 1);
+       if (result < 0) {
+               dev_err(dev, "SET_MODE to OFF IPC transaction Failed\n");
+               goto error_firmware;
+       }
+       data->exec_mode = VTS_OFF_MODE;
+
+       values[0] = VTS_ENABLE_SRAM_LOG;
+       values[1] = 0;
+       values[2] = 0;
+       result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 1);
+       if (result < 0) {
+               dev_err(dev, "Enable_SRAM_log ipc transaction failed\n");
+               goto error_firmware;
+       }
+
+       if (data->vtslog_enabled) {
+               values[0] = VTS_ENABLE_DEBUGLOG;
+               values[1] = 0;
+               values[2] = 0;
+               result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 1);
+               if (result < 0) {
+                       dev_err(dev, "Enable_debuglog ipc transaction failed\n");
+                       goto error_firmware;
+               }
+       }
+
+       /* Enable Audio data Dump */
+       if (data->audiodump_enabled) {
+               values[0] = VTS_ENABLE_AUDIODUMP;
+               values[1] = (VTS_ADUIODUMP_AFTER_MINS * 60);
+               values[2] = 0;
+               result = vts_start_ipc_transaction(dev, data,
+                               VTS_IRQ_AP_TEST_COMMAND, &values,
+                               0, 2);
+               if (result < 0) {
+                       dev_err(dev, "Enable_AudioDump ipc failed\n");
+                       goto error_firmware;
+               }
+       }
+
+       /* Enable VTS FW log Dump */
+       if (data->logdump_enabled) {
+               values[0] = VTS_ENABLE_LOGDUMP;
+               values[1] = (VTS_LOGDUMP_AFTER_MINS * 60);
+               values[2] = 0;
+               result = vts_start_ipc_transaction(dev, data,
+                               VTS_IRQ_AP_TEST_COMMAND, &values,
+                               0, 2);
+               if (result < 0) {
+                       dev_err(dev, "Enable_LogDump ipc failed\n");
+                       goto error_firmware;
+               }
+       }
+
+       /* Enable CM4 GPR Dump */
+       writel(0x1, data->gpr_base);
+
+       dev_dbg(dev, "%s DRAM-setting and VTS-Mode is completed \n", __func__);
+       dev_info(dev, "%s Exit \n", __func__);
+
+       data->running = true;
+       data->vts_state = VTS_STATE_IDLE;
+       return 0;
+
+error_firmware:
+       vts_cpu_power(false);
+       clk_disable(data->clk_dmic);
+error_clk:
+       if (data->irq_state) {
+               vts_irq_enable(pdev, false);
+               data->irq_state = false;
+       }
+       data->running = false;
+       return 0;
+}
+
+static const struct dev_pm_ops samsung_vts_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(vts_suspend, vts_resume)
+       SET_RUNTIME_PM_OPS(vts_runtime_suspend, vts_runtime_resume, NULL)
+};
+
+static const struct of_device_id exynos_vts_of_match[] = {
+       {
+               .compatible = "samsung,vts",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_vts_of_match);
+
+static ssize_t vts_google_model_read(struct file *file, struct kobject *kobj,
+               struct bin_attribute *bin_attr, char *buffer,
+               loff_t ppos, size_t count)
+{
+       dev_info(kobj_to_dev(kobj), "%s\n", __func__);
+
+       return 0;
+}
+
+static ssize_t vts_google_model_write(struct file *file, struct kobject *kobj,
+               struct bin_attribute *bin_attr, char *buffer, loff_t ppos, size_t count)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct vts_data *data = dev_get_drvdata(dev);
+       char *to;
+       ssize_t available;
+
+       dev_info(dev, "%s\n", __func__);
+
+       if (!data->google_info.data) {
+               dev_info(dev, "%s Google binary Buffer not allocated\n", __func__);
+               return -EINVAL;
+       }
+
+       to = data->google_info.data;
+       available = data->google_info.max_sz;
+
+       dev_dbg(dev, "%s available %zu Cur-pos %lld size-to-copy %zu\n", __func__,
+                available, ppos, count);
+       if (ppos < 0) {
+               dev_info(dev, "%s Error wrong current position\n", __func__);
+               return -EINVAL;
+       }
+       if (ppos >= available || !count) {
+               dev_info(dev, "%s Error copysize[%lld] greater than available buffer\n",
+                       __func__, ppos);
+               return 0;
+       }
+       if (count > available - ppos)
+               count = available - ppos;
+
+       memcpy(to + ppos, buffer, count);
+
+       to = (to + ppos);
+       data->google_info.actual_sz = ppos + count;
+       dev_info(dev, "%s updated Size %zu\n", __func__,
+                data->google_info.actual_sz);
+       data->google_info.loaded = true;
+
+       return count;
+}
+
+static ssize_t vts_svoice_model_read(struct file *file, struct kobject *kobj,
+               struct bin_attribute *bin_attr, char *buffer,
+               loff_t ppos, size_t count)
+{
+       dev_info(kobj_to_dev(kobj), "%s\n", __func__);
+
+       return 0;
+}
+
+static ssize_t vts_svoice_model_write(struct file *file, struct kobject *kobj,
+               struct bin_attribute *bin_attr, char *buffer, loff_t ppos, size_t count)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct vts_data *data = dev_get_drvdata(dev);
+       char *to;
+       ssize_t available;
+
+       dev_info(dev, "%s\n", __func__);
+
+       if (!data->svoice_info.data) {
+               dev_info(dev, "%s Grammar binary Buffer not allocated\n", __func__);
+               return -EINVAL;
+       }
+
+       to = data->svoice_info.data;
+       available = data->svoice_info.max_sz;
+
+       dev_dbg(dev, "%s available %zu Cur-pos %lld size-to-copy %zu\n", __func__,
+                available, ppos, count);
+       if (ppos < 0) {
+               dev_info(dev, "%s Error wrong current position\n", __func__);
+               return -EINVAL;
+       }
+       if (ppos >= available || !count) {
+               dev_info(dev, "%s Error copysize[%lld] greater than available buffer\n",
+                       __func__, ppos);
+               return 0;
+       }
+       if (count > available - ppos)
+               count = available - ppos;
+
+       memcpy(to + ppos, buffer, count);
+
+       to = (to + ppos);
+       data->svoice_info.actual_sz = ppos + count;
+       dev_info(dev, "%s updated Size %zu\n", __func__,
+                data->svoice_info.actual_sz);
+       data->svoice_info.loaded = true;
+
+       return count;
+}
+
+static const BIN_ATTR_RW(vts_google_model, VTS_MODEL_GOOGLE_BIN_MAXSZ);
+static const BIN_ATTR_RW(vts_svoice_model, VTS_MODEL_SVOICE_BIN_MAXSZ);
+
+static ssize_t vtsfw_version_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+       unsigned int version = data->vtsfw_version;
+
+       buf[0] = (((version >> 24) & 0xFF) + '0');
+       buf[1] = '.';
+       buf[2] = (((version >> 16) & 0xFF) + '0');
+       buf[3] = '.';
+       buf[4] = (((version >> 8) & 0xFF) + '0');
+       buf[5] = '.';
+       buf[6] = ((version & 0xFF) + '0');
+       buf[7] = '\n';
+       buf[8] = '\0';
+
+       return 9;
+}
+
+static ssize_t vtsdetectlib_version_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+       unsigned int version = data->vtsdetectlib_version;
+
+       buf[0] = (((version >> 24) & 0xFF) + '0');
+       buf[1] = '.';
+       buf[2] = (((version >> 16) & 0xFF) + '0');
+       buf[3] = '.';
+       buf[4] = ((version & 0xFF) + '0');
+       buf[5] = '\n';
+       buf[6] = '\0';
+
+       return 7;
+}
+
+static ssize_t vts_audiodump_show(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", data->audiodump_enabled);
+}
+
+static ssize_t vts_audiodump_store(struct device *dev,
+                       struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+       u32 val = 0;
+       u32 values[3] = {0, 0, 0};
+       int result;
+       int err = kstrtouint(buf, 0, &val);
+
+       if (err < 0)
+               return err;
+       data->audiodump_enabled = (val ? true : false);
+
+       if (data->vts_ready) {
+               if (data->audiodump_enabled) {
+                       values[0] = VTS_ENABLE_AUDIODUMP;
+                       values[1] = (VTS_ADUIODUMP_AFTER_MINS * 60);
+               } else {
+                       values[0] = VTS_DISABLE_AUDIODUMP;
+                       values[1] = 0;
+               }
+               values[2] = 0;
+               result = vts_start_ipc_transaction(dev, data,
+                               VTS_IRQ_AP_TEST_COMMAND, &values,
+                               0, 2);
+               if (result < 0) {
+                       dev_err(dev, "AudioDump[%d] ipc failed\n",
+                               data->audiodump_enabled);
+               }
+       }
+
+       dev_info(dev, "%s: Audio dump %sabled\n",
+               __func__, (val ? "en" : "dis"));
+       return count;
+}
+
+static ssize_t vts_logdump_show(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", data->logdump_enabled);
+}
+
+static ssize_t vts_logdump_store(struct device *dev,
+                       struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+       u32 val = 0;
+       u32 values[3] = {0, 0, 0};
+       int result;
+       int err = kstrtouint(buf, 0, &val);
+
+       if (err < 0)
+               return err;
+
+       data->logdump_enabled = (val ? true : false);
+
+       if (data->vts_ready) {
+               if (data->logdump_enabled) {
+                       values[0] = VTS_ENABLE_LOGDUMP;
+                       values[1] = (VTS_LOGDUMP_AFTER_MINS * 60);
+               } else {
+                       values[0] = VTS_DISABLE_LOGDUMP;
+                       values[1] = 0;
+               }
+               values[2] = 0;
+               result = vts_start_ipc_transaction(dev, data,
+                               VTS_IRQ_AP_TEST_COMMAND, &values,
+                               0, 2);
+               if (result < 0) {
+                       dev_err(dev, "LogDump[%d] ipc failed\n",
+                               data->logdump_enabled);
+               }
+       }
+
+       dev_info(dev, "%s: Log dump %sabled\n",
+               __func__, (val ? "en" : "dis"));
+       return count;
+}
+
+static DEVICE_ATTR_RO(vtsfw_version);
+static DEVICE_ATTR_RO(vtsdetectlib_version);
+static DEVICE_ATTR_RW(vts_audiodump);
+static DEVICE_ATTR_RW(vts_logdump);
+
+static void vts_complete_firmware_request(const struct firmware *fw, void *context)
+{
+       struct platform_device *pdev = context;
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+       unsigned int *pversion = NULL;
+
+       if (!fw) {
+               dev_err(dev, "Failed to request firmware\n");
+               return;
+       }
+
+       data->firmware = fw;
+       pversion = (unsigned int*) (fw->data + VTSFW_VERSION_OFFSET);
+       data->vtsfw_version = *pversion;
+       pversion = (unsigned int*) (fw->data + DETLIB_VERSION_OFFSET);
+       data->vtsdetectlib_version = *pversion;
+
+       dev_info(dev, "Firmware loaded at %p (%zu)\n", fw->data, fw->size);
+       dev_info(dev, "Firmware version: 0x%x Detection library version: 0x%x\n", data->vtsfw_version, data->vtsdetectlib_version);
+}
+
+static void __iomem *samsung_vts_devm_request_and_map(struct platform_device *pdev, const char *name, size_t *size)
+{
+       struct resource *res;
+       void __iomem *result;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(&pdev->dev, "Failed to get %s\n", name);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (size) {
+               *size = resource_size(res);
+       }
+
+       res = devm_request_mem_region(&pdev->dev, res->start, resource_size(res), name);
+       if (IS_ERR_OR_NULL(res)) {
+               dev_err(&pdev->dev, "Failed to request %s\n", name);
+               return ERR_PTR(-EFAULT);
+       }
+
+       result = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (IS_ERR_OR_NULL(result)) {
+               dev_err(&pdev->dev, "Failed to map %s\n", name);
+               return ERR_PTR(-EFAULT);
+       }
+
+       dev_info(&pdev->dev, "%s: %s(%p) is mapped on %p with size of %zu",
+                       __func__, name, (void *)res->start, result, (size_t)resource_size(res));
+
+       return result;
+}
+
+static int samsung_vts_devm_request_threaded_irq(
+               struct platform_device *pdev, const char *irq_name,
+               unsigned int hw_irq, irq_handler_t thread_fn)
+{
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+       int result;
+
+       data->irq[hw_irq] = platform_get_irq_byname(pdev, irq_name);
+       if (data->irq[hw_irq] < 0) {
+               dev_err(dev, "Failed to get irq %s: %d\n", irq_name, data->irq[hw_irq]);
+               return data->irq[hw_irq];
+       }
+
+       result = devm_request_threaded_irq(dev, data->irq[hw_irq],
+                       NULL, thread_fn,
+                       IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev->init_name,
+                       pdev);
+
+       if (result < 0) {
+               dev_err(dev, "Unable to request irq %s: %d\n", irq_name, result);
+       }
+
+       return result;
+}
+
+static struct clk *devm_clk_get_and_prepare(struct device *dev, const char *name)
+{
+       struct clk *clk;
+       int result;
+
+       clk = devm_clk_get(dev, name);
+       if (IS_ERR(clk)) {
+               dev_err(dev, "Failed to get clock %s\n", name);
+               goto error;
+       }
+
+       result = clk_prepare(clk);
+       if (result < 0) {
+               dev_err(dev, "Failed to prepare clock %s\n", name);
+               goto error;
+       }
+
+error:
+       return clk;
+}
+
+static const struct reg_default vts_dmic_reg_defaults[] = {
+       {0x0000, 0x00030000},
+       {0x0004, 0x00000000},
+};
+
+static const struct regmap_config vts_component_regmap_config = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .max_register = VTS_DMIC_CONTROL_DMIC_IF,
+       .reg_defaults = vts_dmic_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(vts_dmic_reg_defaults),
+       .cache_type = REGCACHE_RBTREE,
+       .fast_io = true,
+};
+
+static int samsung_vts_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct vts_data *data;
+       int result;
+       int dmic_clkctrl = 0;
+
+       dev_info(dev, "%s \n", __func__);
+       data = devm_kzalloc(dev, sizeof(struct vts_data), GFP_KERNEL);
+       if (!data) {
+               dev_err(dev, "Failed to allocate memory\n");
+               result = -ENOMEM;
+               goto error;
+       }
+
+       /* Model binary memory allocation */
+       data->google_info.max_sz = VTS_MODEL_GOOGLE_BIN_MAXSZ;
+       data->google_info.actual_sz = 0;
+       data->google_info.loaded = false;
+       data->google_info.data = vmalloc(VTS_MODEL_GOOGLE_BIN_MAXSZ);
+       if (!data->google_info.data) {
+               dev_err(dev, "%s Failed to allocate Grammar Bin memory\n", __func__);
+               result = -ENOMEM;
+               goto error;
+       }
+
+       data->svoice_info.max_sz = VTS_MODEL_SVOICE_BIN_MAXSZ;
+       data->svoice_info.actual_sz = 0;
+       data->svoice_info.loaded = false;
+       data->svoice_info.data = vmalloc(VTS_MODEL_SVOICE_BIN_MAXSZ);
+       if (!data->svoice_info.data) {
+               dev_err(dev, "%s Failed to allocate Net Bin memory\n", __func__);
+               result = -ENOMEM;
+               goto error;
+       }
+
+       /* initialize device structure members */
+       data->active_trigger = TRIGGER_SVOICE;
+
+       /* initialize micbias setting count */
+       data->micclk_init_cnt = 0;
+       data->mic_ready = 0;
+       data->vts_state = VTS_STATE_NONE;
+
+       platform_set_drvdata(pdev, data);
+       data->pdev = pdev;
+       p_vts_data = data;
+
+       init_waitqueue_head(&data->ipc_wait_queue);
+       spin_lock_init(&data->ipc_spinlock);
+       mutex_init(&data->ipc_mutex);
+       wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "vts");
+
+       data->pinctrl = devm_pinctrl_get(dev);
+       if (IS_ERR(data->pinctrl)) {
+               dev_err(dev, "Couldn't get pins (%li)\n",
+                               PTR_ERR(data->pinctrl));
+               return PTR_ERR(data->pinctrl);
+       }
+
+       data->sfr_base = samsung_vts_devm_request_and_map(pdev, "sfr", NULL);
+       if (IS_ERR(data->sfr_base)) {
+               result = PTR_ERR(data->sfr_base);
+               goto error;
+       }
+
+       data->baaw_base = samsung_vts_devm_request_and_map(pdev, "baaw", NULL);
+       if (IS_ERR(data->baaw_base)) {
+               result = PTR_ERR(data->baaw_base);
+               goto error;
+       }
+
+       data->sram_base = samsung_vts_devm_request_and_map(pdev, "sram", &data->sram_size);
+       if (IS_ERR(data->sram_base)) {
+               result = PTR_ERR(data->sram_base);
+               goto error;
+       }
+
+       data->dmic_base = samsung_vts_devm_request_and_map(pdev, "dmic", NULL);
+       if (IS_ERR(data->dmic_base)) {
+               result = PTR_ERR(data->dmic_base);
+               goto error;
+       }
+
+       data->gpr_base = samsung_vts_devm_request_and_map(pdev, "gpr", NULL);
+       if (IS_ERR(data->gpr_base)) {
+               result = PTR_ERR(data->gpr_base);
+               goto error;
+       }
+
+       data->lpsdgain = 0;
+       data->dmicgain = 0;
+       data->amicgain = 0;
+
+       /* read tunned VTS gain values */
+       of_property_read_u32(np, "lpsd-gain", &data->lpsdgain);
+       of_property_read_u32(np, "dmic-gain", &data->dmicgain);
+       of_property_read_u32(np, "amic-gain", &data->amicgain);
+
+       dev_info(dev, "VTS Tunned Gain value LPSD: %d DMIC: %d AMIC: %d\n",
+                       data->lpsdgain, data->dmicgain, data->amicgain);
+
+       data->dmab.area = dmam_alloc_coherent(dev, BUFFER_BYTES_MAX, &data->dmab.addr, GFP_KERNEL);
+       if (data->dmab.area == NULL) {
+               result = -ENOMEM;
+               goto error;
+       }
+       data->dmab.bytes = BUFFER_BYTES_MAX/2;
+       data->dmab.dev.dev = dev;
+       data->dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+
+       data->dmab_rec.area = (data->dmab.area + BUFFER_BYTES_MAX/2);
+       data->dmab_rec.addr = (data->dmab.addr + BUFFER_BYTES_MAX/2);
+       data->dmab_rec.bytes = BUFFER_BYTES_MAX/2;
+       data->dmab_rec.dev.dev = dev;
+       data->dmab_rec.dev.type = SNDRV_DMA_TYPE_DEV;
+
+       data->dmab_log.area = dmam_alloc_coherent(dev, LOG_BUFFER_BYTES_MAX,
+                               &data->dmab_log.addr, GFP_KERNEL);
+       if (data->dmab_log.area == NULL) {
+               result = -ENOMEM;
+               goto error;
+       }
+       data->dmab.bytes = LOG_BUFFER_BYTES_MAX;
+       data->dmab.dev.dev = dev;
+       data->dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+
+#ifdef CONFIG_SOC_EXYNOS8895
+       data->clk_rco = devm_clk_get_and_prepare(dev, "rco");
+       if (IS_ERR(data->clk_rco)) {
+               result = PTR_ERR(data->clk_rco);
+               goto error;
+       }
+
+       result = clk_enable(data->clk_rco);
+       if (result < 0) {
+               dev_err(dev, "Failed to enable the rco\n");
+               goto error;
+       }
+#endif
+       data->clk_dmic = devm_clk_get_and_prepare(dev, "dmic");
+       if (IS_ERR(data->clk_dmic)) {
+               result = PTR_ERR(data->clk_dmic);
+               goto error;
+       }
+
+       data->clk_dmic_if= devm_clk_get_and_prepare(dev, "dmic_if");
+       if (IS_ERR(data->clk_dmic_if)) {
+               result = PTR_ERR(data->clk_dmic_if);
+               goto error;
+       }
+
+       data->clk_dmic_sync = devm_clk_get_and_prepare(dev, "dmic_sync");
+       if (IS_ERR(data->clk_dmic_sync)) {
+               result = PTR_ERR(data->clk_dmic_sync);
+               goto error;
+       }
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "error",
+                       VTS_IRQ_VTS_ERROR, vts_error_handler);
+       if (result < 0) {
+               goto error;
+       }
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "boot_completed",
+                       VTS_IRQ_VTS_BOOT_COMPLETED, vts_boot_completed_handler);
+       if (result < 0) {
+               goto error;
+       }
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "ipc_received",
+                       VTS_IRQ_VTS_IPC_RECEIVED, vts_ipc_received_handler);
+       if (result < 0) {
+               goto error;
+       }
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "voice_triggered",
+                       VTS_IRQ_VTS_VOICE_TRIGGERED, vts_voice_triggered_handler);
+       if (result < 0) {
+               goto error;
+       }
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "trigger_period_elapsed",
+                       VTS_IRQ_VTS_PERIOD_ELAPSED, vts_trigger_period_elapsed_handler);
+       if (result < 0) {
+               goto error;
+       }
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "record_period_elapsed",
+                       VTS_IRQ_VTS_REC_PERIOD_ELAPSED, vts_record_period_elapsed_handler);
+       if (result < 0) {
+               goto error;
+       }
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "debuglog_bufzero",
+                       VTS_IRQ_VTS_DBGLOG_BUFZERO, vts_debuglog_bufzero_handler);
+       if (result < 0)
+               goto error;
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "debuglog_bufone",
+                       VTS_IRQ_VTS_DBGLOG_BUFONE, vts_debuglog_bufone_handler);
+       if (result < 0)
+               goto error;
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "audio_dump",
+                       VTS_IRQ_VTS_AUDIO_DUMP, vts_audiodump_handler);
+       if (result < 0)
+               goto error;
+
+       result = samsung_vts_devm_request_threaded_irq(pdev, "log_dump",
+                       VTS_IRQ_VTS_LOG_DUMP, vts_logdump_handler);
+       if (result < 0)
+               goto error;
+
+       data->irq_state = true;
+
+       data->pdev_mailbox = of_find_device_by_node(of_parse_phandle(np, "mailbox", 0));
+       if (!data->pdev_mailbox) {
+               dev_err(dev, "Failed to get mailbox\n");
+               result = -EPROBE_DEFER;
+               goto error;
+       }
+
+       result = request_firmware_nowait(THIS_MODULE,
+                       FW_ACTION_HOTPLUG,
+                       "vts.bin",
+                       dev,
+                       GFP_KERNEL,
+                       pdev,
+                       vts_complete_firmware_request);
+       if (result < 0) {
+               dev_err(dev, "Failed to request firmware\n");
+               goto error;
+       }
+
+       result = device_create_bin_file(dev, &bin_attr_vts_google_model);
+       if (result < 0) {
+               dev_err(dev, "Failed to create attribute %s\n", "vts_google_model");
+               goto error;
+       }
+
+       result = device_create_bin_file(dev, &bin_attr_vts_svoice_model);
+       if (result < 0) {
+               dev_err(dev, "Failed to create attribute %s\n", "vts_svoice_model");
+               goto error;
+       }
+
+       data->regmap_dmic = devm_regmap_init_mmio_clk(dev,
+                       NULL,
+                       data->dmic_base,
+                       &vts_component_regmap_config);
+
+       result = snd_soc_register_component(dev, &vts_component, vts_dai, ARRAY_SIZE(vts_dai));
+       if (result < 0) {
+               dev_err(dev, "Failed to register ASoC component\n");
+               goto error;
+       }
+
+#ifdef EMULATOR
+       pmu_alive = ioremap(0x16480000, 0x10000);
+#endif
+       printk("come hear %d\n", __LINE__);
+       pm_runtime_enable(dev);
+       pm_runtime_get_sync(dev);
+
+       vts_cfg_gpio(dev, "idle");
+
+       data->voicecall_enabled = false;
+       data->voicerecog_start = 0;
+       data->syssel_rate = 0;
+       data->target_size = 0;
+       data->vtsfw_version = 0x0;
+       data->vtsdetectlib_version = 0x0;
+       data->vtslog_enabled = 0;
+       data->audiodump_enabled = false;
+       data->logdump_enabled = false;
+
+       /* set VTS BAAW config */
+       writel(0x40300, data->baaw_base);
+       writel(0x40600, data->baaw_base + 0x4);
+       writel(0x14100, data->baaw_base + 0x8);
+       writel(0x80000003, data->baaw_base + 0xC);
+
+       dmic_clkctrl = readl(data->sfr_base + VTS_DMIC_CLK_CTRL);
+       writel(dmic_clkctrl & ~(0x1 << VTS_CLK_ENABLE_OFFSET),
+                               data->sfr_base + VTS_DMIC_CLK_CTRL);
+       dev_dbg(dev, "DMIC_CLK_CTRL: Before 0x%x After 0x%x \n", dmic_clkctrl,
+                       readl(data->sfr_base + VTS_DMIC_CLK_CTRL));
+
+       result = device_create_file(dev, &dev_attr_vtsfw_version);
+       if (result < 0)
+               dev_warn(dev, "Failed to create file: %s\n", "vtsfw_version");
+
+       result = device_create_file(dev, &dev_attr_vtsdetectlib_version);
+       if (result < 0)
+               dev_warn(dev, "Failed to create file: %s\n", "vtsdetectlib_version");
+
+       result = device_create_file(dev, &dev_attr_vts_audiodump);
+       if (result < 0)
+               dev_warn(dev, "Failed to create file: %s\n", "vts_audiodump");
+
+       result = device_create_file(dev, &dev_attr_vts_logdump);
+       if (result < 0)
+               dev_warn(dev, "Failed to create file: %s\n", "vts_logdump");
+
+       data->sramlog_baseaddr = (char *)(data->sram_base + VTS_SRAMLOG_MSGS_OFFSET);
+
+       atomic_notifier_chain_register(&panic_notifier_list, &vts_panic_notifier);
+
+       /* initialize log buffer offset as non */
+       vts_register_log_buffer(dev, 0, 0);
+
+       device_init_wakeup(dev, true);
+       dev_info(dev, "Probed successfully\n");
+
+error:
+       return result;
+}
+
+static int samsung_vts_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct vts_data *data = platform_get_drvdata(pdev);
+
+       pm_runtime_disable(dev);
+       clk_unprepare(data->clk_dmic);
+#ifndef CONFIG_PM
+       vts_runtime_suspend(dev);
+#endif
+       release_firmware(data->firmware);
+       if (data->google_info.data)
+               vfree(data->google_info.data);
+       if (data->svoice_info.data)
+               vfree(data->svoice_info.data);
+       snd_soc_unregister_component(dev);
+#ifdef EMULATOR
+       iounmap(pmu_alive);
+#endif
+       return 0;
+}
+
+static struct platform_driver samsung_vts_driver = {
+       .probe  = samsung_vts_probe,
+       .remove = samsung_vts_remove,
+       .driver = {
+               .name = "samsung-vts",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(exynos_vts_of_match),
+               .pm = &samsung_vts_pm,
+       },
+};
+
+module_platform_driver(samsung_vts_driver);
+
+static int __init samsung_vts_late_initcall(void)
+{
+       pr_info("%s\n", __func__);
+
+       if (p_vts_data && p_vts_data->pdev) {
+               pm_runtime_put_sync(&p_vts_data->pdev->dev);
+       } else {
+               pr_err("%s: p_vts_data or p_vts_data->pdev is null", __func__);
+       }
+       return 0;
+}
+late_initcall(samsung_vts_late_initcall);
+
+/* Module information */
+MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
+MODULE_AUTHOR("Palli Satish Kumar Reddy, <palli.satish@samsung.com>");
+MODULE_DESCRIPTION("Samsung Voice Trigger System");
+MODULE_ALIAS("platform:samsung-vts");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/vts/vts.h b/sound/soc/samsung/vts/vts.h
new file mode 100644 (file)
index 0000000..249092b
--- /dev/null
@@ -0,0 +1,325 @@
+/* sound/soc/samsung/vts/vts.h
+ *
+ * ALSA SoC - Samsung VTS driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+  *
+ * 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 __SND_SOC_VTS_H
+#define __SND_SOC_VTS_H
+
+#include <sound/memalloc.h>
+#include <linux/wakelock.h>
+
+/* SYSREG_VTS */
+#define VTS_DEBUG                      (0x0404)
+#define VTS_DMIC_CLK_CTRL              (0x0408)
+#define VTS_HWACG_CM4_CLKREQ           (0x0428)
+#define VTS_DMIC_CLK_CON               (0x0434)
+#define VTS_SYSPOWER_CTRL              (0x0500)
+#define VTS_SYSPOWER_STATUS            (0x0504)
+
+/* VTS_DEBUG */
+#define VTS_NMI_EN_BY_WDT_OFFSET       (0)
+#define VTS_NMI_EN_BY_WDT_SIZE         (1)
+/* VTS_DMIC_CLK_CTRL */
+#define VTS_CG_STATUS_OFFSET            (5)
+#define VTS_CG_STATUS_SIZE             (1)
+#define VTS_CLK_ENABLE_OFFSET          (4)
+#define VTS_CLK_ENABLE_SIZE            (1)
+#define VTS_CLK_SEL_OFFSET             (0)
+#define VTS_CLK_SEL_SIZE               (1)
+/* VTS_HWACG_CM4_CLKREQ */
+#define VTS_MASK_OFFSET                        (0)
+#define VTS_MASK_SIZE                  (32)
+/* VTS_DMIC_CLK_CON */
+#define VTS_ENABLE_CLK_GEN_OFFSET       (0)
+#define VTS_ENABLE_CLK_GEN_SIZE         (1)
+#define VTS_SEL_EXT_DMIC_CLK_OFFSET     (1)
+#define VTS_SEL_EXT_DMIC_CLK_SIZE       (1)
+#define VTS_ENABLE_CLK_CLK_GEN_OFFSET   (14)
+#define VTS_ENABLE_CLK_CLK_GEN_SIZE     (1)
+
+/* VTS_SYSPOWER_CTRL */
+#define VTS_SYSPOWER_CTRL_OFFSET       (0)
+#define VTS_SYSPOWER_CTRL_SIZE         (1)
+/* VTS_SYSPOWER_STATUS */
+#define VTS_SYSPOWER_STATUS_OFFSET     (0)
+#define VTS_SYSPOWER_STATUS_SIZE       (1)
+
+
+#define VTS_DMIC_ENABLE_DMIC_IF                (0x0000)
+#define VTS_DMIC_CONTROL_DMIC_IF       (0x0004)
+/* VTS_DMIC_ENABLE_DMIC_IF */
+#define VTS_DMIC_ENABLE_DMIC_IF_OFFSET (31)
+#define VTS_DMIC_ENABLE_DMIC_IF_SIZE   (1)
+#define VTS_DMIC_PERIOD_DATA2REQ_OFFSET        (16)
+#define VTS_DMIC_PERIOD_DATA2REQ_SIZE  (2)
+/* VTS_DMIC_CONTROL_DMIC_IF */
+#define VTS_DMIC_HPF_EN_OFFSET         (31)
+#define VTS_DMIC_HPF_EN_SIZE           (1)
+#define VTS_DMIC_HPF_SEL_OFFSET                (28)
+#define VTS_DMIC_HPF_SEL_SIZE          (1)
+#define VTS_DMIC_CPS_SEL_OFFSET                (27)
+#define VTS_DMIC_CPS_SEL_SIZE          (1)
+#define VTS_DMIC_GAIN_OFFSET           (24)
+#define VTS_DMIC_GAIN_SIZE             (3)
+#define VTS_DMIC_DMIC_SEL_OFFSET       (18)
+#define VTS_DMIC_DMIC_SEL_SIZE         (1)
+#define VTS_DMIC_RCH_EN_OFFSET         (17)
+#define VTS_DMIC_RCH_EN_SIZE           (1)
+#define VTS_DMIC_LCH_EN_OFFSET         (16)
+#define VTS_DMIC_LCH_EN_SIZE           (1)
+#define VTS_DMIC_SYS_SEL_OFFSET                (12)
+#define VTS_DMIC_SYS_SEL_SIZE          (2)
+#define VTS_DMIC_POLARITY_CLK_OFFSET   (10)
+#define VTS_DMIC_POLARITY_CLK_SIZE     (1)
+#define VTS_DMIC_POLARITY_OUTPUT_OFFSET        (9)
+#define VTS_DMIC_POLARITY_OUTPUT_SIZE  (1)
+#define VTS_DMIC_POLARITY_INPUT_OFFSET (8)
+#define VTS_DMIC_POLARITY_INPUT_SIZE   (1)
+#define VTS_DMIC_OVFW_CTRL_OFFSET      (4)
+#define VTS_DMIC_OVFW_CTRL_SIZE                (1)
+#define VTS_DMIC_CIC_SEL_OFFSET                (0)
+#define VTS_DMIC_CIC_SEL_SIZE          (1)
+
+/* CM4 */
+#define VTS_CM4_R(x)                   (0x0010 + (x * 0x4))
+#define VTS_CM4_PC                     (0x0004)
+
+#define VTS_IRQ_VTS_ERROR               (16)
+#define VTS_IRQ_VTS_BOOT_COMPLETED      (17)
+#define VTS_IRQ_VTS_IPC_RECEIVED        (18)
+#define VTS_IRQ_VTS_VOICE_TRIGGERED     (19)
+#define VTS_IRQ_VTS_PERIOD_ELAPSED      (20)
+#define VTS_IRQ_VTS_REC_PERIOD_ELAPSED  (21)
+#define VTS_IRQ_VTS_DBGLOG_BUFZERO      (22)
+#define VTS_IRQ_VTS_DBGLOG_BUFONE       (23)
+#define VTS_IRQ_VTS_AUDIO_DUMP          (24)
+#define VTS_IRQ_VTS_LOG_DUMP            (25)
+#define VTS_IRQ_COUNT                   (26)
+
+#define VTS_IRQ_AP_IPC_RECEIVED                (0)
+#define VTS_IRQ_AP_SET_DRAM_BUFFER     (1)
+#define VTS_IRQ_AP_START_RECOGNITION   (2)
+#define VTS_IRQ_AP_STOP_RECOGNITION    (3)
+#define VTS_IRQ_AP_START_COPY          (4)
+#define VTS_IRQ_AP_STOP_COPY           (5)
+#define VTS_IRQ_AP_SET_MODE            (6)
+#define VTS_IRQ_AP_POWER_DOWN          (7)
+#define VTS_IRQ_AP_TARGET_SIZE         (8)
+#define VTS_IRQ_AP_SET_REC_BUFFER      (9)
+#define VTS_IRQ_AP_START_REC           (10)
+#define VTS_IRQ_AP_STOP_REC            (11)
+#define VTS_IRQ_AP_RESTART_RECOGNITION (13)
+#define VTS_IRQ_AP_TEST_COMMAND                (15)
+
+#define VTS_IRQ_LIMIT                  (32)
+
+#define VTS_BAAW_BASE                  (0x60000000)
+#define VTS_BAAW_SRC_START_ADDRESS     (0x10000)
+#define VTS_BAAW_SRC_END_ADDRESS       (0x10004)
+#define VTS_BAAW_REMAPPED_ADDRESS      (0x10008)
+#define VTS_BAAW_INIT_DONE             (0x1000C)
+
+#define BUFFER_BYTES_MAX (0xa0000)
+#define PERIOD_BYTES_MIN (SZ_4)
+#define PERIOD_BYTES_MAX (BUFFER_BYTES_MAX / 2)
+
+#define SOUND_MODEL_SIZE_MAX (SZ_32K)
+#define SOUND_MODEL_COUNT (3)
+
+/* DRAM for copying VTS firmware logs */
+#define LOG_BUFFER_BYTES_MAX   (0x2000)
+#define VTS_SRAMLOG_MSGS_OFFSET (0x59000)
+
+/* VTS firmware version information offset */
+#define VTSFW_VERSION_OFFSET   (0x7c)
+#define DETLIB_VERSION_OFFSET  (0x78)
+
+/* VTS Model Binary Max buffer sizes */
+#define VTS_MODEL_SVOICE_BIN_MAXSZ     (SZ_64K)
+#define VTS_MODEL_GOOGLE_BIN_MAXSZ     (SZ_64K)
+
+enum ipc_state {
+       IDLE,
+       SEND_MSG,
+       SEND_MSG_OK,
+       SEND_MSG_FAIL,
+};
+
+enum trigger {
+       TRIGGER_NONE    = -1,
+       TRIGGER_SVOICE  = 0,
+       TRIGGER_GOOGLE  = 1,
+       TRIGGER_SENSORY = 2,
+       TRIGGER_COUNT,
+};
+
+enum vts_platform_type {
+       PLATFORM_VTS_NORMAL_RECORD,
+       PLATFORM_VTS_TRIGGER_RECORD,
+};
+
+enum executionmode {
+       //default is off
+       VTS_OFF_MODE                    = 0,
+       //voice-trig-mode:Both LPSD & Trigger are enabled
+       VTS_VOICE_TRIGGER_MODE          = 1,
+       //sound-detect-mode: Low Power sound Detect
+       VTS_SOUND_DETECT_MODE           = 2,
+       //vt-always-mode: key phrase Detection only(Trigger)
+       VTS_VT_ALWAYS_ON_MODE           = 3,
+       //google-trigger: key phrase Detection only(Trigger)
+       VTS_GOOGLE_TRIGGER_MODE         = 4,
+       //sensory-trigger: key phrase Detection only(Trigger)
+       VTS_SENSORY_TRIGGER_MODE        = 5,
+       //off:voice-trig-mode:Both LPSD & Trigger are enabled
+       VTS_VOICE_TRIGGER_MODE_OFF      = 6,
+       //off:sound-detect-mode: Low Power sound Detect
+       VTS_SOUND_DETECT_MODE_OFF       = 7,
+       //off:vt-always-mode: key phrase Detection only(Trigger)
+       VTS_VT_ALWAYS_ON_MODE_OFF       = 8,
+       //off:google-trigger: key phrase Detection only(Trigger)
+       VTS_GOOGLE_TRIGGER_MODE_OFF     = 9,
+       //off:sensory-trigger: key phrase Detection only(Trigger)
+       VTS_SENSORY_TRIGGER_MODE_OFF    = 10,
+       VTS_MODE_COUNT,
+};
+
+enum vts_dump_type {
+       KERNEL_PANIC_DUMP = 0,
+       RUNTIME_SUSPEND_DUMP = 1,
+};
+
+enum vts_test_command {
+       VTS_DISABLE_LOGDUMP     = 0x01000000,
+       VTS_ENABLE_LOGDUMP      = 0x02000000,
+       VTS_DISABLE_AUDIODUMP   = 0x04000000,
+       VTS_ENABLE_AUDIODUMP    = 0x08000000,
+       VTS_DISABLE_DEBUGLOG    = 0x10000000,
+       VTS_ENABLE_DEBUGLOG     = 0x20000000,
+       VTS_ENABLE_SRAM_LOG     = 0x80000000,
+};
+
+struct vts_ipc_msg {
+       int msg;
+       u32 values[3];
+};
+
+enum vts_micconf_type {
+       VTS_MICCONF_FOR_RECORD  = 0,
+       VTS_MICCONF_FOR_TRIGGER = 1,
+       VTS_MICCONF_FOR_GOOGLE  = 2,
+};
+
+enum vts_state_machine {
+       VTS_STATE_NONE                  = 0,    //runtime_suspended state
+       VTS_STATE_VOICECALL             = 1,    //sram L2Cache voicecall state
+       VTS_STATE_IDLE                  = 2,    //runtime_resume state
+       VTS_STATE_RECOG_STARTED         = 3,    //Voice Recognization started
+       VTS_STATE_RECOG_TRIGGERED       = 4,    //Voice Recognize triggered
+       VTS_STATE_SEAMLESS_REC_STARTED  = 5,    //seamless record started
+       VTS_STATE_SEAMLESS_REC_STOPPED  = 6,    //seamless record started
+       VTS_STATE_RECOG_STOPPED         = 7,    //Voice Recognization stopped
+};
+
+struct vts_model_bin_info {
+       unsigned char *data;
+       size_t  actual_sz;
+       size_t  max_sz;
+       bool loaded;
+};
+
+struct vts_data {
+       struct platform_device *pdev;
+       struct snd_soc_component *cmpnt;
+       void __iomem *sfr_base;
+       void __iomem *baaw_base;
+       void __iomem *sram_base;
+       void __iomem *dmic_base;
+       void __iomem *gpr_base;
+       size_t sram_size;
+       struct regmap *regmap_dmic;
+       struct clk *clk_rco;
+       struct clk *clk_dmic;
+       struct clk *clk_dmic_if;
+       struct clk *clk_dmic_sync;
+       struct pinctrl *pinctrl;
+       unsigned int vtsfw_version;
+       unsigned int vtsdetectlib_version;
+       const struct firmware *firmware;
+       unsigned int vtsdma_count;
+       unsigned long syssel_rate;
+       struct platform_device *pdev_mailbox;
+       struct platform_device *pdev_vtsdma[2];
+       struct proc_dir_entry *proc_dir_entry;
+       int irq[VTS_IRQ_LIMIT];
+       volatile enum ipc_state ipc_state_ap;
+       wait_queue_head_t ipc_wait_queue;
+       spinlock_t ipc_spinlock;
+       struct mutex ipc_mutex;
+       u32 dma_area_vts;
+       struct snd_dma_buffer dmab;
+       struct snd_dma_buffer dmab_rec;
+       struct snd_dma_buffer dmab_log;
+       u32 target_size;
+       volatile enum trigger active_trigger;
+       u32 voicerecog_start;
+       enum executionmode exec_mode;
+       bool vts_ready;
+       volatile unsigned long sram_acquired;
+       volatile bool enabled;
+       volatile bool running;
+       bool voicecall_enabled;
+       struct snd_soc_card *card;
+       int micclk_init_cnt;
+       unsigned int mic_ready;
+       bool irq_state;
+       u32 lpsdgain;
+       u32 dmicgain;
+       u32 amicgain;
+       char *sramlog_baseaddr;
+       u32 running_ipc;
+       struct wake_lock wake_lock;
+       unsigned int vts_state;
+       u32 vtslog_enabled;
+       bool audiodump_enabled;
+       bool logdump_enabled;
+       struct vts_model_bin_info svoice_info;
+       struct vts_model_bin_info google_info;
+};
+
+struct vts_platform_data {
+       unsigned int id;
+       struct platform_device *pdev_vts;
+       struct vts_data *vts_data;
+       struct snd_pcm_substream *substream;
+       enum vts_platform_type type;
+       volatile unsigned int pointer;
+};
+
+struct vts_dbg_dump {
+       char sram[SZ_2K];
+       unsigned int gpr[17];
+       long long time;
+       char reason[SZ_32];
+};
+
+struct vts_log_buffer {
+       char *addr;
+       unsigned int size;
+};
+
+extern int vts_start_ipc_transaction(struct device *dev, struct vts_data *data,
+               int msg, u32 (*values)[3], int atomic, int sync);
+
+extern int vts_send_ipc_ack(struct vts_data *data, u32 result);
+extern void vts_register_dma(struct platform_device *pdev_vts,
+               struct platform_device *pdev_vts_dma, unsigned int id);
+extern int vts_set_dmicctrl(struct platform_device *pdev, int micconf_type, bool enable);
+#endif /* __SND_SOC_VTS_H */
diff --git a/sound/soc/samsung/vts/vts_dma.c b/sound/soc/samsung/vts/vts_dma.c
new file mode 100644 (file)
index 0000000..6d77ab7
--- /dev/null
@@ -0,0 +1,355 @@
+/* sound/soc/samsung/vts/vts-plat.c
+ *
+ * ALSA SoC - Samsung VTS platfrom driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co. Ltd.
+  *
+ * 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.
+ */
+//#define DEBUG
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/wakelock.h>
+
+#include <asm-generic/delay.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include <sound/samsung/mailbox.h>
+#include <sound/samsung/vts.h>
+#include <soc/samsung/exynos-pmu.h>
+
+#include "vts.h"
+
+static const struct snd_pcm_hardware vts_platform_hardware = {
+       .info                   = SNDRV_PCM_INFO_INTERLEAVED
+                               | SNDRV_PCM_INFO_BLOCK_TRANSFER
+                               | SNDRV_PCM_INFO_MMAP
+                               | SNDRV_PCM_INFO_MMAP_VALID,
+       .formats                = SNDRV_PCM_FMTBIT_S16,
+       .rates                  = SNDRV_PCM_RATE_16000,
+       .channels_min           = 1,
+       .channels_max           = 2,
+       .buffer_bytes_max       = BUFFER_BYTES_MAX,
+       .period_bytes_min       = PERIOD_BYTES_MIN,
+       .period_bytes_max       = PERIOD_BYTES_MAX,
+       .periods_min            = BUFFER_BYTES_MAX / PERIOD_BYTES_MAX,
+       .periods_max            = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+};
+
+static int vts_platform_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_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct vts_platform_data *data = dev_get_drvdata(dev);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       if (data->type == PLATFORM_VTS_TRIGGER_RECORD) {
+               snd_pcm_set_runtime_buffer(substream, &data->vts_data->dmab);
+       } else {
+               snd_pcm_set_runtime_buffer(substream, &data->vts_data->dmab_rec);
+       }
+       dev_info(dev, "%s:%s:DmaAddr=%pad Total=%zu PrdSz=%u(%u) #Prds=%u dma_area=%p\n",
+                       __func__, snd_pcm_stream_str(substream), &runtime->dma_addr,
+                       runtime->dma_bytes, params_period_size(params),
+                       params_period_bytes(params), params_periods(params),
+                       runtime->dma_area);
+
+       data->pointer = 0;
+
+       return 0;
+}
+
+static int vts_platform_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       return 0;
+}
+
+static int vts_platform_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+
+       dev_info(dev, "%s\n", __func__);
+
+       return 0;
+}
+
+static int vts_platform_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct vts_platform_data *data = dev_get_drvdata(dev);
+       u32 values[3] = {0,0,0};
+       int result = 0;
+
+       dev_info(dev, "%s ++ CMD: %d\n", __func__, cmd);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (data->type == PLATFORM_VTS_TRIGGER_RECORD) {
+                       dev_dbg(dev, "%s VTS_IRQ_AP_START_COPY\n", __func__);
+                       result = vts_start_ipc_transaction(dev, data->vts_data, VTS_IRQ_AP_START_COPY, &values, 1, 1);
+               } else {
+                       dev_dbg(dev, "%s VTS_IRQ_AP_START_REC\n", __func__);
+                       result = vts_start_ipc_transaction(dev, data->vts_data, VTS_IRQ_AP_START_REC, &values, 1, 1);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (data->type == PLATFORM_VTS_TRIGGER_RECORD) {
+                       dev_dbg(dev, "%s VTS_IRQ_AP_STOP_COPY\n", __func__);
+                       result = vts_start_ipc_transaction(dev, data->vts_data, VTS_IRQ_AP_STOP_COPY, &values, 1, 1);
+               } else {
+                       dev_dbg(dev, "%s VTS_IRQ_AP_STOP_REC\n", __func__);
+                       result = vts_start_ipc_transaction(dev, data->vts_data, VTS_IRQ_AP_STOP_REC, &values, 1, 1);
+               }
+               break;
+       default:
+               result = -EINVAL;
+               break;
+       }
+
+       dev_info(dev, "%s -- CMD: %d\n", __func__, cmd);
+       return result;
+}
+
+static snd_pcm_uframes_t vts_platform_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct vts_platform_data *data = dev_get_drvdata(dev);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       dev_dbg(dev, "%s: pointer=%08x\n", __func__, data->pointer);
+
+       return bytes_to_frames(runtime, data->pointer);
+}
+
+static int vts_platform_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct vts_platform_data *data = dev_get_drvdata(dev);
+       int result = 0;
+
+       dev_info(dev, "%s\n", __func__);
+
+       if (data->vts_data->voicecall_enabled) {
+               dev_warn(dev, "%s VTS SRAM is Used for CP call\n",
+                                       __func__);
+               return -EBUSY;
+       }
+
+       pm_runtime_get_sync(dev);
+       snd_soc_set_runtime_hwparams(substream, &vts_platform_hardware);
+       if (data->type == PLATFORM_VTS_NORMAL_RECORD) {
+               dev_info(dev, "%s open --\n", __func__);
+               result = vts_set_dmicctrl(data->vts_data->pdev,
+                                       VTS_MICCONF_FOR_RECORD, true);
+               if (result < 0) {
+                       dev_err(dev, "%s: MIC control configuration failed\n", __func__);
+                       pm_runtime_put_sync(dev);
+               }
+       }
+
+       return result;
+}
+
+static int vts_platform_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct vts_platform_data *data = dev_get_drvdata(dev);
+       int result = 0;
+
+       dev_info(dev, "%s\n", __func__);
+
+       if (data->vts_data->voicecall_enabled) {
+               dev_warn(dev, "%s VTS SRAM is Used for CP call\n",
+                                       __func__);
+               return -EBUSY;
+       }
+
+       if (data->type == PLATFORM_VTS_NORMAL_RECORD) {
+               dev_info(dev, "%s close --\n", __func__);
+               result = vts_set_dmicctrl(data->vts_data->pdev,
+                                       VTS_MICCONF_FOR_RECORD, false);
+               if (result < 0)
+                       dev_warn(dev, "%s: MIC control configuration failed\n", __func__);
+       }
+
+       pm_runtime_put_sync(dev);
+       return result;
+}
+
+static int vts_platform_mmap(struct snd_pcm_substream *substream,
+               struct vm_area_struct *vma)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct device *dev = platform->dev;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       dev_info(dev, "%s\n", __func__);
+
+       return dma_mmap_writecombine(dev, vma,
+                       runtime->dma_area,
+                       runtime->dma_addr,
+                       runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops vts_platform_ops = {
+       .open           = vts_platform_open,
+       .close          = vts_platform_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = vts_platform_hw_params,
+       .hw_free        = vts_platform_hw_free,
+       .prepare        = vts_platform_prepare,
+       .trigger        = vts_platform_trigger,
+       .pointer        = vts_platform_pointer,
+       .mmap           = vts_platform_mmap,
+};
+
+static int vts_platform_new(struct snd_soc_pcm_runtime *runtime)
+{
+       struct snd_soc_platform *platform = runtime->platform;
+       struct device *dev = platform->dev;
+       struct vts_platform_data *data = dev_get_drvdata(dev);
+       struct snd_pcm_substream *substream = runtime->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+
+       dev_info(dev, "%s \n", __func__);
+       data->substream = substream;
+       dev_info(dev, "%s Update Soc Card from runtime!!\n", __func__);
+       data->vts_data->card = runtime->card;
+
+       return 0;
+}
+
+static void vts_platform_free(struct snd_pcm *pcm)
+{
+       return;
+}
+
+static const struct snd_soc_platform_driver vts_dma = {
+       .ops            = &vts_platform_ops,
+       .pcm_new        = vts_platform_new,
+       .pcm_free       = vts_platform_free,
+};
+
+static int samsung_vts_dma_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *np_vts;
+       struct vts_platform_data *data;
+       int result;
+       const char *type;
+
+       dev_info(dev, "%s \n", __func__);
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               dev_err(dev, "Failed to allocate memory\n");
+               return -ENOMEM;
+       }
+       platform_set_drvdata(pdev, data);
+
+       np_vts = of_parse_phandle(np, "vts", 0);
+       if (!np_vts) {
+               dev_err(dev, "Failed to get vts device node\n");
+               return -EPROBE_DEFER;
+       }
+       data->pdev_vts = of_find_device_by_node(np_vts);
+       if (!data->pdev_vts) {
+               dev_err(dev, "Failed to get vts platform device\n");
+               return -EPROBE_DEFER;
+       }
+       data->vts_data = platform_get_drvdata(data->pdev_vts);
+
+       result = of_property_read_u32_index(np, "id", 0, &data->id);
+       if (result < 0) {
+               dev_err(dev, "id property reading fail\n");
+               return result;
+       }
+
+       result = of_property_read_string(np, "type", &type);
+       if (result < 0) {
+               dev_err(dev, "type property reading fail\n");
+               return result;
+       }
+
+       if (!strncmp(type, "vts-record", sizeof("vts-record"))) {
+               data->type = PLATFORM_VTS_NORMAL_RECORD;
+               dev_info(dev, "%s - vts-record Probed \n", __func__);
+       } else {
+               data->type = PLATFORM_VTS_TRIGGER_RECORD;
+               dev_info(dev, "%s - vts-trigger-record Probed \n", __func__);
+       }
+
+       vts_register_dma(data->vts_data->pdev, pdev, data->id);
+
+       return snd_soc_register_platform(&pdev->dev, &vts_dma);
+}
+
+static int samsung_vts_dma_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_platform(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id samsung_vts_dma_match[] = {
+       {
+               .compatible = "samsung,vts-dma",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_vts_dma_match);
+
+static struct platform_driver samsung_vts_dma_driver = {
+       .probe  = samsung_vts_dma_probe,
+       .remove = samsung_vts_dma_remove,
+       .driver = {
+               .name = "samsung-vts-dma",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_vts_dma_match),
+       },
+};
+
+module_platform_driver(samsung_vts_dma_driver);
+
+/* Module information */
+MODULE_AUTHOR("Palli Satish Kumar Reddy, <palli.satish@samsung.com>");
+MODULE_DESCRIPTION("Samsung VTS DMA");
+MODULE_ALIAS("platform:samsung-vts-dma");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/vts/vts_dump.c b/sound/soc/samsung/vts/vts_dump.c
new file mode 100644 (file)
index 0000000..b981564
--- /dev/null
@@ -0,0 +1,183 @@
+/* sound/soc/samsung/vts/vts_dump.c
+ *
+ * ALSA SoC - Samsung VTS dump driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+//#define DEBUG
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/vmalloc.h>
+#include <sound/soc.h>
+
+#include "vts_dump.h"
+
+#define S_IRWUG (S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP)
+
+struct vts_dump_info {
+       struct device *dev;
+       struct mutex audiolock;
+       struct mutex loglock;
+       u32 audioaddr_offset;
+       u32 audiodump_sz;
+       u32 logaddr_offset;
+       u32 logdump_sz;
+};
+
+static struct vts_dump_info gdump_info;
+
+static void vts_audiodump_flush_work_func(struct work_struct *work)
+{
+       struct device *dev = gdump_info.dev;
+       struct vts_dump_info *dump_info = &gdump_info;
+       struct vts_data *data = dev_get_drvdata(dev);
+       char filename[SZ_64];
+       struct file *filp;
+
+       dev_dbg(dev, "%s: SRAM Offset: 0x%x Size: %d\n", __func__,
+               dump_info->audioaddr_offset, dump_info->audiodump_sz);
+
+       mutex_lock(&gdump_info.audiolock);
+       sprintf(filename, "/data/vts_audiodump.raw");
+
+       filp = filp_open(filename, O_RDWR | O_APPEND |
+                               O_CREAT, S_IRUSR | S_IWUSR);
+       dev_info(dev, "AudioDump appended mode\n");
+       if (!IS_ERR_OR_NULL(filp)) {
+               void *area = (void *)(data->sram_base +
+                                       dump_info->audioaddr_offset);
+               size_t bytes = (size_t)dump_info->audiodump_sz;
+
+               /* dev_dbg(dev, " %p, %zx\n", area, bytes); */
+               kernel_write(filp, area, bytes, filp->f_pos);
+               dev_dbg(dev, "kernel_write %p, %zx\n", area, bytes);
+
+               vfs_fsync(filp, 1);
+               filp_close(filp, NULL);
+       } else {
+               dev_err(dev, "VTS Audiodump file [%s] open error: %ld\n",
+                       filename, PTR_ERR(filp));
+       }
+
+       vts_send_ipc_ack(data, 1);
+       mutex_unlock(&gdump_info.audiolock);
+}
+
+static void vts_logdump_flush_work_func(struct work_struct *work)
+{
+       struct device *dev = gdump_info.dev;
+       struct vts_dump_info *dump_info = &gdump_info;
+       struct vts_data *data = dev_get_drvdata(dev);
+       char filename[SZ_64];
+       struct file *filp;
+
+       dev_dbg(dev, "%s: SRAM Offset: 0x%x Size: %d\n", __func__,
+               dump_info->logaddr_offset, dump_info->logdump_sz);
+
+       mutex_lock(&gdump_info.loglock);
+       sprintf(filename, "/data/vts_logdump.txt");
+
+       filp = filp_open(filename, O_RDWR | O_APPEND |
+                               O_CREAT, S_IRUSR | S_IWUSR);
+       dev_info(dev, "LogDump appended mode\n");
+       if (!IS_ERR_OR_NULL(filp)) {
+               void *area = (void *)(data->sram_base +
+                                       dump_info->logaddr_offset);
+               size_t bytes = (size_t)dump_info->logdump_sz;
+
+               /* dev_dbg(dev, " %p, %zx\n", area, bytes); */
+               kernel_write(filp, area, bytes, filp->f_pos);
+               dev_dbg(dev, "kernel_write %p, %zx\n", area, bytes);
+
+               vfs_fsync(filp, 1);
+               filp_close(filp, NULL);
+       } else {
+               dev_err(dev, "VTS Logdump file [%s] open error: %ld\n",
+                       filename, PTR_ERR(filp));
+       }
+
+       vts_send_ipc_ack(data, 1);
+       mutex_unlock(&gdump_info.loglock);
+}
+
+static DECLARE_WORK(vts_audiodump_work, vts_audiodump_flush_work_func);
+static DECLARE_WORK(vts_logdump_work, vts_logdump_flush_work_func);
+void vts_audiodump_schedule_flush(struct device *dev)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+
+       if (gdump_info.audioaddr_offset) {
+               schedule_work(&vts_audiodump_work);
+               dev_dbg(dev, "%s: AudioDump Scheduled\n", __func__);
+       } else {
+               dev_warn(dev, "%s: AudioDump address not registered\n",
+                               __func__);
+               /* send ipc ack to unblock vts firmware */
+               vts_send_ipc_ack(data, 1);
+       }
+}
+EXPORT_SYMBOL(vts_audiodump_schedule_flush);
+
+void vts_logdump_schedule_flush(struct device *dev)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+
+       if (gdump_info.logaddr_offset) {
+               schedule_work(&vts_logdump_work);
+               dev_dbg(dev, "%s: LogDump Scheduled\n", __func__);
+       } else {
+               dev_warn(dev, "%s: LogDump address not registered\n",
+                               __func__);
+               /* send ipc ack to unblock vts firmware */
+               vts_send_ipc_ack(data, 1);
+       }
+}
+EXPORT_SYMBOL(vts_logdump_schedule_flush);
+
+void vts_dump_addr_register(
+       struct device *dev,
+       u32 addroffset,
+       u32 dumpsz,
+       u32 dump_mode)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+
+       gdump_info.dev = dev;
+       if ((addroffset + dumpsz) > data->sram_size) {
+               dev_warn(dev, "%s: wrong offset[0x%x] or size[0x%x]\n",
+                               __func__, addroffset, dumpsz);
+               return;
+       }
+
+       if (dump_mode == VTS_AUDIO_DUMP) {
+               gdump_info.audioaddr_offset = addroffset;
+               gdump_info.audiodump_sz = dumpsz;
+       } else if (dump_mode == VTS_LOG_DUMP) {
+               gdump_info.logaddr_offset = addroffset;
+               gdump_info.logdump_sz = dumpsz;
+       } else
+               dev_warn(dev, "%s: Unknown dump mode\n", __func__);
+
+       dev_info(dev, "%s: %sDump offset[0x%x] size [%d]Scheduled\n",
+                       __func__, (dump_mode ? "Log" : "Audio"),
+                       addroffset, dumpsz);
+}
+EXPORT_SYMBOL(vts_dump_addr_register);
+
+static int __init samsung_vts_dump_late_initcall(void)
+{
+       pr_info("%s\n", __func__);
+       mutex_init(&gdump_info.audiolock);
+       mutex_init(&gdump_info.loglock);
+       gdump_info.audioaddr_offset = 0;
+       gdump_info.audiodump_sz = 0;
+       gdump_info.logaddr_offset = 0;
+       gdump_info.logdump_sz = 0;
+
+       return 0;
+}
+late_initcall(samsung_vts_dump_late_initcall);
diff --git a/sound/soc/samsung/vts/vts_dump.h b/sound/soc/samsung/vts/vts_dump.h
new file mode 100644 (file)
index 0000000..ba9367c
--- /dev/null
@@ -0,0 +1,40 @@
+/* sound/soc/samsung/vts/vts_dump.h
+ *
+ * ALSA SoC - Samsung vts dump driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_VTS_DUMP_H
+#define __SND_SOC_VTS_DUMP_H
+
+#include <linux/device.h>
+#include "vts.h"
+
+#define VTS_ADUIODUMP_AFTER_MINS       2 // VTS will dump 4.4 sec data after every 2 minutes
+#define VTS_LOGDUMP_AFTER_MINS         1 // VTS will dump available log after every 1 minute
+
+enum vts_dump_mode {
+       VTS_AUDIO_DUMP = 0,
+       VTS_LOG_DUMP = 1,
+};
+
+/**
+ * Schedule pcm dump from sram memory to file
+ * @param[in]  dev             pointer to vts device
+ * @param[in]  addroffset      SRAM offset for PCM dta
+ * @param[in]  size            size of pcm data
+ */
+extern void vts_audiodump_schedule_flush(struct device *dev);
+extern void vts_logdump_schedule_flush(struct device *dev);
+extern void vts_dump_addr_register(
+       struct device *dev,
+       u32 addroffset,
+       u32 dumpsz,
+       u32 dump_mode);
+
+#endif /* __SND_SOC_VTS_DUMP_H */
diff --git a/sound/soc/samsung/vts/vts_log.c b/sound/soc/samsung/vts/vts_log.c
new file mode 100644 (file)
index 0000000..25c4a2f
--- /dev/null
@@ -0,0 +1,302 @@
+/* sound/soc/samsung/vts/vts_log.c
+ *
+ * ALSA SoC - Samsung VTS Log driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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.
+ */
+//#define DEBUG
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/vmalloc.h>
+#include <sound/soc.h>
+
+#include "vts.h"
+#include "vts_log.h"
+
+#define VTS_LOG_BUFFER_SIZE (SZ_1M)
+#define S_IRWUG (S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP)
+
+struct vts_kernel_log_buffer {
+       char *buffer;
+       unsigned int index;
+       bool wrap;
+       bool updated;
+       wait_queue_head_t wq;
+};
+
+struct vts_log_buffer_info {
+       struct device *dev;
+       struct mutex lock;
+       struct vts_log_buffer log_buffer;
+       struct vts_kernel_log_buffer kernel_buffer;
+       bool registered;
+       u32 logbuf_index;
+};
+
+static struct dentry *vts_dbg_root_dir __read_mostly;
+static struct vts_log_buffer_info glogbuf_info;
+
+struct dentry *vts_dbg_get_root_dir(void)
+{
+       pr_debug("%s\n", __func__);
+
+       if (vts_dbg_root_dir == NULL)
+               vts_dbg_root_dir = debugfs_create_dir("vts", NULL);
+
+       return vts_dbg_root_dir;
+}
+
+static ssize_t vts_log_file_index;
+
+static int vts_log_file_open(struct inode *inode, struct  file *file)
+{
+       struct vts_log_buffer_info *info = inode->i_private;
+       struct vts_data *data = dev_get_drvdata(info->dev);
+       u32 values[3] = {0, 0, 0};
+       int result = 0;
+
+       dev_dbg(info->dev, "%s\n", __func__);
+
+       file->private_data = inode->i_private;
+       vts_log_file_index = -1;
+
+       if (data->vts_ready) {
+               values[0] = VTS_ENABLE_DEBUGLOG;
+               values[1] = 0;
+               values[2] = 0;
+               result = vts_start_ipc_transaction(info->dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 1);
+               if (result < 0)
+                       dev_err(info->dev, "%s Enable_debuglog ipc transaction failed\n", __func__);
+       }
+
+       data->vtslog_enabled = 1;
+       return 0;
+}
+
+static int vts_log_file_close(struct inode *inode, struct  file *file)
+{
+       struct vts_log_buffer_info *info = inode->i_private;
+       struct vts_data *data = dev_get_drvdata(info->dev);
+       u32 values[3] = {0, 0, 0};
+       int result = 0;
+
+       dev_dbg(info->dev, "%s\n", __func__);
+
+       vts_log_file_index = -1;
+
+       if (data->vts_ready) {
+               values[0] = VTS_DISABLE_DEBUGLOG;
+               values[1] = 0;
+               values[2] = 0;
+               result = vts_start_ipc_transaction(info->dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 1);
+               if (result < 0)
+                       dev_err(info->dev, "%s Disable_debuglog ipc transaction failed\n", __func__);
+               /* reset VTS SRAM debug log buffer */
+               vts_register_log_buffer(info->dev, 0, 0);
+       }
+
+       data->vtslog_enabled = 0;
+       return 0;
+}
+
+static ssize_t vts_log_file_read(
+       struct file *file,
+       char __user *buf,
+       size_t count,
+       loff_t *ppos)
+{
+       struct vts_log_buffer_info *info = file->private_data;
+       struct vts_kernel_log_buffer *kernel_buffer = &info->kernel_buffer;
+       size_t end, size;
+       bool first = (vts_log_file_index < 0);
+       int result;
+
+       dev_dbg(info->dev, "%s(%zu, %lld)\n", __func__, count, *ppos);
+
+       mutex_lock(&info->lock);
+
+       if (vts_log_file_index < 0)
+               vts_log_file_index = likely(kernel_buffer->wrap) ?
+                                               kernel_buffer->index : 0;
+
+       do {
+               end = ((vts_log_file_index < kernel_buffer->index) ||
+                       ((vts_log_file_index == kernel_buffer->index)
+                       && !first)) ? kernel_buffer->index
+                       : VTS_LOG_BUFFER_SIZE;
+               size = min(end - vts_log_file_index, count);
+               if (size == 0) {
+                       mutex_unlock(&info->lock);
+                       if (file->f_flags & O_NONBLOCK) {
+                               dev_dbg(info->dev, "non block\n");
+                               return -EAGAIN;
+                       }
+                       kernel_buffer->updated = false;
+
+                       result = wait_event_interruptible(kernel_buffer->wq,
+                                       kernel_buffer->updated);
+
+                       if (result != 0) {
+                               dev_dbg(info->dev, "interrupted\n");
+                               return result;
+                       }
+                       mutex_lock(&info->lock);
+               }
+#ifdef VERBOSE_LOG
+               dev_dbg(info->dev, "loop %zu, %zu, %zd, %zu\n", size, end, vts_log_file_index, count);
+#endif
+       } while (size == 0);
+
+       dev_dbg(info->dev, "start=%zd, end=%zd size=%zd\n", vts_log_file_index, end, size);
+       if (copy_to_user(buf, kernel_buffer->buffer + vts_log_file_index, size))
+               return -EFAULT;
+
+       vts_log_file_index += size;
+       if (vts_log_file_index >= VTS_LOG_BUFFER_SIZE)
+               vts_log_file_index = 0;
+
+       mutex_unlock(&info->lock);
+
+       dev_dbg(info->dev, "%s: size = %zd\n", __func__, size);
+
+       return size;
+}
+
+static unsigned int vts_log_file_poll(struct file *file, poll_table *wait)
+{
+       struct vts_log_buffer_info *info = file->private_data;
+       struct vts_kernel_log_buffer *kernel_buffer = &info->kernel_buffer;
+
+       dev_dbg(info->dev, "%s\n", __func__);
+
+       poll_wait(file, &kernel_buffer->wq, wait);
+       return POLLIN | POLLRDNORM;
+}
+
+static const struct file_operations vts_log_fops = {
+       .open = vts_log_file_open,
+       .release = vts_log_file_close,
+       .read = vts_log_file_read,
+       .poll = vts_log_file_poll,
+       .llseek = generic_file_llseek,
+       .owner = THIS_MODULE,
+};
+
+static void vts_log_memcpy(struct device *dev,
+        char *src, size_t size)
+{
+       struct vts_kernel_log_buffer *kernel_buffer = NULL;
+       size_t left_size = 0;
+
+       kernel_buffer = &glogbuf_info.kernel_buffer;
+       left_size = VTS_LOG_BUFFER_SIZE - kernel_buffer->index;
+
+       dev_dbg(dev, "%s(%zu)\n", __func__, size);
+
+       if (left_size < size) {
+#ifdef VERBOSE_LOG
+               dev_dbg(dev, "0: %s\n", src);
+#endif
+               memcpy_fromio(kernel_buffer->buffer +
+                       kernel_buffer->index, src, left_size);
+               src += left_size;
+               size -= left_size;
+               kernel_buffer->index = 0;
+               kernel_buffer->wrap = true;
+       }
+#ifdef VERBOSE_LOG
+       dev_dbg(dev, "1: %s\n", src);
+#endif
+       memcpy_fromio(kernel_buffer->buffer + kernel_buffer->index, src, size);
+       kernel_buffer->index += size;
+}
+
+static void vts_log_flush_work_func(struct work_struct *work)
+{
+       struct device *dev = glogbuf_info.dev;
+       struct vts_log_buffer *log_buffer = &glogbuf_info.log_buffer;
+       struct vts_kernel_log_buffer *kernel_buffer = NULL;
+       int logbuf_index = glogbuf_info.logbuf_index;
+
+       kernel_buffer = &glogbuf_info.kernel_buffer;
+
+       dev_dbg(dev, "%s: LogBuffer Index: %d\n", __func__,
+               logbuf_index);
+
+       mutex_lock(&glogbuf_info.lock);
+
+       vts_log_memcpy(dev, (log_buffer->addr +
+                       log_buffer->size * logbuf_index), log_buffer->size);
+       /* memory barrier */
+       wmb();
+       mutex_unlock(&glogbuf_info.lock);
+
+       kernel_buffer->updated = true;
+       wake_up_interruptible(&kernel_buffer->wq);
+}
+
+static DECLARE_WORK(vts_log_work, vts_log_flush_work_func);
+void vts_log_schedule_flush(struct device *dev, u32 index)
+{
+       if (glogbuf_info.registered &&
+               glogbuf_info.log_buffer.size) {
+               glogbuf_info.logbuf_index = index;
+               schedule_work(&vts_log_work);
+               dev_dbg(dev, "%s: VTS Log Buffer[%d] Scheduled\n",
+                        __func__, index);
+       } else
+               dev_warn(dev, "%s: VTS Debugging buffer not registered\n",
+                                __func__);
+}
+EXPORT_SYMBOL(vts_log_schedule_flush);
+
+int vts_register_log_buffer(
+       struct device *dev,
+       u32 addroffset,
+       u32 logsz)
+{
+       struct vts_data *data = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s(offset 0x%x)\n", __func__, addroffset);
+
+       if ((addroffset + logsz) > data->sram_size) {
+               dev_warn(dev, "%s: wrong offset[0x%x] or size[0x%x]\n",
+                               __func__, addroffset, logsz);
+               return -EINVAL;
+       }
+
+       if (!glogbuf_info.registered) {
+               glogbuf_info.dev = dev;
+               mutex_init(&glogbuf_info.lock);
+               glogbuf_info.kernel_buffer.buffer =
+                               vzalloc(VTS_LOG_BUFFER_SIZE);
+               glogbuf_info.kernel_buffer.index = 0;
+               glogbuf_info.kernel_buffer.wrap = false;
+               init_waitqueue_head(&glogbuf_info.kernel_buffer.wq);
+
+               debugfs_create_file("vts-log", S_IRWUG,
+                               vts_dbg_get_root_dir(),
+                               &glogbuf_info, &vts_log_fops);
+               glogbuf_info.registered = true;
+       }
+
+       /* Update logging buffer address and size info */
+       glogbuf_info.log_buffer.addr = data->sram_base + addroffset;
+       glogbuf_info.log_buffer.size = logsz;
+
+       return 0;
+}
+EXPORT_SYMBOL(vts_register_log_buffer);
+
+static int __init samsung_vts_log_late_initcall(void)
+{
+       pr_info("%s\n", __func__);
+
+       return 0;
+}
+late_initcall(samsung_vts_log_late_initcall);
diff --git a/sound/soc/samsung/vts/vts_log.h b/sound/soc/samsung/vts/vts_log.h
new file mode 100644 (file)
index 0000000..b5a9b8b
--- /dev/null
@@ -0,0 +1,36 @@
+/* sound/soc/samsung/vts/vts_log.h
+ *
+ * ALSA SoC - Samsung vts Log driver
+ *
+ * Copyright (c) 2017 Samsung Electronics Co. Ltd.
+ *
+ * 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 __SND_SOC_VTS_LOG_H
+#define __SND_SOC_VTS_LOG_H
+
+#include <linux/device.h>
+#include "vts.h"
+
+/**
+ * Schedule log flush sram memory to kernel memory
+ * @param[in]  dev             pointer to vts device
+ */
+extern void vts_log_schedule_flush(struct device *dev, u32 index);
+
+/**
+ * Register abox log buffer
+ * @param[in]  dev             pointer to abox device
+ * @param[in]  addroffset      Sram log buffer offset
+ * @param[in]  logsz           log buffer size
+ * @return     error code if any
+ */
+extern int vts_register_log_buffer(
+       struct device *dev,
+       u32 addroffset,
+       u32 logsz);
+
+#endif /* __SND_SOC_VTS_LOG_H */
index 2cb8d3b55fbc210cd76d110df6d9fb11c976cb50..30fcbbcaf43a79c910fd178af7dcf58b8f9449e1 100644 (file)
@@ -155,7 +155,9 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
        fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
 
+       mutex_lock_nested(&fe->pcm_mutex, fe->pcm_subclass);
        snd_soc_runtime_activate(fe, stream);
+       mutex_unlock(&fe->pcm_mutex);
 
        mutex_unlock(&fe->card->mutex);
 
@@ -278,7 +280,9 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
        else
                stream = SNDRV_PCM_STREAM_CAPTURE;
 
+       mutex_lock_nested(&fe->pcm_mutex, fe->pcm_subclass);
        snd_soc_runtime_deactivate(fe, stream);
+       mutex_unlock(&fe->pcm_mutex);
 
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
 
@@ -354,6 +358,7 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
        struct snd_soc_pcm_runtime *fe = cstream->private_data;
        struct snd_soc_platform *platform = fe->platform;
        struct snd_soc_dai *cpu_dai = fe->cpu_dai;
+       enum snd_soc_dpcm_trigger trigger;
        int ret = 0, stream;
 
        if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN ||
@@ -370,6 +375,37 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
        else
                stream = SNDRV_PCM_STREAM_CAPTURE;
 
+       trigger = fe->dai_link->trigger[stream];
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               switch (trigger) {
+               case SND_SOC_DPCM_TRIGGER_PRE_POST:
+                       trigger = SND_SOC_DPCM_TRIGGER_PRE;
+                       break;
+               case SND_SOC_DPCM_TRIGGER_POST_PRE:
+                       trigger = SND_SOC_DPCM_TRIGGER_POST;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               switch (trigger) {
+               case SND_SOC_DPCM_TRIGGER_PRE_POST:
+                       trigger = SND_SOC_DPCM_TRIGGER_POST;
+                       break;
+               case SND_SOC_DPCM_TRIGGER_POST_PRE:
+                       trigger = SND_SOC_DPCM_TRIGGER_PRE;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       }
 
        mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
 
@@ -379,10 +415,12 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
                        goto out;
        }
 
-       if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
-               ret = platform->driver->compr_ops->trigger(cstream, cmd);
-               if (ret < 0)
-                       goto out;
+       if (trigger != SND_SOC_DPCM_TRIGGER_POST) {
+               if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
+                       ret = platform->driver->compr_ops->trigger(cstream, cmd);
+                       if (ret < 0)
+                               goto out;
+                       }
        }
 
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
@@ -404,6 +442,13 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
                break;
        }
 
+       if (trigger == SND_SOC_DPCM_TRIGGER_POST) {
+               if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
+                       ret = platform->driver->compr_ops->trigger(cstream, cmd);
+                       if (ret < 0)
+                               goto out;
+               }
+       }
 out:
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
        mutex_unlock(&fe->card->mutex);
@@ -507,6 +552,13 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
        memset(&fe->dpcm[fe_substream->stream].hw_params, 0,
                sizeof(struct snd_pcm_hw_params));
 
+       if (platform->driver->compr_ops && platform->driver->compr_ops->get_hw_params) {
+               ret = platform->driver->compr_ops->get_hw_params(cstream,
+                               &fe->dpcm[fe_substream->stream].hw_params);
+               if (ret < 0)
+                       goto out;
+       }
+
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
 
        ret = dpcm_be_dai_hw_params(fe, stream);
index 320d262c16c984c887a21a905f54a1f1359ee3e6..143879f6c2cd7fa5b3e8b6dd0ca95be01eca453c 100644 (file)
@@ -1240,6 +1240,18 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
                        is_connected_input_ep, custom_stop_condition);
 }
 
+int snd_soc_dapm_connected_output_ep(struct snd_soc_dapm_widget *widget,
+       struct list_head *list)
+{
+       return is_connected_output_ep(widget, list, NULL);
+}
+
+int snd_soc_dapm_connected_input_ep(struct snd_soc_dapm_widget *widget,
+       struct list_head *list)
+{
+       return is_connected_input_ep(widget, list, NULL);
+}
+
 /**
  * snd_soc_dapm_get_connected_widgets - query audio path and it's widgets.
  * @dai: the soc DAI.
index 3a9c875534c1ff8327088430ed395f03879bd672..2dc0ead36795c92f3b2f80a3dc04dc3bc72cc6b1 100644 (file)
@@ -461,15 +461,14 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        const char *codec_dai_name = "multicodec";
        int i, ret = 0;
 
-       pinctrl_pm_select_default_state(cpu_dai->dev);
-       for (i = 0; i < rtd->num_codecs; i++)
-               pinctrl_pm_select_default_state(rtd->codec_dais[i]->dev);
-
        for_each_rtdcom(rtd, rtdcom) {
                component = rtdcom->component;
 
                pm_runtime_get_sync(component->dev);
        }
+       pinctrl_pm_select_default_state(cpu_dai->dev);
+       for (i = 0; i < rtd->num_codecs; i++)
+               pinctrl_pm_select_default_state(rtd->codec_dais[i]->dev);
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
@@ -607,19 +606,18 @@ platform_err:
 out:
        mutex_unlock(&rtd->pcm_mutex);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
-               pm_runtime_mark_last_busy(component->dev);
-               pm_runtime_put_autosuspend(component->dev);
-       }
-
        for (i = 0; i < rtd->num_codecs; i++) {
                if (!rtd->codec_dais[i]->active)
                        pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
        }
        if (!cpu_dai->active)
                pinctrl_pm_select_sleep_state(cpu_dai->dev);
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               pm_runtime_mark_last_busy(component->dev);
+               pm_runtime_put_autosuspend(component->dev);
+       }
 
        return ret;
 }
@@ -719,19 +717,18 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
 
        mutex_unlock(&rtd->pcm_mutex);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
-               pm_runtime_mark_last_busy(component->dev);
-               pm_runtime_put_autosuspend(component->dev);
-       }
-
        for (i = 0; i < rtd->num_codecs; i++) {
                if (!rtd->codec_dais[i]->active)
                        pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
        }
        if (!cpu_dai->active)
                pinctrl_pm_select_sleep_state(cpu_dai->dev);
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               pm_runtime_mark_last_busy(component->dev);
+               pm_runtime_put_autosuspend(component->dev);
+       }
 
        return 0;
 }
@@ -912,6 +909,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                codec_dai->channels = params_channels(&codec_params);
                codec_dai->sample_bits = snd_pcm_format_physical_width(
                                                params_format(&codec_params));
+               codec_dai->sample_width = params_width(&codec_params);
        }
 
        ret = soc_dai_hw_params(substream, params, cpu_dai);
@@ -932,6 +930,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        cpu_dai->channels = params_channels(params);
        cpu_dai->sample_bits =
                snd_pcm_format_physical_width(params_format(params));
+       cpu_dai->sample_width = params_width(params);
 
 out:
        mutex_unlock(&rtd->pcm_mutex);
@@ -978,6 +977,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
                cpu_dai->rate = 0;
                cpu_dai->channels = 0;
                cpu_dai->sample_bits = 0;
+               cpu_dai->sample_width = 0;
        }
 
        for (i = 0; i < rtd->num_codecs; i++) {
@@ -986,6 +986,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
                        codec_dai->rate = 0;
                        codec_dai->channels = 0;
                        codec_dai->sample_bits = 0;
+                       codec_dai->sample_width = 0;
                }
        }
 
@@ -1149,9 +1150,11 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
                        stream ? "<-" : "->", be->dai_link->name);
 
 #ifdef CONFIG_DEBUG_FS
+#ifndef CONFIG_SND_SOC_SAMSUNG_ABOX
        if (fe->debugfs_dpcm_root)
                dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644,
                                fe->debugfs_dpcm_root, &dpcm->state);
+#endif
 #endif
        return 1;
 }
@@ -1207,7 +1210,9 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
                dpcm_be_reparent(fe, dpcm->be, stream);
 
 #ifdef CONFIG_DEBUG_FS
+#ifndef CONFIG_SND_SOC_SAMSUNG_ABOX
                debugfs_remove(dpcm->debugfs_state);
+#endif
 #endif
                list_del(&dpcm->list_be);
                list_del(&dpcm->list_fe);
@@ -1923,7 +1928,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
                /* perform any hw_params fixups */
                if (be->dai_link->be_hw_params_fixup) {
                        ret = be->dai_link->be_hw_params_fixup(be,
-                                       &dpcm->hw_params);
+                                       &dpcm->hw_params, stream);
                        if (ret < 0) {
                                dev_err(be->dev,
                                        "ASoC: hw_params BE fixup failed %d\n",
@@ -2168,6 +2173,37 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
 
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
 
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               switch (trigger) {
+               case SND_SOC_DPCM_TRIGGER_PRE_POST:
+                       trigger = SND_SOC_DPCM_TRIGGER_PRE;
+                       break;
+               case SND_SOC_DPCM_TRIGGER_POST_PRE:
+                       trigger = SND_SOC_DPCM_TRIGGER_POST;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               switch (trigger) {
+               case SND_SOC_DPCM_TRIGGER_PRE_POST:
+                       trigger = SND_SOC_DPCM_TRIGGER_POST;
+                       break;
+               case SND_SOC_DPCM_TRIGGER_POST_PRE:
+                       trigger = SND_SOC_DPCM_TRIGGER_PRE;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       }
+
        switch (trigger) {
        case SND_SOC_DPCM_TRIGGER_PRE:
                switch (cmd) {