ASoC: rockchip: Adds support DLP for Multi-DAIs

This patch add support DMA-based digital loopback for
Multi-DAIs.

Ref: commit 9975bc50f3 ("ASoC: rockchip: Add support for Digital Loopback")

Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Change-Id: I2861632eb28b4c5f596cfa5af855676c6140d1e1
This commit is contained in:
Sugar Zhang
2023-10-09 23:25:46 +08:00
committed by Tao Huang
parent 08de812a1e
commit 4ae3554bf7
2 changed files with 211 additions and 28 deletions

View File

@@ -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.

View File

@@ -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 <sound/soc.h>
#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;