xref: /openbmc/linux/sound/soc/sof/sof-audio.c (revision d6b6592ac6d11eab91e6758d224eac35f4122aca)
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 //
6ee1e79b7SRanjani Sridharan // Copyright(c) 2019 Intel Corporation. All rights reserved.
7ee1e79b7SRanjani Sridharan //
8ee1e79b7SRanjani Sridharan // Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
9ee1e79b7SRanjani Sridharan //
10ee1e79b7SRanjani Sridharan 
11b30b60a2SPierre-Louis Bossart #include <linux/bitfield.h>
12fa6e73d6SBard Liao #include <trace/events/sof.h>
13ee1e79b7SRanjani Sridharan #include "sof-audio.h"
146ace85b9SChunxu Li #include "sof-of-dev.h"
15ee1e79b7SRanjani Sridharan #include "ops.h"
16ee1e79b7SRanjani Sridharan 
is_virtual_widget(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * widget,const char * func)1790ce7538SBard Liao static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
1890ce7538SBard Liao 			      const char *func)
1990ce7538SBard Liao {
2090ce7538SBard Liao 	switch (widget->id) {
2190ce7538SBard Liao 	case snd_soc_dapm_out_drv:
2290ce7538SBard Liao 	case snd_soc_dapm_output:
2390ce7538SBard Liao 	case snd_soc_dapm_input:
2490ce7538SBard Liao 		dev_dbg(sdev->dev, "%s: %s is a virtual widget\n", func, widget->name);
2590ce7538SBard Liao 		return true;
2690ce7538SBard Liao 	default:
2790ce7538SBard Liao 		return false;
2890ce7538SBard Liao 	}
2990ce7538SBard Liao }
3090ce7538SBard Liao 
sof_reset_route_setup_status(struct snd_sof_dev * sdev,struct snd_sof_widget * widget)315fcdbb2dSRanjani Sridharan static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
325fcdbb2dSRanjani Sridharan {
33cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
345fcdbb2dSRanjani Sridharan 	struct snd_sof_route *sroute;
355fcdbb2dSRanjani Sridharan 
365fcdbb2dSRanjani Sridharan 	list_for_each_entry(sroute, &sdev->route_list, list)
37d77d7795SRanjani Sridharan 		if (sroute->src_widget == widget || sroute->sink_widget == widget) {
38cd6afb06SPeter Ujfalusi 			if (sroute->setup && tplg_ops && tplg_ops->route_free)
39d77d7795SRanjani Sridharan 				tplg_ops->route_free(sdev, sroute);
40d77d7795SRanjani Sridharan 
415fcdbb2dSRanjani Sridharan 			sroute->setup = false;
425fcdbb2dSRanjani Sridharan 		}
43d77d7795SRanjani Sridharan }
445fcdbb2dSRanjani Sridharan 
sof_widget_free_unlocked(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)45f94f3915SPeter Ujfalusi static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
46f94f3915SPeter Ujfalusi 				    struct snd_sof_widget *swidget)
478b001416SRanjani Sridharan {
48cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
493c124f09SRanjani Sridharan 	struct snd_sof_pipeline *spipe = swidget->spipe;
509c04363dSRanjani Sridharan 	struct snd_sof_widget *pipe_widget;
51051744b1SRanjani Sridharan 	int err = 0;
52051744b1SRanjani Sridharan 	int ret;
538b001416SRanjani Sridharan 
548b001416SRanjani Sridharan 	if (!swidget->private)
558b001416SRanjani Sridharan 		return 0;
568b001416SRanjani Sridharan 
57fa6e73d6SBard Liao 	trace_sof_widget_free(swidget);
58fa6e73d6SBard Liao 
598b001416SRanjani Sridharan 	/* only free when use_count is 0 */
608b001416SRanjani Sridharan 	if (--swidget->use_count)
618b001416SRanjani Sridharan 		return 0;
628b001416SRanjani Sridharan 
639c04363dSRanjani Sridharan 	pipe_widget = swidget->spipe->pipe_widget;
649c04363dSRanjani Sridharan 
6533a3facdSRanjani Sridharan 	/* reset route setup status for all routes that contain this widget */
6633a3facdSRanjani Sridharan 	sof_reset_route_setup_status(sdev, swidget);
6733a3facdSRanjani Sridharan 
68b66bfc3aSRanjani Sridharan 	/* free DAI config and continue to free widget even if it fails */
69b66bfc3aSRanjani Sridharan 	if (WIDGET_IS_DAI(swidget->id)) {
70b66bfc3aSRanjani Sridharan 		struct snd_sof_dai_config_data data;
71b66bfc3aSRanjani Sridharan 		unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_FREE;
72b66bfc3aSRanjani Sridharan 
73b66bfc3aSRanjani Sridharan 		data.dai_data = DMA_CHAN_INVALID;
74b66bfc3aSRanjani Sridharan 
75b66bfc3aSRanjani Sridharan 		if (tplg_ops && tplg_ops->dai_config) {
76b66bfc3aSRanjani Sridharan 			err = tplg_ops->dai_config(sdev, swidget, flags, &data);
77b66bfc3aSRanjani Sridharan 			if (err < 0)
78b66bfc3aSRanjani Sridharan 				dev_err(sdev->dev, "failed to free config for widget %s\n",
79b66bfc3aSRanjani Sridharan 					swidget->widget->name);
80b66bfc3aSRanjani Sridharan 		}
81b66bfc3aSRanjani Sridharan 	}
82b66bfc3aSRanjani Sridharan 
839ea80748SRanjani Sridharan 	/* continue to disable core even if IPC fails */
84b66bfc3aSRanjani Sridharan 	if (tplg_ops && tplg_ops->widget_free) {
85b66bfc3aSRanjani Sridharan 		ret = tplg_ops->widget_free(sdev, swidget);
86b66bfc3aSRanjani Sridharan 		if (ret < 0 && !err)
87b66bfc3aSRanjani Sridharan 			err = ret;
88b66bfc3aSRanjani Sridharan 	}
899ea80748SRanjani Sridharan 
909ea80748SRanjani Sridharan 	/*
913c124f09SRanjani Sridharan 	 * decrement ref count for cores associated with all modules in the pipeline and clear
923c124f09SRanjani Sridharan 	 * the complete flag
939ea80748SRanjani Sridharan 	 */
943c124f09SRanjani Sridharan 	if (swidget->id == snd_soc_dapm_scheduler) {
953c124f09SRanjani Sridharan 		int i;
963c124f09SRanjani Sridharan 
973c124f09SRanjani Sridharan 		for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {
983c124f09SRanjani Sridharan 			ret = snd_sof_dsp_core_put(sdev, i);
9940c2c63aSRanjani Sridharan 			if (ret < 0) {
1003c124f09SRanjani Sridharan 				dev_err(sdev->dev, "failed to disable target core: %d for pipeline %s\n",
1013c124f09SRanjani Sridharan 					i, swidget->widget->name);
10240c2c63aSRanjani Sridharan 				if (!err)
10340c2c63aSRanjani Sridharan 					err = ret;
1048b001416SRanjani Sridharan 			}
1053c124f09SRanjani Sridharan 		}
1063c124f09SRanjani Sridharan 		swidget->spipe->complete = 0;
1073c124f09SRanjani Sridharan 	}
1088b001416SRanjani Sridharan 
10940c2c63aSRanjani Sridharan 	/*
11040c2c63aSRanjani Sridharan 	 * free the scheduler widget (same as pipe_widget) associated with the current swidget.
11140c2c63aSRanjani Sridharan 	 * skip for static pipelines
11240c2c63aSRanjani Sridharan 	 */
11340c2c63aSRanjani Sridharan 	if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
114f94f3915SPeter Ujfalusi 		ret = sof_widget_free_unlocked(sdev, pipe_widget);
11540c2c63aSRanjani Sridharan 		if (ret < 0 && !err)
11640c2c63aSRanjani Sridharan 			err = ret;
11740c2c63aSRanjani Sridharan 	}
11840c2c63aSRanjani Sridharan 
11940c2c63aSRanjani Sridharan 	if (!err)
1208b001416SRanjani Sridharan 		dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
1218b001416SRanjani Sridharan 
12240c2c63aSRanjani Sridharan 	return err;
1238b001416SRanjani Sridharan }
124f94f3915SPeter Ujfalusi 
sof_widget_free(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)125f94f3915SPeter Ujfalusi int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
126f94f3915SPeter Ujfalusi {
127f94f3915SPeter Ujfalusi 	int ret;
128f94f3915SPeter Ujfalusi 
129f94f3915SPeter Ujfalusi 	mutex_lock(&swidget->setup_mutex);
130f94f3915SPeter Ujfalusi 	ret = sof_widget_free_unlocked(sdev, swidget);
131f94f3915SPeter Ujfalusi 	mutex_unlock(&swidget->setup_mutex);
132f94f3915SPeter Ujfalusi 
133f94f3915SPeter Ujfalusi 	return ret;
134f94f3915SPeter Ujfalusi }
1358b001416SRanjani Sridharan EXPORT_SYMBOL(sof_widget_free);
1368b001416SRanjani Sridharan 
sof_widget_setup_unlocked(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)137f94f3915SPeter Ujfalusi static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
138f94f3915SPeter Ujfalusi 				     struct snd_sof_widget *swidget)
13993d71245SRanjani Sridharan {
140cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
1413c124f09SRanjani Sridharan 	struct snd_sof_pipeline *spipe = swidget->spipe;
142955a6f13SPeter Ujfalusi 	bool use_count_decremented = false;
14393d71245SRanjani Sridharan 	int ret;
1443c124f09SRanjani Sridharan 	int i;
14593d71245SRanjani Sridharan 
14693d71245SRanjani Sridharan 	/* skip if there is no private data */
14793d71245SRanjani Sridharan 	if (!swidget->private)
14893d71245SRanjani Sridharan 		return 0;
14993d71245SRanjani Sridharan 
150fa6e73d6SBard Liao 	trace_sof_widget_setup(swidget);
151fa6e73d6SBard Liao 
1528b001416SRanjani Sridharan 	/* widget already set up */
1538b001416SRanjani Sridharan 	if (++swidget->use_count > 1)
1548b001416SRanjani Sridharan 		return 0;
1558b001416SRanjani Sridharan 
15640c2c63aSRanjani Sridharan 	/*
15740c2c63aSRanjani Sridharan 	 * The scheduler widget for a pipeline is not part of the connected DAPM
15840c2c63aSRanjani Sridharan 	 * widget list and it needs to be set up before the widgets in the pipeline
15940c2c63aSRanjani Sridharan 	 * are set up. The use_count for the scheduler widget is incremented for every
16040c2c63aSRanjani Sridharan 	 * widget in a given pipeline to ensure that it is freed only after the last
16140c2c63aSRanjani Sridharan 	 * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines.
16240c2c63aSRanjani Sridharan 	 */
16340c2c63aSRanjani Sridharan 	if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
1649c04363dSRanjani Sridharan 		if (!swidget->spipe || !swidget->spipe->pipe_widget) {
1659c04363dSRanjani Sridharan 			dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name);
16640c2c63aSRanjani Sridharan 			ret = -EINVAL;
16740c2c63aSRanjani Sridharan 			goto use_count_dec;
16840c2c63aSRanjani Sridharan 		}
16940c2c63aSRanjani Sridharan 
170f94f3915SPeter Ujfalusi 		ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget);
17140c2c63aSRanjani Sridharan 		if (ret < 0)
17240c2c63aSRanjani Sridharan 			goto use_count_dec;
17340c2c63aSRanjani Sridharan 	}
17440c2c63aSRanjani Sridharan 
1753c124f09SRanjani Sridharan 	/* update ref count for cores associated with all modules in the pipeline */
1763c124f09SRanjani Sridharan 	if (swidget->id == snd_soc_dapm_scheduler) {
1773c124f09SRanjani Sridharan 		for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {
1783c124f09SRanjani Sridharan 			ret = snd_sof_dsp_core_get(sdev, i);
17993d71245SRanjani Sridharan 			if (ret < 0) {
1803c124f09SRanjani Sridharan 				dev_err(sdev->dev, "failed to enable target core %d for pipeline %s\n",
1813c124f09SRanjani Sridharan 					i, swidget->widget->name);
18240c2c63aSRanjani Sridharan 				goto pipe_widget_free;
18393d71245SRanjani Sridharan 			}
1843c124f09SRanjani Sridharan 		}
1853c124f09SRanjani Sridharan 	}
18693d71245SRanjani Sridharan 
187051744b1SRanjani Sridharan 	/* setup widget in the DSP */
188cd6afb06SPeter Ujfalusi 	if (tplg_ops && tplg_ops->widget_setup) {
189051744b1SRanjani Sridharan 		ret = tplg_ops->widget_setup(sdev, swidget);
190051744b1SRanjani Sridharan 		if (ret < 0)
1913c124f09SRanjani Sridharan 			goto pipe_widget_free;
1925fcdbb2dSRanjani Sridharan 	}
1935fcdbb2dSRanjani Sridharan 
194051744b1SRanjani Sridharan 	/* send config for DAI components */
195051744b1SRanjani Sridharan 	if (WIDGET_IS_DAI(swidget->id)) {
196b66bfc3aSRanjani Sridharan 		unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
1979ea80748SRanjani Sridharan 
198b66bfc3aSRanjani Sridharan 		/*
199b66bfc3aSRanjani Sridharan 		 * The config flags saved during BE DAI hw_params will be used for IPC3. IPC4 does
200b66bfc3aSRanjani Sridharan 		 * not use the flags argument.
201b66bfc3aSRanjani Sridharan 		 */
202cd6afb06SPeter Ujfalusi 		if (tplg_ops && tplg_ops->dai_config) {
203051744b1SRanjani Sridharan 			ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);
204051744b1SRanjani Sridharan 			if (ret < 0)
205051744b1SRanjani Sridharan 				goto widget_free;
2065fcdbb2dSRanjani Sridharan 		}
2075f3aad73SRanjani Sridharan 	}
2085f3aad73SRanjani Sridharan 
2095f3aad73SRanjani Sridharan 	/* restore kcontrols for widget */
210cd6afb06SPeter Ujfalusi 	if (tplg_ops && tplg_ops->control && tplg_ops->control->widget_kcontrol_setup) {
21150d4d8cfSPeter Ujfalusi 		ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);
21250d4d8cfSPeter Ujfalusi 		if (ret < 0)
213051744b1SRanjani Sridharan 			goto widget_free;
2145f3aad73SRanjani Sridharan 	}
2155f3aad73SRanjani Sridharan 
21693d71245SRanjani Sridharan 	dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
21793d71245SRanjani Sridharan 
2188b001416SRanjani Sridharan 	return 0;
2198b001416SRanjani Sridharan 
220051744b1SRanjani Sridharan widget_free:
2213c124f09SRanjani Sridharan 	/* widget use_count will be decremented by sof_widget_free() */
222f94f3915SPeter Ujfalusi 	sof_widget_free_unlocked(sdev, swidget);
223955a6f13SPeter Ujfalusi 	use_count_decremented = true;
22440c2c63aSRanjani Sridharan pipe_widget_free:
2253c124f09SRanjani Sridharan 	if (swidget->id != snd_soc_dapm_scheduler) {
226f94f3915SPeter Ujfalusi 		sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
2273c124f09SRanjani Sridharan 	} else {
2283c124f09SRanjani Sridharan 		int j;
2293c124f09SRanjani Sridharan 
2303c124f09SRanjani Sridharan 		/* decrement ref count for all cores that were updated previously */
2313c124f09SRanjani Sridharan 		for_each_set_bit(j, &spipe->core_mask, sdev->num_cores) {
2323c124f09SRanjani Sridharan 			if (j >= i)
2333c124f09SRanjani Sridharan 				break;
2343c124f09SRanjani Sridharan 			snd_sof_dsp_core_put(sdev, j);
2353c124f09SRanjani Sridharan 		}
2363c124f09SRanjani Sridharan 	}
2378b001416SRanjani Sridharan use_count_dec:
238955a6f13SPeter Ujfalusi 	if (!use_count_decremented)
2398b001416SRanjani Sridharan 		swidget->use_count--;
240955a6f13SPeter Ujfalusi 
24193d71245SRanjani Sridharan 	return ret;
24293d71245SRanjani Sridharan }
243f94f3915SPeter Ujfalusi 
sof_widget_setup(struct snd_sof_dev * sdev,struct snd_sof_widget * swidget)244f94f3915SPeter Ujfalusi int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
245f94f3915SPeter Ujfalusi {
246f94f3915SPeter Ujfalusi 	int ret;
247f94f3915SPeter Ujfalusi 
248f94f3915SPeter Ujfalusi 	mutex_lock(&swidget->setup_mutex);
249f94f3915SPeter Ujfalusi 	ret = sof_widget_setup_unlocked(sdev, swidget);
250f94f3915SPeter Ujfalusi 	mutex_unlock(&swidget->setup_mutex);
251f94f3915SPeter Ujfalusi 
25293d71245SRanjani Sridharan 	return ret;
25393d71245SRanjani Sridharan }
2548b001416SRanjani Sridharan EXPORT_SYMBOL(sof_widget_setup);
25593d71245SRanjani Sridharan 
sof_route_setup(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * wsource,struct snd_soc_dapm_widget * wsink)2563816bbeaSRanjani Sridharan int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
2575fcdbb2dSRanjani Sridharan 		    struct snd_soc_dapm_widget *wsink)
2585fcdbb2dSRanjani Sridharan {
259cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
2605fcdbb2dSRanjani Sridharan 	struct snd_sof_widget *src_widget = wsource->dobj.private;
2615fcdbb2dSRanjani Sridharan 	struct snd_sof_widget *sink_widget = wsink->dobj.private;
2625fcdbb2dSRanjani Sridharan 	struct snd_sof_route *sroute;
2635fcdbb2dSRanjani Sridharan 	bool route_found = false;
2645fcdbb2dSRanjani Sridharan 
2655fcdbb2dSRanjani Sridharan 	/* ignore routes involving virtual widgets in topology */
26690ce7538SBard Liao 	if (is_virtual_widget(sdev, src_widget->widget, __func__) ||
26790ce7538SBard Liao 	    is_virtual_widget(sdev, sink_widget->widget, __func__))
2685fcdbb2dSRanjani Sridharan 		return 0;
2695fcdbb2dSRanjani Sridharan 
2705fcdbb2dSRanjani Sridharan 	/* find route matching source and sink widgets */
2715fcdbb2dSRanjani Sridharan 	list_for_each_entry(sroute, &sdev->route_list, list)
2725fcdbb2dSRanjani Sridharan 		if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
2735fcdbb2dSRanjani Sridharan 			route_found = true;
2745fcdbb2dSRanjani Sridharan 			break;
2755fcdbb2dSRanjani Sridharan 		}
2765fcdbb2dSRanjani Sridharan 
2775fcdbb2dSRanjani Sridharan 	if (!route_found) {
2785fcdbb2dSRanjani Sridharan 		dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n",
2795fcdbb2dSRanjani Sridharan 			wsource->name, wsink->name);
2805fcdbb2dSRanjani Sridharan 		return -EINVAL;
2815fcdbb2dSRanjani Sridharan 	}
2825fcdbb2dSRanjani Sridharan 
28385ec8560SRanjani Sridharan 	/* nothing to do if route is already set up */
28485ec8560SRanjani Sridharan 	if (sroute->setup)
28585ec8560SRanjani Sridharan 		return 0;
28685ec8560SRanjani Sridharan 
287cd6afb06SPeter Ujfalusi 	if (tplg_ops && tplg_ops->route_setup) {
288cd6afb06SPeter Ujfalusi 		int ret = tplg_ops->route_setup(sdev, sroute);
28953154117SPeter Ujfalusi 
29085ec8560SRanjani Sridharan 		if (ret < 0)
29185ec8560SRanjani Sridharan 			return ret;
29253154117SPeter Ujfalusi 	}
29385ec8560SRanjani Sridharan 
29485ec8560SRanjani Sridharan 	sroute->setup = true;
29585ec8560SRanjani Sridharan 	return 0;
2965fcdbb2dSRanjani Sridharan }
2975fcdbb2dSRanjani Sridharan 
sof_setup_pipeline_connections(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget_list * list,int dir)2985fcdbb2dSRanjani Sridharan static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
2995fcdbb2dSRanjani Sridharan 					  struct snd_soc_dapm_widget_list *list, int dir)
3005fcdbb2dSRanjani Sridharan {
3018cd3cb17SChao Song 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
3025fcdbb2dSRanjani Sridharan 	struct snd_soc_dapm_widget *widget;
3038cd3cb17SChao Song 	struct snd_sof_route *sroute;
3045fcdbb2dSRanjani Sridharan 	struct snd_soc_dapm_path *p;
3058cd3cb17SChao Song 	int ret = 0;
3065fcdbb2dSRanjani Sridharan 	int i;
3075fcdbb2dSRanjani Sridharan 
3085fcdbb2dSRanjani Sridharan 	/*
3095fcdbb2dSRanjani Sridharan 	 * Set up connections between widgets in the sink/source paths based on direction.
3105fcdbb2dSRanjani Sridharan 	 * Some non-SOF widgets exist in topology either for compatibility or for the
3115fcdbb2dSRanjani Sridharan 	 * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM
3125fcdbb2dSRanjani Sridharan 	 * events. But they are not handled by the firmware. So ignore them.
3135fcdbb2dSRanjani Sridharan 	 */
3145fcdbb2dSRanjani Sridharan 	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
3155fcdbb2dSRanjani Sridharan 		for_each_dapm_widgets(list, i, widget) {
3165fcdbb2dSRanjani Sridharan 			if (!widget->dobj.private)
3175fcdbb2dSRanjani Sridharan 				continue;
3185fcdbb2dSRanjani Sridharan 
3194639029bSRanjani Sridharan 			snd_soc_dapm_widget_for_each_sink_path(widget, p) {
3204639029bSRanjani Sridharan 				if (!widget_in_list(list, p->sink))
3214639029bSRanjani Sridharan 					continue;
3224639029bSRanjani Sridharan 
3235fcdbb2dSRanjani Sridharan 				if (p->sink->dobj.private) {
3245fcdbb2dSRanjani Sridharan 					ret = sof_route_setup(sdev, widget, p->sink);
3255fcdbb2dSRanjani Sridharan 					if (ret < 0)
3265fcdbb2dSRanjani Sridharan 						return ret;
3275fcdbb2dSRanjani Sridharan 				}
3285fcdbb2dSRanjani Sridharan 			}
3294639029bSRanjani Sridharan 		}
3305fcdbb2dSRanjani Sridharan 	} else {
3315fcdbb2dSRanjani Sridharan 		for_each_dapm_widgets(list, i, widget) {
3325fcdbb2dSRanjani Sridharan 			if (!widget->dobj.private)
3335fcdbb2dSRanjani Sridharan 				continue;
3345fcdbb2dSRanjani Sridharan 
3354639029bSRanjani Sridharan 			snd_soc_dapm_widget_for_each_source_path(widget, p) {
3364639029bSRanjani Sridharan 				if (!widget_in_list(list, p->source))
3374639029bSRanjani Sridharan 					continue;
3384639029bSRanjani Sridharan 
3395fcdbb2dSRanjani Sridharan 				if (p->source->dobj.private) {
3405fcdbb2dSRanjani Sridharan 					ret = sof_route_setup(sdev, p->source, widget);
3415fcdbb2dSRanjani Sridharan 					if (ret < 0)
3425fcdbb2dSRanjani Sridharan 						return ret;
3435fcdbb2dSRanjani Sridharan 				}
3445fcdbb2dSRanjani Sridharan 			}
3455fcdbb2dSRanjani Sridharan 		}
3464639029bSRanjani Sridharan 	}
3475fcdbb2dSRanjani Sridharan 
3488cd3cb17SChao Song 	/*
3498cd3cb17SChao Song 	 * The above loop handles connections between widgets that belong to the DAPM widget list.
3508cd3cb17SChao Song 	 * This is not sufficient to handle loopback cases between pipelines configured with
3518cd3cb17SChao Song 	 * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker
3528cd3cb17SChao Song 	 * protection module.
3538cd3cb17SChao Song 	 */
3548cd3cb17SChao Song 	list_for_each_entry(sroute, &sdev->route_list, list) {
3558cd3cb17SChao Song 		bool src_widget_in_dapm_list, sink_widget_in_dapm_list;
3568cd3cb17SChao Song 		struct snd_sof_widget *swidget;
3578cd3cb17SChao Song 
3588cd3cb17SChao Song 		if (sroute->setup)
3598cd3cb17SChao Song 			continue;
3608cd3cb17SChao Song 
3618cd3cb17SChao Song 		src_widget_in_dapm_list = widget_in_list(list, sroute->src_widget->widget);
3628cd3cb17SChao Song 		sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget);
3638cd3cb17SChao Song 
3648cd3cb17SChao Song 		/*
3658cd3cb17SChao Song 		 * if both source and sink are in the DAPM list, the route must already have been
3668cd3cb17SChao Song 		 * set up above. And if neither are in the DAPM list, the route shouldn't be
3678cd3cb17SChao Song 		 * handled now.
3688cd3cb17SChao Song 		 */
3698cd3cb17SChao Song 		if (src_widget_in_dapm_list == sink_widget_in_dapm_list)
3708cd3cb17SChao Song 			continue;
3718cd3cb17SChao Song 
3728cd3cb17SChao Song 		/*
3738cd3cb17SChao Song 		 * At this point either the source widget or the sink widget is in the DAPM list
3748cd3cb17SChao Song 		 * with a route that might need to be set up. Check the use_count of the widget
3758cd3cb17SChao Song 		 * that is not in the DAPM list to confirm if it is in use currently before setting
3768cd3cb17SChao Song 		 * up the route.
3778cd3cb17SChao Song 		 */
3788cd3cb17SChao Song 		if (src_widget_in_dapm_list)
3798cd3cb17SChao Song 			swidget = sroute->sink_widget;
3808cd3cb17SChao Song 		else
3818cd3cb17SChao Song 			swidget = sroute->src_widget;
3828cd3cb17SChao Song 
3838cd3cb17SChao Song 		mutex_lock(&swidget->setup_mutex);
3848cd3cb17SChao Song 		if (!swidget->use_count) {
3858cd3cb17SChao Song 			mutex_unlock(&swidget->setup_mutex);
3868cd3cb17SChao Song 			continue;
3878cd3cb17SChao Song 		}
3888cd3cb17SChao Song 
3898cd3cb17SChao Song 		if (tplg_ops && tplg_ops->route_setup) {
3908cd3cb17SChao Song 			/*
3918cd3cb17SChao Song 			 * this route will get freed when either the source widget or the sink
3928cd3cb17SChao Song 			 * widget is freed during hw_free
3938cd3cb17SChao Song 			 */
3948cd3cb17SChao Song 			ret = tplg_ops->route_setup(sdev, sroute);
3958cd3cb17SChao Song 			if (!ret)
3968cd3cb17SChao Song 				sroute->setup = true;
3978cd3cb17SChao Song 		}
3988cd3cb17SChao Song 
3998cd3cb17SChao Song 		mutex_unlock(&swidget->setup_mutex);
4008cd3cb17SChao Song 
4018cd3cb17SChao Song 		if (ret < 0)
4028cd3cb17SChao Song 			return ret;
4038cd3cb17SChao Song 	}
4048cd3cb17SChao Song 
4055fcdbb2dSRanjani Sridharan 	return 0;
4065fcdbb2dSRanjani Sridharan }
4075fcdbb2dSRanjani Sridharan 
40866344c6dSRanjani 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)4094639029bSRanjani Sridharan sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
4104639029bSRanjani Sridharan 			      struct snd_soc_dapm_widget_list *list)
41166344c6dSRanjani Sridharan {
412cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
41366344c6dSRanjani Sridharan 	struct snd_sof_widget *swidget = widget->dobj.private;
414cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_widget_ops *widget_ops;
41566344c6dSRanjani Sridharan 	struct snd_soc_dapm_path *p;
41666344c6dSRanjani Sridharan 
4170557864eSBard Liao 	if (is_virtual_widget(sdev, widget, __func__))
4180557864eSBard Liao 		return;
4190557864eSBard Liao 
420cc755b43SBard Liao 	/* skip if the widget is in use or if it is already unprepared */
4210ad84b11SRanjani Sridharan 	if (!swidget || !swidget->prepared || swidget->use_count > 0)
422cc755b43SBard Liao 		goto sink_unprepare;
42366344c6dSRanjani Sridharan 
424cd6afb06SPeter Ujfalusi 	widget_ops = tplg_ops ? tplg_ops->widget : NULL;
425cd6afb06SPeter Ujfalusi 	if (widget_ops && widget_ops[widget->id].ipc_unprepare)
42666344c6dSRanjani Sridharan 		/* unprepare the source widget */
42766344c6dSRanjani Sridharan 		widget_ops[widget->id].ipc_unprepare(swidget);
428ce59804dSRander Wang 
42966344c6dSRanjani Sridharan 	swidget->prepared = false;
43066344c6dSRanjani Sridharan 
431cc755b43SBard Liao sink_unprepare:
43266344c6dSRanjani Sridharan 	/* unprepare all widgets in the sink paths */
43366344c6dSRanjani Sridharan 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
4344639029bSRanjani Sridharan 		if (!widget_in_list(list, p->sink))
4354639029bSRanjani Sridharan 			continue;
43666344c6dSRanjani Sridharan 		if (!p->walking && p->sink->dobj.private) {
43766344c6dSRanjani Sridharan 			p->walking = true;
4384639029bSRanjani Sridharan 			sof_unprepare_widgets_in_path(sdev, p->sink, list);
43966344c6dSRanjani Sridharan 			p->walking = false;
44066344c6dSRanjani Sridharan 		}
44166344c6dSRanjani Sridharan 	}
44266344c6dSRanjani Sridharan }
44366344c6dSRanjani Sridharan 
44466344c6dSRanjani 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)44566344c6dSRanjani Sridharan sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
44666344c6dSRanjani Sridharan 			    struct snd_pcm_hw_params *fe_params,
44766344c6dSRanjani Sridharan 			    struct snd_sof_platform_stream_params *platform_params,
4484639029bSRanjani Sridharan 			    struct snd_pcm_hw_params *pipeline_params, int dir,
4494639029bSRanjani Sridharan 			    struct snd_soc_dapm_widget_list *list)
45066344c6dSRanjani Sridharan {
451cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
45266344c6dSRanjani Sridharan 	struct snd_sof_widget *swidget = widget->dobj.private;
453cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_widget_ops *widget_ops;
45466344c6dSRanjani Sridharan 	struct snd_soc_dapm_path *p;
45566344c6dSRanjani Sridharan 	int ret;
45666344c6dSRanjani Sridharan 
4570557864eSBard Liao 	if (is_virtual_widget(sdev, widget, __func__))
4580557864eSBard Liao 		return 0;
4590557864eSBard Liao 
460cd6afb06SPeter Ujfalusi 	widget_ops = tplg_ops ? tplg_ops->widget : NULL;
461cd6afb06SPeter Ujfalusi 	if (!widget_ops)
462cd6afb06SPeter Ujfalusi 		return 0;
463cd6afb06SPeter Ujfalusi 
4640ad84b11SRanjani Sridharan 	if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)
46566344c6dSRanjani Sridharan 		goto sink_prepare;
46666344c6dSRanjani Sridharan 
46766344c6dSRanjani Sridharan 	/* prepare the source widget */
46866344c6dSRanjani Sridharan 	ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,
46966344c6dSRanjani Sridharan 					     pipeline_params, dir);
47066344c6dSRanjani Sridharan 	if (ret < 0) {
47166344c6dSRanjani Sridharan 		dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);
47266344c6dSRanjani Sridharan 		return ret;
47366344c6dSRanjani Sridharan 	}
47466344c6dSRanjani Sridharan 
47566344c6dSRanjani Sridharan 	swidget->prepared = true;
47666344c6dSRanjani Sridharan 
47766344c6dSRanjani Sridharan sink_prepare:
47866344c6dSRanjani Sridharan 	/* prepare all widgets in the sink paths */
47966344c6dSRanjani Sridharan 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
4804639029bSRanjani Sridharan 		if (!widget_in_list(list, p->sink))
4814639029bSRanjani Sridharan 			continue;
48266344c6dSRanjani Sridharan 		if (!p->walking && p->sink->dobj.private) {
48366344c6dSRanjani Sridharan 			p->walking = true;
48466344c6dSRanjani Sridharan 			ret = sof_prepare_widgets_in_path(sdev, p->sink,  fe_params,
4854639029bSRanjani Sridharan 							  platform_params, pipeline_params, dir,
4864639029bSRanjani Sridharan 							  list);
48766344c6dSRanjani Sridharan 			p->walking = false;
48866344c6dSRanjani Sridharan 			if (ret < 0) {
48966344c6dSRanjani Sridharan 				/* unprepare the source widget */
490fb429360SPeter Ujfalusi 				if (widget_ops[widget->id].ipc_unprepare &&
491*e18dfef2SPeter Ujfalusi 				    swidget && swidget->prepared && swidget->use_count == 0) {
49266344c6dSRanjani Sridharan 					widget_ops[widget->id].ipc_unprepare(swidget);
49366344c6dSRanjani Sridharan 					swidget->prepared = false;
49466344c6dSRanjani Sridharan 				}
49566344c6dSRanjani Sridharan 				return ret;
49666344c6dSRanjani Sridharan 			}
49766344c6dSRanjani Sridharan 		}
49866344c6dSRanjani Sridharan 	}
49966344c6dSRanjani Sridharan 
50066344c6dSRanjani Sridharan 	return 0;
50166344c6dSRanjani Sridharan }
50266344c6dSRanjani Sridharan 
5035da0590aSRanjani Sridharan /*
5045da0590aSRanjani Sridharan  * free all widgets in the sink path starting from the source widget
5055da0590aSRanjani Sridharan  * (DAI type for capture, AIF type for playback)
5065da0590aSRanjani Sridharan  */
sof_free_widgets_in_path(struct snd_sof_dev * sdev,struct snd_soc_dapm_widget * widget,int dir,struct snd_sof_pcm * spcm)5075da0590aSRanjani Sridharan static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
50819137532SRanjani Sridharan 				    int dir, struct snd_sof_pcm *spcm)
5095da0590aSRanjani Sridharan {
51019137532SRanjani Sridharan 	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
5115da0590aSRanjani Sridharan 	struct snd_soc_dapm_path *p;
5125da0590aSRanjani Sridharan 	int err;
5135da0590aSRanjani Sridharan 	int ret = 0;
5145da0590aSRanjani Sridharan 
5150557864eSBard Liao 	if (is_virtual_widget(sdev, widget, __func__))
5160557864eSBard Liao 		return 0;
5170557864eSBard Liao 
51873ea6609SRanjani Sridharan 	if (widget->dobj.private) {
5195da0590aSRanjani Sridharan 		err = sof_widget_free(sdev, widget->dobj.private);
5205da0590aSRanjani Sridharan 		if (err < 0)
5215da0590aSRanjani Sridharan 			ret = err;
5225da0590aSRanjani Sridharan 	}
5235da0590aSRanjani Sridharan 
52473ea6609SRanjani Sridharan 	/* free all widgets in the sink paths even in case of error to keep use counts balanced */
52573ea6609SRanjani Sridharan 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
52673ea6609SRanjani Sridharan 		if (!p->walking) {
5274639029bSRanjani Sridharan 			if (!widget_in_list(list, p->sink))
5284639029bSRanjani Sridharan 				continue;
5295da0590aSRanjani Sridharan 
53073ea6609SRanjani Sridharan 			p->walking = true;
5315da0590aSRanjani Sridharan 
53219137532SRanjani Sridharan 			err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm);
5335da0590aSRanjani Sridharan 			if (err < 0)
5345da0590aSRanjani Sridharan 				ret = err;
5355da0590aSRanjani Sridharan 			p->walking = false;
5365da0590aSRanjani Sridharan 		}
5375da0590aSRanjani Sridharan 	}
5385da0590aSRanjani Sridharan 
5395da0590aSRanjani Sridharan 	return ret;
5405da0590aSRanjani Sridharan }
5415da0590aSRanjani Sridharan 
5425da0590aSRanjani Sridharan /*
5435da0590aSRanjani Sridharan  * set up all widgets in the sink path starting from the source widget
5445da0590aSRanjani Sridharan  * (DAI type for capture, AIF type for playback).
5455da0590aSRanjani Sridharan  * The error path in this function ensures that all successfully set up widgets getting freed.
5465da0590aSRanjani 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)5475da0590aSRanjani Sridharan static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
54819137532SRanjani Sridharan 				      int dir, struct snd_sof_pcm *spcm)
5495da0590aSRanjani Sridharan {
55019137532SRanjani Sridharan 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
55119137532SRanjani Sridharan 	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
55219137532SRanjani Sridharan 	struct snd_sof_widget *swidget = widget->dobj.private;
5539c04363dSRanjani Sridharan 	struct snd_sof_pipeline *spipe;
5545da0590aSRanjani Sridharan 	struct snd_soc_dapm_path *p;
5555da0590aSRanjani Sridharan 	int ret;
5565da0590aSRanjani Sridharan 
5570557864eSBard Liao 	if (is_virtual_widget(sdev, widget, __func__))
5580557864eSBard Liao 		return 0;
5590557864eSBard Liao 
56019137532SRanjani Sridharan 	if (swidget) {
56119137532SRanjani Sridharan 		int i;
56219137532SRanjani Sridharan 
5635da0590aSRanjani Sridharan 		ret = sof_widget_setup(sdev, widget->dobj.private);
5645da0590aSRanjani Sridharan 		if (ret < 0)
5655da0590aSRanjani Sridharan 			return ret;
56619137532SRanjani Sridharan 
56719137532SRanjani Sridharan 		/* skip populating the pipe_widgets array if it is NULL */
5689c04363dSRanjani Sridharan 		if (!pipeline_list->pipelines)
56919137532SRanjani Sridharan 			goto sink_setup;
57019137532SRanjani Sridharan 
57119137532SRanjani Sridharan 		/*
57219137532SRanjani Sridharan 		 * Add the widget's pipe_widget to the list of pipelines to be triggered if not
57319137532SRanjani Sridharan 		 * already in the list. This will result in the pipelines getting added in the
57419137532SRanjani Sridharan 		 * order source to sink.
57519137532SRanjani Sridharan 		 */
57619137532SRanjani Sridharan 		for (i = 0; i < pipeline_list->count; i++) {
5779c04363dSRanjani Sridharan 			spipe = pipeline_list->pipelines[i];
5789c04363dSRanjani Sridharan 			if (spipe == swidget->spipe)
57919137532SRanjani Sridharan 				break;
5805da0590aSRanjani Sridharan 		}
5815da0590aSRanjani Sridharan 
58219137532SRanjani Sridharan 		if (i == pipeline_list->count) {
58319137532SRanjani Sridharan 			pipeline_list->count++;
5849c04363dSRanjani Sridharan 			pipeline_list->pipelines[i] = swidget->spipe;
58519137532SRanjani Sridharan 		}
58619137532SRanjani Sridharan 	}
58719137532SRanjani Sridharan 
58819137532SRanjani Sridharan sink_setup:
58973ea6609SRanjani Sridharan 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
59073ea6609SRanjani Sridharan 		if (!p->walking) {
5914639029bSRanjani Sridharan 			if (!widget_in_list(list, p->sink))
5924639029bSRanjani Sridharan 				continue;
5934639029bSRanjani Sridharan 
59473ea6609SRanjani Sridharan 			p->walking = true;
5955da0590aSRanjani Sridharan 
59619137532SRanjani Sridharan 			ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm);
5975da0590aSRanjani Sridharan 			p->walking = false;
59873ea6609SRanjani Sridharan 			if (ret < 0) {
59919137532SRanjani Sridharan 				if (swidget)
60019137532SRanjani Sridharan 					sof_widget_free(sdev, swidget);
6015da0590aSRanjani Sridharan 				return ret;
6025da0590aSRanjani Sridharan 			}
6035da0590aSRanjani Sridharan 		}
6045da0590aSRanjani Sridharan 	}
6055da0590aSRanjani Sridharan 
6065da0590aSRanjani Sridharan 	return 0;
6075da0590aSRanjani Sridharan }
6085da0590aSRanjani Sridharan 
6095da0590aSRanjani 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)61019137532SRanjani Sridharan sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
61166344c6dSRanjani Sridharan 			  struct snd_pcm_hw_params *fe_params,
61266344c6dSRanjani Sridharan 			  struct snd_sof_platform_stream_params *platform_params, int dir,
61366344c6dSRanjani Sridharan 			  enum sof_widget_op op)
6145da0590aSRanjani Sridharan {
61519137532SRanjani Sridharan 	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
6165da0590aSRanjani Sridharan 	struct snd_soc_dapm_widget *widget;
61766344c6dSRanjani Sridharan 	char *str;
61866344c6dSRanjani Sridharan 	int ret = 0;
61966344c6dSRanjani Sridharan 	int i;
6205da0590aSRanjani Sridharan 
62119137532SRanjani Sridharan 	if (!list)
62219137532SRanjani Sridharan 		return 0;
62319137532SRanjani Sridharan 
6245da0590aSRanjani Sridharan 	for_each_dapm_widgets(list, i, widget) {
6250557864eSBard Liao 		if (is_virtual_widget(sdev, widget, __func__))
6260557864eSBard Liao 			continue;
6270557864eSBard Liao 
6285da0590aSRanjani Sridharan 		/* starting widget for playback is AIF type */
629fcc4348aSBard Liao 		if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
6305da0590aSRanjani Sridharan 			continue;
6315da0590aSRanjani Sridharan 
6325da0590aSRanjani Sridharan 		/* starting widget for capture is DAI type */
633fcc4348aSBard Liao 		if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out)
6345da0590aSRanjani Sridharan 			continue;
6355da0590aSRanjani Sridharan 
6365da0590aSRanjani Sridharan 		switch (op) {
6375da0590aSRanjani Sridharan 		case SOF_WIDGET_SETUP:
63819137532SRanjani Sridharan 			ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm);
63966344c6dSRanjani Sridharan 			str = "set up";
6405da0590aSRanjani Sridharan 			break;
6415da0590aSRanjani Sridharan 		case SOF_WIDGET_FREE:
64219137532SRanjani Sridharan 			ret = sof_free_widgets_in_path(sdev, widget, dir, spcm);
64366344c6dSRanjani Sridharan 			str = "free";
64466344c6dSRanjani Sridharan 			break;
64566344c6dSRanjani Sridharan 		case SOF_WIDGET_PREPARE:
64666344c6dSRanjani Sridharan 		{
64766344c6dSRanjani Sridharan 			struct snd_pcm_hw_params pipeline_params;
64866344c6dSRanjani Sridharan 
64966344c6dSRanjani Sridharan 			str = "prepare";
65066344c6dSRanjani Sridharan 			/*
65166344c6dSRanjani Sridharan 			 * When walking the list of connected widgets, the pipeline_params for each
65266344c6dSRanjani Sridharan 			 * widget is modified by the source widget in the path. Use a local
65366344c6dSRanjani Sridharan 			 * copy of the runtime params as the pipeline_params so that the runtime
65466344c6dSRanjani Sridharan 			 * params does not get overwritten.
65566344c6dSRanjani Sridharan 			 */
65666344c6dSRanjani Sridharan 			memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
65766344c6dSRanjani Sridharan 
6584639029bSRanjani Sridharan 			ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params,
6594639029bSRanjani Sridharan 							  &pipeline_params, dir, list);
66066344c6dSRanjani Sridharan 			break;
66166344c6dSRanjani Sridharan 		}
66266344c6dSRanjani Sridharan 		case SOF_WIDGET_UNPREPARE:
6634639029bSRanjani Sridharan 			sof_unprepare_widgets_in_path(sdev, widget, list);
6645da0590aSRanjani Sridharan 			break;
6655da0590aSRanjani Sridharan 		default:
6665da0590aSRanjani Sridharan 			dev_err(sdev->dev, "Invalid widget op %d\n", op);
6675da0590aSRanjani Sridharan 			return -EINVAL;
6685da0590aSRanjani Sridharan 		}
6695da0590aSRanjani Sridharan 		if (ret < 0) {
67066344c6dSRanjani Sridharan 			dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
6715da0590aSRanjani Sridharan 			return ret;
6725da0590aSRanjani Sridharan 		}
6735da0590aSRanjani Sridharan 	}
6745da0590aSRanjani Sridharan 
6755da0590aSRanjani Sridharan 	return 0;
6765da0590aSRanjani Sridharan }
6775da0590aSRanjani 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)67866344c6dSRanjani Sridharan int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
67966344c6dSRanjani Sridharan 			  struct snd_pcm_hw_params *fe_params,
68066344c6dSRanjani Sridharan 			  struct snd_sof_platform_stream_params *platform_params,
68166344c6dSRanjani Sridharan 			  int dir)
6825fcdbb2dSRanjani Sridharan {
683cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
6845fcdbb2dSRanjani Sridharan 	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
6855fcdbb2dSRanjani Sridharan 	struct snd_soc_dapm_widget *widget;
6865da0590aSRanjani Sridharan 	int i, ret;
6875fcdbb2dSRanjani Sridharan 
6885fcdbb2dSRanjani Sridharan 	/* nothing to set up */
6895fcdbb2dSRanjani Sridharan 	if (!list)
6905fcdbb2dSRanjani Sridharan 		return 0;
6915fcdbb2dSRanjani Sridharan 
69266344c6dSRanjani Sridharan 	/*
69366344c6dSRanjani Sridharan 	 * Prepare widgets for set up. The prepare step is used to allocate memory, assign
69466344c6dSRanjani Sridharan 	 * instance ID and pick the widget configuration based on the runtime PCM params.
69566344c6dSRanjani Sridharan 	 */
69619137532SRanjani Sridharan 	ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
69766344c6dSRanjani Sridharan 					dir, SOF_WIDGET_PREPARE);
69840c2c63aSRanjani Sridharan 	if (ret < 0)
6995da0590aSRanjani Sridharan 		return ret;
7005fcdbb2dSRanjani Sridharan 
70166344c6dSRanjani Sridharan 	/* Set up is used to send the IPC to the DSP to create the widget */
70219137532SRanjani Sridharan 	ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
70366344c6dSRanjani Sridharan 					dir, SOF_WIDGET_SETUP);
70466344c6dSRanjani Sridharan 	if (ret < 0) {
705c7e328f1SPierre-Louis Bossart 		sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
70666344c6dSRanjani Sridharan 					  dir, SOF_WIDGET_UNPREPARE);
70766344c6dSRanjani Sridharan 		return ret;
70866344c6dSRanjani Sridharan 	}
70966344c6dSRanjani Sridharan 
7105fcdbb2dSRanjani Sridharan 	/*
7115fcdbb2dSRanjani Sridharan 	 * error in setting pipeline connections will result in route status being reset for
7125fcdbb2dSRanjani Sridharan 	 * routes that were successfully set up when the widgets are freed.
7135fcdbb2dSRanjani Sridharan 	 */
7145fcdbb2dSRanjani Sridharan 	ret = sof_setup_pipeline_connections(sdev, list, dir);
7155fcdbb2dSRanjani Sridharan 	if (ret < 0)
7165fcdbb2dSRanjani Sridharan 		goto widget_free;
7175fcdbb2dSRanjani Sridharan 
7185fcdbb2dSRanjani Sridharan 	/* complete pipelines */
7195fcdbb2dSRanjani Sridharan 	for_each_dapm_widgets(list, i, widget) {
7205fcdbb2dSRanjani Sridharan 		struct snd_sof_widget *swidget = widget->dobj.private;
7215fcdbb2dSRanjani Sridharan 		struct snd_sof_widget *pipe_widget;
7229c04363dSRanjani Sridharan 		struct snd_sof_pipeline *spipe;
7235fcdbb2dSRanjani Sridharan 
72428d40e7aSPeter Ujfalusi 		if (!swidget || sdev->dspless_mode_selected)
7255fcdbb2dSRanjani Sridharan 			continue;
7265fcdbb2dSRanjani Sridharan 
7279c04363dSRanjani Sridharan 		spipe = swidget->spipe;
7289c04363dSRanjani Sridharan 		if (!spipe) {
7299c04363dSRanjani Sridharan 			dev_err(sdev->dev, "no pipeline found for %s\n",
7309c04363dSRanjani Sridharan 				swidget->widget->name);
7319c04363dSRanjani Sridharan 			ret = -EINVAL;
7329c04363dSRanjani Sridharan 			goto widget_free;
7339c04363dSRanjani Sridharan 		}
7349c04363dSRanjani Sridharan 
7359c04363dSRanjani Sridharan 		pipe_widget = spipe->pipe_widget;
7365fcdbb2dSRanjani Sridharan 		if (!pipe_widget) {
7375fcdbb2dSRanjani Sridharan 			dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
7385fcdbb2dSRanjani Sridharan 				swidget->widget->name);
7395fcdbb2dSRanjani Sridharan 			ret = -EINVAL;
7405fcdbb2dSRanjani Sridharan 			goto widget_free;
7415fcdbb2dSRanjani Sridharan 		}
7425fcdbb2dSRanjani Sridharan 
7439c04363dSRanjani Sridharan 		if (spipe->complete)
7445fcdbb2dSRanjani Sridharan 			continue;
7455fcdbb2dSRanjani Sridharan 
746cd6afb06SPeter Ujfalusi 		if (tplg_ops && tplg_ops->pipeline_complete) {
7479c04363dSRanjani Sridharan 			spipe->complete = tplg_ops->pipeline_complete(sdev, pipe_widget);
7489c04363dSRanjani Sridharan 			if (spipe->complete < 0) {
7499c04363dSRanjani Sridharan 				ret = spipe->complete;
7505fcdbb2dSRanjani Sridharan 				goto widget_free;
7515fcdbb2dSRanjani Sridharan 			}
7525fcdbb2dSRanjani Sridharan 		}
75361ad28ffSRanjani Sridharan 	}
7545fcdbb2dSRanjani Sridharan 
7555fcdbb2dSRanjani Sridharan 	return 0;
7565fcdbb2dSRanjani Sridharan 
7575fcdbb2dSRanjani Sridharan widget_free:
75819137532SRanjani Sridharan 	sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir,
75966344c6dSRanjani Sridharan 				  SOF_WIDGET_FREE);
76019137532SRanjani Sridharan 	sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
7615fcdbb2dSRanjani Sridharan 
7625fcdbb2dSRanjani Sridharan 	return ret;
7635fcdbb2dSRanjani Sridharan }
7645fcdbb2dSRanjani Sridharan 
sof_widget_list_free(struct snd_sof_dev * sdev,struct snd_sof_pcm * spcm,int dir)7655fcdbb2dSRanjani Sridharan int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
7665fcdbb2dSRanjani Sridharan {
76719137532SRanjani Sridharan 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
7685fcdbb2dSRanjani Sridharan 	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
7695da0590aSRanjani Sridharan 	int ret;
7705fcdbb2dSRanjani Sridharan 
7715fcdbb2dSRanjani Sridharan 	/* nothing to free */
7725fcdbb2dSRanjani Sridharan 	if (!list)
7735fcdbb2dSRanjani Sridharan 		return 0;
7745fcdbb2dSRanjani Sridharan 
77566344c6dSRanjani Sridharan 	/* send IPC to free widget in the DSP */
77619137532SRanjani Sridharan 	ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE);
77766344c6dSRanjani Sridharan 
77866344c6dSRanjani Sridharan 	/* unprepare the widget */
77919137532SRanjani Sridharan 	sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
7805fcdbb2dSRanjani Sridharan 
7815fcdbb2dSRanjani Sridharan 	snd_soc_dapm_dai_free_widgets(&list);
7825fcdbb2dSRanjani Sridharan 	spcm->stream[dir].list = NULL;
7835fcdbb2dSRanjani Sridharan 
78419137532SRanjani Sridharan 	pipeline_list->count = 0;
78519137532SRanjani Sridharan 
7865da0590aSRanjani Sridharan 	return ret;
7875fcdbb2dSRanjani Sridharan }
7885fcdbb2dSRanjani Sridharan 
789de23a838SRanjani Sridharan /*
790de23a838SRanjani Sridharan  * helper to determine if there are only D0i3 compatible
791de23a838SRanjani Sridharan  * streams active
792de23a838SRanjani Sridharan  */
snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev * sdev)793de23a838SRanjani Sridharan bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
794de23a838SRanjani Sridharan {
795de23a838SRanjani Sridharan 	struct snd_pcm_substream *substream;
796de23a838SRanjani Sridharan 	struct snd_sof_pcm *spcm;
797de23a838SRanjani Sridharan 	bool d0i3_compatible_active = false;
798de23a838SRanjani Sridharan 	int dir;
799de23a838SRanjani Sridharan 
800de23a838SRanjani Sridharan 	list_for_each_entry(spcm, &sdev->pcm_list, list) {
801525c4107SKuninori Morimoto 		for_each_pcm_streams(dir) {
802de23a838SRanjani Sridharan 			substream = spcm->stream[dir].substream;
803de23a838SRanjani Sridharan 			if (!substream || !substream->runtime)
804de23a838SRanjani Sridharan 				continue;
805de23a838SRanjani Sridharan 
806de23a838SRanjani Sridharan 			/*
8078932f0cbSRandy Dunlap 			 * substream->runtime being not NULL indicates
808de23a838SRanjani Sridharan 			 * that the stream is open. No need to check the
809de23a838SRanjani Sridharan 			 * stream state.
810de23a838SRanjani Sridharan 			 */
811de23a838SRanjani Sridharan 			if (!spcm->stream[dir].d0i3_compatible)
812de23a838SRanjani Sridharan 				return false;
813de23a838SRanjani Sridharan 
814de23a838SRanjani Sridharan 			d0i3_compatible_active = true;
815de23a838SRanjani Sridharan 		}
816de23a838SRanjani Sridharan 	}
817de23a838SRanjani Sridharan 
818de23a838SRanjani Sridharan 	return d0i3_compatible_active;
819de23a838SRanjani Sridharan }
820de23a838SRanjani Sridharan EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
821de23a838SRanjani Sridharan 
snd_sof_stream_suspend_ignored(struct snd_sof_dev * sdev)822700d1677SRanjani Sridharan bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
823ee1e79b7SRanjani Sridharan {
824ee1e79b7SRanjani Sridharan 	struct snd_sof_pcm *spcm;
825ee1e79b7SRanjani Sridharan 
826ee1e79b7SRanjani Sridharan 	list_for_each_entry(spcm, &sdev->pcm_list, list) {
827ee1e79b7SRanjani Sridharan 		if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
828ee1e79b7SRanjani Sridharan 		    spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
829ee1e79b7SRanjani Sridharan 			return true;
830ee1e79b7SRanjani Sridharan 	}
831ee1e79b7SRanjani Sridharan 
832ee1e79b7SRanjani Sridharan 	return false;
833ee1e79b7SRanjani Sridharan }
834ee1e79b7SRanjani Sridharan 
sof_pcm_stream_free(struct snd_sof_dev * sdev,struct snd_pcm_substream * substream,struct snd_sof_pcm * spcm,int dir,bool free_widget_list)835d9a72465SRanjani Sridharan int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
836d9a72465SRanjani Sridharan 			struct snd_sof_pcm *spcm, int dir, bool free_widget_list)
837d9a72465SRanjani Sridharan {
838cd6afb06SPeter Ujfalusi 	const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
839d9a72465SRanjani Sridharan 	int ret;
840d9a72465SRanjani Sridharan 
8416d0a21ddSRanjani Sridharan 	if (spcm->prepared[substream->stream]) {
8426d0a21ddSRanjani Sridharan 		/* stop DMA first if needed */
8436d0a21ddSRanjani Sridharan 		if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
8446d0a21ddSRanjani Sridharan 			snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
8456d0a21ddSRanjani Sridharan 
846d9a72465SRanjani Sridharan 		/* Send PCM_FREE IPC to reset pipeline */
8476d0a21ddSRanjani Sridharan 		if (pcm_ops && pcm_ops->hw_free) {
8484123c24bSRanjani Sridharan 			ret = pcm_ops->hw_free(sdev->component, substream);
849d9a72465SRanjani Sridharan 			if (ret < 0)
850d9a72465SRanjani Sridharan 				return ret;
8514123c24bSRanjani Sridharan 		}
8524123c24bSRanjani Sridharan 
8534123c24bSRanjani Sridharan 		spcm->prepared[substream->stream] = false;
8546d0a21ddSRanjani Sridharan 	}
855d9a72465SRanjani Sridharan 
8566d0a21ddSRanjani Sridharan 	/* reset the DMA */
857d9a72465SRanjani Sridharan 	ret = snd_sof_pcm_platform_hw_free(sdev, substream);
858d9a72465SRanjani Sridharan 	if (ret < 0)
859d9a72465SRanjani Sridharan 		return ret;
860d9a72465SRanjani Sridharan 
861d9a72465SRanjani Sridharan 	/* free widget list */
862d9a72465SRanjani Sridharan 	if (free_widget_list) {
863d9a72465SRanjani Sridharan 		ret = sof_widget_list_free(sdev, spcm, dir);
864d9a72465SRanjani Sridharan 		if (ret < 0)
865d9a72465SRanjani Sridharan 			dev_err(sdev->dev, "failed to free widgets during suspend\n");
866d9a72465SRanjani Sridharan 	}
867d9a72465SRanjani Sridharan 
868d9a72465SRanjani Sridharan 	return ret;
869d9a72465SRanjani Sridharan }
870d9a72465SRanjani Sridharan 
8718b001416SRanjani Sridharan /*
872ee1e79b7SRanjani Sridharan  * Generic object lookup APIs.
873ee1e79b7SRanjani Sridharan  */
874ee1e79b7SRanjani Sridharan 
snd_sof_find_spcm_name(struct snd_soc_component * scomp,const char * name)875ee1e79b7SRanjani Sridharan struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
876ee1e79b7SRanjani Sridharan 					   const char *name)
877ee1e79b7SRanjani Sridharan {
878ee1e79b7SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
879ee1e79b7SRanjani Sridharan 	struct snd_sof_pcm *spcm;
880ee1e79b7SRanjani Sridharan 
881ee1e79b7SRanjani Sridharan 	list_for_each_entry(spcm, &sdev->pcm_list, list) {
882ee1e79b7SRanjani Sridharan 		/* match with PCM dai name */
883ee1e79b7SRanjani Sridharan 		if (strcmp(spcm->pcm.dai_name, name) == 0)
884ee1e79b7SRanjani Sridharan 			return spcm;
885ee1e79b7SRanjani Sridharan 
886ee1e79b7SRanjani Sridharan 		/* match with playback caps name if set */
887ee1e79b7SRanjani Sridharan 		if (*spcm->pcm.caps[0].name &&
888ee1e79b7SRanjani Sridharan 		    !strcmp(spcm->pcm.caps[0].name, name))
889ee1e79b7SRanjani Sridharan 			return spcm;
890ee1e79b7SRanjani Sridharan 
891ee1e79b7SRanjani Sridharan 		/* match with capture caps name if set */
892ee1e79b7SRanjani Sridharan 		if (*spcm->pcm.caps[1].name &&
893ee1e79b7SRanjani Sridharan 		    !strcmp(spcm->pcm.caps[1].name, name))
894ee1e79b7SRanjani Sridharan 			return spcm;
895ee1e79b7SRanjani Sridharan 	}
896ee1e79b7SRanjani Sridharan 
897ee1e79b7SRanjani Sridharan 	return NULL;
898ee1e79b7SRanjani Sridharan }
899ee1e79b7SRanjani Sridharan 
snd_sof_find_spcm_comp(struct snd_soc_component * scomp,unsigned int comp_id,int * direction)900ee1e79b7SRanjani Sridharan struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
901ee1e79b7SRanjani Sridharan 					   unsigned int comp_id,
902ee1e79b7SRanjani Sridharan 					   int *direction)
903ee1e79b7SRanjani Sridharan {
904ee1e79b7SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
905ee1e79b7SRanjani Sridharan 	struct snd_sof_pcm *spcm;
906ee1e79b7SRanjani Sridharan 	int dir;
907ee1e79b7SRanjani Sridharan 
908ee1e79b7SRanjani Sridharan 	list_for_each_entry(spcm, &sdev->pcm_list, list) {
909525c4107SKuninori Morimoto 		for_each_pcm_streams(dir) {
910ee1e79b7SRanjani Sridharan 			if (spcm->stream[dir].comp_id == comp_id) {
911ee1e79b7SRanjani Sridharan 				*direction = dir;
912ee1e79b7SRanjani Sridharan 				return spcm;
913ee1e79b7SRanjani Sridharan 			}
914ee1e79b7SRanjani Sridharan 		}
915ee1e79b7SRanjani Sridharan 	}
916ee1e79b7SRanjani Sridharan 
917ee1e79b7SRanjani Sridharan 	return NULL;
918ee1e79b7SRanjani Sridharan }
919ee1e79b7SRanjani Sridharan 
snd_sof_find_swidget(struct snd_soc_component * scomp,const char * name)920ee1e79b7SRanjani Sridharan struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
921ee1e79b7SRanjani Sridharan 					    const char *name)
922ee1e79b7SRanjani Sridharan {
923ee1e79b7SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
924ee1e79b7SRanjani Sridharan 	struct snd_sof_widget *swidget;
925ee1e79b7SRanjani Sridharan 
926ee1e79b7SRanjani Sridharan 	list_for_each_entry(swidget, &sdev->widget_list, list) {
927ee1e79b7SRanjani Sridharan 		if (strcmp(name, swidget->widget->name) == 0)
928ee1e79b7SRanjani Sridharan 			return swidget;
929ee1e79b7SRanjani Sridharan 	}
930ee1e79b7SRanjani Sridharan 
931ee1e79b7SRanjani Sridharan 	return NULL;
932ee1e79b7SRanjani Sridharan }
933ee1e79b7SRanjani Sridharan 
934ee1e79b7SRanjani Sridharan /* find widget by stream name and direction */
935ee1e79b7SRanjani Sridharan struct snd_sof_widget *
snd_sof_find_swidget_sname(struct snd_soc_component * scomp,const char * pcm_name,int dir)936ee1e79b7SRanjani Sridharan snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
937ee1e79b7SRanjani Sridharan 			   const char *pcm_name, int dir)
938ee1e79b7SRanjani Sridharan {
939ee1e79b7SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
940ee1e79b7SRanjani Sridharan 	struct snd_sof_widget *swidget;
941ee1e79b7SRanjani Sridharan 	enum snd_soc_dapm_type type;
942ee1e79b7SRanjani Sridharan 
943ee1e79b7SRanjani Sridharan 	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
944ee1e79b7SRanjani Sridharan 		type = snd_soc_dapm_aif_in;
945ee1e79b7SRanjani Sridharan 	else
946ee1e79b7SRanjani Sridharan 		type = snd_soc_dapm_aif_out;
947ee1e79b7SRanjani Sridharan 
948ee1e79b7SRanjani Sridharan 	list_for_each_entry(swidget, &sdev->widget_list, list) {
949ee1e79b7SRanjani Sridharan 		if (!strcmp(pcm_name, swidget->widget->sname) &&
950ee1e79b7SRanjani Sridharan 		    swidget->id == type)
951ee1e79b7SRanjani Sridharan 			return swidget;
952ee1e79b7SRanjani Sridharan 	}
953ee1e79b7SRanjani Sridharan 
954ee1e79b7SRanjani Sridharan 	return NULL;
955ee1e79b7SRanjani Sridharan }
956ee1e79b7SRanjani Sridharan 
snd_sof_find_dai(struct snd_soc_component * scomp,const char * name)957ee1e79b7SRanjani Sridharan struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
958ee1e79b7SRanjani Sridharan 				     const char *name)
959ee1e79b7SRanjani Sridharan {
960ee1e79b7SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
961ee1e79b7SRanjani Sridharan 	struct snd_sof_dai *dai;
962ee1e79b7SRanjani Sridharan 
963ee1e79b7SRanjani Sridharan 	list_for_each_entry(dai, &sdev->dai_list, list) {
964ee1e79b7SRanjani Sridharan 		if (dai->name && (strcmp(name, dai->name) == 0))
965ee1e79b7SRanjani Sridharan 			return dai;
966ee1e79b7SRanjani Sridharan 	}
967ee1e79b7SRanjani Sridharan 
968ee1e79b7SRanjani Sridharan 	return NULL;
969ee1e79b7SRanjani Sridharan }
970ee1e79b7SRanjani Sridharan 
sof_dai_get_clk(struct snd_soc_pcm_runtime * rtd,int clk_type)971bc619cfcSBrent Lu static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type)
972b951b51eSKeyon Jie {
973b951b51eSKeyon Jie 	struct snd_soc_component *component =
974b951b51eSKeyon Jie 		snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
975b951b51eSKeyon Jie 	struct snd_sof_dai *dai =
976b951b51eSKeyon Jie 		snd_sof_find_dai(component, (char *)rtd->dai_link->name);
97785f7a8b6SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
978cd6afb06SPeter Ujfalusi 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
979b951b51eSKeyon Jie 
980b951b51eSKeyon Jie 	/* use the tplg configured mclk if existed */
98185f7a8b6SRanjani Sridharan 	if (!dai)
982b951b51eSKeyon Jie 		return 0;
983b951b51eSKeyon Jie 
984cd6afb06SPeter Ujfalusi 	if (tplg_ops && tplg_ops->dai_get_clk)
98585f7a8b6SRanjani Sridharan 		return tplg_ops->dai_get_clk(sdev, dai, clk_type);
98685f7a8b6SRanjani Sridharan 
98785f7a8b6SRanjani Sridharan 	return 0;
988b951b51eSKeyon Jie }
989bc619cfcSBrent Lu 
990bc619cfcSBrent Lu /*
991bc619cfcSBrent Lu  * Helper to get SSP MCLK from a pcm_runtime.
992bc619cfcSBrent Lu  * Return 0 if not exist.
993bc619cfcSBrent Lu  */
sof_dai_get_mclk(struct snd_soc_pcm_runtime * rtd)994bc619cfcSBrent Lu int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd)
995bc619cfcSBrent Lu {
996bc619cfcSBrent Lu 	return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_MCLK);
997bc619cfcSBrent Lu }
998b951b51eSKeyon Jie EXPORT_SYMBOL(sof_dai_get_mclk);
999b951b51eSKeyon Jie 
1000b951b51eSKeyon Jie /*
1001bc619cfcSBrent Lu  * Helper to get SSP BCLK from a pcm_runtime.
1002bc619cfcSBrent Lu  * Return 0 if not exist.
1003bc619cfcSBrent Lu  */
sof_dai_get_bclk(struct snd_soc_pcm_runtime * rtd)1004bc619cfcSBrent Lu int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd)
1005bc619cfcSBrent Lu {
1006bc619cfcSBrent Lu 	return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK);
1007bc619cfcSBrent Lu }
1008bc619cfcSBrent Lu EXPORT_SYMBOL(sof_dai_get_bclk);
1009bc619cfcSBrent Lu 
sof_of_machine_select(struct snd_sof_dev * sdev)1010354f6008SChunxu Li static struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev)
1011354f6008SChunxu Li {
1012354f6008SChunxu Li 	struct snd_sof_pdata *sof_pdata = sdev->pdata;
1013354f6008SChunxu Li 	const struct sof_dev_desc *desc = sof_pdata->desc;
1014354f6008SChunxu Li 	struct snd_sof_of_mach *mach = desc->of_machines;
1015354f6008SChunxu Li 
1016354f6008SChunxu Li 	if (!mach)
1017354f6008SChunxu Li 		return NULL;
1018354f6008SChunxu Li 
1019354f6008SChunxu Li 	for (; mach->compatible; mach++) {
1020354f6008SChunxu Li 		if (of_machine_is_compatible(mach->compatible)) {
1021354f6008SChunxu Li 			sof_pdata->tplg_filename = mach->sof_tplg_filename;
1022354f6008SChunxu Li 			if (mach->fw_filename)
1023354f6008SChunxu Li 				sof_pdata->fw_filename = mach->fw_filename;
1024354f6008SChunxu Li 
1025354f6008SChunxu Li 			return mach;
1026354f6008SChunxu Li 		}
1027354f6008SChunxu Li 	}
1028354f6008SChunxu Li 
1029354f6008SChunxu Li 	return NULL;
1030354f6008SChunxu Li }
1031354f6008SChunxu Li 
1032bc619cfcSBrent Lu /*
1033285880a2SDaniel Baluta  * SOF Driver enumeration.
1034285880a2SDaniel Baluta  */
sof_machine_check(struct snd_sof_dev * sdev)1035285880a2SDaniel Baluta int sof_machine_check(struct snd_sof_dev *sdev)
1036285880a2SDaniel Baluta {
1037285880a2SDaniel Baluta 	struct snd_sof_pdata *sof_pdata = sdev->pdata;
1038285880a2SDaniel Baluta 	const struct sof_dev_desc *desc = sof_pdata->desc;
1039285880a2SDaniel Baluta 	struct snd_soc_acpi_mach *mach;
1040285880a2SDaniel Baluta 
10414c1cc83fSPierre-Louis Bossart 	if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
1042354f6008SChunxu Li 		const struct snd_sof_of_mach *of_mach;
1043285880a2SDaniel Baluta 
10444bd1adb8SPierre-Louis Bossart 		if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
10454bd1adb8SPierre-Louis Bossart 		    sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
10464bd1adb8SPierre-Louis Bossart 			goto nocodec;
10474bd1adb8SPierre-Louis Bossart 
1048285880a2SDaniel Baluta 		/* find machine */
1049cb515f10SGuennadi Liakhovetski 		mach = snd_sof_machine_select(sdev);
1050cb515f10SGuennadi Liakhovetski 		if (mach) {
1051cb515f10SGuennadi Liakhovetski 			sof_pdata->machine = mach;
105217560515SRichard Fitzgerald 
105317560515SRichard Fitzgerald 			if (sof_pdata->subsystem_id_set) {
105417560515SRichard Fitzgerald 				mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor;
105517560515SRichard Fitzgerald 				mach->mach_params.subsystem_device = sof_pdata->subsystem_device;
105617560515SRichard Fitzgerald 				mach->mach_params.subsystem_id_set = true;
105717560515SRichard Fitzgerald 			}
105817560515SRichard Fitzgerald 
1059cb515f10SGuennadi Liakhovetski 			snd_sof_set_mach_params(mach, sdev);
1060285880a2SDaniel Baluta 			return 0;
1061285880a2SDaniel Baluta 		}
1062285880a2SDaniel Baluta 
1063354f6008SChunxu Li 		of_mach = sof_of_machine_select(sdev);
1064354f6008SChunxu Li 		if (of_mach) {
1065354f6008SChunxu Li 			sof_pdata->of_machine = of_mach;
1066354f6008SChunxu Li 			return 0;
1067354f6008SChunxu Li 		}
1068354f6008SChunxu Li 
10694c1cc83fSPierre-Louis Bossart 		if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
1070285880a2SDaniel Baluta 			dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
1071285880a2SDaniel Baluta 			return -ENODEV;
10724c1cc83fSPierre-Louis Bossart 		}
10734c1cc83fSPierre-Louis Bossart 	} else {
107464e2c37eSPierre-Louis Bossart 		dev_warn(sdev->dev, "Force to use nocodec mode\n");
10754c1cc83fSPierre-Louis Bossart 	}
10764c1cc83fSPierre-Louis Bossart 
10774bd1adb8SPierre-Louis Bossart nocodec:
1078285880a2SDaniel Baluta 	/* select nocodec mode */
1079285880a2SDaniel Baluta 	dev_warn(sdev->dev, "Using nocodec machine driver\n");
1080285880a2SDaniel Baluta 	mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
1081285880a2SDaniel Baluta 	if (!mach)
1082285880a2SDaniel Baluta 		return -ENOMEM;
1083285880a2SDaniel Baluta 
1084d612b455SRanjani Sridharan 	mach->drv_name = "sof-nocodec";
108553fe24c2SPierre-Louis Bossart 	if (!sof_pdata->tplg_filename)
1086d612b455SRanjani Sridharan 		sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
1087d612b455SRanjani Sridharan 
1088285880a2SDaniel Baluta 	sof_pdata->machine = mach;
1089cb515f10SGuennadi Liakhovetski 	snd_sof_set_mach_params(mach, sdev);
1090285880a2SDaniel Baluta 
1091285880a2SDaniel Baluta 	return 0;
1092285880a2SDaniel Baluta }
1093285880a2SDaniel Baluta EXPORT_SYMBOL(sof_machine_check);
1094285880a2SDaniel Baluta 
sof_machine_register(struct snd_sof_dev * sdev,void * pdata)1095285880a2SDaniel Baluta int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
1096285880a2SDaniel Baluta {
1097db69bcf9SGuennadi Liakhovetski 	struct snd_sof_pdata *plat_data = pdata;
1098285880a2SDaniel Baluta 	const char *drv_name;
1099285880a2SDaniel Baluta 	const void *mach;
1100285880a2SDaniel Baluta 	int size;
1101285880a2SDaniel Baluta 
1102285880a2SDaniel Baluta 	drv_name = plat_data->machine->drv_name;
1103db69bcf9SGuennadi Liakhovetski 	mach = plat_data->machine;
1104285880a2SDaniel Baluta 	size = sizeof(*plat_data->machine);
1105285880a2SDaniel Baluta 
1106285880a2SDaniel Baluta 	/* register machine driver, pass machine info as pdata */
1107285880a2SDaniel Baluta 	plat_data->pdev_mach =
1108285880a2SDaniel Baluta 		platform_device_register_data(sdev->dev, drv_name,
1109285880a2SDaniel Baluta 					      PLATFORM_DEVID_NONE, mach, size);
1110285880a2SDaniel Baluta 	if (IS_ERR(plat_data->pdev_mach))
1111285880a2SDaniel Baluta 		return PTR_ERR(plat_data->pdev_mach);
1112285880a2SDaniel Baluta 
1113285880a2SDaniel Baluta 	dev_dbg(sdev->dev, "created machine %s\n",
1114285880a2SDaniel Baluta 		dev_name(&plat_data->pdev_mach->dev));
1115285880a2SDaniel Baluta 
1116285880a2SDaniel Baluta 	return 0;
1117285880a2SDaniel Baluta }
1118285880a2SDaniel Baluta EXPORT_SYMBOL(sof_machine_register);
1119285880a2SDaniel Baluta 
sof_machine_unregister(struct snd_sof_dev * sdev,void * pdata)1120285880a2SDaniel Baluta void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
1121285880a2SDaniel Baluta {
1122db69bcf9SGuennadi Liakhovetski 	struct snd_sof_pdata *plat_data = pdata;
1123285880a2SDaniel Baluta 
1124285880a2SDaniel Baluta 	if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
1125285880a2SDaniel Baluta 		platform_device_unregister(plat_data->pdev_mach);
1126285880a2SDaniel Baluta }
1127285880a2SDaniel Baluta EXPORT_SYMBOL(sof_machine_unregister);
1128