1
0
mirror of https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2026-01-12 01:20:14 +00:00

ASoC: fsl: fsl_qmc_audio: Reduce amount of

Merge series from Christophe Leroy <christophe.leroy@csgroup.eu>:

This is a RESEND of v3 sent one month ago, see:
https://lore.kernel.org/all/cover.1754993232.git.christophe.leroy@csgroup.eu/

This series reduces significantly the amount of interrupts on
fsl_qmc_audio device.

Patches 1 and 2 are preparatory patches.
Patch 3 is the main change
Patch 4 is a cleanup which is enabled by previous patch
This commit is contained in:
Mark Brown 2025-09-19 21:23:48 +01:00
commit 5998f0d07d
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 87 additions and 82 deletions

View File

@ -461,9 +461,16 @@ int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
ctrl = qmc_read16(&bd->cbd_sc);
if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) {
/* We are full ... */
ret = -EBUSY;
goto end;
if (!(ctrl & (QMC_BD_TX_R | QMC_BD_TX_I)) && bd == chan->txbd_done) {
if (ctrl & QMC_BD_TX_W)
chan->txbd_done = chan->txbds;
else
chan->txbd_done++;
} else {
/* We are full ... */
ret = -EBUSY;
goto end;
}
}
qmc_write16(&bd->cbd_datlen, length);
@ -475,6 +482,10 @@ int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
/* Activate the descriptor */
ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB);
if (complete)
ctrl |= QMC_BD_TX_I;
else
ctrl &= ~QMC_BD_TX_I;
wmb(); /* Be sure to flush the descriptor before control update */
qmc_write16(&bd->cbd_sc, ctrl);
@ -569,9 +580,16 @@ int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
ctrl = qmc_read16(&bd->cbd_sc);
if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) {
/* We are full ... */
ret = -EBUSY;
goto end;
if (!(ctrl & (QMC_BD_RX_E | QMC_BD_RX_I)) && bd == chan->rxbd_done) {
if (ctrl & QMC_BD_RX_W)
chan->rxbd_done = chan->rxbds;
else
chan->rxbd_done++;
} else {
/* We are full ... */
ret = -EBUSY;
goto end;
}
}
qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */
@ -587,6 +605,10 @@ int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
/* Activate the descriptor */
ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB);
if (complete)
ctrl |= QMC_BD_RX_I;
else
ctrl &= ~QMC_BD_RX_I;
wmb(); /* Be sure to flush data before descriptor activation */
qmc_write16(&bd->cbd_sc, ctrl);
@ -1482,19 +1504,19 @@ static int qmc_setup_chan(struct qmc *qmc, struct qmc_chan *chan)
/* Init Rx BDs and set Wrap bit on last descriptor */
BUILD_BUG_ON(QMC_NB_RXBDS == 0);
val = QMC_BD_RX_I;
for (i = 0; i < QMC_NB_RXBDS; i++) {
bd = chan->rxbds + i;
qmc_write16(&bd->cbd_sc, val);
qmc_write16(&bd->cbd_sc, 0);
}
bd = chan->rxbds + QMC_NB_RXBDS - 1;
qmc_write16(&bd->cbd_sc, val | QMC_BD_RX_W);
qmc_write16(&bd->cbd_sc, QMC_BD_RX_W);
/* Init Tx BDs and set Wrap bit on last descriptor */
BUILD_BUG_ON(QMC_NB_TXBDS == 0);
val = QMC_BD_TX_I;
if (chan->mode == QMC_HDLC)
val |= QMC_BD_TX_L | QMC_BD_TX_TC;
val = QMC_BD_TX_L | QMC_BD_TX_TC;
else
val = 0;
for (i = 0; i < QMC_NB_TXBDS; i++) {
bd = chan->txbds + i;
qmc_write16(&bd->cbd_sc, val);

View File

@ -17,12 +17,6 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
struct qmc_dai_chan {
struct qmc_dai_prtd *prtd_tx;
struct qmc_dai_prtd *prtd_rx;
struct qmc_chan *qmc_chan;
};
struct qmc_dai {
char *name;
int id;
@ -33,7 +27,7 @@ struct qmc_dai {
unsigned int nb_chans_avail;
unsigned int nb_chans_used_tx;
unsigned int nb_chans_used_rx;
struct qmc_dai_chan *chans;
struct qmc_chan **qmc_chans;
};
struct qmc_audio {
@ -57,7 +51,6 @@ struct qmc_dai_prtd {
size_t ch_dma_offset;
unsigned int channels;
DECLARE_BITMAP(chans_pending, 64);
struct snd_pcm_substream *substream;
};
@ -126,17 +119,14 @@ static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd)
int ret;
for (i = 0; i < prtd->channels; i++) {
bitmap_set(prtd->chans_pending, i, 1);
ret = qmc_chan_write_submit(prtd->qmc_dai->chans[i].qmc_chan,
ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chans[i],
prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
prtd->ch_dma_size,
qmc_audio_pcm_write_complete,
&prtd->qmc_dai->chans[i]);
i == prtd->channels - 1 ? qmc_audio_pcm_write_complete :
NULL, prtd);
if (ret) {
dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n",
i, ret);
bitmap_clear(prtd->chans_pending, i, 1);
return ret;
}
}
@ -146,20 +136,7 @@ static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd)
static void qmc_audio_pcm_write_complete(void *context)
{
struct qmc_dai_chan *chan = context;
struct qmc_dai_prtd *prtd;
prtd = chan->prtd_tx;
/* Mark the current channel as completed */
bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1);
/*
* All QMC channels involved must have completed their transfer before
* submitting a new one.
*/
if (!bitmap_empty(prtd->chans_pending, 64))
return;
struct qmc_dai_prtd *prtd = context;
prtd->buffer_ended += prtd->period_size;
if (prtd->buffer_ended >= prtd->buffer_size)
@ -182,17 +159,14 @@ static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd)
int ret;
for (i = 0; i < prtd->channels; i++) {
bitmap_set(prtd->chans_pending, i, 1);
ret = qmc_chan_read_submit(prtd->qmc_dai->chans[i].qmc_chan,
ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chans[i],
prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
prtd->ch_dma_size,
qmc_audio_pcm_read_complete,
&prtd->qmc_dai->chans[i]);
i == prtd->channels - 1 ? qmc_audio_pcm_read_complete :
NULL, prtd);
if (ret) {
dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n",
i, ret);
bitmap_clear(prtd->chans_pending, i, 1);
return ret;
}
}
@ -202,26 +176,13 @@ static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd)
static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags)
{
struct qmc_dai_chan *chan = context;
struct qmc_dai_prtd *prtd;
prtd = chan->prtd_rx;
/* Mark the current channel as completed */
bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1);
struct qmc_dai_prtd *prtd = context;
if (length != prtd->ch_dma_size) {
dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
length, prtd->ch_dma_size);
}
/*
* All QMC channels involved must have completed their transfer before
* submitting a new one.
*/
if (!bitmap_empty(prtd->chans_pending, 64))
return;
prtd->buffer_ended += prtd->period_size;
if (prtd->buffer_ended >= prtd->buffer_size)
prtd->buffer_ended = 0;
@ -239,7 +200,6 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct qmc_dai_prtd *prtd = substream->runtime->private_data;
unsigned int i;
int ret;
if (!prtd->qmc_dai) {
@ -249,14 +209,10 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
bitmap_zero(prtd->chans_pending, 64);
prtd->buffer_ended = 0;
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
for (i = 0; i < prtd->channels; i++)
prtd->qmc_dai->chans[i].prtd_tx = prtd;
/* Submit first chunk ... */
ret = qmc_audio_pcm_write_submit(prtd);
if (ret)
@ -272,9 +228,6 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
if (ret)
return ret;
} else {
for (i = 0; i < prtd->channels; i++)
prtd->qmc_dai->chans[i].prtd_rx = prtd;
/* Submit first chunk ... */
ret = qmc_audio_pcm_read_submit(prtd);
if (ret)
@ -644,9 +597,9 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
chan_param.mode = QMC_TRANSPARENT;
chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used;
for (i = 0; i < nb_chans_used; i++) {
ret = qmc_chan_set_param(qmc_dai->chans[i].qmc_chan, &chan_param);
ret = qmc_chan_set_param(qmc_dai->qmc_chans[i], &chan_param);
if (ret) {
dev_err(dai->dev, "chans[%u], set param failed %d\n",
dev_err(dai->dev, "qmc_chans[%u], set param failed %d\n",
i, ret);
return ret;
}
@ -688,7 +641,7 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
for (i = 0; i < nb_chans_used; i++) {
ret = qmc_chan_start(qmc_dai->chans[i].qmc_chan, direction);
ret = qmc_chan_start(qmc_dai->qmc_chans[i], direction);
if (ret)
goto err_stop;
}
@ -697,13 +650,13 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_STOP:
/* Stop and reset all QMC channels and return the first error encountered */
for (i = 0; i < nb_chans_used; i++) {
ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
ret_tmp = qmc_chan_stop(qmc_dai->qmc_chans[i], direction);
if (!ret)
ret = ret_tmp;
if (ret_tmp)
continue;
ret_tmp = qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction);
ret_tmp = qmc_chan_reset(qmc_dai->qmc_chans[i], direction);
if (!ret)
ret = ret_tmp;
}
@ -715,7 +668,7 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* Stop all QMC channels and return the first error encountered */
for (i = 0; i < nb_chans_used; i++) {
ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
ret_tmp = qmc_chan_stop(qmc_dai->qmc_chans[i], direction);
if (!ret)
ret = ret_tmp;
}
@ -731,8 +684,8 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
err_stop:
while (i--) {
qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction);
qmc_chan_stop(qmc_dai->qmc_chans[i], direction);
qmc_chan_reset(qmc_dai->qmc_chans[i], direction);
}
return ret;
}
@ -791,12 +744,17 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *
struct qmc_dai *qmc_dai,
struct snd_soc_dai_driver *qmc_soc_dai_driver)
{
struct qmc_chan_ts_info ts_info;
struct qmc_chan_info info;
unsigned long rx_fs_rate;
unsigned long tx_fs_rate;
int prev_last_rx_ts = 0;
int prev_last_tx_ts = 0;
unsigned int nb_tx_ts;
unsigned int nb_rx_ts;
unsigned int i;
int last_rx_ts;
int last_tx_ts;
int count;
u32 val;
int ret;
@ -823,19 +781,20 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *
return dev_err_probe(qmc_audio->dev, -EINVAL,
"dai %d no QMC channel defined\n", qmc_dai->id);
qmc_dai->chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->chans), GFP_KERNEL);
if (!qmc_dai->chans)
qmc_dai->qmc_chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->qmc_chans),
GFP_KERNEL);
if (!qmc_dai->qmc_chans)
return -ENOMEM;
for (i = 0; i < count; i++) {
qmc_dai->chans[i].qmc_chan = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np,
"fsl,qmc-chan", i);
if (IS_ERR(qmc_dai->chans[i].qmc_chan)) {
return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->chans[i].qmc_chan),
qmc_dai->qmc_chans[i] = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np,
"fsl,qmc-chan", i);
if (IS_ERR(qmc_dai->qmc_chans[i])) {
return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->qmc_chans[i]),
"dai %d get QMC channel %d failed\n", qmc_dai->id, i);
}
ret = qmc_chan_get_info(qmc_dai->chans[i].qmc_chan, &info);
ret = qmc_chan_get_info(qmc_dai->qmc_chans[i], &info);
if (ret) {
dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n",
qmc_dai->id, i, ret);
@ -879,6 +838,30 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *
return -EINVAL;
}
}
ret = qmc_chan_get_ts_info(qmc_dai->qmc_chans[i], &ts_info);
if (ret) {
dev_err(qmc_audio->dev, "dai %d get QMC %d channel TS info failed %d\n",
qmc_dai->id, i, ret);
return ret;
}
last_rx_ts = fls64(ts_info.rx_ts_mask);
last_tx_ts = fls64(ts_info.tx_ts_mask);
if (prev_last_rx_ts > last_rx_ts) {
dev_err(qmc_audio->dev, "dai %d QMC chan %d unordered channels (RX timeslot %d before %d)\n",
qmc_dai->id, i, prev_last_rx_ts, last_rx_ts);
return -EINVAL;
}
if (prev_last_tx_ts > last_tx_ts) {
dev_err(qmc_audio->dev, "dai %d QMC chan %d unordered channels (TX timeslot %d before %d)\n",
qmc_dai->id, i, prev_last_tx_ts, last_tx_ts);
return -EINVAL;
}
prev_last_rx_ts = last_rx_ts;
prev_last_tx_ts = last_tx_ts;
}
qmc_dai->nb_chans_avail = count;