Skip to content

Commit c5782bb

Browse files
committed
ASoC: meson: tdm fixes
Merge series from Jerome Brunet <jbrunet@baylibre.com>: This patchset fixes 2 problems on TDM which both find a solution by properly implementing the .trigger() callback for the TDM backend. ATM, enabling the TDM formatters is done by the .prepare() callback because handling the formatter is slow due to necessary calls to CCF. The first problem affects the TDMIN. Because .prepare() is called on DPCM backend first, the formatter are started before the FIFOs and this may cause a random channel shifts if the TDMIN use multiple lanes with more than 2 slots per lanes. Using trigger() allows to set the FE/BE order, solving the problem. There has already been an attempt to fix this 3y ago [1] and reverted [2] It triggered a 'sleep in irq' error on the period IRQ. The solution is to just use the bottom half of threaded IRQ. This is patch #1. Patch #2 and #3 remain mostly the same as 3y ago. For TDMOUT, the problem is on pause. ATM pause only stops the FIFO and the TDMOUT just starves. When it does, it will actually repeat the last sample continuously. Depending on the platform, if there is no high-pass filter on the analog path, this may translate to a constant position of the speaker membrane. There is no audible glitch but it may damage the speaker coil. Properly stopping the TDMOUT in pause solves the problem. There is behaviour change associated with that fix. Clocks used to be continuous on pause because of the problem above. They will now be gated on pause by default, as they should. The last change introduce the proper support for continuous clocks, if needed. [1]: https://lore.kernel.org/linux-amlogic/20211020114217.133153-1-jbrunet@baylibre.com [2]: https://lore.kernel.org/linux-amlogic/20220421155725.2589089-1-narmstrong@baylibre.com
2 parents fbd741f + a5a8903 commit c5782bb

File tree

5 files changed

+93
-20
lines changed

5 files changed

+93
-20
lines changed

sound/soc/meson/axg-card.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
318318

319319
dai_link->cpus = cpu;
320320
dai_link->num_cpus = 1;
321+
dai_link->nonatomic = true;
321322

322323
ret = meson_card_parse_dai(card, np, dai_link->cpus);
323324
if (ret)

sound/soc/meson/axg-fifo.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -204,18 +204,26 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id)
204204
unsigned int status;
205205

206206
regmap_read(fifo->map, FIFO_STATUS1, &status);
207-
208207
status = FIELD_GET(STATUS1_INT_STS, status);
208+
axg_fifo_ack_irq(fifo, status);
209+
210+
/* Use the thread to call period elapsed on nonatomic links */
209211
if (status & FIFO_INT_COUNT_REPEAT)
210-
snd_pcm_period_elapsed(ss);
211-
else
212-
dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n",
213-
status);
212+
return IRQ_WAKE_THREAD;
214213

215-
/* Ack irqs */
216-
axg_fifo_ack_irq(fifo, status);
214+
dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n",
215+
status);
216+
217+
return IRQ_NONE;
218+
}
219+
220+
static irqreturn_t axg_fifo_pcm_irq_block_thread(int irq, void *dev_id)
221+
{
222+
struct snd_pcm_substream *ss = dev_id;
223+
224+
snd_pcm_period_elapsed(ss);
217225

218-
return IRQ_RETVAL(status);
226+
return IRQ_HANDLED;
219227
}
220228

