fuze+: correctly handle settings (flip and invert) accross enable
[maemo-rb.git] / firmware / pcm.c
blob94b0d6eefb848cf090fd5f4e7b2fddebca79f2a5
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Michael Sevakis
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 ****************************************************************************/
21 #include <stdlib.h>
22 #include "system.h"
23 #include "kernel.h"
25 /* Define LOGF_ENABLE to enable logf output in this file */
26 /*#define LOGF_ENABLE*/
27 #include "logf.h"
28 #include "audio.h"
29 #include "sound.h"
30 #include "general.h"
31 #include "pcm-internal.h"
32 #include "pcm_mixer.h"
34 /**
35 * Aspects implemented in the target-specific portion:
37 * ==Playback==
38 * Public -
39 * pcm_postinit
40 * pcm_get_bytes_waiting
41 * pcm_play_lock
42 * pcm_play_unlock
43 * Semi-private -
44 * pcm_play_dma_complete_callback
45 * pcm_play_dma_status_callback
46 * pcm_play_dma_init
47 * pcm_play_dma_postinit
48 * pcm_play_dma_start
49 * pcm_play_dma_stop
50 * pcm_play_dma_pause
51 * pcm_play_dma_get_peak_buffer
52 * Data Read/Written within TSP -
53 * pcm_sampr (R)
54 * pcm_fsel (R)
55 * pcm_curr_sampr (R)
56 * pcm_playing (R)
57 * pcm_paused (R)
59 * ==Playback/Recording==
60 * Public -
61 * pcm_dma_addr
62 * Semi-private -
63 * pcm_dma_apply_settings
65 * ==Recording==
66 * Public -
67 * pcm_rec_lock
68 * pcm_rec_unlock
69 * Semi-private -
70 * pcm_rec_dma_complete_callback
71 * pcm_rec_dma_status_callback
72 * pcm_rec_dma_init
73 * pcm_rec_dma_close
74 * pcm_rec_dma_start
75 * pcm_rec_dma_stop
76 * pcm_rec_dma_get_peak_buffer
77 * Data Read/Written within TSP -
78 * pcm_recording (R)
80 * States are set _after_ the target's pcm driver is called so that it may
81 * know from whence the state is changed. One exception is init.
85 /* 'true' when all stages of pcm initialization have completed */
86 static bool pcm_is_ready = false;
88 /* The registered callback function to ask for more mp3 data */
89 static volatile pcm_play_callback_type
90 pcm_callback_for_more SHAREDBSS_ATTR = NULL;
91 /* The registered callback function to inform of DMA status */
92 volatile pcm_status_callback_type
93 pcm_play_status_callback SHAREDBSS_ATTR = NULL;
94 /* PCM playback state */
95 volatile bool pcm_playing SHAREDBSS_ATTR = false;
96 /* PCM paused state. paused implies playing */
97 volatile bool pcm_paused SHAREDBSS_ATTR = false;
98 /* samplerate of currently playing audio - undefined if stopped */
99 unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0;
100 /* samplerate waiting to be set */
101 unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT;
102 /* samplerate frequency selection index */
103 int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT;
105 /* Called internally by functions to reset the state */
106 static void pcm_play_stopped(void)
108 pcm_callback_for_more = NULL;
109 pcm_play_status_callback = NULL;
110 pcm_paused = false;
111 pcm_playing = false;
114 static void pcm_wait_for_init(void)
116 while (!pcm_is_ready)
117 sleep(0);
121 * Perform peak calculation on a buffer of packed 16-bit samples.
123 * Used for recording and playback.
125 static void pcm_peak_peeker(const int16_t *p, int count,
126 struct pcm_peaks *peaks)
128 uint32_t peak_l = 0, peak_r = 0;
129 const int16_t *pend = p + 2 * count;
133 int32_t s;
135 s = p[0];
137 if (s < 0)
138 s = -s;
140 if ((uint32_t)s > peak_l)
141 peak_l = s;
143 s = p[1];
145 if (s < 0)
146 s = -s;
148 if ((uint32_t)s > peak_r)
149 peak_r = s;
151 p += 4 * 2; /* Every 4th sample, interleaved */
153 while (p < pend);
155 peaks->left = peak_l;
156 peaks->right = peak_r;
159 void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active,
160 const void *addr, int count)
162 long tick = current_tick;
164 /* Peak no farther ahead than expected period to avoid overcalculation */
165 long period = tick - peaks->tick;
167 /* Keep reasonable limits on period */
168 if (period < 1)
169 period = 1;
170 else if (period > HZ/5)
171 period = HZ/5;
173 peaks->period = (3*peaks->period + period) / 4;
174 peaks->tick = tick;
176 if (active)
178 int framecount = peaks->period*pcm_curr_sampr / HZ;
179 count = MIN(framecount, count);
181 if (count > 0)
182 pcm_peak_peeker(addr, count, peaks);
183 /* else keep previous peak values */
185 else
187 /* peaks are zero */
188 peaks->left = peaks->right = 0;
192 void pcm_calculate_peaks(int *left, int *right)
194 /* peak data for the global peak values - i.e. what the final output is */
195 static struct pcm_peaks peaks;
197 int count;
198 const void *addr = pcm_play_dma_get_peak_buffer(&count);
200 pcm_do_peak_calculation(&peaks, pcm_playing && !pcm_paused,
201 addr, count);
203 if (left)
204 *left = peaks.left;
206 if (right)
207 *right = peaks.right;
210 const void* pcm_get_peak_buffer(int * count)
212 return pcm_play_dma_get_peak_buffer(count);
215 bool pcm_is_playing(void)
217 return pcm_playing;
220 bool pcm_is_paused(void)
222 return pcm_paused;
225 /****************************************************************************
226 * Functions that do not require targeted implementation but only a targeted
227 * interface
230 /* This should only be called at startup before any audio playback or
231 recording is attempted */
232 void pcm_init(void)
234 logf("pcm_init");
236 pcm_play_stopped();
238 pcm_set_frequency(HW_SAMPR_DEFAULT);
240 logf(" pcm_play_dma_init");
241 pcm_play_dma_init();
244 /* Finish delayed init */
245 void pcm_postinit(void)
247 logf("pcm_postinit");
249 logf(" pcm_play_dma_postinit");
251 pcm_play_dma_postinit();
253 pcm_is_ready = true;
256 bool pcm_is_initialized(void)
258 return pcm_is_ready;
261 /* Common code to pcm_play_data and pcm_play_pause */
262 static void pcm_play_data_start(const void *addr, size_t size)
264 ALIGN_AUDIOBUF(addr, size);
266 if (!(addr && size))
268 pcm_play_callback_type get_more = pcm_callback_for_more;
269 addr = NULL;
270 size = 0;
272 if (get_more)
274 logf(" get_more");
275 get_more(&addr, &size);
276 ALIGN_AUDIOBUF(addr, size);
280 if (addr && size)
282 logf(" pcm_play_dma_start");
283 pcm_apply_settings();
284 pcm_play_dma_start(addr, size);
285 pcm_playing = true;
286 pcm_paused = false;
287 return;
290 /* Force a stop */
291 logf(" pcm_play_dma_stop");
292 pcm_play_dma_stop();
293 pcm_play_stopped();
296 void pcm_play_data(pcm_play_callback_type get_more,
297 pcm_status_callback_type status_cb,
298 const void *start, size_t size)
300 logf("pcm_play_data");
302 pcm_play_lock();
304 pcm_callback_for_more = get_more;
305 pcm_play_status_callback = status_cb;
307 logf(" pcm_play_data_start");
308 pcm_play_data_start(start, size);
310 pcm_play_unlock();
313 bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
314 const void **addr, size_t *size)
316 /* Check status callback first if error */
317 if (status < PCM_DMAST_OK)
318 status = pcm_play_dma_status_callback(status);
320 pcm_play_callback_type get_more = pcm_callback_for_more;
322 if (get_more && status >= PCM_DMAST_OK)
324 *addr = NULL;
325 *size = 0;
327 /* Call registered callback to obtain next buffer */
328 get_more(addr, size);
329 ALIGN_AUDIOBUF(*addr, *size);
331 if (*addr && *size)
332 return true;
335 /* Error, callback missing or no more DMA to do */
336 pcm_play_dma_stop();
337 pcm_play_stopped();
339 return false;
342 void pcm_play_pause(bool play)
344 logf("pcm_play_pause: %s", play ? "play" : "pause");
346 pcm_play_lock();
348 if (play == pcm_paused && pcm_playing)
350 if (!play)
352 logf(" pcm_play_dma_pause");
353 pcm_play_dma_pause(true);
354 pcm_paused = true;
356 else if (pcm_get_bytes_waiting() > 0)
358 logf(" pcm_play_dma_pause");
359 pcm_apply_settings();
360 pcm_play_dma_pause(false);
361 pcm_paused = false;
363 else
365 logf(" pcm_play_dma_start: no data");
366 pcm_play_data_start(NULL, 0);
369 else
371 logf(" no change");
374 pcm_play_unlock();
377 void pcm_play_stop(void)
379 logf("pcm_play_stop");
381 pcm_play_lock();
383 if (pcm_playing)
385 logf(" pcm_play_dma_stop");
386 pcm_play_dma_stop();
387 pcm_play_stopped();
389 else
391 logf(" not playing");
394 pcm_play_unlock();
397 /**/
399 /* set frequency next frequency used by the audio hardware -
400 * what pcm_apply_settings will set */
401 void pcm_set_frequency(unsigned int samplerate)
403 logf("pcm_set_frequency");
405 int index;
407 #ifdef CONFIG_SAMPR_TYPES
408 unsigned int type = samplerate & SAMPR_TYPE_MASK;
409 samplerate &= ~SAMPR_TYPE_MASK;
411 /* For now, supported targets have direct conversion when configured with
412 * CONFIG_SAMPR_TYPES.
413 * Some hypothetical target with independent rates would need slightly
414 * different handling throughout this source. */
415 samplerate = pcm_sampr_to_hw_sampr(samplerate, type);
416 #endif /* CONFIG_SAMPR_TYPES */
418 index = round_value_to_list32(samplerate, hw_freq_sampr,
419 HW_NUM_FREQ, false);
421 if (samplerate != hw_freq_sampr[index])
422 index = HW_FREQ_DEFAULT; /* Invalid = default */
424 pcm_sampr = hw_freq_sampr[index];
425 pcm_fsel = index;
428 /* apply pcm settings to the hardware */
429 void pcm_apply_settings(void)
431 logf("pcm_apply_settings");
433 pcm_wait_for_init();
435 if (pcm_sampr != pcm_curr_sampr)
437 logf(" pcm_dma_apply_settings");
438 pcm_dma_apply_settings();
439 pcm_curr_sampr = pcm_sampr;
443 #ifdef HAVE_RECORDING
444 /** Low level pcm recording apis **/
446 /* Next start for recording peaks */
447 static const void * volatile pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
448 /* the registered callback function for when more data is available */
449 static volatile pcm_rec_callback_type
450 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
451 volatile pcm_status_callback_type
452 pcm_rec_status_callback SHAREDBSS_ATTR = NULL;
453 /* DMA transfer in is currently active */
454 volatile bool pcm_recording SHAREDBSS_ATTR = false;
456 /* Called internally by functions to reset the state */
457 static void pcm_recording_stopped(void)
459 pcm_recording = false;
460 pcm_callback_more_ready = NULL;
461 pcm_rec_status_callback = NULL;
465 * Return recording peaks - From the end of the last peak up to
466 * current write position.
468 void pcm_calculate_rec_peaks(int *left, int *right)
470 static struct pcm_peaks peaks;
472 if (pcm_recording)
474 const int16_t *peak_addr = pcm_rec_peak_addr;
475 const int16_t *addr = pcm_rec_dma_get_peak_buffer();
477 if (addr != NULL)
479 int count = (addr - peak_addr) / 2; /* Interleaved L+R */
481 if (count > 0)
483 pcm_peak_peeker(peak_addr, count, &peaks);
485 if (peak_addr == pcm_rec_peak_addr)
486 pcm_rec_peak_addr = addr;
489 /* else keep previous peak values */
491 else
493 peaks.left = peaks.right = 0;
496 if (left)
497 *left = peaks.left;
499 if (right)
500 *right = peaks.right;
503 bool pcm_is_recording(void)
505 return pcm_recording;
508 /****************************************************************************
509 * Functions that do not require targeted implementation but only a targeted
510 * interface
513 void pcm_init_recording(void)
515 logf("pcm_init_recording");
517 pcm_wait_for_init();
519 /* Stop the beasty before attempting recording */
520 mixer_reset();
522 /* Recording init is locked unlike general pcm init since this is not
523 * just a one-time event at startup and it should and must be safe by
524 * now. */
525 pcm_rec_lock();
527 logf(" pcm_rec_dma_init");
528 pcm_recording_stopped();
529 pcm_rec_dma_init();
531 pcm_rec_unlock();
534 void pcm_close_recording(void)
536 logf("pcm_close_recording");
538 pcm_rec_lock();
540 if (pcm_recording)
542 logf(" pcm_rec_dma_stop");
543 pcm_rec_dma_stop();
544 pcm_recording_stopped();
547 logf(" pcm_rec_dma_close");
548 pcm_rec_dma_close();
550 pcm_rec_unlock();
553 void pcm_record_data(pcm_rec_callback_type more_ready,
554 pcm_status_callback_type status_cb,
555 void *addr, size_t size)
557 logf("pcm_record_data");
559 ALIGN_AUDIOBUF(addr, size);
561 if (!(addr && size))
563 logf(" no buffer");
564 return;
567 pcm_rec_lock();
569 pcm_callback_more_ready = more_ready;
570 pcm_rec_status_callback = status_cb;
572 /* Need a physical DMA address translation, if not already physical. */
573 pcm_rec_peak_addr = pcm_rec_dma_addr(addr);
575 logf(" pcm_rec_dma_start");
576 pcm_apply_settings();
577 pcm_rec_dma_start(addr, size);
578 pcm_recording = true;
580 pcm_rec_unlock();
581 } /* pcm_record_data */
583 void pcm_stop_recording(void)
585 logf("pcm_stop_recording");
587 pcm_rec_lock();
589 if (pcm_recording)
591 logf(" pcm_rec_dma_stop");
592 pcm_rec_dma_stop();
593 pcm_recording_stopped();
596 pcm_rec_unlock();
597 } /* pcm_stop_recording */
599 bool pcm_rec_dma_complete_callback(enum pcm_dma_status status,
600 void **addr, size_t *size)
602 /* Check status callback first if error */
603 if (status < PCM_DMAST_OK)
604 status = pcm_rec_dma_status_callback(status);
606 pcm_rec_callback_type have_more = pcm_callback_more_ready;
608 if (have_more && status >= PCM_DMAST_OK)
610 /* Call registered callback to obtain next buffer */
611 have_more(addr, size);
612 ALIGN_AUDIOBUF(*addr, *size);
614 if (*addr && *size)
616 /* Need a physical DMA address translation, if not already
617 * physical. */
618 pcm_rec_peak_addr = pcm_rec_dma_addr(*addr);
619 return true;
623 /* Error, callback missing or no more DMA to do */
624 pcm_rec_dma_stop();
625 pcm_recording_stopped();
627 return false;
630 #endif /* HAVE_RECORDING */