aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-pcm.c
diff options
context:
space:
mode:
authorLiam Girdwood <lrg@ti.com>2011-06-09 09:45:53 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-06-09 10:07:27 -0400
commitddee627cf6bb601aa980104fc17d4f84201380be (patch)
tree6fac651b4891e65dd4c869fffb672a40933913c1 /sound/soc/soc-pcm.c
parentbf564ea99797f6e66796d0d0a24a8fe872d5f26e (diff)
ASoC: core - Separate out PCM operations into new file.
In preparation for Dynamic PCM support (AKA DSP support). There will be future patches that add support to allow PCMs to be dynamically routed to multiple DAIs at startup and also during stream runtime. This patch moves the ASoC core PCM operaitions into a new file called soc-pcm.c. This will in simplify the ASoC core features into distinct files. Signed-off-by: Liam Girdwood <lrg@ti.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/soc-pcm.c')
-rw-r--r--sound/soc/soc-pcm.c639
1 files changed, 639 insertions, 0 deletions
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
new file mode 100644
index 000000000000..9bebee82bc15
--- /dev/null
+++ b/sound/soc/soc-pcm.c
@@ -0,0 +1,639 @@
1/*
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>
22#include <linux/slab.h>
23#include <linux/workqueue.h>
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include <sound/pcm_params.h>
27#include <sound/soc.h>
28#include <sound/initval.h>
29
30static DEFINE_MUTEX(pcm_mutex);
31
32static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
33{
34 struct snd_soc_pcm_runtime *rtd = substream->private_data;
35 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
36 struct snd_soc_dai *codec_dai = rtd->codec_dai;
37 int ret;
38
39 if (!codec_dai->driver->symmetric_rates &&
40 !cpu_dai->driver->symmetric_rates &&
41 !rtd->dai_link->symmetric_rates)
42 return 0;
43
44 /* This can happen if multiple streams are starting simultaneously -
45 * the second can need to get its constraints before the first has
46 * picked a rate. Complain and allow the application to carry on.
47 */
48 if (!rtd->rate) {
49 dev_warn(&rtd->dev,
50 "Not enforcing symmetric_rates due to race\n");
51 return 0;
52 }
53
54 dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate);
55
56 ret = snd_pcm_hw_constraint_minmax(substream->runtime,
57 SNDRV_PCM_HW_PARAM_RATE,
58 rtd->rate, rtd->rate);
59 if (ret < 0) {
60 dev_err(&rtd->dev,
61 "Unable to apply rate symmetry constraint: %d\n", ret);
62 return ret;
63 }
64
65 return 0;
66}
67
68/*
69 * Called by ALSA when a PCM substream is opened, the runtime->hw record is
70 * then initialized and any private data can be allocated. This also calls
71 * startup for the cpu DAI, platform, machine and codec DAI.
72 */
73static int soc_pcm_open(struct snd_pcm_substream *substream)
74{
75 struct snd_soc_pcm_runtime *rtd = substream->private_data;
76 struct snd_pcm_runtime *runtime = substream->runtime;
77 struct snd_soc_platform *platform = rtd->platform;
78 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
79 struct snd_soc_dai *codec_dai = rtd->codec_dai;
80 struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
81 struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
82 int ret = 0;
83
84 mutex_lock(&pcm_mutex);
85
86 /* startup the audio subsystem */
87 if (cpu_dai->driver->ops->startup) {
88 ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
89 if (ret < 0) {
90 printk(KERN_ERR "asoc: can't open interface %s\n",
91 cpu_dai->name);
92 goto out;
93 }
94 }
95
96 if (platform->driver->ops && platform->driver->ops->open) {
97 ret = platform->driver->ops->open(substream);
98 if (ret < 0) {
99 printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
100 goto platform_err;
101 }
102 }
103
104 if (codec_dai->driver->ops->startup) {
105 ret = codec_dai->driver->ops->startup(substream, codec_dai);
106 if (ret < 0) {
107 printk(KERN_ERR "asoc: can't open codec %s\n",
108 codec_dai->name);
109 goto codec_dai_err;
110 }
111 }
112
113 if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
114 ret = rtd->dai_link->ops->startup(substream);
115 if (ret < 0) {
116 printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
117 goto machine_err;
118 }
119 }
120
121 /* Check that the codec and cpu DAIs are compatible */
122 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
123 runtime->hw.rate_min =
124 max(codec_dai_drv->playback.rate_min,
125 cpu_dai_drv->playback.rate_min);
126 runtime->hw.rate_max =
127 min(codec_dai_drv->playback.rate_max,
128 cpu_dai_drv->playback.rate_max);
129 runtime->hw.channels_min =
130 max(codec_dai_drv->playback.channels_min,
131 cpu_dai_drv->playback.channels_min);
132 runtime->hw.channels_max =
133 min(codec_dai_drv->playback.channels_max,
134 cpu_dai_drv->playback.channels_max);
135 runtime->hw.formats =
136 codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
137 runtime->hw.rates =
138 codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
139 if (codec_dai_drv->playback.rates
140 & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
141 runtime->hw.rates |= cpu_dai_drv->playback.rates;
142 if (cpu_dai_drv->playback.rates
143 & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
144 runtime->hw.rates |= codec_dai_drv->playback.rates;
145 } else {
146 runtime->hw.rate_min =
147 max(codec_dai_drv->capture.rate_min,
148 cpu_dai_drv->capture.rate_min);
149 runtime->hw.rate_max =
150 min(codec_dai_drv->capture.rate_max,
151 cpu_dai_drv->capture.rate_max);
152 runtime->hw.channels_min =
153 max(codec_dai_drv->capture.channels_min,
154 cpu_dai_drv->capture.channels_min);
155 runtime->hw.channels_max =
156 min(codec_dai_drv->capture.channels_max,
157 cpu_dai_drv->capture.channels_max);
158 runtime->hw.formats =
159 codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
160 runtime->hw.rates =
161 codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
162 if (codec_dai_drv->capture.rates
163 & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
164 runtime->hw.rates |= cpu_dai_drv->capture.rates;
165 if (cpu_dai_drv->capture.rates
166 & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
167 runtime->hw.rates |= codec_dai_drv->capture.rates;
168 }
169
170 ret = -EINVAL;
171 snd_pcm_limit_hw_rates(runtime);
172 if (!runtime->hw.rates) {
173 printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
174 codec_dai->name, cpu_dai->name);
175 goto config_err;
176 }
177 if (!runtime->hw.formats) {
178 printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
179 codec_dai->name, cpu_dai->name);
180 goto config_err;
181 }
182 if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
183 runtime->hw.channels_min > runtime->hw.channels_max) {
184 printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
185 codec_dai->name, cpu_dai->name);
186 goto config_err;
187 }
188
189 /* Symmetry only applies if we've already got an active stream. */
190 if (cpu_dai->active || codec_dai->active) {
191 ret = soc_pcm_apply_symmetry(substream);
192 if (ret != 0)
193 goto config_err;
194 }
195
196 pr_debug("asoc: %s <-> %s info:\n",
197 codec_dai->name, cpu_dai->name);
198 pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
199 pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
200 runtime->hw.channels_max);
201 pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
202 runtime->hw.rate_max);
203
204 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
205 cpu_dai->playback_active++;
206 codec_dai->playback_active++;
207 } else {
208 cpu_dai->capture_active++;
209 codec_dai->capture_active++;
210 }
211 cpu_dai->active++;
212 codec_dai->active++;
213 rtd->codec->active++;
214 mutex_unlock(&pcm_mutex);
215 return 0;
216
217config_err:
218 if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
219 rtd->dai_link->ops->shutdown(substream);
220
221machine_err:
222 if (codec_dai->driver->ops->shutdown)
223 codec_dai->driver->ops->shutdown(substream, codec_dai);
224
225codec_dai_err:
226 if (platform->driver->ops && platform->driver->ops->close)
227 platform->driver->ops->close(substream);
228
229platform_err:
230 if (cpu_dai->driver->ops->shutdown)
231 cpu_dai->driver->ops->shutdown(substream, cpu_dai);
232out:
233 mutex_unlock(&pcm_mutex);
234 return ret;
235}
236
237/*
238 * Power down the audio subsystem pmdown_time msecs after close is called.
239 * This is to ensure there are no pops or clicks in between any music tracks
240 * due to DAPM power cycling.
241 */
242static void close_delayed_work(struct work_struct *work)
243{
244 struct snd_soc_pcm_runtime *rtd =
245 container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
246 struct snd_soc_dai *codec_dai = rtd->codec_dai;
247
248 mutex_lock(&pcm_mutex);
249
250 pr_debug("pop wq checking: %s status: %s waiting: %s\n",
251 codec_dai->driver->playback.stream_name,
252 codec_dai->playback_active ? "active" : "inactive",
253 codec_dai->pop_wait ? "yes" : "no");
254
255 /* are we waiting on this codec DAI stream */
256 if (codec_dai->pop_wait == 1) {
257 codec_dai->pop_wait = 0;
258 snd_soc_dapm_stream_event(rtd,
259 codec_dai->driver->playback.stream_name,
260 SND_SOC_DAPM_STREAM_STOP);
261 }
262
263 mutex_unlock(&pcm_mutex);
264}
265
266/*
267 * Called by ALSA when a PCM substream is closed. Private data can be
268 * freed here. The cpu DAI, codec DAI, machine and platform are also
269 * shutdown.
270 */
271static int soc_codec_close(struct snd_pcm_substream *substream)
272{
273 struct snd_soc_pcm_runtime *rtd = substream->private_data;
274 struct snd_soc_platform *platform = rtd->platform;
275 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
276 struct snd_soc_dai *codec_dai = rtd->codec_dai;
277 struct snd_soc_codec *codec = rtd->codec;
278
279 mutex_lock(&pcm_mutex);
280
281 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
282 cpu_dai->playback_active--;
283 codec_dai->playback_active--;
284 } else {
285 cpu_dai->capture_active--;
286 codec_dai->capture_active--;
287 }
288
289 cpu_dai->active--;
290 codec_dai->active--;
291 codec->active--;
292
293 /* Muting the DAC suppresses artifacts caused during digital
294 * shutdown, for example from stopping clocks.
295 */
296 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
297 snd_soc_dai_digital_mute(codec_dai, 1);
298
299 if (cpu_dai->driver->ops->shutdown)
300 cpu_dai->driver->ops->shutdown(substream, cpu_dai);
301
302 if (codec_dai->driver->ops->shutdown)
303 codec_dai->driver->ops->shutdown(substream, codec_dai);
304
305 if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
306 rtd->dai_link->ops->shutdown(substream);
307
308 if (platform->driver->ops && platform->driver->ops->close)
309 platform->driver->ops->close(substream);
310 cpu_dai->runtime = NULL;
311
312 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
313 /* start delayed pop wq here for playback streams */
314 codec_dai->pop_wait = 1;
315 schedule_delayed_work(&rtd->delayed_work,
316 msecs_to_jiffies(rtd->pmdown_time));
317 } else {
318 /* capture streams can be powered down now */
319 snd_soc_dapm_stream_event(rtd,
320 codec_dai->driver->capture.stream_name,
321 SND_SOC_DAPM_STREAM_STOP);
322 }
323
324 mutex_unlock(&pcm_mutex);
325 return 0;
326}
327
328/*
329 * Called by ALSA when the PCM substream is prepared, can set format, sample
330 * rate, etc. This function is non atomic and can be called multiple times,
331 * it can refer to the runtime info.
332 */
333static int soc_pcm_prepare(struct snd_pcm_substream *substream)
334{
335 struct snd_soc_pcm_runtime *rtd = substream->private_data;
336 struct snd_soc_platform *platform = rtd->platform;
337 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
338 struct snd_soc_dai *codec_dai = rtd->codec_dai;
339 int ret = 0;
340
341 mutex_lock(&pcm_mutex);
342
343 if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
344 ret = rtd->dai_link->ops->prepare(substream);
345 if (ret < 0) {
346 printk(KERN_ERR "asoc: machine prepare error\n");
347 goto out;
348 }
349 }
350
351 if (platform->driver->ops && platform->driver->ops->prepare) {
352 ret = platform->driver->ops->prepare(substream);
353 if (ret < 0) {
354 printk(KERN_ERR "asoc: platform prepare error\n");
355 goto out;
356 }
357 }
358
359 if (codec_dai->driver->ops->prepare) {
360 ret = codec_dai->driver->ops->prepare(substream, codec_dai);
361 if (ret < 0) {
362 printk(KERN_ERR "asoc: codec DAI prepare error\n");
363 goto out;
364 }
365 }
366
367 if (cpu_dai->driver->ops->prepare) {
368 ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
369 if (ret < 0) {
370 printk(KERN_ERR "asoc: cpu DAI prepare error\n");
371 goto out;
372 }
373 }
374
375 /* cancel any delayed stream shutdown that is pending */
376 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
377 codec_dai->pop_wait) {
378 codec_dai->pop_wait = 0;
379 cancel_delayed_work(&rtd->delayed_work);
380 }
381
382 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
383 snd_soc_dapm_stream_event(rtd,
384 codec_dai->driver->playback.stream_name,
385 SND_SOC_DAPM_STREAM_START);
386 else
387 snd_soc_dapm_stream_event(rtd,
388 codec_dai->driver->capture.stream_name,
389 SND_SOC_DAPM_STREAM_START);
390
391 snd_soc_dai_digital_mute(codec_dai, 0);
392
393out:
394 mutex_unlock(&pcm_mutex);
395 return ret;
396}
397
398/*
399 * Called by ALSA when the hardware params are set by application. This
400 * function can also be called multiple times and can allocate buffers
401 * (using snd_pcm_lib_* ). It's non-atomic.
402 */
403static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
404 struct snd_pcm_hw_params *params)
405{
406 struct snd_soc_pcm_runtime *rtd = substream->private_data;
407 struct snd_soc_platform *platform = rtd->platform;
408 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
409 struct snd_soc_dai *codec_dai = rtd->codec_dai;
410 int ret = 0;
411
412 mutex_lock(&pcm_mutex);
413
414 if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
415 ret = rtd->dai_link->ops->hw_params(substream, params);
416 if (ret < 0) {
417 printk(KERN_ERR "asoc: machine hw_params failed\n");
418 goto out;
419 }
420 }
421
422 if (codec_dai->driver->ops->hw_params) {
423 ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
424 if (ret < 0) {
425 printk(KERN_ERR "asoc: can't set codec %s hw params\n",
426 codec_dai->name);
427 goto codec_err;
428 }
429 }
430
431 if (cpu_dai->driver->ops->hw_params) {
432 ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
433 if (ret < 0) {
434 printk(KERN_ERR "asoc: interface %s hw params failed\n",
435 cpu_dai->name);
436 goto interface_err;
437 }
438 }
439
440 if (platform->driver->ops && platform->driver->ops->hw_params) {
441 ret = platform->driver->ops->hw_params(substream, params);
442 if (ret < 0) {
443 printk(KERN_ERR "asoc: platform %s hw params failed\n",
444 platform->name);
445 goto platform_err;
446 }
447 }
448
449 rtd->rate = params_rate(params);
450
451out:
452 mutex_unlock(&pcm_mutex);
453 return ret;
454
455platform_err:
456 if (cpu_dai->driver->ops->hw_free)
457 cpu_dai->driver->ops->hw_free(substream, cpu_dai);
458
459interface_err:
460 if (codec_dai->driver->ops->hw_free)
461 codec_dai->driver->ops->hw_free(substream, codec_dai);
462
463codec_err:
464 if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
465 rtd->dai_link->ops->hw_free(substream);
466
467 mutex_unlock(&pcm_mutex);
468 return ret;
469}
470
471/*
472 * Frees resources allocated by hw_params, can be called multiple times
473 */
474static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
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 struct snd_soc_codec *codec = rtd->codec;
481
482 mutex_lock(&pcm_mutex);
483
484 /* apply codec digital mute */
485 if (!codec->active)
486 snd_soc_dai_digital_mute(codec_dai, 1);
487
488 /* free any machine hw params */
489 if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
490 rtd->dai_link->ops->hw_free(substream);
491
492 /* free any DMA resources */
493 if (platform->driver->ops && platform->driver->ops->hw_free)
494 platform->driver->ops->hw_free(substream);
495
496 /* now free hw params for the DAIs */
497 if (codec_dai->driver->ops->hw_free)
498 codec_dai->driver->ops->hw_free(substream, codec_dai);
499
500 if (cpu_dai->driver->ops->hw_free)
501 cpu_dai->driver->ops->hw_free(substream, cpu_dai);
502
503 mutex_unlock(&pcm_mutex);
504 return 0;
505}
506
507static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
508{
509 struct snd_soc_pcm_runtime *rtd = substream->private_data;
510 struct snd_soc_platform *platform = rtd->platform;
511 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
512 struct snd_soc_dai *codec_dai = rtd->codec_dai;
513 int ret;
514
515 if (codec_dai->driver->ops->trigger) {
516 ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
517 if (ret < 0)
518 return ret;
519 }
520
521 if (platform->driver->ops && platform->driver->ops->trigger) {
522 ret = platform->driver->ops->trigger(substream, cmd);
523 if (ret < 0)
524 return ret;
525 }
526
527 if (cpu_dai->driver->ops->trigger) {
528 ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
529 if (ret < 0)
530 return ret;
531 }
532 return 0;
533}
534
535/*
536 * soc level wrapper for pointer callback
537 * If cpu_dai, codec_dai, platform driver has the delay callback, than
538 * the runtime->delay will be updated accordingly.
539 */
540static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
541{
542 struct snd_soc_pcm_runtime *rtd = substream->private_data;
543 struct snd_soc_platform *platform = rtd->platform;
544 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
545 struct snd_soc_dai *codec_dai = rtd->codec_dai;
546 struct snd_pcm_runtime *runtime = substream->runtime;
547 snd_pcm_uframes_t offset = 0;
548 snd_pcm_sframes_t delay = 0;
549
550 if (platform->driver->ops && platform->driver->ops->pointer)
551 offset = platform->driver->ops->pointer(substream);
552
553 if (cpu_dai->driver->ops->delay)
554 delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
555
556 if (codec_dai->driver->ops->delay)
557 delay += codec_dai->driver->ops->delay(substream, codec_dai);
558
559 if (platform->driver->delay)
560 delay += platform->driver->delay(substream, codec_dai);
561
562 runtime->delay = delay;
563
564 return offset;
565}
566
567/* ASoC PCM operations */
568static struct snd_pcm_ops soc_pcm_ops = {
569 .open = soc_pcm_open,
570 .close = soc_codec_close,
571 .hw_params = soc_pcm_hw_params,
572 .hw_free = soc_pcm_hw_free,
573 .prepare = soc_pcm_prepare,
574 .trigger = soc_pcm_trigger,
575 .pointer = soc_pcm_pointer,
576};
577
578/* create a new pcm */
579int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
580{
581 struct snd_soc_codec *codec = rtd->codec;
582 struct snd_soc_platform *platform = rtd->platform;
583 struct snd_soc_dai *codec_dai = rtd->codec_dai;
584 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
585 struct snd_pcm *pcm;
586 char new_name[64];
587 int ret = 0, playback = 0, capture = 0;
588
589 /* check client and interface hw capabilities */
590 snprintf(new_name, sizeof(new_name), "%s %s-%d",
591 rtd->dai_link->stream_name, codec_dai->name, num);
592
593 if (codec_dai->driver->playback.channels_min)
594 playback = 1;
595 if (codec_dai->driver->capture.channels_min)
596 capture = 1;
597
598 dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
599 ret = snd_pcm_new(rtd->card->snd_card, new_name,
600 num, playback, capture, &pcm);
601 if (ret < 0) {
602 printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
603 return ret;
604 }
605
606 /* DAPM dai link stream work */
607 INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
608
609 rtd->pcm = pcm;
610 pcm->private_data = rtd;
611 if (platform->driver->ops) {
612 soc_pcm_ops.mmap = platform->driver->ops->mmap;
613 soc_pcm_ops.pointer = platform->driver->ops->pointer;
614 soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
615 soc_pcm_ops.copy = platform->driver->ops->copy;
616 soc_pcm_ops.silence = platform->driver->ops->silence;
617 soc_pcm_ops.ack = platform->driver->ops->ack;
618 soc_pcm_ops.page = platform->driver->ops->page;
619 }
620
621 if (playback)
622 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
623
624 if (capture)
625 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
626
627 if (platform->driver->pcm_new) {
628 ret = platform->driver->pcm_new(rtd);
629 if (ret < 0) {
630 pr_err("asoc: platform pcm constructor failed\n");
631 return ret;
632 }
633 }
634
635 pcm->private_free = platform->driver->pcm_free;
636 printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
637 cpu_dai->name);
638 return ret;
639}