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 "sof-audio.h"
13 #include "ops.h"
14
sof_kcontrol_setup(struct snd_sof_dev * sdev,struct snd_sof_control * scontrol)15 static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
16 {
17 int ipc_cmd, ctrl_type;
18 int ret;
19
20 /* reset readback offset for scontrol */
21 scontrol->readback_offset = 0;
22
23 /* notify DSP of kcontrol values */
24 switch (scontrol->cmd) {
25 case SOF_CTRL_CMD_VOLUME:
26 case SOF_CTRL_CMD_ENUM:
27 case SOF_CTRL_CMD_SWITCH:
28 ipc_cmd = SOF_IPC_COMP_SET_VALUE;
29 ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
30 break;
31 case SOF_CTRL_CMD_BINARY:
32 ipc_cmd = SOF_IPC_COMP_SET_DATA;
33 ctrl_type = SOF_CTRL_TYPE_DATA_SET;
34 break;
35 default:
36 return 0;
37 }
38
39 ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd, ctrl_type, scontrol->cmd, true);
40 if (ret < 0)
41 dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n",
42 scontrol->comp_id);
43
44 return ret;
45 }
46
sof_dai_config_setup(struct snd_sof_dev * sdev,struct snd_sof_dai * dai)47 static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *dai)
48 {
49 struct sof_ipc_dai_config *config;
50 struct sof_ipc_reply reply;
51 int ret;
52
53 config = &dai->dai_config[dai->current_config];
54 if (!config) {
55 dev_err(sdev->dev, "error: no config for DAI %s\n", dai->name);
56 return -EINVAL;
57 }
58
59 /* set NONE flag to clear all previous settings */
60 config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_NONE);
61
62 ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
63 &reply, sizeof(reply));
64
65 if (ret < 0)
66 dev_err(sdev->dev, "error: failed to set dai config for %s\n", dai->name);
67
68 return ret;
69 }
70
sof_widget_kcontrol_setup(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)71 static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
72 {
73 struct snd_sof_control *scontrol;
74 int ret;
75
76 /* set up all controls for the widget */
77 list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
78 if (scontrol->comp_id == swidget->comp_id) {
79 ret = sof_kcontrol_setup(sdev, scontrol);
80 if (ret < 0) {
81 dev_err(sdev->dev, "error: fail to set up kcontrols for widget %s\n",
82 swidget->widget->name);
83 return ret;
84 }
85 }
86
87 return 0;
88 }
89
sof_reset_route_setup_status(struct snd_sof_dev * sdev,struct snd_sof_widget * widget)90 static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
91 {
92 struct snd_sof_route *sroute;
93
94 list_for_each_entry(sroute, &sdev->route_list, list)
95 if (sroute->src_widget == widget || sroute->sink_widget == widget)
96 sroute->setup = false;
97 }
98
sof_widget_free(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)99 int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
100 {
101 struct sof_ipc_free ipc_free = {
102 .hdr = {
103 .size = sizeof(ipc_free),
104 .cmd = SOF_IPC_GLB_TPLG_MSG,
105 },
106 .id = swidget->comp_id,
107 };
108 struct sof_ipc_reply reply;
109 int ret;
110
111 if (!swidget->private)
112 return 0;
113
114 /* only free when use_count is 0 */
115 if (--swidget->use_count)
116 return 0;
117
118 switch (swidget->id) {
119 case snd_soc_dapm_scheduler:
120 ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE;
121 break;
122 case snd_soc_dapm_buffer:
123 ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE;
124 break;
125 default:
126 ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE;
127 break;
128 }
129
130 ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
131 &reply, sizeof(reply));
132 if (ret < 0) {
133 dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name);
134 swidget->use_count++;
135 return ret;
136 }
137
138 /* reset route setup status for all routes that contain this widget */
139 sof_reset_route_setup_status(sdev, swidget);
140 swidget->complete = 0;
141 dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
142
143 return 0;
144 }
145 EXPORT_SYMBOL(sof_widget_free);
146
sof_widget_setup(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)147 int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
148 {
149 struct sof_ipc_pipe_new *pipeline;
150 struct sof_ipc_comp_reply r;
151 struct sof_ipc_cmd_hdr *hdr;
152 struct sof_ipc_comp *comp;
153 struct snd_sof_dai *dai;
154 size_t ipc_size;
155 int ret;
156
157 /* skip if there is no private data */
158 if (!swidget->private)
159 return 0;
160
161 /* widget already set up */
162 if (++swidget->use_count > 1)
163 return 0;
164
165 ret = sof_pipeline_core_enable(sdev, swidget);
166 if (ret < 0) {
167 dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n",
168 ret, swidget->widget->name);
169 goto use_count_dec;
170 }
171
172 switch (swidget->id) {
173 case snd_soc_dapm_dai_in:
174 case snd_soc_dapm_dai_out:
175 ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext);
176 comp = kzalloc(ipc_size, GFP_KERNEL);
177 if (!comp)
178 return -ENOMEM;
179
180 dai = swidget->private;
181 dai->configured = false;
182 memcpy(comp, &dai->comp_dai, sizeof(struct sof_ipc_comp_dai));
183
184 /* append extended data to the end of the component */
185 memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), &swidget->comp_ext,
186 sizeof(swidget->comp_ext));
187
188 ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, comp, ipc_size, &r, sizeof(r));
189 kfree(comp);
190 if (ret < 0) {
191 dev_err(sdev->dev, "error: failed to load widget %s\n",
192 swidget->widget->name);
193 goto use_count_dec;
194 }
195
196 ret = sof_dai_config_setup(sdev, dai);
197 if (ret < 0) {
198 dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n",
199 swidget->widget->name);
200 sof_widget_free(sdev, swidget);
201 return ret;
202 }
203 break;
204 case snd_soc_dapm_scheduler:
205 pipeline = swidget->private;
206 ret = sof_load_pipeline_ipc(sdev, pipeline, &r);
207 break;
208 default:
209 hdr = swidget->private;
210 ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size,
211 &r, sizeof(r));
212 break;
213 }
214 if (ret < 0) {
215 dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name);
216 goto use_count_dec;
217 }
218
219 /* restore kcontrols for widget */
220 ret = sof_widget_kcontrol_setup(sdev, swidget);
221 if (ret < 0) {
222 dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n",
223 swidget->widget->name);
224 sof_widget_free(sdev, swidget);
225 return ret;
226 }
227
228 dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
229
230 return 0;
231
232 use_count_dec:
233 swidget->use_count--;
234 return ret;
235 }
236 EXPORT_SYMBOL(sof_widget_setup);
237
sof_route_setup_ipc(struct snd_sof_dev * sdev,struct snd_sof_route * sroute)238 static int sof_route_setup_ipc(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
239 {
240 struct sof_ipc_pipe_comp_connect *connect;
241 struct sof_ipc_reply reply;
242 int ret;
243
244 /* skip if there's no private data */
245 if (!sroute->private)
246 return 0;
247
248 /* nothing to do if route is already set up */
249 if (sroute->setup)
250 return 0;
251
252 connect = sroute->private;
253
254 dev_dbg(sdev->dev, "setting up route %s -> %s\n",
255 sroute->src_widget->widget->name,
256 sroute->sink_widget->widget->name);
257
258 /* send ipc */
259 ret = sof_ipc_tx_message(sdev->ipc,
260 connect->hdr.cmd,
261 connect, sizeof(*connect),
262 &reply, sizeof(reply));
263 if (ret < 0) {
264 dev_err(sdev->dev, "%s: route setup failed %d\n", __func__, ret);
265 return ret;
266 }
267
268 sroute->setup = true;
269
270 return 0;
271 }
272
sof_route_setup(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * wsource,struct snd_soc_dapm_widget * wsink)273 static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
274 struct snd_soc_dapm_widget *wsink)
275 {
276 struct snd_sof_widget *src_widget = wsource->dobj.private;
277 struct snd_sof_widget *sink_widget = wsink->dobj.private;
278 struct snd_sof_route *sroute;
279 bool route_found = false;
280
281 /* ignore routes involving virtual widgets in topology */
282 switch (src_widget->id) {
283 case snd_soc_dapm_out_drv:
284 case snd_soc_dapm_output:
285 case snd_soc_dapm_input:
286 return 0;
287 default:
288 break;
289 }
290
291 switch (sink_widget->id) {
292 case snd_soc_dapm_out_drv:
293 case snd_soc_dapm_output:
294 case snd_soc_dapm_input:
295 return 0;
296 default:
297 break;
298 }
299
300 /* find route matching source and sink widgets */
301 list_for_each_entry(sroute, &sdev->route_list, list)
302 if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
303 route_found = true;
304 break;
305 }
306
307 if (!route_found) {
308 dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n",
309 wsource->name, wsink->name);
310 return -EINVAL;
311 }
312
313 return sof_route_setup_ipc(sdev, sroute);
314 }
315
sof_setup_pipeline_connections(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget_list * list,int dir)316 static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
317 struct snd_soc_dapm_widget_list *list, int dir)
318 {
319 struct snd_soc_dapm_widget *widget;
320 struct snd_soc_dapm_path *p;
321 int ret;
322 int i;
323
324 /*
325 * Set up connections between widgets in the sink/source paths based on direction.
326 * Some non-SOF widgets exist in topology either for compatibility or for the
327 * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM
328 * events. But they are not handled by the firmware. So ignore them.
329 */
330 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
331 for_each_dapm_widgets(list, i, widget) {
332 if (!widget->dobj.private)
333 continue;
334
335 snd_soc_dapm_widget_for_each_sink_path(widget, p)
336 if (p->sink->dobj.private) {
337 ret = sof_route_setup(sdev, widget, p->sink);
338 if (ret < 0)
339 return ret;
340 }
341 }
342 } else {
343 for_each_dapm_widgets(list, i, widget) {
344 if (!widget->dobj.private)
345 continue;
346
347 snd_soc_dapm_widget_for_each_source_path(widget, p)
348 if (p->source->dobj.private) {
349 ret = sof_route_setup(sdev, p->source, widget);
350 if (ret < 0)
351 return ret;
352 }
353 }
354 }
355
356 return 0;
357 }
358
sof_widget_list_setup(struct snd_sof_dev * sdev,struct snd_sof_pcm * spcm,int dir)359 int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
360 {
361 struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
362 struct snd_soc_dapm_widget *widget;
363 int i, ret, num_widgets;
364
365 /* nothing to set up */
366 if (!list)
367 return 0;
368
369 /* set up widgets in the list */
370 for_each_dapm_widgets(list, num_widgets, widget) {
371 struct snd_sof_widget *swidget = widget->dobj.private;
372 struct snd_sof_widget *pipe_widget;
373
374 if (!swidget)
375 continue;
376
377 /*
378 * The scheduler widget for a pipeline is not part of the connected DAPM
379 * widget list and it needs to be set up before the widgets in the pipeline
380 * are set up. The use_count for the scheduler widget is incremented for every
381 * widget in a given pipeline to ensure that it is freed only after the last
382 * widget in the pipeline is freed.
383 */
384 pipe_widget = swidget->pipe_widget;
385 if (!pipe_widget) {
386 dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
387 swidget->widget->name);
388 ret = -EINVAL;
389 goto widget_free;
390 }
391
392 ret = sof_widget_setup(sdev, pipe_widget);
393 if (ret < 0)
394 goto widget_free;
395
396 /* set up the widget */
397 ret = sof_widget_setup(sdev, swidget);
398 if (ret < 0) {
399 sof_widget_free(sdev, pipe_widget);
400 goto widget_free;
401 }
402 }
403
404 /*
405 * error in setting pipeline connections will result in route status being reset for
406 * routes that were successfully set up when the widgets are freed.
407 */
408 ret = sof_setup_pipeline_connections(sdev, list, dir);
409 if (ret < 0)
410 goto widget_free;
411
412 /* complete pipelines */
413 for_each_dapm_widgets(list, i, widget) {
414 struct snd_sof_widget *swidget = widget->dobj.private;
415 struct snd_sof_widget *pipe_widget;
416
417 if (!swidget)
418 continue;
419
420 pipe_widget = swidget->pipe_widget;
421 if (!pipe_widget) {
422 dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
423 swidget->widget->name);
424 ret = -EINVAL;
425 goto widget_free;
426 }
427
428 if (pipe_widget->complete)
429 continue;
430
431 pipe_widget->complete = snd_sof_complete_pipeline(sdev, pipe_widget);
432 if (pipe_widget->complete < 0) {
433 ret = pipe_widget->complete;
434 goto widget_free;
435 }
436 }
437
438 return 0;
439
440 widget_free:
441 /* free all widgets that have been set up successfully */
442 for_each_dapm_widgets(list, i, widget) {
443 struct snd_sof_widget *swidget = widget->dobj.private;
444
445 if (!swidget)
446 continue;
447
448 if (!num_widgets--)
449 break;
450
451 sof_widget_free(sdev, swidget);
452 sof_widget_free(sdev, swidget->pipe_widget);
453 }
454
455 return ret;
456 }
457
sof_widget_list_free(struct snd_sof_dev * sdev,struct snd_sof_pcm * spcm,int dir)458 int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
459 {
460 struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
461 struct snd_soc_dapm_widget *widget;
462 int i, ret;
463 int ret1 = 0;
464
465 /* nothing to free */
466 if (!list)
467 return 0;
468
469 /*
470 * Free widgets in the list. This can fail but continue freeing other widgets to keep
471 * use_counts balanced.
472 */
473 for_each_dapm_widgets(list, i, widget) {
474 struct snd_sof_widget *swidget = widget->dobj.private;
475
476 if (!swidget)
477 continue;
478
479 /*
480 * free widget and its pipe_widget. Either of these can fail, but free as many as
481 * possible before freeing the list and returning the error.
482 */
483 ret = sof_widget_free(sdev, swidget);
484 if (ret < 0)
485 ret1 = ret;
486
487 ret = sof_widget_free(sdev, swidget->pipe_widget);
488 if (ret < 0)
489 ret1 = ret;
490 }
491
492 snd_soc_dapm_dai_free_widgets(&list);
493 spcm->stream[dir].list = NULL;
494
495 return ret1;
496 }
497
498 /*
499 * helper to determine if there are only D0i3 compatible
500 * streams active
501 */
snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev * sdev)502 bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
503 {
504 struct snd_pcm_substream *substream;
505 struct snd_sof_pcm *spcm;
506 bool d0i3_compatible_active = false;
507 int dir;
508
509 list_for_each_entry(spcm, &sdev->pcm_list, list) {
510 for_each_pcm_streams(dir) {
511 substream = spcm->stream[dir].substream;
512 if (!substream || !substream->runtime)
513 continue;
514
515 /*
516 * substream->runtime being not NULL indicates
517 * that the stream is open. No need to check the
518 * stream state.
519 */
520 if (!spcm->stream[dir].d0i3_compatible)
521 return false;
522
523 d0i3_compatible_active = true;
524 }
525 }
526
527 return d0i3_compatible_active;
528 }
529 EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
530
snd_sof_stream_suspend_ignored(struct snd_sof_dev * sdev)531 bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
532 {
533 struct snd_sof_pcm *spcm;
534
535 list_for_each_entry(spcm, &sdev->pcm_list, list) {
536 if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
537 spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
538 return true;
539 }
540
541 return false;
542 }
543
sof_set_hw_params_upon_resume(struct device * dev)544 int sof_set_hw_params_upon_resume(struct device *dev)
545 {
546 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
547 struct snd_pcm_substream *substream;
548 struct snd_sof_pcm *spcm;
549 snd_pcm_state_t state;
550 int dir;
551
552 /*
553 * SOF requires hw_params to be set-up internally upon resume.
554 * So, set the flag to indicate this for those streams that
555 * have been suspended.
556 */
557 list_for_each_entry(spcm, &sdev->pcm_list, list) {
558 for_each_pcm_streams(dir) {
559 /*
560 * do not reset hw_params upon resume for streams that
561 * were kept running during suspend
562 */
563 if (spcm->stream[dir].suspend_ignored)
564 continue;
565
566 substream = spcm->stream[dir].substream;
567 if (!substream || !substream->runtime)
568 continue;
569
570 state = substream->runtime->status->state;
571 if (state == SNDRV_PCM_STATE_SUSPENDED)
572 spcm->prepared[dir] = false;
573 }
574 }
575
576 /* set internal flag for BE */
577 return snd_sof_dsp_hw_params_upon_resume(sdev);
578 }
579
snd_sof_pipeline_find(struct snd_sof_dev * sdev,int pipeline_id)580 const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
581 int pipeline_id)
582 {
583 const struct snd_sof_widget *swidget;
584
585 list_for_each_entry(swidget, &sdev->widget_list, list)
586 if (swidget->id == snd_soc_dapm_scheduler) {
587 const struct sof_ipc_pipe_new *pipeline =
588 swidget->private;
589 if (pipeline->pipeline_id == pipeline_id)
590 return pipeline;
591 }
592
593 return NULL;
594 }
595
sof_set_up_pipelines(struct snd_sof_dev * sdev,bool verify)596 int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify)
597 {
598 struct snd_sof_widget *swidget;
599 struct snd_sof_route *sroute;
600 int ret;
601
602 /* restore pipeline components */
603 list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
604 /* only set up the widgets belonging to static pipelines */
605 if (!verify && swidget->dynamic_pipeline_widget)
606 continue;
607
608 /* update DAI config. The IPC will be sent in sof_widget_setup() */
609 if (WIDGET_IS_DAI(swidget->id)) {
610 struct snd_sof_dai *dai = swidget->private;
611 struct sof_ipc_dai_config *config;
612
613 if (!dai || !dai->dai_config)
614 continue;
615
616 config = dai->dai_config;
617 /*
618 * The link DMA channel would be invalidated for running
619 * streams but not for streams that were in the PAUSED
620 * state during suspend. So invalidate it here before setting
621 * the dai config in the DSP.
622 */
623 if (config->type == SOF_DAI_INTEL_HDA)
624 config->hda.link_dma_ch = DMA_CHAN_INVALID;
625 }
626
627 ret = sof_widget_setup(sdev, swidget);
628 if (ret < 0)
629 return ret;
630 }
631
632 /* restore pipeline connections */
633 list_for_each_entry(sroute, &sdev->route_list, list) {
634
635 /* only set up routes belonging to static pipelines */
636 if (!verify && (sroute->src_widget->dynamic_pipeline_widget ||
637 sroute->sink_widget->dynamic_pipeline_widget))
638 continue;
639
640 ret = sof_route_setup_ipc(sdev, sroute);
641 if (ret < 0) {
642 dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__);
643 return ret;
644 }
645 }
646
647 /* complete pipeline */
648 list_for_each_entry(swidget, &sdev->widget_list, list) {
649 switch (swidget->id) {
650 case snd_soc_dapm_scheduler:
651 /* only complete static pipelines */
652 if (!verify && swidget->dynamic_pipeline_widget)
653 continue;
654
655 swidget->complete =
656 snd_sof_complete_pipeline(sdev, swidget);
657 break;
658 default:
659 break;
660 }
661 }
662
663 return 0;
664 }
665
666 /*
667 * This function doesn't free widgets during suspend. It only resets the set up status for all
668 * routes and use_count for all widgets.
669 */
sof_tear_down_pipelines(struct snd_sof_dev * sdev,bool verify)670 int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify)
671 {
672 struct snd_sof_widget *swidget;
673 struct snd_sof_route *sroute;
674 int ret;
675
676 /*
677 * This function is called during suspend and for one-time topology verification during
678 * first boot. In both cases, there is no need to protect swidget->use_count and
679 * sroute->setup because during suspend all streams are suspended and during topology
680 * loading the sound card unavailable to open PCMs.
681 */
682 list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
683 if (!verify) {
684 swidget->use_count = 0;
685 continue;
686 }
687
688 ret = sof_widget_free(sdev, swidget);
689 if (ret < 0)
690 return ret;
691 }
692
693 list_for_each_entry(sroute, &sdev->route_list, list)
694 sroute->setup = false;
695
696 return 0;
697 }
698
699 /*
700 * Generic object lookup APIs.
701 */
702
snd_sof_find_spcm_name(struct snd_soc_component * scomp,const char * name)703 struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
704 const char *name)
705 {
706 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
707 struct snd_sof_pcm *spcm;
708
709 list_for_each_entry(spcm, &sdev->pcm_list, list) {
710 /* match with PCM dai name */
711 if (strcmp(spcm->pcm.dai_name, name) == 0)
712 return spcm;
713
714 /* match with playback caps name if set */
715 if (*spcm->pcm.caps[0].name &&
716 !strcmp(spcm->pcm.caps[0].name, name))
717 return spcm;
718
719 /* match with capture caps name if set */
720 if (*spcm->pcm.caps[1].name &&
721 !strcmp(spcm->pcm.caps[1].name, name))
722 return spcm;
723 }
724
725 return NULL;
726 }
727
snd_sof_find_spcm_comp(struct snd_soc_component * scomp,unsigned int comp_id,int * direction)728 struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
729 unsigned int comp_id,
730 int *direction)
731 {
732 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
733 struct snd_sof_pcm *spcm;
734 int dir;
735
736 list_for_each_entry(spcm, &sdev->pcm_list, list) {
737 for_each_pcm_streams(dir) {
738 if (spcm->stream[dir].comp_id == comp_id) {
739 *direction = dir;
740 return spcm;
741 }
742 }
743 }
744
745 return NULL;
746 }
747
snd_sof_find_spcm_pcm_id(struct snd_soc_component * scomp,unsigned int pcm_id)748 struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp,
749 unsigned int pcm_id)
750 {
751 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
752 struct snd_sof_pcm *spcm;
753
754 list_for_each_entry(spcm, &sdev->pcm_list, list) {
755 if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id)
756 return spcm;
757 }
758
759 return NULL;
760 }
761
snd_sof_find_swidget(struct snd_soc_component * scomp,const char * name)762 struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
763 const char *name)
764 {
765 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
766 struct snd_sof_widget *swidget;
767
768 list_for_each_entry(swidget, &sdev->widget_list, list) {
769 if (strcmp(name, swidget->widget->name) == 0)
770 return swidget;
771 }
772
773 return NULL;
774 }
775
776 /* find widget by stream name and direction */
777 struct snd_sof_widget *
snd_sof_find_swidget_sname(struct snd_soc_component * scomp,const char * pcm_name,int dir)778 snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
779 const char *pcm_name, int dir)
780 {
781 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
782 struct snd_sof_widget *swidget;
783 enum snd_soc_dapm_type type;
784
785 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
786 type = snd_soc_dapm_aif_in;
787 else
788 type = snd_soc_dapm_aif_out;
789
790 list_for_each_entry(swidget, &sdev->widget_list, list) {
791 if (!strcmp(pcm_name, swidget->widget->sname) &&
792 swidget->id == type)
793 return swidget;
794 }
795
796 return NULL;
797 }
798
snd_sof_find_dai(struct snd_soc_component * scomp,const char * name)799 struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
800 const char *name)
801 {
802 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
803 struct snd_sof_dai *dai;
804
805 list_for_each_entry(dai, &sdev->dai_list, list) {
806 if (dai->name && (strcmp(name, dai->name) == 0))
807 return dai;
808 }
809
810 return NULL;
811 }
812
813 #define SOF_DAI_CLK_INTEL_SSP_MCLK 0
814 #define SOF_DAI_CLK_INTEL_SSP_BCLK 1
815
sof_dai_get_clk(struct snd_soc_pcm_runtime * rtd,int clk_type)816 static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type)
817 {
818 struct snd_soc_component *component =
819 snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
820 struct snd_sof_dai *dai =
821 snd_sof_find_dai(component, (char *)rtd->dai_link->name);
822
823 /* use the tplg configured mclk if existed */
824 if (!dai || !dai->dai_config)
825 return 0;
826
827 switch (dai->dai_config->type) {
828 case SOF_DAI_INTEL_SSP:
829 switch (clk_type) {
830 case SOF_DAI_CLK_INTEL_SSP_MCLK:
831 return dai->dai_config->ssp.mclk_rate;
832 case SOF_DAI_CLK_INTEL_SSP_BCLK:
833 return dai->dai_config->ssp.bclk_rate;
834 default:
835 dev_err(rtd->dev, "fail to get SSP clk %d rate\n",
836 clk_type);
837 return -EINVAL;
838 }
839 break;
840 default:
841 /* not yet implemented for platforms other than the above */
842 dev_err(rtd->dev, "DAI type %d not supported yet!\n",
843 dai->dai_config->type);
844 return -EINVAL;
845 }
846 }
847
848 /*
849 * Helper to get SSP MCLK from a pcm_runtime.
850 * Return 0 if not exist.
851 */
sof_dai_get_mclk(struct snd_soc_pcm_runtime * rtd)852 int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd)
853 {
854 return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_MCLK);
855 }
856 EXPORT_SYMBOL(sof_dai_get_mclk);
857
858 /*
859 * Helper to get SSP BCLK from a pcm_runtime.
860 * Return 0 if not exist.
861 */
sof_dai_get_bclk(struct snd_soc_pcm_runtime * rtd)862 int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd)
863 {
864 return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK);
865 }
866 EXPORT_SYMBOL(sof_dai_get_bclk);
867
868 /*
869 * SOF Driver enumeration.
870 */
sof_machine_check(struct snd_sof_dev * sdev)871 int sof_machine_check(struct snd_sof_dev *sdev)
872 {
873 struct snd_sof_pdata *sof_pdata = sdev->pdata;
874 const struct sof_dev_desc *desc = sof_pdata->desc;
875 struct snd_soc_acpi_mach *mach;
876
877 if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
878
879 /* find machine */
880 snd_sof_machine_select(sdev);
881 if (sof_pdata->machine) {
882 snd_sof_set_mach_params(sof_pdata->machine, sdev);
883 return 0;
884 }
885
886 if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
887 dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
888 return -ENODEV;
889 }
890 } else {
891 dev_warn(sdev->dev, "Force to use nocodec mode\n");
892 }
893
894 /* select nocodec mode */
895 dev_warn(sdev->dev, "Using nocodec machine driver\n");
896 mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
897 if (!mach)
898 return -ENOMEM;
899
900 mach->drv_name = "sof-nocodec";
901 sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
902
903 sof_pdata->machine = mach;
904 snd_sof_set_mach_params(sof_pdata->machine, sdev);
905
906 return 0;
907 }
908 EXPORT_SYMBOL(sof_machine_check);
909
sof_machine_register(struct snd_sof_dev * sdev,void * pdata)910 int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
911 {
912 struct snd_sof_pdata *plat_data = pdata;
913 const char *drv_name;
914 const void *mach;
915 int size;
916
917 drv_name = plat_data->machine->drv_name;
918 mach = plat_data->machine;
919 size = sizeof(*plat_data->machine);
920
921 /* register machine driver, pass machine info as pdata */
922 plat_data->pdev_mach =
923 platform_device_register_data(sdev->dev, drv_name,
924 PLATFORM_DEVID_NONE, mach, size);
925 if (IS_ERR(plat_data->pdev_mach))
926 return PTR_ERR(plat_data->pdev_mach);
927
928 dev_dbg(sdev->dev, "created machine %s\n",
929 dev_name(&plat_data->pdev_mach->dev));
930
931 return 0;
932 }
933 EXPORT_SYMBOL(sof_machine_register);
934
sof_machine_unregister(struct snd_sof_dev * sdev,void * pdata)935 void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
936 {
937 struct snd_sof_pdata *plat_data = pdata;
938
939 if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
940 platform_device_unregister(plat_data->pdev_mach);
941 }
942 EXPORT_SYMBOL(sof_machine_unregister);
943