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