Skip to content

Commit 640894b

Browse files
committed
ASoC: SOF: sof-audio: Add support for hostless/dailess pipelines
This patch add supports for hostless/dailess pipelines in the SOF topology. An example of hostless pipeline is a pipeline that's completely internal to the DSP with the tone generator as the source. In order to support this with DPCM, we make use of a virtual widget of type snd_soc_dapm_input as the source widget with the pipeline as follows: virtual widget (snd_soc_dapm_input) -> tone generator (effect) -> gain -> DAI As far as the SOF driver is concerned, any widget of type snd_soc_dapm_input is not handled and is treated as a virtual widget but the previoius path allows traversing the list of widgets past this widget to set up the rest of the widgets in the pipeline. This patch also amends the logic for finding the source widget for this pipeline to include the type snd_soc_dapm_input in order to walk from the source to the sink DAI widget when the pipeline is started. An example of a DAI-less pipeline would be the echo reference capture in the speaker playback path. This pipeline is set up as follows: Host(Playback) -> mixin -> mixout -> gain -> module-copier -> DAI | V Host(Capture) <- Process module <- virtual DAI In the above example, the virtual DAI exploits the concept of an aggregated DAI (one with a non-zero DAI ID) in topology to enable this pipeline to work with DPCM. A virtual DAI is a DAI widget with a non-zero DAI ID and hence is skipped when traversing the list of DAPM widgets during widget prepare/set/up/free/unprepare. The process module in the above pipeline generates 0's that are captured by the echo reference PCM. When the playback path is active, the process module acts as a passthrough module to allow the playback samples to be passthrough to the capture host. In order for these pipelines to work properly, the logic for setting/preparing/freeing/unpreparing the widgets needs to be amended to make sure that only the widgets that are in the pipeline in the same direction as the PCM being started are set up. For example, when the playback PCM is started, the capture pipeline widgets also show up in the list of connected DAPM widgets but they shouldn't be set up yet because the echo reference capture PCM hasn't been started yet. Alternatively, when the echo reference capture PCM is started, the playback pipeline widgets should not be setup. Finally, the last step needed to put this all together is the set the routes for widgets connecting the playback and the capture pipelines when both are active. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
1 parent 9c00d76 commit 640894b

File tree

1 file changed

+132
-67
lines changed

1 file changed

+132
-67
lines changed

sound/soc/sof/sof-audio.c

Lines changed: 132 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
269269
is_virtual_widget(sdev, sink_widget->widget, __func__))
270270
return 0;
271271

272+
/* skip route if source/sink widget is not set up */
273+
if (!src_widget->use_count || !sink_widget->use_count)
274+
return 0;
275+
272276
/* find route matching source and sink widgets */
273277
list_for_each_entry(sroute, &sdev->route_list, list)
274278
if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
@@ -297,10 +301,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
297301
return 0;
298302
}
299303

