From 1c450e6957e905ac9e41490b738fe31fc0beb829 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 3 Jul 2023 10:14:43 +0100 Subject: [PATCH 0547/1085] 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 --- sound/soc/dwc/dwc-i2s.c | 87 +++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 20 deletions(-) --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -208,15 +208,10 @@ 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); if (dev->use_pio || dev->is_jh7110) i2s_disable_irqs(dev, substream->stream, 8); @@ -225,23 +220,15 @@ static void i2s_stop(struct dw_i2s_dev * 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 int dw_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) +static void i2s_stop(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) { - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - - if (dev->is_jh7110) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai_link *dai_link = rtd->dai_link; - - dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; - } + i2s_clear_irqs(dev, substream->stream); - return 0; + i2s_disable_irqs(dev, substream->stream, 8); } static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) @@ -354,6 +341,62 @@ 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, I2S_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, I2S_DMACR, dmacr); + + if (dev->is_jh7110) { + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; + } + + 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) { @@ -381,9 +424,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; @@ -512,6 +558,7 @@ static int dw_i2s_dai_probe(struct snd_s static const struct snd_soc_dai_ops dw_i2s_dai_ops = { .probe = dw_i2s_dai_probe, .startup = dw_i2s_startup, + .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger,