Blame view

sound/soc/soc-pcm.c 64.6 KB
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   * soc-pcm.c  --  ALSA SoC PCM
   *
   * Copyright 2005 Wolfson Microelectronics PLC.
   * Copyright 2005 Openedhand Ltd.
   * Copyright (C) 2010 Slimlogic Ltd.
   * Copyright (C) 2010 Texas Instruments Inc.
   *
   * Authors: Liam Girdwood <lrg@ti.com>
   *          Mark Brown <broonie@opensource.wolfsonmicro.com>       
   *
   *  This program is free software; you can redistribute  it and/or modify it
   *  under  the terms of  the GNU General  Public License as published by the
   *  Free Software Foundation;  either version 2 of the  License, or (at your
   *  option) any later version.
   *
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/delay.h>
d6652ef82   Mark Brown   ASoC: Hold runtim...
22
  #include <linux/pm_runtime.h>
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
23
24
  #include <linux/slab.h>
  #include <linux/workqueue.h>
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
25
  #include <linux/export.h>
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
26
  #include <linux/debugfs.h>
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
27
28
29
30
  #include <sound/core.h>
  #include <sound/pcm.h>
  #include <sound/pcm_params.h>
  #include <sound/soc.h>
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
31
  #include <sound/soc-dpcm.h>
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
32
  #include <sound/initval.h>
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
33
34
35
36
37
38
39
40
41
42
43
  #define DPCM_MAX_BE_USERS	8
  
  /* DPCM stream event, send event to FE and all active BEs. */
  static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
  	int event)
  {
  	struct snd_soc_dpcm *dpcm;
  
  	list_for_each_entry(dpcm, &fe->dpcm[dir].be_clients, list_be) {
  
  		struct snd_soc_pcm_runtime *be = dpcm->be;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
44
45
  		dev_dbg(be->dev, "ASoC: BE %s event %d dir %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
46
47
48
49
50
51
52
53
54
  				be->dai_link->name, event, dir);
  
  		snd_soc_dapm_stream_event(be, dir, event);
  	}
  
  	snd_soc_dapm_stream_event(fe, dir, event);
  
  	return 0;
  }
17841020e   Dong Aisheng   ASoC: soc-core: s...
55
56
  static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
  					struct snd_soc_dai *soc_dai)
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
57
58
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
59
  	int ret;
17841020e   Dong Aisheng   ASoC: soc-core: s...
60
  	if (!soc_dai->driver->symmetric_rates &&
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
61
62
63
64
65
66
67
  	    !rtd->dai_link->symmetric_rates)
  		return 0;
  
  	/* This can happen if multiple streams are starting simultaneously -
  	 * the second can need to get its constraints before the first has
  	 * picked a rate.  Complain and allow the application to carry on.
  	 */
17841020e   Dong Aisheng   ASoC: soc-core: s...
68
69
  	if (!soc_dai->rate) {
  		dev_warn(soc_dai->dev,
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
70
71
  			 "ASoC: Not enforcing symmetric_rates due to race
  ");
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
72
73
  		return 0;
  	}
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
74
75
  	dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate
  ", soc_dai->rate);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
76
77
78
  
  	ret = snd_pcm_hw_constraint_minmax(substream->runtime,
  					   SNDRV_PCM_HW_PARAM_RATE,
17841020e   Dong Aisheng   ASoC: soc-core: s...
79
  					   soc_dai->rate, soc_dai->rate);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
80
  	if (ret < 0) {
17841020e   Dong Aisheng   ASoC: soc-core: s...
81
  		dev_err(soc_dai->dev,
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
82
83
84
  			"ASoC: Unable to apply rate symmetry constraint: %d
  ",
  			ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
85
86
87
88
89
90
91
  		return ret;
  	}
  
  	return 0;
  }
  
  /*
58ba9b254   Mark Brown   ASoC: Allow drive...
92
93
94
95
96
   * List of sample sizes that might go over the bus for parameter
   * application.  There ought to be a wildcard sample size for things
   * like the DAC/ADC resolution to use but there isn't right now.
   */
  static int sample_sizes[] = {
88e339541   Peter Ujfalusi   ASoC: soc-pcm: ms...
97
  	24, 32,
58ba9b254   Mark Brown   ASoC: Allow drive...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  };
  
  static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
  			      struct snd_soc_dai *dai)
  {
  	int ret, i, bits;
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		bits = dai->driver->playback.sig_bits;
  	else
  		bits = dai->driver->capture.sig_bits;
  
  	if (!bits)
  		return;
  
  	for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) {
278047fd6   Mark Brown   ASoC: Don't tell ...
114
115
116
117
118
  		if (bits >= sample_sizes[i])
  			continue;
  
  		ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0,
  						   sample_sizes[i], bits);
58ba9b254   Mark Brown   ASoC: Allow drive...
119
120
  		if (ret != 0)
  			dev_warn(dai->dev,
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
121
122
  				 "ASoC: Failed to set MSB %d/%d: %d
  ",
58ba9b254   Mark Brown   ASoC: Allow drive...
123
124
125
126
127
  				 bits, sample_sizes[i], ret);
  	}
  }
  
  /*
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
   * Called by ALSA when a PCM substream is opened, the runtime->hw record is
   * then initialized and any private data can be allocated. This also calls
   * startup for the cpu DAI, platform, machine and codec DAI.
   */
  static int soc_pcm_open(struct snd_pcm_substream *substream)
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct snd_soc_platform *platform = rtd->platform;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
  	struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
  	struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
  	int ret = 0;
d6652ef82   Mark Brown   ASoC: Hold runtim...
142
143
144
  	pm_runtime_get_sync(cpu_dai->dev);
  	pm_runtime_get_sync(codec_dai->dev);
  	pm_runtime_get_sync(platform->dev);
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
145
  	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
146
147
148
149
150
  
  	/* startup the audio subsystem */
  	if (cpu_dai->driver->ops->startup) {
  		ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
151
152
153
  			dev_err(cpu_dai->dev, "ASoC: can't open interface"
  				" %s: %d
  ", cpu_dai->name, ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
154
155
156
157
158
159
160
  			goto out;
  		}
  	}
  
  	if (platform->driver->ops && platform->driver->ops->open) {
  		ret = platform->driver->ops->open(substream);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
161
162
163
  			dev_err(platform->dev, "ASoC: can't open platform"
  				" %s: %d
  ", platform->name, ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
164
165
166
167
168
169
170
  			goto platform_err;
  		}
  	}
  
  	if (codec_dai->driver->ops->startup) {
  		ret = codec_dai->driver->ops->startup(substream, codec_dai);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
171
172
173
  			dev_err(codec_dai->dev, "ASoC: can't open codec"
  				" %s: %d
  ", codec_dai->name, ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
174
175
176
177
178
179
180
  			goto codec_dai_err;
  		}
  	}
  
  	if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
  		ret = rtd->dai_link->ops->startup(substream);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
181
182
  			pr_err("ASoC: %s startup failed: %d
  ",
25bfe662e   Mark Brown   ASoC: pcm: Improv...
183
  			       rtd->dai_link->name, ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
184
185
186
  			goto machine_err;
  		}
  	}
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
187
188
189
  	/* Dynamic PCM DAI links compat checks use dynamic capabilities */
  	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
  		goto dynamic;
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  	/* Check that the codec and cpu DAIs are compatible */
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		runtime->hw.rate_min =
  			max(codec_dai_drv->playback.rate_min,
  			    cpu_dai_drv->playback.rate_min);
  		runtime->hw.rate_max =
  			min(codec_dai_drv->playback.rate_max,
  			    cpu_dai_drv->playback.rate_max);
  		runtime->hw.channels_min =
  			max(codec_dai_drv->playback.channels_min,
  				cpu_dai_drv->playback.channels_min);
  		runtime->hw.channels_max =
  			min(codec_dai_drv->playback.channels_max,
  				cpu_dai_drv->playback.channels_max);
  		runtime->hw.formats =
  			codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
  		runtime->hw.rates =
  			codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
  		if (codec_dai_drv->playback.rates
  			   & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
  			runtime->hw.rates |= cpu_dai_drv->playback.rates;
  		if (cpu_dai_drv->playback.rates
  			   & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
  			runtime->hw.rates |= codec_dai_drv->playback.rates;
  	} else {
  		runtime->hw.rate_min =
  			max(codec_dai_drv->capture.rate_min,
  			    cpu_dai_drv->capture.rate_min);
  		runtime->hw.rate_max =
  			min(codec_dai_drv->capture.rate_max,
  			    cpu_dai_drv->capture.rate_max);
  		runtime->hw.channels_min =
  			max(codec_dai_drv->capture.channels_min,
  				cpu_dai_drv->capture.channels_min);
  		runtime->hw.channels_max =
  			min(codec_dai_drv->capture.channels_max,
  				cpu_dai_drv->capture.channels_max);
  		runtime->hw.formats =
  			codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
  		runtime->hw.rates =
  			codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
  		if (codec_dai_drv->capture.rates
  			   & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
  			runtime->hw.rates |= cpu_dai_drv->capture.rates;
  		if (cpu_dai_drv->capture.rates
  			   & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
  			runtime->hw.rates |= codec_dai_drv->capture.rates;
  	}
  
  	ret = -EINVAL;
  	snd_pcm_limit_hw_rates(runtime);
  	if (!runtime->hw.rates) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
242
243
  		printk(KERN_ERR "ASoC: %s <-> %s No matching rates
  ",
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
244
245
246
247
  			codec_dai->name, cpu_dai->name);
  		goto config_err;
  	}
  	if (!runtime->hw.formats) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
248
249
  		printk(KERN_ERR "ASoC: %s <-> %s No matching formats
  ",
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
250
251
252
253
254
  			codec_dai->name, cpu_dai->name);
  		goto config_err;
  	}
  	if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
  	    runtime->hw.channels_min > runtime->hw.channels_max) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
255
256
  		printk(KERN_ERR "ASoC: %s <-> %s No matching channels
  ",
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
257
258
259
  				codec_dai->name, cpu_dai->name);
  		goto config_err;
  	}
58ba9b254   Mark Brown   ASoC: Allow drive...
260
261
  	soc_pcm_apply_msb(substream, codec_dai);
  	soc_pcm_apply_msb(substream, cpu_dai);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
262
  	/* Symmetry only applies if we've already got an active stream. */
17841020e   Dong Aisheng   ASoC: soc-core: s...
263
264
265
266
267
268
269
270
  	if (cpu_dai->active) {
  		ret = soc_pcm_apply_symmetry(substream, cpu_dai);
  		if (ret != 0)
  			goto config_err;
  	}
  
  	if (codec_dai->active) {
  		ret = soc_pcm_apply_symmetry(substream, codec_dai);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
271
272
273
  		if (ret != 0)
  			goto config_err;
  	}
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
274
275
  	pr_debug("ASoC: %s <-> %s info:
  ",
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
276
  			codec_dai->name, cpu_dai->name);
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
277
278
279
280
  	pr_debug("ASoC: rate mask 0x%x
  ", runtime->hw.rates);
  	pr_debug("ASoC: min ch %d max ch %d
  ", runtime->hw.channels_min,
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
281
  		 runtime->hw.channels_max);
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
282
283
  	pr_debug("ASoC: min rate %d max rate %d
  ", runtime->hw.rate_min,
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
284
  		 runtime->hw.rate_max);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
285
  dynamic:
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
286
287
288
289
290
291
292
293
294
295
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		cpu_dai->playback_active++;
  		codec_dai->playback_active++;
  	} else {
  		cpu_dai->capture_active++;
  		codec_dai->capture_active++;
  	}
  	cpu_dai->active++;
  	codec_dai->active++;
  	rtd->codec->active++;
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
296
  	mutex_unlock(&rtd->pcm_mutex);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
  	return 0;
  
  config_err:
  	if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
  		rtd->dai_link->ops->shutdown(substream);
  
  machine_err:
  	if (codec_dai->driver->ops->shutdown)
  		codec_dai->driver->ops->shutdown(substream, codec_dai);
  
  codec_dai_err:
  	if (platform->driver->ops && platform->driver->ops->close)
  		platform->driver->ops->close(substream);
  
  platform_err:
  	if (cpu_dai->driver->ops->shutdown)
  		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
  out:
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
315
  	mutex_unlock(&rtd->pcm_mutex);
d6652ef82   Mark Brown   ASoC: Hold runtim...
316
317
318
319
  
  	pm_runtime_put(platform->dev);
  	pm_runtime_put(codec_dai->dev);
  	pm_runtime_put(cpu_dai->dev);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
320
321
322
323
324
325
326
327
328
329
330
331
332
  	return ret;
  }
  
  /*
   * Power down the audio subsystem pmdown_time msecs after close is called.
   * This is to ensure there are no pops or clicks in between any music tracks
   * due to DAPM power cycling.
   */
  static void close_delayed_work(struct work_struct *work)
  {
  	struct snd_soc_pcm_runtime *rtd =
  			container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
333
  	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
334

103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
335
336
  	dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s
  ",
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
337
338
  		 codec_dai->driver->playback.stream_name,
  		 codec_dai->playback_active ? "active" : "inactive",
9bffb1fb7   Misael Lopez Cruz   ASoC: Prevent pop...
339
  		 rtd->pop_wait ? "yes" : "no");
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
340
341
  
  	/* are we waiting on this codec DAI stream */
9bffb1fb7   Misael Lopez Cruz   ASoC: Prevent pop...
342
343
  	if (rtd->pop_wait == 1) {
  		rtd->pop_wait = 0;
7bd3a6f34   Mark Brown   ASoC: dapm: Suppl...
344
  		snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
d9b0951b9   Liam Girdwood   ASoC: dapm: Add p...
345
  					  SND_SOC_DAPM_STREAM_STOP);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
346
  	}
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
347
  	mutex_unlock(&rtd->pcm_mutex);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
348
349
350
351
352
353
354
  }
  
  /*
   * Called by ALSA when a PCM substream is closed. Private data can be
   * freed here. The cpu DAI, codec DAI, machine and platform are also
   * shutdown.
   */
91d5e6b4f   Liam Girdwood   ASoC: pcm - renam...
355
  static int soc_pcm_close(struct snd_pcm_substream *substream)
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
356
357
358
359
360
361
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_soc_platform *platform = rtd->platform;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
  	struct snd_soc_codec *codec = rtd->codec;
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
362
  	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
363
364
365
366
367
368
369
370
371
372
373
374
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		cpu_dai->playback_active--;
  		codec_dai->playback_active--;
  	} else {
  		cpu_dai->capture_active--;
  		codec_dai->capture_active--;
  	}
  
  	cpu_dai->active--;
  	codec_dai->active--;
  	codec->active--;
17841020e   Dong Aisheng   ASoC: soc-core: s...
375
376
377
378
379
380
  	/* clear the corresponding DAIs rate when inactive */
  	if (!cpu_dai->active)
  		cpu_dai->rate = 0;
  
  	if (!codec_dai->active)
  		codec_dai->rate = 0;
25b767913   Sascha Hauer   ASoC: Fix check f...
381

ddee627cf   Liam Girdwood   ASoC: core - Sepa...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
  	/* Muting the DAC suppresses artifacts caused during digital
  	 * shutdown, for example from stopping clocks.
  	 */
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		snd_soc_dai_digital_mute(codec_dai, 1);
  
  	if (cpu_dai->driver->ops->shutdown)
  		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
  
  	if (codec_dai->driver->ops->shutdown)
  		codec_dai->driver->ops->shutdown(substream, codec_dai);
  
  	if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
  		rtd->dai_link->ops->shutdown(substream);
  
  	if (platform->driver->ops && platform->driver->ops->close)
  		platform->driver->ops->close(substream);
  	cpu_dai->runtime = NULL;
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
b5d1d036e   Mark Brown   ASoC: pcm: If pmd...
402
  		if (!rtd->pmdown_time || codec->ignore_pmdown_time ||
5b6247abc   Mark Brown   ASoC: Remove need...
403
  		    rtd->dai_link->ignore_pmdown_time) {
1d69c5c5d   Peter Ujfalusi   ASoC: core: Add f...
404
405
  			/* powered down playback stream now */
  			snd_soc_dapm_stream_event(rtd,
7bd3a6f34   Mark Brown   ASoC: dapm: Suppl...
406
  						  SNDRV_PCM_STREAM_PLAYBACK,
7bd3a6f34   Mark Brown   ASoC: dapm: Suppl...
407
  						  SND_SOC_DAPM_STREAM_STOP);
1d69c5c5d   Peter Ujfalusi   ASoC: core: Add f...
408
409
  		} else {
  			/* start delayed pop wq here for playback streams */
9bffb1fb7   Misael Lopez Cruz   ASoC: Prevent pop...
410
  			rtd->pop_wait = 1;
1d69c5c5d   Peter Ujfalusi   ASoC: core: Add f...
411
412
413
  			schedule_delayed_work(&rtd->delayed_work,
  				msecs_to_jiffies(rtd->pmdown_time));
  		}
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
414
415
  	} else {
  		/* capture streams can be powered down now */
7bd3a6f34   Mark Brown   ASoC: dapm: Suppl...
416
  		snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
d9b0951b9   Liam Girdwood   ASoC: dapm: Add p...
417
  					  SND_SOC_DAPM_STREAM_STOP);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
418
  	}
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
419
  	mutex_unlock(&rtd->pcm_mutex);
d6652ef82   Mark Brown   ASoC: Hold runtim...
420
421
422
423
  
  	pm_runtime_put(platform->dev);
  	pm_runtime_put(codec_dai->dev);
  	pm_runtime_put(cpu_dai->dev);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
  	return 0;
  }
  
  /*
   * Called by ALSA when the PCM substream is prepared, can set format, sample
   * rate, etc.  This function is non atomic and can be called multiple times,
   * it can refer to the runtime info.
   */
  static int soc_pcm_prepare(struct snd_pcm_substream *substream)
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_soc_platform *platform = rtd->platform;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
  	int ret = 0;
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
439
  	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
440
441
442
443
  
  	if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
  		ret = rtd->dai_link->ops->prepare(substream);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
