Skip to content

Commit 22f1037

Browse files
committed
ASoC: SOF: ipc4: Fix pipeline state transitions for aggregate DAIs
For aggregate DAIs (num_cpus > 1) the pre_trigger/post_trigger callbacks send pipeline state IPCs per-DAI without considering that multiple DAIs may share the same pipeline. This causes premature state transitions where the pipeline goes RUNNING before all link DMAs have started, or individual DAIs send redundant IPCs for shared pipelines. Add a per-pipeline trigger_count to gate state transitions: - START/PAUSE_RELEASE: defer RUNNING IPC until all DAIs sharing the same pipeline have completed their link DMA operations - STOP/SUSPEND/PAUSE_PUSH: use pipeline state dedup so the PAUSED IPC is sent once regardless of how many DAIs share the pipeline The per-spipe DAI count is computed dynamically to handle all aggregate topologies: shared pipelines, independent pipelines, and mixed cases. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
1 parent cd7e8dc commit 22f1037

2 files changed

Lines changed: 79 additions & 2 deletions

File tree

sound/soc/sof/intel/hda-dai-ops.c

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,38 @@ static struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev,
292292
return hdac_bus_eml_sdw_get_hlink(bus);
293293
}
294294

295+
/*
296+
* Count how many CPU DAIs in this BE link share the same pipeline (spipe)
297+
* as the target. Used to determine the per-pipeline trigger threshold for
298+
* aggregate DAIs where some may share a pipeline and others may not.
299+
*/
300+
static int hda_ipc4_count_spipe_dais(struct snd_pcm_substream *substream,
301+
struct snd_sof_pipeline *target_spipe)
302+
{
303+
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
304+
struct snd_soc_dai *dai;
305+
int count = 0;
306+
int i;
307+
308+
for_each_rtd_cpu_dais(rtd, i, dai) {
309+
struct snd_soc_dapm_widget *w;
310+
struct snd_sof_widget *sw;
311+
312+
w = snd_soc_dai_get_widget(dai, substream->stream);
313+
if (!w)
314+
continue;
315+
316+
sw = w->dobj.private;
317+
if (!sw)
318+
continue;
319+
320+
if (sw->spipe == target_spipe)
321+
count++;
322+
}
323+
324+
return count;
325+
}
326+
295327
static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
296328
struct snd_pcm_substream *substream, int cmd)
297329
{
@@ -319,13 +351,22 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp
319351
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
320352
case SNDRV_PCM_TRIGGER_SUSPEND:
321353
case SNDRV_PCM_TRIGGER_STOP:
354+
/*
355+
* For aggregate DAIs with shared pipelines, the state check
356+
* deduplicates: the first DAI sends the IPC, subsequent DAIs
357+
* sharing the same pipeline see it already paused and skip.
358+
* For aggregate DAIs with different pipelines, each DAI pauses
359+
* its own pipeline independently.
360+
*/
361+
if (pipeline->state == SOF_IPC4_PIPE_PAUSED)
362+
break;
363+
322364
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
323365
SOF_IPC4_PIPE_PAUSED);
324366
if (ret < 0)
325367
return ret;
326368

327369
pipeline->state = SOF_IPC4_PIPE_PAUSED;
328-
329370
break;
330371
default:
331372
dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
@@ -372,11 +413,13 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
372413
static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
373414
struct snd_pcm_substream *substream, int cmd)
374415
{
416+
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
375417
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
376418
struct snd_sof_widget *pipe_widget;
377419
struct sof_ipc4_pipeline *pipeline;
378420
struct snd_sof_widget *swidget;
379421
struct snd_soc_dapm_widget *w;
422+
int num_cpus = rtd->dai_link->num_cpus;
380423
int ret = 0;
381424

382425
w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
@@ -391,6 +434,25 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
391434

392435
switch (cmd) {
393436
case SNDRV_PCM_TRIGGER_START:
437+
/*
438+
* For aggregated DAIs (num_cpus > 1), defer pipeline RUNNING
439+
* IPC until all CPU DAIs sharing this pipeline have started
440+
* their link DMAs. The per-spipe threshold handles all cases:
441+
* - shared pipeline: waits for all DAIs on the same spipe
442+
* - different pipelines: each fires independently (threshold=1)
443+
* - mixed: each group fires when its members complete
444+
*/
445+
if (num_cpus > 1) {
446+
int spipe_dais = hda_ipc4_count_spipe_dais(substream,
447+
swidget->spipe);
448+
449+
swidget->spipe->trigger_count++;
450+
if (swidget->spipe->trigger_count < spipe_dais)
451+
break;
452+
453+
swidget->spipe->trigger_count = 0;
454+
}
455+
394456
if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
395457
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
396458
SOF_IPC4_PIPE_PAUSED);
@@ -409,6 +471,17 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
409471
swidget->spipe->started_count++;
410472
break;
411473
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
474+
if (num_cpus > 1) {
475+
int spipe_dais = hda_ipc4_count_spipe_dais(substream,
476+
swidget->spipe);
477+
478+
swidget->spipe->trigger_count++;
479+
if (swidget->spipe->trigger_count < spipe_dais)
480+
break;
481+
482+
swidget->spipe->trigger_count = 0;
483+
}
484+
412485
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
413486
SOF_IPC4_PIPE_RUNNING);
414487
if (ret < 0)
@@ -420,9 +493,11 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
420493
case SNDRV_PCM_TRIGGER_STOP:
421494
/*
422495
* STOP/SUSPEND trigger is invoked only once when all users of this pipeline have
423-
* been stopped. So, clear the started_count so that the pipeline can be reset
496+
* been stopped. So, clear the started_count so that the pipeline can be reset.
497+
* Also reset trigger_count for aggregate DAIs in case of partial trigger rollback.
424498
*/
425499
swidget->spipe->started_count = 0;
500+
swidget->spipe->trigger_count = 0;
426501
break;
427502
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
428503
break;

sound/soc/sof/sof-audio.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ struct snd_sof_widget {
512512
* @complete: flag used to indicate that pipeline set up is complete.
513513
* @core_mask: Mask containing target cores for all modules in the pipeline
514514
* @list: List item in sdev pipeline_list
515+
* @trigger_count: tracks the number of DAIs that have completed link DMA trigger for aggregate
515516
* @direction_valid: flag indicating if the direction is set in topology
516517
* @direction: pipeline direction set in topology, valid is direction_valid is true
517518
*
@@ -523,6 +524,7 @@ struct snd_sof_pipeline {
523524
int complete;
524525
unsigned long core_mask;
525526
struct list_head list;
527+
int trigger_count;
526528
bool direction_valid;
527529
u32 direction;
528530
};

0 commit comments

Comments
 (0)