summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/ibasso/pcm-ibasso.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/hosted/ibasso/pcm-ibasso.c')
-rw-r--r--firmware/target/hosted/ibasso/pcm-ibasso.c488
1 files changed, 488 insertions, 0 deletions
diff --git a/firmware/target/hosted/ibasso/pcm-ibasso.c b/firmware/target/hosted/ibasso/pcm-ibasso.c
new file mode 100644
index 0000000000..14ef298af0
--- /dev/null
+++ b/firmware/target/hosted/ibasso/pcm-ibasso.c
@@ -0,0 +1,488 @@
1/***************************************************************************
2 * __________ __ ___
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
10 * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
11 * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
12 * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24
25#include <pthread.h>
26#include <stdbool.h>
27#include <unistd.h>
28
29#include "config.h"
30#include "debug.h"
31#include "panic.h"
32#include "pcm.h"
33#include "pcm-internal.h"
34
35#include "sound/asound.h"
36#include "tinyalsa/asoundlib.h"
37
38#include "debug-ibasso.h"
39#include "sysfs-ibasso.h"
40
41
42/* Tiny alsa handle. */
43static struct pcm* _alsa_handle = NULL;
44
45
46/* Bytes left in the Rockbox PCM frame buffer. */
47static size_t _pcm_buffer_size = 0;
48
49
50/* Rockbox PCM frame buffer. */
51static const void *_pcm_buffer = NULL;
52
53
54/*
55 1: PCM thread suspended.
56 0: PCM thread running.
57 These are used by pcm_play_[lock|unlock] or pcm_play_dma_[start|stop|pause]. These need to be
58 separated because of nested calls for locking and stopping.
59*/
60static volatile sig_atomic_t _dma_stopped = 1;
61static volatile sig_atomic_t _dma_locked = 1;
62
63
64/* Mutex for PCM thread suspend/unsuspend. */
65static pthread_mutex_t _dma_suspended_mtx = PTHREAD_MUTEX_INITIALIZER;
66
67
68/* Signal condition for PCM thread suspend/unsuspend. */
69static pthread_cond_t _dma_suspended_cond = PTHREAD_COND_INITIALIZER;
70
71
72static void* pcm_thread_run(void* nothing)
73{
74 (void) nothing;
75
76 DEBUGF("DEBUG %s: Thread start.", __func__);
77
78 while(true)
79 {
80 pthread_mutex_lock(&_dma_suspended_mtx);
81 while((_dma_stopped == 1) || (_dma_locked == 1))
82 {
83 DEBUGF("DEBUG %s: Playback suspended.", __func__);
84 pthread_cond_wait(&_dma_suspended_cond, &_dma_suspended_mtx);
85 DEBUGF("DEBUG %s: Playback resumed.", __func__);
86 }
87 pthread_mutex_unlock(&_dma_suspended_mtx);
88
89 if(_pcm_buffer_size == 0)
90 {
91 /* Retrive a new PCM buffer from Rockbox. */
92 if(! pcm_play_dma_complete_callback(PCM_DMAST_OK, &_pcm_buffer, &_pcm_buffer_size))
93 {
94 DEBUGF("DEBUG %s: No new buffer.", __func__);
95
96 usleep( 10000 );
97 continue;
98 }
99 }
100 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
101
102 /* This relies on Rockbox PCM frame buffer size == ALSA PCM frame buffer size. */
103 if(pcm_write(_alsa_handle, _pcm_buffer, _pcm_buffer_size) != 0)
104 {
105 DEBUGF("ERROR %s: pcm_write failed: %s.", __func__, pcm_get_error(_alsa_handle));
106
107 usleep( 10000 );
108 continue;
109 }
110
111 _pcm_buffer_size = 0;
112
113 /*DEBUGF("DEBUG %s: Thread running.", __func__);*/
114 }
115
116 DEBUGF("DEBUG %s: Thread end.", __func__);
117
118 return 0;
119}
120
121
122#ifdef DEBUG
123
124/* https://github.com/tinyalsa/tinyalsa/blob/master/tinypcminfo.c */
125
126static const char* format_lookup[] =
127{
128 /*[0] =*/ "S8",
129 "U8",
130 "S16_LE",
131 "S16_BE",
132 "U16_LE",
133 "U16_BE",
134 "S24_LE",
135 "S24_BE",
136 "U24_LE",
137 "U24_BE",
138 "S32_LE",
139 "S32_BE",
140 "U32_LE",
141 "U32_BE",
142 "FLOAT_LE",
143 "FLOAT_BE",
144 "FLOAT64_LE",
145 "FLOAT64_BE",
146 "IEC958_SUBFRAME_LE",
147 "IEC958_SUBFRAME_BE",
148 "MU_LAW",
149 "A_LAW",
150 "IMA_ADPCM",
151 "MPEG",
152 /*[24] =*/ "GSM",
153 [31] = "SPECIAL",
154 "S24_3LE",
155 "S24_3BE",
156 "U24_3LE",
157 "U24_3BE",
158 "S20_3LE",
159 "S20_3BE",
160 "U20_3LE",
161 "U20_3BE",
162 "S18_3LE",
163 "S18_3BE",
164 "U18_3LE",
165 /*[43] =*/ "U18_3BE"
166};
167
168
169static const char* pcm_get_format_name(unsigned int bit_index)
170{
171 return(bit_index < 43 ? format_lookup[bit_index] : NULL);
172}
173
174#endif
175
176
177/* Thread that copies the Rockbox PCM buffer to ALSA. */
178static pthread_t _pcm_thread;
179
180
181/* ALSA card and device. */
182static const unsigned int CARD = 0;
183static const unsigned int DEVICE = 0;
184
185
186/* ALSA config. */
187static struct pcm_config _config;
188
189
190void pcm_play_dma_init(void)
191{
192 TRACE;
193
194#ifdef DEBUG
195
196 /*
197 DEBUG pcm_play_dma_init: Access: 0x000009
198 DEBUG pcm_play_dma_init: Format[0]: 0x000044
199 DEBUG pcm_play_dma_init: Format[1]: 0x000010
200 DEBUG pcm_play_dma_init: Format: S16_LE
201 DEBUG pcm_play_dma_init: Format: S24_LE
202 DEBUG pcm_play_dma_init: Format: S20_3LE
203 DEBUG pcm_play_dma_init: Subformat: 0x000001
204 DEBUG pcm_play_dma_init: Rate: min = 8000Hz, max = 192000Hz
205 DEBUG pcm_play_dma_init: Channels: min = 2, max = 2
206 DEBUG pcm_play_dma_init: Sample bits: min=16, max=32
207 DEBUG pcm_play_dma_init: Period size: min=8, max=10922
208 DEBUG pcm_play_dma_init: Period count: min=3, max=128
209 DEBUG pcm_play_dma_init: 0 mixer controls.
210 */
211
212 struct pcm_params* params = pcm_params_get(CARD, DEVICE, PCM_OUT);
213 if(params == NULL)
214 {
215 DEBUGF("ERROR %s: Card/device does not exist.", __func__);
216 panicf("ERROR %s: Card/device does not exist.", __func__);
217 return;
218 }
219
220 struct pcm_mask* m = pcm_params_get_mask(params, PCM_PARAM_ACCESS);
221 if(m)
222 {
223 DEBUGF("DEBUG %s: Access: %#08x", __func__, m->bits[0]);
224 }
225
226 m = pcm_params_get_mask(params, PCM_PARAM_FORMAT);
227 if(m)
228 {
229 DEBUGF("DEBUG %s: Format[0]: %#08x", __func__, m->bits[0]);
230 DEBUGF("DEBUG %s: Format[1]: %#08x", __func__, m->bits[1]);
231
232 unsigned int j;
233 unsigned int k;
234 const unsigned int bitcount = sizeof(m->bits[0]) * 8;
235 for(k = 0; k < 2; ++k)
236 {
237 for(j = 0; j < bitcount; ++j)
238 {
239 const char* name;
240 if(m->bits[k] & (1 << j))
241 {
242 name = pcm_get_format_name(j + (k * bitcount));
243 if(name)
244 {
245 DEBUGF("DEBUG %s: Format: %s", __func__, name);
246 }
247 }
248 }
249 }
250 }
251
252 m = pcm_params_get_mask(params, PCM_PARAM_SUBFORMAT);
253 if(m)
254 {
255 DEBUGF("DEBUG %s: Subformat: %#08x", __func__, m->bits[0]);
256 }
257
258 unsigned int min = pcm_params_get_min(params, PCM_PARAM_RATE);
259 unsigned int max = pcm_params_get_max(params, PCM_PARAM_RATE) ;
260 DEBUGF("DEBUG %s: Rate: min = %uHz, max = %uHz", __func__, min, max);
261
262 min = pcm_params_get_min(params, PCM_PARAM_CHANNELS);
263 max = pcm_params_get_max(params, PCM_PARAM_CHANNELS);
264 DEBUGF("DEBUG %s: Channels: min = %u, max = %u", __func__, min, max);
265
266 min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS);
267 max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS);
268 DEBUGF("DEBUG %s: Sample bits: min=%u, max=%u", __func__, min, max);
269
270 min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE);
271 max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE);
272 DEBUGF("DEBUG %s: Period size: min=%u, max=%u", __func__, min, max);
273
274 min = pcm_params_get_min(params, PCM_PARAM_PERIODS);
275 max = pcm_params_get_max(params, PCM_PARAM_PERIODS);
276 DEBUGF("DEBUG %s: Period count: min=%u, max=%u", __func__, min, max);
277
278 pcm_params_free(params);
279
280 struct mixer* mixer = mixer_open(CARD);
281 if(! mixer)
282 {
283 DEBUGF("ERROR %s: Failed to open mixer.", __func__);
284 }
285 else
286 {
287 int num_ctls = mixer_get_num_ctls(mixer);
288
289 DEBUGF("DEBUG %s: %d mixer controls.", __func__, num_ctls);
290
291 mixer_close(mixer);
292 }
293
294#endif
295
296 if(_alsa_handle != NULL)
297 {
298 DEBUGF("ERROR %s: Allready initialized.", __func__);
299 panicf("ERROR %s: Allready initialized.", __func__);
300 return;
301 }
302
303 /*
304 Rockbox outputs 16 Bit/44.1kHz stereo by default.
305
306 ALSA frame buffer size = config.period_count * config.period_size * config.channels * (16 \ 8)
307 = 4 * 256 * 2 * 2
308 = 4096
309 = Rockbox PCM buffer size
310 pcm_thread_run relies on this size match. See pcm_mixer.h.
311 */
312 _config.channels = 2;
313 _config.rate = 44100;
314 _config.period_size = 256;
315 _config.period_count = 4;
316 _config.format = PCM_FORMAT_S16_LE;
317 _config.start_threshold = 0;
318 _config.stop_threshold = 0;
319 _config.silence_threshold = 0;
320
321 _alsa_handle = pcm_open(CARD, DEVICE, PCM_OUT, &_config);
322 if(! pcm_is_ready(_alsa_handle))
323 {
324 DEBUGF("ERROR %s: pcm_open failed: %s.", __func__, pcm_get_error(_alsa_handle));
325 panicf("ERROR %s: pcm_open failed: %s.", __func__, pcm_get_error(_alsa_handle));
326 return;
327 }
328
329 DEBUGF("DEBUG %s: ALSA PCM frame buffer size: %d.", __func__, pcm_frames_to_bytes(_alsa_handle, pcm_get_buffer_size(_alsa_handle)));
330
331 /* Create pcm thread in the suspended state. */
332 pthread_mutex_lock(&_dma_suspended_mtx);
333 _dma_stopped = 1;
334 _dma_locked = 1;
335 pthread_create(&_pcm_thread, NULL, pcm_thread_run, NULL);
336 pthread_mutex_unlock(&_dma_suspended_mtx);
337}
338
339
340void pcm_play_dma_start(const void *addr, size_t size)
341{
342 TRACE;
343
344 /*
345 DX50
346 /sys/class/codec/mute
347 Mute: echo 'A' > /sys/class/codec/mute
348 Unmute: echo 'B' > /sys/class/codec/mute
349
350 DX90?
351 */
352 if(! sysfs_set_char(SYSFS_MUTE, 'B'))
353 {
354 DEBUGF("ERROR %s: Could not unmute.", __func__);
355 panicf("ERROR %s: Could not unmute.", __func__);
356 }
357
358 _pcm_buffer = addr;
359 _pcm_buffer_size = size;
360
361 pthread_mutex_lock(&_dma_suspended_mtx);
362 _dma_stopped = 0;
363 pthread_cond_signal(&_dma_suspended_cond);
364 pthread_mutex_unlock(&_dma_suspended_mtx);
365}
366
367
368/* TODO: Why is this in the API if it gets never called? */
369void pcm_play_dma_pause(bool pause)
370{
371 TRACE;
372
373 pthread_mutex_lock(&_dma_suspended_mtx);
374 _dma_stopped = pause ? 1 : 0;
375 if(_dma_stopped == 0)
376 {
377 pthread_cond_signal(&_dma_suspended_cond);
378 }
379 pthread_mutex_unlock(&_dma_suspended_mtx);
380}
381
382
383void pcm_play_dma_stop(void)
384{
385 TRACE;
386
387 pthread_mutex_lock(&_dma_suspended_mtx);
388 _dma_stopped = 1;
389 pcm_stop(_alsa_handle);
390 pthread_mutex_unlock(&_dma_suspended_mtx);
391}
392
393
394/* Unessecary play locks before pcm_play_dma_postinit. */
395static int _play_lock_recursion_count = -10000;
396
397
398void pcm_play_dma_postinit(void)
399{
400 TRACE;
401
402 _play_lock_recursion_count = 0;
403}
404
405
406void pcm_play_lock(void)
407{
408 TRACE;
409
410 ++_play_lock_recursion_count;
411
412 if(_play_lock_recursion_count == 1)
413 {
414 pthread_mutex_lock(&_dma_suspended_mtx);
415 _dma_locked = 1;
416 pthread_mutex_unlock(&_dma_suspended_mtx);
417 }
418}
419
420
421void pcm_play_unlock(void)
422{
423 TRACE;
424
425 --_play_lock_recursion_count;
426
427 if(_play_lock_recursion_count == 0)
428 {
429 pthread_mutex_lock(&_dma_suspended_mtx);
430 _dma_locked = 0;
431 pthread_cond_signal(&_dma_suspended_cond);
432 pthread_mutex_unlock(&_dma_suspended_mtx);
433 }
434}
435
436
437void pcm_dma_apply_settings(void)
438{
439 unsigned int rate = pcm_get_frequency();
440
441 DEBUGF("DEBUG %s: Current sample rate: %u, next sampe rate: %u.", __func__, _config.rate, rate);
442
443 if(( _config.rate != rate) && (rate >= 8000) && (rate <= 192000))
444 {
445 _config.rate = rate;
446
447 pcm_close(_alsa_handle);
448 _alsa_handle = pcm_open(CARD, DEVICE, PCM_OUT, &_config);
449
450 if(! pcm_is_ready(_alsa_handle))
451 {
452 DEBUGF("ERROR %s: pcm_open failed: %s.", __func__, pcm_get_error(_alsa_handle));
453 panicf("ERROR %s: pcm_open failed: %s.", __func__, pcm_get_error(_alsa_handle));
454 }
455 }
456}
457
458
459size_t pcm_get_bytes_waiting(void)
460{
461 TRACE;
462
463 return _pcm_buffer_size;
464}
465
466
467/* TODO: WTF */
468const void* pcm_play_dma_get_peak_buffer(int* count)
469{
470 TRACE;
471
472 uintptr_t addr = (uintptr_t) _pcm_buffer;
473 *count = _pcm_buffer_size / 4;
474 return (void*) ((addr + 3) & ~3);
475}
476
477
478void pcm_close_device(void)
479{
480 TRACE;
481
482 pthread_mutex_lock(&_dma_suspended_mtx);
483 _dma_stopped = 1;
484 pthread_mutex_unlock(&_dma_suspended_mtx);
485
486 pcm_close(_alsa_handle);
487 _alsa_handle = NULL;
488}