444
445
446
  			dev_err(rtd->card->dev, "ASoC: machine prepare error:"
  				" %d
  ", ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
447
448
449
450
451
452
453
  			goto out;
  		}
  	}
  
  	if (platform->driver->ops && platform->driver->ops->prepare) {
  		ret = platform->driver->ops->prepare(substream);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
454
455
456
  			dev_err(platform->dev, "ASoC: platform prepare error:"
  				" %d
  ", ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
457
458
459
460
461
462
463
  			goto out;
  		}
  	}
  
  	if (codec_dai->driver->ops->prepare) {
  		ret = codec_dai->driver->ops->prepare(substream, codec_dai);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
464
465
  			dev_err(codec_dai->dev, "ASoC: DAI prepare error: %d
  ",
25bfe662e   Mark Brown   ASoC: pcm: Improv...
466
  				ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
467
468
469
470
471
472
473
  			goto out;
  		}
  	}
  
  	if (cpu_dai->driver->ops->prepare) {
  		ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
474
475
  			dev_err(cpu_dai->dev, "ASoC: DAI prepare error: %d
  ",
25bfe662e   Mark Brown   ASoC: pcm: Improv...
476
  				ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
477
478
479
480
481
482
  			goto out;
  		}
  	}
  
  	/* cancel any delayed stream shutdown that is pending */
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
9bffb1fb7   Misael Lopez Cruz   ASoC: Prevent pop...
483
484
  	    rtd->pop_wait) {
  		rtd->pop_wait = 0;
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
485
486
  		cancel_delayed_work(&rtd->delayed_work);
  	}
d9b0951b9   Liam Girdwood   ASoC: dapm: Add p...
487
488
  	snd_soc_dapm_stream_event(rtd, substream->stream,
  			SND_SOC_DAPM_STREAM_START);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
489
490
491
492
  
  	snd_soc_dai_digital_mute(codec_dai, 0);
  
  out:
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
493
  	mutex_unlock(&rtd->pcm_mutex);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
  	return ret;
  }
  
  /*
   * Called by ALSA when the hardware params are set by application. This
   * function can also be called multiple times and can allocate buffers
   * (using snd_pcm_lib_* ). It's non-atomic.
   */
  static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
  				struct snd_pcm_hw_params *params)
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_soc_platform *platform = rtd->platform;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
  	int ret = 0;
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
510
  	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
511
512
513
514
  
  	if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
  		ret = rtd->dai_link->ops->hw_params(substream, params);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
515
516
517
  			dev_err(rtd->card->dev, "ASoC: machine hw_params"
  				" failed: %d
  ", ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
518
519
520
521
522
523
524
  			goto out;
  		}
  	}
  
  	if (codec_dai->driver->ops->hw_params) {
  		ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
525
526
527
  			dev_err(codec_dai->dev, "ASoC: can't set %s hw params:"
  				" %d
  ", codec_dai->name, ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
528
529
530
531
532
533
534
  			goto codec_err;
  		}
  	}
  
  	if (cpu_dai->driver->ops->hw_params) {
  		ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
535
536
  			dev_err(cpu_dai->dev, "ASoC: %s hw params failed: %d
  ",
25bfe662e   Mark Brown   ASoC: pcm: Improv...
537
  				cpu_dai->name, ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
538
539
540
541
542
543
544
  			goto interface_err;
  		}
  	}
  
  	if (platform->driver->ops && platform->driver->ops->hw_params) {
  		ret = platform->driver->ops->hw_params(substream, params);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
545
546
  			dev_err(platform->dev, "ASoC: %s hw params failed: %d
  ",
25bfe662e   Mark Brown   ASoC: pcm: Improv...
547
  			       platform->name, ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
548
549
550
  			goto platform_err;
  		}
  	}
17841020e   Dong Aisheng   ASoC: soc-core: s...
551
552
553
  	/* store the rate for each DAIs */
  	cpu_dai->rate = params_rate(params);
  	codec_dai->rate = params_rate(params);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
554
555
  
  out:
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
556
  	mutex_unlock(&rtd->pcm_mutex);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
557
558
559
560
561
562
563
564
565
566
567
568
569
  	return ret;
  
  platform_err:
  	if (cpu_dai->driver->ops->hw_free)
  		cpu_dai->driver->ops->hw_free(substream, cpu_dai);
  
  interface_err:
  	if (codec_dai->driver->ops->hw_free)
  		codec_dai->driver->ops->hw_free(substream, codec_dai);
  
  codec_err:
  	if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
  		rtd->dai_link->ops->hw_free(substream);
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
570
  	mutex_unlock(&rtd->pcm_mutex);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
571
572
573
574
575
576
577
578
579
580
581
582
583
  	return ret;
  }
  
  /*
   * Frees resources allocated by hw_params, can be called multiple times
   */
  static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_soc_platform *platform = rtd->platform;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
  	struct snd_soc_codec *codec = rtd->codec;
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
584
  	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
  
  	/* apply codec digital mute */
  	if (!codec->active)
  		snd_soc_dai_digital_mute(codec_dai, 1);
  
  	/* free any machine hw params */
  	if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
  		rtd->dai_link->ops->hw_free(substream);
  
  	/* free any DMA resources */
  	if (platform->driver->ops && platform->driver->ops->hw_free)
  		platform->driver->ops->hw_free(substream);
  
  	/* now free hw params for the DAIs  */
  	if (codec_dai->driver->ops->hw_free)
  		codec_dai->driver->ops->hw_free(substream, codec_dai);
  
  	if (cpu_dai->driver->ops->hw_free)
  		cpu_dai->driver->ops->hw_free(substream, cpu_dai);
b8c0dab9b   Liam Girdwood   ASoC: core - PCM ...
604
  	mutex_unlock(&rtd->pcm_mutex);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
  	return 0;
  }
  
  static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_soc_platform *platform = rtd->platform;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
  	int ret;
  
  	if (codec_dai->driver->ops->trigger) {
  		ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
  		if (ret < 0)
  			return ret;
  	}
  
  	if (platform->driver->ops && platform->driver->ops->trigger) {
  		ret = platform->driver->ops->trigger(substream, cmd);
  		if (ret < 0)
  			return ret;
  	}
  
  	if (cpu_dai->driver->ops->trigger) {
  		ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
  		if (ret < 0)
  			return ret;
  	}
  	return 0;
  }
45c0a188c   Mark Brown   ASoC: pcm: Static...
635
636
  static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
  				   int cmd)
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_soc_platform *platform = rtd->platform;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
  	int ret;
  
  	if (codec_dai->driver->ops->bespoke_trigger) {
  		ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai);
  		if (ret < 0)
  			return ret;
  	}
  
  	if (platform->driver->bespoke_trigger) {
  		ret = platform->driver->bespoke_trigger(substream, cmd);
  		if (ret < 0)
  			return ret;
  	}
  
  	if (cpu_dai->driver->ops->bespoke_trigger) {
  		ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai);
  		if (ret < 0)
  			return ret;
  	}
  	return 0;
  }
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
  /*
   * soc level wrapper for pointer callback
   * If cpu_dai, codec_dai, platform driver has the delay callback, than
   * the runtime->delay will be updated accordingly.
   */
  static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_soc_platform *platform = rtd->platform;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	snd_pcm_uframes_t offset = 0;
  	snd_pcm_sframes_t delay = 0;
  
  	if (platform->driver->ops && platform->driver->ops->pointer)
  		offset = platform->driver->ops->pointer(substream);
  
  	if (cpu_dai->driver->ops->delay)
  		delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
  
  	if (codec_dai->driver->ops->delay)
  		delay += codec_dai->driver->ops->delay(substream, codec_dai);
  
  	if (platform->driver->delay)
  		delay += platform->driver->delay(substream, codec_dai);
  
  	runtime->delay = delay;
  
  	return offset;
  }
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
  /* connect a FE and BE */
  static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
  		struct snd_soc_pcm_runtime *be, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  
  	/* only add new dpcms */
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  		if (dpcm->be == be && dpcm->fe == fe)
  			return 0;
  	}
  
  	dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL);
  	if (!dpcm)
  		return -ENOMEM;
  
  	dpcm->be = be;
  	dpcm->fe = fe;
  	be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
  	dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
  	list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
  	list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
  
  	dev_dbg(fe->dev, "  connected new DPCM %s path %s %s %s
  ",
  			stream ? "capture" : "playback",  fe->dai_link->name,
  			stream ? "<-" : "->", be->dai_link->name);
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
721
722
723
724
  #ifdef CONFIG_DEBUG_FS
  	dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644,
  			fe->debugfs_dpcm_root, &dpcm->state);
  #endif
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
  	return 1;
  }
  
  /* reparent a BE onto another FE */
  static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
  			struct snd_soc_pcm_runtime *be, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  	struct snd_pcm_substream *fe_substream, *be_substream;
  
  	/* reparent if BE is connected to other FEs */
  	if (!be->dpcm[stream].users)
  		return;
  
  	be_substream = snd_soc_dpcm_get_substream(be, stream);
  
  	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
  		if (dpcm->fe == fe)
  			continue;
  
  		dev_dbg(fe->dev, "  reparent %s path %s %s %s
  ",
  			stream ? "capture" : "playback",
  			dpcm->fe->dai_link->name,
  			stream ? "<-" : "->", dpcm->be->dai_link->name);
  
  		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream);
  		be_substream->runtime = fe_substream->runtime;
  		break;
  	}
  }
  
  /* disconnect a BE and FE */
  static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	struct snd_soc_dpcm *dpcm, *d;
  
  	list_for_each_entry_safe(dpcm, d, &fe->dpcm[stream].be_clients, list_be) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
