CR_1827_cannot_record_play_simultaneously
authorWalker Chen <walker.chen@starfivetech.com>
Fri, 26 Aug 2022 03:21:41 +0000 (11:21 +0800)
committerWalker Chen <walker.chen@starfivetech.com>
Fri, 26 Aug 2022 03:21:57 +0000 (11:21 +0800)
1.Resolved playback and record can work simultaneously.
2.Rearrange audio configuration in kernel menu.

Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
13 files changed:
arch/riscv/configs/starfive_jh7110_defconfig
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
include/linux/dma/starfive-dma.h [new file with mode: 0755]
sound/soc/codecs/wm8960.c
sound/soc/dwc/Kconfig [changed mode: 0644->0755]
sound/soc/dwc/Makefile [changed mode: 0644->0755]
sound/soc/starfive/Kconfig [changed mode: 0644->0755]
sound/soc/starfive/Makefile [changed mode: 0644->0755]
sound/soc/starfive/i2srx-master.c [moved from sound/soc/dwc/i2srx-master.c with 100% similarity, mode: 0755]
sound/soc/starfive/i2srx-master.h [moved from sound/soc/dwc/i2srx-master.h with 100% similarity, mode: 0755]
sound/soc/starfive/starfive_pwmdac.c
sound/soc/starfive/starfive_spdif.h
sound/soc/starfive/starfive_tdm.c [changed mode: 0644->0755]

index 3cb7ad1..770e5ef 100644 (file)
@@ -192,7 +192,6 @@ CONFIG_DRM_VERISILICON=y
 CONFIG_STARFIVE_INNO_HDMI=y
 CONFIG_STARFIVE_DSI=y
 CONFIG_DRM_IMG_ROGUE=y
-# CONFIG_DRM_IMG_NULLDISP is not set
 CONFIG_DRM_LEGACY=y
 CONFIG_FB=y
 CONFIG_BACKLIGHT_CLASS_DEVICE=y
@@ -203,12 +202,12 @@ CONFIG_SND_USB_AUDIO=y
 CONFIG_SND_SOC=y
 CONFIG_SND_DESIGNWARE_I2S=y
 CONFIG_SND_DESIGNWARE_I2S_STARFIVE_JH7110=y
-CONFIG_SND_I2S_MASTER_STARFIVE=y
-CONFIG_SND_STARFIVE_SPDIF=y
-CONFIG_SND_STARFIVE_SPDIF_PCM=y
-CONFIG_SND_STARFIVE_PWMDAC=y
-CONFIG_SND_STARFIVE_PDM=y
-CONFIG_SND_STARFIVE_TDM=y
+CONFIG_SND_SOC_STARFIVE=y
+CONFIG_SND_SOC_STARFIVE_PWMDAC=y
+CONFIG_SND_SOC_STARFIVE_PDM=y
+CONFIG_SND_SOC_STARFIVE_TDM=y
+CONFIG_SND_SOC_STARFIVE_SPDIF=y
+CONFIG_SND_SOC_STARFIVE_SPDIF_PCM=y
 CONFIG_SND_SOC_AC108=y
 CONFIG_SND_SOC_SPDIF=y
 CONFIG_SND_SOC_WM8960=y
@@ -242,7 +241,6 @@ CONFIG_DMATEST=y
 # CONFIG_VIRTIO_MENU is not set
 # CONFIG_VHOST_MENU is not set
 CONFIG_GOLDFISH=y
-CONFIG_CLK_STARFIVE_JH7110_PLL=y
 CONFIG_STARFIVE_TIMER=y
 CONFIG_MAILBOX=y
 CONFIG_STARFIVE_MBOX=m
index 29d7934..ddbd9a9 100755 (executable)
@@ -205,11 +205,9 @@ static inline void axi_chan_disable(struct axi_dma_chan *chan)
        val &= ~(BIT(chan->id) << multi->en.ch_en_shift);
        val |=   BIT(chan->id) << multi->en.ch_en_we_shift;
        axi_dma_iowrite32(chan->chip, multi->en.ch_en, val);
-       
+
        ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
