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 <namartax.kohli@intel.com> |
8 | // Ramesh Babu K V <ramesh.babu@linux.intel.com> |
9 | // Vinod Koul <vinod.koul@linux.intel.com> |
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> |
21 | #include <sound/soc-dpcm.h> |
22 | #include <sound/soc-link.h> |
23 | |
24 | static int snd_soc_compr_components_open(struct snd_compr_stream *cstream) |
25 | { |
26 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
27 | struct snd_soc_component *component; |
28 | int ret = 0; |
29 | int i; |
30 | |
31 | for_each_rtd_components(rtd, i, component) { |
32 | ret = snd_soc_component_module_get_when_open(component, cstream); |
33 | if (ret < 0) |
34 | break; |
35 | |
36 | ret = snd_soc_component_compr_open(component, cstream); |
37 | if (ret < 0) |
38 | break; |
39 | } |
40 | |
41 | return ret; |
42 | } |
43 | |
44 | static void snd_soc_compr_components_free(struct snd_compr_stream *cstream, |
45 | int rollback) |
46 | { |
47 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
48 | struct snd_soc_component *component; |
49 | int i; |
50 | |
51 | for_each_rtd_components(rtd, i, component) { |
52 | snd_soc_component_compr_free(component, cstream, rollback); |
53 | snd_soc_component_module_put_when_close(component, cstream, rollback); |
54 | } |
55 | } |
56 | |
57 | static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback) |
58 | { |
59 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
60 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
61 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
62 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
63 | |
64 | snd_soc_dpcm_mutex_lock(rtd); |
65 | |
66 | if (!rollback) |
67 | snd_soc_runtime_deactivate(rtd, stream); |
68 | |
69 | snd_soc_dai_digital_mute(dai: codec_dai, mute: 1, direction: stream); |
70 | |
71 | if (!snd_soc_dai_active(dai: cpu_dai)) |
72 | cpu_dai->rate = 0; |
73 | |
74 | if (!snd_soc_dai_active(dai: codec_dai)) |
75 | codec_dai->rate = 0; |
76 | |
77 | snd_soc_link_compr_shutdown(cstream, rollback); |
78 | |
79 | snd_soc_compr_components_free(cstream, rollback); |
80 | |
81 | snd_soc_dai_compr_shutdown(dai: cpu_dai, cstream, rollback); |
82 | |
83 | if (!rollback) |
84 | snd_soc_dapm_stream_stop(rtd, stream); |
85 | |
86 | snd_soc_dpcm_mutex_unlock(rtd); |
87 | |
88 | snd_soc_pcm_component_pm_runtime_put(rtd, stream: cstream, rollback); |
89 | |
90 | return 0; |
91 | } |
92 | |
93 | static int soc_compr_free(struct snd_compr_stream *cstream) |
94 | { |
95 | return soc_compr_clean(cstream, rollback: 0); |
96 | } |
97 | |
98 | static int soc_compr_open(struct snd_compr_stream *cstream) |
99 | { |
100 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
101 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
102 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
103 | int ret; |
104 | |
105 | ret = snd_soc_pcm_component_pm_runtime_get(rtd, stream: cstream); |
106 | if (ret < 0) |
107 | goto err_no_lock; |
108 | |
109 | snd_soc_dpcm_mutex_lock(rtd); |
110 | |
111 | ret = snd_soc_dai_compr_startup(dai: cpu_dai, cstream); |
112 | if (ret < 0) |
113 | goto err; |
114 | |
115 | ret = snd_soc_compr_components_open(cstream); |
116 | if (ret < 0) |
117 | goto err; |
118 | |
119 | ret = snd_soc_link_compr_startup(cstream); |
120 | if (ret < 0) |
121 | goto err; |
122 | |
123 | snd_soc_runtime_activate(rtd, stream); |
124 | err: |
125 | snd_soc_dpcm_mutex_unlock(rtd); |
126 | err_no_lock: |
127 | if (ret < 0) |
128 | soc_compr_clean(cstream, rollback: 1); |
129 | |
130 | return ret; |
131 | } |
132 | |
133 | static int soc_compr_open_fe(struct snd_compr_stream *cstream) |
134 | { |
135 | struct snd_soc_pcm_runtime *fe = cstream->private_data; |
136 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0); |
137 | struct snd_soc_dpcm *dpcm; |
138 | struct snd_soc_dapm_widget_list *list; |
139 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
140 | int ret; |
141 | |
142 | snd_soc_card_mutex_lock(card: fe->card); |
143 | |
144 | ret = dpcm_path_get(fe, stream, list_: &list); |
145 | if (ret < 0) |
146 | goto be_err; |
147 | |
148 | snd_soc_dpcm_mutex_lock(fe); |
149 | |
150 | /* calculate valid and active FE <-> BE dpcms */ |
151 | dpcm_process_paths(fe, stream, list: &list, new: 1); |
152 | |
153 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; |
154 | |
155 | ret = dpcm_be_dai_startup(fe, stream); |
156 | if (ret < 0) { |
157 | /* clean up all links */ |
158 | for_each_dpcm_be(fe, stream, dpcm) |
159 | dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; |
160 | |
161 | dpcm_be_disconnect(fe, stream); |
162 | goto out; |
163 | } |
164 | |
165 | ret = snd_soc_dai_compr_startup(dai: cpu_dai, cstream); |
166 | if (ret < 0) |
167 | goto out; |
168 | |
169 | ret = snd_soc_compr_components_open(cstream); |
170 | if (ret < 0) |
171 | goto open_err; |
172 | |
173 | ret = snd_soc_link_compr_startup(cstream); |
174 | if (ret < 0) |
175 | goto machine_err; |
176 | |
177 | dpcm_clear_pending_state(fe, stream); |
178 | dpcm_path_put(list: &list); |
179 | |
180 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; |
181 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; |
182 | |
183 | snd_soc_runtime_activate(rtd: fe, stream); |
184 | snd_soc_dpcm_mutex_unlock(fe); |
185 | |
186 | snd_soc_card_mutex_unlock(card: fe->card); |
187 | |
188 | return 0; |
189 | |
190 | machine_err: |
191 | snd_soc_compr_components_free(cstream, rollback: 1); |
192 | open_err: |
193 | snd_soc_dai_compr_shutdown(dai: cpu_dai, cstream, rollback: 1); |
194 | out: |
195 | dpcm_path_put(list: &list); |
196 | snd_soc_dpcm_mutex_unlock(fe); |
197 | be_err: |
198 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; |
199 | snd_soc_card_mutex_unlock(card: fe->card); |
200 | return ret; |
201 | } |
202 | |
203 | static int soc_compr_free_fe(struct snd_compr_stream *cstream) |
204 | { |
205 | struct snd_soc_pcm_runtime *fe = cstream->private_data; |
206 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0); |
207 | struct snd_soc_dpcm *dpcm; |
208 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
209 | |
210 | snd_soc_card_mutex_lock(card: fe->card); |
211 | |
212 | snd_soc_dpcm_mutex_lock(fe); |
213 | snd_soc_runtime_deactivate(rtd: fe, stream); |
214 | |
215 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; |
216 | |
217 | dpcm_be_dai_hw_free(fe, stream); |
218 | |
219 | dpcm_be_dai_shutdown(fe, stream); |
220 | |
221 | /* mark FE's links ready to prune */ |
222 | for_each_dpcm_be(fe, stream, dpcm) |
223 | dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; |
224 | |
225 | dpcm_dapm_stream_event(fe, dir: stream, SND_SOC_DAPM_STREAM_STOP); |
226 | |
227 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; |
228 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; |
229 | |
230 | dpcm_be_disconnect(fe, stream); |
231 | |
232 | snd_soc_dpcm_mutex_unlock(fe); |
233 | |
234 | snd_soc_link_compr_shutdown(cstream, rollback: 0); |
235 | |
236 | snd_soc_compr_components_free(cstream, rollback: 0); |
237 | |
238 | snd_soc_dai_compr_shutdown(dai: cpu_dai, cstream, rollback: 0); |
239 | |
240 | snd_soc_card_mutex_unlock(card: fe->card); |
241 | return 0; |
242 | } |
243 | |
244 | static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) |
245 | { |
246 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
247 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
248 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
249 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
250 | int ret; |
251 | |
252 | snd_soc_dpcm_mutex_lock(rtd); |
253 | |
254 | ret = snd_soc_component_compr_trigger(cstream, cmd); |
255 | if (ret < 0) |
256 | goto out; |
257 | |
258 | ret = snd_soc_dai_compr_trigger(dai: cpu_dai, cstream, cmd); |
259 | if (ret < 0) |
260 | goto out; |
261 | |
262 | switch (cmd) { |
263 | case SNDRV_PCM_TRIGGER_START: |
264 | snd_soc_dai_digital_mute(dai: codec_dai, mute: 0, direction: stream); |
265 | break; |
266 | case SNDRV_PCM_TRIGGER_STOP: |
267 | snd_soc_dai_digital_mute(dai: codec_dai, mute: 1, direction: stream); |
268 | break; |
269 | } |
270 | |
271 | out: |
272 | snd_soc_dpcm_mutex_unlock(rtd); |
273 | return ret; |
274 | } |
275 | |
276 | static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) |
277 | { |
278 | struct snd_soc_pcm_runtime *fe = cstream->private_data; |
279 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0); |
280 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
281 | int ret; |
282 | |
283 | if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || |
284 | cmd == SND_COMPR_TRIGGER_DRAIN) |
285 | return snd_soc_component_compr_trigger(cstream, cmd); |
286 | |
287 | snd_soc_card_mutex_lock(card: fe->card); |
288 | |
289 | ret = snd_soc_dai_compr_trigger(dai: cpu_dai, cstream, cmd); |
290 | if (ret < 0) |
291 | goto out; |
292 | |
293 | ret = snd_soc_component_compr_trigger(cstream, cmd); |
294 | if (ret < 0) |
295 | goto out; |
296 | |
297 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; |
298 | |
299 | ret = dpcm_be_dai_trigger(fe, stream, cmd); |
300 | |
301 | switch (cmd) { |
302 | case SNDRV_PCM_TRIGGER_START: |
303 | case SNDRV_PCM_TRIGGER_RESUME: |
304 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
305 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; |
306 | break; |
307 | case SNDRV_PCM_TRIGGER_STOP: |
308 | case SNDRV_PCM_TRIGGER_SUSPEND: |
309 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; |
310 | break; |
311 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
312 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; |
313 | break; |
314 | } |
315 | |
316 | out: |
317 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; |
318 | snd_soc_card_mutex_unlock(card: fe->card); |
319 | return ret; |
320 | } |
321 | |
322 | static int soc_compr_set_params(struct snd_compr_stream *cstream, |
323 | struct snd_compr_params *params) |
324 | { |
325 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
326 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
327 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
328 | int ret; |
329 | |
330 | snd_soc_dpcm_mutex_lock(rtd); |
331 | |
332 | /* |
333 | * First we call set_params for the CPU DAI, then the component |
334 | * driver this should configure the SoC side. If the machine has |
335 | * compressed ops then we call that as well. The expectation is |
336 | * that these callbacks will configure everything for this compress |
337 | * path, like configuring a PCM port for a CODEC. |
338 | */ |
339 | ret = snd_soc_dai_compr_set_params(dai: cpu_dai, cstream, params); |
340 | if (ret < 0) |
341 | goto err; |
342 | |
343 | ret = snd_soc_component_compr_set_params(cstream, params); |
344 | if (ret < 0) |
345 | goto err; |
346 | |
347 | ret = snd_soc_link_compr_set_params(cstream); |
348 | if (ret < 0) |
349 | goto err; |
350 | |
351 | snd_soc_dapm_stream_event(rtd, stream, SND_SOC_DAPM_STREAM_START); |
352 | |
353 | /* cancel any delayed stream shutdown that is pending */ |
354 | rtd->pop_wait = 0; |
355 | snd_soc_dpcm_mutex_unlock(rtd); |
356 | |
357 | cancel_delayed_work_sync(dwork: &rtd->delayed_work); |
358 | |
359 | return 0; |
360 | |
361 | err: |
362 | snd_soc_dpcm_mutex_unlock(rtd); |
363 | return ret; |
364 | } |
365 | |
366 | static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, |
367 | struct snd_compr_params *params) |
368 | { |
369 | struct snd_soc_pcm_runtime *fe = cstream->private_data; |
370 | struct snd_pcm_substream *fe_substream = |
371 | fe->pcm->streams[cstream->direction].substream; |
372 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0); |
373 | int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ |
374 | int ret; |
375 | |
376 | snd_soc_card_mutex_lock(card: fe->card); |
377 | |
378 | /* |
379 | * Create an empty hw_params for the BE as the machine driver must |
380 | * fix this up to match DSP decoder and ASRC configuration. |
381 | * I.e. machine driver fixup for compressed BE is mandatory. |
382 | */ |
383 | memset(&fe->dpcm[fe_substream->stream].hw_params, 0, |
384 | sizeof(struct snd_pcm_hw_params)); |
385 | |
386 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; |
387 | |
388 | snd_soc_dpcm_mutex_lock(fe); |
389 | ret = dpcm_be_dai_hw_params(fe, tream: stream); |
390 | snd_soc_dpcm_mutex_unlock(fe); |
391 | if (ret < 0) |
392 | goto out; |
393 | |
394 | snd_soc_dpcm_mutex_lock(fe); |
395 | ret = dpcm_be_dai_prepare(fe, stream); |
396 | snd_soc_dpcm_mutex_unlock(fe); |
397 | if (ret < 0) |
398 | goto out; |
399 | |
400 | ret = snd_soc_dai_compr_set_params(dai: cpu_dai, cstream, params); |
401 | if (ret < 0) |
402 | goto out; |
403 | |
404 | ret = snd_soc_component_compr_set_params(cstream, params); |
405 | if (ret < 0) |
406 | goto out; |
407 | |
408 | ret = snd_soc_link_compr_set_params(cstream); |
409 | if (ret < 0) |
410 | goto out; |
411 | snd_soc_dpcm_mutex_lock(fe); |
412 | dpcm_dapm_stream_event(fe, dir: stream, SND_SOC_DAPM_STREAM_START); |
413 | snd_soc_dpcm_mutex_unlock(fe); |
414 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; |
415 | |
416 | out: |
417 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; |
418 | snd_soc_card_mutex_unlock(card: fe->card); |
419 | return ret; |
420 | } |
421 | |
422 | static int soc_compr_get_params(struct snd_compr_stream *cstream, |
423 | struct snd_codec *params) |
424 | { |
425 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
426 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
427 | int ret = 0; |
428 | |
429 | snd_soc_dpcm_mutex_lock(rtd); |
430 | |
431 | ret = snd_soc_dai_compr_get_params(dai: cpu_dai, cstream, params); |
432 | if (ret < 0) |
433 | goto err; |
434 | |
435 | ret = snd_soc_component_compr_get_params(cstream, params); |
436 | err: |
437 | snd_soc_dpcm_mutex_unlock(rtd); |
438 | return ret; |
439 | } |
440 | |
441 | static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) |
442 | { |
443 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
444 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
445 | int ret; |
446 | |
447 | snd_soc_dpcm_mutex_lock(rtd); |
448 | |
449 | ret = snd_soc_dai_compr_ack(dai: cpu_dai, cstream, bytes); |
450 | if (ret < 0) |
451 | goto err; |
452 | |
453 | ret = snd_soc_component_compr_ack(cstream, bytes); |
454 | err: |
455 | snd_soc_dpcm_mutex_unlock(rtd); |
456 | return ret; |
457 | } |
458 | |
459 | static int soc_compr_pointer(struct snd_compr_stream *cstream, |
460 | struct snd_compr_tstamp *tstamp) |
461 | { |
462 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
463 | int ret; |
464 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
465 | |
466 | snd_soc_dpcm_mutex_lock(rtd); |
467 | |
468 | ret = snd_soc_dai_compr_pointer(dai: cpu_dai, cstream, tstamp); |
469 | if (ret < 0) |
470 | goto out; |
471 | |
472 | ret = snd_soc_component_compr_pointer(cstream, tstamp); |
473 | out: |
474 | snd_soc_dpcm_mutex_unlock(rtd); |
475 | return ret; |
476 | } |
477 | |
478 | static int soc_compr_set_metadata(struct snd_compr_stream *cstream, |
479 | struct snd_compr_metadata *metadata) |
480 | { |
481 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
482 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
483 | int ret; |
484 | |
485 | ret = snd_soc_dai_compr_set_metadata(dai: cpu_dai, cstream, metadata); |
486 | if (ret < 0) |
487 | return ret; |
488 | |
489 | return snd_soc_component_compr_set_metadata(cstream, metadata); |
490 | } |
491 | |
492 | static int soc_compr_get_metadata(struct snd_compr_stream *cstream, |
493 | struct snd_compr_metadata *metadata) |
494 | { |
495 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
496 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
497 | int ret; |
498 | |
499 | ret = snd_soc_dai_compr_get_metadata(dai: cpu_dai, cstream, metadata); |
500 | if (ret < 0) |
501 | return ret; |
502 | |
503 | return snd_soc_component_compr_get_metadata(cstream, metadata); |
504 | } |
505 | |
506 | /* ASoC Compress operations */ |
507 | static struct snd_compr_ops soc_compr_ops = { |
508 | .open = soc_compr_open, |
509 | .free = soc_compr_free, |
510 | .set_params = soc_compr_set_params, |
511 | .set_metadata = soc_compr_set_metadata, |
512 | .get_metadata = soc_compr_get_metadata, |
513 | .get_params = soc_compr_get_params, |
514 | .trigger = soc_compr_trigger, |
515 | .pointer = soc_compr_pointer, |
516 | .ack = soc_compr_ack, |
517 | .get_caps = snd_soc_component_compr_get_caps, |
518 | .get_codec_caps = snd_soc_component_compr_get_codec_caps, |
519 | }; |
520 | |
521 | /* ASoC Dynamic Compress operations */ |
522 | static struct snd_compr_ops soc_compr_dyn_ops = { |
523 | .open = soc_compr_open_fe, |
524 | .free = soc_compr_free_fe, |
525 | .set_params = soc_compr_set_params_fe, |
526 | .get_params = soc_compr_get_params, |
527 | .set_metadata = soc_compr_set_metadata, |
528 | .get_metadata = soc_compr_get_metadata, |
529 | .trigger = soc_compr_trigger_fe, |
530 | .pointer = soc_compr_pointer, |
531 | .ack = soc_compr_ack, |
532 | .get_caps = snd_soc_component_compr_get_caps, |
533 | .get_codec_caps = snd_soc_component_compr_get_codec_caps, |
534 | }; |
535 | |
536 | /** |
537 | * snd_soc_new_compress - create a new compress. |
538 | * |
539 | * @rtd: The runtime for which we will create compress |
540 | * @num: the device index number (zero based - shared with normal PCMs) |
541 | * |
542 | * Return: 0 for success, else error. |
543 | */ |
544 | int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) |
545 | { |
546 | struct snd_soc_component *component; |
547 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
548 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
549 | struct snd_compr *compr; |
550 | struct snd_pcm *be_pcm; |
551 | char new_name[64]; |
552 | int ret = 0, direction = 0; |
553 | int playback = 0, capture = 0; |
554 | int i; |
555 | |
556 | /* |
557 | * make sure these are same value, |
558 | * and then use these as equally |
559 | */ |
560 | BUILD_BUG_ON((int)SNDRV_PCM_STREAM_PLAYBACK != (int)SND_COMPRESS_PLAYBACK); |
561 | BUILD_BUG_ON((int)SNDRV_PCM_STREAM_CAPTURE != (int)SND_COMPRESS_CAPTURE); |
562 | |
563 | if (rtd->dai_link->num_cpus > 1 || |
564 | rtd->dai_link->num_codecs > 1) { |
565 | dev_err(rtd->card->dev, |
566 | "Compress ASoC: Multi CPU/Codec not supported\n" ); |
567 | return -EINVAL; |
568 | } |
569 | |
570 | if (!codec_dai) { |
571 | dev_err(rtd->card->dev, "Missing codec\n" ); |
572 | return -EINVAL; |
573 | } |
574 | |
575 | /* check client and interface hw capabilities */ |
576 | if (snd_soc_dai_stream_valid(dai: codec_dai, stream: SNDRV_PCM_STREAM_PLAYBACK) && |
577 | snd_soc_dai_stream_valid(dai: cpu_dai, stream: SNDRV_PCM_STREAM_PLAYBACK)) |
578 | playback = 1; |
579 | if (snd_soc_dai_stream_valid(dai: codec_dai, stream: SNDRV_PCM_STREAM_CAPTURE) && |
580 | snd_soc_dai_stream_valid(dai: cpu_dai, stream: SNDRV_PCM_STREAM_CAPTURE)) |
581 | capture = 1; |
582 | |
583 | /* |
584 | * Compress devices are unidirectional so only one of the directions |
585 | * should be set, check for that (xor) |
586 | */ |
587 | if (playback + capture != 1) { |
588 | dev_err(rtd->card->dev, |
589 | "Compress ASoC: Invalid direction for P %d, C %d\n" , |
590 | playback, capture); |
591 | return -EINVAL; |
592 | } |
593 | |
594 | if (playback) |
595 | direction = SND_COMPRESS_PLAYBACK; |
596 | else |
597 | direction = SND_COMPRESS_CAPTURE; |
598 | |
599 | compr = devm_kzalloc(dev: rtd->card->dev, size: sizeof(*compr), GFP_KERNEL); |
600 | if (!compr) |
601 | return -ENOMEM; |
602 | |
603 | compr->ops = devm_kzalloc(dev: rtd->card->dev, size: sizeof(soc_compr_ops), |
604 | GFP_KERNEL); |
605 | if (!compr->ops) |
606 | return -ENOMEM; |
607 | |
608 | if (rtd->dai_link->dynamic) { |
609 | snprintf(buf: new_name, size: sizeof(new_name), fmt: "(%s)" , |
610 | rtd->dai_link->stream_name); |
611 | |
612 | ret = snd_pcm_new_internal(card: rtd->card->snd_card, id: new_name, device: num, |
613 | playback_count: rtd->dai_link->dpcm_playback, |
614 | capture_count: rtd->dai_link->dpcm_capture, rpcm: &be_pcm); |
615 | if (ret < 0) { |
616 | dev_err(rtd->card->dev, |
617 | "Compress ASoC: can't create compressed for %s: %d\n" , |
618 | rtd->dai_link->name, ret); |
619 | return ret; |
620 | } |
621 | |
622 | /* inherit atomicity from DAI link */ |
623 | be_pcm->nonatomic = rtd->dai_link->nonatomic; |
624 | |
625 | rtd->pcm = be_pcm; |
626 | rtd->fe_compr = 1; |
627 | if (rtd->dai_link->dpcm_playback) |
628 | be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; |
629 | if (rtd->dai_link->dpcm_capture) |
630 | be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; |
631 | memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops)); |
632 | } else { |
633 | snprintf(buf: new_name, size: sizeof(new_name), fmt: "%s %s-%d" , |
634 | rtd->dai_link->stream_name, codec_dai->name, num); |
635 | |
636 | memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); |
637 | } |
638 | |
639 | for_each_rtd_components(rtd, i, component) { |
640 | if (!component->driver->compress_ops || |
641 | !component->driver->compress_ops->copy) |
642 | continue; |
643 | |
644 | compr->ops->copy = snd_soc_component_compr_copy; |
645 | break; |
646 | } |
647 | |
648 | ret = snd_compress_new(card: rtd->card->snd_card, device: num, type: direction, |
649 | id: new_name, compr); |
650 | if (ret < 0) { |
651 | component = snd_soc_rtd_to_codec(rtd, 0)->component; |
652 | dev_err(component->dev, |
653 | "Compress ASoC: can't create compress for codec %s: %d\n" , |
654 | component->name, ret); |
655 | return ret; |
656 | } |
657 | |
658 | /* DAPM dai link stream work */ |
659 | rtd->close_delayed_work_func = snd_soc_close_delayed_work; |
660 | |
661 | rtd->compr = compr; |
662 | compr->private_data = rtd; |
663 | |
664 | dev_dbg(rtd->card->dev, "Compress ASoC: %s <-> %s mapping ok\n" , |
665 | codec_dai->name, cpu_dai->name); |
666 | |
667 | return 0; |
668 | } |
669 | EXPORT_SYMBOL_GPL(snd_soc_new_compress); |
670 | |