221229
int axg_fifo_pcm_open(struct snd_soc_component *component,
@@ -243,8 +251,9 @@ int axg_fifo_pcm_open(struct snd_soc_component *component,
243251
if (ret)
244252
return ret;
245253

246-
ret = request_irq(fifo->irq, axg_fifo_pcm_irq_block, 0,
247-
dev_name(dev), ss);
254+
ret = request_threaded_irq(fifo->irq, axg_fifo_pcm_irq_block,
255+
axg_fifo_pcm_irq_block_thread,
256+
IRQF_ONESHOT, dev_name(dev), ss);
248257
if (ret)
249258
return ret;
250259

sound/soc/meson/axg-tdm-formatter.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,46 @@ void axg_tdm_stream_free(struct axg_tdm_stream *ts)
392392
}
393393
EXPORT_SYMBOL_GPL(axg_tdm_stream_free);
394394

395+
int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts,
396+
unsigned int fmt)
397+
{
398+
int ret = 0;
399+
400+
if (fmt & SND_SOC_DAIFMT_CONT) {
401+
/* Clock are already enabled - skipping */
402+
if (ts->clk_enabled)
403+
return 0;
404+
405+
ret = clk_prepare_enable(ts->iface->mclk);
406+
if (ret)
407+
return ret;
408+
409+
ret = clk_prepare_enable(ts->iface->sclk);
410+
if (ret)
411+
goto err_sclk;
412+
413+
ret = clk_prepare_enable(ts->iface->lrclk);
414+
if (ret)
415+
goto err_lrclk;
416+
417+
ts->clk_enabled = true;
418+
return 0;
419+
}
420+
421+
/* Clocks are already disabled - skipping */
422+
if (!ts->clk_enabled)
423+
return 0;
424+
425+
clk_disable_unprepare(ts->iface->lrclk);
426+
err_lrclk:
427+
clk_disable_unprepare(ts->iface->sclk);
428+
err_sclk:
429+
clk_disable_unprepare(ts->iface->mclk);
430+
ts->clk_enabled = false;
431+
return ret;
432+
}
433+
EXPORT_SYMBOL_GPL(axg_tdm_stream_set_cont_clocks);
434+
395435
MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
396436
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
397437
MODULE_LICENSE("GPL v2");

sound/soc/meson/axg-tdm-interface.c

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream,
309309
struct snd_soc_dai *dai)
310310
{
311311
struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
312+
struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
312313
int ret;
313314

314315
switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -346,27 +347,44 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream,
346347
return ret;
347348
}
348349

349-
return 0;
350+
ret = axg_tdm_stream_set_cont_clocks(ts, iface->fmt);
351+
if (ret)
352+
dev_err(dai->dev, "failed to apply continuous clock setting\n");
353+
354+
return ret;
350355
}
351356

352357
static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream,
353358
struct snd_soc_dai *dai)
354359
{
355360
struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
356361

357-
/* Stop all attached formatters */
358-
axg_tdm_stream_stop(ts);
359-
360-
return 0;
362+
return axg_tdm_stream_set_cont_clocks(ts, 0);
361363
}
362364

363-
static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream,
365+
static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream,
366+
int cmd,
364367
struct snd_soc_dai *dai)
365368
{
366-
struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
369+
struct axg_tdm_stream *ts =
370+
snd_soc_dai_get_dma_data(dai, substream);
371+
372+
switch (cmd) {
373+
case SNDRV_PCM_TRIGGER_START:
374+
case SNDRV_PCM_TRIGGER_RESUME:
375+
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
376+
axg_tdm_stream_start(ts);
377+
break;
378+
case SNDRV_PCM_TRIGGER_SUSPEND:
379+
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
380+
case SNDRV_PCM_TRIGGER_STOP:
381+
axg_tdm_stream_stop(ts);
382+
break;
383+
default:
384+
return -EINVAL;
385+
}
367386

368-
/* Force all attached formatters to update */
369-
return axg_tdm_stream_reset(ts);
387+
return 0;
370388
}
371389

372390
static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai)
@@ -412,8 +430,8 @@ static const struct snd_soc_dai_ops axg_tdm_iface_ops = {
412430
.set_fmt = axg_tdm_iface_set_fmt,
413431
.startup = axg_tdm_iface_startup,
414432
.hw_params = axg_tdm_iface_hw_params,
415-
.prepare = axg_tdm_iface_prepare,
416433
.hw_free = axg_tdm_iface_hw_free,
434+
.trigger = axg_tdm_iface_trigger,
417435
};
418436

419437
/* TDM Backend DAIs */

sound/soc/meson/axg-tdm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,17 @@ struct axg_tdm_stream {
5858
unsigned int physical_width;
5959
u32 *mask;
6060
bool ready;
61+
62+
/* For continuous clock tracking */
63+
bool clk_enabled;
6164
};
6265

6366
struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface);
6467
void axg_tdm_stream_free(struct axg_tdm_stream *ts);
6568
int axg_tdm_stream_start(struct axg_tdm_stream *ts);
6669
void axg_tdm_stream_stop(struct axg_tdm_stream *ts);
70+
int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts,
71+
unsigned int fmt);
6772

6873
static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts)
6974
{

0 commit comments

Comments
 (0)