763
764
  		dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
765
766
767
768
769
770
771
772
773
774
775
776
777
  				stream ? "capture" : "playback",
  				dpcm->be->dai_link->name);
  
  		if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE)
  			continue;
  
  		dev_dbg(fe->dev, "  freed DSP %s path %s %s %s
  ",
  			stream ? "capture" : "playback", fe->dai_link->name,
  			stream ? "<-" : "->", dpcm->be->dai_link->name);
  
  		/* BEs still alive need new FE */
  		dpcm_be_reparent(fe, dpcm->be, stream);
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
778
779
780
  #ifdef CONFIG_DEBUG_FS
  		debugfs_remove(dpcm->debugfs_state);
  #endif
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
  		list_del(&dpcm->list_be);
  		list_del(&dpcm->list_fe);
  		kfree(dpcm);
  	}
  }
  
  /* get BE for DAI widget and stream */
  static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
  		struct snd_soc_dapm_widget *widget, int stream)
  {
  	struct snd_soc_pcm_runtime *be;
  	int i;
  
  	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		for (i = 0; i < card->num_links; i++) {
  			be = &card->rtd[i];
35ea0655a   Liam Girdwood   ASoC: dpcm: Fix d...
797
798
  			if (!be->dai_link->no_pcm)
  				continue;
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
799
800
801
802
803
804
805
806
  			if (be->cpu_dai->playback_widget == widget ||
  				be->codec_dai->playback_widget == widget)
  				return be;
  		}
  	} else {
  
  		for (i = 0; i < card->num_links; i++) {
  			be = &card->rtd[i];
35ea0655a   Liam Girdwood   ASoC: dpcm: Fix d...
807
808
  			if (!be->dai_link->no_pcm)
  				continue;
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
809
810
811
812
813
  			if (be->cpu_dai->capture_widget == widget ||
  				be->codec_dai->capture_widget == widget)
  				return be;
  		}
  	}
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
814
815
  	dev_err(card->dev, "ASoC: can't get %s BE for %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
  		stream ? "capture" : "playback", widget->name);
  	return NULL;
  }
  
  static inline struct snd_soc_dapm_widget *
  	rtd_get_cpu_widget(struct snd_soc_pcm_runtime *rtd, int stream)
  {
  	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
  		return rtd->cpu_dai->playback_widget;
  	else
  		return rtd->cpu_dai->capture_widget;
  }
  
  static inline struct snd_soc_dapm_widget *
  	rtd_get_codec_widget(struct snd_soc_pcm_runtime *rtd, int stream)
  {
  	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
  		return rtd->codec_dai->playback_widget;
  	else
  		return rtd->codec_dai->capture_widget;
  }
  
  static int widget_in_list(struct snd_soc_dapm_widget_list *list,
  		struct snd_soc_dapm_widget *widget)
  {
  	int i;
  
  	for (i = 0; i < list->num_widgets; i++) {
  		if (widget == list->widgets[i])
  			return 1;
  	}
  
  	return 0;
  }
  
  static int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
  	int stream, struct snd_soc_dapm_widget_list **list_)
  {
  	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
  	struct snd_soc_dapm_widget_list *list;
  	int paths;
  
  	list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) +
  			sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL);
  	if (list == NULL)
  		return -ENOMEM;
  
  	/* get number of valid DAI paths and their widgets */
  	paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list);
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
865
866
  	dev_dbg(fe->dev, "ASoC: found %d audio %s paths
  ", paths,
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
  			stream ? "capture" : "playback");
  
  	*list_ = list;
  	return paths;
  }
  
  static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list)
  {
  	kfree(*list);
  }
  
  static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
  	struct snd_soc_dapm_widget_list **list_)
  {
  	struct snd_soc_dpcm *dpcm;
  	struct snd_soc_dapm_widget_list *list = *list_;
  	struct snd_soc_dapm_widget *widget;
  	int prune = 0;
  
  	/* Destroy any old FE <--> BE connections */
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  
  		/* is there a valid CPU DAI widget for this BE */
  		widget = rtd_get_cpu_widget(dpcm->be, stream);
  
  		/* prune the BE if it's no longer in our active list */
  		if (widget && widget_in_list(list, widget))
  			continue;
  
  		/* is there a valid CODEC DAI widget for this BE */
  		widget = rtd_get_codec_widget(dpcm->be, stream);
  
  		/* prune the BE if it's no longer in our active list */
  		if (widget && widget_in_list(list, widget))
  			continue;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
902
903
  		dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
904
905
906
907
908
909
  			stream ? "capture" : "playback",
  			dpcm->be->dai_link->name, fe->dai_link->name);
  		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
  		dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
  		prune++;
  	}
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
910
911
  	dev_dbg(fe->dev, "ASoC: found %d old BE paths for pruning
  ", prune);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
  	return prune;
  }
  
  static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
  	struct snd_soc_dapm_widget_list **list_)
  {
  	struct snd_soc_card *card = fe->card;
  	struct snd_soc_dapm_widget_list *list = *list_;
  	struct snd_soc_pcm_runtime *be;
  	int i, new = 0, err;
  
  	/* Create any new FE <--> BE connections */
  	for (i = 0; i < list->num_widgets; i++) {
  
  		if (list->widgets[i]->id != snd_soc_dapm_dai)
  			continue;
  
  		/* is there a valid BE rtd for this widget */
  		be = dpcm_get_be(card, list->widgets[i], stream);
  		if (!be) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
932
933
  			dev_err(fe->dev, "ASoC: no BE found for %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
  					list->widgets[i]->name);
  			continue;
  		}
  
  		/* make sure BE is a real BE */
  		if (!be->dai_link->no_pcm)
  			continue;
  
  		/* don't connect if FE is not running */
  		if (!fe->dpcm[stream].runtime)
  			continue;
  
  		/* newly connected FE and BE */
  		err = dpcm_be_connect(fe, be, stream);
  		if (err < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
949
950
  			dev_err(fe->dev, "ASoC: can't connect %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
951
952
953
954
955
956
957
958
959
  				list->widgets[i]->name);
  			break;
  		} else if (err == 0) /* already connected */
  			continue;
  
  		/* new */
  		be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
  		new++;
  	}
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
960
961
  	dev_dbg(fe->dev, "ASoC: found %d new BE paths
  ", new);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
  	return new;
  }
  
  /*
   * Find the corresponding BE DAIs that source or sink audio to this
   * FE substream.
   */
  static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
  	int stream, struct snd_soc_dapm_widget_list **list, int new)
  {
  	if (new)
  		return dpcm_add_paths(fe, stream, list);
  	else
  		return dpcm_prune_paths(fe, stream, list);
  }
  
  static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
  		dpcm->be->dpcm[stream].runtime_update =
  						SND_SOC_DPCM_UPDATE_NO;
  }
  
  static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
  	int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  
  	/* disable any enabled and non active backends */
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_pcm_substream *be_substream =
  			snd_soc_dpcm_get_substream(be, stream);
  
  		if (be->dpcm[stream].users == 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1000
1001
  			dev_err(be->dev, "ASoC: no users %s at close - state %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
  				stream ? "capture" : "playback",
  				be->dpcm[stream].state);
  
  		if (--be->dpcm[stream].users != 0)
  			continue;
  
  		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
  			continue;
  
  		soc_pcm_close(be_substream);
  		be_substream->runtime = NULL;
  		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
  	}
  }
  
  static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  	int err, count = 0;
  
  	/* only startup BE DAIs that are either sinks or sources to this FE DAI */
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_pcm_substream *be_substream =
  			snd_soc_dpcm_get_substream(be, stream);
  
  		/* is this op for this BE ? */
  		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
  			continue;
  
  		/* first time the dpcm is open ? */
  		if (be->dpcm[stream].users == DPCM_MAX_BE_USERS)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1035
1036
  			dev_err(be->dev, "ASoC: too many users %s at open %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1037
1038
1039
1040
1041
1042
1043
1044
1045
  				stream ? "capture" : "playback",
  				be->dpcm[stream].state);
  
  		if (be->dpcm[stream].users++ != 0)
  			continue;
  
  		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
  		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
  			continue;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1046
