]>
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 <namartax.kohli@intel.com> | |
8 | // Ramesh Babu K V <ramesh.babu@linux.intel.com> | |
9 | // Vinod Koul <vinod.koul@linux.intel.com> | |
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 | |
1e57b828 CK |
25 | static int soc_compr_components_open(struct snd_compr_stream *cstream, |
26 | struct snd_soc_component **last) | |
1245b700 NK |
27 | { |
28 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 29 | struct snd_soc_component *component; |
613fb500 | 30 | int i, ret; |
1245b700 | 31 | |
c6cb522c KM |
32 | for_each_rtd_components(rtd, i, component) { |
33 | if (!component->driver->compress_ops || | |
34 | !component->driver->compress_ops->open) | |
35 | continue; | |
36 | ||
37 | ret = component->driver->compress_ops->open(component, cstream); | |
38 | if (ret < 0) { | |
39 | dev_err(component->dev, | |
40 | "Compress ASoC: can't open platform %s: %d\n", | |
41 | component->name, ret); | |
42 | ||
43 | *last = component; | |
44 | return ret; | |
45 | } | |
46 | } | |
47 | ||
1e57b828 CK |
48 | *last = NULL; |
49 | return 0; | |
50 | } | |
51 | ||
52 | static int soc_compr_components_free(struct snd_compr_stream *cstream, | |
53 | struct snd_soc_component *last) | |
54 | { | |
55 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
56 | struct snd_soc_component *component; | |
613fb500 | 57 | int i; |
1e57b828 | 58 | |
c6cb522c KM |
59 | for_each_rtd_components(rtd, i, component) { |
60 | if (component == last) | |
61 | break; | |
62 | ||
63 | if (!component->driver->compress_ops || | |
64 | !component->driver->compress_ops->free) | |
65 | continue; | |
66 | ||
67 | component->driver->compress_ops->free(component, cstream); | |
68 | } | |
69 | ||
1e57b828 CK |
70 | return 0; |
71 | } | |
72 | ||
73 | static int soc_compr_open(struct snd_compr_stream *cstream) | |
74 | { | |
75 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
3e645a4a | 76 | struct snd_soc_component *component = NULL, *save = NULL; |
c2233a26 | 77 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
613fb500 | 78 | int ret, i; |
1e57b828 | 79 | |
613fb500 | 80 | for_each_rtd_components(rtd, i, component) { |
4137f4b6 CR |
81 | ret = pm_runtime_get_sync(component->dev); |
82 | if (ret < 0 && ret != -EACCES) { | |
83 | pm_runtime_put_noidle(component->dev); | |
84 | save = component; | |
85 | goto pm_err; | |
86 | } | |
87 | } | |
88 | ||
72b745e3 | 89 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
1e57b828 | 90 | |
b5ae4cce KM |
91 | ret = snd_soc_dai_compr_startup(cpu_dai, cstream); |
92 | if (ret < 0) | |
93 | goto out; | |
1e57b828 CK |
94 | |
95 | ret = soc_compr_components_open(cstream, &component); | |
96 | if (ret < 0) | |
97 | goto machine_err; | |
9e7e3738 | 98 | |
9ab711cb KM |
99 | ret = snd_soc_link_compr_startup(cstream); |
100 | if (ret < 0) | |
101 | goto machine_err; | |
1245b700 | 102 | |
24894b76 | 103 | snd_soc_runtime_activate(rtd, cstream->direction); |
1245b700 | 104 | |
72b745e3 | 105 | mutex_unlock(&rtd->card->pcm_mutex); |
15e2e619 | 106 | |
1245b700 NK |
107 | return 0; |
108 | ||
109 | machine_err: | |
1e57b828 | 110 | soc_compr_components_free(cstream, component); |
9e7e3738 | 111 | |
2b25f81d | 112 | snd_soc_dai_compr_shutdown(cpu_dai, cstream); |
1245b700 | 113 | out: |
72b745e3 | 114 | mutex_unlock(&rtd->card->pcm_mutex); |
4137f4b6 | 115 | pm_err: |
613fb500 | 116 | for_each_rtd_components(rtd, i, component) { |
4137f4b6 CR |
117 | if (component == save) |
118 | break; | |
119 | pm_runtime_mark_last_busy(component->dev); | |
120 | pm_runtime_put_autosuspend(component->dev); | |
121 | } | |
122 | ||
1245b700 NK |
123 | return ret; |
124 | } | |
125 | ||
2a99ef0f LG |
126 | static int soc_compr_open_fe(struct snd_compr_stream *cstream) |
127 | { | |
128 | struct snd_soc_pcm_runtime *fe = cstream->private_data; | |
01b8cedf SBP |
129 | struct snd_pcm_substream *fe_substream = |
130 | fe->pcm->streams[cstream->direction].substream; | |
9e7e3738 | 131 | struct snd_soc_component *component; |
c2233a26 | 132 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); |
2a99ef0f LG |
133 | struct snd_soc_dpcm *dpcm; |
134 | struct snd_soc_dapm_widget_list *list; | |
135 | int stream; | |
572e6c8d | 136 | int ret; |
2a99ef0f LG |
137 | |
138 | if (cstream->direction == SND_COMPRESS_PLAYBACK) | |
139 | stream = SNDRV_PCM_STREAM_PLAYBACK; | |
140 | else | |
141 | stream = SNDRV_PCM_STREAM_CAPTURE; | |
142 | ||
143 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); | |
0b0722e1 SK |
144 | fe->dpcm[stream].runtime = fe_substream->runtime; |
145 | ||
146 | ret = dpcm_path_get(fe, stream, &list); | |
147 | if (ret < 0) | |
148 | goto be_err; | |
149 | else if (ret == 0) | |
150 | dev_dbg(fe->dev, "Compress ASoC: %s no valid %s route\n", | |
151 | fe->dai_link->name, stream ? "capture" : "playback"); | |
152 | /* calculate valid and active FE <-> BE dpcms */ | |
153 | dpcm_process_paths(fe, stream, &list, 1); | |
154 | fe->dpcm[stream].runtime = fe_substream->runtime; | |
155 | ||
156 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | |
157 | ||
158 | ret = dpcm_be_dai_startup(fe, stream); | |
159 | if (ret < 0) { | |
160 | /* clean up all links */ | |
8d6258a4 | 161 | for_each_dpcm_be(fe, stream, dpcm) |
0b0722e1 SK |
162 | dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; |
163 | ||
164 | dpcm_be_disconnect(fe, stream); | |
165 | fe->dpcm[stream].runtime = NULL; | |
166 | goto out; | |
167 | } | |
2a99ef0f | 168 | |
b5ae4cce KM |
169 | ret = snd_soc_dai_compr_startup(cpu_dai, cstream); |
170 | if (ret < 0) | |
171 | goto out; | |
2e622ae4 | 172 | |
1e57b828 CK |
173 | ret = soc_compr_components_open(cstream, &component); |
174 | if (ret < 0) | |
0b0722e1 | 175 | goto open_err; |
9e7e3738 | 176 | |
9ab711cb KM |
177 | ret = snd_soc_link_compr_startup(cstream); |
178 | if (ret < 0) | |
179 | goto machine_err; | |
2a99ef0f | 180 | |
2a99ef0f LG |
181 | dpcm_clear_pending_state(fe, stream); |
182 | dpcm_path_put(&list); | |
183 | ||
184 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; | |
185 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | |
186 | ||
24894b76 | 187 | snd_soc_runtime_activate(fe, stream); |
2a99ef0f LG |
188 | |
189 | mutex_unlock(&fe->card->mutex); | |
190 | ||
191 | return 0; | |
192 | ||
2a99ef0f | 193 | machine_err: |
1e57b828 | 194 | soc_compr_components_free(cstream, component); |
0b0722e1 | 195 | open_err: |
2b25f81d | 196 | snd_soc_dai_compr_shutdown(cpu_dai, cstream); |
2a99ef0f | 197 | out: |
0b0722e1 SK |
198 | dpcm_path_put(&list); |
199 | be_err: | |
2a99ef0f LG |
200 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; |
201 | mutex_unlock(&fe->card->mutex); | |
202 | return ret; | |
203 | } | |
204 | ||
1245b700 NK |
205 | static int soc_compr_free(struct snd_compr_stream *cstream) |
206 | { | |
207 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
4137f4b6 | 208 | struct snd_soc_component *component; |
c2233a26 KM |
209 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
210 | struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); | |
613fb500 | 211 | int stream, i; |
1245b700 | 212 | |
72b745e3 | 213 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 214 | |
24894b76 LPC |
215 | if (cstream->direction == SND_COMPRESS_PLAYBACK) |
216 | stream = SNDRV_PCM_STREAM_PLAYBACK; | |
217 | else | |
218 | stream = SNDRV_PCM_STREAM_CAPTURE; | |
1245b700 | 219 | |
24894b76 | 220 | snd_soc_runtime_deactivate(rtd, stream); |
da18396f | 221 | |
24894b76 | 222 | snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); |
1245b700 | 223 | |
b3dea624 | 224 | if (!snd_soc_dai_active(cpu_dai)) |
1245b700 NK |
225 | cpu_dai->rate = 0; |
226 | ||
b3dea624 | 227 | if (!snd_soc_dai_active(codec_dai)) |
1245b700 NK |
228 | codec_dai->rate = 0; |
229 | ||
0e532c99 | 230 | snd_soc_link_compr_shutdown(cstream); |
1245b700 | 231 | |
1e57b828 | 232 | soc_compr_components_free(cstream, NULL); |
9e7e3738 | 233 | |
2b25f81d | 234 | snd_soc_dai_compr_shutdown(cpu_dai, cstream); |
2e622ae4 | 235 | |
3f4cf797 | 236 | snd_soc_dapm_stream_stop(rtd, stream); |
1245b700 | 237 | |
72b745e3 | 238 | mutex_unlock(&rtd->card->pcm_mutex); |
4137f4b6 | 239 | |
613fb500 | 240 | for_each_rtd_components(rtd, i, component) { |
4137f4b6 CR |
241 | pm_runtime_mark_last_busy(component->dev); |
242 | pm_runtime_put_autosuspend(component->dev); | |
243 | } | |
244 | ||
1245b700 NK |
245 | return 0; |
246 | } | |
247 | ||
2a99ef0f LG |
248 | static int soc_compr_free_fe(struct snd_compr_stream *cstream) |
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); |
2a99ef0f LG |
252 | struct snd_soc_dpcm *dpcm; |
253 | int stream, ret; | |
254 | ||
255 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); | |
256 | ||
24894b76 | 257 | if (cstream->direction == SND_COMPRESS_PLAYBACK) |
2a99ef0f | 258 | stream = SNDRV_PCM_STREAM_PLAYBACK; |
24894b76 | 259 | else |
2a99ef0f | 260 | stream = SNDRV_PCM_STREAM_CAPTURE; |
2a99ef0f | 261 | |
24894b76 | 262 | snd_soc_runtime_deactivate(fe, stream); |
2a99ef0f LG |
263 | |
264 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | |
265 | ||
266 | ret = dpcm_be_dai_hw_free(fe, stream); | |
267 | if (ret < 0) | |
141dfc9e | 268 | dev_err(fe->dev, "Compressed ASoC: hw_free failed: %d\n", ret); |
2a99ef0f LG |
269 | |
270 | ret = dpcm_be_dai_shutdown(fe, stream); | |
271 | ||
272 | /* mark FE's links ready to prune */ | |
8d6258a4 | 273 | for_each_dpcm_be(fe, stream, dpcm) |
2a99ef0f LG |
274 | dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; |
275 | ||
1c531230 | 276 | dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); |
2a99ef0f LG |
277 | |
278 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; | |
279 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | |
280 | ||
281 | dpcm_be_disconnect(fe, stream); | |
282 | ||
283 | fe->dpcm[stream].runtime = NULL; | |
284 | ||
0e532c99 | 285 | snd_soc_link_compr_shutdown(cstream); |
2a99ef0f | 286 | |
1e57b828 | 287 | soc_compr_components_free(cstream, NULL); |
9e7e3738 | 288 | |
2b25f81d | 289 | snd_soc_dai_compr_shutdown(cpu_dai, cstream); |
2e622ae4 | 290 | |
2a99ef0f LG |
291 | mutex_unlock(&fe->card->mutex); |
292 | return 0; | |
293 | } | |
294 | ||
4ef0ecb8 CK |
295 | static int soc_compr_components_trigger(struct snd_compr_stream *cstream, |
296 | int cmd) | |
1245b700 | 297 | { |
1245b700 | 298 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
9e7e3738 | 299 | struct snd_soc_component *component; |
613fb500 | 300 | int i, ret; |
15e2e619 | 301 | |
c6cb522c KM |
302 | for_each_rtd_components(rtd, i, component) { |
303 | if (!component->driver->compress_ops || | |
304 | !component->driver->compress_ops->trigger) | |
305 | continue; | |
306 | ||
307 | ret = component->driver->compress_ops->trigger( | |
308 | component, cstream, cmd); | |
309 | if (ret < 0) | |
310 | return ret; | |
311 | } | |
312 | ||
4ef0ecb8 CK |
313 | return 0; |
314 | } | |
315 | ||
316 | static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) | |
317 | { | |
318 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
c2233a26 KM |
319 | struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); |
320 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); | |
4ef0ecb8 CK |
321 | int ret; |
322 | ||
72b745e3 | 323 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
4ef0ecb8 CK |
324 | |
325 | ret = soc_compr_components_trigger(cstream, cmd); | |
326 | if (ret < 0) | |
327 | goto out; | |
328 | ||
eb08411b KM |
329 | ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd); |
330 | if (ret < 0) | |
331 | goto out; | |
2e622ae4 | 332 | |
da18396f MB |
333 | switch (cmd) { |
334 | case SNDRV_PCM_TRIGGER_START: | |
335 | snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); | |
336 | break; | |
337 | case SNDRV_PCM_TRIGGER_STOP: | |
338 | snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); | |
339 | break; | |
e38b9b74 | 340 | } |
1245b700 | 341 | |
15e2e619 | 342 | out: |
72b745e3 | 343 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
344 | return ret; |
345 | } | |
346 | ||
2a99ef0f LG |
347 | static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) |
348 | { | |
349 | struct snd_soc_pcm_runtime *fe = cstream->private_data; | |
c2233a26 | 350 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); |
52cadf1f | 351 | int ret, stream; |
2a99ef0f LG |
352 | |
353 | if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || | |
4ef0ecb8 CK |
354 | cmd == SND_COMPR_TRIGGER_DRAIN) |
355 | return soc_compr_components_trigger(cstream, cmd); | |
2a99ef0f LG |
356 | |
357 | if (cstream->direction == SND_COMPRESS_PLAYBACK) | |
358 | stream = SNDRV_PCM_STREAM_PLAYBACK; | |
359 | else | |
360 | stream = SNDRV_PCM_STREAM_CAPTURE; | |
361 | ||
2a99ef0f LG |
362 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); |
363 | ||
eb08411b KM |
364 | ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd); |
365 | if (ret < 0) | |
366 | goto out; | |
2e622ae4 | 367 | |
4ef0ecb8 CK |
368 | ret = soc_compr_components_trigger(cstream, cmd); |
369 | if (ret < 0) | |
370 | goto out; | |
9e7e3738 | 371 | |
2a99ef0f LG |
372 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; |
373 | ||
374 | ret = dpcm_be_dai_trigger(fe, stream, cmd); | |
375 | ||
376 | switch (cmd) { | |
377 | case SNDRV_PCM_TRIGGER_START: | |
378 | case SNDRV_PCM_TRIGGER_RESUME: | |
379 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
380 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; | |
381 | break; | |
382 | case SNDRV_PCM_TRIGGER_STOP: | |
383 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
384 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; | |
385 | break; | |
386 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
387 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; | |
388 | break; | |
389 | } | |
390 | ||
391 | out: | |
392 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | |
393 | mutex_unlock(&fe->card->mutex); | |
394 | return ret; | |
395 | } | |
396 | ||
4ef0ecb8 CK |
397 | static int soc_compr_components_set_params(struct snd_compr_stream *cstream, |
398 | struct snd_compr_params *params) | |
1245b700 NK |
399 | { |
400 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 401 | struct snd_soc_component *component; |
613fb500 | 402 | int i, ret; |
4ef0ecb8 | 403 | |
c6cb522c KM |
404 | for_each_rtd_components(rtd, i, component) { |
405 | if (!component->driver->compress_ops || | |
406 | !component->driver->compress_ops->set_params) | |
407 | continue; | |
408 | ||
409 | ret = component->driver->compress_ops->set_params( | |
410 | component, cstream, params); | |
411 | if (ret < 0) | |
412 | return ret; | |
413 | } | |
414 | ||
4ef0ecb8 CK |
415 | return 0; |
416 | } | |
417 | ||
418 | static int soc_compr_set_params(struct snd_compr_stream *cstream, | |
419 | struct snd_compr_params *params) | |
420 | { | |
421 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
c2233a26 | 422 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
52cadf1f | 423 | int ret; |
1245b700 | 424 | |
72b745e3 | 425 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 426 | |
ef050bec CK |
427 | /* |
428 | * First we call set_params for the CPU DAI, then the component | |
429 | * driver this should configure the SoC side. If the machine has | |
430 | * compressed ops then we call that as well. The expectation is | |
431 | * that these callbacks will configure everything for this compress | |
432 | * path, like configuring a PCM port for a CODEC. | |
1245b700 | 433 | */ |
8dfedafb KM |
434 | ret = snd_soc_dai_compr_set_params(cpu_dai, cstream, params); |
435 | if (ret < 0) | |
436 | goto err; | |
2e622ae4 | 437 | |
4ef0ecb8 CK |
438 | ret = soc_compr_components_set_params(cstream, params); |
439 | if (ret < 0) | |
440 | goto err; | |
9e7e3738 | 441 | |
eab810f3 KM |
442 | ret = snd_soc_link_compr_set_params(cstream); |
443 | if (ret < 0) | |
444 | goto err; | |
1245b700 | 445 | |
2c071ed7 CK |
446 | if (cstream->direction == SND_COMPRESS_PLAYBACK) |
447 | snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, | |
89027d9e | 448 | SND_SOC_DAPM_STREAM_START); |
2c071ed7 CK |
449 | else |
450 | snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, | |
89027d9e | 451 | SND_SOC_DAPM_STREAM_START); |
1245b700 | 452 | |
fa40ef20 CK |
453 | /* cancel any delayed stream shutdown that is pending */ |
454 | rtd->pop_wait = 0; | |
72b745e3 | 455 | mutex_unlock(&rtd->card->pcm_mutex); |
fa40ef20 CK |
456 | |
457 | cancel_delayed_work_sync(&rtd->delayed_work); | |
458 | ||
52cadf1f | 459 | return 0; |
fa40ef20 CK |
460 | |
461 | err: | |
72b745e3 | 462 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
463 | return ret; |
464 | } | |
465 | ||
2a99ef0f | 466 | static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, |
89027d9e | 467 | struct snd_compr_params *params) |
2a99ef0f LG |
468 | { |
469 | struct snd_soc_pcm_runtime *fe = cstream->private_data; | |
01b8cedf SBP |
470 | struct snd_pcm_substream *fe_substream = |
471 | fe->pcm->streams[cstream->direction].substream; | |
c2233a26 | 472 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); |
52cadf1f | 473 | int ret, stream; |
2a99ef0f LG |
474 | |
475 | if (cstream->direction == SND_COMPRESS_PLAYBACK) | |
476 | stream = SNDRV_PCM_STREAM_PLAYBACK; | |
477 | else | |
478 | stream = SNDRV_PCM_STREAM_CAPTURE; | |
479 | ||
480 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); | |
481 | ||
0b0722e1 SK |
482 | /* |
483 | * Create an empty hw_params for the BE as the machine driver must | |
484 | * fix this up to match DSP decoder and ASRC configuration. | |
485 | * I.e. machine driver fixup for compressed BE is mandatory. | |
486 | */ | |
487 | memset(&fe->dpcm[fe_substream->stream].hw_params, 0, | |
488 | sizeof(struct snd_pcm_hw_params)); | |
489 | ||
490 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | |
491 | ||
492 | ret = dpcm_be_dai_hw_params(fe, stream); | |
493 | if (ret < 0) | |
494 | goto out; | |
495 | ||
496 | ret = dpcm_be_dai_prepare(fe, stream); | |
497 | if (ret < 0) | |
498 | goto out; | |
499 | ||
8dfedafb KM |
500 | ret = snd_soc_dai_compr_set_params(cpu_dai, cstream, params); |
501 | if (ret < 0) | |
502 | goto out; | |
2e622ae4 | 503 | |
4ef0ecb8 CK |
504 | ret = soc_compr_components_set_params(cstream, params); |
505 | if (ret < 0) | |
506 | goto out; | |
9e7e3738 | 507 | |
eab810f3 KM |
508 | ret = snd_soc_link_compr_set_params(cstream); |
509 | if (ret < 0) | |
510 | goto out; | |
2a99ef0f | 511 | |
15f6b09a | 512 | dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); |
2a99ef0f LG |
513 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; |
514 | ||
515 | out: | |
516 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | |
517 | mutex_unlock(&fe->card->mutex); | |
518 | return ret; | |
519 | } | |
520 | ||
1245b700 | 521 | static int soc_compr_get_params(struct snd_compr_stream *cstream, |
89027d9e | 522 | struct snd_codec *params) |
1245b700 NK |
523 | { |
524 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 525 | struct snd_soc_component *component; |
c2233a26 | 526 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
613fb500 | 527 | int i, ret = 0; |
1245b700 | 528 | |
72b745e3 | 529 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 530 | |
adbef543 KM |
531 | ret = snd_soc_dai_compr_get_params(cpu_dai, cstream, params); |
532 | if (ret < 0) | |
533 | goto err; | |
2e622ae4 | 534 | |
c6cb522c KM |
535 | for_each_rtd_components(rtd, i, component) { |
536 | if (!component->driver->compress_ops || | |
537 | !component->driver->compress_ops->get_params) | |
538 | continue; | |
539 | ||
540 | ret = component->driver->compress_ops->get_params( | |
541 | component, cstream, params); | |
542 | break; | |
543 | } | |
544 | ||
2e622ae4 | 545 | err: |
72b745e3 | 546 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
547 | return ret; |
548 | } | |
549 | ||
550 | static int soc_compr_get_caps(struct snd_compr_stream *cstream, | |
89027d9e | 551 | struct snd_compr_caps *caps) |
1245b700 NK |
552 | { |
553 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 554 | struct snd_soc_component *component; |
613fb500 | 555 | int i, ret = 0; |
1245b700 | 556 | |
72b745e3 | 557 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 558 | |
c6cb522c KM |
559 | for_each_rtd_components(rtd, i, component) { |
560 | if (!component->driver->compress_ops || | |
561 | !component->driver->compress_ops->get_caps) | |
562 | continue; | |
563 | ||
564 | ret = component->driver->compress_ops->get_caps( | |
565 | component, cstream, caps); | |
566 | break; | |
567 | } | |
568 | ||
72b745e3 | 569 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
570 | return ret; |
571 | } | |
572 | ||
573 | static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, | |
89027d9e | 574 | struct snd_compr_codec_caps *codec) |
1245b700 NK |
575 | { |
576 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 577 | struct snd_soc_component *component; |
613fb500 | 578 | int i, ret = 0; |
1245b700 | 579 | |
72b745e3 | 580 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 581 | |
c6cb522c KM |
582 | for_each_rtd_components(rtd, i, component) { |
583 | if (!component->driver->compress_ops || | |
584 | !component->driver->compress_ops->get_codec_caps) | |
585 | continue; | |
586 | ||
587 | ret = component->driver->compress_ops->get_codec_caps( | |
588 | component, cstream, codec); | |
589 | break; | |
590 | } | |
591 | ||
72b745e3 | 592 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
593 | return ret; |
594 | } | |
595 | ||
596 | static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) | |
597 | { | |
598 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 599 | struct snd_soc_component *component; |
c2233a26 | 600 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
613fb500 | 601 | int i, ret = 0; |
1245b700 | 602 | |
72b745e3 | 603 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 604 | |
53294353 KM |
605 | ret = snd_soc_dai_compr_ack(cpu_dai, cstream, bytes); |
606 | if (ret < 0) | |
607 | goto err; | |
2e622ae4 | 608 | |
c6cb522c KM |
609 | for_each_rtd_components(rtd, i, component) { |
610 | if (!component->driver->compress_ops || | |
611 | !component->driver->compress_ops->ack) | |
612 | continue; | |
613 | ||
614 | ret = component->driver->compress_ops->ack( | |
615 | component, cstream, bytes); | |
616 | if (ret < 0) | |
617 | goto err; | |
618 | } | |
619 | ||
2e622ae4 | 620 | err: |
72b745e3 | 621 | mutex_unlock(&rtd->card->pcm_mutex); |
1245b700 NK |
622 | return ret; |
623 | } | |
624 | ||
625 | static int soc_compr_pointer(struct snd_compr_stream *cstream, | |
89027d9e | 626 | struct snd_compr_tstamp *tstamp) |
1245b700 NK |
627 | { |
628 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 629 | struct snd_soc_component *component; |
613fb500 | 630 | int i, ret = 0; |
c2233a26 | 631 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
1245b700 | 632 | |
72b745e3 | 633 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
15e2e619 | 634 | |
ed38cc59 KM |
635 | ret = snd_soc_dai_compr_pointer(cpu_dai, cstream, tstamp); |
636 | if (ret < 0) | |
637 | goto out; | |
2e622ae4 | 638 | |
c6cb522c KM |
639 | for_each_rtd_components(rtd, i, component) { |
640 | if (!component->driver->compress_ops || | |
641 | !component->driver->compress_ops->pointer) | |
642 | continue; | |
643 | ||
644 | ret = component->driver->compress_ops->pointer( | |
645 | component, cstream, tstamp); | |
646 | break; | |
647 | } | |
ed38cc59 | 648 | out: |
72b745e3 | 649 | mutex_unlock(&rtd->card->pcm_mutex); |
7c9190f7 | 650 | return ret; |
1245b700 NK |
651 | } |
652 | ||
1f88eb0f | 653 | static int soc_compr_copy(struct snd_compr_stream *cstream, |
4daf891c | 654 | char __user *buf, size_t count) |
1f88eb0f CK |
655 | { |
656 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 657 | struct snd_soc_component *component; |
613fb500 | 658 | int i, ret = 0; |
1f88eb0f | 659 | |
72b745e3 | 660 | mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); |
1f88eb0f | 661 | |
c6cb522c KM |
662 | for_each_rtd_components(rtd, i, component) { |
663 | if (!component->driver->compress_ops || | |
664 | !component->driver->compress_ops->copy) | |
665 | continue; | |
666 | ||
667 | ret = component->driver->compress_ops->copy( | |
668 | component, cstream, buf, count); | |
669 | break; | |
670 | } | |
671 | ||
72b745e3 | 672 | mutex_unlock(&rtd->card->pcm_mutex); |
1f88eb0f CK |
673 | return ret; |
674 | } | |
675 | ||
02bd90e8 | 676 | static int soc_compr_set_metadata(struct snd_compr_stream *cstream, |
89027d9e | 677 | struct snd_compr_metadata *metadata) |
36953d98 JK |
678 | { |
679 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 680 | struct snd_soc_component *component; |
c2233a26 | 681 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
613fb500 | 682 | int i, ret; |
36953d98 | 683 | |
88b3a7df KM |
684 | ret = snd_soc_dai_compr_set_metadata(cpu_dai, cstream, metadata); |
685 | if (ret < 0) | |
686 | return ret; | |
2e622ae4 | 687 | |
c6cb522c KM |
688 | for_each_rtd_components(rtd, i, component) { |
689 | if (!component->driver->compress_ops || | |
690 | !component->driver->compress_ops->set_metadata) | |
691 | continue; | |
692 | ||
693 | ret = component->driver->compress_ops->set_metadata( | |
694 | component, cstream, metadata); | |
695 | if (ret < 0) | |
696 | return ret; | |
697 | } | |
698 | ||
52cadf1f | 699 | return 0; |
36953d98 JK |
700 | } |
701 | ||
02bd90e8 | 702 | static int soc_compr_get_metadata(struct snd_compr_stream *cstream, |
89027d9e | 703 | struct snd_compr_metadata *metadata) |
36953d98 JK |
704 | { |
705 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; | |
9e7e3738 | 706 | struct snd_soc_component *component; |
c2233a26 | 707 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
613fb500 | 708 | int i, ret; |
36953d98 | 709 | |
94d72819 KM |
710 | ret = snd_soc_dai_compr_get_metadata(cpu_dai, cstream, metadata); |
711 | if (ret < 0) | |
712 | return ret; | |
2e622ae4 | 713 | |
c6cb522c KM |
714 | for_each_rtd_components(rtd, i, component) { |
715 | if (!component->driver->compress_ops || | |
716 | !component->driver->compress_ops->get_metadata) | |
717 | continue; | |
718 | ||
719 | return component->driver->compress_ops->get_metadata( | |
720 | component, cstream, metadata); | |
721 | } | |
722 | ||
52cadf1f | 723 | return 0; |
36953d98 | 724 | } |
2a99ef0f | 725 | |
1245b700 NK |
726 | /* ASoC Compress operations */ |
727 | static struct snd_compr_ops soc_compr_ops = { | |
728 | .open = soc_compr_open, | |
729 | .free = soc_compr_free, | |
730 | .set_params = soc_compr_set_params, | |
02bd90e8 VK |
731 | .set_metadata = soc_compr_set_metadata, |
732 | .get_metadata = soc_compr_get_metadata, | |
1245b700 NK |
733 | .get_params = soc_compr_get_params, |
734 | .trigger = soc_compr_trigger, | |
735 | .pointer = soc_compr_pointer, | |
736 | .ack = soc_compr_ack, | |
737 | .get_caps = soc_compr_get_caps, | |
738 | .get_codec_caps = soc_compr_get_codec_caps | |
739 | }; | |
740 | ||
2a99ef0f LG |
741 | /* ASoC Dynamic Compress operations */ |
742 | static struct snd_compr_ops soc_compr_dyn_ops = { | |
743 | .open = soc_compr_open_fe, | |
744 | .free = soc_compr_free_fe, | |
745 | .set_params = soc_compr_set_params_fe, | |
746 | .get_params = soc_compr_get_params, | |
747 | .set_metadata = soc_compr_set_metadata, | |
748 | .get_metadata = soc_compr_get_metadata, | |
749 | .trigger = soc_compr_trigger_fe, | |
750 | .pointer = soc_compr_pointer, | |
751 | .ack = soc_compr_ack, | |
752 | .get_caps = soc_compr_get_caps, | |
753 | .get_codec_caps = soc_compr_get_codec_caps | |
754 | }; | |
755 | ||
6f0c4226 JY |
756 | /** |
757 | * snd_soc_new_compress - create a new compress. | |
758 | * | |
759 | * @rtd: The runtime for which we will create compress | |
760 | * @num: the device index number (zero based - shared with normal PCMs) | |
761 | * | |
762 | * Return: 0 for success, else error. | |
763 | */ | |
764 | int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) | |
1245b700 | 765 | { |
9e7e3738 | 766 | struct snd_soc_component *component; |
c2233a26 KM |
767 | struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); |
768 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); | |
1245b700 | 769 | struct snd_compr *compr; |
2a99ef0f | 770 | struct snd_pcm *be_pcm; |
1245b700 NK |
771 | char new_name[64]; |
772 | int ret = 0, direction = 0; | |
a1068045 | 773 | int playback = 0, capture = 0; |
613fb500 | 774 | int i; |
1245b700 | 775 | |
6e1276a5 BL |
776 | if (rtd->num_cpus > 1 || |
777 | rtd->num_codecs > 1) { | |
141dfc9e | 778 | dev_err(rtd->card->dev, |
6e1276a5 | 779 | "Compress ASoC: Multi CPU/Codec not supported\n"); |
8151d5e6 BC |
780 | return -EINVAL; |
781 | } | |
782 | ||
1245b700 | 783 | /* check client and interface hw capabilities */ |
467fece8 KM |
784 | if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && |
785 | snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) | |
a1068045 | 786 | playback = 1; |
467fece8 KM |
787 | if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && |
788 | snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) | |
a1068045 VK |
789 | capture = 1; |
790 | ||
a1068045 VK |
791 | /* |
792 | * Compress devices are unidirectional so only one of the directions | |
793 | * should be set, check for that (xor) | |
794 | */ | |
795 | if (playback + capture != 1) { | |
141dfc9e CK |
796 | dev_err(rtd->card->dev, |
797 | "Compress ASoC: Invalid direction for P %d, C %d\n", | |
798 | playback, capture); | |
a1068045 VK |
799 | return -EINVAL; |
800 | } | |
801 | ||
aeb6fa0f | 802 | if (playback) |
daa2db59 | 803 | direction = SND_COMPRESS_PLAYBACK; |
daa2db59 | 804 | else |
a1068045 | 805 | direction = SND_COMPRESS_CAPTURE; |
daa2db59 | 806 | |
09f448a4 | 807 | compr = devm_kzalloc(rtd->card->dev, sizeof(*compr), GFP_KERNEL); |
7a0cf42e | 808 | if (!compr) |
1245b700 | 809 | return -ENOMEM; |
1245b700 | 810 | |
1f88eb0f CK |
811 | compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops), |
812 | GFP_KERNEL); | |
09f448a4 AS |
813 | if (!compr->ops) |
814 | return -ENOMEM; | |
2a99ef0f LG |
815 | |
816 | if (rtd->dai_link->dynamic) { | |
817 | snprintf(new_name, sizeof(new_name), "(%s)", | |
818 | rtd->dai_link->stream_name); | |
819 | ||
820 | ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, | |
d3268a40 QY |
821 | rtd->dai_link->dpcm_playback, |
822 | rtd->dai_link->dpcm_capture, &be_pcm); | |
2a99ef0f | 823 | if (ret < 0) { |
141dfc9e CK |
824 | dev_err(rtd->card->dev, |
825 | "Compress ASoC: can't create compressed for %s: %d\n", | |
826 | rtd->dai_link->name, ret); | |
09f448a4 | 827 | return ret; |
2a99ef0f LG |
828 | } |
829 | ||
830 | rtd->pcm = be_pcm; | |
831 | rtd->fe_compr = 1; | |
d3268a40 QY |
832 | if (rtd->dai_link->dpcm_playback) |
833 | be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; | |
834 | else if (rtd->dai_link->dpcm_capture) | |
835 | be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; | |
2a99ef0f | 836 | memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops)); |
aeb6fa0f PD |
837 | } else { |
838 | snprintf(new_name, sizeof(new_name), "%s %s-%d", | |
839 | rtd->dai_link->stream_name, codec_dai->name, num); | |
840 | ||
2a99ef0f | 841 | memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); |
aeb6fa0f | 842 | } |
1f88eb0f | 843 | |
c6cb522c KM |
844 | for_each_rtd_components(rtd, i, component) { |
845 | if (!component->driver->compress_ops || | |
846 | !component->driver->compress_ops->copy) | |
847 | continue; | |
848 | ||
849 | compr->ops->copy = soc_compr_copy; | |
850 | break; | |
851 | } | |
852 | ||
1245b700 | 853 | mutex_init(&compr->lock); |
e5241a8c RF |
854 | ret = snd_compress_new(rtd->card->snd_card, num, direction, |
855 | new_name, compr); | |
1245b700 | 856 | if (ret < 0) { |
c2233a26 | 857 | component = asoc_rtd_to_codec(rtd, 0)->component; |
141dfc9e CK |
858 | dev_err(component->dev, |
859 | "Compress ASoC: can't create compress for codec %s: %d\n", | |
860 | component->name, ret); | |
09f448a4 | 861 | return ret; |
1245b700 NK |
862 | } |
863 | ||
202c8f70 | 864 | /* DAPM dai link stream work */ |
83f94a2e | 865 | rtd->close_delayed_work_func = snd_soc_close_delayed_work; |
202c8f70 | 866 | |
1245b700 NK |
867 | rtd->compr = compr; |
868 | compr->private_data = rtd; | |
869 | ||
1d5cd525 PLB |
870 | dev_dbg(rtd->card->dev, "Compress ASoC: %s <-> %s mapping ok\n", |
871 | codec_dai->name, cpu_dai->name); | |
1f88eb0f | 872 | |
09f448a4 | 873 | return 0; |
1245b700 | 874 | } |
6f0c4226 | 875 | EXPORT_SYMBOL_GPL(snd_soc_new_compress); |