ASoC: meson: axg-fifo: use threaded irq to check periods

With the AXG audio subsystem, there is a possible random channel shift on
TDM capture, when the slot number per lane is more than 2, and there is
more than one lane used.

The problem has been there since the introduction of the axg audio support
but such scenario is pretty uncommon. This is why there is no loud
complains about the problem.

Solving the problem require to make the links non-atomic and use the
trigger() callback to start FEs and BEs in the appropriate order.

This was tried in the past and reverted because it caused the block irq to
sleep while atomic. However, instead of reverting, the solution is to call
snd_pcm_period_elapsed() in a non atomic context.

Use the bottom half of a threaded IRQ to do so.

Fixes: 6dc4fa179f ("ASoC: meson: add axg fifo base driver")
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Link: https://lore.kernel.org/r/20240426152946.3078805-2-jbrunet@baylibre.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Jerome Brunet 2024-04-26 17:29:38 +02:00 committed by Mark Brown
parent bda16500dd
commit b11d26660d
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -204,18 +204,26 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id)
unsigned int status;
regmap_read(fifo->map, FIFO_STATUS1, &status);
status = FIELD_GET(STATUS1_INT_STS, status);
axg_fifo_ack_irq(fifo, status);
/* Use the thread to call period elapsed on nonatomic links */
if (status & FIFO_INT_COUNT_REPEAT)
snd_pcm_period_elapsed(ss);
else
return IRQ_WAKE_THREAD;
dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n",
status);
/* Ack irqs */
axg_fifo_ack_irq(fifo, status);
return IRQ_NONE;
}
return IRQ_RETVAL(status);
static irqreturn_t axg_fifo_pcm_irq_block_thread(int irq, void *dev_id)
{
struct snd_pcm_substream *ss = dev_id;
snd_pcm_period_elapsed(ss);
return IRQ_HANDLED;
}
int axg_fifo_pcm_open(struct snd_soc_component *component,
@ -243,8 +251,9 @@ int axg_fifo_pcm_open(struct snd_soc_component *component,
if (ret)
return ret;
ret = request_irq(fifo->irq, axg_fifo_pcm_irq_block, 0,
dev_name(dev), ss);
ret = request_threaded_irq(fifo->irq, axg_fifo_pcm_irq_block,
axg_fifo_pcm_irq_block_thread,
IRQF_ONESHOT, dev_name(dev), ss);
if (ret)
return ret;