summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorDong Aisheng <aisheng.dong@nxp.com>2019-12-02 18:00:39 +0800
committerDong Aisheng <aisheng.dong@nxp.com>2019-12-02 18:00:39 +0800
commitd40375dbf7f00501769e204dba88b4c30d9e4f6d (patch)
treeda6eb2969c02bb81848950f000cf32c7e25533fd /sound
parentdfc1c26529095728014535bf79680b346dbd83a5 (diff)
parent0803f9c776c8e6f2e21d8af8202b8dbb2b5bc702 (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/Makefile2
-rw-r--r--sound/soc/fsl/imx-pcm-dma-v2.c268
-rw-r--r--sound/soc/fsl/imx-pcm.h14
-rw-r--r--sound/soc/soc-pcm.c4
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;
}