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:
commit
5998f0d07d
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user