diff options
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/fsl/Kconfig | 7 | ||||
-rw-r--r-- | sound/soc/fsl/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_esai_client.c | 252 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_esai_client.h | 40 |
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 */ |