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