diff options
Diffstat (limited to 'sound/soc/fsl/imx-pcm-dma-v2.c')
-rw-r--r-- | sound/soc/fsl/imx-pcm-dma-v2.c | 70 |
1 files changed, 63 insertions, 7 deletions
diff --git a/sound/soc/fsl/imx-pcm-dma-v2.c b/sound/soc/fsl/imx-pcm-dma-v2.c index 4479247f0adf..8aa3cd8b2c2f 100644 --- a/sound/soc/fsl/imx-pcm-dma-v2.c +++ b/sound/soc/fsl/imx-pcm-dma-v2.c @@ -22,13 +22,11 @@ #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; } |