diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch b/target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch new file mode 100644 index 0000000000..73704a8be3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch @@ -0,0 +1,128 @@ +From e6baee4502c0228c79408b047096a1259a84353f Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 3 Jul 2023 10:14:43 +0100 +Subject: [PATCH] ASOC: dwc: Improve DMA shutdown + +Disabling the I2S interface with outstanding transfers prevents the +DMAC from shutting down, so keep it partially active after a stop. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + sound/soc/dwc/dwc-i2s.c | 72 ++++++++++++++++++++++++++++++++++++----- + 1 file changed, 64 insertions(+), 8 deletions(-) + +--- a/sound/soc/dwc/dwc-i2s.c ++++ b/sound/soc/dwc/dwc-i2s.c +@@ -165,24 +165,26 @@ static void i2s_start(struct dw_i2s_dev + i2s_write_reg(dev->i2s_base, CER, 1); + } + +-static void i2s_stop(struct dw_i2s_dev *dev, +- struct snd_pcm_substream *substream) ++static void i2s_pause(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); ++ /* Keep the device enabled until the shutdown - do not clear IER */ + } + } + ++static void i2s_stop(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) ++{ ++ i2s_clear_irqs(dev, substream->stream); ++ ++ i2s_disable_irqs(dev, substream->stream, 8); ++} ++ + static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) + { + struct i2s_clk_config_data *config = &dev->config; +@@ -288,6 +290,55 @@ static int dw_i2s_hw_params(struct snd_p + return 0; + } + ++static int dw_i2s_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *cpu_dai) ++{ ++ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); ++ union dw_i2s_snd_dma_data *dma_data = NULL; ++ u32 dmacr; ++ ++ dev_dbg(dev->dev, "%s(%s)\n", __func__, substream->name); ++ if (!(dev->capability & DWC_I2S_RECORD) && ++ substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ return -EINVAL; ++ ++ if (!(dev->capability & DWC_I2S_PLAY) && ++ substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ return -EINVAL; ++ ++ dw_i2s_config(dev, substream->stream); ++ dmacr = i2s_read_reg(dev->i2s_base, DMACR); ++ 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); ++ i2s_write_reg(dev->i2s_base, DMACR, dmacr); ++ ++ return 0; ++} ++ ++static void dw_i2s_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ dev_dbg(dev->dev, "%s(%s)\n", __func__, substream->name); ++ i2s_disable_channels(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 int dw_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { +@@ -315,9 +366,12 @@ static int dw_i2s_trigger(struct snd_pcm + i2s_start(dev, substream); + break; + ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ dev->active--; ++ i2s_pause(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; +@@ -394,6 +448,8 @@ static int dw_i2s_set_bclk_ratio(struct + + static const struct snd_soc_dai_ops dw_i2s_dai_ops = { + .hw_params = dw_i2s_hw_params, ++ .startup = dw_i2s_startup, ++ .shutdown = dw_i2s_shutdown, + .prepare = dw_i2s_prepare, + .trigger = dw_i2s_trigger, + .set_fmt = dw_i2s_set_fmt, |