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