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