diff options
author | Shengjiu Wang <shengjiu.wang@nxp.com> | 2020-08-25 13:46:02 +0800 |
---|---|---|
committer | Shengjiu Wang <shengjiu.wang@nxp.com> | 2020-08-31 18:08:17 +0800 |
commit | 3184f6eabe276bea8d3b7e5448bbce634efd3941 (patch) | |
tree | a474249e95467f4e21521c9178a8a51be6768915 /sound/soc/fsl | |
parent | d5986dc1d41899a11bd636fe337ef7d503183683 (diff) |
MLK-24612-2: ASoC: fsl_esai: Add esai mix driver
ESAI mixer is for mixing the data from clients. There is
a ping-pong buffer in the mixer for storing the mixed data.
The period size is same as the period size in client (unit is
sample number).
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Peng Zhang <peng.zhang_8@nxp.com>
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r-- | sound/soc/fsl/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_esai.c | 118 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_esai.h | 70 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_esai_mix.c | 353 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_esai_mix.h | 53 |
5 files changed, 527 insertions, 69 deletions
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 5906d9f0c81f..e1dd77c90d2a 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -22,7 +22,7 @@ snd-soc-fsl-sai-objs := fsl_sai.o fsl_sai_sysfs.o snd-soc-fsl-ssi-y := fsl_ssi.o snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o snd-soc-fsl-spdif-objs := fsl_spdif.o -snd-soc-fsl-esai-objs := fsl_esai.o +snd-soc-fsl-esai-objs := fsl_esai.o fsl_esai_mix.o snd-soc-fsl-dai-objs := fsl_dai.o snd-soc-fsl-micfil-objs := fsl_micfil.o snd-soc-fsl-utils-objs := fsl_utils.o diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 3bd7ac30406c..47df69f57bb0 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -14,6 +14,7 @@ #include <sound/pcm_params.h> #include "fsl_esai.h" +#include "fsl_esai_mix.h" #include "imx-pcm.h" #define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ @@ -21,70 +22,6 @@ SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE) -/** - * fsl_esai_soc_data: soc specific data - * - * @imx: for imx platform - * @reset_at_xrun: flags for enable reset operaton - * @use_edma: edma is used. - */ -struct fsl_esai_soc_data { - bool imx; - bool reset_at_xrun; - bool use_edma; -}; - -/** - * fsl_esai: ESAI private data - * - * @dma_params_rx: DMA parameters for receive channel - * @dma_params_tx: DMA parameters for transmit channel - * @pdev: platform device pointer - * @regmap: regmap handler - * @coreclk: clock source to access register - * @extalclk: esai clock source to derive HCK, SCK and FS - * @fsysclk: system clock source to derive HCK, SCK and FS - * @spbaclk: SPBA clock (optional, depending on SoC design) - * @soc: soc specific data - * @lock: spin lock between hw_reset() and trigger() - * @fifo_depth: depth of tx/rx FIFO - * @slot_width: width of each DAI slot - * @slots: number of slots - * @channels: channel num for tx or rx - * @hck_rate: clock rate of desired HCKx clock - * @sck_rate: clock rate of desired SCKx clock - * @hck_dir: the direction of HCKx pads - * @sck_div: if using PSR/PM dividers for SCKx clock - * @slave_mode: if fully using DAI slave mode - * @synchronous: if using tx/rx synchronous mode - * @name: driver name - */ -struct fsl_esai { - struct snd_dmaengine_dai_dma_data dma_params_rx; - struct snd_dmaengine_dai_dma_data dma_params_tx; - struct platform_device *pdev; - struct regmap *regmap; - struct clk *coreclk; - struct clk *extalclk; - struct clk *fsysclk; - struct clk *spbaclk; - const struct fsl_esai_soc_data *soc; - spinlock_t lock; /* Protect hw_reset and trigger */ - u32 fifo_depth; - u32 slot_width; - u32 slots; - u32 tx_mask; - u32 rx_mask; - u32 channels[2]; - u32 hck_rate[2]; - u32 sck_rate[2]; - bool hck_dir[2]; - bool sck_div[2]; - bool slave_mode[2]; - bool synchronous; - char name[32]; -}; - static struct fsl_esai_soc_data fsl_esai_vf610 = { .imx = false, .reset_at_xrun = true, @@ -567,11 +504,23 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, tx ? esai_priv->dma_params_tx.maxburst : esai_priv->dma_params_rx.maxburst); + if (esai_priv->sw_mix) + fsl_esai_mix_open(substream, &esai_priv->mix[tx]); return 0; } +static void fsl_esai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + if (esai_priv->sw_mix) + fsl_esai_mix_close(substream, &esai_priv->mix[tx]); +} + static int fsl_esai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -627,6 +576,10 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); + + if (esai_priv->sw_mix) + fsl_esai_mix_hw_params(substream, params, &esai_priv->mix[tx]); + return 0; } @@ -799,13 +752,23 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; unsigned long lock_flags; + u32 state; - esai_priv->channels[tx] = substream->runtime->channels; + if (esai_priv->sw_mix) + esai_priv->channels[tx] = esai_priv->mix[tx].channels; + else + esai_priv->channels[tx] = substream->runtime->channels; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (esai_priv->sw_mix) { + state = atomic_cmpxchg(&esai_priv->mix[tx].active, 0, 1); + if (!state) + fsl_esai_mix_trigger(substream, cmd, &esai_priv->mix[tx]); + } + spin_lock_irqsave(&esai_priv->lock, lock_flags); fsl_esai_trigger_start(esai_priv, tx); spin_unlock_irqrestore(&esai_priv->lock, lock_flags); @@ -813,6 +776,12 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (esai_priv->sw_mix) { + state = atomic_cmpxchg(&esai_priv->mix[tx].active, 1, 0); + if (state) + fsl_esai_mix_trigger(substream, cmd, &esai_priv->mix[tx]); + } + spin_lock_irqsave(&esai_priv->lock, lock_flags); fsl_esai_trigger_stop(esai_priv, tx); spin_unlock_irqrestore(&esai_priv->lock, lock_flags); @@ -826,6 +795,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, static const struct snd_soc_dai_ops fsl_esai_dai_ops = { .startup = fsl_esai_startup, + .shutdown = fsl_esai_shutdown, .trigger = fsl_esai_trigger, .hw_params = fsl_esai_hw_params, .set_sysclk = fsl_esai_set_dai_sysclk, @@ -1137,15 +1107,27 @@ static int fsl_esai_probe(struct platform_device *pdev) regcache_cache_only(esai_priv->regmap, true); - ret = imx_pcm_platform_register(&pdev->dev); - if (ret) - dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); + if (of_property_read_bool(pdev->dev.of_node, "client-dais")) { + esai_priv->sw_mix = true; + ret = fsl_esai_mix_probe(&pdev->dev, &esai_priv->mix[0], &esai_priv->mix[1]); + if (ret) + dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); + } else { + ret = imx_pcm_platform_register(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); + } return ret; } static int fsl_esai_remove(struct platform_device *pdev) { + struct fsl_esai *esai_priv = platform_get_drvdata(pdev); + + if (esai_priv->sw_mix) + fsl_esai_mix_remove(&pdev->dev, &esai_priv->mix[0], &esai_priv->mix[1]); + pm_runtime_disable(&pdev->dev); return 0; diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h index f873588d9045..628d711ac214 100644 --- a/sound/soc/fsl/fsl_esai.h +++ b/sound/soc/fsl/fsl_esai.h @@ -10,6 +10,9 @@ #ifndef _FSL_ESAI_DAI_H #define _FSL_ESAI_DAI_H +#include <sound/dmaengine_pcm.h> +#include "fsl_esai_mix.h" + /* ESAI Register Map */ #define REG_ESAI_ETDR 0x00 #define REG_ESAI_ERDR 0x04 @@ -348,4 +351,71 @@ #define ESAI_RX_DIV_PSR 3 #define ESAI_RX_DIV_PM 4 #define ESAI_RX_DIV_FP 5 + +/** + * fsl_esai_soc_data: soc specific data + * + * @imx: for imx platform + * @reset_at_xrun: flags for enable reset operaton + * @use_edma: edma is used. + */ +struct fsl_esai_soc_data { + bool imx; + bool reset_at_xrun; + bool use_edma; +}; + +/** + * fsl_esai: ESAI private data + * + * @dma_params_rx: DMA parameters for receive channel + * @dma_params_tx: DMA parameters for transmit channel + * @pdev: platform device pointer + * @regmap: regmap handler + * @coreclk: clock source to access register + * @extalclk: esai clock source to derive HCK, SCK and FS + * @fsysclk: system clock source to derive HCK, SCK and FS + * @spbaclk: SPBA clock (optional, depending on SoC design) + * @soc: soc specific data + * @lock: spin lock between hw_reset() and trigger() + * @fifo_depth: depth of tx/rx FIFO + * @slot_width: width of each DAI slot + * @slots: number of slots + * @channels: channel num for tx or rx + * @hck_rate: clock rate of desired HCKx clock + * @sck_rate: clock rate of desired SCKx clock + * @hck_dir: the direction of HCKx pads + * @sck_div: if using PSR/PM dividers for SCKx clock + * @slave_mode: if fully using DAI slave mode + * @synchronous: if using tx/rx synchronous mode + * @sw_mix: enable sw mix in driver + * @name: driver name + */ +struct fsl_esai { + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; + struct platform_device *pdev; + struct regmap *regmap; + struct clk *coreclk; + struct clk *extalclk; + struct clk *fsysclk; + struct clk *spbaclk; + const struct fsl_esai_soc_data *soc; + struct fsl_esai_mix mix[2]; + spinlock_t lock; /* Protect hw_reset and trigger */ + u32 fifo_depth; + u32 slot_width; + u32 slots; + u32 tx_mask; + u32 rx_mask; + u32 channels[2]; + u32 hck_rate[2]; + u32 sck_rate[2]; + bool hck_dir[2]; + bool sck_div[2]; + bool slave_mode[2]; + bool synchronous; + bool sw_mix; + char name[32]; +}; #endif /* _FSL_ESAI_DAI_H */ diff --git a/sound/soc/fsl/fsl_esai_mix.c b/sound/soc/fsl/fsl_esai_mix.c new file mode 100644 index 000000000000..58ddd6725f96 --- /dev/null +++ b/sound/soc/fsl/fsl_esai_mix.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2019 NXP +/* + * Support mix two streams for ESAI + * + */ +#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" +#include "fsl_esai_client.h" +#include "fsl_esai.h" +#include "fsl_esai_mix.h" + +int fsl_esai_mix_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct fsl_esai_mix *mix) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_data; + struct dma_slave_config config; + int err = 0; + + mix->channels = params_channels(params); + mix->word_width = snd_pcm_format_physical_width(params_format(params)) / 8; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_data) + return 0; + + /* 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); + + return dmaengine_slave_config(mix->chan, &config); +} + +int fsl_esai_mix_open(struct snd_pcm_substream *substream, struct fsl_esai_mix *mix) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + mix->chan = dma_request_slave_channel(rtd->cpu_dai->dev, + dma_data->chan_name); + + return 0; +} + +int fsl_esai_mix_close(struct snd_pcm_substream *substream, + struct fsl_esai_mix *mix) +{ + dmaengine_synchronize(mix->chan); + dma_release_channel(mix->chan); + + return 0; +} + +static void fsl_esai_mix_buffer_from_fe_tx(struct snd_pcm_substream *substream, int size, bool elapse) +{ + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); + struct fsl_esai_mix *mix = &esai->mix[tx]; + struct fsl_esai_client *client; + struct fsl_esai_client_dma *client_dma; + struct snd_soc_dpcm *dpcm; + unsigned long flags; + int sample_offset = 0; + int channel_cnt = 0; + int i = 0, j = 0; + int dst_idx; + u16 *src16; + u16 *dst16; + + for (j = 0; j < MAX_CLIENT_NUM; j++) { + mix->fe_substream[j] = NULL; + mix->client[j] = NULL; + } + + /* Get the active client */ + spin_lock_irqsave(&rtd->card->dpcm_lock, flags); + for_each_dpcm_fe(rtd, substream->stream, dpcm) { + if (dpcm->be != rtd) + continue; + + mix->fe_substream[i] = snd_soc_dpcm_get_substream(dpcm->fe, substream->stream); + mix->client[i] = snd_soc_dai_get_drvdata(dpcm->fe->cpu_dai); + + i++; + if (i >= MAX_CLIENT_NUM) + break; + } + spin_unlock_irqrestore(&rtd->card->dpcm_lock, flags); + + /* mix->word_width == client->word_width */ + /* Mix the internal buffer */ + while (sample_offset * mix->word_width < size) { + dst16 = (u16 *)(mix->dma_buffer.area + mix->buffer_offset); + for (channel_cnt = 0; channel_cnt < mix->channels; channel_cnt++) + *dst16++ = 0; + + for (i = 0; i < mix->client_cnt; i++) { + if (!mix->client[i]) + continue; + + client = mix->client[i]; + client_dma = &client->dma[tx]; + + /* check client is active ? */ + if (client_dma->active) { + src16 = (u16 *)(client_dma->dma_buffer.area + client_dma->buffer_offset); + dst16 = (u16 *)(mix->dma_buffer.area + mix->buffer_offset); + + /* mix the data and reorder it for correct pin */ + for (j = 0; j < client_dma->channels; j++) { + dst_idx = client->id + j * mix->sdo_cnt; + dst16[dst_idx] = *src16++; + } + + client_dma->buffer_offset += client_dma->channels * client_dma->word_width; + client_dma->buffer_offset = client_dma->buffer_offset % client_dma->buffer_bytes; + } + } + + sample_offset += mix->channels; + mix->buffer_offset += mix->channels * mix->word_width; + mix->buffer_offset = mix->buffer_offset % mix->buffer_bytes; + } + + /* update the pointer of client buffer */ + for (i = 0; i < mix->client_cnt; i++) { + if (elapse && mix->fe_substream[i]) + snd_pcm_period_elapsed(mix->fe_substream[i]); + } +} + +static void fsl_esai_split_buffer_from_be_rx(struct snd_pcm_substream *substream, int size, bool elapse) +{ + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); + struct fsl_esai_mix *mix = &esai->mix[tx]; + struct fsl_esai_client *client; + struct fsl_esai_client_dma *client_dma; + struct snd_soc_dpcm *dpcm; + unsigned long flags; + int sample_offset = 0; + int i = 0, j = 0; + int src_idx; + u16 *src16; + u16 *dst16; + + for (j = 0; j < MAX_CLIENT_NUM; j++) { + mix->fe_substream[j] = NULL; + mix->client[j] = NULL; + } + /* Get the active client */ + spin_lock_irqsave(&rtd->card->dpcm_lock, flags); + for_each_dpcm_fe(rtd, substream->stream, dpcm) { + if (dpcm->be != rtd) + continue; + + mix->fe_substream[i] = snd_soc_dpcm_get_substream(dpcm->fe, substream->stream); + mix->client[i] = snd_soc_dai_get_drvdata(dpcm->fe->cpu_dai); + + i++; + if (i >= MAX_CLIENT_NUM) + break; + } + spin_unlock_irqrestore(&rtd->card->dpcm_lock, flags); + + /* mix->word_width == client->word_width */ + /* split the internal buffer */ + while (sample_offset * mix->word_width < size) { + for (i = 0; i < mix->client_cnt; i++) { + if (!mix->client[i]) + continue; + + client = mix->client[i]; + client_dma = &client->dma[tx]; + + if (client_dma->active) { + dst16 = (u16 *)(client_dma->dma_buffer.area + client_dma->buffer_offset); + src16 = (u16 *)(mix->dma_buffer.area + mix->buffer_offset); + + /* split the data to corret client*/ + for (j = 0; j < client_dma->channels; j++) { + src_idx = client->id + j * mix->sdi_cnt; + *dst16++ = src16[src_idx]; + } + + client_dma->buffer_offset += client_dma->channels * client_dma->word_width; + client_dma->buffer_offset = client_dma->buffer_offset % client_dma->buffer_bytes; + } + } + + sample_offset += mix->channels; + mix->buffer_offset += mix->channels * mix->word_width; + mix->buffer_offset = mix->buffer_offset % mix->buffer_bytes; + } + + /* update the pointer of client buffer */ + for (i = 0; i < mix->client_cnt; i++) { + if (elapse && mix->fe_substream[i]) + snd_pcm_period_elapsed(mix->fe_substream[i]); + } +} + +/* call back of dma event */ +static void fsl_esai_mix_dma_complete(void *arg) +{ + struct snd_pcm_substream *substream = arg; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct fsl_esai_mix *mix = &esai->mix[tx]; + + if (tx) + fsl_esai_mix_buffer_from_fe_tx(substream, mix->period_bytes, true); + else + fsl_esai_split_buffer_from_be_rx(substream, mix->period_bytes, true); +} + +static int fsl_esai_mix_prepare_and_submit(struct snd_pcm_substream *substream, + struct fsl_esai_mix *mix) +{ + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction direction; + unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + + direction = snd_pcm_substream_to_dma_direction(substream); + + /* ping-pong buffer for mix */ + desc = dmaengine_prep_dma_cyclic(mix->chan, + mix->dma_buffer.addr, + mix->buffer_bytes, + mix->period_bytes, + direction, flags); + if (!desc) + return -ENOMEM; + + desc->callback = fsl_esai_mix_dma_complete; + desc->callback_param = substream; + dmaengine_submit(desc); + + mix->buffer_offset = 0; + + /* Mix the tx buffer */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + fsl_esai_mix_buffer_from_fe_tx(substream, mix->buffer_bytes, false); + + return 0; +} + +int fsl_esai_mix_trigger(struct snd_pcm_substream *substream, int cmd, + struct fsl_esai_mix *mix) +{ + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = fsl_esai_mix_prepare_and_submit(substream, mix); + if (ret) + return ret; + dma_async_issue_pending(mix->chan); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + dmaengine_terminate_async(mix->chan); + break; + default: + return -EINVAL; + } + + return 0; +} + +int fsl_esai_mix_probe(struct device *dev, struct fsl_esai_mix *mix_rx, struct fsl_esai_mix *mix_tx) +{ + int ret = 0; + + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + /** + * initialize info for mixing + * two clients, TX0 pin is for client 0, TX1 pin is for client 1 + * total supported channel is 4. + */ + mix_tx->client_cnt = 2; + mix_tx->sdo_cnt = 2; + mix_tx->sdi_cnt = 2; + mix_tx->channels = 4; + mix_tx->buffer_bytes = 2048 * mix_tx->client_cnt * 2; + mix_tx->period_bytes = 2048 * mix_tx->client_cnt; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + dev, + IMX_SSI_DMABUF_SIZE * mix_tx->client_cnt, + &mix_tx->dma_buffer); + if (ret) + return ret; + + /** + * initialize info for mixing + * two clients, TX0 pin is for client 0, TX1 pin is for client 1 + * total supported channel is 4. + */ + mix_rx->client_cnt = 2; + mix_rx->sdo_cnt = 2; + mix_rx->sdi_cnt = 2; + mix_rx->channels = 4; + mix_rx->buffer_bytes = 2048 * mix_rx->client_cnt * 2; + mix_rx->period_bytes = 2048 * mix_rx->client_cnt; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + dev, + IMX_SSI_DMABUF_SIZE * mix_rx->client_cnt, + &mix_rx->dma_buffer); + if (ret) + return ret; + + return ret; +} + +int fsl_esai_mix_remove(struct device *dev, struct fsl_esai_mix *mix_rx, struct fsl_esai_mix *mix_tx) +{ + snd_dma_free_pages(&mix_tx->dma_buffer); + snd_dma_free_pages(&mix_rx->dma_buffer); + + return 0; +} +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_esai_mix.h b/sound/soc/fsl/fsl_esai_mix.h new file mode 100644 index 000000000000..7d117154b27a --- /dev/null +++ b/sound/soc/fsl/fsl_esai_mix.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _FSL_ESAI_MIX_H +#define _FSL_ESAI_MIX_H + +/* maximum client number is 4; */ +#define MAX_CLIENT_NUM 4 + +/** + * fsl_esai_mix: esai mix/split data + * @chan: dma channel + * @fe_substream: handler of front end substream + * @client: handler of client + * @dma_buffer: structure of dma buffer + * @buffer_offset: read offset of buffer + * @buffer_bytes: buffer size in bytes + * @period_bytes: period size in bytes + * @period_num: period number + * @word_width: word width in bytes + * @channels: channel number + * @client_cnt: client number, default 2. + * @sdo_cnt: output pin number of esai + * @sdi_cnt: input pin number of esai + * @active: mixer is enabled or not + */ +struct fsl_esai_mix { + struct dma_chan *chan; + struct snd_pcm_substream *fe_substream[MAX_CLIENT_NUM]; + struct fsl_esai_client *client[MAX_CLIENT_NUM]; + struct snd_dma_buffer dma_buffer; + u32 buffer_offset; + u32 buffer_bytes; + u32 period_bytes; + u32 period_num; + u32 word_width; + u32 channels; + u32 client_cnt; + u32 sdo_cnt; + u32 sdi_cnt; + atomic_t active; +}; + +int fsl_esai_mix_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct fsl_esai_mix *mix); +int fsl_esai_mix_open(struct snd_pcm_substream *substream, struct fsl_esai_mix *mix); +int fsl_esai_mix_close(struct snd_pcm_substream *substream, struct fsl_esai_mix *mix); +int fsl_esai_mix_trigger(struct snd_pcm_substream *substream, int cmd, + struct fsl_esai_mix *mix); +int fsl_esai_mix_probe(struct device *dev, struct fsl_esai_mix *mix_rx, struct fsl_esai_mix *mix_tx); +int fsl_esai_mix_remove(struct device *dev, struct fsl_esai_mix *mix_rx, struct fsl_esai_mix *mix_tx); + +#endif /* _FSL_ESAI_MIX_H */ |