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