summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-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 */