From 4ae3554bf7548d6068c1c3d2c93fd674a01d7880 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Mon, 9 Oct 2023 23:25:46 +0800 Subject: [PATCH] ASoC: rockchip: Adds support DLP for Multi-DAIs This patch add support DMA-based digital loopback for Multi-DAIs. Ref: commit 9975bc50f325 ("ASoC: rockchip: Add support for Digital Loopback") Signed-off-by: Sugar Zhang Change-Id: I2861632eb28b4c5f596cfa5af855676c6140d1e1 --- sound/soc/rockchip/Kconfig | 1 + sound/soc/rockchip/rockchip_multi_dais_pcm.c | 238 ++++++++++++++++--- 2 files changed, 211 insertions(+), 28 deletions(-) diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 7e5e2f22a806..e1b10483ae6f 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -47,6 +47,7 @@ config SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES config SND_SOC_ROCKCHIP_MULTI_DAIS tristate "Rockchip Multi-DAIS Device Driver" depends on HAVE_CLK && SND_SOC_ROCKCHIP + select SND_SOC_ROCKCHIP_DLP help Say Y or M if you want to add support for Multi-dais driver for Rockchip. diff --git a/sound/soc/rockchip/rockchip_multi_dais_pcm.c b/sound/soc/rockchip/rockchip_multi_dais_pcm.c index a3e9b59da49c..d15875e7bf02 100644 --- a/sound/soc/rockchip/rockchip_multi_dais_pcm.c +++ b/sound/soc/rockchip/rockchip_multi_dais_pcm.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * ALSA SoC Audio Layer - Rockchip Multi-DAIS-PCM driver * @@ -16,22 +16,34 @@ #include #include "rockchip_multi_dais.h" +#include "rockchip_dlp.h" -#define MAX_FIFO_SIZE 32 /* max fifo size in frames */ -#define SND_DMAENGINE_MPCM_DRV_NAME "snd_dmaengine_mpcm" +#define I2S_TXFIFOLR 0xc +#define I2S_RXFIFOLR 0x2c + +/* XFL4 is compatible for old version */ +#define I2S_FIFOLR_XFL4(v) (((v) & GENMASK(29, 24)) >> 24) +#define I2S_FIFOLR_XFL3(v) (((v) & GENMASK(23, 18)) >> 18) +#define I2S_FIFOLR_XFL2(v) (((v) & GENMASK(17, 12)) >> 12) +#define I2S_FIFOLR_XFL1(v) (((v) & GENMASK(11, 6)) >> 6) +#define I2S_FIFOLR_XFL0(v) (((v) & GENMASK(5, 0)) >> 0) + +#define MAX_FIFO_SIZE 32 /* max fifo size in frames */ +#define SND_DMAENGINE_MPCM_DRV_NAME "snd_dmaengine_mpcm" static unsigned int prealloc_buffer_size_kbytes = 512; module_param(prealloc_buffer_size_kbytes, uint, 0444); MODULE_PARM_DESC(prealloc_buffer_size_kbytes, "Preallocate DMA buffer size (KB)."); struct dmaengine_mpcm { + struct dlp dlp; struct rk_mdais_dev *mdais; struct dma_chan *tx_chans[MAX_DAIS]; struct dma_chan *rx_chans[MAX_DAIS]; - struct snd_soc_component component; }; struct dmaengine_mpcm_runtime_data { + struct dlp_runtime_data drd; struct dma_chan *chans[MAX_DAIS]; struct dma_interleaved_template *xt; dma_cookie_t cookies[MAX_DAIS]; @@ -49,12 +61,17 @@ struct dmaengine_mpcm_runtime_data { static inline struct dmaengine_mpcm_runtime_data *substream_to_prtd( const struct snd_pcm_substream *substream) { - return substream->runtime->private_data; + struct dlp_runtime_data *drd = substream_to_drd(substream); + + if (!drd) + return NULL; + + return container_of(drd, struct dmaengine_mpcm_runtime_data, drd); } static struct dmaengine_mpcm *soc_component_to_mpcm(struct snd_soc_component *p) { - return container_of(p, struct dmaengine_mpcm, component); + return container_of(soc_component_to_dlp(p), struct dmaengine_mpcm, dlp); } static struct dma_chan *to_chan(struct dmaengine_mpcm *pcm, @@ -104,7 +121,20 @@ static void dmaengine_mpcm_dma_complete(void *arg) { struct snd_pcm_substream *substream = arg; #ifdef CONFIG_SND_SOC_ROCKCHIP_VAD - struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); + struct dmaengine_mpcm_runtime_data *prtd; +#endif + struct dlp_runtime_data *drd; + struct dlp *dlp; + + snd_pcm_stream_lock_irq(substream); + if (!substream->runtime) { + snd_pcm_stream_unlock_irq(substream); + return; + } +#ifdef CONFIG_SND_SOC_ROCKCHIP_VAD + prtd = substream_to_prtd(substream); + if (unlikely(!prtd)) + return; if (snd_pcm_vad_attached(substream) && substream->stream == SNDRV_PCM_STREAM_CAPTURE) { @@ -119,6 +149,12 @@ static void dmaengine_mpcm_dma_complete(void *arg) prtd->pos = 0; #endif + drd = substream_to_drd(substream); + dlp = drd->parent; + + dlp_dma_complete(dlp, drd); + snd_pcm_stream_unlock_irq(substream); + snd_pcm_period_elapsed(substream); } @@ -126,6 +162,9 @@ static void dmaengine_mpcm_get_master_chan(struct dmaengine_mpcm_runtime_data *p { int i; + if (unlikely(!prtd)) + return; + for (i = prtd->num_chans; i > 0; i--) { if (prtd->chans[i - 1]) { prtd->master_chan = i - 1; @@ -176,18 +215,23 @@ static int dmaengine_mpcm_prepare_and_submit(struct snd_pcm_substream *substream struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct dma_async_tx_descriptor *desc = NULL; - struct dma_interleaved_template *xt = prtd->xt; + struct dma_interleaved_template *xt; unsigned long flags = DMA_CTRL_ACK; - unsigned int *maps = prtd->channel_maps; + unsigned int *maps; int offset; int i; + if (unlikely(!prtd || !runtime)) + return -EINVAL; + if (!substream->runtime->no_period_wakeup) flags |= DMA_PREP_INTERRUPT; prtd->pos = 0; offset = 0; + xt = prtd->xt; + maps = prtd->channel_maps; for (i = 0; i < prtd->num_chans; i++) { if (!prtd->chans[i]) continue; @@ -222,6 +266,9 @@ static void mpcm_dma_async_issue_pending(struct dmaengine_mpcm_runtime_data *prt { int i; + if (unlikely(!prtd)) + return; + for (i = 0; i < prtd->num_chans; i++) { if (prtd->chans[i]) dma_async_issue_pending(prtd->chans[i]); @@ -232,6 +279,9 @@ static void mpcm_dmaengine_resume(struct dmaengine_mpcm_runtime_data *prtd) { int i; + if (unlikely(!prtd)) + return; + for (i = 0; i < prtd->num_chans; i++) { if (prtd->chans[i]) dmaengine_resume(prtd->chans[i]); @@ -242,6 +292,9 @@ static void mpcm_dmaengine_pause(struct dmaengine_mpcm_runtime_data *prtd) { int i; + if (unlikely(!prtd)) + return; + for (i = 0; i < prtd->num_chans; i++) { if (prtd->chans[i]) dmaengine_pause(prtd->chans[i]); @@ -252,6 +305,9 @@ static void mpcm_dmaengine_terminate_all(struct dmaengine_mpcm_runtime_data *prt { int i; + if (unlikely(!prtd)) + return; + for (i = 0; i < prtd->num_chans; i++) { if (prtd->chans[i]) dmaengine_terminate_all(prtd->chans[i]); @@ -266,6 +322,9 @@ static void dmaengine_mpcm_single_dma_complete(void *arg) unsigned int pos, size; void *buf; + if (unlikely(!prtd)) + return; + if (snd_pcm_vad_attached(substream) && substream->stream == SNDRV_PCM_STREAM_CAPTURE) { buf = substream->runtime->dma_area + prtd->vpos; @@ -289,18 +348,23 @@ static int __mpcm_prepare_single_and_submit(struct snd_pcm_substream *substream, int buf_offset, int size) { struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); - struct dma_interleaved_template *xt = prtd->xt; + struct dma_interleaved_template *xt; struct snd_pcm_runtime *runtime = substream->runtime; struct dma_async_tx_descriptor *desc; unsigned long flags = DMA_CTRL_ACK; - unsigned int *maps = prtd->channel_maps; + unsigned int *maps; int offset, i; bool callback = false; + if (unlikely(!prtd || !runtime)) + return -EINVAL; + if (!substream->runtime->no_period_wakeup) flags |= DMA_PREP_INTERRUPT; offset = buf_offset; + xt = prtd->xt; + maps = prtd->channel_maps; for (i = 0; i < prtd->num_chans; i++) { if (!prtd->chans[i]) continue; @@ -336,6 +400,9 @@ static int dmaengine_mpcm_prepare_single_and_submit(struct snd_pcm_substream *su int offset, i, count, ret; int buffer_bytes, period_bytes, residue_bytes; + if (unlikely(!prtd)) + return -EINVAL; + direction = snd_pcm_substream_to_dma_direction(substream); if (!substream->runtime->no_period_wakeup) @@ -372,13 +439,51 @@ static int dmaengine_mpcm_prepare_single_and_submit(struct snd_pcm_substream *su } #endif -static int snd_dmaengine_mpcm_trigger(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int cmd) +static snd_pcm_uframes_t dmaengine_mpcm_raw_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_tx_state state; + unsigned int buf_size; + unsigned int pos = 0; + unsigned int master; + + if (unlikely(!prtd)) + return 0; + + master = prtd->master_chan; + buf_size = snd_pcm_lib_buffer_bytes(substream); + dmaengine_tx_status(prtd->chans[master], prtd->cookies[master], &state); + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + + return bytes_to_frames(substream->runtime, pos); +} + +static int dmaengine_mpcm_dlp_start(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct dmaengine_mpcm *pcm = soc_component_to_mpcm(component); + + return dlp_start(component, substream, pcm->mdais->dev, dmaengine_mpcm_raw_pointer); +} + +static void dmaengine_mpcm_dlp_stop(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + dlp_stop(component, substream, dmaengine_mpcm_raw_pointer); +} + +static int dmaengine_mpcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; int ret; + if (unlikely(!prtd || !runtime)) + return -EINVAL; + switch (cmd) { case SNDRV_PCM_TRIGGER_START: #ifdef CONFIG_SND_SOC_ROCKCHIP_VAD @@ -393,16 +498,19 @@ static int snd_dmaengine_mpcm_trigger(struct snd_soc_component *component, if (ret) return ret; mpcm_dma_async_issue_pending(prtd); + dmaengine_mpcm_dlp_start(component, substream); break; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: mpcm_dmaengine_resume(prtd); break; case SNDRV_PCM_TRIGGER_SUSPEND: - if (runtime->info & SNDRV_PCM_INFO_PAUSE) + if (runtime->info & SNDRV_PCM_INFO_PAUSE) { mpcm_dmaengine_pause(prtd); - else + } else { + dmaengine_mpcm_dlp_stop(component, substream); mpcm_dmaengine_terminate_all(prtd); + } prtd->start_flag = false; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -410,6 +518,7 @@ static int snd_dmaengine_mpcm_trigger(struct snd_soc_component *component, prtd->start_flag = false; break; case SNDRV_PCM_TRIGGER_STOP: + dmaengine_mpcm_dlp_stop(component, substream); mpcm_dmaengine_terminate_all(prtd); prtd->start_flag = false; break; @@ -475,6 +584,11 @@ static int dmaengine_mpcm_hw_params(struct snd_soc_component *component, if (ret) return ret; } + + ret = dlp_hw_params(component, substream, params); + if (ret) + return ret; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } @@ -500,7 +614,7 @@ static int dmaengine_mpcm_set_runtime_hwparams(struct snd_pcm_substream *substre memset(&hw, 0, sizeof(hw)); hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED; + SNDRV_PCM_INFO_INTERLEAVED; hw.periods_min = 2; hw.periods_max = UINT_MAX; hw.period_bytes_min = 256; @@ -589,7 +703,8 @@ static int dmaengine_mpcm_open(struct snd_soc_component *component, prtd->num_chans = pcm->mdais->num_dais; prtd->start_flag = false; - substream->runtime->private_data = prtd; + + dlp_open(&pcm->dlp, &prtd->drd, substream); return 0; } @@ -629,8 +744,12 @@ static snd_pcm_uframes_t dmaengine_mpcm_pointer(struct snd_soc_component *compon snd_pcm_uframes_t frames; unsigned int buf_size; unsigned int pos = 0; - unsigned int master = prtd->master_chan; + unsigned int master; + if (unlikely(!prtd || !runtime)) + return 0; + + master = prtd->master_chan; buf_size = snd_pcm_lib_buffer_bytes(substream); dmaengine_tx_status(prtd->chans[master], prtd->cookies[master], &state); if (state.residue > 0 && state.residue <= buf_size) @@ -675,25 +794,54 @@ static int dmaengine_mpcm_hw_free(struct snd_soc_component *component, static int dmaengine_mpcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { + struct dmaengine_mpcm *pcm = soc_component_to_mpcm(component); struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); + if (unlikely(!prtd)) + return -EINVAL; + + dlp_close(&pcm->dlp, &prtd->drd, substream); + kfree(prtd->xt); kfree(prtd); return 0; } +static int dmaengine_mpcm_copy_user(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void __user *buf, unsigned long bytes) +{ + return dlp_copy_user(component, substream, channel, hwoff, buf, bytes); +} + + +static int dmaengine_mpcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return dlp_prepare(component, substream); +} + +static int dmaengine_mpcm_probe(struct snd_soc_component *component) +{ + return dlp_probe(component); +} + static const struct snd_soc_component_driver dmaengine_mpcm_platform = { .name = SND_DMAENGINE_MPCM_DRV_NAME, .probe_order = SND_SOC_COMP_ORDER_LATE, + .probe = dmaengine_mpcm_probe, .pcm_construct = dmaengine_mpcm_new, .open = dmaengine_mpcm_open, .close = dmaengine_mpcm_close, .ioctl = dmaengine_mpcm_ioctl, .hw_params = dmaengine_mpcm_hw_params, .hw_free = dmaengine_mpcm_hw_free, - .trigger = snd_dmaengine_mpcm_trigger, + .prepare = dmaengine_mpcm_prepare, + .trigger = dmaengine_mpcm_trigger, .pointer = dmaengine_mpcm_pointer, + .copy_user = dmaengine_mpcm_copy_user, }; static void dmaengine_mpcm_release_chan(struct dmaengine_mpcm *pcm) @@ -708,6 +856,48 @@ static void dmaengine_mpcm_release_chan(struct dmaengine_mpcm *pcm) } } +static int dmaengine_mpcm_get_fifo_count(struct device *dev, + struct snd_pcm_substream *substream) +{ + struct rk_mdais_dev *mdais = dev_get_drvdata(dev); + struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); + struct snd_soc_component *component; + unsigned int tx, rx; + int val = 0; + + if (unlikely(!prtd)) + return -EINVAL; + + component = mdais->dais[prtd->master_chan].dai->component; + if (unlikely(!component)) + return -EINVAL; + + if (strstr(dev_driver_string(component->dev), "i2s")) { + /* compatible for both I2S and I2STDM controller */ + tx = snd_soc_component_read(component, I2S_TXFIFOLR); + rx = snd_soc_component_read(component, I2S_RXFIFOLR); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + val = I2S_FIFOLR_XFL3(tx) + + I2S_FIFOLR_XFL2(tx) + + I2S_FIFOLR_XFL1(tx) + + I2S_FIFOLR_XFL0(tx); + else + /* XFL4 is compatible for old version */ + val = I2S_FIFOLR_XFL4(tx) + + I2S_FIFOLR_XFL3(rx) + + I2S_FIFOLR_XFL2(rx) + + I2S_FIFOLR_XFL1(rx) + + I2S_FIFOLR_XFL0(rx); + } + + return val; +} + +static const struct snd_dlp_config dconfig = { + .get_fifo_count = dmaengine_mpcm_get_fifo_count, +}; + int snd_dmaengine_mpcm_register(struct rk_mdais_dev *mdais) { struct device *dev; @@ -725,9 +915,6 @@ int snd_dmaengine_mpcm_register(struct rk_mdais_dev *mdais) if (!pcm) return -ENOMEM; -#ifdef CONFIG_DEBUG_FS - pcm->component.debugfs_prefix = "dma"; -#endif pcm->mdais = mdais; for (i = 0; i < num; i++) { child = mdais->dais[i].dev; @@ -746,12 +933,7 @@ int snd_dmaengine_mpcm_register(struct rk_mdais_dev *mdais) } } - ret = snd_soc_component_initialize(&pcm->component, &dmaengine_mpcm_platform, - dev); - if (ret) - goto err_free_dma; - - ret = snd_soc_add_component(&pcm->component, NULL, 0); + ret = dlp_register(&pcm->dlp, dev, &dmaengine_mpcm_platform, &dconfig); if (ret) goto err_free_dma;