304+
static bool sof_widget_in_same_direction(struct snd_sof_widget *swidget, int dir);
300305
static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
301306
struct snd_soc_dapm_widget_list *list, int dir)
302307
{
303-
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
304308
struct snd_soc_dapm_widget *widget;
305309
struct snd_sof_route *sroute;
306310
struct snd_soc_dapm_path *p;
@@ -315,14 +319,30 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
315319
*/
316320
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
317321
for_each_dapm_widgets(list, i, widget) {
322+
struct snd_sof_widget *src_widget, *sink_widget;
323+
318324
if (!widget->dobj.private)
319325
continue;
320326

321327
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
322328
if (!widget_in_list(list, p->sink))
323329
continue;
330+
src_widget = widget->dobj.private;
324331

325332
if (p->sink->dobj.private) {
333+
sink_widget = p->sink->dobj.private;
334+
335+
/*
336+
* skip if source and sink are in different directions
337+
* (ex. playback and echo ref) if the direction is set in
338+
* topology. These will be set up later. It is enough to
339+
* check if the direction_valid is set for one of the
340+
* widgets as all widgets will have the direction set
341+
* in topology if one is set.
342+
*/
343+
if (sink_widget->spipe->direction_valid &&
344+
!sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction))
345+
continue;
326346
ret = sof_route_setup(sdev, widget, p->sink);
327347
if (ret < 0)
328348
return ret;
@@ -331,14 +351,32 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
331351
}
332352
} else {
333353
for_each_dapm_widgets(list, i, widget) {
354+
struct snd_sof_widget *src_widget, *sink_widget;
355+
334356
if (!widget->dobj.private)
335357
continue;
336358

337359
snd_soc_dapm_widget_for_each_source_path(widget, p) {
338360
if (!widget_in_list(list, p->source))
339361
continue;
340362

363+
sink_widget = widget->dobj.private;
364+
341365
if (p->source->dobj.private) {
366+
src_widget = p->source->dobj.private;
367+
368+
/*
369+
* skip if source and sink are in different directions
370+
* (ex. playback and echo ref) if the direction is set in
371+
* topology. These will be set up later. It is enough to
372+
* check if the direction_valid is set for one of the
373+
* widgets as all widgets will have the direction set
374+
* in topology if one is set.
375+
*/
376+
if (sink_widget->spipe->direction_valid &&
377+
!sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction))
378+
continue;
379+
342380
ret = sof_route_setup(sdev, p->source, widget);
343381
if (ret < 0)
344382
return ret;
@@ -355,7 +393,6 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
355393
*/
356394
list_for_each_entry(sroute, &sdev->route_list, list) {
357395
bool src_widget_in_dapm_list, sink_widget_in_dapm_list;
358-
struct snd_sof_widget *swidget;
359396

360397
if (sroute->setup)
361398
continue;
@@ -364,63 +401,69 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
364401
sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget);
365402

366403
/*
367-
* if both source and sink are in the DAPM list, the route must already have been
368-
* set up above. And if neither are in the DAPM list, the route shouldn't be
369-
* handled now.
404+
* no need to set up the route if both the source and sink widgets are not in the
405+
* DAPM list
370406
*/
371-
if (src_widget_in_dapm_list == sink_widget_in_dapm_list)
407+
if (!src_widget_in_dapm_list && !sink_widget_in_dapm_list)
372408
continue;
373409

374410
/*
375-
* At this point either the source widget or the sink widget is in the DAPM list
376-
* with a route that might need to be set up. Check the use_count of the widget
377-
* that is not in the DAPM list to confirm if it is in use currently before setting
378-
* up the route.
411+
* set up the route only if both the source and sink widgets are in the DAPM list
412+
* but are in different directions. The ones in the same direction would already
413+
* have been set up in the previous loop.
379414
*/
380-
if (src_widget_in_dapm_list)
381-
swidget = sroute->sink_widget;
382-
else
383-
swidget = sroute->src_widget;
415+
if (src_widget_in_dapm_list && sink_widget_in_dapm_list) {
416+
struct snd_sof_widget *src_widget, *sink_widget;
384417

385-
scoped_guard(mutex, &swidget->setup_mutex) {
386-
if (!swidget->use_count)
387-
continue;
418+
src_widget = sroute->src_widget->widget->dobj.private;
419+
sink_widget = sroute->sink_widget->widget->dobj.private;
388420

389-
if (tplg_ops && tplg_ops->route_setup) {
390-
/*
391-
* this route will get freed when either the
392-
* source widget or the sink widget is freed
393-
* during hw_free
394-
*/
395-
ret = tplg_ops->route_setup(sdev, sroute);
396-
if (!ret)
397-
sroute->setup = true;
398-
}
421+
/*
422+
* it is enough to check if the direction_valid is set for one of the
423+
* widgets as all widgets will have the direction set in topology if one
424+
* is set.
425+
*/
426+
if (src_widget && sink_widget &&
427+
src_widget->spipe->direction_valid &&
428+
sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction))
429+
continue;
399430
}
400431

432+
ret = sof_route_setup(sdev, sroute->src_widget->widget,
433+
sroute->sink_widget->widget);
434+
401435
if (ret < 0)
402436
return ret;
403437
}
404438

405439
return 0;
406440
}
407441

442+
static bool sof_widget_in_same_direction(struct snd_sof_widget *swidget, int dir)
443+
{
444+
if (swidget->spipe->direction == dir)
445+
return true;
446+
447+
return false;
448+
}
449+
408450
static void
409451
sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
410-
struct snd_soc_dapm_widget_list *list)
452+
struct snd_soc_dapm_widget_list *list, int dir)
411453
{
412454
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
413455
struct snd_sof_widget *swidget = widget->dobj.private;
414456
const struct sof_ipc_tplg_widget_ops *widget_ops;
415457
struct snd_soc_dapm_path *p;
416458

417-
/* skip if the widget is in use or if it is already unprepared */
418-
if (!swidget || !swidget->prepared || swidget->use_count > 0 ||
419-
is_virtual_widget(sdev, widget, __func__))
459+
if (!swidget || is_virtual_widget(sdev, widget, __func__))
420460
goto sink_unprepare;
421461

422-
/* skip aggregated DAIs */
423-
if(is_aggregated_dai(swidget))
462+
if (swidget->spipe->direction_valid && !sof_widget_in_same_direction(swidget, dir))
463+
return;
464+
465+
/* skip widgets in use, those already unprepared or aggregated DAIs */
466+
if (!swidget->prepared || swidget->use_count > 0 || is_aggregated_dai(swidget))
424467
goto sink_unprepare;
425468

426469
widget_ops = tplg_ops ? tplg_ops->widget : NULL;
@@ -435,9 +478,10 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg
435478
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
436479
if (!widget_in_list(list, p->sink))
437480
continue;
481+
438482
if (!p->walking && p->sink->dobj.private) {
439483
p->walking = true;
440-
sof_unprepare_widgets_in_path(sdev, p->sink, list);
484+
sof_unprepare_widgets_in_path(sdev, p->sink, list, dir);
441485
p->walking = false;
442486
}
443487
}
@@ -456,18 +500,21 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
456500
struct snd_soc_dapm_path *p;
457501
int ret;
458502

