1e149ca29SPierre-Louis Bossart // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2ee1e79b7SRanjani Sridharan //
3ee1e79b7SRanjani Sridharan // This file is provided under a dual BSD/GPLv2 license. When using or
4ee1e79b7SRanjani Sridharan // redistributing this file, you may do so under either license.
5ee1e79b7SRanjani Sridharan //
6293ad281SPierre-Louis Bossart // Copyright(c) 2019 Intel Corporation
7ee1e79b7SRanjani Sridharan //
8ee1e79b7SRanjani Sridharan // Author: Ranjani Sridharan <[email protected]>
9ee1e79b7SRanjani Sridharan //
10ee1e79b7SRanjani Sridharan
11b30b60a2SPierre-Louis Bossart #include <linux/bitfield.h>
12fa6e73d6SBard Liao #include <trace/events/sof.h>
13ee1e79b7SRanjani Sridharan #include "sof-audio.h"
14ee1e79b7SRanjani Sridharan #include "ops.h"
15ee1e79b7SRanjani Sridharan
is_virtual_widget(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * widget,const char * func)1690ce7538SBard Liao static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
1790ce7538SBard Liao const char *func)
1890ce7538SBard Liao {
1990ce7538SBard Liao switch (widget->id) {
2090ce7538SBard Liao case snd_soc_dapm_out_drv:
2190ce7538SBard Liao case snd_soc_dapm_output:
2290ce7538SBard Liao case snd_soc_dapm_input:
2390ce7538SBard Liao dev_dbg(sdev->dev, "%s: %s is a virtual widget\n", func, widget->name);
2490ce7538SBard Liao return true;
2590ce7538SBard Liao default:
2690ce7538SBard Liao return false;
2790ce7538SBard Liao }
2890ce7538SBard Liao }
2990ce7538SBard Liao
sof_reset_route_setup_status(struct snd_sof_dev * sdev,struct snd_sof_widget * widget)305fcdbb2dSRanjani Sridharan static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
315fcdbb2dSRanjani Sridharan {
32cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
335fcdbb2dSRanjani Sridharan struct snd_sof_route *sroute;
345fcdbb2dSRanjani Sridharan
355fcdbb2dSRanjani Sridharan list_for_each_entry(sroute, &sdev->route_list, list)
36d77d7795SRanjani Sridharan if (sroute->src_widget == widget || sroute->sink_widget == widget) {
37cd6afb06SPeter Ujfalusi if (sroute->setup && tplg_ops && tplg_ops->route_free)
38d77d7795SRanjani Sridharan tplg_ops->route_free(sdev, sroute);
39d77d7795SRanjani Sridharan
405fcdbb2dSRanjani Sridharan sroute->setup = false;
415fcdbb2dSRanjani Sridharan }
42d77d7795SRanjani Sridharan }
435fcdbb2dSRanjani Sridharan
sof_widget_free_unlocked(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)44f94f3915SPeter Ujfalusi static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
45f94f3915SPeter Ujfalusi struct snd_sof_widget *swidget)
468b001416SRanjani Sridharan {
47cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
4831ed8da1SRanjani Sridharan struct snd_sof_pipeline *spipe = swidget->spipe;
49051744b1SRanjani Sridharan int err = 0;
50051744b1SRanjani Sridharan int ret;
518b001416SRanjani Sridharan
528b001416SRanjani Sridharan if (!swidget->private)
538b001416SRanjani Sridharan return 0;
548b001416SRanjani Sridharan
55fa6e73d6SBard Liao trace_sof_widget_free(swidget);
56fa6e73d6SBard Liao
578b001416SRanjani Sridharan /* only free when use_count is 0 */
588b001416SRanjani Sridharan if (--swidget->use_count)
598b001416SRanjani Sridharan return 0;
608b001416SRanjani Sridharan
6133a3facdSRanjani Sridharan /* reset route setup status for all routes that contain this widget */
6233a3facdSRanjani Sridharan sof_reset_route_setup_status(sdev, swidget);
6333a3facdSRanjani Sridharan
64b66bfc3aSRanjani Sridharan /* free DAI config and continue to free widget even if it fails */
65b66bfc3aSRanjani Sridharan if (WIDGET_IS_DAI(swidget->id)) {
66b66bfc3aSRanjani Sridharan struct snd_sof_dai_config_data data;
67b66bfc3aSRanjani Sridharan unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_FREE;
68b66bfc3aSRanjani Sridharan
69b66bfc3aSRanjani Sridharan data.dai_data = DMA_CHAN_INVALID;
70b66bfc3aSRanjani Sridharan
71b66bfc3aSRanjani Sridharan if (tplg_ops && tplg_ops->dai_config) {
72b66bfc3aSRanjani Sridharan err = tplg_ops->dai_config(sdev, swidget, flags, &data);
73b66bfc3aSRanjani Sridharan if (err < 0)
74b66bfc3aSRanjani Sridharan dev_err(sdev->dev, "failed to free config for widget %s\n",
75b66bfc3aSRanjani Sridharan swidget->widget->name);
76b66bfc3aSRanjani Sridharan }
77b66bfc3aSRanjani Sridharan }
78b66bfc3aSRanjani Sridharan
799ea80748SRanjani Sridharan /* continue to disable core even if IPC fails */
80b66bfc3aSRanjani Sridharan if (tplg_ops && tplg_ops->widget_free) {
81b66bfc3aSRanjani Sridharan ret = tplg_ops->widget_free(sdev, swidget);
82b66bfc3aSRanjani Sridharan if (ret < 0 && !err)
83b66bfc3aSRanjani Sridharan err = ret;
84b66bfc3aSRanjani Sridharan }
859ea80748SRanjani Sridharan
869ea80748SRanjani Sridharan /*
8731ed8da1SRanjani Sridharan * decrement ref count for cores associated with all modules in the pipeline and clear
8831ed8da1SRanjani Sridharan * the complete flag
899ea80748SRanjani Sridharan */
9031ed8da1SRanjani Sridharan if (swidget->id == snd_soc_dapm_scheduler) {
9131ed8da1SRanjani Sridharan int i;
9231ed8da1SRanjani Sridharan
9331ed8da1SRanjani Sridharan for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {
9431ed8da1SRanjani Sridharan ret = snd_sof_dsp_core_put(sdev, i);
9540c2c63aSRanjani Sridharan if (ret < 0) {
9631ed8da1SRanjani Sridharan dev_err(sdev->dev, "failed to disable target core: %d for pipeline %s\n",
9731ed8da1SRanjani Sridharan i, swidget->widget->name);
9840c2c63aSRanjani Sridharan if (!err)
9940c2c63aSRanjani Sridharan err = ret;
1008b001416SRanjani Sridharan }
10131ed8da1SRanjani Sridharan }
10231ed8da1SRanjani Sridharan swidget->spipe->complete = 0;
10331ed8da1SRanjani Sridharan }
1048b001416SRanjani Sridharan
10540c2c63aSRanjani Sridharan /*
10640c2c63aSRanjani Sridharan * free the scheduler widget (same as pipe_widget) associated with the current swidget.
10740c2c63aSRanjani Sridharan * skip for static pipelines
10840c2c63aSRanjani Sridharan */
109743eb6c6SPierre-Louis Bossart if (swidget->spipe && swidget->dynamic_pipeline_widget &&
110743eb6c6SPierre-Louis Bossart swidget->id != snd_soc_dapm_scheduler) {
111743eb6c6SPierre-Louis Bossart ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
11240c2c63aSRanjani Sridharan if (ret < 0 && !err)
11340c2c63aSRanjani Sridharan err = ret;
11440c2c63aSRanjani Sridharan }
11540c2c63aSRanjani Sridharan
11640c2c63aSRanjani Sridharan if (!err)
1178b001416SRanjani Sridharan dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
1188b001416SRanjani Sridharan
11940c2c63aSRanjani Sridharan return err;
1208b001416SRanjani Sridharan }
121f94f3915SPeter Ujfalusi
sof_widget_free(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)122f94f3915SPeter Ujfalusi int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
123f94f3915SPeter Ujfalusi {
124f94f3915SPeter Ujfalusi int ret;
125f94f3915SPeter Ujfalusi
126f94f3915SPeter Ujfalusi mutex_lock(&swidget->setup_mutex);
127f94f3915SPeter Ujfalusi ret = sof_widget_free_unlocked(sdev, swidget);
128f94f3915SPeter Ujfalusi mutex_unlock(&swidget->setup_mutex);
129f94f3915SPeter Ujfalusi
130f94f3915SPeter Ujfalusi return ret;
131f94f3915SPeter Ujfalusi }
1328b001416SRanjani Sridharan EXPORT_SYMBOL(sof_widget_free);
1338b001416SRanjani Sridharan
sof_widget_setup_unlocked(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)134f94f3915SPeter Ujfalusi static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
135f94f3915SPeter Ujfalusi struct snd_sof_widget *swidget)
13693d71245SRanjani Sridharan {
137cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
13831ed8da1SRanjani Sridharan struct snd_sof_pipeline *spipe = swidget->spipe;
139955a6f13SPeter Ujfalusi bool use_count_decremented = false;
14093d71245SRanjani Sridharan int ret;
14131ed8da1SRanjani Sridharan int i;
14293d71245SRanjani Sridharan
14393d71245SRanjani Sridharan /* skip if there is no private data */
14493d71245SRanjani Sridharan if (!swidget->private)
14593d71245SRanjani Sridharan return 0;
14693d71245SRanjani Sridharan
147fa6e73d6SBard Liao trace_sof_widget_setup(swidget);
148fa6e73d6SBard Liao
1498b001416SRanjani Sridharan /* widget already set up */
1508b001416SRanjani Sridharan if (++swidget->use_count > 1)
1518b001416SRanjani Sridharan return 0;
1528b001416SRanjani Sridharan
15340c2c63aSRanjani Sridharan /*
15440c2c63aSRanjani Sridharan * The scheduler widget for a pipeline is not part of the connected DAPM
15540c2c63aSRanjani Sridharan * widget list and it needs to be set up before the widgets in the pipeline
15640c2c63aSRanjani Sridharan * are set up. The use_count for the scheduler widget is incremented for every
15740c2c63aSRanjani Sridharan * widget in a given pipeline to ensure that it is freed only after the last
15840c2c63aSRanjani Sridharan * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines.
15940c2c63aSRanjani Sridharan */
16040c2c63aSRanjani Sridharan if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
1619c04363dSRanjani Sridharan if (!swidget->spipe || !swidget->spipe->pipe_widget) {
1629c04363dSRanjani Sridharan dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name);
16340c2c63aSRanjani Sridharan ret = -EINVAL;
16440c2c63aSRanjani Sridharan goto use_count_dec;
16540c2c63aSRanjani Sridharan }
16640c2c63aSRanjani Sridharan
167f94f3915SPeter Ujfalusi ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget);
16840c2c63aSRanjani Sridharan if (ret < 0)
16940c2c63aSRanjani Sridharan goto use_count_dec;
17040c2c63aSRanjani Sridharan }
17140c2c63aSRanjani Sridharan
17231ed8da1SRanjani Sridharan /* update ref count for cores associated with all modules in the pipeline */
17331ed8da1SRanjani Sridharan if (swidget->id == snd_soc_dapm_scheduler) {
17431ed8da1SRanjani Sridharan for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {
17531ed8da1SRanjani Sridharan ret = snd_sof_dsp_core_get(sdev, i);
17693d71245SRanjani Sridharan if (ret < 0) {
17731ed8da1SRanjani Sridharan dev_err(sdev->dev, "failed to enable target core %d for pipeline %s\n",
17831ed8da1SRanjani Sridharan i, swidget->widget->name);
17940c2c63aSRanjani Sridharan goto pipe_widget_free;
18093d71245SRanjani Sridharan }
18131ed8da1SRanjani Sridharan }
18231ed8da1SRanjani Sridharan }
18393d71245SRanjani Sridharan
184051744b1SRanjani Sridharan /* setup widget in the DSP */
185cd6afb06SPeter Ujfalusi if (tplg_ops && tplg_ops->widget_setup) {
186051744b1SRanjani Sridharan ret = tplg_ops->widget_setup(sdev, swidget);
187051744b1SRanjani Sridharan if (ret < 0)
18831ed8da1SRanjani Sridharan goto pipe_widget_free;
1895fcdbb2dSRanjani Sridharan }
1905fcdbb2dSRanjani Sridharan
191051744b1SRanjani Sridharan /* send config for DAI components */
192051744b1SRanjani Sridharan if (WIDGET_IS_DAI(swidget->id)) {
193b66bfc3aSRanjani Sridharan unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
1949ea80748SRanjani Sridharan
195b66bfc3aSRanjani Sridharan /*
196b66bfc3aSRanjani Sridharan * The config flags saved during BE DAI hw_params will be used for IPC3. IPC4 does
197b66bfc3aSRanjani Sridharan * not use the flags argument.
198b66bfc3aSRanjani Sridharan */
199cd6afb06SPeter Ujfalusi if (tplg_ops && tplg_ops->dai_config) {
200051744b1SRanjani Sridharan ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);
201051744b1SRanjani Sridharan if (ret < 0)
202051744b1SRanjani Sridharan goto widget_free;
2035fcdbb2dSRanjani Sridharan }
2045f3aad73SRanjani Sridharan }
2055f3aad73SRanjani Sridharan
2065f3aad73SRanjani Sridharan /* restore kcontrols for widget */
207cd6afb06SPeter Ujfalusi if (tplg_ops && tplg_ops->control && tplg_ops->control->widget_kcontrol_setup) {
20850d4d8cfSPeter Ujfalusi ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);
20950d4d8cfSPeter Ujfalusi if (ret < 0)
210051744b1SRanjani Sridharan goto widget_free;
2115f3aad73SRanjani Sridharan }
2125f3aad73SRanjani Sridharan
21393d71245SRanjani Sridharan dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
21493d71245SRanjani Sridharan
2158b001416SRanjani Sridharan return 0;
2168b001416SRanjani Sridharan
217051744b1SRanjani Sridharan widget_free:
21831ed8da1SRanjani Sridharan /* widget use_count will be decremented by sof_widget_free() */
219f94f3915SPeter Ujfalusi sof_widget_free_unlocked(sdev, swidget);
220955a6f13SPeter Ujfalusi use_count_decremented = true;
22140c2c63aSRanjani Sridharan pipe_widget_free:
22231ed8da1SRanjani Sridharan if (swidget->id != snd_soc_dapm_scheduler) {
223f94f3915SPeter Ujfalusi sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
22431ed8da1SRanjani Sridharan } else {
22531ed8da1SRanjani Sridharan int j;
22631ed8da1SRanjani Sridharan
22731ed8da1SRanjani Sridharan /* decrement ref count for all cores that were updated previously */
22831ed8da1SRanjani Sridharan for_each_set_bit(j, &spipe->core_mask, sdev->num_cores) {
22931ed8da1SRanjani Sridharan if (j >= i)
23031ed8da1SRanjani Sridharan break;
23131ed8da1SRanjani Sridharan snd_sof_dsp_core_put(sdev, j);
23231ed8da1SRanjani Sridharan }
23331ed8da1SRanjani Sridharan }
2348b001416SRanjani Sridharan use_count_dec:
235955a6f13SPeter Ujfalusi if (!use_count_decremented)
2368b001416SRanjani Sridharan swidget->use_count--;
237955a6f13SPeter Ujfalusi
23893d71245SRanjani Sridharan return ret;
23993d71245SRanjani Sridharan }
240f94f3915SPeter Ujfalusi
sof_widget_setup(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)241f94f3915SPeter Ujfalusi int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
242f94f3915SPeter Ujfalusi {
243f94f3915SPeter Ujfalusi int ret;
244f94f3915SPeter Ujfalusi
245f94f3915SPeter Ujfalusi mutex_lock(&swidget->setup_mutex);
246f94f3915SPeter Ujfalusi ret = sof_widget_setup_unlocked(sdev, swidget);
247f94f3915SPeter Ujfalusi mutex_unlock(&swidget->setup_mutex);
248f94f3915SPeter Ujfalusi
24993d71245SRanjani Sridharan return ret;
25093d71245SRanjani Sridharan }
2518b001416SRanjani Sridharan EXPORT_SYMBOL(sof_widget_setup);
25293d71245SRanjani Sridharan
sof_route_setup(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * wsource,struct snd_soc_dapm_widget * wsink)2533816bbeaSRanjani Sridharan int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
2545fcdbb2dSRanjani Sridharan struct snd_soc_dapm_widget *wsink)
2555fcdbb2dSRanjani Sridharan {
256cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
2575fcdbb2dSRanjani Sridharan struct snd_sof_widget *src_widget = wsource->dobj.private;
2585fcdbb2dSRanjani Sridharan struct snd_sof_widget *sink_widget = wsink->dobj.private;
2595fcdbb2dSRanjani Sridharan struct snd_sof_route *sroute;
2605fcdbb2dSRanjani Sridharan bool route_found = false;
2615fcdbb2dSRanjani Sridharan
2625fcdbb2dSRanjani Sridharan /* ignore routes involving virtual widgets in topology */
26390ce7538SBard Liao if (is_virtual_widget(sdev, src_widget->widget, __func__) ||
26490ce7538SBard Liao is_virtual_widget(sdev, sink_widget->widget, __func__))
2655fcdbb2dSRanjani Sridharan return 0;
2665fcdbb2dSRanjani Sridharan
2675fcdbb2dSRanjani Sridharan /* find route matching source and sink widgets */
2685fcdbb2dSRanjani Sridharan list_for_each_entry(sroute, &sdev->route_list, list)
2695fcdbb2dSRanjani Sridharan if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
2705fcdbb2dSRanjani Sridharan route_found = true;
2715fcdbb2dSRanjani Sridharan break;
2725fcdbb2dSRanjani Sridharan }
2735fcdbb2dSRanjani Sridharan
2745fcdbb2dSRanjani Sridharan if (!route_found) {
2755fcdbb2dSRanjani Sridharan dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n",
2765fcdbb2dSRanjani Sridharan wsource->name, wsink->name);
2775fcdbb2dSRanjani Sridharan return -EINVAL;
2785fcdbb2dSRanjani Sridharan }
2795fcdbb2dSRanjani Sridharan
28085ec8560SRanjani Sridharan /* nothing to do if route is already set up */
28185ec8560SRanjani Sridharan if (sroute->setup)
28285ec8560SRanjani Sridharan return 0;
28385ec8560SRanjani Sridharan
284cd6afb06SPeter Ujfalusi if (tplg_ops && tplg_ops->route_setup) {
285cd6afb06SPeter Ujfalusi int ret = tplg_ops->route_setup(sdev, sroute);
28653154117SPeter Ujfalusi
28785ec8560SRanjani Sridharan if (ret < 0)
28885ec8560SRanjani Sridharan return ret;
28953154117SPeter Ujfalusi }
29085ec8560SRanjani Sridharan
29185ec8560SRanjani Sridharan sroute->setup = true;
29285ec8560SRanjani Sridharan return 0;
2935fcdbb2dSRanjani Sridharan }
2945fcdbb2dSRanjani Sridharan
sof_setup_pipeline_connections(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget_list * list,int dir)2955fcdbb2dSRanjani Sridharan static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
2965fcdbb2dSRanjani Sridharan struct snd_soc_dapm_widget_list *list, int dir)
2975fcdbb2dSRanjani Sridharan {
2988cd3cb17SChao Song const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
2995fcdbb2dSRanjani Sridharan struct snd_soc_dapm_widget *widget;
3008cd3cb17SChao Song struct snd_sof_route *sroute;
3015fcdbb2dSRanjani Sridharan struct snd_soc_dapm_path *p;
3028cd3cb17SChao Song int ret = 0;
3035fcdbb2dSRanjani Sridharan int i;
3045fcdbb2dSRanjani Sridharan
3055fcdbb2dSRanjani Sridharan /*
3065fcdbb2dSRanjani Sridharan * Set up connections between widgets in the sink/source paths based on direction.
3075fcdbb2dSRanjani Sridharan * Some non-SOF widgets exist in topology either for compatibility or for the
3085fcdbb2dSRanjani Sridharan * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM
3095fcdbb2dSRanjani Sridharan * events. But they are not handled by the firmware. So ignore them.
3105fcdbb2dSRanjani Sridharan */
3115fcdbb2dSRanjani Sridharan if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
3125fcdbb2dSRanjani Sridharan for_each_dapm_widgets(list, i, widget) {
3135fcdbb2dSRanjani Sridharan if (!widget->dobj.private)
3145fcdbb2dSRanjani Sridharan continue;
3155fcdbb2dSRanjani Sridharan
3164639029bSRanjani Sridharan snd_soc_dapm_widget_for_each_sink_path(widget, p) {
3174639029bSRanjani Sridharan if (!widget_in_list(list, p->sink))
3184639029bSRanjani Sridharan continue;
3194639029bSRanjani Sridharan
3205fcdbb2dSRanjani Sridharan if (p->sink->dobj.private) {
3215fcdbb2dSRanjani Sridharan ret = sof_route_setup(sdev, widget, p->sink);
3225fcdbb2dSRanjani Sridharan if (ret < 0)
3235fcdbb2dSRanjani Sridharan return ret;
3245fcdbb2dSRanjani Sridharan }
3255fcdbb2dSRanjani Sridharan }
3264639029bSRanjani Sridharan }
3275fcdbb2dSRanjani Sridharan } else {
3285fcdbb2dSRanjani Sridharan for_each_dapm_widgets(list, i, widget) {
3295fcdbb2dSRanjani Sridharan if (!widget->dobj.private)
3305fcdbb2dSRanjani Sridharan continue;
3315fcdbb2dSRanjani Sridharan
3324639029bSRanjani Sridharan snd_soc_dapm_widget_for_each_source_path(widget, p) {
3334639029bSRanjani Sridharan if (!widget_in_list(list, p->source))
3344639029bSRanjani Sridharan continue;
3354639029bSRanjani Sridharan
3365fcdbb2dSRanjani Sridharan if (p->source->dobj.private) {
3375fcdbb2dSRanjani Sridharan ret = sof_route_setup(sdev, p->source, widget);
3385fcdbb2dSRanjani Sridharan if (ret < 0)
3395fcdbb2dSRanjani Sridharan return ret;
3405fcdbb2dSRanjani Sridharan }
3415fcdbb2dSRanjani Sridharan }
3425fcdbb2dSRanjani Sridharan }
3434639029bSRanjani Sridharan }
3445fcdbb2dSRanjani Sridharan
3458cd3cb17SChao Song /*
3468cd3cb17SChao Song * The above loop handles connections between widgets that belong to the DAPM widget list.
3478cd3cb17SChao Song * This is not sufficient to handle loopback cases between pipelines configured with
3488cd3cb17SChao Song * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker
3498cd3cb17SChao Song * protection module.
3508cd3cb17SChao Song */
3518cd3cb17SChao Song list_for_each_entry(sroute, &sdev->route_list, list) {
3528cd3cb17SChao Song bool src_widget_in_dapm_list, sink_widget_in_dapm_list;
3538cd3cb17SChao Song struct snd_sof_widget *swidget;
3548cd3cb17SChao Song
3558cd3cb17SChao Song if (sroute->setup)
3568cd3cb17SChao Song continue;
3578cd3cb17SChao Song
3588cd3cb17SChao Song src_widget_in_dapm_list = widget_in_list(list, sroute->src_widget->widget);
3598cd3cb17SChao Song sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget);
3608cd3cb17SChao Song
3618cd3cb17SChao Song /*
3628cd3cb17SChao Song * if both source and sink are in the DAPM list, the route must already have been
3638cd3cb17SChao Song * set up above. And if neither are in the DAPM list, the route shouldn't be
3648cd3cb17SChao Song * handled now.
3658cd3cb17SChao Song */
3668cd3cb17SChao Song if (src_widget_in_dapm_list == sink_widget_in_dapm_list)
3678cd3cb17SChao Song continue;
3688cd3cb17SChao Song
3698cd3cb17SChao Song /*
3708cd3cb17SChao Song * At this point either the source widget or the sink widget is in the DAPM list
3718cd3cb17SChao Song * with a route that might need to be set up. Check the use_count of the widget
3728cd3cb17SChao Song * that is not in the DAPM list to confirm if it is in use currently before setting
3738cd3cb17SChao Song * up the route.
3748cd3cb17SChao Song */
3758cd3cb17SChao Song if (src_widget_in_dapm_list)
3768cd3cb17SChao Song swidget = sroute->sink_widget;
3778cd3cb17SChao Song else
3788cd3cb17SChao Song swidget = sroute->src_widget;
3798cd3cb17SChao Song
3808cd3cb17SChao Song mutex_lock(&swidget->setup_mutex);
3818cd3cb17SChao Song if (!swidget->use_count) {
3828cd3cb17SChao Song mutex_unlock(&swidget->setup_mutex);
3838cd3cb17SChao Song continue;
3848cd3cb17SChao Song }
3858cd3cb17SChao Song
3868cd3cb17SChao Song if (tplg_ops && tplg_ops->route_setup) {
3878cd3cb17SChao Song /*
3888cd3cb17SChao Song * this route will get freed when either the source widget or the sink
3898cd3cb17SChao Song * widget is freed during hw_free
3908cd3cb17SChao Song */
3918cd3cb17SChao Song ret = tplg_ops->route_setup(sdev, sroute);
3928cd3cb17SChao Song if (!ret)
3938cd3cb17SChao Song sroute->setup = true;
3948cd3cb17SChao Song }
3958cd3cb17SChao Song
3968cd3cb17SChao Song mutex_unlock(&swidget->setup_mutex);
3978cd3cb17SChao Song
3988cd3cb17SChao Song if (ret < 0)
3998cd3cb17SChao Song return ret;
4008cd3cb17SChao Song }
4018cd3cb17SChao Song
4025fcdbb2dSRanjani Sridharan return 0;
4035fcdbb2dSRanjani Sridharan }
4045fcdbb2dSRanjani Sridharan
40566344c6dSRanjani Sridharan static void
sof_unprepare_widgets_in_path(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * widget,struct snd_soc_dapm_widget_list * list)4064639029bSRanjani Sridharan sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
4074639029bSRanjani Sridharan struct snd_soc_dapm_widget_list *list)
40866344c6dSRanjani Sridharan {
409cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
41066344c6dSRanjani Sridharan struct snd_sof_widget *swidget = widget->dobj.private;
411cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_widget_ops *widget_ops;
41266344c6dSRanjani Sridharan struct snd_soc_dapm_path *p;
41366344c6dSRanjani Sridharan
4140557864eSBard Liao if (is_virtual_widget(sdev, widget, __func__))
4150557864eSBard Liao return;
4160557864eSBard Liao
417cc755b43SBard Liao /* skip if the widget is in use or if it is already unprepared */
4180ad84b11SRanjani Sridharan if (!swidget || !swidget->prepared || swidget->use_count > 0)
419cc755b43SBard Liao goto sink_unprepare;
42066344c6dSRanjani Sridharan
421cd6afb06SPeter Ujfalusi widget_ops = tplg_ops ? tplg_ops->widget : NULL;
422cd6afb06SPeter Ujfalusi if (widget_ops && widget_ops[widget->id].ipc_unprepare)
42366344c6dSRanjani Sridharan /* unprepare the source widget */
42466344c6dSRanjani Sridharan widget_ops[widget->id].ipc_unprepare(swidget);
425ce59804dSRander Wang
42666344c6dSRanjani Sridharan swidget->prepared = false;
42766344c6dSRanjani Sridharan
428cc755b43SBard Liao sink_unprepare:
42966344c6dSRanjani Sridharan /* unprepare all widgets in the sink paths */
43066344c6dSRanjani Sridharan snd_soc_dapm_widget_for_each_sink_path(widget, p) {
4314639029bSRanjani Sridharan if (!widget_in_list(list, p->sink))
4324639029bSRanjani Sridharan continue;
43366344c6dSRanjani Sridharan if (!p->walking && p->sink->dobj.private) {
43466344c6dSRanjani Sridharan p->walking = true;
4354639029bSRanjani Sridharan sof_unprepare_widgets_in_path(sdev, p->sink, list);
43666344c6dSRanjani Sridharan p->walking = false;
43766344c6dSRanjani Sridharan }
43866344c6dSRanjani Sridharan }
43966344c6dSRanjani Sridharan }
44066344c6dSRanjani Sridharan
44166344c6dSRanjani Sridharan static int
sof_prepare_widgets_in_path(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * widget,struct snd_pcm_hw_params * fe_params,struct snd_sof_platform_stream_params * platform_params,struct snd_pcm_hw_params * pipeline_params,int dir,struct snd_soc_dapm_widget_list * list)44266344c6dSRanjani Sridharan sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
44366344c6dSRanjani Sridharan struct snd_pcm_hw_params *fe_params,
44466344c6dSRanjani Sridharan struct snd_sof_platform_stream_params *platform_params,
4454639029bSRanjani Sridharan struct snd_pcm_hw_params *pipeline_params, int dir,
4464639029bSRanjani Sridharan struct snd_soc_dapm_widget_list *list)
44766344c6dSRanjani Sridharan {
448cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
44966344c6dSRanjani Sridharan struct snd_sof_widget *swidget = widget->dobj.private;
450cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_widget_ops *widget_ops;
45166344c6dSRanjani Sridharan struct snd_soc_dapm_path *p;
45266344c6dSRanjani Sridharan int ret;
45366344c6dSRanjani Sridharan
4540557864eSBard Liao if (is_virtual_widget(sdev, widget, __func__))
4550557864eSBard Liao return 0;
4560557864eSBard Liao
457cd6afb06SPeter Ujfalusi widget_ops = tplg_ops ? tplg_ops->widget : NULL;
458cd6afb06SPeter Ujfalusi if (!widget_ops)
459cd6afb06SPeter Ujfalusi return 0;
460cd6afb06SPeter Ujfalusi
4610ad84b11SRanjani Sridharan if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)
46266344c6dSRanjani Sridharan goto sink_prepare;
46366344c6dSRanjani Sridharan
46466344c6dSRanjani Sridharan /* prepare the source widget */
46566344c6dSRanjani Sridharan ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,
46666344c6dSRanjani Sridharan pipeline_params, dir);
46766344c6dSRanjani Sridharan if (ret < 0) {
46866344c6dSRanjani Sridharan dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);
46966344c6dSRanjani Sridharan return ret;
47066344c6dSRanjani Sridharan }
47166344c6dSRanjani Sridharan
47266344c6dSRanjani Sridharan swidget->prepared = true;
47366344c6dSRanjani Sridharan
47466344c6dSRanjani Sridharan sink_prepare:
47566344c6dSRanjani Sridharan /* prepare all widgets in the sink paths */
47666344c6dSRanjani Sridharan snd_soc_dapm_widget_for_each_sink_path(widget, p) {
4774639029bSRanjani Sridharan if (!widget_in_list(list, p->sink))
4784639029bSRanjani Sridharan continue;
47966344c6dSRanjani Sridharan if (!p->walking && p->sink->dobj.private) {
48066344c6dSRanjani Sridharan p->walking = true;
48166344c6dSRanjani Sridharan ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
4824639029bSRanjani Sridharan platform_params, pipeline_params, dir,
4834639029bSRanjani Sridharan list);
48466344c6dSRanjani Sridharan p->walking = false;
48566344c6dSRanjani Sridharan if (ret < 0) {
48666344c6dSRanjani Sridharan /* unprepare the source widget */
487fb429360SPeter Ujfalusi if (widget_ops[widget->id].ipc_unprepare &&
488*6f2a43e3SPeter Ujfalusi swidget && swidget->prepared && swidget->use_count == 0) {
48966344c6dSRanjani Sridharan widget_ops[widget->id].ipc_unprepare(swidget);
49066344c6dSRanjani Sridharan swidget->prepared = false;
49166344c6dSRanjani Sridharan }
49266344c6dSRanjani Sridharan return ret;
49366344c6dSRanjani Sridharan }
49466344c6dSRanjani Sridharan }
49566344c6dSRanjani Sridharan }
49666344c6dSRanjani Sridharan
49766344c6dSRanjani Sridharan return 0;
49866344c6dSRanjani Sridharan }
49966344c6dSRanjani Sridharan
5005da0590aSRanjani Sridharan /*
5015da0590aSRanjani Sridharan * free all widgets in the sink path starting from the source widget
5025da0590aSRanjani Sridharan * (DAI type for capture, AIF type for playback)
5035da0590aSRanjani Sridharan */
sof_free_widgets_in_path(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * widget,int dir,struct snd_sof_pcm * spcm)5045da0590aSRanjani Sridharan static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
50519137532SRanjani Sridharan int dir, struct snd_sof_pcm *spcm)
5065da0590aSRanjani Sridharan {
50719137532SRanjani Sridharan struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
5085da0590aSRanjani Sridharan struct snd_soc_dapm_path *p;
5095da0590aSRanjani Sridharan int err;
5105da0590aSRanjani Sridharan int ret = 0;
5115da0590aSRanjani Sridharan
5120557864eSBard Liao if (is_virtual_widget(sdev, widget, __func__))
5130557864eSBard Liao return 0;
5140557864eSBard Liao
51573ea6609SRanjani Sridharan if (widget->dobj.private) {
5165da0590aSRanjani Sridharan err = sof_widget_free(sdev, widget->dobj.private);
5175da0590aSRanjani Sridharan if (err < 0)
5185da0590aSRanjani Sridharan ret = err;
5195da0590aSRanjani Sridharan }
5205da0590aSRanjani Sridharan
52173ea6609SRanjani Sridharan /* free all widgets in the sink paths even in case of error to keep use counts balanced */
52273ea6609SRanjani Sridharan snd_soc_dapm_widget_for_each_sink_path(widget, p) {
52373ea6609SRanjani Sridharan if (!p->walking) {
5244639029bSRanjani Sridharan if (!widget_in_list(list, p->sink))
5254639029bSRanjani Sridharan continue;
5265da0590aSRanjani Sridharan
52773ea6609SRanjani Sridharan p->walking = true;
5285da0590aSRanjani Sridharan
52919137532SRanjani Sridharan err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm);
5305da0590aSRanjani Sridharan if (err < 0)
5315da0590aSRanjani Sridharan ret = err;
5325da0590aSRanjani Sridharan p->walking = false;
5335da0590aSRanjani Sridharan }
5345da0590aSRanjani Sridharan }
5355da0590aSRanjani Sridharan
5365da0590aSRanjani Sridharan return ret;
5375da0590aSRanjani Sridharan }
5385da0590aSRanjani Sridharan
5395da0590aSRanjani Sridharan /*
5405da0590aSRanjani Sridharan * set up all widgets in the sink path starting from the source widget
5415da0590aSRanjani Sridharan * (DAI type for capture, AIF type for playback).
5425da0590aSRanjani Sridharan * The error path in this function ensures that all successfully set up widgets getting freed.
5435da0590aSRanjani Sridharan */
sof_set_up_widgets_in_path(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * widget,int dir,struct snd_sof_pcm * spcm)5445da0590aSRanjani Sridharan static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
54519137532SRanjani Sridharan int dir, struct snd_sof_pcm *spcm)
5465da0590aSRanjani Sridharan {
54719137532SRanjani Sridharan struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
54819137532SRanjani Sridharan struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
54919137532SRanjani Sridharan struct snd_sof_widget *swidget = widget->dobj.private;
5509c04363dSRanjani Sridharan struct snd_sof_pipeline *spipe;
5515da0590aSRanjani Sridharan struct snd_soc_dapm_path *p;
5525da0590aSRanjani Sridharan int ret;
5535da0590aSRanjani Sridharan
5540557864eSBard Liao if (is_virtual_widget(sdev, widget, __func__))
5550557864eSBard Liao return 0;
5560557864eSBard Liao
55719137532SRanjani Sridharan if (swidget) {
55819137532SRanjani Sridharan int i;
55919137532SRanjani Sridharan
5605da0590aSRanjani Sridharan ret = sof_widget_setup(sdev, widget->dobj.private);
5615da0590aSRanjani Sridharan if (ret < 0)
5625da0590aSRanjani Sridharan return ret;
56319137532SRanjani Sridharan
56419137532SRanjani Sridharan /* skip populating the pipe_widgets array if it is NULL */
5659c04363dSRanjani Sridharan if (!pipeline_list->pipelines)
56619137532SRanjani Sridharan goto sink_setup;
56719137532SRanjani Sridharan
56819137532SRanjani Sridharan /*
56919137532SRanjani Sridharan * Add the widget's pipe_widget to the list of pipelines to be triggered if not
57019137532SRanjani Sridharan * already in the list. This will result in the pipelines getting added in the
57119137532SRanjani Sridharan * order source to sink.
57219137532SRanjani Sridharan */
57319137532SRanjani Sridharan for (i = 0; i < pipeline_list->count; i++) {
5749c04363dSRanjani Sridharan spipe = pipeline_list->pipelines[i];
5759c04363dSRanjani Sridharan if (spipe == swidget->spipe)
57619137532SRanjani Sridharan break;
5775da0590aSRanjani Sridharan }
5785da0590aSRanjani Sridharan
57919137532SRanjani Sridharan if (i == pipeline_list->count) {
58019137532SRanjani Sridharan pipeline_list->count++;
5819c04363dSRanjani Sridharan pipeline_list->pipelines[i] = swidget->spipe;
58219137532SRanjani Sridharan }
58319137532SRanjani Sridharan }
58419137532SRanjani Sridharan
58519137532SRanjani Sridharan sink_setup:
58673ea6609SRanjani Sridharan snd_soc_dapm_widget_for_each_sink_path(widget, p) {
58773ea6609SRanjani Sridharan if (!p->walking) {
5884639029bSRanjani Sridharan if (!widget_in_list(list, p->sink))
5894639029bSRanjani Sridharan continue;
5904639029bSRanjani Sridharan
59173ea6609SRanjani Sridharan p->walking = true;
5925da0590aSRanjani Sridharan
59319137532SRanjani Sridharan ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm);
5945da0590aSRanjani Sridharan p->walking = false;
59573ea6609SRanjani Sridharan if (ret < 0) {
59619137532SRanjani Sridharan if (swidget)
59719137532SRanjani Sridharan sof_widget_free(sdev, swidget);
5985da0590aSRanjani Sridharan return ret;
5995da0590aSRanjani Sridharan }
6005da0590aSRanjani Sridharan }
6015da0590aSRanjani Sridharan }
6025da0590aSRanjani Sridharan
6035da0590aSRanjani Sridharan return 0;
6045da0590aSRanjani Sridharan }
6055da0590aSRanjani Sridharan
6065da0590aSRanjani Sridharan static int
sof_walk_widgets_in_order(struct snd_sof_dev * sdev,struct snd_sof_pcm * spcm,struct snd_pcm_hw_params * fe_params,struct snd_sof_platform_stream_params * platform_params,int dir,enum sof_widget_op op)60719137532SRanjani Sridharan sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
60866344c6dSRanjani Sridharan struct snd_pcm_hw_params *fe_params,
60966344c6dSRanjani Sridharan struct snd_sof_platform_stream_params *platform_params, int dir,
61066344c6dSRanjani Sridharan enum sof_widget_op op)
6115da0590aSRanjani Sridharan {
61219137532SRanjani Sridharan struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
6135da0590aSRanjani Sridharan struct snd_soc_dapm_widget *widget;
61466344c6dSRanjani Sridharan char *str;
61566344c6dSRanjani Sridharan int ret = 0;
61666344c6dSRanjani Sridharan int i;
6175da0590aSRanjani Sridharan
61819137532SRanjani Sridharan if (!list)
61919137532SRanjani Sridharan return 0;
62019137532SRanjani Sridharan
6215da0590aSRanjani Sridharan for_each_dapm_widgets(list, i, widget) {
6220557864eSBard Liao if (is_virtual_widget(sdev, widget, __func__))
6230557864eSBard Liao continue;
6240557864eSBard Liao
6255da0590aSRanjani Sridharan /* starting widget for playback is AIF type */
626fcc4348aSBard Liao if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
6275da0590aSRanjani Sridharan continue;
6285da0590aSRanjani Sridharan
6295da0590aSRanjani Sridharan /* starting widget for capture is DAI type */
630fcc4348aSBard Liao if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out)
6315da0590aSRanjani Sridharan continue;
6325da0590aSRanjani Sridharan
6335da0590aSRanjani Sridharan switch (op) {
6345da0590aSRanjani Sridharan case SOF_WIDGET_SETUP:
63519137532SRanjani Sridharan ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm);
63666344c6dSRanjani Sridharan str = "set up";
6375da0590aSRanjani Sridharan break;
6385da0590aSRanjani Sridharan case SOF_WIDGET_FREE:
63919137532SRanjani Sridharan ret = sof_free_widgets_in_path(sdev, widget, dir, spcm);
64066344c6dSRanjani Sridharan str = "free";
64166344c6dSRanjani Sridharan break;
64266344c6dSRanjani Sridharan case SOF_WIDGET_PREPARE:
64366344c6dSRanjani Sridharan {
64466344c6dSRanjani Sridharan struct snd_pcm_hw_params pipeline_params;
64566344c6dSRanjani Sridharan
64666344c6dSRanjani Sridharan str = "prepare";
64766344c6dSRanjani Sridharan /*
64866344c6dSRanjani Sridharan * When walking the list of connected widgets, the pipeline_params for each
64966344c6dSRanjani Sridharan * widget is modified by the source widget in the path. Use a local
65066344c6dSRanjani Sridharan * copy of the runtime params as the pipeline_params so that the runtime
65166344c6dSRanjani Sridharan * params does not get overwritten.
65266344c6dSRanjani Sridharan */
65366344c6dSRanjani Sridharan memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
65466344c6dSRanjani Sridharan
6554639029bSRanjani Sridharan ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params,
6564639029bSRanjani Sridharan &pipeline_params, dir, list);
65766344c6dSRanjani Sridharan break;
65866344c6dSRanjani Sridharan }
65966344c6dSRanjani Sridharan case SOF_WIDGET_UNPREPARE:
6604639029bSRanjani Sridharan sof_unprepare_widgets_in_path(sdev, widget, list);
6615da0590aSRanjani Sridharan break;
6625da0590aSRanjani Sridharan default:
6635da0590aSRanjani Sridharan dev_err(sdev->dev, "Invalid widget op %d\n", op);
6645da0590aSRanjani Sridharan return -EINVAL;
6655da0590aSRanjani Sridharan }
6665da0590aSRanjani Sridharan if (ret < 0) {
66766344c6dSRanjani Sridharan dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
6685da0590aSRanjani Sridharan return ret;
6695da0590aSRanjani Sridharan }
6705da0590aSRanjani Sridharan }
6715da0590aSRanjani Sridharan
6725da0590aSRanjani Sridharan return 0;
6735da0590aSRanjani Sridharan }
6745da0590aSRanjani Sridharan
sof_widget_list_setup(struct snd_sof_dev * sdev,struct snd_sof_pcm * spcm,struct snd_pcm_hw_params * fe_params,struct snd_sof_platform_stream_params * platform_params,int dir)67566344c6dSRanjani Sridharan int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
67666344c6dSRanjani Sridharan struct snd_pcm_hw_params *fe_params,
67766344c6dSRanjani Sridharan struct snd_sof_platform_stream_params *platform_params,
67866344c6dSRanjani Sridharan int dir)
6795fcdbb2dSRanjani Sridharan {
680cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
6815fcdbb2dSRanjani Sridharan struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
6825fcdbb2dSRanjani Sridharan struct snd_soc_dapm_widget *widget;
6835da0590aSRanjani Sridharan int i, ret;
6845fcdbb2dSRanjani Sridharan
6855fcdbb2dSRanjani Sridharan /* nothing to set up */
6865fcdbb2dSRanjani Sridharan if (!list)
6875fcdbb2dSRanjani Sridharan return 0;
6885fcdbb2dSRanjani Sridharan
68966344c6dSRanjani Sridharan /*
69066344c6dSRanjani Sridharan * Prepare widgets for set up. The prepare step is used to allocate memory, assign
69166344c6dSRanjani Sridharan * instance ID and pick the widget configuration based on the runtime PCM params.
69266344c6dSRanjani Sridharan */
69319137532SRanjani Sridharan ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
69466344c6dSRanjani Sridharan dir, SOF_WIDGET_PREPARE);
69540c2c63aSRanjani Sridharan if (ret < 0)
6965da0590aSRanjani Sridharan return ret;
6975fcdbb2dSRanjani Sridharan
69866344c6dSRanjani Sridharan /* Set up is used to send the IPC to the DSP to create the widget */
69919137532SRanjani Sridharan ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
70066344c6dSRanjani Sridharan dir, SOF_WIDGET_SETUP);
70166344c6dSRanjani Sridharan if (ret < 0) {
702c7e328f1SPierre-Louis Bossart sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
70366344c6dSRanjani Sridharan dir, SOF_WIDGET_UNPREPARE);
70466344c6dSRanjani Sridharan return ret;
70566344c6dSRanjani Sridharan }
70666344c6dSRanjani Sridharan
7075fcdbb2dSRanjani Sridharan /*
7085fcdbb2dSRanjani Sridharan * error in setting pipeline connections will result in route status being reset for
7095fcdbb2dSRanjani Sridharan * routes that were successfully set up when the widgets are freed.
7105fcdbb2dSRanjani Sridharan */
7115fcdbb2dSRanjani Sridharan ret = sof_setup_pipeline_connections(sdev, list, dir);
7125fcdbb2dSRanjani Sridharan if (ret < 0)
7135fcdbb2dSRanjani Sridharan goto widget_free;
7145fcdbb2dSRanjani Sridharan
7155fcdbb2dSRanjani Sridharan /* complete pipelines */
7165fcdbb2dSRanjani Sridharan for_each_dapm_widgets(list, i, widget) {
7175fcdbb2dSRanjani Sridharan struct snd_sof_widget *swidget = widget->dobj.private;
7185fcdbb2dSRanjani Sridharan struct snd_sof_widget *pipe_widget;
7199c04363dSRanjani Sridharan struct snd_sof_pipeline *spipe;
7205fcdbb2dSRanjani Sridharan
72128d40e7aSPeter Ujfalusi if (!swidget || sdev->dspless_mode_selected)
7225fcdbb2dSRanjani Sridharan continue;
7235fcdbb2dSRanjani Sridharan
7249c04363dSRanjani Sridharan spipe = swidget->spipe;
7259c04363dSRanjani Sridharan if (!spipe) {
7269c04363dSRanjani Sridharan dev_err(sdev->dev, "no pipeline found for %s\n",
7279c04363dSRanjani Sridharan swidget->widget->name);
7289c04363dSRanjani Sridharan ret = -EINVAL;
7299c04363dSRanjani Sridharan goto widget_free;
7309c04363dSRanjani Sridharan }
7319c04363dSRanjani Sridharan
7329c04363dSRanjani Sridharan pipe_widget = spipe->pipe_widget;
7335fcdbb2dSRanjani Sridharan if (!pipe_widget) {
7345fcdbb2dSRanjani Sridharan dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
7355fcdbb2dSRanjani Sridharan swidget->widget->name);
7365fcdbb2dSRanjani Sridharan ret = -EINVAL;
7375fcdbb2dSRanjani Sridharan goto widget_free;
7385fcdbb2dSRanjani Sridharan }
7395fcdbb2dSRanjani Sridharan
7409c04363dSRanjani Sridharan if (spipe->complete)
7415fcdbb2dSRanjani Sridharan continue;
7425fcdbb2dSRanjani Sridharan
743cd6afb06SPeter Ujfalusi if (tplg_ops && tplg_ops->pipeline_complete) {
7449c04363dSRanjani Sridharan spipe->complete = tplg_ops->pipeline_complete(sdev, pipe_widget);
7459c04363dSRanjani Sridharan if (spipe->complete < 0) {
7469c04363dSRanjani Sridharan ret = spipe->complete;
7475fcdbb2dSRanjani Sridharan goto widget_free;
7485fcdbb2dSRanjani Sridharan }
7495fcdbb2dSRanjani Sridharan }
75061ad28ffSRanjani Sridharan }
7515fcdbb2dSRanjani Sridharan
7525fcdbb2dSRanjani Sridharan return 0;
7535fcdbb2dSRanjani Sridharan
7545fcdbb2dSRanjani Sridharan widget_free:
75519137532SRanjani Sridharan sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir,
75666344c6dSRanjani Sridharan SOF_WIDGET_FREE);
75719137532SRanjani Sridharan sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
7585fcdbb2dSRanjani Sridharan
7595fcdbb2dSRanjani Sridharan return ret;
7605fcdbb2dSRanjani Sridharan }
7615fcdbb2dSRanjani Sridharan
sof_widget_list_free(struct snd_sof_dev * sdev,struct snd_sof_pcm * spcm,int dir)7625fcdbb2dSRanjani Sridharan int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
7635fcdbb2dSRanjani Sridharan {
76419137532SRanjani Sridharan struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
7655fcdbb2dSRanjani Sridharan struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
7665da0590aSRanjani Sridharan int ret;
7675fcdbb2dSRanjani Sridharan
7685fcdbb2dSRanjani Sridharan /* nothing to free */
7695fcdbb2dSRanjani Sridharan if (!list)
7705fcdbb2dSRanjani Sridharan return 0;
7715fcdbb2dSRanjani Sridharan
77266344c6dSRanjani Sridharan /* send IPC to free widget in the DSP */
77319137532SRanjani Sridharan ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE);
77466344c6dSRanjani Sridharan
77566344c6dSRanjani Sridharan /* unprepare the widget */
77619137532SRanjani Sridharan sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
7775fcdbb2dSRanjani Sridharan
7785fcdbb2dSRanjani Sridharan snd_soc_dapm_dai_free_widgets(&list);
7795fcdbb2dSRanjani Sridharan spcm->stream[dir].list = NULL;
7805fcdbb2dSRanjani Sridharan
78119137532SRanjani Sridharan pipeline_list->count = 0;
78219137532SRanjani Sridharan
7835da0590aSRanjani Sridharan return ret;
7845fcdbb2dSRanjani Sridharan }
7855fcdbb2dSRanjani Sridharan
786de23a838SRanjani Sridharan /*
787de23a838SRanjani Sridharan * helper to determine if there are only D0i3 compatible
788de23a838SRanjani Sridharan * streams active
789de23a838SRanjani Sridharan */
snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev * sdev)790de23a838SRanjani Sridharan bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
791de23a838SRanjani Sridharan {
792de23a838SRanjani Sridharan struct snd_pcm_substream *substream;
793de23a838SRanjani Sridharan struct snd_sof_pcm *spcm;
794de23a838SRanjani Sridharan bool d0i3_compatible_active = false;
795de23a838SRanjani Sridharan int dir;
796de23a838SRanjani Sridharan
797de23a838SRanjani Sridharan list_for_each_entry(spcm, &sdev->pcm_list, list) {
798525c4107SKuninori Morimoto for_each_pcm_streams(dir) {
799de23a838SRanjani Sridharan substream = spcm->stream[dir].substream;
800de23a838SRanjani Sridharan if (!substream || !substream->runtime)
801de23a838SRanjani Sridharan continue;
802de23a838SRanjani Sridharan
803de23a838SRanjani Sridharan /*
8048932f0cbSRandy Dunlap * substream->runtime being not NULL indicates
805de23a838SRanjani Sridharan * that the stream is open. No need to check the
806de23a838SRanjani Sridharan * stream state.
807de23a838SRanjani Sridharan */
808de23a838SRanjani Sridharan if (!spcm->stream[dir].d0i3_compatible)
809de23a838SRanjani Sridharan return false;
810de23a838SRanjani Sridharan
811de23a838SRanjani Sridharan d0i3_compatible_active = true;
812de23a838SRanjani Sridharan }
813de23a838SRanjani Sridharan }
814de23a838SRanjani Sridharan
815de23a838SRanjani Sridharan return d0i3_compatible_active;
816de23a838SRanjani Sridharan }
817de23a838SRanjani Sridharan EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
818de23a838SRanjani Sridharan
snd_sof_stream_suspend_ignored(struct snd_sof_dev * sdev)819700d1677SRanjani Sridharan bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
820ee1e79b7SRanjani Sridharan {
821ee1e79b7SRanjani Sridharan struct snd_sof_pcm *spcm;
822ee1e79b7SRanjani Sridharan
823ee1e79b7SRanjani Sridharan list_for_each_entry(spcm, &sdev->pcm_list, list) {
824ee1e79b7SRanjani Sridharan if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
825ee1e79b7SRanjani Sridharan spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
826ee1e79b7SRanjani Sridharan return true;
827ee1e79b7SRanjani Sridharan }
828ee1e79b7SRanjani Sridharan
829ee1e79b7SRanjani Sridharan return false;
830ee1e79b7SRanjani Sridharan }
831ee1e79b7SRanjani Sridharan
8328b001416SRanjani Sridharan /*
833ee1e79b7SRanjani Sridharan * Generic object lookup APIs.
834ee1e79b7SRanjani Sridharan */
835ee1e79b7SRanjani Sridharan
snd_sof_find_spcm_name(struct snd_soc_component * scomp,const char * name)836ee1e79b7SRanjani Sridharan struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
837ee1e79b7SRanjani Sridharan const char *name)
838ee1e79b7SRanjani Sridharan {
839ee1e79b7SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
840ee1e79b7SRanjani Sridharan struct snd_sof_pcm *spcm;
841ee1e79b7SRanjani Sridharan
842ee1e79b7SRanjani Sridharan list_for_each_entry(spcm, &sdev->pcm_list, list) {
843ee1e79b7SRanjani Sridharan /* match with PCM dai name */
844ee1e79b7SRanjani Sridharan if (strcmp(spcm->pcm.dai_name, name) == 0)
845ee1e79b7SRanjani Sridharan return spcm;
846ee1e79b7SRanjani Sridharan
847ee1e79b7SRanjani Sridharan /* match with playback caps name if set */
848ee1e79b7SRanjani Sridharan if (*spcm->pcm.caps[0].name &&
849ee1e79b7SRanjani Sridharan !strcmp(spcm->pcm.caps[0].name, name))
850ee1e79b7SRanjani Sridharan return spcm;
851ee1e79b7SRanjani Sridharan
852ee1e79b7SRanjani Sridharan /* match with capture caps name if set */
853ee1e79b7SRanjani Sridharan if (*spcm->pcm.caps[1].name &&
854ee1e79b7SRanjani Sridharan !strcmp(spcm->pcm.caps[1].name, name))
855ee1e79b7SRanjani Sridharan return spcm;
856ee1e79b7SRanjani Sridharan }
857ee1e79b7SRanjani Sridharan
858ee1e79b7SRanjani Sridharan return NULL;
859ee1e79b7SRanjani Sridharan }
860ee1e79b7SRanjani Sridharan
snd_sof_find_spcm_comp(struct snd_soc_component * scomp,unsigned int comp_id,int * direction)861ee1e79b7SRanjani Sridharan struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
862ee1e79b7SRanjani Sridharan unsigned int comp_id,
863ee1e79b7SRanjani Sridharan int *direction)
864ee1e79b7SRanjani Sridharan {
865ee1e79b7SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
866ee1e79b7SRanjani Sridharan struct snd_sof_pcm *spcm;
867ee1e79b7SRanjani Sridharan int dir;
868ee1e79b7SRanjani Sridharan
869ee1e79b7SRanjani Sridharan list_for_each_entry(spcm, &sdev->pcm_list, list) {
870525c4107SKuninori Morimoto for_each_pcm_streams(dir) {
871ee1e79b7SRanjani Sridharan if (spcm->stream[dir].comp_id == comp_id) {
872ee1e79b7SRanjani Sridharan *direction = dir;
873ee1e79b7SRanjani Sridharan return spcm;
874ee1e79b7SRanjani Sridharan }
875ee1e79b7SRanjani Sridharan }
876ee1e79b7SRanjani Sridharan }
877ee1e79b7SRanjani Sridharan
878ee1e79b7SRanjani Sridharan return NULL;
879ee1e79b7SRanjani Sridharan }
880ee1e79b7SRanjani Sridharan
snd_sof_find_swidget(struct snd_soc_component * scomp,const char * name)881ee1e79b7SRanjani Sridharan struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
882ee1e79b7SRanjani Sridharan const char *name)
883ee1e79b7SRanjani Sridharan {
884ee1e79b7SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
885ee1e79b7SRanjani Sridharan struct snd_sof_widget *swidget;
886ee1e79b7SRanjani Sridharan
887ee1e79b7SRanjani Sridharan list_for_each_entry(swidget, &sdev->widget_list, list) {
888ee1e79b7SRanjani Sridharan if (strcmp(name, swidget->widget->name) == 0)
889ee1e79b7SRanjani Sridharan return swidget;
890ee1e79b7SRanjani Sridharan }
891ee1e79b7SRanjani Sridharan
892ee1e79b7SRanjani Sridharan return NULL;
893ee1e79b7SRanjani Sridharan }
894ee1e79b7SRanjani Sridharan
895ee1e79b7SRanjani Sridharan /* find widget by stream name and direction */
896ee1e79b7SRanjani Sridharan struct snd_sof_widget *
snd_sof_find_swidget_sname(struct snd_soc_component * scomp,const char * pcm_name,int dir)897ee1e79b7SRanjani Sridharan snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
898ee1e79b7SRanjani Sridharan const char *pcm_name, int dir)
899ee1e79b7SRanjani Sridharan {
900ee1e79b7SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
901ee1e79b7SRanjani Sridharan struct snd_sof_widget *swidget;
902ee1e79b7SRanjani Sridharan enum snd_soc_dapm_type type;
903ee1e79b7SRanjani Sridharan
904ee1e79b7SRanjani Sridharan if (dir == SNDRV_PCM_STREAM_PLAYBACK)
905ee1e79b7SRanjani Sridharan type = snd_soc_dapm_aif_in;
906ee1e79b7SRanjani Sridharan else
907ee1e79b7SRanjani Sridharan type = snd_soc_dapm_aif_out;
908ee1e79b7SRanjani Sridharan
909ee1e79b7SRanjani Sridharan list_for_each_entry(swidget, &sdev->widget_list, list) {
910ee1e79b7SRanjani Sridharan if (!strcmp(pcm_name, swidget->widget->sname) &&
911ee1e79b7SRanjani Sridharan swidget->id == type)
912ee1e79b7SRanjani Sridharan return swidget;
913ee1e79b7SRanjani Sridharan }
914ee1e79b7SRanjani Sridharan
915ee1e79b7SRanjani Sridharan return NULL;
916ee1e79b7SRanjani Sridharan }
917ee1e79b7SRanjani Sridharan
snd_sof_find_dai(struct snd_soc_component * scomp,const char * name)918ee1e79b7SRanjani Sridharan struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
919ee1e79b7SRanjani Sridharan const char *name)
920ee1e79b7SRanjani Sridharan {
921ee1e79b7SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
922ee1e79b7SRanjani Sridharan struct snd_sof_dai *dai;
923ee1e79b7SRanjani Sridharan
924ee1e79b7SRanjani Sridharan list_for_each_entry(dai, &sdev->dai_list, list) {
925ee1e79b7SRanjani Sridharan if (dai->name && (strcmp(name, dai->name) == 0))
926ee1e79b7SRanjani Sridharan return dai;
927ee1e79b7SRanjani Sridharan }
928ee1e79b7SRanjani Sridharan
929ee1e79b7SRanjani Sridharan return NULL;
930ee1e79b7SRanjani Sridharan }
931ee1e79b7SRanjani Sridharan
sof_dai_get_param(struct snd_soc_pcm_runtime * rtd,int param_type)9326073c477SBrent Lu static int sof_dai_get_param(struct snd_soc_pcm_runtime *rtd, int param_type)
933b951b51eSKeyon Jie {
934b951b51eSKeyon Jie struct snd_soc_component *component =
935b951b51eSKeyon Jie snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
936b951b51eSKeyon Jie struct snd_sof_dai *dai =
937b951b51eSKeyon Jie snd_sof_find_dai(component, (char *)rtd->dai_link->name);
93885f7a8b6SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
939cd6afb06SPeter Ujfalusi const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
940b951b51eSKeyon Jie
941b951b51eSKeyon Jie /* use the tplg configured mclk if existed */
94285f7a8b6SRanjani Sridharan if (!dai)
943b951b51eSKeyon Jie return 0;
944b951b51eSKeyon Jie
9456073c477SBrent Lu if (tplg_ops && tplg_ops->dai_get_param)
9466073c477SBrent Lu return tplg_ops->dai_get_param(sdev, dai, param_type);
94785f7a8b6SRanjani Sridharan
94885f7a8b6SRanjani Sridharan return 0;
949b951b51eSKeyon Jie }
950bc619cfcSBrent Lu
951bc619cfcSBrent Lu /*
952bc619cfcSBrent Lu * Helper to get SSP MCLK from a pcm_runtime.
953bc619cfcSBrent Lu * Return 0 if not exist.
954bc619cfcSBrent Lu */
sof_dai_get_mclk(struct snd_soc_pcm_runtime * rtd)955bc619cfcSBrent Lu int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd)
956bc619cfcSBrent Lu {
9576073c477SBrent Lu return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_MCLK);
958bc619cfcSBrent Lu }
959b951b51eSKeyon Jie EXPORT_SYMBOL(sof_dai_get_mclk);
960b951b51eSKeyon Jie
961b951b51eSKeyon Jie /*
962bc619cfcSBrent Lu * Helper to get SSP BCLK from a pcm_runtime.
963bc619cfcSBrent Lu * Return 0 if not exist.
964bc619cfcSBrent Lu */
sof_dai_get_bclk(struct snd_soc_pcm_runtime * rtd)965bc619cfcSBrent Lu int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd)
966bc619cfcSBrent Lu {
9676073c477SBrent Lu return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_BCLK);
968bc619cfcSBrent Lu }
969bc619cfcSBrent Lu EXPORT_SYMBOL(sof_dai_get_bclk);
9701deba6e2SBrent Lu
9711deba6e2SBrent Lu /*
9721deba6e2SBrent Lu * Helper to get SSP TDM slot number from a pcm_runtime.
9731deba6e2SBrent Lu * Return 0 if not exist.
9741deba6e2SBrent Lu */
sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime * rtd)9751deba6e2SBrent Lu int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd)
9761deba6e2SBrent Lu {
9771deba6e2SBrent Lu return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS);
9781deba6e2SBrent Lu }
9791deba6e2SBrent Lu EXPORT_SYMBOL(sof_dai_get_tdm_slots);
980