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