Skip to content

Commit 4f4063d

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 106e683 commit 4f4063d

2 files changed

Lines changed: 79 additions & 3 deletions

File tree

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

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,35 @@ 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->spipe == target_spipe)
318+
count++;
319+
}
320+
321+
return count;
322+
}
323+
295324
static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
296325
struct snd_pcm_substream *substream, int cmd)
297326
{
@@ -319,13 +348,22 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp
319348
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
320349
case SNDRV_PCM_TRIGGER_SUSPEND:
321350
case SNDRV_PCM_TRIGGER_STOP:
351+
/*
352+
* For aggregate DAIs with shared pipelines, the state check
353+
* deduplicates: the first DAI sends the IPC, subsequent DAIs
354+
* sharing the same pipeline see it already paused and skip.
355+
* For aggregate DAIs with different pipelines, each DAI pauses
356+
* its own pipeline independently.
357+
*/
358+
if (pipeline->state == SOF_IPC4_PIPE_PAUSED)
359+
break;
360+
322361
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
323362
SOF_IPC4_PIPE_PAUSED);
324363
if (ret < 0)
325364
return ret;
326365

327366
pipeline->state = SOF_IPC4_PIPE_PAUSED;
328-
329367
break;
330368
default:
331369
dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
@@ -372,11 +410,13 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
372410
static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
373411
struct snd_pcm_substream *substream, int cmd)
374412
{
413+
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
375414
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
376415
struct snd_sof_widget *pipe_widget;
377416
struct sof_ipc4_pipeline *pipeline;
378417
struct snd_sof_widget *swidget;
379418
struct snd_soc_dapm_widget *w;
419+
int num_cpus = rtd->dai_link->num_cpus;
380420
int ret = 0;
381421

382422
w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
@@ -391,6 +431,26 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
391431

392432
switch (cmd) {
393433
case SNDRV_PCM_TRIGGER_START:
434+
/*
435+
* For aggregated DAIs (num_cpus > 1), defer pipeline RUNNING
436+
* IPC until all CPU DAIs sharing this pipeline have started
437+
* their link DMAs. The per-spipe threshold handles all cases:
438+
* - shared pipeline: waits for all DAIs on the same spipe
439+
* - different pipelines: each fires independently (threshold=1)
440+
* - mixed: each group fires when its members complete
441+
*/
442+
if (num_cpus > 1) {
443+
int spipe_dais = hda_ipc4_count_spipe_dais(substream,
444+
swidget->spipe);
445+
446+
swidget->spipe->trigger_count++;
447+
swidget->spipe->started_count++;
448+
if (swidget->spipe->trigger_count < spipe_dais)
449+
break;
450+
451+
swidget->spipe->trigger_count = 0;
452+
}
453+
394454
if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
395455
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
396456
SOF_IPC4_PIPE_PAUSED);
@@ -406,9 +466,21 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
406466
return ret;
407467

408468
pipeline->state = SOF_IPC4_PIPE_RUNNING;
409-
swidget->spipe->started_count++;
469+
if (num_cpus <= 1)
470+
swidget->spipe->started_count++;
410471
break;
411472
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
473+
if (num_cpus > 1) {
474+
int spipe_dais = hda_ipc4_count_spipe_dais(substream,
475+
swidget->spipe);
476+
477+
swidget->spipe->trigger_count++;
478+
if (swidget->spipe->trigger_count < spipe_dais)
479+
break;
480+
481+
swidget->spipe->trigger_count = 0;
482+
}
483+
412484
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
413485
SOF_IPC4_PIPE_RUNNING);
414486
if (ret < 0)
@@ -420,9 +492,11 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
420492
case SNDRV_PCM_TRIGGER_STOP:
421493
/*
422494
* 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
495+
* been stopped. So, clear the started_count so that the pipeline can be reset.
496+
* Also reset trigger_count for aggregate DAIs in case of partial trigger rollback.
424497
*/
425498
swidget->spipe->started_count = 0;
499+
swidget->spipe->trigger_count = 0;
426500
break;
427501
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
428502
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)