1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the people's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <aw-alsa-lib/pcm_plugin.h>
37 #include <aw-alsa-lib/pcm_config.h>
38 #include "pcm_local.h"
39
40 typedef struct {
41 int card_num;
42 int device_num;
43 void *substream;
44 /* status,control struct define in snd_core.h */
45 /* mmap_status,mmap_control sync from kernel */
46 struct snd_pcm_mmap_status mmap_status;
47 struct snd_pcm_mmap_control mmap_control;
48 } snd_pcm_hw_t;
49
snd_pcm_sync_ptr(snd_pcm_t * pcm,snd_pcm_hw_t * hw,unsigned int flags)50 static int snd_pcm_sync_ptr(snd_pcm_t *pcm, snd_pcm_hw_t *hw, unsigned int flags)
51 {
52 return ksnd_pcm_sync_ptr(hw->substream, &hw->mmap_status, &hw->mmap_control, flags);
53 }
54
snd_pcm_hw_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)55 static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
56 {
57 assert(pcm);
58 awalsa_debug("\n");
59 snd_pcm_hw_t *hw = pcm->private_data;
60 /* fill params from hw_constrains */
61 return ksnd_pcm_hw_refine(hw->substream, params);
62 }
63
snd_pcm_hw_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)64 static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
65 {
66 assert(pcm && params);
67 awalsa_debug("\n");
68
69 snd_pcm_hw_t *hw = pcm->private_data;
70
71 /*TODO: dmix can't support pause */
72 params->can_paused = 1;
73
74 return ksnd_pcm_hw_params(hw->substream, params);
75 }
76
snd_pcm_hw_hw_free(snd_pcm_t * pcm)77 static int snd_pcm_hw_hw_free(snd_pcm_t *pcm)
78 {
79 int ret;
80
81 assert(pcm);
82 awalsa_debug("\n");
83
84 snd_pcm_hw_t *hw = pcm->private_data;
85 ret = ksnd_pcm_hw_free(hw->substream);
86 pcm->setup = 0;
87
88 return ret;
89 }
90
snd_pcm_hw_sw_params(snd_pcm_t * pcm,snd_pcm_sw_params_t * params)91 static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
92 {
93 int ret = 0;
94
95 assert(pcm && params);
96 awalsa_debug("\n");
97
98 snd_pcm_hw_t *hw = pcm->private_data;
99 ret = ksnd_pcm_sw_params(hw->substream, params);
100 if (!ret)
101 hw->mmap_control.avail_min = params->avail_min;
102
103 return ret;
104 }
105
snd_pcm_hw_channel_info(snd_pcm_t * pcm,snd_pcm_channel_info_t * info)106 static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
107 {
108 int ret = 0;
109 snd_pcm_hw_t *hw = pcm->private_data;
110 snd_pcm_channel_info_t i;
111
112 assert(pcm && info);
113 awalsa_debug("\n");
114
115 i.channel = info->channel;
116
117 ret = ksnd_pcm_channel_info(hw->substream, &i);
118 if (ret < 0)
119 return ret;
120
121 info->channel = i.channel;
122 info->addr = i.addr;
123 info->first = i.first;
124 info->step = i.step;
125 info->type = SND_PCM_AREA_MMAP;
126
127 return 0;
128 }
129
snd_pcm_hw_mmap(snd_pcm_t * pcm ATTRIBUTE_UNUSED)130 static int snd_pcm_hw_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
131 {
132 return 0;
133 }
134
snd_pcm_hw_munmap(snd_pcm_t * pcm ATTRIBUTE_UNUSED)135 static int snd_pcm_hw_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
136 {
137 return 0;
138 }
139
snd_pcm_hw_close(snd_pcm_t * pcm)140 static int snd_pcm_hw_close(snd_pcm_t *pcm)
141 {
142 int ret = 0, res = 0;
143 snd_pcm_hw_t *hw = pcm->private_data;
144
145 awalsa_debug("\n");
146 ret = ksnd_pcm_release(hw->card_num, hw->device_num, pcm->stream);
147 if (ret < 0)
148 res = ret;
149
150 snd_free(hw);
151 return res;
152 }
153
snd_pcm_hw_state(snd_pcm_t * pcm)154 static snd_pcm_state_t snd_pcm_hw_state(snd_pcm_t *pcm)
155 {
156 int ret = 0;
157 snd_pcm_hw_t *hw = pcm->private_data;
158
159 awalsa_debug("\n");
160 ret = snd_pcm_sync_ptr(pcm, hw, SNDRV_PCM_SYNC_PTR_APPL);
161 if (ret < 0)
162 return ret;
163
164 return (snd_pcm_state_t)hw->mmap_status.state;
165 }
166
snd_pcm_hw_hwsync(snd_pcm_t * pcm)167 static int snd_pcm_hw_hwsync(snd_pcm_t *pcm)
168 {
169 int ret = 0;
170 snd_pcm_hw_t *hw = pcm->private_data;
171
172 awalsa_debug("\n");
173 ret = snd_pcm_sync_ptr(pcm, hw, SNDRV_PCM_SYNC_PTR_HWSYNC);
174 if (ret < 0)
175 return ret;
176 return 0;
177 }
178
snd_pcm_hw_prepare(snd_pcm_t * pcm)179 static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
180 {
181 int ret = 0;
182
183 assert(pcm);
184 awalsa_debug("\n");
185
186 snd_pcm_hw_t *hw = pcm->private_data;
187 ret = ksnd_pcm_prepare(hw->substream);
188 return ret;
189 }
190
snd_pcm_hw_reset(snd_pcm_t * pcm)191 static int snd_pcm_hw_reset(snd_pcm_t *pcm)
192 {
193 int ret = 0;
194
195 assert(pcm);
196 awalsa_debug("\n");
197
198 snd_pcm_hw_t *hw = pcm->private_data;
199 ret = ksnd_pcm_reset(hw->substream);
200 return ret;
201 }
202
snd_pcm_hw_start(snd_pcm_t * pcm)203 static int snd_pcm_hw_start(snd_pcm_t *pcm)
204 {
205 assert(pcm);
206 awalsa_debug("\n");
207
208 snd_pcm_hw_t *hw = pcm->private_data;
209 return ksnd_pcm_start(hw->substream);
210 }
211
snd_pcm_hw_drop(snd_pcm_t * pcm)212 static int snd_pcm_hw_drop(snd_pcm_t *pcm)
213 {
214 int ret = 0;
215 snd_pcm_hw_t *hw;
216
217 assert(pcm);
218 awalsa_debug("\n");
219
220 hw = pcm->private_data;
221 ret = ksnd_pcm_drop(hw->substream);
222 return ret;
223 }
224
snd_pcm_hw_drain(snd_pcm_t * pcm)225 static int snd_pcm_hw_drain(snd_pcm_t *pcm)
226 {
227 int ret = 0;
228
229 assert(pcm);
230 awalsa_debug("\n");
231
232 snd_pcm_hw_t *hw = pcm->private_data;
233 ret = ksnd_pcm_drain(hw->substream);
234
235 return ret;
236 }
237
snd_pcm_hw_pause(snd_pcm_t * pcm,int enable)238 static int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable)
239 {
240 int ret = 0;
241
242 assert(pcm);
243 awalsa_debug("\n");
244
245 snd_pcm_hw_t *hw = pcm->private_data;
246 ret = ksnd_pcm_pause(hw->substream, enable);
247 return ret;
248 }
249
snd_pcm_hw_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)250 static int snd_pcm_hw_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
251 {
252 int ret = 0;
253
254 assert(pcm);
255 assert(delayp);
256 awalsa_debug("\n");
257
258 snd_pcm_hw_t *hw = pcm->private_data;
259 ret = ksnd_pcm_delay(hw->substream, delayp);
260
261 return ret;
262 }
263
snd_pcm_hw_resume(snd_pcm_t * pcm)264 static int snd_pcm_hw_resume(snd_pcm_t *pcm)
265 {
266 assert(pcm);
267 awalsa_err("suspend state not supported.\n");
268 return -1;
269 }
270
snd_pcm_hw_writei(snd_pcm_t * pcm,const void * buffer,snd_pcm_uframes_t size)271 static snd_pcm_sframes_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer,
272 snd_pcm_uframes_t size)
273 {
274 snd_pcm_sframes_t ret = 0;
275
276 assert(pcm);
277 assert(size == 0 || buffer);
278 /*awalsa_debug("\n");*/
279
280 snd_pcm_hw_t *hw = pcm->private_data;
281 ret = ksnd_pcm_writei(hw->substream, buffer, size);
282
283 return ret;
284 }
snd_pcm_hw_readi(snd_pcm_t * pcm,void * buffer,snd_pcm_uframes_t size)285 static snd_pcm_sframes_t snd_pcm_hw_readi(snd_pcm_t *pcm, void *buffer,
286 snd_pcm_uframes_t size)
287 {
288 snd_pcm_sframes_t ret = 0;
289
290 assert(pcm);
291 assert(size == 0 || buffer);
292 awalsa_debug("\n");
293
294 snd_pcm_hw_t *hw = pcm->private_data;
295 ret = ksnd_pcm_readi(hw->substream, buffer, size);
296
297 return ret;
298 }
299
snd_pcm_hw_avail_update(snd_pcm_t * pcm)300 static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
301 {
302 snd_pcm_hw_t *hw = pcm->private_data;
303 snd_pcm_uframes_t avail;
304
305 awalsa_debug("\n");
306 snd_pcm_sync_ptr(pcm, hw, 0);
307 avail = snd_pcm_mmap_avail(pcm);
308 switch (hw->mmap_status.state) {
309 case SNDRV_PCM_STATE_RUNNING:
310 if (avail >= pcm->stop_threshold) {
311 // TODO: notify XRUN to driver?
312
313 /* everything is ok, state == SND_PCM_STATE_XRUN at the moment */
314 return -EPIPE;
315 }
316 break;
317 case SNDRV_PCM_STATE_XRUN:
318 return -EPIPE;
319 default:
320 break;
321 }
322 return avail;
323 }
324
snd_pcm_hw_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)325 static snd_pcm_sframes_t snd_pcm_hw_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
326 snd_pcm_uframes_t size)
327 {
328 snd_pcm_hw_t *hw = pcm->private_data;
329
330 awalsa_debug("\n");
331
332 /* flush/invalidate cache */
333 ksnd_pcm_hw_mmap_dcache_update(hw->substream, offset, size);
334
335 snd_pcm_mmap_appl_forward(pcm, size);
336 snd_pcm_sync_ptr(pcm, hw, 0);
337 return size;
338 }
339
snd_pcm_hw_may_wait_for_avail_min(snd_pcm_t * pcm,snd_pcm_uframes_t avail)340 static int snd_pcm_hw_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
341 {
342 return 1;
343 }
344
345 /*TODO*/
snd_pcm_hw_wait_with_index(snd_pcm_t * hw_pcm,int index,int timeout)346 int snd_pcm_hw_wait_with_index(snd_pcm_t *hw_pcm, int index, int timeout)
347 {
348 snd_pcm_hw_t *hw = hw_pcm->private_data;
349 awalsa_debug("\n");
350 return ksnd_pcm_wait(hw->substream, index, timeout);
351 }
352
snd_pcm_hw_poll_index_init(snd_pcm_t * hw_pcm)353 int snd_pcm_hw_poll_index_init(snd_pcm_t *hw_pcm)
354 {
355 snd_pcm_hw_t *hw = hw_pcm->private_data;
356 awalsa_debug("\n");
357 return ksnd_pcm_dsleep_init(hw->substream);
358 }
359
snd_pcm_hw_poll_index_release(snd_pcm_t * hw_pcm,int index)360 int snd_pcm_hw_poll_index_release(snd_pcm_t *hw_pcm, int index)
361 {
362 snd_pcm_hw_t *hw = hw_pcm->private_data;
363 awalsa_debug("\n");
364 return ksnd_pcm_dsleep_release(hw->substream, index);
365 }
366
snd_pcm_hw_wait(snd_pcm_t * pcm,int timeout)367 static int snd_pcm_hw_wait(snd_pcm_t *pcm, int timeout)
368 {
369 snd_pcm_hw_t *hw = pcm->private_data;
370 awalsa_debug("\n");
371 return ksnd_pcm_wait(hw->substream, 0, timeout);
372 }
373
snd_pcm_hw_cache_update(snd_pcm_t * pcm,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)374 static void snd_pcm_hw_cache_update(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
375 snd_pcm_uframes_t size)
376 {
377 snd_pcm_hw_t *hw = pcm->private_data;
378 awalsa_debug("\n");
379 ksnd_pcm_hw_mmap_dcache_update(hw->substream, offset, size);
380 }
381
snd_pcm_hw_dump(snd_pcm_t * pcm)382 static void snd_pcm_hw_dump(snd_pcm_t *pcm)
383 {
384 snd_pcm_hw_t *hw = pcm->private_data;
385 awalsa_debug("\n");
386
387 printf("Hardware PCM card %d '%s'\n",
388 hw->card_num, ksnd_card_name(hw->card_num));
389 if (pcm->setup) {
390 printf("Its setup is:\n");
391 snd_pcm_dump_setup(pcm);
392 printf(" appl_ptr : %li\n", hw->mmap_control.appl_ptr);
393 printf(" hw_ptr : %li\n", hw->mmap_status.hw_ptr);
394 }
395 return;
396 }
397
snd_pcm_hw_rewind(snd_pcm_t * pcm,snd_pcm_uframes_t frames)398 static snd_pcm_sframes_t snd_pcm_hw_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
399 {
400 snd_pcm_hw_t *hw = pcm->private_data;
401 int err;
402
403 if (ksnd_pcm_rewind(hw->substream, frames) < 0) {
404 err = -errno;
405 awalsa_err("SNDRV_PCM_IOCTL_REWIND failed (%i)", err);
406 return err;
407 }
408 err = snd_pcm_sync_ptr(pcm, hw, SNDRV_PCM_SYNC_PTR_APPL);
409 if (err < 0)
410 return err;
411 return frames;
412 }
413
414 static const snd_pcm_ops_t snd_pcm_hw_ops = {
415 .close = snd_pcm_hw_close,
416 .hw_refine = snd_pcm_hw_hw_refine,
417 .hw_params = snd_pcm_hw_hw_params,
418 .hw_free = snd_pcm_hw_hw_free,
419 .sw_params = snd_pcm_hw_sw_params,
420 .channel_info = snd_pcm_hw_channel_info,
421 .dump = snd_pcm_hw_dump,
422 .mmap = snd_pcm_hw_mmap,
423 .munmap = snd_pcm_hw_munmap,
424 };
425
426 static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = {
427 .state = snd_pcm_hw_state,
428 .hwsync = snd_pcm_hw_hwsync,
429 .delay = snd_pcm_hw_delay,
430 .prepare = snd_pcm_hw_prepare,
431 .reset = snd_pcm_hw_reset,
432 .start = snd_pcm_hw_start,
433 .drop = snd_pcm_hw_drop,
434 .rewind = snd_pcm_hw_rewind,
435 .drain = snd_pcm_hw_drain,
436 .pause = snd_pcm_hw_pause,
437 .resume = snd_pcm_hw_resume,
438 .writei = snd_pcm_hw_writei,
439 .readi = snd_pcm_hw_readi,
440 .avail_update = snd_pcm_hw_avail_update,
441 .mmap_commit = snd_pcm_hw_mmap_commit,
442 .may_wait_for_avail_min = snd_pcm_hw_may_wait_for_avail_min,
443 .wait = snd_pcm_hw_wait,
444 .cache_update = snd_pcm_hw_cache_update,
445 };
446
_snd_pcm_hw_open(snd_pcm_t ** pcmp,const snd_pcm_config_t * pcm_config,snd_pcm_stream_t stream,int mode)447 int _snd_pcm_hw_open(snd_pcm_t **pcmp, const snd_pcm_config_t *pcm_config,
448 snd_pcm_stream_t stream, int mode)
449 {
450 const snd_pcm_hw_config_t *hw_config = (const snd_pcm_hw_config_t *)(pcm_config->config);
451 int card_num = 0;
452 int ret = 0;
453 snd_pcm_t *pcm = NULL;
454 void *substream = NULL;
455 snd_pcm_hw_t *hw = NULL;
456
457 awalsa_debug("\n");
458 card_num = ksnd_card_index(hw_config->card_name);
459 if (card_num < 0) {
460 awalsa_err("no such card:%s\n", hw_config->card_name);
461 ret = -ENODEV;
462 goto err_out;
463 }
464
465 awalsa_debug("\n");
466
467 ret = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, pcm_config->name, stream, mode);
468 if (ret < 0) {
469 awalsa_err("failed to new hw pcm\n");
470 goto err_out;
471 }
472 pcm->ops = &snd_pcm_hw_ops;
473 pcm->fast_ops = &snd_pcm_hw_fast_ops;
474
475 hw = snd_malloc(sizeof(snd_pcm_hw_t));
476 if (!hw) {
477 awalsa_err("no memory\n");
478 ret = -ENOMEM;
479 goto free_pcm;
480 }
481
482 ret = ksnd_pcm_open(card_num, hw_config->device_num, stream, mode, &substream);
483 if (ret != 0) {
484 goto free_hw;
485 }
486 hw->card_num = card_num;
487 hw->device_num = hw_config->device_num;
488 hw->substream = substream;
489
490 snd_pcm_set_hw_ptr(pcm, &hw->mmap_status.hw_ptr, 0, 0);
491 snd_pcm_set_appl_ptr(pcm, &hw->mmap_control.appl_ptr, 0, 0);
492 /* sync control, status */
493 snd_pcm_sync_ptr(pcm, hw, 0);
494
495 pcm->private_data = hw;
496
497 *pcmp = pcm;
498 return 0;
499
500 free_hw:
501 snd_free(hw);
502 free_pcm:
503 snd_pcm_free(pcm);
504 err_out:
505 return ret;
506 }
507