]>
Commit | Line | Data |
---|---|---|
b3ed4c86 KM |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // | |
3 | // soc-compress.c -- ALSA SoC Compress | |
4 | // | |
5 | // Copyright (C) 2012 Intel Corp. | |
6 | // | |
7 | // Authors: Namarta Kohli <[email protected]> | |
8 | // Ramesh Babu K V <[email protected]> | |
9 | // Vinod Koul <[email protected]> | |
1245b700 NK |
10 | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/workqueue.h> | |
16 | #include <sound/core.h> | |
17 | #include <sound/compress_params.h> | |
18 | #include <sound/compress_driver.h> | |
19 | #include <sound/soc.h> | |
20 | #include <sound/initval.h> | |
2a99ef0f | 21 | #include <sound/soc-dpcm.h> |
9ab711cb | 22 | #include <sound/soc-link.h> |
4137f4b6 | 23 | #include <linux/pm_runtime.h> |
1245b700 | 24 | |
453d32c2 | 25 | static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback) |
15a7b8c1 KM |
26 | { |
27 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
28 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); | |
29 | struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); | |
30 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ | |
31 | ||
32 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); | |
33 | ||
453d32c2 KM |
34 | if (!rollback) |
35 | snd_soc_runtime_deactivate(rtd, stream); | |
15a7b8c1 KM |
36 | |
37 | snd_soc_dai_digital_mute(codec_dai, 1, stream); | |
38 | ||
39 | if (!snd_soc_dai_active(cpu_dai)) | |
40 | cpu_dai->rate = 0; | |
41 | ||
42 | if (!snd_soc_dai_active(codec_dai)) | |
43 | codec_dai->rate = 0; | |
44 | ||
453d32c2 | 45 | snd_soc_link_compr_shutdown(cstream, rollback); |
15a7b8c1 | 46 | |
453d32c2 | 47 | snd_soc_component_compr_free(cstream, rollback); |
15a7b8c1 | 48 | |
453d32c2 | 49 | snd_soc_dai_compr_shutdown(cpu_dai, cstream, rollback); |
15a7b8c1 | 50 | |
453d32c2 KM |
51 | if (!rollback) |
52 | snd_soc_dapm_stream_stop(rtd, stream); | |
15a7b8c1 KM |
53 | |
54 | mutex_unlock(&rtd->card->pcm_mutex); | |
55 | ||
453d32c2 | 56 | snd_soc_pcm_component_pm_runtime_put(rtd, cstream, rollback); |
15a7b8c1 KM |
57 | |
58 | return 0; | |
59 | } | |
60 | ||
453d32c2 KM |
61 | static int soc_compr_free(struct snd_compr_stream *cstream) |
62 | { | |
63 | return soc_compr_clean(cstream, 0); | |
64 | } | |
65 | ||
1e57b828 CK |
66 | static int soc_compr_open(struct snd_compr_stream *cstream) |
67 | { | |
68 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
c2233a26 | 69 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
7428d8c8 | 70 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
939a5cfb | 71 | int ret; |
1e57b828 | 72 | |
939a5cfb KM |
73 | ret = snd_soc_pcm_component_pm_runtime_get(rtd, cstream); |
74 | if (ret < 0) | |
453d32c2 | 75 | goto err_no_lock; |
4137f4b6 | 76 | |
72b745e3 | 77 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
1e57b828 | 78 | |
b5ae4cce KM |
79 | ret = snd_soc_dai_compr_startup(cpu_dai, cstream); |
80 | if (ret < 0) | |
453d32c2 | 81 | goto err; |
1e57b828 | 82 | |
f94ba9ac | 83 | ret = snd_soc_component_compr_open(cstream); |
1e57b828 | 84 | if (ret < 0) |
453d32c2 | 85 | goto err; |
9e7e3738 | 86 | |
9ab711cb KM |
87 | ret = snd_soc_link_compr_startup(cstream); |
88 | if (ret < 0) | |
453d32c2 | 89 | goto err; |
1245b700 | 90 | |
eb84959a | 91 | snd_soc_runtime_activate(rtd, stream); |
453d32c2 | 92 | err: |
72b745e3 | 93 | mutex_unlock(&rtd->card->pcm_mutex); |
453d32c2 KM |
94 | err_no_lock: |
95 | if (ret < 0) | |
96 | soc_compr_clean(cstream, 1); | |
4137f4b6 | 97 | |
1245b700 NK |
98 | return ret; |
99 | } | |
100 | ||
2a99ef0f LG |
101 | static int soc_compr_open_fe(struct snd_compr_stream *cstream) |
102 | { | |
103 | struct snd_soc_pcm_runtime *fe = cstream->private_data; | |
01b8cedf SBP |
104 | struct snd_pcm_substream *fe_substream = |
105 | fe->pcm->streams[cstream->direction].substream; | |
c2233a26 | 106 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); |
2a99ef0f LG |
107 | struct snd_soc_dpcm *dpcm; |
108 | struct snd_soc_dapm_widget_list *list; | |
7428d8c8 | 109 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
572e6c8d | 110 | int ret; |
2a99ef0f | 111 | |
2a99ef0f | 112 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); |
0b0722e1 SK |
113 | fe->dpcm[stream].runtime = fe_substream->runtime; |
114 | ||
115 | ret = dpcm_path_get(fe, stream, &list); | |
116 | if (ret < 0) | |
117 | goto be_err; | |
d479f00b | 118 | |
0b0722e1 SK |
119 | /* calculate valid and active FE <-> BE dpcms */ |
120 | dpcm_process_paths(fe, stream, &list, 1); | |
121 | fe->dpcm[stream].runtime = fe_substream->runtime; | |
122 | ||
123 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | |
124 | ||
125 | ret = dpcm_be_dai_startup(fe, stream); | |
126 | if (ret < 0) { | |
127 | /* clean up all links */ | |
8d6258a4 | 128 | for_each_dpcm_be(fe, stream, dpcm) |
0b0722e1 SK |
129 | dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; |
130 | ||
131 | dpcm_be_disconnect(fe, stream); | |
132 | fe->dpcm[stream].runtime = NULL; | |
133 | goto out; | |
134 | } | |
2a99ef0f | 135 | |
b5ae4cce KM |
136 | ret = snd_soc_dai_compr_startup(cpu_dai, cstream); |
137 | if (ret < 0) | |
138 | goto out; | |
2e622ae4 | 139 | |
f94ba9ac | 140 | ret = snd_soc_component_compr_open(cstream); |
1e57b828 | 141 | if (ret < 0) |
0b0722e1 | 142 | goto open_err; |
9e7e3738 | 143 | |
9ab711cb KM |
144 | ret = snd_soc_link_compr_startup(cstream); |
145 | if (ret < 0) | |
146 | goto machine_err; | |
2a99ef0f | 147 | |
2a99ef0f LG |
148 | dpcm_clear_pending_state(fe, stream); |
149 | dpcm_path_put(&list); | |
150 | ||
151 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; | |
152 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | |
153 | ||
45475bf6 | 154 | mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass); |
24894b76 | 155 | snd_soc_runtime_activate(fe, stream); |
45475bf6 | 156 | mutex_unlock(&fe->card->pcm_mutex); |
2a99ef0f LG |
157 | |
158 | mutex_unlock(&fe->card->mutex); | |
159 | ||
160 | return 0; | |
161 | ||
2a99ef0f | 162 | machine_err: |
f94ba9ac | 163 | snd_soc_component_compr_free(cstream, 1); |
0b0722e1 | 164 | open_err: |
1e6a93cf | 165 | snd_soc_dai_compr_shutdown(cpu_dai, cstream, 1); |
2a99ef0f | 166 | out: |
0b0722e1 SK |
167 | dpcm_path_put(&list); |
168 | be_err: | |
2a99ef0f LG |
169 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; |
170 | mutex_unlock(&fe->card->mutex); | |
171 | return ret; | |
172 | } | |
173 | ||
2a99ef0f LG |
174 | static int soc_compr_free_fe(struct snd_compr_stream *cstream) |
175 | { | |
176 | struct snd_soc_pcm_runtime *fe = cstream->private_data; | |
c2233a26 | 177 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); |
2a99ef0f | 178 | struct snd_soc_dpcm *dpcm; |
7428d8c8 | 179 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
2a99ef0f LG |
180 | |
181 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); | |
182 | ||
45475bf6 | 183 | mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass); |
24894b76 | 184 | snd_soc_runtime_deactivate(fe, stream); |
45475bf6 | 185 | mutex_unlock(&fe->card->pcm_mutex); |
2a99ef0f LG |
186 | |
187 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | |
188 | ||
f52366e6 | 189 | dpcm_be_dai_hw_free(fe, stream); |
2a99ef0f | 190 | |
531590bb | 191 | dpcm_be_dai_shutdown(fe, stream); |
2a99ef0f LG |
192 | |
193 | /* mark FE's links ready to prune */ | |
8d6258a4 | 194 | for_each_dpcm_be(fe, stream, dpcm) |
2a99ef0f LG |
195 | dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; |
196 | ||
1c531230 | 197 | dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); |
2a99ef0f LG |
198 | |
199 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; | |
200 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | |
201 | ||
202 | dpcm_be_disconnect(fe, stream); | |
203 | ||
204 | fe->dpcm[stream].runtime = NULL; | |
205 | ||
cd7c7d10 | 206 | snd_soc_link_compr_shutdown(cstream, 0); |
2a99ef0f | 207 | |
f94ba9ac | 208 | snd_soc_component_compr_free(cstream, 0); |
9e7e3738 | 209 | |
1e6a93cf | 210 | snd_soc_dai_compr_shutdown(cpu_dai, cstream, 0); |
2e622ae4 | 211 | |
2a99ef0f LG |
212 | mutex_unlock(&fe->card->mutex); |
213 | return 0; | |
214 | } | |
215 | ||
4ef0ecb8 CK |
216 | static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) |
217 | { | |
218 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
c2233a26 KM |
219 | struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); |
220 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); | |
7428d8c8 | 221 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
4ef0ecb8 CK |
222 | int ret; |
223 | ||
72b745e3 | 224 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
4ef0ecb8 | 225 | |
08aee251 | 226 | ret = snd_soc_component_compr_trigger(cstream, cmd); |
4ef0ecb8 CK |
227 | if (ret < 0) |
228 | goto out; | |
229 | ||
eb08411b KM |
230 | ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd); |
231 | if (ret < 0) | |
232 | goto out; | |
2e622ae4 | 233 | |
da18396f MB |
234 | switch (cmd) { |
235 | case SNDRV_PCM_TRIGGER_START: | |
eb84959a | 236 | snd_soc_dai_digital_mute(codec_dai, 0, stream); |
da18396f MB |
237 | break; |
238 | case SNDRV_PCM_TRIGGER_STOP: | |
eb84959a | 239 | snd_soc_dai_digital_mute(codec_dai, 1, stream); |
da18396f | 240 | break; |
e38b9b74 | 241 | } |
1245b700 | 242 | |
15e2e619 | 243 | out: |
72b745e3 | 244 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
245 | return ret; |
246 | } | |
247 | ||
2a99ef0f LG |
248 | static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) |
249 | { | |
250 | struct snd_soc_pcm_runtime *fe = cstream->private_data; | |
c2233a26 | 251 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); |
7428d8c8 KM |
252 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
253 | int ret; | |
2a99ef0f LG |
254 | |
255 | if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || | |
4ef0ecb8 | 256 | cmd == SND_COMPR_TRIGGER_DRAIN) |
08aee251 | 257 | return snd_soc_component_compr_trigger(cstream, cmd); |
2a99ef0f | 258 | |
2a99ef0f LG |
259 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); |
260 | ||
eb08411b KM |
261 | ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd); |
262 | if (ret < 0) | |
263 | goto out; | |
2e622ae4 | 264 | |
08aee251 | 265 | ret = snd_soc_component_compr_trigger(cstream, cmd); |
4ef0ecb8 CK |
266 | if (ret < 0) |
267 | goto out; | |
9e7e3738 | 268 | |
2a99ef0f LG |
269 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; |
270 | ||
271 | ret = dpcm_be_dai_trigger(fe, stream, cmd); | |
272 | ||
273 | switch (cmd) { | |
274 | case SNDRV_PCM_TRIGGER_START: | |
275 | case SNDRV_PCM_TRIGGER_RESUME: | |
276 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
277 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; | |
278 | break; | |
279 | case SNDRV_PCM_TRIGGER_STOP: | |
280 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
281 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; | |
282 | break; | |
283 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
284 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; | |
285 | break; | |
286 | } | |
287 | ||
288 | out: | |
289 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | |
290 | mutex_unlock(&fe->card->mutex); | |
291 | return ret; | |
292 | } | |
293 | ||
4ef0ecb8 CK |
294 | static int soc_compr_set_params(struct snd_compr_stream *cstream, |
295 | struct snd_compr_params *params) | |
296 | { | |
297 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
c2233a26 | 298 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
7428d8c8 | 299 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
52cadf1f | 300 | int ret; |
1245b700 | 301 | |
72b745e3 | 302 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 303 | |
ef050bec CK |
304 | /* |
305 | * First we call set_params for the CPU DAI, then the component | |
306 | * driver this should configure the SoC side. If the machine has | |
307 | * compressed ops then we call that as well. The expectation is | |
308 | * that these callbacks will configure everything for this compress | |
309 | * path, like configuring a PCM port for a CODEC. | |
1245b700 | 310 | */ |
8dfedafb KM |
311 | ret = snd_soc_dai_compr_set_params(cpu_dai, cstream, params); |
312 | if (ret < 0) | |
313 | goto err; | |
2e622ae4 | 314 | |
ff08cf80 | 315 | ret = snd_soc_component_compr_set_params(cstream, params); |
4ef0ecb8 CK |
316 | if (ret < 0) |
317 | goto err; | |
9e7e3738 | 318 | |
eab810f3 KM |
319 | ret = snd_soc_link_compr_set_params(cstream); |
320 | if (ret < 0) | |
321 | goto err; | |
1245b700 | 322 | |
7428d8c8 | 323 | snd_soc_dapm_stream_event(rtd, stream, SND_SOC_DAPM_STREAM_START); |
1245b700 | 324 | |
fa40ef20 CK |
325 | /* cancel any delayed stream shutdown that is pending */ |
326 | rtd->pop_wait = 0; | |
72b745e3 | 327 | mutex_unlock(&rtd->card->pcm_mutex); |
fa40ef20 CK |
328 | |
329 | cancel_delayed_work_sync(&rtd->delayed_work); | |
330 | ||
52cadf1f | 331 | return 0; |
fa40ef20 CK |
332 | |
333 | err: | |
72b745e3 | 334 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
335 | return ret; |
336 | } | |
337 | ||
2a99ef0f | 338 | static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, |
89027d9e | 339 | struct snd_compr_params *params) |
2a99ef0f LG |
340 | { |
341 | struct snd_soc_pcm_runtime *fe = cstream->private_data; | |
01b8cedf SBP |
342 | struct snd_pcm_substream *fe_substream = |
343 | fe->pcm->streams[cstream->direction].substream; | |
c2233a26 | 344 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); |
7428d8c8 KM |
345 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
346 | int ret; | |
2a99ef0f LG |
347 | |
348 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); | |
349 | ||
0b0722e1 SK |
350 | /* |
351 | * Create an empty hw_params for the BE as the machine driver must | |
352 | * fix this up to match DSP decoder and ASRC configuration. | |
353 | * I.e. machine driver fixup for compressed BE is mandatory. | |
354 | */ | |
355 | memset(&fe->dpcm[fe_substream->stream].hw_params, 0, | |
356 | sizeof(struct snd_pcm_hw_params)); | |
357 | ||
358 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | |
359 | ||
360 | ret = dpcm_be_dai_hw_params(fe, stream); | |
361 | if (ret < 0) | |
362 | goto out; | |
363 | ||
364 | ret = dpcm_be_dai_prepare(fe, stream); | |
365 | if (ret < 0) | |
366 | goto out; | |
367 | ||
8dfedafb KM |
368 | ret = snd_soc_dai_compr_set_params(cpu_dai, cstream, params); |
369 | if (ret < 0) | |
370 | goto out; | |
2e622ae4 | 371 | |
ff08cf80 | 372 | ret = snd_soc_component_compr_set_params(cstream, params); |
4ef0ecb8 CK |
373 | if (ret < 0) |
374 | goto out; | |
9e7e3738 | 375 | |
eab810f3 KM |
376 | ret = snd_soc_link_compr_set_params(cstream); |
377 | if (ret < 0) | |
378 | goto out; | |
2a99ef0f | 379 | |
15f6b09a | 380 | dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); |
2a99ef0f LG |
381 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; |
382 | ||
383 | out: | |
384 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | |
385 | mutex_unlock(&fe->card->mutex); | |
386 | return ret; | |
387 | } | |
388 | ||
1245b700 | 389 | static int soc_compr_get_params(struct snd_compr_stream *cstream, |
89027d9e | 390 | struct snd_codec *params) |
1245b700 NK |
391 | { |
392 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
c2233a26 | 393 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
77c221ec | 394 | int ret = 0; |
1245b700 | 395 | |
72b745e3 | 396 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 397 | |
adbef543 KM |
398 | ret = snd_soc_dai_compr_get_params(cpu_dai, cstream, params); |
399 | if (ret < 0) | |
400 | goto err; | |
2e622ae4 | 401 | |
77c221ec | 402 | ret = snd_soc_component_compr_get_params(cstream, params); |
2e622ae4 | 403 | err: |
72b745e3 | 404 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
405 | return ret; |
406 | } | |
407 | ||
1245b700 NK |
408 | static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) |
409 | { | |
410 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
c2233a26 | 411 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
0506b885 | 412 | int ret; |
1245b700 | 413 | |
72b745e3 | 414 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 415 | |
53294353 KM |
416 | ret = snd_soc_dai_compr_ack(cpu_dai, cstream, bytes); |
417 | if (ret < 0) | |
418 | goto err; | |
2e622ae4 | 419 | |
0506b885 | 420 | ret = snd_soc_component_compr_ack(cstream, bytes); |
2e622ae4 | 421 | err: |
72b745e3 | 422 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
423 | return ret; |
424 | } | |
425 | ||
426 | static int soc_compr_pointer(struct snd_compr_stream *cstream, | |
89027d9e | 427 | struct snd_compr_tstamp *tstamp) |
1245b700 NK |
428 | { |
429 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
03ecea64 | 430 | int ret; |
c2233a26 | 431 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
1245b700 | 432 | |
72b745e3 | 433 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 434 | |
ed38cc59 KM |
435 | ret = snd_soc_dai_compr_pointer(cpu_dai, cstream, tstamp); |
436 | if (ret < 0) | |
437 | goto out; | |
2e622ae4 | 438 | |
03ecea64 | 439 | ret = snd_soc_component_compr_pointer(cstream, tstamp); |
ed38cc59 | 440 | out: |
72b745e3 | 441 | mutex_unlock(&rtd->card->pcm_mutex); |
7c9190f7 | 442 | return ret; |
1245b700 NK |
443 | } |
444 | ||
02bd90e8 | 445 | static int soc_compr_set_metadata(struct snd_compr_stream *cstream, |
89027d9e | 446 | struct snd_compr_metadata *metadata) |
36953d98 JK |
447 | { |
448 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
c2233a26 | 449 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
1b308fb1 | 450 | int ret; |
36953d98 | 451 | |
88b3a7df KM |
452 | ret = snd_soc_dai_compr_set_metadata(cpu_dai, cstream, metadata); |
453 | if (ret < 0) | |
454 | return ret; | |
2e622ae4 | 455 | |
1b308fb1 | 456 | return snd_soc_component_compr_set_metadata(cstream, metadata); |
36953d98 JK |
457 | } |
458 | ||
02bd90e8 | 459 | static int soc_compr_get_metadata(struct snd_compr_stream *cstream, |
89027d9e | 460 | struct snd_compr_metadata *metadata) |
36953d98 JK |
461 | { |
462 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
c2233a26 | 463 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
bab78c23 | 464 | int ret; |
36953d98 | 465 | |
94d72819 KM |
466 | ret = snd_soc_dai_compr_get_metadata(cpu_dai, cstream, metadata); |
467 | if (ret < 0) | |
468 | return ret; | |
2e622ae4 | 469 | |
bab78c23 | 470 | return snd_soc_component_compr_get_metadata(cstream, metadata); |
36953d98 | 471 | } |
2a99ef0f | 472 | |
1245b700 NK |
473 | /* ASoC Compress operations */ |
474 | static struct snd_compr_ops soc_compr_ops = { | |
475 | .open = soc_compr_open, | |
476 | .free = soc_compr_free, | |
477 | .set_params = soc_compr_set_params, | |
02bd90e8 VK |
478 | .set_metadata = soc_compr_set_metadata, |
479 | .get_metadata = soc_compr_get_metadata, | |
1245b700 NK |
480 | .get_params = soc_compr_get_params, |
481 | .trigger = soc_compr_trigger, | |
482 | .pointer = soc_compr_pointer, | |
483 | .ack = soc_compr_ack, | |
d67fcb2d | 484 | .get_caps = snd_soc_component_compr_get_caps, |
0f6fe097 | 485 | .get_codec_caps = snd_soc_component_compr_get_codec_caps, |
1245b700 NK |
486 | }; |
487 | ||
2a99ef0f LG |
488 | /* ASoC Dynamic Compress operations */ |
489 | static struct snd_compr_ops soc_compr_dyn_ops = { | |
490 | .open = soc_compr_open_fe, | |
491 | .free = soc_compr_free_fe, | |
492 | .set_params = soc_compr_set_params_fe, | |
493 | .get_params = soc_compr_get_params, | |
494 | .set_metadata = soc_compr_set_metadata, | |
495 | .get_metadata = soc_compr_get_metadata, | |
496 | .trigger = soc_compr_trigger_fe, | |
497 | .pointer = soc_compr_pointer, | |
498 | .ack = soc_compr_ack, | |
d67fcb2d | 499 | .get_caps = snd_soc_component_compr_get_caps, |
0f6fe097 | 500 | .get_codec_caps = snd_soc_component_compr_get_codec_caps, |
2a99ef0f LG |
501 | }; |
502 | ||
6f0c4226 JY |
503 | /** |
504 | * snd_soc_new_compress - create a new compress. | |
505 | * | |
506 | * @rtd: The runtime for which we will create compress | |
507 | * @num: the device index number (zero based - shared with normal PCMs) | |
508 | * | |
509 | * Return: 0 for success, else error. | |
510 | */ | |
511 | int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) | |
1245b700 | 512 | { |
9e7e3738 | 513 | struct snd_soc_component *component; |
c2233a26 KM |
514 | struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); |
515 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); | |
1245b700 | 516 | struct snd_compr *compr; |
2a99ef0f | 517 | struct snd_pcm *be_pcm; |
1245b700 NK |
518 | char new_name[64]; |
519 | int ret = 0, direction = 0; | |
a1068045 | 520 | int playback = 0, capture = 0; |
613fb500 | 521 | int i; |
1245b700 | 522 | |
7428d8c8 KM |
523 | /* |
524 | * make sure these are same value, | |
525 | * and then use these as equally | |
526 | */ | |
527 | BUILD_BUG_ON((int)SNDRV_PCM_STREAM_PLAYBACK != (int)SND_COMPRESS_PLAYBACK); | |
528 | BUILD_BUG_ON((int)SNDRV_PCM_STREAM_CAPTURE != (int)SND_COMPRESS_CAPTURE); | |
529 | ||
6e1276a5 BL |
530 | if (rtd->num_cpus > 1 || |
531 | rtd->num_codecs > 1) { | |
141dfc9e | 532 | dev_err(rtd->card->dev, |
6e1276a5 | 533 | "Compress ASoC: Multi CPU/Codec not supported\n"); |
8151d5e6 BC |
534 | return -EINVAL; |
535 | } | |
536 | ||
1245b700 | 537 | /* check client and interface hw capabilities */ |
467fece8 KM |
538 | if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && |
539 | snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) | |
a1068045 | 540 | playback = 1; |
467fece8 KM |
541 | if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && |
542 | snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) | |
a1068045 VK |
543 | capture = 1; |
544 | ||
a1068045 VK |
545 | /* |
546 | * Compress devices are unidirectional so only one of the directions | |
547 | * should be set, check for that (xor) | |
548 | */ | |
549 | if (playback + capture != 1) { | |
141dfc9e CK |
550 | dev_err(rtd->card->dev, |
551 | "Compress ASoC: Invalid direction for P %d, C %d\n", | |
552 | playback, capture); | |
a1068045 VK |
553 | return -EINVAL; |
554 | } | |
555 | ||
aeb6fa0f | 556 | if (playback) |
daa2db59 | 557 | direction = SND_COMPRESS_PLAYBACK; |
daa2db59 | 558 | else |
a1068045 | 559 | direction = SND_COMPRESS_CAPTURE; |
daa2db59 | 560 | |
09f448a4 | 561 | compr = devm_kzalloc(rtd->card->dev, sizeof(*compr), GFP_KERNEL); |
7a0cf42e | 562 | if (!compr) |
1245b700 | 563 | return -ENOMEM; |
1245b700 | 564 | |
1f88eb0f CK |
565 | compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops), |
566 | GFP_KERNEL); | |
09f448a4 AS |
567 | if (!compr->ops) |
568 | return -ENOMEM; | |
2a99ef0f LG |
569 | |
570 | if (rtd->dai_link->dynamic) { | |
571 | snprintf(new_name, sizeof(new_name), "(%s)", | |
572 | rtd->dai_link->stream_name); | |
573 | ||
574 | ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, | |
d3268a40 QY |
575 | rtd->dai_link->dpcm_playback, |
576 | rtd->dai_link->dpcm_capture, &be_pcm); | |
2a99ef0f | 577 | if (ret < 0) { |
141dfc9e CK |
578 | dev_err(rtd->card->dev, |
579 | "Compress ASoC: can't create compressed for %s: %d\n", | |
580 | rtd->dai_link->name, ret); | |
09f448a4 | 581 | return ret; |
2a99ef0f LG |
582 | } |
583 | ||
584 | rtd->pcm = be_pcm; | |
585 | rtd->fe_compr = 1; | |
d3268a40 QY |
586 | if (rtd->dai_link->dpcm_playback) |
587 | be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; | |
588 | else if (rtd->dai_link->dpcm_capture) | |
589 | be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; | |
2a99ef0f | 590 | memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops)); |
aeb6fa0f PD |
591 | } else { |
592 | snprintf(new_name, sizeof(new_name), "%s %s-%d", | |
593 | rtd->dai_link->stream_name, codec_dai->name, num); | |
594 | ||
2a99ef0f | 595 | memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); |
aeb6fa0f | 596 | } |
1f88eb0f | 597 | |
c6cb522c KM |
598 | for_each_rtd_components(rtd, i, component) { |
599 | if (!component->driver->compress_ops || | |
600 | !component->driver->compress_ops->copy) | |
601 | continue; | |
602 | ||
b5852e66 | 603 | compr->ops->copy = snd_soc_component_compr_copy; |
c6cb522c KM |
604 | break; |
605 | } | |
606 | ||
1245b700 | 607 | mutex_init(&compr->lock); |
e5241a8c RF |
608 | ret = snd_compress_new(rtd->card->snd_card, num, direction, |
609 | new_name, compr); | |
1245b700 | 610 | if (ret < 0) { |
c2233a26 | 611 | component = asoc_rtd_to_codec(rtd, 0)->component; |
141dfc9e CK |
612 | dev_err(component->dev, |
613 | "Compress ASoC: can't create compress for codec %s: %d\n", | |
614 | component->name, ret); | |
09f448a4 | 615 | return ret; |
1245b700 NK |
616 | } |
617 | ||
202c8f70 | 618 | /* DAPM dai link stream work */ |
83f94a2e | 619 | rtd->close_delayed_work_func = snd_soc_close_delayed_work; |
202c8f70 | 620 | |
1245b700 NK |
621 | rtd->compr = compr; |
622 | compr->private_data = rtd; | |
623 | ||
1d5cd525 PLB |
624 | dev_dbg(rtd->card->dev, "Compress ASoC: %s <-> %s mapping ok\n", |
625 | codec_dai->name, cpu_dai->name); | |
1f88eb0f | 626 | |
09f448a4 | 627 | return 0; |
1245b700 | 628 | } |
6f0c4226 | 629 | EXPORT_SYMBOL_GPL(snd_soc_new_compress); |