xref: /openbmc/linux/sound/soc/soc-pcm.c (revision fbb16563c6c2b7fc4944adc49f93c1dc6fe25770)
1ddee627cSLiam Girdwood /*
2ddee627cSLiam Girdwood  * soc-pcm.c  --  ALSA SoC PCM
3ddee627cSLiam Girdwood  *
4ddee627cSLiam Girdwood  * Copyright 2005 Wolfson Microelectronics PLC.
5ddee627cSLiam Girdwood  * Copyright 2005 Openedhand Ltd.
6ddee627cSLiam Girdwood  * Copyright (C) 2010 Slimlogic Ltd.
7ddee627cSLiam Girdwood  * Copyright (C) 2010 Texas Instruments Inc.
8ddee627cSLiam Girdwood  *
9ddee627cSLiam Girdwood  * Authors: Liam Girdwood <lrg@ti.com>
10ddee627cSLiam Girdwood  *          Mark Brown <broonie@opensource.wolfsonmicro.com>
11ddee627cSLiam Girdwood  *
12ddee627cSLiam Girdwood  *  This program is free software; you can redistribute  it and/or modify it
13ddee627cSLiam Girdwood  *  under  the terms of  the GNU General  Public License as published by the
14ddee627cSLiam Girdwood  *  Free Software Foundation;  either version 2 of the  License, or (at your
15ddee627cSLiam Girdwood  *  option) any later version.
16ddee627cSLiam Girdwood  *
17ddee627cSLiam Girdwood  */
18ddee627cSLiam Girdwood 
19ddee627cSLiam Girdwood #include <linux/kernel.h>
20ddee627cSLiam Girdwood #include <linux/init.h>
21ddee627cSLiam Girdwood #include <linux/delay.h>
22988e8cc4SNicolin Chen #include <linux/pinctrl/consumer.h>
23d6652ef8SMark Brown #include <linux/pm_runtime.h>
24ddee627cSLiam Girdwood #include <linux/slab.h>
25ddee627cSLiam Girdwood #include <linux/workqueue.h>
2601d7584cSLiam Girdwood #include <linux/export.h>
27f86dcef8SLiam Girdwood #include <linux/debugfs.h>
28ddee627cSLiam Girdwood #include <sound/core.h>
29ddee627cSLiam Girdwood #include <sound/pcm.h>
30ddee627cSLiam Girdwood #include <sound/pcm_params.h>
31ddee627cSLiam Girdwood #include <sound/soc.h>
3201d7584cSLiam Girdwood #include <sound/soc-dpcm.h>
33ddee627cSLiam Girdwood #include <sound/initval.h>
34ddee627cSLiam Girdwood 
3501d7584cSLiam Girdwood #define DPCM_MAX_BE_USERS	8
3601d7584cSLiam Girdwood 
37cde79035SRicard Wanderlof /*
38cde79035SRicard Wanderlof  * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
39cde79035SRicard Wanderlof  *
40cde79035SRicard Wanderlof  * Returns true if the DAI supports the indicated stream type.
41cde79035SRicard Wanderlof  */
42cde79035SRicard Wanderlof static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream)
43cde79035SRicard Wanderlof {
44cde79035SRicard Wanderlof 	struct snd_soc_pcm_stream *codec_stream;
45cde79035SRicard Wanderlof 
46cde79035SRicard Wanderlof 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
47cde79035SRicard Wanderlof 		codec_stream = &dai->driver->playback;
48cde79035SRicard Wanderlof 	else
49cde79035SRicard Wanderlof 		codec_stream = &dai->driver->capture;
50cde79035SRicard Wanderlof 
51cde79035SRicard Wanderlof 	/* If the codec specifies any rate at all, it supports the stream. */
52cde79035SRicard Wanderlof 	return codec_stream->rates;
53cde79035SRicard Wanderlof }
54cde79035SRicard Wanderlof 
5590996f43SLars-Peter Clausen /**
5624894b76SLars-Peter Clausen  * snd_soc_runtime_activate() - Increment active count for PCM runtime components
5724894b76SLars-Peter Clausen  * @rtd: ASoC PCM runtime that is activated
5824894b76SLars-Peter Clausen  * @stream: Direction of the PCM stream
5924894b76SLars-Peter Clausen  *
6024894b76SLars-Peter Clausen  * Increments the active count for all the DAIs and components attached to a PCM
6124894b76SLars-Peter Clausen  * runtime. Should typically be called when a stream is opened.
6224894b76SLars-Peter Clausen  *
6324894b76SLars-Peter Clausen  * Must be called with the rtd->pcm_mutex being held
6424894b76SLars-Peter Clausen  */
6524894b76SLars-Peter Clausen void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
6624894b76SLars-Peter Clausen {
6724894b76SLars-Peter Clausen 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
682e5894d7SBenoit Cousson 	int i;
6924894b76SLars-Peter Clausen 
7024894b76SLars-Peter Clausen 	lockdep_assert_held(&rtd->pcm_mutex);
7124894b76SLars-Peter Clausen 
7224894b76SLars-Peter Clausen 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
7324894b76SLars-Peter Clausen 		cpu_dai->playback_active++;
742e5894d7SBenoit Cousson 		for (i = 0; i < rtd->num_codecs; i++)
752e5894d7SBenoit Cousson 			rtd->codec_dais[i]->playback_active++;
7624894b76SLars-Peter Clausen 	} else {
7724894b76SLars-Peter Clausen 		cpu_dai->capture_active++;
782e5894d7SBenoit Cousson 		for (i = 0; i < rtd->num_codecs; i++)
792e5894d7SBenoit Cousson 			rtd->codec_dais[i]->capture_active++;
8024894b76SLars-Peter Clausen 	}
8124894b76SLars-Peter Clausen 
8224894b76SLars-Peter Clausen 	cpu_dai->active++;
83cdde4ccbSLars-Peter Clausen 	cpu_dai->component->active++;
842e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
852e5894d7SBenoit Cousson 		rtd->codec_dais[i]->active++;
862e5894d7SBenoit Cousson 		rtd->codec_dais[i]->component->active++;
872e5894d7SBenoit Cousson 	}
8824894b76SLars-Peter Clausen }
8924894b76SLars-Peter Clausen 
9024894b76SLars-Peter Clausen /**
9124894b76SLars-Peter Clausen  * snd_soc_runtime_deactivate() - Decrement active count for PCM runtime components
9224894b76SLars-Peter Clausen  * @rtd: ASoC PCM runtime that is deactivated
9324894b76SLars-Peter Clausen  * @stream: Direction of the PCM stream
9424894b76SLars-Peter Clausen  *
9524894b76SLars-Peter Clausen  * Decrements the active count for all the DAIs and components attached to a PCM
9624894b76SLars-Peter Clausen  * runtime. Should typically be called when a stream is closed.
9724894b76SLars-Peter Clausen  *
9824894b76SLars-Peter Clausen  * Must be called with the rtd->pcm_mutex being held
9924894b76SLars-Peter Clausen  */
10024894b76SLars-Peter Clausen void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
10124894b76SLars-Peter Clausen {
10224894b76SLars-Peter Clausen 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
1032e5894d7SBenoit Cousson 	int i;
10424894b76SLars-Peter Clausen 
10524894b76SLars-Peter Clausen 	lockdep_assert_held(&rtd->pcm_mutex);
10624894b76SLars-Peter Clausen 
10724894b76SLars-Peter Clausen 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
10824894b76SLars-Peter Clausen 		cpu_dai->playback_active--;
1092e5894d7SBenoit Cousson 		for (i = 0; i < rtd->num_codecs; i++)
1102e5894d7SBenoit Cousson 			rtd->codec_dais[i]->playback_active--;
11124894b76SLars-Peter Clausen 	} else {
11224894b76SLars-Peter Clausen 		cpu_dai->capture_active--;
1132e5894d7SBenoit Cousson 		for (i = 0; i < rtd->num_codecs; i++)
1142e5894d7SBenoit Cousson 			rtd->codec_dais[i]->capture_active--;
11524894b76SLars-Peter Clausen 	}
11624894b76SLars-Peter Clausen 
11724894b76SLars-Peter Clausen 	cpu_dai->active--;
118cdde4ccbSLars-Peter Clausen 	cpu_dai->component->active--;
1192e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
1202e5894d7SBenoit Cousson 		rtd->codec_dais[i]->component->active--;
1212e5894d7SBenoit Cousson 		rtd->codec_dais[i]->active--;
1222e5894d7SBenoit Cousson 	}
12324894b76SLars-Peter Clausen }
12424894b76SLars-Peter Clausen 
12524894b76SLars-Peter Clausen /**
126208a1589SLars-Peter Clausen  * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay
127208a1589SLars-Peter Clausen  * @rtd: The ASoC PCM runtime that should be checked.
128208a1589SLars-Peter Clausen  *
129208a1589SLars-Peter Clausen  * This function checks whether the power down delay should be ignored for a
130208a1589SLars-Peter Clausen  * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has
131208a1589SLars-Peter Clausen  * been configured to ignore the delay, or if none of the components benefits
132208a1589SLars-Peter Clausen  * from having the delay.
133208a1589SLars-Peter Clausen  */
134208a1589SLars-Peter Clausen bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
135208a1589SLars-Peter Clausen {
136*fbb16563SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
137*fbb16563SKuninori Morimoto 	struct snd_soc_component *component;
1382e5894d7SBenoit Cousson 	int i;
1392e5894d7SBenoit Cousson 	bool ignore = true;
1402e5894d7SBenoit Cousson 
141208a1589SLars-Peter Clausen 	if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
142208a1589SLars-Peter Clausen 		return true;
143208a1589SLars-Peter Clausen 
144*fbb16563SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
145*fbb16563SKuninori Morimoto 		component = rtdcom->component;
146*fbb16563SKuninori Morimoto 
147*fbb16563SKuninori Morimoto 		ignore &= !component->driver->pmdown_time;
148*fbb16563SKuninori Morimoto 	}
149*fbb16563SKuninori Morimoto 
150*fbb16563SKuninori Morimoto 	/* this will be removed */
1512e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++)
1522e5894d7SBenoit Cousson 		ignore &= rtd->codec_dais[i]->component->ignore_pmdown_time;
1532e5894d7SBenoit Cousson 
154*fbb16563SKuninori Morimoto 	return ignore;
155208a1589SLars-Peter Clausen }
156208a1589SLars-Peter Clausen 
157208a1589SLars-Peter Clausen /**
15890996f43SLars-Peter Clausen  * snd_soc_set_runtime_hwparams - set the runtime hardware parameters
15990996f43SLars-Peter Clausen  * @substream: the pcm substream
16090996f43SLars-Peter Clausen  * @hw: the hardware parameters
16190996f43SLars-Peter Clausen  *
16290996f43SLars-Peter Clausen  * Sets the substream runtime hardware parameters.
16390996f43SLars-Peter Clausen  */
16490996f43SLars-Peter Clausen int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
16590996f43SLars-Peter Clausen 	const struct snd_pcm_hardware *hw)
16690996f43SLars-Peter Clausen {
16790996f43SLars-Peter Clausen 	struct snd_pcm_runtime *runtime = substream->runtime;
16890996f43SLars-Peter Clausen 	runtime->hw.info = hw->info;
16990996f43SLars-Peter Clausen 	runtime->hw.formats = hw->formats;
17090996f43SLars-Peter Clausen 	runtime->hw.period_bytes_min = hw->period_bytes_min;
17190996f43SLars-Peter Clausen 	runtime->hw.period_bytes_max = hw->period_bytes_max;
17290996f43SLars-Peter Clausen 	runtime->hw.periods_min = hw->periods_min;
17390996f43SLars-Peter Clausen 	runtime->hw.periods_max = hw->periods_max;
17490996f43SLars-Peter Clausen 	runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
17590996f43SLars-Peter Clausen 	runtime->hw.fifo_size = hw->fifo_size;
17690996f43SLars-Peter Clausen 	return 0;
17790996f43SLars-Peter Clausen }
17890996f43SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
17990996f43SLars-Peter Clausen 
18001d7584cSLiam Girdwood /* DPCM stream event, send event to FE and all active BEs. */
18123607025SLiam Girdwood int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
18201d7584cSLiam Girdwood 	int event)
18301d7584cSLiam Girdwood {
18401d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
18501d7584cSLiam Girdwood 
18601d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[dir].be_clients, list_be) {
18701d7584cSLiam Girdwood 
18801d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
18901d7584cSLiam Girdwood 
190103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n",
19101d7584cSLiam Girdwood 				be->dai_link->name, event, dir);
19201d7584cSLiam Girdwood 
193b1cd2e34SBanajit Goswami 		if ((event == SND_SOC_DAPM_STREAM_STOP) &&
194b1cd2e34SBanajit Goswami 		    (be->dpcm[dir].users >= 1))
195b1cd2e34SBanajit Goswami 			continue;
196b1cd2e34SBanajit Goswami 
19701d7584cSLiam Girdwood 		snd_soc_dapm_stream_event(be, dir, event);
19801d7584cSLiam Girdwood 	}
19901d7584cSLiam Girdwood 
20001d7584cSLiam Girdwood 	snd_soc_dapm_stream_event(fe, dir, event);
20101d7584cSLiam Girdwood 
20201d7584cSLiam Girdwood 	return 0;
20301d7584cSLiam Girdwood }
20401d7584cSLiam Girdwood 
20517841020SDong Aisheng static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
20617841020SDong Aisheng 					struct snd_soc_dai *soc_dai)
207ddee627cSLiam Girdwood {
208ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
209ddee627cSLiam Girdwood 	int ret;
210ddee627cSLiam Girdwood 
2113635bf09SNicolin Chen 	if (soc_dai->rate && (soc_dai->driver->symmetric_rates ||
2123635bf09SNicolin Chen 				rtd->dai_link->symmetric_rates)) {
2133635bf09SNicolin Chen 		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
2143635bf09SNicolin Chen 				soc_dai->rate);
215ddee627cSLiam Girdwood 
2164dcdd43bSLars-Peter Clausen 		ret = snd_pcm_hw_constraint_single(substream->runtime,
217ddee627cSLiam Girdwood 						SNDRV_PCM_HW_PARAM_RATE,
2184dcdd43bSLars-Peter Clausen 						soc_dai->rate);
219ddee627cSLiam Girdwood 		if (ret < 0) {
22017841020SDong Aisheng 			dev_err(soc_dai->dev,
2213635bf09SNicolin Chen 				"ASoC: Unable to apply rate constraint: %d\n",
222103d84a3SLiam Girdwood 				ret);
223ddee627cSLiam Girdwood 			return ret;
224ddee627cSLiam Girdwood 		}
2253635bf09SNicolin Chen 	}
2263635bf09SNicolin Chen 
2273635bf09SNicolin Chen 	if (soc_dai->channels && (soc_dai->driver->symmetric_channels ||
2283635bf09SNicolin Chen 				rtd->dai_link->symmetric_channels)) {
2293635bf09SNicolin Chen 		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n",
2303635bf09SNicolin Chen 				soc_dai->channels);
2313635bf09SNicolin Chen 
2324dcdd43bSLars-Peter Clausen 		ret = snd_pcm_hw_constraint_single(substream->runtime,
2333635bf09SNicolin Chen 						SNDRV_PCM_HW_PARAM_CHANNELS,
2343635bf09SNicolin Chen 						soc_dai->channels);
2353635bf09SNicolin Chen 		if (ret < 0) {
2363635bf09SNicolin Chen 			dev_err(soc_dai->dev,
2373635bf09SNicolin Chen 				"ASoC: Unable to apply channel symmetry constraint: %d\n",
2383635bf09SNicolin Chen 				ret);
2393635bf09SNicolin Chen 			return ret;
2403635bf09SNicolin Chen 		}
2413635bf09SNicolin Chen 	}
2423635bf09SNicolin Chen 
2433635bf09SNicolin Chen 	if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits ||
2443635bf09SNicolin Chen 				rtd->dai_link->symmetric_samplebits)) {
2453635bf09SNicolin Chen 		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n",
2463635bf09SNicolin Chen 				soc_dai->sample_bits);
2473635bf09SNicolin Chen 
2484dcdd43bSLars-Peter Clausen 		ret = snd_pcm_hw_constraint_single(substream->runtime,
2493635bf09SNicolin Chen 						SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
2503635bf09SNicolin Chen 						soc_dai->sample_bits);
2513635bf09SNicolin Chen 		if (ret < 0) {
2523635bf09SNicolin Chen 			dev_err(soc_dai->dev,
2533635bf09SNicolin Chen 				"ASoC: Unable to apply sample bits symmetry constraint: %d\n",
2543635bf09SNicolin Chen 				ret);
2553635bf09SNicolin Chen 			return ret;
2563635bf09SNicolin Chen 		}
2573635bf09SNicolin Chen 	}
258ddee627cSLiam Girdwood 
259ddee627cSLiam Girdwood 	return 0;
260ddee627cSLiam Girdwood }
261ddee627cSLiam Girdwood 
2623635bf09SNicolin Chen static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
2633635bf09SNicolin Chen 				struct snd_pcm_hw_params *params)
2643635bf09SNicolin Chen {
2653635bf09SNicolin Chen 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2663635bf09SNicolin Chen 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
2672e5894d7SBenoit Cousson 	unsigned int rate, channels, sample_bits, symmetry, i;
2683635bf09SNicolin Chen 
2693635bf09SNicolin Chen 	rate = params_rate(params);
2703635bf09SNicolin Chen 	channels = params_channels(params);
2713635bf09SNicolin Chen 	sample_bits = snd_pcm_format_physical_width(params_format(params));
2723635bf09SNicolin Chen 
2733635bf09SNicolin Chen 	/* reject unmatched parameters when applying symmetry */
2743635bf09SNicolin Chen 	symmetry = cpu_dai->driver->symmetric_rates ||
2753635bf09SNicolin Chen 		rtd->dai_link->symmetric_rates;
2762e5894d7SBenoit Cousson 
2772e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++)
2782e5894d7SBenoit Cousson 		symmetry |= rtd->codec_dais[i]->driver->symmetric_rates;
2792e5894d7SBenoit Cousson 
2803635bf09SNicolin Chen 	if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) {
2813635bf09SNicolin Chen 		dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n",
2823635bf09SNicolin Chen 				cpu_dai->rate, rate);
2833635bf09SNicolin Chen 		return -EINVAL;
2843635bf09SNicolin Chen 	}
2853635bf09SNicolin Chen 
2863635bf09SNicolin Chen 	symmetry = cpu_dai->driver->symmetric_channels ||
2873635bf09SNicolin Chen 		rtd->dai_link->symmetric_channels;
2882e5894d7SBenoit Cousson 
2892e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++)
2902e5894d7SBenoit Cousson 		symmetry |= rtd->codec_dais[i]->driver->symmetric_channels;
2912e5894d7SBenoit Cousson 
2923635bf09SNicolin Chen 	if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) {
2933635bf09SNicolin Chen 		dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n",
2943635bf09SNicolin Chen 				cpu_dai->channels, channels);
2953635bf09SNicolin Chen 		return -EINVAL;
2963635bf09SNicolin Chen 	}
2973635bf09SNicolin Chen 
2983635bf09SNicolin Chen 	symmetry = cpu_dai->driver->symmetric_samplebits ||
2993635bf09SNicolin Chen 		rtd->dai_link->symmetric_samplebits;
3002e5894d7SBenoit Cousson 
3012e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++)
3022e5894d7SBenoit Cousson 		symmetry |= rtd->codec_dais[i]->driver->symmetric_samplebits;
3032e5894d7SBenoit Cousson 
3043635bf09SNicolin Chen 	if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) {
3053635bf09SNicolin Chen 		dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n",
3063635bf09SNicolin Chen 				cpu_dai->sample_bits, sample_bits);
3073635bf09SNicolin Chen 		return -EINVAL;
3083635bf09SNicolin Chen 	}
309ddee627cSLiam Girdwood 
310ddee627cSLiam Girdwood 	return 0;
311ddee627cSLiam Girdwood }
312ddee627cSLiam Girdwood 
31362e5f676SLars-Peter Clausen static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream)
31462e5f676SLars-Peter Clausen {
31562e5f676SLars-Peter Clausen 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
31662e5f676SLars-Peter Clausen 	struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver;
31762e5f676SLars-Peter Clausen 	struct snd_soc_dai_link *link = rtd->dai_link;
3182e5894d7SBenoit Cousson 	unsigned int symmetry, i;
31962e5f676SLars-Peter Clausen 
3202e5894d7SBenoit Cousson 	symmetry = cpu_driver->symmetric_rates || link->symmetric_rates ||
3212e5894d7SBenoit Cousson 		cpu_driver->symmetric_channels || link->symmetric_channels ||
3222e5894d7SBenoit Cousson 		cpu_driver->symmetric_samplebits || link->symmetric_samplebits;
3232e5894d7SBenoit Cousson 
3242e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++)
3252e5894d7SBenoit Cousson 		symmetry = symmetry ||
3262e5894d7SBenoit Cousson 			rtd->codec_dais[i]->driver->symmetric_rates ||
3272e5894d7SBenoit Cousson 			rtd->codec_dais[i]->driver->symmetric_channels ||
3282e5894d7SBenoit Cousson 			rtd->codec_dais[i]->driver->symmetric_samplebits;
3292e5894d7SBenoit Cousson 
3302e5894d7SBenoit Cousson 	return symmetry;
33162e5f676SLars-Peter Clausen }
33262e5f676SLars-Peter Clausen 
3332e5894d7SBenoit Cousson static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits)
33458ba9b25SMark Brown {
3352e5894d7SBenoit Cousson 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
336c6068d3aSTakashi Iwai 	int ret;
33758ba9b25SMark Brown 
33858ba9b25SMark Brown 	if (!bits)
33958ba9b25SMark Brown 		return;
34058ba9b25SMark Brown 
3410e2a3751SLars-Peter Clausen 	ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, bits);
34258ba9b25SMark Brown 	if (ret != 0)
3430e2a3751SLars-Peter Clausen 		dev_warn(rtd->dev, "ASoC: Failed to set MSB %d: %d\n",
3440e2a3751SLars-Peter Clausen 				 bits, ret);
34558ba9b25SMark Brown }
34658ba9b25SMark Brown 
347c8dd1fecSBenoit Cousson static void soc_pcm_apply_msb(struct snd_pcm_substream *substream)
348bd477c31SLars-Peter Clausen {
349c8dd1fecSBenoit Cousson 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
350c8dd1fecSBenoit Cousson 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
3512e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
3522e5894d7SBenoit Cousson 	int i;
353c8dd1fecSBenoit Cousson 	unsigned int bits = 0, cpu_bits;
354c8dd1fecSBenoit Cousson 
355c8dd1fecSBenoit Cousson 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3562e5894d7SBenoit Cousson 		for (i = 0; i < rtd->num_codecs; i++) {
3572e5894d7SBenoit Cousson 			codec_dai = rtd->codec_dais[i];
3582e5894d7SBenoit Cousson 			if (codec_dai->driver->playback.sig_bits == 0) {
3592e5894d7SBenoit Cousson 				bits = 0;
3602e5894d7SBenoit Cousson 				break;
3612e5894d7SBenoit Cousson 			}
3622e5894d7SBenoit Cousson 			bits = max(codec_dai->driver->playback.sig_bits, bits);
3632e5894d7SBenoit Cousson 		}
364c8dd1fecSBenoit Cousson 		cpu_bits = cpu_dai->driver->playback.sig_bits;
365c8dd1fecSBenoit Cousson 	} else {
3662e5894d7SBenoit Cousson 		for (i = 0; i < rtd->num_codecs; i++) {
3672e5894d7SBenoit Cousson 			codec_dai = rtd->codec_dais[i];
3685e63dfccSDaniel Mack 			if (codec_dai->driver->capture.sig_bits == 0) {
3692e5894d7SBenoit Cousson 				bits = 0;
3702e5894d7SBenoit Cousson 				break;
3712e5894d7SBenoit Cousson 			}
3722e5894d7SBenoit Cousson 			bits = max(codec_dai->driver->capture.sig_bits, bits);
3732e5894d7SBenoit Cousson 		}
374c8dd1fecSBenoit Cousson 		cpu_bits = cpu_dai->driver->capture.sig_bits;
375c8dd1fecSBenoit Cousson 	}
376c8dd1fecSBenoit Cousson 
3772e5894d7SBenoit Cousson 	soc_pcm_set_msb(substream, bits);
3782e5894d7SBenoit Cousson 	soc_pcm_set_msb(substream, cpu_bits);
379c8dd1fecSBenoit Cousson }
380c8dd1fecSBenoit Cousson 
3812e5894d7SBenoit Cousson static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
382bd477c31SLars-Peter Clausen {
3832e5894d7SBenoit Cousson 	struct snd_pcm_runtime *runtime = substream->runtime;
38478e45c99SLars-Peter Clausen 	struct snd_pcm_hardware *hw = &runtime->hw;
3852e5894d7SBenoit Cousson 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
3862e5894d7SBenoit Cousson 	struct snd_soc_dai_driver *cpu_dai_drv = rtd->cpu_dai->driver;
3872e5894d7SBenoit Cousson 	struct snd_soc_dai_driver *codec_dai_drv;
3882e5894d7SBenoit Cousson 	struct snd_soc_pcm_stream *codec_stream;
3892e5894d7SBenoit Cousson 	struct snd_soc_pcm_stream *cpu_stream;
3902e5894d7SBenoit Cousson 	unsigned int chan_min = 0, chan_max = UINT_MAX;
3912e5894d7SBenoit Cousson 	unsigned int rate_min = 0, rate_max = UINT_MAX;
3922e5894d7SBenoit Cousson 	unsigned int rates = UINT_MAX;
3932e5894d7SBenoit Cousson 	u64 formats = ULLONG_MAX;
3942e5894d7SBenoit Cousson 	int i;
39578e45c99SLars-Peter Clausen 
3962e5894d7SBenoit Cousson 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3972e5894d7SBenoit Cousson 		cpu_stream = &cpu_dai_drv->playback;
39816d7ea91SLars-Peter Clausen 	else
3992e5894d7SBenoit Cousson 		cpu_stream = &cpu_dai_drv->capture;
40078e45c99SLars-Peter Clausen 
4012e5894d7SBenoit Cousson 	/* first calculate min/max only for CODECs in the DAI link */
4022e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
403cde79035SRicard Wanderlof 
404cde79035SRicard Wanderlof 		/*
405cde79035SRicard Wanderlof 		 * Skip CODECs which don't support the current stream type.
406cde79035SRicard Wanderlof 		 * Otherwise, since the rate, channel, and format values will
407cde79035SRicard Wanderlof 		 * zero in that case, we would have no usable settings left,
408cde79035SRicard Wanderlof 		 * causing the resulting setup to fail.
409cde79035SRicard Wanderlof 		 * At least one CODEC should match, otherwise we should have
410cde79035SRicard Wanderlof 		 * bailed out on a higher level, since there would be no
411cde79035SRicard Wanderlof 		 * CODEC to support the transfer direction in that case.
412cde79035SRicard Wanderlof 		 */
413cde79035SRicard Wanderlof 		if (!snd_soc_dai_stream_valid(rtd->codec_dais[i],
414cde79035SRicard Wanderlof 					      substream->stream))
415cde79035SRicard Wanderlof 			continue;
416cde79035SRicard Wanderlof 
4172e5894d7SBenoit Cousson 		codec_dai_drv = rtd->codec_dais[i]->driver;
4182e5894d7SBenoit Cousson 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
4192e5894d7SBenoit Cousson 			codec_stream = &codec_dai_drv->playback;
4202e5894d7SBenoit Cousson 		else
4212e5894d7SBenoit Cousson 			codec_stream = &codec_dai_drv->capture;
4222e5894d7SBenoit Cousson 		chan_min = max(chan_min, codec_stream->channels_min);
4232e5894d7SBenoit Cousson 		chan_max = min(chan_max, codec_stream->channels_max);
4242e5894d7SBenoit Cousson 		rate_min = max(rate_min, codec_stream->rate_min);
4252e5894d7SBenoit Cousson 		rate_max = min_not_zero(rate_max, codec_stream->rate_max);
4262e5894d7SBenoit Cousson 		formats &= codec_stream->formats;
4272e5894d7SBenoit Cousson 		rates = snd_pcm_rate_mask_intersect(codec_stream->rates, rates);
4282e5894d7SBenoit Cousson 	}
4292e5894d7SBenoit Cousson 
4302e5894d7SBenoit Cousson 	/*
4312e5894d7SBenoit Cousson 	 * chan min/max cannot be enforced if there are multiple CODEC DAIs
4322e5894d7SBenoit Cousson 	 * connected to a single CPU DAI, use CPU DAI's directly and let
4332e5894d7SBenoit Cousson 	 * channel allocation be fixed up later
4342e5894d7SBenoit Cousson 	 */
4352e5894d7SBenoit Cousson 	if (rtd->num_codecs > 1) {
4362e5894d7SBenoit Cousson 		chan_min = cpu_stream->channels_min;
4372e5894d7SBenoit Cousson 		chan_max = cpu_stream->channels_max;
4382e5894d7SBenoit Cousson 	}
4392e5894d7SBenoit Cousson 
4402e5894d7SBenoit Cousson 	hw->channels_min = max(chan_min, cpu_stream->channels_min);
4412e5894d7SBenoit Cousson 	hw->channels_max = min(chan_max, cpu_stream->channels_max);
4422e5894d7SBenoit Cousson 	if (hw->formats)
4432e5894d7SBenoit Cousson 		hw->formats &= formats & cpu_stream->formats;
4442e5894d7SBenoit Cousson 	else
4452e5894d7SBenoit Cousson 		hw->formats = formats & cpu_stream->formats;
4462e5894d7SBenoit Cousson 	hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_stream->rates);
44778e45c99SLars-Peter Clausen 
44878e45c99SLars-Peter Clausen 	snd_pcm_limit_hw_rates(runtime);
44978e45c99SLars-Peter Clausen 
45078e45c99SLars-Peter Clausen 	hw->rate_min = max(hw->rate_min, cpu_stream->rate_min);
4512e5894d7SBenoit Cousson 	hw->rate_min = max(hw->rate_min, rate_min);
45278e45c99SLars-Peter Clausen 	hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max);
4532e5894d7SBenoit Cousson 	hw->rate_max = min_not_zero(hw->rate_max, rate_max);
454bd477c31SLars-Peter Clausen }
455bd477c31SLars-Peter Clausen 
45658ba9b25SMark Brown /*
457ddee627cSLiam Girdwood  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
458ddee627cSLiam Girdwood  * then initialized and any private data can be allocated. This also calls
459ddee627cSLiam Girdwood  * startup for the cpu DAI, platform, machine and codec DAI.
460ddee627cSLiam Girdwood  */
461ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream)
462ddee627cSLiam Girdwood {
463ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
464ddee627cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
465ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
46690be711eSKuninori Morimoto 	struct snd_soc_component *component;
46790be711eSKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
468ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
4692e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
4702e5894d7SBenoit Cousson 	const char *codec_dai_name = "multicodec";
471b8135864SKuninori Morimoto 	int i, ret = 0, __ret;
472ddee627cSLiam Girdwood 
473988e8cc4SNicolin Chen 	pinctrl_pm_select_default_state(cpu_dai->dev);
4742e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++)
4752e5894d7SBenoit Cousson 		pinctrl_pm_select_default_state(rtd->codec_dais[i]->dev);
47690be711eSKuninori Morimoto 
47790be711eSKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
47890be711eSKuninori Morimoto 		component = rtdcom->component;
47990be711eSKuninori Morimoto 
48090be711eSKuninori Morimoto 		pm_runtime_get_sync(component->dev);
48190be711eSKuninori Morimoto 	}
482d6652ef8SMark Brown 
483b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
484ddee627cSLiam Girdwood 
485ddee627cSLiam Girdwood 	/* startup the audio subsystem */
4869900a422SKuninori Morimoto 	if (cpu_dai->driver->ops->startup) {
487ddee627cSLiam Girdwood 		ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
488ddee627cSLiam Girdwood 		if (ret < 0) {
489103d84a3SLiam Girdwood 			dev_err(cpu_dai->dev, "ASoC: can't open interface"
490103d84a3SLiam Girdwood 				" %s: %d\n", cpu_dai->name, ret);
491ddee627cSLiam Girdwood 			goto out;
492ddee627cSLiam Girdwood 		}
493ddee627cSLiam Girdwood 	}
494ddee627cSLiam Girdwood 
495b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->open) {
496ddee627cSLiam Girdwood 		ret = platform->driver->ops->open(substream);
497ddee627cSLiam Girdwood 		if (ret < 0) {
498103d84a3SLiam Girdwood 			dev_err(platform->dev, "ASoC: can't open platform"
499f4333203SLars-Peter Clausen 				" %s: %d\n", platform->component.name, ret);
500ddee627cSLiam Girdwood 			goto platform_err;
501ddee627cSLiam Girdwood 		}
502ddee627cSLiam Girdwood 	}
503ddee627cSLiam Girdwood 
504b8135864SKuninori Morimoto 	ret = 0;
505b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
506b8135864SKuninori Morimoto 		component = rtdcom->component;
507b8135864SKuninori Morimoto 
508b8135864SKuninori Morimoto 		/* ignore duplication for now */
509b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
510b8135864SKuninori Morimoto 			continue;
511b8135864SKuninori Morimoto 
512b8135864SKuninori Morimoto 		if (!component->driver->ops ||
513b8135864SKuninori Morimoto 		    !component->driver->ops->open)
514b8135864SKuninori Morimoto 			continue;
515b8135864SKuninori Morimoto 
516b8135864SKuninori Morimoto 		__ret = component->driver->ops->open(substream);
517b8135864SKuninori Morimoto 		if (__ret < 0) {
518b8135864SKuninori Morimoto 			dev_err(component->dev,
519b8135864SKuninori Morimoto 				"ASoC: can't open component %s: %d\n",
520b8135864SKuninori Morimoto 				component->name, ret);
521b8135864SKuninori Morimoto 			ret = __ret;
522b8135864SKuninori Morimoto 		}
523b8135864SKuninori Morimoto 	}
524b8135864SKuninori Morimoto 	if (ret < 0)
525b8135864SKuninori Morimoto 		goto component_err;
526b8135864SKuninori Morimoto 
5272e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
5282e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
5299900a422SKuninori Morimoto 		if (codec_dai->driver->ops->startup) {
5302e5894d7SBenoit Cousson 			ret = codec_dai->driver->ops->startup(substream,
5312e5894d7SBenoit Cousson 							      codec_dai);
532ddee627cSLiam Girdwood 			if (ret < 0) {
5332e5894d7SBenoit Cousson 				dev_err(codec_dai->dev,
5342e5894d7SBenoit Cousson 					"ASoC: can't open codec %s: %d\n",
5352e5894d7SBenoit Cousson 					codec_dai->name, ret);
536ddee627cSLiam Girdwood 				goto codec_dai_err;
537ddee627cSLiam Girdwood 			}
538ddee627cSLiam Girdwood 		}
539ddee627cSLiam Girdwood 
5402e5894d7SBenoit Cousson 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5412e5894d7SBenoit Cousson 			codec_dai->tx_mask = 0;
5422e5894d7SBenoit Cousson 		else
5432e5894d7SBenoit Cousson 			codec_dai->rx_mask = 0;
5442e5894d7SBenoit Cousson 	}
5452e5894d7SBenoit Cousson 
54675ab9eb6SKuninori Morimoto 	if (rtd->dai_link->ops->startup) {
547ddee627cSLiam Girdwood 		ret = rtd->dai_link->ops->startup(substream);
548ddee627cSLiam Girdwood 		if (ret < 0) {
549103d84a3SLiam Girdwood 			pr_err("ASoC: %s startup failed: %d\n",
55025bfe662SMark Brown 			       rtd->dai_link->name, ret);
551ddee627cSLiam Girdwood 			goto machine_err;
552ddee627cSLiam Girdwood 		}
553ddee627cSLiam Girdwood 	}
554ddee627cSLiam Girdwood 
55501d7584cSLiam Girdwood 	/* Dynamic PCM DAI links compat checks use dynamic capabilities */
55601d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
55701d7584cSLiam Girdwood 		goto dynamic;
55801d7584cSLiam Girdwood 
559ddee627cSLiam Girdwood 	/* Check that the codec and cpu DAIs are compatible */
5602e5894d7SBenoit Cousson 	soc_pcm_init_runtime_hw(substream);
5612e5894d7SBenoit Cousson 
5622e5894d7SBenoit Cousson 	if (rtd->num_codecs == 1)
5632e5894d7SBenoit Cousson 		codec_dai_name = rtd->codec_dai->name;
564ddee627cSLiam Girdwood 
56562e5f676SLars-Peter Clausen 	if (soc_pcm_has_symmetry(substream))
56662e5f676SLars-Peter Clausen 		runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
56762e5f676SLars-Peter Clausen 
568ddee627cSLiam Girdwood 	ret = -EINVAL;
569ddee627cSLiam Girdwood 	if (!runtime->hw.rates) {
570103d84a3SLiam Girdwood 		printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
5712e5894d7SBenoit Cousson 			codec_dai_name, cpu_dai->name);
572ddee627cSLiam Girdwood 		goto config_err;
573ddee627cSLiam Girdwood 	}
574ddee627cSLiam Girdwood 	if (!runtime->hw.formats) {
575103d84a3SLiam Girdwood 		printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n",
5762e5894d7SBenoit Cousson 			codec_dai_name, cpu_dai->name);
577ddee627cSLiam Girdwood 		goto config_err;
578ddee627cSLiam Girdwood 	}
579ddee627cSLiam Girdwood 	if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
580ddee627cSLiam Girdwood 	    runtime->hw.channels_min > runtime->hw.channels_max) {
581103d84a3SLiam Girdwood 		printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n",
5822e5894d7SBenoit Cousson 				codec_dai_name, cpu_dai->name);
583ddee627cSLiam Girdwood 		goto config_err;
584ddee627cSLiam Girdwood 	}
585ddee627cSLiam Girdwood 
586c8dd1fecSBenoit Cousson 	soc_pcm_apply_msb(substream);
58758ba9b25SMark Brown 
588ddee627cSLiam Girdwood 	/* Symmetry only applies if we've already got an active stream. */
58917841020SDong Aisheng 	if (cpu_dai->active) {
59017841020SDong Aisheng 		ret = soc_pcm_apply_symmetry(substream, cpu_dai);
59117841020SDong Aisheng 		if (ret != 0)
59217841020SDong Aisheng 			goto config_err;
59317841020SDong Aisheng 	}
59417841020SDong Aisheng 
5952e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
5962e5894d7SBenoit Cousson 		if (rtd->codec_dais[i]->active) {
5972e5894d7SBenoit Cousson 			ret = soc_pcm_apply_symmetry(substream,
5982e5894d7SBenoit Cousson 						     rtd->codec_dais[i]);
599ddee627cSLiam Girdwood 			if (ret != 0)
600ddee627cSLiam Girdwood 				goto config_err;
601ddee627cSLiam Girdwood 		}
6022e5894d7SBenoit Cousson 	}
603ddee627cSLiam Girdwood 
604103d84a3SLiam Girdwood 	pr_debug("ASoC: %s <-> %s info:\n",
6052e5894d7SBenoit Cousson 			codec_dai_name, cpu_dai->name);
606103d84a3SLiam Girdwood 	pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates);
607103d84a3SLiam Girdwood 	pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min,
608ddee627cSLiam Girdwood 		 runtime->hw.channels_max);
609103d84a3SLiam Girdwood 	pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min,
610ddee627cSLiam Girdwood 		 runtime->hw.rate_max);
611ddee627cSLiam Girdwood 
61201d7584cSLiam Girdwood dynamic:
61324894b76SLars-Peter Clausen 
61424894b76SLars-Peter Clausen 	snd_soc_runtime_activate(rtd, substream->stream);
61524894b76SLars-Peter Clausen 
616b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
617ddee627cSLiam Girdwood 	return 0;
618ddee627cSLiam Girdwood 
619ddee627cSLiam Girdwood config_err:
62075ab9eb6SKuninori Morimoto 	if (rtd->dai_link->ops->shutdown)
621ddee627cSLiam Girdwood 		rtd->dai_link->ops->shutdown(substream);
622ddee627cSLiam Girdwood 
623ddee627cSLiam Girdwood machine_err:
6242e5894d7SBenoit Cousson 	i = rtd->num_codecs;
625ddee627cSLiam Girdwood 
626ddee627cSLiam Girdwood codec_dai_err:
6272e5894d7SBenoit Cousson 	while (--i >= 0) {
6282e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
6292e5894d7SBenoit Cousson 		if (codec_dai->driver->ops->shutdown)
6302e5894d7SBenoit Cousson 			codec_dai->driver->ops->shutdown(substream, codec_dai);
6312e5894d7SBenoit Cousson 	}
6322e5894d7SBenoit Cousson 
633b8135864SKuninori Morimoto component_err:
634b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
635b8135864SKuninori Morimoto 		component = rtdcom->component;
636b8135864SKuninori Morimoto 
637b8135864SKuninori Morimoto 		/* ignore duplication for now */
638b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
639b8135864SKuninori Morimoto 			continue;
640b8135864SKuninori Morimoto 
641b8135864SKuninori Morimoto 		if (!component->driver->ops ||
642b8135864SKuninori Morimoto 		    !component->driver->ops->close)
643b8135864SKuninori Morimoto 			continue;
644b8135864SKuninori Morimoto 
645b8135864SKuninori Morimoto 		component->driver->ops->close(substream);
646b8135864SKuninori Morimoto 	}
647b8135864SKuninori Morimoto 
648b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->close)
649ddee627cSLiam Girdwood 		platform->driver->ops->close(substream);
650ddee627cSLiam Girdwood 
651ddee627cSLiam Girdwood platform_err:
652ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->shutdown)
653ddee627cSLiam Girdwood 		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
654ddee627cSLiam Girdwood out:
655b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
656d6652ef8SMark Brown 
65790be711eSKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
65890be711eSKuninori Morimoto 		component = rtdcom->component;
65990be711eSKuninori Morimoto 
66090be711eSKuninori Morimoto 		pm_runtime_mark_last_busy(component->dev);
66190be711eSKuninori Morimoto 		pm_runtime_put_autosuspend(component->dev);
6623f809783SSanyog Kale 	}
6633f809783SSanyog Kale 
6642e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
6652e5894d7SBenoit Cousson 		if (!rtd->codec_dais[i]->active)
6662e5894d7SBenoit Cousson 			pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
6672e5894d7SBenoit Cousson 	}
668988e8cc4SNicolin Chen 	if (!cpu_dai->active)
669988e8cc4SNicolin Chen 		pinctrl_pm_select_sleep_state(cpu_dai->dev);
670d6652ef8SMark Brown 
671ddee627cSLiam Girdwood 	return ret;
672ddee627cSLiam Girdwood }
673ddee627cSLiam Girdwood 
674ddee627cSLiam Girdwood /*
675ddee627cSLiam Girdwood  * Power down the audio subsystem pmdown_time msecs after close is called.
676ddee627cSLiam Girdwood  * This is to ensure there are no pops or clicks in between any music tracks
677ddee627cSLiam Girdwood  * due to DAPM power cycling.
678ddee627cSLiam Girdwood  */
679ddee627cSLiam Girdwood static void close_delayed_work(struct work_struct *work)
680ddee627cSLiam Girdwood {
681ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd =
682ddee627cSLiam Girdwood 			container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
6832e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai = rtd->codec_dais[0];
684ddee627cSLiam Girdwood 
685b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
686ddee627cSLiam Girdwood 
687103d84a3SLiam Girdwood 	dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n",
688ddee627cSLiam Girdwood 		 codec_dai->driver->playback.stream_name,
689ddee627cSLiam Girdwood 		 codec_dai->playback_active ? "active" : "inactive",
6909bffb1fbSMisael Lopez Cruz 		 rtd->pop_wait ? "yes" : "no");
691ddee627cSLiam Girdwood 
692ddee627cSLiam Girdwood 	/* are we waiting on this codec DAI stream */
6939bffb1fbSMisael Lopez Cruz 	if (rtd->pop_wait == 1) {
6949bffb1fbSMisael Lopez Cruz 		rtd->pop_wait = 0;
6957bd3a6f3SMark Brown 		snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
696d9b0951bSLiam Girdwood 					  SND_SOC_DAPM_STREAM_STOP);
697ddee627cSLiam Girdwood 	}
698ddee627cSLiam Girdwood 
699b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
700ddee627cSLiam Girdwood }
701ddee627cSLiam Girdwood 
702ddee627cSLiam Girdwood /*
703ddee627cSLiam Girdwood  * Called by ALSA when a PCM substream is closed. Private data can be
704ddee627cSLiam Girdwood  * freed here. The cpu DAI, codec DAI, machine and platform are also
705ddee627cSLiam Girdwood  * shutdown.
706ddee627cSLiam Girdwood  */
70791d5e6b4SLiam Girdwood static int soc_pcm_close(struct snd_pcm_substream *substream)
708ddee627cSLiam Girdwood {
709ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
710ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
71190be711eSKuninori Morimoto 	struct snd_soc_component *component;
71290be711eSKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
713ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
7142e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
7152e5894d7SBenoit Cousson 	int i;
716ddee627cSLiam Girdwood 
717b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
718ddee627cSLiam Girdwood 
71924894b76SLars-Peter Clausen 	snd_soc_runtime_deactivate(rtd, substream->stream);
720ddee627cSLiam Girdwood 
72117841020SDong Aisheng 	/* clear the corresponding DAIs rate when inactive */
72217841020SDong Aisheng 	if (!cpu_dai->active)
72317841020SDong Aisheng 		cpu_dai->rate = 0;
72417841020SDong Aisheng 
7252e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
7262e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
72717841020SDong Aisheng 		if (!codec_dai->active)
72817841020SDong Aisheng 			codec_dai->rate = 0;
7292e5894d7SBenoit Cousson 	}
73025b76791SSascha Hauer 
731ae11601bSRamesh Babu 	snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
732ae11601bSRamesh Babu 
733ddee627cSLiam Girdwood 	if (cpu_dai->driver->ops->shutdown)
734ddee627cSLiam Girdwood 		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
735ddee627cSLiam Girdwood 
7362e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
7372e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
738ddee627cSLiam Girdwood 		if (codec_dai->driver->ops->shutdown)
739ddee627cSLiam Girdwood 			codec_dai->driver->ops->shutdown(substream, codec_dai);
7402e5894d7SBenoit Cousson 	}
741ddee627cSLiam Girdwood 
74275ab9eb6SKuninori Morimoto 	if (rtd->dai_link->ops->shutdown)
743ddee627cSLiam Girdwood 		rtd->dai_link->ops->shutdown(substream);
744ddee627cSLiam Girdwood 
745b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->close)
746ddee627cSLiam Girdwood 		platform->driver->ops->close(substream);
747ddee627cSLiam Girdwood 
748b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
749b8135864SKuninori Morimoto 		component = rtdcom->component;
750b8135864SKuninori Morimoto 
751b8135864SKuninori Morimoto 		/* ignore duplication for now */
752b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
753b8135864SKuninori Morimoto 			continue;
754b8135864SKuninori Morimoto 
755b8135864SKuninori Morimoto 		if (!component->driver->ops ||
756b8135864SKuninori Morimoto 		    !component->driver->ops->close)
757b8135864SKuninori Morimoto 			continue;
758b8135864SKuninori Morimoto 
759b8135864SKuninori Morimoto 		component->driver->ops->close(substream);
760b8135864SKuninori Morimoto 	}
761b8135864SKuninori Morimoto 
762ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
763208a1589SLars-Peter Clausen 		if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
7641d69c5c5SPeter Ujfalusi 			/* powered down playback stream now */
7651d69c5c5SPeter Ujfalusi 			snd_soc_dapm_stream_event(rtd,
7667bd3a6f3SMark Brown 						  SNDRV_PCM_STREAM_PLAYBACK,
7671d69c5c5SPeter Ujfalusi 						  SND_SOC_DAPM_STREAM_STOP);
7681d69c5c5SPeter Ujfalusi 		} else {
769ddee627cSLiam Girdwood 			/* start delayed pop wq here for playback streams */
7709bffb1fbSMisael Lopez Cruz 			rtd->pop_wait = 1;
771d4e1a73aSMark Brown 			queue_delayed_work(system_power_efficient_wq,
772d4e1a73aSMark Brown 					   &rtd->delayed_work,
773ddee627cSLiam Girdwood 					   msecs_to_jiffies(rtd->pmdown_time));
7741d69c5c5SPeter Ujfalusi 		}
775ddee627cSLiam Girdwood 	} else {
776ddee627cSLiam Girdwood 		/* capture streams can be powered down now */
7777bd3a6f3SMark Brown 		snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
778d9b0951bSLiam Girdwood 					  SND_SOC_DAPM_STREAM_STOP);
779ddee627cSLiam Girdwood 	}
780ddee627cSLiam Girdwood 
781b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
782d6652ef8SMark Brown 
78390be711eSKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
78490be711eSKuninori Morimoto 		component = rtdcom->component;
7853f809783SSanyog Kale 
78690be711eSKuninori Morimoto 		pm_runtime_mark_last_busy(component->dev);
78790be711eSKuninori Morimoto 		pm_runtime_put_autosuspend(component->dev);
7883f809783SSanyog Kale 	}
7893f809783SSanyog Kale 
7902e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
7912e5894d7SBenoit Cousson 		if (!rtd->codec_dais[i]->active)
7922e5894d7SBenoit Cousson 			pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
7932e5894d7SBenoit Cousson 	}
794988e8cc4SNicolin Chen 	if (!cpu_dai->active)
795988e8cc4SNicolin Chen 		pinctrl_pm_select_sleep_state(cpu_dai->dev);
796d6652ef8SMark Brown 
797ddee627cSLiam Girdwood 	return 0;
798ddee627cSLiam Girdwood }
799ddee627cSLiam Girdwood 
800ddee627cSLiam Girdwood /*
801ddee627cSLiam Girdwood  * Called by ALSA when the PCM substream is prepared, can set format, sample
802ddee627cSLiam Girdwood  * rate, etc.  This function is non atomic and can be called multiple times,
803ddee627cSLiam Girdwood  * it can refer to the runtime info.
804ddee627cSLiam Girdwood  */
805ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream)
806ddee627cSLiam Girdwood {
807ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
808ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
809b8135864SKuninori Morimoto 	struct snd_soc_component *component;
810b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
811ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
8122e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
8132e5894d7SBenoit Cousson 	int i, ret = 0;
814ddee627cSLiam Girdwood 
815b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
816ddee627cSLiam Girdwood 
81775ab9eb6SKuninori Morimoto 	if (rtd->dai_link->ops->prepare) {
818ddee627cSLiam Girdwood 		ret = rtd->dai_link->ops->prepare(substream);
819ddee627cSLiam Girdwood 		if (ret < 0) {
820103d84a3SLiam Girdwood 			dev_err(rtd->card->dev, "ASoC: machine prepare error:"
821103d84a3SLiam Girdwood 				" %d\n", ret);
822ddee627cSLiam Girdwood 			goto out;
823ddee627cSLiam Girdwood 		}
824ddee627cSLiam Girdwood 	}
825ddee627cSLiam Girdwood 
826b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->prepare) {
827ddee627cSLiam Girdwood 		ret = platform->driver->ops->prepare(substream);
828ddee627cSLiam Girdwood 		if (ret < 0) {
829103d84a3SLiam Girdwood 			dev_err(platform->dev, "ASoC: platform prepare error:"
830103d84a3SLiam Girdwood 				" %d\n", ret);
831ddee627cSLiam Girdwood 			goto out;
832ddee627cSLiam Girdwood 		}
833ddee627cSLiam Girdwood 	}
834ddee627cSLiam Girdwood 
835b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
836b8135864SKuninori Morimoto 		component = rtdcom->component;
837b8135864SKuninori Morimoto 
838b8135864SKuninori Morimoto 		/* ignore duplication for now */
839b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
840b8135864SKuninori Morimoto 			continue;
841b8135864SKuninori Morimoto 
842b8135864SKuninori Morimoto 		if (!component->driver->ops ||
843b8135864SKuninori Morimoto 		    !component->driver->ops->prepare)
844b8135864SKuninori Morimoto 			continue;
845b8135864SKuninori Morimoto 
846b8135864SKuninori Morimoto 		ret = component->driver->ops->prepare(substream);
847b8135864SKuninori Morimoto 		if (ret < 0) {
848b8135864SKuninori Morimoto 			dev_err(component->dev,
849b8135864SKuninori Morimoto 				"ASoC: platform prepare error: %d\n", ret);
850b8135864SKuninori Morimoto 			goto out;
851b8135864SKuninori Morimoto 		}
852b8135864SKuninori Morimoto 	}
853b8135864SKuninori Morimoto 
8542e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
8552e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
8569900a422SKuninori Morimoto 		if (codec_dai->driver->ops->prepare) {
8572e5894d7SBenoit Cousson 			ret = codec_dai->driver->ops->prepare(substream,
8582e5894d7SBenoit Cousson 							      codec_dai);
859ddee627cSLiam Girdwood 			if (ret < 0) {
8602e5894d7SBenoit Cousson 				dev_err(codec_dai->dev,
86190cc7f1cSJarkko Nikula 					"ASoC: codec DAI prepare error: %d\n",
86290cc7f1cSJarkko Nikula 					ret);
863ddee627cSLiam Girdwood 				goto out;
864ddee627cSLiam Girdwood 			}
865ddee627cSLiam Girdwood 		}
8662e5894d7SBenoit Cousson 	}
867ddee627cSLiam Girdwood 
8689900a422SKuninori Morimoto 	if (cpu_dai->driver->ops->prepare) {
869ddee627cSLiam Girdwood 		ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
870ddee627cSLiam Girdwood 		if (ret < 0) {
87190cc7f1cSJarkko Nikula 			dev_err(cpu_dai->dev,
87290cc7f1cSJarkko Nikula 				"ASoC: cpu DAI prepare error: %d\n", ret);
873ddee627cSLiam Girdwood 			goto out;
874ddee627cSLiam Girdwood 		}
875ddee627cSLiam Girdwood 	}
876ddee627cSLiam Girdwood 
877ddee627cSLiam Girdwood 	/* cancel any delayed stream shutdown that is pending */
878ddee627cSLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
8799bffb1fbSMisael Lopez Cruz 	    rtd->pop_wait) {
8809bffb1fbSMisael Lopez Cruz 		rtd->pop_wait = 0;
881ddee627cSLiam Girdwood 		cancel_delayed_work(&rtd->delayed_work);
882ddee627cSLiam Girdwood 	}
883ddee627cSLiam Girdwood 
884d9b0951bSLiam Girdwood 	snd_soc_dapm_stream_event(rtd, substream->stream,
885ddee627cSLiam Girdwood 			SND_SOC_DAPM_STREAM_START);
886ddee627cSLiam Girdwood 
8872e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++)
8882e5894d7SBenoit Cousson 		snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
8892e5894d7SBenoit Cousson 					 substream->stream);
890ae11601bSRamesh Babu 	snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
891ddee627cSLiam Girdwood 
892ddee627cSLiam Girdwood out:
893b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
894ddee627cSLiam Girdwood 	return ret;
895ddee627cSLiam Girdwood }
896ddee627cSLiam Girdwood 
8972e5894d7SBenoit Cousson static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
8982e5894d7SBenoit Cousson 				       unsigned int mask)
8992e5894d7SBenoit Cousson {
9002e5894d7SBenoit Cousson 	struct snd_interval *interval;
9012e5894d7SBenoit Cousson 	int channels = hweight_long(mask);
9022e5894d7SBenoit Cousson 
9032e5894d7SBenoit Cousson 	interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
9042e5894d7SBenoit Cousson 	interval->min = channels;
9052e5894d7SBenoit Cousson 	interval->max = channels;
9062e5894d7SBenoit Cousson }
9072e5894d7SBenoit Cousson 
90893e6958aSBenoit Cousson int soc_dai_hw_params(struct snd_pcm_substream *substream,
90993e6958aSBenoit Cousson 		      struct snd_pcm_hw_params *params,
91093e6958aSBenoit Cousson 		      struct snd_soc_dai *dai)
91193e6958aSBenoit Cousson {
91293e6958aSBenoit Cousson 	int ret;
91393e6958aSBenoit Cousson 
9149900a422SKuninori Morimoto 	if (dai->driver->ops->hw_params) {
91593e6958aSBenoit Cousson 		ret = dai->driver->ops->hw_params(substream, params, dai);
91693e6958aSBenoit Cousson 		if (ret < 0) {
91793e6958aSBenoit Cousson 			dev_err(dai->dev, "ASoC: can't set %s hw params: %d\n",
91893e6958aSBenoit Cousson 				dai->name, ret);
91993e6958aSBenoit Cousson 			return ret;
92093e6958aSBenoit Cousson 		}
92193e6958aSBenoit Cousson 	}
92293e6958aSBenoit Cousson 
92393e6958aSBenoit Cousson 	return 0;
92493e6958aSBenoit Cousson }
92593e6958aSBenoit Cousson 
926ddee627cSLiam Girdwood /*
927ddee627cSLiam Girdwood  * Called by ALSA when the hardware params are set by application. This
928ddee627cSLiam Girdwood  * function can also be called multiple times and can allocate buffers
929ddee627cSLiam Girdwood  * (using snd_pcm_lib_* ). It's non-atomic.
930ddee627cSLiam Girdwood  */
931ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
932ddee627cSLiam Girdwood 				struct snd_pcm_hw_params *params)
933ddee627cSLiam Girdwood {
934ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
935ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
936b8135864SKuninori Morimoto 	struct snd_soc_component *component;
937b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
938ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
939b8135864SKuninori Morimoto 	int i, ret = 0, __ret;
940ddee627cSLiam Girdwood 
941b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
94275ab9eb6SKuninori Morimoto 	if (rtd->dai_link->ops->hw_params) {
943ddee627cSLiam Girdwood 		ret = rtd->dai_link->ops->hw_params(substream, params);
944ddee627cSLiam Girdwood 		if (ret < 0) {
945103d84a3SLiam Girdwood 			dev_err(rtd->card->dev, "ASoC: machine hw_params"
946103d84a3SLiam Girdwood 				" failed: %d\n", ret);
947ddee627cSLiam Girdwood 			goto out;
948ddee627cSLiam Girdwood 		}
949ddee627cSLiam Girdwood 	}
950ddee627cSLiam Girdwood 
9512e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
9522e5894d7SBenoit Cousson 		struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
9532e5894d7SBenoit Cousson 		struct snd_pcm_hw_params codec_params;
9542e5894d7SBenoit Cousson 
955cde79035SRicard Wanderlof 		/*
956cde79035SRicard Wanderlof 		 * Skip CODECs which don't support the current stream type,
957cde79035SRicard Wanderlof 		 * the idea being that if a CODEC is not used for the currently
958cde79035SRicard Wanderlof 		 * set up transfer direction, it should not need to be
959cde79035SRicard Wanderlof 		 * configured, especially since the configuration used might
960cde79035SRicard Wanderlof 		 * not even be supported by that CODEC. There may be cases
961cde79035SRicard Wanderlof 		 * however where a CODEC needs to be set up although it is
962cde79035SRicard Wanderlof 		 * actually not being used for the transfer, e.g. if a
963cde79035SRicard Wanderlof 		 * capture-only CODEC is acting as an LRCLK and/or BCLK master
964cde79035SRicard Wanderlof 		 * for the DAI link including a playback-only CODEC.
965cde79035SRicard Wanderlof 		 * If this becomes necessary, we will have to augment the
966cde79035SRicard Wanderlof 		 * machine driver setup with information on how to act, so
967cde79035SRicard Wanderlof 		 * we can do the right thing here.
968cde79035SRicard Wanderlof 		 */
969cde79035SRicard Wanderlof 		if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
970cde79035SRicard Wanderlof 			continue;
971cde79035SRicard Wanderlof 
9722e5894d7SBenoit Cousson 		/* copy params for each codec */
9732e5894d7SBenoit Cousson 		codec_params = *params;
9742e5894d7SBenoit Cousson 
9752e5894d7SBenoit Cousson 		/* fixup params based on TDM slot masks */
9762e5894d7SBenoit Cousson 		if (codec_dai->tx_mask)
9772e5894d7SBenoit Cousson 			soc_pcm_codec_params_fixup(&codec_params,
9782e5894d7SBenoit Cousson 						   codec_dai->tx_mask);
9792e5894d7SBenoit Cousson 		if (codec_dai->rx_mask)
9802e5894d7SBenoit Cousson 			soc_pcm_codec_params_fixup(&codec_params,
9812e5894d7SBenoit Cousson 						   codec_dai->rx_mask);
9822e5894d7SBenoit Cousson 
98393e6958aSBenoit Cousson 		ret = soc_dai_hw_params(substream, &codec_params, codec_dai);
98493e6958aSBenoit Cousson 		if(ret < 0)
985ddee627cSLiam Girdwood 			goto codec_err;
986ddee627cSLiam Girdwood 
9872e5894d7SBenoit Cousson 		codec_dai->rate = params_rate(&codec_params);
9882e5894d7SBenoit Cousson 		codec_dai->channels = params_channels(&codec_params);
9892e5894d7SBenoit Cousson 		codec_dai->sample_bits = snd_pcm_format_physical_width(
9902e5894d7SBenoit Cousson 						params_format(&codec_params));
991ddee627cSLiam Girdwood 	}
992ddee627cSLiam Girdwood 
99393e6958aSBenoit Cousson 	ret = soc_dai_hw_params(substream, params, cpu_dai);
99493e6958aSBenoit Cousson 	if (ret < 0)
995ddee627cSLiam Girdwood 		goto interface_err;
996ddee627cSLiam Girdwood 
997b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->hw_params) {
998ddee627cSLiam Girdwood 		ret = platform->driver->ops->hw_params(substream, params);
999ddee627cSLiam Girdwood 		if (ret < 0) {
1000103d84a3SLiam Girdwood 			dev_err(platform->dev, "ASoC: %s hw params failed: %d\n",
1001f4333203SLars-Peter Clausen 			       platform->component.name, ret);
1002ddee627cSLiam Girdwood 			goto platform_err;
1003ddee627cSLiam Girdwood 		}
1004ddee627cSLiam Girdwood 	}
1005ddee627cSLiam Girdwood 
1006b8135864SKuninori Morimoto 	ret = 0;
1007b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
1008b8135864SKuninori Morimoto 		component = rtdcom->component;
1009b8135864SKuninori Morimoto 
1010b8135864SKuninori Morimoto 		/* ignore duplication for now */
1011b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
1012b8135864SKuninori Morimoto 			continue;
1013b8135864SKuninori Morimoto 
1014b8135864SKuninori Morimoto 		if (!component->driver->ops ||
1015b8135864SKuninori Morimoto 		    !component->driver->ops->hw_params)
1016b8135864SKuninori Morimoto 			continue;
1017b8135864SKuninori Morimoto 
1018b8135864SKuninori Morimoto 		__ret = component->driver->ops->hw_params(substream, params);
1019b8135864SKuninori Morimoto 		if (__ret < 0) {
1020b8135864SKuninori Morimoto 			dev_err(component->dev,
1021b8135864SKuninori Morimoto 				"ASoC: %s hw params failed: %d\n",
1022b8135864SKuninori Morimoto 				component->name, ret);
1023b8135864SKuninori Morimoto 			ret = __ret;
1024b8135864SKuninori Morimoto 		}
1025b8135864SKuninori Morimoto 	}
1026b8135864SKuninori Morimoto 	if (ret < 0)
1027b8135864SKuninori Morimoto 		goto component_err;
1028b8135864SKuninori Morimoto 
10293635bf09SNicolin Chen 	/* store the parameters for each DAIs */
103017841020SDong Aisheng 	cpu_dai->rate = params_rate(params);
10313635bf09SNicolin Chen 	cpu_dai->channels = params_channels(params);
10323635bf09SNicolin Chen 	cpu_dai->sample_bits =
10333635bf09SNicolin Chen 		snd_pcm_format_physical_width(params_format(params));
10343635bf09SNicolin Chen 
1035957ce0c6Sjiada wang 	ret = soc_pcm_params_symmetry(substream, params);
1036957ce0c6Sjiada wang         if (ret)
1037b8135864SKuninori Morimoto 		goto component_err;
1038ddee627cSLiam Girdwood out:
1039b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
1040ddee627cSLiam Girdwood 	return ret;
1041ddee627cSLiam Girdwood 
1042b8135864SKuninori Morimoto component_err:
1043b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
1044b8135864SKuninori Morimoto 		component = rtdcom->component;
1045b8135864SKuninori Morimoto 
1046b8135864SKuninori Morimoto 		/* ignore duplication */
1047b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
1048b8135864SKuninori Morimoto 			continue;
1049b8135864SKuninori Morimoto 
1050b8135864SKuninori Morimoto 		if (!component->driver->ops ||
1051b8135864SKuninori Morimoto 		    !component->driver->ops->hw_free)
1052b8135864SKuninori Morimoto 			continue;
1053b8135864SKuninori Morimoto 
1054b8135864SKuninori Morimoto 		component->driver->ops->hw_free(substream);
1055b8135864SKuninori Morimoto 	}
1056b8135864SKuninori Morimoto 
1057b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->hw_free)
1058b8135864SKuninori Morimoto 		platform->driver->ops->hw_free(substream);
1059b8135864SKuninori Morimoto 
1060ddee627cSLiam Girdwood platform_err:
10619900a422SKuninori Morimoto 	if (cpu_dai->driver->ops->hw_free)
1062ddee627cSLiam Girdwood 		cpu_dai->driver->ops->hw_free(substream, cpu_dai);
1063ddee627cSLiam Girdwood 
1064ddee627cSLiam Girdwood interface_err:
10652e5894d7SBenoit Cousson 	i = rtd->num_codecs;
1066ddee627cSLiam Girdwood 
1067ddee627cSLiam Girdwood codec_err:
10682e5894d7SBenoit Cousson 	while (--i >= 0) {
10692e5894d7SBenoit Cousson 		struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
10709900a422SKuninori Morimoto 		if (codec_dai->driver->ops->hw_free)
10712e5894d7SBenoit Cousson 			codec_dai->driver->ops->hw_free(substream, codec_dai);
10722e5894d7SBenoit Cousson 		codec_dai->rate = 0;
10732e5894d7SBenoit Cousson 	}
10742e5894d7SBenoit Cousson 
107575ab9eb6SKuninori Morimoto 	if (rtd->dai_link->ops->hw_free)
1076ddee627cSLiam Girdwood 		rtd->dai_link->ops->hw_free(substream);
1077ddee627cSLiam Girdwood 
1078b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
1079ddee627cSLiam Girdwood 	return ret;
1080ddee627cSLiam Girdwood }
1081ddee627cSLiam Girdwood 
1082ddee627cSLiam Girdwood /*
1083ddee627cSLiam Girdwood  * Frees resources allocated by hw_params, can be called multiple times
1084ddee627cSLiam Girdwood  */
1085ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
1086ddee627cSLiam Girdwood {
1087ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1088ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
1089b8135864SKuninori Morimoto 	struct snd_soc_component *component;
1090b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
1091ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
10922e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
10937f62b6eeSNicolin Chen 	bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
10942e5894d7SBenoit Cousson 	int i;
1095ddee627cSLiam Girdwood 
1096b8c0dab9SLiam Girdwood 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
1097ddee627cSLiam Girdwood 
1098d3383420SNicolin Chen 	/* clear the corresponding DAIs parameters when going to be inactive */
1099d3383420SNicolin Chen 	if (cpu_dai->active == 1) {
1100d3383420SNicolin Chen 		cpu_dai->rate = 0;
1101d3383420SNicolin Chen 		cpu_dai->channels = 0;
1102d3383420SNicolin Chen 		cpu_dai->sample_bits = 0;
1103d3383420SNicolin Chen 	}
1104d3383420SNicolin Chen 
11052e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
11062e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
1107d3383420SNicolin Chen 		if (codec_dai->active == 1) {
1108d3383420SNicolin Chen 			codec_dai->rate = 0;
1109d3383420SNicolin Chen 			codec_dai->channels = 0;
1110d3383420SNicolin Chen 			codec_dai->sample_bits = 0;
1111d3383420SNicolin Chen 		}
11122e5894d7SBenoit Cousson 	}
1113d3383420SNicolin Chen 
1114ddee627cSLiam Girdwood 	/* apply codec digital mute */
11152e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
11162e5894d7SBenoit Cousson 		if ((playback && rtd->codec_dais[i]->playback_active == 1) ||
11172e5894d7SBenoit Cousson 		    (!playback && rtd->codec_dais[i]->capture_active == 1))
11182e5894d7SBenoit Cousson 			snd_soc_dai_digital_mute(rtd->codec_dais[i], 1,
11192e5894d7SBenoit Cousson 						 substream->stream);
11202e5894d7SBenoit Cousson 	}
1121ddee627cSLiam Girdwood 
1122ddee627cSLiam Girdwood 	/* free any machine hw params */
112375ab9eb6SKuninori Morimoto 	if (rtd->dai_link->ops->hw_free)
1124ddee627cSLiam Girdwood 		rtd->dai_link->ops->hw_free(substream);
1125ddee627cSLiam Girdwood 
1126ddee627cSLiam Girdwood 	/* free any DMA resources */
1127b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->hw_free)
1128ddee627cSLiam Girdwood 		platform->driver->ops->hw_free(substream);
1129ddee627cSLiam Girdwood 
1130b8135864SKuninori Morimoto 	/* free any component resources */
1131b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
1132b8135864SKuninori Morimoto 		component = rtdcom->component;
1133b8135864SKuninori Morimoto 
1134b8135864SKuninori Morimoto 		/* ignore duplication for now */
1135b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
1136b8135864SKuninori Morimoto 			continue;
1137b8135864SKuninori Morimoto 
1138b8135864SKuninori Morimoto 		if (!component->driver->ops ||
1139b8135864SKuninori Morimoto 		    !component->driver->ops->hw_free)
1140b8135864SKuninori Morimoto 			continue;
1141b8135864SKuninori Morimoto 
1142b8135864SKuninori Morimoto 		component->driver->ops->hw_free(substream);
1143b8135864SKuninori Morimoto 	}
1144b8135864SKuninori Morimoto 
1145ddee627cSLiam Girdwood 	/* now free hw params for the DAIs  */
11462e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
11472e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
11489900a422SKuninori Morimoto 		if (codec_dai->driver->ops->hw_free)
1149ddee627cSLiam Girdwood 			codec_dai->driver->ops->hw_free(substream, codec_dai);
11502e5894d7SBenoit Cousson 	}
1151ddee627cSLiam Girdwood 
11529900a422SKuninori Morimoto 	if (cpu_dai->driver->ops->hw_free)
1153ddee627cSLiam Girdwood 		cpu_dai->driver->ops->hw_free(substream, cpu_dai);
1154ddee627cSLiam Girdwood 
1155b8c0dab9SLiam Girdwood 	mutex_unlock(&rtd->pcm_mutex);
1156ddee627cSLiam Girdwood 	return 0;
1157ddee627cSLiam Girdwood }
1158ddee627cSLiam Girdwood 
1159ddee627cSLiam Girdwood static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
1160ddee627cSLiam Girdwood {
1161ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1162ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
1163b8135864SKuninori Morimoto 	struct snd_soc_component *component;
1164b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
1165ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
11662e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
11672e5894d7SBenoit Cousson 	int i, ret;
1168ddee627cSLiam Girdwood 
11692e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
11702e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
11719900a422SKuninori Morimoto 		if (codec_dai->driver->ops->trigger) {
11722e5894d7SBenoit Cousson 			ret = codec_dai->driver->ops->trigger(substream,
11732e5894d7SBenoit Cousson 							      cmd, codec_dai);
1174ddee627cSLiam Girdwood 			if (ret < 0)
1175ddee627cSLiam Girdwood 				return ret;
1176ddee627cSLiam Girdwood 		}
11772e5894d7SBenoit Cousson 	}
1178ddee627cSLiam Girdwood 
1179b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->trigger) {
1180ddee627cSLiam Girdwood 		ret = platform->driver->ops->trigger(substream, cmd);
1181ddee627cSLiam Girdwood 		if (ret < 0)
1182ddee627cSLiam Girdwood 			return ret;
1183ddee627cSLiam Girdwood 	}
1184ddee627cSLiam Girdwood 
1185b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
1186b8135864SKuninori Morimoto 		component = rtdcom->component;
1187b8135864SKuninori Morimoto 
1188b8135864SKuninori Morimoto 		/* ignore duplication for now */
1189b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
1190b8135864SKuninori Morimoto 			continue;
1191b8135864SKuninori Morimoto 
1192b8135864SKuninori Morimoto 		if (!component->driver->ops ||
1193b8135864SKuninori Morimoto 		    !component->driver->ops->trigger)
1194b8135864SKuninori Morimoto 			continue;
1195b8135864SKuninori Morimoto 
1196b8135864SKuninori Morimoto 		ret = component->driver->ops->trigger(substream, cmd);
1197b8135864SKuninori Morimoto 		if (ret < 0)
1198b8135864SKuninori Morimoto 			return ret;
1199b8135864SKuninori Morimoto 	}
1200b8135864SKuninori Morimoto 
12019900a422SKuninori Morimoto 	if (cpu_dai->driver->ops->trigger) {
1202ddee627cSLiam Girdwood 		ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
1203ddee627cSLiam Girdwood 		if (ret < 0)
1204ddee627cSLiam Girdwood 			return ret;
1205ddee627cSLiam Girdwood 	}
12064792b0dbSJarkko Nikula 
120775ab9eb6SKuninori Morimoto 	if (rtd->dai_link->ops->trigger) {
12084792b0dbSJarkko Nikula 		ret = rtd->dai_link->ops->trigger(substream, cmd);
12094792b0dbSJarkko Nikula 		if (ret < 0)
12104792b0dbSJarkko Nikula 			return ret;
12114792b0dbSJarkko Nikula 	}
12124792b0dbSJarkko Nikula 
1213ddee627cSLiam Girdwood 	return 0;
1214ddee627cSLiam Girdwood }
1215ddee627cSLiam Girdwood 
121645c0a188SMark Brown static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
121745c0a188SMark Brown 				   int cmd)
121807bf84aaSLiam Girdwood {
121907bf84aaSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
122007bf84aaSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
12212e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
12222e5894d7SBenoit Cousson 	int i, ret;
122307bf84aaSLiam Girdwood 
12242e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
12252e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
12269900a422SKuninori Morimoto 		if (codec_dai->driver->ops->bespoke_trigger) {
12272e5894d7SBenoit Cousson 			ret = codec_dai->driver->ops->bespoke_trigger(substream,
12282e5894d7SBenoit Cousson 								cmd, codec_dai);
122907bf84aaSLiam Girdwood 			if (ret < 0)
123007bf84aaSLiam Girdwood 				return ret;
123107bf84aaSLiam Girdwood 		}
12322e5894d7SBenoit Cousson 	}
123307bf84aaSLiam Girdwood 
12349900a422SKuninori Morimoto 	if (cpu_dai->driver->ops->bespoke_trigger) {
123507bf84aaSLiam Girdwood 		ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai);
123607bf84aaSLiam Girdwood 		if (ret < 0)
123707bf84aaSLiam Girdwood 			return ret;
123807bf84aaSLiam Girdwood 	}
123907bf84aaSLiam Girdwood 	return 0;
124007bf84aaSLiam Girdwood }
1241ddee627cSLiam Girdwood /*
1242ddee627cSLiam Girdwood  * soc level wrapper for pointer callback
1243ddee627cSLiam Girdwood  * If cpu_dai, codec_dai, platform driver has the delay callback, than
1244ddee627cSLiam Girdwood  * the runtime->delay will be updated accordingly.
1245ddee627cSLiam Girdwood  */
1246ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
1247ddee627cSLiam Girdwood {
1248ddee627cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1249ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
1250b8135864SKuninori Morimoto 	struct snd_soc_component *component;
1251b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
1252ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
12532e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
1254ddee627cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
1255ddee627cSLiam Girdwood 	snd_pcm_uframes_t offset = 0;
1256ddee627cSLiam Girdwood 	snd_pcm_sframes_t delay = 0;
12572e5894d7SBenoit Cousson 	snd_pcm_sframes_t codec_delay = 0;
12582e5894d7SBenoit Cousson 	int i;
1259ddee627cSLiam Girdwood 
1260b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->pointer)
1261ddee627cSLiam Girdwood 		offset = platform->driver->ops->pointer(substream);
1262ddee627cSLiam Girdwood 
1263b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
1264b8135864SKuninori Morimoto 		component = rtdcom->component;
1265b8135864SKuninori Morimoto 
1266b8135864SKuninori Morimoto 		/* ignore duplication for now */
1267b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
1268b8135864SKuninori Morimoto 			continue;
1269b8135864SKuninori Morimoto 
1270b8135864SKuninori Morimoto 		if (!component->driver->ops ||
1271b8135864SKuninori Morimoto 		    !component->driver->ops->pointer)
1272b8135864SKuninori Morimoto 			continue;
1273b8135864SKuninori Morimoto 
1274b8135864SKuninori Morimoto 		/* FIXME: use 1st pointer */
1275b8135864SKuninori Morimoto 		offset = component->driver->ops->pointer(substream);
1276b8135864SKuninori Morimoto 		break;
1277b8135864SKuninori Morimoto 	}
1278b8135864SKuninori Morimoto 
12799900a422SKuninori Morimoto 	if (cpu_dai->driver->ops->delay)
1280ddee627cSLiam Girdwood 		delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
1281ddee627cSLiam Girdwood 
12822e5894d7SBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
12832e5894d7SBenoit Cousson 		codec_dai = rtd->codec_dais[i];
12849900a422SKuninori Morimoto 		if (codec_dai->driver->ops->delay)
12852e5894d7SBenoit Cousson 			codec_delay = max(codec_delay,
12862e5894d7SBenoit Cousson 					codec_dai->driver->ops->delay(substream,
12872e5894d7SBenoit Cousson 								    codec_dai));
12882e5894d7SBenoit Cousson 	}
12892e5894d7SBenoit Cousson 	delay += codec_delay;
1290ddee627cSLiam Girdwood 
1291ddee627cSLiam Girdwood 	runtime->delay = delay;
1292ddee627cSLiam Girdwood 
1293ddee627cSLiam Girdwood 	return offset;
1294ddee627cSLiam Girdwood }
1295ddee627cSLiam Girdwood 
129601d7584cSLiam Girdwood /* connect a FE and BE */
129701d7584cSLiam Girdwood static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
129801d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
129901d7584cSLiam Girdwood {
130001d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
130101d7584cSLiam Girdwood 
130201d7584cSLiam Girdwood 	/* only add new dpcms */
130301d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
130401d7584cSLiam Girdwood 		if (dpcm->be == be && dpcm->fe == fe)
130501d7584cSLiam Girdwood 			return 0;
130601d7584cSLiam Girdwood 	}
130701d7584cSLiam Girdwood 
130801d7584cSLiam Girdwood 	dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL);
130901d7584cSLiam Girdwood 	if (!dpcm)
131001d7584cSLiam Girdwood 		return -ENOMEM;
131101d7584cSLiam Girdwood 
131201d7584cSLiam Girdwood 	dpcm->be = be;
131301d7584cSLiam Girdwood 	dpcm->fe = fe;
131401d7584cSLiam Girdwood 	be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
131501d7584cSLiam Girdwood 	dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
131601d7584cSLiam Girdwood 	list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
131701d7584cSLiam Girdwood 	list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
131801d7584cSLiam Girdwood 
131901d7584cSLiam Girdwood 	dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n",
132001d7584cSLiam Girdwood 			stream ? "capture" : "playback",  fe->dai_link->name,
132101d7584cSLiam Girdwood 			stream ? "<-" : "->", be->dai_link->name);
132201d7584cSLiam Girdwood 
1323f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS
13246553bf06SLars-Peter Clausen 	if (fe->debugfs_dpcm_root)
1325f86dcef8SLiam Girdwood 		dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644,
1326f86dcef8SLiam Girdwood 				fe->debugfs_dpcm_root, &dpcm->state);
1327f86dcef8SLiam Girdwood #endif
132801d7584cSLiam Girdwood 	return 1;
132901d7584cSLiam Girdwood }
133001d7584cSLiam Girdwood 
133101d7584cSLiam Girdwood /* reparent a BE onto another FE */
133201d7584cSLiam Girdwood static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
133301d7584cSLiam Girdwood 			struct snd_soc_pcm_runtime *be, int stream)
133401d7584cSLiam Girdwood {
133501d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
133601d7584cSLiam Girdwood 	struct snd_pcm_substream *fe_substream, *be_substream;
133701d7584cSLiam Girdwood 
133801d7584cSLiam Girdwood 	/* reparent if BE is connected to other FEs */
133901d7584cSLiam Girdwood 	if (!be->dpcm[stream].users)
134001d7584cSLiam Girdwood 		return;
134101d7584cSLiam Girdwood 
134201d7584cSLiam Girdwood 	be_substream = snd_soc_dpcm_get_substream(be, stream);
134301d7584cSLiam Girdwood 
134401d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
134501d7584cSLiam Girdwood 		if (dpcm->fe == fe)
134601d7584cSLiam Girdwood 			continue;
134701d7584cSLiam Girdwood 
134801d7584cSLiam Girdwood 		dev_dbg(fe->dev, "reparent %s path %s %s %s\n",
134901d7584cSLiam Girdwood 			stream ? "capture" : "playback",
135001d7584cSLiam Girdwood 			dpcm->fe->dai_link->name,
135101d7584cSLiam Girdwood 			stream ? "<-" : "->", dpcm->be->dai_link->name);
135201d7584cSLiam Girdwood 
135301d7584cSLiam Girdwood 		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream);
135401d7584cSLiam Girdwood 		be_substream->runtime = fe_substream->runtime;
135501d7584cSLiam Girdwood 		break;
135601d7584cSLiam Girdwood 	}
135701d7584cSLiam Girdwood }
135801d7584cSLiam Girdwood 
135901d7584cSLiam Girdwood /* disconnect a BE and FE */
136023607025SLiam Girdwood void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
136101d7584cSLiam Girdwood {
136201d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm, *d;
136301d7584cSLiam Girdwood 
136401d7584cSLiam Girdwood 	list_for_each_entry_safe(dpcm, d, &fe->dpcm[stream].be_clients, list_be) {
1365103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n",
136601d7584cSLiam Girdwood 				stream ? "capture" : "playback",
136701d7584cSLiam Girdwood 				dpcm->be->dai_link->name);
136801d7584cSLiam Girdwood 
136901d7584cSLiam Girdwood 		if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE)
137001d7584cSLiam Girdwood 			continue;
137101d7584cSLiam Girdwood 
137201d7584cSLiam Girdwood 		dev_dbg(fe->dev, "freed DSP %s path %s %s %s\n",
137301d7584cSLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name,
137401d7584cSLiam Girdwood 			stream ? "<-" : "->", dpcm->be->dai_link->name);
137501d7584cSLiam Girdwood 
137601d7584cSLiam Girdwood 		/* BEs still alive need new FE */
137701d7584cSLiam Girdwood 		dpcm_be_reparent(fe, dpcm->be, stream);
137801d7584cSLiam Girdwood 
1379f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS
1380f86dcef8SLiam Girdwood 		debugfs_remove(dpcm->debugfs_state);
1381f86dcef8SLiam Girdwood #endif
138201d7584cSLiam Girdwood 		list_del(&dpcm->list_be);
138301d7584cSLiam Girdwood 		list_del(&dpcm->list_fe);
138401d7584cSLiam Girdwood 		kfree(dpcm);
138501d7584cSLiam Girdwood 	}
138601d7584cSLiam Girdwood }
138701d7584cSLiam Girdwood 
138801d7584cSLiam Girdwood /* get BE for DAI widget and stream */
138901d7584cSLiam Girdwood static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
139001d7584cSLiam Girdwood 		struct snd_soc_dapm_widget *widget, int stream)
139101d7584cSLiam Girdwood {
139201d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *be;
13931a497983SMengdong Lin 	int i;
139401d7584cSLiam Girdwood 
139501d7584cSLiam Girdwood 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
13961a497983SMengdong Lin 		list_for_each_entry(be, &card->rtd_list, list) {
139701d7584cSLiam Girdwood 
139835ea0655SLiam Girdwood 			if (!be->dai_link->no_pcm)
139935ea0655SLiam Girdwood 				continue;
140035ea0655SLiam Girdwood 
14012e5894d7SBenoit Cousson 			if (be->cpu_dai->playback_widget == widget)
140201d7584cSLiam Girdwood 				return be;
14032e5894d7SBenoit Cousson 
14041a497983SMengdong Lin 			for (i = 0; i < be->num_codecs; i++) {
14051a497983SMengdong Lin 				struct snd_soc_dai *dai = be->codec_dais[i];
14062e5894d7SBenoit Cousson 				if (dai->playback_widget == widget)
14072e5894d7SBenoit Cousson 					return be;
14082e5894d7SBenoit Cousson 			}
140901d7584cSLiam Girdwood 		}
141001d7584cSLiam Girdwood 	} else {
141101d7584cSLiam Girdwood 
14121a497983SMengdong Lin 		list_for_each_entry(be, &card->rtd_list, list) {
141301d7584cSLiam Girdwood 
141435ea0655SLiam Girdwood 			if (!be->dai_link->no_pcm)
141535ea0655SLiam Girdwood 				continue;
141635ea0655SLiam Girdwood 
14172e5894d7SBenoit Cousson 			if (be->cpu_dai->capture_widget == widget)
141801d7584cSLiam Girdwood 				return be;
14192e5894d7SBenoit Cousson 
14201a497983SMengdong Lin 			for (i = 0; i < be->num_codecs; i++) {
14211a497983SMengdong Lin 				struct snd_soc_dai *dai = be->codec_dais[i];
14222e5894d7SBenoit Cousson 				if (dai->capture_widget == widget)
14232e5894d7SBenoit Cousson 					return be;
14242e5894d7SBenoit Cousson 			}
142501d7584cSLiam Girdwood 		}
142601d7584cSLiam Girdwood 	}
142701d7584cSLiam Girdwood 
1428103d84a3SLiam Girdwood 	dev_err(card->dev, "ASoC: can't get %s BE for %s\n",
142901d7584cSLiam Girdwood 		stream ? "capture" : "playback", widget->name);
143001d7584cSLiam Girdwood 	return NULL;
143101d7584cSLiam Girdwood }
143201d7584cSLiam Girdwood 
143301d7584cSLiam Girdwood static inline struct snd_soc_dapm_widget *
143437018610SBenoit Cousson 	dai_get_widget(struct snd_soc_dai *dai, int stream)
143501d7584cSLiam Girdwood {
143601d7584cSLiam Girdwood 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
143737018610SBenoit Cousson 		return dai->playback_widget;
143801d7584cSLiam Girdwood 	else
143937018610SBenoit Cousson 		return dai->capture_widget;
144001d7584cSLiam Girdwood }
144101d7584cSLiam Girdwood 
144201d7584cSLiam Girdwood static int widget_in_list(struct snd_soc_dapm_widget_list *list,
144301d7584cSLiam Girdwood 		struct snd_soc_dapm_widget *widget)
144401d7584cSLiam Girdwood {
144501d7584cSLiam Girdwood 	int i;
144601d7584cSLiam Girdwood 
144701d7584cSLiam Girdwood 	for (i = 0; i < list->num_widgets; i++) {
144801d7584cSLiam Girdwood 		if (widget == list->widgets[i])
144901d7584cSLiam Girdwood 			return 1;
145001d7584cSLiam Girdwood 	}
145101d7584cSLiam Girdwood 
145201d7584cSLiam Girdwood 	return 0;
145301d7584cSLiam Girdwood }
145401d7584cSLiam Girdwood 
14555fdd022cSPiotr Stankiewicz static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
14565fdd022cSPiotr Stankiewicz 		enum snd_soc_dapm_direction dir)
14575fdd022cSPiotr Stankiewicz {
14585fdd022cSPiotr Stankiewicz 	struct snd_soc_card *card = widget->dapm->card;
14595fdd022cSPiotr Stankiewicz 	struct snd_soc_pcm_runtime *rtd;
14605fdd022cSPiotr Stankiewicz 	int i;
14615fdd022cSPiotr Stankiewicz 
14625fdd022cSPiotr Stankiewicz 	if (dir == SND_SOC_DAPM_DIR_OUT) {
14635fdd022cSPiotr Stankiewicz 		list_for_each_entry(rtd, &card->rtd_list, list) {
14645fdd022cSPiotr Stankiewicz 			if (!rtd->dai_link->no_pcm)
14655fdd022cSPiotr Stankiewicz 				continue;
14665fdd022cSPiotr Stankiewicz 
14675fdd022cSPiotr Stankiewicz 			if (rtd->cpu_dai->playback_widget == widget)
14685fdd022cSPiotr Stankiewicz 				return true;
14695fdd022cSPiotr Stankiewicz 
14705fdd022cSPiotr Stankiewicz 			for (i = 0; i < rtd->num_codecs; ++i) {
14715fdd022cSPiotr Stankiewicz 				struct snd_soc_dai *dai = rtd->codec_dais[i];
14725fdd022cSPiotr Stankiewicz 				if (dai->playback_widget == widget)
14735fdd022cSPiotr Stankiewicz 					return true;
14745fdd022cSPiotr Stankiewicz 			}
14755fdd022cSPiotr Stankiewicz 		}
14765fdd022cSPiotr Stankiewicz 	} else { /* SND_SOC_DAPM_DIR_IN */
14775fdd022cSPiotr Stankiewicz 		list_for_each_entry(rtd, &card->rtd_list, list) {
14785fdd022cSPiotr Stankiewicz 			if (!rtd->dai_link->no_pcm)
14795fdd022cSPiotr Stankiewicz 				continue;
14805fdd022cSPiotr Stankiewicz 
14815fdd022cSPiotr Stankiewicz 			if (rtd->cpu_dai->capture_widget == widget)
14825fdd022cSPiotr Stankiewicz 				return true;
14835fdd022cSPiotr Stankiewicz 
14845fdd022cSPiotr Stankiewicz 			for (i = 0; i < rtd->num_codecs; ++i) {
14855fdd022cSPiotr Stankiewicz 				struct snd_soc_dai *dai = rtd->codec_dais[i];
14865fdd022cSPiotr Stankiewicz 				if (dai->capture_widget == widget)
14875fdd022cSPiotr Stankiewicz 					return true;
14885fdd022cSPiotr Stankiewicz 			}
14895fdd022cSPiotr Stankiewicz 		}
14905fdd022cSPiotr Stankiewicz 	}
14915fdd022cSPiotr Stankiewicz 
14925fdd022cSPiotr Stankiewicz 	return false;
14935fdd022cSPiotr Stankiewicz }
14945fdd022cSPiotr Stankiewicz 
149523607025SLiam Girdwood int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
14961ce43acfSLars-Peter Clausen 	int stream, struct snd_soc_dapm_widget_list **list)
149701d7584cSLiam Girdwood {
149801d7584cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
149901d7584cSLiam Girdwood 	int paths;
150001d7584cSLiam Girdwood 
150101d7584cSLiam Girdwood 	/* get number of valid DAI paths and their widgets */
15026742064aSPiotr Stankiewicz 	paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list,
15035fdd022cSPiotr Stankiewicz 			dpcm_end_walk_at_be);
150401d7584cSLiam Girdwood 
1505103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
150601d7584cSLiam Girdwood 			stream ? "capture" : "playback");
150701d7584cSLiam Girdwood 
150801d7584cSLiam Girdwood 	return paths;
150901d7584cSLiam Girdwood }
151001d7584cSLiam Girdwood 
151101d7584cSLiam Girdwood static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
151201d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list **list_)
151301d7584cSLiam Girdwood {
151401d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
151501d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list = *list_;
151601d7584cSLiam Girdwood 	struct snd_soc_dapm_widget *widget;
151701d7584cSLiam Girdwood 	int prune = 0;
151801d7584cSLiam Girdwood 
151901d7584cSLiam Girdwood 	/* Destroy any old FE <--> BE connections */
152001d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
15212e5894d7SBenoit Cousson 		unsigned int i;
152201d7584cSLiam Girdwood 
152301d7584cSLiam Girdwood 		/* is there a valid CPU DAI widget for this BE */
152437018610SBenoit Cousson 		widget = dai_get_widget(dpcm->be->cpu_dai, stream);
152501d7584cSLiam Girdwood 
152601d7584cSLiam Girdwood 		/* prune the BE if it's no longer in our active list */
152701d7584cSLiam Girdwood 		if (widget && widget_in_list(list, widget))
152801d7584cSLiam Girdwood 			continue;
152901d7584cSLiam Girdwood 
153001d7584cSLiam Girdwood 		/* is there a valid CODEC DAI widget for this BE */
15312e5894d7SBenoit Cousson 		for (i = 0; i < dpcm->be->num_codecs; i++) {
15322e5894d7SBenoit Cousson 			struct snd_soc_dai *dai = dpcm->be->codec_dais[i];
15332e5894d7SBenoit Cousson 			widget = dai_get_widget(dai, stream);
153401d7584cSLiam Girdwood 
153501d7584cSLiam Girdwood 			/* prune the BE if it's no longer in our active list */
153601d7584cSLiam Girdwood 			if (widget && widget_in_list(list, widget))
153701d7584cSLiam Girdwood 				continue;
15382e5894d7SBenoit Cousson 		}
153901d7584cSLiam Girdwood 
1540103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
154101d7584cSLiam Girdwood 			stream ? "capture" : "playback",
154201d7584cSLiam Girdwood 			dpcm->be->dai_link->name, fe->dai_link->name);
154301d7584cSLiam Girdwood 		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
154401d7584cSLiam Girdwood 		dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
154501d7584cSLiam Girdwood 		prune++;
154601d7584cSLiam Girdwood 	}
154701d7584cSLiam Girdwood 
1548103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: found %d old BE paths for pruning\n", prune);
154901d7584cSLiam Girdwood 	return prune;
155001d7584cSLiam Girdwood }
155101d7584cSLiam Girdwood 
155201d7584cSLiam Girdwood static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
155301d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list **list_)
155401d7584cSLiam Girdwood {
155501d7584cSLiam Girdwood 	struct snd_soc_card *card = fe->card;
155601d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list = *list_;
155701d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *be;
155801d7584cSLiam Girdwood 	int i, new = 0, err;
155901d7584cSLiam Girdwood 
156001d7584cSLiam Girdwood 	/* Create any new FE <--> BE connections */
156101d7584cSLiam Girdwood 	for (i = 0; i < list->num_widgets; i++) {
156201d7584cSLiam Girdwood 
15634616274dSMark Brown 		switch (list->widgets[i]->id) {
15644616274dSMark Brown 		case snd_soc_dapm_dai_in:
1565c5b8540dSKoro Chen 			if (stream != SNDRV_PCM_STREAM_PLAYBACK)
1566c5b8540dSKoro Chen 				continue;
1567c5b8540dSKoro Chen 			break;
15684616274dSMark Brown 		case snd_soc_dapm_dai_out:
1569c5b8540dSKoro Chen 			if (stream != SNDRV_PCM_STREAM_CAPTURE)
1570c5b8540dSKoro Chen 				continue;
15714616274dSMark Brown 			break;
15724616274dSMark Brown 		default:
157301d7584cSLiam Girdwood 			continue;
15744616274dSMark Brown 		}
157501d7584cSLiam Girdwood 
157601d7584cSLiam Girdwood 		/* is there a valid BE rtd for this widget */
157701d7584cSLiam Girdwood 		be = dpcm_get_be(card, list->widgets[i], stream);
157801d7584cSLiam Girdwood 		if (!be) {
1579103d84a3SLiam Girdwood 			dev_err(fe->dev, "ASoC: no BE found for %s\n",
158001d7584cSLiam Girdwood 					list->widgets[i]->name);
158101d7584cSLiam Girdwood 			continue;
158201d7584cSLiam Girdwood 		}
158301d7584cSLiam Girdwood 
158401d7584cSLiam Girdwood 		/* make sure BE is a real BE */
158501d7584cSLiam Girdwood 		if (!be->dai_link->no_pcm)
158601d7584cSLiam Girdwood 			continue;
158701d7584cSLiam Girdwood 
158801d7584cSLiam Girdwood 		/* don't connect if FE is not running */
158923607025SLiam Girdwood 		if (!fe->dpcm[stream].runtime && !fe->fe_compr)
159001d7584cSLiam Girdwood 			continue;
159101d7584cSLiam Girdwood 
159201d7584cSLiam Girdwood 		/* newly connected FE and BE */
159301d7584cSLiam Girdwood 		err = dpcm_be_connect(fe, be, stream);
159401d7584cSLiam Girdwood 		if (err < 0) {
1595103d84a3SLiam Girdwood 			dev_err(fe->dev, "ASoC: can't connect %s\n",
159601d7584cSLiam Girdwood 				list->widgets[i]->name);
159701d7584cSLiam Girdwood 			break;
159801d7584cSLiam Girdwood 		} else if (err == 0) /* already connected */
159901d7584cSLiam Girdwood 			continue;
160001d7584cSLiam Girdwood 
160101d7584cSLiam Girdwood 		/* new */
160201d7584cSLiam Girdwood 		be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
160301d7584cSLiam Girdwood 		new++;
160401d7584cSLiam Girdwood 	}
160501d7584cSLiam Girdwood 
1606103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: found %d new BE paths\n", new);
160701d7584cSLiam Girdwood 	return new;
160801d7584cSLiam Girdwood }
160901d7584cSLiam Girdwood 
161001d7584cSLiam Girdwood /*
161101d7584cSLiam Girdwood  * Find the corresponding BE DAIs that source or sink audio to this
161201d7584cSLiam Girdwood  * FE substream.
161301d7584cSLiam Girdwood  */
161423607025SLiam Girdwood int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
161501d7584cSLiam Girdwood 	int stream, struct snd_soc_dapm_widget_list **list, int new)
161601d7584cSLiam Girdwood {
161701d7584cSLiam Girdwood 	if (new)
161801d7584cSLiam Girdwood 		return dpcm_add_paths(fe, stream, list);
161901d7584cSLiam Girdwood 	else
162001d7584cSLiam Girdwood 		return dpcm_prune_paths(fe, stream, list);
162101d7584cSLiam Girdwood }
162201d7584cSLiam Girdwood 
162323607025SLiam Girdwood void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
162401d7584cSLiam Girdwood {
162501d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
162601d7584cSLiam Girdwood 
162701d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
162801d7584cSLiam Girdwood 		dpcm->be->dpcm[stream].runtime_update =
162901d7584cSLiam Girdwood 						SND_SOC_DPCM_UPDATE_NO;
163001d7584cSLiam Girdwood }
163101d7584cSLiam Girdwood 
163201d7584cSLiam Girdwood static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
163301d7584cSLiam Girdwood 	int stream)
163401d7584cSLiam Girdwood {
163501d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
163601d7584cSLiam Girdwood 
163701d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
163801d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
163901d7584cSLiam Girdwood 
164001d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
164101d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
164201d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
164301d7584cSLiam Girdwood 
164401d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1645103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: no users %s at close - state %d\n",
164601d7584cSLiam Girdwood 				stream ? "capture" : "playback",
164701d7584cSLiam Girdwood 				be->dpcm[stream].state);
164801d7584cSLiam Girdwood 
164901d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
165001d7584cSLiam Girdwood 			continue;
165101d7584cSLiam Girdwood 
165201d7584cSLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
165301d7584cSLiam Girdwood 			continue;
165401d7584cSLiam Girdwood 
165501d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
165601d7584cSLiam Girdwood 		be_substream->runtime = NULL;
165701d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
165801d7584cSLiam Girdwood 	}
165901d7584cSLiam Girdwood }
166001d7584cSLiam Girdwood 
166123607025SLiam Girdwood int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
166201d7584cSLiam Girdwood {
166301d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
166401d7584cSLiam Girdwood 	int err, count = 0;
166501d7584cSLiam Girdwood 
166601d7584cSLiam Girdwood 	/* only startup BE DAIs that are either sinks or sources to this FE DAI */
166701d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
166801d7584cSLiam Girdwood 
166901d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
167001d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
167101d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
167201d7584cSLiam Girdwood 
16732062b4c5SRussell King - ARM Linux 		if (!be_substream) {
16742062b4c5SRussell King - ARM Linux 			dev_err(be->dev, "ASoC: no backend %s stream\n",
16752062b4c5SRussell King - ARM Linux 				stream ? "capture" : "playback");
16762062b4c5SRussell King - ARM Linux 			continue;
16772062b4c5SRussell King - ARM Linux 		}
16782062b4c5SRussell King - ARM Linux 
167901d7584cSLiam Girdwood 		/* is this op for this BE ? */
168001d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
168101d7584cSLiam Girdwood 			continue;
168201d7584cSLiam Girdwood 
168301d7584cSLiam Girdwood 		/* first time the dpcm is open ? */
168401d7584cSLiam Girdwood 		if (be->dpcm[stream].users == DPCM_MAX_BE_USERS)
1685103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: too many users %s at open %d\n",
168601d7584cSLiam Girdwood 				stream ? "capture" : "playback",
168701d7584cSLiam Girdwood 				be->dpcm[stream].state);
168801d7584cSLiam Girdwood 
168901d7584cSLiam Girdwood 		if (be->dpcm[stream].users++ != 0)
169001d7584cSLiam Girdwood 			continue;
169101d7584cSLiam Girdwood 
169201d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
169301d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
169401d7584cSLiam Girdwood 			continue;
169501d7584cSLiam Girdwood 
16962062b4c5SRussell King - ARM Linux 		dev_dbg(be->dev, "ASoC: open %s BE %s\n",
16972062b4c5SRussell King - ARM Linux 			stream ? "capture" : "playback", be->dai_link->name);
169801d7584cSLiam Girdwood 
169901d7584cSLiam Girdwood 		be_substream->runtime = be->dpcm[stream].runtime;
170001d7584cSLiam Girdwood 		err = soc_pcm_open(be_substream);
170101d7584cSLiam Girdwood 		if (err < 0) {
1702103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: BE open failed %d\n", err);
170301d7584cSLiam Girdwood 			be->dpcm[stream].users--;
170401d7584cSLiam Girdwood 			if (be->dpcm[stream].users < 0)
1705103d84a3SLiam Girdwood 				dev_err(be->dev, "ASoC: no users %s at unwind %d\n",
170601d7584cSLiam Girdwood 					stream ? "capture" : "playback",
170701d7584cSLiam Girdwood 					be->dpcm[stream].state);
170801d7584cSLiam Girdwood 
170901d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
171001d7584cSLiam Girdwood 			goto unwind;
171101d7584cSLiam Girdwood 		}
171201d7584cSLiam Girdwood 
171301d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
171401d7584cSLiam Girdwood 		count++;
171501d7584cSLiam Girdwood 	}
171601d7584cSLiam Girdwood 
171701d7584cSLiam Girdwood 	return count;
171801d7584cSLiam Girdwood 
171901d7584cSLiam Girdwood unwind:
172001d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
172101d7584cSLiam Girdwood 	list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
172201d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
172301d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
172401d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
172501d7584cSLiam Girdwood 
172601d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
172701d7584cSLiam Girdwood 			continue;
172801d7584cSLiam Girdwood 
172901d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1730103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: no users %s at close %d\n",
173101d7584cSLiam Girdwood 				stream ? "capture" : "playback",
173201d7584cSLiam Girdwood 				be->dpcm[stream].state);
173301d7584cSLiam Girdwood 
173401d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
173501d7584cSLiam Girdwood 			continue;
173601d7584cSLiam Girdwood 
173701d7584cSLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
173801d7584cSLiam Girdwood 			continue;
173901d7584cSLiam Girdwood 
174001d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
174101d7584cSLiam Girdwood 		be_substream->runtime = NULL;
174201d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
174301d7584cSLiam Girdwood 	}
174401d7584cSLiam Girdwood 
174501d7584cSLiam Girdwood 	return err;
174601d7584cSLiam Girdwood }
174701d7584cSLiam Girdwood 
174808ae9b45SLars-Peter Clausen static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
1749b073ed4eSKuninori Morimoto 				 struct snd_soc_pcm_stream *stream,
1750b073ed4eSKuninori Morimoto 				 u64 formats)
175108ae9b45SLars-Peter Clausen {
175208ae9b45SLars-Peter Clausen 	runtime->hw.rate_min = stream->rate_min;
175308ae9b45SLars-Peter Clausen 	runtime->hw.rate_max = stream->rate_max;
175408ae9b45SLars-Peter Clausen 	runtime->hw.channels_min = stream->channels_min;
175508ae9b45SLars-Peter Clausen 	runtime->hw.channels_max = stream->channels_max;
1756002220a9SLars-Peter Clausen 	if (runtime->hw.formats)
1757b073ed4eSKuninori Morimoto 		runtime->hw.formats &= formats & stream->formats;
1758002220a9SLars-Peter Clausen 	else
1759b073ed4eSKuninori Morimoto 		runtime->hw.formats = formats & stream->formats;
176008ae9b45SLars-Peter Clausen 	runtime->hw.rates = stream->rates;
176108ae9b45SLars-Peter Clausen }
176208ae9b45SLars-Peter Clausen 
1763b073ed4eSKuninori Morimoto static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream)
1764b073ed4eSKuninori Morimoto {
1765b073ed4eSKuninori Morimoto 	struct snd_soc_pcm_runtime *fe = substream->private_data;
1766b073ed4eSKuninori Morimoto 	struct snd_soc_dpcm *dpcm;
1767b073ed4eSKuninori Morimoto 	u64 formats = ULLONG_MAX;
1768b073ed4eSKuninori Morimoto 	int stream = substream->stream;
1769b073ed4eSKuninori Morimoto 
1770b073ed4eSKuninori Morimoto 	if (!fe->dai_link->dpcm_merged_format)
1771b073ed4eSKuninori Morimoto 		return formats;
1772b073ed4eSKuninori Morimoto 
1773b073ed4eSKuninori Morimoto 	/*
1774b073ed4eSKuninori Morimoto 	 * It returns merged BE codec format
1775b073ed4eSKuninori Morimoto 	 * if FE want to use it (= dpcm_merged_format)
1776b073ed4eSKuninori Morimoto 	 */
1777b073ed4eSKuninori Morimoto 
1778b073ed4eSKuninori Morimoto 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
1779b073ed4eSKuninori Morimoto 		struct snd_soc_pcm_runtime *be = dpcm->be;
1780b073ed4eSKuninori Morimoto 		struct snd_soc_dai_driver *codec_dai_drv;
1781b073ed4eSKuninori Morimoto 		struct snd_soc_pcm_stream *codec_stream;
1782b073ed4eSKuninori Morimoto 		int i;
1783b073ed4eSKuninori Morimoto 
1784b073ed4eSKuninori Morimoto 		for (i = 0; i < be->num_codecs; i++) {
1785b073ed4eSKuninori Morimoto 			codec_dai_drv = be->codec_dais[i]->driver;
1786b073ed4eSKuninori Morimoto 			if (stream == SNDRV_PCM_STREAM_PLAYBACK)
1787b073ed4eSKuninori Morimoto 				codec_stream = &codec_dai_drv->playback;
1788b073ed4eSKuninori Morimoto 			else
1789b073ed4eSKuninori Morimoto 				codec_stream = &codec_dai_drv->capture;
1790b073ed4eSKuninori Morimoto 
1791b073ed4eSKuninori Morimoto 			formats &= codec_stream->formats;
1792b073ed4eSKuninori Morimoto 		}
1793b073ed4eSKuninori Morimoto 	}
1794b073ed4eSKuninori Morimoto 
1795b073ed4eSKuninori Morimoto 	return formats;
1796b073ed4eSKuninori Morimoto }
1797b073ed4eSKuninori Morimoto 
179845c0a188SMark Brown static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
179901d7584cSLiam Girdwood {
180001d7584cSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
180101d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
180201d7584cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
180301d7584cSLiam Girdwood 	struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
1804b073ed4eSKuninori Morimoto 	u64 format = dpcm_runtime_base_format(substream);
180501d7584cSLiam Girdwood 
180608ae9b45SLars-Peter Clausen 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1807b073ed4eSKuninori Morimoto 		dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback, format);
180808ae9b45SLars-Peter Clausen 	else
1809b073ed4eSKuninori Morimoto 		dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture, format);
181001d7584cSLiam Girdwood }
181101d7584cSLiam Girdwood 
1812ea9d0d77STakashi Iwai static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
1813ea9d0d77STakashi Iwai 
1814ea9d0d77STakashi Iwai /* Set FE's runtime_update state; the state is protected via PCM stream lock
1815ea9d0d77STakashi Iwai  * for avoiding the race with trigger callback.
1816ea9d0d77STakashi Iwai  * If the state is unset and a trigger is pending while the previous operation,
1817ea9d0d77STakashi Iwai  * process the pending trigger action here.
1818ea9d0d77STakashi Iwai  */
1819ea9d0d77STakashi Iwai static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
1820ea9d0d77STakashi Iwai 				     int stream, enum snd_soc_dpcm_update state)
1821ea9d0d77STakashi Iwai {
1822ea9d0d77STakashi Iwai 	struct snd_pcm_substream *substream =
1823ea9d0d77STakashi Iwai 		snd_soc_dpcm_get_substream(fe, stream);
1824ea9d0d77STakashi Iwai 
1825ea9d0d77STakashi Iwai 	snd_pcm_stream_lock_irq(substream);
1826ea9d0d77STakashi Iwai 	if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
1827ea9d0d77STakashi Iwai 		dpcm_fe_dai_do_trigger(substream,
1828ea9d0d77STakashi Iwai 				       fe->dpcm[stream].trigger_pending - 1);
1829ea9d0d77STakashi Iwai 		fe->dpcm[stream].trigger_pending = 0;
1830ea9d0d77STakashi Iwai 	}
1831ea9d0d77STakashi Iwai 	fe->dpcm[stream].runtime_update = state;
1832ea9d0d77STakashi Iwai 	snd_pcm_stream_unlock_irq(substream);
1833ea9d0d77STakashi Iwai }
1834ea9d0d77STakashi Iwai 
1835906c7d69SPC Liao static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
1836906c7d69SPC Liao 			       int stream)
1837906c7d69SPC Liao {
1838906c7d69SPC Liao 	struct snd_soc_dpcm *dpcm;
1839906c7d69SPC Liao 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
1840906c7d69SPC Liao 	struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai;
1841906c7d69SPC Liao 	int err;
1842906c7d69SPC Liao 
1843906c7d69SPC Liao 	/* apply symmetry for FE */
1844906c7d69SPC Liao 	if (soc_pcm_has_symmetry(fe_substream))
1845906c7d69SPC Liao 		fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
1846906c7d69SPC Liao 
1847906c7d69SPC Liao 	/* Symmetry only applies if we've got an active stream. */
1848906c7d69SPC Liao 	if (fe_cpu_dai->active) {
1849906c7d69SPC Liao 		err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
1850906c7d69SPC Liao 		if (err < 0)
1851906c7d69SPC Liao 			return err;
1852906c7d69SPC Liao 	}
1853906c7d69SPC Liao 
1854906c7d69SPC Liao 	/* apply symmetry for BE */
1855906c7d69SPC Liao 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
1856906c7d69SPC Liao 		struct snd_soc_pcm_runtime *be = dpcm->be;
1857906c7d69SPC Liao 		struct snd_pcm_substream *be_substream =
1858906c7d69SPC Liao 			snd_soc_dpcm_get_substream(be, stream);
1859906c7d69SPC Liao 		struct snd_soc_pcm_runtime *rtd = be_substream->private_data;
1860906c7d69SPC Liao 		int i;
1861906c7d69SPC Liao 
1862f1176614SJeeja KP 		if (rtd->dai_link->be_hw_params_fixup)
1863f1176614SJeeja KP 			continue;
1864f1176614SJeeja KP 
1865906c7d69SPC Liao 		if (soc_pcm_has_symmetry(be_substream))
1866906c7d69SPC Liao 			be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
1867906c7d69SPC Liao 
1868906c7d69SPC Liao 		/* Symmetry only applies if we've got an active stream. */
1869906c7d69SPC Liao 		if (rtd->cpu_dai->active) {
1870906c7d69SPC Liao 			err = soc_pcm_apply_symmetry(be_substream, rtd->cpu_dai);
1871906c7d69SPC Liao 			if (err < 0)
1872906c7d69SPC Liao 				return err;
1873906c7d69SPC Liao 		}
1874906c7d69SPC Liao 
1875906c7d69SPC Liao 		for (i = 0; i < rtd->num_codecs; i++) {
1876906c7d69SPC Liao 			if (rtd->codec_dais[i]->active) {
1877906c7d69SPC Liao 				err = soc_pcm_apply_symmetry(be_substream,
1878906c7d69SPC Liao 							     rtd->codec_dais[i]);
1879906c7d69SPC Liao 				if (err < 0)
1880906c7d69SPC Liao 					return err;
1881906c7d69SPC Liao 			}
1882906c7d69SPC Liao 		}
1883906c7d69SPC Liao 	}
1884906c7d69SPC Liao 
1885906c7d69SPC Liao 	return 0;
1886906c7d69SPC Liao }
1887906c7d69SPC Liao 
188801d7584cSLiam Girdwood static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
188901d7584cSLiam Girdwood {
189001d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
189101d7584cSLiam Girdwood 	struct snd_pcm_runtime *runtime = fe_substream->runtime;
189201d7584cSLiam Girdwood 	int stream = fe_substream->stream, ret = 0;
189301d7584cSLiam Girdwood 
1894ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
189501d7584cSLiam Girdwood 
189601d7584cSLiam Girdwood 	ret = dpcm_be_dai_startup(fe, fe_substream->stream);
189701d7584cSLiam Girdwood 	if (ret < 0) {
1898103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: failed to start some BEs %d\n", ret);
189901d7584cSLiam Girdwood 		goto be_err;
190001d7584cSLiam Girdwood 	}
190101d7584cSLiam Girdwood 
1902103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name);
190301d7584cSLiam Girdwood 
190401d7584cSLiam Girdwood 	/* start the DAI frontend */
190501d7584cSLiam Girdwood 	ret = soc_pcm_open(fe_substream);
190601d7584cSLiam Girdwood 	if (ret < 0) {
1907103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: failed to start FE %d\n", ret);
190801d7584cSLiam Girdwood 		goto unwind;
190901d7584cSLiam Girdwood 	}
191001d7584cSLiam Girdwood 
191101d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
191201d7584cSLiam Girdwood 
191301d7584cSLiam Girdwood 	dpcm_set_fe_runtime(fe_substream);
191401d7584cSLiam Girdwood 	snd_pcm_limit_hw_rates(runtime);
191501d7584cSLiam Girdwood 
1916906c7d69SPC Liao 	ret = dpcm_apply_symmetry(fe_substream, stream);
1917906c7d69SPC Liao 	if (ret < 0) {
1918906c7d69SPC Liao 		dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n",
1919906c7d69SPC Liao 			ret);
1920906c7d69SPC Liao 		goto unwind;
1921906c7d69SPC Liao 	}
1922906c7d69SPC Liao 
1923ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
192401d7584cSLiam Girdwood 	return 0;
192501d7584cSLiam Girdwood 
192601d7584cSLiam Girdwood unwind:
192701d7584cSLiam Girdwood 	dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
192801d7584cSLiam Girdwood be_err:
1929ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
193001d7584cSLiam Girdwood 	return ret;
193101d7584cSLiam Girdwood }
193201d7584cSLiam Girdwood 
193323607025SLiam Girdwood int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
193401d7584cSLiam Girdwood {
193501d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
193601d7584cSLiam Girdwood 
193701d7584cSLiam Girdwood 	/* only shutdown BEs that are either sinks or sources to this FE DAI */
193801d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
193901d7584cSLiam Girdwood 
194001d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
194101d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
194201d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
194301d7584cSLiam Girdwood 
194401d7584cSLiam Girdwood 		/* is this op for this BE ? */
194501d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
194601d7584cSLiam Girdwood 			continue;
194701d7584cSLiam Girdwood 
194801d7584cSLiam Girdwood 		if (be->dpcm[stream].users == 0)
1949103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: no users %s at close - state %d\n",
195001d7584cSLiam Girdwood 				stream ? "capture" : "playback",
195101d7584cSLiam Girdwood 				be->dpcm[stream].state);
195201d7584cSLiam Girdwood 
195301d7584cSLiam Girdwood 		if (--be->dpcm[stream].users != 0)
195401d7584cSLiam Girdwood 			continue;
195501d7584cSLiam Girdwood 
195601d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
195701d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN))
195801d7584cSLiam Girdwood 			continue;
195901d7584cSLiam Girdwood 
1960103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: close BE %s\n",
196194d215ccS彭东林 			be->dai_link->name);
196201d7584cSLiam Girdwood 
196301d7584cSLiam Girdwood 		soc_pcm_close(be_substream);
196401d7584cSLiam Girdwood 		be_substream->runtime = NULL;
196501d7584cSLiam Girdwood 
196601d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
196701d7584cSLiam Girdwood 	}
196801d7584cSLiam Girdwood 	return 0;
196901d7584cSLiam Girdwood }
197001d7584cSLiam Girdwood 
197101d7584cSLiam Girdwood static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
197201d7584cSLiam Girdwood {
197301d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
197401d7584cSLiam Girdwood 	int stream = substream->stream;
197501d7584cSLiam Girdwood 
1976ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
197701d7584cSLiam Girdwood 
197801d7584cSLiam Girdwood 	/* shutdown the BEs */
197901d7584cSLiam Girdwood 	dpcm_be_dai_shutdown(fe, substream->stream);
198001d7584cSLiam Girdwood 
1981103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name);
198201d7584cSLiam Girdwood 
198301d7584cSLiam Girdwood 	/* now shutdown the frontend */
198401d7584cSLiam Girdwood 	soc_pcm_close(substream);
198501d7584cSLiam Girdwood 
198601d7584cSLiam Girdwood 	/* run the stream event for each BE */
198701d7584cSLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
198801d7584cSLiam Girdwood 
198901d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
1990ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
199101d7584cSLiam Girdwood 	return 0;
199201d7584cSLiam Girdwood }
199301d7584cSLiam Girdwood 
199423607025SLiam Girdwood int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
199501d7584cSLiam Girdwood {
199601d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
199701d7584cSLiam Girdwood 
199801d7584cSLiam Girdwood 	/* only hw_params backends that are either sinks or sources
199901d7584cSLiam Girdwood 	 * to this frontend DAI */
200001d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
200101d7584cSLiam Girdwood 
200201d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
200301d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
200401d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
200501d7584cSLiam Girdwood 
200601d7584cSLiam Girdwood 		/* is this op for this BE ? */
200701d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
200801d7584cSLiam Girdwood 			continue;
200901d7584cSLiam Girdwood 
201001d7584cSLiam Girdwood 		/* only free hw when no longer used - check all FEs */
201101d7584cSLiam Girdwood 		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
201201d7584cSLiam Girdwood 				continue;
201301d7584cSLiam Girdwood 
201436fba62cSQiao Zhou 		/* do not free hw if this BE is used by other FE */
201536fba62cSQiao Zhou 		if (be->dpcm[stream].users > 1)
201636fba62cSQiao Zhou 			continue;
201736fba62cSQiao Zhou 
201801d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
201901d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
202001d7584cSLiam Girdwood 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
202108b27848SPatrick Lai 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) &&
20225e82d2beSVinod Koul 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
20235e82d2beSVinod Koul 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
202401d7584cSLiam Girdwood 			continue;
202501d7584cSLiam Girdwood 
2026103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: hw_free BE %s\n",
202794d215ccS彭东林 			be->dai_link->name);
202801d7584cSLiam Girdwood 
202901d7584cSLiam Girdwood 		soc_pcm_hw_free(be_substream);
203001d7584cSLiam Girdwood 
203101d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
203201d7584cSLiam Girdwood 	}
203301d7584cSLiam Girdwood 
203401d7584cSLiam Girdwood 	return 0;
203501d7584cSLiam Girdwood }
203601d7584cSLiam Girdwood 
203745c0a188SMark Brown static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
203801d7584cSLiam Girdwood {
203901d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
204001d7584cSLiam Girdwood 	int err, stream = substream->stream;
204101d7584cSLiam Girdwood 
204201d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
2043ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
204401d7584cSLiam Girdwood 
2045103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
204601d7584cSLiam Girdwood 
204701d7584cSLiam Girdwood 	/* call hw_free on the frontend */
204801d7584cSLiam Girdwood 	err = soc_pcm_hw_free(substream);
204901d7584cSLiam Girdwood 	if (err < 0)
2050103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_free FE %s failed\n",
205101d7584cSLiam Girdwood 			fe->dai_link->name);
205201d7584cSLiam Girdwood 
205301d7584cSLiam Girdwood 	/* only hw_params backends that are either sinks or sources
205401d7584cSLiam Girdwood 	 * to this frontend DAI */
205501d7584cSLiam Girdwood 	err = dpcm_be_dai_hw_free(fe, stream);
205601d7584cSLiam Girdwood 
205701d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
2058ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
205901d7584cSLiam Girdwood 
206001d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
206101d7584cSLiam Girdwood 	return 0;
206201d7584cSLiam Girdwood }
206301d7584cSLiam Girdwood 
206423607025SLiam Girdwood int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
206501d7584cSLiam Girdwood {
206601d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
206701d7584cSLiam Girdwood 	int ret;
206801d7584cSLiam Girdwood 
206901d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
207001d7584cSLiam Girdwood 
207101d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
207201d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
207301d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
207401d7584cSLiam Girdwood 
207501d7584cSLiam Girdwood 		/* is this op for this BE ? */
207601d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
207701d7584cSLiam Girdwood 			continue;
207801d7584cSLiam Girdwood 
207901d7584cSLiam Girdwood 		/* copy params for each dpcm */
208001d7584cSLiam Girdwood 		memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params,
208101d7584cSLiam Girdwood 				sizeof(struct snd_pcm_hw_params));
208201d7584cSLiam Girdwood 
208301d7584cSLiam Girdwood 		/* perform any hw_params fixups */
208401d7584cSLiam Girdwood 		if (be->dai_link->be_hw_params_fixup) {
208501d7584cSLiam Girdwood 			ret = be->dai_link->be_hw_params_fixup(be,
208601d7584cSLiam Girdwood 					&dpcm->hw_params);
208701d7584cSLiam Girdwood 			if (ret < 0) {
208801d7584cSLiam Girdwood 				dev_err(be->dev,
2089103d84a3SLiam Girdwood 					"ASoC: hw_params BE fixup failed %d\n",
209001d7584cSLiam Girdwood 					ret);
209101d7584cSLiam Girdwood 				goto unwind;
209201d7584cSLiam Girdwood 			}
209301d7584cSLiam Girdwood 		}
209401d7584cSLiam Girdwood 
2095b0639bd2SKuninori Morimoto 		/* only allow hw_params() if no connected FEs are running */
2096b0639bd2SKuninori Morimoto 		if (!snd_soc_dpcm_can_be_params(fe, be, stream))
2097b0639bd2SKuninori Morimoto 			continue;
2098b0639bd2SKuninori Morimoto 
2099b0639bd2SKuninori Morimoto 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
2100b0639bd2SKuninori Morimoto 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
2101b0639bd2SKuninori Morimoto 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
2102b0639bd2SKuninori Morimoto 			continue;
2103b0639bd2SKuninori Morimoto 
2104b0639bd2SKuninori Morimoto 		dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
210594d215ccS彭东林 			be->dai_link->name);
2106b0639bd2SKuninori Morimoto 
210701d7584cSLiam Girdwood 		ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
210801d7584cSLiam Girdwood 		if (ret < 0) {
210901d7584cSLiam Girdwood 			dev_err(dpcm->be->dev,
2110103d84a3SLiam Girdwood 				"ASoC: hw_params BE failed %d\n", ret);
211101d7584cSLiam Girdwood 			goto unwind;
211201d7584cSLiam Girdwood 		}
211301d7584cSLiam Girdwood 
211401d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
211501d7584cSLiam Girdwood 	}
211601d7584cSLiam Girdwood 	return 0;
211701d7584cSLiam Girdwood 
211801d7584cSLiam Girdwood unwind:
211901d7584cSLiam Girdwood 	/* disable any enabled and non active backends */
212001d7584cSLiam Girdwood 	list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
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 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
212601d7584cSLiam Girdwood 			continue;
212701d7584cSLiam Girdwood 
212801d7584cSLiam Girdwood 		/* only allow hw_free() if no connected FEs are running */
212901d7584cSLiam Girdwood 		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
213001d7584cSLiam Girdwood 			continue;
213101d7584cSLiam Girdwood 
213201d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
213301d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
213401d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
213501d7584cSLiam Girdwood 		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
213601d7584cSLiam Girdwood 			continue;
213701d7584cSLiam Girdwood 
213801d7584cSLiam Girdwood 		soc_pcm_hw_free(be_substream);
213901d7584cSLiam Girdwood 	}
214001d7584cSLiam Girdwood 
214101d7584cSLiam Girdwood 	return ret;
214201d7584cSLiam Girdwood }
214301d7584cSLiam Girdwood 
214445c0a188SMark Brown static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
214501d7584cSLiam Girdwood 				 struct snd_pcm_hw_params *params)
214601d7584cSLiam Girdwood {
214701d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
214801d7584cSLiam Girdwood 	int ret, stream = substream->stream;
214901d7584cSLiam Girdwood 
215001d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
2151ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
215201d7584cSLiam Girdwood 
215301d7584cSLiam Girdwood 	memcpy(&fe->dpcm[substream->stream].hw_params, params,
215401d7584cSLiam Girdwood 			sizeof(struct snd_pcm_hw_params));
215501d7584cSLiam Girdwood 	ret = dpcm_be_dai_hw_params(fe, substream->stream);
215601d7584cSLiam Girdwood 	if (ret < 0) {
2157103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_params BE failed %d\n", ret);
215801d7584cSLiam Girdwood 		goto out;
215901d7584cSLiam Girdwood 	}
216001d7584cSLiam Girdwood 
2161103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: hw_params FE %s rate %d chan %x fmt %d\n",
216201d7584cSLiam Girdwood 			fe->dai_link->name, params_rate(params),
216301d7584cSLiam Girdwood 			params_channels(params), params_format(params));
216401d7584cSLiam Girdwood 
216501d7584cSLiam Girdwood 	/* call hw_params on the frontend */
216601d7584cSLiam Girdwood 	ret = soc_pcm_hw_params(substream, params);
216701d7584cSLiam Girdwood 	if (ret < 0) {
2168103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret);
216901d7584cSLiam Girdwood 		dpcm_be_dai_hw_free(fe, stream);
217001d7584cSLiam Girdwood 	 } else
217101d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
217201d7584cSLiam Girdwood 
217301d7584cSLiam Girdwood out:
2174ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
217501d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
217601d7584cSLiam Girdwood 	return ret;
217701d7584cSLiam Girdwood }
217801d7584cSLiam Girdwood 
217901d7584cSLiam Girdwood static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm,
218001d7584cSLiam Girdwood 		struct snd_pcm_substream *substream, int cmd)
218101d7584cSLiam Girdwood {
218201d7584cSLiam Girdwood 	int ret;
218301d7584cSLiam Girdwood 
2184103d84a3SLiam Girdwood 	dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d\n",
218594d215ccS彭东林 			dpcm->be->dai_link->name, cmd);
218601d7584cSLiam Girdwood 
218701d7584cSLiam Girdwood 	ret = soc_pcm_trigger(substream, cmd);
218801d7584cSLiam Girdwood 	if (ret < 0)
2189103d84a3SLiam Girdwood 		dev_err(dpcm->be->dev,"ASoC: trigger BE failed %d\n", ret);
219001d7584cSLiam Girdwood 
219101d7584cSLiam Girdwood 	return ret;
219201d7584cSLiam Girdwood }
219301d7584cSLiam Girdwood 
219423607025SLiam Girdwood int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
219545c0a188SMark Brown 			       int cmd)
219601d7584cSLiam Girdwood {
219701d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
219801d7584cSLiam Girdwood 	int ret = 0;
219901d7584cSLiam Girdwood 
220001d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
220101d7584cSLiam Girdwood 
220201d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
220301d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
220401d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
220501d7584cSLiam Girdwood 
220601d7584cSLiam Girdwood 		/* is this op for this BE ? */
220701d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
220801d7584cSLiam Girdwood 			continue;
220901d7584cSLiam Girdwood 
221001d7584cSLiam Girdwood 		switch (cmd) {
221101d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_START:
221201d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
221301d7584cSLiam Girdwood 			    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
221401d7584cSLiam Girdwood 				continue;
221501d7584cSLiam Girdwood 
221601d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
221701d7584cSLiam Girdwood 			if (ret)
221801d7584cSLiam Girdwood 				return ret;
221901d7584cSLiam Girdwood 
222001d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
222101d7584cSLiam Girdwood 			break;
222201d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_RESUME:
222301d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
222401d7584cSLiam Girdwood 				continue;
222501d7584cSLiam Girdwood 
222601d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
222701d7584cSLiam Girdwood 			if (ret)
222801d7584cSLiam Girdwood 				return ret;
222901d7584cSLiam Girdwood 
223001d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
223101d7584cSLiam Girdwood 			break;
223201d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
223301d7584cSLiam Girdwood 			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
223401d7584cSLiam Girdwood 				continue;
223501d7584cSLiam Girdwood 
223601d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
223701d7584cSLiam Girdwood 			if (ret)
223801d7584cSLiam Girdwood 				return ret;
223901d7584cSLiam Girdwood 
224001d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
224101d7584cSLiam Girdwood 			break;
224201d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_STOP:
224301d7584cSLiam Girdwood 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
224401d7584cSLiam Girdwood 				continue;
224501d7584cSLiam Girdwood 
224601d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
224701d7584cSLiam Girdwood 				continue;
224801d7584cSLiam Girdwood 
224901d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
225001d7584cSLiam Girdwood 			if (ret)
225101d7584cSLiam Girdwood 				return ret;
225201d7584cSLiam Girdwood 
225301d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
225401d7584cSLiam Girdwood 			break;
225501d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_SUSPEND:
2256868a6ca8SNicolin Chen 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
225701d7584cSLiam Girdwood 				continue;
225801d7584cSLiam Girdwood 
225901d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
226001d7584cSLiam Girdwood 				continue;
226101d7584cSLiam Girdwood 
226201d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
226301d7584cSLiam Girdwood 			if (ret)
226401d7584cSLiam Girdwood 				return ret;
226501d7584cSLiam Girdwood 
226601d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
226701d7584cSLiam Girdwood 			break;
226801d7584cSLiam Girdwood 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
226901d7584cSLiam Girdwood 			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
227001d7584cSLiam Girdwood 				continue;
227101d7584cSLiam Girdwood 
227201d7584cSLiam Girdwood 			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
227301d7584cSLiam Girdwood 				continue;
227401d7584cSLiam Girdwood 
227501d7584cSLiam Girdwood 			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
227601d7584cSLiam Girdwood 			if (ret)
227701d7584cSLiam Girdwood 				return ret;
227801d7584cSLiam Girdwood 
227901d7584cSLiam Girdwood 			be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
228001d7584cSLiam Girdwood 			break;
228101d7584cSLiam Girdwood 		}
228201d7584cSLiam Girdwood 	}
228301d7584cSLiam Girdwood 
228401d7584cSLiam Girdwood 	return ret;
228501d7584cSLiam Girdwood }
228601d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
228701d7584cSLiam Girdwood 
2288ea9d0d77STakashi Iwai static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
228901d7584cSLiam Girdwood {
229001d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
229101d7584cSLiam Girdwood 	int stream = substream->stream, ret;
229201d7584cSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
229301d7584cSLiam Girdwood 
229401d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
229501d7584cSLiam Girdwood 
229601d7584cSLiam Girdwood 	switch (trigger) {
229701d7584cSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_PRE:
229801d7584cSLiam Girdwood 		/* call trigger on the frontend before the backend. */
229901d7584cSLiam Girdwood 
2300103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
230101d7584cSLiam Girdwood 				fe->dai_link->name, cmd);
230201d7584cSLiam Girdwood 
230301d7584cSLiam Girdwood 		ret = soc_pcm_trigger(substream, cmd);
230401d7584cSLiam Girdwood 		if (ret < 0) {
2305103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
230601d7584cSLiam Girdwood 			goto out;
230701d7584cSLiam Girdwood 		}
230801d7584cSLiam Girdwood 
230901d7584cSLiam Girdwood 		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
231001d7584cSLiam Girdwood 		break;
231101d7584cSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_POST:
231201d7584cSLiam Girdwood 		/* call trigger on the frontend after the backend. */
231301d7584cSLiam Girdwood 
231401d7584cSLiam Girdwood 		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
231501d7584cSLiam Girdwood 		if (ret < 0) {
2316103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
231701d7584cSLiam Girdwood 			goto out;
231801d7584cSLiam Girdwood 		}
231901d7584cSLiam Girdwood 
2320103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
232101d7584cSLiam Girdwood 				fe->dai_link->name, cmd);
232201d7584cSLiam Girdwood 
232301d7584cSLiam Girdwood 		ret = soc_pcm_trigger(substream, cmd);
232401d7584cSLiam Girdwood 		break;
232507bf84aaSLiam Girdwood 	case SND_SOC_DPCM_TRIGGER_BESPOKE:
232607bf84aaSLiam Girdwood 		/* bespoke trigger() - handles both FE and BEs */
232707bf84aaSLiam Girdwood 
2328103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd %d\n",
232907bf84aaSLiam Girdwood 				fe->dai_link->name, cmd);
233007bf84aaSLiam Girdwood 
233107bf84aaSLiam Girdwood 		ret = soc_pcm_bespoke_trigger(substream, cmd);
233207bf84aaSLiam Girdwood 		if (ret < 0) {
2333103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
233407bf84aaSLiam Girdwood 			goto out;
233507bf84aaSLiam Girdwood 		}
233607bf84aaSLiam Girdwood 		break;
233701d7584cSLiam Girdwood 	default:
2338103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd,
233901d7584cSLiam Girdwood 				fe->dai_link->name);
234001d7584cSLiam Girdwood 		ret = -EINVAL;
234101d7584cSLiam Girdwood 		goto out;
234201d7584cSLiam Girdwood 	}
234301d7584cSLiam Girdwood 
234401d7584cSLiam Girdwood 	switch (cmd) {
234501d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_START:
234601d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_RESUME:
234701d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
234801d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
234901d7584cSLiam Girdwood 		break;
235001d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_STOP:
235101d7584cSLiam Girdwood 	case SNDRV_PCM_TRIGGER_SUSPEND:
235201d7584cSLiam Girdwood 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
235301d7584cSLiam Girdwood 		break;
23549f169b9fSPatrick Lai 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
23559f169b9fSPatrick Lai 		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
23569f169b9fSPatrick Lai 		break;
235701d7584cSLiam Girdwood 	}
235801d7584cSLiam Girdwood 
235901d7584cSLiam Girdwood out:
236001d7584cSLiam Girdwood 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
236101d7584cSLiam Girdwood 	return ret;
236201d7584cSLiam Girdwood }
236301d7584cSLiam Girdwood 
2364ea9d0d77STakashi Iwai static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
2365ea9d0d77STakashi Iwai {
2366ea9d0d77STakashi Iwai 	struct snd_soc_pcm_runtime *fe = substream->private_data;
2367ea9d0d77STakashi Iwai 	int stream = substream->stream;
2368ea9d0d77STakashi Iwai 
2369ea9d0d77STakashi Iwai 	/* if FE's runtime_update is already set, we're in race;
2370ea9d0d77STakashi Iwai 	 * process this trigger later at exit
2371ea9d0d77STakashi Iwai 	 */
2372ea9d0d77STakashi Iwai 	if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) {
2373ea9d0d77STakashi Iwai 		fe->dpcm[stream].trigger_pending = cmd + 1;
2374ea9d0d77STakashi Iwai 		return 0; /* delayed, assuming it's successful */
2375ea9d0d77STakashi Iwai 	}
2376ea9d0d77STakashi Iwai 
2377ea9d0d77STakashi Iwai 	/* we're alone, let's trigger */
2378ea9d0d77STakashi Iwai 	return dpcm_fe_dai_do_trigger(substream, cmd);
2379ea9d0d77STakashi Iwai }
2380ea9d0d77STakashi Iwai 
238123607025SLiam Girdwood int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
238201d7584cSLiam Girdwood {
238301d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
238401d7584cSLiam Girdwood 	int ret = 0;
238501d7584cSLiam Girdwood 
238601d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
238701d7584cSLiam Girdwood 
238801d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
238901d7584cSLiam Girdwood 		struct snd_pcm_substream *be_substream =
239001d7584cSLiam Girdwood 			snd_soc_dpcm_get_substream(be, stream);
239101d7584cSLiam Girdwood 
239201d7584cSLiam Girdwood 		/* is this op for this BE ? */
239301d7584cSLiam Girdwood 		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
239401d7584cSLiam Girdwood 			continue;
239501d7584cSLiam Girdwood 
239601d7584cSLiam Girdwood 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
239795f444dcSKoro Chen 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
239895f444dcSKoro Chen 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
239901d7584cSLiam Girdwood 			continue;
240001d7584cSLiam Girdwood 
2401103d84a3SLiam Girdwood 		dev_dbg(be->dev, "ASoC: prepare BE %s\n",
240294d215ccS彭东林 			be->dai_link->name);
240301d7584cSLiam Girdwood 
240401d7584cSLiam Girdwood 		ret = soc_pcm_prepare(be_substream);
240501d7584cSLiam Girdwood 		if (ret < 0) {
2406103d84a3SLiam Girdwood 			dev_err(be->dev, "ASoC: backend prepare failed %d\n",
240701d7584cSLiam Girdwood 				ret);
240801d7584cSLiam Girdwood 			break;
240901d7584cSLiam Girdwood 		}
241001d7584cSLiam Girdwood 
241101d7584cSLiam Girdwood 		be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
241201d7584cSLiam Girdwood 	}
241301d7584cSLiam Girdwood 	return ret;
241401d7584cSLiam Girdwood }
241501d7584cSLiam Girdwood 
241645c0a188SMark Brown static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
241701d7584cSLiam Girdwood {
241801d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = substream->private_data;
241901d7584cSLiam Girdwood 	int stream = substream->stream, ret = 0;
242001d7584cSLiam Girdwood 
242101d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
242201d7584cSLiam Girdwood 
2423103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
242401d7584cSLiam Girdwood 
2425ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
242601d7584cSLiam Girdwood 
242701d7584cSLiam Girdwood 	/* there is no point preparing this FE if there are no BEs */
242801d7584cSLiam Girdwood 	if (list_empty(&fe->dpcm[stream].be_clients)) {
2429103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: no backend DAIs enabled for %s\n",
243001d7584cSLiam Girdwood 				fe->dai_link->name);
243101d7584cSLiam Girdwood 		ret = -EINVAL;
243201d7584cSLiam Girdwood 		goto out;
243301d7584cSLiam Girdwood 	}
243401d7584cSLiam Girdwood 
243501d7584cSLiam Girdwood 	ret = dpcm_be_dai_prepare(fe, substream->stream);
243601d7584cSLiam Girdwood 	if (ret < 0)
243701d7584cSLiam Girdwood 		goto out;
243801d7584cSLiam Girdwood 
243901d7584cSLiam Girdwood 	/* call prepare on the frontend */
244001d7584cSLiam Girdwood 	ret = soc_pcm_prepare(substream);
244101d7584cSLiam Girdwood 	if (ret < 0) {
2442103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: prepare FE %s failed\n",
244301d7584cSLiam Girdwood 			fe->dai_link->name);
244401d7584cSLiam Girdwood 		goto out;
244501d7584cSLiam Girdwood 	}
244601d7584cSLiam Girdwood 
244701d7584cSLiam Girdwood 	/* run the stream event for each BE */
244801d7584cSLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
244901d7584cSLiam Girdwood 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
245001d7584cSLiam Girdwood 
245101d7584cSLiam Girdwood out:
2452ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
245301d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
245401d7584cSLiam Girdwood 
245501d7584cSLiam Girdwood 	return ret;
245601d7584cSLiam Girdwood }
245701d7584cSLiam Girdwood 
2458be3f3f2cSLiam Girdwood static int soc_pcm_ioctl(struct snd_pcm_substream *substream,
2459be3f3f2cSLiam Girdwood 		     unsigned int cmd, void *arg)
2460be3f3f2cSLiam Girdwood {
2461be3f3f2cSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2462be3f3f2cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
2463b8135864SKuninori Morimoto 	struct snd_soc_component *component;
2464b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
2465be3f3f2cSLiam Girdwood 
2466b8135864SKuninori Morimoto 	if (platform && platform->driver->ops && platform->driver->ops->ioctl)
2467be3f3f2cSLiam Girdwood 		return platform->driver->ops->ioctl(substream, cmd, arg);
2468b8135864SKuninori Morimoto 
2469b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
2470b8135864SKuninori Morimoto 		component = rtdcom->component;
2471b8135864SKuninori Morimoto 
2472b8135864SKuninori Morimoto 		/* ignore duplication for now */
2473b8135864SKuninori Morimoto 		if (platform && (component == &platform->component))
2474b8135864SKuninori Morimoto 			continue;
2475b8135864SKuninori Morimoto 
2476b8135864SKuninori Morimoto 		if (!component->driver->ops ||
2477b8135864SKuninori Morimoto 		    !component->driver->ops->ioctl)
2478b8135864SKuninori Morimoto 			continue;
2479b8135864SKuninori Morimoto 
2480b8135864SKuninori Morimoto 		/* FIXME: use 1st ioctl */
2481b8135864SKuninori Morimoto 		return component->driver->ops->ioctl(substream, cmd, arg);
2482b8135864SKuninori Morimoto 	}
2483b8135864SKuninori Morimoto 
2484be3f3f2cSLiam Girdwood 	return snd_pcm_lib_ioctl(substream, cmd, arg);
2485be3f3f2cSLiam Girdwood }
2486be3f3f2cSLiam Girdwood 
2487618dae11SLiam Girdwood static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
2488618dae11SLiam Girdwood {
248907bf84aaSLiam Girdwood 	struct snd_pcm_substream *substream =
249007bf84aaSLiam Girdwood 		snd_soc_dpcm_get_substream(fe, stream);
249107bf84aaSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
2492618dae11SLiam Girdwood 	int err;
249301d7584cSLiam Girdwood 
2494103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n",
2495618dae11SLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name);
2496618dae11SLiam Girdwood 
249707bf84aaSLiam Girdwood 	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
249807bf84aaSLiam Girdwood 		/* call bespoke trigger - FE takes care of all BE triggers */
2499103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop\n",
250007bf84aaSLiam Girdwood 				fe->dai_link->name);
250107bf84aaSLiam Girdwood 
250207bf84aaSLiam Girdwood 		err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP);
250307bf84aaSLiam Girdwood 		if (err < 0)
2504103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err);
250507bf84aaSLiam Girdwood 	} else {
2506103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop\n",
250707bf84aaSLiam Girdwood 			fe->dai_link->name);
250807bf84aaSLiam Girdwood 
2509618dae11SLiam Girdwood 		err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
2510618dae11SLiam Girdwood 		if (err < 0)
2511103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err);
251207bf84aaSLiam Girdwood 	}
2513618dae11SLiam Girdwood 
2514618dae11SLiam Girdwood 	err = dpcm_be_dai_hw_free(fe, stream);
2515618dae11SLiam Girdwood 	if (err < 0)
2516103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err);
2517618dae11SLiam Girdwood 
2518618dae11SLiam Girdwood 	err = dpcm_be_dai_shutdown(fe, stream);
2519618dae11SLiam Girdwood 	if (err < 0)
2520103d84a3SLiam Girdwood 		dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err);
2521618dae11SLiam Girdwood 
2522618dae11SLiam Girdwood 	/* run the stream event for each BE */
2523618dae11SLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
2524618dae11SLiam Girdwood 
2525618dae11SLiam Girdwood 	return 0;
2526618dae11SLiam Girdwood }
2527618dae11SLiam Girdwood 
2528618dae11SLiam Girdwood static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
2529618dae11SLiam Girdwood {
253007bf84aaSLiam Girdwood 	struct snd_pcm_substream *substream =
253107bf84aaSLiam Girdwood 		snd_soc_dpcm_get_substream(fe, stream);
2532618dae11SLiam Girdwood 	struct snd_soc_dpcm *dpcm;
253307bf84aaSLiam Girdwood 	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
2534618dae11SLiam Girdwood 	int ret;
2535618dae11SLiam Girdwood 
2536103d84a3SLiam Girdwood 	dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n",
2537618dae11SLiam Girdwood 			stream ? "capture" : "playback", fe->dai_link->name);
2538618dae11SLiam Girdwood 
2539618dae11SLiam Girdwood 	/* Only start the BE if the FE is ready */
2540618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE ||
2541618dae11SLiam Girdwood 		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE)
2542618dae11SLiam Girdwood 		return -EINVAL;
2543618dae11SLiam Girdwood 
2544618dae11SLiam Girdwood 	/* startup must always be called for new BEs */
2545618dae11SLiam Girdwood 	ret = dpcm_be_dai_startup(fe, stream);
2546fffc0ca2SDan Carpenter 	if (ret < 0)
2547618dae11SLiam Girdwood 		goto disconnect;
2548618dae11SLiam Girdwood 
2549618dae11SLiam Girdwood 	/* keep going if FE state is > open */
2550618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
2551618dae11SLiam Girdwood 		return 0;
2552618dae11SLiam Girdwood 
2553618dae11SLiam Girdwood 	ret = dpcm_be_dai_hw_params(fe, stream);
2554fffc0ca2SDan Carpenter 	if (ret < 0)
2555618dae11SLiam Girdwood 		goto close;
2556618dae11SLiam Girdwood 
2557618dae11SLiam Girdwood 	/* keep going if FE state is > hw_params */
2558618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
2559618dae11SLiam Girdwood 		return 0;
2560618dae11SLiam Girdwood 
2561618dae11SLiam Girdwood 
2562618dae11SLiam Girdwood 	ret = dpcm_be_dai_prepare(fe, stream);
2563fffc0ca2SDan Carpenter 	if (ret < 0)
2564618dae11SLiam Girdwood 		goto hw_free;
2565618dae11SLiam Girdwood 
2566618dae11SLiam Girdwood 	/* run the stream event for each BE */
2567618dae11SLiam Girdwood 	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
2568618dae11SLiam Girdwood 
2569618dae11SLiam Girdwood 	/* keep going if FE state is > prepare */
2570618dae11SLiam Girdwood 	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE ||
2571618dae11SLiam Girdwood 		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP)
2572618dae11SLiam Girdwood 		return 0;
2573618dae11SLiam Girdwood 
257407bf84aaSLiam Girdwood 	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
257507bf84aaSLiam Girdwood 		/* call trigger on the frontend - FE takes care of all BE triggers */
2576103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start\n",
257707bf84aaSLiam Girdwood 				fe->dai_link->name);
257807bf84aaSLiam Girdwood 
257907bf84aaSLiam Girdwood 		ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START);
258007bf84aaSLiam Girdwood 		if (ret < 0) {
2581103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: bespoke trigger FE failed %d\n", ret);
258207bf84aaSLiam Girdwood 			goto hw_free;
258307bf84aaSLiam Girdwood 		}
258407bf84aaSLiam Girdwood 	} else {
2585103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start\n",
2586618dae11SLiam Girdwood 			fe->dai_link->name);
2587618dae11SLiam Girdwood 
2588618dae11SLiam Girdwood 		ret = dpcm_be_dai_trigger(fe, stream,
2589618dae11SLiam Girdwood 					SNDRV_PCM_TRIGGER_START);
2590618dae11SLiam Girdwood 		if (ret < 0) {
2591103d84a3SLiam Girdwood 			dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
2592618dae11SLiam Girdwood 			goto hw_free;
2593618dae11SLiam Girdwood 		}
259407bf84aaSLiam Girdwood 	}
2595618dae11SLiam Girdwood 
2596618dae11SLiam Girdwood 	return 0;
2597618dae11SLiam Girdwood 
2598618dae11SLiam Girdwood hw_free:
2599618dae11SLiam Girdwood 	dpcm_be_dai_hw_free(fe, stream);
2600618dae11SLiam Girdwood close:
2601618dae11SLiam Girdwood 	dpcm_be_dai_shutdown(fe, stream);
2602618dae11SLiam Girdwood disconnect:
2603618dae11SLiam Girdwood 	/* disconnect any non started BEs */
2604618dae11SLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
2605618dae11SLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
2606618dae11SLiam Girdwood 		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
2607618dae11SLiam Girdwood 				dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
2608618dae11SLiam Girdwood 	}
2609618dae11SLiam Girdwood 
2610618dae11SLiam Girdwood 	return ret;
2611618dae11SLiam Girdwood }
2612618dae11SLiam Girdwood 
2613618dae11SLiam Girdwood static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
2614618dae11SLiam Girdwood {
2615618dae11SLiam Girdwood 	int ret;
2616618dae11SLiam Girdwood 
2617ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
2618618dae11SLiam Girdwood 	ret = dpcm_run_update_startup(fe, stream);
2619618dae11SLiam Girdwood 	if (ret < 0)
2620103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
2621ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
2622618dae11SLiam Girdwood 
2623618dae11SLiam Girdwood 	return ret;
2624618dae11SLiam Girdwood }
2625618dae11SLiam Girdwood 
2626618dae11SLiam Girdwood static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
2627618dae11SLiam Girdwood {
2628618dae11SLiam Girdwood 	int ret;
2629618dae11SLiam Girdwood 
2630ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
2631618dae11SLiam Girdwood 	ret = dpcm_run_update_shutdown(fe, stream);
2632618dae11SLiam Girdwood 	if (ret < 0)
2633103d84a3SLiam Girdwood 		dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
2634ea9d0d77STakashi Iwai 	dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
2635618dae11SLiam Girdwood 
2636618dae11SLiam Girdwood 	return ret;
2637618dae11SLiam Girdwood }
2638618dae11SLiam Girdwood 
2639618dae11SLiam Girdwood /* Called by DAPM mixer/mux changes to update audio routing between PCMs and
2640618dae11SLiam Girdwood  * any DAI links.
2641618dae11SLiam Girdwood  */
2642c3f48ae6SLars-Peter Clausen int soc_dpcm_runtime_update(struct snd_soc_card *card)
2643618dae11SLiam Girdwood {
26441a497983SMengdong Lin 	struct snd_soc_pcm_runtime *fe;
26451a497983SMengdong Lin 	int old, new, paths;
2646618dae11SLiam Girdwood 
2647618dae11SLiam Girdwood 	mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
26481a497983SMengdong Lin 	list_for_each_entry(fe, &card->rtd_list, list) {
2649618dae11SLiam Girdwood 		struct snd_soc_dapm_widget_list *list;
2650618dae11SLiam Girdwood 
2651618dae11SLiam Girdwood 		/* make sure link is FE */
2652618dae11SLiam Girdwood 		if (!fe->dai_link->dynamic)
2653618dae11SLiam Girdwood 			continue;
2654618dae11SLiam Girdwood 
2655618dae11SLiam Girdwood 		/* only check active links */
2656618dae11SLiam Girdwood 		if (!fe->cpu_dai->active)
2657618dae11SLiam Girdwood 			continue;
2658618dae11SLiam Girdwood 
2659618dae11SLiam Girdwood 		/* DAPM sync will call this to update DSP paths */
2660103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s\n",
2661618dae11SLiam Girdwood 			fe->dai_link->name);
2662618dae11SLiam Girdwood 
2663618dae11SLiam Girdwood 		/* skip if FE doesn't have playback capability */
2664075207d2SQiao Zhou 		if (!fe->cpu_dai->driver->playback.channels_min
2665075207d2SQiao Zhou 		    || !fe->codec_dai->driver->playback.channels_min)
2666075207d2SQiao Zhou 			goto capture;
2667075207d2SQiao Zhou 
2668075207d2SQiao Zhou 		/* skip if FE isn't currently playing */
2669075207d2SQiao Zhou 		if (!fe->cpu_dai->playback_active
2670075207d2SQiao Zhou 		    || !fe->codec_dai->playback_active)
2671618dae11SLiam Girdwood 			goto capture;
2672618dae11SLiam Girdwood 
2673618dae11SLiam Girdwood 		paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
2674618dae11SLiam Girdwood 		if (paths < 0) {
2675103d84a3SLiam Girdwood 			dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
2676618dae11SLiam Girdwood 					fe->dai_link->name,  "playback");
2677618dae11SLiam Girdwood 			mutex_unlock(&card->mutex);
2678618dae11SLiam Girdwood 			return paths;
2679618dae11SLiam Girdwood 		}
2680618dae11SLiam Girdwood 
2681618dae11SLiam Girdwood 		/* update any new playback paths */
2682618dae11SLiam Girdwood 		new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1);
2683618dae11SLiam Girdwood 		if (new) {
2684618dae11SLiam Girdwood 			dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
2685618dae11SLiam Girdwood 			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
2686618dae11SLiam Girdwood 			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
2687618dae11SLiam Girdwood 		}
2688618dae11SLiam Girdwood 
2689618dae11SLiam Girdwood 		/* update any old playback paths */
2690618dae11SLiam Girdwood 		old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0);
2691618dae11SLiam Girdwood 		if (old) {
2692618dae11SLiam Girdwood 			dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
2693618dae11SLiam Girdwood 			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
2694618dae11SLiam Girdwood 			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
2695618dae11SLiam Girdwood 		}
2696618dae11SLiam Girdwood 
26977ed9de76SQiao Zhou 		dpcm_path_put(&list);
2698618dae11SLiam Girdwood capture:
2699618dae11SLiam Girdwood 		/* skip if FE doesn't have capture capability */
2700075207d2SQiao Zhou 		if (!fe->cpu_dai->driver->capture.channels_min
2701075207d2SQiao Zhou 		    || !fe->codec_dai->driver->capture.channels_min)
2702075207d2SQiao Zhou 			continue;
2703075207d2SQiao Zhou 
2704075207d2SQiao Zhou 		/* skip if FE isn't currently capturing */
2705075207d2SQiao Zhou 		if (!fe->cpu_dai->capture_active
2706075207d2SQiao Zhou 		    || !fe->codec_dai->capture_active)
2707618dae11SLiam Girdwood 			continue;
2708618dae11SLiam Girdwood 
2709618dae11SLiam Girdwood 		paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
2710618dae11SLiam Girdwood 		if (paths < 0) {
2711103d84a3SLiam Girdwood 			dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
2712618dae11SLiam Girdwood 					fe->dai_link->name,  "capture");
2713618dae11SLiam Girdwood 			mutex_unlock(&card->mutex);
2714618dae11SLiam Girdwood 			return paths;
2715618dae11SLiam Girdwood 		}
2716618dae11SLiam Girdwood 
2717618dae11SLiam Girdwood 		/* update any new capture paths */
2718618dae11SLiam Girdwood 		new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1);
2719618dae11SLiam Girdwood 		if (new) {
2720618dae11SLiam Girdwood 			dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE);
2721618dae11SLiam Girdwood 			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
2722618dae11SLiam Girdwood 			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
2723618dae11SLiam Girdwood 		}
2724618dae11SLiam Girdwood 
2725618dae11SLiam Girdwood 		/* update any old capture paths */
2726618dae11SLiam Girdwood 		old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0);
2727618dae11SLiam Girdwood 		if (old) {
2728618dae11SLiam Girdwood 			dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE);
2729618dae11SLiam Girdwood 			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
2730618dae11SLiam Girdwood 			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
2731618dae11SLiam Girdwood 		}
2732618dae11SLiam Girdwood 
2733618dae11SLiam Girdwood 		dpcm_path_put(&list);
2734618dae11SLiam Girdwood 	}
2735618dae11SLiam Girdwood 
2736618dae11SLiam Girdwood 	mutex_unlock(&card->mutex);
2737618dae11SLiam Girdwood 	return 0;
2738618dae11SLiam Girdwood }
273901d7584cSLiam Girdwood int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
274001d7584cSLiam Girdwood {
274101d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
274201d7584cSLiam Girdwood 	struct list_head *clients =
274301d7584cSLiam Girdwood 		&fe->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients;
274401d7584cSLiam Girdwood 
274501d7584cSLiam Girdwood 	list_for_each_entry(dpcm, clients, list_be) {
274601d7584cSLiam Girdwood 
274701d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
27482e5894d7SBenoit Cousson 		int i;
274901d7584cSLiam Girdwood 
275001d7584cSLiam Girdwood 		if (be->dai_link->ignore_suspend)
275101d7584cSLiam Girdwood 			continue;
275201d7584cSLiam Girdwood 
27532e5894d7SBenoit Cousson 		for (i = 0; i < be->num_codecs; i++) {
27542e5894d7SBenoit Cousson 			struct snd_soc_dai *dai = be->codec_dais[i];
27552e5894d7SBenoit Cousson 			struct snd_soc_dai_driver *drv = dai->driver;
275601d7584cSLiam Girdwood 
27572e5894d7SBenoit Cousson 			dev_dbg(be->dev, "ASoC: BE digital mute %s\n",
27582e5894d7SBenoit Cousson 					 be->dai_link->name);
27592e5894d7SBenoit Cousson 
27602e5894d7SBenoit Cousson 			if (drv->ops && drv->ops->digital_mute &&
27612e5894d7SBenoit Cousson 							dai->playback_active)
276201d7584cSLiam Girdwood 				drv->ops->digital_mute(dai, mute);
276301d7584cSLiam Girdwood 		}
27642e5894d7SBenoit Cousson 	}
276501d7584cSLiam Girdwood 
276601d7584cSLiam Girdwood 	return 0;
276701d7584cSLiam Girdwood }
276801d7584cSLiam Girdwood 
276945c0a188SMark Brown static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
277001d7584cSLiam Girdwood {
277101d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
277201d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
277301d7584cSLiam Girdwood 	struct snd_soc_dapm_widget_list *list;
277401d7584cSLiam Girdwood 	int ret;
277501d7584cSLiam Girdwood 	int stream = fe_substream->stream;
277601d7584cSLiam Girdwood 
277701d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
277801d7584cSLiam Girdwood 	fe->dpcm[stream].runtime = fe_substream->runtime;
277901d7584cSLiam Girdwood 
27808f70e515SQiao Zhou 	ret = dpcm_path_get(fe, stream, &list);
27818f70e515SQiao Zhou 	if (ret < 0) {
27828f70e515SQiao Zhou 		mutex_unlock(&fe->card->mutex);
27838f70e515SQiao Zhou 		return ret;
27848f70e515SQiao Zhou 	} else if (ret == 0) {
2785103d84a3SLiam Girdwood 		dev_dbg(fe->dev, "ASoC: %s no valid %s route\n",
278601d7584cSLiam Girdwood 			fe->dai_link->name, stream ? "capture" : "playback");
278701d7584cSLiam Girdwood 	}
278801d7584cSLiam Girdwood 
278901d7584cSLiam Girdwood 	/* calculate valid and active FE <-> BE dpcms */
279001d7584cSLiam Girdwood 	dpcm_process_paths(fe, stream, &list, 1);
279101d7584cSLiam Girdwood 
279201d7584cSLiam Girdwood 	ret = dpcm_fe_dai_startup(fe_substream);
279301d7584cSLiam Girdwood 	if (ret < 0) {
279401d7584cSLiam Girdwood 		/* clean up all links */
279501d7584cSLiam Girdwood 		list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
279601d7584cSLiam Girdwood 			dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
279701d7584cSLiam Girdwood 
279801d7584cSLiam Girdwood 		dpcm_be_disconnect(fe, stream);
279901d7584cSLiam Girdwood 		fe->dpcm[stream].runtime = NULL;
280001d7584cSLiam Girdwood 	}
280101d7584cSLiam Girdwood 
280201d7584cSLiam Girdwood 	dpcm_clear_pending_state(fe, stream);
280301d7584cSLiam Girdwood 	dpcm_path_put(&list);
280401d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
280501d7584cSLiam Girdwood 	return ret;
280601d7584cSLiam Girdwood }
280701d7584cSLiam Girdwood 
280845c0a188SMark Brown static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
280901d7584cSLiam Girdwood {
281001d7584cSLiam Girdwood 	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
281101d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
281201d7584cSLiam Girdwood 	int stream = fe_substream->stream, ret;
281301d7584cSLiam Girdwood 
281401d7584cSLiam Girdwood 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
281501d7584cSLiam Girdwood 	ret = dpcm_fe_dai_shutdown(fe_substream);
281601d7584cSLiam Girdwood 
281701d7584cSLiam Girdwood 	/* mark FE's links ready to prune */
281801d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
281901d7584cSLiam Girdwood 		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
282001d7584cSLiam Girdwood 
282101d7584cSLiam Girdwood 	dpcm_be_disconnect(fe, stream);
282201d7584cSLiam Girdwood 
282301d7584cSLiam Girdwood 	fe->dpcm[stream].runtime = NULL;
282401d7584cSLiam Girdwood 	mutex_unlock(&fe->card->mutex);
282501d7584cSLiam Girdwood 	return ret;
282601d7584cSLiam Girdwood }
282701d7584cSLiam Girdwood 
28285d61f0baSTakashi Iwai static void soc_pcm_private_free(struct snd_pcm *pcm)
28295d61f0baSTakashi Iwai {
28305d61f0baSTakashi Iwai 	struct snd_soc_pcm_runtime *rtd = pcm->private_data;
2831f523acebSKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
2832f523acebSKuninori Morimoto 	struct snd_soc_component *component;
28335d61f0baSTakashi Iwai 
2834f523acebSKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
28355d61f0baSTakashi Iwai 		/* need to sync the delayed work before releasing resources */
2836f523acebSKuninori Morimoto 
28375d61f0baSTakashi Iwai 		flush_delayed_work(&rtd->delayed_work);
2838f523acebSKuninori Morimoto 		component = rtdcom->component;
2839f523acebSKuninori Morimoto 
2840f523acebSKuninori Morimoto 		if (component->pcm_free)
2841f523acebSKuninori Morimoto 			component->pcm_free(component, pcm);
2842f523acebSKuninori Morimoto 	}
28435d61f0baSTakashi Iwai }
28445d61f0baSTakashi Iwai 
2845b8135864SKuninori Morimoto static int soc_rtdcom_ack(struct snd_pcm_substream *substream)
2846b8135864SKuninori Morimoto {
2847b8135864SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2848b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
2849b8135864SKuninori Morimoto 	struct snd_soc_component *component;
2850b8135864SKuninori Morimoto 
2851b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
2852b8135864SKuninori Morimoto 		component = rtdcom->component;
2853b8135864SKuninori Morimoto 
2854b8135864SKuninori Morimoto 		if (!component->driver->ops ||
2855b8135864SKuninori Morimoto 		    !component->driver->ops->ack)
2856b8135864SKuninori Morimoto 			continue;
2857b8135864SKuninori Morimoto 
2858b8135864SKuninori Morimoto 		/* FIXME. it returns 1st ask now */
2859b8135864SKuninori Morimoto 		return component->driver->ops->ack(substream);
2860b8135864SKuninori Morimoto 	}
2861b8135864SKuninori Morimoto 
2862b8135864SKuninori Morimoto 	return -EINVAL;
2863b8135864SKuninori Morimoto }
2864b8135864SKuninori Morimoto 
2865b8135864SKuninori Morimoto static int soc_rtdcom_copy_user(struct snd_pcm_substream *substream, int channel,
2866b8135864SKuninori Morimoto 				unsigned long pos, void __user *buf,
2867b8135864SKuninori Morimoto 				unsigned long bytes)
2868b8135864SKuninori Morimoto {
2869b8135864SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2870b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
2871b8135864SKuninori Morimoto 	struct snd_soc_component *component;
2872b8135864SKuninori Morimoto 
2873b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
2874b8135864SKuninori Morimoto 		component = rtdcom->component;
2875b8135864SKuninori Morimoto 
2876b8135864SKuninori Morimoto 		if (!component->driver->ops ||
2877b8135864SKuninori Morimoto 		    !component->driver->ops->copy_user)
2878b8135864SKuninori Morimoto 			continue;
2879b8135864SKuninori Morimoto 
2880b8135864SKuninori Morimoto 		/* FIXME. it returns 1st copy now */
2881b8135864SKuninori Morimoto 		return component->driver->ops->copy_user(substream, channel,
2882b8135864SKuninori Morimoto 							 pos, buf, bytes);
2883b8135864SKuninori Morimoto 	}
2884b8135864SKuninori Morimoto 
2885b8135864SKuninori Morimoto 	return -EINVAL;
2886b8135864SKuninori Morimoto }
2887b8135864SKuninori Morimoto 
2888b8135864SKuninori Morimoto static int soc_rtdcom_copy_kernel(struct snd_pcm_substream *substream, int channel,
2889b8135864SKuninori Morimoto 				  unsigned long pos, void *buf, unsigned long bytes)
2890b8135864SKuninori Morimoto {
2891b8135864SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2892b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
2893b8135864SKuninori Morimoto 	struct snd_soc_component *component;
2894b8135864SKuninori Morimoto 
2895b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
2896b8135864SKuninori Morimoto 		component = rtdcom->component;
2897b8135864SKuninori Morimoto 
2898b8135864SKuninori Morimoto 		if (!component->driver->ops ||
2899b8135864SKuninori Morimoto 		    !component->driver->ops->copy_kernel)
2900b8135864SKuninori Morimoto 			continue;
2901b8135864SKuninori Morimoto 
2902b8135864SKuninori Morimoto 		/* FIXME. it returns 1st copy now */
2903b8135864SKuninori Morimoto 		return component->driver->ops->copy_kernel(substream, channel,
2904b8135864SKuninori Morimoto 							   pos, buf, bytes);
2905b8135864SKuninori Morimoto 	}
2906b8135864SKuninori Morimoto 
2907b8135864SKuninori Morimoto 	return -EINVAL;
2908b8135864SKuninori Morimoto }
2909b8135864SKuninori Morimoto 
2910b8135864SKuninori Morimoto static int soc_rtdcom_fill_silence(struct snd_pcm_substream *substream, int channel,
2911b8135864SKuninori Morimoto 				   unsigned long pos, unsigned long bytes)
2912b8135864SKuninori Morimoto {
2913b8135864SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2914b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
2915b8135864SKuninori Morimoto 	struct snd_soc_component *component;
2916b8135864SKuninori Morimoto 
2917b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
2918b8135864SKuninori Morimoto 		component = rtdcom->component;
2919b8135864SKuninori Morimoto 
2920b8135864SKuninori Morimoto 		if (!component->driver->ops ||
2921b8135864SKuninori Morimoto 		    !component->driver->ops->fill_silence)
2922b8135864SKuninori Morimoto 			continue;
2923b8135864SKuninori Morimoto 
2924b8135864SKuninori Morimoto 		/* FIXME. it returns 1st silence now */
2925b8135864SKuninori Morimoto 		return component->driver->ops->fill_silence(substream, channel,
2926b8135864SKuninori Morimoto 							    pos, bytes);
2927b8135864SKuninori Morimoto 	}
2928b8135864SKuninori Morimoto 
2929b8135864SKuninori Morimoto 	return -EINVAL;
2930b8135864SKuninori Morimoto }
2931b8135864SKuninori Morimoto 
2932b8135864SKuninori Morimoto static struct page *soc_rtdcom_page(struct snd_pcm_substream *substream,
2933b8135864SKuninori Morimoto 				    unsigned long offset)
2934b8135864SKuninori Morimoto {
2935b8135864SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2936b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
2937b8135864SKuninori Morimoto 	struct snd_soc_component *component;
2938b8135864SKuninori Morimoto 	struct page *page;
2939b8135864SKuninori Morimoto 
2940b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
2941b8135864SKuninori Morimoto 		component = rtdcom->component;
2942b8135864SKuninori Morimoto 
2943b8135864SKuninori Morimoto 		if (!component->driver->ops ||
2944b8135864SKuninori Morimoto 		    !component->driver->ops->page)
2945b8135864SKuninori Morimoto 			continue;
2946b8135864SKuninori Morimoto 
2947b8135864SKuninori Morimoto 		/* FIXME. it returns 1st page now */
2948b8135864SKuninori Morimoto 		page = component->driver->ops->page(substream, offset);
2949b8135864SKuninori Morimoto 		if (page)
2950b8135864SKuninori Morimoto 			return page;
2951b8135864SKuninori Morimoto 	}
2952b8135864SKuninori Morimoto 
2953b8135864SKuninori Morimoto 	return NULL;
2954b8135864SKuninori Morimoto }
2955b8135864SKuninori Morimoto 
2956b8135864SKuninori Morimoto static int soc_rtdcom_mmap(struct snd_pcm_substream *substream,
2957b8135864SKuninori Morimoto 			   struct vm_area_struct *vma)
2958b8135864SKuninori Morimoto {
2959b8135864SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2960b8135864SKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
2961b8135864SKuninori Morimoto 	struct snd_soc_component *component;
2962b8135864SKuninori Morimoto 
2963b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
2964b8135864SKuninori Morimoto 		component = rtdcom->component;
2965b8135864SKuninori Morimoto 
2966b8135864SKuninori Morimoto 		if (!component->driver->ops ||
2967b8135864SKuninori Morimoto 		    !component->driver->ops->mmap)
2968b8135864SKuninori Morimoto 			continue;
2969b8135864SKuninori Morimoto 
2970b8135864SKuninori Morimoto 		/* FIXME. it returns 1st mmap now */
2971b8135864SKuninori Morimoto 		return component->driver->ops->mmap(substream, vma);
2972b8135864SKuninori Morimoto 	}
2973b8135864SKuninori Morimoto 
2974b8135864SKuninori Morimoto 	return -EINVAL;
2975b8135864SKuninori Morimoto }
2976b8135864SKuninori Morimoto 
2977ddee627cSLiam Girdwood /* create a new pcm */
2978ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
2979ddee627cSLiam Girdwood {
2980ddee627cSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
29812e5894d7SBenoit Cousson 	struct snd_soc_dai *codec_dai;
2982ddee627cSLiam Girdwood 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
2983f523acebSKuninori Morimoto 	struct snd_soc_component *component;
2984f523acebSKuninori Morimoto 	struct snd_soc_rtdcom_list *rtdcom;
2985ddee627cSLiam Girdwood 	struct snd_pcm *pcm;
2986ddee627cSLiam Girdwood 	char new_name[64];
2987ddee627cSLiam Girdwood 	int ret = 0, playback = 0, capture = 0;
29882e5894d7SBenoit Cousson 	int i;
2989ddee627cSLiam Girdwood 
299001d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
29911e9de42fSLiam Girdwood 		playback = rtd->dai_link->dpcm_playback;
29921e9de42fSLiam Girdwood 		capture = rtd->dai_link->dpcm_capture;
299301d7584cSLiam Girdwood 	} else {
29942e5894d7SBenoit Cousson 		for (i = 0; i < rtd->num_codecs; i++) {
29952e5894d7SBenoit Cousson 			codec_dai = rtd->codec_dais[i];
29962e5894d7SBenoit Cousson 			if (codec_dai->driver->playback.channels_min)
2997ddee627cSLiam Girdwood 				playback = 1;
29982e5894d7SBenoit Cousson 			if (codec_dai->driver->capture.channels_min)
2999ddee627cSLiam Girdwood 				capture = 1;
300001d7584cSLiam Girdwood 		}
3001ddee627cSLiam Girdwood 
30022e5894d7SBenoit Cousson 		capture = capture && cpu_dai->driver->capture.channels_min;
30032e5894d7SBenoit Cousson 		playback = playback && cpu_dai->driver->playback.channels_min;
30042e5894d7SBenoit Cousson 	}
30052e5894d7SBenoit Cousson 
3006d6bead02SFabio Estevam 	if (rtd->dai_link->playback_only) {
3007d6bead02SFabio Estevam 		playback = 1;
3008d6bead02SFabio Estevam 		capture = 0;
3009d6bead02SFabio Estevam 	}
3010d6bead02SFabio Estevam 
3011d6bead02SFabio Estevam 	if (rtd->dai_link->capture_only) {
3012d6bead02SFabio Estevam 		playback = 0;
3013d6bead02SFabio Estevam 		capture = 1;
3014d6bead02SFabio Estevam 	}
3015d6bead02SFabio Estevam 
301601d7584cSLiam Girdwood 	/* create the PCM */
301701d7584cSLiam Girdwood 	if (rtd->dai_link->no_pcm) {
301801d7584cSLiam Girdwood 		snprintf(new_name, sizeof(new_name), "(%s)",
301901d7584cSLiam Girdwood 			rtd->dai_link->stream_name);
302001d7584cSLiam Girdwood 
302101d7584cSLiam Girdwood 		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
302201d7584cSLiam Girdwood 				playback, capture, &pcm);
302301d7584cSLiam Girdwood 	} else {
302401d7584cSLiam Girdwood 		if (rtd->dai_link->dynamic)
302501d7584cSLiam Girdwood 			snprintf(new_name, sizeof(new_name), "%s (*)",
302601d7584cSLiam Girdwood 				rtd->dai_link->stream_name);
302701d7584cSLiam Girdwood 		else
302801d7584cSLiam Girdwood 			snprintf(new_name, sizeof(new_name), "%s %s-%d",
30292e5894d7SBenoit Cousson 				rtd->dai_link->stream_name,
30302e5894d7SBenoit Cousson 				(rtd->num_codecs > 1) ?
30312e5894d7SBenoit Cousson 				"multicodec" : rtd->codec_dai->name, num);
303201d7584cSLiam Girdwood 
303301d7584cSLiam Girdwood 		ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
303401d7584cSLiam Girdwood 			capture, &pcm);
303501d7584cSLiam Girdwood 	}
3036ddee627cSLiam Girdwood 	if (ret < 0) {
3037103d84a3SLiam Girdwood 		dev_err(rtd->card->dev, "ASoC: can't create pcm for %s\n",
30385cb9b748SLiam Girdwood 			rtd->dai_link->name);
3039ddee627cSLiam Girdwood 		return ret;
3040ddee627cSLiam Girdwood 	}
3041103d84a3SLiam Girdwood 	dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name);
3042ddee627cSLiam Girdwood 
3043ddee627cSLiam Girdwood 	/* DAPM dai link stream work */
3044ddee627cSLiam Girdwood 	INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
3045ddee627cSLiam Girdwood 
304648c7699fSVinod Koul 	pcm->nonatomic = rtd->dai_link->nonatomic;
3047ddee627cSLiam Girdwood 	rtd->pcm = pcm;
3048ddee627cSLiam Girdwood 	pcm->private_data = rtd;
304901d7584cSLiam Girdwood 
305001d7584cSLiam Girdwood 	if (rtd->dai_link->no_pcm) {
305101d7584cSLiam Girdwood 		if (playback)
305201d7584cSLiam Girdwood 			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
305301d7584cSLiam Girdwood 		if (capture)
305401d7584cSLiam Girdwood 			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
305501d7584cSLiam Girdwood 		goto out;
305601d7584cSLiam Girdwood 	}
305701d7584cSLiam Girdwood 
305801d7584cSLiam Girdwood 	/* ASoC PCM operations */
305901d7584cSLiam Girdwood 	if (rtd->dai_link->dynamic) {
306001d7584cSLiam Girdwood 		rtd->ops.open		= dpcm_fe_dai_open;
306101d7584cSLiam Girdwood 		rtd->ops.hw_params	= dpcm_fe_dai_hw_params;
306201d7584cSLiam Girdwood 		rtd->ops.prepare	= dpcm_fe_dai_prepare;
306301d7584cSLiam Girdwood 		rtd->ops.trigger	= dpcm_fe_dai_trigger;
306401d7584cSLiam Girdwood 		rtd->ops.hw_free	= dpcm_fe_dai_hw_free;
306501d7584cSLiam Girdwood 		rtd->ops.close		= dpcm_fe_dai_close;
306601d7584cSLiam Girdwood 		rtd->ops.pointer	= soc_pcm_pointer;
3067be3f3f2cSLiam Girdwood 		rtd->ops.ioctl		= soc_pcm_ioctl;
306801d7584cSLiam Girdwood 	} else {
306901d7584cSLiam Girdwood 		rtd->ops.open		= soc_pcm_open;
307001d7584cSLiam Girdwood 		rtd->ops.hw_params	= soc_pcm_hw_params;
307101d7584cSLiam Girdwood 		rtd->ops.prepare	= soc_pcm_prepare;
307201d7584cSLiam Girdwood 		rtd->ops.trigger	= soc_pcm_trigger;
307301d7584cSLiam Girdwood 		rtd->ops.hw_free	= soc_pcm_hw_free;
307401d7584cSLiam Girdwood 		rtd->ops.close		= soc_pcm_close;
307501d7584cSLiam Girdwood 		rtd->ops.pointer	= soc_pcm_pointer;
3076be3f3f2cSLiam Girdwood 		rtd->ops.ioctl		= soc_pcm_ioctl;
307701d7584cSLiam Girdwood 	}
307801d7584cSLiam Girdwood 
3079b8135864SKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
3080b8135864SKuninori Morimoto 		const struct snd_pcm_ops *ops = rtdcom->component->driver->ops;
3081b8135864SKuninori Morimoto 
3082b8135864SKuninori Morimoto 		if (!ops)
3083b8135864SKuninori Morimoto 			continue;
3084b8135864SKuninori Morimoto 
3085b8135864SKuninori Morimoto 		if (ops->ack)
3086b8135864SKuninori Morimoto 			rtd->ops.ack		= soc_rtdcom_ack;
3087b8135864SKuninori Morimoto 		if (ops->copy_user)
3088b8135864SKuninori Morimoto 			rtd->ops.copy_user	= soc_rtdcom_copy_user;
3089b8135864SKuninori Morimoto 		if (ops->copy_kernel)
3090b8135864SKuninori Morimoto 			rtd->ops.copy_kernel	= soc_rtdcom_copy_kernel;
3091b8135864SKuninori Morimoto 		if (ops->fill_silence)
3092b8135864SKuninori Morimoto 			rtd->ops.fill_silence	= soc_rtdcom_fill_silence;
3093b8135864SKuninori Morimoto 		if (ops->page)
3094b8135864SKuninori Morimoto 			rtd->ops.page		= soc_rtdcom_page;
3095b8135864SKuninori Morimoto 		if (ops->mmap)
3096b8135864SKuninori Morimoto 			rtd->ops.mmap		= soc_rtdcom_mmap;
3097b8135864SKuninori Morimoto 	}
3098b8135864SKuninori Morimoto 
3099b8135864SKuninori Morimoto 	/* overwrite */
3100b8135864SKuninori Morimoto 	if (platform && platform->driver->ops) {
310101d7584cSLiam Girdwood 		rtd->ops.ack		= platform->driver->ops->ack;
310229d1a873STakashi Iwai 		rtd->ops.copy_user	= platform->driver->ops->copy_user;
310329d1a873STakashi Iwai 		rtd->ops.copy_kernel	= platform->driver->ops->copy_kernel;
310429d1a873STakashi Iwai 		rtd->ops.fill_silence	= platform->driver->ops->fill_silence;
310501d7584cSLiam Girdwood 		rtd->ops.page		= platform->driver->ops->page;
310601d7584cSLiam Girdwood 		rtd->ops.mmap		= platform->driver->ops->mmap;
3107ddee627cSLiam Girdwood 	}
3108ddee627cSLiam Girdwood 
3109ddee627cSLiam Girdwood 	if (playback)
311001d7584cSLiam Girdwood 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
3111ddee627cSLiam Girdwood 
3112ddee627cSLiam Girdwood 	if (capture)
311301d7584cSLiam Girdwood 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
3114ddee627cSLiam Girdwood 
3115f523acebSKuninori Morimoto 	for_each_rtdcom(rtd, rtdcom) {
3116f523acebSKuninori Morimoto 		component = rtdcom->component;
3117f523acebSKuninori Morimoto 
3118f523acebSKuninori Morimoto 		if (!component->pcm_new)
3119f523acebSKuninori Morimoto 			continue;
3120f523acebSKuninori Morimoto 
3121f523acebSKuninori Morimoto 		ret = component->pcm_new(component, rtd);
3122ddee627cSLiam Girdwood 		if (ret < 0) {
3123f523acebSKuninori Morimoto 			dev_err(component->dev,
3124b1bc7b3cSMark Brown 				"ASoC: pcm constructor failed: %d\n",
3125b1bc7b3cSMark Brown 				ret);
3126ddee627cSLiam Girdwood 			return ret;
3127ddee627cSLiam Girdwood 		}
3128ddee627cSLiam Girdwood 	}
3129c641e5b2SJohan Hovold 
31305d61f0baSTakashi Iwai 	pcm->private_free = soc_pcm_private_free;
313101d7584cSLiam Girdwood out:
31322e5894d7SBenoit Cousson 	dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
31332e5894d7SBenoit Cousson 		 (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
3134ddee627cSLiam Girdwood 		 cpu_dai->name);
3135ddee627cSLiam Girdwood 	return ret;
3136ddee627cSLiam Girdwood }
313701d7584cSLiam Girdwood 
313801d7584cSLiam Girdwood /* is the current PCM operation for this FE ? */
313901d7584cSLiam Girdwood int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream)
314001d7584cSLiam Girdwood {
314101d7584cSLiam Girdwood 	if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE)
314201d7584cSLiam Girdwood 		return 1;
314301d7584cSLiam Girdwood 	return 0;
314401d7584cSLiam Girdwood }
314501d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update);
314601d7584cSLiam Girdwood 
314701d7584cSLiam Girdwood /* is the current PCM operation for this BE ? */
314801d7584cSLiam Girdwood int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
314901d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
315001d7584cSLiam Girdwood {
315101d7584cSLiam Girdwood 	if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) ||
315201d7584cSLiam Girdwood 	   ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) &&
315301d7584cSLiam Girdwood 		  be->dpcm[stream].runtime_update))
315401d7584cSLiam Girdwood 		return 1;
315501d7584cSLiam Girdwood 	return 0;
315601d7584cSLiam Girdwood }
315701d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update);
315801d7584cSLiam Girdwood 
315901d7584cSLiam Girdwood /* get the substream for this BE */
316001d7584cSLiam Girdwood struct snd_pcm_substream *
316101d7584cSLiam Girdwood 	snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream)
316201d7584cSLiam Girdwood {
316301d7584cSLiam Girdwood 	return be->pcm->streams[stream].substream;
316401d7584cSLiam Girdwood }
316501d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream);
316601d7584cSLiam Girdwood 
316701d7584cSLiam Girdwood /* get the BE runtime state */
316801d7584cSLiam Girdwood enum snd_soc_dpcm_state
316901d7584cSLiam Girdwood 	snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream)
317001d7584cSLiam Girdwood {
317101d7584cSLiam Girdwood 	return be->dpcm[stream].state;
317201d7584cSLiam Girdwood }
317301d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state);
317401d7584cSLiam Girdwood 
317501d7584cSLiam Girdwood /* set the BE runtime state */
317601d7584cSLiam Girdwood void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be,
317701d7584cSLiam Girdwood 		int stream, enum snd_soc_dpcm_state state)
317801d7584cSLiam Girdwood {
317901d7584cSLiam Girdwood 	be->dpcm[stream].state = state;
318001d7584cSLiam Girdwood }
318101d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state);
318201d7584cSLiam Girdwood 
318301d7584cSLiam Girdwood /*
318401d7584cSLiam Girdwood  * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE
318501d7584cSLiam Girdwood  * are not running, paused or suspended for the specified stream direction.
318601d7584cSLiam Girdwood  */
318701d7584cSLiam Girdwood int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
318801d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
318901d7584cSLiam Girdwood {
319001d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
319101d7584cSLiam Girdwood 	int state;
319201d7584cSLiam Girdwood 
319301d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
319401d7584cSLiam Girdwood 
319501d7584cSLiam Girdwood 		if (dpcm->fe == fe)
319601d7584cSLiam Girdwood 			continue;
319701d7584cSLiam Girdwood 
319801d7584cSLiam Girdwood 		state = dpcm->fe->dpcm[stream].state;
319901d7584cSLiam Girdwood 		if (state == SND_SOC_DPCM_STATE_START ||
320001d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_PAUSED ||
320101d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_SUSPEND)
320201d7584cSLiam Girdwood 			return 0;
320301d7584cSLiam Girdwood 	}
320401d7584cSLiam Girdwood 
320501d7584cSLiam Girdwood 	/* it's safe to free/stop this BE DAI */
320601d7584cSLiam Girdwood 	return 1;
320701d7584cSLiam Girdwood }
320801d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
320901d7584cSLiam Girdwood 
321001d7584cSLiam Girdwood /*
321101d7584cSLiam Girdwood  * We can only change hw params a BE DAI if any of it's FE are not prepared,
321201d7584cSLiam Girdwood  * running, paused or suspended for the specified stream direction.
321301d7584cSLiam Girdwood  */
321401d7584cSLiam Girdwood int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
321501d7584cSLiam Girdwood 		struct snd_soc_pcm_runtime *be, int stream)
321601d7584cSLiam Girdwood {
321701d7584cSLiam Girdwood 	struct snd_soc_dpcm *dpcm;
321801d7584cSLiam Girdwood 	int state;
321901d7584cSLiam Girdwood 
322001d7584cSLiam Girdwood 	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
322101d7584cSLiam Girdwood 
322201d7584cSLiam Girdwood 		if (dpcm->fe == fe)
322301d7584cSLiam Girdwood 			continue;
322401d7584cSLiam Girdwood 
322501d7584cSLiam Girdwood 		state = dpcm->fe->dpcm[stream].state;
322601d7584cSLiam Girdwood 		if (state == SND_SOC_DPCM_STATE_START ||
322701d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_PAUSED ||
322801d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_SUSPEND ||
322901d7584cSLiam Girdwood 			state == SND_SOC_DPCM_STATE_PREPARE)
323001d7584cSLiam Girdwood 			return 0;
323101d7584cSLiam Girdwood 	}
323201d7584cSLiam Girdwood 
323301d7584cSLiam Girdwood 	/* it's safe to change hw_params */
323401d7584cSLiam Girdwood 	return 1;
323501d7584cSLiam Girdwood }
323601d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
3237f86dcef8SLiam Girdwood 
3238f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS
323985280141SLars-Peter Clausen static const char *dpcm_state_string(enum snd_soc_dpcm_state state)
3240f86dcef8SLiam Girdwood {
3241f86dcef8SLiam Girdwood 	switch (state) {
3242f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_NEW:
3243f86dcef8SLiam Girdwood 		return "new";
3244f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_OPEN:
3245f86dcef8SLiam Girdwood 		return "open";
3246f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_HW_PARAMS:
3247f86dcef8SLiam Girdwood 		return "hw_params";
3248f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_PREPARE:
3249f86dcef8SLiam Girdwood 		return "prepare";
3250f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_START:
3251f86dcef8SLiam Girdwood 		return "start";
3252f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_STOP:
3253f86dcef8SLiam Girdwood 		return "stop";
3254f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_SUSPEND:
3255f86dcef8SLiam Girdwood 		return "suspend";
3256f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_PAUSED:
3257f86dcef8SLiam Girdwood 		return "paused";
3258f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_HW_FREE:
3259f86dcef8SLiam Girdwood 		return "hw_free";
3260f86dcef8SLiam Girdwood 	case SND_SOC_DPCM_STATE_CLOSE:
3261f86dcef8SLiam Girdwood 		return "close";
3262f86dcef8SLiam Girdwood 	}
3263f86dcef8SLiam Girdwood 
3264f86dcef8SLiam Girdwood 	return "unknown";
3265f86dcef8SLiam Girdwood }
3266f86dcef8SLiam Girdwood 
3267f86dcef8SLiam Girdwood static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
3268f86dcef8SLiam Girdwood 				int stream, char *buf, size_t size)
3269f86dcef8SLiam Girdwood {
3270f86dcef8SLiam Girdwood 	struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
3271f86dcef8SLiam Girdwood 	struct snd_soc_dpcm *dpcm;
3272f86dcef8SLiam Girdwood 	ssize_t offset = 0;
3273f86dcef8SLiam Girdwood 
3274f86dcef8SLiam Girdwood 	/* FE state */
3275f86dcef8SLiam Girdwood 	offset += snprintf(buf + offset, size - offset,
3276f86dcef8SLiam Girdwood 			"[%s - %s]\n", fe->dai_link->name,
3277f86dcef8SLiam Girdwood 			stream ? "Capture" : "Playback");
3278f86dcef8SLiam Girdwood 
3279f86dcef8SLiam Girdwood 	offset += snprintf(buf + offset, size - offset, "State: %s\n",
3280f86dcef8SLiam Girdwood 	                dpcm_state_string(fe->dpcm[stream].state));
3281f86dcef8SLiam Girdwood 
3282f86dcef8SLiam Girdwood 	if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
3283f86dcef8SLiam Girdwood 	    (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
3284f86dcef8SLiam Girdwood 		offset += snprintf(buf + offset, size - offset,
3285f86dcef8SLiam Girdwood 				"Hardware Params: "
3286f86dcef8SLiam Girdwood 				"Format = %s, Channels = %d, Rate = %d\n",
3287f86dcef8SLiam Girdwood 				snd_pcm_format_name(params_format(params)),
3288f86dcef8SLiam Girdwood 				params_channels(params),
3289f86dcef8SLiam Girdwood 				params_rate(params));
3290f86dcef8SLiam Girdwood 
3291f86dcef8SLiam Girdwood 	/* BEs state */
3292f86dcef8SLiam Girdwood 	offset += snprintf(buf + offset, size - offset, "Backends:\n");
3293f86dcef8SLiam Girdwood 
3294f86dcef8SLiam Girdwood 	if (list_empty(&fe->dpcm[stream].be_clients)) {
3295f86dcef8SLiam Girdwood 		offset += snprintf(buf + offset, size - offset,
3296f86dcef8SLiam Girdwood 				" No active DSP links\n");
3297f86dcef8SLiam Girdwood 		goto out;
3298f86dcef8SLiam Girdwood 	}
3299f86dcef8SLiam Girdwood 
3300f86dcef8SLiam Girdwood 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
3301f86dcef8SLiam Girdwood 		struct snd_soc_pcm_runtime *be = dpcm->be;
3302f86dcef8SLiam Girdwood 		params = &dpcm->hw_params;
3303f86dcef8SLiam Girdwood 
3304f86dcef8SLiam Girdwood 		offset += snprintf(buf + offset, size - offset,
3305f86dcef8SLiam Girdwood 				"- %s\n", be->dai_link->name);
3306f86dcef8SLiam Girdwood 
3307f86dcef8SLiam Girdwood 		offset += snprintf(buf + offset, size - offset,
3308f86dcef8SLiam Girdwood 				"   State: %s\n",
3309f86dcef8SLiam Girdwood 				dpcm_state_string(be->dpcm[stream].state));
3310f86dcef8SLiam Girdwood 
3311f86dcef8SLiam Girdwood 		if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
3312f86dcef8SLiam Girdwood 		    (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
3313f86dcef8SLiam Girdwood 			offset += snprintf(buf + offset, size - offset,
3314f86dcef8SLiam Girdwood 				"   Hardware Params: "
3315f86dcef8SLiam Girdwood 				"Format = %s, Channels = %d, Rate = %d\n",
3316f86dcef8SLiam Girdwood 				snd_pcm_format_name(params_format(params)),
3317f86dcef8SLiam Girdwood 				params_channels(params),
3318f86dcef8SLiam Girdwood 				params_rate(params));
3319f86dcef8SLiam Girdwood 	}
3320f86dcef8SLiam Girdwood 
3321f86dcef8SLiam Girdwood out:
3322f86dcef8SLiam Girdwood 	return offset;
3323f86dcef8SLiam Girdwood }
3324f86dcef8SLiam Girdwood 
3325f86dcef8SLiam Girdwood static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
3326f86dcef8SLiam Girdwood 				size_t count, loff_t *ppos)
3327f86dcef8SLiam Girdwood {
3328f86dcef8SLiam Girdwood 	struct snd_soc_pcm_runtime *fe = file->private_data;
3329f86dcef8SLiam Girdwood 	ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
3330f86dcef8SLiam Girdwood 	char *buf;
3331f86dcef8SLiam Girdwood 
3332f86dcef8SLiam Girdwood 	buf = kmalloc(out_count, GFP_KERNEL);
3333f86dcef8SLiam Girdwood 	if (!buf)
3334f86dcef8SLiam Girdwood 		return -ENOMEM;
3335f86dcef8SLiam Girdwood 
3336f86dcef8SLiam Girdwood 	if (fe->cpu_dai->driver->playback.channels_min)
3337f86dcef8SLiam Girdwood 		offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK,
3338f86dcef8SLiam Girdwood 					buf + offset, out_count - offset);
3339f86dcef8SLiam Girdwood 
3340f86dcef8SLiam Girdwood 	if (fe->cpu_dai->driver->capture.channels_min)
3341f86dcef8SLiam Girdwood 		offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE,
3342f86dcef8SLiam Girdwood 					buf + offset, out_count - offset);
3343f86dcef8SLiam Girdwood 
3344f86dcef8SLiam Girdwood 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
3345f86dcef8SLiam Girdwood 
3346f86dcef8SLiam Girdwood 	kfree(buf);
3347f86dcef8SLiam Girdwood 	return ret;
3348f86dcef8SLiam Girdwood }
3349f86dcef8SLiam Girdwood 
3350f86dcef8SLiam Girdwood static const struct file_operations dpcm_state_fops = {
3351f57b8488SLiam Girdwood 	.open = simple_open,
3352f86dcef8SLiam Girdwood 	.read = dpcm_state_read_file,
3353f86dcef8SLiam Girdwood 	.llseek = default_llseek,
3354f86dcef8SLiam Girdwood };
3355f86dcef8SLiam Girdwood 
33562e55b90aSLars-Peter Clausen void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
3357f86dcef8SLiam Girdwood {
3358b3bba9a1SMark Brown 	if (!rtd->dai_link)
33592e55b90aSLars-Peter Clausen 		return;
3360b3bba9a1SMark Brown 
33616553bf06SLars-Peter Clausen 	if (!rtd->card->debugfs_card_root)
33626553bf06SLars-Peter Clausen 		return;
3363f86dcef8SLiam Girdwood 
3364f86dcef8SLiam Girdwood 	rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
3365f86dcef8SLiam Girdwood 			rtd->card->debugfs_card_root);
3366f86dcef8SLiam Girdwood 	if (!rtd->debugfs_dpcm_root) {
3367f86dcef8SLiam Girdwood 		dev_dbg(rtd->dev,
3368f86dcef8SLiam Girdwood 			 "ASoC: Failed to create dpcm debugfs directory %s\n",
3369f86dcef8SLiam Girdwood 			 rtd->dai_link->name);
33702e55b90aSLars-Peter Clausen 		return;
3371f86dcef8SLiam Girdwood 	}
3372f86dcef8SLiam Girdwood 
3373f1e3f409SFabio Estevam 	debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root,
3374f86dcef8SLiam Girdwood 			    rtd, &dpcm_state_fops);
3375f86dcef8SLiam Girdwood }
3376f86dcef8SLiam Girdwood #endif
3377