diff --git a/sound/soc/rockchip/rockchip_multi_dais.c b/sound/soc/rockchip/rockchip_multi_dais.c index 532c2528efb4..d3dffb45b98b 100644 --- a/sound/soc/rockchip/rockchip_multi_dais.c +++ b/sound/soc/rockchip/rockchip_multi_dais.c @@ -25,6 +25,9 @@ #define SOUND_NAME_PREFIX "sound-name-prefix" +#define I2S_CKR 0x8 +#define IS_I2S_TRCM(v) ((v) & GENMASK(29, 28)) + static inline struct rk_mdais_dev *to_info(struct snd_soc_dai *dai) { return snd_soc_dai_get_drvdata(dai); @@ -515,7 +518,7 @@ static int rockchip_mdais_probe(struct platform_device *pdev) struct device_node *node; struct snd_soc_dai_driver *soc_dai; struct rk_dai *dais; - unsigned int *map; + unsigned int *map, val; int count, mp_count; int ret = 0, i = 0; @@ -575,6 +578,11 @@ static int rockchip_mdais_probe(struct platform_device *pdev) dais[i].dai = rockchip_mdais_find_dai(node); if (!dais[i].dai) return -EPROBE_DEFER; + + if (strstr(dev_driver_string(dais[i].dai->dev), "i2s")) { + val = snd_soc_component_read(dais[i].dai->component, I2S_CKR); + dais[i].trcm = IS_I2S_TRCM(val); + } } mdais_parse_daifmt(np, dais, count); diff --git a/sound/soc/rockchip/rockchip_multi_dais.h b/sound/soc/rockchip/rockchip_multi_dais.h index 5d9a076afb78..c0a8ef3e4845 100644 --- a/sound/soc/rockchip/rockchip_multi_dais.h +++ b/sound/soc/rockchip/rockchip_multi_dais.h @@ -17,6 +17,7 @@ struct rk_dai { struct snd_soc_dai *dai; unsigned int fmt; unsigned int fmt_msk; + bool trcm; }; struct rk_mdais_dev { diff --git a/sound/soc/rockchip/rockchip_multi_dais_pcm.c b/sound/soc/rockchip/rockchip_multi_dais_pcm.c index b83b3a2bed95..794e20e75b2b 100644 --- a/sound/soc/rockchip/rockchip_multi_dais_pcm.c +++ b/sound/soc/rockchip/rockchip_multi_dais_pcm.c @@ -18,6 +18,8 @@ #include "rockchip_multi_dais.h" #include "rockchip_dlp.h" +#define DMA_GUARD_BUFFER_SIZE 64 + #define I2S_TXFIFOLR 0xc #define I2S_RXFIFOLR 0x2c #define SAI_TXFIFOLR 0x1c @@ -43,11 +45,19 @@ 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 trcm_dma_guard { + dma_addr_t dma_addr; + unsigned char *dma_area; +}; + 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 trcm_dma_guard tx_guards[MAX_DAIS]; + struct trcm_dma_guard rx_guards[MAX_DAIS]; + bool guard; }; struct dmaengine_mpcm_runtime_data { @@ -484,6 +494,73 @@ static void dmaengine_mpcm_dlp_stop(struct snd_soc_component *component, dlp_stop(component, substream, dmaengine_mpcm_raw_pointer); } +static int dmaengine_mpcm_trcm_dma_guard_ctrl(struct snd_soc_component *component, + int stream, bool en) +{ + struct dmaengine_mpcm *pcm = soc_component_to_mpcm(component); + struct dma_chan **chans; + struct trcm_dma_guard *guards; + struct rk_dai *dais; + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction direction; + unsigned int *maps, buf_sz; + int i, ret, num; + + if (!pcm->guard) + return 0; + + dais = pcm->mdais->dais; + num = pcm->mdais->num_dais; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + chans = pcm->tx_chans; + guards = pcm->tx_guards; + direction = DMA_MEM_TO_DEV; + maps = pcm->mdais->playback_channel_maps; + } else { + chans = pcm->rx_chans; + guards = pcm->rx_guards; + direction = DMA_DEV_TO_MEM; + maps = pcm->mdais->capture_channel_maps; + } + + if (!en) { + for (i = 0; i < num; i++) { + if (!chans[i] || !dais[i].trcm || !maps[i]) + continue; + + ret = dmaengine_terminate_all(chans[i]); + if (ret) + return ret; + } + + return 0; + } + + for (i = 0; i < num; i++) { + if (!chans[i] || !dais[i].trcm || !maps[i]) + continue; + + buf_sz = DMA_GUARD_BUFFER_SIZE / (maps[i] * 4); + buf_sz = buf_sz * maps[i] * 4; + if (!buf_sz) + return -EINVAL; + + desc = dmaengine_prep_dma_cyclic(chans[i], guards[i].dma_addr, + buf_sz, buf_sz, direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -ENOMEM; + + desc->callback = NULL; + desc->callback_param = NULL; + dmaengine_submit(desc); + dma_async_issue_pending(chans[i]); + } + + return 0; +} + static int dmaengine_mpcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { @@ -504,6 +581,7 @@ static int dmaengine_mpcm_trigger(struct snd_soc_component *component, mpcm_dma_async_issue_pending(prtd); } #endif + mpcm_dmaengine_terminate_all(prtd); ret = dmaengine_mpcm_prepare_and_submit(substream); if (ret) return ret; @@ -530,6 +608,7 @@ static int dmaengine_mpcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_STOP: dmaengine_mpcm_dlp_stop(component, substream); mpcm_dmaengine_terminate_all(prtd); + dmaengine_mpcm_trcm_dma_guard_ctrl(component, substream->stream, 1); prtd->start_flag = false; break; default: @@ -725,6 +804,82 @@ static int dmaengine_mpcm_open(struct snd_soc_component *component, return 0; } +static int dmaengine_mpcm_trcm_dma_guard_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct dmaengine_mpcm *pcm = soc_component_to_mpcm(component); + struct dma_chan **chans; + struct trcm_dma_guard *guards; + struct rk_dai *dais; + struct snd_dmaengine_dai_dma_data *dma_data; + struct snd_pcm_substream *substream; + struct dma_slave_config slave_config; + struct device *dev; + dma_addr_t dma_addr; + unsigned char *dma_area; + int i, j, ret, num; + + dais = pcm->mdais->dais; + num = pcm->mdais->num_dais; + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { + substream = rtd->pcm->streams[i].substream; + if (!substream) + continue; + + dev = dmaengine_dma_dev(pcm, substream); + + chans = substream->stream ? pcm->rx_chans : pcm->tx_chans; + guards = substream->stream ? pcm->rx_guards : pcm->tx_guards; + + for (j = 0; j < num; j++) { + if (!chans[j] || !dais[j].trcm) + continue; + + pcm->guard = true; + + dma_area = dma_alloc_coherent(dev, DMA_GUARD_BUFFER_SIZE, + &dma_addr, GFP_KERNEL); + if (!dma_area) + return -ENOMEM; + + memset(dma_area, 0x0, DMA_GUARD_BUFFER_SIZE); + + guards[j].dma_addr = dma_addr; + guards[j].dma_area = dma_area; + + memset(&slave_config, 0, sizeof(slave_config)); + + dma_data = snd_soc_dai_get_dma_data(dais[j].dai, substream); + if (!dma_data) + continue; + + snd_dmaengine_mpcm_set_config_from_dai_data(substream, + dma_data, + &slave_config); + + /* + * Use the max-16w to cover all 2^n cases, maybe better + * per channels and fmt, at the moment, we use the simple + * way. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config.direction = DMA_MEM_TO_DEV; + slave_config.dst_maxburst = 16; + } else { + slave_config.direction = DMA_DEV_TO_MEM; + slave_config.src_maxburst = 16; + } + + ret = dmaengine_slave_config(chans[j], &slave_config); + if (ret) + return ret; + } + } + + return 0; +} + static int dmaengine_mpcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { struct dmaengine_mpcm *pcm = soc_component_to_mpcm(component); @@ -733,10 +888,15 @@ static int dmaengine_mpcm_new(struct snd_soc_component *component, struct snd_so size_t prealloc_buffer_size; size_t max_buffer_size; unsigned int i; + int ret; prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024; max_buffer_size = SIZE_MAX; + ret = dmaengine_mpcm_trcm_dma_guard_new(component, rtd); + if (ret) + return ret; + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { substream = rtd->pcm->streams[i].substream; if (!substream)