-                                       !(val & chan_active), 10, 100000); //10 ms
-       if (ret == -ETIMEDOUT)
-               pr_info("dma: failed to stop\n");
+                                       !(val & chan_active), 10, 100000);
 }
 
 static inline void axi_chan_enable(struct axi_dma_chan *chan)
@@ -397,7 +395,6 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
        u8 lms = 0; /* Select AXI0 master for LLI fetching */
 
        if (unlikely(axi_chan_is_hw_enable(chan))) {
-               //printk(KERN_INFO ">>>>>>>>>>axi_chan_block_xfer_start\n");
                dev_err(chan2dev(chan), "%s is non-idle!\n",
                        axi_chan_name(chan));
                axi_chan_disable(chan);
@@ -528,7 +525,6 @@ static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
 
        /* ASSERT: channel is idle */
        if (axi_chan_is_hw_enable(chan)) {
-               printk(KERN_INFO ">>>>>>>>>>dma_chan_alloc_chan_resources\n");
                dev_err(chan2dev(chan), "%s is non-idle!\n",
                        axi_chan_name(chan));
                return -EBUSY;
@@ -556,7 +552,6 @@ static void dma_chan_free_chan_resources(struct dma_chan *dchan)
 
        /* ASSERT: channel is idle */
        if (axi_chan_is_hw_enable(chan))
-               printk(KERN_INFO ">>>>>>>>>>dma_chan_free_chan_resources\n");
                dev_err(dchan2dev(dchan), "%s is non-idle!\n",
                        axi_chan_name(chan));
 
@@ -805,8 +800,7 @@ 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))
-       {
+       if (!IS_ALIGNED(segment_len, 4)) {
                segment_len = ALIGN(segment_len, 4);
                period_len = segment_len * num_segments;
        }
@@ -1086,7 +1080,7 @@ static void axi_chan_tasklet(struct tasklet_struct *t)
        int ret;
 
        ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
-                                       !(val & chan_active), 10, 1000);
+                                       !(val & chan_active), 10, 10000);
        if (ret == -ETIMEDOUT)
                dev_warn(chan2dev(chan),
                         "irq %s failed to stop\n", axi_chan_name(chan));
@@ -1127,12 +1121,11 @@ static void axi_chan_tasklet(struct tasklet_struct *t)
 static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
 {
        unsigned long flags;
-       
+
        spin_lock_irqsave(&chan->vc.lock, flags);
 
-       if (unlikely(axi_chan_is_hw_enable(chan))) {
+       if (unlikely(axi_chan_is_hw_enable(chan)))
                axi_chan_disable(chan);
-       }
 
        tasklet_schedule(&chan->dma_tasklet);
        spin_unlock_irqrestore(&chan->vc.lock, flags);
@@ -1150,7 +1143,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);
        }
@@ -1355,6 +1349,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);
@@ -1530,13 +1539,13 @@ static int dw_probe(struct platform_device *pdev)
        chip->rst_core = devm_reset_control_get_exclusive(&pdev->dev, "rst_axi");
        if (IS_ERR(chip->rst_core)) {
                dev_err(&pdev->dev, "%s: failed to get rst_core reset control\n", __func__);
-                return PTR_ERR(chip->rst_core);
-   }
+               return PTR_ERR(chip->rst_core);
+       }
        chip->rst_cfgr = devm_reset_control_get_exclusive(&pdev->dev, "rst_ahb");
        if (IS_ERR(chip->rst_cfgr)) {
                dev_err(&pdev->dev, "%s: failed to get rst_cfgr reset control\n", __func__);
-                return PTR_ERR(chip->rst_cfgr);
-    }
+               return PTR_ERR(chip->rst_cfgr);
+       }
 
        reset_control_deassert(chip->rst_core);
        reset_control_deassert(chip->rst_cfgr);
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 c18f55e..5081039 100755 (executable)
@@ -880,28 +880,26 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
                snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x7 << 6, j << 6);
                snd_soc_component_update_bits(component, WM8960_CLOCK2, 0xf, k);
 
