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