1047
  		dev_dbg(be->dev, "ASoC: open BE %s
  ", be->dai_link->name);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1048
1049
1050
1051
  
  		be_substream->runtime = be->dpcm[stream].runtime;
  		err = soc_pcm_open(be_substream);
  		if (err < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1052
1053
  			dev_err(be->dev, "ASoC: BE open failed %d
  ", err);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1054
1055
  			be->dpcm[stream].users--;
  			if (be->dpcm[stream].users < 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1056
1057
  				dev_err(be->dev, "ASoC: no users %s at unwind %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
  					stream ? "capture" : "playback",
  					be->dpcm[stream].state);
  
  			be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
  			goto unwind;
  		}
  
  		be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
  		count++;
  	}
  
  	return count;
  
  unwind:
  	/* disable any enabled and non active backends */
  	list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_pcm_substream *be_substream =
  			snd_soc_dpcm_get_substream(be, stream);
  
  		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
  			continue;
  
  		if (be->dpcm[stream].users == 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1082
1083
  			dev_err(be->dev, "ASoC: no users %s at close %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
  				stream ? "capture" : "playback",
  				be->dpcm[stream].state);
  
  		if (--be->dpcm[stream].users != 0)
  			continue;
  
  		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
  			continue;
  
  		soc_pcm_close(be_substream);
  		be_substream->runtime = NULL;
  		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
  	}
  
  	return err;
  }
45c0a188c   Mark Brown   ASoC: pcm: Static...
1100
  static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		runtime->hw.rate_min = cpu_dai_drv->playback.rate_min;
  		runtime->hw.rate_max = cpu_dai_drv->playback.rate_max;
  		runtime->hw.channels_min = cpu_dai_drv->playback.channels_min;
  		runtime->hw.channels_max = cpu_dai_drv->playback.channels_max;
  		runtime->hw.formats &= cpu_dai_drv->playback.formats;
  		runtime->hw.rates = cpu_dai_drv->playback.rates;
  	} else {
  		runtime->hw.rate_min = cpu_dai_drv->capture.rate_min;
  		runtime->hw.rate_max = cpu_dai_drv->capture.rate_max;
  		runtime->hw.channels_min = cpu_dai_drv->capture.channels_min;
  		runtime->hw.channels_max = cpu_dai_drv->capture.channels_max;
  		runtime->hw.formats &= cpu_dai_drv->capture.formats;
  		runtime->hw.rates = cpu_dai_drv->capture.rates;
  	}
  }
  
  static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
  {
  	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
  	struct snd_pcm_runtime *runtime = fe_substream->runtime;
  	int stream = fe_substream->stream, ret = 0;
  
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
  
  	ret = dpcm_be_dai_startup(fe, fe_substream->stream);
  	if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1134
1135
  		dev_err(fe->dev,"ASoC: failed to start some BEs %d
  ", ret);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1136
1137
  		goto be_err;
  	}
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1138
1139
  	dev_dbg(fe->dev, "ASoC: open FE %s
  ", fe->dai_link->name);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1140
1141
1142
1143
  
  	/* start the DAI frontend */
  	ret = soc_pcm_open(fe_substream);
  	if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1144
1145
  		dev_err(fe->dev,"ASoC: failed to start FE %d
  ", ret);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
  		goto unwind;
  	}
  
  	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
  
  	dpcm_set_fe_runtime(fe_substream);
  	snd_pcm_limit_hw_rates(runtime);
  
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
  	return 0;
  
  unwind:
  	dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
  be_err:
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
  	return ret;
  }
  
  static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  
  	/* only shutdown BEs that are either sinks or sources to this FE DAI */
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_pcm_substream *be_substream =
  			snd_soc_dpcm_get_substream(be, stream);
  
  		/* is this op for this BE ? */
  		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
  			continue;
  
  		if (be->dpcm[stream].users == 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1180
1181
  			dev_err(be->dev, "ASoC: no users %s at close - state %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1182
1183
1184
1185
1186
1187
1188
1189
1190
  				stream ? "capture" : "playback",
  				be->dpcm[stream].state);
  
  		if (--be->dpcm[stream].users != 0)
  			continue;
  
  		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
  		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN))
  			continue;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1191
1192
  		dev_dbg(be->dev, "ASoC: close BE %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
  			dpcm->fe->dai_link->name);
  
  		soc_pcm_close(be_substream);
  		be_substream->runtime = NULL;
  
  		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
  	}
  	return 0;
  }
  
  static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
  {
  	struct snd_soc_pcm_runtime *fe = substream->private_data;
  	int stream = substream->stream;
  
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
  
  	/* shutdown the BEs */
  	dpcm_be_dai_shutdown(fe, substream->stream);
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1212
1213
  	dev_dbg(fe->dev, "ASoC: close FE %s
  ", fe->dai_link->name);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
  
  	/* now shutdown the frontend */
  	soc_pcm_close(substream);
  
  	/* run the stream event for each BE */
  	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
  
  	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
  	return 0;
  }
  
  static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  
  	/* only hw_params backends that are either sinks or sources
  	 * to this frontend DAI */
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_pcm_substream *be_substream =
  			snd_soc_dpcm_get_substream(be, stream);
  
  		/* is this op for this BE ? */
  		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
  			continue;
  
  		/* only free hw when no longer used - check all FEs */
  		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
  				continue;
  
  		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
  		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
  		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
  		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
  			continue;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1251
1252
  		dev_dbg(be->dev, "ASoC: hw_free BE %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1253
1254
1255
1256
1257
1258
1259
1260
1261
  			dpcm->fe->dai_link->name);
  
  		soc_pcm_hw_free(be_substream);
  
  		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
  	}
  
  	return 0;
  }
45c0a188c   Mark Brown   ASoC: pcm: Static...
1262
  static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1263
1264
1265
1266
1267
1268
  {
  	struct snd_soc_pcm_runtime *fe = substream->private_data;
  	int err, stream = substream->stream;
  
  	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1269
1270
  	dev_dbg(fe->dev, "ASoC: hw_free FE %s
  ", fe->dai_link->name);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1271
1272
1273
1274
  
  	/* call hw_free on the frontend */
  	err = soc_pcm_hw_free(substream);
  	if (err < 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1275
1276
  		dev_err(fe->dev,"ASoC: hw_free FE %s failed
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
  			fe->dai_link->name);
  
  	/* only hw_params backends that are either sinks or sources
  	 * to this frontend DAI */
  	err = dpcm_be_dai_hw_free(fe, stream);
  
  	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
  
  	mutex_unlock(&fe->card->mutex);
  	return 0;
  }
  
  static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  	int ret;
  
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_pcm_substream *be_substream =
  			snd_soc_dpcm_get_substream(be, stream);
  
  		/* is this op for this BE ? */
  		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
  			continue;
  
  		/* only allow hw_params() if no connected FEs are running */
  		if (!snd_soc_dpcm_can_be_params(fe, be, stream))
  			continue;
  
  		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
  		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
  		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
  			continue;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1313
1314
  		dev_dbg(be->dev, "ASoC: hw_params BE %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
  			dpcm->fe->dai_link->name);
  
  		/* copy params for each dpcm */
  		memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params,
  				sizeof(struct snd_pcm_hw_params));
  
  		/* perform any hw_params fixups */
  		if (be->dai_link->be_hw_params_fixup) {
  			ret = be->dai_link->be_hw_params_fixup(be,
  					&dpcm->hw_params);
  			if (ret < 0) {
  				dev_err(be->dev,
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1327
1328
  					"ASoC: hw_params BE fixup failed %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1329
1330
1331
1332
1333
1334
1335
1336
  					ret);
  				goto unwind;
  			}
  		}
  
  		ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
  		if (ret < 0) {
  			dev_err(dpcm->be->dev,
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1337
1338
  				"ASoC: hw_params BE failed %d
  ", ret);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
  			goto unwind;
  		}
  
  		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
  	}
  	return 0;
  
  unwind:
  	/* disable any enabled and non active backends */
  	list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_pcm_substream *be_substream =
  			snd_soc_dpcm_get_substream(be, stream);
  
  		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
  			continue;
  
  		/* only allow hw_free() if no connected FEs are running */
  		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
  			continue;
  
  		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
  		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
  		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
  		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
  			continue;
  
  		soc_pcm_hw_free(be_substream);
  	}
  
  	return ret;
  }
