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