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