Merge tag 'printk-for-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/printk...
[linux-2.6-microblaze.git] / sound / soc / sof / sof-audio.c
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 "sof-audio.h"
12 #include "ops.h"
13
14 /*
15  * helper to determine if there are only D0i3 compatible
16  * streams active
17  */
18 bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
19 {
20         struct snd_pcm_substream *substream;
21         struct snd_sof_pcm *spcm;
22         bool d0i3_compatible_active = false;
23         int dir;
24
25         list_for_each_entry(spcm, &sdev->pcm_list, list) {
26                 for_each_pcm_streams(dir) {
27                         substream = spcm->stream[dir].substream;
28                         if (!substream || !substream->runtime)
29                                 continue;
30
31                         /*
32                          * substream->runtime being not NULL indicates
33                          * that the stream is open. No need to check the
34                          * stream state.
35                          */
36                         if (!spcm->stream[dir].d0i3_compatible)
37                                 return false;
38
39                         d0i3_compatible_active = true;
40                 }
41         }
42
43         return d0i3_compatible_active;
44 }
45 EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
46
47 bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
48 {
49         struct snd_sof_pcm *spcm;
50
51         list_for_each_entry(spcm, &sdev->pcm_list, list) {
52                 if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
53                     spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
54                         return true;
55         }
56
57         return false;
58 }
59
60 int sof_set_hw_params_upon_resume(struct device *dev)
61 {
62         struct snd_sof_dev *sdev = dev_get_drvdata(dev);
63         struct snd_pcm_substream *substream;
64         struct snd_sof_pcm *spcm;
65         snd_pcm_state_t state;
66         int dir;
67
68         /*
69          * SOF requires hw_params to be set-up internally upon resume.
70          * So, set the flag to indicate this for those streams that
71          * have been suspended.
72          */
73         list_for_each_entry(spcm, &sdev->pcm_list, list) {
74                 for_each_pcm_streams(dir) {
75                         /*
76                          * do not reset hw_params upon resume for streams that
77                          * were kept running during suspend
78                          */
79                         if (spcm->stream[dir].suspend_ignored)
80                                 continue;
81
82                         substream = spcm->stream[dir].substream;
83                         if (!substream || !substream->runtime)
84                                 continue;
85
86                         state = substream->runtime->status->state;
87                         if (state == SNDRV_PCM_STATE_SUSPENDED)
88                                 spcm->prepared[dir] = false;
89                 }
90         }
91
92         /* set internal flag for BE */
93         return snd_sof_dsp_hw_params_upon_resume(sdev);
94 }
95
96 static int sof_restore_kcontrols(struct device *dev)
97 {
98         struct snd_sof_dev *sdev = dev_get_drvdata(dev);
99         struct snd_sof_control *scontrol;
100         int ipc_cmd, ctrl_type;
101         int ret = 0;
102
103         /* restore kcontrol values */
104         list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
105                 /* reset readback offset for scontrol after resuming */
106                 scontrol->readback_offset = 0;
107
108                 /* notify DSP of kcontrol values */
109                 switch (scontrol->cmd) {
110                 case SOF_CTRL_CMD_VOLUME:
111                 case SOF_CTRL_CMD_ENUM:
112                 case SOF_CTRL_CMD_SWITCH:
113                         ipc_cmd = SOF_IPC_COMP_SET_VALUE;
114                         ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
115                         ret = snd_sof_ipc_set_get_comp_data(scontrol,
116                                                             ipc_cmd, ctrl_type,
117                                                             scontrol->cmd,
118                                                             true);
119                         break;
120                 case SOF_CTRL_CMD_BINARY:
121                         ipc_cmd = SOF_IPC_COMP_SET_DATA;
122                         ctrl_type = SOF_CTRL_TYPE_DATA_SET;
123                         ret = snd_sof_ipc_set_get_comp_data(scontrol,
124                                                             ipc_cmd, ctrl_type,
125                                                             scontrol->cmd,
126                                                             true);
127                         break;
128
129                 default:
130                         break;
131                 }
132
133                 if (ret < 0) {
134                         dev_err(dev,
135                                 "error: failed kcontrol value set for widget: %d\n",
136                                 scontrol->comp_id);
137
138                         return ret;
139                 }
140         }
141
142         return 0;
143 }
144
145 const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
146                                                      int pipeline_id)
147 {
148         const struct snd_sof_widget *swidget;
149
150         list_for_each_entry(swidget, &sdev->widget_list, list)
151                 if (swidget->id == snd_soc_dapm_scheduler) {
152                         const struct sof_ipc_pipe_new *pipeline =
153                                 swidget->private;
154                         if (pipeline->pipeline_id == pipeline_id)
155                                 return pipeline;
156                 }
157
158         return NULL;
159 }
160
161 int sof_restore_pipelines(struct device *dev)
162 {
163         struct snd_sof_dev *sdev = dev_get_drvdata(dev);
164         struct snd_sof_widget *swidget;
165         struct snd_sof_route *sroute;
166         struct sof_ipc_pipe_new *pipeline;
167         struct snd_sof_dai *dai;
168         struct sof_ipc_cmd_hdr *hdr;
169         struct sof_ipc_comp *comp;
170         size_t ipc_size;
171         int ret;
172
173         /* restore pipeline components */
174         list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
175                 struct sof_ipc_comp_reply r;
176
177                 /* skip if there is no private data */
178                 if (!swidget->private)
179                         continue;
180
181                 ret = sof_pipeline_core_enable(sdev, swidget);
182                 if (ret < 0) {
183                         dev_err(dev,
184                                 "error: failed to enable target core: %d\n",
185                                 ret);
186
187                         return ret;
188                 }
189
190                 switch (swidget->id) {
191                 case snd_soc_dapm_dai_in:
192                 case snd_soc_dapm_dai_out:
193                         ipc_size = sizeof(struct sof_ipc_comp_dai) +
194                                    sizeof(struct sof_ipc_comp_ext);
195                         comp = kzalloc(ipc_size, GFP_KERNEL);
196                         if (!comp)
197                                 return -ENOMEM;
198
199                         dai = swidget->private;
200                         memcpy(comp, &dai->comp_dai,
201                                sizeof(struct sof_ipc_comp_dai));
202
203                         /* append extended data to the end of the component */
204                         memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai),
205                                &swidget->comp_ext, sizeof(swidget->comp_ext));
206
207                         ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd,
208                                                  comp, ipc_size,
209                                                  &r, sizeof(r));
210                         kfree(comp);
211                         break;
212                 case snd_soc_dapm_scheduler:
213
214                         /*
215                          * During suspend, all DSP cores are powered off.
216                          * Therefore upon resume, create the pipeline comp
217                          * and power up the core that the pipeline is
218                          * scheduled on.
219                          */
220                         pipeline = swidget->private;
221                         ret = sof_load_pipeline_ipc(dev, pipeline, &r);
222                         break;
223                 default:
224                         hdr = swidget->private;
225                         ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd,
226                                                  swidget->private, hdr->size,
227                                                  &r, sizeof(r));
228                         break;
229                 }
230                 if (ret < 0) {
231                         dev_err(dev,
232                                 "error: failed to load widget type %d with ID: %d\n",
233                                 swidget->widget->id, swidget->comp_id);
234
235                         return ret;
236                 }
237         }
238
239         /* restore pipeline connections */
240         list_for_each_entry_reverse(sroute, &sdev->route_list, list) {
241                 struct sof_ipc_pipe_comp_connect *connect;
242                 struct sof_ipc_reply reply;
243
244                 /* skip if there's no private data */
245                 if (!sroute->private)
246                         continue;
247
248                 connect = sroute->private;
249
250                 /* send ipc */
251                 ret = sof_ipc_tx_message(sdev->ipc,
252                                          connect->hdr.cmd,
253                                          connect, sizeof(*connect),
254                                          &reply, sizeof(reply));
255                 if (ret < 0) {
256                         dev_err(dev,
257                                 "error: failed to load route sink %s control %s source %s\n",
258                                 sroute->route->sink,
259                                 sroute->route->control ? sroute->route->control
260                                         : "none",
261                                 sroute->route->source);
262
263                         return ret;
264                 }
265         }
266
267         /* restore dai links */
268         list_for_each_entry_reverse(dai, &sdev->dai_list, list) {
269                 struct sof_ipc_reply reply;
270                 struct sof_ipc_dai_config *config = dai->dai_config;
271
272                 if (!config) {
273                         dev_err(dev, "error: no config for DAI %s\n",
274                                 dai->name);
275                         continue;
276                 }
277
278                 /*
279                  * The link DMA channel would be invalidated for running
280                  * streams but not for streams that were in the PAUSED
281                  * state during suspend. So invalidate it here before setting
282                  * the dai config in the DSP.
283                  */
284                 if (config->type == SOF_DAI_INTEL_HDA)
285                         config->hda.link_dma_ch = DMA_CHAN_INVALID;
286
287                 ret = sof_ipc_tx_message(sdev->ipc,
288                                          config->hdr.cmd, config,
289                                          config->hdr.size,
290                                          &reply, sizeof(reply));
291
292                 if (ret < 0) {
293                         dev_err(dev,
294                                 "error: failed to set dai config for %s\n",
295                                 dai->name);
296
297                         return ret;
298                 }
299         }
300
301         /* complete pipeline */
302         list_for_each_entry(swidget, &sdev->widget_list, list) {
303                 switch (swidget->id) {
304                 case snd_soc_dapm_scheduler:
305                         swidget->complete =
306                                 snd_sof_complete_pipeline(dev, swidget);
307                         break;
308                 default:
309                         break;
310                 }
311         }
312
313         /* restore pipeline kcontrols */
314         ret = sof_restore_kcontrols(dev);
315         if (ret < 0)
316                 dev_err(dev,
317                         "error: restoring kcontrols after resume\n");
318
319         return ret;
320 }
321
322 /*
323  * Generic object lookup APIs.
324  */
325
326 struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
327                                            const char *name)
328 {
329         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
330         struct snd_sof_pcm *spcm;
331
332         list_for_each_entry(spcm, &sdev->pcm_list, list) {
333                 /* match with PCM dai name */
334                 if (strcmp(spcm->pcm.dai_name, name) == 0)
335                         return spcm;
336
337                 /* match with playback caps name if set */
338                 if (*spcm->pcm.caps[0].name &&
339                     !strcmp(spcm->pcm.caps[0].name, name))
340                         return spcm;
341
342                 /* match with capture caps name if set */
343                 if (*spcm->pcm.caps[1].name &&
344                     !strcmp(spcm->pcm.caps[1].name, name))
345                         return spcm;
346         }
347
348         return NULL;
349 }
350
351 struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
352                                            unsigned int comp_id,
353                                            int *direction)
354 {
355         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
356         struct snd_sof_pcm *spcm;
357         int dir;
358
359         list_for_each_entry(spcm, &sdev->pcm_list, list) {
360                 for_each_pcm_streams(dir) {
361                         if (spcm->stream[dir].comp_id == comp_id) {
362                                 *direction = dir;
363                                 return spcm;
364                         }
365                 }
366         }
367
368         return NULL;
369 }
370
371 struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp,
372                                              unsigned int pcm_id)
373 {
374         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
375         struct snd_sof_pcm *spcm;
376
377         list_for_each_entry(spcm, &sdev->pcm_list, list) {
378                 if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id)
379                         return spcm;
380         }
381
382         return NULL;
383 }
384
385 struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
386                                             const char *name)
387 {
388         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
389         struct snd_sof_widget *swidget;
390
391         list_for_each_entry(swidget, &sdev->widget_list, list) {
392                 if (strcmp(name, swidget->widget->name) == 0)
393                         return swidget;
394         }
395
396         return NULL;
397 }
398
399 /* find widget by stream name and direction */
400 struct snd_sof_widget *
401 snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
402                            const char *pcm_name, int dir)
403 {
404         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
405         struct snd_sof_widget *swidget;
406         enum snd_soc_dapm_type type;
407
408         if (dir == SNDRV_PCM_STREAM_PLAYBACK)
409                 type = snd_soc_dapm_aif_in;
410         else
411                 type = snd_soc_dapm_aif_out;
412
413         list_for_each_entry(swidget, &sdev->widget_list, list) {
414                 if (!strcmp(pcm_name, swidget->widget->sname) &&
415                     swidget->id == type)
416                         return swidget;
417         }
418
419         return NULL;
420 }
421
422 struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
423                                      const char *name)
424 {
425         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
426         struct snd_sof_dai *dai;
427
428         list_for_each_entry(dai, &sdev->dai_list, list) {
429                 if (dai->name && (strcmp(name, dai->name) == 0))
430                         return dai;
431         }
432
433         return NULL;
434 }
435
436 /*
437  * SOF Driver enumeration.
438  */
439 int sof_machine_check(struct snd_sof_dev *sdev)
440 {
441         struct snd_sof_pdata *sof_pdata = sdev->pdata;
442         const struct sof_dev_desc *desc = sof_pdata->desc;
443         struct snd_soc_acpi_mach *mach;
444         int ret;
445
446 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)
447
448         /* find machine */
449         snd_sof_machine_select(sdev);
450         if (sof_pdata->machine) {
451                 snd_sof_set_mach_params(sof_pdata->machine, sdev->dev);
452                 return 0;
453         }
454
455 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)
456         dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
457         return -ENODEV;
458 #endif
459 #else
460         dev_warn(sdev->dev, "Force to use nocodec mode\n");
461 #endif
462         /* select nocodec mode */
463         dev_warn(sdev->dev, "Using nocodec machine driver\n");
464         mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
465         if (!mach)
466                 return -ENOMEM;
467
468         mach->drv_name = "sof-nocodec";
469         sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
470
471         ret = sof_nocodec_setup(sdev->dev, desc->ops, sof_pcm_dai_link_fixup);
472         if (ret < 0)
473                 return ret;
474
475         sof_pdata->machine = mach;
476         snd_sof_set_mach_params(sof_pdata->machine, sdev->dev);
477
478         return 0;
479 }
480 EXPORT_SYMBOL(sof_machine_check);
481
482 int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
483 {
484         struct snd_sof_pdata *plat_data = pdata;
485         const char *drv_name;
486         const void *mach;
487         int size;
488
489         drv_name = plat_data->machine->drv_name;
490         mach = plat_data->machine;
491         size = sizeof(*plat_data->machine);
492
493         /* register machine driver, pass machine info as pdata */
494         plat_data->pdev_mach =
495                 platform_device_register_data(sdev->dev, drv_name,
496                                               PLATFORM_DEVID_NONE, mach, size);
497         if (IS_ERR(plat_data->pdev_mach))
498                 return PTR_ERR(plat_data->pdev_mach);
499
500         dev_dbg(sdev->dev, "created machine %s\n",
501                 dev_name(&plat_data->pdev_mach->dev));
502
503         return 0;
504 }
505 EXPORT_SYMBOL(sof_machine_register);
506
507 void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
508 {
509         struct snd_sof_pdata *plat_data = pdata;
510
511         if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
512                 platform_device_unregister(plat_data->pdev_mach);
513 }
514 EXPORT_SYMBOL(sof_machine_unregister);