ASoC: SOC: Add support jh7110 audio driver
authorcurry.zhang <curry.zhang@starfivetech.com>
Thu, 16 Dec 2021 06:14:04 +0000 (22:14 -0800)
committerJaehoon Chung <jh80.chung@samsung.com>
Mon, 24 Jul 2023 23:25:02 +0000 (08:25 +0900)
Add support audio driver and device tree of starfive JH7110. This
driver is from [1], and the imported patch list[2]. We modified
compatible and clock in the device-tree to fix kernel v6.1. Also fix
the build error for incompatible pointer types and others. And add
set 'legacy_dai_naming' value into starfive_i2s driver, for set to use
legacy dai naming when looking up DAI.

[1] https://github.com/starfive-tech/linux

[2] patch list from v5.15.y
  258954bfe609 Xingyu Wu sound: starfive: I2S: Fixed error after hibernation when building module
  09407b6be383 Xingyu Wu sound: starfive: pwmdac: Support module building
  00a1eaebe170 Xingyu Wu sound: starfive: pwmdac: Fixed playback failed after hibernation
  1f6659ddf77c Xingyu Wu sound:starfive:Move playback and capture driver as slave to starfive I2S
  c1ec8ddaa121 Xingyu Wu sound:starfive:pwmdac:Add runtime pm operation
  78f5ba02befa Xingyu Wu sound:starfive:Add hibernation in I2S
  40cbb24940b3 Walker Chen CR_2307_PWMDAC_515: fix playback repeatly issue
  099a9cd86939 Xingyu Wu sound:starfive:Remove i2srx-master and merge into starfive_i2s
  c813586d1913 Xingyu Wu  sound:starfive:Add I2S driver
  abd5f99857c5 Walker Chen CR_2071_audio_compatible_standard_515
  14d6015d73ed Xingyu Wu sound:starfive:pwmdac:Modify compatible name
  8fba0f28d470 Walker Chen CR_1827_cannot_record_play_simultaneously
  ae6c3943ef0c minda.chen  pwmdac:sound: support 11025hz play mode.
  279155cf4805 curry.zhang [Audio: PWMDAC] enable pwmdac driver on 7110 evb board
  7868835794ac curry.zhang [Audio: PWMDAC] Adjust code style
  1e872b48a7a7 curry.zhang [Audio: PWMDAC] Add standard system clock tree api
  e0f5d0916020 “jenny.zhang” [alsa] Add jh7110 audio module driver code

Signed-off-by: jenny.zhang <jenny.zhang@starfivetech.com>
Signed-off-by: curry.zhang <curry.zhang@starfivetech.com>
Signed-off-by: minda.chen <minda.chen@starfivetech.com>
Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
Signed-off-by: PeterYang <linsheng.yang@seeed.cc>
Signed-off-by: Baozhu Zuo <zuobaozhu@gmail.com>
Signed-off-by: Rajeev Kumar <rajeevkumar.linux@gmail.com>
Signed-off-by: michael.yan <michael.yan@starfivetech.com>
Change-Id: If1b6c957bbe5ea9f3464e591138da4cf57c6fd84
Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
30 files changed:
arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
arch/riscv/boot/dts/starfive/jh7110.dtsi
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
include/linux/dma/starfive-dma.h [new file with mode: 0755]
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ac101.c [new file with mode: 0644]
sound/soc/codecs/ac101_regs.h [new file with mode: 0644]
sound/soc/codecs/ac108.c [new file with mode: 0644]
sound/soc/codecs/ac108.h [new file with mode: 0644]
sound/soc/codecs/ac10x.h [new file with mode: 0644]
sound/soc/codecs/wm8960.c
sound/soc/dwc/dwc-i2s.c
sound/soc/starfive/Kconfig [new file with mode: 0644]
sound/soc/starfive/Makefile [new file with mode: 0644]
sound/soc/starfive/pwmdac.h [new file with mode: 0644]
sound/soc/starfive/starfive_i2s.c [new file with mode: 0644]
sound/soc/starfive/starfive_i2s.h [new file with mode: 0644]
sound/soc/starfive/starfive_pdm.c [new file with mode: 0644]
sound/soc/starfive/starfive_pdm.h [new file with mode: 0644]
sound/soc/starfive/starfive_pwmdac.c [new file with mode: 0644]
sound/soc/starfive/starfive_pwmdac_pcm.c [new file with mode: 0644]
sound/soc/starfive/starfive_pwmdac_transmitter.c [new file with mode: 0755]
sound/soc/starfive/starfive_spdif.c [new file with mode: 0644]
sound/soc/starfive/starfive_spdif.h [new file with mode: 0644]
sound/soc/starfive/starfive_spdif_pcm.c [new file with mode: 0644]
sound/soc/starfive/starfive_tdm.c [new file with mode: 0644]
sound/soc/starfive/starfive_tdm.h [new file with mode: 0644]

index 9937b08..dca93b4 100644 (file)
                        slew-rate = <0>;
                };
        };
+
+       mclk_ext_pins: mclk-ext-pins {
+               mclk-ext-pins {
+                       pinmux = <GPIOMUX(4, GPOUT_HIGH,
+                                            GPOEN_ENABLE,
+                                            GPI_SYS_MCLK_EXT)>;
+               };
+       };
+
+       pwmdac0_pins: pwmdac0-pins {
+               pwmdac0-pins-left {
+                       pinmux = <GPIOMUX(33, GPOUT_SYS_PWMDAC_LEFT,
+                                            GPOEN_ENABLE,
+                                            GPI_NONE)>;
+               };
+
+               pwmdac0-pins-right {
+                       pinmux = <GPIOMUX(34, GPOUT_SYS_PWMDAC_RIGHT,
+                                            GPOEN_ENABLE,
+                                            GPI_NONE)>;
+               };
+       };
 };
 
 &uart0 {
 &U74_4 {
        cpu-supply = <&vdd_cpu>;
 };
+
+&i2stx_4ch0 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&mclk_ext_pins>;
+       status = "okay";
+};
+
+&sound3 {
+       simple-audio-card,dai-link@0 {
+               reg = <0>;
+               format = "left_j";
+               bitclock-master = <&sndcpu0>;
+               frame-master = <&sndcpu0>;
+               status = "okay";
+
+               sndcpu0: cpu {
+                       sound-dai = <&pwmdac>;
+               };
+
+               codec {
+                       sound-dai = <&pwmdac_codec>;
+               };
+       };
+};
+
+&pwmdac {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pwmdac0_pins>;
+       status = "okay";
+};
index 6779b55..b823ada 100644 (file)
                                interrupt-controller;
                        };
                };
+
+               pwmdac: pwmdac@100b0000 {
+                       compatible = "starfive,jh7110-pwmdac";
+                       reg = <0x0 0x100b0000 0x0 0x1000>;
+                       clocks = <&syscrg JH7110_SYSCLK_APB0>,
+                               <&syscrg JH7110_SYSCLK_PWMDAC_APB>,
+                               <&syscrg JH7110_SYSCLK_PWMDAC_CORE>;
+                       clock-names = "apb0", "pwmdac-apb", "pwmdac-core";
+                       resets = <&syscrg JH7110_SYSRST_PWMDAC_APB>;
+                       reset-names = "rst-apb";
+                       dmas = <&dma 22>;
+                       dma-names = "tx";
+                       #sound-dai-cells = <0>;
+                       status = "disabled";
+               };
+
+               i2stx: i2stx@100c0000 {
+                       compatible = "snps,designware-i2stx";
+                       reg = <0x0 0x100c0000 0x0 0x1000>;
+                       interrupt-names = "tx";
+                       #sound-dai-cells = <0>;
+                       dmas = <&dma 28>;
+                       dma-names = "rx";
+                       status = "disabled";
+               };
+
+               i2srx_mst: i2srx_mst@100e0000 {
+                       compatible = "starfive,jh7110-i2srx-master";
+                       reg = <0x0 0x100e0000 0x0 0x1000>;
+                       clocks = <&syscrg JH7110_SYSCLK_APB0>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_APB>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_LRCK_MST>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_BCLK>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_LRCK>,
+                                <&syscrg JH7110_SYSCLK_MCLK>,
+                                <&mclk_ext>;
+                       clock-names = "apb0", "i2srx_apb",
+                                     "i2srx_bclk_mst", "i2srx_lrck_mst",
+                                     "i2srx_bclk", "i2srx_lrck",
+                                     "mclk", "mclk_ext";
+                       resets = <&syscrg JH7110_SYSRST_I2SRX_APB>,
+                                <&syscrg JH7110_SYSRST_I2SRX_BCLK>;
+                       reset-names = "rst_apb_rx", "rst_bclk_rx";
+                       dmas = <&dma 24>;
+                       dma-names = "rx";
+                       starfive,sys-syscon = <&sys_syscon 0x18 0x34>;
+                       #sound-dai-cells = <0>;
+                       status = "disabled";
+               };
+
+               i2srx_3ch: i2srx-3ch@100e0000 {
+                       compatible = "starfive,jh7110-i2srx", "snps,designware-i2srx";
+                       reg = <0x0 0x100e0000 0x0 0x1000>;
+                       clocks = <&syscrg JH7110_SYSCLK_APB0>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_APB>,
+                                <&syscrg JH7110_SYSCLK_AUDIO_ROOT>,
+                                <&syscrg JH7110_SYSCLK_MCLK_INNER>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_LRCK_MST>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_BCLK>,
+                                <&syscrg JH7110_SYSCLK_I2SRX_LRCK>,
+                                <&syscrg JH7110_SYSCLK_MCLK>,
+                                <&mclk_ext>,
+                                <&i2srx_bclk_ext>,
+                                <&i2srx_lrck_ext>;
+                       clock-names = "apb0", "3ch-apb",
+                                     "audioroot", "mclk-inner",
+                                     "bclk_mst", "3ch-lrck",
+                                     "rx-bclk", "rx-lrck",
+                                     "mclk", "mclk_ext",
+                                     "bclk-ext", "lrck-ext";
+                       resets = <&syscrg JH7110_SYSRST_I2SRX_APB>,
+                                <&syscrg JH7110_SYSRST_I2SRX_BCLK>;
+                       dmas = <&dma 24>;
+                       dma-names = "rx";
+                       starfive,sys-syscon = <&sys_syscon 0x18 0x34>;
+                       #sound-dai-cells = <0>;
+                       status = "disabled";
+               };
+
+               i2stx_4ch0: i2stx-4ch0@120b0000 {
+                       compatible = "starfive,jh7110-i2stx-4ch0", "snps,designware-i2stx-4ch0";
+                       reg = <0x0 0x120b0000 0x0 0x1000>;
+                       clocks = <&syscrg JH7110_SYSCLK_MCLK_INNER>,
+                                <&syscrg JH7110_SYSCLK_I2STX0_BCLK_MST>,
+                                <&syscrg JH7110_SYSCLK_I2STX0_LRCK_MST>,
+                                <&syscrg JH7110_SYSCLK_MCLK>,
+                                <&syscrg JH7110_SYSCLK_I2STX0_BCLK>,
+                                <&syscrg JH7110_SYSCLK_I2STX0_LRCK>,
+                                <&syscrg JH7110_SYSCLK_I2STX0_APB>,
+                                <&mclk_ext>;
+                       clock-names = "inner", "bclk-mst",
+                                       "lrck-mst", "mclk",
+                                       "bclk0", "lrck0",
+                                       "i2s_apb", "mclk_ext";
+                       resets = <&syscrg JH7110_SYSRST_I2STX0_APB>,
+                                <&syscrg JH7110_SYSRST_I2STX0_BCLK>;
+                       reset-names = "rst_apb", "rst_bclk";
+                       dmas = <&dma 47>;
+                       dma-names = "tx";
+                       #sound-dai-cells = <0>;
+                       status = "disabled";
+               };
+
+               i2stx_4ch1: i2stx-4ch1@120c0000 {
+                       compatible = "starfive,jh7110-i2stx-4ch1", "snps,designware-i2stx-4ch1";
+                       reg = <0x0 0x120c0000 0x0 0x1000>;
+                       clocks = <&syscrg JH7110_SYSCLK_AUDIO_ROOT>,
+                                <&syscrg JH7110_SYSCLK_MCLK_INNER>,
+                                <&syscrg JH7110_SYSCLK_I2STX1_BCLK_MST>,
+                                <&syscrg JH7110_SYSCLK_I2STX1_LRCK_MST>,
+                                <&syscrg JH7110_SYSCLK_MCLK>,
+                                <&syscrg JH7110_SYSCLK_I2STX1_BCLK>,
+                                <&syscrg JH7110_SYSCLK_I2STX1_LRCK>,
+                                <&syscrg JH7110_SYSCLK_MCLK_OUT>,
+                                <&syscrg JH7110_SYSCLK_APB0>,
+                                <&syscrg JH7110_SYSCLK_I2STX1_APB>,
+                                <&mclk_ext>,
+                                <&i2stx_bclk_ext>,
+                                <&i2stx_lrck_ext>;
+                       clock-names = "audroot", "mclk_inner", "bclk_mst",
+                                     "lrck_mst", "mclk", "4chbclk",
+                                     "4chlrck", "mclk_out",
+                                     "apb0", "clk_apb",
+                                     "mclk_ext", "bclk_ext", "lrck_ext";
+                       resets = <&syscrg JH7110_SYSRST_I2STX1_APB>,
+                                <&syscrg JH7110_SYSRST_I2STX1_BCLK>;
+                       dmas = <&dma 48>;
+                       dma-names = "tx";
+                       #sound-dai-cells = <0>;
+                       status = "disabled";
+               };
+
+               ac108_mclk: ac108-mclk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <24576000>;
+               };
+
+               wm8960_mclk: wm8960-mclk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <4000000>;
+               };
+
+               pwmdac_codec: pwmdac-transmitter {
+                       compatible = "starfive,jh7110-pwmdac-dit";
+                       #sound-dai-cells = <0>;
+               };
+
+               dmic_codec: dmic-codec {
+                       compatible = "dmic-codec";
+                       #sound-dai-cells = <0>;
+                       status = "disabled";
+               };
+
+               sound0: snd-card0 {
+                       compatible = "simple-audio-card";
+                       simple-audio-card,name = "Starfive-AC108-Sound-Card";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+               };
+
+               sound3: snd-card3 {
+                       compatible = "simple-audio-card";
+                       simple-audio-card,name = "Starfive-PWMDAC-Sound-Card";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+               };
+
+               sound6: snd-card6 {
+                       compatible = "simple-audio-card";
+                       simple-audio-card,name = "Starfive-WM8960-Sound-Card";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+               };
        };
 };
index f077d3f..ea3fc29 100644 (file)
@@ -523,7 +523,7 @@ static void dw_axi_dma_set_hw_channel(struct axi_dma_chan *chan, bool set)
        unsigned long reg_value, val;
 
        if (!chip->apb_regs) {
-               dev_err(chip->dev, "apb_regs not initialized\n");
+               dev_dbg(chip->dev, "apb_regs not initialized\n");
                return;
        }
 
@@ -741,6 +741,10 @@ dw_axi_dma_chan_prep_cyclic(struct dma_chan *dchan, dma_addr_t dma_addr,
 
        num_segments = DIV_ROUND_UP(period_len, axi_block_len);
        segment_len = DIV_ROUND_UP(period_len, num_segments);
+       if (!IS_ALIGNED(segment_len, 4)) {
+               segment_len = ALIGN(segment_len, 4);
+               period_len = segment_len * num_segments;
+       }
 
        total_segments = num_periods * num_segments;
 
@@ -1058,7 +1062,8 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
 
        spin_lock_irqsave(&chan->vc.lock, flags);
        if (unlikely(axi_chan_is_hw_enable(chan))) {
-               dev_err(chan2dev(chan), "BUG: %s caught DWAXIDMAC_IRQ_DMA_TRF, but channel not idle!\n",
+               dev_err(chan2dev(chan),
+                       "BUG: %s caught DWAXIDMAC_IRQ_DMA_TRF, but channel not idle!\n",
                        axi_chan_name(chan));
                axi_chan_disable(chan);
        }
@@ -1272,6 +1277,21 @@ static int axi_dma_resume(struct axi_dma_chip *chip)
        return 0;
 }
 
+void axi_dma_cyclic_stop(struct dma_chan *dchan)
+{
+       struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+
+       unsigned long flags;
+
+       spin_lock_irqsave(&chan->vc.lock, flags);
+
+       axi_chan_disable(chan);
+       
+       spin_unlock_irqrestore(&chan->vc.lock, flags);
+
+}
+EXPORT_SYMBOL(axi_dma_cyclic_stop);
+
 static int __maybe_unused axi_dma_runtime_suspend(struct device *dev)
 {
        struct axi_dma_chip *chip = dev_get_drvdata(dev);
diff --git a/include/linux/dma/starfive-dma.h b/include/linux/dma/starfive-dma.h
new file mode 100755 (executable)
index 0000000..2bb7c46
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _STARFIVE_DMA_H_
+#define _STARFIVE_DMA_H_
+
+#include <linux/dmaengine.h>
+
+void axi_dma_cyclic_stop(struct dma_chan *chan);
+
+#endif /* _STARFIVE_DMA_H_ */
index 848fbae..40c0d9b 100644 (file)
@@ -100,6 +100,7 @@ source "sound/soc/uniphier/Kconfig"
 source "sound/soc/ux500/Kconfig"
 source "sound/soc/xilinx/Kconfig"
 source "sound/soc/xtensa/Kconfig"
+source "sound/soc/starfive/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
index 507eaed..1d85f61 100644 (file)
@@ -68,3 +68,4 @@ obj-$(CONFIG_SND_SOC) += uniphier/
 obj-$(CONFIG_SND_SOC)  += ux500/
 obj-$(CONFIG_SND_SOC)  += xilinx/
 obj-$(CONFIG_SND_SOC)  += xtensa/
+obj-$(CONFIG_SND_SOC)  += starfive/
\ No newline at end of file
index 965ae55..c77590e 100644 (file)
@@ -14,6 +14,7 @@ menu "CODEC drivers"
 config SND_SOC_ALL_CODECS
        tristate "Build all ASoC CODEC drivers"
        depends on COMPILE_TEST
+       imply SND_SOC_AC108
        imply SND_SOC_88PM860X
        imply SND_SOC_L3
        imply SND_SOC_AB8500_CODEC
@@ -325,6 +326,10 @@ config SND_SOC_ALL_CODECS
 
          If unsure select "N".
 
+config SND_SOC_AC108
+       tristate "AC108"
+       depends on I2C
+
 config SND_SOC_88PM860X
        tristate
        depends on MFD_88PM860X
index 71d3ce5..d1a1a2d 100644 (file)
@@ -2,6 +2,7 @@
 snd-soc-88pm860x-objs := 88pm860x-codec.o
 snd-soc-ab8500-codec-objs := ab8500-codec.o
 snd-soc-ac97-objs := ac97.o
+snd-soc-ac108-objs := ac108.o ac101.o
 snd-soc-ad1836-objs := ad1836.o
 snd-soc-ad193x-objs := ad193x.o
 snd-soc-ad193x-spi-objs := ad193x-spi.o
@@ -361,6 +362,7 @@ snd-soc-simple-mux-objs := simple-mux.o
 obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
 obj-$(CONFIG_SND_SOC_AB8500_CODEC)     += snd-soc-ab8500-codec.o
 obj-$(CONFIG_SND_SOC_AC97_CODEC)       += snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_AC108)            += snd-soc-ac108.o
 obj-$(CONFIG_SND_SOC_AD1836)   += snd-soc-ad1836.o
 obj-$(CONFIG_SND_SOC_AD193X)   += snd-soc-ad193x.o
 obj-$(CONFIG_SND_SOC_AD193X_SPI)       += snd-soc-ad193x-spi.o
diff --git a/sound/soc/codecs/ac101.c b/sound/soc/codecs/ac101.c
new file mode 100644 (file)
index 0000000..0a32950
--- /dev/null
@@ -0,0 +1,1693 @@
+/*
+ * ac101.c
+ *
+ * (C) Copyright 2017-2018
+ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
+ *
+ * PeterYang <linsheng.yang@seeed.cc>
+ *
+ * (C) Copyright 2014-2017
+ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
+ *
+ * huangxin <huangxin@Reuuimllatech.com>
+ * liushaohua <liushaohua@allwinnertech.com>
+ *
+ * X-Powers AC101 codec driver
+ *
+ * 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.
+ *
+ */
+
+/* #undef AC101_DEBG
+ * use 'make DEBUG=1' to enable debugging
+ */
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include "ac101_regs.h"
+#include "ac10x.h"
+
+/*
+ * *** To sync channels ***
+ *
+ * 1. disable clock in codec   hw_params()
+ * 2. clear   fifo  in bcm2835 hw_params()
+ * 3. clear   fifo  in bcm2385 prepare()
+ * 4. enable  RX    in bcm2835 trigger()
+ * 5. enable  clock in machine trigger()
+ */
+
+/*Default initialize configuration*/
+static bool speaker_double_used = 1;
+static int double_speaker_val  = 0x1B;
+static int single_speaker_val  = 0x19;
+static int headset_val         = 0x3B;
+static int mainmic_val         = 0x4;
+static int headsetmic_val      = 0x4;
+static bool dmic_used          = 0;
+static int adc_digital_val     = 0xb0b0;
+static bool drc_used           = false;
+
+#define AC101_RATES  (SNDRV_PCM_RATE_8000_96000 &              \
+               ~(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000 | \
+               SNDRV_PCM_RATE_88200))
+#define AC101_FORMATS (/*SNDRV_PCM_FMTBIT_S16_LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE |*/     \
+                       SNDRV_PCM_FMTBIT_S32_LE | \
+                       0)
+
+static struct ac10x_priv* static_ac10x;
+
+
+int ac101_read(struct snd_soc_codec *codec, unsigned reg) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       int r, v = 0;
+
+       if ((r = regmap_read(ac10x->regmap101, reg, &v)) < 0) {
+               dev_err(codec->dev, "read reg %02X fail\n",
+                        reg);
+               return r;
+       }
+       return v;
+}
+
+int ac101_write(struct snd_soc_codec *codec, unsigned reg, unsigned val) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       int v;
+
+       v = regmap_write(ac10x->regmap101, reg, val);
+       return v;
+}
+
+int ac101_update_bits(struct snd_soc_codec *codec, unsigned reg,
+                       unsigned mask, unsigned value
+) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       int v;
+
+       v = regmap_update_bits(ac10x->regmap101, reg, mask, value);
+       return v;
+}
+
+
+
+#ifdef CONFIG_AC101_SWITCH_DETECT
+/******************************************************************************/
+/********************************switch****************************************/
+/******************************************************************************/
+#define KEY_HEADSETHOOK         226            /* key define */
+#define HEADSET_FILTER_CNT     (10)
+
+/*
+ * switch_hw_config:config the 53 codec register
+ */
+static void switch_hw_config(struct snd_soc_codec *codec)
+{
+       int r;
+
+       AC101_DBG();
+
+       /*HMIC/MMIC BIAS voltage level select:2.5v*/
+       ac101_update_bits(codec, OMIXER_BST1_CTRL, (0xf<<BIASVOLTAGE), (0xf<<BIASVOLTAGE));
+       /*debounce when Key down or keyup*/
+       ac101_update_bits(codec, HMIC_CTRL1, (0xf<<HMIC_M), (0x0<<HMIC_M));
+       /*debounce when earphone plugin or pullout*/
+       ac101_update_bits(codec, HMIC_CTRL1, (0xf<<HMIC_N), (0x0<<HMIC_N));
+       /*Down Sample Setting Select: Downby 4,32Hz*/
+       ac101_update_bits(codec, HMIC_CTRL2, (0x3<<HMIC_SAMPLE_SELECT), (0x02<<HMIC_SAMPLE_SELECT));
+       /*Hmic_th2 for detecting Keydown or Keyup.*/
+       ac101_update_bits(codec, HMIC_CTRL2, (0x1f<<HMIC_TH2), (0x8<<HMIC_TH2));
+       /*Hmic_th1[4:0],detecting eraphone plugin or pullout*/
+       ac101_update_bits(codec, HMIC_CTRL2, (0x1f<<HMIC_TH1), (0x1<<HMIC_TH1));
+       /*Headset microphone BIAS working mode: when HBIASEN = 1 */
+       ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASMOD), (0x1<<HBIASMOD));
+       /*Headset microphone BIAS Enable*/
+       ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASEN), (0x1<<HBIASEN));
+       /*Headset microphone BIAS Current sensor & ADC Enable*/
+       ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASADCEN), (0x1<<HBIASADCEN));
+       /*Earphone Plugin/out Irq Enable*/
+       ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_PULLOUT_IRQ), (0x1<<HMIC_PULLOUT_IRQ));
+       ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_PLUGIN_IRQ), (0x1<<HMIC_PLUGIN_IRQ));
+
+       /*Hmic KeyUp/key down Irq Enable*/
+       ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_KEYDOWN_IRQ), (0x1<<HMIC_KEYDOWN_IRQ));
+       ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_KEYUP_IRQ), (0x1<<HMIC_KEYUP_IRQ));
+
+       /*headphone calibration clock frequency select*/
+       ac101_update_bits(codec, SPKOUT_CTRL, (0x7<<HPCALICKS), (0x7<<HPCALICKS));
+
+       /*clear hmic interrupt */
+       r = HMIC_PEND_ALL;
+       ac101_write(codec, HMIC_STS, r);
+
+       return;
+}
+
+/*
+ * switch_status_update: update the switch state.
+ */
+static void switch_status_update(struct ac10x_priv *ac10x)
+{
+       AC101_DBG("ac10x->state:%d\n", ac10x->state);
+
+    input_report_switch(ac10x->inpdev, SW_HEADPHONE_INSERT, ac10x->state);
+    input_sync(ac10x->inpdev);
+    return;
+}
+
+/*
+ * work_cb_clear_irq: clear audiocodec pending and Record the interrupt.
+ */
+static void work_cb_clear_irq(struct work_struct *work)
+{
+       int reg_val = 0;
+       struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_clear_irq);
+       struct snd_soc_codec *codec = ac10x->codec;
+
+       ac10x->irq_cntr++;
+
+       reg_val = ac101_read(codec, HMIC_STS);
+       if (BIT(HMIC_PULLOUT_PEND) & reg_val) {
+               ac10x->pullout_cntr++;
+               AC101_DBG("ac10x->pullout_cntr: %d\n", ac10x->pullout_cntr);
+       }
+
+       reg_val |= HMIC_PEND_ALL;
+       ac101_write(codec, HMIC_STS, reg_val);
+
+       reg_val = ac101_read(codec, HMIC_STS);
+       if ((reg_val & HMIC_PEND_ALL) != 0){
+               reg_val |= HMIC_PEND_ALL;
+               ac101_write(codec, HMIC_STS, reg_val);
+       }
+
+       if (cancel_work_sync(&ac10x->work_switch) != 0) {
+               ac10x->irq_cntr--;
+       }
+
+       if (0 == schedule_work(&ac10x->work_switch)) {
+               ac10x->irq_cntr--;
+               AC101_DBG("[work_cb_clear_irq] add work struct failed!\n");
+       }
+}
+
+enum {
+       HBIAS_LEVEL_1 = 0x02,
+       HBIAS_LEVEL_2 = 0x0B,
+       HBIAS_LEVEL_3 = 0x13,
+       HBIAS_LEVEL_4 = 0x17,
+       HBIAS_LEVEL_5 = 0x19,
+};
+
+static int __ac101_get_hmic_data(struct snd_soc_codec *codec) {
+       #ifdef AC101_DEBG
+       static long counter;
+       #endif
+       int r, d;
+
+       d = GET_HMIC_DATA(ac101_read(codec, HMIC_STS));
+
+       r = 0x1 << HMIC_DATA_PEND;
+       ac101_write(codec, HMIC_STS, r);
+
+       /* prevent i2c accessing too frequently */
+       usleep_range(1500, 3000);
+
+       AC101_DBG("HMIC_DATA(%3ld): %02X\n", counter++, d);
+       return d;
+}
+
+/*
+ * work_cb_earphone_switch: judge the status of the headphone
+ */
+static void work_cb_earphone_switch(struct work_struct *work)
+{
+       struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_switch);
+       struct snd_soc_codec *codec = ac10x->codec;
+
+       static int hook_flag1 = 0, hook_flag2 = 0;
+       static int KEY_VOLUME_FLAG = 0;
+
+       unsigned filter_buf = 0;
+       int filt_index = 0;
+       int t = 0;
+
+       ac10x->irq_cntr--;
+
+       /* read HMIC_DATA */
+       t = __ac101_get_hmic_data(codec);
+
+       if ((t >= HBIAS_LEVEL_2) && (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) {
+               t = __ac101_get_hmic_data(codec);
+
+               if (t >= HBIAS_LEVEL_5){
+                       msleep(150);
+                       t = __ac101_get_hmic_data(codec);
+                       if (((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1 - 1) || t >= HBIAS_LEVEL_5)
+                       && (ac10x->pullout_cntr == 0)) {
+                               input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 1);
+                               input_sync(ac10x->inpdev);
+
+                               AC101_DBG("KEY_HEADSETHOOK1\n");
+
+                               if (hook_flag1 != hook_flag2)
+                                       hook_flag1 = hook_flag2 = 0;
+                               hook_flag1++;
+                       }
+                       if (ac10x->pullout_cntr)
+                               ac10x->pullout_cntr--;
+               } else if (t >= HBIAS_LEVEL_4) {
+                       msleep(80);
+                       t = __ac101_get_hmic_data(codec);
+                       if (t < HBIAS_LEVEL_5 && t >= HBIAS_LEVEL_4 && (ac10x->pullout_cntr == 0)) {
+                               KEY_VOLUME_FLAG = 1;
+                               input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 1);
+                               input_sync(ac10x->inpdev);
+                               input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 0);
+                               input_sync(ac10x->inpdev);
+
+                               AC101_DBG("HMIC_DATA: %d KEY_VOLUMEUP\n", t);
+                       }
+                       if (ac10x->pullout_cntr)
+                               ac10x->pullout_cntr--;
+               } else if (t >= HBIAS_LEVEL_3){
+                       msleep(80);
+                       t = __ac101_get_hmic_data(codec);
+                       if (t < HBIAS_LEVEL_4 && t >= HBIAS_LEVEL_3  && (ac10x->pullout_cntr == 0)){
+                               KEY_VOLUME_FLAG = 1;
+                               input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 1);
+                               input_sync(ac10x->inpdev);
+                               input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 0);
+                               input_sync(ac10x->inpdev);
+                               AC101_DBG("KEY_VOLUMEDOWN\n");
+                       }
+                       if (ac10x->pullout_cntr)
+                               ac10x->pullout_cntr--;
+               }
+       } else if ((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) && (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) {
+               t = __ac101_get_hmic_data(codec);
+               if (t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) {
+                       if (KEY_VOLUME_FLAG) {
+                               KEY_VOLUME_FLAG = 0;
+                       }
+                       if (hook_flag1 == (++hook_flag2)) {
+                               hook_flag1 = hook_flag2 = 0;
+                               input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 0);
+                               input_sync(ac10x->inpdev);
+
+                               AC101_DBG("KEY_HEADSETHOOK0\n");
+                       }
+               }
+       } else {
+               while (ac10x->irq_cntr == 0 && ac10x->irq != 0) {
+                       msleep(20);
+
+                       t = __ac101_get_hmic_data(codec);
+
+                       if (filt_index <= HEADSET_FILTER_CNT) {
+                               if (filt_index++ == 0) {
+                                       filter_buf = t;
+                               } else if (filter_buf != t) {
+                                       filt_index = 0;
+                               }
+                               continue;
+                       }
+
+                       filt_index = 0;
+                       if (filter_buf >= HBIAS_LEVEL_2) {
+                               ac10x->mode = THREE_HEADPHONE_PLUGIN;
+                               ac10x->state = 2;
+                       } else if (filter_buf >= HBIAS_LEVEL_1 - 1) {
+                               ac10x->mode = FOUR_HEADPHONE_PLUGIN;
+                               ac10x->state = 1;
+                       } else {
+                               ac10x->mode = HEADPHONE_IDLE;
+                               ac10x->state = 0;
+                       }
+                       switch_status_update(ac10x);
+                       ac10x->pullout_cntr = 0;
+                       break;
+               }
+       }
+}
+
+/*
+ * audio_hmic_irq:  the interrupt handlers
+ */
+static irqreturn_t audio_hmic_irq(int irq, void *para)
+{
+       struct ac10x_priv *ac10x = (struct ac10x_priv *)para;
+       if (ac10x == NULL) {
+               return -EINVAL;
+       }
+
+       if (0 == schedule_work(&ac10x->work_clear_irq)){
+               AC101_DBG("[audio_hmic_irq] work already in queue_codec_irq, adding failed!\n");
+       }
+       return IRQ_HANDLED;
+}
+
+static int ac101_switch_probe(struct ac10x_priv *ac10x) {
+       struct i2c_client *i2c = ac10x->i2c101;
+       long ret;
+
+       ac10x->gpiod_irq = devm_gpiod_get_optional(&i2c->dev, "switch-irq", GPIOD_IN);
+       if (IS_ERR(ac10x->gpiod_irq)) {
+               ac10x->gpiod_irq = NULL;
+               dev_err(&i2c->dev, "failed get switch-irq in device tree\n");
+               goto _err_irq;
+       }
+
+       gpiod_direction_input(ac10x->gpiod_irq);
+
+       ac10x->irq = gpiod_to_irq(ac10x->gpiod_irq);
+       if (IS_ERR_VALUE(ac10x->irq)) {
+               pr_info("[ac101] map gpio to irq failed, errno = %ld\n", ac10x->irq);
+               ac10x->irq = 0;
+               goto _err_irq;
+       }
+
+       /* request irq, set irq type to falling edge trigger */
+       ret = devm_request_irq(ac10x->codec->dev, ac10x->irq, audio_hmic_irq, IRQF_TRIGGER_FALLING, "SWTICH_EINT", ac10x);
+       if (IS_ERR_VALUE(ret)) {
+               pr_info("[ac101] request virq %ld failed, errno = %ld\n", ac10x->irq, ret);
+               goto _err_irq;
+       }
+
+       ac10x->mode = HEADPHONE_IDLE;
+       ac10x->state = -1;
+
+       /*use for judge the state of switch*/
+       INIT_WORK(&ac10x->work_switch, work_cb_earphone_switch);
+       INIT_WORK(&ac10x->work_clear_irq, work_cb_clear_irq);
+
+       /********************create input device************************/
+       ac10x->inpdev = devm_input_allocate_device(ac10x->codec->dev);
+       if (!ac10x->inpdev) {
+               AC101_DBG("input_allocate_device: not enough memory for input device\n");
+               ret = -ENOMEM;
+               goto _err_input_allocate_device;
+       }
+
+       ac10x->inpdev->name          = "seed-voicecard-headset";
+       ac10x->inpdev->phys          = dev_name(ac10x->codec->dev);
+       ac10x->inpdev->id.bustype    = BUS_I2C;
+       ac10x->inpdev->dev.parent    = ac10x->codec->dev;
+       input_set_drvdata(ac10x->inpdev, ac10x->codec);
+
+       ac10x->inpdev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SW);
+
+       set_bit(KEY_HEADSETHOOK, ac10x->inpdev->keybit);
+       set_bit(KEY_VOLUMEUP,    ac10x->inpdev->keybit);
+       set_bit(KEY_VOLUMEDOWN,  ac10x->inpdev->keybit);
+       input_set_capability(ac10x->inpdev, EV_SW, SW_HEADPHONE_INSERT);
+
+       ret = input_register_device(ac10x->inpdev);
+       if (ret) {
+               AC101_DBG("input_register_device: input_register_device failed\n");
+               goto _err_input_register_device;
+       }
+
+       /* the first headset state checking */
+       switch_hw_config(ac10x->codec);
+       ac10x->irq_cntr = 1;
+       schedule_work(&ac10x->work_switch);
+
+       return 0;
+
+_err_input_register_device:
+_err_input_allocate_device:
+
+       if (ac10x->irq) {
+               devm_free_irq(&i2c->dev, ac10x->irq, ac10x);
+               ac10x->irq = 0;
+       }
+_err_irq:
+       return ret;
+}
+/******************************************************************************/
+/********************************switch****************************************/
+/******************************************************************************/
+#endif
+
+
+
+void drc_config(struct snd_soc_codec *codec)
+{
+       int reg_val;
+       reg_val = ac101_read(codec, 0xa3);
+       reg_val &= ~(0x7ff<<0);
+       reg_val |= 1<<0;
+       ac101_write(codec, 0xa3, reg_val);
+       ac101_write(codec, 0xa4, 0x2baf);
+
+       reg_val = ac101_read(codec, 0xa5);
+       reg_val &= ~(0x7ff<<0);
+       reg_val |= 1<<0;
+       ac101_write(codec, 0xa5, reg_val);
+       ac101_write(codec, 0xa6, 0x2baf);
+
+       reg_val = ac101_read(codec, 0xa7);
+       reg_val &= ~(0x7ff<<0);
+       ac101_write(codec, 0xa7, reg_val);
+       ac101_write(codec, 0xa8, 0x44a);
+
+       reg_val = ac101_read(codec, 0xa9);
+       reg_val &= ~(0x7ff<<0);
+       ac101_write(codec, 0xa9, reg_val);
+       ac101_write(codec, 0xaa, 0x1e06);
+
+       reg_val = ac101_read(codec, 0xab);
+       reg_val &= ~(0x7ff<<0);
+       reg_val |= (0x352<<0);
+       ac101_write(codec, 0xab, reg_val);
+       ac101_write(codec, 0xac, 0x6910);
+
+       reg_val = ac101_read(codec, 0xad);
+       reg_val &= ~(0x7ff<<0);
+       reg_val |= (0x77a<<0);
+       ac101_write(codec, 0xad, reg_val);
+       ac101_write(codec, 0xae, 0xaaaa);
+
+       reg_val = ac101_read(codec, 0xaf);
+       reg_val &= ~(0x7ff<<0);
+       reg_val |= (0x2de<<0);
+       ac101_write(codec, 0xaf, reg_val);
+       ac101_write(codec, 0xb0, 0xc982);
+
+       ac101_write(codec, 0x16, 0x9f9f);
+
+}
+
+void drc_enable(struct snd_soc_codec *codec,bool on)
+{
+       int reg_val;
+       if (on) {
+               ac101_write(codec, 0xb5, 0xA080);
+               reg_val = ac101_read(codec, MOD_CLK_ENA);
+               reg_val |= (0x1<<6);
+               ac101_write(codec, MOD_CLK_ENA, reg_val);
+               reg_val = ac101_read(codec, MOD_RST_CTRL);
+               reg_val |= (0x1<<6);
+               ac101_write(codec, MOD_RST_CTRL, reg_val);
+
+               reg_val = ac101_read(codec, 0xa0);
+               reg_val |= (0x7<<0);
+               ac101_write(codec, 0xa0, reg_val);
+       } else {
+               ac101_write(codec, 0xb5, 0x0);
+               reg_val = ac101_read(codec, MOD_CLK_ENA);
+               reg_val &= ~(0x1<<6);
+               ac101_write(codec, MOD_CLK_ENA, reg_val);
+               reg_val = ac101_read(codec, MOD_RST_CTRL);
+               reg_val &= ~(0x1<<6);
+               ac101_write(codec, MOD_RST_CTRL, reg_val);
+
+               reg_val = ac101_read(codec, 0xa0);
+               reg_val &= ~(0x7<<0);
+               ac101_write(codec, 0xa0, reg_val);
+       }
+}
+
+void set_configuration(struct snd_soc_codec *codec)
+{
+       if (speaker_double_used) {
+               ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<<SPK_VOL), (double_speaker_val<<SPK_VOL));
+       } else {
+               ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<<SPK_VOL), (single_speaker_val<<SPK_VOL));
+       }
+       ac101_update_bits(codec, HPOUT_CTRL, (0x3f<<HP_VOL), (headset_val<<HP_VOL));
+       ac101_update_bits(codec, ADC_SRCBST_CTRL, (0x7<<ADC_MIC1G), (mainmic_val<<ADC_MIC1G));
+       ac101_update_bits(codec, ADC_SRCBST_CTRL, (0x7<<ADC_MIC2G), (headsetmic_val<<ADC_MIC2G));
+       if (dmic_used) {
+               ac101_write(codec, ADC_VOL_CTRL, adc_digital_val);
+       }
+       if (drc_used) {
+               drc_config(codec);
+       }
+       /*headphone calibration clock frequency select*/
+       ac101_update_bits(codec, SPKOUT_CTRL, (0x7<<HPCALICKS), (0x7<<HPCALICKS));
+
+       /* I2S1 DAC Timeslot 0 data <- I2S1 DAC channel 0 */
+       // "AIF1IN0L Mux" <= "AIF1DACL"
+       // "AIF1IN0R Mux" <= "AIF1DACR"
+       ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0L_SRC, 0x0 << AIF1_DA0L_SRC);
+       ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0R_SRC, 0x0 << AIF1_DA0R_SRC);
+       /* Timeslot 0 Left & Right Channel enable */
+       ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0R_ENA, 0x3 << AIF1_DA0R_ENA);
+
+       /* DAC Digital Mixer Source Select <- I2S1 DA0 */
+       // "DACL Mixer" += "AIF1IN0L Mux"
+       // "DACR Mixer" += "AIF1IN0R Mux"
+       ac101_update_bits(codec, DAC_MXR_SRC, 0xF << DACL_MXR_ADCL, 0x8 << DACL_MXR_ADCL);
+       ac101_update_bits(codec, DAC_MXR_SRC, 0xF << DACR_MXR_ADCR, 0x8 << DACR_MXR_ADCR);
+       /* Internal DAC Analog Left & Right Channel enable */
+       ac101_update_bits(codec, OMIXER_DACA_CTRL, 0x3 << DACALEN, 0x3 << DACALEN);
+
+       /* Output Mixer Source Select */
+       // "Left Output Mixer"  += "DACL Mixer"
+       // "Right Output Mixer" += "DACR Mixer"
+       ac101_update_bits(codec, OMIXER_SR, 0x1 << LMIXMUTEDACL, 0x1 << LMIXMUTEDACL);
+       ac101_update_bits(codec, OMIXER_SR, 0x1 << RMIXMUTEDACR, 0x1 << RMIXMUTEDACR);
+       /* Left & Right Analog Output Mixer enable */
+       ac101_update_bits(codec, OMIXER_DACA_CTRL, 0x3 << LMIXEN, 0x3 << LMIXEN);
+
+       /* Headphone Ouput Control */ 
+       // "HP_R Mux" <= "DACR Mixer"
+       // "HP_L Mux" <= "DACL Mixer"
+       ac101_update_bits(codec, HPOUT_CTRL, 0x1 << LHPS, 0x0 << LHPS);
+       ac101_update_bits(codec, HPOUT_CTRL, 0x1 << RHPS, 0x0 << RHPS);
+
+       /* Speaker Output Control */
+       // "SPK_L Mux" <= "SPK_LR Adder"
+       // "SPK_R Mux" <= "SPK_LR Adder"
+       ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPKS) | (0x1 << RSPKS), (0x1 << LSPKS) | (0x1 << RSPKS));
+       /* Enable Left & Right Speaker */
+       ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN), (0x1 << LSPK_EN) | (0x1 << RSPK_EN));
+       return;
+}
+
+static int late_enable_dac(struct snd_soc_codec* codec, int event) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       mutex_lock(&ac10x->dac_mutex);
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               AC101_DBG();
+               if (ac10x->dac_enable == 0){
+                       /*enable dac module clk*/
+                       ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_DAC_DIG), (0x1<<MOD_CLK_DAC_DIG));
+                       ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_DAC_DIG), (0x1<<MOD_RESET_DAC_DIG));
+                       ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENDA), (0x1<<ENDA));
+                       ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENHPF),(0x1<<ENHPF));
+               }
+               ac10x->dac_enable++;
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               if (ac10x->dac_enable != 0){
+                       ac10x->dac_enable = 0;
+
+                       ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENHPF),(0x0<<ENHPF));
+                       ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENDA), (0x0<<ENDA));
+                       /*disable dac module clk*/
+                       ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_DAC_DIG), (0x0<<MOD_CLK_DAC_DIG));
+                       ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_DAC_DIG), (0x0<<MOD_RESET_DAC_DIG));
+               }
+               break;
+       }
+       mutex_unlock(&ac10x->dac_mutex);
+       return 0;
+}
+
+static int ac101_headphone_event(struct snd_soc_codec* codec, int event) {
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /*open*/
+               AC101_DBG("post:open\n");
+               ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE), (0xf<<HPOUTPUTENABLE));
+               msleep(10);
+               ac101_update_bits(codec, HPOUT_CTRL, (0x1<<HPPA_EN), (0x1<<HPPA_EN));
+               ac101_update_bits(codec, HPOUT_CTRL, (0x3<<LHPPA_MUTE), (0x3<<LHPPA_MUTE));
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               /*close*/
+               AC101_DBG("pre:close\n");
+               ac101_update_bits(codec, HPOUT_CTRL, (0x3<<LHPPA_MUTE), (0x0<<LHPPA_MUTE));
+               msleep(10);
+               ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE), (0x0<<HPOUTPUTENABLE));
+               ac101_update_bits(codec, HPOUT_CTRL, (0x1<<HPPA_EN), (0x0<<HPPA_EN));
+               break;
+       }
+       return 0;
+}
+
+static int ac101_sysclk_started(void) {
+       int reg_val;
+
+       reg_val = ac101_read(static_ac10x->codec, SYSCLK_CTRL);
+       return (reg_val & (0x1<<SYSCLK_ENA));
+}
+
+static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (ac10x->aif1_clken == 0){
+                       ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA), (0x1<<AIF1CLK_ENA));
+                       if(!quick || _MASTER_MULTI_CODEC != _MASTER_AC101) {
+                               /* enable aif1clk & sysclk */
+                               ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
+                               ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x1<<MOD_RESET_AIF1));
+                       }
+                       ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x1<<SYSCLK_ENA));
+
+                       if (ret) {
+                               AC101_DBG("start sysclk failed\n");
+                       } else {
+                               AC101_DBG("hw sysclk enable\n");
+                               ac10x->aif1_clken++;
+                       }
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               if (ac10x->aif1_clken != 0) {
+                       /* disable aif1clk & sysclk */
+                       ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA),(0x0<<AIF1CLK_ENA));
+                       ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x0<<MOD_CLK_AIF1));
+                       ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x0<<MOD_RESET_AIF1));
+                       ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x0<<SYSCLK_ENA));
+
+                       if (ret) {
+                               AC101_DBG("stop sysclk failed\n");
+                       } else {
+                               AC101_DBG("hw sysclk disable\n");
+                               ac10x->aif1_clken = 0;
+                       }
+                       break;
+               }
+       }
+
+       AC101_DBG("event=%d pre_up/%d post_down/%d\n", event, SND_SOC_DAPM_PRE_PMU, SND_SOC_DAPM_POST_PMD);
+
+       return ret;
+}
+
+/**
+ * snd_ac101_get_volsw - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+static int snd_ac101_get_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol
+){
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int val, mask = (1 << fls(mc->max)) - 1;
+       unsigned int invert = mc->invert;
+       int ret;
+
+       if ((ret = ac101_read(static_ac10x->codec, mc->reg)) < 0)
+               return ret;
+
+       val = (ret >> mc->shift) & mask;
+       ucontrol->value.integer.value[0] = val - mc->min;
+       if (invert) {
+               ucontrol->value.integer.value[0] =
+                       mc->max - ucontrol->value.integer.value[0];
+       }
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               val = (ret >> mc->rshift) & mask;
+               ucontrol->value.integer.value[1] = val - mc->min;
+               if (invert) {
+                       ucontrol->value.integer.value[1] =
+                               mc->max - ucontrol->value.integer.value[1];
+               }
+       }
+       return 0;
+}
+
+/**
+ * snd_ac101_put_volsw - single mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+static int snd_ac101_put_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol
+){
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int sign_bit = mc->sign_bit;
+       unsigned int val, mask = (1 << fls(mc->max)) - 1;
+       unsigned int invert = mc->invert;
+       int ret;
+
+       if (sign_bit)
+               mask = BIT(sign_bit + 1) - 1;
+
+       val = ((ucontrol->value.integer.value[0] + mc->min) & mask);
+       if (invert) {
+               val = mc->max - val;
+       }
+
+       ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->shift, val << mc->shift);
+
+       if (! snd_soc_volsw_is_stereo(mc)) {
+               return ret;
+       }
+       val = ((ucontrol->value.integer.value[1] + mc->min) & mask);
+       if (invert) {
+               val = mc->max - val;
+       }
+
+       ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->rshift, val << mc->rshift);
+       return ret;
+}
+
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -11925, 75, 0);
+static const DECLARE_TLV_DB_SCALE(dac_mix_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(dig_vol_tlv, -7308, 116, 0);
+static const DECLARE_TLV_DB_SCALE(speaker_vol_tlv, -4800, 150, 0);
+static const DECLARE_TLV_DB_SCALE(headphone_vol_tlv, -6300, 100, 0);
+
+static struct snd_kcontrol_new ac101_controls[] = {
+       /*DAC*/
+       SOC_DOUBLE_TLV("DAC volume", DAC_VOL_CTRL, DAC_VOL_L, DAC_VOL_R, 0xff, 0, dac_vol_tlv),
+       SOC_DOUBLE_TLV("DAC mixer gain", DAC_MXR_GAIN, DACL_MXR_GAIN, DACR_MXR_GAIN, 0xf, 0, dac_mix_vol_tlv),
+       SOC_SINGLE_TLV("digital volume", DAC_DBG_CTRL, DVC, 0x3f, 1, dig_vol_tlv),
+       SOC_SINGLE_TLV("speaker volume", SPKOUT_CTRL, SPK_VOL, 0x1f, 0, speaker_vol_tlv),
+       SOC_SINGLE_TLV("headphone volume", HPOUT_CTRL, HP_VOL, 0x3f, 0, headphone_vol_tlv),
+};
+
+/* PLL divisors */
+struct pll_div {
+       unsigned int pll_in;
+       unsigned int pll_out;
+       int m;
+       int n_i;
+       int n_f;
+};
+
+struct aif1_fs {
+       unsigned samp_rate;
+       int bclk_div;
+       int srbit;
+       #define _SERIES_24_576K         0
+       #define _SERIES_22_579K         1
+       int series;
+};
+
+struct kv_map {
+       int val;
+       int bit;
+};
+
+/*
+ * Note : pll code from original tdm/i2s driver.
+ * freq_out = freq_in * N/(M*(2k+1)) , k=1,N=N_i+N_f,N_f=factor*0.2;
+ *             N_i[0,1023], N_f_factor[0,7], m[1,64]=REG_VAL[1-63,0]
+ */
+static const struct pll_div codec_pll_div[] = {
+       {128000,   _FREQ_22_579K,  1, 529, 1},
+       {192000,   _FREQ_22_579K,  1, 352, 4},
+       {256000,   _FREQ_22_579K,  1, 264, 3},
+       {384000,   _FREQ_22_579K,  1, 176, 2}, /*((176+2*0.2)*6000000)/(38*(2*1+1))*/
+       {1411200,  _FREQ_22_579K,  1,  48, 0},
+       {2822400,  _FREQ_22_579K,  1,  24, 0}, /* accurate, 11025 * 256 */
+       {5644800,  _FREQ_22_579K,  1,  12, 0}, /* accurate, 22050 * 256 */
+       {6000000,  _FREQ_22_579K, 38, 429, 0}, /*((429+0*0.2)*6000000)/(38*(2*1+1))*/
+       {11289600, _FREQ_22_579K,  1,   6, 0}, /* accurate, 44100 * 256 */
+       {13000000, _FREQ_22_579K, 19,  99, 0},
+       {19200000, _FREQ_22_579K, 25,  88, 1},
+       {24000000, _FREQ_22_579K, 63, 177, 4}, /* 22577778 Hz */
+
+       {128000,   _FREQ_24_576K,  1, 576, 0},
+       {192000,   _FREQ_24_576K,  1, 384, 0},
+       {256000,   _FREQ_24_576K,  1, 288, 0},
+       {384000,   _FREQ_24_576K,  1, 192, 0},
+       {2048000,  _FREQ_24_576K,  1,  36, 0}, /* accurate,  8000 * 256 */
+       {3072000,  _FREQ_24_576K,  1,  24, 0}, /* accurate, 12000 * 256 */
+       {4096000,  _FREQ_24_576K,  1,  18, 0}, /* accurate, 16000 * 256 */
+       {6000000,  _FREQ_24_576K, 25, 307, 1},
+       {6144000,  _FREQ_24_576K,  4,  48, 0}, /* accurate, 24000 * 256 */
+       {12288000, _FREQ_24_576K,  8,  48, 0}, /* accurate, 48000 * 256 */
+       {13000000, _FREQ_24_576K, 42, 238, 1},
+       {19200000, _FREQ_24_576K, 25,  96, 0},
+       {24000000, _FREQ_24_576K, 25,  76, 4}, /* accurate */
+
+       {_FREQ_22_579K, _FREQ_22_579K,  8,  24, 0}, /* accurate, 88200 * 256 */
+       {_FREQ_24_576K, _FREQ_24_576K,  8,  24, 0}, /* accurate, 96000 * 256 */
+};
+
+static const struct aif1_fs codec_aif1_fs[] = {
+       {8000, 12, 0},
+       {11025, 8, 1, _SERIES_22_579K},
+       {12000, 8, 2},
+       {16000, 6, 3},
+       {22050, 4, 4, _SERIES_22_579K},
+       {24000, 4, 5},
+       /* {32000, 3, 6}, dividing by 3 is not support */
+       {44100, 2, 7, _SERIES_22_579K},
+       {48000, 2, 8},
+       {96000, 1, 9},
+};
+
+static const struct kv_map codec_aif1_lrck[] = {
+       {16, 0},
+       {32, 1},
+       {64, 2},
+       {128, 3},
+       {256, 4},
+};
+
+static const struct kv_map codec_aif1_wsize[] = {
+       {8, 0},
+       {16, 1},
+       {20, 2},
+       {24, 3},
+       {32, 3},
+};
+
+static const unsigned ac101_bclkdivs[] = {
+         1,   2,   4,   6,
+         8,  12,  16,  24,
+        32,  48,  64,  96,
+       128, 192,   0,   0,
+};
+
+static int ac101_aif_play(struct ac10x_priv* ac10x) {
+       struct snd_soc_codec * codec = ac10x->codec;
+
+       late_enable_dac(codec, SND_SOC_DAPM_PRE_PMU);
+       ac101_headphone_event(codec, SND_SOC_DAPM_POST_PMU);
+       if (drc_used) {
+               drc_enable(codec, 1);
+       }
+
+       /* Enable Left & Right Speaker */
+       ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN), (0x1 << LSPK_EN) | (0x1 << RSPK_EN));
+       if (ac10x->gpiod_spk_amp_gate) {
+               gpiod_set_value(ac10x->gpiod_spk_amp_gate, 1);
+       }
+       return 0;
+}
+
+static void ac10x_work_aif_play(struct work_struct *work) {
+       struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, dlywork.work);
+
+       ac101_aif_play(ac10x);
+       return;
+}
+
+int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       AC101_DBG("mute=%d\n",  mute);
+
+       ac101_write(codec, DAC_VOL_CTRL, mute? 0: 0xA0A0);
+
+       if (!mute) {
+               #if _MASTER_MULTI_CODEC != _MASTER_AC101
+               /* enable global clock */
+               ac10x->aif1_clken = 0;
+               ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0);
+               ac101_aif_play(ac10x);
+               #else
+               schedule_delayed_work(&ac10x->dlywork, msecs_to_jiffies(50));
+               #endif
+       } else {
+               #if _MASTER_MULTI_CODEC == _MASTER_AC101
+               cancel_delayed_work_sync(&ac10x->dlywork);
+               #endif
+
+               if (ac10x->gpiod_spk_amp_gate) {
+                       gpiod_set_value(ac10x->gpiod_spk_amp_gate, 0);
+               }
+               /* Disable Left & Right Speaker */
+               ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN), (0x0 << LSPK_EN) | (0x0 << RSPK_EN));
+               if (drc_used) {
+                       drc_enable(codec, 0);
+               }
+               ac101_headphone_event(codec, SND_SOC_DAPM_PRE_PMD);
+               late_enable_dac(codec, SND_SOC_DAPM_POST_PMD);
+
+               #if _MASTER_MULTI_CODEC != _MASTER_AC101
+               ac10x->aif1_clken = 1;
+               ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
+               #endif
+       }
+       return 0;
+}
+
+void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n", 
+               snd_pcm_stream_str(substream),
+               codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE],
+               snd_soc_dai_active(codec_dai));
+
+       if (!snd_soc_dai_active(codec_dai)) {
+               ac10x->aif1_clken = 1;
+               ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
+       } else {
+               ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0);
+       }
+}
+
+static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source,
+                       unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       int i, m, n_i, n_f;
+
+       AC101_DBG("pll_id:%d\n",  pll_id);
+
+       /* clear volatile reserved bits*/
+       ac101_update_bits(codec, SYSCLK_CTRL, 0xFF & ~(0x1 << SYSCLK_ENA), 0x0);
+
+       /* select aif1 clk srouce from mclk1 */
+       ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC), (0x0<<AIF1CLK_SRC));
+       /* disable pll */
+       ac101_update_bits(codec, PLL_CTRL2, (0x1<<PLL_EN), (0<<PLL_EN));
+
+       if (!freq_out)
+               return 0;
+       if ((freq_in < 128000) || (freq_in > _FREQ_24_576K)) {
+               return -EINVAL;
+       } else if ((freq_in == _FREQ_24_576K) || (freq_in == _FREQ_22_579K)) {
+               if (pll_id == AC101_MCLK1) {
+                       /*select aif1 clk source from mclk1*/
+                       ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC), (0x0<<AIF1CLK_SRC));
+                       return 0;
+               }
+       }
+
+       switch (pll_id) {
+       case AC101_MCLK1:
+               /*pll source from MCLK1*/
+               ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<PLLCLK_SRC), (0x0<<PLLCLK_SRC));
+               break;
+       case AC101_BCLK1:
+               /*pll source from BCLK1*/
+               ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<PLLCLK_SRC), (0x2<<PLLCLK_SRC));
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* freq_out = freq_in * n/(m*(2k+1)) , k=1,N=N_i+N_f */
+       for (i = m = n_i = n_f = 0; i < ARRAY_SIZE(codec_pll_div); i++) {
+               if ((codec_pll_div[i].pll_in == freq_in) && (codec_pll_div[i].pll_out == freq_out)) {
+                       m   = codec_pll_div[i].m;
+                       n_i = codec_pll_div[i].n_i;
+                       n_f = codec_pll_div[i].n_f;
+                       break;
+               }
+       }
+       /* config pll m */
+       if (m  == 64) m = 0;
+       ac101_update_bits(codec, PLL_CTRL1, (0x3f<<PLL_POSTDIV_M), (m<<PLL_POSTDIV_M));
+       /* config pll n */
+       ac101_update_bits(codec, PLL_CTRL2, (0x3ff<<PLL_PREDIV_NI), (n_i<<PLL_PREDIV_NI));
+       ac101_update_bits(codec, PLL_CTRL2, (0x7<<PLL_POSTDIV_NF), (n_f<<PLL_POSTDIV_NF));
+       /* enable pll */
+       ac101_update_bits(codec, PLL_CTRL2, (0x1<<PLL_EN), (1<<PLL_EN));
+       ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<PLLCLK_ENA),  (0x1<<PLLCLK_ENA));
+       ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC), (0x3<<AIF1CLK_SRC));
+
+       return 0;
+}
+
+int ac101_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *codec_dai)
+{
+       int i = 0;
+       int AIF_CLK_CTRL = AIF1_CLK_CTRL;
+       int aif1_word_size = 24;
+       int aif1_slot_size = 32;
+       int aif1_lrck_div;
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       int reg_val, freq_out;
+       unsigned channels;
+
+       AC101_DBG("+++\n");
+
+       if (_MASTER_MULTI_CODEC == _MASTER_AC101 && ac101_sysclk_started()) {
+               /* not configure hw_param twice if stream is playback, tell the caller it's started */
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       return 1;
+               }
+       }
+
+       /* get channels count & slot size */
+       channels = params_channels(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S24_LE:
+       case SNDRV_PCM_FORMAT_S32_LE:
+               aif1_slot_size = 32;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+       default:
+               aif1_slot_size = 16;
+               break;
+       }
+
+       /* set LRCK/BCLK ratio */
+       aif1_lrck_div = aif1_slot_size * channels;
+       for (i = 0; i < ARRAY_SIZE(codec_aif1_lrck); i++) {
+               if (codec_aif1_lrck[i].val == aif1_lrck_div) {
+                       break;
+               }
+       }
+       ac101_update_bits(codec, AIF_CLK_CTRL, (0x7<<AIF1_LRCK_DIV), codec_aif1_lrck[i].bit<<AIF1_LRCK_DIV);
+
+       /* set PLL output freq */
+       freq_out = _FREQ_24_576K;
+       for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) {
+               if (codec_aif1_fs[i].samp_rate == params_rate(params)) {
+                       if (codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] && dmic_used && codec_aif1_fs[i].samp_rate == 44100) {
+                               ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS), (0x4<<AIF1_FS));
+                       } else {
+                               ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS), ((codec_aif1_fs[i].srbit)<<AIF1_FS));
+                       }
+                       if (codec_aif1_fs[i].series == _SERIES_22_579K)
+                               freq_out = _FREQ_22_579K;
+                       break;
+               }
+       }
+
+       /* set I2S word size */
+       for (i = 0; i < ARRAY_SIZE(codec_aif1_wsize); i++) {
+               if (codec_aif1_wsize[i].val == aif1_word_size) {
+                       break;
+               }
+       }
+       ac101_update_bits(codec, AIF_CLK_CTRL, (0x3<<AIF1_WORK_SIZ), ((codec_aif1_wsize[i].bit)<<AIF1_WORK_SIZ));
+
+       /* set TDM slot size */
+       if ((reg_val = codec_aif1_wsize[i].bit) > 2) reg_val = 2;
+       ac101_update_bits(codec, AIF1_ADCDAT_CTRL, 0x3 << AIF1_SLOT_SIZ, reg_val << AIF1_SLOT_SIZ);
+
+       /* setting pll if it's master mode */
+       reg_val = ac101_read(codec, AIF_CLK_CTRL);
+       if ((reg_val & (0x1 << AIF1_MSTR_MOD)) == 0) {
+               unsigned bclkdiv;
+
+               ac101_set_pll(codec_dai, AC101_MCLK1, 0, ac10x->sysclk, freq_out);
+
+               bclkdiv = freq_out / (aif1_lrck_div * params_rate(params));
+               for (i = 0; i < ARRAY_SIZE(ac101_bclkdivs) - 1; i++) {
+                       if (ac101_bclkdivs[i] >= bclkdiv) {
+                               break;
+                       }
+               }
+               ac101_update_bits(codec, AIF_CLK_CTRL, (0xf<<AIF1_BCLK_DIV), i<<AIF1_BCLK_DIV);
+       } else {
+               /* set pll clock source to BCLK if slave mode */
+               ac101_set_pll(codec_dai, AC101_BCLK1, 0, aif1_lrck_div * params_rate(params), freq_out);
+       }
+
+       #if _MASTER_MULTI_CODEC == _MASTER_AC101
+       /* Master mode, to clear cpu_dai fifos, disable output bclk & lrck */
+       ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
+       #endif
+
+       AC101_DBG("rate: %d , channels: %d , samp_res: %d",
+               params_rate(params), channels, aif1_slot_size);
+
+       AC101_DBG("---\n");
+       return 0;
+}
+
+int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       int reg_val;
+       int AIF_CLK_CTRL = AIF1_CLK_CTRL;
+       struct snd_soc_codec *codec = codec_dai->codec;
+
+       AC101_DBG();
+
+       /*
+        *      master or slave selection
+        *      0 = Master mode
+        *      1 = Slave mode
+        */
+       reg_val = ac101_read(codec, AIF_CLK_CTRL);
+       reg_val &= ~(0x1<<AIF1_MSTR_MOD);
+       switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:   /* codec clk & frm master, ap is slave*/
+               #if _MASTER_MULTI_CODEC == _MASTER_AC101
+               pr_warn("AC101 as Master\n");
+               reg_val |= (0x0<<AIF1_MSTR_MOD);
+               break;
+               #else
+               pr_warn("AC108 as Master\n");
+               #endif
+       case SND_SOC_DAIFMT_CBS_CFS:   /* codec clk & frm slave, ap is master*/
+               pr_warn("AC101 as Slave\n");
+               reg_val |= (0x1<<AIF1_MSTR_MOD);
+               break;
+       default:
+               pr_err("unknwon master/slave format\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Enable TDM mode
+        */
+       reg_val |=  (0x1 << AIF1_TDMM_ENA);
+       ac101_write(codec, AIF_CLK_CTRL, reg_val);
+
+       /* i2s mode selection */
+       reg_val = ac101_read(codec, AIF_CLK_CTRL);
+       reg_val&=~(3<<AIF1_DATA_FMT);
+       switch(fmt & SND_SOC_DAIFMT_FORMAT_MASK){
+       case SND_SOC_DAIFMT_I2S:        /* I2S1 mode */
+               reg_val |= (0x0<<AIF1_DATA_FMT);
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:    /* Right Justified mode */
+               reg_val |= (0x2<<AIF1_DATA_FMT);
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:     /* Left Justified mode */
+               reg_val |= (0x1<<AIF1_DATA_FMT);
+               break;
+       case SND_SOC_DAIFMT_DSP_A:      /* L reg_val msb after FRM LRC */
+               reg_val |= (0x3<<AIF1_DATA_FMT);
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               /* TODO: data offset set to 0 */
+               reg_val |= (0x3<<AIF1_DATA_FMT);
+               break;
+       default:
+               pr_err("%s, line:%d\n", __func__, __LINE__);
+               return -EINVAL;
+       }
+       ac101_write(codec, AIF_CLK_CTRL, reg_val);
+
+       /* DAI signal inversions */
+       reg_val = ac101_read(codec, AIF_CLK_CTRL);
+       switch(fmt & SND_SOC_DAIFMT_INV_MASK){
+       case SND_SOC_DAIFMT_NB_NF:     /* normal bit clock + nor frame */
+               reg_val &= ~(0x1<<AIF1_LRCK_INV);
+               reg_val &= ~(0x1<<AIF1_BCLK_INV);
+               break;
+       case SND_SOC_DAIFMT_NB_IF:     /* normal bclk + inv frm */
+               reg_val |= (0x1<<AIF1_LRCK_INV);
+               reg_val &= ~(0x1<<AIF1_BCLK_INV);
+               break;
+       case SND_SOC_DAIFMT_IB_NF:     /* invert bclk + nor frm */
+               reg_val &= ~(0x1<<AIF1_LRCK_INV);
+               reg_val |= (0x1<<AIF1_BCLK_INV);
+               break;
+       case SND_SOC_DAIFMT_IB_IF:     /* invert bclk + inv frm */
+               reg_val |= (0x1<<AIF1_LRCK_INV);
+               reg_val |= (0x1<<AIF1_BCLK_INV);
+               break;
+       }
+       ac101_write(codec, AIF_CLK_CTRL, reg_val);
+
+       return 0;
+}
+
+int ac101_audio_startup(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *codec_dai)
+{
+       // struct snd_soc_codec *codec = codec_dai->codec;
+
+       AC101_DBG("\n\n\n");
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+       }
+       return 0;
+}
+
+#if _MASTER_MULTI_CODEC == _MASTER_AC101
+static int ac101_set_clock(int y_start_n_stop) {
+       int r;
+
+       if (y_start_n_stop) {
+               /* enable global clock */
+               r = ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU, 1);
+       } else {
+               /* disable global clock */
+               static_ac10x->aif1_clken = 1;
+               r = ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_POST_PMD, 0);
+       }
+       return r;
+}
+#endif
+
+int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
+                 struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       AC101_DBG("stream=%s  cmd=%d\n",
+               snd_pcm_stream_str(substream),
+               cmd);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               #if _MASTER_MULTI_CODEC == _MASTER_AC101
+               if (ac10x->aif1_clken == 0){
+                       /*
+                        * enable aif1clk, it' here due to reduce time between 'AC108 Sysclk Enable' and 'AC101 Sysclk Enable'
+                        * Or else the two AC108 chips lost the sync.
+                        */
+                       ret = 0;
+                       ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
+                       ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x1<<MOD_RESET_AIF1));
+               }
+               #endif
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+#if 0
+static int ac101_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       AC101_DBG("id=%d freq=%d, dir=%d\n", 
+               clk_id, freq, dir);
+
+       ac10x->sysclk = freq;
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops ac101_aif1_dai_ops = {
+       //.startup      = ac101_audio_startup,
+       //.shutdown     = ac101_aif_shutdown,
+       //.set_sysclk   = ac101_set_dai_sysclk,
+       //.set_pll      = ac101_set_pll,
+       //.set_fmt      = ac101_set_dai_fmt,
+       //.hw_params    = ac101_hw_params,
+       //.trigger      = ac101_trigger,
+       //.digital_mute = ac101_aif_mute,
+};
+
+static struct snd_soc_dai_driver ac101_dai[] = {
+       {
+               .name = "ac10x-aif1",
+               .id = AIF1_CLK,
+               .playback = {
+                       .stream_name = "Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = AC101_RATES,
+                       .formats = AC101_FORMATS,
+               },
+               #if 0
+               .capture = {
+                       .stream_name = "Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = AC101_RATES,
+                       .formats = AC101_FORMATS,
+               },
+               #endif
+               .ops = &ac101_aif1_dai_ops,
+       }
+};
+#endif
+
+static void codec_resume_work(struct work_struct *work)
+{
+       struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, codec_resume);
+       struct snd_soc_codec *codec = ac10x->codec;
+
+       AC101_DBG("+++\n");
+
+       set_configuration(codec);
+       if (drc_used) {
+               drc_config(codec);
+       }
+       /*enable this bit to prevent leakage from ldoin*/
+       ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0x1<<OSCEN));
+
+       AC101_DBG("---\n");
+       return;
+}
+
+int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               AC101_DBG("SND_SOC_BIAS_ON\n");
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               AC101_DBG("SND_SOC_BIAS_PREPARE\n");
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               AC101_DBG("SND_SOC_BIAS_STANDBY\n");
+               #ifdef CONFIG_AC101_SWITCH_DETECT
+               switch_hw_config(codec);
+               #endif
+               break;
+       case SND_SOC_BIAS_OFF:
+               #ifdef CONFIG_AC101_SWITCH_DETECT
+               ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASEN), (0<<HBIASEN));
+               ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASADCEN), (0<<HBIASADCEN));
+               #endif
+               ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE), (0<<HPOUTPUTENABLE));
+               ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0<<OSCEN));
+               AC101_DBG("SND_SOC_BIAS_OFF\n");
+               break;
+       }
+       snd_soc_codec_get_dapm(codec)->bias_level = level;
+       return 0;
+}
+
+int ac101_codec_probe(struct snd_soc_codec *codec)
+{
+       int ret = 0;
+       struct ac10x_priv *ac10x;
+
+       ac10x = dev_get_drvdata(codec->dev);
+       if (ac10x == NULL) {
+               AC101_DBG("not set client data!\n");
+               return -ENOMEM;
+       }
+       ac10x->codec = codec;
+
+       INIT_DELAYED_WORK(&ac10x->dlywork, ac10x_work_aif_play);
+       INIT_WORK(&ac10x->codec_resume, codec_resume_work);
+       ac10x->dac_enable = 0;
+       ac10x->aif1_clken = 0;
+       mutex_init(&ac10x->dac_mutex);
+
+       #if _MASTER_MULTI_CODEC == _MASTER_AC101
+       //seeed_voice_card_register_set_clock(SNDRV_PCM_STREAM_PLAYBACK, ac101_set_clock);
+       #endif
+
+       set_configuration(ac10x->codec);
+
+       /*enable this bit to prevent leakage from ldoin*/
+       ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0x1<<OSCEN));
+       ac101_write(codec, DAC_VOL_CTRL, 0);
+
+       /* customized get/put inteface */
+       for (ret = 0; ret < ARRAY_SIZE(ac101_controls); ret++) {
+               struct snd_kcontrol_new* skn = &ac101_controls[ret];
+
+               skn->get = snd_ac101_get_volsw;
+               skn->put = snd_ac101_put_volsw;
+       }
+       ret = snd_soc_add_codec_controls(codec, ac101_controls, ARRAY_SIZE(ac101_controls));
+       if (ret) {
+               pr_err("[ac10x] Failed to register audio mode control, "
+                               "will continue without it.\n");
+       }
+
+       #ifdef CONFIG_AC101_SWITCH_DETECT
+       ret = ac101_switch_probe(ac10x);
+       if (ret) {
+               // not care the switch return value
+       }
+       #endif
+
+       return 0;
+}
+
+/* power down chip */
+int ac101_codec_remove(struct snd_soc_codec *codec)
+{
+       #ifdef CONFIG_AC101_SWITCH_DETECT
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       if (ac10x->irq) {
+               devm_free_irq(codec->dev, ac10x->irq, ac10x);
+               ac10x->irq = 0;
+       }
+
+       if (cancel_work_sync(&ac10x->work_switch) != 0) {
+       }
+
+       if (cancel_work_sync(&ac10x->work_clear_irq) != 0) {
+       }
+
+       if (ac10x->inpdev) {
+               input_unregister_device(ac10x->inpdev);
+               ac10x->inpdev = NULL;
+       }
+       #endif
+
+       return 0;
+}
+
+int ac101_codec_suspend(struct snd_soc_codec *codec)
+{
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       AC101_DBG("[codec]:suspend\n");
+       regcache_cache_only(ac10x->regmap101, true);
+       return 0;
+}
+
+int ac101_codec_resume(struct snd_soc_codec *codec)
+{
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       AC101_DBG("[codec]:resume");
+
+       /* Sync reg_cache with the hardware */
+       regcache_cache_only(ac10x->regmap101, false);
+       ret = regcache_sync(ac10x->regmap101);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to sync register cache: %d\n", ret);
+               regcache_cache_only(ac10x->regmap101, true);
+               return ret;
+       }
+
+       #ifdef CONFIG_AC101_SWITCH_DETECT
+       ac10x->mode = HEADPHONE_IDLE;
+       ac10x->state = -1;
+       #endif
+
+       ac101_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       schedule_work(&ac10x->codec_resume);
+       return 0;
+}
+
+/***************************************************************************/
+static ssize_t ac101_debug_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct ac10x_priv *ac10x = dev_get_drvdata(dev);
+       int val = 0, flag = 0;
+       u16 value_w, value_r;
+       u8 reg, num, i=0;
+
+       val = simple_strtol(buf, NULL, 16);
+       flag = (val >> 24) & 0xF;
+       if (flag) {
+               reg = (val >> 16) & 0xFF;
+               value_w =  val & 0xFFFF;
+               ac101_write(ac10x->codec, reg, value_w);
+               printk("write 0x%x to reg:0x%x\n", value_w, reg);
+       } else {
+               reg = (val >> 8) & 0xFF;
+               num = val & 0xff;
+               printk("\n");
+               printk("read:start add:0x%x,count:0x%x\n", reg, num);
+
+               regcache_cache_bypass(ac10x->regmap101, true);
+               do {
+                       value_r = ac101_read(ac10x->codec, reg);
+                       printk("0x%x: 0x%04x ", reg++, value_r);
+                       if (++i % 4 == 0 || i == num)
+                               printk("\n");
+               } while (i < num);
+               regcache_cache_bypass(ac10x->regmap101, false);
+       }
+       return count;
+}
+static ssize_t ac101_debug_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       printk("echo flag|reg|val > ac10x\n");
+       printk("eg read star addres=0x06,count 0x10:echo 0610 >ac10x\n");
+       printk("eg write value:0x13fe to address:0x06 :echo 10613fe > ac10x\n");
+       return 0;
+}
+static DEVICE_ATTR(ac10x, 0644, ac101_debug_show, ac101_debug_store);
+
+static struct attribute *audio_debug_attrs[] = {
+       &dev_attr_ac10x.attr,
+       NULL,
+};
+
+static struct attribute_group audio_debug_attr_group = {
+       .name   = "ac101_debug",
+       .attrs  = audio_debug_attrs,
+};
+/***************************************************************************/
+
+/************************************************************/
+static bool ac101_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case PLL_CTRL2:
+       case HMIC_STS:
+               return true;
+       }
+       return false;
+}
+
+static const struct regmap_config ac101_regmap = {
+       .reg_bits = 8,
+       .val_bits = 16,
+       .reg_stride = 1,
+       .max_register = 0xB5,
+       .cache_type = REGCACHE_FLAT,
+       .volatile_reg = ac101_volatile_reg,
+};
+
+/* Sync reg_cache from the hardware */
+int ac10x_fill_regcache(struct device* dev, struct regmap* map) {
+       int r, i, n;
+       int v;
+
+       n = regmap_get_max_register(map);
+       for (i = 0; i < n; i++) {
+               regcache_cache_bypass(map, true);
+               r = regmap_read(map, i, &v);
+               if (r) {
+                       dev_err(dev, "failed to read register %d\n", i);
+                       continue;
+               }
+               regcache_cache_bypass(map, false);
+
+               regcache_cache_only(map, true);
+               r = regmap_write(map, i, v);
+               regcache_cache_only(map, false);
+       }
+       regcache_cache_bypass(map, false);
+       regcache_cache_only(map, false);
+
+       return 0;
+}
+
+int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+       struct ac10x_priv *ac10x = i2c_get_clientdata(i2c);
+       int ret = 0;
+       unsigned v = 0;
+
+       AC101_DBG();
+
+       static_ac10x = ac10x;
+
+       ac10x->regmap101 = devm_regmap_init_i2c(i2c, &ac101_regmap);
+       if (IS_ERR(ac10x->regmap101)) {
+               ret = PTR_ERR(ac10x->regmap101);
+               dev_err(&i2c->dev, "Fail to initialize I/O: %d\n", ret);
+               return ret;
+       }
+
+       /* Chip reset */
+       regcache_cache_only(ac10x->regmap101, false);
+       ret = regmap_write(ac10x->regmap101, CHIP_AUDIO_RST, 0);
+       msleep(50);
+
+       /* sync regcache for FLAT type */
+       ac10x_fill_regcache(&i2c->dev, ac10x->regmap101);
+
+       ret = regmap_read(ac10x->regmap101, CHIP_AUDIO_RST, &v);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "failed to read vendor ID: %d\n", ret);
+               return ret;
+       }
+
+       if (v != AC101_CHIP_ID) {
+               dev_err(&i2c->dev, "chip is not AC101 (%X)\n", v);
+               dev_err(&i2c->dev, "Expected %X\n", AC101_CHIP_ID);
+               return -ENODEV;
+       }
+
+       ret = sysfs_create_group(&i2c->dev.kobj, &audio_debug_attr_group);
+       if (ret) {
+               pr_err("failed to create attr group\n");
+       }
+
+       ac10x->gpiod_spk_amp_gate = devm_gpiod_get_optional(&i2c->dev, "spk-amp-switch", GPIOD_OUT_LOW);
+       if (IS_ERR(ac10x->gpiod_spk_amp_gate)) {
+               ac10x->gpiod_spk_amp_gate = NULL;
+               dev_err(&i2c->dev, "failed get spk-amp-switch in device tree\n");
+       }
+
+       return 0;
+}
+
+void ac101_shutdown(struct i2c_client *i2c)
+{
+       struct ac10x_priv *ac10x = i2c_get_clientdata(i2c);
+       struct snd_soc_codec *codec = ac10x->codec;
+       int reg_val;
+
+       if (codec == NULL) {
+               pr_err(": no sound card.\n");
+               return;
+       }
+
+       /*set headphone volume to 0*/
+       reg_val = ac101_read(codec, HPOUT_CTRL);
+       reg_val &= ~(0x3f<<HP_VOL);
+       ac101_write(codec, HPOUT_CTRL, reg_val);
+
+       /*disable pa*/
+       reg_val = ac101_read(codec, HPOUT_CTRL);
+       reg_val &= ~(0x1<<HPPA_EN);
+       ac101_write(codec, HPOUT_CTRL, reg_val);
+
+       /*hardware xzh support*/
+       reg_val = ac101_read(codec, OMIXER_DACA_CTRL);
+       reg_val &= ~(0xf<<HPOUTPUTENABLE);
+       ac101_write(codec, OMIXER_DACA_CTRL, reg_val);
+
+       /*unmute l/r headphone pa*/
+       reg_val = ac101_read(codec, HPOUT_CTRL);
+       reg_val &= ~((0x1<<RHPPA_MUTE)|(0x1<<LHPPA_MUTE));
+       ac101_write(codec, HPOUT_CTRL, reg_val);
+       return;
+}
+
+int ac101_remove(struct i2c_client *i2c)
+{
+       sysfs_remove_group(&i2c->dev.kobj, &audio_debug_attr_group);
+       return 0;
+}
+
+MODULE_DESCRIPTION("ASoC ac10x driver");
+MODULE_AUTHOR("huangxin,liushaohua");
+MODULE_AUTHOR("PeterYang<linsheng.yang@seeed.cc>");
diff --git a/sound/soc/codecs/ac101_regs.h b/sound/soc/codecs/ac101_regs.h
new file mode 100644 (file)
index 0000000..8be570e
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * ac101_regs.h
+ *
+ * (C) Copyright 2017-2018
+ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
+ *
+ * PeterYang <linsheng.yang@seeed.cc>
+ *
+ * (C) Copyright 2010-2017
+ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
+ * huangxin <huangxin@reuuimllatech.com>
+ *
+ * some simple description for this code
+ *
+ * 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.
+ *
+ */
+#ifndef __AC101_REGS_H__
+#define __AC101_REGS_H__
+
+/*pll source*/
+#define AC101_MCLK1 1
+#define AC101_MCLK2 2
+#define AC101_BCLK1 3
+#define AC101_BCLK2 4
+
+#define AIF1_CLK 1
+#define AIF2_CLK 2
+
+#define        CHIP_AUDIO_RST          0x0
+#define PLL_CTRL1              0x1
+#define PLL_CTRL2              0x2
+#define SYSCLK_CTRL            0x3
+#define MOD_CLK_ENA            0x4
+#define MOD_RST_CTRL           0x5
+#define AIF_SR_CTRL            0x6
+
+#define AIF1_CLK_CTRL          0x10
+#define AIF1_ADCDAT_CTRL       0x11
+#define AIF1_DACDAT_CTRL       0x12
+#define AIF1_MXR_SRC           0x13
+#define AIF1_VOL_CTRL1         0x14
+#define AIF1_VOL_CTRL2         0x15
+#define AIF1_VOL_CTRL3         0x16
+#define AIF1_VOL_CTRL4         0x17
+#define AIF1_MXR_GAIN          0x18
+#define AIF1_RXD_CTRL          0x19
+#define ADC_DIG_CTRL           0x40
+#define ADC_VOL_CTRL           0x41
+#define ADC_DBG_CTRL           0x42
+
+#define HMIC_CTRL1                     0x44
+#define HMIC_CTRL2                     0x45
+#define HMIC_STS                       0x46
+
+#define DAC_DIG_CTRL           0x48
+#define DAC_VOL_CTRL           0x49
+#define DAC_DBG_CTRL           0x4a
+#define DAC_MXR_SRC                    0x4c
+#define DAC_MXR_GAIN           0x4d
+
+#define ADC_APC_CTRL           0x50
+#define ADC_SRC                                0x51
+#define ADC_SRCBST_CTRL                0x52
+#define OMIXER_DACA_CTRL       0x53
+#define OMIXER_SR                      0x54
+#define OMIXER_BST1_CTRL       0x55
+#define HPOUT_CTRL                     0x56
+#define ESPKOUT_CTRL           0x57
+#define SPKOUT_CTRL                    0x58
+#define LOUT_CTRL                      0x59
+#define ADDA_TUNE1                     0x5a
+#define ADDA_TUNE2                     0x5b
+#define ADDA_TUNE3                     0x5c
+#define HPOUT_STR                      0x5d
+
+/*CHIP_AUDIO_RST*/
+#define AC101_CHIP_ID                  0x0101
+
+/*PLL_CTRL1*/
+#define DPLL_DAC_BIAS          14
+#define PLL_POSTDIV_M          8
+#define CLOSE_LOOP             6
+#define INT                    0
+
+/*PLL_CTRL2*/
+#define PLL_EN                 15
+#define PLL_LOCK_STATUS                14
+#define PLL_PREDIV_NI          4
+#define PLL_POSTDIV_NF         0
+
+/*SYSCLK_CTRL*/
+#define PLLCLK_ENA                     15
+#define PLLCLK_SRC                     12
+#define AIF1CLK_ENA                    11
+#define AIF1CLK_SRC                    8
+#define AIF2CLK_ENA                    7
+#define AIF2CLK_SRC                    4
+#define SYSCLK_ENA                     3
+#define SYSCLK_SRC                     0
+
+/*MOD_CLK_ENA*/
+#define MOD_CLK_AIF1           15
+#define MOD_CLK_AIF2           14
+#define MOD_CLK_AIF3           13
+#define MOD_CLK_SRC1           11
+#define MOD_CLK_SRC2           10
+#define MOD_CLK_HPF_AGC                7
+#define MOD_CLK_HPF_DRC                6
+#define MOD_CLK_ADC_DIG                3
+#define MOD_CLK_DAC_DIG                2
+
+/*MOD_RST_CTRL*/
+#define MOD_RESET_CTL          0
+#define MOD_RESET_AIF1         15
+#define MOD_RESET_AIF2         14
+#define MOD_RESET_AIF3         13
+#define MOD_RESET_SRC1         11
+#define MOD_RESET_SRC2         10
+#define MOD_RESET_HPF_AGC      7
+#define MOD_RESET_HPF_DRC      6
+#define MOD_RESET_ADC_DIG      3
+#define MOD_RESET_DAC_DIG      2
+
+/*AIF_SR_CTRL*/
+#define AIF1_FS                                12      //AIF1 Sample Rate
+#define AIF2_FS                                8       //AIF2 Sample Rate
+#define SRC1_ENA                       3
+#define SRC1_SRC                       2
+#define SRC2_ENA                       1
+#define SRC2_SRC                       0
+
+/*AIF1LCK_CTRL*/
+#define AIF1_MSTR_MOD          15
+#define AIF1_BCLK_INV          14
+#define AIF1_LRCK_INV          13
+#define AIF1_BCLK_DIV          9
+#define AIF1_LRCK_DIV          6
+#define AIF1_WORK_SIZ          4
+#define AIF1_DATA_FMT          2
+#define DSP_MONO_PCM           1
+#define AIF1_TDMM_ENA          0
+
+/*AIF1_ADCDAT_CTRL*/
+#define AIF1_AD0L_ENA          15
+#define AIF1_AD0R_ENA          14
+#define AIF1_AD1L_ENA          13
+#define AIF1_AD1R_ENA          12
+#define AIF1_AD0L_SRC          10
+#define AIF1_AD0R_SRC          8
+#define AIF1_AD1L_SRC          6
+#define AIF1_AD1R_SRC          4
+#define AIF1_ADCP_ENA          3
+#define AIF1_ADUL_ENA          2
+#define AIF1_SLOT_SIZ          0
+
+/*AIF1_DACDAT_CTRL*/
+#define AIF1_DA0L_ENA          15
+#define AIF1_DA0R_ENA          14
+#define AIF1_DA1L_ENA          13
+#define AIF1_DA1R_ENA          12
+#define AIF1_DA0L_SRC          10
+#define AIF1_DA0R_SRC          8
+#define AIF1_DA1L_SRC          6
+#define AIF1_DA1R_SRC          4
+#define AIF1_DACP_ENA          3
+#define AIF1_DAUL_ENA          2
+#define AIF1_SLOT_SIZ          0
+
+/*AIF1_MXR_SRC*/
+#define AIF1_AD0L_AIF1_DA0L_MXR                15
+#define AIF1_AD0L_AIF2_DACL_MXR                14
+#define AIF1_AD0L_ADCL_MXR             13
+#define AIF1_AD0L_AIF2_DACR_MXR                12
+#define AIF1_AD0R_AIF1_DA0R_MXR                11
+#define AIF1_AD0R_AIF2_DACR_MXR                10
+#define AIF1_AD0R_ADCR_MXR             9
+#define AIF1_AD0R_AIF2_DACL_MXR                8
+#define AIF1_AD1L_AIF2_DACL_MXR                7
+#define AIF1_AD1L_ADCL_MXR             6
+#define AIF1_AD1L_MXR_SRC      6
+#define AIF1_AD1R_AIF2_DACR_MXR                3
+#define AIF1_AD1R_ADCR_MXR             2
+#define AIF1_AD1R_MXR_SRC      2
+
+/*AIF1_VOL_CTRL1*/
+#define AIF1_AD0L_VOL          8
+#define AIF1_AD0R_VOL          0
+
+/*AIF1_VOL_CTRL2*/
+#define AIF1_AD1L_VOL          8
+#define AIF1_AD1R_VOL          0
+
+/*AIF1_VOL_CTRL3*/
+#define AIF1_DA0L_VOL          8
+#define AIF1_DA0R_VOL          0
+
+/*AIF1_VOL_CTRL4*/
+#define AIF1_DA1L_VOL          8
+#define AIF1_DA1R_VOL          0
+
+/*AIF1_MXR_GAIN*/
+#define AIF1_AD0L_MXR_GAIN     12
+#define AIF1_AD0R_MXR_GAIN     8
+#define AIF1_AD1L_MXR_GAIN     6
+#define AIF1_AD1R_MXR_GAIN     2
+
+/*AIF1_RXD_CTRL*/
+#define AIF1_N_DATA_DISCARD    8
+
+/*ADC_DIG_CTRL*/
+#define ENAD                           15
+#define ENDM                           14
+#define ADFIR32                                13
+#define ADOUT_DTS                      2
+#define ADOUT_DLY                      1
+
+/*ADC_VOL_CTRL*/
+#define ADC_VOL_L                      8
+#define ADC_VOL_R                      0
+
+/*ADC_DBG_CTRL*/
+#define ADSW                           15
+#define DMIC_CLK_PIN_CTRL      12
+
+/*HMIC_CTRL1*/
+#define HMIC_M                         12
+#define HMIC_N                         8
+#define HMIC_DATA_IRQ_MODE     7
+#define HMIC_TH1_HYSTERESIS    5
+#define HMIC_PULLOUT_IRQ       4
+#define HMIC_PLUGIN_IRQ                3
+#define HMIC_KEYUP_IRQ         2
+#define HMIC_KEYDOWN_IRQ       1
+#define HMIC_DATA_IRQ_EN       0
+
+/*HMIC_CTRL2*/
+#define HMIC_SAMPLE_SELECT     14
+#define HMIC_TH2_HYSTERESIS    13
+#define HMIC_TH2                       8
+#define HMIC_SF                                6
+#define KEYUP_CLEAR                    5
+#define HMIC_TH1                       0
+
+/*HMIC_STS*/
+#define HMIC_DATA                      8
+#define GET_HMIC_DATA(r)               (((r) >> HMIC_DATA) & 0x1F)
+#define HMIC_PULLOUT_PEND      4
+#define HMIC_PLUGIN_PEND       3
+#define HMIC_KEYUP_PEND                2
+#define HMKC_KEYDOWN_PEND      1
+#define HMIC_DATA_PEND         0
+#define HMIC_PEND_ALL          (0x1F)
+
+/*DAC_DIG_CTRL*/
+#define ENDA                           15
+#define ENHPF                          14
+#define DAFIR32                                13
+#define MODQU                          8
+
+/*DAC_VOL_CTRL*/
+#define DAC_VOL_L                      8
+#define DAC_VOL_R                      0
+
+/*DAC_DBG_CTRL*/
+#define DASW                           15
+#define ENDWA_N                                14
+#define DAC_MOD_DBG                    13
+#define DAC_PTN_SEL                    6
+#define DVC                            0
+
+/*DAC_MXR_SRC*/
+#define DACL_MXR_AIF1_DA0L             15
+#define DACL_MXR_AIF1_DA1L             14
+#define DACL_MXR_AIF2_DACL             13
+#define DACL_MXR_ADCL                  12
+#define DACL_MXR_SRC                   12
+#define DACR_MXR_AIF1_DA0R             11
+#define DACR_MXR_AIF1_DA1R             10
+#define DACR_MXR_AIF2_DACR             9
+#define DACR_MXR_ADCR                  8
+#define DACR_MXR_SRC           8
+
+/*DAC_MXR_GAIN*/
+#define DACL_MXR_GAIN          12
+#define DACR_MXR_GAIN          8
+
+/*ADC_APC_CTRL*/
+#define ADCREN                         15
+#define ADCRG                          12
+#define ADCLEN                         11
+#define ADCLG                          8
+#define MBIASEN                                7
+#define MMIC_BIAS_CHOP_EN              6
+#define MMIC_BIAS_CHOP_CKS             4
+#define HBIASMOD                       2
+#define HBIASEN                                1
+#define HBIASADCEN                     0
+
+/*ADC_SRC*/
+#define RADCMIXMUTEMIC1BOOST     (13)
+#define RADCMIXMUTEMIC2BOOST     (12)
+#define RADCMIXMUTELINEINLR              (11)
+#define RADCMIXMUTELINEINR               (10)
+#define RADCMIXMUTEAUXINR                (9)
+#define RADCMIXMUTEROUTPUT               (8)
+#define RADCMIXMUTELOUTPUT               (7)
+#define LADCMIXMUTEMIC1BOOST     (6)
+#define LADCMIXMUTEMIC2BOOST     (5)
+#define LADCMIXMUTELINEINLR              (4)
+#define LADCMIXMUTELINEINL               (3)
+#define LADCMIXMUTEAUXINL                (2)
+#define LADCMIXMUTELOUTPUT               (1)
+#define LADCMIXMUTEROUTPUT               (0)
+
+
+/*ADC_SRCBST_CTRL*/
+#define MIC1AMPEN                      15
+#define ADC_MIC1G                      12
+#define MIC2AMPEN                      11
+#define ADC_MIC2G                      8
+#define MIC2SLT                                7
+#define LINEIN_PREG                    4
+#define AUXI_PREG                      0
+
+/*OMIXER_DACA_CTRL*/
+#define DACAREN                                15
+#define DACALEN                                14
+#define RMIXEN                         13
+#define LMIXEN                         12
+#define HPOUTPUTENABLE                 8
+
+/*OMIXER_SR*/
+#define RMIXMUTEMIC1BOOST                (13)
+#define RMIXMUTEMIC2BOOST                (12)
+#define RMIXMUTELINEINLR                 (11)
+#define RMIXMUTELINEINR                          (10)
+#define RMIXMUTEAUXINR                   (9)
+#define RMIXMUTEDACR                     (8)
+#define RMIXMUTEDACL                     (7)
+#define LMIXMUTEMIC1BOOST                (6)
+#define LMIXMUTEMIC2BOOST                (5)
+#define LMIXMUTELINEINLR                 (4)
+#define LMIXMUTELINEINL                          (3)
+#define LMIXMUTEAUXINL                   (2)
+#define LMIXMUTEDACL                     (1)
+#define LMIXMUTEDACR                     (0)
+
+/*OMIXER_BST1_CTRL*/
+#define BIASVOLTAGE                    12
+#define AXG                            9
+#define OMIXER_MIC1G                   6
+#define OMIXER_MIC2G                   3
+#define LINEING                                0
+
+/*HPOUT_CTRL*/
+#define RHPS                           15
+#define LHPS                           14
+#define RHPPA_MUTE                     13
+#define LHPPA_MUTE                     12
+#define HPPA_EN                                11
+#define HP_VOL                         4
+#define HPPA_DEL                       2
+#define HPPA_IS                                0
+
+/*ESPKOUT_CTRL*/
+#define EAR_RAMP_TIME          11
+#define        ESPA_OUT_CURRENT        9
+#define ESPSR                          7
+#define ESPPA_MUTE                     6
+#define ESPPA_EN                       5
+#define ESP_VOL                                0
+
+/*SPKOUT_CTRL*/
+#define HPCALICKS                      13
+#define RSPKS                          12
+#define RSPKINVEN                      11
+#define RSPK_EN                                9
+#define LSPKS                          8
+#define LSPKINVEN                      7
+#define LSPK_EN                                5
+#define SPK_VOL                                0
+
+/*LOUT_CTRL*/
+#define LINEOUTG                       5
+#define LINEOUTEN                      4
+#define LINEOUTS0                      3
+#define LINEOUTS1                      2
+#define LINEOUTS2                      1
+#define LINEOUTS3                      0
+
+/*ADDA_TUNE1*/
+#define CURRENT_TEST_SELECT    14
+#define BIHE_CTRL                      12
+#define DITHER                         11
+#define DITHER_CLK                     9
+#define ZERO_CROSSOVER_EN      8
+#define ZERO_CROSSOVER_TIME 7
+#define EAR_SPEED_SELECT       6
+#define REF_CHOPPEN_CKS                4
+#define OPMIC_BIAS_CUR         0
+
+/*ADDA_TUNE2*/
+#define OPDAC_BIAS_CUR         14
+#define OPDRV_BIAS_CUR         12
+#define OPMIX_BIAS_CUR         10
+#define OPEAR_BIAS_CUR         8
+#define OPVR_BIAS_CUR          6
+#define OPAAF_BIAS_CUR         4
+#define OPADC1_BIAS_CUR                2
+#define OPADC2_BIAS_CUR                0
+
+/*ADDA_TUNE3*/
+#define LDOEN                          15
+#define LDO_SEL                                12
+#define BIASCALIVERIFY                 11
+#define BIASMODE                       10
+#define BIASCALIDATA                   9
+#define OSCS                           1
+#define OSCEN                          0
+
+/*HPOUT_STR*/
+#define HPVL_SOFT_MOD          14
+#define        HPVL_STEP_CTRL          8
+#define  DACA_CHND_ENA         7
+#define HPPA_MXRD_ENA          6
+#define HPVL_CTRL_OUT          0
+
+#endif//__AC101_REGS_H__
diff --git a/sound/soc/codecs/ac108.c b/sound/soc/codecs/ac108.c
new file mode 100644 (file)
index 0000000..617db21
--- /dev/null
@@ -0,0 +1,1582 @@
+/*
+ * ac10x.c  --  ac10x ALSA SoC Audio driver
+ *
+ *
+ * Author: Baozhu Zuo<zuobaozhu@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* #undef DEBUG
+ * use 'make DEBUG=1' to enable debugging
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/regmap.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "ac108.h"
+#include "ac10x.h"
+
+#define _USE_CAPTURE   1
+#define _MASTER_INDEX  0
+
+/**
+ * TODO: 
+ * 1, add PM API:  ac108_suspend,ac108_resume 
+ * 2,0x65-0x6a 
+ * 3,0x76-0x79 high 4bit 
+ */
+struct pll_div {
+       unsigned int freq_in;
+       unsigned int freq_out;
+       unsigned int m1;
+       unsigned int m2;
+       unsigned int n;
+       unsigned int k1;
+       unsigned int k2;
+};
+
+static struct ac10x_priv *ac10x;
+
+struct real_val_to_reg_val {
+       unsigned int real_val;
+       unsigned int reg_val;
+};
+
+static const struct real_val_to_reg_val ac108_sample_rate[] = {
+       { 8000,  0 },
+       { 11025, 1 },
+       { 12000, 2 },
+       { 16000, 3 },
+       { 22050, 4 },
+       { 24000, 5 },
+       { 32000, 6 },
+       { 44100, 7 },
+       { 48000, 8 },
+       { 96000, 9 },
+};
+
+/* Sample resolution */
+static const struct real_val_to_reg_val ac108_samp_res[] = {
+       { 8,  1 },
+       { 12, 2 },
+       { 16, 3 },
+       { 20, 4 },
+       { 24, 5 },
+       { 28, 6 },
+       { 32, 7 },
+};
+
+static const unsigned ac108_bclkdivs[] = {
+        0,   1,   2,   4,
+        6,   8,  12,  16,
+       24,  32,  48,  64,
+       96, 128, 176, 192,
+};
+
+/* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ; M1[0,31],  M2[0,1],  N[0,1023],  K1[0,31],  K2[0,1] */
+static const struct pll_div ac108_pll_div_list[] = {
+       { 400000,   _FREQ_24_576K, 0,  0, 614, 4, 1 },
+       { 512000,   _FREQ_24_576K, 0,  0, 960, 9, 1 }, //_FREQ_24_576K/48
+       { 768000,   _FREQ_24_576K, 0,  0, 640, 9, 1 }, //_FREQ_24_576K/32
+       { 800000,   _FREQ_24_576K, 0,  0, 614, 9, 1 },
+       { 1024000,  _FREQ_24_576K, 0,  0, 480, 9, 1 }, //_FREQ_24_576K/24
+       { 1600000,  _FREQ_24_576K, 0,  0, 307, 9, 1 },
+       { 2048000,  _FREQ_24_576K, 0,  0, 240, 9, 1 }, /* accurate,  8000 * 256 */
+       { 3072000,  _FREQ_24_576K, 0,  0, 160, 9, 1 }, /* accurate, 12000 * 256 */
+       { 4096000,  _FREQ_24_576K, 2,  0, 360, 9, 1 }, /* accurate, 16000 * 256 */
+       { 6000000,  _FREQ_24_576K, 4,  0, 410, 9, 1 },
+       { 12000000, _FREQ_24_576K, 9,  0, 410, 9, 1 },
+       { 13000000, _FREQ_24_576K, 8,  0, 340, 9, 1 },
+       { 15360000, _FREQ_24_576K, 12, 0, 415, 9, 1 },
+       { 16000000, _FREQ_24_576K, 12, 0, 400, 9, 1 },
+       { 19200000, _FREQ_24_576K, 15, 0, 410, 9, 1 },
+       { 19680000, _FREQ_24_576K, 15, 0, 400, 9, 1 },
+       { 24000000, _FREQ_24_576K, 4,  0, 128,24, 0 }, // accurate, 24M -> 24.576M */
+
+       { 400000,   _FREQ_22_579K, 0,  0, 566, 4, 1 },
+       { 512000,   _FREQ_22_579K, 0,  0, 880, 9, 1 },
+       { 768000,   _FREQ_22_579K, 0,  0, 587, 9, 1 },
+       { 800000,   _FREQ_22_579K, 0,  0, 567, 9, 1 },
+       { 1024000,  _FREQ_22_579K, 0,  0, 440, 9, 1 },
+       { 1600000,  _FREQ_22_579K, 1,  0, 567, 9, 1 },
+       { 2048000,  _FREQ_22_579K, 0,  0, 220, 9, 1 },
+       { 3072000,  _FREQ_22_579K, 0,  0, 148, 9, 1 },
+       { 4096000,  _FREQ_22_579K, 2,  0, 330, 9, 1 },
+       { 6000000,  _FREQ_22_579K, 2,  0, 227, 9, 1 },
+       { 12000000, _FREQ_22_579K, 8,  0, 340, 9, 1 },
+       { 13000000, _FREQ_22_579K, 9,  0, 350, 9, 1 },
+       { 15360000, _FREQ_22_579K, 10, 0, 325, 9, 1 },
+       { 16000000, _FREQ_22_579K, 11, 0, 340, 9, 1 },
+       { 19200000, _FREQ_22_579K, 13, 0, 330, 9, 1 },
+       { 19680000, _FREQ_22_579K, 14, 0, 345, 9, 1 },
+       { 24000000, _FREQ_22_579K, 24, 0, 588,24, 0 }, // accurate, 24M -> 22.5792M */
+
+
+       { _FREQ_24_576K / 1,   _FREQ_24_576K, 9,  0, 200, 9, 1 }, //_FREQ_24_576K
+       { _FREQ_24_576K / 2,   _FREQ_24_576K, 9,  0, 400, 9, 1 }, /*12288000,accurate, 48000 * 256 */
+       { _FREQ_24_576K / 4,   _FREQ_24_576K, 4,  0, 400, 9, 1 }, /*6144000, accurate, 24000 * 256 */
+       { _FREQ_24_576K / 16,  _FREQ_24_576K, 0,  0, 320, 9, 1 }, //1536000
+       { _FREQ_24_576K / 64,  _FREQ_24_576K, 0,  0, 640, 4, 1 }, //384000
+       { _FREQ_24_576K / 96,  _FREQ_24_576K, 0,  0, 960, 4, 1 }, //256000
+       { _FREQ_24_576K / 128, _FREQ_24_576K, 0,  0, 512, 1, 1 }, //192000
+       { _FREQ_24_576K / 176, _FREQ_24_576K, 0,  0, 880, 4, 0 }, //140000
+       { _FREQ_24_576K / 192, _FREQ_24_576K, 0,  0, 960, 4, 0 }, //128000
+
+       { _FREQ_22_579K / 1,   _FREQ_22_579K, 9,  0, 200, 9, 1 }, //_FREQ_22_579K
+       { _FREQ_22_579K / 2,   _FREQ_22_579K, 9,  0, 400, 9, 1 }, /*11289600,accurate, 44100 * 256 */
+       { _FREQ_22_579K / 4,   _FREQ_22_579K, 4,  0, 400, 9, 1 }, /*5644800, accurate, 22050 * 256 */
+       { _FREQ_22_579K / 16,  _FREQ_22_579K, 0,  0, 320, 9, 1 }, //1411200
+       { _FREQ_22_579K / 64,  _FREQ_22_579K, 0,  0, 640, 4, 1 }, //352800
+       { _FREQ_22_579K / 96,  _FREQ_22_579K, 0,  0, 960, 4, 1 }, //235200
+       { _FREQ_22_579K / 128, _FREQ_22_579K, 0,  0, 512, 1, 1 }, //176400
+       { _FREQ_22_579K / 176, _FREQ_22_579K, 0,  0, 880, 4, 0 }, //128290
+       { _FREQ_22_579K / 192, _FREQ_22_579K, 0,  0, 960, 4, 0 }, //117600
+
+       { _FREQ_22_579K / 6,   _FREQ_22_579K, 2,  0, 360, 9, 1 }, //3763200
+       { _FREQ_22_579K / 8,   _FREQ_22_579K, 0,  0, 160, 9, 1 }, /*2822400, accurate, 11025 * 256 */
+       { _FREQ_22_579K / 12,  _FREQ_22_579K, 0,  0, 240, 9, 1 }, //1881600
+       { _FREQ_22_579K / 24,  _FREQ_22_579K, 0,  0, 480, 9, 1 }, //940800
+       { _FREQ_22_579K / 32,  _FREQ_22_579K, 0,  0, 640, 9, 1 }, //705600
+       { _FREQ_22_579K / 48,  _FREQ_22_579K, 0,  0, 960, 9, 1 }, //470400
+};
+
+
+/* AC108 definition */
+#define AC108_CHANNELS_MAX             8               /* range[1, 16] */
+#define AC108_RATES                    (SNDRV_PCM_RATE_8000_96000 &            \
+                                       ~(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000 | \
+                                       SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000))
+#define AC108_FORMATS                  (/*SNDRV_PCM_FMTBIT_S16_LE | \
+                                       SNDRV_PCM_FMTBIT_S20_3LE |   \
+                                       SNDRV_PCM_FMTBIT_S24_LE |*/  \
+                                       SNDRV_PCM_FMTBIT_S32_LE)
+
+static const DECLARE_TLV_DB_SCALE(tlv_adc_pga_gain, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925,75,0);
+
+int ac10x_read(u8 reg, u8* rt_val, struct regmap* i2cm) {
+       int r, v = 0;
+
+       if ((r = regmap_read(i2cm, reg, &v)) < 0) {
+               pr_err("ac10x_read error->[REG-0x%02x]\n", reg);
+       } else {
+               *rt_val = v;
+       }
+       return r;
+}
+
+int ac10x_write(u8 reg, u8 val, struct regmap* i2cm) {
+       int r;
+
+       if ((r = regmap_write(i2cm, reg, val)) < 0) {
+               pr_err("ac10x_write error->[REG-0x%02x,val-0x%02x]\n", reg, val);
+       }
+       return r;
+}
+
+int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct regmap* i2cm) {
+       int r;
+
+       if ((r = regmap_update_bits(i2cm, reg, mask, val)) < 0) {
+               pr_err("%s() error->[REG-0x%02x,val-0x%02x]\n", __func__, reg, val);
+       }
+       return r;
+}
+
+/**
+ * snd_ac108_get_volsw - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+static int snd_ac108_get_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol
+){
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int mask = (1 << fls(mc->max)) - 1;
+       unsigned int invert = mc->invert;
+       int ret, chip = mc->autodisable;
+       u8 val;
+
+       if ((ret = ac10x_read(mc->reg, &val, ac10x->i2cmap[chip])) < 0)
+               return ret;
+
+       val = ((val >> mc->shift) & mask) - mc->min;
+       if (invert) {
+               val = mc->max - val;
+       }
+       ucontrol->value.integer.value[0] = val;
+       return 0;
+}
+
+/**
+ * snd_ac108_put_volsw - single mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+static int snd_ac108_put_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol
+){
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int sign_bit = mc->sign_bit;
+       unsigned int val, mask = (1 << fls(mc->max)) - 1;
+       unsigned int invert = mc->invert;
+       int ret, chip = mc->autodisable;
+
+       if (sign_bit)
+               mask = BIT(sign_bit + 1) - 1;
+
+       val = ((ucontrol->value.integer.value[0] + mc->min) & mask);
+       if (invert) {
+               val = mc->max - val;
+       }
+
+       mask = mask << mc->shift;
+       val = val << mc->shift;
+
+       ret = ac10x_update_bits(mc->reg, mask, val, ac10x->i2cmap[chip]);
+       return ret;
+}
+
+#define SOC_AC108_SINGLE_TLV(xname, reg, shift, max, invert, chip, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, .get = snd_ac108_get_volsw,\
+       .put = snd_ac108_put_volsw, \
+       .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, chip) }
+
+/* single ac108 */
+static const struct snd_kcontrol_new ac108_snd_controls[] = {
+       /* ### chip 0 ### */
+       /*0x70: ADC1 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL, 0, 0xff, 0, 0, tlv_ch_digital_vol),
+       /*0x71: ADC2 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL, 0, 0xff, 0, 0, tlv_ch_digital_vol),
+       /*0x72: ADC3 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL, 0, 0xff, 0, 0, tlv_ch_digital_vol),
+       /*0x73: ADC4 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL, 0, 0xff, 0, 0, tlv_ch_digital_vol),
+
+       /*0x90: Analog PGA1 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL, ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
+       /*0x91: Analog PGA2 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL, ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
+       /*0x92: Analog PGA3 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL, ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
+       /*0x93: Analog PGA4 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL, ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
+};
+/* multiple ac108s */
+static const struct snd_kcontrol_new ac108tdm_snd_controls[] = {
+       /* ### chip 1 ### */
+       /*0x70: ADC1 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL, 0, 0xff, 0, 1, tlv_ch_digital_vol),
+       /*0x71: ADC2 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL, 0, 0xff, 0, 1, tlv_ch_digital_vol),
+       /*0x72: ADC3 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL, 0, 0xff, 0, 1, tlv_ch_digital_vol),
+       /*0x73: ADC4 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL, 0, 0xff, 0, 1, tlv_ch_digital_vol),
+
+       /*0x90: Analog PGA1 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL, ADC1_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
+       /*0x91: Analog PGA2 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL, ADC2_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
+       /*0x92: Analog PGA3 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL, ADC3_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
+       /*0x93: Analog PGA4 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL, ADC4_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
+
+       /* ### chip 0 ### */
+       /*0x70: ADC1 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH5 digital volume", ADC1_DVOL_CTRL, 0, 0xff, 0, 0, tlv_ch_digital_vol),
+       /*0x71: ADC2 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH6 digital volume", ADC2_DVOL_CTRL, 0, 0xff, 0, 0, tlv_ch_digital_vol),
+       /*0x72: ADC3 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH7 digital volume", ADC3_DVOL_CTRL, 0, 0xff, 0, 0, tlv_ch_digital_vol),
+       /*0x73: ADC4 Digital Channel Volume Control Register*/
+       SOC_AC108_SINGLE_TLV("CH8 digital volume", ADC4_DVOL_CTRL, 0, 0xff, 0, 0, tlv_ch_digital_vol),
+
+       /*0x90: Analog PGA1 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC5 PGA gain", ANA_PGA1_CTRL, ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
+       /*0x91: Analog PGA2 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC6 PGA gain", ANA_PGA2_CTRL, ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
+       /*0x92: Analog PGA3 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC7 PGA gain", ANA_PGA3_CTRL, ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
+       /*0x93: Analog PGA4 Control Register*/
+       SOC_AC108_SINGLE_TLV("ADC8 PGA gain", ANA_PGA4_CTRL, ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
+};
+
+
+static const struct snd_soc_dapm_widget ac108_dapm_widgets[] = {
+       //input widgets
+       SND_SOC_DAPM_INPUT("MIC1P"),
+       SND_SOC_DAPM_INPUT("MIC1N"),
+
+       SND_SOC_DAPM_INPUT("MIC2P"),
+       SND_SOC_DAPM_INPUT("MIC2N"),
+
+       SND_SOC_DAPM_INPUT("MIC3P"),
+       SND_SOC_DAPM_INPUT("MIC3N"),
+
+       SND_SOC_DAPM_INPUT("MIC4P"),
+       SND_SOC_DAPM_INPUT("MIC4N"),
+
+       SND_SOC_DAPM_INPUT("DMIC1"),
+       SND_SOC_DAPM_INPUT("DMIC2"),
+
+       /*0xa0: ADC1 Analog Control 1 Register*/
+       /*0xa1-0xa6:use the defualt value*/
+       SND_SOC_DAPM_AIF_IN("Channel 1 AAF", "Capture", 0, ANA_ADC1_CTRL1, ADC1_DSM_ENABLE, 1),
+       SND_SOC_DAPM_SUPPLY("Channel 1 EN", ANA_ADC1_CTRL1, ADC1_PGA_ENABLE, 1, NULL, 0),
+       SND_SOC_DAPM_MICBIAS("MIC1BIAS", ANA_ADC1_CTRL1, ADC1_MICBIAS_EN, 1),
+
+       /*0xa7: ADC2 Analog Control 1 Register*/
+       /*0xa8-0xad:use the defualt value*/
+       SND_SOC_DAPM_AIF_IN("Channel 2 AAF", "Capture", 0, ANA_ADC2_CTRL1, ADC2_DSM_ENABLE, 1),
+       SND_SOC_DAPM_SUPPLY("Channel 2 EN", ANA_ADC2_CTRL1, ADC2_PGA_ENABLE, 1, NULL, 0),
+       SND_SOC_DAPM_MICBIAS("MIC2BIAS", ANA_ADC2_CTRL1, ADC2_MICBIAS_EN, 1),
+
+       /*0xae: ADC3 Analog Control 1 Register*/
+       /*0xaf-0xb4:use the defualt value*/
+       SND_SOC_DAPM_AIF_IN("Channel 3 AAF", "Capture", 0, ANA_ADC3_CTRL1, ADC3_DSM_ENABLE, 1),
+       SND_SOC_DAPM_SUPPLY("Channel 3 EN", ANA_ADC3_CTRL1, ADC3_PGA_ENABLE, 1, NULL, 0),
+       SND_SOC_DAPM_MICBIAS("MIC3BIAS", ANA_ADC3_CTRL1, ADC3_MICBIAS_EN, 1),
+
+       /*0xb5: ADC4 Analog Control 1 Register*/
+       /*0xb6-0xbb:use the defualt value*/
+       SND_SOC_DAPM_AIF_IN("Channel 4 AAF", "Capture", 0, ANA_ADC4_CTRL1, ADC4_DSM_ENABLE, 1),
+       SND_SOC_DAPM_SUPPLY("Channel 4 EN", ANA_ADC4_CTRL1, ADC4_PGA_ENABLE, 1, NULL, 0),
+       SND_SOC_DAPM_MICBIAS("MIC4BIAS", ANA_ADC4_CTRL1, ADC4_MICBIAS_EN, 1),
+
+
+       /*0x61: ADC Digital Part Enable Register*/
+       SND_SOC_DAPM_SUPPLY("ADC EN", ADC_DIG_EN, 4,  1, NULL, 0),
+       SND_SOC_DAPM_ADC("ADC1", "Capture", ADC_DIG_EN, 0,  1),
+       SND_SOC_DAPM_ADC("ADC2", "Capture", ADC_DIG_EN, 1,  1),
+       SND_SOC_DAPM_ADC("ADC3", "Capture", ADC_DIG_EN, 2,  1),
+       SND_SOC_DAPM_ADC("ADC4", "Capture", ADC_DIG_EN, 3,  1),
+
+       SND_SOC_DAPM_SUPPLY("ADC1 CLK", ANA_ADC4_CTRL7, ADC1_CLK_GATING, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC2 CLK", ANA_ADC4_CTRL7, ADC2_CLK_GATING, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC3 CLK", ANA_ADC4_CTRL7, ADC3_CLK_GATING, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC4 CLK", ANA_ADC4_CTRL7, ADC4_CLK_GATING, 1, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("DSM EN", ANA_ADC4_CTRL6, DSM_DEMOFF, 1, NULL, 0),
+
+       /*0x62:Digital MIC Enable Register*/
+       SND_SOC_DAPM_MICBIAS("DMIC1 enable", DMIC_EN, 0, 0),
+       SND_SOC_DAPM_MICBIAS("DMIC2 enable", DMIC_EN, 1, 0),
+};
+
+static const struct snd_soc_dapm_route ac108_dapm_routes[] = {
+
+       { "ADC1", NULL, "Channel 1 AAF" },
+       { "ADC2", NULL, "Channel 2 AAF" },
+       { "ADC3", NULL, "Channel 3 AAF" },
+       { "ADC4", NULL, "Channel 4 AAF" },
+
+       { "Channel 1 AAF", NULL, "MIC1BIAS" },
+       { "Channel 2 AAF", NULL, "MIC2BIAS" },
+       { "Channel 3 AAF", NULL, "MIC3BIAS" },
+       { "Channel 4 AAF", NULL, "MIC4BIAS" },
+
+       { "MIC1BIAS", NULL, "ADC1 CLK" },
+       { "MIC2BIAS", NULL, "ADC2 CLK" },
+       { "MIC3BIAS", NULL, "ADC3 CLK" },
+       { "MIC4BIAS", NULL, "ADC4 CLK" },
+
+
+       { "ADC1 CLK", NULL, "DSM EN" },
+       { "ADC2 CLK", NULL, "DSM EN" },
+       { "ADC3 CLK", NULL, "DSM EN" },
+       { "ADC4 CLK", NULL, "DSM EN" },
+
+
+       { "DSM EN", NULL, "ADC EN" },
+
+       { "Channel 1 EN", NULL, "DSM EN" },
+       { "Channel 2 EN", NULL, "DSM EN" },
+       { "Channel 3 EN", NULL, "DSM EN" },
+       { "Channel 4 EN", NULL, "DSM EN" },
+
+
+       { "MIC1P", NULL, "Channel 1 EN" },
+       { "MIC1N", NULL, "Channel 1 EN" },
+
+       { "MIC2P", NULL, "Channel 2 EN" },
+       { "MIC2N", NULL, "Channel 2 EN" },
+
+       { "MIC3P", NULL, "Channel 3 EN" },
+       { "MIC3N", NULL, "Channel 3 EN" },
+
+       { "MIC4P", NULL, "Channel 4 EN" },
+       { "MIC4N", NULL, "Channel 4 EN" },
+
+};
+
+static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x) {
+       u8 i;
+       for (i = 0; i < ac10x->codec_cnt; i++) {
+               ac10x_write(reg, val, ac10x->i2cmap[i]);
+       }
+       return 0;
+}
+
+static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x) {
+       int r = 0;
+       u8 i;
+
+       for (i = 0; i < ac10x->codec_cnt; i++) {
+               r |= ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]);
+       }
+       return r;
+}
+
+static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg) {
+       unsigned char val_r;
+       struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
+       /*read one chip is fine*/
+       ac10x_read(reg, &val_r, ac10x->i2cmap[_MASTER_INDEX]);
+       return val_r;
+}
+
+static int ac108_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) {
+       struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
+       ac108_multi_write(reg, val, ac10x);
+       return 0;
+}
+
+/**
+ * The Power management related registers are Reg01h~Reg09h
+ * 0x01-0x05,0x08,use the default value
+ * @author baozhu (17-6-21)
+ * 
+ * @param ac10x 
+ */
+static void ac108_configure_power(struct ac10x_priv *ac10x) {
+       /**
+        * 0x06:Enable Analog LDO
+        */
+       ac108_multi_update_bits(PWR_CTRL6, 0x01 << LDO33ANA_ENABLE, 0x01 << LDO33ANA_ENABLE, ac10x);
+       /**
+        * 0x07: 
+        * Control VREF output and micbias voltage ? 
+        * REF faststart disable, enable Enable VREF (needed for Analog 
+        * LDO and MICBIAS) 
+        */
+       ac108_multi_update_bits(PWR_CTRL7, 0x1f << VREF_SEL | 0x01 << VREF_FASTSTART_ENABLE | 0x01 << VREF_ENABLE,
+                                          0x13 << VREF_SEL | 0x00 << VREF_FASTSTART_ENABLE | 0x01 << VREF_ENABLE, ac10x);
+       /**
+        * 0x09: 
+        * Disable fast-start circuit on VREFP 
+        * VREFP_RESCTRL=00=1 MOhm 
+        * IGEN_TRIM=100=+25% 
+        * Enable VREFP (needed by all audio input channels) 
+        */
+       ac108_multi_update_bits(PWR_CTRL9, 0x01 << VREFP_FASTSTART_ENABLE | 0x03 << VREFP_RESCTRL | 0x07 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
+                                          0x00 << VREFP_FASTSTART_ENABLE | 0x00 << VREFP_RESCTRL | 0x04 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
+                                          ac10x);
+}
+
+/**
+ * The clock management related registers are Reg20h~Reg25h
+ * The PLL management related registers are Reg10h~Reg18h.
+ * @author baozhu (17-6-20)
+ * 
+ * @param ac10x 
+ * @param rate : sample rate
+ * 
+ * @return int : fail or success
+ */
+static int ac108_config_pll(struct ac10x_priv *ac10x, unsigned rate, unsigned lrck_ratio) {
+       unsigned int i = 0;
+       struct pll_div ac108_pll_div = { 0 };
+
+       if (ac10x->clk_id == SYSCLK_SRC_PLL) {
+               unsigned pll_src, pll_freq_in;
+
+               if (lrck_ratio == 0) {
+                       /* PLL clock source from MCLK */
+                       pll_freq_in = ac10x->sysclk;
+                       pll_src = 0x0;
+               } else {
+                       /* PLL clock source from BCLK */
+                       pll_freq_in = rate * lrck_ratio;
+                       pll_src = 0x1;
+               }
+
+               /* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] */
+               for (i = 0; i < ARRAY_SIZE(ac108_pll_div_list); i++) {
+                       if (ac108_pll_div_list[i].freq_in == pll_freq_in && ac108_pll_div_list[i].freq_out % rate == 0) {
+                               ac108_pll_div = ac108_pll_div_list[i];
+                               dev_dbg(&ac10x->i2c[_MASTER_INDEX]->dev, "AC108 PLL freq_in match:%u, freq_out:%u\n\n",
+                                                               ac108_pll_div.freq_in, ac108_pll_div.freq_out);
+                               break;
+                       }
+               }
+               /* 0x11,0x12,0x13,0x14: Config PLL DIV param M1/M2/N/K1/K2 */
+               ac108_multi_update_bits(PLL_CTRL5, 0x1f << PLL_POSTDIV1 | 0x01 << PLL_POSTDIV2,
+                                                  ac108_pll_div.k1 << PLL_POSTDIV1 | ac108_pll_div.k2 << PLL_POSTDIV2, ac10x);
+               ac108_multi_update_bits(PLL_CTRL4, 0xff << PLL_LOOPDIV_LSB, (unsigned char)ac108_pll_div.n << PLL_LOOPDIV_LSB, ac10x);
+               ac108_multi_update_bits(PLL_CTRL3, 0x03 << PLL_LOOPDIV_MSB, (ac108_pll_div.n >> 8) << PLL_LOOPDIV_MSB, ac10x);
+               ac108_multi_update_bits(PLL_CTRL2, 0x1f << PLL_PREDIV1 | 0x01 << PLL_PREDIV2,
+                                                   ac108_pll_div.m1 << PLL_PREDIV1 | ac108_pll_div.m2 << PLL_PREDIV2, ac10x);
+
+               /*0x18: PLL clk lock enable*/
+               ac108_multi_update_bits(PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN, 0x1 << PLL_LOCK_EN, ac10x);
+
+               /**
+                * 0x20: enable pll, pll source from mclk/bclk, sysclk source from pll, enable sysclk
+                */
+               ac108_multi_update_bits(SYSCLK_CTRL, 0x01 << PLLCLK_EN | 0x03  << PLLCLK_SRC | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
+                                                    0x01 << PLLCLK_EN |pll_src<< PLLCLK_SRC | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac10x);
+               ac10x->mclk = ac108_pll_div.freq_out;
+       }
+       if (ac10x->clk_id == SYSCLK_SRC_MCLK) {
+               /**
+                *0x20: sysclk source from mclk, enable sysclk
+                */
+               ac108_multi_update_bits(SYSCLK_CTRL, 0x01 << PLLCLK_EN | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
+                                                    0x00 << PLLCLK_EN | 0x00 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac10x);
+               ac10x->mclk = ac10x->sysclk;
+       }
+
+       return 0;
+}
+
+/*
+ * support no more than 16 slots.
+ */
+static int ac108_multi_chips_slots(struct ac10x_priv *ac, int slots) {
+       int i;
+
+       /*
+        * codec0 enable slots 2,3,0,1 when 1 codec
+        *
+        * codec0 enable slots 6,7,0,1 when 2 codec
+        * codec1 enable slots 2,3,4,5
+        *
+        * ...
+        */
+       for (i = 0; i < ac->codec_cnt; i++) {
+               /* rotate map, due to channels rotated by CPU_DAI */
+               const unsigned vec_mask[] = {
+                       0x3 << 6 | 0x3, // slots 6,7,0,1
+                       0xF << 2,       // slots 2,3,4,5
+                       0,
+                       0,
+               };
+               const unsigned vec_maps[] = {
+                       /*
+                        * chip 0,
+                        * mic 0 sample -> slot 6
+                        * mic 1 sample -> slot 7
+                        * mic 2 sample -> slot 0
+                        * mic 3 sample -> slot 1
+                        */
+                       0x0 << 12 | 0x1 << 14 | 0x2 << 0 | 0x3 << 2,
+                       /*
+                        * chip 1,
+                        * mic 0 sample -> slot 2
+                        * mic 1 sample -> slot 3
+                        * mic 2 sample -> slot 4
+                        * mic 3 sample -> slot 5
+                        */
+                       0x0 << 4  | 0x1 << 6  | 0x2 << 8 | 0x3 << 10,
+                       0,
+                       0,
+               };
+               unsigned vec;
+
+               /* 0x38-0x3A I2S_TX1_CTRLx */
+               if (ac->codec_cnt == 1) {
+                       vec = 0xFUL;
+               } else {
+                       vec = vec_mask[i];
+               }
+               ac10x_write(I2S_TX1_CTRL1, slots - 1, ac->i2cmap[i]);
+               ac10x_write(I2S_TX1_CTRL2, (vec >> 0) & 0xFF, ac->i2cmap[i]);
+               ac10x_write(I2S_TX1_CTRL3, (vec >> 8) & 0xFF, ac->i2cmap[i]);
+
+               /* 0x3C-0x3F I2S_TX1_CHMP_CTRLx */
+               if (ac->codec_cnt == 1) {
+                       vec = (0x2 << 0 | 0x3 << 2 | 0x0 << 4 | 0x1 << 6);
+               } else if (ac->codec_cnt == 2) {
+                       vec = vec_maps[i];
+               }
+
+               ac10x_write(I2S_TX1_CHMP_CTRL1, (vec >>  0) & 0xFF, ac->i2cmap[i]);
+               ac10x_write(I2S_TX1_CHMP_CTRL2, (vec >>  8) & 0xFF, ac->i2cmap[i]);
+               ac10x_write(I2S_TX1_CHMP_CTRL3, (vec >> 16) & 0xFF, ac->i2cmap[i]);
+               ac10x_write(I2S_TX1_CHMP_CTRL4, (vec >> 24) & 0xFF, ac->i2cmap[i]);
+       }
+       return 0;
+}
+
+static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) {
+       unsigned int i, channels, samp_res, rate;
+       struct snd_soc_codec *codec = dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       unsigned bclkdiv;
+       int ret = 0;
+       u8 v;
+
+       dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__,
+                       snd_pcm_stream_str(substream),
+                       dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK], dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]);
+
+       if (ac10x->i2c101) {
+               ret = ac101_hw_params(substream, params, dai);
+               if (ret > 0) {
+                       dev_dbg(dai->dev, "%s() L%d returned\n", __func__, __LINE__);
+                       /* not configure hw_param twice */
+                       return 0;
+               }
+       }
+
+       if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK])
+        || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->stream_active[SNDRV_PCM_STREAM_CAPTURE])) {
+               /* not configure hw_param twice */
+               /* return 0; */
+       }
+
+       channels = params_channels(params);
+
+       /* Master mode, to clear cpu_dai fifos, output bclk without lrck */
+       ac10x_read(I2S_CTRL, &v, ac10x->i2cmap[_MASTER_INDEX]);
+       if (v & (0x01 << BCLK_IOEN)) {
+               ac10x_update_bits(I2S_CTRL, 0x1 << LRCK_IOEN, 0x0 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
+       }
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S8:
+               samp_res = 0;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               samp_res = 2;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               samp_res = 3;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               samp_res = 4;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               samp_res = 6;
+               break;
+       default:
+               pr_err("AC108 don't supported the sample resolution: %u\n", params_format(params));
+               return -EINVAL;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(ac108_sample_rate); i++) {
+               if (ac108_sample_rate[i].real_val == params_rate(params) / (ac10x->data_protocol + 1UL)) {
+                       rate = i;
+                       break;
+               }
+       }
+       if (i >= ARRAY_SIZE(ac108_sample_rate)) {
+               return -EINVAL;
+       }
+
+       if (channels == 8 && ac108_sample_rate[rate].real_val == 96000) {
+               /* 24.576M bit clock is not support by ac108 */
+               return -EINVAL;
+       }
+
+       dev_dbg(dai->dev, "rate: %d , channels: %d , samp_res: %d",
+                       ac108_sample_rate[rate].real_val,
+                       channels,
+                       ac108_samp_res[samp_res].real_val);
+
+       /**
+        * 0x33: 
+        *  The 8-Low bit of LRCK period value. It is used to program
+        *  the number of BCLKs per channel of sample frame. This value
+        *  is interpreted as follow:
+        *  The 8-Low bit of LRCK period value. It is used to program
+        *  the number of BCLKs per channel of sample frame. This value
+        *  is interpreted as follow: PCM mode: Number of BCLKs within
+        *  (Left + Right) channel width I2S / Left-Justified /
+        *  Right-Justified mode: Number of BCLKs within each individual
+        *  channel width (Left or Right) N+1
+        *  For example:
+        *  n = 7: 8 BCLK width
+        *  …
+        *  n = 1023: 1024 BCLKs width
+        *  0X32[0:1]:
+        *  The 2-High bit of LRCK period value. 
+        */
+       if (ac10x->i2s_mode != PCM_FORMAT) {
+               if (ac10x->data_protocol) {
+                       ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1, ac10x);
+                       /*encoding mode, the max LRCK period value < 32,so the 2-High bit is zero*/
+                       ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
+               } else {
+                       /*TDM mode or normal mode*/
+                       //ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
+                       ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1, ac10x);
+                       ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
+               }
+       } else {
+               unsigned div;
+
+               /*TDM mode or normal mode*/
+               div = ac108_samp_res[samp_res].real_val * channels - 1;
+               ac108_multi_write(I2S_LRCK_CTRL2, (div & 0xFF), ac10x);
+               ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, (div >> 8) << 0, ac10x);
+       }
+
+       /**
+        * 0x35: 
+        * TX Encoding mode will add  4bits to mark channel number 
+        * TODO: need a chat to explain this 
+        */
+       ac108_multi_update_bits(I2S_FMT_CTRL2, 0x07 << SAMPLE_RESOLUTION | 0x07 << SLOT_WIDTH_SEL,
+                                               ac108_samp_res[samp_res].reg_val << SAMPLE_RESOLUTION
+                                                 | ac108_samp_res[samp_res].reg_val << SLOT_WIDTH_SEL, ac10x);
+
+       /**
+        * 0x60: 
+        * ADC Sample Rate synchronised with I2S1 clock zone 
+        */
+       ac108_multi_update_bits(ADC_SPRC, 0x0f << ADC_FS_I2S1, ac108_sample_rate[rate].reg_val << ADC_FS_I2S1, ac10x);
+       ac108_multi_write(HPF_EN, 0x0F, ac10x);
+
+       if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
+               ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, ac108_samp_res[samp_res].real_val * channels);
+       } else {
+               ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, 0);
+       }
+
+       /*
+        * master mode only
+        */
+       bclkdiv = ac10x->mclk / (ac108_sample_rate[rate].real_val * channels * ac108_samp_res[samp_res].real_val);
+       for (i = 0; i < ARRAY_SIZE(ac108_bclkdivs) - 1; i++) {
+               if (ac108_bclkdivs[i] >= bclkdiv) {
+                       break;
+               }
+       }
+       ac108_multi_update_bits(I2S_BCLK_CTRL, 0x0F << BCLKDIV, i << BCLKDIV, ac10x);
+
+       /*
+        * slots allocation for each chip
+        */
+       ac108_multi_chips_slots(ac10x, channels);
+
+       /*0x21: Module clock enable<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
+       ac108_multi_write(MOD_CLK_EN, 1 << I2S | 1 << ADC_DIGITAL | 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
+       /*0x22: Module reset de-asserted<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
+       ac108_multi_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL | 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
+       
+       ac108_multi_write(I2S_TX1_CHMP_CTRL1, 0xE4, ac10x);
+       ac108_multi_write(I2S_TX1_CHMP_CTRL2, 0xE4, ac10x);
+       ac108_multi_write(I2S_TX1_CHMP_CTRL3, 0xE4, ac10x);
+       ac108_multi_write(I2S_TX1_CHMP_CTRL4, 0xE4, ac10x);
+       
+       ac108_multi_write(I2S_TX2_CHMP_CTRL1, 0xE4, ac10x);
+       ac108_multi_write(I2S_TX2_CHMP_CTRL2, 0xE4, ac10x);
+       ac108_multi_write(I2S_TX2_CHMP_CTRL3, 0xE4, ac10x);
+       ac108_multi_write(I2S_TX2_CHMP_CTRL4, 0xE4, ac10x);
+
+       dev_dbg(dai->dev, "%s() stream=%s ---\n", __func__,
+                       snd_pcm_stream_str(substream));
+
+       return 0;
+}
+
+static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) {
+
+       struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai);
+
+       freq = 24000000;
+       clk_id = SYSCLK_SRC_PLL;
+
+       pr_info("%s  :%d\n", __FUNCTION__, freq);
+
+       switch (clk_id) {
+       case SYSCLK_SRC_MCLK:
+               ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, SYSCLK_SRC_MCLK << SYSCLK_SRC, ac10x);
+               break;
+       case SYSCLK_SRC_PLL:
+               ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, SYSCLK_SRC_PLL << SYSCLK_SRC, ac10x);
+               break;
+       default:
+               return -EINVAL;
+       }
+       ac10x->sysclk = freq;
+       ac10x->clk_id = clk_id;
+
+       return 0;
+}
+
+/**
+ *  The i2s format management related registers are Reg
+ *  30h~Reg36h
+ *  33h,35h will be set in ac108_hw_params, It's BCLK width and
+ *  Sample Resolution.
+ * @author baozhu (17-6-20)
+ * 
+ * @param dai 
+ * @param fmt 
+ * 
+ * @return int 
+ */
+static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) {
+       unsigned char tx_offset, lrck_polarity, brck_polarity;
+       struct ac10x_priv *ac10x = dev_get_drvdata(dai->dev);
+
+       dev_dbg(dai->dev, "%s\n", __FUNCTION__);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:    /*AC108 Master*/
+               if (! ac10x->i2c101 || _MASTER_MULTI_CODEC == _MASTER_AC108) {
+                       dev_dbg(dai->dev, "AC108 set to work as Master\n");
+                       /**
+                        * 0x30:chip is master mode ,BCLK & LRCK output
+                        */
+                       ac108_multi_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN,
+                                                         0x00 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN, ac10x);
+                       /* multi_chips: only one chip set as Master, and the others also need to set as Slave */
+                       ac10x_update_bits(I2S_CTRL, 0x3 << LRCK_IOEN, 0x01 << BCLK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
+                       break;
+               } else {
+                       /* TODO: Both cpu_dai and codec_dai(AC108) be set as slave in DTS */
+                       dev_dbg(dai->dev, "used as slave when AC101 is master\n");
+               }
+       case SND_SOC_DAIFMT_CBS_CFS:    /*AC108 Slave*/
+               dev_dbg(dai->dev, "AC108 set to work as Slave\n");
+               /**
+                * 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and 
+                *  SDO2_EN, Transmitter Block Enable, Globe Enable
+                */
+               ac108_multi_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN,
+                                                 0x00 << LRCK_IOEN | 0x03 << SDO1_EN | 0x0 << TXEN | 0x0 << GEN, ac10x);
+               break;
+       default:
+               pr_err("AC108 Master/Slave mode config error:%u\n\n", (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12);
+               return -EINVAL;
+       }
+
+       /*AC108 config I2S/LJ/RJ/PCM format*/
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               dev_dbg(dai->dev, "AC108 config I2S format\n");
+               ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
+               tx_offset = 1;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               dev_dbg(dai->dev, "AC108 config RIGHT-JUSTIFIED format\n");
+               ac10x->i2s_mode = RIGHT_JUSTIFIED_FORMAT;
+               tx_offset = 0;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               dev_dbg(dai->dev, "AC108 config LEFT-JUSTIFIED format\n");
+               ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
+               tx_offset = 0;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               dev_dbg(dai->dev, "AC108 config PCM-A format\n");
+               ac10x->i2s_mode = PCM_FORMAT;
+               tx_offset = 1;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               dev_dbg(dai->dev, "AC108 config PCM-B format\n");
+               ac10x->i2s_mode = PCM_FORMAT;
+               tx_offset = 0;
+               break;
+       default:
+               pr_err("AC108 I2S format config error:%u\n\n", fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+               return -EINVAL;
+       }
+
+       /*AC108 config BCLK&LRCK polarity*/
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_normal,LRCK_normal\n");
+               brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
+               lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_normal,LRCK_invert\n");
+               brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
+               lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_invert,LRCK_normal\n");
+               brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
+               lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_invert,LRCK_invert\n");
+               brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
+               lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
+               break;
+       default:
+               pr_err("AC108 config BCLK/LRCLK polarity error:%u\n\n", (fmt & SND_SOC_DAIFMT_INV_MASK) >> 8);
+               return -EINVAL;
+       }
+
+       ac108_configure_power(ac10x);
+
+       /**
+        *0x31: 0: normal mode, negative edge drive and positive edge sample
+               1: invert mode, positive edge drive and negative edge sample
+        */
+       ac108_multi_update_bits(I2S_BCLK_CTRL,  0x01 << BCLK_POLARITY, brck_polarity << BCLK_POLARITY, ac10x);
+       /**
+        * 0x32: same as 0x31
+        */
+       ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x01 << LRCK_POLARITY, lrck_polarity << LRCK_POLARITY, ac10x);
+       /**
+        * 0x34:Encoding Mode Selection,Mode 
+        * Selection,data is offset by 1 BCLKs to LRCK 
+        * normal mode for the last half cycle of BCLK in the slot ?
+        * turn to hi-z state (TDM) when not transferring slot ?
+        */
+       ac108_multi_update_bits(I2S_FMT_CTRL1,  0x01 << ENCD_SEL | 0x03 << MODE_SEL | 0x01 << TX2_OFFSET |
+                                               0x01 << TX1_OFFSET | 0x01 << TX_SLOT_HIZ | 0x01 << TX_STATE,
+                                                                 ac10x->data_protocol << ENCD_SEL      |
+                                                                 ac10x->i2s_mode << MODE_SEL           |
+                                                                 tx_offset << TX2_OFFSET                       |
+                                                                 tx_offset << TX1_OFFSET                       |
+                                                                 0x00 << TX_SLOT_HIZ                           |
+                                                                 0x01 << TX_STATE, ac10x);
+
+       /**
+        * 0x60: 
+        * MSB / LSB First Select: This driver only support MSB First Select . 
+        * OUT2_MUTE,OUT1_MUTE shoule be set in widget. 
+        * LRCK = 1 BCLK width 
+        * Linear PCM 
+        *  
+        * TODO:pcm mode, bit[0:1] and bit[2] is special
+        */
+       ac108_multi_update_bits(I2S_FMT_CTRL3,  0x01 << TX_MLS | 0x03 << SEXT  | 0x01 << LRCK_WIDTH | 0x03 << TX_PDM,
+                                               0x00 << TX_MLS | 0x03 << SEXT  | 0x00 << LRCK_WIDTH | 0x00 << TX_PDM, ac10x);
+
+       ac108_multi_write(HPF_EN, 0x00, ac10x);
+
+       if (ac10x->i2c101) {
+               return ac101_set_dai_fmt(dai, fmt);
+       }
+       return 0;
+}
+
+/*
+ * due to miss channels order in cpu_dai, we meed defer the clock starting.
+ */
+static int ac108_set_clock(int y_start_n_stop) {
+       u8 reg;
+       int ret = 0;
+
+       dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop);
+
+       /* spin_lock move to machine trigger */
+
+       if (y_start_n_stop && ac10x->sysclk_en == 0) {
+               /* enable lrck clock */
+               ac10x_read(I2S_CTRL, &reg, ac10x->i2cmap[_MASTER_INDEX]);
+               if (reg & (0x01 << BCLK_IOEN)) {
+                       ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
+               }
+
+               /*0x10: PLL Common voltage enable, PLL enable */
+               ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
+                                                  0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x);
+               /* enable global clock */
+               ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x);
+
+               ac10x->sysclk_en = 1UL;
+       } else if (!y_start_n_stop && ac10x->sysclk_en != 0) {
+               /* disable global clock */
+               ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x);
+
+               /*0x10: PLL Common voltage disable, PLL disable */
+               ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
+                                                  0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x);
+
+               /* disable lrck clock if it's enabled */
+               ac10x_read(I2S_CTRL, &reg, ac10x->i2cmap[_MASTER_INDEX]);
+               if (reg & (0x01 << LRCK_IOEN)) {
+                       ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x01 << BCLK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
+               }
+               if (!ret) {
+                       ac10x->sysclk_en = 0UL;
+               }
+       }
+
+       return ret;
+}
+
+static int ac108_prepare(struct snd_pcm_substream *substream,
+                                       struct snd_soc_dai *dai)
+{
+       u8 r;
+
+       dev_dbg(dai->dev, "%s() stream=%s\n",
+               __func__,
+               snd_pcm_stream_str(substream));
+       
+       if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
+               ac101_trigger(substream, SNDRV_PCM_TRIGGER_START, dai);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                      return 0;
+               }
+       }
+
+       ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
+       if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) {
+               /* disable global clock */
+               ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x);
+       }
+
+       /* delayed clock starting, move to machine trigger() */
+       ac108_set_clock(1);
+
+       return 0;
+}
+
+static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       unsigned long flags;
+       int ret = 0;
+       u8 r;
+
+       dev_dbg(dai->dev, "%s() stream=%s  cmd=%d\n",
+               __FUNCTION__,
+               snd_pcm_stream_str(substream),
+               cmd);
+
+       spin_lock_irqsave(&ac10x->lock, flags);
+
+       if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
+               ac101_trigger(substream, cmd, dai);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                      goto __ret;
+               }
+       }
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               /* disable global clock if lrck disabled */
+               ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
+               if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) {
+                       /* disable global clock */
+                       ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x);
+               }
+
+               /* delayed clock starting, move to machine trigger() */
+               //msleep(10);
+               ac108_set_clock(1);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               //msleep(10);
+               ac108_set_clock(0);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+__ret:
+       spin_unlock_irqrestore(&ac10x->lock, flags);
+
+       return ret;
+}
+
+int ac108_audio_startup(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai
+) {
+       struct snd_soc_codec *codec = dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       if (ac10x->i2c101) {
+               return ac101_audio_startup(substream, dai);
+       }
+       return 0;
+}
+
+void ac108_aif_shutdown(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai
+) {
+       struct snd_soc_codec *codec = dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       ac108_set_clock(0);
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               /*0x21: Module clock disable <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
+               ac108_multi_write(MOD_CLK_EN, 0x0, ac10x);
+               /*0x22: Module reset asserted <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
+               ac108_multi_write(MOD_RST_CTRL, 0x0, ac10x);
+       }
+
+       if (ac10x->i2c101) {
+               ac101_aif_shutdown(substream, dai);
+       }
+}
+
+int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int stream) {
+       struct snd_soc_codec *codec = dai->codec;
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       if (ac10x->i2c101) {
+               return ac101_aif_mute(dai, mute);
+       }
+       return 0;       
+}
+
+static const struct snd_soc_dai_ops ac108_dai_ops = {
+       .startup        = ac108_audio_startup,
+       .shutdown       = ac108_aif_shutdown,
+
+       /*DAI clocking configuration*/
+       .set_sysclk     = ac108_set_sysclk,
+
+       /*ALSA PCM audio operations*/
+       .hw_params      = ac108_hw_params,
+       .prepare        = ac108_prepare,
+       /*.trigger      = ac108_trigger, */
+       .mute_stream    = ac108_aif_mute,
+
+       /*DAI format configuration*/
+       .set_fmt        = ac108_set_fmt,
+
+       // .hw_free = ac108_hw_free,
+};
+
+static  struct snd_soc_dai_driver ac108_dai0 = {
+       .name = "ac10x-codec0",
+       #if _USE_CAPTURE
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = AC108_CHANNELS_MAX,
+               .rates = AC108_RATES,
+               .formats = AC108_FORMATS,
+       },
+       #endif
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = AC108_CHANNELS_MAX,
+               .rates = AC108_RATES,
+               .formats = AC108_FORMATS,
+       },
+       .ops = &ac108_dai_ops,
+};
+
+static  struct snd_soc_dai_driver ac108_dai1 = {
+       .name = "ac10x-codec1",
+       #if _USE_CAPTURE
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = AC108_CHANNELS_MAX,
+               .rates = AC108_RATES,
+               .formats = AC108_FORMATS,
+       },
+       #endif
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = AC108_CHANNELS_MAX,
+               .rates = AC108_RATES,
+               .formats = AC108_FORMATS,
+       },
+       .ops = &ac108_dai_ops,
+};
+
+static  struct snd_soc_dai_driver *ac108_dai[] = {
+       &ac108_dai0,
+       &ac108_dai1,
+};
+
+static int ac108_add_widgets(struct snd_soc_codec *codec) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       const struct snd_kcontrol_new* snd_kcntl = ac108_snd_controls;
+       int ctrl_cnt = ARRAY_SIZE(ac108_snd_controls);
+
+       /* only register controls correspond to exist chips */
+       if (ac10x->tdm_chips_cnt >= 2) {
+               snd_kcntl = ac108tdm_snd_controls;
+               ctrl_cnt = ARRAY_SIZE(ac108tdm_snd_controls);
+       }
+       snd_soc_add_codec_controls(codec, snd_kcntl, ctrl_cnt);
+
+       snd_soc_dapm_new_controls(dapm, ac108_dapm_widgets,ARRAY_SIZE(ac108_dapm_widgets));
+       snd_soc_dapm_add_routes(dapm, ac108_dapm_routes, ARRAY_SIZE(ac108_dapm_routes));
+
+       return 0;
+}
+
+static int ac108_codec_probe(struct snd_soc_codec *codec) {
+       spin_lock_init(&ac10x->lock);
+
+       ac10x->codec = codec;
+       dev_set_drvdata(codec->dev, ac10x);
+       ac108_add_widgets(codec);
+
+       if (ac10x->i2c101) {
+               ac101_codec_probe(codec);
+       }
+
+       return 0;
+}
+
+static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       dev_dbg(codec->dev, "AC108 level:%d\n", level);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN,  0x01 << ADC1_MICBIAS_EN, ac10x);
+               ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN,  0x01 << ADC2_MICBIAS_EN, ac10x);
+               ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN,  0x01 << ADC3_MICBIAS_EN, ac10x);
+               ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN,  0x01 << ADC4_MICBIAS_EN, ac10x);
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* Put the MICBIASes into regulating mode */
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN,  0x00 << ADC1_MICBIAS_EN, ac10x);
+               ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN,  0x00 << ADC2_MICBIAS_EN, ac10x);
+               ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN,  0x00 << ADC3_MICBIAS_EN, ac10x);
+               ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN,  0x00 << ADC4_MICBIAS_EN, ac10x);
+               break;
+       }
+
+       if (ac10x->i2c101) {
+               ac101_set_bias_level(codec, level);
+       }
+       return 0;
+}
+
+int ac108_codec_remove(struct snd_soc_codec *codec) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+
+       if (! ac10x->i2c101) {
+               return 0;
+       }
+       return ac101_codec_remove(codec);
+}
+#if __NO_SND_SOC_CODEC_DRV
+void ac108_codec_remove_void(struct snd_soc_codec *codec) {
+       ac108_codec_remove(codec);
+}
+#define ac108_codec_remove ac108_codec_remove_void
+#endif
+
+int ac108_codec_suspend(struct snd_soc_codec *codec) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       int i;
+
+       for (i = 0; i < ac10x->codec_cnt; i++) {
+               regcache_cache_only(ac10x->i2cmap[i], true);
+       }
+
+       if (! ac10x->i2c101) {
+               return 0;
+       }
+       return ac101_codec_suspend(codec);
+}
+
+int ac108_codec_resume(struct snd_soc_codec *codec) {
+       struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
+       int i, ret;
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < ac10x->codec_cnt; i++) {
+               regcache_cache_only(ac10x->i2cmap[i], false);
+               ret = regcache_sync(ac10x->i2cmap[i]);
+               if (ret != 0) {
+                       dev_err(codec->dev, "Failed to sync i2cmap%d register cache: %d\n", i, ret);
+                       regcache_cache_only(ac10x->i2cmap[i], true);
+               }
+       }
+
+       if (! ac10x->i2c101) {
+               return 0;
+       }
+       return ac101_codec_resume(codec);
+}
+
+static struct snd_soc_codec_driver ac10x_soc_codec_driver = {
+       .probe          = ac108_codec_probe,
+       .remove         = ac108_codec_remove,
+       .suspend        = ac108_codec_suspend,
+       .resume         = ac108_codec_resume,
+       .set_bias_level = ac108_set_bias_level,
+       .read           = ac108_codec_read,
+       .write          = ac108_codec_write,
+};
+
+static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
+       int val = 0, flag = 0;
+       u8 i = 0, reg, num, value_w, value_r[4];
+
+       val = simple_strtol(buf, NULL, 16);
+       flag = (val >> 16) & 0xF;
+
+       if (flag) {
+               reg = (val >> 8) & 0xFF;
+               value_w = val & 0xFF;
+               ac108_multi_write(reg, value_w, ac10x);
+               printk("Write 0x%02x to REG:0x%02x\n", value_w, reg);
+       } else {
+               int k;
+
+               reg = (val >> 8) & 0xFF;
+               num = val & 0xff;
+               printk("\nRead: start REG:0x%02x,count:0x%02x\n", reg, num);
+
+               for (k = 0; k < ac10x->codec_cnt; k++) {
+                       regcache_cache_bypass(ac10x->i2cmap[k], true);
+               }
+               do {
+
+                       memset(value_r, 0, sizeof value_r);
+
+                       for (k = 0; k < ac10x->codec_cnt; k++) {
+                               ac10x_read(reg, &value_r[k], ac10x->i2cmap[k]);
+                       }
+                       if (ac10x->codec_cnt >= 2) {
+                               printk("REG[0x%02x]: 0x%02x 0x%02x", reg, value_r[0], value_r[1]);
+                       } else {
+                               printk("REG[0x%02x]: 0x%02x", reg, value_r[0]);
+                       }
+                       reg++;
+
+                       if ((++i == num) || (i % 4 == 0)) {
+                               printk("\n");
+                       }
+               } while (i < num);
+               for (k = 0; k < ac10x->codec_cnt; k++) {
+                       regcache_cache_bypass(ac10x->i2cmap[k], false);
+               }
+       }
+
+       return count;
+}
+
+static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf) {
+#if 1
+       printk("echo flag|reg|val > ac108\n");
+       printk("eg read star addres=0x06,count 0x10:echo 0610 >ac108\n");
+       printk("eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
+       return 0;
+#else
+       return snprintf(buf, PAGE_SIZE,"echo flag|reg|val > ac108\n"
+                                       "eg read star addres=0x06,count 0x10:echo 0610 >ac108\n"
+                                       "eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
+#endif
+}
+
+static DEVICE_ATTR(ac108, 0644, ac108_show, ac108_store);
+static struct attribute *ac108_debug_attrs[] = {
+       &dev_attr_ac108.attr,
+       NULL,
+};
+static struct attribute_group ac108_debug_attr_group = {
+       .name   = "ac108_debug",
+       .attrs  = ac108_debug_attrs,
+};
+
+static const struct regmap_config ac108_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .reg_stride = 1,
+       .max_register = 0xDF,
+       .cache_type = REGCACHE_FLAT,
+};
+static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i2c_id) {
+       struct device_node *np = i2c->dev.of_node;
+       unsigned int val = 0;
+       int ret = 0, index;
+
+       if (ac10x == NULL) {
+               ac10x = kzalloc(sizeof(struct ac10x_priv), GFP_KERNEL);
+               if (ac10x == NULL) {
+                       dev_err(&i2c->dev, "Unable to allocate ac10x private data\n");
+                       return -ENOMEM;
+               }
+       }
+
+       index = (int)i2c_id->driver_data;
+       if (index == AC101_I2C_ID) {
+               ac10x->i2c101 = i2c;
+               i2c_set_clientdata(i2c, ac10x);
+               ret = ac101_probe(i2c, i2c_id);
+               if (ret) {
+                       ac10x->i2c101 = NULL;
+                       return ret;
+               }
+               goto __ret;
+       }
+
+       ret = of_property_read_u32(np, "data-protocol", &val);
+       if (ret) {
+               pr_err("Please set data-protocol.\n");
+               return -EINVAL;
+       }
+       ac10x->data_protocol = val;
+
+       if (of_property_read_u32(np, "tdm-chips-count", &val)) val = 1;
+       ac10x->tdm_chips_cnt = val;
+
+       pr_err(" ac10x i2c_id number: %d\n", index);
+       pr_err(" ac10x data protocol: %d\n", ac10x->data_protocol);
+
+       ac10x->i2c[index] = i2c;
+       ac10x->i2cmap[index] = devm_regmap_init_i2c(i2c, &ac108_regmap);
+       if (IS_ERR(ac10x->i2cmap[index])) {
+               ret = PTR_ERR(ac10x->i2cmap[index]);
+               dev_err(&i2c->dev, "Fail to initialize i2cmap%d I/O: %d\n", index, ret);
+               return ret;
+       }
+
+       /*
+        * Writing this register with 0x12 
+        * will resets all register to their default state.
+        */
+       regcache_cache_only(ac10x->i2cmap[index], false);
+       
+       ret = regmap_write(ac10x->i2cmap[index], CHIP_RST, CHIP_RST_VAL);
+       msleep(1);
+
+       /* sync regcache for FLAT type */
+       ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]);
+
+       ac10x->codec_cnt++;
+       pr_err(" ac10x codec count  : %d\n", ac10x->codec_cnt);
+
+       ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group);
+       if (ret) {
+               pr_err("failed to create attr group\n");
+       }
+
+__ret:
+       /* It's time to bind codec to i2c[_MASTER_INDEX] when all i2c are ready */
+       if ((ac10x->codec_cnt != 0 && ac10x->tdm_chips_cnt < 2)
+       || (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) {
+               //seeed_voice_card_register_set_clock(SNDRV_PCM_STREAM_CAPTURE, ac108_set_clock);
+               /* no playback stream */
+               if (! ac10x->i2c101) {
+                       memset(&ac108_dai[_MASTER_INDEX]->playback, '\0', sizeof ac108_dai[_MASTER_INDEX]->playback);
+               }
+               ret = snd_soc_register_codec(&ac10x->i2c[_MASTER_INDEX]->dev, &ac10x_soc_codec_driver,
+                                               ac108_dai[_MASTER_INDEX], 1);
+               if (ret < 0) {
+                       dev_err(&i2c->dev, "Failed to register ac10x codec: %d\n", ret);
+               }
+       }
+       return ret;
+}
+
+static void ac108_i2c_remove(struct i2c_client *i2c) {
+       if (ac10x->codec != NULL) {
+               snd_soc_unregister_codec(&ac10x->i2c[_MASTER_INDEX]->dev);
+               ac10x->codec = NULL;
+       }
+       if (i2c == ac10x->i2c101) {
+               ac101_remove(ac10x->i2c101);
+               ac10x->i2c101 = NULL;
+               goto __ret;
+       }
+
+       if (i2c == ac10x->i2c[0]) {
+               ac10x->i2c[0] = NULL;
+       }
+       if (i2c == ac10x->i2c[1]) {
+               ac10x->i2c[1] = NULL;
+       }
+
+       sysfs_remove_group(&i2c->dev.kobj, &ac108_debug_attr_group);
+
+__ret:
+       if (!ac10x->i2c[0] && !ac10x->i2c[1] && !ac10x->i2c101) {
+               kfree(ac10x);
+               ac10x = NULL;
+       }
+}
+
+static const struct i2c_device_id ac108_i2c_id[] = {
+       { "ac108_0", 0 },
+       { "ac108_1", 1 },
+       { "ac108_2", 2 },
+       { "ac108_3", 3 },
+       { "ac101", AC101_I2C_ID },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ac108_i2c_id);
+
+static const struct of_device_id ac108_of_match[] = {
+       { .compatible = "x-power,ac108_0", },
+       { .compatible = "x-power,ac108_1", },
+       { .compatible = "x-power,ac108_2", },
+       { .compatible = "x-power,ac108_3", },
+       { .compatible = "x-power,ac101",   },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ac108_of_match);
+
+static struct i2c_driver ac108_i2c_driver = {
+       .driver = {
+               .name = "ac10x-codec",
+               .of_match_table = ac108_of_match,
+       },
+       .probe =    ac108_i2c_probe,
+       .remove =   ac108_i2c_remove,
+       .id_table = ac108_i2c_id,
+};
+
+module_i2c_driver(ac108_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AC108 driver");
+MODULE_AUTHOR("Baozhu Zuo<zuobaozhu@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ac108.h b/sound/soc/codecs/ac108.h
new file mode 100644 (file)
index 0000000..d40edfc
--- /dev/null
@@ -0,0 +1,774 @@
+/*
+ * ac108.h --  ac108 ALSA Soc Audio driver
+ *
+ * Author: panjunwen
+ *
+ * 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 _AC108_H
+#define _AC108_H
+
+
+/*** AC108 Codec Register Define***/
+
+//Chip Reset
+#define CHIP_RST                       0x00
+#define CHIP_RST_VAL           0x12
+
+//Power Control
+#define PWR_CTRL1                      0x01
+#define PWR_CTRL2                      0x02
+#define PWR_CTRL3                      0x03
+#define PWR_CTRL4                      0x04
+#define PWR_CTRL5                      0x05
+#define PWR_CTRL6                      0x06
+#define PWR_CTRL7                      0x07
+#define PWR_CTRL8                      0x08
+#define PWR_CTRL9                      0x09
+
+//PLL Configure Control
+#define PLL_CTRL1                      0x10
+#define PLL_CTRL2                      0x11
+#define PLL_CTRL3                      0x12
+#define PLL_CTRL4                      0x13
+#define PLL_CTRL5                      0x14
+#define PLL_CTRL6                      0x16
+#define PLL_CTRL7                      0x17
+#define PLL_LOCK_CTRL          0x18
+
+//System Clock Control
+#define SYSCLK_CTRL                    0x20
+#define MOD_CLK_EN                     0x21
+#define MOD_RST_CTRL           0x22
+#define DSM_CLK_CTRL           0x25
+
+//I2S Common Control
+#define I2S_CTRL                       0x30
+#define I2S_BCLK_CTRL          0x31
+#define I2S_LRCK_CTRL1         0x32
+#define I2S_LRCK_CTRL2         0x33
+#define I2S_FMT_CTRL1          0x34
+#define I2S_FMT_CTRL2          0x35
+#define I2S_FMT_CTRL3          0x36
+
+//I2S TX1 Control
+#define I2S_TX1_CTRL1          0x38
+#define I2S_TX1_CTRL2          0x39
+#define I2S_TX1_CTRL3          0x3A
+#define I2S_TX1_CHMP_CTRL1     0x3C
+#define I2S_TX1_CHMP_CTRL2     0x3D
+#define I2S_TX1_CHMP_CTRL3     0x3E
+#define I2S_TX1_CHMP_CTRL4     0x3F
+
+//I2S TX2 Control
+#define I2S_TX2_CTRL1          0x40
+#define I2S_TX2_CTRL2          0x41
+#define I2S_TX2_CTRL3          0x42
+#define I2S_TX2_CHMP_CTRL1     0x44
+#define I2S_TX2_CHMP_CTRL2     0x45
+#define I2S_TX2_CHMP_CTRL3     0x46
+#define I2S_TX2_CHMP_CTRL4     0x47
+
+//I2S RX1 Control
+#define I2S_RX1_CTRL1          0x50
+#define I2S_RX1_CHMP_CTRL1     0x54
+#define I2S_RX1_CHMP_CTRL2     0x55
+#define I2S_RX1_CHMP_CTRL3     0x56
+#define I2S_RX1_CHMP_CTRL4     0x57
+
+//I2S Loopback Debug
+#define I2S_LPB_DEBUG          0x58
+
+//ADC Common Control
+#define ADC_SPRC                       0x60
+#define ADC_DIG_EN                     0x61
+#define DMIC_EN                                0x62
+#define ADC_DSR                                0x63
+#define ADC_FIR                                0x64
+#define ADC_DDT_CTRL           0x65
+
+//HPF Control
+#define HPF_EN                         0x66
+#define HPF_COEF_REGH1         0x67
+#define HPF_COEF_REGH2         0x68
+#define HPF_COEF_REGL1         0x69
+#define HPF_COEF_REGL2         0x6A
+#define HPF_GAIN_REGH1         0x6B
+#define HPF_GAIN_REGH2         0x6C
+#define HPF_GAIN_REGL1         0x6D
+#define HPF_GAIN_REGL2         0x6E
+
+//ADC Digital Channel Volume Control
+#define ADC1_DVOL_CTRL         0x70
+#define ADC2_DVOL_CTRL         0x71
+#define ADC3_DVOL_CTRL         0x72
+#define ADC4_DVOL_CTRL         0x73
+
+//ADC Digital Mixer Source and Gain Control
+#define ADC1_DMIX_SRC          0x76
+#define ADC2_DMIX_SRC          0x77
+#define ADC3_DMIX_SRC          0x78
+#define ADC4_DMIX_SRC          0x79
+
+//ADC Digital Debug Control
+#define ADC_DIG_DEBUG          0x7F
+
+//I2S Pad Drive Control
+#define I2S_DAT_PADDRV_CTRL    0x80
+#define I2S_CLK_PADDRV_CTRL    0x81
+
+//Analog PGA Control
+#define ANA_PGA1_CTRL          0x90
+#define ANA_PGA2_CTRL          0x91
+#define ANA_PGA3_CTRL          0x92
+#define ANA_PGA4_CTRL          0x93
+
+//MIC Offset Control
+#define MIC_OFFSET_CTRL1       0x96
+#define MIC_OFFSET_CTRL2       0x97
+#define MIC1_OFFSET_STATU1     0x98
+#define MIC1_OFFSET_STATU2     0x99
+#define MIC2_OFFSET_STATU1     0x9A
+#define MIC2_OFFSET_STATU2     0x9B
+#define MIC3_OFFSET_STATU1     0x9C
+#define MIC3_OFFSET_STATU2     0x9D
+#define MIC4_OFFSET_STATU1     0x9E
+#define MIC4_OFFSET_STATU2     0x9F
+
+//ADC1 Analog Control
+#define ANA_ADC1_CTRL1         0xA0
+#define ANA_ADC1_CTRL2         0xA1
+#define ANA_ADC1_CTRL3         0xA2
+#define ANA_ADC1_CTRL4         0xA3
+#define ANA_ADC1_CTRL5         0xA4
+#define ANA_ADC1_CTRL6         0xA5
+#define ANA_ADC1_CTRL7         0xA6
+
+//ADC2 Analog Control
+#define ANA_ADC2_CTRL1         0xA7
+#define ANA_ADC2_CTRL2         0xA8
+#define ANA_ADC2_CTRL3         0xA9
+#define ANA_ADC2_CTRL4         0xAA
+#define ANA_ADC2_CTRL5         0xAB
+#define ANA_ADC2_CTRL6         0xAC
+#define ANA_ADC2_CTRL7         0xAD
+
+//ADC3 Analog Control
+#define ANA_ADC3_CTRL1         0xAE
+#define ANA_ADC3_CTRL2         0xAF
+#define ANA_ADC3_CTRL3         0xB0
+#define ANA_ADC3_CTRL4         0xB1
+#define ANA_ADC3_CTRL5         0xB2
+#define ANA_ADC3_CTRL6         0xB3
+#define ANA_ADC3_CTRL7         0xB4
+
+//ADC4 Analog Control
+#define ANA_ADC4_CTRL1         0xB5
+#define ANA_ADC4_CTRL2         0xB6
+#define ANA_ADC4_CTRL3         0xB7
+#define ANA_ADC4_CTRL4         0xB8
+#define ANA_ADC4_CTRL5         0xB9
+#define ANA_ADC4_CTRL6         0xBA
+#define ANA_ADC4_CTRL7         0xBB
+
+//GPIO Configure
+#define GPIO_CFG1                      0xC0
+#define GPIO_CFG2                      0xC1
+#define GPIO_DAT                       0xC2
+#define GPIO_DRV                       0xC3
+#define GPIO_PULL                      0xC4
+#define GPIO_INT_CFG           0xC5
+#define GPIO_INT_EN                    0xC6
+#define GPIO_INT_STATUS                0xC7
+
+//Misc
+#define BGTC_DAT                       0xD1
+#define BGVC_DAT                       0xD2
+#define PRNG_CLK_CTRL          0xDF
+
+
+
+/*** AC108 Codec Register Bit Define***/
+
+/*PWR_CTRL1*/
+#define CP12_CTRL                              4
+#define CP12_SENSE_SELECT              3
+
+/*PWR_CTRL2*/
+#define CP12_SENSE_FILT                        6
+#define CP12_COMP_FF_EN                        3
+#define CP12_FORCE_ENABLE              2
+#define CP12_FORCE_RSTB                        1
+
+/*PWR_CTRL3*/
+#define LDO33DIG_CTRL                  0
+
+/*PWR_CTRL6*/
+#define LDO33ANA_2XHDRM                        2
+#define LDO33ANA_ENABLE                        0
+
+/*PWR_CTRL7*/
+#define VREF_SEL                               3
+#define VREF_FASTSTART_ENABLE  1
+#define VREF_ENABLE                            0
+
+/*PWR_CTRL9*/
+#define VREFP_FASTSTART_ENABLE 7
+#define VREFP_RESCTRL                  5
+#define VREFP_LPMODE                   4
+#define IGEN_TRIM                              1
+#define VREFP_ENABLE                   0
+
+
+/*PLL_CTRL1*/
+#define PLL_IBIAS                              4
+#define PLL_NDET                               3
+#define PLL_LOCKED_STATUS              2
+#define PLL_COM_EN                             1
+#define PLL_EN                                 0
+
+/*PLL_CTRL2*/
+#define PLL_PREDIV2                            5
+#define PLL_PREDIV1                            0
+
+/*PLL_CTRL3*/
+#define PLL_LOOPDIV_MSB                        0
+
+/*PLL_CTRL4*/
+#define PLL_LOOPDIV_LSB                        0
+
+/*PLL_CTRL5*/
+#define PLL_POSTDIV2                   5
+#define PLL_POSTDIV1                   0
+
+/*PLL_CTRL6*/
+#define PLL_LDO                                        6
+#define PLL_CP                                 0
+
+/*PLL_CTRL7*/
+#define PLL_CAP                                        6
+#define PLL_RES                                        4
+#define PLL_TEST_EN                            0
+
+/*PLL_LOCK_CTRL*/
+#define LOCK_LEVEL1                            2
+#define LOCK_LEVEL2                            1
+#define PLL_LOCK_EN                            0
+
+
+/*SYSCLK_CTRL*/
+#define PLLCLK_EN                              7
+#define PLLCLK_SRC                             4
+#define SYSCLK_SRC                             3
+#define SYSCLK_EN                              0
+
+/*MOD_CLK_EN & MOD_RST_CTRL*/
+#define I2S                                            7
+#define ADC_DIGITAL                            4
+#define MIC_OFFSET_CALIBRATION 1
+#define ADC_ANALOG                             0
+
+/*DSM_CLK_CTRL*/
+#define MIC_OFFSET_DIV                 4
+#define DSM_CLK_SEL                            0
+
+
+/*I2S_CTRL*/
+#define BCLK_IOEN                              7
+#define LRCK_IOEN                              6
+#define SDO2_EN                                        5
+#define SDO1_EN                                        4
+#define TXEN                                   2
+#define RXEN                                   1
+#define GEN                                            0
+
+/*I2S_BCLK_CTRL*/
+#define EDGE_TRANSFER                  5
+#define BCLK_POLARITY                  4
+#define BCLKDIV                                        0
+
+/*I2S_LRCK_CTRL1*/
+#define LRCK_POLARITY                  4
+#define LRCK_PERIODH                   0
+
+/*I2S_LRCK_CTRL2*/
+#define LRCK_PERIODL                   0
+
+/*I2S_FMT_CTRL1*/
+#define ENCD_SEL                               6
+#define MODE_SEL                               4
+#define TX2_OFFSET                             3
+#define TX1_OFFSET                             2
+#define TX_SLOT_HIZ                            1
+#define TX_STATE                               0
+
+/*I2S_FMT_CTRL2*/
+#define SLOT_WIDTH_SEL                 4
+#define SAMPLE_RESOLUTION              0
+
+/*I2S_FMT_CTRL3*/
+#define TX_MLS                                 7
+#define SEXT                                   5
+#define OUT2_MUTE                              4
+#define OUT1_MUTE                              3
+#define LRCK_WIDTH                             2
+#define TX_PDM                                 0
+
+
+/*I2S_TX1_CTRL1*/
+#define TX1_CHSEL                              0
+
+/*I2S_TX1_CTRL2*/
+#define TX1_CH8_EN                             7
+#define TX1_CH7_EN                             6
+#define TX1_CH6_EN                             5
+#define TX1_CH5_EN                             4
+#define TX1_CH4_EN                             3
+#define TX1_CH3_EN                             2
+#define TX1_CH2_EN                             1
+#define TX1_CH1_EN                             0
+
+/*I2S_TX1_CTRL3*/
+#define TX1_CH16_EN                            7
+#define TX1_CH15_EN                            6
+#define TX1_CH14_EN                            5
+#define TX1_CH13_EN                            4
+#define TX1_CH12_EN                            3
+#define TX1_CH11_EN                            2
+#define TX1_CH10_EN                            1
+#define TX1_CH9_EN                             0
+
+/*I2S_TX1_CHMP_CTRL1*/
+#define TX1_CH4_MAP                            6
+#define TX1_CH3_MAP                            4
+#define TX1_CH2_MAP                            2
+#define TX1_CH1_MAP                            0
+
+/*I2S_TX1_CHMP_CTRL2*/
+#define TX1_CH8_MAP                            6
+#define TX1_CH7_MAP                            4
+#define TX1_CH6_MAP                            2
+#define TX1_CH5_MAP                            0
+
+/*I2S_TX1_CHMP_CTRL3*/
+#define TX1_CH12_MAP                   6
+#define TX1_CH11_MAP                   4
+#define TX1_CH10_MAP                   2
+#define TX1_CH9_MAP                            0
+
+/*I2S_TX1_CHMP_CTRL4*/
+#define TX1_CH16_MAP                   6
+#define TX1_CH15_MAP                   4
+#define TX1_CH14_MAP                   2
+#define TX1_CH13_MAP                   0
+
+
+/*I2S_TX2_CTRL1*/
+#define TX2_CHSEL                              0
+
+/*I2S_TX2_CHMP_CTRL1*/
+#define TX2_CH4_MAP                            6
+#define TX2_CH3_MAP                            4
+#define TX2_CH2_MAP                            2
+#define TX2_CH1_MAP                            0
+
+/*I2S_TX2_CHMP_CTRL2*/
+#define TX2_CH8_MAP                            6
+#define TX2_CH7_MAP                            4
+#define TX2_CH6_MAP                            2
+#define TX2_CH5_MAP                            0
+
+/*I2S_TX2_CHMP_CTRL3*/
+#define TX2_CH12_MAP                   6
+#define TX2_CH11_MAP                   4
+#define TX2_CH10_MAP                   2
+#define TX2_CH9_MAP                            0
+
+/*I2S_TX2_CHMP_CTRL4*/
+#define TX2_CH16_MAP                   6
+#define TX2_CH15_MAP                   4
+#define TX2_CH14_MAP                   2
+#define TX2_CH13_MAP                   0
+
+
+/*I2S_RX1_CTRL1*/
+#define RX1_CHSEL                              0
+
+/*I2S_RX1_CHMP_CTRL1*/
+#define RX1_CH4_MAP                            6
+#define RX1_CH3_MAP                            4
+#define RX1_CH2_MAP                            2
+#define RX1_CH1_MAP                            0
+
+/*I2S_RX1_CHMP_CTRL2*/
+#define RX1_CH8_MAP                            6
+#define RX1_CH7_MAP                            4
+#define RX1_CH6_MAP                            2
+#define RX1_CH5_MAP                            0
+
+/*I2S_RX1_CHMP_CTRL3*/
+#define RX1_CH12_MAP                   6
+#define RX1_CH11_MAP                   4
+#define RX1_CH10_MAP                   2
+#define RX1_CH9_MAP                            0
+
+/*I2S_RX1_CHMP_CTRL4*/
+#define RX1_CH16_MAP                   6
+#define RX1_CH15_MAP                   4
+#define RX1_CH14_MAP                   2
+#define RX1_CH13_MAP                   0
+
+
+/*I2S_LPB_DEBUG*/
+#define I2S_LPB_DEBUG_EN               0
+
+
+/*ADC_SPRC*/
+#define ADC_FS_I2S1                            0
+
+/*ADC_DIG_EN*/
+#define DG_EN                                  4
+#define ENAD4                                  3
+#define ENAD3                                  2
+#define ENAD2                                  1
+#define ENAD1                                  0
+
+/*DMIC_EN*/
+#define DMIC2_EN                               1
+#define DMIC1_EN                               0
+
+/*ADC_DSR*/
+#define DIG_ADC4_SRS                   6
+#define DIG_ADC3_SRS                   4
+#define DIG_ADC2_SRS                   2
+#define DIG_ADC1_SRS                   0
+
+/*ADC_DDT_CTRL*/
+#define ADOUT_DLY_EN                   2
+#define ADOUT_DTS                              0
+
+
+/*HPF_EN*/
+#define DIG_ADC4_HPF_EN                        3
+#define DIG_ADC3_HPF_EN                        2
+#define DIG_ADC2_HPF_EN                        1
+#define DIG_ADC1_HPF_EN                        0
+
+
+/*ADC1_DMIX_SRC*/
+#define ADC1_ADC4_DMXL_GC              7
+#define ADC1_ADC3_DMXL_GC              6
+#define ADC1_ADC2_DMXL_GC              5
+#define ADC1_ADC1_DMXL_GC              4
+#define ADC1_ADC4_DMXL_SRC             3
+#define ADC1_ADC3_DMXL_SRC             2
+#define ADC1_ADC2_DMXL_SRC             1
+#define ADC1_ADC1_DMXL_SRC             0
+
+/*ADC2_DMIX_SRC*/
+#define ADC2_ADC4_DMXL_GC              7
+#define ADC2_ADC3_DMXL_GC              6
+#define ADC2_ADC2_DMXL_GC              5
+#define ADC2_ADC1_DMXL_GC              4
+#define ADC2_ADC4_DMXL_SRC             3
+#define ADC2_ADC3_DMXL_SRC             2
+#define ADC2_ADC2_DMXL_SRC             1
+#define ADC2_ADC1_DMXL_SRC             0
+
+/*ADC3_DMIX_SRC*/
+#define ADC3_ADC4_DMXL_GC              7
+#define ADC3_ADC3_DMXL_GC              6
+#define ADC3_ADC2_DMXL_GC              5
+#define ADC3_ADC1_DMXL_GC              4
+#define ADC3_ADC4_DMXL_SRC             3
+#define ADC3_ADC3_DMXL_SRC             2
+#define ADC3_ADC2_DMXL_SRC             1
+#define ADC3_ADC1_DMXL_SRC             0
+
+/*ADC4_DMIX_SRC*/
+#define ADC4_ADC4_DMXL_GC              7
+#define ADC4_ADC3_DMXL_GC              6
+#define ADC4_ADC2_DMXL_GC              5
+#define ADC4_ADC1_DMXL_GC              4
+#define ADC4_ADC4_DMXL_SRC             3
+#define ADC4_ADC3_DMXL_SRC             2
+#define ADC4_ADC2_DMXL_SRC             1
+#define ADC4_ADC1_DMXL_SRC             0
+
+
+/*ADC_DIG_DEBUG*/
+#define ADC_PTN_SEL                            0
+
+
+/*I2S_DAT_PADDRV_CTRL*/
+#define TX2_DAT_DRV                            4
+#define TX1_DAT_DRV                            0
+
+/*I2S_CLK_PADDRV_CTRL*/
+#define LRCK_DRV                               4
+#define BCLK_DRV                               0
+
+
+/*ANA_PGA1_CTRL*/
+#define ADC1_ANALOG_PGA                        1
+#define ADC1_ANALOG_PGA_STEP   0
+
+/*ANA_PGA2_CTRL*/
+#define ADC2_ANALOG_PGA                        1
+#define ADC2_ANALOG_PGA_STEP   0
+
+/*ANA_PGA3_CTRL*/
+#define ADC3_ANALOG_PGA                        1
+#define ADC3_ANALOG_PGA_STEP   0
+
+/*ANA_PGA4_CTRL*/
+#define ADC4_ANALOG_PGA                        1
+#define ADC4_ANALOG_PGA_STEP   0
+
+
+/*MIC_OFFSET_CTRL1*/
+#define MIC_OFFSET_CAL_EN4             3
+#define MIC_OFFSET_CAL_EN3             2
+#define MIC_OFFSET_CAL_EN2             1
+#define MIC_OFFSET_CAL_EN1             0
+
+/*MIC_OFFSET_CTRL2*/
+#define MIC_OFFSET_CAL_GAIN            3
+#define MIC_OFFSET_CAL_CHANNEL 1
+#define MIC_OFFSET_CAL_EN_ONCE 0
+
+/*MIC1_OFFSET_STATU1*/
+#define MIC1_OFFSET_CAL_DONE   7
+#define MIC1_OFFSET_CAL_RUN_STA        6
+#define MIC1_OFFSET_MSB                        0
+
+/*MIC1_OFFSET_STATU2*/
+#define MIC1_OFFSET_LSB                        0
+
+/*MIC2_OFFSET_STATU1*/
+#define MIC2_OFFSET_CAL_DONE   7
+#define MIC2_OFFSET_CAL_RUN_STA        6
+#define MIC2_OFFSET_MSB                        0
+
+/*MIC2_OFFSET_STATU2*/
+#define MIC2_OFFSET_LSB                        0
+
+/*MIC3_OFFSET_STATU1*/
+#define MIC3_OFFSET_CAL_DONE   7
+#define MIC3_OFFSET_CAL_RUN_STA        6
+#define MIC3_OFFSET_MSB                        0
+
+/*MIC3_OFFSET_STATU2*/
+#define MIC3_OFFSET_LSB                        0
+
+/*MIC4_OFFSET_STATU1*/
+#define MIC4_OFFSET_CAL_DONE   7
+#define MIC4_OFFSET_CAL_RUN_STA        6
+#define MIC4_OFFSET_MSB                        0
+
+/*MIC4_OFFSET_STATU2*/
+#define MIC4_OFFSET_LSB                        0
+
+
+/*ANA_ADC1_CTRL1*/
+#define ADC1_PGA_BYPASS                        7
+#define ADC1_PGA_BYP_RCM               6
+#define ADC1_PGA_CTRL_RCM              4
+#define ADC1_PGA_MUTE                  3
+#define ADC1_DSM_ENABLE                        2
+#define ADC1_PGA_ENABLE                        1
+#define ADC1_MICBIAS_EN                        0
+
+/*ANA_ADC1_CTRL3*/
+#define ADC1_ANA_CAL_EN                        5
+#define ADC1_SEL_OUT_EDGE              3
+#define ADC1_DSM_DISABLE               2
+#define ADC1_VREFP_DISABLE             1
+#define ADC1_AAF_DISABLE               0
+
+/*ANA_ADC1_CTRL6*/
+#define PGA_CTRL_TC                            6
+#define PGA_CTRL_RC                            4
+#define PGA_CTRL_I_LIN                 2
+#define PGA_CTRL_I_IN                  0
+
+/*ANA_ADC1_CTRL7*/
+#define PGA_CTRL_HI_Z                  7
+#define PGA_CTRL_SHORT_RF              6
+#define PGA_CTRL_VCM_VG                        4
+#define PGA_CTRL_VCM_IN                        0
+
+
+/*ANA_ADC2_CTRL1*/
+#define ADC2_PGA_BYPASS                        7
+#define ADC2_PGA_BYP_RCM               6
+#define ADC2_PGA_CTRL_RCM              4
+#define ADC2_PGA_MUTE                  3
+#define ADC2_DSM_ENABLE                        2
+#define ADC2_PGA_ENABLE                        1
+#define ADC2_MICBIAS_EN                        0
+
+/*ANA_ADC2_CTRL3*/
+#define ADC2_ANA_CAL_EN                        5
+#define ADC2_SEL_OUT_EDGE              3
+#define ADC2_DSM_DISABLE               2
+#define ADC2_VREFP_DISABLE             1
+#define ADC2_AAF_DISABLE               0
+
+/*ANA_ADC2_CTRL6*/
+#define PGA_CTRL_IBOOST                        7
+#define PGA_CTRL_IQCTRL                        6
+#define PGA_CTRL_OABIAS                        4
+#define PGA_CTRL_CMLP_DIS              3
+#define PGA_CTRL_PDB_RIN               2
+#define PGA_CTRL_PEAKDET               0
+
+/*ANA_ADC2_CTRL7*/
+#define AAF_LPMODE_EN                  7
+#define AAF_STG2_IB_SEL                        4
+#define AAFDSM_IB_DIV2                 3
+#define AAF_STG1_IB_SEL                        0
+
+
+/*ANA_ADC3_CTRL1*/
+#define ADC3_PGA_BYPASS                        7
+#define ADC3_PGA_BYP_RCM               6
+#define ADC3_PGA_CTRL_RCM              4
+#define ADC3_PGA_MUTE                  3
+#define ADC3_DSM_ENABLE                        2
+#define ADC3_PGA_ENABLE                        1
+#define ADC3_MICBIAS_EN                        0
+
+/*ANA_ADC3_CTRL3*/
+#define ADC3_ANA_CAL_EN                        5
+#define ADC3_INVERT_CLK                        4
+#define ADC3_SEL_OUT_EDGE              3
+#define ADC3_DSM_DISABLE               2
+#define ADC3_VREFP_DISABLE             1
+#define ADC3_AAF_DISABLE               0
+
+/*ANA_ADC3_CTRL7*/
+#define DSM_COMP_IB_SEL                        6
+#define DSM_OTA_CTRL                   4
+#define DSM_LPMODE                             3
+#define DSM_OTA_IB_SEL                 0
+
+
+/*ANA_ADC4_CTRL1*/
+#define ADC4_PGA_BYPASS                        7
+#define ADC4_PGA_BYP_RCM               6
+#define ADC4_PGA_CTRL_RCM              4
+#define ADC4_PGA_MUTE                  3
+#define ADC4_DSM_ENABLE                        2
+#define ADC4_PGA_ENABLE                        1
+#define ADC4_MICBIAS_EN                        0
+
+/*ANA_ADC4_CTRL3*/
+#define ADC4_ANA_CAL_EN                        5
+#define ADC4_SEL_OUT_EDGE              3
+#define ADC4_DSM_DISABLE               2
+#define ADC4_VREFP_DISABLE             1
+#define ADC4_AAF_DISABLE               0
+
+/*ANA_ADC4_CTRL6*/
+#define DSM_DEMOFF                             5
+#define DSM_EN_DITHER                  4
+#define DSM_VREFP_LPMODE               2
+#define DSM_VREFP_OUTCTRL              0
+
+/*ANA_ADC4_CTRL7*/
+#define CK8M_EN                                        5
+#define OSC_EN                                 4
+#define ADC4_CLK_GATING                        3
+#define ADC3_CLK_GATING                        2
+#define ADC2_CLK_GATING                        1
+#define ADC1_CLK_GATING                        0
+
+
+/*GPIO_CFG1*/
+#define GPIO2_SELECT                   4
+#define GPIO1_SELECT                   0
+
+/*GPIO_CFG2*/
+#define GPIO4_SELECT                   4
+#define GPIO3_SELECT                   0
+
+/*GPIO_DAT*///order???
+#define GPIO4_DAT                              3
+#define GPIO3_DAT                              2
+#define GPIO2_DAT                              1
+#define GPIO1_DAT                              0
+
+/*GPIO_DRV*/
+#define GPIO4_DRV                              6
+#define GPIO3_DRV                              4
+#define GPIO2_DRV                              2
+#define GPIO1_DRV                              0
+
+/*GPIO_PULL*/
+#define GPIO4_PULL                             6
+#define GPIO3_PULL                             4
+#define GPIO2_PULL                             2
+#define GPIO1_PULL                             0
+
+/*GPIO_INT_CFG*/
+#define GPIO4_EINT_CFG                 6
+#define GPIO3_EINT_CFG                 4
+#define GPIO2_EINT_CFG                 2
+#define GPIO1_EINT_CFG                 0
+
+/*GPIO_INT_EN*///order???
+#define GPIO4_EINT_EN                  3
+#define GPIO3_EINT_EN                  2
+#define GPIO2_EINT_EN                  1
+#define GPIO1_EINT_EN                  0
+
+/*GPIO_INT_STATUS*///order???
+#define GPIO4_EINT_STA                 3
+#define GPIO3_EINT_STA                 2
+#define GPIO2_EINT_STA                 1
+#define GPIO1_EINT_STA                 0
+
+
+/*PRNG_CLK_CTRL*/
+#define PRNG_CLK_EN                            1
+#define PRNG_CLK_POS                   0
+
+
+
+/*** Some Config Value ***/
+
+//[SYSCLK_CTRL]: PLLCLK_SRC
+#define PLLCLK_SRC_MCLK                        0
+#define PLLCLK_SRC_BCLK                        1
+#define PLLCLK_SRC_GPIO2               2
+#define PLLCLK_SRC_GPIO3               3
+
+//[SYSCLK_CTRL]: SYSCLK_SRC
+#define SYSCLK_SRC_MCLK                        0
+#define SYSCLK_SRC_PLL                 1
+
+//I2S BCLK POLARITY Control
+#define BCLK_NORMAL_DRIVE_N_SAMPLE_P   0
+#define BCLK_INVERT_DRIVE_P_SAMPLE_N   1
+
+//I2S LRCK POLARITY Control
+#define        LRCK_LEFT_LOW_RIGHT_HIGH                0
+#define LRCK_LEFT_HIGH_RIGHT_LOW               1
+
+//I2S Format Selection
+#define PCM_FORMAT                                             0
+#define LEFT_JUSTIFIED_FORMAT                  1
+#define RIGHT_JUSTIFIED_FORMAT                 2
+
+
+//I2S data protocol types
+
+#define IS_ENCODING_MODE                0
+
+#endif
+
diff --git a/sound/soc/codecs/ac10x.h b/sound/soc/codecs/ac10x.h
new file mode 100644 (file)
index 0000000..d8ad442
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * ac10x.h
+ *
+ * (C) Copyright 2017-2018
+ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
+ *
+ * PeterYang <linsheng.yang@seeed.cc>
+ *
+ * (C) Copyright 2010-2017
+ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
+ * huangxin <huangxin@reuuimllatech.com>
+ *
+ * some simple description for this code
+ *
+ * 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.
+ *
+ */
+#ifndef __AC10X_H__
+#define __AC10X_H__
+
+#define AC101_I2C_ID           4
+#define _MASTER_AC108          0
+#define _MASTER_AC101          1
+#define _MASTER_MULTI_CODEC    _MASTER_AC101
+
+/* enable headset detecting & headset button pressing */
+#define CONFIG_AC101_SWITCH_DETECT
+
+/* obsolete */
+#define CONFIG_AC10X_TRIG_LOCK 0
+
+#ifdef AC101_DEBG
+    #define AC101_DBG(format,args...)  printk("[AC101] %s() L%d " format, __func__, __LINE__, ##args)
+#else
+    #define AC101_DBG(...)
+#endif
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,18,0)
+#define __NO_SND_SOC_CODEC_DRV     1
+#else
+#define __NO_SND_SOC_CODEC_DRV     0
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
+#if __has_attribute(__fallthrough__)
+# define fallthrough                    __attribute__((__fallthrough__))
+#else
+# define fallthrough                    do {} while (0)  /* fallthrough */
+#endif
+#endif
+
+#if __NO_SND_SOC_CODEC_DRV
+#define codec                      component
+#define snd_soc_codec              snd_soc_component
+#define snd_soc_codec_driver       snd_soc_component_driver
+#define snd_soc_codec_get_drvdata  snd_soc_component_get_drvdata
+#define snd_soc_codec_get_dapm     snd_soc_component_get_dapm
+#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level
+#define snd_soc_kcontrol_codec     snd_soc_kcontrol_component
+#define snd_soc_read               snd_soc_component_read32
+#define snd_soc_register_codec     snd_soc_register_component
+#define snd_soc_unregister_codec   snd_soc_unregister_component
+#define snd_soc_update_bits        snd_soc_component_update_bits
+#define snd_soc_write              snd_soc_component_write
+#define snd_soc_add_codec_controls snd_soc_add_component_controls
+#endif
+
+
+#ifdef CONFIG_AC101_SWITCH_DETECT
+enum headphone_mode_u {
+       HEADPHONE_IDLE,
+       FOUR_HEADPHONE_PLUGIN,
+       THREE_HEADPHONE_PLUGIN,
+};
+#endif
+
+struct ac10x_priv {
+       struct i2c_client *i2c[4];
+       struct regmap* i2cmap[4];
+       int codec_cnt;
+       unsigned sysclk;
+#define _FREQ_24_576K          24576000
+#define _FREQ_22_579K          22579200
+       unsigned mclk;  /* master clock or aif_clock/aclk */
+       int clk_id;
+       unsigned char i2s_mode;
+       unsigned char data_protocol;
+       struct delayed_work dlywork;
+       int tdm_chips_cnt;
+       int sysclk_en;
+
+       /* member for ac101 .begin */
+       struct snd_soc_codec *codec;
+       struct i2c_client *i2c101;
+       struct regmap* regmap101;
+
+       struct mutex dac_mutex;
+       u8 dac_enable;
+       spinlock_t lock;
+       u8 aif1_clken;
+
+       struct work_struct codec_resume;
+       struct gpio_desc* gpiod_spk_amp_gate;
+
+       #ifdef CONFIG_AC101_SWITCH_DETECT
+       struct gpio_desc* gpiod_irq;
+       long irq;
+       volatile int irq_cntr;
+       volatile int pullout_cntr;
+       volatile int state;
+
+       enum headphone_mode_u mode;
+       struct work_struct work_switch;
+       struct work_struct work_clear_irq;
+
+       struct input_dev* inpdev;
+       #endif
+       /* member for ac101 .end */
+};
+
+
+/* AC101 DAI operations */
+int ac101_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
+void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
+int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
+int ac101_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *codec_dai);
+int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
+                 struct snd_soc_dai *dai);
+int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute);
+
+/* codec driver specific */
+int ac101_codec_probe(struct snd_soc_codec *codec);
+int ac101_codec_remove(struct snd_soc_codec *codec);
+int ac101_codec_suspend(struct snd_soc_codec *codec);
+int ac101_codec_resume(struct snd_soc_codec *codec);
+int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level);
+
+/* i2c device specific */
+int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id);
+void ac101_shutdown(struct i2c_client *i2c);
+int ac101_remove(struct i2c_client *i2c);
+
+int ac10x_fill_regcache(struct device* dev, struct regmap* map);
+
+#endif//__AC10X_H__
index 0d16723..040795c 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "wm8960.h"
 
+#define WM8960_MCLK             4096000
 /* R25 - Power 1 */
 #define WM8960_VMID_MASK 0x180
 #define WM8960_VREF      0x40
@@ -1297,6 +1298,8 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
 {
        struct snd_soc_component *component = dai->component;
        struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
+       clk_id = WM8960_SYSCLK_MCLK;
+
 
        switch (clk_id) {
        case WM8960_SYSCLK_MCLK:
@@ -1312,7 +1315,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
        default:
                return -EINVAL;
        }
-
+       wm8960->freq_in = WM8960_MCLK;
        wm8960->sysclk = freq;
        wm8960->clk_id = clk_id;
 
@@ -1364,6 +1367,22 @@ static int wm8960_probe(struct snd_soc_component *component)
        else
                wm8960->set_bias_level = wm8960_set_bias_level_out3;
 
+       #if 1
+       snd_soc_component_update_bits(component, WM8960_LDAC, 0x100, 0x100);
+       snd_soc_component_update_bits(component, WM8960_RDAC, 0x100, 0x100);
+       snd_soc_component_update_bits(component, WM8960_LOUT1, 0x100, 0x100); 
+       snd_soc_component_update_bits(component, WM8960_ROUT1, 0x100, 0x100); 
+       snd_soc_component_update_bits(component, WM8960_LOUT2, 0x100, 0x100);
+       snd_soc_component_update_bits(component, WM8960_ROUT2, 0x100, 0x100);
+       snd_soc_component_update_bits(component, WM8960_POWER2, 0x1fB, 0x198);
+       snd_soc_component_update_bits(component, WM8960_LOUTMIX, 0x1F0, 0x100);
+       snd_soc_component_update_bits(component, WM8960_ROUTMIX, 0x1F0, 0x100);
+       snd_soc_component_update_bits(component, WM8960_LOUT1, 0x7f, 0x6f);
+       snd_soc_component_update_bits(component, WM8960_ROUT1, 0x7f, 0x6f);
+       snd_soc_component_update_bits(component, WM8960_LOUT2, 0x7f, 0x7f);
+       snd_soc_component_update_bits(component, WM8960_ROUT2, 0x7f, 0x7f);
+       #endif
+
        snd_soc_add_component_controls(component, wm8960_snd_controls,
                                     ARRAY_SIZE(wm8960_snd_controls));
        wm8960_add_widgets(component);
@@ -1421,6 +1440,8 @@ static int wm8960_i2c_probe(struct i2c_client *i2c)
        if (wm8960 == NULL)
                return -ENOMEM;
 
+       wm8960->clk_id = WM8960_SYSCLK_MCLK;
+
        wm8960->mclk = devm_clk_get(&i2c->dev, "mclk");
        if (IS_ERR(wm8960->mclk)) {
                if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER)
@@ -1463,7 +1484,14 @@ static int wm8960_i2c_probe(struct i2c_client *i2c)
        regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100);
        regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
        regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);
-
+       
+       regmap_update_bits(wm8960->regmap, WM8960_LINPATH, 0x138, 0x138);
+       regmap_update_bits(wm8960->regmap, WM8960_RINPATH, 0x138, 0x138);
+       regmap_update_bits(wm8960->regmap, WM8960_POWER1, 0x7E, 0x7E);
+       regmap_update_bits(wm8960->regmap, WM8960_POWER3, 0x30, 0x30);
+       regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x19F, 0x197);
+       regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x19F, 0x197);
+       
        /* ADCLRC pin configured as GPIO. */
        regmap_update_bits(wm8960->regmap, WM8960_IFACE2, 1 << 6,
                           wm8960->pdata.gpio_cfg[0] << 6);
index f966d39..9a755e8 100644 (file)
@@ -720,7 +720,7 @@ static int dw_i2s_remove(struct platform_device *pdev)
 
 #ifdef CONFIG_OF
 static const struct of_device_id dw_i2s_of_match[] = {
-       { .compatible = "snps,designware-i2s",   },
+       { .compatible = "snps,designware-i2s",   },
        {},
 };
 
diff --git a/sound/soc/starfive/Kconfig b/sound/soc/starfive/Kconfig
new file mode 100644 (file)
index 0000000..918614a
--- /dev/null
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_SOC_STARFIVE
+       tristate "Audio support for Starfive"
+       depends on COMPILE_TEST || SOC_STARFIVE
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the Starfive SoCs' Audio interfaces. You will also need to
+         select the audio interfaces to support below.
+
+config SND_SOC_STARFIVE_PWMDAC
+       tristate "Starfive PWMDAC module"
+       depends on HAVE_CLK && SND_SOC_STARFIVE
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       help
+        Say Y or M if you want to add support for sf pwmdac driver.
+
+config SND_SOC_STARFIVE_PWMDAC_TRANSMITTER
+        tristate "Starfive PWMDAC transmitter dit codecs"
+        depends on SND_SOC_STARFIVE_PWMDAC
+        default SND_SOC_STARFIVE_PWMDAC
+        help
+         Say Y or M if you want to add support for sf pwmdac transmitter dit driver.
+
+config SND_SOC_STARFIVE_PWMDAC_PCM
+       bool "PCM PIO extension for PWMDAC"
+       depends on SND_SOC_STARFIVE_PWMDAC
+       help
+        Say Y or N if you want to add a custom ALSA extension that registers
+        a PCM and uses PIO to transfer data.
+
+config SND_SOC_STARFIVE_I2S
+       tristate "Starfive I2S module"
+       depends on HAVE_CLK && SND_SOC_STARFIVE
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       help
+         Say 'Y' or 'M' here if you are building for StarFive SoCs
+         platforms that contain i2s controller core like i2stx_4ch0
+         and i2srx_master.
+
+config SND_SOC_STARFIVE_PDM
+       tristate "Starfive PDM module"
+       depends on HAVE_CLK && SND_SOC_STARFIVE
+       select SND_SOC_STARFIVE_I2S
+       select REGMAP_MMIO
+       help
+        Say Y or M if you want to add support for sf pdm driver.
+
+config SND_SOC_STARFIVE_TDM
+       tristate "Starfive TDM module"
+       depends on HAVE_CLK && SND_SOC_STARFIVE
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       help
+        Say Y or M if you want to add support for sf tdm driver.
+
+config SND_SOC_STARFIVE_SPDIF
+       tristate "Starfive SPDIF module"
+       depends on HAVE_CLK && SND_SOC_STARFIVE
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       select REGMAP_MMIO
+       help
+         Say Y or M if you want to add support for codecs attached to the
+         I2S interface on VIC vic_starlight board. You will also need to select
+         the drivers for the rest of VIC audio subsystem.
+
+config SND_SOC_STARFIVE_SPDIF_PCM
+       bool "PCM PIO extension for SPDIF"
+       depends on SND_SOC_STARFIVE_SPDIF
+       help
+        Say Y or N if you want to add a custom ALSA extension that registers
+        a PCM and uses PIO to transfer data.
diff --git a/sound/soc/starfive/Makefile b/sound/soc/starfive/Makefile
new file mode 100644 (file)
index 0000000..85c0759
--- /dev/null
@@ -0,0 +1,13 @@
+# starfive Platform Support
+obj-$(CONFIG_SND_SOC_STARFIVE_PWMDAC) += starfive_pwmdac.o
+obj-$(CONFIG_SND_SOC_STARFIVE_PWMDAC_TRANSMITTER) += starfive_pwmdac_transmitter.o
+pwmdac-$(CONFIG_SND_SOC_STARFIVE_PWMDAC_PCM) += starfive_pwmdac_pcm.o
+
+obj-$(CONFIG_SND_SOC_STARFIVE_PDM) += starfive_pdm.o
+obj-$(CONFIG_SND_SOC_STARFIVE_TDM) += starfive_tdm.o
+
+obj-$(CONFIG_SND_SOC_STARFIVE_SPDIF) += spdif.o
+spdif-y := starfive_spdif.o
+spdif-$(CONFIG_SND_SOC_STARFIVE_SPDIF_PCM) += starfive_spdif_pcm.o
+
+obj-$(CONFIG_SND_SOC_STARFIVE_I2S) += starfive_i2s.o
diff --git a/sound/soc/starfive/pwmdac.h b/sound/soc/starfive/pwmdac.h
new file mode 100644 (file)
index 0000000..8175c94
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * PWMDAC driver for the StarFive JH7110 SoC
+ *
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ */
+
+#ifndef __STARFIVE_PWMDAC_LOCAL_H
+#define __STARFIVE_PWMDAC_LOCAL_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+
+#define PWMDAC_WDATA           0       /*PWMDAC_BASE_ADDR*/
+#define PWMDAC_CTRL            0x04    /*PWMDAC_BASE_ADDR + 0x04*/
+#define PWMDAC_SATAE           0x08    /*PWMDAC_BASE_ADDR + 0x08*/
+#define PWMDAC_RESERVED                0x0C    /*PWMDAC_BASE_ADDR + 0x0C*/
+
+#define SFC_PWMDAC_SHIFT                       BIT(1)
+#define SFC_PWMDAC_DUTY_CYCLE                  BIT(2)
+#define SFC_PWMDAC_CNT_N                       BIT(4)
+
+#define SFC_PWMDAC_LEFT_RIGHT_DATA_CHANGE      BIT(13)
+#define SFC_PWMDAC_DATA_MODE                   BIT(14)
+
+#define FIFO_UN_FULL   0
+#define FIFO_FULL      1
+
+#define PWMDAC_CTRL_DATA_SHIFT  4
+#define PWMDAC_CTRL_DATA_MASK  0xF
+#define PWMDAC_DATA_LEFT_SHIFT  15
+#define PWMDAC_DUTY_CYCLE_LOW  2
+#define PWMDAC_DUTY_CYCLE_HIGH 3
+
+#define PWMDAC_MCLK    4096000
+
+enum pwmdac_lr_change{
+       NO_CHANGE = 0,
+       CHANGE,
+};
+
+enum pwmdac_d_mode{
+       UNSINGED_DATA = 0,
+       INVERTER_DATA_MSB,
+};
+
+enum pwmdac_shift_bit{
+       PWMDAC_SHIFT_8 = 8,             /*pwmdac shift 8 bit*/
+       PWMDAC_SHIFT_10 = 10,           /*pwmdac shift 10 bit*/
+};
+
+enum pwmdac_duty_cycle{
+       PWMDAC_CYCLE_LEFT = 0,          /*pwmdac duty cycle left*/
+       PWMDAC_CYCLE_RIGHT = 1,         /*pwmdac duty cycle right*/
+       PWMDAC_CYCLE_CENTER = 2,        /*pwmdac duty cycle center*/
+};
+
+/*sample count [12:4] <511*/
+enum pwmdac_sample_count{
+       PWMDAC_SAMPLE_CNT_1 = 1,
+       PWMDAC_SAMPLE_CNT_2,
+       PWMDAC_SAMPLE_CNT_3,
+       PWMDAC_SAMPLE_CNT_4,
+       PWMDAC_SAMPLE_CNT_5,
+       PWMDAC_SAMPLE_CNT_6,
+       PWMDAC_SAMPLE_CNT_7,
+       PWMDAC_SAMPLE_CNT_8 = 1,        /*(32.468/8) == (12.288/3) == 4.096*/
+       PWMDAC_SAMPLE_CNT_9,
+       PWMDAC_SAMPLE_CNT_10,
+       PWMDAC_SAMPLE_CNT_11,
+       PWMDAC_SAMPLE_CNT_12,
+       PWMDAC_SAMPLE_CNT_13,
+       PWMDAC_SAMPLE_CNT_14,
+       PWMDAC_SAMPLE_CNT_15,
+       PWMDAC_SAMPLE_CNT_16,
+       PWMDAC_SAMPLE_CNT_17,
+       PWMDAC_SAMPLE_CNT_18,
+       PWMDAC_SAMPLE_CNT_19,
+       PWMDAC_SAMPLE_CNT_20 = 20,
+       PWMDAC_SAMPLE_CNT_30 = 30,
+       PWMDAC_SAMPLE_CNT_511 = 511,
+};
+
+enum data_shift{
+       PWMDAC_DATA_LEFT_SHIFT_BIT_0 = 0,
+       PWMDAC_DATA_LEFT_SHIFT_BIT_1,
+       PWMDAC_DATA_LEFT_SHIFT_BIT_2,
+       PWMDAC_DATA_LEFT_SHIFT_BIT_3,
+       PWMDAC_DATA_LEFT_SHIFT_BIT_4,
+       PWMDAC_DATA_LEFT_SHIFT_BIT_5,
+       PWMDAC_DATA_LEFT_SHIFT_BIT_6,
+       PWMDAC_DATA_LEFT_SHIFT_BIT_7,
+       PWMDAC_DATA_LEFT_SHIFT_BIT_ALL,
+};
+
+enum pwmdac_config_list{
+       shift_8Bit_unsigned = 0,
+       shift_8Bit_unsigned_dataShift,
+       shift_10Bit_unsigned,
+       shift_10Bit_unsigned_dataShift,
+
+       shift_8Bit_inverter,
+       shift_8Bit_inverter_dataShift,
+       shift_10Bit_inverter,
+       shift_10Bit_inverter_dataShift,
+};
+
+struct sf_pwmdac_dev {
+       void __iomem *pwmdac_base;
+       resource_size_t mapbase;
+       u8  mode;
+       u8 shift_bit;
+       u8 duty_cycle;
+       u8 datan;
+       u8 data_mode;
+       u8 lr_change;
+       u8 shift;
+       u8 fifo_th;
+       bool use_pio;
+       spinlock_t lock;
+       int active;
+
+       struct clk *clk_apb0;
+       struct clk *clk_pwmdac_apb;
+       struct clk *clk_pwmdac_core;
+       struct reset_control *rst_apb;
+
+       struct device *dev;
+       struct snd_dmaengine_dai_dma_data play_dma_data;
+       struct snd_pcm_substream __rcu *tx_substream;
+       unsigned int (*tx_fn)(struct sf_pwmdac_dev *dev,
+                       struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+                       bool *period_elapsed);
+       unsigned int tx_ptr;
+       struct task_struct *tx_thread;
+       bool tx_thread_exit;
+
+       struct clk *audio_src;
+       struct clk *pwmdac_apb;
+       struct clk *pwmdac_mclk;
+       unsigned int pwmdac_ctrl_data;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_STARFIVE_PWMDAC_PCM)
+void sf_pwmdac_pcm_push_tx(struct sf_pwmdac_dev *dev);
+void sf_pwmdac_pcm_pop_rx(struct sf_pwmdac_dev *dev);
+int sf_pwmdac_pcm_register(struct platform_device *pdev);
+#else
+void sf_pwmdac_pcm_push_tx(struct sf_pwmdac_dev *dev) { }
+void sf_pwmdac_pcm_pop_rx(struct sf_pwmdac_dev *dev) { }
+int sf_pwmdac_pcm_register(struct platform_device *pdev)
+{
+       return -EINVAL;
+}
+#endif
+
+#endif
diff --git a/sound/soc/starfive/starfive_i2s.c b/sound/soc/starfive/starfive_i2s.c
new file mode 100644 (file)
index 0000000..8a1ccfe
--- /dev/null
@@ -0,0 +1,1379 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Synopsys I2S Audio Layer
+ *
+ * sound/soc/dwc/designware_i2s.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Rajeev Kumar <rajeevkumar.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <sound/designware_i2s.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include "starfive_i2s.h"
+
+static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
+{
+       writel(val, io_base + reg);
+}
+
+static inline u32 i2s_read_reg(void __iomem *io_base, int reg)
+{
+       return readl(io_base + reg);
+}
+
+static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream)
+{
+       u32 i = 0;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < 4; i++)
+                       i2s_write_reg(dev->i2s_base, TER(i), 0);
+       } else {
+               for (i = 0; i < 4; i++)
+                       i2s_write_reg(dev->i2s_base, RER(i), 0);
+       }
+}
+
+static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
+{
+       u32 i = 0;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < 4; i++)
+                       i2s_read_reg(dev->i2s_base, TOR(i));
+       } else {
+               for (i = 0; i < 4; i++)
+                       i2s_read_reg(dev->i2s_base, ROR(i));
+       }
+}
+
+static inline void i2s_disable_irqs(struct dw_i2s_dev *dev, u32 stream,
+                                   int chan_nr)
+{
+       u32 i, irq;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
+               }
+       } else {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
+               }
+       }
+}
+
+static inline void i2s_enable_irqs(struct dw_i2s_dev *dev, u32 stream,
+                                  int chan_nr)
+{
+       u32 i, irq;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
+               }
+       } else {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
+               }
+       }
+}
+
+static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
+{
+       struct dw_i2s_dev *dev = dev_id;
+       bool irq_valid = false;
+       u32 isr[4];
+       int i;
+
+       for (i = 0; i < 4; i++)
+               isr[i] = i2s_read_reg(dev->i2s_base, ISR(i));
+
+       i2s_clear_irqs(dev, SNDRV_PCM_STREAM_PLAYBACK);
+       i2s_clear_irqs(dev, SNDRV_PCM_STREAM_CAPTURE);
+
+       for (i = 0; i < 4; i++) {
+               /*
+                * Check if TX fifo is empty. If empty fill FIFO with samples
+                * NOTE: Only two channels supported
+                */
+               if ((isr[i] & ISR_TXFE) && (i == 0) && dev->use_pio) {
+                       dw_pcm_push_tx(dev);
+                       irq_valid = true;
+               }
+
+               /*
+                * Data available. Retrieve samples from FIFO
+                * NOTE: Only two channels supported
+                */
+               if ((isr[i] & ISR_RXDA) && (i == 0) && dev->use_pio) {
+                       dw_pcm_pop_rx(dev);
+                       irq_valid = true;
+               }
+
+               /* Error Handling: TX */
+               if (isr[i] & ISR_TXFO) {
+                       dev_err(dev->dev, "TX overrun (ch_id=%d)\n", i);
+                       irq_valid = true;
+               }
+
+               /* Error Handling: TX */
+               if (isr[i] & ISR_RXFO) {
+                       dev_err(dev->dev, "RX overrun (ch_id=%d)\n", i);
+                       irq_valid = true;
+               }
+       }
+
+       if (irq_valid)
+               return IRQ_HANDLED;
+       else
+               return IRQ_NONE;
+}
+
+static void i2s_start(struct dw_i2s_dev *dev,
+                     struct snd_pcm_substream *substream)
+{
+       struct i2s_clk_config_data *config = &dev->config;
+
+       i2s_write_reg(dev->i2s_base, IER, 1);
+       i2s_enable_irqs(dev, substream->stream, config->chan_nr);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               i2s_write_reg(dev->i2s_base, ITER, 1);
+       else
+               i2s_write_reg(dev->i2s_base, IRER, 1);
+
+       i2s_write_reg(dev->i2s_base, CER, 1);
+
+}
+
+static void i2s_stop(struct dw_i2s_dev *dev,
+               struct snd_pcm_substream *substream)
+{
+       i2s_clear_irqs(dev, substream->stream);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               i2s_write_reg(dev->i2s_base, ITER, 0);
+       else
+               i2s_write_reg(dev->i2s_base, IRER, 0);
+
+       i2s_disable_irqs(dev, substream->stream, 8);
+
+       if (!dev->active) {
+               i2s_write_reg(dev->i2s_base, CER, 0);
+               i2s_write_reg(dev->i2s_base, IER, 0);
+       }
+}
+
+static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
+{
+       u32 ch_reg;
+       struct i2s_clk_config_data *config = &dev->config;
+
+       i2s_disable_channels(dev, stream);
+
+       for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       i2s_write_reg(dev->i2s_base, TCR(ch_reg),
+                                     dev->xfer_resolution);
+                       i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
+                                     dev->fifo_th - 1);
+                       i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
+               } else {
+                       i2s_write_reg(dev->i2s_base, RCR(ch_reg),
+                                     dev->xfer_resolution);
+                       i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
+                                     dev->fifo_th - 1);
+                       i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
+               }
+       }
+}
+
+static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+       struct i2s_clk_config_data *config = &dev->config;
+       int ret;
+       unsigned int bclk_rate;
+       bool playback;
+       union dw_i2s_snd_dma_data *dma_data = NULL;
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+       dai_link->stop_dma_first = 1;
+       config->chan_nr = params_channels(params);
+       config->sample_rate = params_rate(params);
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               playback = false;
+       else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               playback = true;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               if ((config->sample_rate == 8000) && playback &&
+                       (dev->capability & DW_I2S_SLAVE)) {
+                       dev_warn(dev->dev, "I2S: unsupported 8000 rate with S16_LE, Stereo if use wm8960.\n");
+               }
+
+               config->data_width = 16;
+               dev->ccr = 0x00;
+               dev->xfer_resolution = 0x02;
+               if (playback)
+                       dev->play_dma_data.dt.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+               else
+                       dev->capture_dma_data.dt.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+               break;
+
+       case SNDRV_PCM_FORMAT_S32_LE:
+               config->data_width = 32;
+               dev->ccr = 0x10;
+               dev->xfer_resolution = 0x05;
+               if (playback)
+                       dev->play_dma_data.dt.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+               else
+                       dev->capture_dma_data.dt.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+               break;
+
+       default:
+               dev_err(dev->dev, "designware-i2s: unsupported PCM fmt");
+               return -EINVAL;
+       }
+
+       if (dev->capability & DW_I2S_MASTER) {
+               switch (params_rate(params)) {
+               case 8000:
+                       bclk_rate = 512000;
+                       break;
+               case 11025:
+                       bclk_rate = 705600;
+                       break;
+               case 16000:
+                       bclk_rate = 1024000;
+                       break;
+               case 32000:
+                       bclk_rate = 2048000;
+                       break;
+               case 48000:
+                       bclk_rate = 3072000;
+                       break;
+               default:
+                       dev_err(dai->dev, "%d rate not supported\n",
+                                       params_rate(params));
+                       ret = -EINVAL;
+                       goto err_return;
+               }
+       }
+
+       switch (config->chan_nr) {
+       case EIGHT_CHANNEL_SUPPORT:
+       case SIX_CHANNEL_SUPPORT:
+       case FOUR_CHANNEL_SUPPORT:
+       case TWO_CHANNEL_SUPPORT:
+               break;
+       default:
+               dev_err(dev->dev, "channel not supported\n");
+               return -EINVAL;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               dma_data = &dev->capture_dma_data;
+       else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dma_data = &dev->play_dma_data;
+       snd_soc_dai_set_dma_data(dai, substream, (void *)dma_data);
+
+       dw_i2s_config(dev, substream->stream);
+
+       i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
+
+       if (dev->capability & DW_I2S_MASTER) {
+               if (dev->i2s_clk_cfg) {
+                       ret = dev->i2s_clk_cfg(config);
+                       if (ret < 0) {
+                               dev_err(dev->dev, "runtime audio clk config fail\n");
+                               goto err_return;
+                       }
+               } else {
+                       if (playback) {
+                               ret = clk_set_parent(dev->clk_mclk, dev->clk_mclk_ext);
+                               if (ret) {
+                                       dev_err(dev->dev,
+                                               "failed to set mclk parent to mclk_ext\n");
+                                       goto err_return;
+                               }
+                       }
+
+                       ret = clk_set_rate(dev->clk_i2s_bclk_mst, bclk_rate);
+                       if (ret) {
+                               dev_err(dev->dev, "Can't set i2s bclk: %d\n", ret);
+                               goto err_return;
+                       }
+
+                       dev_dbg(dev->dev, "get rate mclk: %ld\n",
+                               clk_get_rate(dev->clk_mclk));
+                       dev_dbg(dev->dev, "get rate bclk:%ld\n",
+                               clk_get_rate(dev->clk_i2s_bclk));
+                       dev_dbg(dev->dev, "get rate lrck:%ld\n",
+                               clk_get_rate(dev->clk_i2s_lrck));
+               }
+       }
+       return 0;
+
+err_return:
+       return ret;
+}
+
+static int dw_i2s_prepare(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               i2s_write_reg(dev->i2s_base, TXFFR, 1);
+       else
+               i2s_write_reg(dev->i2s_base, RXFFR, 1);
+
+       return 0;
+}
+
+static int dw_i2s_trigger(struct snd_pcm_substream *substream,
+               int cmd, struct snd_soc_dai *dai)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               dev->active++;
+               i2s_start(dev, substream);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               dev->active--;
+               i2s_stop(dev, substream);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+       int ret = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               if (dev->capability & DW_I2S_SLAVE)
+                       ret = 0;
+               else
+                       ret = -EINVAL;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               if (dev->capability & DW_I2S_MASTER)
+                       ret = 0;
+               else
+                       ret = -EINVAL;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+       case SND_SOC_DAIFMT_CBS_CFM:
+               ret = -EINVAL;
+               break;
+       default:
+               dev_dbg(dev->dev, "dwc : Invalid master/slave format\n");
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
+       .hw_params      = dw_i2s_hw_params,
+       .prepare        = dw_i2s_prepare,
+       .trigger        = dw_i2s_trigger,
+       .set_fmt        = dw_i2s_set_fmt,
+};
+
+#ifdef CONFIG_PM
+static int dw_i2s_runtime_suspend(struct device *dev)
+{
+       struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(dw_dev->clk_i2s_bclk_mst);
+       clk_disable_unprepare(dw_dev->clk_i2s_apb);
+
+       return 0;
+}
+
+static int dw_i2s_runtime_resume(struct device *dev)
+{
+       struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(dw_dev->clk_i2s_apb);
+       if (ret) {
+               dev_err(dw_dev->dev, "Failed to enable clk_i2s_apb\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(dw_dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(dw_dev->dev, "Failed to enable clk_i2s_lrck\n");
+               return ret;
+       }
+
+       if (dw_dev->rst_i2s_apb) {
+               ret = reset_control_deassert(dw_dev->rst_i2s_apb);
+               if (ret)
+                       return ret;
+       }
+       if (dw_dev->rst_i2s_bclk) {
+               ret = reset_control_deassert(dw_dev->rst_i2s_bclk);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int dw_i2s_suspend(struct snd_soc_component *component)
+{
+       return pm_runtime_force_suspend(component->dev);
+}
+
+static int dw_i2s_resume(struct snd_soc_component *component)
+{
+       struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
+       struct snd_soc_dai *dai;
+       int stream;
+       int ret;
+
+       if (!dev->is_master) {
+               ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_i2s_bclk_mst);
+               if (ret) {
+                       dev_err(dev->dev, "%s: failed to set clk_i2s_bclk parent to bclk_mst.\n",
+                               __func__);
+                       goto exit;
+               }
+
+               ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_i2s_lrck_mst);
+               if (ret) {
+                       dev_err(dev->dev, "%s: failed to set clk_i2s_lrck parent to lrck_mst.\n",
+                               __func__);
+                       goto exit;
+               }
+       }
+
+       ret = pm_runtime_force_resume(component->dev);
+       if (ret)
+               return ret;
+
+       if (dev->syscon_base) {
+               if (dev->is_master) {
+                       /* config i2s data source: PDM  */
+                       regmap_update_bits(dev->syscon_base, dev->syscon_offset_34,
+                                               AUDIO_SDIN_MUX_MASK, I2SRX_DATA_SRC_PDM);
+
+                       regmap_update_bits(dev->syscon_base, dev->syscon_offset_18,
+                               I2SRX_3CH_ADC_MASK, I2SRX_3CH_ADC_EN);
+               } else {
+                       /*i2srx_3ch_adc_enable*/
+                       regmap_update_bits(dev->syscon_base, dev->syscon_offset_18,
+                                               0x1 << 1, 0x1 << 1);
+
+                       /*set i2sdin_sel*/
+                       regmap_update_bits(dev->syscon_base, dev->syscon_offset_34,
+                               (0x1 << 10) | (0x1 << 14) | (0x1<<17), (0x0<<10) | (0x0<<14) | (0x0<<17));
+               }
+       }
+
+       for_each_component_dais(component, dai) {
+               for_each_pcm_streams(stream)
+                       if (snd_soc_dai_stream_active(dai, stream))
+                               dw_i2s_config(dev, stream);
+       }
+
+       i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
+
+       if (!dev->is_master) {
+               ret = clk_set_parent(dev->clk_mclk, dev->clk_mclk_ext);
+               if (ret) {
+                       dev_err(dev->dev, "failed to set mclk parent to mclk_ext\n");
+                       goto exit;
+               }
+
+               ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_bclk_ext);
+               if (ret) {
+                       dev_err(dev->dev, "%s: failed to set clk_i2s_bclk parent to bclk_ext.\n",
+                               __func__);
+                       goto exit;
+               }
+
+               ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_lrck_ext);
+               if (ret) {
+                       dev_err(dev->dev, "%s: failed to set clk_i2s_lrck parent to lrck_ext.\n",
+                               __func__);
+                       goto exit;
+               }
+       }
+
+       return 0;
+
+exit:
+       return ret;
+}
+
+#else
+#define dw_i2s_suspend NULL
+#define dw_i2s_resume  NULL
+#endif
+
+static const struct snd_soc_component_driver dw_i2s_component = {
+       .name           = "dw-i2s",
+       .suspend        = dw_i2s_suspend,
+       .resume         = dw_i2s_resume,
+       .legacy_dai_naming      = 1,
+};
+
+static int dw_i2srx_mst_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev)
+{
+       int ret = 0;
+
+       static struct clk_bulk_data clks[] = {
+               { .id = "i2srx_apb" },
+               { .id = "i2srx_bclk_mst" },
+               { .id = "i2srx_lrck_mst" },
+               { .id = "i2srx_bclk" },
+               { .id = "i2srx_lrck" },
+               { .id = "mclk" },
+               { .id = "mclk_ext" },
+       };
+
+       ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to get i2srx clocks\n", __func__);
+               goto exit;
+       }
+
+       dev->clk_i2s_apb = clks[0].clk;
+       dev->clk_i2s_bclk_mst = clks[1].clk;
+       dev->clk_i2s_lrck_mst = clks[2].clk;
+       dev->clk_i2s_bclk = clks[3].clk;
+       dev->clk_i2s_lrck = clks[4].clk;
+       dev->clk_mclk = clks[5].clk;
+       dev->clk_mclk_ext = clks[6].clk;
+
+       dev->rst_i2s_apb = devm_reset_control_get_exclusive(&pdev->dev, "rst_apb_rx");
+       if (IS_ERR(dev->rst_i2s_apb)) {
+               dev_err(&pdev->dev, "failed to get apb_i2srx reset control\n");
+               ret = PTR_ERR(dev->rst_i2s_apb);
+               goto exit;
+       }
+
+       dev->rst_i2s_bclk = devm_reset_control_get_exclusive(&pdev->dev, "rst_bclk_rx");
+       if (IS_ERR(dev->rst_i2s_bclk)) {
+               dev_err(&pdev->dev, "failed to get i2s bclk rx reset control\n");
+               ret = PTR_ERR(dev->rst_i2s_bclk);
+               goto exit;
+       }
+
+       ret = clk_prepare_enable(dev->clk_i2s_apb);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to prepare enable clk_i2srx_apb\n");
+               goto exit;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set parent clk_i2s_bclk\n");
+               goto err_dis_bclk_mst;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_i2s_lrck_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set parent clk_i2s_lrck\n");
+               goto err_dis_bclk_mst;
+       }
+
+       ret = clk_set_parent(dev->clk_mclk, dev->clk_mclk_ext);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set mclk parent to mclk_ext\n");
+               goto err_dis_bclk_mst;
+       }
+
+       ret = clk_prepare_enable(dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to prepare enable clk_i2srx_3ch_bclk_mst\n");
+               goto err_dis_bclk_mst;
+       }
+
+       reset_control_deassert(dev->rst_i2s_apb);
+       reset_control_deassert(dev->rst_i2s_bclk);
+
+       regmap_update_bits(dev->syscon_base, dev->syscon_offset_18,
+                               I2SRX_3CH_ADC_MASK, I2SRX_3CH_ADC_EN);
+
+       return 0;
+
+err_dis_bclk_mst:
+       clk_disable_unprepare(dev->clk_i2s_apb);
+exit:
+       return ret;
+}
+
+static int dw_i2srx_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev)
+{
+       int ret = 0;
+
+       static struct clk_bulk_data clks[] = {
+               { .id = "3ch-apb" },
+               { .id = "bclk_mst" },
+               { .id = "3ch-lrck" },
+               { .id = "rx-bclk" },
+               { .id = "rx-lrck" },
+               { .id = "bclk-ext" },
+               { .id = "lrck-ext" },
+               { .id = "mclk" },
+               { .id = "mclk_ext" },
+       };
+
+       ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to get audio_subsys clocks\n", __func__);
+               return ret;
+       }
+
+       dev->clk_i2s_apb = clks[0].clk;
+       dev->clk_i2s_bclk_mst = clks[1].clk;
+       dev->clk_i2s_lrck_mst = clks[2].clk;
+       dev->clk_i2s_bclk = clks[3].clk;
+       dev->clk_i2s_lrck = clks[4].clk;
+       dev->clk_bclk_ext = clks[5].clk;
+       dev->clk_lrck_ext = clks[6].clk;
+       dev->clk_mclk = clks[7].clk;
+       dev->clk_mclk_ext = clks[8].clk;
+
+       ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to set clk_i2s_bclk parent to bclk_mst.\n",
+                       __func__);
+               goto exit;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_i2s_lrck_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to set clk_i2s_lrck parent to lrck_mst.\n",
+                       __func__);
+               goto exit;
+       }
+
+       ret = clk_prepare_enable(dev->clk_i2s_apb);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to enable clk_i2s_apb\n", __func__);
+               goto disable_apb_clk;
+       }
+
+       ret = clk_prepare_enable(dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_BCLK\n", __func__);
+               goto disable_bclk;
+       }
+
+       dev->rst_i2s_apb = devm_reset_control_array_get_exclusive(&pdev->dev);
+       if (IS_ERR(dev->rst_i2s_apb)) {
+               dev_err(&pdev->dev, "%s: failed to get rsts reset control\n", __func__);
+               goto disable_rst;
+       }
+
+       ret = reset_control_assert(dev->rst_i2s_apb);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to reset control assert rstc_rx\n", __func__);
+               goto disable_rst;
+       }
+       udelay(5);
+       ret = reset_control_deassert(dev->rst_i2s_apb);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to reset control deassert rstc_rx\n", __func__);
+               goto disable_rst;
+       }
+
+       /*i2srx_3ch_adc_enable*/
+       regmap_update_bits(dev->syscon_base, dev->syscon_offset_18,
+                                       0x1 << 1, 0x1 << 1);
+
+       /*set i2sdin_sel*/
+       regmap_update_bits(dev->syscon_base, dev->syscon_offset_34,
+               (0x1 << 10) | (0x1 << 14) | (0x1<<17), (0x0<<10) | (0x0<<14) | (0x0<<17));
+
+
+       ret = clk_set_parent(dev->clk_mclk, dev->clk_mclk_ext);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set mclk parent to mclk_ext\n");
+               goto disable_parent;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_bclk_ext);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to set clk_i2s_bclk parent to bclk_ext.\n",
+                       __func__);
+               goto disable_parent;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_lrck_ext);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to set clk_i2s_lrck parent to lrck_ext.\n",
+                       __func__);
+               goto disable_parent;
+       }
+
+       return 0;
+
+disable_parent:
+disable_rst:
+       clk_disable_unprepare(dev->clk_i2s_bclk_mst);
+disable_bclk:
+       clk_disable_unprepare(dev->clk_i2s_apb);
+disable_apb_clk:
+exit:
+
+       return ret;
+}
+
+static int dw_i2stx_4ch0_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev)
+{
+       int ret = 0;
+
+       static struct clk_bulk_data clks[] = {
+               { .id = "bclk-mst" },
+               { .id = "lrck-mst" },
+               { .id = "mclk" },
+               { .id = "bclk0" },
+               { .id = "lrck0" },
+               { .id = "i2s_apb" },
+               { .id = "mclk_ext" },
+       };
+
+       ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to get i2s clocks\n", __func__);
+               goto exit;
+       }
+
+       dev->clk_i2s_bclk_mst = clks[0].clk;
+       dev->clk_i2s_lrck_mst = clks[1].clk;
+       dev->clk_mclk = clks[2].clk;
+       dev->clk_i2s_bclk = clks[3].clk;
+       dev->clk_i2s_lrck = clks[4].clk;
+       dev->clk_i2s_apb = clks[5].clk;
+       dev->clk_mclk_ext = clks[6].clk;
+
+       dev->rst_i2s_apb = devm_reset_control_get_exclusive(&pdev->dev, "rst_apb");
+       if (IS_ERR(dev->rst_i2s_apb)) {
+               dev_err(&pdev->dev, "failed to get i2s apb reset control\n");
+               ret = PTR_ERR(dev->rst_i2s_apb);
+               goto exit;
+       }
+
+       dev->rst_i2s_bclk = devm_reset_control_get_exclusive(&pdev->dev, "rst_bclk");
+       if (IS_ERR(dev->rst_i2s_bclk)) {
+               dev_err(&pdev->dev, "failed to get i2s bclk reset control\n");
+               ret = PTR_ERR(dev->rst_i2s_bclk);
+               goto exit;
+       }
+
+       ret = clk_prepare_enable(dev->clk_i2s_apb);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to prepare enable clk_i2s_apb\n");
+               goto exit;
+       }
+
+       ret = clk_set_parent(dev->clk_mclk, dev->clk_mclk_ext);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set mclk parent to mclk_ext\n");
+               goto err_set_parent;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_lrck_mst, dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set parent clk_i2s_lrck_mst\n");
+               goto err_dis_bclk_mst;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set parent clk_i2s_bclk\n");
+               goto err_dis_bclk_mst;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_i2s_lrck_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set parent clk_i2s_lrck\n");
+               goto err_dis_bclk_mst;
+       }
+
+       ret = clk_prepare_enable(dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to prepare enable clk_i2srx_3ch_bclk_mst\n");
+               goto err_dis_bclk_mst;
+       }
+
+       reset_control_deassert(dev->rst_i2s_apb);
+       reset_control_deassert(dev->rst_i2s_bclk);
+
+       return 0;
+
+err_dis_bclk_mst:
+err_set_parent:
+       clk_disable_unprepare(dev->clk_i2s_apb);
+exit:
+       return ret;
+}
+
+static int dw_i2stx_4ch1_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev)
+{
+       int ret = 0;
+
+       static struct clk_bulk_data clks[] = {
+               { .id = "bclk_mst" },
+               { .id = "lrck_mst" },
+               { .id = "mclk" },
+               { .id = "4chbclk" },
+               { .id = "4chlrck" },
+               { .id = "clk_apb" },
+               { .id = "mclk_ext" },
+               { .id = "bclk_ext" },
+               { .id = "lrck_ext" },
+       };
+
+       ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to get i2s clocks\n", __func__);
+               goto exit;
+       }
+
+       dev->clk_i2s_bclk_mst = clks[0].clk;
+       dev->clk_i2s_lrck_mst = clks[1].clk;
+       dev->clk_mclk = clks[2].clk;
+       dev->clk_i2s_bclk = clks[3].clk;
+       dev->clk_i2s_lrck = clks[4].clk;
+       dev->clk_i2s_apb = clks[5].clk;
+       dev->clk_mclk_ext = clks[6].clk;
+       dev->clk_bclk_ext = clks[7].clk;
+       dev->clk_lrck_ext = clks[8].clk;
+
+       ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to set clk_i2s_bclk parent to bclk_mst.\n",
+                       __func__);
+               goto exit;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_i2s_lrck_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to set clk_i2s_lrck parent to lrck_mst.\n",
+                       __func__);
+               goto exit;
+       }
+
+       ret = clk_prepare_enable(dev->clk_i2s_apb);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to enable clk_i2s_apb\n", __func__);
+               goto exit;
+       }
+
+       ret = clk_prepare_enable(dev->clk_i2s_bclk_mst);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to enable clk_i2s_bclk_mst\n", __func__);
+               goto disable_bclk;
+       }
+
+       dev->rst_i2s_apb = devm_reset_control_array_get_exclusive(&pdev->dev);
+       if (IS_ERR(dev->rst_i2s_apb)) {
+               dev_err(&pdev->dev, "%s: failed to get rsts reset control\n", __func__);
+               goto disable_rst;
+       }
+
+       ret = reset_control_assert(dev->rst_i2s_apb);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to reset control assert rsts\n", __func__);
+               goto disable_rst;
+       }
+       udelay(5);
+       ret = reset_control_deassert(dev->rst_i2s_apb);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to reset control deassert rsts\n", __func__);
+               goto disable_rst;
+       }
+
+       ret = clk_set_parent(dev->clk_mclk, dev->clk_mclk_ext);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set mclk parent to mclk_ext\n");
+               goto exit;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_bclk_ext);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to set clk_i2s_bclk parent to bclk_ext.\n",
+                       __func__);
+               goto disable_parent;
+       }
+
+       ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_lrck_ext);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to set clk_i2s_lrck parent to lrck_ext.\n",
+                       __func__);
+               goto disable_parent;
+       }
+
+       return 0;
+
+disable_parent:
+disable_rst:
+       clk_disable_unprepare(dev->clk_i2s_bclk_mst);
+disable_bclk:
+       clk_disable_unprepare(dev->clk_i2s_apb);
+exit:
+       return ret;
+}
+
+/*
+ * The following tables allow a direct lookup of various parameters
+ * defined in the I2S block's configuration in terms of sound system
+ * parameters.  Each table is sized to the number of entries possible
+ * according to the number of configuration bits describing an I2S
+ * block parameter.
+ */
+
+/* Maximum bit resolution of a channel - not uniformly spaced */
+static const u32 fifo_width[COMP_MAX_WORDSIZE] = {
+       12, 16, 20, 24, 32, 0, 0, 0
+};
+
+/* Width of (DMA) bus */
+static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = {
+       DMA_SLAVE_BUSWIDTH_1_BYTE,
+       DMA_SLAVE_BUSWIDTH_2_BYTES,
+       DMA_SLAVE_BUSWIDTH_4_BYTES,
+       DMA_SLAVE_BUSWIDTH_UNDEFINED
+};
+
+/* PCM format to support channel resolution */
+static const u32 formats[COMP_MAX_WORDSIZE] = {
+       SNDRV_PCM_FMTBIT_S16_LE,
+       SNDRV_PCM_FMTBIT_S16_LE,
+       SNDRV_PCM_FMTBIT_S24_LE,
+       SNDRV_PCM_FMTBIT_S24_LE,
+       SNDRV_PCM_FMTBIT_S32_LE,
+       0,
+       0,
+       0
+};
+
+#define SF_PCM_RATE_32000_192000  (SNDRV_PCM_RATE_32000 | \
+                                  SNDRV_PCM_RATE_44100 | \
+                                  SNDRV_PCM_RATE_48000 | \
+                                  SNDRV_PCM_RATE_88200 | \
+                                  SNDRV_PCM_RATE_96000 | \
+                                  SNDRV_PCM_RATE_176400 | \
+                                  SNDRV_PCM_RATE_192000)
+
+static int dw_configure_dai(struct dw_i2s_dev *dev,
+                                  struct snd_soc_dai_driver *dw_i2s_dai,
+                                  unsigned int rates)
+{
+       /*
+        * Read component parameter registers to extract
+        * the I2S block's configuration.
+        */
+       u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
+       u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
+       u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
+       u32 idx;
+
+       if (dev->capability & DWC_I2S_RECORD &&
+                       dev->quirks & DW_I2S_QUIRK_COMP_PARAM1)
+               comp1 = comp1 & ~BIT(5);
+
+       if (dev->capability & DWC_I2S_PLAY &&
+                       dev->quirks & DW_I2S_QUIRK_COMP_PARAM1)
+               comp1 = comp1 & ~BIT(6);
+
+       if (COMP1_TX_ENABLED(comp1)) {
+               dev_info(dev->dev, " designware: play supported\n");
+               idx = COMP1_TX_WORDSIZE_0(comp1);
+               if (WARN_ON(idx >= ARRAY_SIZE(formats)))
+                       return -EINVAL;
+               if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
+                       idx = 1;
+               dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
+               dw_i2s_dai->playback.channels_max =
+                               (COMP1_TX_CHANNELS(comp1) + 1);
+               dw_i2s_dai->playback.formats = formats[idx];
+               for (; idx > 0; idx--)
+                       dw_i2s_dai->playback.formats |= formats[idx - 1];
+               dw_i2s_dai->playback.rates = rates;
+       }
+
+       if (COMP1_RX_ENABLED(comp1)) {
+               dev_info(dev->dev, "designware: record supported\n");
+               idx = COMP2_RX_WORDSIZE_0(comp2);
+               if (WARN_ON(idx >= ARRAY_SIZE(formats)))
+                       return -EINVAL;
+               if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
+                       idx = 1;
+               dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
+               dw_i2s_dai->capture.channels_max =
+                               (COMP1_RX_CHANNELS(comp1) + 1);
+               dw_i2s_dai->capture.formats = formats[idx];
+               for (; idx > 0; idx--)
+                       dw_i2s_dai->capture.formats |= formats[idx - 1];
+               dw_i2s_dai->capture.rates = rates;
+       }
+
+       if (COMP1_MODE_EN(comp1) || dev->is_master) {
+               dev_err(dev->dev, "designware: i2s master mode supported\n");
+               dev->capability |= DW_I2S_MASTER;
+       } else {
+               dev_err(dev->dev, "designware: i2s slave mode supported\n");
+               dev->capability |= DW_I2S_SLAVE;
+       }
+
+       dev->fifo_th = fifo_depth / 2;
+       dev_dbg(dev->dev, "fifo_th :%d\n", dev->fifo_th);
+       return 0;
+}
+
+static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev,
+                                  struct snd_soc_dai_driver *dw_i2s_dai,
+                                  struct resource *res,
+                                  const struct i2s_platform_data *pdata)
+{
+       u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
+       u32 idx = COMP1_APB_DATA_WIDTH(comp1);
+       int ret;
+
+       if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
+               return -EINVAL;
+
+       ret = dw_configure_dai(dev, dw_i2s_dai, pdata->snd_rates);
+       if (ret < 0)
+               return ret;
+
+       if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
+               idx = 1;
+       /* Set DMA slaves info */
+       dev->play_dma_data.pd.data = pdata->play_dma_data;
+       dev->capture_dma_data.pd.data = pdata->capture_dma_data;
+       dev->play_dma_data.pd.addr = res->start + I2S_TXDMA;
+       dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA;
+       dev->play_dma_data.pd.max_burst = 16;
+       dev->capture_dma_data.pd.max_burst = 16;
+       dev->play_dma_data.pd.addr_width = bus_widths[idx];
+       dev->capture_dma_data.pd.addr_width = bus_widths[idx];
+       dev->play_dma_data.pd.filter = pdata->filter;
+       dev->capture_dma_data.pd.filter = pdata->filter;
+
+       return 0;
+}
+
+static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
+                                  struct snd_soc_dai_driver *dw_i2s_dai,
+                                  struct resource *res)
+{
+       u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
+       u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
+       u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
+       u32 idx = COMP1_APB_DATA_WIDTH(comp1);
+       u32 idx2;
+       int ret;
+
+       if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
+               return -EINVAL;
+
+       ret = dw_configure_dai(dev, dw_i2s_dai, dev->susport_rate);
+       if (ret < 0)
+               return ret;
+
+       if (COMP1_TX_ENABLED(comp1)) {
+               idx2 = COMP1_TX_WORDSIZE_0(comp1);
+
+               dev->capability |= DWC_I2S_PLAY;
+               dev->play_dma_data.dt.addr = res->start + I2S_TXDMA;
+               dev->play_dma_data.dt.addr_width = bus_widths[idx];
+               dev->play_dma_data.dt.fifo_size = fifo_depth *
+                       (fifo_width[idx2]) >> 8;
+               dev->play_dma_data.dt.maxburst = 16;
+       }
+       if (COMP1_RX_ENABLED(comp1)) {
+               idx2 = COMP2_RX_WORDSIZE_0(comp2);
+
+               /* force change to 1 */
+               if (dev->is_master) {
+                       idx = 1;
+                       idx2 = 1;
+               }
+               dev->capability |= DWC_I2S_RECORD;
+               dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA;
+               dev->capture_dma_data.dt.addr_width = bus_widths[idx];
+               dev->capture_dma_data.dt.fifo_size = fifo_depth *
+                       (fifo_width[idx2] >> 8);
+               dev->capture_dma_data.dt.maxburst = 16;
+       }
+
+       return 0;
+
+}
+
+static int dw_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+       snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data);
+       return 0;
+}
+
+static int dw_i2s_probe(struct platform_device *pdev)
+{
+       const struct i2s_platform_data *pdata = pdev->dev.platform_data;
+       struct device_node *np = pdev->dev.of_node;
+       struct of_phandle_args args;
+       struct dw_i2s_dev *dev;
+       struct resource *res;
+       int ret, irq;
+       struct snd_soc_dai_driver *dw_i2s_dai;
+       const char *clk_id;
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
+       if (!dw_i2s_dai)
+               return -ENOMEM;
+
+       dw_i2s_dai->ops = &dw_i2s_dai_ops;
+       dw_i2s_dai->probe = dw_i2s_dai_probe;
+
+       dev->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+       if (IS_ERR(dev->i2s_base))
+               return PTR_ERR(dev->i2s_base);
+
+       dev->dev = &pdev->dev;
+
+       irq = platform_get_irq_optional(pdev, 0);
+       if (irq >= 0) {
+               ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0,
+                               pdev->name, dev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to request irq\n");
+                       return ret;
+               }
+       }
+
+       if (of_device_is_compatible(np, "starfive,jh7110-i2srx-master")) {
+               ret = of_parse_phandle_with_fixed_args(dev->dev->of_node,
+                                               "starfive,sys-syscon", 2, 0, &args);
+               if (ret) {
+                       dev_err(dev->dev, "Failed to parse starfive,sys-syscon\n");
+                       return -EINVAL;
+               }
+
+               dev->syscon_base = syscon_node_to_regmap(args.np);
+               of_node_put(args.np);
+               if (IS_ERR(dev->syscon_base))
+                       return PTR_ERR(dev->syscon_base);
+
+               dev->syscon_offset_18 = args.args[0];
+               dev->syscon_offset_34 = args.args[1];
+
+               /* config i2s data source: PDM  */
+               regmap_update_bits(dev->syscon_base, dev->syscon_offset_34,
+                                       AUDIO_SDIN_MUX_MASK, I2SRX_DATA_SRC_PDM);
+
+               ret = dw_i2srx_mst_clk_init(pdev, dev);
+               if (ret < 0)
+                       goto err_init;
+               dev->is_master = true;
+               dev->susport_rate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000;
+       } else if (of_device_is_compatible(np, "starfive,jh7110-i2srx")) {
+               ret = of_parse_phandle_with_fixed_args(dev->dev->of_node,
+                                                       "starfive,sys-syscon", 2, 0, &args);
+               if (ret) {
+                       dev_err(dev->dev, "Failed to parse starfive,sys-syscon\n");
+                       return -EINVAL;
+               }
+               dev->syscon_base = syscon_node_to_regmap(args.np);
+               of_node_put(args.np);
+               if (IS_ERR(dev->syscon_base))
+                       return PTR_ERR(dev->syscon_base);
+
+               dev->syscon_offset_18 = args.args[0];
+               dev->syscon_offset_34 = args.args[1];
+               ret = dw_i2srx_clk_init(pdev, dev);
+               if (ret < 0)
+                       goto err_init;
+               dev->is_master = false;
+               dev->susport_rate = SNDRV_PCM_RATE_8000_192000;
+       } else if (of_device_is_compatible(np, "starfive,jh7110-i2stx-4ch0")) {
+               /* hdmi audio  */
+               ret = dw_i2stx_4ch0_clk_init(pdev, dev);
+               if (ret < 0)
+                       goto err_init;
+               dev->is_master = true;
+               dev->susport_rate = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000;
+       } else if (of_device_is_compatible(np, "starfive,jh7110-i2stx-4ch1")) {
+               ret = dw_i2stx_4ch1_clk_init(pdev, dev);
+               if (ret < 0)
+                       goto err_init;
+               dev->is_master = false;
+               dev->susport_rate = SNDRV_PCM_RATE_8000_192000;
+       }
+
+       dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
+       dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
+
+       if (pdata) {
+               dev->capability = pdata->cap;
+               clk_id = NULL;
+               dev->quirks = pdata->quirks;
+               if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) {
+                       dev->i2s_reg_comp1 = pdata->i2s_reg_comp1;
+                       dev->i2s_reg_comp2 = pdata->i2s_reg_comp2;
+               }
+               ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
+       } else {
+               clk_id = "i2stx_bclk";
+               ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+       }
+       if (ret < 0)
+               goto err_clk_disable;
+
+       if (dev->capability & DW_I2S_MASTER) {
+               if (pdata) {
+                       dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
+                       if (!dev->i2s_clk_cfg) {
+                               dev_err(&pdev->dev, "no clock configure method\n");
+                               ret = -ENODEV;
+                               goto err_clk_disable;
+                       }
+               }
+       }
+
+       dev_set_drvdata(&pdev->dev, dev);
+       ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component,
+                                        dw_i2s_dai, 1);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "not able to register dai\n");
+               goto err_clk_disable;
+       }
+
+       if (!pdata) {
+               if (irq >= 0) {
+                       ret = dw_pcm_register(pdev);
+                       dev->use_pio = true;
+               } else {
+                       ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+                                       0);
+                       dev->use_pio = false;
+               }
+
+               if (ret) {
+                       dev_err(&pdev->dev, "could not register pcm: %d\n",
+                                       ret);
+                       goto err_clk_disable;
+               }
+       }
+
+       pm_runtime_enable(&pdev->dev);
+
+#ifdef CONFIG_PM
+       clk_disable_unprepare(dev->clk_i2s_bclk_mst);
+       clk_disable_unprepare(dev->clk_i2s_apb);
+#endif
+
+       return 0;
+
+err_clk_disable:
+       clk_disable_unprepare(dev->clk_i2s_bclk_mst);
+       clk_disable_unprepare(dev->clk_i2s_apb);
+err_init:
+       return ret;
+}
+
+static int dw_i2s_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_PM
+       struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
+
+       clk_disable_unprepare(dev->clk_i2s_bclk_mst);
+       clk_disable_unprepare(dev->clk_i2s_apb);
+#endif
+
+       pm_runtime_disable(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id dw_i2s_of_match[] = {
+       { .compatible = "starfive,jh7110-i2srx-master", },
+       { .compatible = "starfive,jh7110-i2srx", },
+       { .compatible = "starfive,jh7110-i2stx-4ch0", },
+       { .compatible = "starfive,jh7110-i2stx-4ch1", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, dw_i2s_of_match);
+#endif
+
+static const struct dev_pm_ops dwc_pm_ops = {
+       SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver dw_i2s_driver = {
+       .probe          = dw_i2s_probe,
+       .remove         = dw_i2s_remove,
+       .driver         = {
+               .name   = "starfive-i2s",
+               .of_match_table = of_match_ptr(dw_i2s_of_match),
+               .pm = &dwc_pm_ops,
+       },
+};
+
+module_platform_driver(dw_i2s_driver);
+
+MODULE_AUTHOR("Rajeev Kumar <rajeevkumar.linux@gmail.com>");
+MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
+MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
+MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:designware_i2s");
diff --git a/sound/soc/starfive/starfive_i2s.h b/sound/soc/starfive/starfive_i2s.h
new file mode 100644 (file)
index 0000000..8effed7
--- /dev/null
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (ST) 2012 Rajeev Kumar (rajeevkumar.linux@gmail.com)
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __STARFIVE_I2S_H
+#define __STARFIVE_I2S_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/designware_i2s.h>
+
+/* common register for all channel */
+#define IER            0x000
+#define IRER           0x004
+#define ITER           0x008
+#define CER            0x00C
+#define CCR            0x010
+#define RXFFR          0x014
+#define TXFFR          0x018
+
+/* Interrupt status register fields */
+#define ISR_TXFO       BIT(5)
+#define ISR_TXFE       BIT(4)
+#define ISR_RXFO       BIT(1)
+#define ISR_RXDA       BIT(0)
+
+/* I2STxRxRegisters for all channels */
+#define LRBR_LTHR(x)   (0x40 * x + 0x020)
+#define RRBR_RTHR(x)   (0x40 * x + 0x024)
+#define RER(x)         (0x40 * x + 0x028)
+#define TER(x)         (0x40 * x + 0x02C)
+#define RCR(x)         (0x40 * x + 0x030)
+#define TCR(x)         (0x40 * x + 0x034)
+#define ISR(x)         (0x40 * x + 0x038)
+#define IMR(x)         (0x40 * x + 0x03C)
+#define ROR(x)         (0x40 * x + 0x040)
+#define TOR(x)         (0x40 * x + 0x044)
+#define RFCR(x)                (0x40 * x + 0x048)
+#define TFCR(x)                (0x40 * x + 0x04C)
+#define RFF(x)         (0x40 * x + 0x050)
+#define TFF(x)         (0x40 * x + 0x054)
+
+/* I2SCOMPRegisters */
+#define I2S_COMP_PARAM_2       0x01F0
+#define I2S_COMP_PARAM_1       0x01F4
+#define I2S_COMP_VERSION       0x01F8
+#define I2S_COMP_TYPE          0x01FC
+
+/*
+ * Component parameter register fields - define the I2S block's
+ * configuration.
+ */
+#define        COMP1_TX_WORDSIZE_3(r)  (((r) & GENMASK(27, 25)) >> 25)
+#define        COMP1_TX_WORDSIZE_2(r)  (((r) & GENMASK(24, 22)) >> 22)
+#define        COMP1_TX_WORDSIZE_1(r)  (((r) & GENMASK(21, 19)) >> 19)
+#define        COMP1_TX_WORDSIZE_0(r)  (((r) & GENMASK(18, 16)) >> 16)
+#define        COMP1_TX_CHANNELS(r)    (((r) & GENMASK(10, 9)) >> 9)
+#define        COMP1_RX_CHANNELS(r)    (((r) & GENMASK(8, 7)) >> 7)
+#define        COMP1_RX_ENABLED(r)     (((r) & BIT(6)) >> 6)
+#define        COMP1_TX_ENABLED(r)     (((r) & BIT(5)) >> 5)
+#define        COMP1_MODE_EN(r)        (((r) & BIT(4)) >> 4)
+#define        COMP1_FIFO_DEPTH_GLOBAL(r)      (((r) & GENMASK(3, 2)) >> 2)
+#define        COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)
+
+#define        COMP2_RX_WORDSIZE_3(r)  (((r) & GENMASK(12, 10)) >> 10)
+#define        COMP2_RX_WORDSIZE_2(r)  (((r) & GENMASK(9, 7)) >> 7)
+#define        COMP2_RX_WORDSIZE_1(r)  (((r) & GENMASK(5, 3)) >> 3)
+#define        COMP2_RX_WORDSIZE_0(r)  (((r) & GENMASK(2, 0)) >> 0)
+
+/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
+#define        COMP_MAX_WORDSIZE       (1 << 3)
+#define        COMP_MAX_DATA_WIDTH     (1 << 2)
+
+#define MAX_CHANNEL_NUM                8
+#define MIN_CHANNEL_NUM                2
+
+/* SYSCON Registers */
+#define I2SRX_3CH_ADC_MASK     0x2
+#define I2SRX_3CH_ADC_EN       BIT(1)
+#define AUDIO_SDIN_MUX_MASK    0x3FC00
+#define I2SRX_DATA_SRC_PDM     (0x91 << 10)
+
+union dw_i2s_snd_dma_data {
+       struct i2s_dma_data pd;
+       struct snd_dmaengine_dai_dma_data dt;
+};
+
+struct dw_i2s_dev {
+       void __iomem *i2s_base;
+       struct regmap *syscon_base;
+       int active;
+       unsigned int capability;
+       unsigned int quirks;
+       unsigned int i2s_reg_comp1;
+       unsigned int i2s_reg_comp2;
+       struct device *dev;
+       u32 ccr;
+       u32 xfer_resolution;
+       u32 fifo_th;
+       u32 syscon_offset_18;
+       u32 syscon_offset_34;
+
+       struct clk *clk_apb0;
+       struct clk *clk_i2s_apb;
+       struct clk *clk_i2s_bclk_mst;
+       struct clk *clk_i2s_lrck_mst;
+       struct clk *clk_i2s_bclk;
+       struct clk *clk_i2s_lrck;
+       struct clk *clk_mclk;
+       struct clk *clk_mclk_ext;
+       struct clk *clk_mclk_inner;
+       struct clk *clk_bclk_ext;
+       struct clk *clk_lrck_ext;
+       struct reset_control *rst_i2s_apb;
+       struct reset_control *rst_i2s_bclk;
+
+       /* data related to DMA transfers b/w i2s and DMAC */
+       union dw_i2s_snd_dma_data play_dma_data;
+       union dw_i2s_snd_dma_data capture_dma_data;
+       struct i2s_clk_config_data config;
+       int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
+
+       /* data related to PIO transfers */
+       bool use_pio;
+       struct snd_pcm_substream __rcu *tx_substream;
+       struct snd_pcm_substream __rcu *rx_substream;
+       unsigned int (*tx_fn)(struct dw_i2s_dev *dev,
+                       struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+                       bool *period_elapsed);
+       unsigned int (*rx_fn)(struct dw_i2s_dev *dev,
+                       struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
+                       bool *period_elapsed);
+       unsigned int tx_ptr;
+       unsigned int rx_ptr;
+       bool is_master;
+       unsigned int susport_rate;
+};
+
+#if IS_ENABLED(CONFIG_SND_DESIGNWARE_PCM)
+void dw_pcm_push_tx(struct dw_i2s_dev *dev);
+void dw_pcm_pop_rx(struct dw_i2s_dev *dev);
+int dw_pcm_register(struct platform_device *pdev);
+#else
+static inline void dw_pcm_push_tx(struct dw_i2s_dev *dev) { }
+static inline void dw_pcm_pop_rx(struct dw_i2s_dev *dev) { }
+static inline int dw_pcm_register(struct platform_device *pdev)
+{
+       return -EINVAL;
+}
+#endif
+
+#endif
diff --git a/sound/soc/starfive/starfive_pdm.c b/sound/soc/starfive/starfive_pdm.c
new file mode 100644 (file)
index 0000000..4f7c80d
--- /dev/null
@@ -0,0 +1,378 @@
+/**
+  ******************************************************************************
+  * @file  sf_pdm.c
+  * @author  StarFive Technology
+  * @version  V1.0
+  * @date  05/27/2021
+  * @brief
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+  */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "starfive_pdm.h"
+
+#define AUDIOC_CLK     (12288000)
+#define PDM_MUL        (128)
+
+struct sf_pdm {
+       struct regmap *pdm_map;
+       struct regmap *clk_map;
+       struct clk *clk;
+};
+
+static const DECLARE_TLV_DB_SCALE(volume_tlv, -9450, 150, 0);
+
+static const struct snd_kcontrol_new sf_pdm_snd_controls[] = {
+       SOC_SINGLE("DC compensation Control", PDM_DMIC_CTRL0, 30, 1, 0),
+       SOC_SINGLE("High Pass Filter Control", PDM_DMIC_CTRL0, 28, 1, 0),
+       SOC_SINGLE("Left Channel Volume Control", PDM_DMIC_CTRL0, 23, 1, 0),
+       SOC_SINGLE("Right Channel Volume Control", PDM_DMIC_CTRL0, 22, 1, 0),
+       SOC_SINGLE_TLV("Volume", PDM_DMIC_CTRL0, 16, 0x3F, 1, volume_tlv),
+       SOC_SINGLE("Data MSB Shift", PDM_DMIC_CTRL0, 1, 7, 0),
+       SOC_SINGLE("SCALE", PDM_DC_SCALE0, 0, 0x3F, 0),
+       SOC_SINGLE("DC offset", PDM_DC_SCALE0, 8, 0xFFFFF, 0),
+};
+
+static int sf_pdm_set_mclk(struct regmap *map,
+       unsigned int clk, unsigned int weight)
+{
+       int mclk_div,bclk_div,lrclk_div;
+       u32     pdm_div;
+
+       /*
+       audio source clk:12288000, mclk_div:4, mclk:3M
+       support 8K/16K/32K/48K sample reate
+       suapport 16/24/32 bit weight
+       bit weight 32
+       mclk bclk  lrclk
+       3M   1.5M  48K
+       3M   1M    32K
+       3M   0.5M  16K
+       3M   0.25M  8K
+
+       bit weight 24,set lrclk_div as 32
+       mclk bclk  lrclk
+       3M   1.5M  48K
+       3M   1M    32K
+       3M   0.5M  16K
+       3M   0.25M  8K
+
+       bit weight 16
+       mclk bclk   lrclk
+       3M   0.75M  48K
+       3M   0.5M   32K
+       3M   0.25M  16K
+       3M   0.125M 8K
+       */
+
+       switch (clk) {
+       case 8000:
+       case 16000:
+       case 32000:
+       case 48000:
+               break;
+       default:
+               printk(KERN_ERR "sample rate:%d\n", clk);
+               return -EINVAL;
+       }
+
+       switch (weight) {
+       case 16:
+       case 24:
+       case 32:
+               break;
+       default:
+               printk(KERN_ERR "bit weight:%d\n", weight);
+               return -EINVAL;
+       }
+
+       if (24 == weight) {
+               weight = 32;
+       }
+
+       mclk_div = 4;
+       bclk_div = AUDIOC_CLK/mclk_div/(clk*weight);
+       lrclk_div = weight;
+
+       /* PDM MCLK = 128*LRCLK */
+       pdm_div = AUDIOC_CLK/(PDM_MUL*clk);
+
+       regmap_update_bits(map, AUDIO_CLK_ADC_MCLK, 0x0F, mclk_div);
+       regmap_update_bits(map, AUDIO_CLK_I2SADC_BCLK, 0x1F, bclk_div);
+       regmap_update_bits(map, AUDIO_CLK_ADC_LRCLK, 0x3F, lrclk_div);
+       regmap_update_bits(map, AUDIO_CLK_PDM_CLK, 0x0F, pdm_div);
+
+       return 0;
+}
+
+static void sf_pdm_enable(struct regmap *map)
+{
+       /* Enable PDM */
+       regmap_update_bits(map, PDM_DMIC_CTRL0, 0x01<<PDM_DMIC_RVOL_OFFSET, 0);
+       regmap_update_bits(map, PDM_DMIC_CTRL0, 0x01<<PDM_DMIC_LVOL_OFFSET, 0);
+}
+
+static void sf_pdm_disable(struct regmap *map)
+{
+       regmap_update_bits(map, PDM_DMIC_CTRL0,
+               0x01<<PDM_DMIC_RVOL_OFFSET, 0x01<<PDM_DMIC_RVOL_OFFSET);
+       regmap_update_bits(map, PDM_DMIC_CTRL0,
+               0x01<<PDM_DMIC_LVOL_OFFSET, 0x01<<PDM_DMIC_LVOL_OFFSET);
+}
+
+static int sf_pdm_trigger(struct snd_pcm_substream *substream, int cmd,
+                          struct snd_soc_dai *dai)
+{
+       struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               sf_pdm_enable(priv->pdm_map);
+               return 0;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               sf_pdm_disable(priv->pdm_map);
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int sf_pdm_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai);
+       unsigned int rate = params_rate(params);
+       unsigned int width;
+       int ret;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               return 0;
+
+       width = params_width(params);
+       switch (width) {
+       case 16:
+       case 24:
+       case 32:
+               break;
+       default:
+               dev_err(dai->dev, "unsupported sample width\n");
+               return -EINVAL;
+       }
+
+       ret = sf_pdm_set_mclk(priv->clk_map, rate, width);
+       if (ret < 0) {
+               dev_err(dai->dev, "unsupported sample rate\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops sf_pdm_dai_ops = {
+       .trigger        = sf_pdm_trigger,
+       .hw_params      = sf_pdm_hw_params,
+};
+
+static int sf_pdm_dai_probe(struct snd_soc_dai *dai)
+{
+       struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai);
+
+       /* Reset */
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x01<<PDM_DMIC_SW_RSTN_OFFSET, 0x00);
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x01<<PDM_DMIC_SW_RSTN_OFFSET, 0x01<<PDM_DMIC_SW_RSTN_OFFSET);
+
+       /* Make sure the device is initially disabled */
+       sf_pdm_disable(priv->pdm_map);
+
+       /* MUTE */
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x3F<<PDM_DMIC_VOL_OFFSET, 0x3F<<PDM_DMIC_VOL_OFFSET);
+
+       /* UNMUTE */
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x3F<<PDM_DMIC_VOL_OFFSET, 0);
+
+       /* enable high pass filter */
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x01<<PDM_DMIC_ENHPF_OFFSET, 0x01<<PDM_DMIC_ENHPF_OFFSET);
+
+       /* i2s slaver mode */
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x01<<PDM_DMIC_I2SMODE_OFFSET, 0x01<<PDM_DMIC_I2SMODE_OFFSET);
+
+       /* disable fast mode */
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x01<<PDM_DMIC_FASTMODE_OFFSET, 0);
+
+       /* enable dc bypass mode */
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x01<<PDM_DMIC_DCBPS_OFFSET, 0);
+
+       /* dmic msb shift 0 */
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x07<<PDM_DMIC_MSB_SHIFT_OFFSET, 0);
+
+       /* scale:0 */
+       regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, 0x3F, 0x08);
+
+       /* DC offset:0 */
+       regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
+               0xFFFFF<<PDM_DMIC_DCOFF1_OFFSET, 0xC0005<<PDM_DMIC_DCOFF1_OFFSET);
+
+       return 0;
+}
+
+static int sf_pdm_dai_remove(struct snd_soc_dai *dai)
+{
+       struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai);
+
+       /* MUTE */
+       regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
+               0x3F<<PDM_DMIC_VOL_OFFSET, 0x3F<<PDM_DMIC_VOL_OFFSET);
+
+       return 0;
+}
+
+#define SF_PCM_RATE (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
+                                       SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_48000)
+
+static struct snd_soc_dai_driver sf_pdm_dai_drv = {
+       .name = "PDM",
+       .id = 0,
+       .capture = {
+               .stream_name    = "Capture",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          =       SF_PCM_RATE,
+               .formats        =       SNDRV_PCM_FMTBIT_S16_LE|\
+                                               SNDRV_PCM_FMTBIT_S24_LE|\
+                                               SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .ops            = &sf_pdm_dai_ops,
+       .probe          = sf_pdm_dai_probe,
+       .remove         = sf_pdm_dai_remove,
+       .symmetric_rate = 1,
+};
+
+static int pdm_probe(struct snd_soc_component *component)
+{
+       struct sf_pdm *priv = snd_soc_component_get_drvdata(component);
+
+       snd_soc_component_init_regmap(component, priv->pdm_map);
+       snd_soc_add_component_controls(component, sf_pdm_snd_controls,
+                                    ARRAY_SIZE(sf_pdm_snd_controls));
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver sf_pdm_component_drv = {
+       .name = "sf-pdm",
+       .probe = pdm_probe,
+};
+
+static const struct regmap_config sf_pdm_regmap_cfg = {
+       .reg_bits       = 32,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+       .max_register   = 0x20,
+};
+
+static const struct regmap_config sf_audio_clk_regmap_cfg = {
+       .reg_bits       = 32,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+       .max_register   = 0x100,
+};
+
+static int sf_pdm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct sf_pdm *priv;
+       struct resource *res;
+       void __iomem *regs;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, priv);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pdm");
+       regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       priv->pdm_map = devm_regmap_init_mmio(dev, regs, &sf_pdm_regmap_cfg);
+       if (IS_ERR(priv->pdm_map)) {
+               dev_err(dev, "failed to init regmap: %ld\n",
+                       PTR_ERR(priv->pdm_map));
+               return PTR_ERR(priv->pdm_map);
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-clk");
+       regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       priv->clk_map = devm_regmap_init_mmio(dev, regs, &sf_audio_clk_regmap_cfg);
+       if (IS_ERR(priv->clk_map)) {
+               dev_err(dev, "failed to init regmap: %ld\n",
+                       PTR_ERR(priv->clk_map));
+               return PTR_ERR(priv->clk_map);
+       }
+
+       return devm_snd_soc_register_component(dev, &sf_pdm_component_drv,
+                                              &sf_pdm_dai_drv, 1);
+}
+
+static int sf_pdm_dev_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+static const struct of_device_id sf_pdm_of_match[] = {
+       {.compatible = "starfive,sf-pdm",},
+       {}
+};
+MODULE_DEVICE_TABLE(of, sf_pdm_of_match);
+
+static struct platform_driver sf_pdm_driver = {
+
+       .driver = {
+               .name = "sf-pdm",
+               .of_match_table = sf_pdm_of_match,
+       },
+       .probe = sf_pdm_probe,
+       .remove = sf_pdm_dev_remove,
+};
+module_platform_driver(sf_pdm_driver);
+
+MODULE_AUTHOR("michael.yan <michael.yan@starfivetech.com>");
+MODULE_DESCRIPTION("starfive PDM Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/starfive/starfive_pdm.h b/sound/soc/starfive/starfive_pdm.h
new file mode 100644 (file)
index 0000000..8a776b5
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+  ******************************************************************************
+  * @file  sf_pdm.h
+  * @author  StarFive Technology
+  * @version  V1.0
+  * @date  05/27/2021
+  * @brief
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+  */
+
+#ifndef __SND_SOC_STARFIVE_SPDIF_H
+#define __SND_SOC_STARFIVE_PDM_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <linux/dmaengine.h>
+#include <linux/types.h>
+
+#define PDM_DMIC_CTRL0         (0x00)
+#define PDM_DC_SCALE0          (0x04)
+#define PDM_DMIC_CTRL1         (0x10)
+#define PDM_DC_SCALE1          (0x14)
+
+/* PDM CTRL OFFSET */
+#define PDM_DMIC_MSB_SHIFT_OFFSET      (1)
+#define PDM_DMIC_VOL_OFFSET            (16)
+#define PDM_DMIC_RVOL_OFFSET           (22)
+#define PDM_DMIC_LVOL_OFFSET           (23)
+#define PDM_DMIC_I2SMODE_OFFSET                (24)
+#define PDM_DMIC_ENHPF_OFFSET          (28)
+#define PDM_DMIC_FASTMODE_OFFSET       (29)
+#define PDM_DMIC_DCBPS_OFFSET          (30)
+#define PDM_DMIC_SW_RSTN_OFFSET                (31)
+
+/* PDM SCALE OFFSET */
+#define PDM_DMIC_DCOFF3_OFFSET (24)
+#define PDM_DMIC_DCOFF2_OFFSET (16)
+#define PDM_DMIC_DCOFF1_OFFSET (8)
+#define PDM_DMIC_SCALE_OFFSET  (0)
+
+#define AUDIO_CLK_ADC_MCLK             0x0
+#define AUDIO_CLK_I2SADC_BCLK  0xC
+#define AUDIO_CLK_ADC_LRCLK    0x14
+#define AUDIO_CLK_PDM_CLK              0x1C
+
+#endif /* __SND_SOC_STARFIVE_PDM_H */
diff --git a/sound/soc/starfive/starfive_pwmdac.c b/sound/soc/starfive/starfive_pwmdac.c
new file mode 100644 (file)
index 0000000..fba58e2
--- /dev/null
@@ -0,0 +1,989 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PWMDAC driver for the StarFive JH7110 SoC
+ *
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/kthread.h>
+#include <linux/reset.h>
+#include <linux/dma/starfive-dma.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include "pwmdac.h"
+
+struct ct_pwmdac {
+       char *name;
+       unsigned int vals;
+};
+
+static const struct ct_pwmdac pwmdac_ct_shift_bit[] = {
+       { .name = "8bit", .vals = PWMDAC_SHIFT_8 },
+       { .name = "10bit", .vals = PWMDAC_SHIFT_10 }
+};
+
+static const struct ct_pwmdac pwmdac_ct_duty_cycle[] = {
+       { .name = "left", .vals = PWMDAC_CYCLE_LEFT },
+       { .name = "right", .vals = PWMDAC_CYCLE_RIGHT },
+       { .name = "center", .vals = PWMDAC_CYCLE_CENTER }
+};
+
+static const struct ct_pwmdac pwmdac_ct_data_mode[] = {
+       { .name = "unsinged", .vals = UNSINGED_DATA },
+       { .name = "inverter", .vals = INVERTER_DATA_MSB }
+};
+
+static const struct ct_pwmdac pwmdac_ct_lr_change[] = {
+       { .name = "no_change", .vals = NO_CHANGE },
+       { .name = "change", .vals = CHANGE }
+};
+
+static const struct ct_pwmdac pwmdac_ct_shift[] = {
+       { .name = "left 0 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_0 },
+       { .name = "left 1 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_1 },
+       { .name = "left 2 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_2 },
+       { .name = "left 3 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_3 },
+       { .name = "left 4 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_4 },
+       { .name = "left 5 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_5 },
+       { .name = "left 6 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_6 }
+};
+
+static int pwmdac_shift_bit_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_shift_bit);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = items;
+       if (uinfo->value.enumerated.item >= items)
+               uinfo->value.enumerated.item = items - 1;
+       strcpy(uinfo->value.enumerated.name,
+               pwmdac_ct_shift_bit[uinfo->value.enumerated.item].name);
+
+       return 0;
+}
+static int pwmdac_shift_bit_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+       unsigned int item;
+
+       if (dev->shift_bit == pwmdac_ct_shift_bit[0].vals)
+               item = 0;
+       else
+               item = 1;
+
+       ucontrol->value.enumerated.item[0] = item;
+
+       return 0;
+}
+
+static int pwmdac_shift_bit_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+       int sel = ucontrol->value.enumerated.item[0];
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_shift_bit);
+
+       if (sel > items)
+               return 0;
+
+       switch (sel) {
+       case 1:
+               dev->shift_bit = pwmdac_ct_shift_bit[1].vals;
+               break;
+       default:
+               dev->shift_bit = pwmdac_ct_shift_bit[0].vals;
+               break;
+       }
+
+       return 0;
+}
+
+static int pwmdac_duty_cycle_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_duty_cycle);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = items;
+       if (uinfo->value.enumerated.item >= items)
+               uinfo->value.enumerated.item = items - 1;
+       strcpy(uinfo->value.enumerated.name,
+               pwmdac_ct_duty_cycle[uinfo->value.enumerated.item].name);
+
+       return 0;
+}
+
+static int pwmdac_duty_cycle_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+
+       ucontrol->value.enumerated.item[0] = dev->duty_cycle;
+       return 0;
+}
+
+static int pwmdac_duty_cycle_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+       int sel = ucontrol->value.enumerated.item[0];
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_duty_cycle);
+
+       if (sel > items)
+               return 0;
+
+       dev->duty_cycle = pwmdac_ct_duty_cycle[sel].vals;
+       return 0;
+}
+
+static int pwmdac_data_mode_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_data_mode);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = items;
+       if (uinfo->value.enumerated.item >= items)
+               uinfo->value.enumerated.item = items - 1;
+       strcpy(uinfo->value.enumerated.name,
+               pwmdac_ct_data_mode[uinfo->value.enumerated.item].name);
+
+       return 0;
+}
+
+static int pwmdac_data_mode_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+
+       ucontrol->value.enumerated.item[0] = dev->data_mode;
+       return 0;
+}
+
+static int pwmdac_data_mode_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+       int sel = ucontrol->value.enumerated.item[0];
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_data_mode);
+
+       if (sel > items)
+               return 0;
+
+       dev->data_mode = pwmdac_ct_data_mode[sel].vals;
+       return 0;
+}
+
+static int pwmdac_shift_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_shift);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = items;
+       if (uinfo->value.enumerated.item >= items)
+               uinfo->value.enumerated.item = items - 1;
+       strcpy(uinfo->value.enumerated.name,
+               pwmdac_ct_shift[uinfo->value.enumerated.item].name);
+
+       return 0;
+}
+
+static int pwmdac_shift_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+       unsigned int item = dev->shift;
+
+       ucontrol->value.enumerated.item[0] =  pwmdac_ct_shift[item].vals;
+       return 0;
+}
+
+static int pwmdac_shift_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+       int sel = ucontrol->value.enumerated.item[0];
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_shift);
+
+       if (sel > items)
+               return 0;
+
+       dev->shift = pwmdac_ct_shift[sel].vals;
+       return 0;
+}
+
+static int pwmdac_lr_change_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_lr_change);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = items;
+       if (uinfo->value.enumerated.item >= items)
+               uinfo->value.enumerated.item = items - 1;
+       strcpy(uinfo->value.enumerated.name,
+               pwmdac_ct_lr_change[uinfo->value.enumerated.item].name);
+
+       return 0;
+}
+
+static int pwmdac_lr_change_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+
+       ucontrol->value.enumerated.item[0] = dev->lr_change;
+       return 0;
+}
+
+static int pwmdac_lr_change_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+       int sel = ucontrol->value.enumerated.item[0];
+       unsigned int items = ARRAY_SIZE(pwmdac_ct_lr_change);
+
+       if (sel > items)
+               return 0;
+
+       dev->lr_change = pwmdac_ct_lr_change[sel].vals;
+       return 0;
+}
+
+static inline void pwmdc_write_reg(void __iomem *io_base, int reg, u32 val)
+{
+       writel(val, io_base + reg);
+}
+
+static inline u32 pwmdc_read_reg(void __iomem *io_base, int reg)
+{
+       return readl(io_base + reg);
+}
+
+/*
+ * 32bit-4byte
+ */
+static void pwmdac_set_ctrl_enable(struct sf_pwmdac_dev *dev)
+{
+       u32 date;
+
+       date = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL);
+       pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, date | BIT(0));
+}
+
+/*
+ * 32bit-4byte
+ */
+static void pwmdac_set_ctrl_disable(struct sf_pwmdac_dev *dev)
+{
+       u32 date;
+
+       date = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL);
+       pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, date & ~BIT(0));
+}
+
+/*
+ * 8:8-bit
+ * 10:10-bit
+ */
+static void pwmdac_set_ctrl_shift(struct sf_pwmdac_dev *dev, u8 data)
+{
+       u32 value = 0;
+
+       if (data == PWMDAC_SHIFT_8) {
+               value = (~((~value) | SFC_PWMDAC_SHIFT));
+               pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value);
+       } else if (data == PWMDAC_SHIFT_10) {
+               value |= SFC_PWMDAC_SHIFT;
+               pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value);
+       }
+}
+
+/*
+ * 00:left
+ * 01:right
+ * 10:center
+ */
+static void pwmdac_set_ctrl_dutyCycle(struct sf_pwmdac_dev *dev, u8 data)
+{
+       u32 value = 0;
+
+       value = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL);
+       if (data == PWMDAC_CYCLE_LEFT) {
+               value = (~((~value) | (0x03<<PWMDAC_DUTY_CYCLE_LOW)));
+               pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value);
+       } else if (data == PWMDAC_CYCLE_RIGHT) {
+               value = (~((~value) | (0x01<<PWMDAC_DUTY_CYCLE_HIGH))) |
+                       (0x01<<PWMDAC_DUTY_CYCLE_LOW);
+               pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value);
+       } else if (data == PWMDAC_CYCLE_CENTER) {
+               value = (~((~value) | (0x01<<PWMDAC_DUTY_CYCLE_LOW))) |
+                       (0x01<<PWMDAC_DUTY_CYCLE_HIGH);
+               pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value);
+       }
+}
+
+
+static void pwmdac_set_ctrl_N(struct sf_pwmdac_dev *dev, u16 data)
+{
+       u32 value = 0;
+
+       value = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL);
+       pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL,
+               (value & PWMDAC_CTRL_DATA_MASK) | ((data - 1) << PWMDAC_CTRL_DATA_SHIFT));
+}
+
+
+static void pwmdac_LR_data_change(struct sf_pwmdac_dev *dev, u8 data)
+{
+       u32 value = 0;
+
+       value = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL);
+       switch (data) {
+       case NO_CHANGE:
+               value &= (~SFC_PWMDAC_LEFT_RIGHT_DATA_CHANGE);
+               break;
+       case CHANGE:
+               value |= SFC_PWMDAC_LEFT_RIGHT_DATA_CHANGE;
+               break;
+       }
+
+       pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value);
+}
+
+
+static void pwmdac_data_mode(struct sf_pwmdac_dev *dev,  u8 data)
+{
+       u32 value = 0;
+
+       value = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL);
+       if (data == UNSINGED_DATA)
+               value &= (~SFC_PWMDAC_DATA_MODE);
+       else if (data == INVERTER_DATA_MSB)
+               value |= SFC_PWMDAC_DATA_MODE;
+
+       pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value);
+}
+
+
+static int pwmdac_data_shift(struct sf_pwmdac_dev *dev, u8 data)
+{
+       u32 value = 0;
+
+       if ((data < PWMDAC_DATA_LEFT_SHIFT_BIT_0) ||
+               (data > PWMDAC_DATA_LEFT_SHIFT_BIT_7))
+               return -1;
+
+       value = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL);
+       value &= (~(PWMDAC_DATA_LEFT_SHIFT_BIT_ALL << PWMDAC_DATA_LEFT_SHIFT));
+       value |= (data<<PWMDAC_DATA_LEFT_SHIFT);
+       pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value);
+       return 0;
+}
+
+static int get_pwmdac_fifo_state(struct sf_pwmdac_dev *dev)
+{
+       u32 value;
+
+       value = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_SATAE);
+       if ((value & 0x02) == 0)
+               return FIFO_UN_FULL;
+
+       return FIFO_FULL;
+}
+
+
+static void pwmdac_set(struct sf_pwmdac_dev *dev)
+{
+       /*8-bit + left + N=16*/
+       pwmdac_set_ctrl_shift(dev, dev->shift_bit);
+       pwmdac_set_ctrl_dutyCycle(dev, dev->duty_cycle);
+       pwmdac_set_ctrl_N(dev, dev->datan);
+       pwmdac_set_ctrl_enable(dev);
+
+       pwmdac_LR_data_change(dev, dev->lr_change);
+       pwmdac_data_mode(dev, dev->data_mode);
+       if (dev->shift)
+               pwmdac_data_shift(dev, dev->shift);
+}
+
+static void pwmdac_stop(struct sf_pwmdac_dev *dev)
+{
+       pwmdac_set_ctrl_disable(dev);
+}
+
+static int pwmdac_config(struct sf_pwmdac_dev *dev)
+{
+       switch (dev->mode) {
+       case shift_8Bit_unsigned:
+       case shift_8Bit_unsigned_dataShift:
+               /* 8 bit, unsigned */
+               dev->shift_bit  = PWMDAC_SHIFT_8;
+               dev->duty_cycle = PWMDAC_CYCLE_CENTER;
+               dev->datan      = PWMDAC_SAMPLE_CNT_8;
+               dev->data_mode  = UNSINGED_DATA;
+               break;
+
+       case shift_8Bit_inverter:
+       case shift_8Bit_inverter_dataShift:
+               /* 8 bit, invert */
+               dev->shift_bit  = PWMDAC_SHIFT_8;
+               dev->duty_cycle = PWMDAC_CYCLE_CENTER;
+               dev->datan      = PWMDAC_SAMPLE_CNT_8;
+               dev->data_mode  = INVERTER_DATA_MSB;
+               break;
+
+       case shift_10Bit_unsigned:
+       case shift_10Bit_unsigned_dataShift:
+               /* 10 bit, unsigend */
+               dev->shift_bit  = PWMDAC_SHIFT_10;
+               dev->duty_cycle = PWMDAC_CYCLE_CENTER;
+               dev->datan      = PWMDAC_SAMPLE_CNT_8;
+               dev->data_mode  = UNSINGED_DATA;
+               break;
+
+       case shift_10Bit_inverter:
+       case shift_10Bit_inverter_dataShift:
+               /* 10 bit, invert */
+               dev->shift_bit  = PWMDAC_SHIFT_10;
+               dev->duty_cycle = PWMDAC_CYCLE_CENTER;
+               dev->datan      = PWMDAC_SAMPLE_CNT_8;
+               dev->data_mode  = INVERTER_DATA_MSB;
+               break;
+
+       default:
+               return -1;
+       }
+
+       if ((dev->mode == shift_8Bit_unsigned_dataShift) ||
+               (dev->mode == shift_8Bit_inverter_dataShift) ||
+               (dev->mode == shift_10Bit_unsigned_dataShift) ||
+               (dev->mode == shift_10Bit_inverter_dataShift))
+               dev->shift = 4; /*0~7*/
+       else
+               dev->shift = 0;
+
+       dev->lr_change = NO_CHANGE;
+       return 0;
+}
+
+static int sf_pwmdac_prepare(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
+{
+       return 0;
+}
+
+int pwmdac_tx_thread(void *dev)
+{
+       struct sf_pwmdac_dev *pwmdac_dev =  (struct sf_pwmdac_dev *)dev;
+
+       if (!pwmdac_dev) {
+               dev_err(pwmdac_dev->dev, "%s L.%d  dev is null.\n", __FILE__, __LINE__);
+               return -1;
+       }
+
+       while (!pwmdac_dev->tx_thread_exit) {
+               if (get_pwmdac_fifo_state(pwmdac_dev) == 0)
+                       sf_pwmdac_pcm_push_tx(pwmdac_dev);
+               else
+                       udelay(100);
+       }
+
+       return 0;
+}
+
+static int sf_pwmdac_trigger(struct snd_pcm_substream *substream,
+               int cmd, struct snd_soc_dai *dai)
+{
+       struct sf_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai);
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               dev->active++;
+               pwmdac_set(dev);
+               if (dev->use_pio) {
+                       dev->tx_thread = kthread_create(pwmdac_tx_thread, (void *)dev, "pwmdac");
+                       if (IS_ERR(dev->tx_thread))
+                               return PTR_ERR(dev->tx_thread);
+
+                       wake_up_process(dev->tx_thread);
+                       dev->tx_thread_exit = 0;
+               }
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               dev->active--;
+               pwmdac_stop(dev);
+               if (dev->use_pio) {
+                       if (dev->tx_thread)
+                               dev->tx_thread_exit = 1;
+               }
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int sf_pwmdac_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       int ret = 0;
+       unsigned long mclk_dac_value;
+       struct sf_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+       dai_link->stop_dma_first = 1;
+
+       dev->play_dma_data.addr = dev->mapbase + PWMDAC_WDATA;
+
+       switch (params_rate(params)) {
+       case 8000:
+               dev->datan = PWMDAC_SAMPLE_CNT_3;
+               mclk_dac_value = 6144000;
+               break;
+       case 11025:
+               dev->datan = PWMDAC_SAMPLE_CNT_2;
+               mclk_dac_value = 5644800;
+               break;
+       case 16000:
+               dev->datan = PWMDAC_SAMPLE_CNT_3;
+               mclk_dac_value = 12288000;
+               break;
+       case 22050:
+               dev->datan = PWMDAC_SAMPLE_CNT_1;
+               mclk_dac_value = 5644800;
+               break;
+       case 32000:
+               dev->datan = PWMDAC_SAMPLE_CNT_1;
+               mclk_dac_value = 8192000;
+               break;
+       case 44100:
+               dev->datan = PWMDAC_SAMPLE_CNT_1;
+               mclk_dac_value = 11289600;
+               break;
+       case 48000:
+               dev->datan = PWMDAC_SAMPLE_CNT_1;
+               mclk_dac_value = 12288000;
+               break;
+       default:
+               dev_err(dai->dev, "%d rate not supported\n",
+                               params_rate(params));
+               return -EINVAL;
+       }
+
+       switch (params_channels(params)) {
+       case 2:
+               dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+               break;
+       case 1:
+               dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+               break;
+       default:
+               dev_err(dai->dev, "%d channels not supported\n",
+                               params_channels(params));
+               return -EINVAL;
+       }
+
+       /* The mclock for the clock driver always rounds down so add a little slack */
+       mclk_dac_value = mclk_dac_value + 64;
+       pwmdac_set(dev);
+
+       ret = clk_set_rate(dev->clk_pwmdac_core, mclk_dac_value);
+       if (ret) {
+               dev_err(dai->dev, "failed to set rate for clk_pwmdac_core %lu\n", mclk_dac_value);
+               goto err_clk_pwmdac;
+       }
+
+       dev->play_dma_data.fifo_size = 1;
+       dev->play_dma_data.maxburst = 16;
+
+       snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL);
+       snd_soc_dai_set_drvdata(dai, dev);
+
+       return 0;
+
+err_clk_pwmdac:
+       return ret;
+
+}
+
+static int sf_pwmdac_clks_get(struct platform_device *pdev,
+                               struct sf_pwmdac_dev *dev)
+{
+       dev->clk_apb0 = devm_clk_get(&pdev->dev, "apb0");
+       if (IS_ERR(dev->clk_apb0))
+               return PTR_ERR(dev->clk_apb0);
+
+       dev->clk_pwmdac_apb = devm_clk_get(&pdev->dev, "pwmdac-apb");
+       if (IS_ERR(dev->clk_pwmdac_apb))
+               return PTR_ERR(dev->clk_pwmdac_apb);
+
+       dev->clk_pwmdac_core = devm_clk_get(&pdev->dev, "pwmdac-core");
+       if (IS_ERR(dev->clk_pwmdac_core))
+               return PTR_ERR(dev->clk_pwmdac_core);
+
+       return 0;
+}
+
+static int sf_pwmdac_resets_get(struct platform_device *pdev,
+                               struct sf_pwmdac_dev *dev)
+{
+       dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, "rst-apb");
+       if (IS_ERR(dev->rst_apb)) {
+               dev_err(&pdev->dev, "%s: failed to get pwmdac apb reset control\n", __func__);
+               return PTR_ERR(dev->rst_apb);
+       }
+
+       return 0;
+}
+
+static int starfive_pwmdac_crg_enable(struct sf_pwmdac_dev *dev, bool enable)
+{
+       int ret = 0;
+
+       dev_dbg(dev->dev, "starfive_pwmdac clk&rst %sable.\n", enable ? "en":"dis");
+       if (enable) {
+               ret = clk_prepare_enable(dev->clk_apb0);
+               if (ret) {
+                       dev_err(dev->dev, "failed to prepare enable clk_apb0\n");
+                       goto err_clk_apb0;
+               }
+
+               ret = clk_prepare_enable(dev->clk_pwmdac_apb);
+               if (ret) {
+                       dev_err(dev->dev, "failed to prepare enable clk_pwmdac_apb\n");
+                       goto err_clk_apb;
+               }
+
+               ret = clk_prepare_enable(dev->clk_pwmdac_core);
+               if (ret) {
+                       dev_err(dev->dev, "failed to prepare enable clk_pwmdac_core\n");
+                       goto err_clk_core;
+               }
+
+               ret = reset_control_deassert(dev->rst_apb);
+               if (ret) {
+                       dev_err(dev->dev, "failed to deassert apb\n");
+                       goto err_rst_apb;
+               }
+       } else {
+               clk_disable_unprepare(dev->clk_pwmdac_core);
+               clk_disable_unprepare(dev->clk_pwmdac_apb);
+               clk_disable_unprepare(dev->clk_apb0);
+       }
+
+       return 0;
+
+err_rst_apb:
+       clk_disable_unprepare(dev->clk_pwmdac_core);
+err_clk_core:
+       clk_disable_unprepare(dev->clk_pwmdac_apb);
+err_clk_apb:
+       clk_disable_unprepare(dev->clk_apb0);
+err_clk_apb0:
+       return ret;
+}
+
+static int sf_pwmdac_clk_init(struct platform_device *pdev,
+                               struct sf_pwmdac_dev *dev)
+{
+       int ret = 0;
+
+       ret = starfive_pwmdac_crg_enable(dev, true);
+       if (ret)
+               goto err_clk_pwmdac;
+
+       ret = clk_set_rate(dev->clk_pwmdac_core, 4096000);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set rate for clk_pwmdac_core\n");
+               goto err_clk_pwmdac;
+       }
+
+       dev_info(&pdev->dev, "clk_apb0 = %lu, clk_pwmdac_apb = %lu, clk_pwmdac_core = %lu\n",
+               clk_get_rate(dev->clk_apb0), clk_get_rate(dev->clk_pwmdac_apb),
+               clk_get_rate(dev->clk_pwmdac_core));
+
+err_clk_pwmdac:
+       return ret;
+}
+
+static int sf_pwmdac_dai_probe(struct snd_soc_dai *dai)
+{
+       struct sf_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
+
+       dev->play_dma_data.addr = dev->mapbase + PWMDAC_WDATA;
+       dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       dev->play_dma_data.fifo_size = 1;
+       dev->play_dma_data.maxburst = 16;
+
+       snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL);
+       snd_soc_dai_set_drvdata(dai, dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int starfive_pwmdac_system_suspend(struct device *dev)
+{
+       struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
+
+       /* save the register value */
+       pwmdac->pwmdac_ctrl_data = pwmdc_read_reg(pwmdac->pwmdac_base, PWMDAC_CTRL);
+       return pm_runtime_force_suspend(dev);
+}
+
+static int starfive_pwmdac_system_resume(struct device *dev)
+{
+       struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
+       int ret;
+
+       ret = pm_runtime_force_resume(dev);
+       if (ret)
+               return ret;
+
+       /* restore the register value */
+       pwmdc_write_reg(pwmdac->pwmdac_base, PWMDAC_CTRL, pwmdac->pwmdac_ctrl_data);
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int starfive_pwmdac_runtime_suspend(struct device *dev)
+{
+       struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
+
+       return starfive_pwmdac_crg_enable(pwmdac, false);
+}
+
+static int starfive_pwmdac_runtime_resume(struct device *dev)
+{
+       struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
+
+       return starfive_pwmdac_crg_enable(pwmdac, true);
+}
+#endif
+
+static const struct dev_pm_ops starfive_pwmdac_pm_ops = {
+       SET_RUNTIME_PM_OPS(starfive_pwmdac_runtime_suspend, starfive_pwmdac_runtime_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(starfive_pwmdac_system_suspend, starfive_pwmdac_system_resume)
+};
+
+#define SOC_PWMDAC_ENUM_DECL(xname, xinfo, xget, xput) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = xinfo, .get = xget, .put = xput,}
+
+static const struct snd_kcontrol_new pwmdac_snd_controls[] = {
+       SOC_PWMDAC_ENUM_DECL("shift_bit", pwmdac_shift_bit_info,
+               pwmdac_shift_bit_get, pwmdac_shift_bit_put),
+       SOC_PWMDAC_ENUM_DECL("duty_cycle", pwmdac_duty_cycle_info,
+               pwmdac_duty_cycle_get, pwmdac_duty_cycle_put),
+       SOC_PWMDAC_ENUM_DECL("data_mode", pwmdac_data_mode_info,
+               pwmdac_data_mode_get, pwmdac_data_mode_put),
+       SOC_PWMDAC_ENUM_DECL("shift", pwmdac_shift_info,
+               pwmdac_shift_get, pwmdac_shift_put),
+       SOC_PWMDAC_ENUM_DECL("lr_change", pwmdac_lr_change_info,
+               pwmdac_lr_change_get, pwmdac_lr_change_put),
+};
+
+static int pwmdac_probe(struct snd_soc_component *component)
+{
+       snd_soc_add_component_controls(component, pwmdac_snd_controls,
+                                       ARRAY_SIZE(pwmdac_snd_controls));
+       return 0;
+}
+
+static const struct snd_soc_dai_ops sf_pwmdac_dai_ops = {
+       .hw_params  = sf_pwmdac_hw_params,
+       .prepare        = sf_pwmdac_prepare,
+       .trigger        = sf_pwmdac_trigger,
+};
+
+static int pwmdac_component_trigger(struct snd_soc_component *component,
+                             struct snd_pcm_substream *substream, int cmd)
+{
+       int ret = 0;
+       struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               axi_dma_cyclic_stop(chan);
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static const struct snd_soc_component_driver sf_pwmdac_component = {
+       .name           = "starfive-pwmdac",
+       .probe          = pwmdac_probe,
+       .trigger        = pwmdac_component_trigger,
+};
+
+static struct snd_soc_dai_driver pwmdac_dai = {
+       .name = "pwmdac",
+       .id = 0,
+       .probe  = sf_pwmdac_dai_probe,
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &sf_pwmdac_dai_ops,
+};
+
+static int sf_pwmdac_probe(struct platform_device *pdev)
+{
+       struct sf_pwmdac_dev *dev;
+       struct resource *res;
+       int ret;
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       dev->pwmdac_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+       if (IS_ERR(dev->pwmdac_base))
+               return PTR_ERR(dev->pwmdac_base);
+
+       dev->mapbase = res->start;
+       dev->dev = &pdev->dev;
+
+       ret = sf_pwmdac_clks_get(pdev, dev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to get pwmdac clock\n");
+               return ret;
+       }
+
+       ret = sf_pwmdac_resets_get(pdev, dev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to get pwmdac reset controls\n");
+               return ret;
+       }
+
+       ret = sf_pwmdac_clk_init(pdev, dev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable pwmdac clock\n");
+               return ret;
+       }
+
+       dev->mode = shift_8Bit_inverter;
+       dev->fifo_th = 1;//8byte
+       pwmdac_config(dev);
+
+       dev->use_pio = false;
+       dev_set_drvdata(&pdev->dev, dev);
+       ret = devm_snd_soc_register_component(&pdev->dev, &sf_pwmdac_component,
+                                        &pwmdac_dai, 1);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "not able to register dai\n");
+               return ret;
+       }
+
+       if (dev->use_pio) {
+               ret = sf_pwmdac_pcm_register(pdev);
+       } else {
+               ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+                               0);
+       }
+
+#ifdef CONFIG_PM
+       starfive_pwmdac_crg_enable(dev, false);
+#endif
+
+       pm_runtime_enable(dev->dev);
+
+       return 0;
+}
+
+
+static int sf_pwmdac_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sf_pwmdac_of_match[] = {
+       { .compatible = "starfive,jh7110-pwmdac", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, sf_pwmdac_of_match);
+#endif
+
+
+static struct platform_driver sf_pwmdac_driver = {
+       .probe          = sf_pwmdac_probe,
+       .remove         = sf_pwmdac_remove,
+       .driver         = {
+               .name   = "starfive-pwmdac",
+               .of_match_table = of_match_ptr(sf_pwmdac_of_match),
+               .pm = &starfive_pwmdac_pm_ops,
+       },
+};
+
+
+static int __init pwmdac_driver_init(void)
+{
+       return platform_driver_register(&sf_pwmdac_driver);
+}
+
+static void pwmdac_driver_exit(void)
+{
+       platform_driver_unregister(&sf_pwmdac_driver);
+}
+
+late_initcall(pwmdac_driver_init);
+module_exit(pwmdac_driver_exit);
+
+MODULE_AUTHOR("curry.zhang <curry.zhang@starfivetech.com>");
+MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("starfive pwmdac SoC Interface");
+MODULE_ALIAS("platform:starfive-pwmdac");
diff --git a/sound/soc/starfive/starfive_pwmdac_pcm.c b/sound/soc/starfive/starfive_pwmdac_pcm.c
new file mode 100644 (file)
index 0000000..3751d49
--- /dev/null
@@ -0,0 +1,249 @@
+/**
+  ******************************************************************************
+  * @file  sf_pwmdac_pcm.c
+  * @author  StarFive Technology
+  * @version  V1.0
+  * @date  05/27/2021
+  * @brief
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+  */
+
+#include <linux/io.h>
+#include <linux/rcupdate.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "pwmdac.h"
+
+#define BUFFER_BYTES_MAX       (3 * 2 * 8 * PERIOD_BYTES_MIN)
+#define PERIOD_BYTES_MIN       4096
+#define PERIODS_MIN            2
+
+static unsigned int sf_pwmdac_pcm_tx_8(struct sf_pwmdac_dev *dev,
+               struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+               bool *period_elapsed)
+{
+       const u8 (*p)[2] = (void *)runtime->dma_area;
+       unsigned int period_pos = tx_ptr % runtime->period_size;
+       int i;
+       u32 basedat = 0;
+
+       for (i = 0; i < dev->fifo_th; i++) {
+               basedat = (p[tx_ptr][0]<<8)|(p[tx_ptr][1] << 24);
+               iowrite32(basedat,dev->pwmdac_base + PWMDAC_WDATA);
+               period_pos++;
+               if (++tx_ptr >= runtime->buffer_size)
+                       tx_ptr = 0;
+       }
+
+       *period_elapsed = period_pos >= runtime->period_size;
+
+       return tx_ptr;
+}
+
+static unsigned int sf_pwmdac_pcm_tx_16(struct sf_pwmdac_dev *dev,
+               struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+               bool *period_elapsed)
+{
+       const u16 (*p)[2] = (void *)runtime->dma_area;
+       unsigned int period_pos = tx_ptr % runtime->period_size;
+       int i;
+       u32 basedat = 0;
+
+       for (i = 0; i < dev->fifo_th; i++) {
+               basedat = (p[tx_ptr][0])|(p[tx_ptr][1] << 16);
+               iowrite32(basedat,dev->pwmdac_base + PWMDAC_WDATA);
+               period_pos++;
+               if (++tx_ptr >= runtime->buffer_size)
+                       tx_ptr = 0;
+       }
+
+       *period_elapsed = period_pos >= runtime->period_size;
+       return tx_ptr;
+}
+
+static const struct snd_pcm_hardware sf_pcm_hardware = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER,
+       .rates = SNDRV_PCM_RATE_16000,
+       .rate_min = 16000,
+       .rate_max = 16000,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = BUFFER_BYTES_MAX,
+       .period_bytes_min = PERIOD_BYTES_MIN,
+       .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
+       .periods_min = PERIODS_MIN,
+       .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+       .fifo_size = 2,
+};
+
+static void sf_pcm_transfer(struct sf_pwmdac_dev *dev, bool push)
+{
+       struct snd_pcm_substream *substream;
+       bool active, period_elapsed;
+
+       rcu_read_lock();
+       if (push)
+               substream = rcu_dereference(dev->tx_substream);
+
+       active = substream && snd_pcm_running(substream);
+       if (active) {
+               unsigned int ptr;
+               unsigned int new_ptr;
+
+               if (push) {
+                       ptr = READ_ONCE(dev->tx_ptr);
+                       new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
+                                       &period_elapsed);
+                       cmpxchg(&dev->tx_ptr, ptr, new_ptr);
+               }
+
+               if (period_elapsed)
+                       snd_pcm_period_elapsed(substream);
+       }
+       rcu_read_unlock();
+}
+
+void sf_pwmdac_pcm_push_tx(struct sf_pwmdac_dev *dev)
+{
+       sf_pcm_transfer(dev, true);
+}
+
+
+static int sf_pcm_open(struct snd_soc_component *component,
+                   struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct sf_pwmdac_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
+
+       snd_soc_set_runtime_hwparams(substream, &sf_pcm_hardware);
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       runtime->private_data = dev;
+
+       return 0;
+}
+
+
+static int sf_pcm_close(struct snd_soc_component *component,
+                   struct snd_pcm_substream *substream)
+{
+       synchronize_rcu();
+       return 0;
+}
+
+static int sf_pcm_hw_params(struct snd_soc_component *component,
+                           struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct sf_pwmdac_dev *dev = runtime->private_data;
+       int ret;
+
+
+       switch (params_channels(hw_params)) {
+       case 2:
+               break;
+       default:
+               dev_err(dev->dev, "invalid channels number\n");
+               return -EINVAL;
+       }
+
+       switch (params_format(hw_params)) {
+       case SNDRV_PCM_FORMAT_U8:
+       case SNDRV_PCM_FORMAT_S8:
+               dev->tx_fn = sf_pwmdac_pcm_tx_8;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               dev->tx_fn = sf_pwmdac_pcm_tx_16;
+               break;
+       default:
+               dev_err(dev->dev, "invalid format\n");
+               return -EINVAL;
+       }
+
+               return 0;
+}
+
+
+static int sf_pcm_trigger(struct snd_soc_component *component,
+                   struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct sf_pwmdac_dev *dev = runtime->private_data;
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       WRITE_ONCE(dev->tx_ptr, 0);
+                       rcu_assign_pointer(dev->tx_substream, substream);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       rcu_assign_pointer(dev->tx_substream, NULL);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t sf_pcm_pointer(struct snd_soc_component *component,
+                                    struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct sf_pwmdac_dev *dev = runtime->private_data;
+       snd_pcm_uframes_t pos;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               pos = READ_ONCE(dev->tx_ptr);
+
+       return pos < runtime->buffer_size ? pos : 0;
+}
+
+static int sf_pcm_new(struct snd_soc_component *component,
+                     struct snd_soc_pcm_runtime *rtd)
+{
+       size_t size = sf_pcm_hardware.buffer_bytes_max;
+
+       snd_pcm_set_managed_buffer_all(rtd->pcm,
+                       SNDRV_DMA_TYPE_CONTINUOUS,
+                       NULL, size, size);
+       return 0;
+}
+
+static const struct snd_soc_component_driver dw_pcm_component = {
+       .open           = sf_pcm_open,
+       .close          = sf_pcm_close,
+       .hw_params      = sf_pcm_hw_params,
+       .trigger        = sf_pcm_trigger,
+       .pointer        = sf_pcm_pointer,
+       .pcm_construct  = sf_pcm_new,
+};
+
+int sf_pwmdac_pcm_register(struct platform_device *pdev)
+{
+       return devm_snd_soc_register_component(&pdev->dev, &dw_pcm_component,
+                                              NULL, 0);
+}
diff --git a/sound/soc/starfive/starfive_pwmdac_transmitter.c b/sound/soc/starfive/starfive_pwmdac_transmitter.c
new file mode 100755 (executable)
index 0000000..c580800
--- /dev/null
@@ -0,0 +1,110 @@
+/**
+  ******************************************************************************
+  * @file  sf_pwmdac_transmitter.c
+  * @author  StarFive Technology
+  * @version  V1.0
+  * @date  05/27/2021
+  * @brief
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+  */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <linux/of.h>
+
+#define DRV_NAME "pwmdac-dit"
+
+#define STUB_RATES     SNDRV_PCM_RATE_8000_192000
+#define STUB_FORMATS   (SNDRV_PCM_FMTBIT_S8|\
+                       SNDRV_PCM_FMTBIT_U8|\
+                       SNDRV_PCM_FMTBIT_S16_LE | \
+                       SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE  | \
+                       SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dapm_widget dit_widgets[] = {
+       SND_SOC_DAPM_OUTPUT("pwmdac-out"),
+};
+
+static const struct snd_soc_dapm_route dit_routes[] = {
+       { "pwmdac-out", NULL, "Playback" },
+};
+
+static struct snd_soc_component_driver soc_codec_pwmdac_dit = {
+       .dapm_widgets           = dit_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(dit_widgets),
+       .dapm_routes            = dit_routes,
+       .num_dapm_routes        = ARRAY_SIZE(dit_routes),
+       .idle_bias_on           = 1,
+       .use_pmdown_time        = 1,
+       .endianness             = 1,
+       .legacy_dai_naming      = 1,
+};
+
+static struct snd_soc_dai_driver dit_stub_dai = {
+       .name           = "pwmdac-dit-hifi",
+       .playback       = {
+               .stream_name    = "Playback",
+               .channels_min   = 1,
+               .channels_max   = 384,
+               .rates          = STUB_RATES,
+               .formats        = STUB_FORMATS,
+       },
+};
+
+static int pwmdac_dit_probe(struct platform_device *pdev)
+{
+
+       return devm_snd_soc_register_component(&pdev->dev,
+                       &soc_codec_pwmdac_dit,
+                       &dit_stub_dai, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pwmdac_dit_dt_ids[] = {
+       { .compatible = "starfive,jh7110-pwmdac-dit", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pwmdac_dit_dt_ids);
+#endif
+
+static struct platform_driver pwmdac_dit_driver = {
+       .probe          = pwmdac_dit_probe,
+       .driver         = {
+               .name   = DRV_NAME,
+               .of_match_table = of_match_ptr(pwmdac_dit_dt_ids),
+       },
+};
+
+static int __init pwmdac_dit_driver_init(void)
+{
+       return platform_driver_register(&pwmdac_dit_driver);
+}
+
+static void pwmdac_dit_driver_exit(void)
+{
+       platform_driver_unregister(&pwmdac_dit_driver);
+}
+
+late_initcall(pwmdac_dit_driver_init);
+module_exit(pwmdac_dit_driver_exit);
+
+
+MODULE_AUTHOR("curry.zhang <curry.zhang@starfivetech.com>");
+MODULE_DESCRIPTION("pwmdac dummy codec driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform: starfive-pwmdac dummy codec");
diff --git a/sound/soc/starfive/starfive_spdif.c b/sound/soc/starfive/starfive_spdif.c
new file mode 100644 (file)
index 0000000..386b06b
--- /dev/null
@@ -0,0 +1,399 @@
+/**
+  ******************************************************************************
+  * @file  sf_spdif.c
+  * @author  StarFive Technology
+  * @version  V1.0
+  * @date  05/27/2021
+  * @brief
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+  */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+#include "starfive_spdif.h"
+
+static irqreturn_t spdif_irq_handler(int irq, void *dev_id)
+{
+       struct sf_spdif_dev *dev = dev_id;
+       bool irq_valid = false;
+       unsigned int intr;
+       unsigned int stat;
+
+       regmap_read(dev->regmap, SPDIF_INT_REG, &intr);
+       regmap_read(dev->regmap, SPDIF_STAT_REG, &stat);
+       regmap_update_bits(dev->regmap, SPDIF_CTRL,
+               SPDIF_MASK_ENABLE, 0);
+       regmap_update_bits(dev->regmap, SPDIF_INT_REG,
+               SPDIF_INT_REG_BIT, 0);
+
+       if ((stat & SPDIF_EMPTY_FLAG) || (stat & SPDIF_AEMPTY_FLAG)) {
+               sf_spdif_pcm_push_tx(dev);
+               irq_valid = true;
+       }
+
+       if ((stat & SPDIF_FULL_FLAG) || (stat & SPDIF_AFULL_FLAG)) {
+               sf_spdif_pcm_pop_rx(dev);
+               irq_valid = true;
+       }
+
+       if (stat & SPDIF_PARITY_FLAG) {
+               irq_valid = true;
+       }
+
+       if (stat & SPDIF_UNDERR_FLAG) {
+               irq_valid = true;
+       }
+
+       if (stat & SPDIF_OVRERR_FLAG) {
+               irq_valid = true;
+       }
+
+       if (stat & SPDIF_SYNCERR_FLAG) {
+               irq_valid = true;
+       }
+
+       if (stat & SPDIF_LOCK_FLAG) {
+               irq_valid = true;
+       }
+
+       if (stat & SPDIF_BEGIN_FLAG) {
+               irq_valid = true;
+       }
+
+       if (stat & SPDIF_RIGHT_LEFT) {
+               irq_valid = true;
+       }
+
+       regmap_update_bits(dev->regmap, SPDIF_CTRL,
+               SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE);
+
+       if (irq_valid)
+               return IRQ_HANDLED;
+       else
+               return IRQ_NONE;
+}
+
+static int sf_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+       struct snd_soc_dai *dai)
+{
+       struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+       bool tx;
+
+       tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       if (tx) {
+               /* tx mode */
+               regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+                       SPDIF_TR_MODE, SPDIF_TR_MODE);
+
+               regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+                       SPDIF_MASK_FIFO, SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK);
+       } else {
+               /* rx mode */
+               regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+                       SPDIF_TR_MODE, 0);
+
+               regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+                       SPDIF_MASK_FIFO, SPDIF_FULL_MASK | SPDIF_AFULL_MASK);
+       }
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               /* clock recovery form the SPDIF data stream  0:clk_enable */
+               regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+                       SPDIF_CLK_ENABLE, 0);
+
+               regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+                       SPDIF_ENABLE, SPDIF_ENABLE);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               /* clock recovery form the SPDIF data stream  1:power save mode */
+               regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+                       SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE);
+
+               regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+                       SPDIF_ENABLE, 0);
+               break;
+       default:
+               printk(KERN_ERR "%s L.%d cmd:%d\n", __func__, __LINE__, cmd);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sf_spdif_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+       unsigned int channels;
+       unsigned int rate;
+       unsigned int format;
+       unsigned int tsamplerate;
+
+       channels = params_channels(params);
+       rate = params_rate(params);
+       format = params_format(params);
+
+       switch (channels) {
+       case 2:
+               break;
+       default:
+               dev_err(dai->dev, "invalid channels number\n");
+               return -EINVAL;
+       }
+
+       switch (format) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+       case SNDRV_PCM_FORMAT_S24_LE:
+       case SNDRV_PCM_FORMAT_S32_LE:
+               break;
+       default:
+               dev_err(spdif->dev, "invalid format\n");
+               return -EINVAL;
+       }
+
+       switch (rate) {
+       case 8000:
+       case 11025:
+       case 16000:
+       case 22050:
+               break;
+       default:
+               printk(KERN_ERR "channel:%d sample rate:%d\n", channels, rate);
+               return -EINVAL;
+       }
+
+       /* 4096000/128=32000 */
+       tsamplerate = (32000 + rate/2)/rate - 1;
+
+       if (rate < 3) {
+               return -EINVAL;
+       }
+
+       /* transmission sample rate */
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL, 0xFF, tsamplerate);
+
+       return 0;
+}
+
+static int sf_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+       struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+
+       #if 0
+       spdif->play_dma_data.addr = (dma_addr_t)spdif->spdif_base + SPDIF_FIFO_ADDR;
+       spdif->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       spdif->play_dma_data.fifo_size = 16;
+       spdif->play_dma_data.maxburst = 16;
+       spdif->capture_dma_data.addr = (dma_addr_t)spdif->spdif_base + SPDIF_FIFO_ADDR;
+       spdif->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       spdif->capture_dma_data.fifo_size = 16;
+       spdif->capture_dma_data.maxburst = 16;
+       snd_soc_dai_init_dma_data(dai, &spdif->play_dma_data, &spdif->capture_dma_data);
+       snd_soc_dai_set_drvdata(dai, spdif);
+       #endif
+
+       /* reset */
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+               SPDIF_ENABLE | SPDIF_SFR_ENABLE | SPDIF_FIFO_ENABLE, 0);
+
+       /* clear irq */
+       regmap_update_bits(spdif->regmap, SPDIF_INT_REG,
+               SPDIF_INT_REG_BIT, 0);
+
+       /* power save mode */
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+               SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE);
+
+       /* power save mode */
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+               SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE);
+
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+               SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE,
+               SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE);
+
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+               SPDIF_SETPREAMBB, SPDIF_SETPREAMBB);
+
+       regmap_update_bits(spdif->regmap, SPDIF_INT_REG,
+               0x1FFF<<SPDIF_PREAMBLEDEL, 0x3<<SPDIF_PREAMBLEDEL);
+
+       regmap_update_bits(spdif->regmap, SPDIF_FIFO_CTRL,
+               0xFFFFFFFF, 0x20|(0x20<<SPDIF_AFULL_THRESHOLD));
+
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+               SPDIF_PARITYGEN, SPDIF_PARITYGEN);
+
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+               SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE);
+
+       /* APB access to FIFO enable, disable if use DMA/FIFO */
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+               SPDIF_USE_FIFO_IF, 0);
+
+       /* two channel */
+       regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+               SPDIF_CHANNEL_MODE, 0);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops sf_spdif_dai_ops = {
+       .trigger = sf_spdif_trigger,
+       .hw_params = sf_spdif_hw_params,
+};
+
+#define SF_PCM_RATE_44100_192000  (SNDRV_PCM_RATE_44100 | \
+                                                                       SNDRV_PCM_RATE_48000 | \
+                                                                       SNDRV_PCM_RATE_96000 | \
+                                                                       SNDRV_PCM_RATE_192000)
+
+#define SF_PCM_RATE_8000_22050  (SNDRV_PCM_RATE_8000 | \
+                                                                       SNDRV_PCM_RATE_11025 | \
+                                                                       SNDRV_PCM_RATE_16000 | \
+                                                                       SNDRV_PCM_RATE_22050)
+
+static struct snd_soc_dai_driver sf_spdif_dai = {
+       .name = "spdif",
+       .id = 0,
+       .probe = sf_spdif_dai_probe,
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SF_PCM_RATE_8000_22050,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE \
+                                       |SNDRV_PCM_FMTBIT_S24_LE \
+                                       |SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .capture =  {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SF_PCM_RATE_8000_22050,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE \
+                                       |SNDRV_PCM_FMTBIT_S24_LE \
+                                       |SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .ops = &sf_spdif_dai_ops,
+       .symmetric_rate = 1,
+};
+
+static const struct snd_soc_component_driver sf_spdif_component = {
+       .name = "sf-spdif",
+};
+
+static const struct regmap_config sf_spdif_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = 0x200,
+};
+
+static int sf_spdif_probe(struct platform_device *pdev)
+{
+       struct sf_spdif_dev *spdif;
+       struct resource *res;
+       void __iomem *base;
+       int ret;
+       int irq;
+
+       spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
+       if (!spdif)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, spdif);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       spdif->spdif_base = base;
+       spdif->regmap = devm_regmap_init_mmio(&pdev->dev, spdif->spdif_base,
+                                           &sf_spdif_regmap_config);
+       if (IS_ERR(spdif->regmap))
+               return PTR_ERR(spdif->regmap);
+
+       spdif->dev = &pdev->dev;
+       spdif->fifo_th = 16;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq >= 0) {
+               ret = devm_request_irq(&pdev->dev, irq, spdif_irq_handler, 0,
+                               pdev->name, spdif);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to request irq\n");
+                       return ret;
+               }
+       }
+
+       ret = devm_snd_soc_register_component(&pdev->dev, &sf_spdif_component,
+                                        &sf_spdif_dai, 1);
+       if (ret)
+               goto err_clk_disable;
+
+       if (irq >= 0) {
+               ret = sf_spdif_pcm_register(pdev);
+               spdif->use_pio = true;
+       } else {
+               ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+                                       0);
+               spdif->use_pio = false;
+       }
+
+       if (ret)
+               goto err_clk_disable;
+
+       return 0;
+
+err_clk_disable:
+       return ret;
+}
+
+static const struct of_device_id sf_spdif_of_match[] = {
+       { .compatible = "starfive,sf-spdif", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, sf_spdif_of_match);
+
+static struct platform_driver sf_spdif_driver = {
+       .driver = {
+               .name = "sf-spdif",
+               .of_match_table = sf_spdif_of_match,
+       },
+       .probe = sf_spdif_probe,
+};
+module_platform_driver(sf_spdif_driver);
+
+MODULE_AUTHOR("michael.yan <michael.yan@starfive.com>");
+MODULE_DESCRIPTION("starfive SPDIF driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/starfive/starfive_spdif.h b/sound/soc/starfive/starfive_spdif.h
new file mode 100644 (file)
index 0000000..ff641c6
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+  ******************************************************************************
+  * @file  sf_spdif.h
+  * @author  StarFive Technology
+  * @version  V1.0
+  * @date  05/27/2021
+  * @brief
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+  */
+
+#ifndef __SND_SOC_STARFIVE_SPDIF_H
+#define __SND_SOC_STARFIVE_SPDIF_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <linux/dmaengine.h>
+#include <linux/types.h>
+
+#define SPDIF_CTRL                             (0x0)
+#define SPDIF_INT_REG                  (0x4)
+#define SPDIF_FIFO_CTRL                        (0x8)
+#define SPDIF_STAT_REG                 (0xC)
+
+#define SPDIF_FIFO_ADDR                        (0x100)
+#define DMAC_SPDIF_POLLING_LEN (256)
+
+///ctrl: sampled on the rising clock edge
+#define        SPDIF_TSAMPLERATE       0///[SRATEW-1:0]
+#define SPDIF_SFR_ENABLE       (1<<8)  ///0:SFR reg reset to defualt value; auto set back to '1' after reset
+#define SPDIF_ENABLE           (1<<9)  ///0:reset of SPDIF block, SRF bits are unchanged; 1:enables SPDIF module
+#define SPDIF_FIFO_ENABLE      (1<<10) ///0:FIFO pointers are reset to zero,threshold levels for FIFO are unchaned; auto set back to '1'
+#define SPDIF_CLK_ENABLE       (1<<11) ///1:blocked and the modules are in power save mode; 0:block feeds the modules
+#define SPDIF_TR_MODE          (1<<12) ///0:rx; 1:tx
+#define        SPDIF_PARITCHECK        (1<<13) ///0:party bit rx in a sub-frame is repeated on the parity; 1:check on a parity error
+#define        SPDIF_PARITYGEN         (1<<14) ///0:parity bit from FIFO is transmitted in sub-frame;1:parity bit generated inside the core and added to a transmitted sub-frame
+#define SPDIF_VALIDITYCHECK    (1<<15) ///0:validity bit in frame isn't checked and all frame are written; 1:validity bit rx is checked
+#define SPDIF_CHANNEL_MODE     (1<<16) ///0:two-channel; 1:single-channel
+#define SPDIF_DUPLICATE                (1<<17) ///only tx -single-channel mode; 0:secondary channel; 1: left(primary) channel
+#define SPDIF_SETPREAMBB       (1<<18) ///only tx; 0:first preamble B after reset tx valid sub-frame; 1:first preamble B is tx after preambleddel(INT_REG)
+#define        SPDIF_USE_FIFO_IF       (1<<19) ///0:FIFO disabled ,APB accese FIFO; 1:FIFO enable, APB access to FIFO disable;
+///#define RESERVED            (1<<20)
+#define SPDIF_PARITY_MASK      (1<<21)
+#define SPDIF_UNDERR_MASK      (1<<22)
+#define SPDIF_OVRERR_MASK      (1<<23)
+#define SPDIF_EMPTY_MASK       (1<<24)
+#define        SPDIF_AEMPTY_MASK       (1<<25)
+#define SPDIF_FULL_MASK                (1<<26)
+#define SPDIF_AFULL_MASK       (1<<27)
+#define SPDIF_SYNCERR_MASK     (1<<28)
+#define SPDIF_LOCK_MASK                (1<<29)
+#define SPDIF_BEGIN_MASK       (1<<30)
+#define SPDIF_INTEREQ_MAKS     (1<<31)
+
+#define SPDIF_MASK_ENABLE      (SPDIF_PARITY_MASK | SPDIF_UNDERR_MASK | SPDIF_OVRERR_MASK | SPDIF_EMPTY_MASK | \
+                                                        SPDIF_AEMPTY_MASK | SPDIF_FULL_MASK | SPDIF_AFULL_MASK | SPDIF_SYNCERR_MASK |  \
+                                                        SPDIF_LOCK_MASK | SPDIF_BEGIN_MASK | SPDIF_INTEREQ_MAKS)
+
+#define SPDIF_MASK_FIFO     (SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK | SPDIF_FULL_MASK | SPDIF_AFULL_MASK)
+
+////INT_REG
+#define SPDIF_RSAMPLERATE      0               ///[SRATEW-1:0]
+#define SPDIF_PREAMBLEDEL      8               ///[PDELAYW+7:8]        first B delay
+#define SPDIF_PARITYO          (1<<21) ///0:clear parity error
+#define SPDIF_TDATA_UNDERR     (1<<22) ///tx data underrun error;0:clear
+#define SPDIF_RDATA_OVRERR     (1<<23) ///rx data overrun error; 0:clear
+#define SPDIF_FIFO_EMPTY       (1<<24) ///empty; 0:clear
+#define SPDIF_FIOF_AEMPTY      (1<<25) ///almost empty; 0:clear
+#define SPDIF_FIFO_FULL                (1<<26) ///FIFO full; 0:clear
+#define SPDIF_FIFO_AFULL       (1<<27) ///FIFO almost full; 0:clear
+#define SPDIF_SYNCERR          (1<<28) ///sync error; 0:clear
+#define SPDIF_LOCK                     (1<<29) ///sync; 0:clear
+#define SPDIF_BLOCK_BEGIN      (1<<30) ///new start block rx data
+
+#define SPDIF_INT_REG_BIT      (SPDIF_PARITYO | SPDIF_TDATA_UNDERR | SPDIF_RDATA_OVRERR | SPDIF_FIFO_EMPTY |   \
+                                                        SPDIF_FIOF_AEMPTY | SPDIF_FIFO_FULL | SPDIF_FIFO_AFULL | SPDIF_SYNCERR |       \
+                                                        SPDIF_LOCK | SPDIF_BLOCK_BEGIN)
+                                                
+#define SPDIF_ERROR_INT_STATUS (SPDIF_PARITYO | SPDIF_TDATA_UNDERR | SPDIF_RDATA_OVRERR)
+#define SPDIF_FIFO_INT_STATUS  (SPDIF_FIFO_EMPTY | SPDIF_FIOF_AEMPTY | SPDIF_FIFO_FULL | SPDIF_FIFO_AFULL)
+
+#define SPDIF_INT_PARITY_ERROR (-1)
+#define SPDIF_INT_TDATA_UNDERR (-2)
+#define SPDIF_INT_RDATA_OVRERR (-3)
+#define SPDIF_INT_FIFO_EMPTY   1
+#define SPDIF_INT_FIFO_AEMPTY  2
+#define SPDIF_INT_FIFO_FULL            3
+#define SPDIF_INT_FIFO_AFULL   4
+#define SPDIF_INT_SYNCERR              (-4)
+#define SPDIF_INT_LOCK                 5       ///reciever has become synchronized with input data stream
+#define SPDIF_INT_BLOCK_BEGIN  6       ///start a new block in recieve data, written into FIFO
+
+///FIFO_CTRL
+#define SPDIF_AEMPTY_THRESHOLD 0///[depth-1:0] 
+#define SPDIF_AFULL_THRESHOLD  16///[depth+15:16]
+
+///STAT_REG
+#define SPDIF_FIFO_LEVEL       (1<<0)
+#define SPDIF_PARITY_FLAG      (1<<21) ///1:error; 0:repeated
+#define SPDIF_UNDERR_FLAG      (1<<22) ///1:error
+#define SPDIF_OVRERR_FLAG      (1<<23) ///1:error
+#define SPDIF_EMPTY_FLAG       (1<<24) ///1:fifo empty
+#define SPDIF_AEMPTY_FLAG      (1<<25) ///1:fifo almost empty
+#define SPDIF_FULL_FLAG                (1<<26) ///1:fifo full
+#define SPDIF_AFULL_FLAG       (1<<27) ///1:fifo almost full
+#define SPDIF_SYNCERR_FLAG     (1<<28) ///1:rx sync error
+#define SPDIF_LOCK_FLAG                (1<<29) ///1:RX sync
+#define SPDIF_BEGIN_FLAG       (1<<30) ///1:start a new block
+#define SPDIF_RIGHT_LEFT       (1<<31) ///1:left channel received and tx into FIFO; 0:right channel received and tx into FIFO
+
+#define SPDIF_STAT             (SPDIF_PARITY_FLAG | SPDIF_UNDERR_FLAG | SPDIF_OVRERR_FLAG | SPDIF_EMPTY_FLAG |         \
+                                                SPDIF_AEMPTY_FLAG | SPDIF_FULL_FLAG | SPDIF_AFULL_FLAG | SPDIF_SYNCERR_FLAG |          \
+                                                SPDIF_LOCK_FLAG | SPDIF_BEGIN_FLAG | SPDIF_RIGHT_LEFT)
+struct sf_spdif_dev {
+       void __iomem *spdif_base;
+       struct regmap *regmap;
+       struct device *dev;
+       u32 fifo_th;
+       int active;
+
+       /* data related to DMA transfers b/w i2s and DMAC */
+       struct snd_dmaengine_dai_dma_data play_dma_data;
+       struct snd_dmaengine_dai_dma_data capture_dma_data;
+
+       bool use_pio;
+       struct snd_pcm_substream __rcu *tx_substream;
+       struct snd_pcm_substream __rcu *rx_substream;
+
+       unsigned int (*tx_fn)(struct sf_spdif_dev *dev,
+                       struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+                       bool *period_elapsed, snd_pcm_format_t format);
+       unsigned int (*rx_fn)(struct sf_spdif_dev *dev,
+                       struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
+                       bool *period_elapsed, snd_pcm_format_t format);
+
+       snd_pcm_format_t format;
+       //unsigned int sample_bits;
+       unsigned int tx_ptr;
+       unsigned int rx_ptr;
+
+       struct snd_dmaengine_dai_dma_data dma_data;
+};
+
+#if IS_ENABLED(CONFIG_SND_STARFIVE_SPDIF_PCM)
+void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev);
+void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev);
+int sf_spdif_pcm_register(struct platform_device *pdev);
+#else
+void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev) { }
+void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev) { }
+int sf_spdif_pcm_register(struct platform_device *pdev)
+{
+       return -EINVAL;
+}
+#endif
+
+
+#endif /* __SND_SOC_STARFIVE_SPDIF_H */
diff --git a/sound/soc/starfive/starfive_spdif_pcm.c b/sound/soc/starfive/starfive_spdif_pcm.c
new file mode 100644 (file)
index 0000000..eb2d473
--- /dev/null
@@ -0,0 +1,303 @@
+/**
+  ******************************************************************************
+  * @file  sf_spdif_pcm.c
+  * @author  StarFive Technology
+  * @version  V1.0
+  * @date  05/27/2021
+  * @brief
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+  */
+
+#include <linux/io.h>
+#include <linux/rcupdate.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "starfive_spdif.h"
+
+#define BUFFER_BYTES_MAX       (3 * 2 * 8 * PERIOD_BYTES_MIN)
+#define PERIOD_BYTES_MIN       4096
+#define PERIODS_MIN            2
+
+static unsigned int sf_spdif_pcm_tx(struct sf_spdif_dev *dev,
+               struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+               bool *period_elapsed, snd_pcm_format_t format)
+{
+       const u16 (*p16)[2] = (void *)runtime->dma_area;
+       const u32 (*p32)[2] = (void *)runtime->dma_area;
+       u32 data[2];
+       unsigned int period_pos = tx_ptr % runtime->period_size;
+       int i;
+
+       for (i = 0; i < dev->fifo_th; i++) {
+               if (SNDRV_PCM_FORMAT_S16_LE == format) {
+                       data[0] = p16[tx_ptr][0];
+                       data[1] = p16[tx_ptr][1];
+                       data[0] = data[0]<<8;
+                       data[1] = data[1]<<8;
+               } else if (SNDRV_PCM_FORMAT_S24_LE == format) {
+                       data[0] = p32[tx_ptr][0];
+                       data[1] = p32[tx_ptr][1];
+               } else if (SNDRV_PCM_FORMAT_S32_LE == format) {
+                       data[0] = p32[tx_ptr][0];
+                       data[1] = p32[tx_ptr][1];
+                       data[0] = data[0]>>8;
+                       data[1] = data[1]>>8;
+               }
+
+               iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
+               iowrite32(data[1], dev->spdif_base + SPDIF_FIFO_ADDR);
+               period_pos++;
+               if (++tx_ptr >= runtime->buffer_size) {
+                       tx_ptr = 0;
+               }
+       }
+
+       *period_elapsed = period_pos >= runtime->period_size;
+       return tx_ptr;
+}
+
+static unsigned int sf_spdif_pcm_rx(struct sf_spdif_dev *dev,
+               struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
+               bool *period_elapsed, snd_pcm_format_t format)
+{
+       u16 (*p16)[2] = (void *)runtime->dma_area;
+       u32 (*p32)[2] = (void *)runtime->dma_area;
+       u32 data[2];
+       unsigned int period_pos = rx_ptr % runtime->period_size;
+       int i;
+
+       for (i = 0; i < dev->fifo_th; i++) {
+               data[0] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
+               data[1] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
+               if (SNDRV_PCM_FORMAT_S16_LE == format) {
+                       p16[rx_ptr][0] = data[0]>>8;
+                       p16[rx_ptr][1] = data[1]>>8;
+               } else if (SNDRV_PCM_FORMAT_S24_LE == format) {
+                       p32[rx_ptr][0] = data[0];
+                       p32[rx_ptr][1] = data[1];
+               } else if (SNDRV_PCM_FORMAT_S32_LE == format) {
+                       p32[rx_ptr][0] = data[0]<<8;
+                       p32[rx_ptr][1] = data[1]<<8;
+               }
+
+               period_pos++;
+               if (++rx_ptr >= runtime->buffer_size)
+                       rx_ptr = 0;
+       }
+
+       *period_elapsed = period_pos >= runtime->period_size;
+       return rx_ptr;
+}
+
+static const struct snd_pcm_hardware sf_pcm_hardware = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER,
+       .rates = SNDRV_PCM_RATE_8000 |
+               SNDRV_PCM_RATE_11025 |
+               SNDRV_PCM_RATE_16000 |
+               SNDRV_PCM_RATE_22050 |
+               SNDRV_PCM_RATE_32000 |
+               SNDRV_PCM_RATE_44100 |
+               SNDRV_PCM_RATE_48000,
+       .rate_min = 8000,
+       .rate_max = 48000,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE |
+               SNDRV_PCM_FMTBIT_S24_LE |
+               SNDRV_PCM_FMTBIT_S32_LE,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = BUFFER_BYTES_MAX,
+       .period_bytes_min = PERIOD_BYTES_MIN,
+       .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
+       .periods_min = PERIODS_MIN,
+       .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+       .fifo_size = 16,
+};
+
+static void sf_spdif_pcm_transfer(struct sf_spdif_dev *dev, bool push)
+{
+       struct snd_pcm_substream *substream;
+       bool active, period_elapsed;
+
+       rcu_read_lock();
+       if (push)
+               substream = rcu_dereference(dev->tx_substream);
+       else
+               substream = rcu_dereference(dev->rx_substream);
+       active = substream && snd_pcm_running(substream);
+       if (active) {
+               unsigned int ptr;
+               unsigned int new_ptr;
+
+               if (push) {
+                       ptr = READ_ONCE(dev->tx_ptr);
+                       new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
+                                       &period_elapsed, dev->format);
+                       cmpxchg(&dev->tx_ptr, ptr, new_ptr);
+               } else {
+                       ptr = READ_ONCE(dev->rx_ptr);
+                       new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
+                                       &period_elapsed, dev->format);
+                       cmpxchg(&dev->rx_ptr, ptr, new_ptr);
+               }
+
+               if (period_elapsed)
+                       snd_pcm_period_elapsed(substream);
+       }
+       rcu_read_unlock();
+}
+
+void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev)
+{
+       sf_spdif_pcm_transfer(dev, true);
+}
+
+void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev)
+{
+       sf_spdif_pcm_transfer(dev, false);
+}
+
+static int sf_pcm_open(struct snd_soc_component *component,
+                      struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct sf_spdif_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
+
+       snd_soc_set_runtime_hwparams(substream, &sf_pcm_hardware);
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       runtime->private_data = dev;
+
+       return 0;
+}
+
+static int sf_pcm_close(struct snd_soc_component *component,
+                       struct snd_pcm_substream *substream)
+{
+       synchronize_rcu();
+       return 0;
+}
+
+static int sf_pcm_hw_params(struct snd_soc_component *component,
+                           struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct sf_spdif_dev *dev = runtime->private_data;
+       int ret;
+
+       switch (params_channels(hw_params)) {
+       case 2:
+               break;
+       default:
+               dev_err(dev->dev, "invalid channels number\n");
+               return -EINVAL;
+       }
+
+       dev->format = params_format(hw_params);
+       switch (dev->format) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+       case SNDRV_PCM_FORMAT_S24_LE:
+       case SNDRV_PCM_FORMAT_S32_LE:
+               break;
+       default:
+               dev_err(dev->dev, "invalid format\n");
+               return -EINVAL;
+       }
+
+       dev->tx_fn = sf_spdif_pcm_tx;
+       dev->rx_fn = sf_spdif_pcm_rx;
+
+       return 0;
+}
+
+static int sf_pcm_trigger(struct snd_soc_component *component,
+                         struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct sf_spdif_dev *dev = runtime->private_data;
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       WRITE_ONCE(dev->tx_ptr, 0);
+                       rcu_assign_pointer(dev->tx_substream, substream);
+               } else {
+                       WRITE_ONCE(dev->rx_ptr, 0);
+                       rcu_assign_pointer(dev->rx_substream, substream);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       rcu_assign_pointer(dev->tx_substream, NULL);
+               else
+                       rcu_assign_pointer(dev->rx_substream, NULL);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t sf_pcm_pointer(struct snd_soc_component *component,
+                                       struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct sf_spdif_dev *dev = runtime->private_data;
+       snd_pcm_uframes_t pos;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               pos = READ_ONCE(dev->tx_ptr);
+       }
+       else {
+               pos = READ_ONCE(dev->rx_ptr);
+       }
+
+       return pos < runtime->buffer_size ? pos : 0;
+}
+
+static int sf_pcm_new(struct snd_soc_component *component,
+                     struct snd_soc_pcm_runtime *rtd)
+{
+       size_t size = sf_pcm_hardware.buffer_bytes_max;
+
+       snd_pcm_set_managed_buffer_all(rtd->pcm,
+                       SNDRV_DMA_TYPE_CONTINUOUS,
+                       NULL, size, size);
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver sf_pcm_component = {
+       .open           = sf_pcm_open,
+       .close          = sf_pcm_close,
+       .hw_params      = sf_pcm_hw_params,
+       .trigger        = sf_pcm_trigger,
+       .pointer        = sf_pcm_pointer,
+       .pcm_construct  = sf_pcm_new,
+};
+
+int sf_spdif_pcm_register(struct platform_device *pdev)
+{
+       return devm_snd_soc_register_component(&pdev->dev, &sf_pcm_component,
+                                              NULL, 0);
+}
diff --git a/sound/soc/starfive/starfive_tdm.c b/sound/soc/starfive/starfive_tdm.c
new file mode 100644 (file)
index 0000000..a80d4d6
--- /dev/null
@@ -0,0 +1,439 @@
+/**
+  ******************************************************************************
+  * @file  sf_tdm.c
+  * @author  StarFive Technology
+  * @version  V1.0
+  * @date  08/2/2021
+  * @brief
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+  */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "starfive_tdm.h"
+
+#define AUDIOC_CLK     (12288000)
+
+
+static inline u32 sf_tdm_readl(struct sf_tdm_dev *tdm, u16 reg)
+{
+       return readl_relaxed(tdm->tdm_base + reg);
+}
+
+static inline void sf_tdm_writel(struct sf_tdm_dev *tdm, u16 reg, u32 val)
+{
+       writel_relaxed(val, tdm->tdm_base + reg);
+}
+
+static void sf_tdm_start(struct sf_tdm_dev *tdm, struct snd_pcm_substream *substream)
+{
+    u32 data;
+       
+    data = sf_tdm_readl(tdm, TDM_PCMGBCR);
+    sf_tdm_writel(tdm, TDM_PCMGBCR, data | 0x1 | (0x1<<4));
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        sf_tdm_writel(tdm, TDM_PCMTXCR, sf_tdm_readl(tdm, TDM_PCMTXCR) | 0x1);
+    }else{
+        sf_tdm_writel(tdm, TDM_PCMRXCR, sf_tdm_readl(tdm, TDM_PCMRXCR) | 0x1);
+    }
+}
+
+static void sf_tdm_stop(struct sf_tdm_dev*tdm, struct snd_pcm_substream *substream)
+{
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        sf_tdm_writel(tdm, TDM_PCMTXCR, sf_tdm_readl(tdm, TDM_PCMTXCR) & 0xffe);
+    }else{
+        sf_tdm_writel(tdm, TDM_PCMRXCR, sf_tdm_readl(tdm, TDM_PCMRXCR) & 0xffe);
+    }
+    sf_tdm_writel(tdm, TDM_PCMGBCR, sf_tdm_readl(tdm, TDM_PCMGBCR) & 0x1e);
+}
+
+static int sf_tdm_syncdiv(struct sf_tdm_dev *tdm)
+{
+    u32 sl, sscale, syncdiv;
+
+    sl = (tdm->rx.sl >= tdm->tx.sl)? tdm->rx.sl:tdm->tx.sl;
+    sscale = (tdm->rx.sscale >= tdm->tx.sscale)? tdm->rx.sscale:tdm->tx.sscale;
+    syncdiv = tdm->pcmclk/tdm->samplerate - 1;
+
+    if((syncdiv + 1) < (sl * sscale)){
+        printk("set syncdiv failed !\n");
+        return -1;
+    }
+
+    if((tdm->syncm == TDM_SYNCM_LONG) && ((tdm->rx.sscale <= 1) || (tdm->tx.sscale <= 1))){
+        if((syncdiv+1) <= (sl)){
+            printk("set syncdiv failed !  it must be (syncdiv+1) > max[txsl, rxsl]\n");
+            return -1;
+        }
+    }
+
+    sf_tdm_writel(tdm, TDM_PCMDIV, syncdiv);
+       return 0;
+}
+
+static void sf_tdm_contrl(struct sf_tdm_dev *tdm)
+{
+    u32 data;
+
+    data = (tdm->clkpolity << 5) | (tdm->elm << 3) | (tdm->syncm << 2) | (tdm->mode << 1);
+    sf_tdm_writel(tdm, TDM_PCMGBCR, data);
+}
+
+static void sf_tdm_config(struct sf_tdm_dev *tdm, struct snd_pcm_substream *substream)
+{
+    u32 datarx, datatx;
+       
+    sf_tdm_stop(tdm, substream);
+    sf_tdm_contrl(tdm);
+    sf_tdm_syncdiv(tdm);
+
+    datarx = (tdm->rx.ifl << 11) | (tdm->rx.wl << 8) | (tdm->rx.sscale <<4) 
+            | (tdm->rx.sl <<2) | (tdm->rx.lrj <<1);
+
+    datatx = (tdm->tx.ifl << 11) | (tdm->tx.wl << 8) | (tdm->tx.sscale <<4) 
+                | (tdm->tx.sl <<2) | (tdm->tx.lrj <<1);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        sf_tdm_writel(tdm, TDM_PCMTXCR, datatx);
+    }
+    else {
+        sf_tdm_writel(tdm, TDM_PCMRXCR, datarx);
+    }
+
+    sf_tdm_start(tdm, substream);
+}
+
+
+#define sf_tdm_suspend NULL
+#define sf_tdm_resume  NULL
+
+static const struct snd_soc_component_driver sf_tdm_component = {
+       .name           = "sf-tdm",
+       .suspend        = sf_tdm_suspend,
+       .resume         = sf_tdm_resume,
+};
+
+static int sf_tdm_startup(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *cpu_dai)
+{
+       struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+       struct snd_dmaengine_dai_dma_data *dma_data = NULL;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dma_data = &dev->play_dma_data;
+       else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               dma_data = &dev->capture_dma_data;
+
+       snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
+
+       return 0;
+}
+
+static void sf_tdm_shutdown(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+
+static int sf_tdm_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(dai);
+       u32 value;
+       int ret;
+    int chan_wl, chan_sl, chan_nr;
+       u32 sample_rate;
+
+       switch (params_format(params)) {
+               case SNDRV_PCM_FORMAT_S8:
+                               chan_wl = TDM_8BIT_WORD_LEN;
+                               chan_sl = TDM_8BIT_SLOT_LEN;
+                       break;
+               
+               case SNDRV_PCM_FORMAT_S16_LE:
+                               chan_wl = TDM_16BIT_WORD_LEN;
+                               chan_sl = TDM_16BIT_SLOT_LEN;
+                       break;
+
+               case SNDRV_PCM_FORMAT_S24_LE:
+                               chan_wl = TDM_24BIT_WORD_LEN;
+                               chan_sl = TDM_32BIT_SLOT_LEN;
+                       break;
+
+               case SNDRV_PCM_FORMAT_S32_LE:
+                               chan_wl = TDM_32BIT_WORD_LEN;
+                               chan_sl = TDM_32BIT_SLOT_LEN;
+                       break;
+
+               default:
+                       dev_err(dev->dev, "tdm: unsupported PCM fmt");
+                       return -EINVAL;
+       }
+
+       chan_nr = params_channels(params);
+       switch (chan_nr) {
+               case TWO_CHANNEL_SUPPORT:
+               case FOUR_CHANNEL_SUPPORT:
+               case SIX_CHANNEL_SUPPORT:
+               case EIGHT_CHANNEL_SUPPORT:
+                       break;
+
+               default:
+                       dev_err(dev->dev, "channel not supported\n");
+                       return -EINVAL;
+       }
+       
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               dev->tx.wl = chan_wl;
+               dev->tx.sl = chan_sl;
+               dev->tx.sscale = chan_nr;
+       }
+       else
+       {
+               dev->rx.wl = chan_wl;
+               dev->rx.sl = chan_sl;
+               dev->rx.sscale = chan_nr;
+       }
+
+       dev->samplerate = params_rate(params);
+       if (!dev->mode) {
+               sf_tdm_syncdiv(dev);
+       }
+       
+       sf_tdm_config(dev, substream);
+
+       return 0;
+}
+
+
+static int sf_tdm_prepare(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
+{
+       struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+       return 0;
+}
+
+
+static int sf_tdm_trigger(struct snd_pcm_substream *substream,
+               int cmd, struct snd_soc_dai *dai)
+{
+       struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(dai);
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               dev->active++;
+               sf_tdm_start(dev, substream);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               dev->active--;
+               sf_tdm_stop(dev, substream);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int sf_tdm_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+       int ret = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               dev->mode = TDM_AS_SLAVE;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               dev->mode = TDM_AS_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+       case SND_SOC_DAIFMT_CBS_CFM:
+               ret = -EINVAL;
+               break;
+       default:
+               dev_dbg(dev->dev, "tdm : Invalid master/slave format\n");
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+
+static const struct snd_soc_dai_ops sf_tdm_dai_ops = {
+       .startup        = sf_tdm_startup,
+       .shutdown       = sf_tdm_shutdown,
+       .hw_params      = sf_tdm_hw_params,
+       .prepare        = sf_tdm_prepare,
+       .trigger        = sf_tdm_trigger,
+       .set_fmt        = sf_tdm_set_fmt,
+};
+
+static int tdm_configure_dai(struct sf_tdm_dev *dev,
+                                  struct snd_soc_dai_driver *sf_tdm_dai,
+                                  unsigned int rates)
+{
+       sf_tdm_dai->playback.channels_min = TDM_MIN_CHANNEL_NUM;
+       sf_tdm_dai->playback.channels_max = TDM_MAX_CHANNEL_NUM;
+       sf_tdm_dai->playback.formats = SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE;
+       sf_tdm_dai->playback.rates = rates;
+
+       sf_tdm_dai->capture.channels_min = TDM_MIN_CHANNEL_NUM;
+       sf_tdm_dai->capture.channels_max = TDM_MAX_CHANNEL_NUM;
+       sf_tdm_dai->capture.formats = SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE;
+       sf_tdm_dai->capture.rates = rates;
+
+       return 0;
+}
+
+static int sf_tdm_dai_probe(struct snd_soc_dai *dai)
+{
+       struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+       snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data);
+       return 0;
+}
+
+static int sf_tdm_probe(struct platform_device *pdev)
+{
+       struct sf_tdm_dev *dev;
+       struct resource *res;
+       int ret, irq;
+       struct snd_soc_dai_driver *sf_tdm_dai;
+       const char *clk_id;
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       sf_tdm_dai = devm_kzalloc(&pdev->dev, sizeof(*sf_tdm_dai), GFP_KERNEL);
+       if (!sf_tdm_dai)
+               return -ENOMEM;
+
+       sf_tdm_dai->ops = &sf_tdm_dai_ops;
+       sf_tdm_dai->probe = sf_tdm_dai_probe;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dev->tdm_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(dev->tdm_base))
+               return PTR_ERR(dev->tdm_base);
+
+       dev->dev = &pdev->dev;
+
+       ret = tdm_configure_dai(dev, sf_tdm_dai, SNDRV_PCM_RATE_8000_192000);
+       if (ret < 0)
+               return ret;
+
+       int osc_clk = 12288000;
+    u32 pcmclk = 4096000;
+       
+    dev->clkpolity     = TDM_TX_RASING_RX_FALLING;
+       dev->tritxen    = 1;
+    dev->elm           = TDM_ELM_LATE;
+    dev->syncm         = TDM_SYNCM_SHORT;
+       dev->mode               = TDM_AS_MASTER;
+    dev->rx.ifl        = TDM_FIFO_HALF;
+    dev->tx.ifl        = TDM_FIFO_HALF;
+    dev->rx.sscale     = 1;
+    dev->tx.sscale     = 1;
+    dev->rx.lrj        = TDM_LEFT_JUSTIFT;
+       dev->tx.lrj     = TDM_LEFT_JUSTIFT;
+        
+    dev->samplerate = 16000;
+    dev->pcmclk = pcmclk;
+
+       dev->play_dma_data.addr = res->start + TDM_TXDMA;
+       dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       dev->play_dma_data.fifo_size = TDM_FIFO_DEPTH/2;
+       dev->play_dma_data.maxburst = 16;
+
+       dev->capture_dma_data.addr = res->start  + TDM_RXDMA;
+       dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       dev->capture_dma_data.fifo_size = TDM_FIFO_DEPTH/2;
+       dev->capture_dma_data.maxburst = 16;
+       
+#if 0
+       if (!dev->mode) {
+               dev->clk = devm_clk_get(&pdev->dev, clk_id);
+
+               if (IS_ERR(dev->clk))
+                       return PTR_ERR(dev->clk);
+
+               ret = clk_prepare_enable(dev->clk);
+               if (ret < 0)
+                       return ret;
+       }
+#endif
+
+       dev_set_drvdata(&pdev->dev, dev);
+       ret = devm_snd_soc_register_component(&pdev->dev, &sf_tdm_component,
+                                        sf_tdm_dai, 1);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "not able to register dai\n");
+               return ret;
+       }
+       
+       ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+                       0);
+       if (ret) {
+               dev_err(&pdev->dev, "could not register pcm: %d\n",
+                               ret);
+               return ret;
+       }
+       
+       return 0;
+}
+
+
+static int sf_tdm_dev_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+static const struct of_device_id sf_tdm_of_match[] = {
+       {.compatible = "starfive,sf-tdm",}, 
+       {}
+};
+MODULE_DEVICE_TABLE(of, sf_tdm_of_match);
+
+static struct platform_driver sf_tdm_driver = {
+
+       .driver = {
+               .name = "sf-tdm",
+               .of_match_table = sf_tdm_of_match,
+       },
+       .probe = sf_tdm_probe,
+       .remove = sf_tdm_dev_remove,
+};
+module_platform_driver(sf_tdm_driver);
+
+MODULE_AUTHOR("jenny.zhang <jenny.zhang@starfivetech.com>");
+MODULE_DESCRIPTION("starfive TDM Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/starfive/starfive_tdm.h b/sound/soc/starfive/starfive_tdm.h
new file mode 100644 (file)
index 0000000..e18e9c5
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+  ******************************************************************************
+  * @file  sf_tdm.h
+  * @author  StarFive Technology
+  * @version  V1.0
+  * @date  08/2/2021
+  * @brief
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+  */
+
+#ifndef __SND_SOC_STARFIVE_TDM_H
+#define __SND_SOC_STARFIVE_TDM_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <linux/dmaengine.h>
+#include <linux/types.h>
+
+#define TDM_PCMGBCR            (0x00)
+#define TDM_PCMTXCR            (0x04)
+#define TDM_PCMRXCR            (0x08)
+#define TDM_PCMDIV             (0x0c)
+
+/*  DMA registers */
+#define TDM_RXDMA              (0xc0)
+#define TDM_TXDMA              (0xd0)
+
+#define TDM_FIFO_DEPTH         (16)
+
+#define TDM_MAX_CHANNEL_NUM    8
+#define TDM_MIN_CHANNEL_NUM    2
+
+#define TWO_CHANNEL_SUPPORT    2
+#define FOUR_CHANNEL_SUPPORT   4
+#define SIX_CHANNEL_SUPPORT    6
+#define EIGHT_CHANNEL_SUPPORT  8
+
+enum TDM_MODE
+{
+    TDM_AS_MASTER = 0,
+    TDM_AS_SLAVE,
+};
+
+enum TDM_CLKPOL {
+    /* tx raising and rx falling */
+    TDM_TX_RASING_RX_FALLING = 0,
+    /* tx raising and rx falling */
+    TDM_TX_FALLING_RX_RASING,
+};
+
+enum TDM_ELM {
+    /* only work while SYNCM=0 */
+    TDM_ELM_LATE = 0,
+    TDM_ELM_EARLY,
+};
+
+enum TDM_SYNCM {
+    /* short frame sync */
+    TDM_SYNCM_SHORT = 0,
+    /* long frame sync */
+    TDM_SYNCM_LONG,
+};
+
+enum TDM_IFL {
+    /* FIFO to send or recieve : half-1/2, Quarter-1/4*/
+    TDM_FIFO_HALF = 0,
+    TDM_FIFO_QUARTER,
+};
+
+enum TDM_WL {
+    /* send or recieve word length */
+    TDM_8BIT_WORD_LEN = 0,
+    TDM_16BIT_WORD_LEN,
+    TDM_20BIT_WORD_LEN,
+    TDM_24BIT_WORD_LEN,
+    TDM_32BIT_WORD_LEN,
+};
+
+enum TDM_SL {
+    /* send or recieve slot length */
+    TDM_8BIT_SLOT_LEN = 0,
+    TDM_16BIT_SLOT_LEN,
+    TDM_32BIT_SLOT_LEN,
+};
+
+enum TDM_LRJ {
+    /* left-justify or right-justify */
+    TDM_RIGHT_JUSTIFY = 0,
+    TDM_LEFT_JUSTIFT,
+};
+
+typedef struct tdm_chan_cfg {
+    enum TDM_IFL ifl;
+    enum TDM_WL  wl;
+    unsigned char sscale;
+    enum TDM_SL  sl;
+    enum TDM_LRJ lrj;
+    unsigned char enable;
+}tdm_chan_cfg_t;
+
+struct sf_tdm_dev {
+       void __iomem *tdm_base;
+       struct device *dev;
+       struct clk *clk;
+       int active;
+
+       enum TDM_CLKPOL clkpolity;
+       enum TDM_ELM    elm;
+       enum TDM_SYNCM  syncm;
+       enum TDM_MODE   mode;
+       unsigned char   tritxen;
+
+       tdm_chan_cfg_t tx;
+       tdm_chan_cfg_t rx;
+
+       u16 syncdiv;
+       u32 samplerate;
+       u32 pcmclk;
+
+       /* data related to DMA transfers b/w tdm and DMAC */
+       struct snd_dmaengine_dai_dma_data play_dma_data;
+       struct snd_dmaengine_dai_dma_data capture_dma_data;
+};
+
+#endif /* __SND_SOC_STARFIVE_TDM_H */