1029 lines · c
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 or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2019 Intel Corporation7//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 16static 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 30static 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 44static 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 clear88 * the complete flag89 */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 pipelines108 */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 122int 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}132EXPORT_SYMBOL(sof_widget_free);133 134static 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 DAPM155 * widget list and it needs to be set up before the widgets in the pipeline156 * are set up. The use_count for the scheduler widget is incremented for every157 * widget in a given pipeline to ensure that it is freed only after the last158 * 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 does197 * 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 217widget_free:218 /* widget use_count will be decremented by sof_widget_free() */219 sof_widget_free_unlocked(sdev, swidget);220 use_count_decremented = true;221pipe_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 }234use_count_dec:235 if (!use_count_decremented)236 swidget->use_count--;237 238 return ret;239}240 241int 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}251EXPORT_SYMBOL(sof_widget_setup);252 253int 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 295static 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 the308 * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM309 * 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 with348 * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker349 * 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 been363 * set up above. And if neither are in the DAPM list, the route shouldn't be364 * 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 list371 * with a route that might need to be set up. Check the use_count of the widget372 * that is not in the DAPM list to confirm if it is in use currently before setting373 * up the route.374 */375 if (src_widget_in_dapm_list)376 swidget = sroute->sink_widget;377 else378 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 sink389 * widget is freed during hw_free390 */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 405static void406sof_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 428sink_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 441static int442sof_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 474sink_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 widget502 * (DAI type for capture, AIF type for playback)503 */504static 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 widget541 * (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 */544static 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 not570 * already in the list. This will result in the pipelines getting added in the571 * 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 585sink_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 606static int607sof_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 each649 * widget is modified by the source widget in the path. Use a local650 * copy of the runtime params as the pipeline_params so that the runtime651 * 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 675int 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, assign691 * 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 for709 * 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 754widget_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 762int 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 compatible788 * streams active789 */790bool 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 indicates805 * that the stream is open. No need to check the806 * 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}817EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);818 819bool 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 832int 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 885struct 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 910struct 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 930struct 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 */945struct snd_sof_widget *946snd_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 else956 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 967struct 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 981static 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 */1004int 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}1008EXPORT_SYMBOL(sof_dai_get_mclk);1009 1010/*1011 * Helper to get SSP BCLK from a pcm_runtime.1012 * Return 0 if not exist.1013 */1014int 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}1018EXPORT_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 */1024int 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}1028EXPORT_SYMBOL(sof_dai_get_tdm_slots);1029