45c0a188c   Mark Brown   ASoC: pcm: Static...
1371
1372
  static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
  				 struct snd_pcm_hw_params *params)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
  {
  	struct snd_soc_pcm_runtime *fe = substream->private_data;
  	int ret, stream = substream->stream;
  
  	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
  
  	memcpy(&fe->dpcm[substream->stream].hw_params, params,
  			sizeof(struct snd_pcm_hw_params));
  	ret = dpcm_be_dai_hw_params(fe, substream->stream);
  	if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1384
1385
  		dev_err(fe->dev,"ASoC: hw_params BE failed %d
  ", ret);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1386
1387
  		goto out;
  	}
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1388
1389
  	dev_dbg(fe->dev, "ASoC: hw_params FE %s rate %d chan %x fmt %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1390
1391
1392
1393
1394
1395
  			fe->dai_link->name, params_rate(params),
  			params_channels(params), params_format(params));
  
  	/* call hw_params on the frontend */
  	ret = soc_pcm_hw_params(substream, params);
  	if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1396
1397
  		dev_err(fe->dev,"ASoC: hw_params FE failed %d
  ", ret);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
  		dpcm_be_dai_hw_free(fe, stream);
  	 } else
  		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
  
  out:
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
  	mutex_unlock(&fe->card->mutex);
  	return ret;
  }
  
  static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm,
  		struct snd_pcm_substream *substream, int cmd)
  {
  	int ret;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1412
1413
  	dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1414
1415
1416
1417
  			dpcm->fe->dai_link->name, cmd);
  
  	ret = soc_pcm_trigger(substream, cmd);
  	if (ret < 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1418
1419
  		dev_err(dpcm->be->dev,"ASoC: trigger BE failed %d
  ", ret);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1420
1421
1422
  
  	return ret;
  }
45c0a188c   Mark Brown   ASoC: pcm: Static...
1423
1424
  static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
  			       int cmd)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
  {
  	struct snd_soc_dpcm *dpcm;
  	int ret = 0;
  
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_pcm_substream *be_substream =
  			snd_soc_dpcm_get_substream(be, stream);
  
  		/* is this op for this BE ? */
  		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
  			continue;
  
  		switch (cmd) {
  		case SNDRV_PCM_TRIGGER_START:
  			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
  			    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
  				continue;
  
  			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
  			if (ret)
  				return ret;
  
  			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
  			break;
  		case SNDRV_PCM_TRIGGER_RESUME:
  			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
  				continue;
  
  			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
  			if (ret)
  				return ret;
  
  			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
  			break;
  		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
  				continue;
  
  			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
  			if (ret)
  				return ret;
  
  			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
  			break;
  		case SNDRV_PCM_TRIGGER_STOP:
  			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
  				continue;
  
  			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
  				continue;
  
  			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
  			if (ret)
  				return ret;
  
  			be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
  			break;
  		case SNDRV_PCM_TRIGGER_SUSPEND:
  			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)
  				continue;
  
  			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
  				continue;
  
  			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
  			if (ret)
  				return ret;
  
  			be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
  			break;
  		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
  				continue;
  
  			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
  				continue;
  
  			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
  			if (ret)
  				return ret;
  
  			be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
  			break;
  		}
  	}
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
45c0a188c   Mark Brown   ASoC: pcm: Static...
1516
  static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
  {
  	struct snd_soc_pcm_runtime *fe = substream->private_data;
  	int stream = substream->stream, ret;
  	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
  
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
  
  	switch (trigger) {
  	case SND_SOC_DPCM_TRIGGER_PRE:
  		/* call trigger on the frontend before the backend. */
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1527
1528
  		dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1529
1530
1531
1532
  				fe->dai_link->name, cmd);
  
  		ret = soc_pcm_trigger(substream, cmd);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1533
1534
  			dev_err(fe->dev,"ASoC: trigger FE failed %d
  ", ret);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
  			goto out;
  		}
  
  		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
  		break;
  	case SND_SOC_DPCM_TRIGGER_POST:
  		/* call trigger on the frontend after the backend. */
  
  		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1545
1546
  			dev_err(fe->dev,"ASoC: trigger FE failed %d
  ", ret);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1547
1548
  			goto out;
  		}
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1549
1550
  		dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1551
1552
1553
1554
  				fe->dai_link->name, cmd);
  
  		ret = soc_pcm_trigger(substream, cmd);
  		break;
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1555
1556
  	case SND_SOC_DPCM_TRIGGER_BESPOKE:
  		/* bespoke trigger() - handles both FE and BEs */
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1557
1558
  		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd %d
  ",
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1559
1560
1561
1562
  				fe->dai_link->name, cmd);
  
  		ret = soc_pcm_bespoke_trigger(substream, cmd);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1563
1564
  			dev_err(fe->dev,"ASoC: trigger FE failed %d
  ", ret);
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1565
1566
1567
  			goto out;
  		}
  		break;
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1568
  	default:
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1569
1570
  		dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s
  ", cmd,
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
  				fe->dai_link->name);
  		ret = -EINVAL;
  		goto out;
  	}
  
  	switch (cmd) {
  	case SNDRV_PCM_TRIGGER_START:
  	case SNDRV_PCM_TRIGGER_RESUME:
  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
  		break;
  	case SNDRV_PCM_TRIGGER_STOP:
  	case SNDRV_PCM_TRIGGER_SUSPEND:
  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
  		break;
  	}
  
  out:
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
  	return ret;
  }
  
  static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  	int ret = 0;
  
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_pcm_substream *be_substream =
  			snd_soc_dpcm_get_substream(be, stream);
  
  		/* is this op for this BE ? */
  		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
  			continue;
  
  		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
  		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
  			continue;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1612
1613
  		dev_dbg(be->dev, "ASoC: prepare BE %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1614
1615
1616
1617
  			dpcm->fe->dai_link->name);
  
  		ret = soc_pcm_prepare(be_substream);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1618
1619
  			dev_err(be->dev, "ASoC: backend prepare failed %d
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1620
1621
1622
1623
1624
1625
1626
1627
  				ret);
  			break;
  		}
  
  		be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
  	}
  	return ret;
  }
45c0a188c   Mark Brown   ASoC: pcm: Static...
1628
  static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1629
1630
1631
1632
1633
  {
  	struct snd_soc_pcm_runtime *fe = substream->private_data;
  	int stream = substream->stream, ret = 0;
  
  	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1634
1635
  	dev_dbg(fe->dev, "ASoC: prepare FE %s
  ", fe->dai_link->name);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1636
1637
1638
1639
1640
  
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
  
  	/* there is no point preparing this FE if there are no BEs */
  	if (list_empty(&fe->dpcm[stream].be_clients)) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1641
1642
  		dev_err(fe->dev, "ASoC: no backend DAIs enabled for %s
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
  				fe->dai_link->name);
  		ret = -EINVAL;
  		goto out;
  	}
  
  	ret = dpcm_be_dai_prepare(fe, substream->stream);
  	if (ret < 0)
  		goto out;
  
  	/* call prepare on the frontend */
  	ret = soc_pcm_prepare(substream);
  	if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1655
1656
  		dev_err(fe->dev,"ASoC: prepare FE %s failed
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
  			fe->dai_link->name);
  		goto out;
  	}
  
  	/* run the stream event for each BE */
  	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
  	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
  
  out:
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
  	mutex_unlock(&fe->card->mutex);
  
  	return ret;
  }
