Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2//
3// This file is provided under a dual BSD/GPLv2 license. When using or
4// redistributing this file, you may do so under either license.
5//
6// Copyright(c) 2019 Intel Corporation. All rights reserved.
7//
8// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
9//
10
11#include <linux/bitfield.h>
12#include <trace/events/sof.h>
13#include "sof-audio.h"
14#include "sof-of-dev.h"
15#include "ops.h"
16
17static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
18{
19 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
20 struct snd_sof_route *sroute;
21
22 list_for_each_entry(sroute, &sdev->route_list, list)
23 if (sroute->src_widget == widget || sroute->sink_widget == widget) {
24 if (sroute->setup && tplg_ops && tplg_ops->route_free)
25 tplg_ops->route_free(sdev, sroute);
26
27 sroute->setup = false;
28 }
29}
30
31static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
32 struct snd_sof_widget *swidget)
33{
34 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
35 struct snd_sof_widget *pipe_widget;
36 int err = 0;
37 int ret;
38
39 if (!swidget->private)
40 return 0;
41
42 trace_sof_widget_free(swidget);
43
44 /* only free when use_count is 0 */
45 if (--swidget->use_count)
46 return 0;
47
48 pipe_widget = swidget->spipe->pipe_widget;
49
50 /* reset route setup status for all routes that contain this widget */
51 sof_reset_route_setup_status(sdev, swidget);
52
53 /* free DAI config and continue to free widget even if it fails */
54 if (WIDGET_IS_DAI(swidget->id)) {
55 struct snd_sof_dai_config_data data;
56 unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_FREE;
57
58 data.dai_data = DMA_CHAN_INVALID;
59
60 if (tplg_ops && tplg_ops->dai_config) {
61 err = tplg_ops->dai_config(sdev, swidget, flags, &data);
62 if (err < 0)
63 dev_err(sdev->dev, "failed to free config for widget %s\n",
64 swidget->widget->name);
65 }
66 }
67
68 /* continue to disable core even if IPC fails */
69 if (tplg_ops && tplg_ops->widget_free) {
70 ret = tplg_ops->widget_free(sdev, swidget);
71 if (ret < 0 && !err)
72 err = ret;
73 }
74
75 /*
76 * disable widget core. continue to route setup status and complete flag
77 * even if this fails and return the appropriate error
78 */
79 ret = snd_sof_dsp_core_put(sdev, swidget->core);
80 if (ret < 0) {
81 dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n",
82 swidget->core, swidget->widget->name);
83 if (!err)
84 err = ret;
85 }
86
87 /*
88 * free the scheduler widget (same as pipe_widget) associated with the current swidget.
89 * skip for static pipelines
90 */
91 if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
92 ret = sof_widget_free_unlocked(sdev, pipe_widget);
93 if (ret < 0 && !err)
94 err = ret;
95 }
96
97 /* clear pipeline complete */
98 if (swidget->id == snd_soc_dapm_scheduler)
99 swidget->spipe->complete = 0;
100
101 if (!err)
102 dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
103
104 return err;
105}
106
107int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
108{
109 int ret;
110
111 mutex_lock(&swidget->setup_mutex);
112 ret = sof_widget_free_unlocked(sdev, swidget);
113 mutex_unlock(&swidget->setup_mutex);
114
115 return ret;
116}
117EXPORT_SYMBOL(sof_widget_free);
118
119static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
120 struct snd_sof_widget *swidget)
121{
122 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
123 bool use_count_decremented = false;
124 int ret;
125
126 /* skip if there is no private data */
127 if (!swidget->private)
128 return 0;
129
130 trace_sof_widget_setup(swidget);
131
132 /* widget already set up */
133 if (++swidget->use_count > 1)
134 return 0;
135
136 /*
137 * The scheduler widget for a pipeline is not part of the connected DAPM
138 * widget list and it needs to be set up before the widgets in the pipeline
139 * are set up. The use_count for the scheduler widget is incremented for every
140 * widget in a given pipeline to ensure that it is freed only after the last
141 * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines.
142 */
143 if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
144 if (!swidget->spipe || !swidget->spipe->pipe_widget) {
145 dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name);
146 ret = -EINVAL;
147 goto use_count_dec;
148 }
149
150 ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget);
151 if (ret < 0)
152 goto use_count_dec;
153 }
154
155 /* enable widget core */
156 ret = snd_sof_dsp_core_get(sdev, swidget->core);
157 if (ret < 0) {
158 dev_err(sdev->dev, "error: failed to enable target core for widget %s\n",
159 swidget->widget->name);
160 goto pipe_widget_free;
161 }
162
163 /* setup widget in the DSP */
164 if (tplg_ops && tplg_ops->widget_setup) {
165 ret = tplg_ops->widget_setup(sdev, swidget);
166 if (ret < 0)
167 goto core_put;
168 }
169
170 /* send config for DAI components */
171 if (WIDGET_IS_DAI(swidget->id)) {
172 unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
173
174 /*
175 * The config flags saved during BE DAI hw_params will be used for IPC3. IPC4 does
176 * not use the flags argument.
177 */
178 if (tplg_ops && tplg_ops->dai_config) {
179 ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);
180 if (ret < 0)
181 goto widget_free;
182 }
183 }
184
185 /* restore kcontrols for widget */
186 if (tplg_ops && tplg_ops->control && tplg_ops->control->widget_kcontrol_setup) {
187 ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);
188 if (ret < 0)
189 goto widget_free;
190 }
191
192 dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
193
194 return 0;
195
196widget_free:
197 /* widget use_count and core ref_count will both be decremented by sof_widget_free() */
198 sof_widget_free_unlocked(sdev, swidget);
199 use_count_decremented = true;
200core_put:
201 snd_sof_dsp_core_put(sdev, swidget->core);
202pipe_widget_free:
203 if (swidget->id != snd_soc_dapm_scheduler)
204 sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
205use_count_dec:
206 if (!use_count_decremented)
207 swidget->use_count--;
208
209 return ret;
210}
211
212int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
213{
214 int ret;
215
216 mutex_lock(&swidget->setup_mutex);
217 ret = sof_widget_setup_unlocked(sdev, swidget);
218 mutex_unlock(&swidget->setup_mutex);
219
220 return ret;
221}
222EXPORT_SYMBOL(sof_widget_setup);
223
224int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
225 struct snd_soc_dapm_widget *wsink)
226{
227 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
228 struct snd_sof_widget *src_widget = wsource->dobj.private;
229 struct snd_sof_widget *sink_widget = wsink->dobj.private;
230 struct snd_sof_route *sroute;
231 bool route_found = false;
232
233 /* ignore routes involving virtual widgets in topology */
234 switch (src_widget->id) {
235 case snd_soc_dapm_out_drv:
236 case snd_soc_dapm_output:
237 case snd_soc_dapm_input:
238 return 0;
239 default:
240 break;
241 }
242
243 switch (sink_widget->id) {
244 case snd_soc_dapm_out_drv:
245 case snd_soc_dapm_output:
246 case snd_soc_dapm_input:
247 return 0;
248 default:
249 break;
250 }
251
252 /* find route matching source and sink widgets */
253 list_for_each_entry(sroute, &sdev->route_list, list)
254 if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
255 route_found = true;
256 break;
257 }
258
259 if (!route_found) {
260 dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n",
261 wsource->name, wsink->name);
262 return -EINVAL;
263 }
264
265 /* nothing to do if route is already set up */
266 if (sroute->setup)
267 return 0;
268
269 if (tplg_ops && tplg_ops->route_setup) {
270 int ret = tplg_ops->route_setup(sdev, sroute);
271
272 if (ret < 0)
273 return ret;
274 }
275
276 sroute->setup = true;
277 return 0;
278}
279
280static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
281 struct snd_soc_dapm_widget_list *list, int dir)
282{
283 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
284 struct snd_soc_dapm_widget *widget;
285 struct snd_sof_route *sroute;
286 struct snd_soc_dapm_path *p;
287 int ret = 0;
288 int i;
289
290 /*
291 * Set up connections between widgets in the sink/source paths based on direction.
292 * Some non-SOF widgets exist in topology either for compatibility or for the
293 * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM
294 * events. But they are not handled by the firmware. So ignore them.
295 */
296 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
297 for_each_dapm_widgets(list, i, widget) {
298 if (!widget->dobj.private)
299 continue;
300
301 snd_soc_dapm_widget_for_each_sink_path(widget, p) {
302 if (!widget_in_list(list, p->sink))
303 continue;
304
305 if (p->sink->dobj.private) {
306 ret = sof_route_setup(sdev, widget, p->sink);
307 if (ret < 0)
308 return ret;
309 }
310 }
311 }
312 } else {
313 for_each_dapm_widgets(list, i, widget) {
314 if (!widget->dobj.private)
315 continue;
316
317 snd_soc_dapm_widget_for_each_source_path(widget, p) {
318 if (!widget_in_list(list, p->source))
319 continue;
320
321 if (p->source->dobj.private) {
322 ret = sof_route_setup(sdev, p->source, widget);
323 if (ret < 0)
324 return ret;
325 }
326 }
327 }
328 }
329
330 /*
331 * The above loop handles connections between widgets that belong to the DAPM widget list.
332 * This is not sufficient to handle loopback cases between pipelines configured with
333 * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker
334 * protection module.
335 */
336 list_for_each_entry(sroute, &sdev->route_list, list) {
337 bool src_widget_in_dapm_list, sink_widget_in_dapm_list;
338 struct snd_sof_widget *swidget;
339
340 if (sroute->setup)
341 continue;
342
343 src_widget_in_dapm_list = widget_in_list(list, sroute->src_widget->widget);
344 sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget);
345
346 /*
347 * if both source and sink are in the DAPM list, the route must already have been
348 * set up above. And if neither are in the DAPM list, the route shouldn't be
349 * handled now.
350 */
351 if (src_widget_in_dapm_list == sink_widget_in_dapm_list)
352 continue;
353
354 /*
355 * At this point either the source widget or the sink widget is in the DAPM list
356 * with a route that might need to be set up. Check the use_count of the widget
357 * that is not in the DAPM list to confirm if it is in use currently before setting
358 * up the route.
359 */
360 if (src_widget_in_dapm_list)
361 swidget = sroute->sink_widget;
362 else
363 swidget = sroute->src_widget;
364
365 mutex_lock(&swidget->setup_mutex);
366 if (!swidget->use_count) {
367 mutex_unlock(&swidget->setup_mutex);
368 continue;
369 }
370
371 if (tplg_ops && tplg_ops->route_setup) {
372 /*
373 * this route will get freed when either the source widget or the sink
374 * widget is freed during hw_free
375 */
376 ret = tplg_ops->route_setup(sdev, sroute);
377 if (!ret)
378 sroute->setup = true;
379 }
380
381 mutex_unlock(&swidget->setup_mutex);
382
383 if (ret < 0)
384 return ret;
385 }
386
387 return 0;
388}
389
390static void
391sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
392 struct snd_soc_dapm_widget_list *list)
393{
394 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
395 struct snd_sof_widget *swidget = widget->dobj.private;
396 const struct sof_ipc_tplg_widget_ops *widget_ops;
397 struct snd_soc_dapm_path *p;
398
399 /* skip if the widget is in use or if it is already unprepared */
400 if (!swidget || !swidget->prepared || swidget->use_count > 0)
401 goto sink_unprepare;
402
403 widget_ops = tplg_ops ? tplg_ops->widget : NULL;
404 if (widget_ops && widget_ops[widget->id].ipc_unprepare)
405 /* unprepare the source widget */
406 widget_ops[widget->id].ipc_unprepare(swidget);
407
408 swidget->prepared = false;
409
410sink_unprepare:
411 /* unprepare all widgets in the sink paths */
412 snd_soc_dapm_widget_for_each_sink_path(widget, p) {
413 if (!widget_in_list(list, p->sink))
414 continue;
415 if (!p->walking && p->sink->dobj.private) {
416 p->walking = true;
417 sof_unprepare_widgets_in_path(sdev, p->sink, list);
418 p->walking = false;
419 }
420 }
421}
422
423static int
424sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
425 struct snd_pcm_hw_params *fe_params,
426 struct snd_sof_platform_stream_params *platform_params,
427 struct snd_pcm_hw_params *pipeline_params, int dir,
428 struct snd_soc_dapm_widget_list *list)
429{
430 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
431 struct snd_sof_widget *swidget = widget->dobj.private;
432 const struct sof_ipc_tplg_widget_ops *widget_ops;
433 struct snd_soc_dapm_path *p;
434 int ret;
435
436 widget_ops = tplg_ops ? tplg_ops->widget : NULL;
437 if (!widget_ops)
438 return 0;
439
440 if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)
441 goto sink_prepare;
442
443 /* prepare the source widget */
444 ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,
445 pipeline_params, dir);
446 if (ret < 0) {
447 dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);
448 return ret;
449 }
450
451 swidget->prepared = true;
452
453sink_prepare:
454 /* prepare all widgets in the sink paths */
455 snd_soc_dapm_widget_for_each_sink_path(widget, p) {
456 if (!widget_in_list(list, p->sink))
457 continue;
458 if (!p->walking && p->sink->dobj.private) {
459 p->walking = true;
460 ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
461 platform_params, pipeline_params, dir,
462 list);
463 p->walking = false;
464 if (ret < 0) {
465 /* unprepare the source widget */
466 if (widget_ops[widget->id].ipc_unprepare &&
467 swidget && swidget->prepared) {
468 widget_ops[widget->id].ipc_unprepare(swidget);
469 swidget->prepared = false;
470 }
471 return ret;
472 }
473 }
474 }
475
476 return 0;
477}
478
479/*
480 * free all widgets in the sink path starting from the source widget
481 * (DAI type for capture, AIF type for playback)
482 */
483static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
484 int dir, struct snd_sof_pcm *spcm)
485{
486 struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
487 struct snd_soc_dapm_path *p;
488 int err;
489 int ret = 0;
490
491 if (widget->dobj.private) {
492 err = sof_widget_free(sdev, widget->dobj.private);
493 if (err < 0)
494 ret = err;
495 }
496
497 /* free all widgets in the sink paths even in case of error to keep use counts balanced */
498 snd_soc_dapm_widget_for_each_sink_path(widget, p) {
499 if (!p->walking) {
500 if (!widget_in_list(list, p->sink))
501 continue;
502
503 p->walking = true;
504
505 err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm);
506 if (err < 0)
507 ret = err;
508 p->walking = false;
509 }
510 }
511
512 return ret;
513}
514
515/*
516 * set up all widgets in the sink path starting from the source widget
517 * (DAI type for capture, AIF type for playback).
518 * The error path in this function ensures that all successfully set up widgets getting freed.
519 */
520static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
521 int dir, struct snd_sof_pcm *spcm)
522{
523 struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
524 struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
525 struct snd_sof_widget *swidget = widget->dobj.private;
526 struct snd_sof_pipeline *spipe;
527 struct snd_soc_dapm_path *p;
528 int ret;
529
530 if (swidget) {
531 int i;
532
533 ret = sof_widget_setup(sdev, widget->dobj.private);
534 if (ret < 0)
535 return ret;
536
537 /* skip populating the pipe_widgets array if it is NULL */
538 if (!pipeline_list->pipelines)
539 goto sink_setup;
540
541 /*
542 * Add the widget's pipe_widget to the list of pipelines to be triggered if not
543 * already in the list. This will result in the pipelines getting added in the
544 * order source to sink.
545 */
546 for (i = 0; i < pipeline_list->count; i++) {
547 spipe = pipeline_list->pipelines[i];
548 if (spipe == swidget->spipe)
549 break;
550 }
551
552 if (i == pipeline_list->count) {
553 pipeline_list->count++;
554 pipeline_list->pipelines[i] = swidget->spipe;
555 }
556 }
557
558sink_setup:
559 snd_soc_dapm_widget_for_each_sink_path(widget, p) {
560 if (!p->walking) {
561 if (!widget_in_list(list, p->sink))
562 continue;
563
564 p->walking = true;
565
566 ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm);
567 p->walking = false;
568 if (ret < 0) {
569 if (swidget)
570 sof_widget_free(sdev, swidget);
571 return ret;
572 }
573 }
574 }
575
576 return 0;
577}
578
579static int
580sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
581 struct snd_pcm_hw_params *fe_params,
582 struct snd_sof_platform_stream_params *platform_params, int dir,
583 enum sof_widget_op op)
584{
585 struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
586 struct snd_soc_dapm_widget *widget;
587 char *str;
588 int ret = 0;
589 int i;
590
591 if (!list)
592 return 0;
593
594 for_each_dapm_widgets(list, i, widget) {
595 /* starting widget for playback is AIF type */
596 if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
597 continue;
598
599 /* starting widget for capture is DAI type */
600 if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out)
601 continue;
602
603 switch (op) {
604 case SOF_WIDGET_SETUP:
605 ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm);
606 str = "set up";
607 break;
608 case SOF_WIDGET_FREE:
609 ret = sof_free_widgets_in_path(sdev, widget, dir, spcm);
610 str = "free";
611 break;
612 case SOF_WIDGET_PREPARE:
613 {
614 struct snd_pcm_hw_params pipeline_params;
615
616 str = "prepare";
617 /*
618 * When walking the list of connected widgets, the pipeline_params for each
619 * widget is modified by the source widget in the path. Use a local
620 * copy of the runtime params as the pipeline_params so that the runtime
621 * params does not get overwritten.
622 */
623 memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
624
625 ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params,
626 &pipeline_params, dir, list);
627 break;
628 }
629 case SOF_WIDGET_UNPREPARE:
630 sof_unprepare_widgets_in_path(sdev, widget, list);
631 break;
632 default:
633 dev_err(sdev->dev, "Invalid widget op %d\n", op);
634 return -EINVAL;
635 }
636 if (ret < 0) {
637 dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
638 return ret;
639 }
640 }
641
642 return 0;
643}
644
645int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
646 struct snd_pcm_hw_params *fe_params,
647 struct snd_sof_platform_stream_params *platform_params,
648 int dir)
649{
650 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
651 struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
652 struct snd_soc_dapm_widget *widget;
653 int i, ret;
654
655 /* nothing to set up */
656 if (!list)
657 return 0;
658
659 /*
660 * Prepare widgets for set up. The prepare step is used to allocate memory, assign
661 * instance ID and pick the widget configuration based on the runtime PCM params.
662 */
663 ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
664 dir, SOF_WIDGET_PREPARE);
665 if (ret < 0)
666 return ret;
667
668 /* Set up is used to send the IPC to the DSP to create the widget */
669 ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
670 dir, SOF_WIDGET_SETUP);
671 if (ret < 0) {
672 sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
673 dir, SOF_WIDGET_UNPREPARE);
674 return ret;
675 }
676
677 /*
678 * error in setting pipeline connections will result in route status being reset for
679 * routes that were successfully set up when the widgets are freed.
680 */
681 ret = sof_setup_pipeline_connections(sdev, list, dir);
682 if (ret < 0)
683 goto widget_free;
684
685 /* complete pipelines */
686 for_each_dapm_widgets(list, i, widget) {
687 struct snd_sof_widget *swidget = widget->dobj.private;
688 struct snd_sof_widget *pipe_widget;
689 struct snd_sof_pipeline *spipe;
690
691 if (!swidget || sdev->dspless_mode_selected)
692 continue;
693
694 spipe = swidget->spipe;
695 if (!spipe) {
696 dev_err(sdev->dev, "no pipeline found for %s\n",
697 swidget->widget->name);
698 ret = -EINVAL;
699 goto widget_free;
700 }
701
702 pipe_widget = spipe->pipe_widget;
703 if (!pipe_widget) {
704 dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
705 swidget->widget->name);
706 ret = -EINVAL;
707 goto widget_free;
708 }
709
710 if (spipe->complete)
711 continue;
712
713 if (tplg_ops && tplg_ops->pipeline_complete) {
714 spipe->complete = tplg_ops->pipeline_complete(sdev, pipe_widget);
715 if (spipe->complete < 0) {
716 ret = spipe->complete;
717 goto widget_free;
718 }
719 }
720 }
721
722 return 0;
723
724widget_free:
725 sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir,
726 SOF_WIDGET_FREE);
727 sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
728
729 return ret;
730}
731
732int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
733{
734 struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
735 struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
736 int ret;
737
738 /* nothing to free */
739 if (!list)
740 return 0;
741
742 /* send IPC to free widget in the DSP */
743 ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE);
744
745 /* unprepare the widget */
746 sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
747
748 snd_soc_dapm_dai_free_widgets(&list);
749 spcm->stream[dir].list = NULL;
750
751 pipeline_list->count = 0;
752
753 return ret;
754}
755
756/*
757 * helper to determine if there are only D0i3 compatible
758 * streams active
759 */
760bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
761{
762 struct snd_pcm_substream *substream;
763 struct snd_sof_pcm *spcm;
764 bool d0i3_compatible_active = false;
765 int dir;
766
767 list_for_each_entry(spcm, &sdev->pcm_list, list) {
768 for_each_pcm_streams(dir) {
769 substream = spcm->stream[dir].substream;
770 if (!substream || !substream->runtime)
771 continue;
772
773 /*
774 * substream->runtime being not NULL indicates
775 * that the stream is open. No need to check the
776 * stream state.
777 */
778 if (!spcm->stream[dir].d0i3_compatible)
779 return false;
780
781 d0i3_compatible_active = true;
782 }
783 }
784
785 return d0i3_compatible_active;
786}
787EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
788
789bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
790{
791 struct snd_sof_pcm *spcm;
792
793 list_for_each_entry(spcm, &sdev->pcm_list, list) {
794 if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
795 spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
796 return true;
797 }
798
799 return false;
800}
801
802int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
803 struct snd_sof_pcm *spcm, int dir, bool free_widget_list)
804{
805 const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
806 int ret;
807
808 if (spcm->prepared[substream->stream]) {
809 /* stop DMA first if needed */
810 if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
811 snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
812
813 /* Send PCM_FREE IPC to reset pipeline */
814 if (pcm_ops && pcm_ops->hw_free) {
815 ret = pcm_ops->hw_free(sdev->component, substream);
816 if (ret < 0)
817 return ret;
818 }
819
820 spcm->prepared[substream->stream] = false;
821 }
822
823 /* reset the DMA */
824 ret = snd_sof_pcm_platform_hw_free(sdev, substream);
825 if (ret < 0)
826 return ret;
827
828 /* free widget list */
829 if (free_widget_list) {
830 ret = sof_widget_list_free(sdev, spcm, dir);
831 if (ret < 0)
832 dev_err(sdev->dev, "failed to free widgets during suspend\n");
833 }
834
835 return ret;
836}
837
838/*
839 * Generic object lookup APIs.
840 */
841
842struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
843 const char *name)
844{
845 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
846 struct snd_sof_pcm *spcm;
847
848 list_for_each_entry(spcm, &sdev->pcm_list, list) {
849 /* match with PCM dai name */
850 if (strcmp(spcm->pcm.dai_name, name) == 0)
851 return spcm;
852
853 /* match with playback caps name if set */
854 if (*spcm->pcm.caps[0].name &&
855 !strcmp(spcm->pcm.caps[0].name, name))
856 return spcm;
857
858 /* match with capture caps name if set */
859 if (*spcm->pcm.caps[1].name &&
860 !strcmp(spcm->pcm.caps[1].name, name))
861 return spcm;
862 }
863
864 return NULL;
865}
866
867struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
868 unsigned int comp_id,
869 int *direction)
870{
871 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
872 struct snd_sof_pcm *spcm;
873 int dir;
874
875 list_for_each_entry(spcm, &sdev->pcm_list, list) {
876 for_each_pcm_streams(dir) {
877 if (spcm->stream[dir].comp_id == comp_id) {
878 *direction = dir;
879 return spcm;
880 }
881 }
882 }
883
884 return NULL;
885}
886
887struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
888 const char *name)
889{
890 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
891 struct snd_sof_widget *swidget;
892
893 list_for_each_entry(swidget, &sdev->widget_list, list) {
894 if (strcmp(name, swidget->widget->name) == 0)
895 return swidget;
896 }
897
898 return NULL;
899}
900
901/* find widget by stream name and direction */
902struct snd_sof_widget *
903snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
904 const char *pcm_name, int dir)
905{
906 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
907 struct snd_sof_widget *swidget;
908 enum snd_soc_dapm_type type;
909
910 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
911 type = snd_soc_dapm_aif_in;
912 else
913 type = snd_soc_dapm_aif_out;
914
915 list_for_each_entry(swidget, &sdev->widget_list, list) {
916 if (!strcmp(pcm_name, swidget->widget->sname) &&
917 swidget->id == type)
918 return swidget;
919 }
920
921 return NULL;
922}
923
924struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
925 const char *name)
926{
927 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
928 struct snd_sof_dai *dai;
929
930 list_for_each_entry(dai, &sdev->dai_list, list) {
931 if (dai->name && (strcmp(name, dai->name) == 0))
932 return dai;
933 }
934
935 return NULL;
936}
937
938static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type)
939{
940 struct snd_soc_component *component =
941 snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
942 struct snd_sof_dai *dai =
943 snd_sof_find_dai(component, (char *)rtd->dai_link->name);
944 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
945 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
946
947 /* use the tplg configured mclk if existed */
948 if (!dai)
949 return 0;
950
951 if (tplg_ops && tplg_ops->dai_get_clk)
952 return tplg_ops->dai_get_clk(sdev, dai, clk_type);
953
954 return 0;
955}
956
957/*
958 * Helper to get SSP MCLK from a pcm_runtime.
959 * Return 0 if not exist.
960 */
961int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd)
962{
963 return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_MCLK);
964}
965EXPORT_SYMBOL(sof_dai_get_mclk);
966
967/*
968 * Helper to get SSP BCLK from a pcm_runtime.
969 * Return 0 if not exist.
970 */
971int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd)
972{
973 return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK);
974}
975EXPORT_SYMBOL(sof_dai_get_bclk);
976
977static struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev)
978{
979 struct snd_sof_pdata *sof_pdata = sdev->pdata;
980 const struct sof_dev_desc *desc = sof_pdata->desc;
981 struct snd_sof_of_mach *mach = desc->of_machines;
982
983 if (!mach)
984 return NULL;
985
986 for (; mach->compatible; mach++) {
987 if (of_machine_is_compatible(mach->compatible)) {
988 sof_pdata->tplg_filename = mach->sof_tplg_filename;
989 if (mach->fw_filename)
990 sof_pdata->fw_filename = mach->fw_filename;
991
992 return mach;
993 }
994 }
995
996 return NULL;
997}
998
999/*
1000 * SOF Driver enumeration.
1001 */
1002int sof_machine_check(struct snd_sof_dev *sdev)
1003{
1004 struct snd_sof_pdata *sof_pdata = sdev->pdata;
1005 const struct sof_dev_desc *desc = sof_pdata->desc;
1006 struct snd_soc_acpi_mach *mach;
1007
1008 if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
1009 const struct snd_sof_of_mach *of_mach;
1010
1011 if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
1012 sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
1013 goto nocodec;
1014
1015 /* find machine */
1016 mach = snd_sof_machine_select(sdev);
1017 if (mach) {
1018 sof_pdata->machine = mach;
1019 snd_sof_set_mach_params(mach, sdev);
1020 return 0;
1021 }
1022
1023 of_mach = sof_of_machine_select(sdev);
1024 if (of_mach) {
1025 sof_pdata->of_machine = of_mach;
1026 return 0;
1027 }
1028
1029 if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
1030 dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
1031 return -ENODEV;
1032 }
1033 } else {
1034 dev_warn(sdev->dev, "Force to use nocodec mode\n");
1035 }
1036
1037nocodec:
1038 /* select nocodec mode */
1039 dev_warn(sdev->dev, "Using nocodec machine driver\n");
1040 mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
1041 if (!mach)
1042 return -ENOMEM;
1043
1044 mach->drv_name = "sof-nocodec";
1045 if (!sof_pdata->tplg_filename)
1046 sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
1047
1048 sof_pdata->machine = mach;
1049 snd_sof_set_mach_params(mach, sdev);
1050
1051 return 0;
1052}
1053EXPORT_SYMBOL(sof_machine_check);
1054
1055int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
1056{
1057 struct snd_sof_pdata *plat_data = pdata;
1058 const char *drv_name;
1059 const void *mach;
1060 int size;
1061
1062 drv_name = plat_data->machine->drv_name;
1063 mach = plat_data->machine;
1064 size = sizeof(*plat_data->machine);
1065
1066 /* register machine driver, pass machine info as pdata */
1067 plat_data->pdev_mach =
1068 platform_device_register_data(sdev->dev, drv_name,
1069 PLATFORM_DEVID_NONE, mach, size);
1070 if (IS_ERR(plat_data->pdev_mach))
1071 return PTR_ERR(plat_data->pdev_mach);
1072
1073 dev_dbg(sdev->dev, "created machine %s\n",
1074 dev_name(&plat_data->pdev_mach->dev));
1075
1076 return 0;
1077}
1078EXPORT_SYMBOL(sof_machine_register);
1079
1080void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
1081{
1082 struct snd_sof_pdata *plat_data = pdata;
1083
1084 if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
1085 platform_device_unregister(plat_data->pdev_mach);
1086}
1087EXPORT_SYMBOL(sof_machine_unregister);