xref: /openbmc/linux/sound/soc/soc-pcm.c (revision 57be92066f68e63bd4a72a65d45c3407c0cb552a)
1ed517582SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0+
2ed517582SKuninori Morimoto //
3ed517582SKuninori Morimoto // soc-pcm.c  --  ALSA SoC PCM
4ed517582SKuninori Morimoto //
5ed517582SKuninori Morimoto // Copyright 2005 Wolfson Microelectronics PLC.
6ed517582SKuninori Morimoto // Copyright 2005 Openedhand Ltd.
7ed517582SKuninori Morimoto // Copyright (C) 2010 Slimlogic Ltd.
8ed517582SKuninori Morimoto // Copyright (C) 2010 Texas Instruments Inc.
9ed517582SKuninori Morimoto //
10ed517582SKuninori Morimoto // Authors: Liam Girdwood <lrg@ti.com>
11ed517582SKuninori Morimoto //          Mark Brown <broonie@opensource.wolfsonmicro.com>
12ddee627cSLiam Girdwood 
13ddee627cSLiam Girdwood #include <linux/kernel.h>
14ddee627cSLiam Girdwood #include <linux/init.h>
15ddee627cSLiam Girdwood #include <linux/delay.h>
16988e8cc4SNicolin Chen #include <linux/pinctrl/consumer.h>
17d6652ef8SMark Brown #include <linux/pm_runtime.h>
18ddee627cSLiam Girdwood #include <linux/slab.h>
19ddee627cSLiam Girdwood #include <linux/workqueue.h>
2001d7584cSLiam Girdwood #include <linux/export.h>
21f86dcef8SLiam Girdwood #include <linux/debugfs.h>
22ddee627cSLiam Girdwood #include <sound/core.h>
23ddee627cSLiam Girdwood #include <sound/pcm.h>
24ddee627cSLiam Girdwood #include <sound/pcm_params.h>
25ddee627cSLiam Girdwood #include <sound/soc.h>
2601d7584cSLiam Girdwood #include <sound/soc-dpcm.h>
27ddee627cSLiam Girdwood #include <sound/initval.h>
28ddee627cSLiam Girdwood 
2901d7584cSLiam Girdwood #define DPCM_MAX_BE_USERS	8
3001d7584cSLiam Girdwood 
31f183f927SKuninori Morimoto static int soc_rtd_startup(struct snd_soc_pcm_runtime *rtd,
32f183f927SKuninori Morimoto 			   struct snd_pcm_substream *substream)
33f183f927SKuninori Morimoto {
34f183f927SKuninori Morimoto 	if (rtd->dai_link->ops &&
35f183f927SKuninori Morimoto 	    rtd->dai_link->ops->startup)
36f183f927SKuninori Morimoto 		return rtd->dai_link->ops->startup(substream);
37f183f927SKuninori Morimoto 	return 0;
38f183f927SKuninori Morimoto }
39f183f927SKuninori Morimoto 
400be429f9SKuninori Morimoto static void soc_rtd_shutdown(struct snd_soc_pcm_runtime *rtd,
410be429f9SKuninori Morimoto 			     struct snd_pcm_substream *substream)
420be429f9SKuninori Morimoto {
430be429f9SKuninori Morimoto 	if (rtd->dai_link->ops &&
440be429f9SKuninori Morimoto 	    rtd->dai_link->ops->shutdown)
450be429f9SKuninori Morimoto 		rtd->dai_link->ops->shutdown(substream);
460be429f9SKuninori Morimoto }
470be429f9SKuninori Morimoto 
4844c1a75bSKuninori Morimoto static int soc_rtd_prepare(struct snd_soc_pcm_runtime *rtd,
4944c1a75bSKuninori Morimoto 			   struct snd_pcm_substream *substream)
5044c1a75bSKuninori Morimoto {
5144c1a75bSKuninori Morimoto 	if (rtd->dai_link->ops &&
5244c1a75bSKuninori Morimoto 	    rtd->dai_link->ops->prepare)
5344c1a75bSKuninori Morimoto 		return rtd->dai_link->ops->prepare(substream);
5444c1a75bSKuninori Morimoto 	return 0;
5544c1a75bSKuninori Morimoto }
5644c1a75bSKuninori Morimoto 
57de9ad990SKuninori Morimoto static int soc_rtd_hw_params(struct snd_soc_pcm_runtime *rtd,
58de9ad990SKuninori Morimoto 			     struct snd_pcm_substream *substream,
59de9ad990SKuninori Morimoto 			     struct snd_pcm_hw_params *params)
60de9ad990SKuninori Morimoto {
61de9ad990SKuninori Morimoto 	if (rtd->dai_link->ops &&
62de9ad990SKuninori Morimoto 	    rtd->dai_link->ops->hw_params)
63de9ad990SKuninori Morimoto 		return rtd->dai_link->ops->hw_params(substream, params);
64de9ad990SKuninori Morimoto 	return 0;
65de9ad990SKuninori Morimoto }
66de9ad990SKuninori Morimoto 
6749f020e5SKuninori Morimoto static void soc_rtd_hw_free(struct snd_soc_pcm_runtime *rtd,
6849f020e5SKuninori Morimoto 			    struct snd_pcm_substream *substream)
6949f020e5SKuninori Morimoto {
7049f020e5SKuninori Morimoto 	if (rtd->dai_link->ops &&
7149f020e5SKuninori Morimoto 	    rtd->dai_link->ops->hw_free)
7249f020e5SKuninori Morimoto 		rtd->dai_link->ops->hw_free(substream);
7349f020e5SKuninori Morimoto }
7449f020e5SKuninori Morimoto 
75ad2bf9f2SKuninori Morimoto static int soc_rtd_trigger(struct snd_soc_pcm_runtime *rtd,
76ad2bf9f2SKuninori Morimoto 			   struct snd_pcm_substream *substream,
77ad2bf9f2SKuninori Morimoto 			   int cmd)
78ad2bf9f2SKuninori Morimoto {
79ad2bf9f2SKuninori Morimoto 	if (rtd->dai_link->ops &&
80ad2bf9f2SKuninori Morimoto 	    rtd->dai_link->ops->trigger)
81ad2bf9f2SKuninori Morimoto 		return rtd->dai_link->ops->trigger(substream, cmd);
82ad2bf9f2SKuninori Morimoto 	return 0;
83ad2bf9f2SKuninori Morimoto }
84ad2bf9f2SKuninori Morimoto 
85d9303690SKuninori Morimoto static inline
86d9303690SKuninori Morimoto struct snd_soc_dapm_widget *dai_get_widget(struct snd_soc_dai *dai, int stream)
87d9303690SKuninori Morimoto {
88d9303690SKuninori Morimoto 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
89d9303690SKuninori Morimoto 		return dai->playback_widget;
90d9303690SKuninori Morimoto 	else
91d9303690SKuninori Morimoto 		return dai->capture_widget;
92d9303690SKuninori Morimoto }
93d9303690SKuninori Morimoto 
947a5aaba4SKuninori Morimoto static void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
957a5aaba4SKuninori Morimoto 				   int stream, int action)
967a5aaba4SKuninori Morimoto {
977a5aaba4SKuninori Morimoto 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
987a5aaba4SKuninori Morimoto 	struct snd_soc_dai *codec_dai;
997a5aaba4SKuninori Morimoto 	int i;
1007a5aaba4SKuninori Morimoto 
1017a5aaba4SKuninori Morimoto 	lockdep_assert_held(&rtd->card->pcm_mutex);
1027a5aaba4SKuninori Morimoto 
1030f6011fdSKuninori Morimoto 	cpu_dai->stream_active[stream] += action;
1047a5aaba4SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai)
1050f6011fdSKuninori Morimoto 		codec_dai->stream_active[stream] += action;
1067a5aaba4SKuninori Morimoto 
1077a5aaba4SKuninori Morimoto 	cpu_dai->active += action;
1087a5aaba4SKuninori Morimoto 	cpu_dai->component->active += action;
1097a5aaba4SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
1107a5aaba4SKuninori Morimoto 		codec_dai->active += action;
1117a5aaba4SKuninori Morimoto 		codec_dai->component->active += action;
1127a5aaba4SKuninori Morimoto 	}
1137a5aaba4SKuninori Morimoto }
1147a5aaba4SKuninori Morimoto 
11590996f43SLars-Peter Clausen /**
11624894b76SLars-Peter Clausen  * snd_soc_runtime_activate() - Increment active count for PCM runtime components
11724894b76SLars-Peter Clausen  * @rtd: ASoC PCM runtime that is activated
11824894b76SLars-Peter Clausen  * @stream: Direction of the PCM stream
11924894b76SLars-Peter Clausen  *
12024894b76SLars-Peter Clausen  * Increments the active count for all the DAIs and components attached to a PCM
12124894b76SLars-Peter Clausen  * runtime. Should typically be called when a stream is opened.
12224894b76SLars-Peter Clausen  *
12372b745e3SPeter Ujfalusi  * Must be called with the rtd->card->pcm_mutex being held
12424894b76SLars-Peter Clausen  */
12524894b76SLars-Peter Clausen void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
12624894b76SLars-Peter Clausen {
1277a5aaba4SKuninori Morimoto 	snd_soc_runtime_action(rtd, stream, 1);
12824894b76SLars-Peter Clausen }
12924894b76SLars-Peter Clausen 
13024894b76SLars-Peter Clausen /**
13124894b76SLars-Peter Clausen  * snd_soc_runtime_deactivate() - Decrement active count for PCM runtime components
13224894b76SLars-Peter Clausen  * @rtd: ASoC PCM runtime that is deactivated
13324894b76SLars-Peter Clausen  * @stream: Direction of the PCM stream
13424894b76SLars-Peter Clausen  *
13524894b76SLars-Peter Clausen  * Decrements the active count for all the DAIs and components attached to a PCM
13624894b76SLars-Peter Clausen  * runtime. Should typically be called when a stream is closed.
13724894b76SLars-Peter Clausen  *
13872b745e3SPeter Ujfalusi  * Must be called with the rtd->card->pcm_mutex being held
13924894b76SLars-Peter Clausen  */
14024894b76SLars-Peter Clausen void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
14124894b76SLars-Peter Clausen {
1427a5aaba4SKuninori Morimoto 	snd_soc_runtime_action(rtd, stream, -1);
14324894b76SLars-Peter Clausen }
14424894b76SLars-Peter Clausen 
14524894b76SLars-Peter Clausen /**
146208a1589SLars-Peter Clausen  * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay
147208a1589SLars-Peter Clausen  * @rtd: The ASoC PCM runtime that should be checked.
148208a1589SLars-Peter Clausen  *
149208a1589SLars-Peter Clausen  * This function checks whether the power down delay should be ignored for a
150208a1589SLars-Peter Clausen  * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has
151208a1589SLars-Peter Clausen  * been configured to ignore the delay, or if none of the components benefits
152208a1589SLars-Peter Clausen  * from having the delay.
153208a1589SLars-Peter Clausen  */
154208a1589SLars-Peter Clausen bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
155208a1589SLars-Peter Clausen {
156fbb16563SKuninori Morimoto 	struct snd_soc_component *component;
1572e5894d7SBenoit Cousson 	bool ignore = true;
158613fb500SKuninori Morimoto 	int i;
1592e5894d7SBenoit Cousson 
160208a1589SLars-Peter Clausen 	if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
161208a1589SLars-Peter Clausen 		return true;
162208a1589SLars-Peter Clausen 
163613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
16472c38184SKuninori Morimoto 		ignore &= !component->driver->use_pmdown_time;
165fbb16563SKuninori Morimoto 
166fbb16563SKuninori Morimoto 	return ignore;
167208a1589SLars-Peter Clausen }
168208a1589SLars-Peter Clausen 
169208a1589SLars-Peter Clausen /**
17090996f43SLars-Peter Clausen  * snd_soc_set_runtime_hwparams - set the runtime hardware parameters
17190996f43SLars-Peter Clausen  * @substream: the pcm substream
17290996f43SLars-Peter Clausen  * @hw: the hardware parameters
17390996f43SLars-Peter Clausen  *
17490996f43SLars-Peter Clausen  * Sets the substream runtime hardware parameters.
17590996f43SLars-Peter Clausen  */
17690996f43SLars-Peter Clausen int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
17790996f43SLars-Peter Clausen 	const struct snd_pcm_hardware *hw)
17890996f43SLars-Peter Clausen {
17990996f43SLars-Peter Clausen 	struct snd_pcm_runtime *runtime = substream->runtime;
18090996f43SLars-Peter Clausen 	runtime->hw.info = hw->info;
18190996f43SLars-Peter Clausen 	runtime->hw.formats = hw->formats;
18290996f43SLars-Peter Clausen 	runtime->hw.period_bytes_min = hw->period_bytes_min;
18390996f43SLars-Peter Clausen 	runtime->hw.period_bytes_max = hw->period_bytes_max;
18490996f43SLars-Peter Clausen 	runtime->hw.periods_min = hw->periods_min;
18590996f43SLars-Peter Clausen 	runtime->hw.periods_max = hw->periods_max;
18690996f43SLars-Peter Clausen 	runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
18790996f43SLars-Peter Clausen 	runtime->hw.fifo_size = hw->fifo_size;
18890996f43SLars-Peter Clausen 	return 0;
18990996f43SLars-Peter Clausen }
19090996f43SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
19190996f43SLars-Peter Clausen 
19201d7584cSLiam Girdwood /* DPCM stream event, send event to FE and all active BEs. */
19323607025SLiam Girdwood int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
19401d7584cSLiam Girdwood 	int event)
19501d7584cSLiam Girdwood {
19601d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
19701d7584cSLiam Girdwood 
1988d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, dir, dpcm) {
19901d7584cSLiam Girdwood 
20001d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
20101d7584cSLiam Girdwood 
202103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n",
20301d7584cSLiam Girdwood 				be->dai_link->name, event, dir);
20401d7584cSLiam Girdwood 
205b1cd2e34SBanajit Goswami 		if ((event == SND_SOC_DAPM_STREAM_STOP) &&
206b1cd2e34SBanajit Goswami 		    (be->dpcm[dir].users >= 1))
207b1cd2e34SBanajit Goswami 			continue;
208b1cd2e34SBanajit Goswami 
20901d7584cSLiam Girdwood 		snd_soc_dapm_stream_event(be, dir, event);
21001d7584cSLiam Girdwood 	}
21101d7584cSLiam Girdwood 
21201d7584cSLiam Girdwood 	snd_soc_dapm_stream_event(fe, dir, event);
21301d7584cSLiam Girdwood 
21401d7584cSLiam Girdwood 	return 0;
21501d7584cSLiam Girdwood }
21601d7584cSLiam Girdwood 
21717841020SDong Aisheng static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
21817841020SDong Aisheng 					struct snd_soc_dai *soc_dai)
219ddee627cSLiam Girdwood {
220ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
221ddee627cSLiam Girdwood 	int ret;
222ddee627cSLiam Girdwood 
2233635bf09SNicolin Chen 	if (soc_dai->rate && (soc_dai->driver->symmetric_rates ||
2243635bf09SNicolin Chen 				rtd->dai_link->symmetric_rates)) {
2253635bf09SNicolin Chen 		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
2263635bf09SNicolin Chen 				soc_dai->rate);
227ddee627cSLiam Girdwood 
2284dcdd43bSLars-Peter Clausen 		ret = snd_pcm_hw_constraint_single(substream->runtime,
229ddee627cSLiam Girdwood 						SNDRV_PCM_HW_PARAM_RATE,
2304dcdd43bSLars-Peter Clausen 						soc_dai->rate);
231ddee627cSLiam Girdwood 		if (ret < 0) {
23217841020SDong Aisheng 			dev_err(soc_dai->dev,
2333635bf09SNicolin Chen 				"ASoC: Unable to apply rate constraint: %d\n",
234103d84a3SLiam Girdwood 				ret);
235ddee627cSLiam Girdwood 			return ret;
236ddee627cSLiam Girdwood 		}
2373635bf09SNicolin Chen 	}
2383635bf09SNicolin Chen 
2393635bf09SNicolin Chen 	if (soc_dai->channels && (soc_dai->driver->symmetric_channels ||
2403635bf09SNicolin Chen 				rtd->dai_link->symmetric_channels)) {
2413635bf09SNicolin Chen 		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n",
2423635bf09SNicolin Chen 				soc_dai->channels);
2433635bf09SNicolin Chen 
2444dcdd43bSLars-Peter Clausen 		ret = snd_pcm_hw_constraint_single(substream->runtime,
2453635bf09SNicolin Chen 						SNDRV_PCM_HW_PARAM_CHANNELS,
2463635bf09SNicolin Chen 						soc_dai->channels);
2473635bf09SNicolin Chen 		if (ret < 0) {
2483635bf09SNicolin Chen 			dev_err(soc_dai->dev,
2493635bf09SNicolin Chen 				"ASoC: Unable to apply channel symmetry constraint: %d\n",
2503635bf09SNicolin Chen 				ret);
2513635bf09SNicolin Chen 			return ret;
2523635bf09SNicolin Chen 		}
2533635bf09SNicolin Chen 	}
2543635bf09SNicolin Chen 
2553635bf09SNicolin Chen 	if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits ||
2563635bf09SNicolin Chen 				rtd->dai_link->symmetric_samplebits)) {
2573635bf09SNicolin Chen 		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n",
2583635bf09SNicolin Chen 				soc_dai->sample_bits);
2593635bf09SNicolin Chen 
2604dcdd43bSLars-Peter Clausen 		ret = snd_pcm_hw_constraint_single(substream->runtime,
2613635bf09SNicolin Chen 						SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
2623635bf09SNicolin Chen 						soc_dai->sample_bits);
2633635bf09SNicolin Chen 		if (ret < 0) {
2643635bf09SNicolin Chen 			dev_err(soc_dai->dev,
2653635bf09SNicolin Chen 				"ASoC: Unable to apply sample bits symmetry constraint: %d\n",
2663635bf09SNicolin Chen 				ret);
2673635bf09SNicolin Chen 			return ret;
2683635bf09SNicolin Chen 		}
2693635bf09SNicolin Chen 	}
270ddee627cSLiam Girdwood 
271ddee627cSLiam Girdwood 	return 0;
272ddee627cSLiam Girdwood }
273ddee627cSLiam Girdwood 
2743635bf09SNicolin Chen static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
2753635bf09SNicolin Chen 				struct snd_pcm_hw_params *params)
2763635bf09SNicolin Chen {
2773635bf09SNicolin Chen 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2783635bf09SNicolin Chen 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
2790b7990e3SKuninori Morimoto 	struct snd_soc_dai *codec_dai;
2802e5894d7SBenoit Cousson 	unsigned int rate, channels, sample_bits, symmetry, i;
2813635bf09SNicolin Chen 
2823635bf09SNicolin Chen 	rate = params_rate(params);
2833635bf09SNicolin Chen 	channels = params_channels(params);
2843635bf09SNicolin Chen 	sample_bits = snd_pcm_format_physical_width(params_format(params));
2853635bf09SNicolin Chen 
2863635bf09SNicolin Chen 	/* reject unmatched parameters when applying symmetry */
2873635bf09SNicolin Chen 	symmetry = cpu_dai->driver->symmetric_rates ||
2883635bf09SNicolin Chen 		rtd->dai_link->symmetric_rates;
2892e5894d7SBenoit Cousson 
2900b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai)
2910b7990e3SKuninori Morimoto 		symmetry |= codec_dai->driver->symmetric_rates;
2922e5894d7SBenoit Cousson 
2933635bf09SNicolin Chen 	if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) {
2943635bf09SNicolin Chen 		dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n",
2953635bf09SNicolin Chen 				cpu_dai->rate, rate);
2963635bf09SNicolin Chen 		return -EINVAL;
2973635bf09SNicolin Chen 	}
2983635bf09SNicolin Chen 
2993635bf09SNicolin Chen 	symmetry = cpu_dai->driver->symmetric_channels ||
3003635bf09SNicolin Chen 		rtd->dai_link->symmetric_channels;
3012e5894d7SBenoit Cousson 
3020b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai)
3030b7990e3SKuninori Morimoto 		symmetry |= codec_dai->driver->symmetric_channels;
3042e5894d7SBenoit Cousson 
3053635bf09SNicolin Chen 	if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) {
3063635bf09SNicolin Chen 		dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n",
3073635bf09SNicolin Chen 				cpu_dai->channels, channels);
3083635bf09SNicolin Chen 		return -EINVAL;
3093635bf09SNicolin Chen 	}
3103635bf09SNicolin Chen 
3113635bf09SNicolin Chen 	symmetry = cpu_dai->driver->symmetric_samplebits ||
3123635bf09SNicolin Chen 		rtd->dai_link->symmetric_samplebits;
3132e5894d7SBenoit Cousson 
3140b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai)
3150b7990e3SKuninori Morimoto 		symmetry |= codec_dai->driver->symmetric_samplebits;
3162e5894d7SBenoit Cousson 
3173635bf09SNicolin Chen 	if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) {
3183635bf09SNicolin Chen 		dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n",
3193635bf09SNicolin Chen 				cpu_dai->sample_bits, sample_bits);
3203635bf09SNicolin Chen 		return -EINVAL;
3213635bf09SNicolin Chen 	}
322ddee627cSLiam Girdwood 
323ddee627cSLiam Girdwood 	return 0;
324ddee627cSLiam Girdwood }
325ddee627cSLiam Girdwood 
32662e5f676SLars-Peter Clausen static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream)
32762e5f676SLars-Peter Clausen {
32862e5f676SLars-Peter Clausen 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
32962e5f676SLars-Peter Clausen 	struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver;
33062e5f676SLars-Peter Clausen 	struct snd_soc_dai_link *link = rtd->dai_link;
3310b7990e3SKuninori Morimoto 	struct snd_soc_dai *codec_dai;
3322e5894d7SBenoit Cousson 	unsigned int symmetry, i;
33362e5f676SLars-Peter Clausen 
3342e5894d7SBenoit Cousson 	symmetry = cpu_driver->symmetric_rates || link->symmetric_rates ||
3352e5894d7SBenoit Cousson 		cpu_driver->symmetric_channels || link->symmetric_channels ||
3362e5894d7SBenoit Cousson 		cpu_driver->symmetric_samplebits || link->symmetric_samplebits;
3372e5894d7SBenoit Cousson 
3380b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai)
3392e5894d7SBenoit Cousson 		symmetry = symmetry ||
3400b7990e3SKuninori Morimoto 			codec_dai->driver->symmetric_rates ||
3410b7990e3SKuninori Morimoto 			codec_dai->driver->symmetric_channels ||
3420b7990e3SKuninori Morimoto 			codec_dai->driver->symmetric_samplebits;
3432e5894d7SBenoit Cousson 
3442e5894d7SBenoit Cousson 	return symmetry;
34562e5f676SLars-Peter Clausen }
34662e5f676SLars-Peter Clausen 
3472e5894d7SBenoit Cousson static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits)
34858ba9b25SMark Brown {
3492e5894d7SBenoit Cousson 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
350c6068d3aSTakashi Iwai 	int ret;
35158ba9b25SMark Brown 
35258ba9b25SMark Brown 	if (!bits)
35358ba9b25SMark Brown 		return;
35458ba9b25SMark Brown 
3550e2a3751SLars-Peter Clausen 	ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, bits);
35658ba9b25SMark Brown 	if (ret != 0)
3570e2a3751SLars-Peter Clausen 		dev_warn(rtd->dev, "ASoC: Failed to set MSB %d: %d\n",
3580e2a3751SLars-Peter Clausen 				 bits, ret);
35958ba9b25SMark Brown }
36058ba9b25SMark Brown 
361c8dd1fecSBenoit Cousson static void soc_pcm_apply_msb(struct snd_pcm_substream *substream)
362bd477c31SLars-Peter Clausen {
363c8dd1fecSBenoit Cousson 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
364c8dd1fecSBenoit Cousson 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
3652e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
366*57be9206SKuninori Morimoto 	struct snd_soc_pcm_stream *pcm_codec, *pcm_cpu;
367*57be9206SKuninori Morimoto 	int stream = substream->stream;
3682e5894d7SBenoit Cousson 	int i;
369c8dd1fecSBenoit Cousson 	unsigned int bits = 0, cpu_bits;
370c8dd1fecSBenoit Cousson 
3710b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
372*57be9206SKuninori Morimoto 		pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream);
373*57be9206SKuninori Morimoto 
374*57be9206SKuninori Morimoto 		if (pcm_codec->sig_bits == 0) {
3752e5894d7SBenoit Cousson 			bits = 0;
3762e5894d7SBenoit Cousson 			break;
3772e5894d7SBenoit Cousson 		}
378*57be9206SKuninori Morimoto 		bits = max(pcm_codec->sig_bits, bits);
3792e5894d7SBenoit Cousson 	}
380*57be9206SKuninori Morimoto 
381*57be9206SKuninori Morimoto 	pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream);
382*57be9206SKuninori Morimoto 	cpu_bits = pcm_cpu->sig_bits;
383c8dd1fecSBenoit Cousson 
3842e5894d7SBenoit Cousson 	soc_pcm_set_msb(substream, bits);
3852e5894d7SBenoit Cousson 	soc_pcm_set_msb(substream, cpu_bits);
386c8dd1fecSBenoit Cousson }
387c8dd1fecSBenoit Cousson 
3882e5894d7SBenoit Cousson static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
389bd477c31SLars-Peter Clausen {
3902e5894d7SBenoit Cousson 	struct snd_pcm_runtime *runtime = substream->runtime;
39178e45c99SLars-Peter Clausen 	struct snd_pcm_hardware *hw = &runtime->hw;
3922e5894d7SBenoit Cousson 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
3930b7990e3SKuninori Morimoto 	struct snd_soc_dai *codec_dai;
3942e5894d7SBenoit Cousson 	struct snd_soc_pcm_stream *codec_stream;
3952e5894d7SBenoit Cousson 	struct snd_soc_pcm_stream *cpu_stream;
3962e5894d7SBenoit Cousson 	unsigned int chan_min = 0, chan_max = UINT_MAX;
3972e5894d7SBenoit Cousson 	unsigned int rate_min = 0, rate_max = UINT_MAX;
3982e5894d7SBenoit Cousson 	unsigned int rates = UINT_MAX;
3992e5894d7SBenoit Cousson 	u64 formats = ULLONG_MAX;
400acf253c1SKuninori Morimoto 	int stream = substream->stream;
4012e5894d7SBenoit Cousson 	int i;
40278e45c99SLars-Peter Clausen 
403acf253c1SKuninori Morimoto 	cpu_stream = snd_soc_dai_get_pcm_stream(rtd->cpu_dai, stream);
40478e45c99SLars-Peter Clausen 
4052e5894d7SBenoit Cousson 	/* first calculate min/max only for CODECs in the DAI link */
4060b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
407cde79035SRicard Wanderlof 
408cde79035SRicard Wanderlof 		/*
409cde79035SRicard Wanderlof 		 * Skip CODECs which don't support the current stream type.
410cde79035SRicard Wanderlof 		 * Otherwise, since the rate, channel, and format values will
411cde79035SRicard Wanderlof 		 * zero in that case, we would have no usable settings left,
412cde79035SRicard Wanderlof 		 * causing the resulting setup to fail.
413cde79035SRicard Wanderlof 		 * At least one CODEC should match, otherwise we should have
414cde79035SRicard Wanderlof 		 * bailed out on a higher level, since there would be no
415cde79035SRicard Wanderlof 		 * CODEC to support the transfer direction in that case.
416cde79035SRicard Wanderlof 		 */
4170b7990e3SKuninori Morimoto 		if (!snd_soc_dai_stream_valid(codec_dai,
418cde79035SRicard Wanderlof 					      substream->stream))
419cde79035SRicard Wanderlof 			continue;
420cde79035SRicard Wanderlof 
421acf253c1SKuninori Morimoto 		codec_stream = snd_soc_dai_get_pcm_stream(codec_dai, stream);
422acf253c1SKuninori Morimoto 
4232e5894d7SBenoit Cousson 		chan_min = max(chan_min, codec_stream->channels_min);
4242e5894d7SBenoit Cousson 		chan_max = min(chan_max, codec_stream->channels_max);
4252e5894d7SBenoit Cousson 		rate_min = max(rate_min, codec_stream->rate_min);
4262e5894d7SBenoit Cousson 		rate_max = min_not_zero(rate_max, codec_stream->rate_max);
4272e5894d7SBenoit Cousson 		formats &= codec_stream->formats;
4282e5894d7SBenoit Cousson 		rates = snd_pcm_rate_mask_intersect(codec_stream->rates, rates);
4292e5894d7SBenoit Cousson 	}
4302e5894d7SBenoit Cousson 
4312e5894d7SBenoit Cousson 	/*
4322e5894d7SBenoit Cousson 	 * chan min/max cannot be enforced if there are multiple CODEC DAIs
4332e5894d7SBenoit Cousson 	 * connected to a single CPU DAI, use CPU DAI's directly and let
4342e5894d7SBenoit Cousson 	 * channel allocation be fixed up later
4352e5894d7SBenoit Cousson 	 */
4362e5894d7SBenoit Cousson 	if (rtd->num_codecs > 1) {
4372e5894d7SBenoit Cousson 		chan_min = cpu_stream->channels_min;
4382e5894d7SBenoit Cousson 		chan_max = cpu_stream->channels_max;
4392e5894d7SBenoit Cousson 	}
4402e5894d7SBenoit Cousson 
4412e5894d7SBenoit Cousson 	hw->channels_min = max(chan_min, cpu_stream->channels_min);
4422e5894d7SBenoit Cousson 	hw->channels_max = min(chan_max, cpu_stream->channels_max);
4432e5894d7SBenoit Cousson 	if (hw->formats)
4442e5894d7SBenoit Cousson 		hw->formats &= formats & cpu_stream->formats;
4452e5894d7SBenoit Cousson 	else
4462e5894d7SBenoit Cousson 		hw->formats = formats & cpu_stream->formats;
4472e5894d7SBenoit Cousson 	hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_stream->rates);
44878e45c99SLars-Peter Clausen 
44978e45c99SLars-Peter Clausen 	snd_pcm_limit_hw_rates(runtime);
45078e45c99SLars-Peter Clausen 
45178e45c99SLars-Peter Clausen 	hw->rate_min = max(hw->rate_min, cpu_stream->rate_min);
4522e5894d7SBenoit Cousson 	hw->rate_min = max(hw->rate_min, rate_min);
45378e45c99SLars-Peter Clausen 	hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max);
4542e5894d7SBenoit Cousson 	hw->rate_max = min_not_zero(hw->rate_max, rate_max);
455bd477c31SLars-Peter Clausen }
456bd477c31SLars-Peter Clausen 
457dd03907bSKuninori Morimoto static int soc_pcm_components_open(struct snd_pcm_substream *substream)
458e7ecfdb7SKuninori Morimoto {
459e7ecfdb7SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
460e7ecfdb7SKuninori Morimoto 	struct snd_soc_component *component;
461613fb500SKuninori Morimoto 	int i, ret = 0;
462e7ecfdb7SKuninori Morimoto 
463613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
4644a81e8f3SKuninori Morimoto 		ret = snd_soc_component_module_get_when_open(component);
4654a81e8f3SKuninori Morimoto 		if (ret < 0) {
466e7ecfdb7SKuninori Morimoto 			dev_err(component->dev,
467e7ecfdb7SKuninori Morimoto 				"ASoC: can't get module %s\n",
468e7ecfdb7SKuninori Morimoto 				component->name);
4694a81e8f3SKuninori Morimoto 			return ret;
470e7ecfdb7SKuninori Morimoto 		}
471e7ecfdb7SKuninori Morimoto 
472ae2f4849SKuninori Morimoto 		ret = snd_soc_component_open(component, substream);
473e7ecfdb7SKuninori Morimoto 		if (ret < 0) {
474e7ecfdb7SKuninori Morimoto 			dev_err(component->dev,
475e7ecfdb7SKuninori Morimoto 				"ASoC: can't open component %s: %d\n",
476e7ecfdb7SKuninori Morimoto 				component->name, ret);
477e7ecfdb7SKuninori Morimoto 			return ret;
478e7ecfdb7SKuninori Morimoto 		}
479e7ecfdb7SKuninori Morimoto 	}
480dd03907bSKuninori Morimoto 
481e7ecfdb7SKuninori Morimoto 	return 0;
482e7ecfdb7SKuninori Morimoto }
483e7ecfdb7SKuninori Morimoto 
484dd03907bSKuninori Morimoto static int soc_pcm_components_close(struct snd_pcm_substream *substream)
485244e2936SCharles Keepax {
486244e2936SCharles Keepax 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
487244e2936SCharles Keepax 	struct snd_soc_component *component;
488e82ebffcSKuninori Morimoto 	int i, r, ret = 0;
489244e2936SCharles Keepax 
490613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
491e82ebffcSKuninori Morimoto 		r = snd_soc_component_close(component, substream);
492e82ebffcSKuninori Morimoto 		if (r < 0)
493e82ebffcSKuninori Morimoto 			ret = r; /* use last ret */
494e82ebffcSKuninori Morimoto 
4954a81e8f3SKuninori Morimoto 		snd_soc_component_module_put_when_close(component);
496244e2936SCharles Keepax 	}
497244e2936SCharles Keepax 
4983672beb8SKuninori Morimoto 	return ret;
499244e2936SCharles Keepax }
500244e2936SCharles Keepax 
50158ba9b25SMark Brown /*
50262c86d1dSKuninori Morimoto  * Called by ALSA when a PCM substream is closed. Private data can be
50362c86d1dSKuninori Morimoto  * freed here. The cpu DAI, codec DAI, machine and components are also
50462c86d1dSKuninori Morimoto  * shutdown.
50562c86d1dSKuninori Morimoto  */
50662c86d1dSKuninori Morimoto static int soc_pcm_close(struct snd_pcm_substream *substream)
50762c86d1dSKuninori Morimoto {
50862c86d1dSKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
50962c86d1dSKuninori Morimoto 	struct snd_soc_component *component;
51062c86d1dSKuninori Morimoto 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
51162c86d1dSKuninori Morimoto 	struct snd_soc_dai *codec_dai;
51262c86d1dSKuninori Morimoto 	int i;
51362c86d1dSKuninori Morimoto 
51462c86d1dSKuninori Morimoto 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
51562c86d1dSKuninori Morimoto 
51662c86d1dSKuninori Morimoto 	snd_soc_runtime_deactivate(rtd, substream->stream);
51762c86d1dSKuninori Morimoto 
51862c86d1dSKuninori Morimoto 	snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
51962c86d1dSKuninori Morimoto 
52062c86d1dSKuninori Morimoto 	snd_soc_dai_shutdown(cpu_dai, substream);
52162c86d1dSKuninori Morimoto 
52262c86d1dSKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai)
52362c86d1dSKuninori Morimoto 		snd_soc_dai_shutdown(codec_dai, substream);
52462c86d1dSKuninori Morimoto 
52562c86d1dSKuninori Morimoto 	soc_rtd_shutdown(rtd, substream);
52662c86d1dSKuninori Morimoto 
52762c86d1dSKuninori Morimoto 	soc_pcm_components_close(substream);
52862c86d1dSKuninori Morimoto 
52962c86d1dSKuninori Morimoto 	snd_soc_dapm_stream_stop(rtd, substream->stream);
53062c86d1dSKuninori Morimoto 
53162c86d1dSKuninori Morimoto 	mutex_unlock(&rtd->card->pcm_mutex);
53262c86d1dSKuninori Morimoto 
53362c86d1dSKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
53462c86d1dSKuninori Morimoto 		pm_runtime_mark_last_busy(component->dev);
53562c86d1dSKuninori Morimoto 		pm_runtime_put_autosuspend(component->dev);
53662c86d1dSKuninori Morimoto 	}
53762c86d1dSKuninori Morimoto 
53862c86d1dSKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
53962c86d1dSKuninori Morimoto 		if (!component->active)
54062c86d1dSKuninori Morimoto 			pinctrl_pm_select_sleep_state(component->dev);
54162c86d1dSKuninori Morimoto 
54262c86d1dSKuninori Morimoto 	return 0;
54362c86d1dSKuninori Morimoto }
54462c86d1dSKuninori Morimoto 
54562c86d1dSKuninori Morimoto /*
546ddee627cSLiam Girdwood  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
547ddee627cSLiam Girdwood  * then initialized and any private data can be allocated. This also calls
548ef050becSCharles Keepax  * startup for the cpu DAI, component, machine and codec DAI.
549ddee627cSLiam Girdwood  */
550ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream)
551ddee627cSLiam Girdwood {
552ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
553ddee627cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
55490be711eSKuninori Morimoto 	struct snd_soc_component *component;
555ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
5562e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
5572e5894d7SBenoit Cousson 	const char *codec_dai_name = "multicodec";
558244e2936SCharles Keepax 	int i, ret = 0;
559ddee627cSLiam Girdwood 
56076c39e86SKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
56176c39e86SKuninori Morimoto 		pinctrl_pm_select_default_state(component->dev);
56290be711eSKuninori Morimoto 
563613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
56490be711eSKuninori Morimoto 		pm_runtime_get_sync(component->dev);
565d6652ef8SMark Brown 
56672b745e3SPeter Ujfalusi 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
567ddee627cSLiam Girdwood 
5685d9fa03eSKuninori Morimoto 	ret = soc_pcm_components_open(substream);
5695d9fa03eSKuninori Morimoto 	if (ret < 0)
5705d9fa03eSKuninori Morimoto 		goto component_err;
5715d9fa03eSKuninori Morimoto 
5725d9fa03eSKuninori Morimoto 	ret = soc_rtd_startup(rtd, substream);
5735d9fa03eSKuninori Morimoto 	if (ret < 0) {
5745d9fa03eSKuninori Morimoto 		pr_err("ASoC: %s startup failed: %d\n",
5755d9fa03eSKuninori Morimoto 		       rtd->dai_link->name, ret);
5765d9fa03eSKuninori Morimoto 		goto component_err;
5775d9fa03eSKuninori Morimoto 	}
5785d9fa03eSKuninori Morimoto 
579ddee627cSLiam Girdwood 	/* startup the audio subsystem */
5805a52a045SKuninori Morimoto 	ret = snd_soc_dai_startup(cpu_dai, substream);
581ddee627cSLiam Girdwood 	if (ret < 0) {
5825a52a045SKuninori Morimoto 		dev_err(cpu_dai->dev, "ASoC: can't open interface %s: %d\n",
5835a52a045SKuninori Morimoto 			cpu_dai->name, ret);
5845d9fa03eSKuninori Morimoto 		goto cpu_dai_err;
585ddee627cSLiam Girdwood 	}
586ddee627cSLiam Girdwood 
5870b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
5885a52a045SKuninori Morimoto 		ret = snd_soc_dai_startup(codec_dai, substream);
589ddee627cSLiam Girdwood 		if (ret < 0) {
5902e5894d7SBenoit Cousson 			dev_err(codec_dai->dev,
5912e5894d7SBenoit Cousson 				"ASoC: can't open codec %s: %d\n",
5922e5894d7SBenoit Cousson 				codec_dai->name, ret);
5935d9fa03eSKuninori Morimoto 			goto config_err;
594ddee627cSLiam Girdwood 		}
595ddee627cSLiam Girdwood 
5962e5894d7SBenoit Cousson 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5972e5894d7SBenoit Cousson 			codec_dai->tx_mask = 0;
5982e5894d7SBenoit Cousson 		else
5992e5894d7SBenoit Cousson 			codec_dai->rx_mask = 0;
6002e5894d7SBenoit Cousson 	}
6012e5894d7SBenoit Cousson 
60201d7584cSLiam Girdwood 	/* Dynamic PCM DAI links compat checks use dynamic capabilities */
60301d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
60401d7584cSLiam Girdwood 		goto dynamic;
60501d7584cSLiam Girdwood 
606ddee627cSLiam Girdwood 	/* Check that the codec and cpu DAIs are compatible */
6072e5894d7SBenoit Cousson 	soc_pcm_init_runtime_hw(substream);
6082e5894d7SBenoit Cousson 
6092e5894d7SBenoit Cousson 	if (rtd->num_codecs == 1)
6102e5894d7SBenoit Cousson 		codec_dai_name = rtd->codec_dai->name;
611ddee627cSLiam Girdwood 
61262e5f676SLars-Peter Clausen 	if (soc_pcm_has_symmetry(substream))
61362e5f676SLars-Peter Clausen 		runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
61462e5f676SLars-Peter Clausen 
615ddee627cSLiam Girdwood 	ret = -EINVAL;
616ddee627cSLiam Girdwood 	if (!runtime->hw.rates) {
617103d84a3SLiam Girdwood 		printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
6182e5894d7SBenoit Cousson 			codec_dai_name, cpu_dai->name);
619ddee627cSLiam Girdwood 		goto config_err;
620ddee627cSLiam Girdwood 	}
621ddee627cSLiam Girdwood 	if (!runtime->hw.formats) {
622103d84a3SLiam Girdwood 		printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n",
6232e5894d7SBenoit Cousson 			codec_dai_name, cpu_dai->name);
624ddee627cSLiam Girdwood 		goto config_err;
625ddee627cSLiam Girdwood 	}
626ddee627cSLiam Girdwood 	if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
627ddee627cSLiam Girdwood 	    runtime->hw.channels_min > runtime->hw.channels_max) {
628103d84a3SLiam Girdwood 		printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n",
6292e5894d7SBenoit Cousson 				codec_dai_name, cpu_dai->name);
630ddee627cSLiam Girdwood 		goto config_err;
631ddee627cSLiam Girdwood 	}
632ddee627cSLiam Girdwood 
633c8dd1fecSBenoit Cousson 	soc_pcm_apply_msb(substream);
63458ba9b25SMark Brown 
635ddee627cSLiam Girdwood 	/* Symmetry only applies if we've already got an active stream. */
63617841020SDong Aisheng 	if (cpu_dai->active) {
63717841020SDong Aisheng 		ret = soc_pcm_apply_symmetry(substream, cpu_dai);
63817841020SDong Aisheng 		if (ret != 0)
63917841020SDong Aisheng 			goto config_err;
64017841020SDong Aisheng 	}
64117841020SDong Aisheng 
6420b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
6430b7990e3SKuninori Morimoto 		if (codec_dai->active) {
6440b7990e3SKuninori Morimoto 			ret = soc_pcm_apply_symmetry(substream, codec_dai);
645ddee627cSLiam Girdwood 			if (ret != 0)
646ddee627cSLiam Girdwood 				goto config_err;
647ddee627cSLiam Girdwood 		}
6482e5894d7SBenoit Cousson 	}
649ddee627cSLiam Girdwood 
650103d84a3SLiam Girdwood 	pr_debug("ASoC: %s <-> %s info:\n",
6512e5894d7SBenoit Cousson 			codec_dai_name, cpu_dai->name);
652103d84a3SLiam Girdwood 	pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates);
653103d84a3SLiam Girdwood 	pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min,
654ddee627cSLiam Girdwood 		 runtime->hw.channels_max);
655103d84a3SLiam Girdwood 	pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min,
656ddee627cSLiam Girdwood 		 runtime->hw.rate_max);
657ddee627cSLiam Girdwood 
65801d7584cSLiam Girdwood dynamic:
65924894b76SLars-Peter Clausen 
66024894b76SLars-Peter Clausen 	snd_soc_runtime_activate(rtd, substream->stream);
66124894b76SLars-Peter Clausen 
66272b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
663ddee627cSLiam Girdwood 	return 0;
664ddee627cSLiam Girdwood 
665ddee627cSLiam Girdwood config_err:
666b56be800SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai)
667330fcb51SKuninori Morimoto 		snd_soc_dai_shutdown(codec_dai, substream);
6685d9fa03eSKuninori Morimoto cpu_dai_err:
6695d9fa03eSKuninori Morimoto 	snd_soc_dai_shutdown(cpu_dai, substream);
6702e5894d7SBenoit Cousson 
6715d9fa03eSKuninori Morimoto 	soc_rtd_shutdown(rtd, substream);
672b8135864SKuninori Morimoto component_err:
673dd03907bSKuninori Morimoto 	soc_pcm_components_close(substream);
674e7ecfdb7SKuninori Morimoto 
67572b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
676d6652ef8SMark Brown 
677613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
67890be711eSKuninori Morimoto 		pm_runtime_mark_last_busy(component->dev);
67990be711eSKuninori Morimoto 		pm_runtime_put_autosuspend(component->dev);
6803f809783SSanyog Kale 	}
6813f809783SSanyog Kale 
68276c39e86SKuninori Morimoto 	for_each_rtd_components(rtd, i, component)
68376c39e86SKuninori Morimoto 		if (!component->active)
68476c39e86SKuninori Morimoto 			pinctrl_pm_select_sleep_state(component->dev);
685d6652ef8SMark Brown 
686ddee627cSLiam Girdwood 	return ret;
687ddee627cSLiam Girdwood }
688ddee627cSLiam Girdwood 
6894bf2e385SCurtis Malainey static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
690a342031cSJerome Brunet {
691a342031cSJerome Brunet 	/*
692a342031cSJerome Brunet 	 * Currently nothing to do for c2c links
693a342031cSJerome Brunet 	 * Since c2c links are internal nodes in the DAPM graph and
694a342031cSJerome Brunet 	 * don't interface with the outside world or application layer
695a342031cSJerome Brunet 	 * we don't have to do any special handling on close.
696a342031cSJerome Brunet 	 */
697a342031cSJerome Brunet }
698a342031cSJerome Brunet 
699ddee627cSLiam Girdwood /*
700ddee627cSLiam Girdwood  * Called by ALSA when the PCM substream is prepared, can set format, sample
701ddee627cSLiam Girdwood  * rate, etc.  This function is non atomic and can be called multiple times,
702ddee627cSLiam Girdwood  * it can refer to the runtime info.
703ddee627cSLiam Girdwood  */
704ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream)
705ddee627cSLiam Girdwood {
706ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
707b8135864SKuninori Morimoto 	struct snd_soc_component *component;
708ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
7092e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
7102e5894d7SBenoit Cousson 	int i, ret = 0;
711ddee627cSLiam Girdwood 
71272b745e3SPeter Ujfalusi 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
713ddee627cSLiam Girdwood 
71444c1a75bSKuninori Morimoto 	ret = soc_rtd_prepare(rtd, substream);
715ddee627cSLiam Girdwood 	if (ret < 0) {
71644c1a75bSKuninori Morimoto 		dev_err(rtd->card->dev,
71744c1a75bSKuninori Morimoto 			"ASoC: machine prepare error: %d\n", ret);
718ddee627cSLiam Girdwood 		goto out;
719ddee627cSLiam Girdwood 	}
720ddee627cSLiam Girdwood 
721613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
7226d537233SKuninori Morimoto 		ret = snd_soc_component_prepare(component, substream);
723b8135864SKuninori Morimoto 		if (ret < 0) {
724b8135864SKuninori Morimoto 			dev_err(component->dev,
725b8135864SKuninori Morimoto 				"ASoC: platform prepare error: %d\n", ret);
726b8135864SKuninori Morimoto 			goto out;
727b8135864SKuninori Morimoto 		}
728b8135864SKuninori Morimoto 	}
729b8135864SKuninori Morimoto 
7300b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
7314beb8e10SKuninori Morimoto 		ret = snd_soc_dai_prepare(codec_dai, substream);
732ddee627cSLiam Girdwood 		if (ret < 0) {
7332e5894d7SBenoit Cousson 			dev_err(codec_dai->dev,
73490cc7f1cSJarkko Nikula 				"ASoC: codec DAI prepare error: %d\n",
73590cc7f1cSJarkko Nikula 				ret);
736ddee627cSLiam Girdwood 			goto out;
737ddee627cSLiam Girdwood 		}
738ddee627cSLiam Girdwood 	}
739ddee627cSLiam Girdwood 
7404beb8e10SKuninori Morimoto 	ret = snd_soc_dai_prepare(cpu_dai, substream);
741ddee627cSLiam Girdwood 	if (ret < 0) {
74290cc7f1cSJarkko Nikula 		dev_err(cpu_dai->dev,
74390cc7f1cSJarkko Nikula 			"ASoC: cpu DAI prepare error: %d\n", ret);
744ddee627cSLiam Girdwood 		goto out;
745ddee627cSLiam Girdwood 	}
746ddee627cSLiam Girdwood 
747ddee627cSLiam Girdwood 	/* cancel any delayed stream shutdown that is pending */
748ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
7499bffb1fbSMisael Lopez Cruz 	    rtd->pop_wait) {
7509bffb1fbSMisael Lopez Cruz 		rtd->pop_wait = 0;
751ddee627cSLiam Girdwood 		cancel_delayed_work(&rtd->delayed_work);
752ddee627cSLiam Girdwood 	}
753ddee627cSLiam Girdwood 
754d9b0951bSLiam Girdwood 	snd_soc_dapm_stream_event(rtd, substream->stream,
755ddee627cSLiam Girdwood 			SND_SOC_DAPM_STREAM_START);
756ddee627cSLiam Girdwood 
7570b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai)
7580b7990e3SKuninori Morimoto 		snd_soc_dai_digital_mute(codec_dai, 0,
7592e5894d7SBenoit Cousson 					 substream->stream);
760ae11601bSRamesh Babu 	snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
761ddee627cSLiam Girdwood 
762ddee627cSLiam Girdwood out:
76372b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
764ddee627cSLiam Girdwood 	return ret;
765ddee627cSLiam Girdwood }
766ddee627cSLiam Girdwood 
7672e5894d7SBenoit Cousson static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
7682e5894d7SBenoit Cousson 				       unsigned int mask)
7692e5894d7SBenoit Cousson {
7702e5894d7SBenoit Cousson 	struct snd_interval *interval;
7712e5894d7SBenoit Cousson 	int channels = hweight_long(mask);
7722e5894d7SBenoit Cousson 
7732e5894d7SBenoit Cousson 	interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
7742e5894d7SBenoit Cousson 	interval->min = channels;
7752e5894d7SBenoit Cousson 	interval->max = channels;
7762e5894d7SBenoit Cousson }
7772e5894d7SBenoit Cousson 
778244e2936SCharles Keepax static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream,
779244e2936SCharles Keepax 				      struct snd_soc_component *last)
780244e2936SCharles Keepax {
781244e2936SCharles Keepax 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
782244e2936SCharles Keepax 	struct snd_soc_component *component;
783e82ebffcSKuninori Morimoto 	int i, r, ret = 0;
784244e2936SCharles Keepax 
785613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
786244e2936SCharles Keepax 		if (component == last)
787244e2936SCharles Keepax 			break;
788244e2936SCharles Keepax 
789e82ebffcSKuninori Morimoto 		r = snd_soc_component_hw_free(component, substream);
790e82ebffcSKuninori Morimoto 		if (r < 0)
791e82ebffcSKuninori Morimoto 			ret = r; /* use last ret */
792244e2936SCharles Keepax 	}
793244e2936SCharles Keepax 
794eae7136aSKuninori Morimoto 	return ret;
795244e2936SCharles Keepax }
796244e2936SCharles Keepax 
797ddee627cSLiam Girdwood /*
798ddee627cSLiam Girdwood  * Called by ALSA when the hardware params are set by application. This
799ddee627cSLiam Girdwood  * function can also be called multiple times and can allocate buffers
800ddee627cSLiam Girdwood  * (using snd_pcm_lib_* ). It's non-atomic.
801ddee627cSLiam Girdwood  */
802ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
803ddee627cSLiam Girdwood 				struct snd_pcm_hw_params *params)
804ddee627cSLiam Girdwood {
805ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
806b8135864SKuninori Morimoto 	struct snd_soc_component *component;
807ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
8080b7990e3SKuninori Morimoto 	struct snd_soc_dai *codec_dai;
809244e2936SCharles Keepax 	int i, ret = 0;
810ddee627cSLiam Girdwood 
81172b745e3SPeter Ujfalusi 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
8125cca5951SShengjiu Wang 
8135cca5951SShengjiu Wang 	ret = soc_pcm_params_symmetry(substream, params);
8145cca5951SShengjiu Wang 	if (ret)
8155cca5951SShengjiu Wang 		goto out;
8165cca5951SShengjiu Wang 
817de9ad990SKuninori Morimoto 	ret = soc_rtd_hw_params(rtd, substream, params);
818ddee627cSLiam Girdwood 	if (ret < 0) {
819de9ad990SKuninori Morimoto 		dev_err(rtd->card->dev,
820de9ad990SKuninori Morimoto 			"ASoC: machine hw_params failed: %d\n", ret);
821ddee627cSLiam Girdwood 		goto out;
822ddee627cSLiam Girdwood 	}
823ddee627cSLiam Girdwood 
8240b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
8252e5894d7SBenoit Cousson 		struct snd_pcm_hw_params codec_params;
8262e5894d7SBenoit Cousson 
827cde79035SRicard Wanderlof 		/*
828cde79035SRicard Wanderlof 		 * Skip CODECs which don't support the current stream type,
829cde79035SRicard Wanderlof 		 * the idea being that if a CODEC is not used for the currently
830cde79035SRicard Wanderlof 		 * set up transfer direction, it should not need to be
831cde79035SRicard Wanderlof 		 * configured, especially since the configuration used might
832cde79035SRicard Wanderlof 		 * not even be supported by that CODEC. There may be cases
833cde79035SRicard Wanderlof 		 * however where a CODEC needs to be set up although it is
834cde79035SRicard Wanderlof 		 * actually not being used for the transfer, e.g. if a
835cde79035SRicard Wanderlof 		 * capture-only CODEC is acting as an LRCLK and/or BCLK master
836cde79035SRicard Wanderlof 		 * for the DAI link including a playback-only CODEC.
837cde79035SRicard Wanderlof 		 * If this becomes necessary, we will have to augment the
838cde79035SRicard Wanderlof 		 * machine driver setup with information on how to act, so
839cde79035SRicard Wanderlof 		 * we can do the right thing here.
840cde79035SRicard Wanderlof 		 */
841cde79035SRicard Wanderlof 		if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
842cde79035SRicard Wanderlof 			continue;
843cde79035SRicard Wanderlof 
8442e5894d7SBenoit Cousson 		/* copy params for each codec */
8452e5894d7SBenoit Cousson 		codec_params = *params;
8462e5894d7SBenoit Cousson 
8472e5894d7SBenoit Cousson 		/* fixup params based on TDM slot masks */
848570f18b6SRander Wang 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
849570f18b6SRander Wang 		    codec_dai->tx_mask)
8502e5894d7SBenoit Cousson 			soc_pcm_codec_params_fixup(&codec_params,
8512e5894d7SBenoit Cousson 						   codec_dai->tx_mask);
852570f18b6SRander Wang 
853570f18b6SRander Wang 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
854570f18b6SRander Wang 		    codec_dai->rx_mask)
8552e5894d7SBenoit Cousson 			soc_pcm_codec_params_fixup(&codec_params,
8562e5894d7SBenoit Cousson 						   codec_dai->rx_mask);
8572e5894d7SBenoit Cousson 
858aa6166c2SKuninori Morimoto 		ret = snd_soc_dai_hw_params(codec_dai, substream,
859aa6166c2SKuninori Morimoto 					    &codec_params);
86093e6958aSBenoit Cousson 		if(ret < 0)
861ddee627cSLiam Girdwood 			goto codec_err;
862ddee627cSLiam Girdwood 
8632e5894d7SBenoit Cousson 		codec_dai->rate = params_rate(&codec_params);
8642e5894d7SBenoit Cousson 		codec_dai->channels = params_channels(&codec_params);
8652e5894d7SBenoit Cousson 		codec_dai->sample_bits = snd_pcm_format_physical_width(
8662e5894d7SBenoit Cousson 						params_format(&codec_params));
867078a85f2SCharles Keepax 
868078a85f2SCharles Keepax 		snd_soc_dapm_update_dai(substream, &codec_params, codec_dai);
869ddee627cSLiam Girdwood 	}
870ddee627cSLiam Girdwood 
871aa6166c2SKuninori Morimoto 	ret = snd_soc_dai_hw_params(cpu_dai, substream, params);
87293e6958aSBenoit Cousson 	if (ret < 0)
873ddee627cSLiam Girdwood 		goto interface_err;
874ddee627cSLiam Girdwood 
875ca58221dSKuninori Morimoto 	/* store the parameters for each DAIs */
876ca58221dSKuninori Morimoto 	cpu_dai->rate = params_rate(params);
877ca58221dSKuninori Morimoto 	cpu_dai->channels = params_channels(params);
878ca58221dSKuninori Morimoto 	cpu_dai->sample_bits =
879ca58221dSKuninori Morimoto 		snd_pcm_format_physical_width(params_format(params));
880ca58221dSKuninori Morimoto 
881ca58221dSKuninori Morimoto 	snd_soc_dapm_update_dai(substream, params, cpu_dai);
882ca58221dSKuninori Morimoto 
883613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
884245c539aSKuninori Morimoto 		ret = snd_soc_component_hw_params(component, substream, params);
885244e2936SCharles Keepax 		if (ret < 0) {
886b8135864SKuninori Morimoto 			dev_err(component->dev,
887b8135864SKuninori Morimoto 				"ASoC: %s hw params failed: %d\n",
888244e2936SCharles Keepax 				component->name, ret);
889b8135864SKuninori Morimoto 			goto component_err;
890244e2936SCharles Keepax 		}
891244e2936SCharles Keepax 	}
892244e2936SCharles Keepax 	component = NULL;
893b8135864SKuninori Morimoto 
894ddee627cSLiam Girdwood out:
89572b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
896ddee627cSLiam Girdwood 	return ret;
897ddee627cSLiam Girdwood 
898b8135864SKuninori Morimoto component_err:
899244e2936SCharles Keepax 	soc_pcm_components_hw_free(substream, component);
900b8135864SKuninori Morimoto 
901846faaedSKuninori Morimoto 	snd_soc_dai_hw_free(cpu_dai, substream);
9022371abdcSKuninori Morimoto 	cpu_dai->rate = 0;
903ddee627cSLiam Girdwood 
904ddee627cSLiam Girdwood interface_err:
9052e5894d7SBenoit Cousson 	i = rtd->num_codecs;
906ddee627cSLiam Girdwood 
907ddee627cSLiam Girdwood codec_err:
9086d11b128SKuninori Morimoto 	for_each_rtd_codec_dai_rollback(rtd, i, codec_dai) {
909f47b9ad9SJerome Brunet 		if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
910f47b9ad9SJerome Brunet 			continue;
911f47b9ad9SJerome Brunet 
912846faaedSKuninori Morimoto 		snd_soc_dai_hw_free(codec_dai, substream);
9132e5894d7SBenoit Cousson 		codec_dai->rate = 0;
9142e5894d7SBenoit Cousson 	}
9152e5894d7SBenoit Cousson 
91649f020e5SKuninori Morimoto 	soc_rtd_hw_free(rtd, substream);
917ddee627cSLiam Girdwood 
91872b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
919ddee627cSLiam Girdwood 	return ret;
920ddee627cSLiam Girdwood }
921ddee627cSLiam Girdwood 
922ddee627cSLiam Girdwood /*
923ddee627cSLiam Girdwood  * Frees resources allocated by hw_params, can be called multiple times
924ddee627cSLiam Girdwood  */
925ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
926ddee627cSLiam Girdwood {
927ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
928ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
9292e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
9307f62b6eeSNicolin Chen 	bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
9312e5894d7SBenoit Cousson 	int i;
932ddee627cSLiam Girdwood 
93372b745e3SPeter Ujfalusi 	mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
934ddee627cSLiam Girdwood 
935d3383420SNicolin Chen 	/* clear the corresponding DAIs parameters when going to be inactive */
936d3383420SNicolin Chen 	if (cpu_dai->active == 1) {
937d3383420SNicolin Chen 		cpu_dai->rate = 0;
938d3383420SNicolin Chen 		cpu_dai->channels = 0;
939d3383420SNicolin Chen 		cpu_dai->sample_bits = 0;
940d3383420SNicolin Chen 	}
941d3383420SNicolin Chen 
9420b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
943d3383420SNicolin Chen 		if (codec_dai->active == 1) {
944d3383420SNicolin Chen 			codec_dai->rate = 0;
945d3383420SNicolin Chen 			codec_dai->channels = 0;
946d3383420SNicolin Chen 			codec_dai->sample_bits = 0;
947d3383420SNicolin Chen 		}
9482e5894d7SBenoit Cousson 	}
949d3383420SNicolin Chen 
950ddee627cSLiam Girdwood 	/* apply codec digital mute */
9510b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
9520f6011fdSKuninori Morimoto 		int playback_active = codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK];
9530f6011fdSKuninori Morimoto 		int capture_active  = codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE];
9540f6011fdSKuninori Morimoto 
9550f6011fdSKuninori Morimoto 		if ((playback && playback_active == 1) ||
9560f6011fdSKuninori Morimoto 		    (!playback && capture_active == 1))
9570b7990e3SKuninori Morimoto 			snd_soc_dai_digital_mute(codec_dai, 1,
9582e5894d7SBenoit Cousson 						 substream->stream);
9592e5894d7SBenoit Cousson 	}
960ddee627cSLiam Girdwood 
961ddee627cSLiam Girdwood 	/* free any machine hw params */
96249f020e5SKuninori Morimoto 	soc_rtd_hw_free(rtd, substream);
963ddee627cSLiam Girdwood 
964b8135864SKuninori Morimoto 	/* free any component resources */
965244e2936SCharles Keepax 	soc_pcm_components_hw_free(substream, NULL);
966b8135864SKuninori Morimoto 
967ddee627cSLiam Girdwood 	/* now free hw params for the DAIs  */
9680b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
969f47b9ad9SJerome Brunet 		if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
970f47b9ad9SJerome Brunet 			continue;
971f47b9ad9SJerome Brunet 
972846faaedSKuninori Morimoto 		snd_soc_dai_hw_free(codec_dai, substream);
9732e5894d7SBenoit Cousson 	}
974ddee627cSLiam Girdwood 
975846faaedSKuninori Morimoto 	snd_soc_dai_hw_free(cpu_dai, substream);
976ddee627cSLiam Girdwood 
97772b745e3SPeter Ujfalusi 	mutex_unlock(&rtd->card->pcm_mutex);
978ddee627cSLiam Girdwood 	return 0;
979ddee627cSLiam Girdwood }
980ddee627cSLiam Girdwood 
9814378f1fbSPeter Ujfalusi static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd)
982ddee627cSLiam Girdwood {
983ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
984b8135864SKuninori Morimoto 	struct snd_soc_component *component;
985ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
9862e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
9872e5894d7SBenoit Cousson 	int i, ret;
988ddee627cSLiam Girdwood 
989ad2bf9f2SKuninori Morimoto 	ret = soc_rtd_trigger(rtd, substream, cmd);
990ddee627cSLiam Girdwood 	if (ret < 0)
991ddee627cSLiam Girdwood 		return ret;
992ddee627cSLiam Girdwood 
993613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
9945693d50cSKuninori Morimoto 		ret = snd_soc_component_trigger(component, substream, cmd);
995b8135864SKuninori Morimoto 		if (ret < 0)
996b8135864SKuninori Morimoto 			return ret;
997b8135864SKuninori Morimoto 	}
998b8135864SKuninori Morimoto 
999901e822bSDan Carpenter 	ret = snd_soc_dai_trigger(cpu_dai, substream, cmd);
1000ddee627cSLiam Girdwood 	if (ret < 0)
1001ddee627cSLiam Girdwood 		return ret;
10024792b0dbSJarkko Nikula 
10034378f1fbSPeter Ujfalusi 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
10044378f1fbSPeter Ujfalusi 		ret = snd_soc_dai_trigger(codec_dai, substream, cmd);
10054378f1fbSPeter Ujfalusi 		if (ret < 0)
10064378f1fbSPeter Ujfalusi 			return ret;
10074378f1fbSPeter Ujfalusi 	}
10084378f1fbSPeter Ujfalusi 
10094378f1fbSPeter Ujfalusi 	return 0;
10104378f1fbSPeter Ujfalusi }
10114378f1fbSPeter Ujfalusi 
10124378f1fbSPeter Ujfalusi static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd)
10134378f1fbSPeter Ujfalusi {
10144378f1fbSPeter Ujfalusi 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
10154378f1fbSPeter Ujfalusi 	struct snd_soc_component *component;
10164378f1fbSPeter Ujfalusi 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
10174378f1fbSPeter Ujfalusi 	struct snd_soc_dai *codec_dai;
10184378f1fbSPeter Ujfalusi 	int i, ret;
10194378f1fbSPeter Ujfalusi 
10204378f1fbSPeter Ujfalusi 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
10214378f1fbSPeter Ujfalusi 		ret = snd_soc_dai_trigger(codec_dai, substream, cmd);
10224378f1fbSPeter Ujfalusi 		if (ret < 0)
10234378f1fbSPeter Ujfalusi 			return ret;
10244378f1fbSPeter Ujfalusi 	}
10254378f1fbSPeter Ujfalusi 
10264378f1fbSPeter Ujfalusi 	ret = snd_soc_dai_trigger(cpu_dai, substream, cmd);
10274378f1fbSPeter Ujfalusi 	if (ret < 0)
10284378f1fbSPeter Ujfalusi 		return ret;
10294378f1fbSPeter Ujfalusi 
1030613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
10314378f1fbSPeter Ujfalusi 		ret = snd_soc_component_trigger(component, substream, cmd);
10324378f1fbSPeter Ujfalusi 		if (ret < 0)
10334378f1fbSPeter Ujfalusi 			return ret;
10344378f1fbSPeter Ujfalusi 	}
10354378f1fbSPeter Ujfalusi 
1036ad2bf9f2SKuninori Morimoto 	ret = soc_rtd_trigger(rtd, substream, cmd);
10374792b0dbSJarkko Nikula 	if (ret < 0)
10384792b0dbSJarkko Nikula 		return ret;
10394792b0dbSJarkko Nikula 
1040ddee627cSLiam Girdwood 	return 0;
1041ddee627cSLiam Girdwood }
1042ddee627cSLiam Girdwood 
10434378f1fbSPeter Ujfalusi static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
10444378f1fbSPeter Ujfalusi {
10454378f1fbSPeter Ujfalusi 	int ret;
10464378f1fbSPeter Ujfalusi 
10474378f1fbSPeter Ujfalusi 	switch (cmd) {
10484378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_START:
10494378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_RESUME:
10504378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
10514378f1fbSPeter Ujfalusi 		ret = soc_pcm_trigger_start(substream, cmd);
10524378f1fbSPeter Ujfalusi 		break;
10534378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_STOP:
10544378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_SUSPEND:
10554378f1fbSPeter Ujfalusi 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
10564378f1fbSPeter Ujfalusi 		ret = soc_pcm_trigger_stop(substream, cmd);
10574378f1fbSPeter Ujfalusi 		break;
10584378f1fbSPeter Ujfalusi 	default:
10594378f1fbSPeter Ujfalusi 		return -EINVAL;
10604378f1fbSPeter Ujfalusi 	}
10614378f1fbSPeter Ujfalusi 
10624378f1fbSPeter Ujfalusi 	return ret;
10634378f1fbSPeter Ujfalusi }
10644378f1fbSPeter Ujfalusi 
106545c0a188SMark Brown static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
106645c0a188SMark Brown 				   int cmd)
106707bf84aaSLiam Girdwood {
106807bf84aaSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
106907bf84aaSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
10702e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
10712e5894d7SBenoit Cousson 	int i, ret;
107207bf84aaSLiam Girdwood 
10730b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
10745c0769afSKuninori Morimoto 		ret = snd_soc_dai_bespoke_trigger(codec_dai, substream, cmd);
107507bf84aaSLiam Girdwood 		if (ret < 0)
107607bf84aaSLiam Girdwood 			return ret;
107707bf84aaSLiam Girdwood 	}
107807bf84aaSLiam Girdwood 
1079901e822bSDan Carpenter 	ret = snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd);
108007bf84aaSLiam Girdwood 	if (ret < 0)
108107bf84aaSLiam Girdwood 		return ret;
10825c0769afSKuninori Morimoto 
108307bf84aaSLiam Girdwood 	return 0;
108407bf84aaSLiam Girdwood }
1085ddee627cSLiam Girdwood /*
1086ddee627cSLiam Girdwood  * soc level wrapper for pointer callback
1087ef050becSCharles Keepax  * If cpu_dai, codec_dai, component driver has the delay callback, then
1088ddee627cSLiam Girdwood  * the runtime->delay will be updated accordingly.
1089ddee627cSLiam Girdwood  */
1090ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
1091ddee627cSLiam Girdwood {
1092ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1093ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
10942e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
1095ddee627cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
1096ddee627cSLiam Girdwood 	snd_pcm_uframes_t offset = 0;
1097ddee627cSLiam Girdwood 	snd_pcm_sframes_t delay = 0;
10982e5894d7SBenoit Cousson 	snd_pcm_sframes_t codec_delay = 0;
10992e5894d7SBenoit Cousson 	int i;
1100ddee627cSLiam Girdwood 
11019fb4c2bfSAkshu Agrawal 	/* clearing the previous total delay */
11029fb4c2bfSAkshu Agrawal 	runtime->delay = 0;
11039fb4c2bfSAkshu Agrawal 
11040035e256SKuninori Morimoto 	offset = snd_soc_pcm_component_pointer(substream);
1105b8135864SKuninori Morimoto 
11069fb4c2bfSAkshu Agrawal 	/* base delay if assigned in pointer callback */
11079fb4c2bfSAkshu Agrawal 	delay = runtime->delay;
1108b8135864SKuninori Morimoto 
11091dea80d4SKuninori Morimoto 	delay += snd_soc_dai_delay(cpu_dai, substream);
1110ddee627cSLiam Girdwood 
11110b7990e3SKuninori Morimoto 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
11122e5894d7SBenoit Cousson 		codec_delay = max(codec_delay,
11131dea80d4SKuninori Morimoto 				  snd_soc_dai_delay(codec_dai, substream));
11142e5894d7SBenoit Cousson 	}
11152e5894d7SBenoit Cousson 	delay += codec_delay;
1116ddee627cSLiam Girdwood 
1117ddee627cSLiam Girdwood 	runtime->delay = delay;
1118ddee627cSLiam Girdwood 
1119ddee627cSLiam Girdwood 	return offset;
1120ddee627cSLiam Girdwood }
1121ddee627cSLiam Girdwood 
112201d7584cSLiam Girdwood /* connect a FE and BE */
112301d7584cSLiam Girdwood static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
112401d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
112501d7584cSLiam Girdwood {
112601d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1127a9764869SKaiChieh Chuang 	unsigned long flags;
1128bd0b609eSTakashi Iwai #ifdef CONFIG_DEBUG_FS
11290632fa04SHans de Goede 	char *name;
1130bd0b609eSTakashi Iwai #endif
113101d7584cSLiam Girdwood 
113201d7584cSLiam Girdwood 	/* only add new dpcms */
11338d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
113401d7584cSLiam Girdwood 		if (dpcm->be == be && dpcm->fe == fe)
113501d7584cSLiam Girdwood 			return 0;
113601d7584cSLiam Girdwood 	}
113701d7584cSLiam Girdwood 
113801d7584cSLiam Girdwood 	dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL);
113901d7584cSLiam Girdwood 	if (!dpcm)
114001d7584cSLiam Girdwood 		return -ENOMEM;
114101d7584cSLiam Girdwood 
114201d7584cSLiam Girdwood 	dpcm->be = be;
114301d7584cSLiam Girdwood 	dpcm->fe = fe;
114401d7584cSLiam Girdwood 	be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
114501d7584cSLiam Girdwood 	dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
1146a9764869SKaiChieh Chuang 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
114701d7584cSLiam Girdwood 	list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
114801d7584cSLiam Girdwood 	list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
1149a9764869SKaiChieh Chuang 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
115001d7584cSLiam Girdwood 
115101d7584cSLiam Girdwood 	dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n",
115201d7584cSLiam Girdwood 			stream ? "capture" : "playback",  fe->dai_link->name,
115301d7584cSLiam Girdwood 			stream ? "<-" : "->", be->dai_link->name);
115401d7584cSLiam Girdwood 
1155f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS
11560632fa04SHans de Goede 	name = kasprintf(GFP_KERNEL, "%s:%s", be->dai_link->name,
11570632fa04SHans de Goede 			 stream ? "capture" : "playback");
11580632fa04SHans de Goede 	if (name) {
11590632fa04SHans de Goede 		dpcm->debugfs_state = debugfs_create_dir(name,
1160fee531d6SGreg Kroah-Hartman 							 fe->debugfs_dpcm_root);
11610632fa04SHans de Goede 		debugfs_create_u32("state", 0644, dpcm->debugfs_state,
11620632fa04SHans de Goede 				   &dpcm->state);
11630632fa04SHans de Goede 		kfree(name);
11640632fa04SHans de Goede 	}
1165f86dcef8SLiam Girdwood #endif
116601d7584cSLiam Girdwood 	return 1;
116701d7584cSLiam Girdwood }
116801d7584cSLiam Girdwood 
116901d7584cSLiam Girdwood /* reparent a BE onto another FE */
117001d7584cSLiam Girdwood static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
117101d7584cSLiam Girdwood 			struct snd_soc_pcm_runtime *be, int stream)
117201d7584cSLiam Girdwood {
117301d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
117401d7584cSLiam Girdwood 	struct snd_pcm_substream *fe_substream, *be_substream;
117501d7584cSLiam Girdwood 
117601d7584cSLiam Girdwood 	/* reparent if BE is connected to other FEs */
117701d7584cSLiam Girdwood 	if (!be->dpcm[stream].users)
117801d7584cSLiam Girdwood 		return;
117901d7584cSLiam Girdwood 
118001d7584cSLiam Girdwood 	be_substream = snd_soc_dpcm_get_substream(be, stream);
118101d7584cSLiam Girdwood 
1182d2e24d64SKuninori Morimoto 	for_each_dpcm_fe(be, stream, dpcm) {
118301d7584cSLiam Girdwood 		if (dpcm->fe == fe)
118401d7584cSLiam Girdwood 			continue;
118501d7584cSLiam Girdwood 
118601d7584cSLiam Girdwood 		dev_dbg(fe->dev, "reparent %s path %s %s %s\n",
118701d7584cSLiam Girdwood 			stream ? "capture" : "playback",
118801d7584cSLiam Girdwood 			dpcm->fe->dai_link->name,
118901d7584cSLiam Girdwood 			stream ? "<-" : "->", dpcm->be->dai_link->name);
119001d7584cSLiam Girdwood 
119101d7584cSLiam Girdwood 		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream);
119201d7584cSLiam Girdwood 		be_substream->runtime = fe_substream->runtime;
119301d7584cSLiam Girdwood 		break;
119401d7584cSLiam Girdwood 	}
119501d7584cSLiam Girdwood }
119601d7584cSLiam Girdwood 
119701d7584cSLiam Girdwood /* disconnect a BE and FE */
119823607025SLiam Girdwood void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
119901d7584cSLiam Girdwood {
120001d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm, *d;
1201a9764869SKaiChieh Chuang 	unsigned long flags;
120201d7584cSLiam Girdwood 
12038d6258a4SKuninori Morimoto 	for_each_dpcm_be_safe(fe, stream, dpcm, d) {
1204103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n",
120501d7584cSLiam Girdwood 				stream ? "capture" : "playback",
120601d7584cSLiam Girdwood 				dpcm->be->dai_link->name);
120701d7584cSLiam Girdwood 
120801d7584cSLiam Girdwood 		if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE)
120901d7584cSLiam Girdwood 			continue;
121001d7584cSLiam Girdwood 
121101d7584cSLiam Girdwood 		dev_dbg(fe->dev, "freed DSP %s path %s %s %s\n",
121201d7584cSLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name,
121301d7584cSLiam Girdwood 			stream ? "<-" : "->", dpcm->be->dai_link->name);
121401d7584cSLiam Girdwood 
121501d7584cSLiam Girdwood 		/* BEs still alive need new FE */
121601d7584cSLiam Girdwood 		dpcm_be_reparent(fe, dpcm->be, stream);
121701d7584cSLiam Girdwood 
1218f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS
1219fee531d6SGreg Kroah-Hartman 		debugfs_remove_recursive(dpcm->debugfs_state);
1220f86dcef8SLiam Girdwood #endif
1221a9764869SKaiChieh Chuang 		spin_lock_irqsave(&fe->card->dpcm_lock, flags);
122201d7584cSLiam Girdwood 		list_del(&dpcm->list_be);
122301d7584cSLiam Girdwood 		list_del(&dpcm->list_fe);
1224a9764869SKaiChieh Chuang 		spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
122501d7584cSLiam Girdwood 		kfree(dpcm);
122601d7584cSLiam Girdwood 	}
122701d7584cSLiam Girdwood }
122801d7584cSLiam Girdwood 
122901d7584cSLiam Girdwood /* get BE for DAI widget and stream */
123001d7584cSLiam Girdwood static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
123101d7584cSLiam Girdwood 		struct snd_soc_dapm_widget *widget, int stream)
123201d7584cSLiam Girdwood {
123301d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *be;
123493597faeSKuninori Morimoto 	struct snd_soc_dapm_widget *w;
12357afecb30SKuninori Morimoto 	struct snd_soc_dai *dai;
12361a497983SMengdong Lin 	int i;
123701d7584cSLiam Girdwood 
12383c146465SLiam Girdwood 	dev_dbg(card->dev, "ASoC: find BE for widget %s\n", widget->name);
12393c146465SLiam Girdwood 
1240bcb1fd1fSKuninori Morimoto 	for_each_card_rtds(card, be) {
124101d7584cSLiam Girdwood 
124235ea0655SLiam Girdwood 		if (!be->dai_link->no_pcm)
124335ea0655SLiam Girdwood 			continue;
124435ea0655SLiam Girdwood 
124593597faeSKuninori Morimoto 		w = dai_get_widget(be->cpu_dai, stream);
124693597faeSKuninori Morimoto 
12473c146465SLiam Girdwood 		dev_dbg(card->dev, "ASoC: try BE : %s\n",
124893597faeSKuninori Morimoto 			w ? w->name : "(not set)");
12493c146465SLiam Girdwood 
125093597faeSKuninori Morimoto 		if (w == widget)
125101d7584cSLiam Girdwood 			return be;
12522e5894d7SBenoit Cousson 
12537afecb30SKuninori Morimoto 		for_each_rtd_codec_dai(be, i, dai) {
125493597faeSKuninori Morimoto 			w = dai_get_widget(dai, stream);
125593597faeSKuninori Morimoto 
125693597faeSKuninori Morimoto 			if (w == widget)
12572e5894d7SBenoit Cousson 				return be;
12582e5894d7SBenoit Cousson 		}
125901d7584cSLiam Girdwood 	}
126001d7584cSLiam Girdwood 
12619d6ee365SJerome Brunet 	/* Widget provided is not a BE */
126201d7584cSLiam Girdwood 	return NULL;
126301d7584cSLiam Girdwood }
126401d7584cSLiam Girdwood 
126501d7584cSLiam Girdwood static int widget_in_list(struct snd_soc_dapm_widget_list *list,
126601d7584cSLiam Girdwood 		struct snd_soc_dapm_widget *widget)
126701d7584cSLiam Girdwood {
126809e88f8aSKuninori Morimoto 	struct snd_soc_dapm_widget *w;
126901d7584cSLiam Girdwood 	int i;
127001d7584cSLiam Girdwood 
127109e88f8aSKuninori Morimoto 	for_each_dapm_widgets(list, i, w)
127209e88f8aSKuninori Morimoto 		if (widget == w)
127301d7584cSLiam Girdwood 			return 1;
127401d7584cSLiam Girdwood 
127501d7584cSLiam Girdwood 	return 0;
127601d7584cSLiam Girdwood }
127701d7584cSLiam Girdwood 
12785fdd022cSPiotr Stankiewicz static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
12795fdd022cSPiotr Stankiewicz 		enum snd_soc_dapm_direction dir)
12805fdd022cSPiotr Stankiewicz {
12815fdd022cSPiotr Stankiewicz 	struct snd_soc_card *card = widget->dapm->card;
12825fdd022cSPiotr Stankiewicz 	struct snd_soc_pcm_runtime *rtd;
1283c2cd8216SKuninori Morimoto 	int stream;
12845fdd022cSPiotr Stankiewicz 
1285c2cd8216SKuninori Morimoto 	/* adjust dir to stream */
1286c2cd8216SKuninori Morimoto 	if (dir == SND_SOC_DAPM_DIR_OUT)
1287c2cd8216SKuninori Morimoto 		stream = SNDRV_PCM_STREAM_PLAYBACK;
1288c2cd8216SKuninori Morimoto 	else
1289c2cd8216SKuninori Morimoto 		stream = SNDRV_PCM_STREAM_CAPTURE;
1290c2cd8216SKuninori Morimoto 
1291027a4838SKuninori Morimoto 	rtd = dpcm_get_be(card, widget, stream);
1292027a4838SKuninori Morimoto 	if (rtd)
12935fdd022cSPiotr Stankiewicz 		return true;
12945fdd022cSPiotr Stankiewicz 
12955fdd022cSPiotr Stankiewicz 	return false;
12965fdd022cSPiotr Stankiewicz }
12975fdd022cSPiotr Stankiewicz 
129823607025SLiam Girdwood int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
12991ce43acfSLars-Peter Clausen 	int stream, struct snd_soc_dapm_widget_list **list)
130001d7584cSLiam Girdwood {
130101d7584cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
130201d7584cSLiam Girdwood 	int paths;
130301d7584cSLiam Girdwood 
130401d7584cSLiam Girdwood 	/* get number of valid DAI paths and their widgets */
13056742064aSPiotr Stankiewicz 	paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list,
13065fdd022cSPiotr Stankiewicz 			dpcm_end_walk_at_be);
130701d7584cSLiam Girdwood 
1308103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
130901d7584cSLiam Girdwood 			stream ? "capture" : "playback");
131001d7584cSLiam Girdwood 
131101d7584cSLiam Girdwood 	return paths;
131201d7584cSLiam Girdwood }
131301d7584cSLiam Girdwood 
131401d7584cSLiam Girdwood static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
131501d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list **list_)
131601d7584cSLiam Girdwood {
131701d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
131801d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list = *list_;
131901d7584cSLiam Girdwood 	struct snd_soc_dapm_widget *widget;
13207afecb30SKuninori Morimoto 	struct snd_soc_dai *dai;
132101d7584cSLiam Girdwood 	int prune = 0;
1322bed646dcSKuninori Morimoto 	int do_prune;
132301d7584cSLiam Girdwood 
132401d7584cSLiam Girdwood 	/* Destroy any old FE <--> BE connections */
13258d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
13262e5894d7SBenoit Cousson 		unsigned int i;
132701d7584cSLiam Girdwood 
132801d7584cSLiam Girdwood 		/* is there a valid CPU DAI widget for this BE */
132937018610SBenoit Cousson 		widget = dai_get_widget(dpcm->be->cpu_dai, stream);
133001d7584cSLiam Girdwood 
133101d7584cSLiam Girdwood 		/* prune the BE if it's no longer in our active list */
133201d7584cSLiam Girdwood 		if (widget && widget_in_list(list, widget))
133301d7584cSLiam Girdwood 			continue;
133401d7584cSLiam Girdwood 
133501d7584cSLiam Girdwood 		/* is there a valid CODEC DAI widget for this BE */
1336bed646dcSKuninori Morimoto 		do_prune = 1;
13377afecb30SKuninori Morimoto 		for_each_rtd_codec_dai(dpcm->be, i, dai) {
13382e5894d7SBenoit Cousson 			widget = dai_get_widget(dai, stream);
133901d7584cSLiam Girdwood 
134001d7584cSLiam Girdwood 			/* prune the BE if it's no longer in our active list */
134101d7584cSLiam Girdwood 			if (widget && widget_in_list(list, widget))
1342bed646dcSKuninori Morimoto 				do_prune = 0;
13432e5894d7SBenoit Cousson 		}
1344bed646dcSKuninori Morimoto 		if (!do_prune)
1345bed646dcSKuninori Morimoto 			continue;
134601d7584cSLiam Girdwood 
1347103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
134801d7584cSLiam Girdwood 			stream ? "capture" : "playback",
134901d7584cSLiam Girdwood 			dpcm->be->dai_link->name, fe->dai_link->name);
135001d7584cSLiam Girdwood 		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
135101d7584cSLiam Girdwood 		dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
135201d7584cSLiam Girdwood 		prune++;
135301d7584cSLiam Girdwood 	}
135401d7584cSLiam Girdwood 
1355103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: found %d old BE paths for pruning\n", prune);
135601d7584cSLiam Girdwood 	return prune;
135701d7584cSLiam Girdwood }
135801d7584cSLiam Girdwood 
135901d7584cSLiam Girdwood static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
136001d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list **list_)
136101d7584cSLiam Girdwood {
136201d7584cSLiam Girdwood 	struct snd_soc_card *card = fe->card;
136301d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list = *list_;
136401d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *be;
136509e88f8aSKuninori Morimoto 	struct snd_soc_dapm_widget *widget;
136601d7584cSLiam Girdwood 	int i, new = 0, err;
136701d7584cSLiam Girdwood 
136801d7584cSLiam Girdwood 	/* Create any new FE <--> BE connections */
136909e88f8aSKuninori Morimoto 	for_each_dapm_widgets(list, i, widget) {
137001d7584cSLiam Girdwood 
137109e88f8aSKuninori Morimoto 		switch (widget->id) {
13724616274dSMark Brown 		case snd_soc_dapm_dai_in:
1373c5b8540dSKoro Chen 			if (stream != SNDRV_PCM_STREAM_PLAYBACK)
1374c5b8540dSKoro Chen 				continue;
1375c5b8540dSKoro Chen 			break;
13764616274dSMark Brown 		case snd_soc_dapm_dai_out:
1377c5b8540dSKoro Chen 			if (stream != SNDRV_PCM_STREAM_CAPTURE)
1378c5b8540dSKoro Chen 				continue;
13794616274dSMark Brown 			break;
13804616274dSMark Brown 		default:
138101d7584cSLiam Girdwood 			continue;
13824616274dSMark Brown 		}
138301d7584cSLiam Girdwood 
138401d7584cSLiam Girdwood 		/* is there a valid BE rtd for this widget */
138509e88f8aSKuninori Morimoto 		be = dpcm_get_be(card, widget, stream);
138601d7584cSLiam Girdwood 		if (!be) {
1387103d84a3SLiam Girdwood 			dev_err(fe->dev, "ASoC: no BE found for %s\n",
138809e88f8aSKuninori Morimoto 					widget->name);
138901d7584cSLiam Girdwood 			continue;
139001d7584cSLiam Girdwood 		}
139101d7584cSLiam Girdwood 
139201d7584cSLiam Girdwood 		/* make sure BE is a real BE */
139301d7584cSLiam Girdwood 		if (!be->dai_link->no_pcm)
139401d7584cSLiam Girdwood 			continue;
139501d7584cSLiam Girdwood 
139601d7584cSLiam Girdwood 		/* don't connect if FE is not running */
139723607025SLiam Girdwood 		if (!fe->dpcm[stream].runtime && !fe->fe_compr)
139801d7584cSLiam Girdwood 			continue;
139901d7584cSLiam Girdwood 
140001d7584cSLiam Girdwood 		/* newly connected FE and BE */
140101d7584cSLiam Girdwood 		err = dpcm_be_connect(fe, be, stream);
140201d7584cSLiam Girdwood 		if (err < 0) {
1403103d84a3SLiam Girdwood 			dev_err(fe->dev, "ASoC: can't connect %s\n",
140409e88f8aSKuninori Morimoto 				widget->name);
140501d7584cSLiam Girdwood 			break;
140601d7584cSLiam Girdwood 		} else if (err == 0) /* already connected */
140701d7584cSLiam Girdwood 			continue;
140801d7584cSLiam Girdwood 
140901d7584cSLiam Girdwood 		/* new */
141001d7584cSLiam Girdwood 		be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
141101d7584cSLiam Girdwood 		new++;
141201d7584cSLiam Girdwood 	}
141301d7584cSLiam Girdwood 
1414103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: found %d new BE paths\n", new);
141501d7584cSLiam Girdwood 	return new;
141601d7584cSLiam Girdwood }
141701d7584cSLiam Girdwood 
141801d7584cSLiam Girdwood /*
141901d7584cSLiam Girdwood  * Find the corresponding BE DAIs that source or sink audio to this
142001d7584cSLiam Girdwood  * FE substream.
142101d7584cSLiam Girdwood  */
142223607025SLiam Girdwood int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
142301d7584cSLiam Girdwood 	int stream, struct snd_soc_dapm_widget_list **list, int new)
142401d7584cSLiam Girdwood {
142501d7584cSLiam Girdwood 	if (new)
142601d7584cSLiam Girdwood 		return dpcm_add_paths(fe, stream, list);
142701d7584cSLiam Girdwood 	else
142801d7584cSLiam Girdwood 		return dpcm_prune_paths(fe, stream, list);
142901d7584cSLiam Girdwood }
143001d7584cSLiam Girdwood 
143123607025SLiam Girdwood void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
143201d7584cSLiam Girdwood {
143301d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
1434a9764869SKaiChieh Chuang 	unsigned long flags;
143501d7584cSLiam Girdwood 
1436a9764869SKaiChieh Chuang 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
14378d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm)
143801d7584cSLiam Girdwood 		dpcm->be->dpcm[stream].runtime_update =
143901d7584cSLiam Girdwood 						SND_SOC_DPCM_UPDATE_NO;
1440a9764869SKaiChieh Chuang 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
144101d7584cSLiam Girdwood }
144201d7584cSLiam Girdwood 
144301d7584cSLiam Girdwood static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
144401d7584cSLiam Girdwood 	int stream)
144501d7584cSLiam Girdwood {
144601d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
144701d7584cSLiam Girdwood 
144801d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
14498d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
145001d7584cSLiam Girdwood 
145101d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
145201d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
145301d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
145401d7584cSLiam Girdwood 
145501d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1456103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: no users %s at close - state %d\n",
145701d7584cSLiam Girdwood 				stream ? "capture" : "playback",
145801d7584cSLiam Girdwood 				be->dpcm[stream].state);
145901d7584cSLiam Girdwood 
146001d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
146101d7584cSLiam Girdwood 			continue;
146201d7584cSLiam Girdwood 
146301d7584cSLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
146401d7584cSLiam Girdwood 			continue;
146501d7584cSLiam Girdwood 
146601d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
146701d7584cSLiam Girdwood 		be_substream->runtime = NULL;
146801d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
146901d7584cSLiam Girdwood 	}
147001d7584cSLiam Girdwood }
147101d7584cSLiam Girdwood 
147223607025SLiam Girdwood int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
147301d7584cSLiam Girdwood {
147401d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
147501d7584cSLiam Girdwood 	int err, count = 0;
147601d7584cSLiam Girdwood 
147701d7584cSLiam Girdwood 	/* only startup BE DAIs that are either sinks or sources to this FE DAI */
14788d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
147901d7584cSLiam Girdwood 
148001d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
148101d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
148201d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
148301d7584cSLiam Girdwood 
14842062b4c5SRussell King - ARM Linux 		if (!be_substream) {
14852062b4c5SRussell King - ARM Linux 			dev_err(be->dev, "ASoC: no backend %s stream\n",
14862062b4c5SRussell King - ARM Linux 				stream ? "capture" : "playback");
14872062b4c5SRussell King - ARM Linux 			continue;
14882062b4c5SRussell King - ARM Linux 		}
14892062b4c5SRussell King - ARM Linux 
149001d7584cSLiam Girdwood 		/* is this op for this BE ? */
149101d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
149201d7584cSLiam Girdwood 			continue;
149301d7584cSLiam Girdwood 
149401d7584cSLiam Girdwood 		/* first time the dpcm is open ? */
149501d7584cSLiam Girdwood 		if (be->dpcm[stream].users == DPCM_MAX_BE_USERS)
1496103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: too many users %s at open %d\n",
149701d7584cSLiam Girdwood 				stream ? "capture" : "playback",
149801d7584cSLiam Girdwood 				be->dpcm[stream].state);
149901d7584cSLiam Girdwood 
150001d7584cSLiam Girdwood 		if (be->dpcm[stream].users++ != 0)
150101d7584cSLiam Girdwood 			continue;
150201d7584cSLiam Girdwood 
150301d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
150401d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
150501d7584cSLiam Girdwood 			continue;
150601d7584cSLiam Girdwood 
15072062b4c5SRussell King - ARM Linux 		dev_dbg(be->dev, "ASoC: open %s BE %s\n",
15082062b4c5SRussell King - ARM Linux 			stream ? "capture" : "playback", be->dai_link->name);
150901d7584cSLiam Girdwood 
151001d7584cSLiam Girdwood 		be_substream->runtime = be->dpcm[stream].runtime;
151101d7584cSLiam Girdwood 		err = soc_pcm_open(be_substream);
151201d7584cSLiam Girdwood 		if (err < 0) {
1513103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: BE open failed %d\n", err);
151401d7584cSLiam Girdwood 			be->dpcm[stream].users--;
151501d7584cSLiam Girdwood 			if (be->dpcm[stream].users < 0)
1516103d84a3SLiam Girdwood 				dev_err(be->dev, "ASoC: no users %s at unwind %d\n",
151701d7584cSLiam Girdwood 					stream ? "capture" : "playback",
151801d7584cSLiam Girdwood 					be->dpcm[stream].state);
151901d7584cSLiam Girdwood 
152001d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
152101d7584cSLiam Girdwood 			goto unwind;
152201d7584cSLiam Girdwood 		}
152301d7584cSLiam Girdwood 
152401d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
152501d7584cSLiam Girdwood 		count++;
152601d7584cSLiam Girdwood 	}
152701d7584cSLiam Girdwood 
152801d7584cSLiam Girdwood 	return count;
152901d7584cSLiam Girdwood 
153001d7584cSLiam Girdwood unwind:
153101d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
15328d6258a4SKuninori Morimoto 	for_each_dpcm_be_rollback(fe, stream, dpcm) {
153301d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
153401d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
153501d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
153601d7584cSLiam Girdwood 
153701d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
153801d7584cSLiam Girdwood 			continue;
153901d7584cSLiam Girdwood 
154001d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1541103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: no users %s at close %d\n",
154201d7584cSLiam Girdwood 				stream ? "capture" : "playback",
154301d7584cSLiam Girdwood 				be->dpcm[stream].state);
154401d7584cSLiam Girdwood 
154501d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
154601d7584cSLiam Girdwood 			continue;
154701d7584cSLiam Girdwood 
154801d7584cSLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
154901d7584cSLiam Girdwood 			continue;
155001d7584cSLiam Girdwood 
155101d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
155201d7584cSLiam Girdwood 		be_substream->runtime = NULL;
155301d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
155401d7584cSLiam Girdwood 	}
155501d7584cSLiam Girdwood 
155601d7584cSLiam Girdwood 	return err;
155701d7584cSLiam Girdwood }
155801d7584cSLiam Girdwood 
155908ae9b45SLars-Peter Clausen static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
1560435ffb76SJerome Brunet 				 struct snd_soc_pcm_stream *stream)
156108ae9b45SLars-Peter Clausen {
156208ae9b45SLars-Peter Clausen 	runtime->hw.rate_min = stream->rate_min;
1563e33ffbd9SCharles Keepax 	runtime->hw.rate_max = min_not_zero(stream->rate_max, UINT_MAX);
156408ae9b45SLars-Peter Clausen 	runtime->hw.channels_min = stream->channels_min;
156508ae9b45SLars-Peter Clausen 	runtime->hw.channels_max = stream->channels_max;
1566002220a9SLars-Peter Clausen 	if (runtime->hw.formats)
1567435ffb76SJerome Brunet 		runtime->hw.formats &= stream->formats;
1568002220a9SLars-Peter Clausen 	else
1569435ffb76SJerome Brunet 		runtime->hw.formats = stream->formats;
157008ae9b45SLars-Peter Clausen 	runtime->hw.rates = stream->rates;
157108ae9b45SLars-Peter Clausen }
157208ae9b45SLars-Peter Clausen 
1573435ffb76SJerome Brunet static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream,
1574435ffb76SJerome Brunet 				      u64 *formats)
1575b073ed4eSKuninori Morimoto {
1576b073ed4eSKuninori Morimoto 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1577b073ed4eSKuninori Morimoto 	struct snd_soc_dpcm *dpcm;
15787afecb30SKuninori Morimoto 	struct snd_soc_dai *dai;
1579b073ed4eSKuninori Morimoto 	int stream = substream->stream;
1580b073ed4eSKuninori Morimoto 
1581b073ed4eSKuninori Morimoto 	if (!fe->dai_link->dpcm_merged_format)
1582435ffb76SJerome Brunet 		return;
1583b073ed4eSKuninori Morimoto 
1584b073ed4eSKuninori Morimoto 	/*
1585b073ed4eSKuninori Morimoto 	 * It returns merged BE codec format
1586b073ed4eSKuninori Morimoto 	 * if FE want to use it (= dpcm_merged_format)
1587b073ed4eSKuninori Morimoto 	 */
1588b073ed4eSKuninori Morimoto 
15898d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
1590b073ed4eSKuninori Morimoto 		struct snd_soc_pcm_runtime *be = dpcm->be;
1591b073ed4eSKuninori Morimoto 		struct snd_soc_pcm_stream *codec_stream;
1592b073ed4eSKuninori Morimoto 		int i;
1593b073ed4eSKuninori Morimoto 
15947afecb30SKuninori Morimoto 		for_each_rtd_codec_dai(be, i, dai) {
15954febced1SJerome Brunet 			/*
15964febced1SJerome Brunet 			 * Skip CODECs which don't support the current stream
15974febced1SJerome Brunet 			 * type. See soc_pcm_init_runtime_hw() for more details
15984febced1SJerome Brunet 			 */
15997afecb30SKuninori Morimoto 			if (!snd_soc_dai_stream_valid(dai, stream))
16004febced1SJerome Brunet 				continue;
16014febced1SJerome Brunet 
1602acf253c1SKuninori Morimoto 			codec_stream = snd_soc_dai_get_pcm_stream(dai, stream);
1603b073ed4eSKuninori Morimoto 
1604435ffb76SJerome Brunet 			*formats &= codec_stream->formats;
1605435ffb76SJerome Brunet 		}
1606b073ed4eSKuninori Morimoto 	}
1607b073ed4eSKuninori Morimoto }
1608b073ed4eSKuninori Morimoto 
1609435ffb76SJerome Brunet static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream,
1610f4c277b8SJiada Wang 				    unsigned int *channels_min,
1611f4c277b8SJiada Wang 				    unsigned int *channels_max)
1612f4c277b8SJiada Wang {
1613f4c277b8SJiada Wang 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1614f4c277b8SJiada Wang 	struct snd_soc_dpcm *dpcm;
1615f4c277b8SJiada Wang 	int stream = substream->stream;
1616f4c277b8SJiada Wang 
1617f4c277b8SJiada Wang 	if (!fe->dai_link->dpcm_merged_chan)
1618f4c277b8SJiada Wang 		return;
1619f4c277b8SJiada Wang 
1620f4c277b8SJiada Wang 	/*
1621f4c277b8SJiada Wang 	 * It returns merged BE codec channel;
1622f4c277b8SJiada Wang 	 * if FE want to use it (= dpcm_merged_chan)
1623f4c277b8SJiada Wang 	 */
1624f4c277b8SJiada Wang 
16258d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
1626f4c277b8SJiada Wang 		struct snd_soc_pcm_runtime *be = dpcm->be;
1627f4c277b8SJiada Wang 		struct snd_soc_pcm_stream *codec_stream;
16284f2bd18bSJerome Brunet 		struct snd_soc_pcm_stream *cpu_stream;
1629f4c277b8SJiada Wang 
1630acf253c1SKuninori Morimoto 		cpu_stream = snd_soc_dai_get_pcm_stream(be->cpu_dai, stream);
16314f2bd18bSJerome Brunet 
16324f2bd18bSJerome Brunet 		*channels_min = max(*channels_min, cpu_stream->channels_min);
16334f2bd18bSJerome Brunet 		*channels_max = min(*channels_max, cpu_stream->channels_max);
16344f2bd18bSJerome Brunet 
16354f2bd18bSJerome Brunet 		/*
16364f2bd18bSJerome Brunet 		 * chan min/max cannot be enforced if there are multiple CODEC
16374f2bd18bSJerome Brunet 		 * DAIs connected to a single CPU DAI, use CPU DAI's directly
16384f2bd18bSJerome Brunet 		 */
16394f2bd18bSJerome Brunet 		if (be->num_codecs == 1) {
1640acf253c1SKuninori Morimoto 			codec_stream = snd_soc_dai_get_pcm_stream(be->codec_dais[0], stream);
1641f4c277b8SJiada Wang 
1642f4c277b8SJiada Wang 			*channels_min = max(*channels_min,
1643f4c277b8SJiada Wang 					    codec_stream->channels_min);
1644f4c277b8SJiada Wang 			*channels_max = min(*channels_max,
1645f4c277b8SJiada Wang 					    codec_stream->channels_max);
1646f4c277b8SJiada Wang 		}
1647f4c277b8SJiada Wang 	}
1648f4c277b8SJiada Wang }
1649f4c277b8SJiada Wang 
1650baacd8d1SJerome Brunet static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream,
1651baacd8d1SJerome Brunet 				    unsigned int *rates,
1652baacd8d1SJerome Brunet 				    unsigned int *rate_min,
1653baacd8d1SJerome Brunet 				    unsigned int *rate_max)
1654baacd8d1SJerome Brunet {
1655baacd8d1SJerome Brunet 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1656baacd8d1SJerome Brunet 	struct snd_soc_dpcm *dpcm;
1657baacd8d1SJerome Brunet 	int stream = substream->stream;
1658baacd8d1SJerome Brunet 
1659baacd8d1SJerome Brunet 	if (!fe->dai_link->dpcm_merged_rate)
1660baacd8d1SJerome Brunet 		return;
1661baacd8d1SJerome Brunet 
1662baacd8d1SJerome Brunet 	/*
1663baacd8d1SJerome Brunet 	 * It returns merged BE codec channel;
1664baacd8d1SJerome Brunet 	 * if FE want to use it (= dpcm_merged_chan)
1665baacd8d1SJerome Brunet 	 */
1666baacd8d1SJerome Brunet 
16678d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
1668baacd8d1SJerome Brunet 		struct snd_soc_pcm_runtime *be = dpcm->be;
1669baacd8d1SJerome Brunet 		struct snd_soc_pcm_stream *codec_stream;
1670baacd8d1SJerome Brunet 		struct snd_soc_pcm_stream *cpu_stream;
16717afecb30SKuninori Morimoto 		struct snd_soc_dai *dai;
1672baacd8d1SJerome Brunet 		int i;
1673baacd8d1SJerome Brunet 
1674acf253c1SKuninori Morimoto 		cpu_stream = snd_soc_dai_get_pcm_stream(be->cpu_dai, stream);
1675baacd8d1SJerome Brunet 
1676baacd8d1SJerome Brunet 		*rate_min = max(*rate_min, cpu_stream->rate_min);
1677baacd8d1SJerome Brunet 		*rate_max = min_not_zero(*rate_max, cpu_stream->rate_max);
1678baacd8d1SJerome Brunet 		*rates = snd_pcm_rate_mask_intersect(*rates, cpu_stream->rates);
1679baacd8d1SJerome Brunet 
16807afecb30SKuninori Morimoto 		for_each_rtd_codec_dai(be, i, dai) {
1681baacd8d1SJerome Brunet 			/*
1682baacd8d1SJerome Brunet 			 * Skip CODECs which don't support the current stream
1683baacd8d1SJerome Brunet 			 * type. See soc_pcm_init_runtime_hw() for more details
1684baacd8d1SJerome Brunet 			 */
16857afecb30SKuninori Morimoto 			if (!snd_soc_dai_stream_valid(dai, stream))
1686baacd8d1SJerome Brunet 				continue;
1687baacd8d1SJerome Brunet 
1688acf253c1SKuninori Morimoto 			codec_stream = snd_soc_dai_get_pcm_stream(dai, stream);
1689baacd8d1SJerome Brunet 
1690baacd8d1SJerome Brunet 			*rate_min = max(*rate_min, codec_stream->rate_min);
1691baacd8d1SJerome Brunet 			*rate_max = min_not_zero(*rate_max,
1692baacd8d1SJerome Brunet 						 codec_stream->rate_max);
1693baacd8d1SJerome Brunet 			*rates = snd_pcm_rate_mask_intersect(*rates,
1694baacd8d1SJerome Brunet 						codec_stream->rates);
1695baacd8d1SJerome Brunet 		}
1696baacd8d1SJerome Brunet 	}
1697baacd8d1SJerome Brunet }
1698baacd8d1SJerome Brunet 
169945c0a188SMark Brown static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
170001d7584cSLiam Girdwood {
170101d7584cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
170201d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
170301d7584cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
170401d7584cSLiam Girdwood 	struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
170501d7584cSLiam Girdwood 
170608ae9b45SLars-Peter Clausen 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1707435ffb76SJerome Brunet 		dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback);
170808ae9b45SLars-Peter Clausen 	else
1709435ffb76SJerome Brunet 		dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
1710f4c277b8SJiada Wang 
1711435ffb76SJerome Brunet 	dpcm_runtime_merge_format(substream, &runtime->hw.formats);
1712435ffb76SJerome Brunet 	dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min,
1713435ffb76SJerome Brunet 				&runtime->hw.channels_max);
1714baacd8d1SJerome Brunet 	dpcm_runtime_merge_rate(substream, &runtime->hw.rates,
1715baacd8d1SJerome Brunet 				&runtime->hw.rate_min, &runtime->hw.rate_max);
171601d7584cSLiam Girdwood }
171701d7584cSLiam Girdwood 
1718ea9d0d77STakashi Iwai static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
1719ea9d0d77STakashi Iwai 
1720ea9d0d77STakashi Iwai /* Set FE's runtime_update state; the state is protected via PCM stream lock
1721ea9d0d77STakashi Iwai  * for avoiding the race with trigger callback.
1722ea9d0d77STakashi Iwai  * If the state is unset and a trigger is pending while the previous operation,
1723ea9d0d77STakashi Iwai  * process the pending trigger action here.
1724ea9d0d77STakashi Iwai  */
1725ea9d0d77STakashi Iwai static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
1726ea9d0d77STakashi Iwai 				     int stream, enum snd_soc_dpcm_update state)
1727ea9d0d77STakashi Iwai {
1728ea9d0d77STakashi Iwai 	struct snd_pcm_substream *substream =
1729ea9d0d77STakashi Iwai 		snd_soc_dpcm_get_substream(fe, stream);
1730ea9d0d77STakashi Iwai 
1731ea9d0d77STakashi Iwai 	snd_pcm_stream_lock_irq(substream);
1732ea9d0d77STakashi Iwai 	if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
1733ea9d0d77STakashi Iwai 		dpcm_fe_dai_do_trigger(substream,
1734ea9d0d77STakashi Iwai 				       fe->dpcm[stream].trigger_pending - 1);
1735ea9d0d77STakashi Iwai 		fe->dpcm[stream].trigger_pending = 0;
1736ea9d0d77STakashi Iwai 	}
1737ea9d0d77STakashi Iwai 	fe->dpcm[stream].runtime_update = state;
1738ea9d0d77STakashi Iwai 	snd_pcm_stream_unlock_irq(substream);
1739ea9d0d77STakashi Iwai }
1740ea9d0d77STakashi Iwai 
1741906c7d69SPC Liao static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
1742906c7d69SPC Liao 			       int stream)
1743906c7d69SPC Liao {
1744906c7d69SPC Liao 	struct snd_soc_dpcm *dpcm;
1745906c7d69SPC Liao 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
1746906c7d69SPC Liao 	struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai;
1747906c7d69SPC Liao 	int err;
1748906c7d69SPC Liao 
1749906c7d69SPC Liao 	/* apply symmetry for FE */
1750906c7d69SPC Liao 	if (soc_pcm_has_symmetry(fe_substream))
1751906c7d69SPC Liao 		fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
1752906c7d69SPC Liao 
1753906c7d69SPC Liao 	/* Symmetry only applies if we've got an active stream. */
1754906c7d69SPC Liao 	if (fe_cpu_dai->active) {
1755906c7d69SPC Liao 		err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
1756906c7d69SPC Liao 		if (err < 0)
1757906c7d69SPC Liao 			return err;
1758906c7d69SPC Liao 	}
1759906c7d69SPC Liao 
1760906c7d69SPC Liao 	/* apply symmetry for BE */
17618d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
1762906c7d69SPC Liao 		struct snd_soc_pcm_runtime *be = dpcm->be;
1763906c7d69SPC Liao 		struct snd_pcm_substream *be_substream =
1764906c7d69SPC Liao 			snd_soc_dpcm_get_substream(be, stream);
17656246f283SJerome Brunet 		struct snd_soc_pcm_runtime *rtd;
17660b7990e3SKuninori Morimoto 		struct snd_soc_dai *codec_dai;
1767906c7d69SPC Liao 		int i;
1768906c7d69SPC Liao 
17696246f283SJerome Brunet 		/* A backend may not have the requested substream */
17706246f283SJerome Brunet 		if (!be_substream)
17716246f283SJerome Brunet 			continue;
17726246f283SJerome Brunet 
17736246f283SJerome Brunet 		rtd = be_substream->private_data;
1774f1176614SJeeja KP 		if (rtd->dai_link->be_hw_params_fixup)
1775f1176614SJeeja KP 			continue;
1776f1176614SJeeja KP 
1777906c7d69SPC Liao 		if (soc_pcm_has_symmetry(be_substream))
1778906c7d69SPC Liao 			be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
1779906c7d69SPC Liao 
1780906c7d69SPC Liao 		/* Symmetry only applies if we've got an active stream. */
1781906c7d69SPC Liao 		if (rtd->cpu_dai->active) {
178299bcedbdSKai Chieh Chuang 			err = soc_pcm_apply_symmetry(fe_substream,
178399bcedbdSKai Chieh Chuang 						     rtd->cpu_dai);
1784906c7d69SPC Liao 			if (err < 0)
1785906c7d69SPC Liao 				return err;
1786906c7d69SPC Liao 		}
1787906c7d69SPC Liao 
17880b7990e3SKuninori Morimoto 		for_each_rtd_codec_dai(rtd, i, codec_dai) {
17890b7990e3SKuninori Morimoto 			if (codec_dai->active) {
179099bcedbdSKai Chieh Chuang 				err = soc_pcm_apply_symmetry(fe_substream,
17910b7990e3SKuninori Morimoto 							     codec_dai);
1792906c7d69SPC Liao 				if (err < 0)
1793906c7d69SPC Liao 					return err;
1794906c7d69SPC Liao 			}
1795906c7d69SPC Liao 		}
1796906c7d69SPC Liao 	}
1797906c7d69SPC Liao 
1798906c7d69SPC Liao 	return 0;
1799906c7d69SPC Liao }
1800906c7d69SPC Liao 
180101d7584cSLiam Girdwood static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
180201d7584cSLiam Girdwood {
180301d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
180401d7584cSLiam Girdwood 	struct snd_pcm_runtime *runtime = fe_substream->runtime;
180501d7584cSLiam Girdwood 	int stream = fe_substream->stream, ret = 0;
180601d7584cSLiam Girdwood 
1807ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
180801d7584cSLiam Girdwood 
180901d7584cSLiam Girdwood 	ret = dpcm_be_dai_startup(fe, fe_substream->stream);
181001d7584cSLiam Girdwood 	if (ret < 0) {
1811103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: failed to start some BEs %d\n", ret);
181201d7584cSLiam Girdwood 		goto be_err;
181301d7584cSLiam Girdwood 	}
181401d7584cSLiam Girdwood 
1815103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name);
181601d7584cSLiam Girdwood 
181701d7584cSLiam Girdwood 	/* start the DAI frontend */
181801d7584cSLiam Girdwood 	ret = soc_pcm_open(fe_substream);
181901d7584cSLiam Girdwood 	if (ret < 0) {
1820103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: failed to start FE %d\n", ret);
182101d7584cSLiam Girdwood 		goto unwind;
182201d7584cSLiam Girdwood 	}
182301d7584cSLiam Girdwood 
182401d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
182501d7584cSLiam Girdwood 
182601d7584cSLiam Girdwood 	dpcm_set_fe_runtime(fe_substream);
182701d7584cSLiam Girdwood 	snd_pcm_limit_hw_rates(runtime);
182801d7584cSLiam Girdwood 
1829906c7d69SPC Liao 	ret = dpcm_apply_symmetry(fe_substream, stream);
1830906c7d69SPC Liao 	if (ret < 0) {
1831906c7d69SPC Liao 		dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n",
1832906c7d69SPC Liao 			ret);
1833906c7d69SPC Liao 		goto unwind;
1834906c7d69SPC Liao 	}
1835906c7d69SPC Liao 
1836ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
183701d7584cSLiam Girdwood 	return 0;
183801d7584cSLiam Girdwood 
183901d7584cSLiam Girdwood unwind:
184001d7584cSLiam Girdwood 	dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
184101d7584cSLiam Girdwood be_err:
1842ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
184301d7584cSLiam Girdwood 	return ret;
184401d7584cSLiam Girdwood }
184501d7584cSLiam Girdwood 
184623607025SLiam Girdwood int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
184701d7584cSLiam Girdwood {
184801d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
184901d7584cSLiam Girdwood 
185001d7584cSLiam Girdwood 	/* only shutdown BEs that are either sinks or sources to this FE DAI */
18518d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
185201d7584cSLiam Girdwood 
185301d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
185401d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
185501d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
185601d7584cSLiam Girdwood 
185701d7584cSLiam Girdwood 		/* is this op for this BE ? */
185801d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
185901d7584cSLiam Girdwood 			continue;
186001d7584cSLiam Girdwood 
186101d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1862103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: no users %s at close - state %d\n",
186301d7584cSLiam Girdwood 				stream ? "capture" : "playback",
186401d7584cSLiam Girdwood 				be->dpcm[stream].state);
186501d7584cSLiam Girdwood 
186601d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
186701d7584cSLiam Girdwood 			continue;
186801d7584cSLiam Girdwood 
186901d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
18709c0ac70aSKai Chieh Chuang 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) {
18719c0ac70aSKai Chieh Chuang 			soc_pcm_hw_free(be_substream);
18729c0ac70aSKai Chieh Chuang 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
18739c0ac70aSKai Chieh Chuang 		}
187401d7584cSLiam Girdwood 
1875103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: close BE %s\n",
187694d215ccS彭东林 			be->dai_link->name);
187701d7584cSLiam Girdwood 
187801d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
187901d7584cSLiam Girdwood 		be_substream->runtime = NULL;
188001d7584cSLiam Girdwood 
188101d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
188201d7584cSLiam Girdwood 	}
188301d7584cSLiam Girdwood 	return 0;
188401d7584cSLiam Girdwood }
188501d7584cSLiam Girdwood 
188601d7584cSLiam Girdwood static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
188701d7584cSLiam Girdwood {
188801d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
188901d7584cSLiam Girdwood 	int stream = substream->stream;
189001d7584cSLiam Girdwood 
1891ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
189201d7584cSLiam Girdwood 
189301d7584cSLiam Girdwood 	/* shutdown the BEs */
189401d7584cSLiam Girdwood 	dpcm_be_dai_shutdown(fe, substream->stream);
189501d7584cSLiam Girdwood 
1896103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name);
189701d7584cSLiam Girdwood 
189801d7584cSLiam Girdwood 	/* now shutdown the frontend */
189901d7584cSLiam Girdwood 	soc_pcm_close(substream);
190001d7584cSLiam Girdwood 
190101d7584cSLiam Girdwood 	/* run the stream event for each BE */
1902b0edff42SKuninori Morimoto 	snd_soc_dapm_stream_stop(fe, stream);
190301d7584cSLiam Girdwood 
190401d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
1905ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
190601d7584cSLiam Girdwood 	return 0;
190701d7584cSLiam Girdwood }
190801d7584cSLiam Girdwood 
190923607025SLiam Girdwood int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
191001d7584cSLiam Girdwood {
191101d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
191201d7584cSLiam Girdwood 
191301d7584cSLiam Girdwood 	/* only hw_params backends that are either sinks or sources
191401d7584cSLiam Girdwood 	 * to this frontend DAI */
19158d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
191601d7584cSLiam Girdwood 
191701d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
191801d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
191901d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
192001d7584cSLiam Girdwood 
192101d7584cSLiam Girdwood 		/* is this op for this BE ? */
192201d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
192301d7584cSLiam Girdwood 			continue;
192401d7584cSLiam Girdwood 
192501d7584cSLiam Girdwood 		/* only free hw when no longer used - check all FEs */
192601d7584cSLiam Girdwood 		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
192701d7584cSLiam Girdwood 				continue;
192801d7584cSLiam Girdwood 
192936fba62cSQiao Zhou 		/* do not free hw if this BE is used by other FE */
193036fba62cSQiao Zhou 		if (be->dpcm[stream].users > 1)
193136fba62cSQiao Zhou 			continue;
193236fba62cSQiao Zhou 
193301d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
193401d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
193501d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
193608b27848SPatrick Lai 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) &&
19375e82d2beSVinod Koul 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
19385e82d2beSVinod Koul 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
193901d7584cSLiam Girdwood 			continue;
194001d7584cSLiam Girdwood 
1941103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: hw_free BE %s\n",
194294d215ccS彭东林 			be->dai_link->name);
194301d7584cSLiam Girdwood 
194401d7584cSLiam Girdwood 		soc_pcm_hw_free(be_substream);
194501d7584cSLiam Girdwood 
194601d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
194701d7584cSLiam Girdwood 	}
194801d7584cSLiam Girdwood 
194901d7584cSLiam Girdwood 	return 0;
195001d7584cSLiam Girdwood }
195101d7584cSLiam Girdwood 
195245c0a188SMark Brown static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
195301d7584cSLiam Girdwood {
195401d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
195501d7584cSLiam Girdwood 	int err, stream = substream->stream;
195601d7584cSLiam Girdwood 
195701d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
1958ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
195901d7584cSLiam Girdwood 
1960103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
196101d7584cSLiam Girdwood 
196201d7584cSLiam Girdwood 	/* call hw_free on the frontend */
196301d7584cSLiam Girdwood 	err = soc_pcm_hw_free(substream);
196401d7584cSLiam Girdwood 	if (err < 0)
1965103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_free FE %s failed\n",
196601d7584cSLiam Girdwood 			fe->dai_link->name);
196701d7584cSLiam Girdwood 
196801d7584cSLiam Girdwood 	/* only hw_params backends that are either sinks or sources
196901d7584cSLiam Girdwood 	 * to this frontend DAI */
197001d7584cSLiam Girdwood 	err = dpcm_be_dai_hw_free(fe, stream);
197101d7584cSLiam Girdwood 
197201d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
1973ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
197401d7584cSLiam Girdwood 
197501d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
197601d7584cSLiam Girdwood 	return 0;
197701d7584cSLiam Girdwood }
197801d7584cSLiam Girdwood 
197923607025SLiam Girdwood int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
198001d7584cSLiam Girdwood {
198101d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
198201d7584cSLiam Girdwood 	int ret;
198301d7584cSLiam Girdwood 
19848d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
198501d7584cSLiam Girdwood 
198601d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
198701d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
198801d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
198901d7584cSLiam Girdwood 
199001d7584cSLiam Girdwood 		/* is this op for this BE ? */
199101d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
199201d7584cSLiam Girdwood 			continue;
199301d7584cSLiam Girdwood 
199401d7584cSLiam Girdwood 		/* copy params for each dpcm */
199501d7584cSLiam Girdwood 		memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params,
199601d7584cSLiam Girdwood 				sizeof(struct snd_pcm_hw_params));
199701d7584cSLiam Girdwood 
199801d7584cSLiam Girdwood 		/* perform any hw_params fixups */
199901d7584cSLiam Girdwood 		if (be->dai_link->be_hw_params_fixup) {
200001d7584cSLiam Girdwood 			ret = be->dai_link->be_hw_params_fixup(be,
200101d7584cSLiam Girdwood 					&dpcm->hw_params);
200201d7584cSLiam Girdwood 			if (ret < 0) {
200301d7584cSLiam Girdwood 				dev_err(be->dev,
2004103d84a3SLiam Girdwood 					"ASoC: hw_params BE fixup failed %d\n",
200501d7584cSLiam Girdwood 					ret);
200601d7584cSLiam Girdwood 				goto unwind;
200701d7584cSLiam Girdwood 			}
200801d7584cSLiam Girdwood 		}
200901d7584cSLiam Girdwood 
2010ae061d2aSLibin Yang 		/* copy the fixed-up hw params for BE dai */
2011ae061d2aSLibin Yang 		memcpy(&be->dpcm[stream].hw_params, &dpcm->hw_params,
2012ae061d2aSLibin Yang 		       sizeof(struct snd_pcm_hw_params));
2013ae061d2aSLibin Yang 
2014b0639bd2SKuninori Morimoto 		/* only allow hw_params() if no connected FEs are running */
2015b0639bd2SKuninori Morimoto 		if (!snd_soc_dpcm_can_be_params(fe, be, stream))
2016b0639bd2SKuninori Morimoto 			continue;
2017b0639bd2SKuninori Morimoto 
2018b0639bd2SKuninori Morimoto 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
2019b0639bd2SKuninori Morimoto 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
2020b0639bd2SKuninori Morimoto 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
2021b0639bd2SKuninori Morimoto 			continue;
2022b0639bd2SKuninori Morimoto 
2023b0639bd2SKuninori Morimoto 		dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
202494d215ccS彭东林 			be->dai_link->name);
2025b0639bd2SKuninori Morimoto 
202601d7584cSLiam Girdwood 		ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
202701d7584cSLiam Girdwood 		if (ret < 0) {
202801d7584cSLiam Girdwood 			dev_err(dpcm->be->dev,
2029103d84a3SLiam Girdwood 				"ASoC: hw_params BE failed %d\n", ret);
203001d7584cSLiam Girdwood 			goto unwind;
203101d7584cSLiam Girdwood 		}
203201d7584cSLiam Girdwood 
203301d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
203401d7584cSLiam Girdwood 	}
203501d7584cSLiam Girdwood 	return 0;
203601d7584cSLiam Girdwood 
203701d7584cSLiam Girdwood unwind:
203801d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
20398d6258a4SKuninori Morimoto 	for_each_dpcm_be_rollback(fe, stream, dpcm) {
204001d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
204101d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
204201d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
204301d7584cSLiam Girdwood 
204401d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
204501d7584cSLiam Girdwood 			continue;
204601d7584cSLiam Girdwood 
204701d7584cSLiam Girdwood 		/* only allow hw_free() if no connected FEs are running */
204801d7584cSLiam Girdwood 		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
204901d7584cSLiam Girdwood 			continue;
205001d7584cSLiam Girdwood 
205101d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
205201d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
205301d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
205401d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
205501d7584cSLiam Girdwood 			continue;
205601d7584cSLiam Girdwood 
205701d7584cSLiam Girdwood 		soc_pcm_hw_free(be_substream);
205801d7584cSLiam Girdwood 	}
205901d7584cSLiam Girdwood 
206001d7584cSLiam Girdwood 	return ret;
206101d7584cSLiam Girdwood }
206201d7584cSLiam Girdwood 
206345c0a188SMark Brown static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
206401d7584cSLiam Girdwood 				 struct snd_pcm_hw_params *params)
206501d7584cSLiam Girdwood {
206601d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
206701d7584cSLiam Girdwood 	int ret, stream = substream->stream;
206801d7584cSLiam Girdwood 
206901d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
2070ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
207101d7584cSLiam Girdwood 
207201d7584cSLiam Girdwood 	memcpy(&fe->dpcm[substream->stream].hw_params, params,
207301d7584cSLiam Girdwood 			sizeof(struct snd_pcm_hw_params));
207401d7584cSLiam Girdwood 	ret = dpcm_be_dai_hw_params(fe, substream->stream);
207501d7584cSLiam Girdwood 	if (ret < 0) {
2076103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_params BE failed %d\n", ret);
207701d7584cSLiam Girdwood 		goto out;
207801d7584cSLiam Girdwood 	}
207901d7584cSLiam Girdwood 
2080103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: hw_params FE %s rate %d chan %x fmt %d\n",
208101d7584cSLiam Girdwood 			fe->dai_link->name, params_rate(params),
208201d7584cSLiam Girdwood 			params_channels(params), params_format(params));
208301d7584cSLiam Girdwood 
208401d7584cSLiam Girdwood 	/* call hw_params on the frontend */
208501d7584cSLiam Girdwood 	ret = soc_pcm_hw_params(substream, params);
208601d7584cSLiam Girdwood 	if (ret < 0) {
2087103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret);
208801d7584cSLiam Girdwood 		dpcm_be_dai_hw_free(fe, stream);
208901d7584cSLiam Girdwood 	 } else
209001d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
209101d7584cSLiam Girdwood 
209201d7584cSLiam Girdwood out:
2093ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
209401d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
209501d7584cSLiam Girdwood 	return ret;
209601d7584cSLiam Girdwood }
209701d7584cSLiam Girdwood 
209801d7584cSLiam Girdwood static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm,
209901d7584cSLiam Girdwood 		struct snd_pcm_substream *substream, int cmd)
210001d7584cSLiam Girdwood {
210101d7584cSLiam Girdwood 	int ret;
210201d7584cSLiam Girdwood 
2103103d84a3SLiam Girdwood 	dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d\n",
210494d215ccS彭东林 			dpcm->be->dai_link->name, cmd);
210501d7584cSLiam Girdwood 
210601d7584cSLiam Girdwood 	ret = soc_pcm_trigger(substream, cmd);
210701d7584cSLiam Girdwood 	if (ret < 0)
2108103d84a3SLiam Girdwood 		dev_err(dpcm->be->dev,"ASoC: trigger BE failed %d\n", ret);
210901d7584cSLiam Girdwood 
211001d7584cSLiam Girdwood 	return ret;
211101d7584cSLiam Girdwood }
211201d7584cSLiam Girdwood 
211323607025SLiam Girdwood int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
211445c0a188SMark Brown 			       int cmd)
211501d7584cSLiam Girdwood {
211601d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
211701d7584cSLiam Girdwood 	int ret = 0;
211801d7584cSLiam Girdwood 
21198d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
212001d7584cSLiam Girdwood 
212101d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
212201d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
212301d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
212401d7584cSLiam Girdwood 
212501d7584cSLiam Girdwood 		/* is this op for this BE ? */
212601d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
212701d7584cSLiam Girdwood 			continue;
212801d7584cSLiam Girdwood 
212901d7584cSLiam Girdwood 		switch (cmd) {
213001d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_START:
213101d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
213201d7584cSLiam Girdwood 			    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
213301d7584cSLiam Girdwood 				continue;
213401d7584cSLiam Girdwood 
213501d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
213601d7584cSLiam Girdwood 			if (ret)
213701d7584cSLiam Girdwood 				return ret;
213801d7584cSLiam Girdwood 
213901d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
214001d7584cSLiam Girdwood 			break;
214101d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_RESUME:
214201d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
214301d7584cSLiam Girdwood 				continue;
214401d7584cSLiam Girdwood 
214501d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
214601d7584cSLiam Girdwood 			if (ret)
214701d7584cSLiam Girdwood 				return ret;
214801d7584cSLiam Girdwood 
214901d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
215001d7584cSLiam Girdwood 			break;
215101d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
215201d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
215301d7584cSLiam Girdwood 				continue;
215401d7584cSLiam Girdwood 
215501d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
215601d7584cSLiam Girdwood 			if (ret)
215701d7584cSLiam Girdwood 				return ret;
215801d7584cSLiam Girdwood 
215901d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
216001d7584cSLiam Girdwood 			break;
216101d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_STOP:
216201d7584cSLiam Girdwood 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
216301d7584cSLiam Girdwood 				continue;
216401d7584cSLiam Girdwood 
216501d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
216601d7584cSLiam Girdwood 				continue;
216701d7584cSLiam Girdwood 
216801d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
216901d7584cSLiam Girdwood 			if (ret)
217001d7584cSLiam Girdwood 				return ret;
217101d7584cSLiam Girdwood 
217201d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
217301d7584cSLiam Girdwood 			break;
217401d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_SUSPEND:
2175868a6ca8SNicolin Chen 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
217601d7584cSLiam Girdwood 				continue;
217701d7584cSLiam Girdwood 
217801d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
217901d7584cSLiam Girdwood 				continue;
218001d7584cSLiam Girdwood 
218101d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
218201d7584cSLiam Girdwood 			if (ret)
218301d7584cSLiam Girdwood 				return ret;
218401d7584cSLiam Girdwood 
218501d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
218601d7584cSLiam Girdwood 			break;
218701d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
218801d7584cSLiam Girdwood 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
218901d7584cSLiam Girdwood 				continue;
219001d7584cSLiam Girdwood 
219101d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
219201d7584cSLiam Girdwood 				continue;
219301d7584cSLiam Girdwood 
219401d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
219501d7584cSLiam Girdwood 			if (ret)
219601d7584cSLiam Girdwood 				return ret;
219701d7584cSLiam Girdwood 
219801d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
219901d7584cSLiam Girdwood 			break;
220001d7584cSLiam Girdwood 		}
220101d7584cSLiam Girdwood 	}
220201d7584cSLiam Girdwood 
220301d7584cSLiam Girdwood 	return ret;
220401d7584cSLiam Girdwood }
220501d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
220601d7584cSLiam Girdwood 
2207acbf2774SRanjani Sridharan static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream,
2208acbf2774SRanjani Sridharan 				  int cmd, bool fe_first)
2209acbf2774SRanjani Sridharan {
2210acbf2774SRanjani Sridharan 	struct snd_soc_pcm_runtime *fe = substream->private_data;
2211acbf2774SRanjani Sridharan 	int ret;
2212acbf2774SRanjani Sridharan 
2213acbf2774SRanjani Sridharan 	/* call trigger on the frontend before the backend. */
2214acbf2774SRanjani Sridharan 	if (fe_first) {
2215acbf2774SRanjani Sridharan 		dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
2216acbf2774SRanjani Sridharan 			fe->dai_link->name, cmd);
2217acbf2774SRanjani Sridharan 
2218acbf2774SRanjani Sridharan 		ret = soc_pcm_trigger(substream, cmd);
2219acbf2774SRanjani Sridharan 		if (ret < 0)
2220acbf2774SRanjani Sridharan 			return ret;
2221acbf2774SRanjani Sridharan 
2222acbf2774SRanjani Sridharan 		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
2223acbf2774SRanjani Sridharan 		return ret;
2224acbf2774SRanjani Sridharan 	}
2225acbf2774SRanjani Sridharan 
2226acbf2774SRanjani Sridharan 	/* call trigger on the frontend after the backend. */
2227acbf2774SRanjani Sridharan 	ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
2228acbf2774SRanjani Sridharan 	if (ret < 0)
2229acbf2774SRanjani Sridharan 		return ret;
2230acbf2774SRanjani Sridharan 
2231acbf2774SRanjani Sridharan 	dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
2232acbf2774SRanjani Sridharan 		fe->dai_link->name, cmd);
2233acbf2774SRanjani Sridharan 
2234acbf2774SRanjani Sridharan 	ret = soc_pcm_trigger(substream, cmd);
2235acbf2774SRanjani Sridharan 
2236acbf2774SRanjani Sridharan 	return ret;
2237acbf2774SRanjani Sridharan }
2238acbf2774SRanjani Sridharan 
2239ea9d0d77STakashi Iwai static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
224001d7584cSLiam Girdwood {
224101d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
2242acbf2774SRanjani Sridharan 	int stream = substream->stream;
2243acbf2774SRanjani Sridharan 	int ret = 0;
224401d7584cSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
224501d7584cSLiam Girdwood 
224601d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
224701d7584cSLiam Girdwood 
224801d7584cSLiam Girdwood 	switch (trigger) {
224901d7584cSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_PRE:
2250acbf2774SRanjani Sridharan 		switch (cmd) {
2251acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_START:
2252acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_RESUME:
2253acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2254acbf2774SRanjani Sridharan 			ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
2255acbf2774SRanjani Sridharan 			break;
2256acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_STOP:
2257acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_SUSPEND:
2258acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2259acbf2774SRanjani Sridharan 			ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
2260acbf2774SRanjani Sridharan 			break;
2261acbf2774SRanjani Sridharan 		default:
2262acbf2774SRanjani Sridharan 			ret = -EINVAL;
2263acbf2774SRanjani Sridharan 			break;
226401d7584cSLiam Girdwood 		}
226501d7584cSLiam Girdwood 		break;
226601d7584cSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_POST:
2267acbf2774SRanjani Sridharan 		switch (cmd) {
2268acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_START:
2269acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_RESUME:
2270acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2271acbf2774SRanjani Sridharan 			ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
2272acbf2774SRanjani Sridharan 			break;
2273acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_STOP:
2274acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_SUSPEND:
2275acbf2774SRanjani Sridharan 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2276acbf2774SRanjani Sridharan 			ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
2277acbf2774SRanjani Sridharan 			break;
2278acbf2774SRanjani Sridharan 		default:
2279acbf2774SRanjani Sridharan 			ret = -EINVAL;
2280acbf2774SRanjani Sridharan 			break;
228101d7584cSLiam Girdwood 		}
228201d7584cSLiam Girdwood 		break;
228307bf84aaSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_BESPOKE:
228407bf84aaSLiam Girdwood 		/* bespoke trigger() - handles both FE and BEs */
228507bf84aaSLiam Girdwood 
2286103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd %d\n",
228707bf84aaSLiam Girdwood 				fe->dai_link->name, cmd);
228807bf84aaSLiam Girdwood 
228907bf84aaSLiam Girdwood 		ret = soc_pcm_bespoke_trigger(substream, cmd);
229007bf84aaSLiam Girdwood 		break;
229101d7584cSLiam Girdwood 	default:
2292103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd,
229301d7584cSLiam Girdwood 				fe->dai_link->name);
229401d7584cSLiam Girdwood 		ret = -EINVAL;
229501d7584cSLiam Girdwood 		goto out;
229601d7584cSLiam Girdwood 	}
229701d7584cSLiam Girdwood 
2298acbf2774SRanjani Sridharan 	if (ret < 0) {
2299acbf2774SRanjani Sridharan 		dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n",
2300acbf2774SRanjani Sridharan 			cmd, ret);
2301acbf2774SRanjani Sridharan 		goto out;
2302acbf2774SRanjani Sridharan 	}
2303acbf2774SRanjani Sridharan 
230401d7584cSLiam Girdwood 	switch (cmd) {
230501d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_START:
230601d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_RESUME:
230701d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
230801d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
230901d7584cSLiam Girdwood 		break;
231001d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_STOP:
231101d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_SUSPEND:
231201d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
231301d7584cSLiam Girdwood 		break;
23149f169b9fSPatrick Lai 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
23159f169b9fSPatrick Lai 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
23169f169b9fSPatrick Lai 		break;
231701d7584cSLiam Girdwood 	}
231801d7584cSLiam Girdwood 
231901d7584cSLiam Girdwood out:
232001d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
232101d7584cSLiam Girdwood 	return ret;
232201d7584cSLiam Girdwood }
232301d7584cSLiam Girdwood 
2324ea9d0d77STakashi Iwai static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
2325ea9d0d77STakashi Iwai {
2326ea9d0d77STakashi Iwai 	struct snd_soc_pcm_runtime *fe = substream->private_data;
2327ea9d0d77STakashi Iwai 	int stream = substream->stream;
2328ea9d0d77STakashi Iwai 
2329ea9d0d77STakashi Iwai 	/* if FE's runtime_update is already set, we're in race;
2330ea9d0d77STakashi Iwai 	 * process this trigger later at exit
2331ea9d0d77STakashi Iwai 	 */
2332ea9d0d77STakashi Iwai 	if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) {
2333ea9d0d77STakashi Iwai 		fe->dpcm[stream].trigger_pending = cmd + 1;
2334ea9d0d77STakashi Iwai 		return 0; /* delayed, assuming it's successful */
2335ea9d0d77STakashi Iwai 	}
2336ea9d0d77STakashi Iwai 
2337ea9d0d77STakashi Iwai 	/* we're alone, let's trigger */
2338ea9d0d77STakashi Iwai 	return dpcm_fe_dai_do_trigger(substream, cmd);
2339ea9d0d77STakashi Iwai }
2340ea9d0d77STakashi Iwai 
234123607025SLiam Girdwood int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
234201d7584cSLiam Girdwood {
234301d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
234401d7584cSLiam Girdwood 	int ret = 0;
234501d7584cSLiam Girdwood 
23468d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
234701d7584cSLiam Girdwood 
234801d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
234901d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
235001d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
235101d7584cSLiam Girdwood 
235201d7584cSLiam Girdwood 		/* is this op for this BE ? */
235301d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
235401d7584cSLiam Girdwood 			continue;
235501d7584cSLiam Girdwood 
235601d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
235795f444dcSKoro Chen 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
23585087a8f1SLibin Yang 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND) &&
23595087a8f1SLibin Yang 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
236001d7584cSLiam Girdwood 			continue;
236101d7584cSLiam Girdwood 
2362103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: prepare BE %s\n",
236394d215ccS彭东林 			be->dai_link->name);
236401d7584cSLiam Girdwood 
236501d7584cSLiam Girdwood 		ret = soc_pcm_prepare(be_substream);
236601d7584cSLiam Girdwood 		if (ret < 0) {
2367103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: backend prepare failed %d\n",
236801d7584cSLiam Girdwood 				ret);
236901d7584cSLiam Girdwood 			break;
237001d7584cSLiam Girdwood 		}
237101d7584cSLiam Girdwood 
237201d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
237301d7584cSLiam Girdwood 	}
237401d7584cSLiam Girdwood 	return ret;
237501d7584cSLiam Girdwood }
237601d7584cSLiam Girdwood 
237745c0a188SMark Brown static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
237801d7584cSLiam Girdwood {
237901d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
238001d7584cSLiam Girdwood 	int stream = substream->stream, ret = 0;
238101d7584cSLiam Girdwood 
238201d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
238301d7584cSLiam Girdwood 
2384103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
238501d7584cSLiam Girdwood 
2386ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
238701d7584cSLiam Girdwood 
238801d7584cSLiam Girdwood 	/* there is no point preparing this FE if there are no BEs */
238901d7584cSLiam Girdwood 	if (list_empty(&fe->dpcm[stream].be_clients)) {
2390103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: no backend DAIs enabled for %s\n",
239101d7584cSLiam Girdwood 				fe->dai_link->name);
239201d7584cSLiam Girdwood 		ret = -EINVAL;
239301d7584cSLiam Girdwood 		goto out;
239401d7584cSLiam Girdwood 	}
239501d7584cSLiam Girdwood 
239601d7584cSLiam Girdwood 	ret = dpcm_be_dai_prepare(fe, substream->stream);
239701d7584cSLiam Girdwood 	if (ret < 0)
239801d7584cSLiam Girdwood 		goto out;
239901d7584cSLiam Girdwood 
240001d7584cSLiam Girdwood 	/* call prepare on the frontend */
240101d7584cSLiam Girdwood 	ret = soc_pcm_prepare(substream);
240201d7584cSLiam Girdwood 	if (ret < 0) {
2403103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: prepare FE %s failed\n",
240401d7584cSLiam Girdwood 			fe->dai_link->name);
240501d7584cSLiam Girdwood 		goto out;
240601d7584cSLiam Girdwood 	}
240701d7584cSLiam Girdwood 
240801d7584cSLiam Girdwood 	/* run the stream event for each BE */
240901d7584cSLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
241001d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
241101d7584cSLiam Girdwood 
241201d7584cSLiam Girdwood out:
2413ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
241401d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
241501d7584cSLiam Girdwood 
241601d7584cSLiam Girdwood 	return ret;
241701d7584cSLiam Girdwood }
241801d7584cSLiam Girdwood 
2419618dae11SLiam Girdwood static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
2420618dae11SLiam Girdwood {
242107bf84aaSLiam Girdwood 	struct snd_pcm_substream *substream =
242207bf84aaSLiam Girdwood 		snd_soc_dpcm_get_substream(fe, stream);
242307bf84aaSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
2424618dae11SLiam Girdwood 	int err;
242501d7584cSLiam Girdwood 
2426103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n",
2427618dae11SLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name);
2428618dae11SLiam Girdwood 
242907bf84aaSLiam Girdwood 	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
243007bf84aaSLiam Girdwood 		/* call bespoke trigger - FE takes care of all BE triggers */
2431103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop\n",
243207bf84aaSLiam Girdwood 				fe->dai_link->name);
243307bf84aaSLiam Girdwood 
243407bf84aaSLiam Girdwood 		err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP);
243507bf84aaSLiam Girdwood 		if (err < 0)
2436103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err);
243707bf84aaSLiam Girdwood 	} else {
2438103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop\n",
243907bf84aaSLiam Girdwood 			fe->dai_link->name);
244007bf84aaSLiam Girdwood 
2441618dae11SLiam Girdwood 		err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
2442618dae11SLiam Girdwood 		if (err < 0)
2443103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err);
244407bf84aaSLiam Girdwood 	}
2445618dae11SLiam Girdwood 
2446618dae11SLiam Girdwood 	err = dpcm_be_dai_hw_free(fe, stream);
2447618dae11SLiam Girdwood 	if (err < 0)
2448103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err);
2449618dae11SLiam Girdwood 
2450618dae11SLiam Girdwood 	err = dpcm_be_dai_shutdown(fe, stream);
2451618dae11SLiam Girdwood 	if (err < 0)
2452103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err);
2453618dae11SLiam Girdwood 
2454618dae11SLiam Girdwood 	/* run the stream event for each BE */
2455618dae11SLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
2456618dae11SLiam Girdwood 
2457618dae11SLiam Girdwood 	return 0;
2458618dae11SLiam Girdwood }
2459618dae11SLiam Girdwood 
2460618dae11SLiam Girdwood static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
2461618dae11SLiam Girdwood {
246207bf84aaSLiam Girdwood 	struct snd_pcm_substream *substream =
246307bf84aaSLiam Girdwood 		snd_soc_dpcm_get_substream(fe, stream);
2464618dae11SLiam Girdwood 	struct snd_soc_dpcm *dpcm;
246507bf84aaSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
2466618dae11SLiam Girdwood 	int ret;
2467a9764869SKaiChieh Chuang 	unsigned long flags;
2468618dae11SLiam Girdwood 
2469103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n",
2470618dae11SLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name);
2471618dae11SLiam Girdwood 
2472618dae11SLiam Girdwood 	/* Only start the BE if the FE is ready */
2473618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE ||
2474618dae11SLiam Girdwood 		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE)
2475618dae11SLiam Girdwood 		return -EINVAL;
2476618dae11SLiam Girdwood 
2477618dae11SLiam Girdwood 	/* startup must always be called for new BEs */
2478618dae11SLiam Girdwood 	ret = dpcm_be_dai_startup(fe, stream);
2479fffc0ca2SDan Carpenter 	if (ret < 0)
2480618dae11SLiam Girdwood 		goto disconnect;
2481618dae11SLiam Girdwood 
2482618dae11SLiam Girdwood 	/* keep going if FE state is > open */
2483618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
2484618dae11SLiam Girdwood 		return 0;
2485618dae11SLiam Girdwood 
2486618dae11SLiam Girdwood 	ret = dpcm_be_dai_hw_params(fe, stream);
2487fffc0ca2SDan Carpenter 	if (ret < 0)
2488618dae11SLiam Girdwood 		goto close;
2489618dae11SLiam Girdwood 
2490618dae11SLiam Girdwood 	/* keep going if FE state is > hw_params */
2491618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
2492618dae11SLiam Girdwood 		return 0;
2493618dae11SLiam Girdwood 
2494618dae11SLiam Girdwood 
2495618dae11SLiam Girdwood 	ret = dpcm_be_dai_prepare(fe, stream);
2496fffc0ca2SDan Carpenter 	if (ret < 0)
2497618dae11SLiam Girdwood 		goto hw_free;
2498618dae11SLiam Girdwood 
2499618dae11SLiam Girdwood 	/* run the stream event for each BE */
2500618dae11SLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
2501618dae11SLiam Girdwood 
2502618dae11SLiam Girdwood 	/* keep going if FE state is > prepare */
2503618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE ||
2504618dae11SLiam Girdwood 		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP)
2505618dae11SLiam Girdwood 		return 0;
2506618dae11SLiam Girdwood 
250707bf84aaSLiam Girdwood 	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
250807bf84aaSLiam Girdwood 		/* call trigger on the frontend - FE takes care of all BE triggers */
2509103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start\n",
251007bf84aaSLiam Girdwood 				fe->dai_link->name);
251107bf84aaSLiam Girdwood 
251207bf84aaSLiam Girdwood 		ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START);
251307bf84aaSLiam Girdwood 		if (ret < 0) {
2514103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: bespoke trigger FE failed %d\n", ret);
251507bf84aaSLiam Girdwood 			goto hw_free;
251607bf84aaSLiam Girdwood 		}
251707bf84aaSLiam Girdwood 	} else {
2518103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start\n",
2519618dae11SLiam Girdwood 			fe->dai_link->name);
2520618dae11SLiam Girdwood 
2521618dae11SLiam Girdwood 		ret = dpcm_be_dai_trigger(fe, stream,
2522618dae11SLiam Girdwood 					SNDRV_PCM_TRIGGER_START);
2523618dae11SLiam Girdwood 		if (ret < 0) {
2524103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
2525618dae11SLiam Girdwood 			goto hw_free;
2526618dae11SLiam Girdwood 		}
252707bf84aaSLiam Girdwood 	}
2528618dae11SLiam Girdwood 
2529618dae11SLiam Girdwood 	return 0;
2530618dae11SLiam Girdwood 
2531618dae11SLiam Girdwood hw_free:
2532618dae11SLiam Girdwood 	dpcm_be_dai_hw_free(fe, stream);
2533618dae11SLiam Girdwood close:
2534618dae11SLiam Girdwood 	dpcm_be_dai_shutdown(fe, stream);
2535618dae11SLiam Girdwood disconnect:
2536618dae11SLiam Girdwood 	/* disconnect any non started BEs */
2537a9764869SKaiChieh Chuang 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
25388d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
2539618dae11SLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
2540618dae11SLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
2541618dae11SLiam Girdwood 				dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
2542618dae11SLiam Girdwood 	}
2543a9764869SKaiChieh Chuang 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
2544618dae11SLiam Girdwood 
2545618dae11SLiam Girdwood 	return ret;
2546618dae11SLiam Girdwood }
2547618dae11SLiam Girdwood 
2548618dae11SLiam Girdwood static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
2549618dae11SLiam Girdwood {
2550618dae11SLiam Girdwood 	int ret;
2551618dae11SLiam Girdwood 
2552ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
2553618dae11SLiam Girdwood 	ret = dpcm_run_update_startup(fe, stream);
2554618dae11SLiam Girdwood 	if (ret < 0)
2555103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
2556ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
2557618dae11SLiam Girdwood 
2558618dae11SLiam Girdwood 	return ret;
2559618dae11SLiam Girdwood }
2560618dae11SLiam Girdwood 
2561618dae11SLiam Girdwood static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
2562618dae11SLiam Girdwood {
2563618dae11SLiam Girdwood 	int ret;
2564618dae11SLiam Girdwood 
2565ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
2566618dae11SLiam Girdwood 	ret = dpcm_run_update_shutdown(fe, stream);
2567618dae11SLiam Girdwood 	if (ret < 0)
2568103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
2569ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
2570618dae11SLiam Girdwood 
2571618dae11SLiam Girdwood 	return ret;
2572618dae11SLiam Girdwood }
2573618dae11SLiam Girdwood 
2574de15d7ffSJerome Brunet static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
2575618dae11SLiam Girdwood {
2576618dae11SLiam Girdwood 	struct snd_soc_dapm_widget_list *list;
25777083f877SKuninori Morimoto 	int stream;
2578de15d7ffSJerome Brunet 	int count, paths;
2579618dae11SLiam Girdwood 
2580618dae11SLiam Girdwood 	if (!fe->dai_link->dynamic)
2581de15d7ffSJerome Brunet 		return 0;
2582618dae11SLiam Girdwood 
2583618dae11SLiam Girdwood 	/* only check active links */
2584618dae11SLiam Girdwood 	if (!fe->cpu_dai->active)
2585de15d7ffSJerome Brunet 		return 0;
2586618dae11SLiam Girdwood 
2587618dae11SLiam Girdwood 	/* DAPM sync will call this to update DSP paths */
2588de15d7ffSJerome Brunet 	dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n",
2589de15d7ffSJerome Brunet 		new ? "new" : "old", fe->dai_link->name);
2590618dae11SLiam Girdwood 
25917083f877SKuninori Morimoto 	for_each_pcm_streams(stream) {
2592075207d2SQiao Zhou 
25937083f877SKuninori Morimoto 		/* skip if FE doesn't have playback/capture capability */
25947083f877SKuninori Morimoto 		if (!snd_soc_dai_stream_valid(fe->cpu_dai,   stream) ||
25957083f877SKuninori Morimoto 		    !snd_soc_dai_stream_valid(fe->codec_dai, stream))
25967083f877SKuninori Morimoto 			continue;
2597618dae11SLiam Girdwood 
25987083f877SKuninori Morimoto 		/* skip if FE isn't currently playing/capturing */
25997083f877SKuninori Morimoto 		if (!fe->cpu_dai->stream_active[stream] ||
26007083f877SKuninori Morimoto 		    !fe->codec_dai->stream_active[stream])
26017083f877SKuninori Morimoto 			continue;
26027083f877SKuninori Morimoto 
26037083f877SKuninori Morimoto 		paths = dpcm_path_get(fe, stream, &list);
2604618dae11SLiam Girdwood 		if (paths < 0) {
2605103d84a3SLiam Girdwood 			dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
26067083f877SKuninori Morimoto 				 fe->dai_link->name,
26077083f877SKuninori Morimoto 				 stream == SNDRV_PCM_STREAM_PLAYBACK ?
26087083f877SKuninori Morimoto 				 "playback" : "capture");
2609618dae11SLiam Girdwood 			return paths;
2610618dae11SLiam Girdwood 		}
2611618dae11SLiam Girdwood 
26127083f877SKuninori Morimoto 		/* update any playback/capture paths */
26137083f877SKuninori Morimoto 		count = dpcm_process_paths(fe, stream, &list, new);
2614de15d7ffSJerome Brunet 		if (count) {
2615de15d7ffSJerome Brunet 			if (new)
26167083f877SKuninori Morimoto 				dpcm_run_new_update(fe, stream);
2617de15d7ffSJerome Brunet 			else
26187083f877SKuninori Morimoto 				dpcm_run_old_update(fe, stream);
2619de15d7ffSJerome Brunet 
26207083f877SKuninori Morimoto 			dpcm_clear_pending_state(fe, stream);
26217083f877SKuninori Morimoto 			dpcm_be_disconnect(fe, stream);
2622618dae11SLiam Girdwood 		}
2623618dae11SLiam Girdwood 
26247ed9de76SQiao Zhou 		dpcm_path_put(&list);
2625618dae11SLiam Girdwood 	}
2626618dae11SLiam Girdwood 
2627de15d7ffSJerome Brunet 	return 0;
2628618dae11SLiam Girdwood }
2629618dae11SLiam Girdwood 
2630de15d7ffSJerome Brunet /* Called by DAPM mixer/mux changes to update audio routing between PCMs and
2631de15d7ffSJerome Brunet  * any DAI links.
2632de15d7ffSJerome Brunet  */
2633de15d7ffSJerome Brunet int soc_dpcm_runtime_update(struct snd_soc_card *card)
2634de15d7ffSJerome Brunet {
2635de15d7ffSJerome Brunet 	struct snd_soc_pcm_runtime *fe;
2636de15d7ffSJerome Brunet 	int ret = 0;
2637de15d7ffSJerome Brunet 
2638de15d7ffSJerome Brunet 	mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
2639de15d7ffSJerome Brunet 	/* shutdown all old paths first */
2640bcb1fd1fSKuninori Morimoto 	for_each_card_rtds(card, fe) {
2641de15d7ffSJerome Brunet 		ret = soc_dpcm_fe_runtime_update(fe, 0);
2642de15d7ffSJerome Brunet 		if (ret)
2643de15d7ffSJerome Brunet 			goto out;
2644de15d7ffSJerome Brunet 	}
2645de15d7ffSJerome Brunet 
2646de15d7ffSJerome Brunet 	/* bring new paths up */
2647bcb1fd1fSKuninori Morimoto 	for_each_card_rtds(card, fe) {
2648de15d7ffSJerome Brunet 		ret = soc_dpcm_fe_runtime_update(fe, 1);
2649de15d7ffSJerome Brunet 		if (ret)
2650de15d7ffSJerome Brunet 			goto out;
2651de15d7ffSJerome Brunet 	}
2652de15d7ffSJerome Brunet 
2653de15d7ffSJerome Brunet out:
2654618dae11SLiam Girdwood 	mutex_unlock(&card->mutex);
2655de15d7ffSJerome Brunet 	return ret;
2656618dae11SLiam Girdwood }
265701d7584cSLiam Girdwood 
265845c0a188SMark Brown static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
265901d7584cSLiam Girdwood {
266001d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
266101d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
266201d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list;
266301d7584cSLiam Girdwood 	int ret;
266401d7584cSLiam Girdwood 	int stream = fe_substream->stream;
266501d7584cSLiam Girdwood 
266601d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
266701d7584cSLiam Girdwood 	fe->dpcm[stream].runtime = fe_substream->runtime;
266801d7584cSLiam Girdwood 
26698f70e515SQiao Zhou 	ret = dpcm_path_get(fe, stream, &list);
26708f70e515SQiao Zhou 	if (ret < 0) {
2671cae06eb9SKuninori Morimoto 		goto open_end;
26728f70e515SQiao Zhou 	} else if (ret == 0) {
2673103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: %s no valid %s route\n",
267401d7584cSLiam Girdwood 			fe->dai_link->name, stream ? "capture" : "playback");
267501d7584cSLiam Girdwood 	}
267601d7584cSLiam Girdwood 
267701d7584cSLiam Girdwood 	/* calculate valid and active FE <-> BE dpcms */
267801d7584cSLiam Girdwood 	dpcm_process_paths(fe, stream, &list, 1);
267901d7584cSLiam Girdwood 
268001d7584cSLiam Girdwood 	ret = dpcm_fe_dai_startup(fe_substream);
268101d7584cSLiam Girdwood 	if (ret < 0) {
268201d7584cSLiam Girdwood 		/* clean up all links */
26838d6258a4SKuninori Morimoto 		for_each_dpcm_be(fe, stream, dpcm)
268401d7584cSLiam Girdwood 			dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
268501d7584cSLiam Girdwood 
268601d7584cSLiam Girdwood 		dpcm_be_disconnect(fe, stream);
268701d7584cSLiam Girdwood 		fe->dpcm[stream].runtime = NULL;
268801d7584cSLiam Girdwood 	}
268901d7584cSLiam Girdwood 
269001d7584cSLiam Girdwood 	dpcm_clear_pending_state(fe, stream);
269101d7584cSLiam Girdwood 	dpcm_path_put(&list);
2692cae06eb9SKuninori Morimoto open_end:
269301d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
269401d7584cSLiam Girdwood 	return ret;
269501d7584cSLiam Girdwood }
269601d7584cSLiam Girdwood 
269745c0a188SMark Brown static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
269801d7584cSLiam Girdwood {
269901d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
270001d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
270101d7584cSLiam Girdwood 	int stream = fe_substream->stream, ret;
270201d7584cSLiam Girdwood 
270301d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
270401d7584cSLiam Girdwood 	ret = dpcm_fe_dai_shutdown(fe_substream);
270501d7584cSLiam Girdwood 
270601d7584cSLiam Girdwood 	/* mark FE's links ready to prune */
27078d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm)
270801d7584cSLiam Girdwood 		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
270901d7584cSLiam Girdwood 
271001d7584cSLiam Girdwood 	dpcm_be_disconnect(fe, stream);
271101d7584cSLiam Girdwood 
271201d7584cSLiam Girdwood 	fe->dpcm[stream].runtime = NULL;
271301d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
271401d7584cSLiam Girdwood 	return ret;
271501d7584cSLiam Girdwood }
271601d7584cSLiam Girdwood 
2717ddee627cSLiam Girdwood /* create a new pcm */
2718ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
2719ddee627cSLiam Girdwood {
27202e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
2721ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
27222b544dd7SKuninori Morimoto 	struct snd_soc_component *component;
2723ddee627cSLiam Girdwood 	struct snd_pcm *pcm;
2724ddee627cSLiam Girdwood 	char new_name[64];
2725ddee627cSLiam Girdwood 	int ret = 0, playback = 0, capture = 0;
27262e5894d7SBenoit Cousson 	int i;
2727ddee627cSLiam Girdwood 
272801d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
27291e9de42fSLiam Girdwood 		playback = rtd->dai_link->dpcm_playback;
27301e9de42fSLiam Girdwood 		capture = rtd->dai_link->dpcm_capture;
273101d7584cSLiam Girdwood 	} else {
2732a342031cSJerome Brunet 		/* Adapt stream for codec2codec links */
2733a4877a6fSStephan Gerhold 		int cpu_capture = rtd->dai_link->params ?
2734a4877a6fSStephan Gerhold 			SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
2735a4877a6fSStephan Gerhold 		int cpu_playback = rtd->dai_link->params ?
2736a4877a6fSStephan Gerhold 			SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
2737a342031cSJerome Brunet 
27380b7990e3SKuninori Morimoto 		for_each_rtd_codec_dai(rtd, i, codec_dai) {
2739467fece8SKuninori Morimoto 			if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
2740a4877a6fSStephan Gerhold 			    snd_soc_dai_stream_valid(cpu_dai,   cpu_playback))
2741ddee627cSLiam Girdwood 				playback = 1;
2742467fece8SKuninori Morimoto 			if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
2743a4877a6fSStephan Gerhold 			    snd_soc_dai_stream_valid(cpu_dai,   cpu_capture))
2744ddee627cSLiam Girdwood 				capture = 1;
274501d7584cSLiam Girdwood 		}
27462e5894d7SBenoit Cousson 	}
27472e5894d7SBenoit Cousson 
2748d6bead02SFabio Estevam 	if (rtd->dai_link->playback_only) {
2749d6bead02SFabio Estevam 		playback = 1;
2750d6bead02SFabio Estevam 		capture = 0;
2751d6bead02SFabio Estevam 	}
2752d6bead02SFabio Estevam 
2753d6bead02SFabio Estevam 	if (rtd->dai_link->capture_only) {
2754d6bead02SFabio Estevam 		playback = 0;
2755d6bead02SFabio Estevam 		capture = 1;
2756d6bead02SFabio Estevam 	}
2757d6bead02SFabio Estevam 
275801d7584cSLiam Girdwood 	/* create the PCM */
2759a342031cSJerome Brunet 	if (rtd->dai_link->params) {
2760a342031cSJerome Brunet 		snprintf(new_name, sizeof(new_name), "codec2codec(%s)",
2761a342031cSJerome Brunet 			 rtd->dai_link->stream_name);
2762a342031cSJerome Brunet 
2763a342031cSJerome Brunet 		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
2764a342031cSJerome Brunet 					   playback, capture, &pcm);
2765a342031cSJerome Brunet 	} else if (rtd->dai_link->no_pcm) {
276601d7584cSLiam Girdwood 		snprintf(new_name, sizeof(new_name), "(%s)",
276701d7584cSLiam Girdwood 			rtd->dai_link->stream_name);
276801d7584cSLiam Girdwood 
276901d7584cSLiam Girdwood 		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
277001d7584cSLiam Girdwood 				playback, capture, &pcm);
277101d7584cSLiam Girdwood 	} else {
277201d7584cSLiam Girdwood 		if (rtd->dai_link->dynamic)
277301d7584cSLiam Girdwood 			snprintf(new_name, sizeof(new_name), "%s (*)",
277401d7584cSLiam Girdwood 				rtd->dai_link->stream_name);
277501d7584cSLiam Girdwood 		else
277601d7584cSLiam Girdwood 			snprintf(new_name, sizeof(new_name), "%s %s-%d",
27772e5894d7SBenoit Cousson 				rtd->dai_link->stream_name,
27782e5894d7SBenoit Cousson 				(rtd->num_codecs > 1) ?
27792e5894d7SBenoit Cousson 				"multicodec" : rtd->codec_dai->name, num);
278001d7584cSLiam Girdwood 
278101d7584cSLiam Girdwood 		ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
278201d7584cSLiam Girdwood 			capture, &pcm);
278301d7584cSLiam Girdwood 	}
2784ddee627cSLiam Girdwood 	if (ret < 0) {
2785103d84a3SLiam Girdwood 		dev_err(rtd->card->dev, "ASoC: can't create pcm for %s\n",
27865cb9b748SLiam Girdwood 			rtd->dai_link->name);
2787ddee627cSLiam Girdwood 		return ret;
2788ddee627cSLiam Girdwood 	}
2789103d84a3SLiam Girdwood 	dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name);
2790ddee627cSLiam Girdwood 
2791ddee627cSLiam Girdwood 	/* DAPM dai link stream work */
2792a342031cSJerome Brunet 	if (rtd->dai_link->params)
27934bf2e385SCurtis Malainey 		rtd->close_delayed_work_func = codec2codec_close_delayed_work;
2794a342031cSJerome Brunet 	else
279583f94a2eSKuninori Morimoto 		rtd->close_delayed_work_func = snd_soc_close_delayed_work;
2796ddee627cSLiam Girdwood 
279748c7699fSVinod Koul 	pcm->nonatomic = rtd->dai_link->nonatomic;
2798ddee627cSLiam Girdwood 	rtd->pcm = pcm;
2799ddee627cSLiam Girdwood 	pcm->private_data = rtd;
280001d7584cSLiam Girdwood 
2801a342031cSJerome Brunet 	if (rtd->dai_link->no_pcm || rtd->dai_link->params) {
280201d7584cSLiam Girdwood 		if (playback)
280301d7584cSLiam Girdwood 			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
280401d7584cSLiam Girdwood 		if (capture)
280501d7584cSLiam Girdwood 			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
280601d7584cSLiam Girdwood 		goto out;
280701d7584cSLiam Girdwood 	}
280801d7584cSLiam Girdwood 
280901d7584cSLiam Girdwood 	/* ASoC PCM operations */
281001d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic) {
281101d7584cSLiam Girdwood 		rtd->ops.open		= dpcm_fe_dai_open;
281201d7584cSLiam Girdwood 		rtd->ops.hw_params	= dpcm_fe_dai_hw_params;
281301d7584cSLiam Girdwood 		rtd->ops.prepare	= dpcm_fe_dai_prepare;
281401d7584cSLiam Girdwood 		rtd->ops.trigger	= dpcm_fe_dai_trigger;
281501d7584cSLiam Girdwood 		rtd->ops.hw_free	= dpcm_fe_dai_hw_free;
281601d7584cSLiam Girdwood 		rtd->ops.close		= dpcm_fe_dai_close;
281701d7584cSLiam Girdwood 		rtd->ops.pointer	= soc_pcm_pointer;
281801d7584cSLiam Girdwood 	} else {
281901d7584cSLiam Girdwood 		rtd->ops.open		= soc_pcm_open;
282001d7584cSLiam Girdwood 		rtd->ops.hw_params	= soc_pcm_hw_params;
282101d7584cSLiam Girdwood 		rtd->ops.prepare	= soc_pcm_prepare;
282201d7584cSLiam Girdwood 		rtd->ops.trigger	= soc_pcm_trigger;
282301d7584cSLiam Girdwood 		rtd->ops.hw_free	= soc_pcm_hw_free;
282401d7584cSLiam Girdwood 		rtd->ops.close		= soc_pcm_close;
282501d7584cSLiam Girdwood 		rtd->ops.pointer	= soc_pcm_pointer;
282601d7584cSLiam Girdwood 	}
282701d7584cSLiam Girdwood 
2828613fb500SKuninori Morimoto 	for_each_rtd_components(rtd, i, component) {
28292b544dd7SKuninori Morimoto 		const struct snd_soc_component_driver *drv = component->driver;
2830b8135864SKuninori Morimoto 
28313b1c952cSTakashi Iwai 		if (drv->ioctl)
28323b1c952cSTakashi Iwai 			rtd->ops.ioctl		= snd_soc_pcm_component_ioctl;
28331e5ddb6bSTakashi Iwai 		if (drv->sync_stop)
28341e5ddb6bSTakashi Iwai 			rtd->ops.sync_stop	= snd_soc_pcm_component_sync_stop;
2835e9067bb5SKuninori Morimoto 		if (drv->copy_user)
283682d81f5cSKuninori Morimoto 			rtd->ops.copy_user	= snd_soc_pcm_component_copy_user;
2837e9067bb5SKuninori Morimoto 		if (drv->page)
28389c712e4fSKuninori Morimoto 			rtd->ops.page		= snd_soc_pcm_component_page;
2839e9067bb5SKuninori Morimoto 		if (drv->mmap)
2840205875e1SKuninori Morimoto 			rtd->ops.mmap		= snd_soc_pcm_component_mmap;
2841b8135864SKuninori Morimoto 	}
2842b8135864SKuninori Morimoto 
2843ddee627cSLiam Girdwood 	if (playback)
284401d7584cSLiam Girdwood 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
2845ddee627cSLiam Girdwood 
2846ddee627cSLiam Girdwood 	if (capture)
284701d7584cSLiam Girdwood 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
2848ddee627cSLiam Girdwood 
2849b2b2afbbSKuninori Morimoto 	ret = snd_soc_pcm_component_new(rtd);
2850ddee627cSLiam Girdwood 	if (ret < 0) {
28517484291eSKuninori Morimoto 		dev_err(rtd->dev, "ASoC: pcm constructor failed: %d\n", ret);
2852ddee627cSLiam Girdwood 		return ret;
2853ddee627cSLiam Girdwood 	}
2854c641e5b2SJohan Hovold 
28553d21ef0bSTakashi Iwai 	pcm->no_device_suspend = true;
285601d7584cSLiam Girdwood out:
28572e5894d7SBenoit Cousson 	dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
28582e5894d7SBenoit Cousson 		 (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
2859ddee627cSLiam Girdwood 		 cpu_dai->name);
2860ddee627cSLiam Girdwood 	return ret;
2861ddee627cSLiam Girdwood }
286201d7584cSLiam Girdwood 
286301d7584cSLiam Girdwood /* is the current PCM operation for this FE ? */
286401d7584cSLiam Girdwood int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream)
286501d7584cSLiam Girdwood {
286601d7584cSLiam Girdwood 	if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE)
286701d7584cSLiam Girdwood 		return 1;
286801d7584cSLiam Girdwood 	return 0;
286901d7584cSLiam Girdwood }
287001d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update);
287101d7584cSLiam Girdwood 
287201d7584cSLiam Girdwood /* is the current PCM operation for this BE ? */
287301d7584cSLiam Girdwood int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
287401d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
287501d7584cSLiam Girdwood {
287601d7584cSLiam Girdwood 	if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) ||
287701d7584cSLiam Girdwood 	   ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) &&
287801d7584cSLiam Girdwood 		  be->dpcm[stream].runtime_update))
287901d7584cSLiam Girdwood 		return 1;
288001d7584cSLiam Girdwood 	return 0;
288101d7584cSLiam Girdwood }
288201d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update);
288301d7584cSLiam Girdwood 
288401d7584cSLiam Girdwood /* get the substream for this BE */
288501d7584cSLiam Girdwood struct snd_pcm_substream *
288601d7584cSLiam Girdwood 	snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream)
288701d7584cSLiam Girdwood {
288801d7584cSLiam Girdwood 	return be->pcm->streams[stream].substream;
288901d7584cSLiam Girdwood }
289001d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream);
289101d7584cSLiam Girdwood 
2892085d22beSKuninori Morimoto static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe,
2893085d22beSKuninori Morimoto 				    struct snd_soc_pcm_runtime *be,
2894085d22beSKuninori Morimoto 				    int stream,
2895085d22beSKuninori Morimoto 				    const enum snd_soc_dpcm_state *states,
2896085d22beSKuninori Morimoto 				    int num_states)
289701d7584cSLiam Girdwood {
289801d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
289901d7584cSLiam Girdwood 	int state;
2900a9764869SKaiChieh Chuang 	int ret = 1;
2901a9764869SKaiChieh Chuang 	unsigned long flags;
2902085d22beSKuninori Morimoto 	int i;
290301d7584cSLiam Girdwood 
2904a9764869SKaiChieh Chuang 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2905d2e24d64SKuninori Morimoto 	for_each_dpcm_fe(be, stream, dpcm) {
290601d7584cSLiam Girdwood 
290701d7584cSLiam Girdwood 		if (dpcm->fe == fe)
290801d7584cSLiam Girdwood 			continue;
290901d7584cSLiam Girdwood 
291001d7584cSLiam Girdwood 		state = dpcm->fe->dpcm[stream].state;
2911085d22beSKuninori Morimoto 		for (i = 0; i < num_states; i++) {
2912085d22beSKuninori Morimoto 			if (state == states[i]) {
2913a9764869SKaiChieh Chuang 				ret = 0;
2914a9764869SKaiChieh Chuang 				break;
291501d7584cSLiam Girdwood 			}
2916a9764869SKaiChieh Chuang 		}
2917085d22beSKuninori Morimoto 	}
2918a9764869SKaiChieh Chuang 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
291901d7584cSLiam Girdwood 
2920085d22beSKuninori Morimoto 	/* it's safe to do this BE DAI */
2921a9764869SKaiChieh Chuang 	return ret;
292201d7584cSLiam Girdwood }
2923085d22beSKuninori Morimoto 
2924085d22beSKuninori Morimoto /*
2925085d22beSKuninori Morimoto  * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE
2926085d22beSKuninori Morimoto  * are not running, paused or suspended for the specified stream direction.
2927085d22beSKuninori Morimoto  */
2928085d22beSKuninori Morimoto int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
2929085d22beSKuninori Morimoto 		struct snd_soc_pcm_runtime *be, int stream)
2930085d22beSKuninori Morimoto {
2931085d22beSKuninori Morimoto 	const enum snd_soc_dpcm_state state[] = {
2932085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_START,
2933085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_PAUSED,
2934085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_SUSPEND,
2935085d22beSKuninori Morimoto 	};
2936085d22beSKuninori Morimoto 
2937085d22beSKuninori Morimoto 	return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state));
2938085d22beSKuninori Morimoto }
293901d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
294001d7584cSLiam Girdwood 
294101d7584cSLiam Girdwood /*
294201d7584cSLiam Girdwood  * We can only change hw params a BE DAI if any of it's FE are not prepared,
294301d7584cSLiam Girdwood  * running, paused or suspended for the specified stream direction.
294401d7584cSLiam Girdwood  */
294501d7584cSLiam Girdwood int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
294601d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
294701d7584cSLiam Girdwood {
2948085d22beSKuninori Morimoto 	const enum snd_soc_dpcm_state state[] = {
2949085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_START,
2950085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_PAUSED,
2951085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_SUSPEND,
2952085d22beSKuninori Morimoto 		SND_SOC_DPCM_STATE_PREPARE,
2953085d22beSKuninori Morimoto 	};
295401d7584cSLiam Girdwood 
2955085d22beSKuninori Morimoto 	return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state));
295601d7584cSLiam Girdwood }
295701d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
2958f86dcef8SLiam Girdwood 
2959f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS
296085280141SLars-Peter Clausen static const char *dpcm_state_string(enum snd_soc_dpcm_state state)
2961f86dcef8SLiam Girdwood {
2962f86dcef8SLiam Girdwood 	switch (state) {
2963f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_NEW:
2964f86dcef8SLiam Girdwood 		return "new";
2965f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_OPEN:
2966f86dcef8SLiam Girdwood 		return "open";
2967f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_HW_PARAMS:
2968f86dcef8SLiam Girdwood 		return "hw_params";
2969f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_PREPARE:
2970f86dcef8SLiam Girdwood 		return "prepare";
2971f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_START:
2972f86dcef8SLiam Girdwood 		return "start";
2973f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_STOP:
2974f86dcef8SLiam Girdwood 		return "stop";
2975f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_SUSPEND:
2976f86dcef8SLiam Girdwood 		return "suspend";
2977f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_PAUSED:
2978f86dcef8SLiam Girdwood 		return "paused";
2979f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_HW_FREE:
2980f86dcef8SLiam Girdwood 		return "hw_free";
2981f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_CLOSE:
2982f86dcef8SLiam Girdwood 		return "close";
2983f86dcef8SLiam Girdwood 	}
2984f86dcef8SLiam Girdwood 
2985f86dcef8SLiam Girdwood 	return "unknown";
2986f86dcef8SLiam Girdwood }
2987f86dcef8SLiam Girdwood 
2988f86dcef8SLiam Girdwood static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
2989f86dcef8SLiam Girdwood 				int stream, char *buf, size_t size)
2990f86dcef8SLiam Girdwood {
2991f86dcef8SLiam Girdwood 	struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
2992f86dcef8SLiam Girdwood 	struct snd_soc_dpcm *dpcm;
2993f86dcef8SLiam Girdwood 	ssize_t offset = 0;
2994a9764869SKaiChieh Chuang 	unsigned long flags;
2995f86dcef8SLiam Girdwood 
2996f86dcef8SLiam Girdwood 	/* FE state */
2997f86dcef8SLiam Girdwood 	offset += snprintf(buf + offset, size - offset,
2998f86dcef8SLiam Girdwood 			"[%s - %s]\n", fe->dai_link->name,
2999f86dcef8SLiam Girdwood 			stream ? "Capture" : "Playback");
3000f86dcef8SLiam Girdwood 
3001f86dcef8SLiam Girdwood 	offset += snprintf(buf + offset, size - offset, "State: %s\n",
3002f86dcef8SLiam Girdwood 	                dpcm_state_string(fe->dpcm[stream].state));
3003f86dcef8SLiam Girdwood 
3004f86dcef8SLiam Girdwood 	if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
3005f86dcef8SLiam Girdwood 	    (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
3006f86dcef8SLiam Girdwood 		offset += snprintf(buf + offset, size - offset,
3007f86dcef8SLiam Girdwood 				"Hardware Params: "
3008f86dcef8SLiam Girdwood 				"Format = %s, Channels = %d, Rate = %d\n",
3009f86dcef8SLiam Girdwood 				snd_pcm_format_name(params_format(params)),
3010f86dcef8SLiam Girdwood 				params_channels(params),
3011f86dcef8SLiam Girdwood 				params_rate(params));
3012f86dcef8SLiam Girdwood 
3013f86dcef8SLiam Girdwood 	/* BEs state */
3014f86dcef8SLiam Girdwood 	offset += snprintf(buf + offset, size - offset, "Backends:\n");
3015f86dcef8SLiam Girdwood 
3016f86dcef8SLiam Girdwood 	if (list_empty(&fe->dpcm[stream].be_clients)) {
3017f86dcef8SLiam Girdwood 		offset += snprintf(buf + offset, size - offset,
3018f86dcef8SLiam Girdwood 				" No active DSP links\n");
3019f86dcef8SLiam Girdwood 		goto out;
3020f86dcef8SLiam Girdwood 	}
3021f86dcef8SLiam Girdwood 
3022a9764869SKaiChieh Chuang 	spin_lock_irqsave(&fe->card->dpcm_lock, flags);
30238d6258a4SKuninori Morimoto 	for_each_dpcm_be(fe, stream, dpcm) {
3024f86dcef8SLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
3025f86dcef8SLiam Girdwood 		params = &dpcm->hw_params;
3026f86dcef8SLiam Girdwood 
3027f86dcef8SLiam Girdwood 		offset += snprintf(buf + offset, size - offset,
3028f86dcef8SLiam Girdwood 				"- %s\n", be->dai_link->name);
3029f86dcef8SLiam Girdwood 
3030f86dcef8SLiam Girdwood 		offset += snprintf(buf + offset, size - offset,
3031f86dcef8SLiam Girdwood 				"   State: %s\n",
3032f86dcef8SLiam Girdwood 				dpcm_state_string(be->dpcm[stream].state));
3033f86dcef8SLiam Girdwood 
3034f86dcef8SLiam Girdwood 		if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
3035f86dcef8SLiam Girdwood 		    (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
3036f86dcef8SLiam Girdwood 			offset += snprintf(buf + offset, size - offset,
3037f86dcef8SLiam Girdwood 				"   Hardware Params: "
3038f86dcef8SLiam Girdwood 				"Format = %s, Channels = %d, Rate = %d\n",
3039f86dcef8SLiam Girdwood 				snd_pcm_format_name(params_format(params)),
3040f86dcef8SLiam Girdwood 				params_channels(params),
3041f86dcef8SLiam Girdwood 				params_rate(params));
3042f86dcef8SLiam Girdwood 	}
3043a9764869SKaiChieh Chuang 	spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
3044f86dcef8SLiam Girdwood out:
3045f86dcef8SLiam Girdwood 	return offset;
3046f86dcef8SLiam Girdwood }
3047f86dcef8SLiam Girdwood 
3048f86dcef8SLiam Girdwood static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
3049f86dcef8SLiam Girdwood 				size_t count, loff_t *ppos)
3050f86dcef8SLiam Girdwood {
3051f86dcef8SLiam Girdwood 	struct snd_soc_pcm_runtime *fe = file->private_data;
3052f86dcef8SLiam Girdwood 	ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
30537083f877SKuninori Morimoto 	int stream;
3054f86dcef8SLiam Girdwood 	char *buf;
3055f86dcef8SLiam Girdwood 
3056f86dcef8SLiam Girdwood 	buf = kmalloc(out_count, GFP_KERNEL);
3057f86dcef8SLiam Girdwood 	if (!buf)
3058f86dcef8SLiam Girdwood 		return -ENOMEM;
3059f86dcef8SLiam Girdwood 
30607083f877SKuninori Morimoto 	for_each_pcm_streams(stream)
30617083f877SKuninori Morimoto 		if (snd_soc_dai_stream_valid(fe->cpu_dai, stream))
30627083f877SKuninori Morimoto 			offset += dpcm_show_state(fe, stream,
30637083f877SKuninori Morimoto 						  buf + offset,
30647083f877SKuninori Morimoto 						  out_count - offset);
3065f86dcef8SLiam Girdwood 
3066f86dcef8SLiam Girdwood 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
3067f86dcef8SLiam Girdwood 
3068f86dcef8SLiam Girdwood 	kfree(buf);
3069f86dcef8SLiam Girdwood 	return ret;
3070f86dcef8SLiam Girdwood }
3071f86dcef8SLiam Girdwood 
3072f86dcef8SLiam Girdwood static const struct file_operations dpcm_state_fops = {
3073f57b8488SLiam Girdwood 	.open = simple_open,
3074f86dcef8SLiam Girdwood 	.read = dpcm_state_read_file,
3075f86dcef8SLiam Girdwood 	.llseek = default_llseek,
3076f86dcef8SLiam Girdwood };
3077f86dcef8SLiam Girdwood 
30782e55b90aSLars-Peter Clausen void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
3079f86dcef8SLiam Girdwood {
3080b3bba9a1SMark Brown 	if (!rtd->dai_link)
30812e55b90aSLars-Peter Clausen 		return;
3082b3bba9a1SMark Brown 
3083596becd3SKuninori Morimoto 	if (!rtd->dai_link->dynamic)
3084596becd3SKuninori Morimoto 		return;
3085596becd3SKuninori Morimoto 
30866553bf06SLars-Peter Clausen 	if (!rtd->card->debugfs_card_root)
30876553bf06SLars-Peter Clausen 		return;
3088f86dcef8SLiam Girdwood 
3089f86dcef8SLiam Girdwood 	rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
3090f86dcef8SLiam Girdwood 			rtd->card->debugfs_card_root);
3091f86dcef8SLiam Girdwood 
3092f1e3f409SFabio Estevam 	debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root,
3093f86dcef8SLiam Girdwood 			    rtd, &dpcm_state_fops);
3094f86dcef8SLiam Girdwood }
3095f86dcef8SLiam Girdwood #endif
3096