be3f3f2ce   Liam Girdwood   ASoC: pcm: Add pc...
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
  static int soc_pcm_ioctl(struct snd_pcm_substream *substream,
  		     unsigned int cmd, void *arg)
  {
  	struct snd_soc_pcm_runtime *rtd = substream->private_data;
  	struct snd_soc_platform *platform = rtd->platform;
  
  	if (platform->driver->ops->ioctl)
  		return platform->driver->ops->ioctl(substream, cmd, arg);
  	return snd_pcm_lib_ioctl(substream, cmd, arg);
  }
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1681
1682
  static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
  {
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1683
1684
1685
  	struct snd_pcm_substream *substream =
  		snd_soc_dpcm_get_substream(fe, stream);
  	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1686
  	int err;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1687
1688
  	dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s
  ",
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1689
  			stream ? "capture" : "playback", fe->dai_link->name);
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1690
1691
  	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
  		/* call bespoke trigger - FE takes care of all BE triggers */
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1692
1693
  		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop
  ",
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1694
1695
1696
1697
  				fe->dai_link->name);
  
  		err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP);
  		if (err < 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1698
1699
  			dev_err(fe->dev,"ASoC: trigger FE failed %d
  ", err);
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1700
  	} else {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1701
1702
  		dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop
  ",
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1703
1704
1705
1706
  			fe->dai_link->name);
  
  		err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
  		if (err < 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1707
1708
  			dev_err(fe->dev,"ASoC: trigger FE failed %d
  ", err);
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1709
  	}
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1710
1711
1712
  
  	err = dpcm_be_dai_hw_free(fe, stream);
  	if (err < 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1713
1714
  		dev_err(fe->dev,"ASoC: hw_free FE failed %d
  ", err);
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1715
1716
1717
  
  	err = dpcm_be_dai_shutdown(fe, stream);
  	if (err < 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1718
1719
  		dev_err(fe->dev,"ASoC: shutdown FE failed %d
  ", err);
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1720
1721
1722
1723
1724
1725
1726
1727
1728
  
  	/* run the stream event for each BE */
  	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
  
  	return 0;
  }
  
  static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
  {
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1729
1730
  	struct snd_pcm_substream *substream =
  		snd_soc_dpcm_get_substream(fe, stream);
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1731
  	struct snd_soc_dpcm *dpcm;
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1732
  	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1733
  	int ret;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1734
1735
  	dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s
  ",
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
  			stream ? "capture" : "playback", fe->dai_link->name);
  
  	/* Only start the BE if the FE is ready */
  	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE ||
  		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE)
  		return -EINVAL;
  
  	/* startup must always be called for new BEs */
  	ret = dpcm_be_dai_startup(fe, stream);
  	if (ret < 0) {
  		goto disconnect;
  		return ret;
  	}
  
  	/* keep going if FE state is > open */
  	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
  		return 0;
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1753

618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
  	ret = dpcm_be_dai_hw_params(fe, stream);
  	if (ret < 0) {
  		goto close;
  		return ret;
  	}
  
  	/* keep going if FE state is > hw_params */
  	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
  		return 0;
  
  
  	ret = dpcm_be_dai_prepare(fe, stream);
  	if (ret < 0) {
  		goto hw_free;
  		return ret;
  	}
  
  	/* run the stream event for each BE */
  	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
  
  	/* keep going if FE state is > prepare */
  	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE ||
  		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP)
  		return 0;
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1778
1779
  	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
  		/* call trigger on the frontend - FE takes care of all BE triggers */
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1780
1781
  		dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start
  ",
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1782
  				fe->dai_link->name);
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1783

07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1784
1785
  		ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1786
1787
  			dev_err(fe->dev,"ASoC: bespoke trigger FE failed %d
  ", ret);
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1788
1789
1790
  			goto hw_free;
  		}
  	} else {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1791
1792
  		dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start
  ",
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1793
1794
1795
1796
1797
  			fe->dai_link->name);
  
  		ret = dpcm_be_dai_trigger(fe, stream,
  					SNDRV_PCM_TRIGGER_START);
  		if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1798
1799
  			dev_err(fe->dev,"ASoC: trigger FE failed %d
  ", ret);
07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
1800
1801
  			goto hw_free;
  		}
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
  	}
  
  	return 0;
  
  hw_free:
  	dpcm_be_dai_hw_free(fe, stream);
  close:
  	dpcm_be_dai_shutdown(fe, stream);
  disconnect:
  	/* disconnect any non started BEs */
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
  				dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
  	}
  
  	return ret;
  }
  
  static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	int ret;
  
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
  	ret = dpcm_run_update_startup(fe, stream);
  	if (ret < 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1828
1829
  		dev_err(fe->dev, "ASoC: failed to startup some BEs
  ");
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
  
  	return ret;
  }
  
  static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	int ret;
  
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
  	ret = dpcm_run_update_shutdown(fe, stream);
  	if (ret < 0)
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1842
1843
  		dev_err(fe->dev, "ASoC: failed to shutdown some BEs
  ");
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
  	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
  
  	return ret;
  }
  
  /* Called by DAPM mixer/mux changes to update audio routing between PCMs and
   * any DAI links.
   */
  int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget)
  {
  	struct snd_soc_card *card;
  	int i, old, new, paths;
  
  	if (widget->codec)
  		card = widget->codec->card;
  	else if (widget->platform)
  		card = widget->platform->card;
  	else
  		return -EINVAL;
  
  	mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
  	for (i = 0; i < card->num_rtd; i++) {
  		struct snd_soc_dapm_widget_list *list;
  		struct snd_soc_pcm_runtime *fe = &card->rtd[i];
  
  		/* make sure link is FE */
  		if (!fe->dai_link->dynamic)
  			continue;
  
  		/* only check active links */
  		if (!fe->cpu_dai->active)
  			continue;
  
  		/* DAPM sync will call this to update DSP paths */
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1878
1879
  		dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s
  ",
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1880
1881
1882
1883
1884
1885
1886
1887
  			fe->dai_link->name);
  
  		/* skip if FE doesn't have playback capability */
  		if (!fe->cpu_dai->driver->playback.channels_min)
  			goto capture;
  
  		paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
  		if (paths < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1888
1889
  			dev_warn(fe->dev, "ASoC: %s no valid %s path
  ",
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
  					fe->dai_link->name,  "playback");
  			mutex_unlock(&card->mutex);
  			return paths;
  		}
  
  		/* update any new playback paths */
  		new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1);
  		if (new) {
  			dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
  			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
  			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
  		}
  
  		/* update any old playback paths */
  		old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0);
  		if (old) {
  			dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
  			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
  			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
  		}
  
  capture:
  		/* skip if FE doesn't have capture capability */
  		if (!fe->cpu_dai->driver->capture.channels_min)
  			continue;
  
  		paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
  		if (paths < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1918
1919
  			dev_warn(fe->dev, "ASoC: %s no valid %s path
  ",
618dae11f   Liam Girdwood   ASoC: dpcm: Add r...
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
  					fe->dai_link->name,  "capture");
  			mutex_unlock(&card->mutex);
  			return paths;
  		}
  
  		/* update any new capture paths */
  		new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1);
  		if (new) {
  			dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE);
  			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
  			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
  		}
  
  		/* update any old capture paths */
  		old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0);
  		if (old) {
  			dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE);
  			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
  			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
  		}
  
  		dpcm_path_put(&list);
  	}
  
  	mutex_unlock(&card->mutex);
  	return 0;
  }
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
  int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
  {
  	struct snd_soc_dpcm *dpcm;
  	struct list_head *clients =
  		&fe->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients;
  
  	list_for_each_entry(dpcm, clients, list_be) {
  
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		struct snd_soc_dai *dai = be->codec_dai;
  		struct snd_soc_dai_driver *drv = dai->driver;
  
  		if (be->dai_link->ignore_suspend)
  			continue;
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1961
1962
  		dev_dbg(be->dev, "ASoC: BE digital mute %s
  ", be->dai_link->name);
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1963
1964
1965
1966
1967
1968
1969
  
  		if (drv->ops->digital_mute && dai->playback_active)
  				drv->ops->digital_mute(dai, mute);
  	}
  
  	return 0;
  }
45c0a188c   Mark Brown   ASoC: pcm: Static...
1970
  static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
  {
  	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
  	struct snd_soc_dpcm *dpcm;
  	struct snd_soc_dapm_widget_list *list;
  	int ret;
  	int stream = fe_substream->stream;
  
  	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
  	fe->dpcm[stream].runtime = fe_substream->runtime;
  
  	if (dpcm_path_get(fe, stream, &list) <= 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
1982
1983
  		dev_dbg(fe->dev, "ASoC: %s no valid %s route
  ",
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1984
  			fe->dai_link->name, stream ? "capture" : "playback");
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
  	}
  
  	/* calculate valid and active FE <-> BE dpcms */
  	dpcm_process_paths(fe, stream, &list, 1);
  
  	ret = dpcm_fe_dai_startup(fe_substream);
  	if (ret < 0) {
  		/* clean up all links */
  		list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
  			dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
  
  		dpcm_be_disconnect(fe, stream);
  		fe->dpcm[stream].runtime = NULL;
  	}
  
  	dpcm_clear_pending_state(fe, stream);
  	dpcm_path_put(&list);
  	mutex_unlock(&fe->card->mutex);
  	return ret;
  }
45c0a188c   Mark Brown   ASoC: pcm: Static...
2005
  static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
  {
  	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
  	struct snd_soc_dpcm *dpcm;
  	int stream = fe_substream->stream, ret;
  
  	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
  	ret = dpcm_fe_dai_shutdown(fe_substream);
  
  	/* mark FE's links ready to prune */
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
  		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
  
  	dpcm_be_disconnect(fe, stream);
  
  	fe->dpcm[stream].runtime = NULL;
  	mutex_unlock(&fe->card->mutex);
  	return ret;
  }
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2024
2025
2026
  /* create a new pcm */
  int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
  {
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2027
2028
2029
2030
2031
2032
  	struct snd_soc_platform *platform = rtd->platform;
  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  	struct snd_pcm *pcm;
  	char new_name[64];
  	int ret = 0, playback = 0, capture = 0;
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
  	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
  		if (cpu_dai->driver->playback.channels_min)
  			playback = 1;
  		if (cpu_dai->driver->capture.channels_min)
  			capture = 1;
  	} else {
  		if (codec_dai->driver->playback.channels_min)
  			playback = 1;
  		if (codec_dai->driver->capture.channels_min)
  			capture = 1;
  	}
  
  	/* create the PCM */
  	if (rtd->dai_link->no_pcm) {
  		snprintf(new_name, sizeof(new_name), "(%s)",
  			rtd->dai_link->stream_name);
  
  		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
  				playback, capture, &pcm);
  	} else {
  		if (rtd->dai_link->dynamic)
  			snprintf(new_name, sizeof(new_name), "%s (*)",
  				rtd->dai_link->stream_name);
  		else
  			snprintf(new_name, sizeof(new_name), "%s %s-%d",
  				rtd->dai_link->stream_name, codec_dai->name, num);
  
  		ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
  			capture, &pcm);
  	}
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2063
  	if (ret < 0) {
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
2064
2065
  		dev_err(rtd->card->dev, "ASoC: can't create pcm for %s
  ",
5cb9b7482   Liam Girdwood   ASoC: pcm: Clean ...
2066
  			rtd->dai_link->name);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2067
2068
  		return ret;
  	}
103d84a3c   Liam Girdwood   ASoC: pcm: Standa...
2069
2070
  	dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s
  ",num, new_name);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2071
2072
2073
2074
2075
2076
  
  	/* DAPM dai link stream work */
  	INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
  
  	rtd->pcm = pcm;
  	pcm->private_data = rtd;
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
  
  	if (rtd->dai_link->no_pcm) {
  		if (playback)
  			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
  		if (capture)
  			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
  		goto out;
  	}
  
  	/* ASoC PCM operations */
  	if (rtd->dai_link->dynamic) {
  		rtd->ops.open		= dpcm_fe_dai_open;
  		rtd->ops.hw_params	= dpcm_fe_dai_hw_params;
  		rtd->ops.prepare	= dpcm_fe_dai_prepare;
  		rtd->ops.trigger	= dpcm_fe_dai_trigger;
  		rtd->ops.hw_free	= dpcm_fe_dai_hw_free;
  		rtd->ops.close		= dpcm_fe_dai_close;
  		rtd->ops.pointer	= soc_pcm_pointer;
be3f3f2ce   Liam Girdwood   ASoC: pcm: Add pc...
2095
  		rtd->ops.ioctl		= soc_pcm_ioctl;
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2096
2097
2098
2099
2100
2101
2102
2103
  	} else {
  		rtd->ops.open		= soc_pcm_open;
  		rtd->ops.hw_params	= soc_pcm_hw_params;
  		rtd->ops.prepare	= soc_pcm_prepare;
  		rtd->ops.trigger	= soc_pcm_trigger;
  		rtd->ops.hw_free	= soc_pcm_hw_free;
  		rtd->ops.close		= soc_pcm_close;
  		rtd->ops.pointer	= soc_pcm_pointer;
be3f3f2ce   Liam Girdwood   ASoC: pcm: Add pc...
2104
  		rtd->ops.ioctl		= soc_pcm_ioctl;
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2105
  	}
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2106
  	if (platform->driver->ops) {
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2107
2108
2109
2110
2111
  		rtd->ops.ack		= platform->driver->ops->ack;
  		rtd->ops.copy		= platform->driver->ops->copy;
  		rtd->ops.silence	= platform->driver->ops->silence;
  		rtd->ops.page		= platform->driver->ops->page;
  		rtd->ops.mmap		= platform->driver->ops->mmap;
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2112
2113
2114
  	}
  
  	if (playback)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2115
  		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2116
