xref: /openbmc/linux/sound/soc/soc-pcm.c (revision c840f7698d26b078695dbc863ccb6a14ca765f98)
1ed517582SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0+
2ed517582SKuninori Morimoto //
3ed517582SKuninori Morimoto // soc-pcm.c  --  ALSA SoC PCM
4ed517582SKuninori Morimoto //
5ed517582SKuninori Morimoto // Copyright 2005 Wolfson Microelectronics PLC.
6ed517582SKuninori Morimoto // Copyright 2005 Openedhand Ltd.
7ed517582SKuninori Morimoto // Copyright (C) 2010 Slimlogic Ltd.
8ed517582SKuninori Morimoto // Copyright (C) 2010 Texas Instruments Inc.
9ed517582SKuninori Morimoto //
10ed517582SKuninori Morimoto // Authors: Liam Girdwood <lrg@ti.com>
11ed517582SKuninori Morimoto //          Mark Brown <broonie@opensource.wolfsonmicro.com>
12ddee627cSLiam Girdwood 
13ddee627cSLiam Girdwood #include <linux/kernel.h>
14ddee627cSLiam Girdwood #include <linux/init.h>
15ddee627cSLiam Girdwood #include <linux/delay.h>
16988e8cc4SNicolin Chen #include <linux/pinctrl/consumer.h>
17d6652ef8SMark Brown #include <linux/pm_runtime.h>
18ddee627cSLiam Girdwood #include <linux/slab.h>
19ddee627cSLiam Girdwood #include <linux/workqueue.h>
2001d7584cSLiam Girdwood #include <linux/export.h>
21f86dcef8SLiam Girdwood #include <linux/debugfs.h>
22ddee627cSLiam Girdwood #include <sound/core.h>
23ddee627cSLiam Girdwood #include <sound/pcm.h>
24ddee627cSLiam Girdwood #include <sound/pcm_params.h>
25ddee627cSLiam Girdwood #include <sound/soc.h>
2601d7584cSLiam Girdwood #include <sound/soc-dpcm.h>
27ddee627cSLiam Girdwood #include <sound/initval.h>
28ddee627cSLiam Girdwood 
2901d7584cSLiam Girdwood #define DPCM_MAX_BE_USERS	8
3001d7584cSLiam Girdwood 
31c3212829SKuninori Morimoto #ifdef CONFIG_DEBUG_FS
32c3212829SKuninori Morimoto static const char *dpcm_state_string(enum snd_soc_dpcm_state state)
33c3212829SKuninori Morimoto {
34c3212829SKuninori Morimoto 	switch (state) {
35c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_NEW:
36c3212829SKuninori Morimoto 		return "new";
37c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_OPEN:
38c3212829SKuninori Morimoto 		return "open";
39c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_HW_PARAMS:
40c3212829SKuninori Morimoto 		return "hw_params";
41c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_PREPARE:
42c3212829SKuninori Morimoto 		return "prepare";
43c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_START:
44c3212829SKuninori Morimoto 		return "start";
45c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_STOP:
46c3212829SKuninori Morimoto 		return "stop";
47c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_SUSPEND:
48c3212829SKuninori Morimoto 		return "suspend";
49c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_PAUSED:
50c3212829SKuninori Morimoto 		return "paused";
51c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_HW_FREE:
52c3212829SKuninori Morimoto 		return "hw_free";
53c3212829SKuninori Morimoto 	case SND_SOC_DPCM_STATE_CLOSE:
54c3212829SKuninori Morimoto 		return "close";
55c3212829SKuninori Morimoto 	}
56c3212829SKuninori Morimoto 
57c3212829SKuninori Morimoto 	return "unknown";
58c3212829SKuninori Morimoto }
59c3212829SKuninori Morimoto 
60c3212829SKuninori Morimoto static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
61c3212829SKuninori Morimoto 			       int stream, char *buf, size_t size)
62c3212829SKuninori Morimoto {
63c3212829SKuninori Morimoto 	struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
64c3212829SKuninori Morimoto 	struct snd_soc_dpcm *dpcm;
65c3212829SKuninori Morimoto 	ssize_t offset = 0;
66c3212829SKuninori Morimoto 	unsigned long flags;
67c3212829SKuninori Morimoto 
68c3212829SKuninori Morimoto 	/* FE state */
69d0c9abb8STakashi Iwai 	offset += scnprintf(buf + offset, size - offset,
70c3212829SKuninori Morimoto 			   "[%s - %s]\n", fe->dai_link->name,
71c3212829SKuninori Morimoto 			   stream ? "Capture" : "Playback");
72c3212829SKuninori Morimoto 
73d0c9abb8STakashi Iwai 	offset += scnprintf(buf + offset, size - offset, "State: %s\n",
74c3212829SKuninori Morimoto 			   dpcm_state_string(fe->dpcm[stream].state));
75c3212829SKuninori Morimoto 
76c3212829SKuninori Morimoto 	if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
77c3212829SKuninori Morimoto 	    (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
78d0c9abb8STakashi Iwai 		offset += scnprintf(buf + offset, size - offset,
79c3212829SKuninori Morimoto 				   "Hardware Params: "
80c3212829SKuninori Morimoto 				   "Format = %s, Channels = %d, Rate = %d\n",
81c3212829SKuninori Morimoto 				   snd_pcm_format_name(params_format(params)),
82c3212829SKuninori Morimoto 				   params_channels(params),
83c3212829SKuninori Morimoto 				   params_rate(params));
84c3212829SKuninori Morimoto 
85c3212829SKuninori Morimoto 	/* BEs state */
86d0c9abb8STakashi Iwai 	offset += scnprintf(buf + offset, size - offset, "Backends:\n");
87c3212829SKuninori Morimoto 
88c3212829SKuninori Morimoto 	if (list_empty(&fe->dpcm[stream].be_clients)) {
89d0c9abb8STakashi Iwai 		offset += scnprintf(buf + offset, size - offset,
90c3212829SKuninori Morimoto 				   " No active DSP links\n");
91c3212829SKuninori Morimoto 		goto out;
92c3212829SKuninori Morimoto 	}
93c3212829SKuninori Morimoto 
94c3212829SKuninori Morimoto 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
95c3212829SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
96c3212829SKuninori Morimoto 		struct snd_soc_pcm_runtime *be = dpcm->be;
97c3212829SKuninori Morimoto 		params = &dpcm->hw_params;
98c3212829SKuninori Morimoto 
99d0c9abb8STakashi Iwai 		offset += scnprintf(buf + offset, size - offset,
100c3212829SKuninori Morimoto 				   "- %s\n", be->dai_link->name);
101c3212829SKuninori Morimoto 
102d0c9abb8STakashi Iwai 		offset += scnprintf(buf + offset, size - offset,
103c3212829SKuninori Morimoto 				   "   State: %s\n",
104c3212829SKuninori Morimoto 				   dpcm_state_string(be->dpcm[stream].state));
105c3212829SKuninori Morimoto 
106c3212829SKuninori Morimoto 		if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
107c3212829SKuninori Morimoto 		    (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
108d0c9abb8STakashi Iwai 			offset += scnprintf(buf + offset, size - offset,
109c3212829SKuninori Morimoto 					   "   Hardware Params: "
110c3212829SKuninori Morimoto 					   "Format = %s, Channels = %d, Rate = %d\n",
111c3212829SKuninori Morimoto 					   snd_pcm_format_name(params_format(params)),
112c3212829SKuninori Morimoto 					   params_channels(params),
113c3212829SKuninori Morimoto 					   params_rate(params));
114c3212829SKuninori Morimoto 	}
115c3212829SKuninori Morimoto 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
116c3212829SKuninori Morimoto out:
117c3212829SKuninori Morimoto 	return offset;
118c3212829SKuninori Morimoto }
119c3212829SKuninori Morimoto 
120c3212829SKuninori Morimoto static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
121c3212829SKuninori Morimoto 				    size_t count, loff_t *ppos)
122c3212829SKuninori Morimoto {
123c3212829SKuninori Morimoto 	struct snd_soc_pcm_runtime *fe = file->private_data;
124c3212829SKuninori Morimoto 	ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
125c3212829SKuninori Morimoto 	int stream;
126c3212829SKuninori Morimoto 	char *buf;
127c3212829SKuninori Morimoto 
1286e1276a5SBard Liao 	if (fe->num_cpus > 1) {
1296e1276a5SBard Liao 		dev_err(fe->dev,
1306e1276a5SBard Liao 			"%s doesn't support Multi CPU yet\n", __func__);
1316e1276a5SBard Liao 		return -EINVAL;
1326e1276a5SBard Liao 	}
1336e1276a5SBard Liao 
134c3212829SKuninori Morimoto 	buf = kmalloc(out_count, GFP_KERNEL);
135c3212829SKuninori Morimoto 	if (!buf)
136c3212829SKuninori Morimoto 		return -ENOMEM;
137c3212829SKuninori Morimoto 
138c3212829SKuninori Morimoto 	for_each_pcm_streams(stream)
139c3212829SKuninori Morimoto 		if (snd_soc_dai_stream_valid(fe->cpu_dai, stream))
140c3212829SKuninori Morimoto 			offset += dpcm_show_state(fe, stream,
141c3212829SKuninori Morimoto 						  buf + offset,
142c3212829SKuninori Morimoto 						  out_count - offset);
143c3212829SKuninori Morimoto 
144c3212829SKuninori Morimoto 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
145c3212829SKuninori Morimoto 
146c3212829SKuninori Morimoto 	kfree(buf);
147c3212829SKuninori Morimoto 	return ret;
148c3212829SKuninori Morimoto }
149c3212829SKuninori Morimoto 
150c3212829SKuninori Morimoto static const struct file_operations dpcm_state_fops = {
151c3212829SKuninori Morimoto 	.open = simple_open,
152c3212829SKuninori Morimoto 	.read = dpcm_state_read_file,
153c3212829SKuninori Morimoto 	.llseek = default_llseek,
154c3212829SKuninori Morimoto };
155c3212829SKuninori Morimoto 
156c3212829SKuninori Morimoto void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
157c3212829SKuninori Morimoto {
158c3212829SKuninori Morimoto 	if (!rtd->dai_link)
159c3212829SKuninori Morimoto 		return;
160c3212829SKuninori Morimoto 
161c3212829SKuninori Morimoto 	if (!rtd->dai_link->dynamic)
162c3212829SKuninori Morimoto 		return;
163c3212829SKuninori Morimoto 
164c3212829SKuninori Morimoto 	if (!rtd->card->debugfs_card_root)
165c3212829SKuninori Morimoto 		return;
166c3212829SKuninori Morimoto 
167c3212829SKuninori Morimoto 	rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
168c3212829SKuninori Morimoto 						    rtd->card->debugfs_card_root);
169c3212829SKuninori Morimoto 
170c3212829SKuninori Morimoto 	debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root,
171c3212829SKuninori Morimoto 			    rtd, &dpcm_state_fops);
172c3212829SKuninori Morimoto }
173154dae87SKuninori Morimoto 
174154dae87SKuninori Morimoto static void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm, int stream)
175154dae87SKuninori Morimoto {
176154dae87SKuninori Morimoto 	char *name;
177154dae87SKuninori Morimoto 
178154dae87SKuninori Morimoto 	name = kasprintf(GFP_KERNEL, "%s:%s", dpcm->be->dai_link->name,
179154dae87SKuninori Morimoto 			 stream ? "capture" : "playback");
180154dae87SKuninori Morimoto 	if (name) {
181154dae87SKuninori Morimoto 		dpcm->debugfs_state = debugfs_create_dir(
182154dae87SKuninori Morimoto 			name, dpcm->fe->debugfs_dpcm_root);
183154dae87SKuninori Morimoto 		debugfs_create_u32("state", 0644, dpcm->debugfs_state,
184154dae87SKuninori Morimoto 				   &dpcm->state);
185154dae87SKuninori Morimoto 		kfree(name);
186154dae87SKuninori Morimoto 	}
187154dae87SKuninori Morimoto }
188154dae87SKuninori Morimoto 
189154dae87SKuninori Morimoto static void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm)
190154dae87SKuninori Morimoto {
191154dae87SKuninori Morimoto 	debugfs_remove_recursive(dpcm->debugfs_state);
192154dae87SKuninori Morimoto }
193154dae87SKuninori Morimoto 
194154dae87SKuninori Morimoto #else
195154dae87SKuninori Morimoto static inline void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm,
196154dae87SKuninori Morimoto 					     int stream)
197154dae87SKuninori Morimoto {
198154dae87SKuninori Morimoto }
199154dae87SKuninori Morimoto 
200154dae87SKuninori Morimoto static inline void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm)
201154dae87SKuninori Morimoto {
202154dae87SKuninori Morimoto }
203c3212829SKuninori Morimoto #endif
204c3212829SKuninori Morimoto 
205f183f927SKuninori Morimoto static int soc_rtd_startup(struct snd_soc_pcm_runtime *rtd,
206f183f927SKuninori Morimoto 			   struct snd_pcm_substream *substream)
207f183f927SKuninori Morimoto {
208f183f927SKuninori Morimoto 	if (rtd->dai_link->ops &&
209f183f927SKuninori Morimoto 	    rtd->dai_link->ops->startup)
210f183f927SKuninori Morimoto 		return rtd->dai_link->ops->startup(substream);
211f183f927SKuninori Morimoto 	return 0;
212f183f927SKuninori Morimoto }
213f183f927SKuninori Morimoto 
2140be429f9SKuninori Morimoto static void soc_rtd_shutdown(struct snd_soc_pcm_runtime *rtd,
2150be429f9SKuninori Morimoto 			     struct snd_pcm_substream *substream)
2160be429f9SKuninori Morimoto {
2170be429f9SKuninori Morimoto 	if (rtd->dai_link->ops &&
2180be429f9SKuninori Morimoto 	    rtd->dai_link->ops->shutdown)
2190be429f9SKuninori Morimoto 		rtd->dai_link->ops->shutdown(substream);
2200be429f9SKuninori Morimoto }
2210be429f9SKuninori Morimoto 
22244c1a75bSKuninori Morimoto static int soc_rtd_prepare(struct snd_soc_pcm_runtime *rtd,
22344c1a75bSKuninori Morimoto 			   struct snd_pcm_substream *substream)
22444c1a75bSKuninori Morimoto {
22544c1a75bSKuninori Morimoto 	if (rtd->dai_link->ops &&
22644c1a75bSKuninori Morimoto 	    rtd->dai_link->ops->prepare)
22744c1a75bSKuninori Morimoto 		return rtd->dai_link->ops->prepare(substream);
22844c1a75bSKuninori Morimoto 	return 0;
22944c1a75bSKuninori Morimoto }
23044c1a75bSKuninori Morimoto 
231de9ad990SKuninori Morimoto static int soc_rtd_hw_params(struct snd_soc_pcm_runtime *rtd,
232de9ad990SKuninori Morimoto 			     struct snd_pcm_substream *substream,
233de9ad990SKuninori Morimoto 			     struct snd_pcm_hw_params *params)
234de9ad990SKuninori Morimoto {
235de9ad990SKuninori Morimoto 	if (rtd->dai_link->ops &&
236de9ad990SKuninori Morimoto 	    rtd->dai_link->ops->hw_params)
237de9ad990SKuninori Morimoto 		return rtd->dai_link->ops->hw_params(substream, params);
238de9ad990SKuninori Morimoto 	return 0;
239de9ad990SKuninori Morimoto }
240de9ad990SKuninori Morimoto 
24149f020e5SKuninori Morimoto static void soc_rtd_hw_free(struct snd_soc_pcm_runtime *rtd,
24249f020e5SKuninori Morimoto 			    struct snd_pcm_substream *substream)
24349f020e5SKuninori Morimoto {
24449f020e5SKuninori Morimoto 	if (rtd->dai_link->ops &&
24549f020e5SKuninori Morimoto 	    rtd->dai_link->ops->hw_free)
24649f020e5SKuninori Morimoto 		rtd->dai_link->ops->hw_free(substream);
24749f020e5SKuninori Morimoto }
24849f020e5SKuninori Morimoto 
249ad2bf9f2SKuninori Morimoto static int soc_rtd_trigger(struct snd_soc_pcm_runtime *rtd,
250ad2bf9f2SKuninori Morimoto 			   struct snd_pcm_substream *substream,
251ad2bf9f2SKuninori Morimoto 			   int cmd)
252ad2bf9f2SKuninori Morimoto {
253ad2bf9f2SKuninori Morimoto 	if (rtd->dai_link->ops &&
254ad2bf9f2SKuninori Morimoto 	    rtd->dai_link->ops->trigger)
255ad2bf9f2SKuninori Morimoto 		return rtd->dai_link->ops->trigger(substream, cmd);
256ad2bf9f2SKuninori Morimoto 	return 0;
257ad2bf9f2SKuninori Morimoto }
258ad2bf9f2SKuninori Morimoto 
2597a5aaba4SKuninori Morimoto static void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
2607a5aaba4SKuninori Morimoto 				   int stream, int action)
2617a5aaba4SKuninori Morimoto {
262*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
2637a5aaba4SKuninori Morimoto 	int i;
2647a5aaba4SKuninori Morimoto 
2657a5aaba4SKuninori Morimoto 	lockdep_assert_held(&rtd->card->pcm_mutex);
2667a5aaba4SKuninori Morimoto 
267*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai) {
268*c840f769SKuninori Morimoto 		dai->stream_active[stream] += action;
269*c840f769SKuninori Morimoto 		dai->active += action;
270*c840f769SKuninori Morimoto 		dai->component->active += action;
2717a5aaba4SKuninori Morimoto 	}
2727a5aaba4SKuninori Morimoto }
2737a5aaba4SKuninori Morimoto 
27490996f43SLars-Peter Clausen /**
27524894b76SLars-Peter Clausen  * snd_soc_runtime_activate() - Increment active count for PCM runtime components
27624894b76SLars-Peter Clausen  * @rtd: ASoC PCM runtime that is activated
27724894b76SLars-Peter Clausen  * @stream: Direction of the PCM stream
27824894b76SLars-Peter Clausen  *
27924894b76SLars-Peter Clausen  * Increments the active count for all the DAIs and components attached to a PCM
28024894b76SLars-Peter Clausen  * runtime. Should typically be called when a stream is opened.
28124894b76SLars-Peter Clausen  *
28272b745e3SPeter Ujfalusi  * Must be called with the rtd->card->pcm_mutex being held
28324894b76SLars-Peter Clausen  */
28424894b76SLars-Peter Clausen void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
28524894b76SLars-Peter Clausen {
2867a5aaba4SKuninori Morimoto 	snd_soc_runtime_action(rtd, stream, 1);
28724894b76SLars-Peter Clausen }
288f17a1478SGuennadi Liakhovetski EXPORT_SYMBOL_GPL(snd_soc_runtime_activate);
28924894b76SLars-Peter Clausen 
29024894b76SLars-Peter Clausen /**
29124894b76SLars-Peter Clausen  * snd_soc_runtime_deactivate() - Decrement active count for PCM runtime components
29224894b76SLars-Peter Clausen  * @rtd: ASoC PCM runtime that is deactivated
29324894b76SLars-Peter Clausen  * @stream: Direction of the PCM stream
29424894b76SLars-Peter Clausen  *
29524894b76SLars-Peter Clausen  * Decrements the active count for all the DAIs and components attached to a PCM
29624894b76SLars-Peter Clausen  * runtime. Should typically be called when a stream is closed.
29724894b76SLars-Peter Clausen  *
29872b745e3SPeter Ujfalusi  * Must be called with the rtd->card->pcm_mutex being held
29924894b76SLars-Peter Clausen  */
30024894b76SLars-Peter Clausen void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
30124894b76SLars-Peter Clausen {
3027a5aaba4SKuninori Morimoto 	snd_soc_runtime_action(rtd, stream, -1);
30324894b76SLars-Peter Clausen }
304f17a1478SGuennadi Liakhovetski EXPORT_SYMBOL_GPL(snd_soc_runtime_deactivate);
30524894b76SLars-Peter Clausen 
30624894b76SLars-Peter Clausen /**
307208a1589SLars-Peter Clausen  * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay
308208a1589SLars-Peter Clausen  * @rtd: The ASoC PCM runtime that should be checked.
309208a1589SLars-Peter Clausen  *
310208a1589SLars-Peter Clausen  * This function checks whether the power down delay should be ignored for a
311208a1589SLars-Peter Clausen  * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has
312208a1589SLars-Peter Clausen  * been configured to ignore the delay, or if none of the components benefits
313208a1589SLars-Peter Clausen  * from having the delay.
314208a1589SLars-Peter Clausen  */
315208a1589SLars-Peter Clausen bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
316208a1589SLars-Peter Clausen {
317fbb16563SKuninori Morimoto 	struct snd_soc_component *component;
3182e5894d7SBenoit Cousson 	bool ignore = true;
319613fb500SKuninori Morimoto 	int i;
3202e5894d7SBenoit Cousson 
321208a1589SLars-Peter Clausen 	if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
322208a1589SLars-Peter Clausen 		return true;
323208a1589SLars-Peter Clausen 
324613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
32572c38184SKuninori Morimoto 		ignore &= !component->driver->use_pmdown_time;
326fbb16563SKuninori Morimoto 
327fbb16563SKuninori Morimoto 	return ignore;
328208a1589SLars-Peter Clausen }
329208a1589SLars-Peter Clausen 
330208a1589SLars-Peter Clausen /**
33190996f43SLars-Peter Clausen  * snd_soc_set_runtime_hwparams - set the runtime hardware parameters
33290996f43SLars-Peter Clausen  * @substream: the pcm substream
33390996f43SLars-Peter Clausen  * @hw: the hardware parameters
33490996f43SLars-Peter Clausen  *
33590996f43SLars-Peter Clausen  * Sets the substream runtime hardware parameters.
33690996f43SLars-Peter Clausen  */
33790996f43SLars-Peter Clausen int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
33890996f43SLars-Peter Clausen 	const struct snd_pcm_hardware *hw)
33990996f43SLars-Peter Clausen {
34090996f43SLars-Peter Clausen 	struct snd_pcm_runtime *runtime = substream->runtime;
34190996f43SLars-Peter Clausen 	runtime->hw.info = hw->info;
34290996f43SLars-Peter Clausen 	runtime->hw.formats = hw->formats;
34390996f43SLars-Peter Clausen 	runtime->hw.period_bytes_min = hw->period_bytes_min;
34490996f43SLars-Peter Clausen 	runtime->hw.period_bytes_max = hw->period_bytes_max;
34590996f43SLars-Peter Clausen 	runtime->hw.periods_min = hw->periods_min;
34690996f43SLars-Peter Clausen 	runtime->hw.periods_max = hw->periods_max;
34790996f43SLars-Peter Clausen 	runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
34890996f43SLars-Peter Clausen 	runtime->hw.fifo_size = hw->fifo_size;
34990996f43SLars-Peter Clausen 	return 0;
35090996f43SLars-Peter Clausen }
35190996f43SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
35290996f43SLars-Peter Clausen 
35301d7584cSLiam Girdwood /* DPCM stream event, send event to FE and all active BEs. */
35423607025SLiam Girdwood int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
35501d7584cSLiam Girdwood 	int event)
35601d7584cSLiam Girdwood {
35701d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
35801d7584cSLiam Girdwood 
3598d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, dir, dpcm) {
36001d7584cSLiam Girdwood 
36101d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
36201d7584cSLiam Girdwood 
363103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n",
36401d7584cSLiam Girdwood 				be->dai_link->name, event, dir);
36501d7584cSLiam Girdwood 
366b1cd2e34SBanajit Goswami 		if ((event == SND_SOC_DAPM_STREAM_STOP) &&
367b1cd2e34SBanajit Goswami 		    (be->dpcm[dir].users >= 1))
368b1cd2e34SBanajit Goswami 			continue;
369b1cd2e34SBanajit Goswami 
37001d7584cSLiam Girdwood 		snd_soc_dapm_stream_event(be, dir, event);
37101d7584cSLiam Girdwood 	}
37201d7584cSLiam Girdwood 
37301d7584cSLiam Girdwood 	snd_soc_dapm_stream_event(fe, dir, event);
37401d7584cSLiam Girdwood 
37501d7584cSLiam Girdwood 	return 0;
37601d7584cSLiam Girdwood }
37701d7584cSLiam Girdwood 
37817841020SDong Aisheng static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
37917841020SDong Aisheng 					struct snd_soc_dai *soc_dai)
380ddee627cSLiam Girdwood {
381ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
382ddee627cSLiam Girdwood 	int ret;
383ddee627cSLiam Girdwood 
3843635bf09SNicolin Chen 	if (soc_dai->rate && (soc_dai->driver->symmetric_rates ||
3853635bf09SNicolin Chen 				rtd->dai_link->symmetric_rates)) {
3863635bf09SNicolin Chen 		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
3873635bf09SNicolin Chen 				soc_dai->rate);
388ddee627cSLiam Girdwood 
3894dcdd43bSLars-Peter Clausen 		ret = snd_pcm_hw_constraint_single(substream->runtime,
390ddee627cSLiam Girdwood 						SNDRV_PCM_HW_PARAM_RATE,
3914dcdd43bSLars-Peter Clausen 						soc_dai->rate);
392ddee627cSLiam Girdwood 		if (ret < 0) {
39317841020SDong Aisheng 			dev_err(soc_dai->dev,
3943635bf09SNicolin Chen 				"ASoC: Unable to apply rate constraint: %d\n",
395103d84a3SLiam Girdwood 				ret);
396ddee627cSLiam Girdwood 			return ret;
397ddee627cSLiam Girdwood 		}
3983635bf09SNicolin Chen 	}
3993635bf09SNicolin Chen 
4003635bf09SNicolin Chen 	if (soc_dai->channels && (soc_dai->driver->symmetric_channels ||
4013635bf09SNicolin Chen 				rtd->dai_link->symmetric_channels)) {
4023635bf09SNicolin Chen 		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n",
4033635bf09SNicolin Chen 				soc_dai->channels);
4043635bf09SNicolin Chen 
4054dcdd43bSLars-Peter Clausen 		ret = snd_pcm_hw_constraint_single(substream->runtime,
4063635bf09SNicolin Chen 						SNDRV_PCM_HW_PARAM_CHANNELS,
4073635bf09SNicolin Chen 						soc_dai->channels);
4083635bf09SNicolin Chen 		if (ret < 0) {
4093635bf09SNicolin Chen 			dev_err(soc_dai->dev,
4103635bf09SNicolin Chen 				"ASoC: Unable to apply channel symmetry constraint: %d\n",
4113635bf09SNicolin Chen 				ret);
4123635bf09SNicolin Chen 			return ret;
4133635bf09SNicolin Chen 		}
4143635bf09SNicolin Chen 	}
4153635bf09SNicolin Chen 
4163635bf09SNicolin Chen 	if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits ||
4173635bf09SNicolin Chen 				rtd->dai_link->symmetric_samplebits)) {
4183635bf09SNicolin Chen 		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n",
4193635bf09SNicolin Chen 				soc_dai->sample_bits);
4203635bf09SNicolin Chen 
4214dcdd43bSLars-Peter Clausen 		ret = snd_pcm_hw_constraint_single(substream->runtime,
4223635bf09SNicolin Chen 						SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
4233635bf09SNicolin Chen 						soc_dai->sample_bits);
4243635bf09SNicolin Chen 		if (ret < 0) {
4253635bf09SNicolin Chen 			dev_err(soc_dai->dev,
4263635bf09SNicolin Chen 				"ASoC: Unable to apply sample bits symmetry constraint: %d\n",
4273635bf09SNicolin Chen 				ret);
4283635bf09SNicolin Chen 			return ret;
4293635bf09SNicolin Chen 		}
4303635bf09SNicolin Chen 	}
431ddee627cSLiam Girdwood 
432ddee627cSLiam Girdwood 	return 0;
433ddee627cSLiam Girdwood }
434ddee627cSLiam Girdwood 
4353635bf09SNicolin Chen static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
4363635bf09SNicolin Chen 				struct snd_pcm_hw_params *params)
4373635bf09SNicolin Chen {
4383635bf09SNicolin Chen 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
439*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
44019bdcc7aSShreyas NC 	struct snd_soc_dai *cpu_dai;
4412e5894d7SBenoit Cousson 	unsigned int rate, channels, sample_bits, symmetry, i;
4423635bf09SNicolin Chen 
4433635bf09SNicolin Chen 	rate = params_rate(params);
4443635bf09SNicolin Chen 	channels = params_channels(params);
4453635bf09SNicolin Chen 	sample_bits = snd_pcm_format_physical_width(params_format(params));
4463635bf09SNicolin Chen 
4473635bf09SNicolin Chen 	/* reject unmatched parameters when applying symmetry */
44819bdcc7aSShreyas NC 	symmetry = rtd->dai_link->symmetric_rates;
44919bdcc7aSShreyas NC 
450*c840f769SKuninori Morimoto 	for_each_rtd_cpu_dais(rtd, i, dai)
451*c840f769SKuninori Morimoto 		symmetry |= dai->driver->symmetric_rates;
4522e5894d7SBenoit Cousson 
45319bdcc7aSShreyas NC 	if (symmetry) {
454a4be4187SKuninori Morimoto 		for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
45519bdcc7aSShreyas NC 			if (cpu_dai->rate && cpu_dai->rate != rate) {
4563635bf09SNicolin Chen 				dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n",
4573635bf09SNicolin Chen 					cpu_dai->rate, rate);
4583635bf09SNicolin Chen 				return -EINVAL;
4593635bf09SNicolin Chen 			}
46019bdcc7aSShreyas NC 		}
46119bdcc7aSShreyas NC 	}
4623635bf09SNicolin Chen 
46319bdcc7aSShreyas NC 	symmetry = rtd->dai_link->symmetric_channels;
46419bdcc7aSShreyas NC 
465*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai)
466*c840f769SKuninori Morimoto 		symmetry |= dai->driver->symmetric_channels;
4672e5894d7SBenoit Cousson 
46819bdcc7aSShreyas NC 	if (symmetry) {
469a4be4187SKuninori Morimoto 		for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
47019bdcc7aSShreyas NC 			if (cpu_dai->channels &&
47119bdcc7aSShreyas NC 			    cpu_dai->channels != channels) {
4723635bf09SNicolin Chen 				dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n",
4733635bf09SNicolin Chen 					cpu_dai->channels, channels);
4743635bf09SNicolin Chen 				return -EINVAL;
4753635bf09SNicolin Chen 			}
47619bdcc7aSShreyas NC 		}
47719bdcc7aSShreyas NC 	}
4783635bf09SNicolin Chen 
47919bdcc7aSShreyas NC 	symmetry = rtd->dai_link->symmetric_samplebits;
48019bdcc7aSShreyas NC 
481*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai)
482*c840f769SKuninori Morimoto 		symmetry |= dai->driver->symmetric_samplebits;
4832e5894d7SBenoit Cousson 
48419bdcc7aSShreyas NC 	if (symmetry) {
485a4be4187SKuninori Morimoto 		for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
48619bdcc7aSShreyas NC 			if (cpu_dai->sample_bits &&
48719bdcc7aSShreyas NC 			    cpu_dai->sample_bits != sample_bits) {
4883635bf09SNicolin Chen 				dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n",
4893635bf09SNicolin Chen 					cpu_dai->sample_bits, sample_bits);
4903635bf09SNicolin Chen 				return -EINVAL;
4913635bf09SNicolin Chen 			}
49219bdcc7aSShreyas NC 		}
49319bdcc7aSShreyas NC 	}
494ddee627cSLiam Girdwood 
495ddee627cSLiam Girdwood 	return 0;
496ddee627cSLiam Girdwood }
497ddee627cSLiam Girdwood 
49862e5f676SLars-Peter Clausen static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream)
49962e5f676SLars-Peter Clausen {
50062e5f676SLars-Peter Clausen 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
50162e5f676SLars-Peter Clausen 	struct snd_soc_dai_link *link = rtd->dai_link;
502*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
5032e5894d7SBenoit Cousson 	unsigned int symmetry, i;
50462e5f676SLars-Peter Clausen 
50519bdcc7aSShreyas NC 	symmetry = link->symmetric_rates ||
50619bdcc7aSShreyas NC 		link->symmetric_channels ||
50719bdcc7aSShreyas NC 		link->symmetric_samplebits;
50819bdcc7aSShreyas NC 
509*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai)
51019bdcc7aSShreyas NC 		symmetry = symmetry ||
511*c840f769SKuninori Morimoto 			dai->driver->symmetric_rates ||
512*c840f769SKuninori Morimoto 			dai->driver->symmetric_channels ||
513*c840f769SKuninori Morimoto 			dai->driver->symmetric_samplebits;
5142e5894d7SBenoit Cousson 
5152e5894d7SBenoit Cousson 	return symmetry;
51662e5f676SLars-Peter Clausen }
51762e5f676SLars-Peter Clausen 
5182e5894d7SBenoit Cousson static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits)
51958ba9b25SMark Brown {
5202e5894d7SBenoit Cousson 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
521c6068d3aSTakashi Iwai 	int ret;
52258ba9b25SMark Brown 
52358ba9b25SMark Brown 	if (!bits)
52458ba9b25SMark Brown 		return;
52558ba9b25SMark Brown 
5260e2a3751SLars-Peter Clausen 	ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, bits);
52758ba9b25SMark Brown 	if (ret != 0)
5280e2a3751SLars-Peter Clausen 		dev_warn(rtd->dev, "ASoC: Failed to set MSB %d: %d\n",
5290e2a3751SLars-Peter Clausen 				 bits, ret);
53058ba9b25SMark Brown }
53158ba9b25SMark Brown 
532c8dd1fecSBenoit Cousson static void soc_pcm_apply_msb(struct snd_pcm_substream *substream)
533bd477c31SLars-Peter Clausen {
534c8dd1fecSBenoit Cousson 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
53519bdcc7aSShreyas NC 	struct snd_soc_dai *cpu_dai;
5362e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
53757be9206SKuninori Morimoto 	struct snd_soc_pcm_stream *pcm_codec, *pcm_cpu;
53857be9206SKuninori Morimoto 	int stream = substream->stream;
5392e5894d7SBenoit Cousson 	int i;
54019bdcc7aSShreyas NC 	unsigned int bits = 0, cpu_bits = 0;
541c8dd1fecSBenoit Cousson 
542a4be4187SKuninori Morimoto 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
54357be9206SKuninori Morimoto 		pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream);
54457be9206SKuninori Morimoto 
54557be9206SKuninori Morimoto 		if (pcm_codec->sig_bits == 0) {
5462e5894d7SBenoit Cousson 			bits = 0;
5472e5894d7SBenoit Cousson 			break;
5482e5894d7SBenoit Cousson 		}
54957be9206SKuninori Morimoto 		bits = max(pcm_codec->sig_bits, bits);
5502e5894d7SBenoit Cousson 	}
55157be9206SKuninori Morimoto 
552a4be4187SKuninori Morimoto 	for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
55357be9206SKuninori Morimoto 		pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream);
55419bdcc7aSShreyas NC 
55519bdcc7aSShreyas NC 		if (pcm_cpu->sig_bits == 0) {
55619bdcc7aSShreyas NC 			cpu_bits = 0;
55719bdcc7aSShreyas NC 			break;
55819bdcc7aSShreyas NC 		}
55919bdcc7aSShreyas NC 		cpu_bits = max(pcm_cpu->sig_bits, cpu_bits);
56019bdcc7aSShreyas NC 	}
561c8dd1fecSBenoit Cousson 
5622e5894d7SBenoit Cousson 	soc_pcm_set_msb(substream, bits);
5632e5894d7SBenoit Cousson 	soc_pcm_set_msb(substream, cpu_bits);
564c8dd1fecSBenoit Cousson }
565c8dd1fecSBenoit Cousson 
5665854a464SSamuel Holland /**
5675854a464SSamuel Holland  * snd_soc_runtime_calc_hw() - Calculate hw limits for a PCM stream
5685854a464SSamuel Holland  * @rtd: ASoC PCM runtime
5695854a464SSamuel Holland  * @hw: PCM hardware parameters (output)
5705854a464SSamuel Holland  * @stream: Direction of the PCM stream
5715854a464SSamuel Holland  *
5725854a464SSamuel Holland  * Calculates the subset of stream parameters supported by all DAIs
5735854a464SSamuel Holland  * associated with the PCM stream.
5745854a464SSamuel Holland  */
5755854a464SSamuel Holland int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd,
5765854a464SSamuel Holland 			    struct snd_pcm_hardware *hw, int stream)
577bd477c31SLars-Peter Clausen {
5780b7990e3SKuninori Morimoto 	struct snd_soc_dai *codec_dai;
57919bdcc7aSShreyas NC 	struct snd_soc_dai *cpu_dai;
5802e5894d7SBenoit Cousson 	struct snd_soc_pcm_stream *codec_stream;
5812e5894d7SBenoit Cousson 	struct snd_soc_pcm_stream *cpu_stream;
5822e5894d7SBenoit Cousson 	unsigned int chan_min = 0, chan_max = UINT_MAX;
58319bdcc7aSShreyas NC 	unsigned int cpu_chan_min = 0, cpu_chan_max = UINT_MAX;
5842e5894d7SBenoit Cousson 	unsigned int rate_min = 0, rate_max = UINT_MAX;
58519bdcc7aSShreyas NC 	unsigned int cpu_rate_min = 0, cpu_rate_max = UINT_MAX;
58619bdcc7aSShreyas NC 	unsigned int rates = UINT_MAX, cpu_rates = UINT_MAX;
5872e5894d7SBenoit Cousson 	u64 formats = ULLONG_MAX;
5882e5894d7SBenoit Cousson 	int i;
58978e45c99SLars-Peter Clausen 
59019bdcc7aSShreyas NC 	/* first calculate min/max only for CPUs in the DAI link */
591a4be4187SKuninori Morimoto 	for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
5920e9cf4c4SBard Liao 
5930e9cf4c4SBard Liao 		/*
5940e9cf4c4SBard Liao 		 * Skip CPUs which don't support the current stream type.
5950e9cf4c4SBard Liao 		 * Otherwise, since the rate, channel, and format values will
5960e9cf4c4SBard Liao 		 * zero in that case, we would have no usable settings left,
5970e9cf4c4SBard Liao 		 * causing the resulting setup to fail.
5980e9cf4c4SBard Liao 		 */
5995854a464SSamuel Holland 		if (!snd_soc_dai_stream_valid(cpu_dai, stream))
6000e9cf4c4SBard Liao 			continue;
6010e9cf4c4SBard Liao 
60219bdcc7aSShreyas NC 		cpu_stream = snd_soc_dai_get_pcm_stream(cpu_dai, stream);
60378e45c99SLars-Peter Clausen 
60419bdcc7aSShreyas NC 		cpu_chan_min = max(cpu_chan_min, cpu_stream->channels_min);
60519bdcc7aSShreyas NC 		cpu_chan_max = min(cpu_chan_max, cpu_stream->channels_max);
60619bdcc7aSShreyas NC 		cpu_rate_min = max(cpu_rate_min, cpu_stream->rate_min);
60719bdcc7aSShreyas NC 		cpu_rate_max = min_not_zero(cpu_rate_max, cpu_stream->rate_max);
60819bdcc7aSShreyas NC 		formats &= cpu_stream->formats;
60919bdcc7aSShreyas NC 		cpu_rates = snd_pcm_rate_mask_intersect(cpu_stream->rates,
61019bdcc7aSShreyas NC 							cpu_rates);
61119bdcc7aSShreyas NC 	}
61219bdcc7aSShreyas NC 
61319bdcc7aSShreyas NC 	/* second calculate min/max only for CODECs in the DAI link */
614a4be4187SKuninori Morimoto 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
615cde79035SRicard Wanderlof 
616cde79035SRicard Wanderlof 		/*
617cde79035SRicard Wanderlof 		 * Skip CODECs which don't support the current stream type.
618cde79035SRicard Wanderlof 		 * Otherwise, since the rate, channel, and format values will
619cde79035SRicard Wanderlof 		 * zero in that case, we would have no usable settings left,
620cde79035SRicard Wanderlof 		 * causing the resulting setup to fail.
621cde79035SRicard Wanderlof 		 */
62225c2f515SKuninori Morimoto 		if (!snd_soc_dai_stream_valid(codec_dai, stream))
623cde79035SRicard Wanderlof 			continue;
624cde79035SRicard Wanderlof 
625acf253c1SKuninori Morimoto 		codec_stream = snd_soc_dai_get_pcm_stream(codec_dai, stream);
626acf253c1SKuninori Morimoto 
6272e5894d7SBenoit Cousson 		chan_min = max(chan_min, codec_stream->channels_min);
6282e5894d7SBenoit Cousson 		chan_max = min(chan_max, codec_stream->channels_max);
6292e5894d7SBenoit Cousson 		rate_min = max(rate_min, codec_stream->rate_min);
6302e5894d7SBenoit Cousson 		rate_max = min_not_zero(rate_max, codec_stream->rate_max);
6312e5894d7SBenoit Cousson 		formats &= codec_stream->formats;
6322e5894d7SBenoit Cousson 		rates = snd_pcm_rate_mask_intersect(codec_stream->rates, rates);
6332e5894d7SBenoit Cousson 	}
6342e5894d7SBenoit Cousson 
6355854a464SSamuel Holland 	/* Verify both a valid CPU DAI and a valid CODEC DAI were found */
6365854a464SSamuel Holland 	if (!chan_min || !cpu_chan_min)
6375854a464SSamuel Holland 		return -EINVAL;
6385854a464SSamuel Holland 
6392e5894d7SBenoit Cousson 	/*
6402e5894d7SBenoit Cousson 	 * chan min/max cannot be enforced if there are multiple CODEC DAIs
64119bdcc7aSShreyas NC 	 * connected to CPU DAI(s), use CPU DAI's directly and let
6422e5894d7SBenoit Cousson 	 * channel allocation be fixed up later
6432e5894d7SBenoit Cousson 	 */
6442e5894d7SBenoit Cousson 	if (rtd->num_codecs > 1) {
64519bdcc7aSShreyas NC 		chan_min = cpu_chan_min;
64619bdcc7aSShreyas NC 		chan_max = cpu_chan_max;
6472e5894d7SBenoit Cousson 	}
6482e5894d7SBenoit Cousson 
64919bdcc7aSShreyas NC 	/* finally find a intersection between CODECs and CPUs */
65019bdcc7aSShreyas NC 	hw->channels_min = max(chan_min, cpu_chan_min);
65119bdcc7aSShreyas NC 	hw->channels_max = min(chan_max, cpu_chan_max);
65219bdcc7aSShreyas NC 	hw->formats = formats;
65319bdcc7aSShreyas NC 	hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_rates);
65478e45c99SLars-Peter Clausen 
6555854a464SSamuel Holland 	snd_pcm_hw_limit_rates(hw);
65678e45c99SLars-Peter Clausen 
65719bdcc7aSShreyas NC 	hw->rate_min = max(hw->rate_min, cpu_rate_min);
6582e5894d7SBenoit Cousson 	hw->rate_min = max(hw->rate_min, rate_min);
65919bdcc7aSShreyas NC 	hw->rate_max = min_not_zero(hw->rate_max, cpu_rate_max);
6602e5894d7SBenoit Cousson 	hw->rate_max = min_not_zero(hw->rate_max, rate_max);
6615854a464SSamuel Holland 
6625854a464SSamuel Holland 	return 0;
6635854a464SSamuel Holland }
6645854a464SSamuel Holland EXPORT_SYMBOL_GPL(snd_soc_runtime_calc_hw);
6655854a464SSamuel Holland 
6665854a464SSamuel Holland static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
6675854a464SSamuel Holland {
6685854a464SSamuel Holland 	struct snd_pcm_hardware *hw = &substream->runtime->hw;
6695854a464SSamuel Holland 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
6705854a464SSamuel Holland 	u64 formats = hw->formats;
6715854a464SSamuel Holland 
6725854a464SSamuel Holland 	/*
6735854a464SSamuel Holland 	 * At least one CPU and one CODEC should match. Otherwise, we should
6745854a464SSamuel Holland 	 * have bailed out on a higher level, since there would be no CPU or
6755854a464SSamuel Holland 	 * CODEC to support the transfer direction in that case.
6765854a464SSamuel Holland 	 */
6775854a464SSamuel Holland 	snd_soc_runtime_calc_hw(rtd, hw, substream->stream);
6785854a464SSamuel Holland 
6795854a464SSamuel Holland 	if (formats)
6805854a464SSamuel Holland 		hw->formats &= formats;
681bd477c31SLars-Peter Clausen }
682bd477c31SLars-Peter Clausen 
683dd03907bSKuninori Morimoto static int soc_pcm_components_open(struct snd_pcm_substream *substream)
684e7ecfdb7SKuninori Morimoto {
685e7ecfdb7SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
686d2aaa8d8SKai Vehmanen 	struct snd_soc_component *last = NULL;
687e7ecfdb7SKuninori Morimoto 	struct snd_soc_component *component;
688613fb500SKuninori Morimoto 	int i, ret = 0;
689e7ecfdb7SKuninori Morimoto 
690613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
691d2aaa8d8SKai Vehmanen 		last = component;
692d2aaa8d8SKai Vehmanen 
6934a81e8f3SKuninori Morimoto 		ret = snd_soc_component_module_get_when_open(component);
6944a81e8f3SKuninori Morimoto 		if (ret < 0) {
695e7ecfdb7SKuninori Morimoto 			dev_err(component->dev,
696e7ecfdb7SKuninori Morimoto 				"ASoC: can't get module %s\n",
697e7ecfdb7SKuninori Morimoto 				component->name);
698d2aaa8d8SKai Vehmanen 			break;
699e7ecfdb7SKuninori Morimoto 		}
700e7ecfdb7SKuninori Morimoto 
701ae2f4849SKuninori Morimoto 		ret = snd_soc_component_open(component, substream);
702e7ecfdb7SKuninori Morimoto 		if (ret < 0) {
703d2aaa8d8SKai Vehmanen 			snd_soc_component_module_put_when_close(component);
704e7ecfdb7SKuninori Morimoto 			dev_err(component->dev,
705e7ecfdb7SKuninori Morimoto 				"ASoC: can't open component %s: %d\n",
706e7ecfdb7SKuninori Morimoto 				component->name, ret);
707d2aaa8d8SKai Vehmanen 			break;
708e7ecfdb7SKuninori Morimoto 		}
709e7ecfdb7SKuninori Morimoto 	}
710dd03907bSKuninori Morimoto 
711d2aaa8d8SKai Vehmanen 	if (ret < 0) {
712d2aaa8d8SKai Vehmanen 		/* rollback on error */
713d2aaa8d8SKai Vehmanen 		for_each_rtd_components(rtd, i, component) {
714d2aaa8d8SKai Vehmanen 			if (component == last)
715d2aaa8d8SKai Vehmanen 				break;
716d2aaa8d8SKai Vehmanen 
717d2aaa8d8SKai Vehmanen 			snd_soc_component_close(component, substream);
718d2aaa8d8SKai Vehmanen 			snd_soc_component_module_put_when_close(component);
719d2aaa8d8SKai Vehmanen 		}
720d2aaa8d8SKai Vehmanen 	}
721d2aaa8d8SKai Vehmanen 
722d2aaa8d8SKai Vehmanen 	return ret;
723e7ecfdb7SKuninori Morimoto }
724e7ecfdb7SKuninori Morimoto 
725dd03907bSKuninori Morimoto static int soc_pcm_components_close(struct snd_pcm_substream *substream)
726244e2936SCharles Keepax {
727244e2936SCharles Keepax 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
728244e2936SCharles Keepax 	struct snd_soc_component *component;
729e82ebffcSKuninori Morimoto 	int i, r, ret = 0;
730244e2936SCharles Keepax 
731613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
732e82ebffcSKuninori Morimoto 		r = snd_soc_component_close(component, substream);
733e82ebffcSKuninori Morimoto 		if (r < 0)
734e82ebffcSKuninori Morimoto 			ret = r; /* use last ret */
735e82ebffcSKuninori Morimoto 
7364a81e8f3SKuninori Morimoto 		snd_soc_component_module_put_when_close(component);
737244e2936SCharles Keepax 	}
738244e2936SCharles Keepax 
7393672beb8SKuninori Morimoto 	return ret;
740244e2936SCharles Keepax }
741244e2936SCharles Keepax 
74258ba9b25SMark Brown /*
74362c86d1dSKuninori Morimoto  * Called by ALSA when a PCM substream is closed. Private data can be
74462c86d1dSKuninori Morimoto  * freed here. The cpu DAI, codec DAI, machine and components are also
74562c86d1dSKuninori Morimoto  * shutdown.
74662c86d1dSKuninori Morimoto  */
74762c86d1dSKuninori Morimoto static int soc_pcm_close(struct snd_pcm_substream *substream)
74862c86d1dSKuninori Morimoto {
74962c86d1dSKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
75062c86d1dSKuninori Morimoto 	struct snd_soc_component *component;
751*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
75262c86d1dSKuninori Morimoto 	int i;
75362c86d1dSKuninori Morimoto 
75462c86d1dSKuninori Morimoto 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
75562c86d1dSKuninori Morimoto 
75662c86d1dSKuninori Morimoto 	snd_soc_runtime_deactivate(rtd, substream->stream);
75762c86d1dSKuninori Morimoto 
758*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai)
759*c840f769SKuninori Morimoto 		snd_soc_dai_shutdown(dai, substream);
76062c86d1dSKuninori Morimoto 
76162c86d1dSKuninori Morimoto 	soc_rtd_shutdown(rtd, substream);
76262c86d1dSKuninori Morimoto 
76362c86d1dSKuninori Morimoto 	soc_pcm_components_close(substream);
76462c86d1dSKuninori Morimoto 
76562c86d1dSKuninori Morimoto 	snd_soc_dapm_stream_stop(rtd, substream->stream);
76662c86d1dSKuninori Morimoto 
76762c86d1dSKuninori Morimoto 	mutex_unlock(&rtd->card->pcm_mutex);
76862c86d1dSKuninori Morimoto 
76962c86d1dSKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
77062c86d1dSKuninori Morimoto 		pm_runtime_mark_last_busy(component->dev);
77162c86d1dSKuninori Morimoto 		pm_runtime_put_autosuspend(component->dev);
77262c86d1dSKuninori Morimoto 	}
77362c86d1dSKuninori Morimoto 
77462c86d1dSKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
77562c86d1dSKuninori Morimoto 		if (!component->active)
77662c86d1dSKuninori Morimoto 			pinctrl_pm_select_sleep_state(component->dev);
77762c86d1dSKuninori Morimoto 
77862c86d1dSKuninori Morimoto 	return 0;
77962c86d1dSKuninori Morimoto }
78062c86d1dSKuninori Morimoto 
78162c86d1dSKuninori Morimoto /*
782ddee627cSLiam Girdwood  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
783ddee627cSLiam Girdwood  * then initialized and any private data can be allocated. This also calls
784ef050becSCharles Keepax  * startup for the cpu DAI, component, machine and codec DAI.
785ddee627cSLiam Girdwood  */
786ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream)
787ddee627cSLiam Girdwood {
788ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
789ddee627cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
79090be711eSKuninori Morimoto 	struct snd_soc_component *component;
791*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
7922e5894d7SBenoit Cousson 	const char *codec_dai_name = "multicodec";
79319bdcc7aSShreyas NC 	const char *cpu_dai_name = "multicpu";
794244e2936SCharles Keepax 	int i, ret = 0;
795ddee627cSLiam Girdwood 
79676c39e86SKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
79776c39e86SKuninori Morimoto 		pinctrl_pm_select_default_state(component->dev);
79890be711eSKuninori Morimoto 
799613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
80090be711eSKuninori Morimoto 		pm_runtime_get_sync(component->dev);
801d6652ef8SMark Brown 
80272b745e3SPeter Ujfalusi 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
803ddee627cSLiam Girdwood 
8045d9fa03eSKuninori Morimoto 	ret = soc_pcm_components_open(substream);
8055d9fa03eSKuninori Morimoto 	if (ret < 0)
8065d9fa03eSKuninori Morimoto 		goto component_err;
8075d9fa03eSKuninori Morimoto 
8085d9fa03eSKuninori Morimoto 	ret = soc_rtd_startup(rtd, substream);
8095d9fa03eSKuninori Morimoto 	if (ret < 0) {
8105d9fa03eSKuninori Morimoto 		pr_err("ASoC: %s startup failed: %d\n",
8115d9fa03eSKuninori Morimoto 		       rtd->dai_link->name, ret);
812d2aaa8d8SKai Vehmanen 		goto rtd_startup_err;
8135d9fa03eSKuninori Morimoto 	}
8145d9fa03eSKuninori Morimoto 
815ddee627cSLiam Girdwood 	/* startup the audio subsystem */
816*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai) {
817*c840f769SKuninori Morimoto 		ret = snd_soc_dai_startup(dai, substream);
818ddee627cSLiam Girdwood 		if (ret < 0) {
819*c840f769SKuninori Morimoto 			dev_err(dai->dev,
820*c840f769SKuninori Morimoto 				"ASoC: can't open DAI %s: %d\n",
821*c840f769SKuninori Morimoto 				dai->name, ret);
8225d9fa03eSKuninori Morimoto 			goto config_err;
823ddee627cSLiam Girdwood 		}
824ddee627cSLiam Girdwood 
8252e5894d7SBenoit Cousson 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
826*c840f769SKuninori Morimoto 			dai->tx_mask = 0;
8272e5894d7SBenoit Cousson 		else
828*c840f769SKuninori Morimoto 			dai->rx_mask = 0;
8292e5894d7SBenoit Cousson 	}
8302e5894d7SBenoit Cousson 
83101d7584cSLiam Girdwood 	/* Dynamic PCM DAI links compat checks use dynamic capabilities */
83201d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
83301d7584cSLiam Girdwood 		goto dynamic;
83401d7584cSLiam Girdwood 
835ddee627cSLiam Girdwood 	/* Check that the codec and cpu DAIs are compatible */
8362e5894d7SBenoit Cousson 	soc_pcm_init_runtime_hw(substream);
8372e5894d7SBenoit Cousson 
8382e5894d7SBenoit Cousson 	if (rtd->num_codecs == 1)
8392e5894d7SBenoit Cousson 		codec_dai_name = rtd->codec_dai->name;
840ddee627cSLiam Girdwood 
84119bdcc7aSShreyas NC 	if (rtd->num_cpus == 1)
84219bdcc7aSShreyas NC 		cpu_dai_name = rtd->cpu_dai->name;
84319bdcc7aSShreyas NC 
84462e5f676SLars-Peter Clausen 	if (soc_pcm_has_symmetry(substream))
84562e5f676SLars-Peter Clausen 		runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
84662e5f676SLars-Peter Clausen 
847ddee627cSLiam Girdwood 	ret = -EINVAL;
848ddee627cSLiam Girdwood 	if (!runtime->hw.rates) {
849103d84a3SLiam Girdwood 		printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
85019bdcc7aSShreyas NC 			codec_dai_name, cpu_dai_name);
851ddee627cSLiam Girdwood 		goto config_err;
852ddee627cSLiam Girdwood 	}
853ddee627cSLiam Girdwood 	if (!runtime->hw.formats) {
854103d84a3SLiam Girdwood 		printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n",
85519bdcc7aSShreyas NC 			codec_dai_name, cpu_dai_name);
856ddee627cSLiam Girdwood 		goto config_err;
857ddee627cSLiam Girdwood 	}
858ddee627cSLiam Girdwood 	if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
859ddee627cSLiam Girdwood 	    runtime->hw.channels_min > runtime->hw.channels_max) {
860103d84a3SLiam Girdwood 		printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n",
86119bdcc7aSShreyas NC 				codec_dai_name, cpu_dai_name);
862ddee627cSLiam Girdwood 		goto config_err;
863ddee627cSLiam Girdwood 	}
864ddee627cSLiam Girdwood 
865c8dd1fecSBenoit Cousson 	soc_pcm_apply_msb(substream);
86658ba9b25SMark Brown 
867ddee627cSLiam Girdwood 	/* Symmetry only applies if we've already got an active stream. */
868*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai) {
869*c840f769SKuninori Morimoto 		if (dai->active) {
870*c840f769SKuninori Morimoto 			ret = soc_pcm_apply_symmetry(substream, dai);
871ddee627cSLiam Girdwood 			if (ret != 0)
872ddee627cSLiam Girdwood 				goto config_err;
873ddee627cSLiam Girdwood 		}
8742e5894d7SBenoit Cousson 	}
875ddee627cSLiam Girdwood 
876103d84a3SLiam Girdwood 	pr_debug("ASoC: %s <-> %s info:\n",
87719bdcc7aSShreyas NC 		 codec_dai_name, cpu_dai_name);
878103d84a3SLiam Girdwood 	pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates);
879103d84a3SLiam Girdwood 	pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min,
880ddee627cSLiam Girdwood 		 runtime->hw.channels_max);
881103d84a3SLiam Girdwood 	pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min,
882ddee627cSLiam Girdwood 		 runtime->hw.rate_max);
883ddee627cSLiam Girdwood 
88401d7584cSLiam Girdwood dynamic:
88524894b76SLars-Peter Clausen 
88624894b76SLars-Peter Clausen 	snd_soc_runtime_activate(rtd, substream->stream);
88724894b76SLars-Peter Clausen 
88872b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
889ddee627cSLiam Girdwood 	return 0;
890ddee627cSLiam Girdwood 
891ddee627cSLiam Girdwood config_err:
892*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai)
893*c840f769SKuninori Morimoto 		snd_soc_dai_shutdown(dai, substream);
8942e5894d7SBenoit Cousson 
8955d9fa03eSKuninori Morimoto 	soc_rtd_shutdown(rtd, substream);
896d2aaa8d8SKai Vehmanen rtd_startup_err:
897dd03907bSKuninori Morimoto 	soc_pcm_components_close(substream);
898d2aaa8d8SKai Vehmanen component_err:
89972b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
900d6652ef8SMark Brown 
901613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
90290be711eSKuninori Morimoto 		pm_runtime_mark_last_busy(component->dev);
90390be711eSKuninori Morimoto 		pm_runtime_put_autosuspend(component->dev);
9043f809783SSanyog Kale 	}
9053f809783SSanyog Kale 
90676c39e86SKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
90776c39e86SKuninori Morimoto 		if (!component->active)
90876c39e86SKuninori Morimoto 			pinctrl_pm_select_sleep_state(component->dev);
909d6652ef8SMark Brown 
910ddee627cSLiam Girdwood 	return ret;
911ddee627cSLiam Girdwood }
912ddee627cSLiam Girdwood 
9134bf2e385SCurtis Malainey static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
914a342031cSJerome Brunet {
915a342031cSJerome Brunet 	/*
916a342031cSJerome Brunet 	 * Currently nothing to do for c2c links
917a342031cSJerome Brunet 	 * Since c2c links are internal nodes in the DAPM graph and
918a342031cSJerome Brunet 	 * don't interface with the outside world or application layer
919a342031cSJerome Brunet 	 * we don't have to do any special handling on close.
920a342031cSJerome Brunet 	 */
921a342031cSJerome Brunet }
922a342031cSJerome Brunet 
923ddee627cSLiam Girdwood /*
924ddee627cSLiam Girdwood  * Called by ALSA when the PCM substream is prepared, can set format, sample
925ddee627cSLiam Girdwood  * rate, etc.  This function is non atomic and can be called multiple times,
926ddee627cSLiam Girdwood  * it can refer to the runtime info.
927ddee627cSLiam Girdwood  */
928ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream)
929ddee627cSLiam Girdwood {
930ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
931b8135864SKuninori Morimoto 	struct snd_soc_component *component;
932*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
9332e5894d7SBenoit Cousson 	int i, ret = 0;
934ddee627cSLiam Girdwood 
93572b745e3SPeter Ujfalusi 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
936ddee627cSLiam Girdwood 
93744c1a75bSKuninori Morimoto 	ret = soc_rtd_prepare(rtd, substream);
938ddee627cSLiam Girdwood 	if (ret < 0) {
93944c1a75bSKuninori Morimoto 		dev_err(rtd->card->dev,
94044c1a75bSKuninori Morimoto 			"ASoC: machine prepare error: %d\n", ret);
941ddee627cSLiam Girdwood 		goto out;
942ddee627cSLiam Girdwood 	}
943ddee627cSLiam Girdwood 
944613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
9456d537233SKuninori Morimoto 		ret = snd_soc_component_prepare(component, substream);
946b8135864SKuninori Morimoto 		if (ret < 0) {
947b8135864SKuninori Morimoto 			dev_err(component->dev,
948b8135864SKuninori Morimoto 				"ASoC: platform prepare error: %d\n", ret);
949b8135864SKuninori Morimoto 			goto out;
950b8135864SKuninori Morimoto 		}
951b8135864SKuninori Morimoto 	}
952b8135864SKuninori Morimoto 
953*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai) {
954*c840f769SKuninori Morimoto 		ret = snd_soc_dai_prepare(dai, substream);
955ddee627cSLiam Girdwood 		if (ret < 0) {
956*c840f769SKuninori Morimoto 			dev_err(dai->dev,
957*c840f769SKuninori Morimoto 				"ASoC: DAI prepare error: %d\n", ret);
958ddee627cSLiam Girdwood 			goto out;
959ddee627cSLiam Girdwood 		}
96019bdcc7aSShreyas NC 	}
961ddee627cSLiam Girdwood 
962ddee627cSLiam Girdwood 	/* cancel any delayed stream shutdown that is pending */
963ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
9649bffb1fbSMisael Lopez Cruz 	    rtd->pop_wait) {
9659bffb1fbSMisael Lopez Cruz 		rtd->pop_wait = 0;
966ddee627cSLiam Girdwood 		cancel_delayed_work(&rtd->delayed_work);
967ddee627cSLiam Girdwood 	}
968ddee627cSLiam Girdwood 
969d9b0951bSLiam Girdwood 	snd_soc_dapm_stream_event(rtd, substream->stream,
970ddee627cSLiam Girdwood 			SND_SOC_DAPM_STREAM_START);
971ddee627cSLiam Girdwood 
972*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai)
973*c840f769SKuninori Morimoto 		snd_soc_dai_digital_mute(dai, 0, substream->stream);
974ddee627cSLiam Girdwood 
975ddee627cSLiam Girdwood out:
97672b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
977ddee627cSLiam Girdwood 	return ret;
978ddee627cSLiam Girdwood }
979ddee627cSLiam Girdwood 
9802e5894d7SBenoit Cousson static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
9812e5894d7SBenoit Cousson 				       unsigned int mask)
9822e5894d7SBenoit Cousson {
9832e5894d7SBenoit Cousson 	struct snd_interval *interval;
9842e5894d7SBenoit Cousson 	int channels = hweight_long(mask);
9852e5894d7SBenoit Cousson 
9862e5894d7SBenoit Cousson 	interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
9872e5894d7SBenoit Cousson 	interval->min = channels;
9882e5894d7SBenoit Cousson 	interval->max = channels;
9892e5894d7SBenoit Cousson }
9902e5894d7SBenoit Cousson 
991244e2936SCharles Keepax static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream,
992244e2936SCharles Keepax 				      struct snd_soc_component *last)
993244e2936SCharles Keepax {
994244e2936SCharles Keepax 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
995244e2936SCharles Keepax 	struct snd_soc_component *component;
996e82ebffcSKuninori Morimoto 	int i, r, ret = 0;
997244e2936SCharles Keepax 
998613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
999244e2936SCharles Keepax 		if (component == last)
1000244e2936SCharles Keepax 			break;
1001244e2936SCharles Keepax 
1002e82ebffcSKuninori Morimoto 		r = snd_soc_component_hw_free(component, substream);
1003e82ebffcSKuninori Morimoto 		if (r < 0)
1004e82ebffcSKuninori Morimoto 			ret = r; /* use last ret */
1005244e2936SCharles Keepax 	}
1006244e2936SCharles Keepax 
1007eae7136aSKuninori Morimoto 	return ret;
1008244e2936SCharles Keepax }
1009244e2936SCharles Keepax 
1010ddee627cSLiam Girdwood /*
1011ddee627cSLiam Girdwood  * Called by ALSA when the hardware params are set by application. This
1012ddee627cSLiam Girdwood  * function can also be called multiple times and can allocate buffers
1013ddee627cSLiam Girdwood  * (using snd_pcm_lib_* ). It's non-atomic.
1014ddee627cSLiam Girdwood  */
1015ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
1016ddee627cSLiam Girdwood 				struct snd_pcm_hw_params *params)
1017ddee627cSLiam Girdwood {
1018ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1019b8135864SKuninori Morimoto 	struct snd_soc_component *component;
102019bdcc7aSShreyas NC 	struct snd_soc_dai *cpu_dai;
10210b7990e3SKuninori Morimoto 	struct snd_soc_dai *codec_dai;
1022244e2936SCharles Keepax 	int i, ret = 0;
1023ddee627cSLiam Girdwood 
102472b745e3SPeter Ujfalusi 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
10255cca5951SShengjiu Wang 
10265cca5951SShengjiu Wang 	ret = soc_pcm_params_symmetry(substream, params);
10275cca5951SShengjiu Wang 	if (ret)
10285cca5951SShengjiu Wang 		goto out;
10295cca5951SShengjiu Wang 
1030de9ad990SKuninori Morimoto 	ret = soc_rtd_hw_params(rtd, substream, params);
1031ddee627cSLiam Girdwood 	if (ret < 0) {
1032de9ad990SKuninori Morimoto 		dev_err(rtd->card->dev,
1033de9ad990SKuninori Morimoto 			"ASoC: machine hw_params failed: %d\n", ret);
1034ddee627cSLiam Girdwood 		goto out;
1035ddee627cSLiam Girdwood 	}
1036ddee627cSLiam Girdwood 
1037a4be4187SKuninori Morimoto 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
10382e5894d7SBenoit Cousson 		struct snd_pcm_hw_params codec_params;
10392e5894d7SBenoit Cousson 
1040cde79035SRicard Wanderlof 		/*
1041cde79035SRicard Wanderlof 		 * Skip CODECs which don't support the current stream type,
1042cde79035SRicard Wanderlof 		 * the idea being that if a CODEC is not used for the currently
1043cde79035SRicard Wanderlof 		 * set up transfer direction, it should not need to be
1044cde79035SRicard Wanderlof 		 * configured, especially since the configuration used might
1045cde79035SRicard Wanderlof 		 * not even be supported by that CODEC. There may be cases
1046cde79035SRicard Wanderlof 		 * however where a CODEC needs to be set up although it is
1047cde79035SRicard Wanderlof 		 * actually not being used for the transfer, e.g. if a
1048cde79035SRicard Wanderlof 		 * capture-only CODEC is acting as an LRCLK and/or BCLK master
1049cde79035SRicard Wanderlof 		 * for the DAI link including a playback-only CODEC.
1050cde79035SRicard Wanderlof 		 * If this becomes necessary, we will have to augment the
1051cde79035SRicard Wanderlof 		 * machine driver setup with information on how to act, so
1052cde79035SRicard Wanderlof 		 * we can do the right thing here.
1053cde79035SRicard Wanderlof 		 */
1054cde79035SRicard Wanderlof 		if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
1055cde79035SRicard Wanderlof 			continue;
1056cde79035SRicard Wanderlof 
10572e5894d7SBenoit Cousson 		/* copy params for each codec */
10582e5894d7SBenoit Cousson 		codec_params = *params;
10592e5894d7SBenoit Cousson 
10602e5894d7SBenoit Cousson 		/* fixup params based on TDM slot masks */
1061570f18b6SRander Wang 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
1062570f18b6SRander Wang 		    codec_dai->tx_mask)
10632e5894d7SBenoit Cousson 			soc_pcm_codec_params_fixup(&codec_params,
10642e5894d7SBenoit Cousson 						   codec_dai->tx_mask);
1065570f18b6SRander Wang 
1066570f18b6SRander Wang 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
1067570f18b6SRander Wang 		    codec_dai->rx_mask)
10682e5894d7SBenoit Cousson 			soc_pcm_codec_params_fixup(&codec_params,
10692e5894d7SBenoit Cousson 						   codec_dai->rx_mask);
10702e5894d7SBenoit Cousson 
1071aa6166c2SKuninori Morimoto 		ret = snd_soc_dai_hw_params(codec_dai, substream,
1072aa6166c2SKuninori Morimoto 					    &codec_params);
107393e6958aSBenoit Cousson 		if(ret < 0)
1074ddee627cSLiam Girdwood 			goto codec_err;
1075ddee627cSLiam Girdwood 
10762e5894d7SBenoit Cousson 		codec_dai->rate = params_rate(&codec_params);
10772e5894d7SBenoit Cousson 		codec_dai->channels = params_channels(&codec_params);
10782e5894d7SBenoit Cousson 		codec_dai->sample_bits = snd_pcm_format_physical_width(
10792e5894d7SBenoit Cousson 						params_format(&codec_params));
1080078a85f2SCharles Keepax 
1081078a85f2SCharles Keepax 		snd_soc_dapm_update_dai(substream, &codec_params, codec_dai);
1082ddee627cSLiam Girdwood 	}
1083ddee627cSLiam Girdwood 
1084a4be4187SKuninori Morimoto 	for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
10850e9cf4c4SBard Liao 		/*
10860e9cf4c4SBard Liao 		 * Skip CPUs which don't support the current stream
10870e9cf4c4SBard Liao 		 * type. See soc_pcm_init_runtime_hw() for more details
10880e9cf4c4SBard Liao 		 */
10890e9cf4c4SBard Liao 		if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream))
10900e9cf4c4SBard Liao 			continue;
10910e9cf4c4SBard Liao 
1092aa6166c2SKuninori Morimoto 		ret = snd_soc_dai_hw_params(cpu_dai, substream, params);
109393e6958aSBenoit Cousson 		if (ret < 0)
1094ddee627cSLiam Girdwood 			goto interface_err;
1095ddee627cSLiam Girdwood 
109619bdcc7aSShreyas NC 		/* store the parameters for each DAI */
1097ca58221dSKuninori Morimoto 		cpu_dai->rate = params_rate(params);
1098ca58221dSKuninori Morimoto 		cpu_dai->channels = params_channels(params);
1099ca58221dSKuninori Morimoto 		cpu_dai->sample_bits =
1100ca58221dSKuninori Morimoto 			snd_pcm_format_physical_width(params_format(params));
1101ca58221dSKuninori Morimoto 
1102ca58221dSKuninori Morimoto 		snd_soc_dapm_update_dai(substream, params, cpu_dai);
110319bdcc7aSShreyas NC 	}
1104ca58221dSKuninori Morimoto 
1105613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
1106245c539aSKuninori Morimoto 		ret = snd_soc_component_hw_params(component, substream, params);
1107244e2936SCharles Keepax 		if (ret < 0) {
1108b8135864SKuninori Morimoto 			dev_err(component->dev,
1109b8135864SKuninori Morimoto 				"ASoC: %s hw params failed: %d\n",
1110244e2936SCharles Keepax 				component->name, ret);
1111b8135864SKuninori Morimoto 			goto component_err;
1112244e2936SCharles Keepax 		}
1113244e2936SCharles Keepax 	}
1114244e2936SCharles Keepax 	component = NULL;
1115b8135864SKuninori Morimoto 
1116ddee627cSLiam Girdwood out:
111772b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
1118ddee627cSLiam Girdwood 	return ret;
1119ddee627cSLiam Girdwood 
1120b8135864SKuninori Morimoto component_err:
1121244e2936SCharles Keepax 	soc_pcm_components_hw_free(substream, component);
1122b8135864SKuninori Morimoto 
112319bdcc7aSShreyas NC 	i = rtd->num_cpus;
1124ddee627cSLiam Girdwood 
1125ddee627cSLiam Girdwood interface_err:
1126a4be4187SKuninori Morimoto 	for_each_rtd_cpu_dais_rollback(rtd, i, cpu_dai) {
11270e9cf4c4SBard Liao 		if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream))
11280e9cf4c4SBard Liao 			continue;
11290e9cf4c4SBard Liao 
113019bdcc7aSShreyas NC 		snd_soc_dai_hw_free(cpu_dai, substream);
113119bdcc7aSShreyas NC 		cpu_dai->rate = 0;
113219bdcc7aSShreyas NC 	}
113319bdcc7aSShreyas NC 
11342e5894d7SBenoit Cousson 	i = rtd->num_codecs;
1135ddee627cSLiam Girdwood 
1136ddee627cSLiam Girdwood codec_err:
1137a4be4187SKuninori Morimoto 	for_each_rtd_codec_dais_rollback(rtd, i, codec_dai) {
1138f47b9ad9SJerome Brunet 		if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
1139f47b9ad9SJerome Brunet 			continue;
1140f47b9ad9SJerome Brunet 
1141846faaedSKuninori Morimoto 		snd_soc_dai_hw_free(codec_dai, substream);
11422e5894d7SBenoit Cousson 		codec_dai->rate = 0;
11432e5894d7SBenoit Cousson 	}
11442e5894d7SBenoit Cousson 
114549f020e5SKuninori Morimoto 	soc_rtd_hw_free(rtd, substream);
1146ddee627cSLiam Girdwood 
114772b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
1148ddee627cSLiam Girdwood 	return ret;
1149ddee627cSLiam Girdwood }
1150ddee627cSLiam Girdwood 
1151ddee627cSLiam Girdwood /*
1152ddee627cSLiam Girdwood  * Frees resources allocated by hw_params, can be called multiple times
1153ddee627cSLiam Girdwood  */
1154ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
1155ddee627cSLiam Girdwood {
1156ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1157*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
11582e5894d7SBenoit Cousson 	int i;
1159ddee627cSLiam Girdwood 
116072b745e3SPeter Ujfalusi 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
1161ddee627cSLiam Girdwood 
1162d3383420SNicolin Chen 	/* clear the corresponding DAIs parameters when going to be inactive */
1163*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai) {
1164*c840f769SKuninori Morimoto 		int active = dai->stream_active[substream->stream];
1165d3383420SNicolin Chen 
1166*c840f769SKuninori Morimoto 		if (dai->active == 1) {
1167*c840f769SKuninori Morimoto 			dai->rate = 0;
1168*c840f769SKuninori Morimoto 			dai->channels = 0;
1169*c840f769SKuninori Morimoto 			dai->sample_bits = 0;
1170d3383420SNicolin Chen 		}
11710f6011fdSKuninori Morimoto 
117267ad8777SKuninori Morimoto 		if (active == 1)
1173*c840f769SKuninori Morimoto 			snd_soc_dai_digital_mute(dai, 1, substream->stream);
1174a9ee331bSKuninori Morimoto 	}
1175a9ee331bSKuninori Morimoto 
1176ddee627cSLiam Girdwood 	/* free any machine hw params */
117749f020e5SKuninori Morimoto 	soc_rtd_hw_free(rtd, substream);
1178ddee627cSLiam Girdwood 
1179b8135864SKuninori Morimoto 	/* free any component resources */
1180244e2936SCharles Keepax 	soc_pcm_components_hw_free(substream, NULL);
1181b8135864SKuninori Morimoto 
1182ddee627cSLiam Girdwood 	/* now free hw params for the DAIs  */
1183*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai) {
1184*c840f769SKuninori Morimoto 		if (!snd_soc_dai_stream_valid(dai, substream->stream))
1185f47b9ad9SJerome Brunet 			continue;
1186f47b9ad9SJerome Brunet 
1187*c840f769SKuninori Morimoto 		snd_soc_dai_hw_free(dai, substream);
11880e9cf4c4SBard Liao 	}
1189ddee627cSLiam Girdwood 
119072b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
1191ddee627cSLiam Girdwood 	return 0;
1192ddee627cSLiam Girdwood }
1193ddee627cSLiam Girdwood 
11944378f1fbSPeter Ujfalusi static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd)
1195ddee627cSLiam Girdwood {
1196ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1197b8135864SKuninori Morimoto 	struct snd_soc_component *component;
1198*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
11992e5894d7SBenoit Cousson 	int i, ret;
1200ddee627cSLiam Girdwood 
1201ad2bf9f2SKuninori Morimoto 	ret = soc_rtd_trigger(rtd, substream, cmd);
1202ddee627cSLiam Girdwood 	if (ret < 0)
1203ddee627cSLiam Girdwood 		return ret;
1204ddee627cSLiam Girdwood 
1205613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
12065693d50cSKuninori Morimoto 		ret = snd_soc_component_trigger(component, substream, cmd);
1207b8135864SKuninori Morimoto 		if (ret < 0)
1208b8135864SKuninori Morimoto 			return ret;
1209b8135864SKuninori Morimoto 	}
1210b8135864SKuninori Morimoto 
1211*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai) {
1212*c840f769SKuninori Morimoto 		ret = snd_soc_dai_trigger(dai, substream, cmd);
12134378f1fbSPeter Ujfalusi 		if (ret < 0)
12144378f1fbSPeter Ujfalusi 			return ret;
12154378f1fbSPeter Ujfalusi 	}
12164378f1fbSPeter Ujfalusi 
12174378f1fbSPeter Ujfalusi 	return 0;
12184378f1fbSPeter Ujfalusi }
12194378f1fbSPeter Ujfalusi 
12204378f1fbSPeter Ujfalusi static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd)
12214378f1fbSPeter Ujfalusi {
12224378f1fbSPeter Ujfalusi 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
12234378f1fbSPeter Ujfalusi 	struct snd_soc_component *component;
1224*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
12254378f1fbSPeter Ujfalusi 	int i, ret;
12264378f1fbSPeter Ujfalusi 
1227*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai) {
1228*c840f769SKuninori Morimoto 		ret = snd_soc_dai_trigger(dai, substream, cmd);
12294378f1fbSPeter Ujfalusi 		if (ret < 0)
12304378f1fbSPeter Ujfalusi 			return ret;
123119bdcc7aSShreyas NC 	}
12324378f1fbSPeter Ujfalusi 
1233613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
12344378f1fbSPeter Ujfalusi 		ret = snd_soc_component_trigger(component, substream, cmd);
12354378f1fbSPeter Ujfalusi 		if (ret < 0)
12364378f1fbSPeter Ujfalusi 			return ret;
12374378f1fbSPeter Ujfalusi 	}
12384378f1fbSPeter Ujfalusi 
1239ad2bf9f2SKuninori Morimoto 	ret = soc_rtd_trigger(rtd, substream, cmd);
12404792b0dbSJarkko Nikula 	if (ret < 0)
12414792b0dbSJarkko Nikula 		return ret;
12424792b0dbSJarkko Nikula 
1243ddee627cSLiam Girdwood 	return 0;
1244ddee627cSLiam Girdwood }
1245ddee627cSLiam Girdwood 
12464378f1fbSPeter Ujfalusi static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
12474378f1fbSPeter Ujfalusi {
12484378f1fbSPeter Ujfalusi 	int ret;
12494378f1fbSPeter Ujfalusi 
12504378f1fbSPeter Ujfalusi 	switch (cmd) {
12514378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_START:
12524378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_RESUME:
12534378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
12544378f1fbSPeter Ujfalusi 		ret = soc_pcm_trigger_start(substream, cmd);
12554378f1fbSPeter Ujfalusi 		break;
12564378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_STOP:
12574378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_SUSPEND:
12584378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
12594378f1fbSPeter Ujfalusi 		ret = soc_pcm_trigger_stop(substream, cmd);
12604378f1fbSPeter Ujfalusi 		break;
12614378f1fbSPeter Ujfalusi 	default:
12624378f1fbSPeter Ujfalusi 		return -EINVAL;
12634378f1fbSPeter Ujfalusi 	}
12644378f1fbSPeter Ujfalusi 
12654378f1fbSPeter Ujfalusi 	return ret;
12664378f1fbSPeter Ujfalusi }
12674378f1fbSPeter Ujfalusi 
126845c0a188SMark Brown static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
126945c0a188SMark Brown 				   int cmd)
127007bf84aaSLiam Girdwood {
127107bf84aaSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1272*c840f769SKuninori Morimoto 	struct snd_soc_dai *dai;
12732e5894d7SBenoit Cousson 	int i, ret;
127407bf84aaSLiam Girdwood 
1275*c840f769SKuninori Morimoto 	for_each_rtd_dais(rtd, i, dai) {
1276*c840f769SKuninori Morimoto 		ret = snd_soc_dai_bespoke_trigger(dai, substream, cmd);
127707bf84aaSLiam Girdwood 		if (ret < 0)
127807bf84aaSLiam Girdwood 			return ret;
127919bdcc7aSShreyas NC 	}
12805c0769afSKuninori Morimoto 
128107bf84aaSLiam Girdwood 	return 0;
128207bf84aaSLiam Girdwood }
1283ddee627cSLiam Girdwood /*
1284ddee627cSLiam Girdwood  * soc level wrapper for pointer callback
1285ef050becSCharles Keepax  * If cpu_dai, codec_dai, component driver has the delay callback, then
1286ddee627cSLiam Girdwood  * the runtime->delay will be updated accordingly.
1287ddee627cSLiam Girdwood  */
1288ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
1289ddee627cSLiam Girdwood {
1290ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
129119bdcc7aSShreyas NC 	struct snd_soc_dai *cpu_dai;
12922e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
1293ddee627cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
1294ddee627cSLiam Girdwood 	snd_pcm_uframes_t offset = 0;
1295ddee627cSLiam Girdwood 	snd_pcm_sframes_t delay = 0;
12962e5894d7SBenoit Cousson 	snd_pcm_sframes_t codec_delay = 0;
129719bdcc7aSShreyas NC 	snd_pcm_sframes_t cpu_delay = 0;
12982e5894d7SBenoit Cousson 	int i;
1299ddee627cSLiam Girdwood 
13009fb4c2bfSAkshu Agrawal 	/* clearing the previous total delay */
13019fb4c2bfSAkshu Agrawal 	runtime->delay = 0;
13029fb4c2bfSAkshu Agrawal 
13030035e256SKuninori Morimoto 	offset = snd_soc_pcm_component_pointer(substream);
1304b8135864SKuninori Morimoto 
13059fb4c2bfSAkshu Agrawal 	/* base delay if assigned in pointer callback */
13069fb4c2bfSAkshu Agrawal 	delay = runtime->delay;
1307b8135864SKuninori Morimoto 
1308a4be4187SKuninori Morimoto 	for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
130919bdcc7aSShreyas NC 		cpu_delay = max(cpu_delay,
131019bdcc7aSShreyas NC 				snd_soc_dai_delay(cpu_dai, substream));
131119bdcc7aSShreyas NC 	}
131219bdcc7aSShreyas NC 	delay += cpu_delay;
1313ddee627cSLiam Girdwood 
1314a4be4187SKuninori Morimoto 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
13152e5894d7SBenoit Cousson 		codec_delay = max(codec_delay,
13161dea80d4SKuninori Morimoto 				  snd_soc_dai_delay(codec_dai, substream));
13172e5894d7SBenoit Cousson 	}
13182e5894d7SBenoit Cousson 	delay += codec_delay;
1319ddee627cSLiam Girdwood 
1320ddee627cSLiam Girdwood 	runtime->delay = delay;
1321ddee627cSLiam Girdwood 
1322ddee627cSLiam Girdwood 	return offset;
1323ddee627cSLiam Girdwood }
1324ddee627cSLiam Girdwood 
132501d7584cSLiam Girdwood /* connect a FE and BE */
132601d7584cSLiam Girdwood static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
132701d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
132801d7584cSLiam Girdwood {
132901d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1330a9764869SKaiChieh Chuang 	unsigned long flags;
133101d7584cSLiam Girdwood 
133201d7584cSLiam Girdwood 	/* only add new dpcms */
13338d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
133401d7584cSLiam Girdwood 		if (dpcm->be == be && dpcm->fe == fe)
133501d7584cSLiam Girdwood 			return 0;
133601d7584cSLiam Girdwood 	}
133701d7584cSLiam Girdwood 
133801d7584cSLiam Girdwood 	dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL);
133901d7584cSLiam Girdwood 	if (!dpcm)
134001d7584cSLiam Girdwood 		return -ENOMEM;
134101d7584cSLiam Girdwood 
134201d7584cSLiam Girdwood 	dpcm->be = be;
134301d7584cSLiam Girdwood 	dpcm->fe = fe;
134401d7584cSLiam Girdwood 	be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
134501d7584cSLiam Girdwood 	dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
1346a9764869SKaiChieh Chuang 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
134701d7584cSLiam Girdwood 	list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
134801d7584cSLiam Girdwood 	list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
1349a9764869SKaiChieh Chuang 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
135001d7584cSLiam Girdwood 
135101d7584cSLiam Girdwood 	dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n",
135201d7584cSLiam Girdwood 			stream ? "capture" : "playback",  fe->dai_link->name,
135301d7584cSLiam Girdwood 			stream ? "<-" : "->", be->dai_link->name);
135401d7584cSLiam Girdwood 
1355154dae87SKuninori Morimoto 	dpcm_create_debugfs_state(dpcm, stream);
1356154dae87SKuninori Morimoto 
135701d7584cSLiam Girdwood 	return 1;
135801d7584cSLiam Girdwood }
135901d7584cSLiam Girdwood 
136001d7584cSLiam Girdwood /* reparent a BE onto another FE */
136101d7584cSLiam Girdwood static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
136201d7584cSLiam Girdwood 			struct snd_soc_pcm_runtime *be, int stream)
136301d7584cSLiam Girdwood {
136401d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
136501d7584cSLiam Girdwood 	struct snd_pcm_substream *fe_substream, *be_substream;
136601d7584cSLiam Girdwood 
136701d7584cSLiam Girdwood 	/* reparent if BE is connected to other FEs */
136801d7584cSLiam Girdwood 	if (!be->dpcm[stream].users)
136901d7584cSLiam Girdwood 		return;
137001d7584cSLiam Girdwood 
137101d7584cSLiam Girdwood 	be_substream = snd_soc_dpcm_get_substream(be, stream);
137201d7584cSLiam Girdwood 
1373d2e24d64SKuninori Morimoto 	for_each_dpcm_fe(be, stream, dpcm) {
137401d7584cSLiam Girdwood 		if (dpcm->fe == fe)
137501d7584cSLiam Girdwood 			continue;
137601d7584cSLiam Girdwood 
137701d7584cSLiam Girdwood 		dev_dbg(fe->dev, "reparent %s path %s %s %s\n",
137801d7584cSLiam Girdwood 			stream ? "capture" : "playback",
137901d7584cSLiam Girdwood 			dpcm->fe->dai_link->name,
138001d7584cSLiam Girdwood 			stream ? "<-" : "->", dpcm->be->dai_link->name);
138101d7584cSLiam Girdwood 
138201d7584cSLiam Girdwood 		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream);
138301d7584cSLiam Girdwood 		be_substream->runtime = fe_substream->runtime;
138401d7584cSLiam Girdwood 		break;
138501d7584cSLiam Girdwood 	}
138601d7584cSLiam Girdwood }
138701d7584cSLiam Girdwood 
138801d7584cSLiam Girdwood /* disconnect a BE and FE */
138923607025SLiam Girdwood void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
139001d7584cSLiam Girdwood {
139101d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm, *d;
1392a9764869SKaiChieh Chuang 	unsigned long flags;
139301d7584cSLiam Girdwood 
13948d6258a4SKuninori Morimoto 	for_each_dpcm_be_safe(fe, stream, dpcm, d) {
1395103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n",
139601d7584cSLiam Girdwood 				stream ? "capture" : "playback",
139701d7584cSLiam Girdwood 				dpcm->be->dai_link->name);
139801d7584cSLiam Girdwood 
139901d7584cSLiam Girdwood 		if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE)
140001d7584cSLiam Girdwood 			continue;
140101d7584cSLiam Girdwood 
140201d7584cSLiam Girdwood 		dev_dbg(fe->dev, "freed DSP %s path %s %s %s\n",
140301d7584cSLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name,
140401d7584cSLiam Girdwood 			stream ? "<-" : "->", dpcm->be->dai_link->name);
140501d7584cSLiam Girdwood 
140601d7584cSLiam Girdwood 		/* BEs still alive need new FE */
140701d7584cSLiam Girdwood 		dpcm_be_reparent(fe, dpcm->be, stream);
140801d7584cSLiam Girdwood 
1409154dae87SKuninori Morimoto 		dpcm_remove_debugfs_state(dpcm);
1410154dae87SKuninori Morimoto 
1411a9764869SKaiChieh Chuang 		spin_lock_irqsave(&fe->card->dpcm_lock, flags);
141201d7584cSLiam Girdwood 		list_del(&dpcm->list_be);
141301d7584cSLiam Girdwood 		list_del(&dpcm->list_fe);
1414a9764869SKaiChieh Chuang 		spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
141501d7584cSLiam Girdwood 		kfree(dpcm);
141601d7584cSLiam Girdwood 	}
141701d7584cSLiam Girdwood }
141801d7584cSLiam Girdwood 
141901d7584cSLiam Girdwood /* get BE for DAI widget and stream */
142001d7584cSLiam Girdwood static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
142101d7584cSLiam Girdwood 		struct snd_soc_dapm_widget *widget, int stream)
142201d7584cSLiam Girdwood {
142301d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *be;
142493597faeSKuninori Morimoto 	struct snd_soc_dapm_widget *w;
14257afecb30SKuninori Morimoto 	struct snd_soc_dai *dai;
14261a497983SMengdong Lin 	int i;
142701d7584cSLiam Girdwood 
14283c146465SLiam Girdwood 	dev_dbg(card->dev, "ASoC: find BE for widget %s\n", widget->name);
14293c146465SLiam Girdwood 
1430bcb1fd1fSKuninori Morimoto 	for_each_card_rtds(card, be) {
143101d7584cSLiam Girdwood 
143235ea0655SLiam Girdwood 		if (!be->dai_link->no_pcm)
143335ea0655SLiam Girdwood 			continue;
143435ea0655SLiam Girdwood 
1435*c840f769SKuninori Morimoto 		for_each_rtd_dais(be, i, dai) {
143619bdcc7aSShreyas NC 			w = snd_soc_dai_get_widget(dai, stream);
143793597faeSKuninori Morimoto 
14383c146465SLiam Girdwood 			dev_dbg(card->dev, "ASoC: try BE : %s\n",
143993597faeSKuninori Morimoto 				w ? w->name : "(not set)");
14403c146465SLiam Girdwood 
144193597faeSKuninori Morimoto 			if (w == widget)
144201d7584cSLiam Girdwood 				return be;
144319bdcc7aSShreyas NC 		}
144401d7584cSLiam Girdwood 	}
144501d7584cSLiam Girdwood 
14469d6ee365SJerome Brunet 	/* Widget provided is not a BE */
144701d7584cSLiam Girdwood 	return NULL;
144801d7584cSLiam Girdwood }
144901d7584cSLiam Girdwood 
145001d7584cSLiam Girdwood static int widget_in_list(struct snd_soc_dapm_widget_list *list,
145101d7584cSLiam Girdwood 		struct snd_soc_dapm_widget *widget)
145201d7584cSLiam Girdwood {
145309e88f8aSKuninori Morimoto 	struct snd_soc_dapm_widget *w;
145401d7584cSLiam Girdwood 	int i;
145501d7584cSLiam Girdwood 
145609e88f8aSKuninori Morimoto 	for_each_dapm_widgets(list, i, w)
145709e88f8aSKuninori Morimoto 		if (widget == w)
145801d7584cSLiam Girdwood 			return 1;
145901d7584cSLiam Girdwood 
146001d7584cSLiam Girdwood 	return 0;
146101d7584cSLiam Girdwood }
146201d7584cSLiam Girdwood 
14635fdd022cSPiotr Stankiewicz static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
14645fdd022cSPiotr Stankiewicz 		enum snd_soc_dapm_direction dir)
14655fdd022cSPiotr Stankiewicz {
14665fdd022cSPiotr Stankiewicz 	struct snd_soc_card *card = widget->dapm->card;
14675fdd022cSPiotr Stankiewicz 	struct snd_soc_pcm_runtime *rtd;
1468c2cd8216SKuninori Morimoto 	int stream;
14695fdd022cSPiotr Stankiewicz 
1470c2cd8216SKuninori Morimoto 	/* adjust dir to stream */
1471c2cd8216SKuninori Morimoto 	if (dir == SND_SOC_DAPM_DIR_OUT)
1472c2cd8216SKuninori Morimoto 		stream = SNDRV_PCM_STREAM_PLAYBACK;
1473c2cd8216SKuninori Morimoto 	else
1474c2cd8216SKuninori Morimoto 		stream = SNDRV_PCM_STREAM_CAPTURE;
1475c2cd8216SKuninori Morimoto 
1476027a4838SKuninori Morimoto 	rtd = dpcm_get_be(card, widget, stream);
1477027a4838SKuninori Morimoto 	if (rtd)
14785fdd022cSPiotr Stankiewicz 		return true;
14795fdd022cSPiotr Stankiewicz 
14805fdd022cSPiotr Stankiewicz 	return false;
14815fdd022cSPiotr Stankiewicz }
14825fdd022cSPiotr Stankiewicz 
148323607025SLiam Girdwood int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
14841ce43acfSLars-Peter Clausen 	int stream, struct snd_soc_dapm_widget_list **list)
148501d7584cSLiam Girdwood {
148601d7584cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
148701d7584cSLiam Girdwood 	int paths;
148801d7584cSLiam Girdwood 
14896e1276a5SBard Liao 	if (fe->num_cpus > 1) {
14906e1276a5SBard Liao 		dev_err(fe->dev,
14916e1276a5SBard Liao 			"%s doesn't support Multi CPU yet\n", __func__);
14926e1276a5SBard Liao 		return -EINVAL;
14936e1276a5SBard Liao 	}
14946e1276a5SBard Liao 
149501d7584cSLiam Girdwood 	/* get number of valid DAI paths and their widgets */
14966742064aSPiotr Stankiewicz 	paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list,
14975fdd022cSPiotr Stankiewicz 			dpcm_end_walk_at_be);
149801d7584cSLiam Girdwood 
1499103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
150001d7584cSLiam Girdwood 			stream ? "capture" : "playback");
150101d7584cSLiam Girdwood 
150201d7584cSLiam Girdwood 	return paths;
150301d7584cSLiam Girdwood }
150401d7584cSLiam Girdwood 
150552645e33SKuninori Morimoto void dpcm_path_put(struct snd_soc_dapm_widget_list **list)
150652645e33SKuninori Morimoto {
150752645e33SKuninori Morimoto 	snd_soc_dapm_dai_free_widgets(list);
150852645e33SKuninori Morimoto }
150952645e33SKuninori Morimoto 
15108cce6569SGuennadi Liakhovetski static bool dpcm_be_is_active(struct snd_soc_dpcm *dpcm, int stream,
15118cce6569SGuennadi Liakhovetski 			      struct snd_soc_dapm_widget_list *list)
151201d7584cSLiam Girdwood {
151301d7584cSLiam Girdwood 	struct snd_soc_dapm_widget *widget;
15147afecb30SKuninori Morimoto 	struct snd_soc_dai *dai;
15152e5894d7SBenoit Cousson 	unsigned int i;
151601d7584cSLiam Girdwood 
1517*c840f769SKuninori Morimoto 	/* is there a valid DAI widget for this BE */
1518*c840f769SKuninori Morimoto 	for_each_rtd_dais(dpcm->be, i, dai) {
151919bdcc7aSShreyas NC 		widget = snd_soc_dai_get_widget(dai, stream);
152001d7584cSLiam Girdwood 
152119bdcc7aSShreyas NC 		/*
1522*c840f769SKuninori Morimoto 		 * The BE is pruned only if none of the dai
152319bdcc7aSShreyas NC 		 * widgets are in the active list.
152419bdcc7aSShreyas NC 		 */
152501d7584cSLiam Girdwood 		if (widget && widget_in_list(list, widget))
15268cce6569SGuennadi Liakhovetski 			return true;
152719bdcc7aSShreyas NC 	}
152801d7584cSLiam Girdwood 
15298cce6569SGuennadi Liakhovetski 	return false;
15308cce6569SGuennadi Liakhovetski }
15318cce6569SGuennadi Liakhovetski 
15328cce6569SGuennadi Liakhovetski static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
15338cce6569SGuennadi Liakhovetski 			    struct snd_soc_dapm_widget_list **list_)
15348cce6569SGuennadi Liakhovetski {
15358cce6569SGuennadi Liakhovetski 	struct snd_soc_dpcm *dpcm;
15368cce6569SGuennadi Liakhovetski 	int prune = 0;
15378cce6569SGuennadi Liakhovetski 
15388cce6569SGuennadi Liakhovetski 	/* Destroy any old FE <--> BE connections */
15398cce6569SGuennadi Liakhovetski 	for_each_dpcm_be(fe, stream, dpcm) {
15408cce6569SGuennadi Liakhovetski 		if (dpcm_be_is_active(dpcm, stream, *list_))
1541bed646dcSKuninori Morimoto 			continue;
154201d7584cSLiam Girdwood 
1543103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
154401d7584cSLiam Girdwood 			stream ? "capture" : "playback",
154501d7584cSLiam Girdwood 			dpcm->be->dai_link->name, fe->dai_link->name);
154601d7584cSLiam Girdwood 		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
154701d7584cSLiam Girdwood 		dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
154801d7584cSLiam Girdwood 		prune++;
154901d7584cSLiam Girdwood 	}
155001d7584cSLiam Girdwood 
1551103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: found %d old BE paths for pruning\n", prune);
155201d7584cSLiam Girdwood 	return prune;
155301d7584cSLiam Girdwood }
155401d7584cSLiam Girdwood 
155501d7584cSLiam Girdwood static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
155601d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list **list_)
155701d7584cSLiam Girdwood {
155801d7584cSLiam Girdwood 	struct snd_soc_card *card = fe->card;
155901d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list = *list_;
156001d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *be;
156109e88f8aSKuninori Morimoto 	struct snd_soc_dapm_widget *widget;
156201d7584cSLiam Girdwood 	int i, new = 0, err;
156301d7584cSLiam Girdwood 
156401d7584cSLiam Girdwood 	/* Create any new FE <--> BE connections */
156509e88f8aSKuninori Morimoto 	for_each_dapm_widgets(list, i, widget) {
156601d7584cSLiam Girdwood 
156709e88f8aSKuninori Morimoto 		switch (widget->id) {
15684616274dSMark Brown 		case snd_soc_dapm_dai_in:
1569c5b8540dSKoro Chen 			if (stream != SNDRV_PCM_STREAM_PLAYBACK)
1570c5b8540dSKoro Chen 				continue;
1571c5b8540dSKoro Chen 			break;
15724616274dSMark Brown 		case snd_soc_dapm_dai_out:
1573c5b8540dSKoro Chen 			if (stream != SNDRV_PCM_STREAM_CAPTURE)
1574c5b8540dSKoro Chen 				continue;
15754616274dSMark Brown 			break;
15764616274dSMark Brown 		default:
157701d7584cSLiam Girdwood 			continue;
15784616274dSMark Brown 		}
157901d7584cSLiam Girdwood 
158001d7584cSLiam Girdwood 		/* is there a valid BE rtd for this widget */
158109e88f8aSKuninori Morimoto 		be = dpcm_get_be(card, widget, stream);
158201d7584cSLiam Girdwood 		if (!be) {
1583103d84a3SLiam Girdwood 			dev_err(fe->dev, "ASoC: no BE found for %s\n",
158409e88f8aSKuninori Morimoto 					widget->name);
158501d7584cSLiam Girdwood 			continue;
158601d7584cSLiam Girdwood 		}
158701d7584cSLiam Girdwood 
158801d7584cSLiam Girdwood 		/* don't connect if FE is not running */
158923607025SLiam Girdwood 		if (!fe->dpcm[stream].runtime && !fe->fe_compr)
159001d7584cSLiam Girdwood 			continue;
159101d7584cSLiam Girdwood 
159201d7584cSLiam Girdwood 		/* newly connected FE and BE */
159301d7584cSLiam Girdwood 		err = dpcm_be_connect(fe, be, stream);
159401d7584cSLiam Girdwood 		if (err < 0) {
1595103d84a3SLiam Girdwood 			dev_err(fe->dev, "ASoC: can't connect %s\n",
159609e88f8aSKuninori Morimoto 				widget->name);
159701d7584cSLiam Girdwood 			break;
159801d7584cSLiam Girdwood 		} else if (err == 0) /* already connected */
159901d7584cSLiam Girdwood 			continue;
160001d7584cSLiam Girdwood 
160101d7584cSLiam Girdwood 		/* new */
160201d7584cSLiam Girdwood 		be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
160301d7584cSLiam Girdwood 		new++;
160401d7584cSLiam Girdwood 	}
160501d7584cSLiam Girdwood 
1606103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: found %d new BE paths\n", new);
160701d7584cSLiam Girdwood 	return new;
160801d7584cSLiam Girdwood }
160901d7584cSLiam Girdwood 
161001d7584cSLiam Girdwood /*
161101d7584cSLiam Girdwood  * Find the corresponding BE DAIs that source or sink audio to this
161201d7584cSLiam Girdwood  * FE substream.
161301d7584cSLiam Girdwood  */
161423607025SLiam Girdwood int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
161501d7584cSLiam Girdwood 	int stream, struct snd_soc_dapm_widget_list **list, int new)
161601d7584cSLiam Girdwood {
161701d7584cSLiam Girdwood 	if (new)
161801d7584cSLiam Girdwood 		return dpcm_add_paths(fe, stream, list);
161901d7584cSLiam Girdwood 	else
162001d7584cSLiam Girdwood 		return dpcm_prune_paths(fe, stream, list);
162101d7584cSLiam Girdwood }
162201d7584cSLiam Girdwood 
162323607025SLiam Girdwood void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
162401d7584cSLiam Girdwood {
162501d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1626a9764869SKaiChieh Chuang 	unsigned long flags;
162701d7584cSLiam Girdwood 
1628a9764869SKaiChieh Chuang 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
16298d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm)
163001d7584cSLiam Girdwood 		dpcm->be->dpcm[stream].runtime_update =
163101d7584cSLiam Girdwood 						SND_SOC_DPCM_UPDATE_NO;
1632a9764869SKaiChieh Chuang 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
163301d7584cSLiam Girdwood }
163401d7584cSLiam Girdwood 
163501d7584cSLiam Girdwood static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
163601d7584cSLiam Girdwood 	int stream)
163701d7584cSLiam Girdwood {
163801d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
163901d7584cSLiam Girdwood 
164001d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
16418d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
164201d7584cSLiam Girdwood 
164301d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
164401d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
164501d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
164601d7584cSLiam Girdwood 
164701d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1648103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: no users %s at close - state %d\n",
164901d7584cSLiam Girdwood 				stream ? "capture" : "playback",
165001d7584cSLiam Girdwood 				be->dpcm[stream].state);
165101d7584cSLiam Girdwood 
165201d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
165301d7584cSLiam Girdwood 			continue;
165401d7584cSLiam Girdwood 
165501d7584cSLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
165601d7584cSLiam Girdwood 			continue;
165701d7584cSLiam Girdwood 
165801d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
165901d7584cSLiam Girdwood 		be_substream->runtime = NULL;
166001d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
166101d7584cSLiam Girdwood 	}
166201d7584cSLiam Girdwood }
166301d7584cSLiam Girdwood 
166423607025SLiam Girdwood int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
166501d7584cSLiam Girdwood {
166601d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
166701d7584cSLiam Girdwood 	int err, count = 0;
166801d7584cSLiam Girdwood 
166901d7584cSLiam Girdwood 	/* only startup BE DAIs that are either sinks or sources to this FE DAI */
16708d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
167101d7584cSLiam Girdwood 
167201d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
167301d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
167401d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
167501d7584cSLiam Girdwood 
16762062b4c5SRussell King - ARM Linux 		if (!be_substream) {
16772062b4c5SRussell King - ARM Linux 			dev_err(be->dev, "ASoC: no backend %s stream\n",
16782062b4c5SRussell King - ARM Linux 				stream ? "capture" : "playback");
16792062b4c5SRussell King - ARM Linux 			continue;
16802062b4c5SRussell King - ARM Linux 		}
16812062b4c5SRussell King - ARM Linux 
168201d7584cSLiam Girdwood 		/* is this op for this BE ? */
168301d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
168401d7584cSLiam Girdwood 			continue;
168501d7584cSLiam Girdwood 
168601d7584cSLiam Girdwood 		/* first time the dpcm is open ? */
168701d7584cSLiam Girdwood 		if (be->dpcm[stream].users == DPCM_MAX_BE_USERS)
1688103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: too many users %s at open %d\n",
168901d7584cSLiam Girdwood 				stream ? "capture" : "playback",
169001d7584cSLiam Girdwood 				be->dpcm[stream].state);
169101d7584cSLiam Girdwood 
169201d7584cSLiam Girdwood 		if (be->dpcm[stream].users++ != 0)
169301d7584cSLiam Girdwood 			continue;
169401d7584cSLiam Girdwood 
169501d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
169601d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
169701d7584cSLiam Girdwood 			continue;
169801d7584cSLiam Girdwood 
16992062b4c5SRussell King - ARM Linux 		dev_dbg(be->dev, "ASoC: open %s BE %s\n",
17002062b4c5SRussell King - ARM Linux 			stream ? "capture" : "playback", be->dai_link->name);
170101d7584cSLiam Girdwood 
170201d7584cSLiam Girdwood 		be_substream->runtime = be->dpcm[stream].runtime;
170301d7584cSLiam Girdwood 		err = soc_pcm_open(be_substream);
170401d7584cSLiam Girdwood 		if (err < 0) {
1705103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: BE open failed %d\n", err);
170601d7584cSLiam Girdwood 			be->dpcm[stream].users--;
170701d7584cSLiam Girdwood 			if (be->dpcm[stream].users < 0)
1708103d84a3SLiam Girdwood 				dev_err(be->dev, "ASoC: no users %s at unwind %d\n",
170901d7584cSLiam Girdwood 					stream ? "capture" : "playback",
171001d7584cSLiam Girdwood 					be->dpcm[stream].state);
171101d7584cSLiam Girdwood 
171201d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
171301d7584cSLiam Girdwood 			goto unwind;
171401d7584cSLiam Girdwood 		}
171501d7584cSLiam Girdwood 
171601d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
171701d7584cSLiam Girdwood 		count++;
171801d7584cSLiam Girdwood 	}
171901d7584cSLiam Girdwood 
172001d7584cSLiam Girdwood 	return count;
172101d7584cSLiam Girdwood 
172201d7584cSLiam Girdwood unwind:
172301d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
17248d6258a4SKuninori Morimoto 	for_each_dpcm_be_rollback(fe, stream, dpcm) {
172501d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
172601d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
172701d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
172801d7584cSLiam Girdwood 
172901d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
173001d7584cSLiam Girdwood 			continue;
173101d7584cSLiam Girdwood 
173201d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1733103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: no users %s at close %d\n",
173401d7584cSLiam Girdwood 				stream ? "capture" : "playback",
173501d7584cSLiam Girdwood 				be->dpcm[stream].state);
173601d7584cSLiam Girdwood 
173701d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
173801d7584cSLiam Girdwood 			continue;
173901d7584cSLiam Girdwood 
174001d7584cSLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
174101d7584cSLiam Girdwood 			continue;
174201d7584cSLiam Girdwood 
174301d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
174401d7584cSLiam Girdwood 		be_substream->runtime = NULL;
174501d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
174601d7584cSLiam Girdwood 	}
174701d7584cSLiam Girdwood 
174801d7584cSLiam Girdwood 	return err;
174901d7584cSLiam Girdwood }
175001d7584cSLiam Girdwood 
175108ae9b45SLars-Peter Clausen static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
1752435ffb76SJerome Brunet 				 struct snd_soc_pcm_stream *stream)
175308ae9b45SLars-Peter Clausen {
175408ae9b45SLars-Peter Clausen 	runtime->hw.rate_min = stream->rate_min;
1755e33ffbd9SCharles Keepax 	runtime->hw.rate_max = min_not_zero(stream->rate_max, UINT_MAX);
175608ae9b45SLars-Peter Clausen 	runtime->hw.channels_min = stream->channels_min;
175708ae9b45SLars-Peter Clausen 	runtime->hw.channels_max = stream->channels_max;
1758002220a9SLars-Peter Clausen 	if (runtime->hw.formats)
1759435ffb76SJerome Brunet 		runtime->hw.formats &= stream->formats;
1760002220a9SLars-Peter Clausen 	else
1761435ffb76SJerome Brunet 		runtime->hw.formats = stream->formats;
176208ae9b45SLars-Peter Clausen 	runtime->hw.rates = stream->rates;
176308ae9b45SLars-Peter Clausen }
176408ae9b45SLars-Peter Clausen 
1765435ffb76SJerome Brunet static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream,
1766435ffb76SJerome Brunet 				      u64 *formats)
1767b073ed4eSKuninori Morimoto {
1768b073ed4eSKuninori Morimoto 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1769b073ed4eSKuninori Morimoto 	struct snd_soc_dpcm *dpcm;
17707afecb30SKuninori Morimoto 	struct snd_soc_dai *dai;
1771b073ed4eSKuninori Morimoto 	int stream = substream->stream;
1772b073ed4eSKuninori Morimoto 
1773b073ed4eSKuninori Morimoto 	if (!fe->dai_link->dpcm_merged_format)
1774435ffb76SJerome Brunet 		return;
1775b073ed4eSKuninori Morimoto 
1776b073ed4eSKuninori Morimoto 	/*
1777b073ed4eSKuninori Morimoto 	 * It returns merged BE codec format
1778b073ed4eSKuninori Morimoto 	 * if FE want to use it (= dpcm_merged_format)
1779b073ed4eSKuninori Morimoto 	 */
1780b073ed4eSKuninori Morimoto 
17818d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
1782b073ed4eSKuninori Morimoto 		struct snd_soc_pcm_runtime *be = dpcm->be;
1783b073ed4eSKuninori Morimoto 		struct snd_soc_pcm_stream *codec_stream;
1784b073ed4eSKuninori Morimoto 		int i;
1785b073ed4eSKuninori Morimoto 
1786a4be4187SKuninori Morimoto 		for_each_rtd_codec_dais(be, i, dai) {
17874febced1SJerome Brunet 			/*
17884febced1SJerome Brunet 			 * Skip CODECs which don't support the current stream
17894febced1SJerome Brunet 			 * type. See soc_pcm_init_runtime_hw() for more details
17904febced1SJerome Brunet 			 */
17917afecb30SKuninori Morimoto 			if (!snd_soc_dai_stream_valid(dai, stream))
17924febced1SJerome Brunet 				continue;
17934febced1SJerome Brunet 
1794acf253c1SKuninori Morimoto 			codec_stream = snd_soc_dai_get_pcm_stream(dai, stream);
1795b073ed4eSKuninori Morimoto 
1796435ffb76SJerome Brunet 			*formats &= codec_stream->formats;
1797435ffb76SJerome Brunet 		}
1798b073ed4eSKuninori Morimoto 	}
1799b073ed4eSKuninori Morimoto }
1800b073ed4eSKuninori Morimoto 
1801435ffb76SJerome Brunet static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream,
1802f4c277b8SJiada Wang 				    unsigned int *channels_min,
1803f4c277b8SJiada Wang 				    unsigned int *channels_max)
1804f4c277b8SJiada Wang {
1805f4c277b8SJiada Wang 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1806f4c277b8SJiada Wang 	struct snd_soc_dpcm *dpcm;
1807f4c277b8SJiada Wang 	int stream = substream->stream;
1808f4c277b8SJiada Wang 
1809f4c277b8SJiada Wang 	if (!fe->dai_link->dpcm_merged_chan)
1810f4c277b8SJiada Wang 		return;
1811f4c277b8SJiada Wang 
1812f4c277b8SJiada Wang 	/*
1813f4c277b8SJiada Wang 	 * It returns merged BE codec channel;
1814f4c277b8SJiada Wang 	 * if FE want to use it (= dpcm_merged_chan)
1815f4c277b8SJiada Wang 	 */
1816f4c277b8SJiada Wang 
18178d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
1818f4c277b8SJiada Wang 		struct snd_soc_pcm_runtime *be = dpcm->be;
1819f4c277b8SJiada Wang 		struct snd_soc_pcm_stream *codec_stream;
18204f2bd18bSJerome Brunet 		struct snd_soc_pcm_stream *cpu_stream;
182119bdcc7aSShreyas NC 		struct snd_soc_dai *dai;
182219bdcc7aSShreyas NC 		int i;
1823f4c277b8SJiada Wang 
1824a4be4187SKuninori Morimoto 		for_each_rtd_cpu_dais(be, i, dai) {
18250e9cf4c4SBard Liao 			/*
18260e9cf4c4SBard Liao 			 * Skip CPUs which don't support the current stream
18270e9cf4c4SBard Liao 			 * type. See soc_pcm_init_runtime_hw() for more details
18280e9cf4c4SBard Liao 			 */
18290e9cf4c4SBard Liao 			if (!snd_soc_dai_stream_valid(dai, stream))
18300e9cf4c4SBard Liao 				continue;
18310e9cf4c4SBard Liao 
183219bdcc7aSShreyas NC 			cpu_stream = snd_soc_dai_get_pcm_stream(dai, stream);
18334f2bd18bSJerome Brunet 
183419bdcc7aSShreyas NC 			*channels_min = max(*channels_min,
183519bdcc7aSShreyas NC 					    cpu_stream->channels_min);
183619bdcc7aSShreyas NC 			*channels_max = min(*channels_max,
183719bdcc7aSShreyas NC 					    cpu_stream->channels_max);
183819bdcc7aSShreyas NC 		}
18394f2bd18bSJerome Brunet 
18404f2bd18bSJerome Brunet 		/*
18414f2bd18bSJerome Brunet 		 * chan min/max cannot be enforced if there are multiple CODEC
18424f2bd18bSJerome Brunet 		 * DAIs connected to a single CPU DAI, use CPU DAI's directly
18434f2bd18bSJerome Brunet 		 */
18444f2bd18bSJerome Brunet 		if (be->num_codecs == 1) {
1845acf253c1SKuninori Morimoto 			codec_stream = snd_soc_dai_get_pcm_stream(be->codec_dais[0], stream);
1846f4c277b8SJiada Wang 
1847f4c277b8SJiada Wang 			*channels_min = max(*channels_min,
1848f4c277b8SJiada Wang 					    codec_stream->channels_min);
1849f4c277b8SJiada Wang 			*channels_max = min(*channels_max,
1850f4c277b8SJiada Wang 					    codec_stream->channels_max);
1851f4c277b8SJiada Wang 		}
1852f4c277b8SJiada Wang 	}
1853f4c277b8SJiada Wang }
1854f4c277b8SJiada Wang 
1855baacd8d1SJerome Brunet static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream,
1856baacd8d1SJerome Brunet 				    unsigned int *rates,
1857baacd8d1SJerome Brunet 				    unsigned int *rate_min,
1858baacd8d1SJerome Brunet 				    unsigned int *rate_max)
1859baacd8d1SJerome Brunet {
1860baacd8d1SJerome Brunet 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1861baacd8d1SJerome Brunet 	struct snd_soc_dpcm *dpcm;
1862baacd8d1SJerome Brunet 	int stream = substream->stream;
1863baacd8d1SJerome Brunet 
1864baacd8d1SJerome Brunet 	if (!fe->dai_link->dpcm_merged_rate)
1865baacd8d1SJerome Brunet 		return;
1866baacd8d1SJerome Brunet 
1867baacd8d1SJerome Brunet 	/*
1868baacd8d1SJerome Brunet 	 * It returns merged BE codec channel;
1869baacd8d1SJerome Brunet 	 * if FE want to use it (= dpcm_merged_chan)
1870baacd8d1SJerome Brunet 	 */
1871baacd8d1SJerome Brunet 
18728d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
1873baacd8d1SJerome Brunet 		struct snd_soc_pcm_runtime *be = dpcm->be;
1874*c840f769SKuninori Morimoto 		struct snd_soc_pcm_stream *pcm;
18757afecb30SKuninori Morimoto 		struct snd_soc_dai *dai;
1876baacd8d1SJerome Brunet 		int i;
1877baacd8d1SJerome Brunet 
1878*c840f769SKuninori Morimoto 		for_each_rtd_dais(be, i, dai) {
18790e9cf4c4SBard Liao 			/*
1880*c840f769SKuninori Morimoto 			 * Skip DAIs which don't support the current stream
18810e9cf4c4SBard Liao 			 * type. See soc_pcm_init_runtime_hw() for more details
18820e9cf4c4SBard Liao 			 */
18830e9cf4c4SBard Liao 			if (!snd_soc_dai_stream_valid(dai, stream))
18840e9cf4c4SBard Liao 				continue;
18850e9cf4c4SBard Liao 
1886*c840f769SKuninori Morimoto 			pcm = snd_soc_dai_get_pcm_stream(dai, stream);
1887baacd8d1SJerome Brunet 
1888*c840f769SKuninori Morimoto 			*rate_min = max(*rate_min, pcm->rate_min);
1889*c840f769SKuninori Morimoto 			*rate_max = min_not_zero(*rate_max, pcm->rate_max);
1890*c840f769SKuninori Morimoto 			*rates = snd_pcm_rate_mask_intersect(*rates, pcm->rates);
1891baacd8d1SJerome Brunet 		}
1892baacd8d1SJerome Brunet 	}
1893baacd8d1SJerome Brunet }
1894baacd8d1SJerome Brunet 
189545c0a188SMark Brown static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
189601d7584cSLiam Girdwood {
189701d7584cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
189801d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
189919bdcc7aSShreyas NC 	struct snd_soc_dai *cpu_dai;
190019bdcc7aSShreyas NC 	int i;
190101d7584cSLiam Girdwood 
1902a4be4187SKuninori Morimoto 	for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
19030e9cf4c4SBard Liao 		/*
19040e9cf4c4SBard Liao 		 * Skip CPUs which don't support the current stream
19050e9cf4c4SBard Liao 		 * type. See soc_pcm_init_runtime_hw() for more details
19060e9cf4c4SBard Liao 		 */
19070e9cf4c4SBard Liao 		if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream))
19080e9cf4c4SBard Liao 			continue;
19090e9cf4c4SBard Liao 
19100c9ba720SKuninori Morimoto 		dpcm_init_runtime_hw(runtime,
19110c9ba720SKuninori Morimoto 			snd_soc_dai_get_pcm_stream(cpu_dai,
19120c9ba720SKuninori Morimoto 						   substream->stream));
191319bdcc7aSShreyas NC 	}
1914f4c277b8SJiada Wang 
1915435ffb76SJerome Brunet 	dpcm_runtime_merge_format(substream, &runtime->hw.formats);
1916435ffb76SJerome Brunet 	dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min,
1917435ffb76SJerome Brunet 				&runtime->hw.channels_max);
1918baacd8d1SJerome Brunet 	dpcm_runtime_merge_rate(substream, &runtime->hw.rates,
1919baacd8d1SJerome Brunet 				&runtime->hw.rate_min, &runtime->hw.rate_max);
192001d7584cSLiam Girdwood }
192101d7584cSLiam Girdwood 
1922ea9d0d77STakashi Iwai static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
1923ea9d0d77STakashi Iwai 
1924ea9d0d77STakashi Iwai /* Set FE's runtime_update state; the state is protected via PCM stream lock
1925ea9d0d77STakashi Iwai  * for avoiding the race with trigger callback.
1926ea9d0d77STakashi Iwai  * If the state is unset and a trigger is pending while the previous operation,
1927ea9d0d77STakashi Iwai  * process the pending trigger action here.
1928ea9d0d77STakashi Iwai  */
1929ea9d0d77STakashi Iwai static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
1930ea9d0d77STakashi Iwai 				     int stream, enum snd_soc_dpcm_update state)
1931ea9d0d77STakashi Iwai {
1932ea9d0d77STakashi Iwai 	struct snd_pcm_substream *substream =
1933ea9d0d77STakashi Iwai 		snd_soc_dpcm_get_substream(fe, stream);
1934ea9d0d77STakashi Iwai 
1935ea9d0d77STakashi Iwai 	snd_pcm_stream_lock_irq(substream);
1936ea9d0d77STakashi Iwai 	if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
1937ea9d0d77STakashi Iwai 		dpcm_fe_dai_do_trigger(substream,
1938ea9d0d77STakashi Iwai 				       fe->dpcm[stream].trigger_pending - 1);
1939ea9d0d77STakashi Iwai 		fe->dpcm[stream].trigger_pending = 0;
1940ea9d0d77STakashi Iwai 	}
1941ea9d0d77STakashi Iwai 	fe->dpcm[stream].runtime_update = state;
1942ea9d0d77STakashi Iwai 	snd_pcm_stream_unlock_irq(substream);
1943ea9d0d77STakashi Iwai }
1944ea9d0d77STakashi Iwai 
1945906c7d69SPC Liao static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
1946906c7d69SPC Liao 			       int stream)
1947906c7d69SPC Liao {
1948906c7d69SPC Liao 	struct snd_soc_dpcm *dpcm;
1949906c7d69SPC Liao 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
195019bdcc7aSShreyas NC 	struct snd_soc_dai *fe_cpu_dai;
1951906c7d69SPC Liao 	int err;
195219bdcc7aSShreyas NC 	int i;
1953906c7d69SPC Liao 
1954906c7d69SPC Liao 	/* apply symmetry for FE */
1955906c7d69SPC Liao 	if (soc_pcm_has_symmetry(fe_substream))
1956906c7d69SPC Liao 		fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
1957906c7d69SPC Liao 
1958a4be4187SKuninori Morimoto 	for_each_rtd_cpu_dais (fe, i, fe_cpu_dai) {
1959906c7d69SPC Liao 		/* Symmetry only applies if we've got an active stream. */
1960906c7d69SPC Liao 		if (fe_cpu_dai->active) {
1961906c7d69SPC Liao 			err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
1962906c7d69SPC Liao 			if (err < 0)
1963906c7d69SPC Liao 				return err;
1964906c7d69SPC Liao 		}
196519bdcc7aSShreyas NC 	}
1966906c7d69SPC Liao 
1967906c7d69SPC Liao 	/* apply symmetry for BE */
19688d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
1969906c7d69SPC Liao 		struct snd_soc_pcm_runtime *be = dpcm->be;
1970906c7d69SPC Liao 		struct snd_pcm_substream *be_substream =
1971906c7d69SPC Liao 			snd_soc_dpcm_get_substream(be, stream);
19726246f283SJerome Brunet 		struct snd_soc_pcm_runtime *rtd;
1973*c840f769SKuninori Morimoto 		struct snd_soc_dai *dai;
1974906c7d69SPC Liao 		int i;
1975906c7d69SPC Liao 
19766246f283SJerome Brunet 		/* A backend may not have the requested substream */
19776246f283SJerome Brunet 		if (!be_substream)
19786246f283SJerome Brunet 			continue;
19796246f283SJerome Brunet 
19806246f283SJerome Brunet 		rtd = be_substream->private_data;
1981f1176614SJeeja KP 		if (rtd->dai_link->be_hw_params_fixup)
1982f1176614SJeeja KP 			continue;
1983f1176614SJeeja KP 
1984906c7d69SPC Liao 		if (soc_pcm_has_symmetry(be_substream))
1985906c7d69SPC Liao 			be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
1986906c7d69SPC Liao 
1987906c7d69SPC Liao 		/* Symmetry only applies if we've got an active stream. */
1988*c840f769SKuninori Morimoto 		for_each_rtd_dais(rtd, i, dai) {
1989*c840f769SKuninori Morimoto 			if (dai->active) {
1990*c840f769SKuninori Morimoto 				err = soc_pcm_apply_symmetry(fe_substream, dai);
1991906c7d69SPC Liao 				if (err < 0)
1992906c7d69SPC Liao 					return err;
1993906c7d69SPC Liao 			}
1994906c7d69SPC Liao 		}
1995906c7d69SPC Liao 	}
1996906c7d69SPC Liao 
1997906c7d69SPC Liao 	return 0;
1998906c7d69SPC Liao }
1999906c7d69SPC Liao 
200001d7584cSLiam Girdwood static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
200101d7584cSLiam Girdwood {
200201d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
200301d7584cSLiam Girdwood 	struct snd_pcm_runtime *runtime = fe_substream->runtime;
200401d7584cSLiam Girdwood 	int stream = fe_substream->stream, ret = 0;
200501d7584cSLiam Girdwood 
2006ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
200701d7584cSLiam Girdwood 
200825c2f515SKuninori Morimoto 	ret = dpcm_be_dai_startup(fe, stream);
200901d7584cSLiam Girdwood 	if (ret < 0) {
2010103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: failed to start some BEs %d\n", ret);
201101d7584cSLiam Girdwood 		goto be_err;
201201d7584cSLiam Girdwood 	}
201301d7584cSLiam Girdwood 
2014103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name);
201501d7584cSLiam Girdwood 
201601d7584cSLiam Girdwood 	/* start the DAI frontend */
201701d7584cSLiam Girdwood 	ret = soc_pcm_open(fe_substream);
201801d7584cSLiam Girdwood 	if (ret < 0) {
2019103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: failed to start FE %d\n", ret);
202001d7584cSLiam Girdwood 		goto unwind;
202101d7584cSLiam Girdwood 	}
202201d7584cSLiam Girdwood 
202301d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
202401d7584cSLiam Girdwood 
202501d7584cSLiam Girdwood 	dpcm_set_fe_runtime(fe_substream);
202601d7584cSLiam Girdwood 	snd_pcm_limit_hw_rates(runtime);
202701d7584cSLiam Girdwood 
2028906c7d69SPC Liao 	ret = dpcm_apply_symmetry(fe_substream, stream);
20298a01fbf0SKuninori Morimoto 	if (ret < 0)
2030906c7d69SPC Liao 		dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n",
2031906c7d69SPC Liao 			ret);
203201d7584cSLiam Girdwood 
203301d7584cSLiam Girdwood unwind:
20348a01fbf0SKuninori Morimoto 	if (ret < 0)
203525c2f515SKuninori Morimoto 		dpcm_be_dai_startup_unwind(fe, stream);
203601d7584cSLiam Girdwood be_err:
2037ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
203801d7584cSLiam Girdwood 	return ret;
203901d7584cSLiam Girdwood }
204001d7584cSLiam Girdwood 
204123607025SLiam Girdwood int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
204201d7584cSLiam Girdwood {
204301d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
204401d7584cSLiam Girdwood 
204501d7584cSLiam Girdwood 	/* only shutdown BEs that are either sinks or sources to this FE DAI */
20468d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
204701d7584cSLiam Girdwood 
204801d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
204901d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
205001d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
205101d7584cSLiam Girdwood 
205201d7584cSLiam Girdwood 		/* is this op for this BE ? */
205301d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
205401d7584cSLiam Girdwood 			continue;
205501d7584cSLiam Girdwood 
205601d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
2057103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: no users %s at close - state %d\n",
205801d7584cSLiam Girdwood 				stream ? "capture" : "playback",
205901d7584cSLiam Girdwood 				be->dpcm[stream].state);
206001d7584cSLiam Girdwood 
206101d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
206201d7584cSLiam Girdwood 			continue;
206301d7584cSLiam Girdwood 
206401d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
20659c0ac70aSKai Chieh Chuang 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) {
20669c0ac70aSKai Chieh Chuang 			soc_pcm_hw_free(be_substream);
20679c0ac70aSKai Chieh Chuang 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
20689c0ac70aSKai Chieh Chuang 		}
206901d7584cSLiam Girdwood 
2070103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: close BE %s\n",
207194d215ccS彭东林 			be->dai_link->name);
207201d7584cSLiam Girdwood 
207301d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
207401d7584cSLiam Girdwood 		be_substream->runtime = NULL;
207501d7584cSLiam Girdwood 
207601d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
207701d7584cSLiam Girdwood 	}
207801d7584cSLiam Girdwood 	return 0;
207901d7584cSLiam Girdwood }
208001d7584cSLiam Girdwood 
208101d7584cSLiam Girdwood static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
208201d7584cSLiam Girdwood {
208301d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
208401d7584cSLiam Girdwood 	int stream = substream->stream;
208501d7584cSLiam Girdwood 
2086ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
208701d7584cSLiam Girdwood 
208801d7584cSLiam Girdwood 	/* shutdown the BEs */
208925c2f515SKuninori Morimoto 	dpcm_be_dai_shutdown(fe, stream);
209001d7584cSLiam Girdwood 
2091103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name);
209201d7584cSLiam Girdwood 
209301d7584cSLiam Girdwood 	/* now shutdown the frontend */
209401d7584cSLiam Girdwood 	soc_pcm_close(substream);
209501d7584cSLiam Girdwood 
209601d7584cSLiam Girdwood 	/* run the stream event for each BE */
20971c531230SKuninori Morimoto 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
209801d7584cSLiam Girdwood 
209901d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
2100ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
210101d7584cSLiam Girdwood 	return 0;
210201d7584cSLiam Girdwood }
210301d7584cSLiam Girdwood 
210423607025SLiam Girdwood int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
210501d7584cSLiam Girdwood {
210601d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
210701d7584cSLiam Girdwood 
210801d7584cSLiam Girdwood 	/* only hw_params backends that are either sinks or sources
210901d7584cSLiam Girdwood 	 * to this frontend DAI */
21108d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
211101d7584cSLiam Girdwood 
211201d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
211301d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
211401d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
211501d7584cSLiam Girdwood 
211601d7584cSLiam Girdwood 		/* is this op for this BE ? */
211701d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
211801d7584cSLiam Girdwood 			continue;
211901d7584cSLiam Girdwood 
212001d7584cSLiam Girdwood 		/* only free hw when no longer used - check all FEs */
212101d7584cSLiam Girdwood 		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
212201d7584cSLiam Girdwood 				continue;
212301d7584cSLiam Girdwood 
212436fba62cSQiao Zhou 		/* do not free hw if this BE is used by other FE */
212536fba62cSQiao Zhou 		if (be->dpcm[stream].users > 1)
212636fba62cSQiao Zhou 			continue;
212736fba62cSQiao Zhou 
212801d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
212901d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
213001d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
213108b27848SPatrick Lai 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) &&
21325e82d2beSVinod Koul 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
21335e82d2beSVinod Koul 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
213401d7584cSLiam Girdwood 			continue;
213501d7584cSLiam Girdwood 
2136103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: hw_free BE %s\n",
213794d215ccS彭东林 			be->dai_link->name);
213801d7584cSLiam Girdwood 
213901d7584cSLiam Girdwood 		soc_pcm_hw_free(be_substream);
214001d7584cSLiam Girdwood 
214101d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
214201d7584cSLiam Girdwood 	}
214301d7584cSLiam Girdwood 
214401d7584cSLiam Girdwood 	return 0;
214501d7584cSLiam Girdwood }
214601d7584cSLiam Girdwood 
214745c0a188SMark Brown static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
214801d7584cSLiam Girdwood {
214901d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
215001d7584cSLiam Girdwood 	int err, stream = substream->stream;
215101d7584cSLiam Girdwood 
215201d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
2153ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
215401d7584cSLiam Girdwood 
2155103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
215601d7584cSLiam Girdwood 
215701d7584cSLiam Girdwood 	/* call hw_free on the frontend */
215801d7584cSLiam Girdwood 	err = soc_pcm_hw_free(substream);
215901d7584cSLiam Girdwood 	if (err < 0)
2160103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_free FE %s failed\n",
216101d7584cSLiam Girdwood 			fe->dai_link->name);
216201d7584cSLiam Girdwood 
216301d7584cSLiam Girdwood 	/* only hw_params backends that are either sinks or sources
216401d7584cSLiam Girdwood 	 * to this frontend DAI */
216501d7584cSLiam Girdwood 	err = dpcm_be_dai_hw_free(fe, stream);
216601d7584cSLiam Girdwood 
216701d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
2168ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
216901d7584cSLiam Girdwood 
217001d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
217101d7584cSLiam Girdwood 	return 0;
217201d7584cSLiam Girdwood }
217301d7584cSLiam Girdwood 
217423607025SLiam Girdwood int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
217501d7584cSLiam Girdwood {
217601d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
217701d7584cSLiam Girdwood 	int ret;
217801d7584cSLiam Girdwood 
21798d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
218001d7584cSLiam Girdwood 
218101d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
218201d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
218301d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
218401d7584cSLiam Girdwood 
218501d7584cSLiam Girdwood 		/* is this op for this BE ? */
218601d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
218701d7584cSLiam Girdwood 			continue;
218801d7584cSLiam Girdwood 
218901d7584cSLiam Girdwood 		/* copy params for each dpcm */
219001d7584cSLiam Girdwood 		memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params,
219101d7584cSLiam Girdwood 				sizeof(struct snd_pcm_hw_params));
219201d7584cSLiam Girdwood 
219301d7584cSLiam Girdwood 		/* perform any hw_params fixups */
219401d7584cSLiam Girdwood 		if (be->dai_link->be_hw_params_fixup) {
219501d7584cSLiam Girdwood 			ret = be->dai_link->be_hw_params_fixup(be,
219601d7584cSLiam Girdwood 					&dpcm->hw_params);
219701d7584cSLiam Girdwood 			if (ret < 0) {
219801d7584cSLiam Girdwood 				dev_err(be->dev,
2199103d84a3SLiam Girdwood 					"ASoC: hw_params BE fixup failed %d\n",
220001d7584cSLiam Girdwood 					ret);
220101d7584cSLiam Girdwood 				goto unwind;
220201d7584cSLiam Girdwood 			}
220301d7584cSLiam Girdwood 		}
220401d7584cSLiam Girdwood 
2205ae061d2aSLibin Yang 		/* copy the fixed-up hw params for BE dai */
2206ae061d2aSLibin Yang 		memcpy(&be->dpcm[stream].hw_params, &dpcm->hw_params,
2207ae061d2aSLibin Yang 		       sizeof(struct snd_pcm_hw_params));
2208ae061d2aSLibin Yang 
2209b0639bd2SKuninori Morimoto 		/* only allow hw_params() if no connected FEs are running */
2210b0639bd2SKuninori Morimoto 		if (!snd_soc_dpcm_can_be_params(fe, be, stream))
2211b0639bd2SKuninori Morimoto 			continue;
2212b0639bd2SKuninori Morimoto 
2213b0639bd2SKuninori Morimoto 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
2214b0639bd2SKuninori Morimoto 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
2215b0639bd2SKuninori Morimoto 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
2216b0639bd2SKuninori Morimoto 			continue;
2217b0639bd2SKuninori Morimoto 
2218b0639bd2SKuninori Morimoto 		dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
221994d215ccS彭东林 			be->dai_link->name);
2220b0639bd2SKuninori Morimoto 
222101d7584cSLiam Girdwood 		ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
222201d7584cSLiam Girdwood 		if (ret < 0) {
222301d7584cSLiam Girdwood 			dev_err(dpcm->be->dev,
2224103d84a3SLiam Girdwood 				"ASoC: hw_params BE failed %d\n", ret);
222501d7584cSLiam Girdwood 			goto unwind;
222601d7584cSLiam Girdwood 		}
222701d7584cSLiam Girdwood 
222801d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
222901d7584cSLiam Girdwood 	}
223001d7584cSLiam Girdwood 	return 0;
223101d7584cSLiam Girdwood 
223201d7584cSLiam Girdwood unwind:
223301d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
22348d6258a4SKuninori Morimoto 	for_each_dpcm_be_rollback(fe, stream, dpcm) {
223501d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
223601d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
223701d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
223801d7584cSLiam Girdwood 
223901d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
224001d7584cSLiam Girdwood 			continue;
224101d7584cSLiam Girdwood 
224201d7584cSLiam Girdwood 		/* only allow hw_free() if no connected FEs are running */
224301d7584cSLiam Girdwood 		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
224401d7584cSLiam Girdwood 			continue;
224501d7584cSLiam Girdwood 
224601d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
224701d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
224801d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
224901d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
225001d7584cSLiam Girdwood 			continue;
225101d7584cSLiam Girdwood 
225201d7584cSLiam Girdwood 		soc_pcm_hw_free(be_substream);
225301d7584cSLiam Girdwood 	}
225401d7584cSLiam Girdwood 
225501d7584cSLiam Girdwood 	return ret;
225601d7584cSLiam Girdwood }
225701d7584cSLiam Girdwood 
225845c0a188SMark Brown static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
225901d7584cSLiam Girdwood 				 struct snd_pcm_hw_params *params)
226001d7584cSLiam Girdwood {
226101d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
226201d7584cSLiam Girdwood 	int ret, stream = substream->stream;
226301d7584cSLiam Girdwood 
226401d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
2265ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
226601d7584cSLiam Girdwood 
226725c2f515SKuninori Morimoto 	memcpy(&fe->dpcm[stream].hw_params, params,
226801d7584cSLiam Girdwood 			sizeof(struct snd_pcm_hw_params));
226925c2f515SKuninori Morimoto 	ret = dpcm_be_dai_hw_params(fe, stream);
227001d7584cSLiam Girdwood 	if (ret < 0) {
2271103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_params BE failed %d\n", ret);
227201d7584cSLiam Girdwood 		goto out;
227301d7584cSLiam Girdwood 	}
227401d7584cSLiam Girdwood 
2275103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: hw_params FE %s rate %d chan %x fmt %d\n",
227601d7584cSLiam Girdwood 			fe->dai_link->name, params_rate(params),
227701d7584cSLiam Girdwood 			params_channels(params), params_format(params));
227801d7584cSLiam Girdwood 
227901d7584cSLiam Girdwood 	/* call hw_params on the frontend */
228001d7584cSLiam Girdwood 	ret = soc_pcm_hw_params(substream, params);
228101d7584cSLiam Girdwood 	if (ret < 0) {
2282103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret);
228301d7584cSLiam Girdwood 		dpcm_be_dai_hw_free(fe, stream);
228401d7584cSLiam Girdwood 	 } else
228501d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
228601d7584cSLiam Girdwood 
228701d7584cSLiam Girdwood out:
2288ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
228901d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
229001d7584cSLiam Girdwood 	return ret;
229101d7584cSLiam Girdwood }
229201d7584cSLiam Girdwood 
229301d7584cSLiam Girdwood static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm,
229401d7584cSLiam Girdwood 		struct snd_pcm_substream *substream, int cmd)
229501d7584cSLiam Girdwood {
229601d7584cSLiam Girdwood 	int ret;
229701d7584cSLiam Girdwood 
2298103d84a3SLiam Girdwood 	dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d\n",
229994d215ccS彭东林 			dpcm->be->dai_link->name, cmd);
230001d7584cSLiam Girdwood 
230101d7584cSLiam Girdwood 	ret = soc_pcm_trigger(substream, cmd);
230201d7584cSLiam Girdwood 	if (ret < 0)
2303103d84a3SLiam Girdwood 		dev_err(dpcm->be->dev,"ASoC: trigger BE failed %d\n", ret);
230401d7584cSLiam Girdwood 
230501d7584cSLiam Girdwood 	return ret;
230601d7584cSLiam Girdwood }
230701d7584cSLiam Girdwood 
230823607025SLiam Girdwood int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
230945c0a188SMark Brown 			       int cmd)
231001d7584cSLiam Girdwood {
231101d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
231201d7584cSLiam Girdwood 	int ret = 0;
231301d7584cSLiam Girdwood 
23148d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
231501d7584cSLiam Girdwood 
231601d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
231701d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
231801d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
231901d7584cSLiam Girdwood 
232001d7584cSLiam Girdwood 		/* is this op for this BE ? */
232101d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
232201d7584cSLiam Girdwood 			continue;
232301d7584cSLiam Girdwood 
232401d7584cSLiam Girdwood 		switch (cmd) {
232501d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_START:
232601d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
232701d7584cSLiam Girdwood 			    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
232801d7584cSLiam Girdwood 				continue;
232901d7584cSLiam Girdwood 
233001d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
233101d7584cSLiam Girdwood 			if (ret)
233201d7584cSLiam Girdwood 				return ret;
233301d7584cSLiam Girdwood 
233401d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
233501d7584cSLiam Girdwood 			break;
233601d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_RESUME:
233701d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
233801d7584cSLiam Girdwood 				continue;
233901d7584cSLiam Girdwood 
234001d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
234101d7584cSLiam Girdwood 			if (ret)
234201d7584cSLiam Girdwood 				return ret;
234301d7584cSLiam Girdwood 
234401d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
234501d7584cSLiam Girdwood 			break;
234601d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
234701d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
234801d7584cSLiam Girdwood 				continue;
234901d7584cSLiam Girdwood 
235001d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
235101d7584cSLiam Girdwood 			if (ret)
235201d7584cSLiam Girdwood 				return ret;
235301d7584cSLiam Girdwood 
235401d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
235501d7584cSLiam Girdwood 			break;
235601d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_STOP:
235701d7584cSLiam Girdwood 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
235801d7584cSLiam Girdwood 				continue;
235901d7584cSLiam Girdwood 
236001d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
236101d7584cSLiam Girdwood 				continue;
236201d7584cSLiam Girdwood 
236301d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
236401d7584cSLiam Girdwood 			if (ret)
236501d7584cSLiam Girdwood 				return ret;
236601d7584cSLiam Girdwood 
236701d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
236801d7584cSLiam Girdwood 			break;
236901d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_SUSPEND:
2370868a6ca8SNicolin Chen 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
237101d7584cSLiam Girdwood 				continue;
237201d7584cSLiam Girdwood 
237301d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
237401d7584cSLiam Girdwood 				continue;
237501d7584cSLiam Girdwood 
237601d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
237701d7584cSLiam Girdwood 			if (ret)
237801d7584cSLiam Girdwood 				return ret;
237901d7584cSLiam Girdwood 
238001d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
238101d7584cSLiam Girdwood 			break;
238201d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
238301d7584cSLiam Girdwood 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
238401d7584cSLiam Girdwood 				continue;
238501d7584cSLiam Girdwood 
238601d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
238701d7584cSLiam Girdwood 				continue;
238801d7584cSLiam Girdwood 
238901d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
239001d7584cSLiam Girdwood 			if (ret)
239101d7584cSLiam Girdwood 				return ret;
239201d7584cSLiam Girdwood 
239301d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
239401d7584cSLiam Girdwood 			break;
239501d7584cSLiam Girdwood 		}
239601d7584cSLiam Girdwood 	}
239701d7584cSLiam Girdwood 
239801d7584cSLiam Girdwood 	return ret;
239901d7584cSLiam Girdwood }
240001d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
240101d7584cSLiam Girdwood 
2402acbf2774SRanjani Sridharan static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream,
2403acbf2774SRanjani Sridharan 				  int cmd, bool fe_first)
2404acbf2774SRanjani Sridharan {
2405acbf2774SRanjani Sridharan 	struct snd_soc_pcm_runtime *fe = substream->private_data;
2406acbf2774SRanjani Sridharan 	int ret;
2407acbf2774SRanjani Sridharan 
2408acbf2774SRanjani Sridharan 	/* call trigger on the frontend before the backend. */
2409acbf2774SRanjani Sridharan 	if (fe_first) {
2410acbf2774SRanjani Sridharan 		dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
2411acbf2774SRanjani Sridharan 			fe->dai_link->name, cmd);
2412acbf2774SRanjani Sridharan 
2413acbf2774SRanjani Sridharan 		ret = soc_pcm_trigger(substream, cmd);
2414acbf2774SRanjani Sridharan 		if (ret < 0)
2415acbf2774SRanjani Sridharan 			return ret;
2416acbf2774SRanjani Sridharan 
2417acbf2774SRanjani Sridharan 		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
2418acbf2774SRanjani Sridharan 		return ret;
2419acbf2774SRanjani Sridharan 	}
2420acbf2774SRanjani Sridharan 
2421acbf2774SRanjani Sridharan 	/* call trigger on the frontend after the backend. */
2422acbf2774SRanjani Sridharan 	ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
2423acbf2774SRanjani Sridharan 	if (ret < 0)
2424acbf2774SRanjani Sridharan 		return ret;
2425acbf2774SRanjani Sridharan 
2426acbf2774SRanjani Sridharan 	dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
2427acbf2774SRanjani Sridharan 		fe->dai_link->name, cmd);
2428acbf2774SRanjani Sridharan 
2429acbf2774SRanjani Sridharan 	ret = soc_pcm_trigger(substream, cmd);
2430acbf2774SRanjani Sridharan 
2431acbf2774SRanjani Sridharan 	return ret;
2432acbf2774SRanjani Sridharan }
2433acbf2774SRanjani Sridharan 
2434ea9d0d77STakashi Iwai static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
243501d7584cSLiam Girdwood {
243601d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
2437acbf2774SRanjani Sridharan 	int stream = substream->stream;
2438acbf2774SRanjani Sridharan 	int ret = 0;
243901d7584cSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
244001d7584cSLiam Girdwood 
244101d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
244201d7584cSLiam Girdwood 
244301d7584cSLiam Girdwood 	switch (trigger) {
244401d7584cSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_PRE:
2445acbf2774SRanjani Sridharan 		switch (cmd) {
2446acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_START:
2447acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_RESUME:
2448acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2449acbf2774SRanjani Sridharan 			ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
2450acbf2774SRanjani Sridharan 			break;
2451acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_STOP:
2452acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_SUSPEND:
2453acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2454acbf2774SRanjani Sridharan 			ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
2455acbf2774SRanjani Sridharan 			break;
2456acbf2774SRanjani Sridharan 		default:
2457acbf2774SRanjani Sridharan 			ret = -EINVAL;
2458acbf2774SRanjani Sridharan 			break;
245901d7584cSLiam Girdwood 		}
246001d7584cSLiam Girdwood 		break;
246101d7584cSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_POST:
2462acbf2774SRanjani Sridharan 		switch (cmd) {
2463acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_START:
2464acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_RESUME:
2465acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2466acbf2774SRanjani Sridharan 			ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
2467acbf2774SRanjani Sridharan 			break;
2468acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_STOP:
2469acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_SUSPEND:
2470acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2471acbf2774SRanjani Sridharan 			ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
2472acbf2774SRanjani Sridharan 			break;
2473acbf2774SRanjani Sridharan 		default:
2474acbf2774SRanjani Sridharan 			ret = -EINVAL;
2475acbf2774SRanjani Sridharan 			break;
247601d7584cSLiam Girdwood 		}
247701d7584cSLiam Girdwood 		break;
247807bf84aaSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_BESPOKE:
247907bf84aaSLiam Girdwood 		/* bespoke trigger() - handles both FE and BEs */
248007bf84aaSLiam Girdwood 
2481103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd %d\n",
248207bf84aaSLiam Girdwood 				fe->dai_link->name, cmd);
248307bf84aaSLiam Girdwood 
248407bf84aaSLiam Girdwood 		ret = soc_pcm_bespoke_trigger(substream, cmd);
248507bf84aaSLiam Girdwood 		break;
248601d7584cSLiam Girdwood 	default:
2487103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd,
248801d7584cSLiam Girdwood 				fe->dai_link->name);
248901d7584cSLiam Girdwood 		ret = -EINVAL;
249001d7584cSLiam Girdwood 		goto out;
249101d7584cSLiam Girdwood 	}
249201d7584cSLiam Girdwood 
2493acbf2774SRanjani Sridharan 	if (ret < 0) {
2494acbf2774SRanjani Sridharan 		dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n",
2495acbf2774SRanjani Sridharan 			cmd, ret);
2496acbf2774SRanjani Sridharan 		goto out;
2497acbf2774SRanjani Sridharan 	}
2498acbf2774SRanjani Sridharan 
249901d7584cSLiam Girdwood 	switch (cmd) {
250001d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_START:
250101d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_RESUME:
250201d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
250301d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
250401d7584cSLiam Girdwood 		break;
250501d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_STOP:
250601d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_SUSPEND:
250701d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
250801d7584cSLiam Girdwood 		break;
25099f169b9fSPatrick Lai 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
25109f169b9fSPatrick Lai 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
25119f169b9fSPatrick Lai 		break;
251201d7584cSLiam Girdwood 	}
251301d7584cSLiam Girdwood 
251401d7584cSLiam Girdwood out:
251501d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
251601d7584cSLiam Girdwood 	return ret;
251701d7584cSLiam Girdwood }
251801d7584cSLiam Girdwood 
2519ea9d0d77STakashi Iwai static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
2520ea9d0d77STakashi Iwai {
2521ea9d0d77STakashi Iwai 	struct snd_soc_pcm_runtime *fe = substream->private_data;
2522ea9d0d77STakashi Iwai 	int stream = substream->stream;
2523ea9d0d77STakashi Iwai 
2524ea9d0d77STakashi Iwai 	/* if FE's runtime_update is already set, we're in race;
2525ea9d0d77STakashi Iwai 	 * process this trigger later at exit
2526ea9d0d77STakashi Iwai 	 */
2527ea9d0d77STakashi Iwai 	if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) {
2528ea9d0d77STakashi Iwai 		fe->dpcm[stream].trigger_pending = cmd + 1;
2529ea9d0d77STakashi Iwai 		return 0; /* delayed, assuming it's successful */
2530ea9d0d77STakashi Iwai 	}
2531ea9d0d77STakashi Iwai 
2532ea9d0d77STakashi Iwai 	/* we're alone, let's trigger */
2533ea9d0d77STakashi Iwai 	return dpcm_fe_dai_do_trigger(substream, cmd);
2534ea9d0d77STakashi Iwai }
2535ea9d0d77STakashi Iwai 
253623607025SLiam Girdwood int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
253701d7584cSLiam Girdwood {
253801d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
253901d7584cSLiam Girdwood 	int ret = 0;
254001d7584cSLiam Girdwood 
25418d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
254201d7584cSLiam Girdwood 
254301d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
254401d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
254501d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
254601d7584cSLiam Girdwood 
254701d7584cSLiam Girdwood 		/* is this op for this BE ? */
254801d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
254901d7584cSLiam Girdwood 			continue;
255001d7584cSLiam Girdwood 
255101d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
255295f444dcSKoro Chen 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
25535087a8f1SLibin Yang 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND) &&
25545087a8f1SLibin Yang 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
255501d7584cSLiam Girdwood 			continue;
255601d7584cSLiam Girdwood 
2557103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: prepare BE %s\n",
255894d215ccS彭东林 			be->dai_link->name);
255901d7584cSLiam Girdwood 
256001d7584cSLiam Girdwood 		ret = soc_pcm_prepare(be_substream);
256101d7584cSLiam Girdwood 		if (ret < 0) {
2562103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: backend prepare failed %d\n",
256301d7584cSLiam Girdwood 				ret);
256401d7584cSLiam Girdwood 			break;
256501d7584cSLiam Girdwood 		}
256601d7584cSLiam Girdwood 
256701d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
256801d7584cSLiam Girdwood 	}
256901d7584cSLiam Girdwood 	return ret;
257001d7584cSLiam Girdwood }
257101d7584cSLiam Girdwood 
257245c0a188SMark Brown static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
257301d7584cSLiam Girdwood {
257401d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
257501d7584cSLiam Girdwood 	int stream = substream->stream, ret = 0;
257601d7584cSLiam Girdwood 
257701d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
257801d7584cSLiam Girdwood 
2579103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
258001d7584cSLiam Girdwood 
2581ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
258201d7584cSLiam Girdwood 
258301d7584cSLiam Girdwood 	/* there is no point preparing this FE if there are no BEs */
258401d7584cSLiam Girdwood 	if (list_empty(&fe->dpcm[stream].be_clients)) {
2585103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: no backend DAIs enabled for %s\n",
258601d7584cSLiam Girdwood 				fe->dai_link->name);
258701d7584cSLiam Girdwood 		ret = -EINVAL;
258801d7584cSLiam Girdwood 		goto out;
258901d7584cSLiam Girdwood 	}
259001d7584cSLiam Girdwood 
259125c2f515SKuninori Morimoto 	ret = dpcm_be_dai_prepare(fe, stream);
259201d7584cSLiam Girdwood 	if (ret < 0)
259301d7584cSLiam Girdwood 		goto out;
259401d7584cSLiam Girdwood 
259501d7584cSLiam Girdwood 	/* call prepare on the frontend */
259601d7584cSLiam Girdwood 	ret = soc_pcm_prepare(substream);
259701d7584cSLiam Girdwood 	if (ret < 0) {
2598103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: prepare FE %s failed\n",
259901d7584cSLiam Girdwood 			fe->dai_link->name);
260001d7584cSLiam Girdwood 		goto out;
260101d7584cSLiam Girdwood 	}
260201d7584cSLiam Girdwood 
260301d7584cSLiam Girdwood 	/* run the stream event for each BE */
260401d7584cSLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
260501d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
260601d7584cSLiam Girdwood 
260701d7584cSLiam Girdwood out:
2608ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
260901d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
261001d7584cSLiam Girdwood 
261101d7584cSLiam Girdwood 	return ret;
261201d7584cSLiam Girdwood }
261301d7584cSLiam Girdwood 
2614618dae11SLiam Girdwood static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
2615618dae11SLiam Girdwood {
261607bf84aaSLiam Girdwood 	struct snd_pcm_substream *substream =
261707bf84aaSLiam Girdwood 		snd_soc_dpcm_get_substream(fe, stream);
261807bf84aaSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
2619618dae11SLiam Girdwood 	int err;
262001d7584cSLiam Girdwood 
2621103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n",
2622618dae11SLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name);
2623618dae11SLiam Girdwood 
262407bf84aaSLiam Girdwood 	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
262507bf84aaSLiam Girdwood 		/* call bespoke trigger - FE takes care of all BE triggers */
2626103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop\n",
262707bf84aaSLiam Girdwood 				fe->dai_link->name);
262807bf84aaSLiam Girdwood 
262907bf84aaSLiam Girdwood 		err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP);
263007bf84aaSLiam Girdwood 		if (err < 0)
2631103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err);
263207bf84aaSLiam Girdwood 	} else {
2633103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop\n",
263407bf84aaSLiam Girdwood 			fe->dai_link->name);
263507bf84aaSLiam Girdwood 
2636618dae11SLiam Girdwood 		err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
2637618dae11SLiam Girdwood 		if (err < 0)
2638103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err);
263907bf84aaSLiam Girdwood 	}
2640618dae11SLiam Girdwood 
2641618dae11SLiam Girdwood 	err = dpcm_be_dai_hw_free(fe, stream);
2642618dae11SLiam Girdwood 	if (err < 0)
2643103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err);
2644618dae11SLiam Girdwood 
2645618dae11SLiam Girdwood 	err = dpcm_be_dai_shutdown(fe, stream);
2646618dae11SLiam Girdwood 	if (err < 0)
2647103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err);
2648618dae11SLiam Girdwood 
2649618dae11SLiam Girdwood 	/* run the stream event for each BE */
2650618dae11SLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
2651618dae11SLiam Girdwood 
2652618dae11SLiam Girdwood 	return 0;
2653618dae11SLiam Girdwood }
2654618dae11SLiam Girdwood 
2655618dae11SLiam Girdwood static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
2656618dae11SLiam Girdwood {
265707bf84aaSLiam Girdwood 	struct snd_pcm_substream *substream =
265807bf84aaSLiam Girdwood 		snd_soc_dpcm_get_substream(fe, stream);
2659618dae11SLiam Girdwood 	struct snd_soc_dpcm *dpcm;
266007bf84aaSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
2661618dae11SLiam Girdwood 	int ret;
2662a9764869SKaiChieh Chuang 	unsigned long flags;
2663618dae11SLiam Girdwood 
2664103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n",
2665618dae11SLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name);
2666618dae11SLiam Girdwood 
2667618dae11SLiam Girdwood 	/* Only start the BE if the FE is ready */
2668618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE ||
2669618dae11SLiam Girdwood 		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE)
2670618dae11SLiam Girdwood 		return -EINVAL;
2671618dae11SLiam Girdwood 
2672618dae11SLiam Girdwood 	/* startup must always be called for new BEs */
2673618dae11SLiam Girdwood 	ret = dpcm_be_dai_startup(fe, stream);
2674fffc0ca2SDan Carpenter 	if (ret < 0)
2675618dae11SLiam Girdwood 		goto disconnect;
2676618dae11SLiam Girdwood 
2677618dae11SLiam Girdwood 	/* keep going if FE state is > open */
2678618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
2679618dae11SLiam Girdwood 		return 0;
2680618dae11SLiam Girdwood 
2681618dae11SLiam Girdwood 	ret = dpcm_be_dai_hw_params(fe, stream);
2682fffc0ca2SDan Carpenter 	if (ret < 0)
2683618dae11SLiam Girdwood 		goto close;
2684618dae11SLiam Girdwood 
2685618dae11SLiam Girdwood 	/* keep going if FE state is > hw_params */
2686618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
2687618dae11SLiam Girdwood 		return 0;
2688618dae11SLiam Girdwood 
2689618dae11SLiam Girdwood 
2690618dae11SLiam Girdwood 	ret = dpcm_be_dai_prepare(fe, stream);
2691fffc0ca2SDan Carpenter 	if (ret < 0)
2692618dae11SLiam Girdwood 		goto hw_free;
2693618dae11SLiam Girdwood 
2694618dae11SLiam Girdwood 	/* run the stream event for each BE */
2695618dae11SLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
2696618dae11SLiam Girdwood 
2697618dae11SLiam Girdwood 	/* keep going if FE state is > prepare */
2698618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE ||
2699618dae11SLiam Girdwood 		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP)
2700618dae11SLiam Girdwood 		return 0;
2701618dae11SLiam Girdwood 
270207bf84aaSLiam Girdwood 	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
270307bf84aaSLiam Girdwood 		/* call trigger on the frontend - FE takes care of all BE triggers */
2704103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start\n",
270507bf84aaSLiam Girdwood 				fe->dai_link->name);
270607bf84aaSLiam Girdwood 
270707bf84aaSLiam Girdwood 		ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START);
270807bf84aaSLiam Girdwood 		if (ret < 0) {
2709103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: bespoke trigger FE failed %d\n", ret);
271007bf84aaSLiam Girdwood 			goto hw_free;
271107bf84aaSLiam Girdwood 		}
271207bf84aaSLiam Girdwood 	} else {
2713103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start\n",
2714618dae11SLiam Girdwood 			fe->dai_link->name);
2715618dae11SLiam Girdwood 
2716618dae11SLiam Girdwood 		ret = dpcm_be_dai_trigger(fe, stream,
2717618dae11SLiam Girdwood 					SNDRV_PCM_TRIGGER_START);
2718618dae11SLiam Girdwood 		if (ret < 0) {
2719103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
2720618dae11SLiam Girdwood 			goto hw_free;
2721618dae11SLiam Girdwood 		}
272207bf84aaSLiam Girdwood 	}
2723618dae11SLiam Girdwood 
2724618dae11SLiam Girdwood 	return 0;
2725618dae11SLiam Girdwood 
2726618dae11SLiam Girdwood hw_free:
2727618dae11SLiam Girdwood 	dpcm_be_dai_hw_free(fe, stream);
2728618dae11SLiam Girdwood close:
2729618dae11SLiam Girdwood 	dpcm_be_dai_shutdown(fe, stream);
2730618dae11SLiam Girdwood disconnect:
2731618dae11SLiam Girdwood 	/* disconnect any non started BEs */
2732a9764869SKaiChieh Chuang 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
27338d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
2734618dae11SLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
2735618dae11SLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
2736618dae11SLiam Girdwood 				dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
2737618dae11SLiam Girdwood 	}
2738a9764869SKaiChieh Chuang 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
2739618dae11SLiam Girdwood 
2740618dae11SLiam Girdwood 	return ret;
2741618dae11SLiam Girdwood }
2742618dae11SLiam Girdwood 
2743de15d7ffSJerome Brunet static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
2744618dae11SLiam Girdwood {
2745618dae11SLiam Girdwood 	struct snd_soc_dapm_widget_list *list;
27467083f877SKuninori Morimoto 	int stream;
2747de15d7ffSJerome Brunet 	int count, paths;
2748580dff36SKuninori Morimoto 	int ret;
2749618dae11SLiam Girdwood 
27506e1276a5SBard Liao 	if (fe->num_cpus > 1) {
27516e1276a5SBard Liao 		dev_err(fe->dev,
27526e1276a5SBard Liao 			"%s doesn't support Multi CPU yet\n", __func__);
27536e1276a5SBard Liao 		return -EINVAL;
27546e1276a5SBard Liao 	}
27556e1276a5SBard Liao 
2756618dae11SLiam Girdwood 	if (!fe->dai_link->dynamic)
2757de15d7ffSJerome Brunet 		return 0;
2758618dae11SLiam Girdwood 
2759618dae11SLiam Girdwood 	/* only check active links */
2760618dae11SLiam Girdwood 	if (!fe->cpu_dai->active)
2761de15d7ffSJerome Brunet 		return 0;
2762618dae11SLiam Girdwood 
2763618dae11SLiam Girdwood 	/* DAPM sync will call this to update DSP paths */
2764de15d7ffSJerome Brunet 	dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n",
2765de15d7ffSJerome Brunet 		new ? "new" : "old", fe->dai_link->name);
2766618dae11SLiam Girdwood 
27677083f877SKuninori Morimoto 	for_each_pcm_streams(stream) {
2768075207d2SQiao Zhou 
27697083f877SKuninori Morimoto 		/* skip if FE doesn't have playback/capture capability */
27707083f877SKuninori Morimoto 		if (!snd_soc_dai_stream_valid(fe->cpu_dai,   stream) ||
27717083f877SKuninori Morimoto 		    !snd_soc_dai_stream_valid(fe->codec_dai, stream))
27727083f877SKuninori Morimoto 			continue;
2773618dae11SLiam Girdwood 
27747083f877SKuninori Morimoto 		/* skip if FE isn't currently playing/capturing */
27757083f877SKuninori Morimoto 		if (!fe->cpu_dai->stream_active[stream] ||
27767083f877SKuninori Morimoto 		    !fe->codec_dai->stream_active[stream])
27777083f877SKuninori Morimoto 			continue;
27787083f877SKuninori Morimoto 
27797083f877SKuninori Morimoto 		paths = dpcm_path_get(fe, stream, &list);
2780618dae11SLiam Girdwood 		if (paths < 0) {
2781103d84a3SLiam Girdwood 			dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
27827083f877SKuninori Morimoto 				 fe->dai_link->name,
27837083f877SKuninori Morimoto 				 stream == SNDRV_PCM_STREAM_PLAYBACK ?
27847083f877SKuninori Morimoto 				 "playback" : "capture");
2785618dae11SLiam Girdwood 			return paths;
2786618dae11SLiam Girdwood 		}
2787618dae11SLiam Girdwood 
27887083f877SKuninori Morimoto 		/* update any playback/capture paths */
27897083f877SKuninori Morimoto 		count = dpcm_process_paths(fe, stream, &list, new);
2790de15d7ffSJerome Brunet 		if (count) {
2791580dff36SKuninori Morimoto 			dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
2792de15d7ffSJerome Brunet 			if (new)
2793580dff36SKuninori Morimoto 				ret = dpcm_run_update_startup(fe, stream);
2794de15d7ffSJerome Brunet 			else
2795580dff36SKuninori Morimoto 				ret = dpcm_run_update_shutdown(fe, stream);
2796580dff36SKuninori Morimoto 			if (ret < 0)
2797580dff36SKuninori Morimoto 				dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
2798580dff36SKuninori Morimoto 			dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
2799de15d7ffSJerome Brunet 
28007083f877SKuninori Morimoto 			dpcm_clear_pending_state(fe, stream);
28017083f877SKuninori Morimoto 			dpcm_be_disconnect(fe, stream);
2802618dae11SLiam Girdwood 		}
2803618dae11SLiam Girdwood 
28047ed9de76SQiao Zhou 		dpcm_path_put(&list);
2805618dae11SLiam Girdwood 	}
2806618dae11SLiam Girdwood 
2807de15d7ffSJerome Brunet 	return 0;
2808618dae11SLiam Girdwood }
2809618dae11SLiam Girdwood 
2810de15d7ffSJerome Brunet /* Called by DAPM mixer/mux changes to update audio routing between PCMs and
2811de15d7ffSJerome Brunet  * any DAI links.
2812de15d7ffSJerome Brunet  */
2813f17a1478SGuennadi Liakhovetski int snd_soc_dpcm_runtime_update(struct snd_soc_card *card)
2814de15d7ffSJerome Brunet {
2815de15d7ffSJerome Brunet 	struct snd_soc_pcm_runtime *fe;
2816de15d7ffSJerome Brunet 	int ret = 0;
2817de15d7ffSJerome Brunet 
2818de15d7ffSJerome Brunet 	mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
2819de15d7ffSJerome Brunet 	/* shutdown all old paths first */
2820bcb1fd1fSKuninori Morimoto 	for_each_card_rtds(card, fe) {
2821de15d7ffSJerome Brunet 		ret = soc_dpcm_fe_runtime_update(fe, 0);
2822de15d7ffSJerome Brunet 		if (ret)
2823de15d7ffSJerome Brunet 			goto out;
2824de15d7ffSJerome Brunet 	}
2825de15d7ffSJerome Brunet 
2826de15d7ffSJerome Brunet 	/* bring new paths up */
2827bcb1fd1fSKuninori Morimoto 	for_each_card_rtds(card, fe) {
2828de15d7ffSJerome Brunet 		ret = soc_dpcm_fe_runtime_update(fe, 1);
2829de15d7ffSJerome Brunet 		if (ret)
2830de15d7ffSJerome Brunet 			goto out;
2831de15d7ffSJerome Brunet 	}
2832de15d7ffSJerome Brunet 
2833de15d7ffSJerome Brunet out:
2834618dae11SLiam Girdwood 	mutex_unlock(&card->mutex);
2835de15d7ffSJerome Brunet 	return ret;
2836618dae11SLiam Girdwood }
2837f17a1478SGuennadi Liakhovetski EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update);
283801d7584cSLiam Girdwood 
2839265694b6SKuninori Morimoto static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream)
284001d7584cSLiam Girdwood {
284101d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
284201d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
2843265694b6SKuninori Morimoto 	int stream = fe_substream->stream;
284430fca26fSKuninori Morimoto 
284530fca26fSKuninori Morimoto 	/* mark FE's links ready to prune */
284630fca26fSKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm)
284730fca26fSKuninori Morimoto 		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
284830fca26fSKuninori Morimoto 
284930fca26fSKuninori Morimoto 	dpcm_be_disconnect(fe, stream);
285030fca26fSKuninori Morimoto 
285130fca26fSKuninori Morimoto 	fe->dpcm[stream].runtime = NULL;
2852265694b6SKuninori Morimoto }
2853265694b6SKuninori Morimoto 
2854265694b6SKuninori Morimoto static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
2855265694b6SKuninori Morimoto {
2856265694b6SKuninori Morimoto 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
2857265694b6SKuninori Morimoto 	int ret;
2858265694b6SKuninori Morimoto 
2859265694b6SKuninori Morimoto 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
2860265694b6SKuninori Morimoto 	ret = dpcm_fe_dai_shutdown(fe_substream);
2861265694b6SKuninori Morimoto 
2862265694b6SKuninori Morimoto 	dpcm_fe_dai_cleanup(fe_substream);
2863265694b6SKuninori Morimoto 
286430fca26fSKuninori Morimoto 	mutex_unlock(&fe->card->mutex);
286530fca26fSKuninori Morimoto 	return ret;
286630fca26fSKuninori Morimoto }
286730fca26fSKuninori Morimoto 
286801d7584cSLiam Girdwood static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
286901d7584cSLiam Girdwood {
287001d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
287101d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list;
287201d7584cSLiam Girdwood 	int ret;
287301d7584cSLiam Girdwood 	int stream = fe_substream->stream;
287401d7584cSLiam Girdwood 
287501d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
287601d7584cSLiam Girdwood 	fe->dpcm[stream].runtime = fe_substream->runtime;
287701d7584cSLiam Girdwood 
28788f70e515SQiao Zhou 	ret = dpcm_path_get(fe, stream, &list);
28798f70e515SQiao Zhou 	if (ret < 0) {
2880cae06eb9SKuninori Morimoto 		goto open_end;
28818f70e515SQiao Zhou 	} else if (ret == 0) {
2882103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: %s no valid %s route\n",
288301d7584cSLiam Girdwood 			fe->dai_link->name, stream ? "capture" : "playback");
288401d7584cSLiam Girdwood 	}
288501d7584cSLiam Girdwood 
288601d7584cSLiam Girdwood 	/* calculate valid and active FE <-> BE dpcms */
288701d7584cSLiam Girdwood 	dpcm_process_paths(fe, stream, &list, 1);
288801d7584cSLiam Girdwood 
288901d7584cSLiam Girdwood 	ret = dpcm_fe_dai_startup(fe_substream);
2890265694b6SKuninori Morimoto 	if (ret < 0)
2891265694b6SKuninori Morimoto 		dpcm_fe_dai_cleanup(fe_substream);
289201d7584cSLiam Girdwood 
289301d7584cSLiam Girdwood 	dpcm_clear_pending_state(fe, stream);
289401d7584cSLiam Girdwood 	dpcm_path_put(&list);
2895cae06eb9SKuninori Morimoto open_end:
289601d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
289701d7584cSLiam Girdwood 	return ret;
289801d7584cSLiam Girdwood }
289901d7584cSLiam Girdwood 
2900ddee627cSLiam Girdwood /* create a new pcm */
2901ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
2902ddee627cSLiam Girdwood {
29032e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
290419bdcc7aSShreyas NC 	struct snd_soc_dai *cpu_dai;
29052b544dd7SKuninori Morimoto 	struct snd_soc_component *component;
2906ddee627cSLiam Girdwood 	struct snd_pcm *pcm;
2907ddee627cSLiam Girdwood 	char new_name[64];
2908ddee627cSLiam Girdwood 	int ret = 0, playback = 0, capture = 0;
29092e5894d7SBenoit Cousson 	int i;
2910ddee627cSLiam Girdwood 
291101d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
29121e9de42fSLiam Girdwood 		playback = rtd->dai_link->dpcm_playback;
29131e9de42fSLiam Girdwood 		capture = rtd->dai_link->dpcm_capture;
291401d7584cSLiam Girdwood 	} else {
2915a342031cSJerome Brunet 		/* Adapt stream for codec2codec links */
2916a4877a6fSStephan Gerhold 		int cpu_capture = rtd->dai_link->params ?
2917a4877a6fSStephan Gerhold 			SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
2918a4877a6fSStephan Gerhold 		int cpu_playback = rtd->dai_link->params ?
2919a4877a6fSStephan Gerhold 			SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
2920a342031cSJerome Brunet 
2921a4be4187SKuninori Morimoto 		for_each_rtd_codec_dais(rtd, i, codec_dai) {
292219bdcc7aSShreyas NC 			if (rtd->num_cpus == 1) {
292319bdcc7aSShreyas NC 				cpu_dai = rtd->cpu_dais[0];
292419bdcc7aSShreyas NC 			} else if (rtd->num_cpus == rtd->num_codecs) {
292519bdcc7aSShreyas NC 				cpu_dai = rtd->cpu_dais[i];
292619bdcc7aSShreyas NC 			} else {
292719bdcc7aSShreyas NC 				dev_err(rtd->card->dev,
292819bdcc7aSShreyas NC 					"N cpus to M codecs link is not supported yet\n");
292919bdcc7aSShreyas NC 				return -EINVAL;
293019bdcc7aSShreyas NC 			}
293119bdcc7aSShreyas NC 
2932467fece8SKuninori Morimoto 			if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
2933a4877a6fSStephan Gerhold 			    snd_soc_dai_stream_valid(cpu_dai,   cpu_playback))
2934ddee627cSLiam Girdwood 				playback = 1;
2935467fece8SKuninori Morimoto 			if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
2936a4877a6fSStephan Gerhold 			    snd_soc_dai_stream_valid(cpu_dai,   cpu_capture))
2937ddee627cSLiam Girdwood 				capture = 1;
293801d7584cSLiam Girdwood 		}
29392e5894d7SBenoit Cousson 	}
29402e5894d7SBenoit Cousson 
2941d6bead02SFabio Estevam 	if (rtd->dai_link->playback_only) {
2942d6bead02SFabio Estevam 		playback = 1;
2943d6bead02SFabio Estevam 		capture = 0;
2944d6bead02SFabio Estevam 	}
2945d6bead02SFabio Estevam 
2946d6bead02SFabio Estevam 	if (rtd->dai_link->capture_only) {
2947d6bead02SFabio Estevam 		playback = 0;
2948d6bead02SFabio Estevam 		capture = 1;
2949d6bead02SFabio Estevam 	}
2950d6bead02SFabio Estevam 
295101d7584cSLiam Girdwood 	/* create the PCM */
2952a342031cSJerome Brunet 	if (rtd->dai_link->params) {
2953a342031cSJerome Brunet 		snprintf(new_name, sizeof(new_name), "codec2codec(%s)",
2954a342031cSJerome Brunet 			 rtd->dai_link->stream_name);
2955a342031cSJerome Brunet 
2956a342031cSJerome Brunet 		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
2957a342031cSJerome Brunet 					   playback, capture, &pcm);
2958a342031cSJerome Brunet 	} else if (rtd->dai_link->no_pcm) {
295901d7584cSLiam Girdwood 		snprintf(new_name, sizeof(new_name), "(%s)",
296001d7584cSLiam Girdwood 			rtd->dai_link->stream_name);
296101d7584cSLiam Girdwood 
296201d7584cSLiam Girdwood 		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
296301d7584cSLiam Girdwood 				playback, capture, &pcm);
296401d7584cSLiam Girdwood 	} else {
296501d7584cSLiam Girdwood 		if (rtd->dai_link->dynamic)
296601d7584cSLiam Girdwood 			snprintf(new_name, sizeof(new_name), "%s (*)",
296701d7584cSLiam Girdwood 				rtd->dai_link->stream_name);
296801d7584cSLiam Girdwood 		else
296901d7584cSLiam Girdwood 			snprintf(new_name, sizeof(new_name), "%s %s-%d",
29702e5894d7SBenoit Cousson 				rtd->dai_link->stream_name,
29712e5894d7SBenoit Cousson 				(rtd->num_codecs > 1) ?
29722e5894d7SBenoit Cousson 				"multicodec" : rtd->codec_dai->name, num);
297301d7584cSLiam Girdwood 
297401d7584cSLiam Girdwood 		ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
297501d7584cSLiam Girdwood 			capture, &pcm);
297601d7584cSLiam Girdwood 	}
2977ddee627cSLiam Girdwood 	if (ret < 0) {
2978103d84a3SLiam Girdwood 		dev_err(rtd->card->dev, "ASoC: can't create pcm for %s\n",
29795cb9b748SLiam Girdwood 			rtd->dai_link->name);
2980ddee627cSLiam Girdwood 		return ret;
2981ddee627cSLiam Girdwood 	}
2982103d84a3SLiam Girdwood 	dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name);
2983ddee627cSLiam Girdwood 
2984ddee627cSLiam Girdwood 	/* DAPM dai link stream work */
2985a342031cSJerome Brunet 	if (rtd->dai_link->params)
29864bf2e385SCurtis Malainey 		rtd->close_delayed_work_func = codec2codec_close_delayed_work;
2987a342031cSJerome Brunet 	else
298883f94a2eSKuninori Morimoto 		rtd->close_delayed_work_func = snd_soc_close_delayed_work;
2989ddee627cSLiam Girdwood 
299048c7699fSVinod Koul 	pcm->nonatomic = rtd->dai_link->nonatomic;
2991ddee627cSLiam Girdwood 	rtd->pcm = pcm;
2992ddee627cSLiam Girdwood 	pcm->private_data = rtd;
299301d7584cSLiam Girdwood 
2994a342031cSJerome Brunet 	if (rtd->dai_link->no_pcm || rtd->dai_link->params) {
299501d7584cSLiam Girdwood 		if (playback)
299601d7584cSLiam Girdwood 			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
299701d7584cSLiam Girdwood 		if (capture)
299801d7584cSLiam Girdwood 			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
299901d7584cSLiam Girdwood 		goto out;
300001d7584cSLiam Girdwood 	}
300101d7584cSLiam Girdwood 
300201d7584cSLiam Girdwood 	/* ASoC PCM operations */
300301d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic) {
300401d7584cSLiam Girdwood 		rtd->ops.open		= dpcm_fe_dai_open;
300501d7584cSLiam Girdwood 		rtd->ops.hw_params	= dpcm_fe_dai_hw_params;
300601d7584cSLiam Girdwood 		rtd->ops.prepare	= dpcm_fe_dai_prepare;
300701d7584cSLiam Girdwood 		rtd->ops.trigger	= dpcm_fe_dai_trigger;
300801d7584cSLiam Girdwood 		rtd->ops.hw_free	= dpcm_fe_dai_hw_free;
300901d7584cSLiam Girdwood 		rtd->ops.close		= dpcm_fe_dai_close;
301001d7584cSLiam Girdwood 		rtd->ops.pointer	= soc_pcm_pointer;
301101d7584cSLiam Girdwood 	} else {
301201d7584cSLiam Girdwood 		rtd->ops.open		= soc_pcm_open;
301301d7584cSLiam Girdwood 		rtd->ops.hw_params	= soc_pcm_hw_params;
301401d7584cSLiam Girdwood 		rtd->ops.prepare	= soc_pcm_prepare;
301501d7584cSLiam Girdwood 		rtd->ops.trigger	= soc_pcm_trigger;
301601d7584cSLiam Girdwood 		rtd->ops.hw_free	= soc_pcm_hw_free;
301701d7584cSLiam Girdwood 		rtd->ops.close		= soc_pcm_close;
301801d7584cSLiam Girdwood 		rtd->ops.pointer	= soc_pcm_pointer;
301901d7584cSLiam Girdwood 	}
302001d7584cSLiam Girdwood 
3021613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
30222b544dd7SKuninori Morimoto 		const struct snd_soc_component_driver *drv = component->driver;
3023b8135864SKuninori Morimoto 
30243b1c952cSTakashi Iwai 		if (drv->ioctl)
30253b1c952cSTakashi Iwai 			rtd->ops.ioctl		= snd_soc_pcm_component_ioctl;
30261e5ddb6bSTakashi Iwai 		if (drv->sync_stop)
30271e5ddb6bSTakashi Iwai 			rtd->ops.sync_stop	= snd_soc_pcm_component_sync_stop;
3028e9067bb5SKuninori Morimoto 		if (drv->copy_user)
302982d81f5cSKuninori Morimoto 			rtd->ops.copy_user	= snd_soc_pcm_component_copy_user;
3030e9067bb5SKuninori Morimoto 		if (drv->page)
30319c712e4fSKuninori Morimoto 			rtd->ops.page		= snd_soc_pcm_component_page;
3032e9067bb5SKuninori Morimoto 		if (drv->mmap)
3033205875e1SKuninori Morimoto 			rtd->ops.mmap		= snd_soc_pcm_component_mmap;
3034b8135864SKuninori Morimoto 	}
3035b8135864SKuninori Morimoto 
3036ddee627cSLiam Girdwood 	if (playback)
303701d7584cSLiam Girdwood 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
3038ddee627cSLiam Girdwood 
3039ddee627cSLiam Girdwood 	if (capture)
304001d7584cSLiam Girdwood 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
3041ddee627cSLiam Girdwood 
3042b2b2afbbSKuninori Morimoto 	ret = snd_soc_pcm_component_new(rtd);
3043ddee627cSLiam Girdwood 	if (ret < 0) {
30447484291eSKuninori Morimoto 		dev_err(rtd->dev, "ASoC: pcm constructor failed: %d\n", ret);
3045ddee627cSLiam Girdwood 		return ret;
3046ddee627cSLiam Girdwood 	}
3047c641e5b2SJohan Hovold 
30483d21ef0bSTakashi Iwai 	pcm->no_device_suspend = true;
304901d7584cSLiam Girdwood out:
30502e5894d7SBenoit Cousson 	dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
30512e5894d7SBenoit Cousson 		 (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
305219bdcc7aSShreyas NC 		 (rtd->num_cpus > 1) ? "multicpu" : rtd->cpu_dai->name);
3053ddee627cSLiam Girdwood 	return ret;
3054ddee627cSLiam Girdwood }
305501d7584cSLiam Girdwood 
305601d7584cSLiam Girdwood /* is the current PCM operation for this FE ? */
305701d7584cSLiam Girdwood int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream)
305801d7584cSLiam Girdwood {
305901d7584cSLiam Girdwood 	if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE)
306001d7584cSLiam Girdwood 		return 1;
306101d7584cSLiam Girdwood 	return 0;
306201d7584cSLiam Girdwood }
306301d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update);
306401d7584cSLiam Girdwood 
306501d7584cSLiam Girdwood /* is the current PCM operation for this BE ? */
306601d7584cSLiam Girdwood int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
306701d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
306801d7584cSLiam Girdwood {
306901d7584cSLiam Girdwood 	if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) ||
307001d7584cSLiam Girdwood 	   ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) &&
307101d7584cSLiam Girdwood 		  be->dpcm[stream].runtime_update))
307201d7584cSLiam Girdwood 		return 1;
307301d7584cSLiam Girdwood 	return 0;
307401d7584cSLiam Girdwood }
307501d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update);
307601d7584cSLiam Girdwood 
307701d7584cSLiam Girdwood /* get the substream for this BE */
307801d7584cSLiam Girdwood struct snd_pcm_substream *
307901d7584cSLiam Girdwood 	snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream)
308001d7584cSLiam Girdwood {
308101d7584cSLiam Girdwood 	return be->pcm->streams[stream].substream;
308201d7584cSLiam Girdwood }
308301d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream);
308401d7584cSLiam Girdwood 
3085085d22beSKuninori Morimoto static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe,
3086085d22beSKuninori Morimoto 				    struct snd_soc_pcm_runtime *be,
3087085d22beSKuninori Morimoto 				    int stream,
3088085d22beSKuninori Morimoto 				    const enum snd_soc_dpcm_state *states,
3089085d22beSKuninori Morimoto 				    int num_states)
309001d7584cSLiam Girdwood {
309101d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
309201d7584cSLiam Girdwood 	int state;
3093a9764869SKaiChieh Chuang 	int ret = 1;
3094a9764869SKaiChieh Chuang 	unsigned long flags;
3095085d22beSKuninori Morimoto 	int i;
309601d7584cSLiam Girdwood 
3097a9764869SKaiChieh Chuang 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
3098d2e24d64SKuninori Morimoto 	for_each_dpcm_fe(be, stream, dpcm) {
309901d7584cSLiam Girdwood 
310001d7584cSLiam Girdwood 		if (dpcm->fe == fe)
310101d7584cSLiam Girdwood 			continue;
310201d7584cSLiam Girdwood 
310301d7584cSLiam Girdwood 		state = dpcm->fe->dpcm[stream].state;
3104085d22beSKuninori Morimoto 		for (i = 0; i < num_states; i++) {
3105085d22beSKuninori Morimoto 			if (state == states[i]) {
3106a9764869SKaiChieh Chuang 				ret = 0;
3107a9764869SKaiChieh Chuang 				break;
310801d7584cSLiam Girdwood 			}
3109a9764869SKaiChieh Chuang 		}
3110085d22beSKuninori Morimoto 	}
3111a9764869SKaiChieh Chuang 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
311201d7584cSLiam Girdwood 
3113085d22beSKuninori Morimoto 	/* it's safe to do this BE DAI */
3114a9764869SKaiChieh Chuang 	return ret;
311501d7584cSLiam Girdwood }
3116085d22beSKuninori Morimoto 
3117085d22beSKuninori Morimoto /*
3118085d22beSKuninori Morimoto  * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE
3119085d22beSKuninori Morimoto  * are not running, paused or suspended for the specified stream direction.
3120085d22beSKuninori Morimoto  */
3121085d22beSKuninori Morimoto int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
3122085d22beSKuninori Morimoto 		struct snd_soc_pcm_runtime *be, int stream)
3123085d22beSKuninori Morimoto {
3124085d22beSKuninori Morimoto 	const enum snd_soc_dpcm_state state[] = {
3125085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_START,
3126085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_PAUSED,
3127085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_SUSPEND,
3128085d22beSKuninori Morimoto 	};
3129085d22beSKuninori Morimoto 
3130085d22beSKuninori Morimoto 	return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state));
3131085d22beSKuninori Morimoto }
313201d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
313301d7584cSLiam Girdwood 
313401d7584cSLiam Girdwood /*
313501d7584cSLiam Girdwood  * We can only change hw params a BE DAI if any of it's FE are not prepared,
313601d7584cSLiam Girdwood  * running, paused or suspended for the specified stream direction.
313701d7584cSLiam Girdwood  */
313801d7584cSLiam Girdwood int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
313901d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
314001d7584cSLiam Girdwood {
3141085d22beSKuninori Morimoto 	const enum snd_soc_dpcm_state state[] = {
3142085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_START,
3143085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_PAUSED,
3144085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_SUSPEND,
3145085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_PREPARE,
3146085d22beSKuninori Morimoto 	};
314701d7584cSLiam Girdwood 
3148085d22beSKuninori Morimoto 	return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state));
314901d7584cSLiam Girdwood }
315001d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
3151