1.Resolved playback and record can work simultaneously.
2.Rearrange audio configuration in kernel menu.
Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
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
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
# 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
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)
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);
/* 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;
/* 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));
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;
}
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));
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);
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);
}
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);
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);
--- /dev/null
+/* 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_ */
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);
}
#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)
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.
-
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
-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.
# 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
#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 {
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) {
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) {
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);
#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>
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);
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)
#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,
{
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);
.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;
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;
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");