1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
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.
6 // Copyright(c) 2019 Intel Corporation. All rights reserved.
8 // Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
11 #include "sof-audio.h"
15 * helper to determine if there are only D0i3 compatible
18 bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
20 struct snd_pcm_substream *substream;
21 struct snd_sof_pcm *spcm;
22 bool d0i3_compatible_active = false;
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)
32 * substream->runtime being not NULL indicates
33 * that the stream is open. No need to check the
36 if (!spcm->stream[dir].d0i3_compatible)
39 d0i3_compatible_active = true;
43 return d0i3_compatible_active;
45 EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
47 bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
49 struct snd_sof_pcm *spcm;
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)
60 int sof_set_hw_params_upon_resume(struct device *dev)
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;
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.
73 list_for_each_entry(spcm, &sdev->pcm_list, list) {
74 for_each_pcm_streams(dir) {
76 * do not reset hw_params upon resume for streams that
77 * were kept running during suspend
79 if (spcm->stream[dir].suspend_ignored)
82 substream = spcm->stream[dir].substream;
83 if (!substream || !substream->runtime)
86 state = substream->runtime->status->state;
87 if (state == SNDRV_PCM_STATE_SUSPENDED)
88 spcm->prepared[dir] = false;
92 /* set internal flag for BE */
93 return snd_sof_dsp_hw_params_upon_resume(sdev);
96 static int sof_restore_kcontrols(struct device *dev)
98 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
99 struct snd_sof_control *scontrol;
100 int ipc_cmd, ctrl_type;
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;
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,
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,
135 "error: failed kcontrol value set for widget: %d\n",
145 const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
148 const struct snd_sof_widget *swidget;
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 =
154 if (pipeline->pipeline_id == pipeline_id)
161 int sof_restore_pipelines(struct device *dev)
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;
173 /* restore pipeline components */
174 list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
175 struct sof_ipc_comp_reply r;
177 /* skip if there is no private data */
178 if (!swidget->private)
181 ret = sof_pipeline_core_enable(sdev, swidget);
184 "error: failed to enable target core: %d\n",
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);
199 dai = swidget->private;
200 memcpy(comp, &dai->comp_dai,
201 sizeof(struct sof_ipc_comp_dai));
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));
207 ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd,
212 case snd_soc_dapm_scheduler:
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
220 pipeline = swidget->private;
221 ret = sof_load_pipeline_ipc(dev, pipeline, &r);
224 hdr = swidget->private;
225 ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd,
226 swidget->private, hdr->size,
232 "error: failed to load widget type %d with ID: %d\n",
233 swidget->widget->id, swidget->comp_id);
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;
244 /* skip if there's no private data */
245 if (!sroute->private)
248 connect = sroute->private;
251 ret = sof_ipc_tx_message(sdev->ipc,
253 connect, sizeof(*connect),
254 &reply, sizeof(reply));
257 "error: failed to load route sink %s control %s source %s\n",
259 sroute->route->control ? sroute->route->control
261 sroute->route->source);
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;
273 dev_err(dev, "error: no config for DAI %s\n",
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.
284 if (config->type == SOF_DAI_INTEL_HDA)
285 config->hda.link_dma_ch = DMA_CHAN_INVALID;
287 ret = sof_ipc_tx_message(sdev->ipc,
288 config->hdr.cmd, config,
290 &reply, sizeof(reply));
294 "error: failed to set dai config for %s\n",
301 /* complete pipeline */
302 list_for_each_entry(swidget, &sdev->widget_list, list) {
303 switch (swidget->id) {
304 case snd_soc_dapm_scheduler:
306 snd_sof_complete_pipeline(dev, swidget);
313 /* restore pipeline kcontrols */
314 ret = sof_restore_kcontrols(dev);
317 "error: restoring kcontrols after resume\n");
323 * Generic object lookup APIs.
326 struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
329 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
330 struct snd_sof_pcm *spcm;
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)
337 /* match with playback caps name if set */
338 if (*spcm->pcm.caps[0].name &&
339 !strcmp(spcm->pcm.caps[0].name, name))
342 /* match with capture caps name if set */
343 if (*spcm->pcm.caps[1].name &&
344 !strcmp(spcm->pcm.caps[1].name, name))
351 struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
352 unsigned int comp_id,
355 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
356 struct snd_sof_pcm *spcm;
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) {
371 struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp,
374 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
375 struct snd_sof_pcm *spcm;
377 list_for_each_entry(spcm, &sdev->pcm_list, list) {
378 if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id)
385 struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
388 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
389 struct snd_sof_widget *swidget;
391 list_for_each_entry(swidget, &sdev->widget_list, list) {
392 if (strcmp(name, swidget->widget->name) == 0)
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)
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;
408 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
409 type = snd_soc_dapm_aif_in;
411 type = snd_soc_dapm_aif_out;
413 list_for_each_entry(swidget, &sdev->widget_list, list) {
414 if (!strcmp(pcm_name, swidget->widget->sname) &&
422 struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
425 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
426 struct snd_sof_dai *dai;
428 list_for_each_entry(dai, &sdev->dai_list, list) {
429 if (dai->name && (strcmp(name, dai->name) == 0))
437 * SOF Driver enumeration.
439 int sof_machine_check(struct snd_sof_dev *sdev)
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;
446 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)
449 snd_sof_machine_select(sdev);
450 if (sof_pdata->machine) {
451 snd_sof_set_mach_params(sof_pdata->machine, sdev->dev);
455 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)
456 dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
460 dev_warn(sdev->dev, "Force to use nocodec mode\n");
462 /* select nocodec mode */
463 dev_warn(sdev->dev, "Using nocodec machine driver\n");
464 mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
468 mach->drv_name = "sof-nocodec";
469 sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
471 ret = sof_nocodec_setup(sdev->dev, desc->ops, sof_pcm_dai_link_fixup);
475 sof_pdata->machine = mach;
476 snd_sof_set_mach_params(sof_pdata->machine, sdev->dev);
480 EXPORT_SYMBOL(sof_machine_check);
482 int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
484 struct snd_sof_pdata *plat_data = pdata;
485 const char *drv_name;
489 drv_name = plat_data->machine->drv_name;
490 mach = plat_data->machine;
491 size = sizeof(*plat_data->machine);
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);
500 dev_dbg(sdev->dev, "created machine %s\n",
501 dev_name(&plat_data->pdev_mach->dev));
505 EXPORT_SYMBOL(sof_machine_register);
507 void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
509 struct snd_sof_pdata *plat_data = pdata;
511 if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
512 platform_device_unregister(plat_data->pdev_mach);
514 EXPORT_SYMBOL(sof_machine_unregister);