-               snd_soc_component_write(component, WM8960_POWER3, 0x3c);
-
                if (tx) {
                        snd_soc_component_write(component, WM8960_LOUTMIX, 0x100);
                        snd_soc_component_write(component, WM8960_ROUTMIX, 0x100);
-                       snd_soc_component_write(component, WM8960_POWER3, 0xc);
+                       snd_soc_component_update_bits(component, WM8960_POWER3, 0xc, 0xc);
                        snd_soc_component_write(component, WM8960_POWER2, 0x1f9);
                        snd_soc_component_write(component, WM8960_POWER2, 0x1f9);
                        snd_soc_component_write(component, WM8960_IFACE1, 0x3);
                        snd_soc_component_write(component, WM8960_IFACE1, 0x43 | word_length);
                        snd_soc_component_write(component, WM8960_POWER1, 0xd6);
-                       snd_soc_component_write(component, WM8960_POWER1, 0xc6);
+                       snd_soc_component_write(component, WM8960_POWER1, 0xfe);
                        snd_soc_component_write(component, WM8960_ADDCTL2, 0x4);
                } else {
-                       snd_soc_component_write(component, WM8960_POWER3, 0x30);
+                       snd_soc_component_update_bits(component, WM8960_POWER3, 0x30, 0x30);
                        snd_soc_component_write(component, WM8960_POWER1, 0xfe);
                        snd_soc_component_write(component, WM8960_POWER1, 0xfe);
-                       snd_soc_component_write(component, WM8960_POWER3, 0x30);
-                       snd_soc_component_write(component, WM8960_POWER3, 0x30);
+                       snd_soc_component_update_bits(component, WM8960_POWER3, 0x30, 0x30);
+                       snd_soc_component_update_bits(component, WM8960_POWER3, 0x30, 0x30);
                        snd_soc_component_write(component, WM8960_POWER1, 0xfe);
                        snd_soc_component_write(component, WM8960_POWER1, 0xfe);
-                       snd_soc_component_write(component, WM8960_ADDCTL2, 0x0);
+                       snd_soc_component_write(component, WM8960_ADDCTL2, 0x4);
                        snd_soc_component_write(component, WM8960_IFACE1, 0x3);
                        snd_soc_component_write(component, WM8960_IFACE1, 0x43 | word_length);
                        snd_soc_component_write(component, WM8960_POWER1, 0xfe);
@@ -1405,11 +1403,6 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
 }
 
 #define WM8960_RATES SNDRV_PCM_RATE_8000_48000
-/*
-#define WM8960_FORMATS \
-       (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-*/
 
 #define WM8960_FORMATS \
        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
old mode 100644 (file)
new mode 100755 (executable)
index d957fbd..6b249e1
@@ -26,13 +26,3 @@ config SND_DESIGNWARE_I2S_STARFIVE_JH7110
 
         This functionality is specially suited for I2S devices that run on
         Starfive JH7110 SOC platform.
-
-config SND_I2S_MASTER_STARFIVE
-       tristate "I2S_RX master support on StarFive JH7110 SoC platforms"
-       depends on HAVE_CLK
-       depends on SOC_STARFIVE
-       select SND_SOC_GENERIC_DMAENGINE_PCM
-       help
-         Say 'Y' or 'M' here if you are building for StarFive SoCs
-         platforms that contain i2srx_3ch controller core.
-
old mode 100644 (file)
new mode 100755 (executable)
index 43be7b6..22526b3
@@ -5,6 +5,3 @@ obj-$(CONFIG_SND_DESIGNWARE_I2S) += snd-soc-dwc-i2s.o
 
 snd-soc-dwc-pcm-objs := dwc-pcm.o
 obj-$(CONFIG_SND_DESIGNWARE_PCM) += snd-soc-dwc-pcm.o
