MLK-16885-2: ASoC: imx-pcm-dma-v2: query the caps of dma
authorShengjiu Wang <shengjiu.wang@nxp.com>
Mon, 20 Nov 2017 08:23:21 +0000 (16:23 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 20:47:04 +0000 (15:47 -0500)
query the caps of dma, then update the hw parameters according
the caps. for EDMA can't support 24bit sample, but we didn't
add any constraint, that cause issues.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
sound/soc/fsl/imx-pcm-dma-v2.c

index 4479247..8aa3cd8 100644 (file)
 
 #include "imx-pcm.h"
 
-static const struct snd_pcm_hardware imx_pcm_hardware = {
+static struct snd_pcm_hardware imx_pcm_hardware = {
        .info = SNDRV_PCM_INFO_INTERLEAVED |
                SNDRV_PCM_INFO_BLOCK_TRANSFER |
                SNDRV_PCM_INFO_MMAP |
-               SNDRV_PCM_INFO_MMAP_VALID |
-               SNDRV_PCM_INFO_PAUSE |
-               SNDRV_PCM_INFO_RESUME,
+               SNDRV_PCM_INFO_MMAP_VALID,
        .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
        .period_bytes_min = 128,
        .period_bytes_max = 65532, /* Limited by SDMA engine */
@@ -100,9 +98,13 @@ static int imx_pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_dmaengine_dai_dma_data *dma_data;
+       struct dma_slave_caps dma_caps;
+       struct dma_chan *chan;
+       u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+                         BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+                         BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
        int ret;
-
-       snd_soc_set_runtime_hwparams(substream, &imx_pcm_hardware);
+       int i;
 
        dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 
@@ -123,12 +125,66 @@ static int imx_pcm_open(struct snd_pcm_substream *substream)
                        return ret;
        }
 
+       chan = snd_dmaengine_pcm_get_chan(substream);
+
+       ret = dma_get_slave_caps(chan, &dma_caps);
+       if (ret == 0) {
+               if (dma_caps.cmd_pause)
+                       imx_pcm_hardware.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
+               if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
+                       imx_pcm_hardware.info |= SNDRV_PCM_INFO_BATCH;
+
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       addr_widths = dma_caps.dst_addr_widths;
+               else
+                       addr_widths = dma_caps.src_addr_widths;
+       }
+
+       /*
+        * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
+        * hw.formats set to 0, meaning no restrictions are in place.
+        * In this case it's the responsibility of the DAI driver to
+        * provide the supported format information.
+        */
+       if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
+               /*
+                * Prepare formats mask for valid/allowed sample types. If the
+                * dma does not have support for the given physical word size,
+                * it needs to be masked out so user space can not use the
+                * format which produces corrupted audio.
+                * In case the dma driver does not implement the slave_caps the
+                * default assumption is that it supports 1, 2 and 4 bytes
+                * widths.
+                */
+               for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
+                       int bits = snd_pcm_format_physical_width(i);
+
+                       /*
+                        * Enable only samples with DMA supported physical
+                        * widths
+                        */
+                       switch (bits) {
+                       case 8:
+                       case 16:
+                       case 24:
+                       case 32:
+                       case 64:
+                               if (addr_widths & (1 << (bits / 8)))
+                                       imx_pcm_hardware.formats |= (1LL << i);
+                               break;
+                       default:
+                               /* Unsupported types */
+                               break;
+                       }
+               }
+
+       snd_soc_set_runtime_hwparams(substream, &imx_pcm_hardware);
+
        ret = snd_pcm_hw_constraint_integer(substream->runtime,
                                            SNDRV_PCM_HW_PARAM_PERIODS);
        if (ret < 0)
                return ret;
 
-
        return 0;
 }