blob: 93be95b7864c7be2ea9936c8c67020f582a02613 [file] [log] [blame]
Liam Girdwoodddee6272011-06-09 14:45:53 +01001/*
2 * soc-pcm.c -- ALSA SoC PCM
3 *
4 * Copyright 2005 Wolfson Microelectronics PLC.
5 * Copyright 2005 Openedhand Ltd.
6 * Copyright (C) 2010 Slimlogic Ltd.
7 * Copyright (C) 2010 Texas Instruments Inc.
8 *
9 * Authors: Liam Girdwood <lrg@ti.com>
10 * Mark Brown <broonie@opensource.wolfsonmicro.com>
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version.
16 *
17 */
18
19#include <linux/kernel.h>
20#include <linux/init.h>
21#include <linux/delay.h>
Mark Brownd6652ef2011-12-03 20:14:31 +000022#include <linux/pm_runtime.h>
Liam Girdwoodddee6272011-06-09 14:45:53 +010023#include <linux/slab.h>
24#include <linux/workqueue.h>
25#include <sound/core.h>
26#include <sound/pcm.h>
27#include <sound/pcm_params.h>
28#include <sound/soc.h>
29#include <sound/initval.h>
30
Dong Aisheng17841022011-08-29 17:15:14 +080031static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
32 struct snd_soc_dai *soc_dai)
Liam Girdwoodddee6272011-06-09 14:45:53 +010033{
34 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodddee6272011-06-09 14:45:53 +010035 int ret;
36
Dong Aisheng17841022011-08-29 17:15:14 +080037 if (!soc_dai->driver->symmetric_rates &&
Liam Girdwoodddee6272011-06-09 14:45:53 +010038 !rtd->dai_link->symmetric_rates)
39 return 0;
40
41 /* This can happen if multiple streams are starting simultaneously -
42 * the second can need to get its constraints before the first has
43 * picked a rate. Complain and allow the application to carry on.
44 */
Dong Aisheng17841022011-08-29 17:15:14 +080045 if (!soc_dai->rate) {
46 dev_warn(soc_dai->dev,
Liam Girdwoodddee6272011-06-09 14:45:53 +010047 "Not enforcing symmetric_rates due to race\n");
48 return 0;
49 }
50
Dong Aisheng17841022011-08-29 17:15:14 +080051 dev_dbg(soc_dai->dev, "Symmetry forces %dHz rate\n", soc_dai->rate);
Liam Girdwoodddee6272011-06-09 14:45:53 +010052
53 ret = snd_pcm_hw_constraint_minmax(substream->runtime,
54 SNDRV_PCM_HW_PARAM_RATE,
Dong Aisheng17841022011-08-29 17:15:14 +080055 soc_dai->rate, soc_dai->rate);
Liam Girdwoodddee6272011-06-09 14:45:53 +010056 if (ret < 0) {
Dong Aisheng17841022011-08-29 17:15:14 +080057 dev_err(soc_dai->dev,
Liam Girdwoodddee6272011-06-09 14:45:53 +010058 "Unable to apply rate symmetry constraint: %d\n", ret);
59 return ret;
60 }
61
62 return 0;
63}
64
65/*
Mark Brown58ba9b22012-01-16 18:38:51 +000066 * List of sample sizes that might go over the bus for parameter
67 * application. There ought to be a wildcard sample size for things
68 * like the DAC/ADC resolution to use but there isn't right now.
69 */
70static int sample_sizes[] = {
Peter Ujfalusi88e33952012-01-25 10:09:41 +020071 24, 32,
Mark Brown58ba9b22012-01-16 18:38:51 +000072};
73
74static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
75 struct snd_soc_dai *dai)
76{
77 int ret, i, bits;
78
79 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
80 bits = dai->driver->playback.sig_bits;
81 else
82 bits = dai->driver->capture.sig_bits;
83
84 if (!bits)
85 return;
86
87 for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) {
Mark Brown278047f2012-01-19 18:04:18 +000088 if (bits >= sample_sizes[i])
89 continue;
90
91 ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0,
92 sample_sizes[i], bits);
Mark Brown58ba9b22012-01-16 18:38:51 +000093 if (ret != 0)
94 dev_warn(dai->dev,
95 "Failed to set MSB %d/%d: %d\n",
96 bits, sample_sizes[i], ret);
97 }
98}
99
100/*
Liam Girdwoodddee6272011-06-09 14:45:53 +0100101 * Called by ALSA when a PCM substream is opened, the runtime->hw record is
102 * then initialized and any private data can be allocated. This also calls
103 * startup for the cpu DAI, platform, machine and codec DAI.
104 */
105static int soc_pcm_open(struct snd_pcm_substream *substream)
106{
107 struct snd_soc_pcm_runtime *rtd = substream->private_data;
108 struct snd_pcm_runtime *runtime = substream->runtime;
109 struct snd_soc_platform *platform = rtd->platform;
110 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
111 struct snd_soc_dai *codec_dai = rtd->codec_dai;
112 struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
113 struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
114 int ret = 0;
115
Mark Brownd6652ef2011-12-03 20:14:31 +0000116 pm_runtime_get_sync(cpu_dai->dev);
117 pm_runtime_get_sync(codec_dai->dev);
118 pm_runtime_get_sync(platform->dev);
119
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100120 mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100121
122 /* startup the audio subsystem */
123 if (cpu_dai->driver->ops->startup) {
124 ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
125 if (ret < 0) {
126 printk(KERN_ERR "asoc: can't open interface %s\n",
127 cpu_dai->name);
128 goto out;
129 }
130 }
131
132 if (platform->driver->ops && platform->driver->ops->open) {
133 ret = platform->driver->ops->open(substream);
134 if (ret < 0) {
135 printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
136 goto platform_err;
137 }
138 }
139
140 if (codec_dai->driver->ops->startup) {
141 ret = codec_dai->driver->ops->startup(substream, codec_dai);
142 if (ret < 0) {
143 printk(KERN_ERR "asoc: can't open codec %s\n",
144 codec_dai->name);
145 goto codec_dai_err;
146 }
147 }
148
149 if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
150 ret = rtd->dai_link->ops->startup(substream);
151 if (ret < 0) {
152 printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
153 goto machine_err;
154 }
155 }
156
157 /* Check that the codec and cpu DAIs are compatible */
158 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
159 runtime->hw.rate_min =
160 max(codec_dai_drv->playback.rate_min,
161 cpu_dai_drv->playback.rate_min);
162 runtime->hw.rate_max =
163 min(codec_dai_drv->playback.rate_max,
164 cpu_dai_drv->playback.rate_max);
165 runtime->hw.channels_min =
166 max(codec_dai_drv->playback.channels_min,
167 cpu_dai_drv->playback.channels_min);
168 runtime->hw.channels_max =
169 min(codec_dai_drv->playback.channels_max,
170 cpu_dai_drv->playback.channels_max);
171 runtime->hw.formats =
172 codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
173 runtime->hw.rates =
174 codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
175 if (codec_dai_drv->playback.rates
176 & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
177 runtime->hw.rates |= cpu_dai_drv->playback.rates;
178 if (cpu_dai_drv->playback.rates
179 & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
180 runtime->hw.rates |= codec_dai_drv->playback.rates;
181 } else {
182 runtime->hw.rate_min =
183 max(codec_dai_drv->capture.rate_min,
184 cpu_dai_drv->capture.rate_min);
185 runtime->hw.rate_max =
186 min(codec_dai_drv->capture.rate_max,
187 cpu_dai_drv->capture.rate_max);
188 runtime->hw.channels_min =
189 max(codec_dai_drv->capture.channels_min,
190 cpu_dai_drv->capture.channels_min);
191 runtime->hw.channels_max =
192 min(codec_dai_drv->capture.channels_max,
193 cpu_dai_drv->capture.channels_max);
194 runtime->hw.formats =
195 codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
196 runtime->hw.rates =
197 codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
198 if (codec_dai_drv->capture.rates
199 & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
200 runtime->hw.rates |= cpu_dai_drv->capture.rates;
201 if (cpu_dai_drv->capture.rates
202 & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
203 runtime->hw.rates |= codec_dai_drv->capture.rates;
204 }
205
206 ret = -EINVAL;
207 snd_pcm_limit_hw_rates(runtime);
208 if (!runtime->hw.rates) {
209 printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
210 codec_dai->name, cpu_dai->name);
211 goto config_err;
212 }
213 if (!runtime->hw.formats) {
214 printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
215 codec_dai->name, cpu_dai->name);
216 goto config_err;
217 }
218 if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
219 runtime->hw.channels_min > runtime->hw.channels_max) {
220 printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
221 codec_dai->name, cpu_dai->name);
222 goto config_err;
223 }
224
Mark Brown58ba9b22012-01-16 18:38:51 +0000225 soc_pcm_apply_msb(substream, codec_dai);
226 soc_pcm_apply_msb(substream, cpu_dai);
227
Liam Girdwoodddee6272011-06-09 14:45:53 +0100228 /* Symmetry only applies if we've already got an active stream. */
Dong Aisheng17841022011-08-29 17:15:14 +0800229 if (cpu_dai->active) {
230 ret = soc_pcm_apply_symmetry(substream, cpu_dai);
231 if (ret != 0)
232 goto config_err;
233 }
234
235 if (codec_dai->active) {
236 ret = soc_pcm_apply_symmetry(substream, codec_dai);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100237 if (ret != 0)
238 goto config_err;
239 }
240
241 pr_debug("asoc: %s <-> %s info:\n",
242 codec_dai->name, cpu_dai->name);
243 pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
244 pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
245 runtime->hw.channels_max);
246 pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
247 runtime->hw.rate_max);
248
249 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
250 cpu_dai->playback_active++;
251 codec_dai->playback_active++;
252 } else {
253 cpu_dai->capture_active++;
254 codec_dai->capture_active++;
255 }
256 cpu_dai->active++;
257 codec_dai->active++;
258 rtd->codec->active++;
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100259 mutex_unlock(&rtd->pcm_mutex);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100260 return 0;
261
262config_err:
263 if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
264 rtd->dai_link->ops->shutdown(substream);
265
266machine_err:
267 if (codec_dai->driver->ops->shutdown)
268 codec_dai->driver->ops->shutdown(substream, codec_dai);
269
270codec_dai_err:
271 if (platform->driver->ops && platform->driver->ops->close)
272 platform->driver->ops->close(substream);
273
274platform_err:
275 if (cpu_dai->driver->ops->shutdown)
276 cpu_dai->driver->ops->shutdown(substream, cpu_dai);
277out:
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100278 mutex_unlock(&rtd->pcm_mutex);
Mark Brownd6652ef2011-12-03 20:14:31 +0000279
280 pm_runtime_put(platform->dev);
281 pm_runtime_put(codec_dai->dev);
282 pm_runtime_put(cpu_dai->dev);
283
Liam Girdwoodddee6272011-06-09 14:45:53 +0100284 return ret;
285}
286
287/*
288 * Power down the audio subsystem pmdown_time msecs after close is called.
289 * This is to ensure there are no pops or clicks in between any music tracks
290 * due to DAPM power cycling.
291 */
292static void close_delayed_work(struct work_struct *work)
293{
294 struct snd_soc_pcm_runtime *rtd =
295 container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
296 struct snd_soc_dai *codec_dai = rtd->codec_dai;
297
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100298 mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100299
300 pr_debug("pop wq checking: %s status: %s waiting: %s\n",
301 codec_dai->driver->playback.stream_name,
302 codec_dai->playback_active ? "active" : "inactive",
303 codec_dai->pop_wait ? "yes" : "no");
304
305 /* are we waiting on this codec DAI stream */
306 if (codec_dai->pop_wait == 1) {
307 codec_dai->pop_wait = 0;
308 snd_soc_dapm_stream_event(rtd,
309 codec_dai->driver->playback.stream_name,
310 SND_SOC_DAPM_STREAM_STOP);
311 }
312
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100313 mutex_unlock(&rtd->pcm_mutex);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100314}
315
316/*
317 * Called by ALSA when a PCM substream is closed. Private data can be
318 * freed here. The cpu DAI, codec DAI, machine and platform are also
319 * shutdown.
320 */
Liam Girdwood91d5e6b2011-06-09 17:04:59 +0100321static int soc_pcm_close(struct snd_pcm_substream *substream)
Liam Girdwoodddee6272011-06-09 14:45:53 +0100322{
323 struct snd_soc_pcm_runtime *rtd = substream->private_data;
324 struct snd_soc_platform *platform = rtd->platform;
325 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
326 struct snd_soc_dai *codec_dai = rtd->codec_dai;
327 struct snd_soc_codec *codec = rtd->codec;
328
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100329 mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100330
331 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
332 cpu_dai->playback_active--;
333 codec_dai->playback_active--;
334 } else {
335 cpu_dai->capture_active--;
336 codec_dai->capture_active--;
337 }
338
339 cpu_dai->active--;
340 codec_dai->active--;
341 codec->active--;
342
Dong Aisheng17841022011-08-29 17:15:14 +0800343 /* clear the corresponding DAIs rate when inactive */
344 if (!cpu_dai->active)
345 cpu_dai->rate = 0;
346
347 if (!codec_dai->active)
348 codec_dai->rate = 0;
Sascha Hauer25b76792011-08-17 09:20:01 +0200349
Liam Girdwoodddee6272011-06-09 14:45:53 +0100350 /* Muting the DAC suppresses artifacts caused during digital
351 * shutdown, for example from stopping clocks.
352 */
353 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
354 snd_soc_dai_digital_mute(codec_dai, 1);
355
356 if (cpu_dai->driver->ops->shutdown)
357 cpu_dai->driver->ops->shutdown(substream, cpu_dai);
358
359 if (codec_dai->driver->ops->shutdown)
360 codec_dai->driver->ops->shutdown(substream, codec_dai);
361
362 if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
363 rtd->dai_link->ops->shutdown(substream);
364
365 if (platform->driver->ops && platform->driver->ops->close)
366 platform->driver->ops->close(substream);
367 cpu_dai->runtime = NULL;
368
369 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
Mark Brown5b6247a2011-10-27 12:11:26 +0200370 if (codec->ignore_pmdown_time ||
371 rtd->dai_link->ignore_pmdown_time) {
Peter Ujfalusi1d69c5c2011-10-14 14:43:33 +0300372 /* powered down playback stream now */
373 snd_soc_dapm_stream_event(rtd,
374 codec_dai->driver->playback.stream_name,
375 SND_SOC_DAPM_STREAM_STOP);
376 } else {
377 /* start delayed pop wq here for playback streams */
378 codec_dai->pop_wait = 1;
379 schedule_delayed_work(&rtd->delayed_work,
380 msecs_to_jiffies(rtd->pmdown_time));
381 }
Liam Girdwoodddee6272011-06-09 14:45:53 +0100382 } else {
383 /* capture streams can be powered down now */
384 snd_soc_dapm_stream_event(rtd,
385 codec_dai->driver->capture.stream_name,
386 SND_SOC_DAPM_STREAM_STOP);
387 }
388
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100389 mutex_unlock(&rtd->pcm_mutex);
Mark Brownd6652ef2011-12-03 20:14:31 +0000390
391 pm_runtime_put(platform->dev);
392 pm_runtime_put(codec_dai->dev);
393 pm_runtime_put(cpu_dai->dev);
394
Liam Girdwoodddee6272011-06-09 14:45:53 +0100395 return 0;
396}
397
398/*
399 * Called by ALSA when the PCM substream is prepared, can set format, sample
400 * rate, etc. This function is non atomic and can be called multiple times,
401 * it can refer to the runtime info.
402 */
403static int soc_pcm_prepare(struct snd_pcm_substream *substream)
404{
405 struct snd_soc_pcm_runtime *rtd = substream->private_data;
406 struct snd_soc_platform *platform = rtd->platform;
407 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
408 struct snd_soc_dai *codec_dai = rtd->codec_dai;
409 int ret = 0;
410
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100411 mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100412
413 if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
414 ret = rtd->dai_link->ops->prepare(substream);
415 if (ret < 0) {
416 printk(KERN_ERR "asoc: machine prepare error\n");
417 goto out;
418 }
419 }
420
421 if (platform->driver->ops && platform->driver->ops->prepare) {
422 ret = platform->driver->ops->prepare(substream);
423 if (ret < 0) {
424 printk(KERN_ERR "asoc: platform prepare error\n");
425 goto out;
426 }
427 }
428
429 if (codec_dai->driver->ops->prepare) {
430 ret = codec_dai->driver->ops->prepare(substream, codec_dai);
431 if (ret < 0) {
432 printk(KERN_ERR "asoc: codec DAI prepare error\n");
433 goto out;
434 }
435 }
436
437 if (cpu_dai->driver->ops->prepare) {
438 ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
439 if (ret < 0) {
440 printk(KERN_ERR "asoc: cpu DAI prepare error\n");
441 goto out;
442 }
443 }
444
445 /* cancel any delayed stream shutdown that is pending */
446 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
447 codec_dai->pop_wait) {
448 codec_dai->pop_wait = 0;
449 cancel_delayed_work(&rtd->delayed_work);
450 }
451
452 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
453 snd_soc_dapm_stream_event(rtd,
454 codec_dai->driver->playback.stream_name,
455 SND_SOC_DAPM_STREAM_START);
456 else
457 snd_soc_dapm_stream_event(rtd,
458 codec_dai->driver->capture.stream_name,
459 SND_SOC_DAPM_STREAM_START);
460
461 snd_soc_dai_digital_mute(codec_dai, 0);
462
463out:
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100464 mutex_unlock(&rtd->pcm_mutex);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100465 return ret;
466}
467
468/*
469 * Called by ALSA when the hardware params are set by application. This
470 * function can also be called multiple times and can allocate buffers
471 * (using snd_pcm_lib_* ). It's non-atomic.
472 */
473static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
474 struct snd_pcm_hw_params *params)
475{
476 struct snd_soc_pcm_runtime *rtd = substream->private_data;
477 struct snd_soc_platform *platform = rtd->platform;
478 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
479 struct snd_soc_dai *codec_dai = rtd->codec_dai;
480 int ret = 0;
481
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100482 mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100483
484 if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
485 ret = rtd->dai_link->ops->hw_params(substream, params);
486 if (ret < 0) {
487 printk(KERN_ERR "asoc: machine hw_params failed\n");
488 goto out;
489 }
490 }
491
492 if (codec_dai->driver->ops->hw_params) {
493 ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
494 if (ret < 0) {
495 printk(KERN_ERR "asoc: can't set codec %s hw params\n",
496 codec_dai->name);
497 goto codec_err;
498 }
499 }
500
501 if (cpu_dai->driver->ops->hw_params) {
502 ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
503 if (ret < 0) {
504 printk(KERN_ERR "asoc: interface %s hw params failed\n",
505 cpu_dai->name);
506 goto interface_err;
507 }
508 }
509
510 if (platform->driver->ops && platform->driver->ops->hw_params) {
511 ret = platform->driver->ops->hw_params(substream, params);
512 if (ret < 0) {
513 printk(KERN_ERR "asoc: platform %s hw params failed\n",
514 platform->name);
515 goto platform_err;
516 }
517 }
518
Dong Aisheng17841022011-08-29 17:15:14 +0800519 /* store the rate for each DAIs */
520 cpu_dai->rate = params_rate(params);
521 codec_dai->rate = params_rate(params);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100522
523out:
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100524 mutex_unlock(&rtd->pcm_mutex);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100525 return ret;
526
527platform_err:
528 if (cpu_dai->driver->ops->hw_free)
529 cpu_dai->driver->ops->hw_free(substream, cpu_dai);
530
531interface_err:
532 if (codec_dai->driver->ops->hw_free)
533 codec_dai->driver->ops->hw_free(substream, codec_dai);
534
535codec_err:
536 if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
537 rtd->dai_link->ops->hw_free(substream);
538
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100539 mutex_unlock(&rtd->pcm_mutex);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100540 return ret;
541}
542
543/*
544 * Frees resources allocated by hw_params, can be called multiple times
545 */
546static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
547{
548 struct snd_soc_pcm_runtime *rtd = substream->private_data;
549 struct snd_soc_platform *platform = rtd->platform;
550 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
551 struct snd_soc_dai *codec_dai = rtd->codec_dai;
552 struct snd_soc_codec *codec = rtd->codec;
553
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100554 mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100555
556 /* apply codec digital mute */
557 if (!codec->active)
558 snd_soc_dai_digital_mute(codec_dai, 1);
559
560 /* free any machine hw params */
561 if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
562 rtd->dai_link->ops->hw_free(substream);
563
564 /* free any DMA resources */
565 if (platform->driver->ops && platform->driver->ops->hw_free)
566 platform->driver->ops->hw_free(substream);
567
568 /* now free hw params for the DAIs */
569 if (codec_dai->driver->ops->hw_free)
570 codec_dai->driver->ops->hw_free(substream, codec_dai);
571
572 if (cpu_dai->driver->ops->hw_free)
573 cpu_dai->driver->ops->hw_free(substream, cpu_dai);
574
Liam Girdwoodb8c0dab2011-06-09 17:04:39 +0100575 mutex_unlock(&rtd->pcm_mutex);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100576 return 0;
577}
578
579static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
580{
581 struct snd_soc_pcm_runtime *rtd = substream->private_data;
582 struct snd_soc_platform *platform = rtd->platform;
583 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
584 struct snd_soc_dai *codec_dai = rtd->codec_dai;
585 int ret;
586
587 if (codec_dai->driver->ops->trigger) {
588 ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
589 if (ret < 0)
590 return ret;
591 }
592
593 if (platform->driver->ops && platform->driver->ops->trigger) {
594 ret = platform->driver->ops->trigger(substream, cmd);
595 if (ret < 0)
596 return ret;
597 }
598
599 if (cpu_dai->driver->ops->trigger) {
600 ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
601 if (ret < 0)
602 return ret;
603 }
604 return 0;
605}
606
607/*
608 * soc level wrapper for pointer callback
609 * If cpu_dai, codec_dai, platform driver has the delay callback, than
610 * the runtime->delay will be updated accordingly.
611 */
612static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
613{
614 struct snd_soc_pcm_runtime *rtd = substream->private_data;
615 struct snd_soc_platform *platform = rtd->platform;
616 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
617 struct snd_soc_dai *codec_dai = rtd->codec_dai;
618 struct snd_pcm_runtime *runtime = substream->runtime;
619 snd_pcm_uframes_t offset = 0;
620 snd_pcm_sframes_t delay = 0;
621
622 if (platform->driver->ops && platform->driver->ops->pointer)
623 offset = platform->driver->ops->pointer(substream);
624
625 if (cpu_dai->driver->ops->delay)
626 delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
627
628 if (codec_dai->driver->ops->delay)
629 delay += codec_dai->driver->ops->delay(substream, codec_dai);
630
631 if (platform->driver->delay)
632 delay += platform->driver->delay(substream, codec_dai);
633
634 runtime->delay = delay;
635
636 return offset;
637}
638
Liam Girdwoodddee6272011-06-09 14:45:53 +0100639/* create a new pcm */
640int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
641{
642 struct snd_soc_codec *codec = rtd->codec;
643 struct snd_soc_platform *platform = rtd->platform;
644 struct snd_soc_dai *codec_dai = rtd->codec_dai;
645 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
Sangsu Parka5002312012-01-02 17:15:10 +0900646 struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
Liam Girdwoodddee6272011-06-09 14:45:53 +0100647 struct snd_pcm *pcm;
648 char new_name[64];
649 int ret = 0, playback = 0, capture = 0;
650
Sangsu Parka5002312012-01-02 17:15:10 +0900651 soc_pcm_ops->open = soc_pcm_open;
652 soc_pcm_ops->close = soc_pcm_close;
653 soc_pcm_ops->hw_params = soc_pcm_hw_params;
654 soc_pcm_ops->hw_free = soc_pcm_hw_free;
655 soc_pcm_ops->prepare = soc_pcm_prepare;
656 soc_pcm_ops->trigger = soc_pcm_trigger;
657 soc_pcm_ops->pointer = soc_pcm_pointer;
658
Liam Girdwoodddee6272011-06-09 14:45:53 +0100659 /* check client and interface hw capabilities */
660 snprintf(new_name, sizeof(new_name), "%s %s-%d",
661 rtd->dai_link->stream_name, codec_dai->name, num);
662
663 if (codec_dai->driver->playback.channels_min)
664 playback = 1;
665 if (codec_dai->driver->capture.channels_min)
666 capture = 1;
667
668 dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
669 ret = snd_pcm_new(rtd->card->snd_card, new_name,
670 num, playback, capture, &pcm);
671 if (ret < 0) {
672 printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
673 return ret;
674 }
675
676 /* DAPM dai link stream work */
677 INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
678
679 rtd->pcm = pcm;
680 pcm->private_data = rtd;
681 if (platform->driver->ops) {
Sangsu Parka5002312012-01-02 17:15:10 +0900682 soc_pcm_ops->mmap = platform->driver->ops->mmap;
683 soc_pcm_ops->pointer = platform->driver->ops->pointer;
684 soc_pcm_ops->ioctl = platform->driver->ops->ioctl;
685 soc_pcm_ops->copy = platform->driver->ops->copy;
686 soc_pcm_ops->silence = platform->driver->ops->silence;
687 soc_pcm_ops->ack = platform->driver->ops->ack;
688 soc_pcm_ops->page = platform->driver->ops->page;
Liam Girdwoodddee6272011-06-09 14:45:53 +0100689 }
690
691 if (playback)
Sangsu Parka5002312012-01-02 17:15:10 +0900692 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100693
694 if (capture)
Sangsu Parka5002312012-01-02 17:15:10 +0900695 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);
Liam Girdwoodddee6272011-06-09 14:45:53 +0100696
697 if (platform->driver->pcm_new) {
698 ret = platform->driver->pcm_new(rtd);
699 if (ret < 0) {
700 pr_err("asoc: platform pcm constructor failed\n");
701 return ret;
702 }
703 }
704
705 pcm->private_free = platform->driver->pcm_free;
706 printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
707 cpu_dai->name);
708 return ret;
709}