diff options
Diffstat (limited to 'sound/soc/soc-compress.c')
-rw-r--r-- | sound/soc/soc-compress.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c new file mode 100644 index 000000000000..88d85badd932 --- /dev/null +++ b/sound/soc/soc-compress.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * soc-compress.c -- ALSA SoC Compress | ||
3 | * | ||
4 | * Copyright (C) 2012 Intel Corp. | ||
5 | * | ||
6 | * Authors: Namarta Kohli <namartax.kohli@intel.com> | ||
7 | * Ramesh Babu K V <ramesh.babu@linux.intel.com> | ||
8 | * Vinod Koul <vinod.koul@linux.intel.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | #include <sound/core.h> | ||
23 | #include <sound/compress_params.h> | ||
24 | #include <sound/compress_driver.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include <sound/initval.h> | ||
27 | |||
28 | static int soc_compr_open(struct snd_compr_stream *cstream) | ||
29 | { | ||
30 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
31 | struct snd_soc_platform *platform = rtd->platform; | ||
32 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
33 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
34 | int ret = 0; | ||
35 | |||
36 | if (platform->driver->compr_ops && platform->driver->compr_ops->open) { | ||
37 | ret = platform->driver->compr_ops->open(cstream); | ||
38 | if (ret < 0) { | ||
39 | pr_err("compress asoc: can't open platform %s\n", platform->name); | ||
40 | goto out; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) { | ||
45 | ret = rtd->dai_link->compr_ops->startup(cstream); | ||
46 | if (ret < 0) { | ||
47 | pr_err("compress asoc: %s startup failed\n", rtd->dai_link->name); | ||
48 | goto machine_err; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | if (cstream->direction == SND_COMPRESS_PLAYBACK) { | ||
53 | cpu_dai->playback_active++; | ||
54 | codec_dai->playback_active++; | ||
55 | } else { | ||
56 | cpu_dai->capture_active++; | ||
57 | codec_dai->capture_active++; | ||
58 | } | ||
59 | |||
60 | cpu_dai->active++; | ||
61 | codec_dai->active++; | ||
62 | rtd->codec->active++; | ||
63 | |||
64 | return 0; | ||
65 | |||
66 | machine_err: | ||
67 | if (platform->driver->compr_ops && platform->driver->compr_ops->free) | ||
68 | platform->driver->compr_ops->free(cstream); | ||
69 | out: | ||
70 | return ret; | ||
71 | } | ||
72 | |||
73 | static int soc_compr_free(struct snd_compr_stream *cstream) | ||
74 | { | ||
75 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
76 | struct snd_soc_platform *platform = rtd->platform; | ||
77 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
78 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
79 | struct snd_soc_codec *codec = rtd->codec; | ||
80 | |||
81 | if (cstream->direction == SND_COMPRESS_PLAYBACK) { | ||
82 | cpu_dai->playback_active--; | ||
83 | codec_dai->playback_active--; | ||
84 | } else { | ||
85 | cpu_dai->capture_active--; | ||
86 | codec_dai->capture_active--; | ||
87 | } | ||
88 | |||
89 | snd_soc_dai_digital_mute(codec_dai, 1); | ||
90 | |||
91 | cpu_dai->active--; | ||
92 | codec_dai->active--; | ||
93 | codec->active--; | ||
94 | |||
95 | if (!cpu_dai->active) | ||
96 | cpu_dai->rate = 0; | ||
97 | |||
98 | if (!codec_dai->active) | ||
99 | codec_dai->rate = 0; | ||
100 | |||
101 | |||
102 | if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) | ||
103 | rtd->dai_link->compr_ops->shutdown(cstream); | ||
104 | |||
105 | if (platform->driver->compr_ops && platform->driver->compr_ops->free) | ||
106 | platform->driver->compr_ops->free(cstream); | ||
107 | cpu_dai->runtime = NULL; | ||
108 | |||
109 | if (cstream->direction == SND_COMPRESS_PLAYBACK) { | ||
110 | if (!rtd->pmdown_time || codec->ignore_pmdown_time || | ||
111 | rtd->dai_link->ignore_pmdown_time) { | ||
112 | snd_soc_dapm_stream_event(rtd, | ||
113 | SNDRV_PCM_STREAM_PLAYBACK, | ||
114 | SND_SOC_DAPM_STREAM_STOP); | ||
115 | } else | ||
116 | codec_dai->pop_wait = 1; | ||
117 | schedule_delayed_work(&rtd->delayed_work, | ||
118 | msecs_to_jiffies(rtd->pmdown_time)); | ||
119 | } else { | ||
120 | /* capture streams can be powered down now */ | ||
121 | snd_soc_dapm_stream_event(rtd, | ||
122 | SNDRV_PCM_STREAM_CAPTURE, | ||
123 | SND_SOC_DAPM_STREAM_STOP); | ||
124 | } | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) | ||
130 | { | ||
131 | |||
132 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
133 | struct snd_soc_platform *platform = rtd->platform; | ||
134 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
135 | int ret = 0; | ||
136 | |||
137 | if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { | ||
138 | ret = platform->driver->compr_ops->trigger(cstream, cmd); | ||
139 | if (ret < 0) | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | if (cmd == SNDRV_PCM_TRIGGER_START) | ||
144 | snd_soc_dai_digital_mute(codec_dai, 0); | ||
145 | else if (cmd == SNDRV_PCM_TRIGGER_STOP) | ||
146 | snd_soc_dai_digital_mute(codec_dai, 1); | ||
147 | |||
148 | return ret; | ||
149 | } | ||
150 | |||
151 | static int soc_compr_set_params(struct snd_compr_stream *cstream, | ||
152 | struct snd_compr_params *params) | ||
153 | { | ||
154 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
155 | struct snd_soc_platform *platform = rtd->platform; | ||
156 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
157 | int ret = 0; | ||
158 | |||
159 | /* first we call set_params for the platform driver | ||
160 | * this should configure the soc side | ||
161 | * if the machine has compressed ops then we call that as well | ||
162 | * expectation is that platform and machine will configure everything | ||
163 | * for this compress path, like configuring pcm port for codec | ||
164 | */ | ||
165 | if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { | ||
166 | ret = platform->driver->compr_ops->set_params(cstream, params); | ||
167 | if (ret < 0) | ||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { | ||
172 | ret = rtd->dai_link->compr_ops->set_params(cstream); | ||
173 | if (ret < 0) | ||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, | ||
178 | SND_SOC_DAPM_STREAM_START); | ||
179 | |||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | static int soc_compr_get_params(struct snd_compr_stream *cstream, | ||
184 | struct snd_codec *params) | ||
185 | { | ||
186 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
187 | struct snd_soc_platform *platform = rtd->platform; | ||
188 | int ret = 0; | ||
189 | |||
190 | if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) | ||
191 | ret = platform->driver->compr_ops->get_params(cstream, params); | ||
192 | |||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | static int soc_compr_get_caps(struct snd_compr_stream *cstream, | ||
197 | struct snd_compr_caps *caps) | ||
198 | { | ||
199 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
200 | struct snd_soc_platform *platform = rtd->platform; | ||
201 | int ret = 0; | ||
202 | |||
203 | if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) | ||
204 | ret = platform->driver->compr_ops->get_caps(cstream, caps); | ||
205 | |||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, | ||
210 | struct snd_compr_codec_caps *codec) | ||
211 | { | ||
212 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
213 | struct snd_soc_platform *platform = rtd->platform; | ||
214 | int ret = 0; | ||
215 | |||
216 | if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) | ||
217 | ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); | ||
218 | |||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) | ||
223 | { | ||
224 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
225 | struct snd_soc_platform *platform = rtd->platform; | ||
226 | int ret = 0; | ||
227 | |||
228 | if (platform->driver->compr_ops && platform->driver->compr_ops->ack) | ||
229 | ret = platform->driver->compr_ops->ack(cstream, bytes); | ||
230 | |||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | static int soc_compr_pointer(struct snd_compr_stream *cstream, | ||
235 | struct snd_compr_tstamp *tstamp) | ||
236 | { | ||
237 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
238 | struct snd_soc_platform *platform = rtd->platform; | ||
239 | |||
240 | if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) | ||
241 | platform->driver->compr_ops->pointer(cstream, tstamp); | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | /* ASoC Compress operations */ | ||
247 | static struct snd_compr_ops soc_compr_ops = { | ||
248 | .open = soc_compr_open, | ||
249 | .free = soc_compr_free, | ||
250 | .set_params = soc_compr_set_params, | ||
251 | .get_params = soc_compr_get_params, | ||
252 | .trigger = soc_compr_trigger, | ||
253 | .pointer = soc_compr_pointer, | ||
254 | .ack = soc_compr_ack, | ||
255 | .get_caps = soc_compr_get_caps, | ||
256 | .get_codec_caps = soc_compr_get_codec_caps | ||
257 | }; | ||
258 | |||
259 | /* create a new compress */ | ||
260 | int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) | ||
261 | { | ||
262 | struct snd_soc_codec *codec = rtd->codec; | ||
263 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
264 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
265 | struct snd_compr *compr; | ||
266 | char new_name[64]; | ||
267 | int ret = 0, direction = 0; | ||
268 | |||
269 | /* check client and interface hw capabilities */ | ||
270 | snprintf(new_name, sizeof(new_name), "%s %s-%d", | ||
271 | rtd->dai_link->stream_name, codec_dai->name, num); | ||
272 | direction = SND_COMPRESS_PLAYBACK; | ||
273 | compr = kzalloc(sizeof(*compr), GFP_KERNEL); | ||
274 | if (compr == NULL) { | ||
275 | snd_printk(KERN_ERR "Cannot allocate compr\n"); | ||
276 | return -ENOMEM; | ||
277 | } | ||
278 | |||
279 | compr->ops = &soc_compr_ops; | ||
280 | mutex_init(&compr->lock); | ||
281 | ret = snd_compress_new(rtd->card->snd_card, num, direction, compr); | ||
282 | if (ret < 0) { | ||
283 | pr_err("compress asoc: can't create compress for codec %s\n", | ||
284 | codec->name); | ||
285 | kfree(compr); | ||
286 | return ret; | ||
287 | } | ||
288 | |||
289 | rtd->compr = compr; | ||
290 | compr->private_data = rtd; | ||
291 | |||
292 | printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, | ||
293 | cpu_dai->name); | ||
294 | return ret; | ||
295 | } | ||