459-
if (is_virtual_widget(sdev, widget, __func__))
503+
dev_dbg(sdev->dev, "%s: preparing widget %s id %d\n",
504+
__func__, widget->name, widget->id);
505+
506+
if (is_virtual_widget(sdev, widget, __func__) || !swidget)
460507
goto sink_prepare;
461508

462509
widget_ops = tplg_ops ? tplg_ops->widget : NULL;
463510
if (!widget_ops)
464511
return 0;
465512

466-
if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)
467-
goto sink_prepare;
513+
if (swidget->spipe->direction_valid && !sof_widget_in_same_direction(swidget, dir))
514+
return 0;
468515

469-
/* skip aggregated DAIs */
470-
if(is_aggregated_dai(swidget))
516+
if (!widget_ops[widget->id].ipc_prepare || swidget->prepared ||
517+
is_aggregated_dai(swidget))
471518
goto sink_prepare;
472519

473520
/* prepare the source widget */
@@ -485,6 +532,7 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
485532
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
486533
if (!widget_in_list(list, p->sink))
487534
continue;
535+
488536
if (!p->walking && p->sink->dobj.private) {
489537
p->walking = true;
490538
ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
@@ -514,23 +562,25 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
514562
int dir, struct snd_sof_pcm *spcm)
515563
{
516564
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
517-
struct snd_sof_widget *swidget;
565+
struct snd_sof_widget *swidget = widget->dobj.private;
518566
struct snd_soc_dapm_path *p;
519567
int err;
520568
int ret = 0;
521569

522-
if (is_virtual_widget(sdev, widget, __func__))
570+
if (is_virtual_widget(sdev, widget, __func__) || !swidget)
523571
goto sink_free;
524572

525-
swidget = widget->dobj.private;
573+
/* skip aggregated DAIs */
574+
if (swidget->spipe->direction_valid && !sof_widget_in_same_direction(swidget, dir))
575+
return 0;
526576

527-
/* no need to free aggregated DAI widgets */
528-
if (swidget && !is_aggregated_dai(swidget)) {
529-
err = sof_widget_free(sdev, swidget);
530-
if (err < 0)
531-
ret = err;
532-
}
577+
if (is_aggregated_dai(swidget))
578+
goto sink_free;
533579

580+
err = sof_widget_free(sdev, widget->dobj.private);
581+
if (err < 0)
582+
ret = err;
583+
sink_free:
534584
/* free all widgets in the sink paths even in case of error to keep use counts balanced */
535585
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
536586
if (!p->walking) {
@@ -570,6 +620,10 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
570620
if (swidget) {
571621
int i;
572622

623+
/* skip aggregated DAIs */
624+
if (swidget->spipe->direction_valid && !sof_widget_in_same_direction(swidget, dir))
625+
return 0;
626+
573627
if(is_aggregated_dai(swidget))
574628
goto sink_setup;
575629

@@ -635,15 +689,14 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
635689
return 0;
636690

637691
for_each_dapm_widgets(list, i, widget) {
638-
if (is_virtual_widget(sdev, widget, __func__))
639-
continue;
640-
641-
/* starting widget for playback is AIF type */
642-
if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
692+
/* starting widget for playback is of AIF or snd_soc_dapm_input type */
693+
if (dir == SNDRV_PCM_STREAM_PLAYBACK && (widget->id != snd_soc_dapm_aif_in &&
694+
widget->id != snd_soc_dapm_input))
643695
continue;
644696

645697
/* starting widget for capture is DAI type */
646-
if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out)
698+
if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out &&
699+
widget->id != snd_soc_dapm_output)
647700
continue;
648701

649702
switch (op) {
@@ -673,7 +726,7 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
673726
break;
674727
}
675728
case SOF_WIDGET_UNPREPARE:
676-
sof_unprepare_widgets_in_path(sdev, widget, list);
729+
sof_unprepare_widgets_in_path(sdev, widget, list, dir);
677730
break;
678731
default:
679732
dev_err(sdev->dev, "Invalid widget op %d\n", op);
@@ -920,29 +973,41 @@ struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
920973
return NULL;
921974
}
922975

923-
/* find widget by stream name and direction */
924-
struct snd_sof_widget *
925-
snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
926-
const char *pcm_name, int dir)
976+
static struct snd_sof_widget *snd_sof_find_swidget_sname_type(struct snd_soc_component *scomp,
977+
const char *sname,
978+
enum snd_soc_dapm_type type)
927979
{
928980
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
929981
struct snd_sof_widget *swidget;
930-
enum snd_soc_dapm_type type;
931-
932-
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
933-
type = snd_soc_dapm_aif_in;
934-
else
935-
type = snd_soc_dapm_aif_out;
936982

937983
list_for_each_entry(swidget, &sdev->widget_list, list) {
938-
if (!strcmp(pcm_name, swidget->widget->sname) &&
939-
swidget->id == type)
984+
if (!strcmp(sname, swidget->widget->sname) && swidget->id == type)
940985
return swidget;
941986
}
942987

943988
return NULL;
944989
}
945990

991+
/* find widget by stream name and direction */
992+
struct snd_sof_widget *
993+
snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
994+
const char *pcm_name, int dir)
995+
{
996+
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
997+
struct snd_sof_widget *swidget;
998+
999+
/* first look for an aif_in type widget */
1000+
swidget = snd_sof_find_swidget_sname_type(scomp, pcm_name, snd_soc_dapm_aif_in);
1001+
if (swidget)
1002+
return swidget;
1003+
1004+
/* if not found, look for an input type widget */
1005+
return snd_sof_find_swidget_sname_type(scomp, pcm_name, snd_soc_dapm_input);
1006+
}
1007+
1008+
return snd_sof_find_swidget_sname_type(scomp, pcm_name, snd_soc_dapm_aif_out);
1009+
}
1010+
9461011
struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
9471012
const char *name)
9481013
{

0 commit comments

Comments
 (0)