summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/ibasso/pcm-ibasso.c
diff options
context:
space:
mode:
authorUdo Schläpfer <rockbox-2014.10@desktopwarrior.net>2015-02-02 21:44:29 +0100
committerUdo Schläpfer <rockbox-2014.10@desktopwarrior.net>2015-02-02 21:57:55 +0100
commitdbabd0d9c34a33bc0c51243ec37f230d117db955 (patch)
tree46de348929ce739702a230a2587fdb5539585753 /firmware/target/hosted/ibasso/pcm-ibasso.c
parentcef17e3d59ad93f766e8ee23b1610540a33dfe5e (diff)
downloadrockbox-dbabd0d9c34a33bc0c51243ec37f230d117db955.tar.gz
rockbox-dbabd0d9c34a33bc0c51243ec37f230d117db955.zip
iBasso DX50/DX90: Major code cleanup and reorganization.
Reorganization - Separated iBasso devices from PLATFORM_ANDROID. These are now standlone hosted targets. Most device specific code is in the firmware/target/hosted/ibasso directory. - No dependency on Android SDK, only the Android NDK is needed. 32 bit Android NDK and Android API Level 16. - Separate implementation for each device where feasible. Code cleanup - Rewrite of existing code, from simple reformat to complete reimplementation. - New backlight interface, seperating backlight from touchscreen. - Rewrite of device button handler, removing unneeded code and fixing memory leaks. - New Debug messages interface logging to Android adb logcat (DEBUGF, panicf, logf). - Rewrite of lcd device handler, removing unneeded code and fixing memory leaks. - Rewrite of audiohw device handler/pcm interface, removing unneeded code and fixing memory leaks, enabling 44.1/48kHz pthreaded playback. - Rewrite of power and powermng, proper shutdown, using batterylog results (see http://gerrit.rockbox.org/r/#/c/1047/). - Rewrite of configure (Android NDK) and device specific config. - Rewrite of the Android NDK specific Makefile. Misc - All plugins/games/demos activated. - Update tinyalsa to latest from https://github.com/tinyalsa/tinyalsa. Includes - http://gerrit.rockbox.org/r/#/c/993/ - http://gerrit.rockbox.org/r/#/c/1010/ - http://gerrit.rockbox.org/r/#/c/1035/ Does not include http://gerrit.rockbox.org/r/#/c/1007/ due to new backlight interface and new option for hold switch, touchscreen, physical button interaction. Rockbox needs the iBasso DX50/DX90 loader for startup, see http://gerrit.rockbox.org/r/#/c/1099/ The loader expects Rockbox to be installed in /mnt/sdcard/.rockbox/. If /mnt/sdcard/ is accessed as USB mass storage device, Rockbox will exit gracefully and the loader will restart Rockbox on USB disconnect. Tested on iBasso DX50. Compiled (not tested) for iBasso DX90. Compiled (not tested) for PLATFORM_ANDROID. Change-Id: I5f5e22e68f5b4cf29c28e2b40b2c265f2beb7ab7
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}