xref: /linux-6.15/sound/soc/sof/sof-audio.c (revision 169ec0a5)
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