diff options
author | Dong Aisheng <aisheng.dong@nxp.com> | 2019-12-02 18:00:39 +0800 |
---|---|---|
committer | Dong Aisheng <aisheng.dong@nxp.com> | 2019-12-02 18:00:39 +0800 |
commit | d40375dbf7f00501769e204dba88b4c30d9e4f6d (patch) | |
tree | da6eb2969c02bb81848950f000cf32c7e25533fd /sound | |
parent | dfc1c26529095728014535bf79680b346dbd83a5 (diff) | |
parent | 0803f9c776c8e6f2e21d8af8202b8dbb2b5bc702 (diff) |
Merge remote-tracking branch 'origin/audio/dma' into audio/next
* origin/audio/dma: (14 commits)
ASoC: imx-pcm-dma-v2: Fix writecombine/wc build error
ASoC: imx-pcm-dma-v2: Add component name
ASoC: imx: fix build failture
MLK-16224-2: ASoC: dmaengine_pcm: add fifo_num to snd_dmaengine_dai_dma_data
MLK-18947: ASoC: imx-pcm-dma: alloc buffer from IRAM
...
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/fsl/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/fsl/imx-pcm-dma-v2.c | 268 | ||||
-rw-r--r-- | sound/soc/fsl/imx-pcm.h | 14 | ||||
-rw-r--r-- | sound/soc/soc-pcm.c | 4 |
4 files changed, 285 insertions, 3 deletions
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 5914033ef834..baaf124fd86b 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -53,7 +53,7 @@ obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o -obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o +obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o imx-pcm-dma-v2.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o diff --git a/sound/soc/fsl/imx-pcm-dma-v2.c b/sound/soc/fsl/imx-pcm-dma-v2.c new file mode 100644 index 000000000000..54b08131199b --- /dev/null +++ b/sound/soc/fsl/imx-pcm-dma-v2.c @@ -0,0 +1,268 @@ +/* + * imx-pcm-dma-v2.c -- ALSA Soc Audio Layer + * + * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> + * + * This code is based on code copyrighted by Freescale, + * Liam Girdwood, Javier Martin and probably others. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/dmaengine_pcm.h> +#include <sound/soc.h> + +#include "imx-pcm.h" + +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, + .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = 65532, /* Limited by SDMA engine */ + .periods_min = 2, + .periods_max = 255, + .fifo_size = 0, +}; + +static bool imx_dma_filter_fn(struct dma_chan *chan, void *param) +{ + if (!imx_dma_is_general_purpose(chan)) + return false; + + chan->private = param; + + return true; +} + +/* this may get called several times by oss emulation */ +static int imx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_data; + struct dma_slave_config config; + struct dma_chan *chan; + int err = 0; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + /* return if this is a bufferless transfer e.g. + * codec <--> BT codec or GSM modem -- lg FIXME + */ + if (!dma_data) + return 0; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = params_buffer_bytes(params); + + chan = snd_dmaengine_pcm_get_chan(substream); + if (!chan) + return -EINVAL; + + /* fills in addr_width and direction */ + err = snd_hwparams_to_dma_slave_config(substream, params, &config); + if (err) + return err; + + snd_dmaengine_pcm_set_config_from_dai_data(substream, + dma_data, + &config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + config.dst_fifo_num = dma_data->fifo_num; + else + config.src_fifo_num = dma_data->fifo_num; + + return dmaengine_slave_config(chan, &config); +} + +static int imx_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + return 0; +} + +static snd_pcm_uframes_t imx_pcm_pointer(struct snd_pcm_substream *substream) +{ + return snd_dmaengine_pcm_pointer(substream); +} + +static void imx_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream, + struct device *dev) +{ + size_t size = imx_pcm_hardware.buffer_bytes_max; + + snd_pcm_lib_preallocate_pages(substream, + SNDRV_DMA_TYPE_DEV_IRAM, + dev, + size, + size); +} + +static void imx_pcm_free_dma_buffers(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_preallocate_free(substream); +} + +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; + int i; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + /* DT boot: filter_data is the DMA name */ + if (rtd->cpu_dai->dev->of_node) { + struct dma_chan *chan; + + chan = dma_request_slave_channel(rtd->cpu_dai->dev, + dma_data->chan_name); + ret = snd_dmaengine_pcm_open(substream, chan); + if (ret) + return ret; + } else { + ret = snd_dmaengine_pcm_open_request_chan(substream, + imx_dma_filter_fn, + dma_data->filter_data); + if (ret) + 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); + + imx_pcm_preallocate_dma_buffer(substream, chan->device->dev); + + ret = snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + return 0; +} + +static int imx_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_wc(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +static int imx_pcm_close(struct snd_pcm_substream *substream) +{ + imx_pcm_free_dma_buffers(substream); + + return snd_dmaengine_pcm_close_release_chan(substream); +} + +static struct snd_pcm_ops imx_pcm_ops = { + .open = imx_pcm_open, + .close = imx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = imx_pcm_hw_params, + .hw_free = imx_pcm_hw_free, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = imx_pcm_pointer, + .mmap = imx_pcm_mmap, +}; + +static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + return ret; +} + +static struct snd_soc_component_driver imx_soc_platform = { + .name = "imx-pcm-dma-v2", + .ops = &imx_pcm_ops, + .pcm_new = imx_pcm_new, +}; + +int imx_pcm_platform_register(struct device *dev) +{ + return devm_snd_soc_register_component(dev, &imx_soc_platform, NULL, 0); +} +EXPORT_SYMBOL_GPL(imx_pcm_platform_register); + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 5dd406774d3e..edbb268332bd 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -39,13 +39,27 @@ struct imx_pcm_fiq_params { struct snd_dmaengine_dai_dma_data *dma_params_tx; }; +#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_RPMSG) +int imx_rpmsg_platform_register(struct device *dev); +#else +static inline int imx_rpmsg_platform_register(struct device *dev) +{ + return -ENODEV; +} +#endif + #if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA) int imx_pcm_dma_init(struct platform_device *pdev, size_t size); +int imx_pcm_platform_register(struct device *dev); #else static inline int imx_pcm_dma_init(struct platform_device *pdev, size_t size) { return -ENODEV; } +static inline int imx_pcm_platform_register(struct device *dev) +{ + return -ENODEV; +} #endif #if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_FIQ) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b600d3eaaf5c..0eed248a1c4b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1295,7 +1295,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, } /* dai link name and stream name set correctly ? */ - dev_err(card->dev, "ASoC: can't get %s BE for %s\n", + dev_dbg(card->dev, "ASoC: can't get %s BE for %s\n", stream ? "capture" : "playback", widget->name); return NULL; } @@ -1445,7 +1445,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, /* is there a valid BE rtd for this widget */ be = dpcm_get_be(card, list->widgets[i], stream); if (!be) { - dev_err(fe->dev, "ASoC: no BE found for %s\n", + dev_dbg(fe->dev, "ASoC: no BE found for %s\n", list->widgets[i]->name); continue; } |