2117
  
  	if (capture)
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2118
  		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2119
2120
2121
2122
  
  	if (platform->driver->pcm_new) {
  		ret = platform->driver->pcm_new(rtd);
  		if (ret < 0) {
b1bc7b3c9   Mark Brown   ASoC: pcm: Report...
2123
2124
2125
2126
  			dev_err(platform->dev,
  				"ASoC: pcm constructor failed: %d
  ",
  				ret);
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2127
2128
2129
2130
2131
  			return ret;
  		}
  	}
  
  	pcm->private_free = platform->driver->pcm_free;
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2132
  out:
5cb9b7482   Liam Girdwood   ASoC: pcm: Clean ...
2133
2134
  	dev_info(rtd->card->dev, " %s <-> %s mapping ok
  ", codec_dai->name,
ddee627cf   Liam Girdwood   ASoC: core - Sepa...
2135
2136
2137
  		cpu_dai->name);
  	return ret;
  }
01d7584cd   Liam Girdwood   ASoC: dpcm: Add D...
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
  
  /* is the current PCM operation for this FE ? */
  int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream)
  {
  	if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE)
  		return 1;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update);
  
  /* is the current PCM operation for this BE ? */
  int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
  		struct snd_soc_pcm_runtime *be, int stream)
  {
  	if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) ||
  	   ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) &&
  		  be->dpcm[stream].runtime_update))
  		return 1;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update);
  
  /* get the substream for this BE */
  struct snd_pcm_substream *
  	snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream)
  {
  	return be->pcm->streams[stream].substream;
  }
  EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream);
  
  /* get the BE runtime state */
  enum snd_soc_dpcm_state
  	snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream)
  {
  	return be->dpcm[stream].state;
  }
  EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state);
  
  /* set the BE runtime state */
  void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be,
  		int stream, enum snd_soc_dpcm_state state)
  {
  	be->dpcm[stream].state = state;
  }
  EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state);
  
  /*
   * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE
   * are not running, paused or suspended for the specified stream direction.
   */
  int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
  		struct snd_soc_pcm_runtime *be, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  	int state;
  
  	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
  
  		if (dpcm->fe == fe)
  			continue;
  
  		state = dpcm->fe->dpcm[stream].state;
  		if (state == SND_SOC_DPCM_STATE_START ||
  			state == SND_SOC_DPCM_STATE_PAUSED ||
  			state == SND_SOC_DPCM_STATE_SUSPEND)
  			return 0;
  	}
  
  	/* it's safe to free/stop this BE DAI */
  	return 1;
  }
  EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
  
  /*
   * We can only change hw params a BE DAI if any of it's FE are not prepared,
   * running, paused or suspended for the specified stream direction.
   */
  int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
  		struct snd_soc_pcm_runtime *be, int stream)
  {
  	struct snd_soc_dpcm *dpcm;
  	int state;
  
  	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
  
  		if (dpcm->fe == fe)
  			continue;
  
  		state = dpcm->fe->dpcm[stream].state;
  		if (state == SND_SOC_DPCM_STATE_START ||
  			state == SND_SOC_DPCM_STATE_PAUSED ||
  			state == SND_SOC_DPCM_STATE_SUSPEND ||
  			state == SND_SOC_DPCM_STATE_PREPARE)
  			return 0;
  	}
  
  	/* it's safe to change hw_params */
  	return 1;
  }
  EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
2238

07bf84aaf   Liam Girdwood   ASoC: dpcm: Add b...
2239
2240
2241
2242
2243
2244
2245
2246
  int snd_soc_platform_trigger(struct snd_pcm_substream *substream,
  		int cmd, struct snd_soc_platform *platform)
  {
  	if (platform->driver->ops->trigger)
  		return platform->driver->ops->trigger(substream, cmd);
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_soc_platform_trigger);
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
  #ifdef CONFIG_DEBUG_FS
  static char *dpcm_state_string(enum snd_soc_dpcm_state state)
  {
  	switch (state) {
  	case SND_SOC_DPCM_STATE_NEW:
  		return "new";
  	case SND_SOC_DPCM_STATE_OPEN:
  		return "open";
  	case SND_SOC_DPCM_STATE_HW_PARAMS:
  		return "hw_params";
  	case SND_SOC_DPCM_STATE_PREPARE:
  		return "prepare";
  	case SND_SOC_DPCM_STATE_START:
  		return "start";
  	case SND_SOC_DPCM_STATE_STOP:
  		return "stop";
  	case SND_SOC_DPCM_STATE_SUSPEND:
  		return "suspend";
  	case SND_SOC_DPCM_STATE_PAUSED:
  		return "paused";
  	case SND_SOC_DPCM_STATE_HW_FREE:
  		return "hw_free";
  	case SND_SOC_DPCM_STATE_CLOSE:
  		return "close";
  	}
  
  	return "unknown";
  }
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
  static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
  				int stream, char *buf, size_t size)
  {
  	struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
  	struct snd_soc_dpcm *dpcm;
  	ssize_t offset = 0;
  
  	/* FE state */
  	offset += snprintf(buf + offset, size - offset,
  			"[%s - %s]
  ", fe->dai_link->name,
  			stream ? "Capture" : "Playback");
  
  	offset += snprintf(buf + offset, size - offset, "State: %s
  ",
  	                dpcm_state_string(fe->dpcm[stream].state));
  
  	if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
  	    (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
  		offset += snprintf(buf + offset, size - offset,
  				"Hardware Params: "
  				"Format = %s, Channels = %d, Rate = %d
  ",
  				snd_pcm_format_name(params_format(params)),
  				params_channels(params),
  				params_rate(params));
  
  	/* BEs state */
  	offset += snprintf(buf + offset, size - offset, "Backends:
  ");
  
  	if (list_empty(&fe->dpcm[stream].be_clients)) {
  		offset += snprintf(buf + offset, size - offset,
  				" No active DSP links
  ");
  		goto out;
  	}
  
  	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
  		struct snd_soc_pcm_runtime *be = dpcm->be;
  		params = &dpcm->hw_params;
  
  		offset += snprintf(buf + offset, size - offset,
  				"- %s
  ", be->dai_link->name);
  
  		offset += snprintf(buf + offset, size - offset,
  				"   State: %s
  ",
  				dpcm_state_string(be->dpcm[stream].state));
  
  		if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
  		    (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
  			offset += snprintf(buf + offset, size - offset,
  				"   Hardware Params: "
  				"Format = %s, Channels = %d, Rate = %d
  ",
  				snd_pcm_format_name(params_format(params)),
  				params_channels(params),
  				params_rate(params));
  	}
  
  out:
  	return offset;
  }
  
  static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
  				size_t count, loff_t *ppos)
  {
  	struct snd_soc_pcm_runtime *fe = file->private_data;
  	ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
  	char *buf;
  
  	buf = kmalloc(out_count, GFP_KERNEL);
  	if (!buf)
  		return -ENOMEM;
  
  	if (fe->cpu_dai->driver->playback.channels_min)
  		offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK,
  					buf + offset, out_count - offset);
  
  	if (fe->cpu_dai->driver->capture.channels_min)
  		offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE,
  					buf + offset, out_count - offset);
f57b8488b   Liam Girdwood   ASoC: dpcm: Fixup...
2359
  	ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
2360

f57b8488b   Liam Girdwood   ASoC: dpcm: Fixup...
2361
2362
  	kfree(buf);
  	return ret;
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
2363
2364
2365
  }
  
  static const struct file_operations dpcm_state_fops = {
f57b8488b   Liam Girdwood   ASoC: dpcm: Fixup...
2366
  	.open = simple_open,
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
2367
2368
2369
2370
2371
2372
  	.read = dpcm_state_read_file,
  	.llseek = default_llseek,
  };
  
  int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
  {
b3bba9a1a   Mark Brown   ASoC: pcm: Fix DP...
2373
2374
  	if (!rtd->dai_link)
  		return 0;
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
2375
2376
2377
2378
2379
2380
2381
2382
2383
  	rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
  			rtd->card->debugfs_card_root);
  	if (!rtd->debugfs_dpcm_root) {
  		dev_dbg(rtd->dev,
  			 "ASoC: Failed to create dpcm debugfs directory %s
  ",
  			 rtd->dai_link->name);
  		return -EINVAL;
  	}
f57b8488b   Liam Girdwood   ASoC: dpcm: Fixup...
2384
  	rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444,
f86dcef87   Liam Girdwood   ASoC: dpcm: Add d...
2385
2386
2387
2388
2389
2390
  						rtd->debugfs_dpcm_root,
  						rtd, &dpcm_state_fops);
  
  	return 0;
  }
  #endif