diff options
Diffstat (limited to 'sound/soc/soc-compress.c')
-rw-r--r-- | sound/soc/soc-compress.c | 123 |
1 files changed, 111 insertions, 12 deletions
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 5fbfb06e8083..b5b3db71e253 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c | |||
@@ -33,6 +33,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream) | |||
33 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 33 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
34 | int ret = 0; | 34 | int ret = 0; |
35 | 35 | ||
36 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
37 | |||
36 | if (platform->driver->compr_ops && platform->driver->compr_ops->open) { | 38 | if (platform->driver->compr_ops && platform->driver->compr_ops->open) { |
37 | ret = platform->driver->compr_ops->open(cstream); | 39 | ret = platform->driver->compr_ops->open(cstream); |
38 | if (ret < 0) { | 40 | if (ret < 0) { |
@@ -61,15 +63,46 @@ static int soc_compr_open(struct snd_compr_stream *cstream) | |||
61 | codec_dai->active++; | 63 | codec_dai->active++; |
62 | rtd->codec->active++; | 64 | rtd->codec->active++; |
63 | 65 | ||
66 | mutex_unlock(&rtd->pcm_mutex); | ||
67 | |||
64 | return 0; | 68 | return 0; |
65 | 69 | ||
66 | machine_err: | 70 | machine_err: |
67 | if (platform->driver->compr_ops && platform->driver->compr_ops->free) | 71 | if (platform->driver->compr_ops && platform->driver->compr_ops->free) |
68 | platform->driver->compr_ops->free(cstream); | 72 | platform->driver->compr_ops->free(cstream); |
69 | out: | 73 | out: |
74 | mutex_unlock(&rtd->pcm_mutex); | ||
70 | return ret; | 75 | return ret; |
71 | } | 76 | } |
72 | 77 | ||
78 | /* | ||
79 | * Power down the audio subsystem pmdown_time msecs after close is called. | ||
80 | * This is to ensure there are no pops or clicks in between any music tracks | ||
81 | * due to DAPM power cycling. | ||
82 | */ | ||
83 | static void close_delayed_work(struct work_struct *work) | ||
84 | { | ||
85 | struct snd_soc_pcm_runtime *rtd = | ||
86 | container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); | ||
87 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
88 | |||
89 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
90 | |||
91 | dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", | ||
92 | codec_dai->driver->playback.stream_name, | ||
93 | codec_dai->playback_active ? "active" : "inactive", | ||
94 | rtd->pop_wait ? "yes" : "no"); | ||
95 | |||
96 | /* are we waiting on this codec DAI stream */ | ||
97 | if (rtd->pop_wait == 1) { | ||
98 | rtd->pop_wait = 0; | ||
99 | snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, | ||
100 | SND_SOC_DAPM_STREAM_STOP); | ||
101 | } | ||
102 | |||
103 | mutex_unlock(&rtd->pcm_mutex); | ||
104 | } | ||
105 | |||
73 | static int soc_compr_free(struct snd_compr_stream *cstream) | 106 | static int soc_compr_free(struct snd_compr_stream *cstream) |
74 | { | 107 | { |
75 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | 108 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
@@ -78,6 +111,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) | |||
78 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 111 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
79 | struct snd_soc_codec *codec = rtd->codec; | 112 | struct snd_soc_codec *codec = rtd->codec; |
80 | 113 | ||
114 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
115 | |||
81 | if (cstream->direction == SND_COMPRESS_PLAYBACK) { | 116 | if (cstream->direction == SND_COMPRESS_PLAYBACK) { |
82 | cpu_dai->playback_active--; | 117 | cpu_dai->playback_active--; |
83 | codec_dai->playback_active--; | 118 | codec_dai->playback_active--; |
@@ -86,7 +121,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) | |||
86 | codec_dai->capture_active--; | 121 | codec_dai->capture_active--; |
87 | } | 122 | } |
88 | 123 | ||
89 | snd_soc_dai_digital_mute(codec_dai, 1); | 124 | snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); |
90 | 125 | ||
91 | cpu_dai->active--; | 126 | cpu_dai->active--; |
92 | codec_dai->active--; | 127 | codec_dai->active--; |
@@ -112,10 +147,11 @@ static int soc_compr_free(struct snd_compr_stream *cstream) | |||
112 | snd_soc_dapm_stream_event(rtd, | 147 | snd_soc_dapm_stream_event(rtd, |
113 | SNDRV_PCM_STREAM_PLAYBACK, | 148 | SNDRV_PCM_STREAM_PLAYBACK, |
114 | SND_SOC_DAPM_STREAM_STOP); | 149 | SND_SOC_DAPM_STREAM_STOP); |
115 | } else | 150 | } else { |
116 | rtd->pop_wait = 1; | 151 | rtd->pop_wait = 1; |
117 | schedule_delayed_work(&rtd->delayed_work, | 152 | schedule_delayed_work(&rtd->delayed_work, |
118 | msecs_to_jiffies(rtd->pmdown_time)); | 153 | msecs_to_jiffies(rtd->pmdown_time)); |
154 | } | ||
119 | } else { | 155 | } else { |
120 | /* capture streams can be powered down now */ | 156 | /* capture streams can be powered down now */ |
121 | snd_soc_dapm_stream_event(rtd, | 157 | snd_soc_dapm_stream_event(rtd, |
@@ -123,6 +159,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) | |||
123 | SND_SOC_DAPM_STREAM_STOP); | 159 | SND_SOC_DAPM_STREAM_STOP); |
124 | } | 160 | } |
125 | 161 | ||
162 | mutex_unlock(&rtd->pcm_mutex); | ||
126 | return 0; | 163 | return 0; |
127 | } | 164 | } |
128 | 165 | ||
@@ -134,17 +171,25 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) | |||
134 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 171 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
135 | int ret = 0; | 172 | int ret = 0; |
136 | 173 | ||
174 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
175 | |||
137 | if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { | 176 | if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { |
138 | ret = platform->driver->compr_ops->trigger(cstream, cmd); | 177 | ret = platform->driver->compr_ops->trigger(cstream, cmd); |
139 | if (ret < 0) | 178 | if (ret < 0) |
140 | return ret; | 179 | goto out; |
141 | } | 180 | } |
142 | 181 | ||
143 | if (cmd == SNDRV_PCM_TRIGGER_START) | 182 | switch (cmd) { |
144 | snd_soc_dai_digital_mute(codec_dai, 0); | 183 | case SNDRV_PCM_TRIGGER_START: |
145 | else if (cmd == SNDRV_PCM_TRIGGER_STOP) | 184 | snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); |
146 | snd_soc_dai_digital_mute(codec_dai, 1); | 185 | break; |
186 | case SNDRV_PCM_TRIGGER_STOP: | ||
187 | snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); | ||
188 | break; | ||
189 | } | ||
147 | 190 | ||
191 | out: | ||
192 | mutex_unlock(&rtd->pcm_mutex); | ||
148 | return ret; | 193 | return ret; |
149 | } | 194 | } |
150 | 195 | ||
@@ -155,6 +200,8 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, | |||
155 | struct snd_soc_platform *platform = rtd->platform; | 200 | struct snd_soc_platform *platform = rtd->platform; |
156 | int ret = 0; | 201 | int ret = 0; |
157 | 202 | ||
203 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
204 | |||
158 | /* first we call set_params for the platform driver | 205 | /* first we call set_params for the platform driver |
159 | * this should configure the soc side | 206 | * this should configure the soc side |
160 | * if the machine has compressed ops then we call that as well | 207 | * if the machine has compressed ops then we call that as well |
@@ -164,18 +211,20 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, | |||
164 | if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { | 211 | if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { |
165 | ret = platform->driver->compr_ops->set_params(cstream, params); | 212 | ret = platform->driver->compr_ops->set_params(cstream, params); |
166 | if (ret < 0) | 213 | if (ret < 0) |
167 | return ret; | 214 | goto out; |
168 | } | 215 | } |
169 | 216 | ||
170 | if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { | 217 | if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { |
171 | ret = rtd->dai_link->compr_ops->set_params(cstream); | 218 | ret = rtd->dai_link->compr_ops->set_params(cstream); |
172 | if (ret < 0) | 219 | if (ret < 0) |
173 | return ret; | 220 | goto out; |
174 | } | 221 | } |
175 | 222 | ||
176 | snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, | 223 | snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, |
177 | SND_SOC_DAPM_STREAM_START); | 224 | SND_SOC_DAPM_STREAM_START); |
178 | 225 | ||
226 | out: | ||
227 | mutex_unlock(&rtd->pcm_mutex); | ||
179 | return ret; | 228 | return ret; |
180 | } | 229 | } |
181 | 230 | ||
@@ -186,9 +235,12 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, | |||
186 | struct snd_soc_platform *platform = rtd->platform; | 235 | struct snd_soc_platform *platform = rtd->platform; |
187 | int ret = 0; | 236 | int ret = 0; |
188 | 237 | ||
238 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
239 | |||
189 | if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) | 240 | if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) |
190 | ret = platform->driver->compr_ops->get_params(cstream, params); | 241 | ret = platform->driver->compr_ops->get_params(cstream, params); |
191 | 242 | ||
243 | mutex_unlock(&rtd->pcm_mutex); | ||
192 | return ret; | 244 | return ret; |
193 | } | 245 | } |
194 | 246 | ||
@@ -199,9 +251,12 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, | |||
199 | struct snd_soc_platform *platform = rtd->platform; | 251 | struct snd_soc_platform *platform = rtd->platform; |
200 | int ret = 0; | 252 | int ret = 0; |
201 | 253 | ||
254 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
255 | |||
202 | if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) | 256 | if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) |
203 | ret = platform->driver->compr_ops->get_caps(cstream, caps); | 257 | ret = platform->driver->compr_ops->get_caps(cstream, caps); |
204 | 258 | ||
259 | mutex_unlock(&rtd->pcm_mutex); | ||
205 | return ret; | 260 | return ret; |
206 | } | 261 | } |
207 | 262 | ||
@@ -212,9 +267,12 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, | |||
212 | struct snd_soc_platform *platform = rtd->platform; | 267 | struct snd_soc_platform *platform = rtd->platform; |
213 | int ret = 0; | 268 | int ret = 0; |
214 | 269 | ||
270 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
271 | |||
215 | if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) | 272 | if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) |
216 | ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); | 273 | ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); |
217 | 274 | ||
275 | mutex_unlock(&rtd->pcm_mutex); | ||
218 | return ret; | 276 | return ret; |
219 | } | 277 | } |
220 | 278 | ||
@@ -224,9 +282,12 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) | |||
224 | struct snd_soc_platform *platform = rtd->platform; | 282 | struct snd_soc_platform *platform = rtd->platform; |
225 | int ret = 0; | 283 | int ret = 0; |
226 | 284 | ||
285 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
286 | |||
227 | if (platform->driver->compr_ops && platform->driver->compr_ops->ack) | 287 | if (platform->driver->compr_ops && platform->driver->compr_ops->ack) |
228 | ret = platform->driver->compr_ops->ack(cstream, bytes); | 288 | ret = platform->driver->compr_ops->ack(cstream, bytes); |
229 | 289 | ||
290 | mutex_unlock(&rtd->pcm_mutex); | ||
230 | return ret; | 291 | return ret; |
231 | } | 292 | } |
232 | 293 | ||
@@ -236,12 +297,31 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, | |||
236 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | 297 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
237 | struct snd_soc_platform *platform = rtd->platform; | 298 | struct snd_soc_platform *platform = rtd->platform; |
238 | 299 | ||
300 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
301 | |||
239 | if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) | 302 | if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) |
240 | platform->driver->compr_ops->pointer(cstream, tstamp); | 303 | platform->driver->compr_ops->pointer(cstream, tstamp); |
241 | 304 | ||
305 | mutex_unlock(&rtd->pcm_mutex); | ||
242 | return 0; | 306 | return 0; |
243 | } | 307 | } |
244 | 308 | ||
309 | static int soc_compr_copy(struct snd_compr_stream *cstream, | ||
310 | const char __user *buf, size_t count) | ||
311 | { | ||
312 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | ||
313 | struct snd_soc_platform *platform = rtd->platform; | ||
314 | int ret = 0; | ||
315 | |||
316 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | ||
317 | |||
318 | if (platform->driver->compr_ops && platform->driver->compr_ops->copy) | ||
319 | ret = platform->driver->compr_ops->copy(cstream, buf, count); | ||
320 | |||
321 | mutex_unlock(&rtd->pcm_mutex); | ||
322 | return ret; | ||
323 | } | ||
324 | |||
245 | /* ASoC Compress operations */ | 325 | /* ASoC Compress operations */ |
246 | static struct snd_compr_ops soc_compr_ops = { | 326 | static struct snd_compr_ops soc_compr_ops = { |
247 | .open = soc_compr_open, | 327 | .open = soc_compr_open, |
@@ -259,6 +339,7 @@ static struct snd_compr_ops soc_compr_ops = { | |||
259 | int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) | 339 | int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) |
260 | { | 340 | { |
261 | struct snd_soc_codec *codec = rtd->codec; | 341 | struct snd_soc_codec *codec = rtd->codec; |
342 | struct snd_soc_platform *platform = rtd->platform; | ||
262 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 343 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
263 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | 344 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
264 | struct snd_compr *compr; | 345 | struct snd_compr *compr; |
@@ -275,20 +356,38 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) | |||
275 | return -ENOMEM; | 356 | return -ENOMEM; |
276 | } | 357 | } |
277 | 358 | ||
278 | compr->ops = &soc_compr_ops; | 359 | compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops), |
360 | GFP_KERNEL); | ||
361 | if (compr->ops == NULL) { | ||
362 | dev_err(rtd->card->dev, "Cannot allocate compressed ops\n"); | ||
363 | ret = -ENOMEM; | ||
364 | goto compr_err; | ||
365 | } | ||
366 | memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); | ||
367 | |||
368 | /* Add copy callback for not memory mapped DSPs */ | ||
369 | if (platform->driver->compr_ops && platform->driver->compr_ops->copy) | ||
370 | compr->ops->copy = soc_compr_copy; | ||
371 | |||
279 | mutex_init(&compr->lock); | 372 | mutex_init(&compr->lock); |
280 | ret = snd_compress_new(rtd->card->snd_card, num, direction, compr); | 373 | ret = snd_compress_new(rtd->card->snd_card, num, direction, compr); |
281 | if (ret < 0) { | 374 | if (ret < 0) { |
282 | pr_err("compress asoc: can't create compress for codec %s\n", | 375 | pr_err("compress asoc: can't create compress for codec %s\n", |
283 | codec->name); | 376 | codec->name); |
284 | kfree(compr); | 377 | goto compr_err; |
285 | return ret; | ||
286 | } | 378 | } |
287 | 379 | ||
380 | /* DAPM dai link stream work */ | ||
381 | INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); | ||
382 | |||
288 | rtd->compr = compr; | 383 | rtd->compr = compr; |
289 | compr->private_data = rtd; | 384 | compr->private_data = rtd; |
290 | 385 | ||
291 | printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, | 386 | printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, |
292 | cpu_dai->name); | 387 | cpu_dai->name); |
293 | return ret; | 388 | return ret; |
389 | |||
390 | compr_err: | ||
391 | kfree(compr); | ||
392 | return ret; | ||
294 | } | 393 | } |