Fix non-integer display_zoom for charcell.
[maemo-rb.git] / firmware / target / hosted / pcm-alsa.c
blob715af58fd9875c4db1f825320c9e0d3d842fecca
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2010 Thomas Martitz
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
24 * Based, but heavily modified, on the example given at
25 * http://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c-example.html
27 * This driver uses the so-called unsafe async callback method and hardcoded device
28 * names. It fails when the audio device is busy by other apps.
30 * To make the async callback safer, an alternative stack is installed, since
31 * it's run from a signal hanlder (which otherwise uses the user stack). If
32 * tick tasks are run from a signal handler too, please install
33 * an alternative stack for it too.
35 * TODO: Rewrite this to do it properly with multithreading
37 * Alternatively, a version using polling in a tick task is provided. While
38 * supposedly safer, it appears to use more CPU (however I didn't measure it
39 * accurately, only looked at htop). At least, in this mode the "default"
40 * device works which doesnt break with other apps running.
41 * device works which doesnt break with other apps running.
45 #include "autoconf.h"
47 #include <stdlib.h>
48 #include <stdbool.h>
49 #include <alsa/asoundlib.h>
50 #include "system.h"
51 #include "debug.h"
52 #include "kernel.h"
54 #include "pcm.h"
55 #include "pcm-internal.h"
56 #include "pcm_mixer.h"
57 #include "pcm_sampr.h"
58 #include "audiohw.h"
60 #include <pthread.h>
61 #include <signal.h>
63 #define USE_ASYNC_CALLBACK
64 /* plughw:0,0 works with both, however "default" is recommended.
65 * default doesnt seem to work with async callback but doesn't break
66 * with multple applications running */
67 static char device[] = "plughw:0,0"; /* playback device */
68 static const snd_pcm_access_t access_ = SND_PCM_ACCESS_RW_INTERLEAVED; /* access mode */
69 static const snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
70 static const int channels = 2; /* count of channels */
71 static unsigned int rate = 44100; /* stream rate */
73 static snd_pcm_t *handle;
74 static snd_pcm_sframes_t buffer_size = MIX_FRAME_SAMPLES * 32; /* ~16k */
75 static snd_pcm_sframes_t period_size = MIX_FRAME_SAMPLES * 4; /* ~4k */
76 static short *frames;
78 static const void *pcm_data = 0;
79 static size_t pcm_size = 0;
81 #ifdef USE_ASYNC_CALLBACK
82 static snd_async_handler_t *ahandler;
83 static pthread_mutex_t pcm_mtx;
84 static char signal_stack[SIGSTKSZ];
85 #else
86 static int recursion;
87 #endif
89 static int set_hwparams(snd_pcm_t *handle, unsigned sample_rate)
91 unsigned int rrate;
92 int err;
93 snd_pcm_hw_params_t *params;
94 snd_pcm_hw_params_malloc(&params);
97 /* choose all parameters */
98 err = snd_pcm_hw_params_any(handle, params);
99 if (err < 0)
101 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
102 goto error;
104 /* set the interleaved read/write format */
105 err = snd_pcm_hw_params_set_access(handle, params, access_);
106 if (err < 0)
108 printf("Access type not available for playback: %s\n", snd_strerror(err));
109 goto error;
111 /* set the sample format */
112 err = snd_pcm_hw_params_set_format(handle, params, format);
113 if (err < 0)
115 printf("Sample format not available for playback: %s\n", snd_strerror(err));
116 goto error;
118 /* set the count of channels */
119 err = snd_pcm_hw_params_set_channels(handle, params, channels);
120 if (err < 0)
122 printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
123 goto error;
125 /* set the stream rate */
126 rrate = sample_rate;
127 err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
128 if (err < 0)
130 printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
131 goto error;
133 if (rrate != sample_rate)
135 printf("Rate doesn't match (requested %iHz, get %iHz)\n", sample_rate, err);
136 err = -EINVAL;
137 goto error;
140 /* set the buffer size */
141 err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size);
142 if (err < 0)
144 printf("Unable to set buffer size %ld for playback: %s\n", buffer_size, snd_strerror(err));
145 goto error;
148 /* set the period size */
149 err = snd_pcm_hw_params_set_period_size_near (handle, params, &period_size, NULL);
150 if (err < 0)
152 printf("Unable to set period size %ld for playback: %s\n", period_size, snd_strerror(err));
153 goto error;
155 if (!frames)
156 frames = malloc(period_size * channels * sizeof(short));
158 /* write the parameters to device */
159 err = snd_pcm_hw_params(handle, params);
160 if (err < 0)
162 printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
163 goto error;
166 err = 0; /* success */
167 error:
168 snd_pcm_hw_params_free(params);
169 return err;
172 /* Set sw params: playback start threshold and low buffer watermark */
173 static int set_swparams(snd_pcm_t *handle)
175 int err;
177 snd_pcm_sw_params_t *swparams;
178 snd_pcm_sw_params_malloc(&swparams);
180 /* get the current swparams */
181 err = snd_pcm_sw_params_current(handle, swparams);
182 if (err < 0)
184 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
185 goto error;
187 /* start the transfer when the buffer is haalmost full */
188 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size / 2);
189 if (err < 0)
191 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
192 goto error;
194 /* allow the transfer when at least period_size samples can be processed */
195 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
196 if (err < 0)
198 printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
199 goto error;
201 /* write the parameters to the playback device */
202 err = snd_pcm_sw_params(handle, swparams);
203 if (err < 0)
205 printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
206 goto error;
209 err = 0; /* success */
210 error:
211 snd_pcm_sw_params_free(swparams);
212 return err;
215 /* copy pcm samples to a spare buffer, suitable for snd_pcm_writei() */
216 static bool fill_frames(void)
218 ssize_t copy_n, frames_left = period_size;
219 bool new_buffer = false;
221 while (frames_left > 0)
223 if (!pcm_size)
225 new_buffer = true;
226 if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &pcm_data,
227 &pcm_size))
229 return false;
232 copy_n = MIN((ssize_t)pcm_size, frames_left*4);
233 memcpy(&frames[2*(period_size-frames_left)], pcm_data, copy_n);
235 pcm_data += copy_n;
236 pcm_size -= copy_n;
237 frames_left -= copy_n/4;
239 if (new_buffer)
241 new_buffer = false;
242 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
245 return true;
248 #ifdef USE_ASYNC_CALLBACK
249 static void async_callback(snd_async_handler_t *ahandler)
251 snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
253 if (pthread_mutex_trylock(&pcm_mtx) != 0)
254 return;
255 #else
256 static void pcm_tick(void)
258 if (snd_pcm_state(handle) != SND_PCM_STATE_RUNNING)
259 return;
260 #endif
262 while (snd_pcm_avail_update(handle) >= period_size)
264 if (fill_frames())
266 int err = snd_pcm_writei(handle, frames, period_size);
267 if (err < 0 && err != period_size && err != -EAGAIN)
269 printf("Write error: written %i expected %li\n", err, period_size);
270 break;
273 else
275 DEBUGF("%s: No Data.\n", __func__);
276 break;
279 #ifdef USE_ASYNC_CALLBACK
280 pthread_mutex_unlock(&pcm_mtx);
281 #endif
284 static int async_rw(snd_pcm_t *handle)
286 int err;
287 snd_pcm_sframes_t sample_size;
288 short *samples;
290 #ifdef USE_ASYNC_CALLBACK
291 /* assign alternative stack for the signal handlers */
292 stack_t ss = {
293 .ss_sp = signal_stack,
294 .ss_size = sizeof(signal_stack),
295 .ss_flags = 0
297 struct sigaction sa;
299 err = sigaltstack(&ss, NULL);
300 if (err < 0)
302 DEBUGF("Unable to install alternative signal stack: %s", strerror(err));
303 return err;
306 err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, NULL);
307 if (err < 0)
309 DEBUGF("Unable to register async handler: %s\n", snd_strerror(err));
310 return err;
313 /* only modify the stack the handler runs on */
314 sigaction(SIGIO, NULL, &sa);
315 sa.sa_flags |= SA_ONSTACK;
316 err = sigaction(SIGIO, &sa, NULL);
317 if (err < 0)
319 DEBUGF("Unable to install alternative signal stack: %s", strerror(err));
320 return err;
322 #endif
324 /* fill buffer with silence to initiate playback without noisy click */
325 sample_size = buffer_size;
326 samples = malloc(sample_size * channels * sizeof(short));
328 snd_pcm_format_set_silence(format, samples, sample_size);
329 err = snd_pcm_writei(handle, samples, sample_size);
330 free(samples);
332 if (err < 0)
334 DEBUGF("Initial write error: %s\n", snd_strerror(err));
335 return err;
337 if (err != (ssize_t)sample_size)
339 DEBUGF("Initial write error: written %i expected %li\n", err, sample_size);
340 return err;
342 if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED)
344 err = snd_pcm_start(handle);
345 if (err < 0)
347 DEBUGF("Start error: %s\n", snd_strerror(err));
348 return err;
351 return 0;
355 void cleanup(void)
357 free(frames);
358 frames = NULL;
359 snd_pcm_close(handle);
363 void pcm_play_dma_init(void)
365 int err;
366 audiohw_preinit();
368 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
370 printf("%s(): Cannot open device %s: %s\n", __func__, device, snd_strerror(err));
371 exit(EXIT_FAILURE);
372 return;
375 if ((err = snd_pcm_nonblock(handle, 1)))
376 printf("Could not set non-block mode: %s\n", snd_strerror(err));
378 if ((err = set_hwparams(handle, rate)) < 0)
380 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
381 exit(EXIT_FAILURE);
383 if ((err = set_swparams(handle)) < 0)
385 printf("Setting of swparams failed: %s\n", snd_strerror(err));
386 exit(EXIT_FAILURE);
389 pcm_dma_apply_settings();
391 #ifdef USE_ASYNC_CALLBACK
392 pthread_mutexattr_t attr;
393 pthread_mutexattr_init(&attr);
394 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
395 pthread_mutex_init(&pcm_mtx, &attr);
396 #else
397 tick_add_task(pcm_tick);
398 #endif
400 atexit(cleanup);
401 return;
405 void pcm_play_lock(void)
407 #ifdef USE_ASYNC_CALLBACK
408 pthread_mutex_lock(&pcm_mtx);
409 #else
410 if (recursion++ == 0)
411 tick_remove_task(pcm_tick);
412 #endif
415 void pcm_play_unlock(void)
417 #ifdef USE_ASYNC_CALLBACK
418 pthread_mutex_unlock(&pcm_mtx);
419 #else
420 if (--recursion == 0)
421 tick_add_task(pcm_tick);
422 #endif
425 static void pcm_dma_apply_settings_nolock(void)
427 snd_pcm_drop(handle);
428 set_hwparams(handle, pcm_sampr);
431 void pcm_dma_apply_settings(void)
433 pcm_play_lock();
434 pcm_dma_apply_settings_nolock();
435 pcm_play_unlock();
439 void pcm_play_dma_pause(bool pause)
441 snd_pcm_pause(handle, pause);
445 void pcm_play_dma_stop(void)
447 snd_pcm_drain(handle);
450 void pcm_play_dma_start(const void *addr, size_t size)
452 pcm_dma_apply_settings_nolock();
454 pcm_data = addr;
455 pcm_size = size;
457 while (1)
459 snd_pcm_state_t state = snd_pcm_state(handle);
460 switch (state)
462 case SND_PCM_STATE_RUNNING:
463 return;
464 case SND_PCM_STATE_XRUN:
466 DEBUGF("Trying to recover from error\n");
467 int err = snd_pcm_recover(handle, -EPIPE, 0);
468 if (err < 0)
469 DEBUGF("Recovery failed: %s\n", snd_strerror(err));
470 continue;
472 case SND_PCM_STATE_SETUP:
474 int err = snd_pcm_prepare(handle);
475 if (err < 0)
476 printf("Prepare error: %s\n", snd_strerror(err));
477 /* fall through */
479 case SND_PCM_STATE_PREPARED:
480 { /* prepared state, we need to fill the buffer with silence before
481 * starting */
482 int err = async_rw(handle);
483 if (err < 0)
484 printf("Start error: %s\n", snd_strerror(err));
485 return;
487 case SND_PCM_STATE_PAUSED:
488 { /* paused, simply resume */
489 pcm_play_dma_pause(0);
490 return;
492 case SND_PCM_STATE_DRAINING:
493 /* run until drained */
494 continue;
495 default:
496 DEBUGF("Unhandled state: %s\n", snd_pcm_state_name(state));
497 return;
502 size_t pcm_get_bytes_waiting(void)
504 return pcm_size;
507 const void * pcm_play_dma_get_peak_buffer(int *count)
509 uintptr_t addr = (uintptr_t)pcm_data;
510 *count = pcm_size / 4;
511 return (void *)((addr + 3) & ~3);
514 void pcm_play_dma_postinit(void)
516 audiohw_postinit();
520 void pcm_set_mixer_volume(int volume)
522 (void)volume;
524 #ifdef HAVE_RECORDING
525 void pcm_rec_lock(void)
529 void pcm_rec_unlock(void)
533 void pcm_rec_dma_init(void)
537 void pcm_rec_dma_close(void)
541 void pcm_rec_dma_start(void *start, size_t size)
543 (void)start;
544 (void)size;
547 void pcm_rec_dma_stop(void)
551 const void * pcm_rec_dma_get_peak_buffer(void)
553 return NULL;
556 void audiohw_set_recvol(int left, int right, int type)
558 (void)left;
559 (void)right;
560 (void)type;
563 #endif /* HAVE_RECORDING */