-
-snd-soc-i2srx-master-objs := i2srx-master.o
-obj-$(CONFIG_SND_I2S_MASTER_STARFIVE) += snd-soc-i2srx-master.o
old mode 100644 (file)
new mode 100755 (executable)
index 1811394..cbeae68
@@ -1,40 +1,62 @@
-config SND_STARFIVE_SPDIF
-       tristate "starfive spdif"
-       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_STARFIVE_SPDIF_PCM
-       bool "PCM PIO extension for spdif driver"
-       depends on SND_STARFIVE_SPDIF
+# 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 N if you want to add a custom ALSA extension that registers
-        a PCM and uses PIO to transfer data.
-        
-config SND_STARFIVE_PWMDAC
-       tristate "starfive pwmdac Device Driver"
+         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_STARFIVE_PWMDAC_PCM
-       bool "PCM PIO extension for pwmdac driver"
-       depends on SND_STARFIVE_PWMDAC
+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_STARFIVE_PDM
-       tristate "starfive pdm Device Driver"
+config SND_SOC_STARFIVE_I2S_MASTER
+       tristate "Starfive I2S_Rx Master 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 i2srx_3ch controller core.
+
+config SND_SOC_STARFIVE_PDM
+       tristate "Starfive PDM module"
+       depends on HAVE_CLK && SND_SOC_STARFIVE
+       select SND_SOC_STARFIVE_I2S_MASTER
        select REGMAP_MMIO
        help
         Say Y or M if you want to add support for sf pdm driver.
         
-config SND_STARFIVE_TDM
-       tristate "starfive tdm Device 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 sf tdm driver.
\ No newline at end of file
+         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. 
old mode 100644 (file)
new mode 100755 (executable)
index 798208a..8cef7c6
@@ -1,13 +1,14 @@
 # starfive Platform Support
-obj-$(CONFIG_SND_STARFIVE_SPDIF) += spdif.o
+obj-$(CONFIG_SND_SOC_STARFIVE_PWMDAC) += pwmdac.o
+pwmdac-y := starfive_pwmdac.o starfive_pwmdac_transmitter.o
+pwmdac-$(CONFIG_SND_SOC_STARFIVE_PWMDAC_PCM) += starfive_pwmdac_pcm.o
 
-spdif-y := starfive_spdif.o
-spdif-$(CONFIG_SND_STARFIVE_SPDIF_PCM) += starfive_spdif_pcm.o
+snd-soc-i2srx-master-objs := i2srx-master.o
+obj-$(CONFIG_SND_SOC_STARFIVE_I2S_MASTER) += snd-soc-i2srx-master.o
 
-obj-$(CONFIG_SND_STARFIVE_PWMDAC) += pwmdac.o
+obj-$(CONFIG_SND_SOC_STARFIVE_PDM) += starfive_pdm.o
+obj-$(CONFIG_SND_SOC_STARFIVE_TDM) += starfive_tdm.o
 
