summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShengjiu Wang <shengjiu.wang@nxp.com>2020-08-25 13:45:44 +0800
committerShengjiu Wang <shengjiu.wang@nxp.com>2020-08-31 18:08:17 +0800
commitd5986dc1d41899a11bd636fe337ef7d503183683 (patch)
treed5c0b1424c48f7edff33c7ab5e7e7d3b64fa9f2b
parent07a298166a6473854adedb7341f351edb118cbe7 (diff)
MLK-24612-1: ASoC: fsl_esai: Add esai client driver
ESAI client driver is working as cpu dai in front-end. ESAI is an unity, which can't be separated to multiple device in hardware. so we need to separate it in software view. Each client device can accept data from user space indepedently. Then the data of different client is mixed together before sent to ESAI interface. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> Reviewed-by: Peng Zhang <peng.zhang_8@nxp.com>
-rw-r--r--sound/soc/fsl/Kconfig7
-rw-r--r--sound/soc/fsl/Makefile2
-rw-r--r--sound/soc/fsl/fsl_esai_client.c252
-rw-r--r--sound/soc/fsl/fsl_esai_client.h40
4 files changed, 301 insertions, 0 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index b96947d6c026..6856c350d28c 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -130,6 +130,12 @@ config SND_SOC_FSL_XCVR
help
Say Y if you want to add Audio Transceiver (XCVR) support for NXP.
+config SND_SOC_FSL_ESAI_CLIENT
+ tristate "NXP ESAI CLIENT module support"
+ select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+ help
+ Say Y if you want to add ESAI CLIENT module support for NXP.
+
config SND_SOC_FSL_UTILS
tristate
@@ -413,6 +419,7 @@ config SND_SOC_IMX_CS42888
select SND_SOC_FSL_ESAI
select SND_SOC_FSL_ASRC
select SND_SOC_FSL_UTILS
+ select SND_SOC_FSL_ESAI_CLIENT
help
SoC Audio support for i.MX boards with cs42888
Say Y if you want to add support for SoC audio on an i.MX board with
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 827d6b0923b3..5906d9f0c81f 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -30,6 +30,7 @@ snd-soc-fsl-dma-objs := fsl_dma.o
snd-soc-fsl-easrc-objs := fsl_easrc.o fsl_easrc_dma.o
snd-soc-fsl-aud2htx-objs := fsl_aud2htx.o
snd-soc-fsl-xcvr-objs := fsl_xcvr.o
+snd-soc-fsl-esai-client-objs := fsl_esai_client.o
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
@@ -53,6 +54,7 @@ obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
obj-$(CONFIG_SND_SOC_FSL_RPMSG_I2S) += snd-soc-fsl-rpmsg-i2s.o
obj-$(CONFIG_SND_SOC_FSL_AUD2HTX) += snd-soc-fsl-aud2htx.o
obj-$(CONFIG_SND_SOC_FSL_XCVR) += snd-soc-fsl-xcvr.o
+obj-$(CONFIG_SND_SOC_FSL_ESAI_CLIENT) += snd-soc-fsl-esai-client.o
# MPC5200 Platform Support
obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
diff --git a/sound/soc/fsl/fsl_esai_client.c b/sound/soc/fsl/fsl_esai_client.c
new file mode 100644
index 000000000000..7d10db183d42
--- /dev/null
+++ b/sound/soc/fsl/fsl_esai_client.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2019 NXP
+/*
+ * Just allocate memory for FE
+ *
+ */
+#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 "fsl_esai_client.h"
+#include "imx-pcm.h"
+
+static struct snd_pcm_hardware fsl_esai_client_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 = 2048, /* fix period size, for alignment in FE/BE */
+ .period_bytes_max = 2048,
+ .periods_min = 4, /* periods > 2 (pingpong buffer in BE) */
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static int fsl_esai_client_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_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct fsl_esai_client *client = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ snd_pcm_set_runtime_buffer(substream, &client->dma[tx].dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ client->dma[tx].channels = params_channels(params);
+ client->dma[tx].word_width = snd_pcm_format_physical_width(params_format(params)) / 8;
+
+ return 0;
+}
+
+static int fsl_esai_client_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_set_runtime_buffer(substream, NULL);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t fsl_esai_client_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct fsl_esai_client *client = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ return bytes_to_frames(substream->runtime, client->dma[tx].buffer_offset);
+}
+
+static int fsl_esai_client_pcm_open(struct snd_pcm_substream *substream)
+{
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &fsl_esai_client_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int fsl_esai_client_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct fsl_esai_client *client = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* These info are needed by esai mix*/
+ client->dma[tx].buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+ client->dma[tx].period_bytes = snd_pcm_lib_period_bytes(substream);
+ client->dma[tx].buffer_offset = 0;
+ client->dma[tx].active = true;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ client->dma[tx].active = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_esai_client_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 struct snd_pcm_ops fsl_esai_client_pcm_ops = {
+ .open = fsl_esai_client_pcm_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = fsl_esai_client_pcm_hw_params,
+ .hw_free = fsl_esai_client_pcm_hw_free,
+ .pointer = fsl_esai_client_pcm_pointer,
+ .mmap = fsl_esai_client_pcm_mmap,
+ .trigger = fsl_esai_client_pcm_trigger,
+};
+
+static int fsl_esai_client_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ return 0;
+}
+
+static const struct snd_soc_component_driver fsl_esai_client_pcm_platform = {
+ .name = "fsl_esai_client_pcm",
+ .ops = &fsl_esai_client_pcm_ops,
+ .pcm_new = fsl_esai_client_pcm_new,
+};
+
+static const struct snd_soc_component_driver fsl_esai_client_component = {
+ .name = "fsl-esai-client",
+};
+
+/* Fix the sample rate/format/channels, for we don't support
+ * conversion when do mixing.
+ */
+#define FSL_ESAI_CLIENT_RATES SNDRV_PCM_RATE_48000
+#define FSL_ESAI_CLIENT_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+static struct snd_soc_dai_driver fsl_esai_client_dai_template = {
+ .name = "fsl-esai-client-dai",
+ .playback = {
+ .stream_name = "CLIENT-Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_ESAI_CLIENT_RATES,
+ .formats = FSL_ESAI_CLIENT_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CLIENT-Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_ESAI_CLIENT_RATES,
+ .formats = FSL_ESAI_CLIENT_FORMATS,
+ },
+};
+
+static int fsl_esai_client_probe(struct platform_device *pdev)
+{
+ struct fsl_esai_client *client;
+ int ret;
+
+ client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+ memcpy(&client->cpu_dai_drv, &fsl_esai_client_dai_template,
+ sizeof(fsl_esai_client_dai_template));
+
+ platform_set_drvdata(pdev, client);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "fsl,client-id", &client->id);
+ if (ret < 0)
+ return ret;
+
+ if (client->id == 0) {
+ client->cpu_dai_drv.name = "fsl-client0-dai";
+ client->cpu_dai_drv.playback.stream_name = "CLIENT0-Playback";
+ client->cpu_dai_drv.capture.stream_name = "CLIENT0-Capture";
+ }
+
+ if (client->id == 1) {
+ client->cpu_dai_drv.name = "fsl-client1-dai";
+ client->cpu_dai_drv.playback.stream_name = "CLIENT1-Playback";
+ client->cpu_dai_drv.capture.stream_name = "CLIENT1-Capture";
+ }
+
+ /* alloc memory for user read data from it. rx. */
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ &pdev->dev,
+ fsl_esai_client_pcm_hardware.buffer_bytes_max,
+ &client->dma[0].dma_buffer);
+ if (ret)
+ return ret;
+
+ /* alloc memory for user write data from it. tx. */
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ &pdev->dev,
+ fsl_esai_client_pcm_hardware.buffer_bytes_max,
+ &client->dma[1].dma_buffer);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &fsl_esai_client_component, &client->cpu_dai_drv, 1);
+ if (ret < 0)
+ return ret;
+
+ return devm_snd_soc_register_component(&pdev->dev, &fsl_esai_client_pcm_platform, NULL, 0);
+}
+
+static int fsl_esai_client_remove(struct platform_device *pdev)
+{
+ struct fsl_esai_client *client = platform_get_drvdata(pdev);
+
+ snd_dma_free_pages(&client->dma[1].dma_buffer);
+ snd_dma_free_pages(&client->dma[0].dma_buffer);
+
+ return 0;
+}
+
+static const struct of_device_id fsl_esai_client_ids[] = {
+ { .compatible = "fsl,esai-client", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_esai_client_ids);
+
+static struct platform_driver fsl_esai_client_driver = {
+ .probe = fsl_esai_client_probe,
+ .remove = fsl_esai_client_remove,
+ .driver = {
+ .name = "fsl-esai-client",
+ .of_match_table = fsl_esai_client_ids,
+ },
+};
+module_platform_driver(fsl_esai_client_driver);
+
+MODULE_DESCRIPTION("client cpu dai Interface");
+MODULE_ALIAS("platform:fsl-esai-client");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_esai_client.h b/sound/soc/fsl/fsl_esai_client.h
new file mode 100644
index 000000000000..684300383b80
--- /dev/null
+++ b/sound/soc/fsl/fsl_esai_client.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __FSL_ESAI_CLIENT_H
+#define __FSL_ESAI_CLIENT_H
+
+/**
+ * fsl_esai_client_dma: esai dma client
+ * @dma_buffer: structure of dma buffer
+ * @buffer_bytes: buffer size in bytes
+ * @period_bytes: period size in bytes
+ * @period_num: period number
+ * @buffer_offset: read offset of buffer
+ * @channels: channel number
+ * @word_width: word width in bytes
+ * @active: dma transfer is active
+ */
+struct fsl_esai_client_dma {
+ struct snd_dma_buffer dma_buffer;
+ int buffer_bytes;
+ int period_bytes;
+ int period_num;
+ int buffer_offset;
+ int channels;
+ int word_width;
+ bool active;
+};
+
+/**
+ * fsl_esai_client: esai client
+ * @cpu_dai_drv: CPU DAI driver for this device
+ * @dma: dma instance for playback and capture
+ * @id: client index
+ */
+struct fsl_esai_client {
+ struct snd_soc_dai_driver cpu_dai_drv;
+ struct fsl_esai_client_dma dma[2];
+ int id;
+};
+
+#endif /* __FSL_ESAI_CLIENT_H */