xref: /openbmc/linux/sound/soc/soc-pcm.c (revision 01d7584cd2e5a93a2b959c9dddaa0d93ec205404)
1ddee627cSLiam Girdwood /*
2ddee627cSLiam Girdwood  * soc-pcm.c  --  ALSA SoC PCM
3ddee627cSLiam Girdwood  *
4ddee627cSLiam Girdwood  * Copyright 2005 Wolfson Microelectronics PLC.
5ddee627cSLiam Girdwood  * Copyright 2005 Openedhand Ltd.
6ddee627cSLiam Girdwood  * Copyright (C) 2010 Slimlogic Ltd.
7ddee627cSLiam Girdwood  * Copyright (C) 2010 Texas Instruments Inc.
8ddee627cSLiam Girdwood  *
9ddee627cSLiam Girdwood  * Authors: Liam Girdwood <lrg@ti.com>
10ddee627cSLiam Girdwood  *          Mark Brown <broonie@opensource.wolfsonmicro.com>
11ddee627cSLiam Girdwood  *
12ddee627cSLiam Girdwood  *  This program is free software; you can redistribute  it and/or modify it
13ddee627cSLiam Girdwood  *  under  the terms of  the GNU General  Public License as published by the
14ddee627cSLiam Girdwood  *  Free Software Foundation;  either version 2 of the  License, or (at your
15ddee627cSLiam Girdwood  *  option) any later version.
16ddee627cSLiam Girdwood  *
17ddee627cSLiam Girdwood  */
18ddee627cSLiam Girdwood 
19ddee627cSLiam Girdwood #include <linux/kernel.h>
20ddee627cSLiam Girdwood #include <linux/init.h>
21ddee627cSLiam Girdwood #include <linux/delay.h>
22d6652ef8SMark Brown #include <linux/pm_runtime.h>
23ddee627cSLiam Girdwood #include <linux/slab.h>
24ddee627cSLiam Girdwood #include <linux/workqueue.h>
25*01d7584cSLiam Girdwood #include <linux/export.h>
26ddee627cSLiam Girdwood #include <sound/core.h>
27ddee627cSLiam Girdwood #include <sound/pcm.h>
28ddee627cSLiam Girdwood #include <sound/pcm_params.h>
29ddee627cSLiam Girdwood #include <sound/soc.h>
30*01d7584cSLiam Girdwood #include <sound/soc-dpcm.h>
31ddee627cSLiam Girdwood #include <sound/initval.h>
32ddee627cSLiam Girdwood 
33*01d7584cSLiam Girdwood #define DPCM_MAX_BE_USERS	8
34*01d7584cSLiam Girdwood 
35*01d7584cSLiam Girdwood /* DPCM stream event, send event to FE and all active BEs. */
36*01d7584cSLiam Girdwood static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
37*01d7584cSLiam Girdwood 	int event)
38*01d7584cSLiam Girdwood {
39*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
40*01d7584cSLiam Girdwood 
41*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[dir].be_clients, list_be) {
42*01d7584cSLiam Girdwood 
43*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
44*01d7584cSLiam Girdwood 
45*01d7584cSLiam Girdwood 		dev_dbg(be->dev, "pm: BE %s event %d dir %d\n",
46*01d7584cSLiam Girdwood 				be->dai_link->name, event, dir);
47*01d7584cSLiam Girdwood 
48*01d7584cSLiam Girdwood 		snd_soc_dapm_stream_event(be, dir, event);
49*01d7584cSLiam Girdwood 	}
50*01d7584cSLiam Girdwood 
51*01d7584cSLiam Girdwood 	snd_soc_dapm_stream_event(fe, dir, event);
52*01d7584cSLiam Girdwood 
53*01d7584cSLiam Girdwood 	return 0;
54*01d7584cSLiam Girdwood }
55*01d7584cSLiam Girdwood 
5617841020SDong Aisheng static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
5717841020SDong Aisheng 					struct snd_soc_dai *soc_dai)
58ddee627cSLiam Girdwood {
59ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
60ddee627cSLiam Girdwood 	int ret;
61ddee627cSLiam Girdwood 
6217841020SDong Aisheng 	if (!soc_dai->driver->symmetric_rates &&
63ddee627cSLiam Girdwood 	    !rtd->dai_link->symmetric_rates)
64ddee627cSLiam Girdwood 		return 0;
65ddee627cSLiam Girdwood 
66ddee627cSLiam Girdwood 	/* This can happen if multiple streams are starting simultaneously -
67ddee627cSLiam Girdwood 	 * the second can need to get its constraints before the first has
68ddee627cSLiam Girdwood 	 * picked a rate.  Complain and allow the application to carry on.
69ddee627cSLiam Girdwood 	 */
7017841020SDong Aisheng 	if (!soc_dai->rate) {
7117841020SDong Aisheng 		dev_warn(soc_dai->dev,
72ddee627cSLiam Girdwood 			 "Not enforcing symmetric_rates due to race\n");
73ddee627cSLiam Girdwood 		return 0;
74ddee627cSLiam Girdwood 	}
75ddee627cSLiam Girdwood 
7617841020SDong Aisheng 	dev_dbg(soc_dai->dev, "Symmetry forces %dHz rate\n", soc_dai->rate);
77ddee627cSLiam Girdwood 
78ddee627cSLiam Girdwood 	ret = snd_pcm_hw_constraint_minmax(substream->runtime,
79ddee627cSLiam Girdwood 					   SNDRV_PCM_HW_PARAM_RATE,
8017841020SDong Aisheng 					   soc_dai->rate, soc_dai->rate);
81ddee627cSLiam Girdwood 	if (ret < 0) {
8217841020SDong Aisheng 		dev_err(soc_dai->dev,
83ddee627cSLiam Girdwood 			"Unable to apply rate symmetry constraint: %d\n", ret);
84ddee627cSLiam Girdwood 		return ret;
85ddee627cSLiam Girdwood 	}
86ddee627cSLiam Girdwood 
87ddee627cSLiam Girdwood 	return 0;
88ddee627cSLiam Girdwood }
89ddee627cSLiam Girdwood 
90ddee627cSLiam Girdwood /*
9158ba9b25SMark Brown  * List of sample sizes that might go over the bus for parameter
9258ba9b25SMark Brown  * application.  There ought to be a wildcard sample size for things
9358ba9b25SMark Brown  * like the DAC/ADC resolution to use but there isn't right now.
9458ba9b25SMark Brown  */
9558ba9b25SMark Brown static int sample_sizes[] = {
9688e33954SPeter Ujfalusi 	24, 32,
9758ba9b25SMark Brown };
9858ba9b25SMark Brown 
9958ba9b25SMark Brown static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
10058ba9b25SMark Brown 			      struct snd_soc_dai *dai)
10158ba9b25SMark Brown {
10258ba9b25SMark Brown 	int ret, i, bits;
10358ba9b25SMark Brown 
10458ba9b25SMark Brown 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
10558ba9b25SMark Brown 		bits = dai->driver->playback.sig_bits;
10658ba9b25SMark Brown 	else
10758ba9b25SMark Brown 		bits = dai->driver->capture.sig_bits;
10858ba9b25SMark Brown 
10958ba9b25SMark Brown 	if (!bits)
11058ba9b25SMark Brown 		return;
11158ba9b25SMark Brown 
11258ba9b25SMark Brown 	for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) {
113278047fdSMark Brown 		if (bits >= sample_sizes[i])
114278047fdSMark Brown 			continue;
115278047fdSMark Brown 
116278047fdSMark Brown 		ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0,
117278047fdSMark Brown 						   sample_sizes[i], bits);
11858ba9b25SMark Brown 		if (ret != 0)
11958ba9b25SMark Brown 			dev_warn(dai->dev,
12058ba9b25SMark Brown 				 "Failed to set MSB %d/%d: %d\n",
12158ba9b25SMark Brown 				 bits, sample_sizes[i], ret);
12258ba9b25SMark Brown 	}
12358ba9b25SMark Brown }
12458ba9b25SMark Brown 
12558ba9b25SMark Brown /*
126ddee627cSLiam Girdwood  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
127ddee627cSLiam Girdwood  * then initialized and any private data can be allocated. This also calls
128ddee627cSLiam Girdwood  * startup for the cpu DAI, platform, machine and codec DAI.
129ddee627cSLiam Girdwood  */
130ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream)
131ddee627cSLiam Girdwood {
132ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
133ddee627cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
134ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
135ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
136ddee627cSLiam Girdwood 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
137ddee627cSLiam Girdwood 	struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
138ddee627cSLiam Girdwood 	struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
139ddee627cSLiam Girdwood 	int ret = 0;
140ddee627cSLiam Girdwood 
141d6652ef8SMark Brown 	pm_runtime_get_sync(cpu_dai->dev);
142d6652ef8SMark Brown 	pm_runtime_get_sync(codec_dai->dev);
143d6652ef8SMark Brown 	pm_runtime_get_sync(platform->dev);
144d6652ef8SMark Brown 
145b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
146ddee627cSLiam Girdwood 
147ddee627cSLiam Girdwood 	/* startup the audio subsystem */
148ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->startup) {
149ddee627cSLiam Girdwood 		ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
150ddee627cSLiam Girdwood 		if (ret < 0) {
15125bfe662SMark Brown 			dev_err(cpu_dai->dev, "can't open interface %s: %d\n",
15225bfe662SMark Brown 				cpu_dai->name, ret);
153ddee627cSLiam Girdwood 			goto out;
154ddee627cSLiam Girdwood 		}
155ddee627cSLiam Girdwood 	}
156ddee627cSLiam Girdwood 
157ddee627cSLiam Girdwood 	if (platform->driver->ops && platform->driver->ops->open) {
158ddee627cSLiam Girdwood 		ret = platform->driver->ops->open(substream);
159ddee627cSLiam Girdwood 		if (ret < 0) {
16025bfe662SMark Brown 			dev_err(platform->dev, "can't open platform %s: %d\n",
16125bfe662SMark Brown 				platform->name, ret);
162ddee627cSLiam Girdwood 			goto platform_err;
163ddee627cSLiam Girdwood 		}
164ddee627cSLiam Girdwood 	}
165ddee627cSLiam Girdwood 
166ddee627cSLiam Girdwood 	if (codec_dai->driver->ops->startup) {
167ddee627cSLiam Girdwood 		ret = codec_dai->driver->ops->startup(substream, codec_dai);
168ddee627cSLiam Girdwood 		if (ret < 0) {
16925bfe662SMark Brown 			dev_err(codec_dai->dev, "can't open codec %s: %d\n",
17025bfe662SMark Brown 				codec_dai->name, ret);
171ddee627cSLiam Girdwood 			goto codec_dai_err;
172ddee627cSLiam Girdwood 		}
173ddee627cSLiam Girdwood 	}
174ddee627cSLiam Girdwood 
175ddee627cSLiam Girdwood 	if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
176ddee627cSLiam Girdwood 		ret = rtd->dai_link->ops->startup(substream);
177ddee627cSLiam Girdwood 		if (ret < 0) {
17825bfe662SMark Brown 			pr_err("asoc: %s startup failed: %d\n",
17925bfe662SMark Brown 			       rtd->dai_link->name, ret);
180ddee627cSLiam Girdwood 			goto machine_err;
181ddee627cSLiam Girdwood 		}
182ddee627cSLiam Girdwood 	}
183ddee627cSLiam Girdwood 
184*01d7584cSLiam Girdwood 	/* Dynamic PCM DAI links compat checks use dynamic capabilities */
185*01d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
186*01d7584cSLiam Girdwood 		goto dynamic;
187*01d7584cSLiam Girdwood 
188ddee627cSLiam Girdwood 	/* Check that the codec and cpu DAIs are compatible */
189ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
190ddee627cSLiam Girdwood 		runtime->hw.rate_min =
191ddee627cSLiam Girdwood 			max(codec_dai_drv->playback.rate_min,
192ddee627cSLiam Girdwood 			    cpu_dai_drv->playback.rate_min);
193ddee627cSLiam Girdwood 		runtime->hw.rate_max =
194ddee627cSLiam Girdwood 			min(codec_dai_drv->playback.rate_max,
195ddee627cSLiam Girdwood 			    cpu_dai_drv->playback.rate_max);
196ddee627cSLiam Girdwood 		runtime->hw.channels_min =
197ddee627cSLiam Girdwood 			max(codec_dai_drv->playback.channels_min,
198ddee627cSLiam Girdwood 				cpu_dai_drv->playback.channels_min);
199ddee627cSLiam Girdwood 		runtime->hw.channels_max =
200ddee627cSLiam Girdwood 			min(codec_dai_drv->playback.channels_max,
201ddee627cSLiam Girdwood 				cpu_dai_drv->playback.channels_max);
202ddee627cSLiam Girdwood 		runtime->hw.formats =
203ddee627cSLiam Girdwood 			codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
204ddee627cSLiam Girdwood 		runtime->hw.rates =
205ddee627cSLiam Girdwood 			codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
206ddee627cSLiam Girdwood 		if (codec_dai_drv->playback.rates
207ddee627cSLiam Girdwood 			   & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
208ddee627cSLiam Girdwood 			runtime->hw.rates |= cpu_dai_drv->playback.rates;
209ddee627cSLiam Girdwood 		if (cpu_dai_drv->playback.rates
210ddee627cSLiam Girdwood 			   & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
211ddee627cSLiam Girdwood 			runtime->hw.rates |= codec_dai_drv->playback.rates;
212ddee627cSLiam Girdwood 	} else {
213ddee627cSLiam Girdwood 		runtime->hw.rate_min =
214ddee627cSLiam Girdwood 			max(codec_dai_drv->capture.rate_min,
215ddee627cSLiam Girdwood 			    cpu_dai_drv->capture.rate_min);
216ddee627cSLiam Girdwood 		runtime->hw.rate_max =
217ddee627cSLiam Girdwood 			min(codec_dai_drv->capture.rate_max,
218ddee627cSLiam Girdwood 			    cpu_dai_drv->capture.rate_max);
219ddee627cSLiam Girdwood 		runtime->hw.channels_min =
220ddee627cSLiam Girdwood 			max(codec_dai_drv->capture.channels_min,
221ddee627cSLiam Girdwood 				cpu_dai_drv->capture.channels_min);
222ddee627cSLiam Girdwood 		runtime->hw.channels_max =
223ddee627cSLiam Girdwood 			min(codec_dai_drv->capture.channels_max,
224ddee627cSLiam Girdwood 				cpu_dai_drv->capture.channels_max);
225ddee627cSLiam Girdwood 		runtime->hw.formats =
226ddee627cSLiam Girdwood 			codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
227ddee627cSLiam Girdwood 		runtime->hw.rates =
228ddee627cSLiam Girdwood 			codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
229ddee627cSLiam Girdwood 		if (codec_dai_drv->capture.rates
230ddee627cSLiam Girdwood 			   & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
231ddee627cSLiam Girdwood 			runtime->hw.rates |= cpu_dai_drv->capture.rates;
232ddee627cSLiam Girdwood 		if (cpu_dai_drv->capture.rates
233ddee627cSLiam Girdwood 			   & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
234ddee627cSLiam Girdwood 			runtime->hw.rates |= codec_dai_drv->capture.rates;
235ddee627cSLiam Girdwood 	}
236ddee627cSLiam Girdwood 
237ddee627cSLiam Girdwood 	ret = -EINVAL;
238ddee627cSLiam Girdwood 	snd_pcm_limit_hw_rates(runtime);
239ddee627cSLiam Girdwood 	if (!runtime->hw.rates) {
240ddee627cSLiam Girdwood 		printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
241ddee627cSLiam Girdwood 			codec_dai->name, cpu_dai->name);
242ddee627cSLiam Girdwood 		goto config_err;
243ddee627cSLiam Girdwood 	}
244ddee627cSLiam Girdwood 	if (!runtime->hw.formats) {
245ddee627cSLiam Girdwood 		printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
246ddee627cSLiam Girdwood 			codec_dai->name, cpu_dai->name);
247ddee627cSLiam Girdwood 		goto config_err;
248ddee627cSLiam Girdwood 	}
249ddee627cSLiam Girdwood 	if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
250ddee627cSLiam Girdwood 	    runtime->hw.channels_min > runtime->hw.channels_max) {
251ddee627cSLiam Girdwood 		printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
252ddee627cSLiam Girdwood 				codec_dai->name, cpu_dai->name);
253ddee627cSLiam Girdwood 		goto config_err;
254ddee627cSLiam Girdwood 	}
255ddee627cSLiam Girdwood 
25658ba9b25SMark Brown 	soc_pcm_apply_msb(substream, codec_dai);
25758ba9b25SMark Brown 	soc_pcm_apply_msb(substream, cpu_dai);
25858ba9b25SMark Brown 
259ddee627cSLiam Girdwood 	/* Symmetry only applies if we've already got an active stream. */
26017841020SDong Aisheng 	if (cpu_dai->active) {
26117841020SDong Aisheng 		ret = soc_pcm_apply_symmetry(substream, cpu_dai);
26217841020SDong Aisheng 		if (ret != 0)
26317841020SDong Aisheng 			goto config_err;
26417841020SDong Aisheng 	}
26517841020SDong Aisheng 
26617841020SDong Aisheng 	if (codec_dai->active) {
26717841020SDong Aisheng 		ret = soc_pcm_apply_symmetry(substream, codec_dai);
268ddee627cSLiam Girdwood 		if (ret != 0)
269ddee627cSLiam Girdwood 			goto config_err;
270ddee627cSLiam Girdwood 	}
271ddee627cSLiam Girdwood 
272ddee627cSLiam Girdwood 	pr_debug("asoc: %s <-> %s info:\n",
273ddee627cSLiam Girdwood 			codec_dai->name, cpu_dai->name);
274ddee627cSLiam Girdwood 	pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
275ddee627cSLiam Girdwood 	pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
276ddee627cSLiam Girdwood 		 runtime->hw.channels_max);
277ddee627cSLiam Girdwood 	pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
278ddee627cSLiam Girdwood 		 runtime->hw.rate_max);
279ddee627cSLiam Girdwood 
280*01d7584cSLiam Girdwood dynamic:
281ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
282ddee627cSLiam Girdwood 		cpu_dai->playback_active++;
283ddee627cSLiam Girdwood 		codec_dai->playback_active++;
284ddee627cSLiam Girdwood 	} else {
285ddee627cSLiam Girdwood 		cpu_dai->capture_active++;
286ddee627cSLiam Girdwood 		codec_dai->capture_active++;
287ddee627cSLiam Girdwood 	}
288ddee627cSLiam Girdwood 	cpu_dai->active++;
289ddee627cSLiam Girdwood 	codec_dai->active++;
290ddee627cSLiam Girdwood 	rtd->codec->active++;
291b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
292ddee627cSLiam Girdwood 	return 0;
293ddee627cSLiam Girdwood 
294ddee627cSLiam Girdwood config_err:
295ddee627cSLiam Girdwood 	if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
296ddee627cSLiam Girdwood 		rtd->dai_link->ops->shutdown(substream);
297ddee627cSLiam Girdwood 
298ddee627cSLiam Girdwood machine_err:
299ddee627cSLiam Girdwood 	if (codec_dai->driver->ops->shutdown)
300ddee627cSLiam Girdwood 		codec_dai->driver->ops->shutdown(substream, codec_dai);
301ddee627cSLiam Girdwood 
302ddee627cSLiam Girdwood codec_dai_err:
303ddee627cSLiam Girdwood 	if (platform->driver->ops && platform->driver->ops->close)
304ddee627cSLiam Girdwood 		platform->driver->ops->close(substream);
305ddee627cSLiam Girdwood 
306ddee627cSLiam Girdwood platform_err:
307ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->shutdown)
308ddee627cSLiam Girdwood 		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
309ddee627cSLiam Girdwood out:
310b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
311d6652ef8SMark Brown 
312d6652ef8SMark Brown 	pm_runtime_put(platform->dev);
313d6652ef8SMark Brown 	pm_runtime_put(codec_dai->dev);
314d6652ef8SMark Brown 	pm_runtime_put(cpu_dai->dev);
315d6652ef8SMark Brown 
316ddee627cSLiam Girdwood 	return ret;
317ddee627cSLiam Girdwood }
318ddee627cSLiam Girdwood 
319ddee627cSLiam Girdwood /*
320ddee627cSLiam Girdwood  * Power down the audio subsystem pmdown_time msecs after close is called.
321ddee627cSLiam Girdwood  * This is to ensure there are no pops or clicks in between any music tracks
322ddee627cSLiam Girdwood  * due to DAPM power cycling.
323ddee627cSLiam Girdwood  */
324ddee627cSLiam Girdwood static void close_delayed_work(struct work_struct *work)
325ddee627cSLiam Girdwood {
326ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd =
327ddee627cSLiam Girdwood 			container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
328ddee627cSLiam Girdwood 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
329ddee627cSLiam Girdwood 
330b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
331ddee627cSLiam Girdwood 
332ddee627cSLiam Girdwood 	pr_debug("pop wq checking: %s status: %s waiting: %s\n",
333ddee627cSLiam Girdwood 		 codec_dai->driver->playback.stream_name,
334ddee627cSLiam Girdwood 		 codec_dai->playback_active ? "active" : "inactive",
335ddee627cSLiam Girdwood 		 codec_dai->pop_wait ? "yes" : "no");
336ddee627cSLiam Girdwood 
337ddee627cSLiam Girdwood 	/* are we waiting on this codec DAI stream */
338ddee627cSLiam Girdwood 	if (codec_dai->pop_wait == 1) {
339ddee627cSLiam Girdwood 		codec_dai->pop_wait = 0;
3407bd3a6f3SMark Brown 		snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
341d9b0951bSLiam Girdwood 					  SND_SOC_DAPM_STREAM_STOP);
342ddee627cSLiam Girdwood 	}
343ddee627cSLiam Girdwood 
344b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
345ddee627cSLiam Girdwood }
346ddee627cSLiam Girdwood 
347ddee627cSLiam Girdwood /*
348ddee627cSLiam Girdwood  * Called by ALSA when a PCM substream is closed. Private data can be
349ddee627cSLiam Girdwood  * freed here. The cpu DAI, codec DAI, machine and platform are also
350ddee627cSLiam Girdwood  * shutdown.
351ddee627cSLiam Girdwood  */
35291d5e6b4SLiam Girdwood static int soc_pcm_close(struct snd_pcm_substream *substream)
353ddee627cSLiam Girdwood {
354ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
355ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
356ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
357ddee627cSLiam Girdwood 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
358ddee627cSLiam Girdwood 	struct snd_soc_codec *codec = rtd->codec;
359ddee627cSLiam Girdwood 
360b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
361ddee627cSLiam Girdwood 
362ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
363ddee627cSLiam Girdwood 		cpu_dai->playback_active--;
364ddee627cSLiam Girdwood 		codec_dai->playback_active--;
365ddee627cSLiam Girdwood 	} else {
366ddee627cSLiam Girdwood 		cpu_dai->capture_active--;
367ddee627cSLiam Girdwood 		codec_dai->capture_active--;
368ddee627cSLiam Girdwood 	}
369ddee627cSLiam Girdwood 
370ddee627cSLiam Girdwood 	cpu_dai->active--;
371ddee627cSLiam Girdwood 	codec_dai->active--;
372ddee627cSLiam Girdwood 	codec->active--;
373ddee627cSLiam Girdwood 
37417841020SDong Aisheng 	/* clear the corresponding DAIs rate when inactive */
37517841020SDong Aisheng 	if (!cpu_dai->active)
37617841020SDong Aisheng 		cpu_dai->rate = 0;
37717841020SDong Aisheng 
37817841020SDong Aisheng 	if (!codec_dai->active)
37917841020SDong Aisheng 		codec_dai->rate = 0;
38025b76791SSascha Hauer 
381ddee627cSLiam Girdwood 	/* Muting the DAC suppresses artifacts caused during digital
382ddee627cSLiam Girdwood 	 * shutdown, for example from stopping clocks.
383ddee627cSLiam Girdwood 	 */
384ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
385ddee627cSLiam Girdwood 		snd_soc_dai_digital_mute(codec_dai, 1);
386ddee627cSLiam Girdwood 
387ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->shutdown)
388ddee627cSLiam Girdwood 		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
389ddee627cSLiam Girdwood 
390ddee627cSLiam Girdwood 	if (codec_dai->driver->ops->shutdown)
391ddee627cSLiam Girdwood 		codec_dai->driver->ops->shutdown(substream, codec_dai);
392ddee627cSLiam Girdwood 
393ddee627cSLiam Girdwood 	if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
394ddee627cSLiam Girdwood 		rtd->dai_link->ops->shutdown(substream);
395ddee627cSLiam Girdwood 
396ddee627cSLiam Girdwood 	if (platform->driver->ops && platform->driver->ops->close)
397ddee627cSLiam Girdwood 		platform->driver->ops->close(substream);
398ddee627cSLiam Girdwood 	cpu_dai->runtime = NULL;
399ddee627cSLiam Girdwood 
400ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
401b5d1d036SMark Brown 		if (!rtd->pmdown_time || codec->ignore_pmdown_time ||
4025b6247abSMark Brown 		    rtd->dai_link->ignore_pmdown_time) {
4031d69c5c5SPeter Ujfalusi 			/* powered down playback stream now */
4041d69c5c5SPeter Ujfalusi 			snd_soc_dapm_stream_event(rtd,
4057bd3a6f3SMark Brown 						  SNDRV_PCM_STREAM_PLAYBACK,
4061d69c5c5SPeter Ujfalusi 						  SND_SOC_DAPM_STREAM_STOP);
4071d69c5c5SPeter Ujfalusi 		} else {
408ddee627cSLiam Girdwood 			/* start delayed pop wq here for playback streams */
409ddee627cSLiam Girdwood 			codec_dai->pop_wait = 1;
410ddee627cSLiam Girdwood 			schedule_delayed_work(&rtd->delayed_work,
411ddee627cSLiam Girdwood 				msecs_to_jiffies(rtd->pmdown_time));
4121d69c5c5SPeter Ujfalusi 		}
413ddee627cSLiam Girdwood 	} else {
414ddee627cSLiam Girdwood 		/* capture streams can be powered down now */
4157bd3a6f3SMark Brown 		snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
416d9b0951bSLiam Girdwood 					  SND_SOC_DAPM_STREAM_STOP);
417ddee627cSLiam Girdwood 	}
418ddee627cSLiam Girdwood 
419b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
420d6652ef8SMark Brown 
421d6652ef8SMark Brown 	pm_runtime_put(platform->dev);
422d6652ef8SMark Brown 	pm_runtime_put(codec_dai->dev);
423d6652ef8SMark Brown 	pm_runtime_put(cpu_dai->dev);
424d6652ef8SMark Brown 
425ddee627cSLiam Girdwood 	return 0;
426ddee627cSLiam Girdwood }
427ddee627cSLiam Girdwood 
428ddee627cSLiam Girdwood /*
429ddee627cSLiam Girdwood  * Called by ALSA when the PCM substream is prepared, can set format, sample
430ddee627cSLiam Girdwood  * rate, etc.  This function is non atomic and can be called multiple times,
431ddee627cSLiam Girdwood  * it can refer to the runtime info.
432ddee627cSLiam Girdwood  */
433ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream)
434ddee627cSLiam Girdwood {
435ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
436ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
437ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
438ddee627cSLiam Girdwood 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
439ddee627cSLiam Girdwood 	int ret = 0;
440ddee627cSLiam Girdwood 
441b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
442ddee627cSLiam Girdwood 
443ddee627cSLiam Girdwood 	if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
444ddee627cSLiam Girdwood 		ret = rtd->dai_link->ops->prepare(substream);
445ddee627cSLiam Girdwood 		if (ret < 0) {
44625bfe662SMark Brown 			pr_err("asoc: machine prepare error: %d\n", ret);
447ddee627cSLiam Girdwood 			goto out;
448ddee627cSLiam Girdwood 		}
449ddee627cSLiam Girdwood 	}
450ddee627cSLiam Girdwood 
451ddee627cSLiam Girdwood 	if (platform->driver->ops && platform->driver->ops->prepare) {
452ddee627cSLiam Girdwood 		ret = platform->driver->ops->prepare(substream);
453ddee627cSLiam Girdwood 		if (ret < 0) {
45425bfe662SMark Brown 			dev_err(platform->dev, "platform prepare error: %d\n",
45525bfe662SMark Brown 				ret);
456ddee627cSLiam Girdwood 			goto out;
457ddee627cSLiam Girdwood 		}
458ddee627cSLiam Girdwood 	}
459ddee627cSLiam Girdwood 
460ddee627cSLiam Girdwood 	if (codec_dai->driver->ops->prepare) {
461ddee627cSLiam Girdwood 		ret = codec_dai->driver->ops->prepare(substream, codec_dai);
462ddee627cSLiam Girdwood 		if (ret < 0) {
46325bfe662SMark Brown 			dev_err(codec_dai->dev, "DAI prepare error: %d\n",
46425bfe662SMark Brown 				ret);
465ddee627cSLiam Girdwood 			goto out;
466ddee627cSLiam Girdwood 		}
467ddee627cSLiam Girdwood 	}
468ddee627cSLiam Girdwood 
469ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->prepare) {
470ddee627cSLiam Girdwood 		ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
471ddee627cSLiam Girdwood 		if (ret < 0) {
47225bfe662SMark Brown 			dev_err(cpu_dai->dev, "DAI prepare error: %d\n",
47325bfe662SMark Brown 				ret);
474ddee627cSLiam Girdwood 			goto out;
475ddee627cSLiam Girdwood 		}
476ddee627cSLiam Girdwood 	}
477ddee627cSLiam Girdwood 
478ddee627cSLiam Girdwood 	/* cancel any delayed stream shutdown that is pending */
479ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
480ddee627cSLiam Girdwood 	    codec_dai->pop_wait) {
481ddee627cSLiam Girdwood 		codec_dai->pop_wait = 0;
482ddee627cSLiam Girdwood 		cancel_delayed_work(&rtd->delayed_work);
483ddee627cSLiam Girdwood 	}
484ddee627cSLiam Girdwood 
485d9b0951bSLiam Girdwood 	snd_soc_dapm_stream_event(rtd, substream->stream,
486ddee627cSLiam Girdwood 			SND_SOC_DAPM_STREAM_START);
487ddee627cSLiam Girdwood 
488ddee627cSLiam Girdwood 	snd_soc_dai_digital_mute(codec_dai, 0);
489ddee627cSLiam Girdwood 
490ddee627cSLiam Girdwood out:
491b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
492ddee627cSLiam Girdwood 	return ret;
493ddee627cSLiam Girdwood }
494ddee627cSLiam Girdwood 
495ddee627cSLiam Girdwood /*
496ddee627cSLiam Girdwood  * Called by ALSA when the hardware params are set by application. This
497ddee627cSLiam Girdwood  * function can also be called multiple times and can allocate buffers
498ddee627cSLiam Girdwood  * (using snd_pcm_lib_* ). It's non-atomic.
499ddee627cSLiam Girdwood  */
500ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
501ddee627cSLiam Girdwood 				struct snd_pcm_hw_params *params)
502ddee627cSLiam Girdwood {
503ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
504ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
505ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
506ddee627cSLiam Girdwood 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
507ddee627cSLiam Girdwood 	int ret = 0;
508ddee627cSLiam Girdwood 
509b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
510ddee627cSLiam Girdwood 
511ddee627cSLiam Girdwood 	if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
512ddee627cSLiam Girdwood 		ret = rtd->dai_link->ops->hw_params(substream, params);
513ddee627cSLiam Girdwood 		if (ret < 0) {
51425bfe662SMark Brown 			pr_err("asoc: machine hw_params failed: %d\n", ret);
515ddee627cSLiam Girdwood 			goto out;
516ddee627cSLiam Girdwood 		}
517ddee627cSLiam Girdwood 	}
518ddee627cSLiam Girdwood 
519ddee627cSLiam Girdwood 	if (codec_dai->driver->ops->hw_params) {
520ddee627cSLiam Girdwood 		ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
521ddee627cSLiam Girdwood 		if (ret < 0) {
52225bfe662SMark Brown 			dev_err(codec_dai->dev, "can't set %s hw params: %d\n",
52325bfe662SMark Brown 				codec_dai->name, ret);
524ddee627cSLiam Girdwood 			goto codec_err;
525ddee627cSLiam Girdwood 		}
526ddee627cSLiam Girdwood 	}
527ddee627cSLiam Girdwood 
528ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->hw_params) {
529ddee627cSLiam Girdwood 		ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
530ddee627cSLiam Girdwood 		if (ret < 0) {
53125bfe662SMark Brown 			dev_err(cpu_dai->dev, "%s hw params failed: %d\n",
53225bfe662SMark Brown 				cpu_dai->name, ret);
533ddee627cSLiam Girdwood 			goto interface_err;
534ddee627cSLiam Girdwood 		}
535ddee627cSLiam Girdwood 	}
536ddee627cSLiam Girdwood 
537ddee627cSLiam Girdwood 	if (platform->driver->ops && platform->driver->ops->hw_params) {
538ddee627cSLiam Girdwood 		ret = platform->driver->ops->hw_params(substream, params);
539ddee627cSLiam Girdwood 		if (ret < 0) {
54025bfe662SMark Brown 			dev_err(platform->dev, "%s hw params failed: %d\n",
54125bfe662SMark Brown 			       platform->name, ret);
542ddee627cSLiam Girdwood 			goto platform_err;
543ddee627cSLiam Girdwood 		}
544ddee627cSLiam Girdwood 	}
545ddee627cSLiam Girdwood 
54617841020SDong Aisheng 	/* store the rate for each DAIs */
54717841020SDong Aisheng 	cpu_dai->rate = params_rate(params);
54817841020SDong Aisheng 	codec_dai->rate = params_rate(params);
549ddee627cSLiam Girdwood 
550ddee627cSLiam Girdwood out:
551b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
552ddee627cSLiam Girdwood 	return ret;
553ddee627cSLiam Girdwood 
554ddee627cSLiam Girdwood platform_err:
555ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->hw_free)
556ddee627cSLiam Girdwood 		cpu_dai->driver->ops->hw_free(substream, cpu_dai);
557ddee627cSLiam Girdwood 
558ddee627cSLiam Girdwood interface_err:
559ddee627cSLiam Girdwood 	if (codec_dai->driver->ops->hw_free)
560ddee627cSLiam Girdwood 		codec_dai->driver->ops->hw_free(substream, codec_dai);
561ddee627cSLiam Girdwood 
562ddee627cSLiam Girdwood codec_err:
563ddee627cSLiam Girdwood 	if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
564ddee627cSLiam Girdwood 		rtd->dai_link->ops->hw_free(substream);
565ddee627cSLiam Girdwood 
566b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
567ddee627cSLiam Girdwood 	return ret;
568ddee627cSLiam Girdwood }
569ddee627cSLiam Girdwood 
570ddee627cSLiam Girdwood /*
571ddee627cSLiam Girdwood  * Frees resources allocated by hw_params, can be called multiple times
572ddee627cSLiam Girdwood  */
573ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
574ddee627cSLiam Girdwood {
575ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
576ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
577ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
578ddee627cSLiam Girdwood 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
579ddee627cSLiam Girdwood 	struct snd_soc_codec *codec = rtd->codec;
580ddee627cSLiam Girdwood 
581b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
582ddee627cSLiam Girdwood 
583ddee627cSLiam Girdwood 	/* apply codec digital mute */
584ddee627cSLiam Girdwood 	if (!codec->active)
585ddee627cSLiam Girdwood 		snd_soc_dai_digital_mute(codec_dai, 1);
586ddee627cSLiam Girdwood 
587ddee627cSLiam Girdwood 	/* free any machine hw params */
588ddee627cSLiam Girdwood 	if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
589ddee627cSLiam Girdwood 		rtd->dai_link->ops->hw_free(substream);
590ddee627cSLiam Girdwood 
591ddee627cSLiam Girdwood 	/* free any DMA resources */
592ddee627cSLiam Girdwood 	if (platform->driver->ops && platform->driver->ops->hw_free)
593ddee627cSLiam Girdwood 		platform->driver->ops->hw_free(substream);
594ddee627cSLiam Girdwood 
595ddee627cSLiam Girdwood 	/* now free hw params for the DAIs  */
596ddee627cSLiam Girdwood 	if (codec_dai->driver->ops->hw_free)
597ddee627cSLiam Girdwood 		codec_dai->driver->ops->hw_free(substream, codec_dai);
598ddee627cSLiam Girdwood 
599ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->hw_free)
600ddee627cSLiam Girdwood 		cpu_dai->driver->ops->hw_free(substream, cpu_dai);
601ddee627cSLiam Girdwood 
602b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
603ddee627cSLiam Girdwood 	return 0;
604ddee627cSLiam Girdwood }
605ddee627cSLiam Girdwood 
606ddee627cSLiam Girdwood static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
607ddee627cSLiam Girdwood {
608ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
609ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
610ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
611ddee627cSLiam Girdwood 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
612ddee627cSLiam Girdwood 	int ret;
613ddee627cSLiam Girdwood 
614ddee627cSLiam Girdwood 	if (codec_dai->driver->ops->trigger) {
615ddee627cSLiam Girdwood 		ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
616ddee627cSLiam Girdwood 		if (ret < 0)
617ddee627cSLiam Girdwood 			return ret;
618ddee627cSLiam Girdwood 	}
619ddee627cSLiam Girdwood 
620ddee627cSLiam Girdwood 	if (platform->driver->ops && platform->driver->ops->trigger) {
621ddee627cSLiam Girdwood 		ret = platform->driver->ops->trigger(substream, cmd);
622ddee627cSLiam Girdwood 		if (ret < 0)
623ddee627cSLiam Girdwood 			return ret;
624ddee627cSLiam Girdwood 	}
625ddee627cSLiam Girdwood 
626ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->trigger) {
627ddee627cSLiam Girdwood 		ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
628ddee627cSLiam Girdwood 		if (ret < 0)
629ddee627cSLiam Girdwood 			return ret;
630ddee627cSLiam Girdwood 	}
631ddee627cSLiam Girdwood 	return 0;
632ddee627cSLiam Girdwood }
633ddee627cSLiam Girdwood 
634ddee627cSLiam Girdwood /*
635ddee627cSLiam Girdwood  * soc level wrapper for pointer callback
636ddee627cSLiam Girdwood  * If cpu_dai, codec_dai, platform driver has the delay callback, than
637ddee627cSLiam Girdwood  * the runtime->delay will be updated accordingly.
638ddee627cSLiam Girdwood  */
639ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
640ddee627cSLiam Girdwood {
641ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
642ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
643ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
644ddee627cSLiam Girdwood 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
645ddee627cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
646ddee627cSLiam Girdwood 	snd_pcm_uframes_t offset = 0;
647ddee627cSLiam Girdwood 	snd_pcm_sframes_t delay = 0;
648ddee627cSLiam Girdwood 
649ddee627cSLiam Girdwood 	if (platform->driver->ops && platform->driver->ops->pointer)
650ddee627cSLiam Girdwood 		offset = platform->driver->ops->pointer(substream);
651ddee627cSLiam Girdwood 
652ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->delay)
653ddee627cSLiam Girdwood 		delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
654ddee627cSLiam Girdwood 
655ddee627cSLiam Girdwood 	if (codec_dai->driver->ops->delay)
656ddee627cSLiam Girdwood 		delay += codec_dai->driver->ops->delay(substream, codec_dai);
657ddee627cSLiam Girdwood 
658ddee627cSLiam Girdwood 	if (platform->driver->delay)
659ddee627cSLiam Girdwood 		delay += platform->driver->delay(substream, codec_dai);
660ddee627cSLiam Girdwood 
661ddee627cSLiam Girdwood 	runtime->delay = delay;
662ddee627cSLiam Girdwood 
663ddee627cSLiam Girdwood 	return offset;
664ddee627cSLiam Girdwood }
665ddee627cSLiam Girdwood 
666*01d7584cSLiam Girdwood /* connect a FE and BE */
667*01d7584cSLiam Girdwood static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
668*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
669*01d7584cSLiam Girdwood {
670*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
671*01d7584cSLiam Girdwood 
672*01d7584cSLiam Girdwood 	/* only add new dpcms */
673*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
674*01d7584cSLiam Girdwood 		if (dpcm->be == be && dpcm->fe == fe)
675*01d7584cSLiam Girdwood 			return 0;
676*01d7584cSLiam Girdwood 	}
677*01d7584cSLiam Girdwood 
678*01d7584cSLiam Girdwood 	dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL);
679*01d7584cSLiam Girdwood 	if (!dpcm)
680*01d7584cSLiam Girdwood 		return -ENOMEM;
681*01d7584cSLiam Girdwood 
682*01d7584cSLiam Girdwood 	dpcm->be = be;
683*01d7584cSLiam Girdwood 	dpcm->fe = fe;
684*01d7584cSLiam Girdwood 	be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
685*01d7584cSLiam Girdwood 	dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
686*01d7584cSLiam Girdwood 	list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
687*01d7584cSLiam Girdwood 	list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
688*01d7584cSLiam Girdwood 
689*01d7584cSLiam Girdwood 	dev_dbg(fe->dev, "  connected new DPCM %s path %s %s %s\n",
690*01d7584cSLiam Girdwood 			stream ? "capture" : "playback",  fe->dai_link->name,
691*01d7584cSLiam Girdwood 			stream ? "<-" : "->", be->dai_link->name);
692*01d7584cSLiam Girdwood 
693*01d7584cSLiam Girdwood 	return 1;
694*01d7584cSLiam Girdwood }
695*01d7584cSLiam Girdwood 
696*01d7584cSLiam Girdwood /* reparent a BE onto another FE */
697*01d7584cSLiam Girdwood static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
698*01d7584cSLiam Girdwood 			struct snd_soc_pcm_runtime *be, int stream)
699*01d7584cSLiam Girdwood {
700*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
701*01d7584cSLiam Girdwood 	struct snd_pcm_substream *fe_substream, *be_substream;
702*01d7584cSLiam Girdwood 
703*01d7584cSLiam Girdwood 	/* reparent if BE is connected to other FEs */
704*01d7584cSLiam Girdwood 	if (!be->dpcm[stream].users)
705*01d7584cSLiam Girdwood 		return;
706*01d7584cSLiam Girdwood 
707*01d7584cSLiam Girdwood 	be_substream = snd_soc_dpcm_get_substream(be, stream);
708*01d7584cSLiam Girdwood 
709*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
710*01d7584cSLiam Girdwood 		if (dpcm->fe == fe)
711*01d7584cSLiam Girdwood 			continue;
712*01d7584cSLiam Girdwood 
713*01d7584cSLiam Girdwood 		dev_dbg(fe->dev, "  reparent %s path %s %s %s\n",
714*01d7584cSLiam Girdwood 			stream ? "capture" : "playback",
715*01d7584cSLiam Girdwood 			dpcm->fe->dai_link->name,
716*01d7584cSLiam Girdwood 			stream ? "<-" : "->", dpcm->be->dai_link->name);
717*01d7584cSLiam Girdwood 
718*01d7584cSLiam Girdwood 		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream);
719*01d7584cSLiam Girdwood 		be_substream->runtime = fe_substream->runtime;
720*01d7584cSLiam Girdwood 		break;
721*01d7584cSLiam Girdwood 	}
722*01d7584cSLiam Girdwood }
723*01d7584cSLiam Girdwood 
724*01d7584cSLiam Girdwood /* disconnect a BE and FE */
725*01d7584cSLiam Girdwood static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
726*01d7584cSLiam Girdwood {
727*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm, *d;
728*01d7584cSLiam Girdwood 
729*01d7584cSLiam Girdwood 	list_for_each_entry_safe(dpcm, d, &fe->dpcm[stream].be_clients, list_be) {
730*01d7584cSLiam Girdwood 		dev_dbg(fe->dev, "BE %s disconnect check for %s\n",
731*01d7584cSLiam Girdwood 				stream ? "capture" : "playback",
732*01d7584cSLiam Girdwood 				dpcm->be->dai_link->name);
733*01d7584cSLiam Girdwood 
734*01d7584cSLiam Girdwood 		if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE)
735*01d7584cSLiam Girdwood 			continue;
736*01d7584cSLiam Girdwood 
737*01d7584cSLiam Girdwood 		dev_dbg(fe->dev, "  freed DSP %s path %s %s %s\n",
738*01d7584cSLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name,
739*01d7584cSLiam Girdwood 			stream ? "<-" : "->", dpcm->be->dai_link->name);
740*01d7584cSLiam Girdwood 
741*01d7584cSLiam Girdwood 		/* BEs still alive need new FE */
742*01d7584cSLiam Girdwood 		dpcm_be_reparent(fe, dpcm->be, stream);
743*01d7584cSLiam Girdwood 
744*01d7584cSLiam Girdwood 		list_del(&dpcm->list_be);
745*01d7584cSLiam Girdwood 		list_del(&dpcm->list_fe);
746*01d7584cSLiam Girdwood 		kfree(dpcm);
747*01d7584cSLiam Girdwood 	}
748*01d7584cSLiam Girdwood }
749*01d7584cSLiam Girdwood 
750*01d7584cSLiam Girdwood /* get BE for DAI widget and stream */
751*01d7584cSLiam Girdwood static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
752*01d7584cSLiam Girdwood 		struct snd_soc_dapm_widget *widget, int stream)
753*01d7584cSLiam Girdwood {
754*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *be;
755*01d7584cSLiam Girdwood 	int i;
756*01d7584cSLiam Girdwood 
757*01d7584cSLiam Girdwood 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
758*01d7584cSLiam Girdwood 		for (i = 0; i < card->num_links; i++) {
759*01d7584cSLiam Girdwood 			be = &card->rtd[i];
760*01d7584cSLiam Girdwood 
761*01d7584cSLiam Girdwood 			if (be->cpu_dai->playback_widget == widget ||
762*01d7584cSLiam Girdwood 				be->codec_dai->playback_widget == widget)
763*01d7584cSLiam Girdwood 				return be;
764*01d7584cSLiam Girdwood 		}
765*01d7584cSLiam Girdwood 	} else {
766*01d7584cSLiam Girdwood 
767*01d7584cSLiam Girdwood 		for (i = 0; i < card->num_links; i++) {
768*01d7584cSLiam Girdwood 			be = &card->rtd[i];
769*01d7584cSLiam Girdwood 
770*01d7584cSLiam Girdwood 			if (be->cpu_dai->capture_widget == widget ||
771*01d7584cSLiam Girdwood 				be->codec_dai->capture_widget == widget)
772*01d7584cSLiam Girdwood 				return be;
773*01d7584cSLiam Girdwood 		}
774*01d7584cSLiam Girdwood 	}
775*01d7584cSLiam Girdwood 
776*01d7584cSLiam Girdwood 	dev_err(card->dev, "can't get %s BE for %s\n",
777*01d7584cSLiam Girdwood 		stream ? "capture" : "playback", widget->name);
778*01d7584cSLiam Girdwood 	return NULL;
779*01d7584cSLiam Girdwood }
780*01d7584cSLiam Girdwood 
781*01d7584cSLiam Girdwood static inline struct snd_soc_dapm_widget *
782*01d7584cSLiam Girdwood 	rtd_get_cpu_widget(struct snd_soc_pcm_runtime *rtd, int stream)
783*01d7584cSLiam Girdwood {
784*01d7584cSLiam Girdwood 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
785*01d7584cSLiam Girdwood 		return rtd->cpu_dai->playback_widget;
786*01d7584cSLiam Girdwood 	else
787*01d7584cSLiam Girdwood 		return rtd->cpu_dai->capture_widget;
788*01d7584cSLiam Girdwood }
789*01d7584cSLiam Girdwood 
790*01d7584cSLiam Girdwood static inline struct snd_soc_dapm_widget *
791*01d7584cSLiam Girdwood 	rtd_get_codec_widget(struct snd_soc_pcm_runtime *rtd, int stream)
792*01d7584cSLiam Girdwood {
793*01d7584cSLiam Girdwood 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
794*01d7584cSLiam Girdwood 		return rtd->codec_dai->playback_widget;
795*01d7584cSLiam Girdwood 	else
796*01d7584cSLiam Girdwood 		return rtd->codec_dai->capture_widget;
797*01d7584cSLiam Girdwood }
798*01d7584cSLiam Girdwood 
799*01d7584cSLiam Girdwood static int widget_in_list(struct snd_soc_dapm_widget_list *list,
800*01d7584cSLiam Girdwood 		struct snd_soc_dapm_widget *widget)
801*01d7584cSLiam Girdwood {
802*01d7584cSLiam Girdwood 	int i;
803*01d7584cSLiam Girdwood 
804*01d7584cSLiam Girdwood 	for (i = 0; i < list->num_widgets; i++) {
805*01d7584cSLiam Girdwood 		if (widget == list->widgets[i])
806*01d7584cSLiam Girdwood 			return 1;
807*01d7584cSLiam Girdwood 	}
808*01d7584cSLiam Girdwood 
809*01d7584cSLiam Girdwood 	return 0;
810*01d7584cSLiam Girdwood }
811*01d7584cSLiam Girdwood 
812*01d7584cSLiam Girdwood static int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
813*01d7584cSLiam Girdwood 	int stream, struct snd_soc_dapm_widget_list **list_)
814*01d7584cSLiam Girdwood {
815*01d7584cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
816*01d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list;
817*01d7584cSLiam Girdwood 	int paths;
818*01d7584cSLiam Girdwood 
819*01d7584cSLiam Girdwood 	list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) +
820*01d7584cSLiam Girdwood 			sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL);
821*01d7584cSLiam Girdwood 	if (list == NULL)
822*01d7584cSLiam Girdwood 		return -ENOMEM;
823*01d7584cSLiam Girdwood 
824*01d7584cSLiam Girdwood 	/* get number of valid DAI paths and their widgets */
825*01d7584cSLiam Girdwood 	paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list);
826*01d7584cSLiam Girdwood 
827*01d7584cSLiam Girdwood 	dev_dbg(fe->dev, "found %d audio %s paths\n", paths,
828*01d7584cSLiam Girdwood 			stream ? "capture" : "playback");
829*01d7584cSLiam Girdwood 
830*01d7584cSLiam Girdwood 	*list_ = list;
831*01d7584cSLiam Girdwood 	return paths;
832*01d7584cSLiam Girdwood }
833*01d7584cSLiam Girdwood 
834*01d7584cSLiam Girdwood static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list)
835*01d7584cSLiam Girdwood {
836*01d7584cSLiam Girdwood 	kfree(*list);
837*01d7584cSLiam Girdwood }
838*01d7584cSLiam Girdwood 
839*01d7584cSLiam Girdwood static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
840*01d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list **list_)
841*01d7584cSLiam Girdwood {
842*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
843*01d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list = *list_;
844*01d7584cSLiam Girdwood 	struct snd_soc_dapm_widget *widget;
845*01d7584cSLiam Girdwood 	int prune = 0;
846*01d7584cSLiam Girdwood 
847*01d7584cSLiam Girdwood 	/* Destroy any old FE <--> BE connections */
848*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
849*01d7584cSLiam Girdwood 
850*01d7584cSLiam Girdwood 		/* is there a valid CPU DAI widget for this BE */
851*01d7584cSLiam Girdwood 		widget = rtd_get_cpu_widget(dpcm->be, stream);
852*01d7584cSLiam Girdwood 
853*01d7584cSLiam Girdwood 		/* prune the BE if it's no longer in our active list */
854*01d7584cSLiam Girdwood 		if (widget && widget_in_list(list, widget))
855*01d7584cSLiam Girdwood 			continue;
856*01d7584cSLiam Girdwood 
857*01d7584cSLiam Girdwood 		/* is there a valid CODEC DAI widget for this BE */
858*01d7584cSLiam Girdwood 		widget = rtd_get_codec_widget(dpcm->be, stream);
859*01d7584cSLiam Girdwood 
860*01d7584cSLiam Girdwood 		/* prune the BE if it's no longer in our active list */
861*01d7584cSLiam Girdwood 		if (widget && widget_in_list(list, widget))
862*01d7584cSLiam Girdwood 			continue;
863*01d7584cSLiam Girdwood 
864*01d7584cSLiam Girdwood 		dev_dbg(fe->dev, "pruning %s BE %s for %s\n",
865*01d7584cSLiam Girdwood 			stream ? "capture" : "playback",
866*01d7584cSLiam Girdwood 			dpcm->be->dai_link->name, fe->dai_link->name);
867*01d7584cSLiam Girdwood 		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
868*01d7584cSLiam Girdwood 		dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
869*01d7584cSLiam Girdwood 		prune++;
870*01d7584cSLiam Girdwood 	}
871*01d7584cSLiam Girdwood 
872*01d7584cSLiam Girdwood 	dev_dbg(fe->dev, "found %d old BE paths for pruning\n", prune);
873*01d7584cSLiam Girdwood 	return prune;
874*01d7584cSLiam Girdwood }
875*01d7584cSLiam Girdwood 
876*01d7584cSLiam Girdwood static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
877*01d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list **list_)
878*01d7584cSLiam Girdwood {
879*01d7584cSLiam Girdwood 	struct snd_soc_card *card = fe->card;
880*01d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list = *list_;
881*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *be;
882*01d7584cSLiam Girdwood 	int i, new = 0, err;
883*01d7584cSLiam Girdwood 
884*01d7584cSLiam Girdwood 	/* Create any new FE <--> BE connections */
885*01d7584cSLiam Girdwood 	for (i = 0; i < list->num_widgets; i++) {
886*01d7584cSLiam Girdwood 
887*01d7584cSLiam Girdwood 		if (list->widgets[i]->id != snd_soc_dapm_dai)
888*01d7584cSLiam Girdwood 			continue;
889*01d7584cSLiam Girdwood 
890*01d7584cSLiam Girdwood 		/* is there a valid BE rtd for this widget */
891*01d7584cSLiam Girdwood 		be = dpcm_get_be(card, list->widgets[i], stream);
892*01d7584cSLiam Girdwood 		if (!be) {
893*01d7584cSLiam Girdwood 			dev_err(fe->dev, "no BE found for %s\n",
894*01d7584cSLiam Girdwood 					list->widgets[i]->name);
895*01d7584cSLiam Girdwood 			continue;
896*01d7584cSLiam Girdwood 		}
897*01d7584cSLiam Girdwood 
898*01d7584cSLiam Girdwood 		/* make sure BE is a real BE */
899*01d7584cSLiam Girdwood 		if (!be->dai_link->no_pcm)
900*01d7584cSLiam Girdwood 			continue;
901*01d7584cSLiam Girdwood 
902*01d7584cSLiam Girdwood 		/* don't connect if FE is not running */
903*01d7584cSLiam Girdwood 		if (!fe->dpcm[stream].runtime)
904*01d7584cSLiam Girdwood 			continue;
905*01d7584cSLiam Girdwood 
906*01d7584cSLiam Girdwood 		/* newly connected FE and BE */
907*01d7584cSLiam Girdwood 		err = dpcm_be_connect(fe, be, stream);
908*01d7584cSLiam Girdwood 		if (err < 0) {
909*01d7584cSLiam Girdwood 			dev_err(fe->dev, "can't connect %s\n",
910*01d7584cSLiam Girdwood 				list->widgets[i]->name);
911*01d7584cSLiam Girdwood 			break;
912*01d7584cSLiam Girdwood 		} else if (err == 0) /* already connected */
913*01d7584cSLiam Girdwood 			continue;
914*01d7584cSLiam Girdwood 
915*01d7584cSLiam Girdwood 		/* new */
916*01d7584cSLiam Girdwood 		be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
917*01d7584cSLiam Girdwood 		new++;
918*01d7584cSLiam Girdwood 	}
919*01d7584cSLiam Girdwood 
920*01d7584cSLiam Girdwood 	dev_dbg(fe->dev, "found %d new BE paths\n", new);
921*01d7584cSLiam Girdwood 	return new;
922*01d7584cSLiam Girdwood }
923*01d7584cSLiam Girdwood 
924*01d7584cSLiam Girdwood /*
925*01d7584cSLiam Girdwood  * Find the corresponding BE DAIs that source or sink audio to this
926*01d7584cSLiam Girdwood  * FE substream.
927*01d7584cSLiam Girdwood  */
928*01d7584cSLiam Girdwood static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
929*01d7584cSLiam Girdwood 	int stream, struct snd_soc_dapm_widget_list **list, int new)
930*01d7584cSLiam Girdwood {
931*01d7584cSLiam Girdwood 	if (new)
932*01d7584cSLiam Girdwood 		return dpcm_add_paths(fe, stream, list);
933*01d7584cSLiam Girdwood 	else
934*01d7584cSLiam Girdwood 		return dpcm_prune_paths(fe, stream, list);
935*01d7584cSLiam Girdwood }
936*01d7584cSLiam Girdwood 
937*01d7584cSLiam Girdwood static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
938*01d7584cSLiam Girdwood {
939*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
940*01d7584cSLiam Girdwood 
941*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
942*01d7584cSLiam Girdwood 		dpcm->be->dpcm[stream].runtime_update =
943*01d7584cSLiam Girdwood 						SND_SOC_DPCM_UPDATE_NO;
944*01d7584cSLiam Girdwood }
945*01d7584cSLiam Girdwood 
946*01d7584cSLiam Girdwood static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
947*01d7584cSLiam Girdwood 	int stream)
948*01d7584cSLiam Girdwood {
949*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
950*01d7584cSLiam Girdwood 
951*01d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
952*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
953*01d7584cSLiam Girdwood 
954*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
955*01d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
956*01d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
957*01d7584cSLiam Girdwood 
958*01d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
959*01d7584cSLiam Girdwood 			dev_err(be->dev, "no users %s at close - state %d\n",
960*01d7584cSLiam Girdwood 				stream ? "capture" : "playback",
961*01d7584cSLiam Girdwood 				be->dpcm[stream].state);
962*01d7584cSLiam Girdwood 
963*01d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
964*01d7584cSLiam Girdwood 			continue;
965*01d7584cSLiam Girdwood 
966*01d7584cSLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
967*01d7584cSLiam Girdwood 			continue;
968*01d7584cSLiam Girdwood 
969*01d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
970*01d7584cSLiam Girdwood 		be_substream->runtime = NULL;
971*01d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
972*01d7584cSLiam Girdwood 	}
973*01d7584cSLiam Girdwood }
974*01d7584cSLiam Girdwood 
975*01d7584cSLiam Girdwood static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
976*01d7584cSLiam Girdwood {
977*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
978*01d7584cSLiam Girdwood 	int err, count = 0;
979*01d7584cSLiam Girdwood 
980*01d7584cSLiam Girdwood 	/* only startup BE DAIs that are either sinks or sources to this FE DAI */
981*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
982*01d7584cSLiam Girdwood 
983*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
984*01d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
985*01d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
986*01d7584cSLiam Girdwood 
987*01d7584cSLiam Girdwood 		/* is this op for this BE ? */
988*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
989*01d7584cSLiam Girdwood 			continue;
990*01d7584cSLiam Girdwood 
991*01d7584cSLiam Girdwood 		/* first time the dpcm is open ? */
992*01d7584cSLiam Girdwood 		if (be->dpcm[stream].users == DPCM_MAX_BE_USERS)
993*01d7584cSLiam Girdwood 			dev_err(be->dev, "too many users %s at open %d\n",
994*01d7584cSLiam Girdwood 				stream ? "capture" : "playback",
995*01d7584cSLiam Girdwood 				be->dpcm[stream].state);
996*01d7584cSLiam Girdwood 
997*01d7584cSLiam Girdwood 		if (be->dpcm[stream].users++ != 0)
998*01d7584cSLiam Girdwood 			continue;
999*01d7584cSLiam Girdwood 
1000*01d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
1001*01d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
1002*01d7584cSLiam Girdwood 			continue;
1003*01d7584cSLiam Girdwood 
1004*01d7584cSLiam Girdwood 		dev_dbg(be->dev, "dpcm: open BE %s\n", be->dai_link->name);
1005*01d7584cSLiam Girdwood 
1006*01d7584cSLiam Girdwood 		be_substream->runtime = be->dpcm[stream].runtime;
1007*01d7584cSLiam Girdwood 		err = soc_pcm_open(be_substream);
1008*01d7584cSLiam Girdwood 		if (err < 0) {
1009*01d7584cSLiam Girdwood 			dev_err(be->dev, "BE open failed %d\n", err);
1010*01d7584cSLiam Girdwood 			be->dpcm[stream].users--;
1011*01d7584cSLiam Girdwood 			if (be->dpcm[stream].users < 0)
1012*01d7584cSLiam Girdwood 				dev_err(be->dev, "no users %s at unwind %d\n",
1013*01d7584cSLiam Girdwood 					stream ? "capture" : "playback",
1014*01d7584cSLiam Girdwood 					be->dpcm[stream].state);
1015*01d7584cSLiam Girdwood 
1016*01d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
1017*01d7584cSLiam Girdwood 			goto unwind;
1018*01d7584cSLiam Girdwood 		}
1019*01d7584cSLiam Girdwood 
1020*01d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
1021*01d7584cSLiam Girdwood 		count++;
1022*01d7584cSLiam Girdwood 	}
1023*01d7584cSLiam Girdwood 
1024*01d7584cSLiam Girdwood 	return count;
1025*01d7584cSLiam Girdwood 
1026*01d7584cSLiam Girdwood unwind:
1027*01d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
1028*01d7584cSLiam Girdwood 	list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
1029*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
1030*01d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
1031*01d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
1032*01d7584cSLiam Girdwood 
1033*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
1034*01d7584cSLiam Girdwood 			continue;
1035*01d7584cSLiam Girdwood 
1036*01d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1037*01d7584cSLiam Girdwood 			dev_err(be->dev, "no users %s at close %d\n",
1038*01d7584cSLiam Girdwood 				stream ? "capture" : "playback",
1039*01d7584cSLiam Girdwood 				be->dpcm[stream].state);
1040*01d7584cSLiam Girdwood 
1041*01d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
1042*01d7584cSLiam Girdwood 			continue;
1043*01d7584cSLiam Girdwood 
1044*01d7584cSLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
1045*01d7584cSLiam Girdwood 			continue;
1046*01d7584cSLiam Girdwood 
1047*01d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
1048*01d7584cSLiam Girdwood 		be_substream->runtime = NULL;
1049*01d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
1050*01d7584cSLiam Girdwood 	}
1051*01d7584cSLiam Girdwood 
1052*01d7584cSLiam Girdwood 	return err;
1053*01d7584cSLiam Girdwood }
1054*01d7584cSLiam Girdwood 
1055*01d7584cSLiam Girdwood void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
1056*01d7584cSLiam Girdwood {
1057*01d7584cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
1058*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1059*01d7584cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
1060*01d7584cSLiam Girdwood 	struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
1061*01d7584cSLiam Girdwood 
1062*01d7584cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1063*01d7584cSLiam Girdwood 		runtime->hw.rate_min = cpu_dai_drv->playback.rate_min;
1064*01d7584cSLiam Girdwood 		runtime->hw.rate_max = cpu_dai_drv->playback.rate_max;
1065*01d7584cSLiam Girdwood 		runtime->hw.channels_min = cpu_dai_drv->playback.channels_min;
1066*01d7584cSLiam Girdwood 		runtime->hw.channels_max = cpu_dai_drv->playback.channels_max;
1067*01d7584cSLiam Girdwood 		runtime->hw.formats &= cpu_dai_drv->playback.formats;
1068*01d7584cSLiam Girdwood 		runtime->hw.rates = cpu_dai_drv->playback.rates;
1069*01d7584cSLiam Girdwood 	} else {
1070*01d7584cSLiam Girdwood 		runtime->hw.rate_min = cpu_dai_drv->capture.rate_min;
1071*01d7584cSLiam Girdwood 		runtime->hw.rate_max = cpu_dai_drv->capture.rate_max;
1072*01d7584cSLiam Girdwood 		runtime->hw.channels_min = cpu_dai_drv->capture.channels_min;
1073*01d7584cSLiam Girdwood 		runtime->hw.channels_max = cpu_dai_drv->capture.channels_max;
1074*01d7584cSLiam Girdwood 		runtime->hw.formats &= cpu_dai_drv->capture.formats;
1075*01d7584cSLiam Girdwood 		runtime->hw.rates = cpu_dai_drv->capture.rates;
1076*01d7584cSLiam Girdwood 	}
1077*01d7584cSLiam Girdwood }
1078*01d7584cSLiam Girdwood 
1079*01d7584cSLiam Girdwood static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
1080*01d7584cSLiam Girdwood {
1081*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
1082*01d7584cSLiam Girdwood 	struct snd_pcm_runtime *runtime = fe_substream->runtime;
1083*01d7584cSLiam Girdwood 	int stream = fe_substream->stream, ret = 0;
1084*01d7584cSLiam Girdwood 
1085*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
1086*01d7584cSLiam Girdwood 
1087*01d7584cSLiam Girdwood 	ret = dpcm_be_dai_startup(fe, fe_substream->stream);
1088*01d7584cSLiam Girdwood 	if (ret < 0) {
1089*01d7584cSLiam Girdwood 		dev_err(fe->dev,"dpcm: failed to start some BEs %d\n", ret);
1090*01d7584cSLiam Girdwood 		goto be_err;
1091*01d7584cSLiam Girdwood 	}
1092*01d7584cSLiam Girdwood 
1093*01d7584cSLiam Girdwood 	dev_dbg(fe->dev, "dpcm: open FE %s\n", fe->dai_link->name);
1094*01d7584cSLiam Girdwood 
1095*01d7584cSLiam Girdwood 	/* start the DAI frontend */
1096*01d7584cSLiam Girdwood 	ret = soc_pcm_open(fe_substream);
1097*01d7584cSLiam Girdwood 	if (ret < 0) {
1098*01d7584cSLiam Girdwood 		dev_err(fe->dev,"dpcm: failed to start FE %d\n", ret);
1099*01d7584cSLiam Girdwood 		goto unwind;
1100*01d7584cSLiam Girdwood 	}
1101*01d7584cSLiam Girdwood 
1102*01d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
1103*01d7584cSLiam Girdwood 
1104*01d7584cSLiam Girdwood 	dpcm_set_fe_runtime(fe_substream);
1105*01d7584cSLiam Girdwood 	snd_pcm_limit_hw_rates(runtime);
1106*01d7584cSLiam Girdwood 
1107*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
1108*01d7584cSLiam Girdwood 	return 0;
1109*01d7584cSLiam Girdwood 
1110*01d7584cSLiam Girdwood unwind:
1111*01d7584cSLiam Girdwood 	dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
1112*01d7584cSLiam Girdwood be_err:
1113*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
1114*01d7584cSLiam Girdwood 	return ret;
1115*01d7584cSLiam Girdwood }
1116*01d7584cSLiam Girdwood 
1117*01d7584cSLiam Girdwood static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
1118*01d7584cSLiam Girdwood {
1119*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1120*01d7584cSLiam Girdwood 
1121*01d7584cSLiam Girdwood 	/* only shutdown BEs that are either sinks or sources to this FE DAI */
1122*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
1123*01d7584cSLiam Girdwood 
1124*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
1125*01d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
1126*01d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
1127*01d7584cSLiam Girdwood 
1128*01d7584cSLiam Girdwood 		/* is this op for this BE ? */
1129*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
1130*01d7584cSLiam Girdwood 			continue;
1131*01d7584cSLiam Girdwood 
1132*01d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1133*01d7584cSLiam Girdwood 			dev_err(be->dev, "no users %s at close - state %d\n",
1134*01d7584cSLiam Girdwood 				stream ? "capture" : "playback",
1135*01d7584cSLiam Girdwood 				be->dpcm[stream].state);
1136*01d7584cSLiam Girdwood 
1137*01d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
1138*01d7584cSLiam Girdwood 			continue;
1139*01d7584cSLiam Girdwood 
1140*01d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
1141*01d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN))
1142*01d7584cSLiam Girdwood 			continue;
1143*01d7584cSLiam Girdwood 
1144*01d7584cSLiam Girdwood 		dev_dbg(be->dev, "dpcm: close BE %s\n",
1145*01d7584cSLiam Girdwood 			dpcm->fe->dai_link->name);
1146*01d7584cSLiam Girdwood 
1147*01d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
1148*01d7584cSLiam Girdwood 		be_substream->runtime = NULL;
1149*01d7584cSLiam Girdwood 
1150*01d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
1151*01d7584cSLiam Girdwood 	}
1152*01d7584cSLiam Girdwood 	return 0;
1153*01d7584cSLiam Girdwood }
1154*01d7584cSLiam Girdwood 
1155*01d7584cSLiam Girdwood static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
1156*01d7584cSLiam Girdwood {
1157*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1158*01d7584cSLiam Girdwood 	int stream = substream->stream;
1159*01d7584cSLiam Girdwood 
1160*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
1161*01d7584cSLiam Girdwood 
1162*01d7584cSLiam Girdwood 	/* shutdown the BEs */
1163*01d7584cSLiam Girdwood 	dpcm_be_dai_shutdown(fe, substream->stream);
1164*01d7584cSLiam Girdwood 
1165*01d7584cSLiam Girdwood 	dev_dbg(fe->dev, "dpcm: close FE %s\n", fe->dai_link->name);
1166*01d7584cSLiam Girdwood 
1167*01d7584cSLiam Girdwood 	/* now shutdown the frontend */
1168*01d7584cSLiam Girdwood 	soc_pcm_close(substream);
1169*01d7584cSLiam Girdwood 
1170*01d7584cSLiam Girdwood 	/* run the stream event for each BE */
1171*01d7584cSLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
1172*01d7584cSLiam Girdwood 
1173*01d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
1174*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
1175*01d7584cSLiam Girdwood 	return 0;
1176*01d7584cSLiam Girdwood }
1177*01d7584cSLiam Girdwood 
1178*01d7584cSLiam Girdwood static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
1179*01d7584cSLiam Girdwood {
1180*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1181*01d7584cSLiam Girdwood 
1182*01d7584cSLiam Girdwood 	/* only hw_params backends that are either sinks or sources
1183*01d7584cSLiam Girdwood 	 * to this frontend DAI */
1184*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
1185*01d7584cSLiam Girdwood 
1186*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
1187*01d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
1188*01d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
1189*01d7584cSLiam Girdwood 
1190*01d7584cSLiam Girdwood 		/* is this op for this BE ? */
1191*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
1192*01d7584cSLiam Girdwood 			continue;
1193*01d7584cSLiam Girdwood 
1194*01d7584cSLiam Girdwood 		/* only free hw when no longer used - check all FEs */
1195*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
1196*01d7584cSLiam Girdwood 				continue;
1197*01d7584cSLiam Girdwood 
1198*01d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
1199*01d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
1200*01d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
1201*01d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
1202*01d7584cSLiam Girdwood 			continue;
1203*01d7584cSLiam Girdwood 
1204*01d7584cSLiam Girdwood 		dev_dbg(be->dev, "dpcm: hw_free BE %s\n",
1205*01d7584cSLiam Girdwood 			dpcm->fe->dai_link->name);
1206*01d7584cSLiam Girdwood 
1207*01d7584cSLiam Girdwood 		soc_pcm_hw_free(be_substream);
1208*01d7584cSLiam Girdwood 
1209*01d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
1210*01d7584cSLiam Girdwood 	}
1211*01d7584cSLiam Girdwood 
1212*01d7584cSLiam Girdwood 	return 0;
1213*01d7584cSLiam Girdwood }
1214*01d7584cSLiam Girdwood 
1215*01d7584cSLiam Girdwood int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
1216*01d7584cSLiam Girdwood {
1217*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1218*01d7584cSLiam Girdwood 	int err, stream = substream->stream;
1219*01d7584cSLiam Girdwood 
1220*01d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
1221*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
1222*01d7584cSLiam Girdwood 
1223*01d7584cSLiam Girdwood 	dev_dbg(fe->dev, "dpcm: hw_free FE %s\n", fe->dai_link->name);
1224*01d7584cSLiam Girdwood 
1225*01d7584cSLiam Girdwood 	/* call hw_free on the frontend */
1226*01d7584cSLiam Girdwood 	err = soc_pcm_hw_free(substream);
1227*01d7584cSLiam Girdwood 	if (err < 0)
1228*01d7584cSLiam Girdwood 		dev_err(fe->dev,"dpcm: hw_free FE %s failed\n",
1229*01d7584cSLiam Girdwood 			fe->dai_link->name);
1230*01d7584cSLiam Girdwood 
1231*01d7584cSLiam Girdwood 	/* only hw_params backends that are either sinks or sources
1232*01d7584cSLiam Girdwood 	 * to this frontend DAI */
1233*01d7584cSLiam Girdwood 	err = dpcm_be_dai_hw_free(fe, stream);
1234*01d7584cSLiam Girdwood 
1235*01d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
1236*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
1237*01d7584cSLiam Girdwood 
1238*01d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
1239*01d7584cSLiam Girdwood 	return 0;
1240*01d7584cSLiam Girdwood }
1241*01d7584cSLiam Girdwood 
1242*01d7584cSLiam Girdwood static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
1243*01d7584cSLiam Girdwood {
1244*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1245*01d7584cSLiam Girdwood 	int ret;
1246*01d7584cSLiam Girdwood 
1247*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
1248*01d7584cSLiam Girdwood 
1249*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
1250*01d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
1251*01d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
1252*01d7584cSLiam Girdwood 
1253*01d7584cSLiam Girdwood 		/* is this op for this BE ? */
1254*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
1255*01d7584cSLiam Girdwood 			continue;
1256*01d7584cSLiam Girdwood 
1257*01d7584cSLiam Girdwood 		/* only allow hw_params() if no connected FEs are running */
1258*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_can_be_params(fe, be, stream))
1259*01d7584cSLiam Girdwood 			continue;
1260*01d7584cSLiam Girdwood 
1261*01d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
1262*01d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
1263*01d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
1264*01d7584cSLiam Girdwood 			continue;
1265*01d7584cSLiam Girdwood 
1266*01d7584cSLiam Girdwood 		dev_dbg(be->dev, "dpcm: hw_params BE %s\n",
1267*01d7584cSLiam Girdwood 			dpcm->fe->dai_link->name);
1268*01d7584cSLiam Girdwood 
1269*01d7584cSLiam Girdwood 		/* copy params for each dpcm */
1270*01d7584cSLiam Girdwood 		memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params,
1271*01d7584cSLiam Girdwood 				sizeof(struct snd_pcm_hw_params));
1272*01d7584cSLiam Girdwood 
1273*01d7584cSLiam Girdwood 		/* perform any hw_params fixups */
1274*01d7584cSLiam Girdwood 		if (be->dai_link->be_hw_params_fixup) {
1275*01d7584cSLiam Girdwood 			ret = be->dai_link->be_hw_params_fixup(be,
1276*01d7584cSLiam Girdwood 					&dpcm->hw_params);
1277*01d7584cSLiam Girdwood 			if (ret < 0) {
1278*01d7584cSLiam Girdwood 				dev_err(be->dev,
1279*01d7584cSLiam Girdwood 					"dpcm: hw_params BE fixup failed %d\n",
1280*01d7584cSLiam Girdwood 					ret);
1281*01d7584cSLiam Girdwood 				goto unwind;
1282*01d7584cSLiam Girdwood 			}
1283*01d7584cSLiam Girdwood 		}
1284*01d7584cSLiam Girdwood 
1285*01d7584cSLiam Girdwood 		ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
1286*01d7584cSLiam Girdwood 		if (ret < 0) {
1287*01d7584cSLiam Girdwood 			dev_err(dpcm->be->dev,
1288*01d7584cSLiam Girdwood 				"dpcm: hw_params BE failed %d\n", ret);
1289*01d7584cSLiam Girdwood 			goto unwind;
1290*01d7584cSLiam Girdwood 		}
1291*01d7584cSLiam Girdwood 
1292*01d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
1293*01d7584cSLiam Girdwood 	}
1294*01d7584cSLiam Girdwood 	return 0;
1295*01d7584cSLiam Girdwood 
1296*01d7584cSLiam Girdwood unwind:
1297*01d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
1298*01d7584cSLiam Girdwood 	list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
1299*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
1300*01d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
1301*01d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
1302*01d7584cSLiam Girdwood 
1303*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
1304*01d7584cSLiam Girdwood 			continue;
1305*01d7584cSLiam Girdwood 
1306*01d7584cSLiam Girdwood 		/* only allow hw_free() if no connected FEs are running */
1307*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
1308*01d7584cSLiam Girdwood 			continue;
1309*01d7584cSLiam Girdwood 
1310*01d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
1311*01d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
1312*01d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
1313*01d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
1314*01d7584cSLiam Girdwood 			continue;
1315*01d7584cSLiam Girdwood 
1316*01d7584cSLiam Girdwood 		soc_pcm_hw_free(be_substream);
1317*01d7584cSLiam Girdwood 	}
1318*01d7584cSLiam Girdwood 
1319*01d7584cSLiam Girdwood 	return ret;
1320*01d7584cSLiam Girdwood }
1321*01d7584cSLiam Girdwood 
1322*01d7584cSLiam Girdwood int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
1323*01d7584cSLiam Girdwood 				    struct snd_pcm_hw_params *params)
1324*01d7584cSLiam Girdwood {
1325*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1326*01d7584cSLiam Girdwood 	int ret, stream = substream->stream;
1327*01d7584cSLiam Girdwood 
1328*01d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
1329*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
1330*01d7584cSLiam Girdwood 
1331*01d7584cSLiam Girdwood 	memcpy(&fe->dpcm[substream->stream].hw_params, params,
1332*01d7584cSLiam Girdwood 			sizeof(struct snd_pcm_hw_params));
1333*01d7584cSLiam Girdwood 	ret = dpcm_be_dai_hw_params(fe, substream->stream);
1334*01d7584cSLiam Girdwood 	if (ret < 0) {
1335*01d7584cSLiam Girdwood 		dev_err(fe->dev,"dpcm: hw_params BE failed %d\n", ret);
1336*01d7584cSLiam Girdwood 		goto out;
1337*01d7584cSLiam Girdwood 	}
1338*01d7584cSLiam Girdwood 
1339*01d7584cSLiam Girdwood 	dev_dbg(fe->dev, "dpcm: hw_params FE %s rate %d chan %x fmt %d\n",
1340*01d7584cSLiam Girdwood 			fe->dai_link->name, params_rate(params),
1341*01d7584cSLiam Girdwood 			params_channels(params), params_format(params));
1342*01d7584cSLiam Girdwood 
1343*01d7584cSLiam Girdwood 	/* call hw_params on the frontend */
1344*01d7584cSLiam Girdwood 	ret = soc_pcm_hw_params(substream, params);
1345*01d7584cSLiam Girdwood 	if (ret < 0) {
1346*01d7584cSLiam Girdwood 		dev_err(fe->dev,"dpcm: hw_params FE failed %d\n", ret);
1347*01d7584cSLiam Girdwood 		dpcm_be_dai_hw_free(fe, stream);
1348*01d7584cSLiam Girdwood 	 } else
1349*01d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
1350*01d7584cSLiam Girdwood 
1351*01d7584cSLiam Girdwood out:
1352*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
1353*01d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
1354*01d7584cSLiam Girdwood 	return ret;
1355*01d7584cSLiam Girdwood }
1356*01d7584cSLiam Girdwood 
1357*01d7584cSLiam Girdwood static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm,
1358*01d7584cSLiam Girdwood 		struct snd_pcm_substream *substream, int cmd)
1359*01d7584cSLiam Girdwood {
1360*01d7584cSLiam Girdwood 	int ret;
1361*01d7584cSLiam Girdwood 
1362*01d7584cSLiam Girdwood 	dev_dbg(dpcm->be->dev, "dpcm: trigger BE %s cmd %d\n",
1363*01d7584cSLiam Girdwood 			dpcm->fe->dai_link->name, cmd);
1364*01d7584cSLiam Girdwood 
1365*01d7584cSLiam Girdwood 	ret = soc_pcm_trigger(substream, cmd);
1366*01d7584cSLiam Girdwood 	if (ret < 0)
1367*01d7584cSLiam Girdwood 		dev_err(dpcm->be->dev,"dpcm: trigger BE failed %d\n", ret);
1368*01d7584cSLiam Girdwood 
1369*01d7584cSLiam Girdwood 	return ret;
1370*01d7584cSLiam Girdwood }
1371*01d7584cSLiam Girdwood 
1372*01d7584cSLiam Girdwood int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd)
1373*01d7584cSLiam Girdwood {
1374*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1375*01d7584cSLiam Girdwood 	int ret = 0;
1376*01d7584cSLiam Girdwood 
1377*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
1378*01d7584cSLiam Girdwood 
1379*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
1380*01d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
1381*01d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
1382*01d7584cSLiam Girdwood 
1383*01d7584cSLiam Girdwood 		/* is this op for this BE ? */
1384*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
1385*01d7584cSLiam Girdwood 			continue;
1386*01d7584cSLiam Girdwood 
1387*01d7584cSLiam Girdwood 		switch (cmd) {
1388*01d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_START:
1389*01d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
1390*01d7584cSLiam Girdwood 			    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
1391*01d7584cSLiam Girdwood 				continue;
1392*01d7584cSLiam Girdwood 
1393*01d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
1394*01d7584cSLiam Girdwood 			if (ret)
1395*01d7584cSLiam Girdwood 				return ret;
1396*01d7584cSLiam Girdwood 
1397*01d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
1398*01d7584cSLiam Girdwood 			break;
1399*01d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_RESUME:
1400*01d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
1401*01d7584cSLiam Girdwood 				continue;
1402*01d7584cSLiam Girdwood 
1403*01d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
1404*01d7584cSLiam Girdwood 			if (ret)
1405*01d7584cSLiam Girdwood 				return ret;
1406*01d7584cSLiam Girdwood 
1407*01d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
1408*01d7584cSLiam Girdwood 			break;
1409*01d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
1410*01d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
1411*01d7584cSLiam Girdwood 				continue;
1412*01d7584cSLiam Girdwood 
1413*01d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
1414*01d7584cSLiam Girdwood 			if (ret)
1415*01d7584cSLiam Girdwood 				return ret;
1416*01d7584cSLiam Girdwood 
1417*01d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
1418*01d7584cSLiam Girdwood 			break;
1419*01d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_STOP:
1420*01d7584cSLiam Girdwood 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
1421*01d7584cSLiam Girdwood 				continue;
1422*01d7584cSLiam Girdwood 
1423*01d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
1424*01d7584cSLiam Girdwood 				continue;
1425*01d7584cSLiam Girdwood 
1426*01d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
1427*01d7584cSLiam Girdwood 			if (ret)
1428*01d7584cSLiam Girdwood 				return ret;
1429*01d7584cSLiam Girdwood 
1430*01d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
1431*01d7584cSLiam Girdwood 			break;
1432*01d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_SUSPEND:
1433*01d7584cSLiam Girdwood 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)
1434*01d7584cSLiam Girdwood 				continue;
1435*01d7584cSLiam Girdwood 
1436*01d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
1437*01d7584cSLiam Girdwood 				continue;
1438*01d7584cSLiam Girdwood 
1439*01d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
1440*01d7584cSLiam Girdwood 			if (ret)
1441*01d7584cSLiam Girdwood 				return ret;
1442*01d7584cSLiam Girdwood 
1443*01d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
1444*01d7584cSLiam Girdwood 			break;
1445*01d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1446*01d7584cSLiam Girdwood 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
1447*01d7584cSLiam Girdwood 				continue;
1448*01d7584cSLiam Girdwood 
1449*01d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
1450*01d7584cSLiam Girdwood 				continue;
1451*01d7584cSLiam Girdwood 
1452*01d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
1453*01d7584cSLiam Girdwood 			if (ret)
1454*01d7584cSLiam Girdwood 				return ret;
1455*01d7584cSLiam Girdwood 
1456*01d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
1457*01d7584cSLiam Girdwood 			break;
1458*01d7584cSLiam Girdwood 		}
1459*01d7584cSLiam Girdwood 	}
1460*01d7584cSLiam Girdwood 
1461*01d7584cSLiam Girdwood 	return ret;
1462*01d7584cSLiam Girdwood }
1463*01d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
1464*01d7584cSLiam Girdwood 
1465*01d7584cSLiam Girdwood int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
1466*01d7584cSLiam Girdwood {
1467*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1468*01d7584cSLiam Girdwood 	int stream = substream->stream, ret;
1469*01d7584cSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
1470*01d7584cSLiam Girdwood 
1471*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
1472*01d7584cSLiam Girdwood 
1473*01d7584cSLiam Girdwood 	switch (trigger) {
1474*01d7584cSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_PRE:
1475*01d7584cSLiam Girdwood 		/* call trigger on the frontend before the backend. */
1476*01d7584cSLiam Girdwood 
1477*01d7584cSLiam Girdwood 		dev_dbg(fe->dev, "dpcm: pre trigger FE %s cmd %d\n",
1478*01d7584cSLiam Girdwood 				fe->dai_link->name, cmd);
1479*01d7584cSLiam Girdwood 
1480*01d7584cSLiam Girdwood 		ret = soc_pcm_trigger(substream, cmd);
1481*01d7584cSLiam Girdwood 		if (ret < 0) {
1482*01d7584cSLiam Girdwood 			dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
1483*01d7584cSLiam Girdwood 			goto out;
1484*01d7584cSLiam Girdwood 		}
1485*01d7584cSLiam Girdwood 
1486*01d7584cSLiam Girdwood 		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
1487*01d7584cSLiam Girdwood 		break;
1488*01d7584cSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_POST:
1489*01d7584cSLiam Girdwood 		/* call trigger on the frontend after the backend. */
1490*01d7584cSLiam Girdwood 
1491*01d7584cSLiam Girdwood 		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
1492*01d7584cSLiam Girdwood 		if (ret < 0) {
1493*01d7584cSLiam Girdwood 			dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
1494*01d7584cSLiam Girdwood 			goto out;
1495*01d7584cSLiam Girdwood 		}
1496*01d7584cSLiam Girdwood 
1497*01d7584cSLiam Girdwood 		dev_dbg(fe->dev, "dpcm: post trigger FE %s cmd %d\n",
1498*01d7584cSLiam Girdwood 				fe->dai_link->name, cmd);
1499*01d7584cSLiam Girdwood 
1500*01d7584cSLiam Girdwood 		ret = soc_pcm_trigger(substream, cmd);
1501*01d7584cSLiam Girdwood 		break;
1502*01d7584cSLiam Girdwood 	default:
1503*01d7584cSLiam Girdwood 		dev_err(fe->dev, "dpcm: invalid trigger cmd %d for %s\n", cmd,
1504*01d7584cSLiam Girdwood 				fe->dai_link->name);
1505*01d7584cSLiam Girdwood 		ret = -EINVAL;
1506*01d7584cSLiam Girdwood 		goto out;
1507*01d7584cSLiam Girdwood 	}
1508*01d7584cSLiam Girdwood 
1509*01d7584cSLiam Girdwood 	switch (cmd) {
1510*01d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_START:
1511*01d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_RESUME:
1512*01d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
1513*01d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
1514*01d7584cSLiam Girdwood 		break;
1515*01d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_STOP:
1516*01d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_SUSPEND:
1517*01d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1518*01d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
1519*01d7584cSLiam Girdwood 		break;
1520*01d7584cSLiam Girdwood 	}
1521*01d7584cSLiam Girdwood 
1522*01d7584cSLiam Girdwood out:
1523*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
1524*01d7584cSLiam Girdwood 	return ret;
1525*01d7584cSLiam Girdwood }
1526*01d7584cSLiam Girdwood 
1527*01d7584cSLiam Girdwood static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
1528*01d7584cSLiam Girdwood {
1529*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1530*01d7584cSLiam Girdwood 	int ret = 0;
1531*01d7584cSLiam Girdwood 
1532*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
1533*01d7584cSLiam Girdwood 
1534*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
1535*01d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
1536*01d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
1537*01d7584cSLiam Girdwood 
1538*01d7584cSLiam Girdwood 		/* is this op for this BE ? */
1539*01d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
1540*01d7584cSLiam Girdwood 			continue;
1541*01d7584cSLiam Girdwood 
1542*01d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
1543*01d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
1544*01d7584cSLiam Girdwood 			continue;
1545*01d7584cSLiam Girdwood 
1546*01d7584cSLiam Girdwood 		dev_dbg(be->dev, "dpcm: prepare BE %s\n",
1547*01d7584cSLiam Girdwood 			dpcm->fe->dai_link->name);
1548*01d7584cSLiam Girdwood 
1549*01d7584cSLiam Girdwood 		ret = soc_pcm_prepare(be_substream);
1550*01d7584cSLiam Girdwood 		if (ret < 0) {
1551*01d7584cSLiam Girdwood 			dev_err(be->dev, "dpcm: backend prepare failed %d\n",
1552*01d7584cSLiam Girdwood 				ret);
1553*01d7584cSLiam Girdwood 			break;
1554*01d7584cSLiam Girdwood 		}
1555*01d7584cSLiam Girdwood 
1556*01d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
1557*01d7584cSLiam Girdwood 	}
1558*01d7584cSLiam Girdwood 	return ret;
1559*01d7584cSLiam Girdwood }
1560*01d7584cSLiam Girdwood 
1561*01d7584cSLiam Girdwood int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
1562*01d7584cSLiam Girdwood {
1563*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1564*01d7584cSLiam Girdwood 	int stream = substream->stream, ret = 0;
1565*01d7584cSLiam Girdwood 
1566*01d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
1567*01d7584cSLiam Girdwood 
1568*01d7584cSLiam Girdwood 	dev_dbg(fe->dev, "dpcm: prepare FE %s\n", fe->dai_link->name);
1569*01d7584cSLiam Girdwood 
1570*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
1571*01d7584cSLiam Girdwood 
1572*01d7584cSLiam Girdwood 	/* there is no point preparing this FE if there are no BEs */
1573*01d7584cSLiam Girdwood 	if (list_empty(&fe->dpcm[stream].be_clients)) {
1574*01d7584cSLiam Girdwood 		dev_err(fe->dev, "dpcm: no backend DAIs enabled for %s\n",
1575*01d7584cSLiam Girdwood 				fe->dai_link->name);
1576*01d7584cSLiam Girdwood 		ret = -EINVAL;
1577*01d7584cSLiam Girdwood 		goto out;
1578*01d7584cSLiam Girdwood 	}
1579*01d7584cSLiam Girdwood 
1580*01d7584cSLiam Girdwood 	ret = dpcm_be_dai_prepare(fe, substream->stream);
1581*01d7584cSLiam Girdwood 	if (ret < 0)
1582*01d7584cSLiam Girdwood 		goto out;
1583*01d7584cSLiam Girdwood 
1584*01d7584cSLiam Girdwood 	/* call prepare on the frontend */
1585*01d7584cSLiam Girdwood 	ret = soc_pcm_prepare(substream);
1586*01d7584cSLiam Girdwood 	if (ret < 0) {
1587*01d7584cSLiam Girdwood 		dev_err(fe->dev,"dpcm: prepare FE %s failed\n",
1588*01d7584cSLiam Girdwood 			fe->dai_link->name);
1589*01d7584cSLiam Girdwood 		goto out;
1590*01d7584cSLiam Girdwood 	}
1591*01d7584cSLiam Girdwood 
1592*01d7584cSLiam Girdwood 	/* run the stream event for each BE */
1593*01d7584cSLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
1594*01d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
1595*01d7584cSLiam Girdwood 
1596*01d7584cSLiam Girdwood out:
1597*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
1598*01d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
1599*01d7584cSLiam Girdwood 
1600*01d7584cSLiam Girdwood 	return ret;
1601*01d7584cSLiam Girdwood }
1602*01d7584cSLiam Girdwood 
1603*01d7584cSLiam Girdwood 
1604*01d7584cSLiam Girdwood int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
1605*01d7584cSLiam Girdwood {
1606*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1607*01d7584cSLiam Girdwood 	struct list_head *clients =
1608*01d7584cSLiam Girdwood 		&fe->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients;
1609*01d7584cSLiam Girdwood 
1610*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, clients, list_be) {
1611*01d7584cSLiam Girdwood 
1612*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
1613*01d7584cSLiam Girdwood 		struct snd_soc_dai *dai = be->codec_dai;
1614*01d7584cSLiam Girdwood 		struct snd_soc_dai_driver *drv = dai->driver;
1615*01d7584cSLiam Girdwood 
1616*01d7584cSLiam Girdwood 		if (be->dai_link->ignore_suspend)
1617*01d7584cSLiam Girdwood 			continue;
1618*01d7584cSLiam Girdwood 
1619*01d7584cSLiam Girdwood 		dev_dbg(be->dev, "BE digital mute %s\n", be->dai_link->name);
1620*01d7584cSLiam Girdwood 
1621*01d7584cSLiam Girdwood 		if (drv->ops->digital_mute && dai->playback_active)
1622*01d7584cSLiam Girdwood 				drv->ops->digital_mute(dai, mute);
1623*01d7584cSLiam Girdwood 	}
1624*01d7584cSLiam Girdwood 
1625*01d7584cSLiam Girdwood 	return 0;
1626*01d7584cSLiam Girdwood }
1627*01d7584cSLiam Girdwood 
1628*01d7584cSLiam Girdwood int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
1629*01d7584cSLiam Girdwood {
1630*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
1631*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1632*01d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list;
1633*01d7584cSLiam Girdwood 	int ret;
1634*01d7584cSLiam Girdwood 	int stream = fe_substream->stream;
1635*01d7584cSLiam Girdwood 
1636*01d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
1637*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime = fe_substream->runtime;
1638*01d7584cSLiam Girdwood 
1639*01d7584cSLiam Girdwood 	if (dpcm_path_get(fe, stream, &list) <= 0) {
1640*01d7584cSLiam Girdwood 		dev_warn(fe->dev, "asoc: %s no valid %s route\n",
1641*01d7584cSLiam Girdwood 			fe->dai_link->name, stream ? "capture" : "playback");
1642*01d7584cSLiam Girdwood 			mutex_unlock(&fe->card->mutex);
1643*01d7584cSLiam Girdwood 			return -EINVAL;
1644*01d7584cSLiam Girdwood 	}
1645*01d7584cSLiam Girdwood 
1646*01d7584cSLiam Girdwood 	/* calculate valid and active FE <-> BE dpcms */
1647*01d7584cSLiam Girdwood 	dpcm_process_paths(fe, stream, &list, 1);
1648*01d7584cSLiam Girdwood 
1649*01d7584cSLiam Girdwood 	ret = dpcm_fe_dai_startup(fe_substream);
1650*01d7584cSLiam Girdwood 	if (ret < 0) {
1651*01d7584cSLiam Girdwood 		/* clean up all links */
1652*01d7584cSLiam Girdwood 		list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
1653*01d7584cSLiam Girdwood 			dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
1654*01d7584cSLiam Girdwood 
1655*01d7584cSLiam Girdwood 		dpcm_be_disconnect(fe, stream);
1656*01d7584cSLiam Girdwood 		fe->dpcm[stream].runtime = NULL;
1657*01d7584cSLiam Girdwood 	}
1658*01d7584cSLiam Girdwood 
1659*01d7584cSLiam Girdwood 	dpcm_clear_pending_state(fe, stream);
1660*01d7584cSLiam Girdwood 	dpcm_path_put(&list);
1661*01d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
1662*01d7584cSLiam Girdwood 	return ret;
1663*01d7584cSLiam Girdwood }
1664*01d7584cSLiam Girdwood 
1665*01d7584cSLiam Girdwood int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
1666*01d7584cSLiam Girdwood {
1667*01d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
1668*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1669*01d7584cSLiam Girdwood 	int stream = fe_substream->stream, ret;
1670*01d7584cSLiam Girdwood 
1671*01d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
1672*01d7584cSLiam Girdwood 	ret = dpcm_fe_dai_shutdown(fe_substream);
1673*01d7584cSLiam Girdwood 
1674*01d7584cSLiam Girdwood 	/* mark FE's links ready to prune */
1675*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
1676*01d7584cSLiam Girdwood 		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
1677*01d7584cSLiam Girdwood 
1678*01d7584cSLiam Girdwood 	dpcm_be_disconnect(fe, stream);
1679*01d7584cSLiam Girdwood 
1680*01d7584cSLiam Girdwood 	fe->dpcm[stream].runtime = NULL;
1681*01d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
1682*01d7584cSLiam Girdwood 	return ret;
1683*01d7584cSLiam Girdwood }
1684*01d7584cSLiam Girdwood 
1685ddee627cSLiam Girdwood /* create a new pcm */
1686ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
1687ddee627cSLiam Girdwood {
1688ddee627cSLiam Girdwood 	struct snd_soc_codec *codec = rtd->codec;
1689ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
1690ddee627cSLiam Girdwood 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
1691ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
1692ddee627cSLiam Girdwood 	struct snd_pcm *pcm;
1693ddee627cSLiam Girdwood 	char new_name[64];
1694ddee627cSLiam Girdwood 	int ret = 0, playback = 0, capture = 0;
1695ddee627cSLiam Girdwood 
1696*01d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
1697*01d7584cSLiam Girdwood 		if (cpu_dai->driver->playback.channels_min)
1698*01d7584cSLiam Girdwood 			playback = 1;
1699*01d7584cSLiam Girdwood 		if (cpu_dai->driver->capture.channels_min)
1700*01d7584cSLiam Girdwood 			capture = 1;
1701*01d7584cSLiam Girdwood 	} else {
1702ddee627cSLiam Girdwood 		if (codec_dai->driver->playback.channels_min)
1703ddee627cSLiam Girdwood 			playback = 1;
1704ddee627cSLiam Girdwood 		if (codec_dai->driver->capture.channels_min)
1705ddee627cSLiam Girdwood 			capture = 1;
1706*01d7584cSLiam Girdwood 	}
1707ddee627cSLiam Girdwood 
1708*01d7584cSLiam Girdwood 	/* create the PCM */
1709*01d7584cSLiam Girdwood 	if (rtd->dai_link->no_pcm) {
1710*01d7584cSLiam Girdwood 		snprintf(new_name, sizeof(new_name), "(%s)",
1711*01d7584cSLiam Girdwood 			rtd->dai_link->stream_name);
1712*01d7584cSLiam Girdwood 
1713*01d7584cSLiam Girdwood 		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
1714*01d7584cSLiam Girdwood 				playback, capture, &pcm);
1715*01d7584cSLiam Girdwood 	} else {
1716*01d7584cSLiam Girdwood 		if (rtd->dai_link->dynamic)
1717*01d7584cSLiam Girdwood 			snprintf(new_name, sizeof(new_name), "%s (*)",
1718*01d7584cSLiam Girdwood 				rtd->dai_link->stream_name);
1719*01d7584cSLiam Girdwood 		else
1720*01d7584cSLiam Girdwood 			snprintf(new_name, sizeof(new_name), "%s %s-%d",
1721*01d7584cSLiam Girdwood 				rtd->dai_link->stream_name, codec_dai->name, num);
1722*01d7584cSLiam Girdwood 
1723*01d7584cSLiam Girdwood 		ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
1724*01d7584cSLiam Girdwood 			capture, &pcm);
1725*01d7584cSLiam Girdwood 	}
1726ddee627cSLiam Girdwood 	if (ret < 0) {
1727ddee627cSLiam Girdwood 		printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
1728ddee627cSLiam Girdwood 		return ret;
1729ddee627cSLiam Girdwood 	}
1730*01d7584cSLiam Girdwood 	dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num, new_name);
1731ddee627cSLiam Girdwood 
1732ddee627cSLiam Girdwood 	/* DAPM dai link stream work */
1733ddee627cSLiam Girdwood 	INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
1734ddee627cSLiam Girdwood 
1735ddee627cSLiam Girdwood 	rtd->pcm = pcm;
1736ddee627cSLiam Girdwood 	pcm->private_data = rtd;
1737*01d7584cSLiam Girdwood 
1738*01d7584cSLiam Girdwood 	if (rtd->dai_link->no_pcm) {
1739*01d7584cSLiam Girdwood 		if (playback)
1740*01d7584cSLiam Girdwood 			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
1741*01d7584cSLiam Girdwood 		if (capture)
1742*01d7584cSLiam Girdwood 			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
1743*01d7584cSLiam Girdwood 		goto out;
1744*01d7584cSLiam Girdwood 	}
1745*01d7584cSLiam Girdwood 
1746*01d7584cSLiam Girdwood 	/* ASoC PCM operations */
1747*01d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic) {
1748*01d7584cSLiam Girdwood 		rtd->ops.open		= dpcm_fe_dai_open;
1749*01d7584cSLiam Girdwood 		rtd->ops.hw_params	= dpcm_fe_dai_hw_params;
1750*01d7584cSLiam Girdwood 		rtd->ops.prepare	= dpcm_fe_dai_prepare;
1751*01d7584cSLiam Girdwood 		rtd->ops.trigger	= dpcm_fe_dai_trigger;
1752*01d7584cSLiam Girdwood 		rtd->ops.hw_free	= dpcm_fe_dai_hw_free;
1753*01d7584cSLiam Girdwood 		rtd->ops.close		= dpcm_fe_dai_close;
1754*01d7584cSLiam Girdwood 		rtd->ops.pointer	= soc_pcm_pointer;
1755*01d7584cSLiam Girdwood 	} else {
1756*01d7584cSLiam Girdwood 		rtd->ops.open		= soc_pcm_open;
1757*01d7584cSLiam Girdwood 		rtd->ops.hw_params	= soc_pcm_hw_params;
1758*01d7584cSLiam Girdwood 		rtd->ops.prepare	= soc_pcm_prepare;
1759*01d7584cSLiam Girdwood 		rtd->ops.trigger	= soc_pcm_trigger;
1760*01d7584cSLiam Girdwood 		rtd->ops.hw_free	= soc_pcm_hw_free;
1761*01d7584cSLiam Girdwood 		rtd->ops.close		= soc_pcm_close;
1762*01d7584cSLiam Girdwood 		rtd->ops.pointer	= soc_pcm_pointer;
1763*01d7584cSLiam Girdwood 	}
1764*01d7584cSLiam Girdwood 
1765ddee627cSLiam Girdwood 	if (platform->driver->ops) {
1766*01d7584cSLiam Girdwood 		rtd->ops.ack		= platform->driver->ops->ack;
1767*01d7584cSLiam Girdwood 		rtd->ops.copy		= platform->driver->ops->copy;
1768*01d7584cSLiam Girdwood 		rtd->ops.silence	= platform->driver->ops->silence;
1769*01d7584cSLiam Girdwood 		rtd->ops.page		= platform->driver->ops->page;
1770*01d7584cSLiam Girdwood 		rtd->ops.mmap		= platform->driver->ops->mmap;
1771ddee627cSLiam Girdwood 	}
1772ddee627cSLiam Girdwood 
1773ddee627cSLiam Girdwood 	if (playback)
1774*01d7584cSLiam Girdwood 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
1775ddee627cSLiam Girdwood 
1776ddee627cSLiam Girdwood 	if (capture)
1777*01d7584cSLiam Girdwood 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
1778ddee627cSLiam Girdwood 
1779ddee627cSLiam Girdwood 	if (platform->driver->pcm_new) {
1780ddee627cSLiam Girdwood 		ret = platform->driver->pcm_new(rtd);
1781ddee627cSLiam Girdwood 		if (ret < 0) {
1782ddee627cSLiam Girdwood 			pr_err("asoc: platform pcm constructor failed\n");
1783ddee627cSLiam Girdwood 			return ret;
1784ddee627cSLiam Girdwood 		}
1785ddee627cSLiam Girdwood 	}
1786ddee627cSLiam Girdwood 
1787ddee627cSLiam Girdwood 	pcm->private_free = platform->driver->pcm_free;
1788*01d7584cSLiam Girdwood out:
1789ddee627cSLiam Girdwood 	printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
1790ddee627cSLiam Girdwood 		cpu_dai->name);
1791ddee627cSLiam Girdwood 	return ret;
1792ddee627cSLiam Girdwood }
1793*01d7584cSLiam Girdwood 
1794*01d7584cSLiam Girdwood /* is the current PCM operation for this FE ? */
1795*01d7584cSLiam Girdwood int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream)
1796*01d7584cSLiam Girdwood {
1797*01d7584cSLiam Girdwood 	if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE)
1798*01d7584cSLiam Girdwood 		return 1;
1799*01d7584cSLiam Girdwood 	return 0;
1800*01d7584cSLiam Girdwood }
1801*01d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update);
1802*01d7584cSLiam Girdwood 
1803*01d7584cSLiam Girdwood /* is the current PCM operation for this BE ? */
1804*01d7584cSLiam Girdwood int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
1805*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
1806*01d7584cSLiam Girdwood {
1807*01d7584cSLiam Girdwood 	if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) ||
1808*01d7584cSLiam Girdwood 	   ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) &&
1809*01d7584cSLiam Girdwood 		  be->dpcm[stream].runtime_update))
1810*01d7584cSLiam Girdwood 		return 1;
1811*01d7584cSLiam Girdwood 	return 0;
1812*01d7584cSLiam Girdwood }
1813*01d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update);
1814*01d7584cSLiam Girdwood 
1815*01d7584cSLiam Girdwood /* get the substream for this BE */
1816*01d7584cSLiam Girdwood struct snd_pcm_substream *
1817*01d7584cSLiam Girdwood 	snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream)
1818*01d7584cSLiam Girdwood {
1819*01d7584cSLiam Girdwood 	return be->pcm->streams[stream].substream;
1820*01d7584cSLiam Girdwood }
1821*01d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream);
1822*01d7584cSLiam Girdwood 
1823*01d7584cSLiam Girdwood /* get the BE runtime state */
1824*01d7584cSLiam Girdwood enum snd_soc_dpcm_state
1825*01d7584cSLiam Girdwood 	snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream)
1826*01d7584cSLiam Girdwood {
1827*01d7584cSLiam Girdwood 	return be->dpcm[stream].state;
1828*01d7584cSLiam Girdwood }
1829*01d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state);
1830*01d7584cSLiam Girdwood 
1831*01d7584cSLiam Girdwood /* set the BE runtime state */
1832*01d7584cSLiam Girdwood void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be,
1833*01d7584cSLiam Girdwood 		int stream, enum snd_soc_dpcm_state state)
1834*01d7584cSLiam Girdwood {
1835*01d7584cSLiam Girdwood 	be->dpcm[stream].state = state;
1836*01d7584cSLiam Girdwood }
1837*01d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state);
1838*01d7584cSLiam Girdwood 
1839*01d7584cSLiam Girdwood /*
1840*01d7584cSLiam Girdwood  * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE
1841*01d7584cSLiam Girdwood  * are not running, paused or suspended for the specified stream direction.
1842*01d7584cSLiam Girdwood  */
1843*01d7584cSLiam Girdwood int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
1844*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
1845*01d7584cSLiam Girdwood {
1846*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1847*01d7584cSLiam Girdwood 	int state;
1848*01d7584cSLiam Girdwood 
1849*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
1850*01d7584cSLiam Girdwood 
1851*01d7584cSLiam Girdwood 		if (dpcm->fe == fe)
1852*01d7584cSLiam Girdwood 			continue;
1853*01d7584cSLiam Girdwood 
1854*01d7584cSLiam Girdwood 		state = dpcm->fe->dpcm[stream].state;
1855*01d7584cSLiam Girdwood 		if (state == SND_SOC_DPCM_STATE_START ||
1856*01d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_PAUSED ||
1857*01d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_SUSPEND)
1858*01d7584cSLiam Girdwood 			return 0;
1859*01d7584cSLiam Girdwood 	}
1860*01d7584cSLiam Girdwood 
1861*01d7584cSLiam Girdwood 	/* it's safe to free/stop this BE DAI */
1862*01d7584cSLiam Girdwood 	return 1;
1863*01d7584cSLiam Girdwood }
1864*01d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
1865*01d7584cSLiam Girdwood 
1866*01d7584cSLiam Girdwood /*
1867*01d7584cSLiam Girdwood  * We can only change hw params a BE DAI if any of it's FE are not prepared,
1868*01d7584cSLiam Girdwood  * running, paused or suspended for the specified stream direction.
1869*01d7584cSLiam Girdwood  */
1870*01d7584cSLiam Girdwood int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
1871*01d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
1872*01d7584cSLiam Girdwood {
1873*01d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1874*01d7584cSLiam Girdwood 	int state;
1875*01d7584cSLiam Girdwood 
1876*01d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
1877*01d7584cSLiam Girdwood 
1878*01d7584cSLiam Girdwood 		if (dpcm->fe == fe)
1879*01d7584cSLiam Girdwood 			continue;
1880*01d7584cSLiam Girdwood 
1881*01d7584cSLiam Girdwood 		state = dpcm->fe->dpcm[stream].state;
1882*01d7584cSLiam Girdwood 		if (state == SND_SOC_DPCM_STATE_START ||
1883*01d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_PAUSED ||
1884*01d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_SUSPEND ||
1885*01d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_PREPARE)
1886*01d7584cSLiam Girdwood 			return 0;
1887*01d7584cSLiam Girdwood 	}
1888*01d7584cSLiam Girdwood 
1889*01d7584cSLiam Girdwood 	/* it's safe to change hw_params */
1890*01d7584cSLiam Girdwood 	return 1;
1891*01d7584cSLiam Girdwood }
1892*01d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
1893