-pwmdac-y := starfive_pwmdac.o starfive_pwmdac_transmitter.o
-pwmdac-$(CONFIG_SND_STARFIVE_PWMDAC_PCM) += starfive_pwmdac_pcm.o
-
-obj-$(CONFIG_SND_STARFIVE_PDM) += starfive_pdm.o
-obj-$(CONFIG_SND_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
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from sound/soc/dwc/i2srx-master.c
rename to sound/soc/starfive/i2srx-master.c
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from sound/soc/dwc/i2srx-master.h
rename to sound/soc/starfive/i2srx-master.h
index f4980c4..4f6325a 100755 (executable)
 #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 <linux/kthread.h>
-#include <linux/reset.h>
 #include "pwmdac.h"
 
 struct ct_pwmdac {
@@ -523,6 +524,7 @@ 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);
+       struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
        int ret = 0;
        
        switch (cmd) {
@@ -545,6 +547,7 @@ static int sf_pwmdac_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                dev->active--;
+               axi_dma_cyclic_stop(chan);
                pwmdac_stop(dev);
                if (dev->use_pio) {
                        if (dev->tx_thread) {  
index 9120a17..2a86fde 100755 (executable)
@@ -158,7 +158,7 @@ struct sf_spdif_dev {
        struct snd_dmaengine_dai_dma_data dma_data;
 };
 
-#if IS_ENABLED(CONFIG_SND_STARFIVE_SPDIF_PCM)
+#if IS_ENABLED(CONFIG_SND_SOC_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);
old mode 100644 (file)
new mode 100755 (executable)
index 4542d2b..6a154b6
@@ -11,6 +11,7 @@
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/regmap.h>
+#include <linux/dma/starfive-dma.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
 #include <sound/pcm_params.h>
@@ -48,7 +49,6 @@ static void sf_tdm_start(struct sf_tdm_dev *dev, struct snd_pcm_substream *subst
 static void sf_tdm_stop(struct sf_tdm_dev *dev, struct snd_pcm_substream *substream)
 {
        unsigned int val;
-       unsigned int bcr;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                val = sf_tdm_readl(dev, TDM_PCMTXCR);
@@ -59,9 +59,6 @@ static void sf_tdm_stop(struct sf_tdm_dev *dev, struct snd_pcm_substream *substr
                val &= ~PCMRXCR_RXEN;
                sf_tdm_writel(dev, TDM_PCMRXCR, val);
        }
-
-       bcr = sf_tdm_readl(dev, TDM_PCMGBCR);
-       sf_tdm_writel(dev, TDM_PCMGBCR, bcr & PCMGBCR_MASK);
 }
 
 static int sf_tdm_syncdiv(struct sf_tdm_dev *dev)
@@ -120,10 +117,40 @@ static void sf_tdm_config(struct sf_tdm_dev *dev, struct snd_pcm_substream *subs
 #define sf_tdm_suspend NULL
 #define sf_tdm_resume  NULL
 
+/* 
+ * To stop dma first, we must implement this function, because it is
+ * called before stopping the stream. 
+ */
+static int sf_pcm_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_tdm_component = {
        .name           = "sf-tdm",
        .suspend        = sf_tdm_suspend,
        .resume         = sf_tdm_resume,
+       .trigger        = sf_pcm_trigger,
 };
 
 static int sf_tdm_hw_params(struct snd_pcm_substream *substream,
@@ -131,12 +158,16 @@ static int sf_tdm_hw_params(struct snd_pcm_substream *substream,
 {
        struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(dai);
        int chan_wl, chan_sl, chan_nr;
-       struct snd_dmaengine_dai_dma_data *dma_data = NULL;
        unsigned int data_width;
        unsigned int mclk_rate;
        unsigned int dma_bus_width;
        int channels;
        int ret;
+       struct snd_dmaengine_dai_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;
 
        channels = params_channels(params);
        data_width = params_width(params);
@@ -358,6 +389,25 @@ static struct snd_soc_dai_driver sf_tdm_dai = {
        .symmetric_rate = 1,
 };
 
+static const struct snd_pcm_hardware jh71xx_pcm_hardware = {
+       .info                   = (SNDRV_PCM_INFO_MMAP          |
+                                  SNDRV_PCM_INFO_MMAP_VALID    |
+                                  SNDRV_PCM_INFO_INTERLEAVED   |
+                                  SNDRV_PCM_INFO_BLOCK_TRANSFER),
+       .buffer_bytes_max       = 192512,
+       .period_bytes_min       = 4096,
+       .period_bytes_max       = 32768,
+       .periods_min            = 1,
+       .periods_max            = 48,
+       .fifo_size              = 16,
+};
+
+static const struct snd_dmaengine_pcm_config jh71xx_dmaengine_pcm_config = {
+       .pcm_hardware = &jh71xx_pcm_hardware,
+       .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+       .prealloc_buffer_size = 192512,
+};
+
 static void tdm_init_params(struct sf_tdm_dev *dev)
 {
        dev->clkpolity = TDM_TX_RASING_RX_FALLING;
@@ -534,7 +584,9 @@ static int sf_tdm_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+       ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+                                       &jh71xx_dmaengine_pcm_config,
+                                       SND_DMAENGINE_PCM_FLAG_COMPAT);
        if (ret) {
                dev_err(&pdev->dev, "could not register pcm: %d\n", ret);
                return ret;
@@ -565,5 +617,5 @@ static struct platform_driver sf_tdm_driver = {
 module_platform_driver(sf_tdm_driver);
 
 MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
-MODULE_DESCRIPTION("starfive TDM Controller Driver");
+MODULE_DESCRIPTION("Starfive TDM Controller Driver");
 MODULE_LICENSE("GPL v2");