summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/export/config.h5
-rw-r--r--firmware/export/pcm-internal.h81
-rw-r--r--firmware/export/pcm.h44
-rw-r--r--firmware/export/pcm_mixer.h102
-rw-r--r--firmware/pcm.c18
-rw-r--r--firmware/pcm_mixer.c501
-rw-r--r--firmware/target/arm/as3525/pcm-as3525.c9
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c3
-rw-r--r--firmware/target/arm/pcm-mixer-armv4.c182
-rw-r--r--firmware/target/arm/pcm-mixer-armv5.c106
-rw-r--r--firmware/target/arm/pcm-mixer-armv6.c118
-rw-r--r--firmware/target/arm/pcm-pp.c43
-rw-r--r--firmware/target/arm/pcm-telechips.c26
-rw-r--r--firmware/target/arm/pnx0101/pcm-pnx0101.c13
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c3
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c5
-rw-r--r--firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c3
-rw-r--r--firmware/target/arm/s5l8700/pcm-s5l8700.c17
-rw-r--r--firmware/target/arm/s5l8702/pcm-s5l8702.c3
-rw-r--r--firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c1
-rw-r--r--firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c3
-rw-r--r--firmware/target/coldfire/pcm-coldfire.c4
-rw-r--r--firmware/target/coldfire/pcm-mixer-coldfire.c134
-rw-r--r--firmware/target/hosted/android/pcm-android.c54
-rw-r--r--firmware/target/hosted/maemo/pcm-gstreamer.c3
-rw-r--r--firmware/target/hosted/sdl/pcm-sdl.c47
-rw-r--r--firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c2
28 files changed, 1462 insertions, 69 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 7c3a909f22..85d9a28f1b 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -311,6 +311,7 @@ sound.c
311#ifndef BOOTLOADER 311#ifndef BOOTLOADER
312pcm_sampr.c 312pcm_sampr.c
313pcm.c 313pcm.c
314pcm_mixer.c
314#ifdef HAVE_RECORDING 315#ifdef HAVE_RECORDING
315enc_base.c 316enc_base.c
316#endif /* HAVE_RECORDING */ 317#endif /* HAVE_RECORDING */
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 2c7c6e89db..70047ff866 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -1048,4 +1048,9 @@ Lyre prototype 1 */
1048#define HAVE_IO_PRIORITY 1048#define HAVE_IO_PRIORITY
1049#endif 1049#endif
1050 1050
1051#if defined(CPU_COLDIRE) || CONFIG_CPU == IMX31L
1052/* Can record and play simultaneously */
1053#define HAVE_PCM_FULL_DUPLEX
1054#endif
1055
1051#endif /* __CONFIG_H__ */ 1056#endif /* __CONFIG_H__ */
diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h
new file mode 100644
index 0000000000..d69138f534
--- /dev/null
+++ b/firmware/export/pcm-internal.h
@@ -0,0 +1,81 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
11 * Copyright (C) 2011 by Michael Sevakis
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#ifndef PCM_INTERNAL_H
23#define PCM_INTERNAL_H
24
25/** The following are for internal use between pcm.c and target-
26 specific portion **/
27
28/* Called by the bottom layer ISR when more data is needed. Returns non-
29 * zero size if more data is to be played. Setting start to NULL
30 * forces stop. */
31void pcm_play_get_more_callback(void **start, size_t *size);
32
33/* Called by the bottom layer ISR after next transfer has begun in order
34 to fill more data for next "get more" callback to implement double-buffered
35 callbacks - except for a couple ASM handlers, help drivers to implement
36 this functionality with minimal overhead */
37static FORCE_INLINE void pcm_play_dma_started_callback(void)
38{
39 extern void (* pcm_play_dma_started)(void);
40 void (* callback)(void) = pcm_play_dma_started;
41 if (callback)
42 callback();
43}
44
45extern unsigned long pcm_curr_sampr;
46extern unsigned long pcm_sampr;
47extern int pcm_fsel;
48
49#ifdef HAVE_PCM_DMA_ADDRESS
50void * pcm_dma_addr(void *addr);
51#endif
52
53extern volatile bool pcm_playing;
54extern volatile bool pcm_paused;
55
56void pcm_play_dma_lock(void);
57void pcm_play_dma_unlock(void);
58void pcm_play_dma_init(void) INIT_ATTR;
59void pcm_play_dma_start(const void *addr, size_t size);
60void pcm_play_dma_stop(void);
61void pcm_play_dma_pause(bool pause);
62const void * pcm_play_dma_get_peak_buffer(int *count);
63
64void pcm_dma_apply_settings(void);
65
66#ifdef HAVE_RECORDING
67
68/* DMA transfer in is currently active */
69extern volatile bool pcm_recording;
70
71/* APIs implemented in the target-specific portion */
72void pcm_rec_dma_init(void);
73void pcm_rec_dma_close(void);
74void pcm_rec_dma_start(void *addr, size_t size);
75void pcm_rec_dma_record_more(void *start, size_t size);
76void pcm_rec_dma_stop(void);
77const void * pcm_rec_dma_get_peak_buffer(void);
78
79#endif /* HAVE_RECORDING */
80
81#endif /* PCM_INTERNAL_H */
diff --git a/firmware/export/pcm.h b/firmware/export/pcm.h
index 80b5b09a79..22c5ef350e 100644
--- a/firmware/export/pcm.h
+++ b/firmware/export/pcm.h
@@ -49,7 +49,7 @@
49 49
50/** RAW PCM routines used with playback and recording **/ 50/** RAW PCM routines used with playback and recording **/
51 51
52/* Typedef for registered callback */ 52/* Typedef for registered callbacks */
53typedef void (*pcm_play_callback_type)(unsigned char **start, 53typedef void (*pcm_play_callback_type)(unsigned char **start,
54 size_t *size); 54 size_t *size);
55typedef void (*pcm_rec_callback_type)(int status, void **start, size_t *size); 55typedef void (*pcm_rec_callback_type)(int status, void **start, size_t *size);
@@ -90,34 +90,7 @@ void pcm_play_pause(bool play);
90bool pcm_is_paused(void); 90bool pcm_is_paused(void);
91bool pcm_is_playing(void); 91bool pcm_is_playing(void);
92 92
93/** The following are for internal use between pcm.c and target- 93void pcm_play_set_dma_started_callback(void (* callback)(void));
94 specific portion **/
95
96/* Called by the bottom layer ISR when more data is needed. Returns non-
97 * zero size if more data is to be played. Setting start to NULL
98 * forces stop. */
99void pcm_play_get_more_callback(void **start, size_t *size);
100
101extern unsigned long pcm_curr_sampr;
102extern unsigned long pcm_sampr;
103extern int pcm_fsel;
104
105#ifdef HAVE_PCM_DMA_ADDRESS
106void * pcm_dma_addr(void *addr);
107#endif
108
109extern volatile bool pcm_playing;
110extern volatile bool pcm_paused;
111
112void pcm_play_dma_lock(void);
113void pcm_play_dma_unlock(void);
114void pcm_play_dma_init(void) INIT_ATTR;
115void pcm_play_dma_start(const void *addr, size_t size);
116void pcm_play_dma_stop(void);
117void pcm_play_dma_pause(bool pause);
118const void * pcm_play_dma_get_peak_buffer(int *count);
119
120void pcm_dma_apply_settings(void);
121 94
122#ifdef HAVE_RECORDING 95#ifdef HAVE_RECORDING
123 96
@@ -148,19 +121,6 @@ void pcm_rec_more_ready_callback(int status, void **start, size_t *size);
148 121
149void pcm_calculate_rec_peaks(int *left, int *right); 122void pcm_calculate_rec_peaks(int *left, int *right);
150 123
151/** The following are for internal use between pcm.c and target-
152 specific portion **/
153/* DMA transfer in is currently active */
154extern volatile bool pcm_recording;
155
156/* APIs implemented in the target-specific portion */
157void pcm_rec_dma_init(void);
158void pcm_rec_dma_close(void);
159void pcm_rec_dma_start(void *addr, size_t size);
160void pcm_rec_dma_record_more(void *start, size_t size);
161void pcm_rec_dma_stop(void);
162const void * pcm_rec_dma_get_peak_buffer(void);
163
164#endif /* HAVE_RECORDING */ 124#endif /* HAVE_RECORDING */
165 125
166#endif /* PCM_PLAYBACK_H */ 126#endif /* PCM_PLAYBACK_H */
diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h
new file mode 100644
index 0000000000..3b420e1320
--- /dev/null
+++ b/firmware/export/pcm_mixer.h
@@ -0,0 +1,102 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Michael Sevakis
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef PCM_MIXER_H
23#define PCM_MIXER_H
24
25/** Simple config **/
26
27/* Length of PCM frames (always) */
28#if CONFIG_CPU == PP5002
29/* There's far less time to do mixing because HW FIFOs are short */
30#define MIX_FRAME_SAMPLES 64
31#else
32/* Assume HW DMA engine is available or sufficient latency exists in the
33 PCM pathway */
34#define MIX_FRAME_SAMPLES 256
35#endif
36
37#if defined(CPU_COLDFIRE) || defined(CPU_PP)
38/* For Coldfire, it's just faster
39 For PortalPlayer, this also avoids more expensive cache coherency */
40#define DOWNMIX_BUF_IBSS IBSS_ATTR
41#else
42/* Otherwise can't DMA from IRAM, IRAM is pointless or worse */
43#define DOWNMIX_BUF_IBSS
44#endif
45
46
47/** Definitions **/
48
49/* Channels are preassigned for simplicity */
50enum pcm_mixer_channel
51{
52 PCM_MIXER_CHAN_PLAYBACK = 0,
53 PCM_MIXER_CHAN_VOICE,
54#ifndef HAVE_HARDWARE_BEEP
55 PCM_MIXER_CHAN_BEEP,
56#endif
57 /* Add new channel indexes above this line */
58 PCM_MIXER_NUM_CHANNELS,
59};
60
61/* Channel playback states */
62enum channel_status
63{
64 CHANNEL_STOPPED = 0,
65 CHANNEL_PLAYING,
66 CHANNEL_PAUSED,
67};
68
69#define MIX_AMP_UNITY 0x00010000
70#define MIX_AMP_MUTE 0x00000000
71
72
73/** Public interfaces **/
74
75/* Start playback on a channel */
76void mixer_channel_play_data(enum pcm_mixer_channel channel,
77 pcm_play_callback_type get_more,
78 unsigned char *start, size_t size);
79
80/* Pause or resume a channel (when started) */
81void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play);
82
83/* Stop playback on a channel */
84void mixer_channel_stop(enum pcm_mixer_channel channel);
85
86/* Set channel's amplitude factor */
87void mixer_channel_set_amplitude(enum pcm_mixer_channel channel,
88 unsigned int amplitude);
89
90/* Return channel's playback status */
91enum channel_status mixer_channel_status(enum pcm_mixer_channel channel);
92
93/* Returns amount data remaining in channel before next callback */
94size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel);
95
96/* Return pointer to channel's playing audio data and the size remaining */
97void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count);
98
99/* Stop ALL channels and PCM and reset state */
100void mixer_reset(void);
101
102#endif /* PCM_MIXER_H */
diff --git a/firmware/pcm.c b/firmware/pcm.c
index d15c129015..b7415f329b 100644
--- a/firmware/pcm.c
+++ b/firmware/pcm.c
@@ -28,6 +28,8 @@
28#include "audio.h" 28#include "audio.h"
29#include "sound.h" 29#include "sound.h"
30#include "general.h" 30#include "general.h"
31#include "pcm-internal.h"
32#include "pcm_mixer.h"
31 33
32/** 34/**
33 * Aspects implemented in the target-specific portion: 35 * Aspects implemented in the target-specific portion:
@@ -78,8 +80,8 @@
78 */ 80 */
79 81
80/* the registered callback function to ask for more mp3 data */ 82/* the registered callback function to ask for more mp3 data */
81static volatile pcm_play_callback_type pcm_callback_for_more 83static pcm_play_callback_type pcm_callback_for_more SHAREDBSS_ATTR = NULL;
82 SHAREDBSS_ATTR = NULL; 84void (* pcm_play_dma_started)(void) SHAREDBSS_ATTR = NULL;
83/* PCM playback state */ 85/* PCM playback state */
84volatile bool pcm_playing SHAREDBSS_ATTR = false; 86volatile bool pcm_playing SHAREDBSS_ATTR = false;
85/* PCM paused state. paused implies playing */ 87/* PCM paused state. paused implies playing */
@@ -95,6 +97,7 @@ int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT;
95static void pcm_play_stopped(void) 97static void pcm_play_stopped(void)
96{ 98{
97 pcm_callback_for_more = NULL; 99 pcm_callback_for_more = NULL;
100 pcm_play_dma_started = NULL;
98 pcm_paused = false; 101 pcm_paused = false;
99 pcm_playing = false; 102 pcm_playing = false;
100} 103}
@@ -404,6 +407,12 @@ void pcm_apply_settings(void)
404 } 407 }
405} 408}
406 409
410/* register callback to buffer more data */
411void pcm_play_set_dma_started_callback(void (* callback)(void))
412{
413 pcm_play_dma_started = callback;
414}
415
407#ifdef HAVE_RECORDING 416#ifdef HAVE_RECORDING
408/** Low level pcm recording apis **/ 417/** Low level pcm recording apis **/
409 418
@@ -475,6 +484,11 @@ void pcm_init_recording(void)
475{ 484{
476 logf("pcm_init_recording"); 485 logf("pcm_init_recording");
477 486
487#ifndef HAVE_PCM_FULL_DUPLEX
488 /* Stop the beasty before attempting recording */
489 mixer_reset();
490#endif
491
478 /* Recording init is locked unlike general pcm init since this is not 492 /* Recording init is locked unlike general pcm init since this is not
479 * just a one-time event at startup and it should and must be safe by 493 * just a one-time event at startup and it should and must be safe by
480 * now. */ 494 * now. */
diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c
new file mode 100644
index 0000000000..cddd3f0f86
--- /dev/null
+++ b/firmware/pcm_mixer.c
@@ -0,0 +1,501 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Michael Sevakis
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22#include "system.h"
23#include "general.h"
24#include "kernel.h"
25#include "pcm.h"
26#include "pcm_mixer.h"
27#include "dsp.h"
28
29/* Channels use standard-style PCM callback interface but a latency of one
30 frame by double-buffering is introduced in order to facilitate mixing and
31 keep the hardware fed. There must be sufficient time to perform operations
32 before the last samples are sent to the codec and so things are done in
33 parallel (as much as possible) with sending-out data. */
34
35/* Define this to nonzero to add a marker pulse at each frame start */
36#define FRAME_BOUNDARY_MARKERS 0
37
38/* Descriptor for each channel */
39struct mixer_channel
40{
41 unsigned char *start; /* Buffer pointer */
42 size_t size; /* Bytes remaining */
43 size_t last_size; /* Size of consumed data in prev. cycle */
44 pcm_play_callback_type get_more; /* Registered callback */
45 enum channel_status status; /* Playback status */
46 uint32_t amplitude; /* Amp. factor: 0x0000 = mute, 0x10000 = unity */
47};
48
49/* Forget about boost here for the moment */
50#define MIX_FRAME_SIZE (MIX_FRAME_SAMPLES*4)
51
52/* Because of the double-buffering, playback is always from here, otherwise a
53 mechanism for the channel callbacks not to free buffers too early would be
54 needed (if we _really_ want it and it's worth it, we _can_ do that ;-) ) */
55static uint32_t downmix_buf[2][MIX_FRAME_SAMPLES] DOWNMIX_BUF_IBSS MEM_ALIGN_ATTR;
56static int downmix_index = 0; /* Which downmix_buf? */
57static size_t next_size = 0; /* Size of buffer to play next time */
58
59/* Descriptors for all available channels */
60static struct mixer_channel channels[PCM_MIXER_NUM_CHANNELS] IBSS_ATTR;
61
62/* Packed pointer array of all playing (active) channels in "channels" array */
63static struct mixer_channel * active_channels[PCM_MIXER_NUM_CHANNELS+1] IBSS_ATTR;
64
65/* Number of silence frames to play after all data has played */
66#define MAX_IDLE_FRAMES (NATIVE_FREQUENCY*3 / MIX_FRAME_SAMPLES)
67static unsigned int idle_counter = 0;
68
69/* Cheapo buffer align macro to align to the 16-16 PCM size */
70#define ALIGN_CHANNEL(start, size) \
71 ({ start = (void *)(((uintptr_t)start + 3) & ~3); \
72 size &= ~3; })
73
74/* Include any implemented CPU-optimized mixdown routines */
75#if defined(CPU_ARM)
76#if ARM_ARCH >= 6
77#include "pcm-mixer-armv6.c"
78#elif ARM_ARCH >= 5
79#include "pcm-mixer-armv5.c"
80#else
81#include "pcm-mixer-armv4.c"
82#endif /* ARM_ARCH */
83#elif defined (CPU_COLDFIRE)
84#include "pcm-mixer-coldfire.c"
85#endif /* CPU_* */
86
87
88/** Generic mixing routines **/
89
90#ifndef MIXER_OPTIMIZED_MIX_SAMPLES
91/* Clip sample to signed 16 bit range */
92static FORCE_INLINE int32_t clip_sample_16(int32_t sample)
93{
94 if ((int16_t)sample != sample)
95 sample = 0x7fff ^ (sample >> 31);
96 return sample;
97}
98
99/* Mix channels' samples and apply gain factors */
100static FORCE_INLINE void mix_samples(uint32_t *out,
101 int16_t *src0,
102 int32_t src0_amp,
103 int16_t *src1,
104 int32_t src1_amp,
105 size_t size)
106{
107 if (src0_amp == MIX_AMP_UNITY && src1_amp == MIX_AMP_UNITY)
108 {
109 /* Both are unity amplitude */
110 do
111 {
112 int32_t l = *src0++ + *src1++;
113 int32_t h = *src0++ + *src1++;
114 *out++ = (uint16_t)clip_sample_16(l) | (clip_sample_16(h) << 16);
115 }
116 while ((size -= 4) > 0);
117 }
118 else if (src0_amp != MIX_AMP_UNITY && src1_amp != MIX_AMP_UNITY)
119 {
120 /* Neither are unity amplitude */
121 do
122 {
123 int32_t l = (*src0++ * src0_amp >> 16) + (*src1++ * src1_amp >> 16);
124 int32_t h = (*src0++ * src0_amp >> 16) + (*src1++ * src1_amp >> 16);
125 *out++ = (uint16_t)clip_sample_16(l) | (clip_sample_16(h) << 16);
126 }
127 while ((size -= 4) > 0);
128 }
129 else
130 {
131 /* One is unity amplitude */
132 if (src0_amp != MIX_AMP_UNITY)
133 {
134 /* Keep unity in src0, amp0 */
135 int16_t *src_tmp = src0;
136 src0 = src1;
137 src1 = src_tmp;
138 src1_amp = src0_amp;
139 src0_amp = MIX_AMP_UNITY;
140 }
141
142 do
143 {
144 int32_t l = *src0++ + (*src1++ * src1_amp >> 16);
145 int32_t h = *src0++ + (*src1++ * src1_amp >> 16);
146 *out++ = (uint16_t)clip_sample_16(l) | (clip_sample_16(h) << 16);
147 }
148 while ((size -= 4) > 0);
149 }
150}
151#endif /* MIXER_OPTIMIZED_MIX_SAMPLES */
152
153#ifndef MIXER_OPTIMIZED_WRITE_SAMPLES
154/* Write channel's samples and apply gain factor */
155static FORCE_INLINE void write_samples(uint32_t *out,
156 int16_t *src,
157 int32_t amp,
158 size_t size)
159{
160 if (LIKELY(amp == MIX_AMP_UNITY))
161 {
162 /* Channel is unity amplitude */
163 memcpy(out, src, size);
164 }
165 else
166 {
167 /* Channel needs amplitude cut */
168 do
169 {
170 int32_t l = *src++ * amp >> 16;
171 int32_t h = *src++ * amp & 0xffff0000;
172 *out++ = (uint16_t)l | h;
173 }
174 while ((size -= 4) > 0);
175 }
176}
177#endif /* MIXER_OPTIMIZED_WRITE_SAMPLES */
178
179
180/** Private generic routines **/
181
182/* Mark channel active to mix its data */
183static void mixer_activate_channel(struct mixer_channel *chan)
184{
185 void **elem = find_array_ptr((void **)active_channels, chan);
186
187 if (!*elem)
188 {
189 idle_counter = 0;
190 *elem = chan;
191 }
192}
193
194/* Stop channel from mixing */
195static void mixer_deactivate_channel(struct mixer_channel *chan)
196{
197 remove_array_ptr((void **)active_channels, chan);
198}
199
200/* Deactivate channel and change it to stopped state */
201static void channel_stopped(struct mixer_channel *chan)
202{
203 mixer_deactivate_channel(chan);
204 chan->size = 0;
205 chan->start = NULL;
206 chan->status = CHANNEL_STOPPED;
207}
208
209/* Main PCM callback - sends the current prepared frame to play */
210static void mixer_pcm_callback(unsigned char **start, size_t *size)
211{
212 *start = (unsigned char *)downmix_buf[downmix_index];
213 *size = next_size;
214}
215
216/* Buffering callback - calls sub-callbacks and mixes the data for next
217 buffer to be sent from mixer_pcm_callback() */
218static void ICODE_ATTR mixer_buffer_callback(void)
219{
220 downmix_index ^= 1; /* Next buffer */
221
222 void *mixptr = downmix_buf[downmix_index];
223 size_t mixsize = MIX_FRAME_SIZE;
224 struct mixer_channel **chan_p;
225
226 next_size = 0;
227
228 /* "Loop" back here if one round wasn't enough to fill a frame */
229fill_frame:
230 chan_p = active_channels;
231
232 while (*chan_p)
233 {
234 /* Find the active channel with the least data remaining and call any
235 callbacks for channels that ran out - stopping whichever report
236 "no more" */
237 struct mixer_channel *chan = *chan_p;
238 chan->start += chan->last_size;
239 chan->size -= chan->last_size;
240
241 if (chan->size == 0)
242 {
243 if (chan->get_more)
244 {
245 chan->get_more(&chan->start, &chan->size);
246 ALIGN_CHANNEL(chan->start, chan->size);
247 }
248
249 if (!(chan->start && chan->size))
250 {
251 /* Channel is stopping */
252 channel_stopped(chan);
253 continue;
254 }
255 }
256
257 /* Channel will play for at least part of this frame */
258
259 /* Channel with least amount of data remaining determines the downmix
260 size */
261 if (chan->size < mixsize)
262 mixsize = chan->size;
263
264 chan_p++;
265 }
266
267 /* Add all still-active channels to the downmix */
268 chan_p = active_channels;
269
270 if (LIKELY(*chan_p))
271 {
272 struct mixer_channel *chan = *chan_p++;
273
274 if (LIKELY(!*chan_p))
275 {
276 write_samples(mixptr, (void *)chan->start,
277 chan->amplitude, mixsize);
278 }
279 else
280 {
281 void *src0, *src1;
282 unsigned int amp0, amp1;
283
284 /* Mix first two channels with each other as the downmix */
285 src0 = chan->start;
286 amp0 = chan->amplitude;
287 chan->last_size = mixsize;
288
289 chan = *chan_p++;
290 src1 = chan->start;
291 amp1 = chan->amplitude;
292
293 while (1)
294 {
295 mix_samples(mixptr, src0, amp0, src1, amp1, mixsize);
296
297 if (!*chan_p)
298 break;
299
300 /* More channels to mix - mix each with existing downmix */
301 chan->last_size = mixsize;
302 chan = *chan_p++;
303 src0 = mixptr;
304 amp0 = MIX_AMP_UNITY;
305 src1 = chan->start;
306 amp1 = chan->amplitude;
307 }
308 }
309
310 chan->last_size = mixsize;
311 next_size += mixsize;
312
313 if (next_size < MIX_FRAME_SIZE)
314 {
315 /* There is still space remaining in this frame */
316 mixptr += mixsize;
317 mixsize = MIX_FRAME_SIZE - next_size;
318 goto fill_frame;
319 }
320 }
321 else if (idle_counter++ < MAX_IDLE_FRAMES)
322 {
323 /* Pad incomplete frames with silence */
324 if (idle_counter <= 3)
325 memset(mixptr, 0, MIX_FRAME_SIZE - next_size);
326
327 next_size = MIX_FRAME_SIZE;
328 }
329 /* else silence period ran out - go to sleep */
330
331#if FRAME_BOUNDARY_MARKERS != 0
332 if (next_size)
333 *downmix_buf[downmix_index] = downmix_index ? 0x7fff7fff : 0x80008000;
334#endif
335}
336
337/* Start PCM driver if it's not currently playing */
338static void mixer_start_pcm(void)
339{
340 if (pcm_is_playing())
341 return;
342
343#if defined(HAVE_RECORDING) && !defined(HAVE_PCM_FULL_DUPLEX)
344 if (pcm_is_recording())
345 return;
346#endif
347
348 /* Prepare initial frames and set up the double buffer */
349 mixer_buffer_callback();
350
351 /* Save the previous call's output */
352 void *start = downmix_buf[downmix_index];
353
354 mixer_buffer_callback();
355
356 pcm_play_set_dma_started_callback(mixer_buffer_callback);
357 pcm_play_data(mixer_pcm_callback, start, MIX_FRAME_SIZE);
358}
359
360/* Initialize the channel and start it if it has data */
361static void mixer_channel_play_start(struct mixer_channel *chan,
362 pcm_play_callback_type get_more,
363 unsigned char *start, size_t size)
364{
365 pcm_play_unlock(); /* Allow playback while doing any callback */
366
367 ALIGN_CHANNEL(start, size);
368
369 if (!(start && size))
370 {
371 /* Initial buffer not passed - call the callback now */
372 size = 0;
373 if (get_more)
374 {
375 get_more(&start, &size);
376 ALIGN_CHANNEL(start, size);
377 }
378 }
379
380 if (start && size)
381 {
382 /* We have data - start the channel */
383 chan->status = CHANNEL_PLAYING;
384 chan->start = start;
385 chan->size = size;
386 chan->last_size = 0;
387 chan->get_more = get_more;
388
389 pcm_play_lock();
390 mixer_activate_channel(chan);
391 mixer_start_pcm();
392 }
393 else
394 {
395 /* Never had anything - stop it now */
396 pcm_play_lock();
397 channel_stopped(chan);
398 }
399}
400
401
402/** Public interfaces **/
403
404/* Start playback on a channel */
405void mixer_channel_play_data(enum pcm_mixer_channel channel,
406 pcm_play_callback_type get_more,
407 unsigned char *start, size_t size)
408{
409 struct mixer_channel *chan = &channels[channel];
410
411 pcm_play_lock();
412 mixer_deactivate_channel(chan);
413 mixer_channel_play_start(chan, get_more, start, size);
414 pcm_play_unlock();
415}
416
417/* Pause or resume a channel (when started) */
418void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play)
419{
420 struct mixer_channel *chan = &channels[channel];
421
422 pcm_play_lock();
423
424 if (play == (chan->status == CHANNEL_PAUSED) &&
425 chan->status != CHANNEL_STOPPED)
426 {
427 if (play)
428 {
429 chan->status = CHANNEL_PLAYING;
430 mixer_activate_channel(chan);
431 mixer_start_pcm();
432 }
433 else
434 {
435 mixer_deactivate_channel(chan);
436 chan->status = CHANNEL_PAUSED;
437 }
438 }
439
440 pcm_play_unlock();
441}
442
443/* Stop playback on a channel */
444void mixer_channel_stop(enum pcm_mixer_channel channel)
445{
446 struct mixer_channel *chan = &channels[channel];
447
448 pcm_play_lock();
449 channel_stopped(chan);
450 pcm_play_unlock();
451}
452
453/* Set channel's amplitude factor */
454void mixer_channel_set_amplitude(enum pcm_mixer_channel channel,
455 unsigned int amplitude)
456{
457 channels[channel].amplitude = MIN(amplitude, MIX_AMP_UNITY);
458}
459
460/* Return channel's playback status */
461enum channel_status mixer_channel_status(enum pcm_mixer_channel channel)
462{
463 return channels[channel].status;
464}
465
466/* Returns amount data remaining in channel before next callback */
467size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel)
468{
469 return channels[channel].size;
470}
471
472/* Return pointer to channel's playing audio data and the size remaining */
473void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count)
474{
475 struct mixer_channel *chan = &channels[channel];
476 void * buf = *(unsigned char * volatile *)&chan->start;
477 size_t size = *(size_t volatile *)&chan->size;
478 void * buf2 = *(unsigned char * volatile *)&chan->start;
479
480 /* Still same buffer? */
481 if (buf == buf2)
482 {
483 *count = size >> 2;
484 return buf;
485 }
486 /* else can't be sure buf and size are related */
487
488 *count = 0;
489 return NULL;
490}
491
492/* Stop ALL channels and PCM and reset state */
493void mixer_reset(void)
494{
495 pcm_play_stop();
496
497 while (*active_channels)
498 channel_stopped(*active_channels);
499
500 idle_counter = 0;
501}
diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c
index 469833b05c..1b22d48f7f 100644
--- a/firmware/target/arm/as3525/pcm-as3525.c
+++ b/firmware/target/arm/as3525/pcm-as3525.c
@@ -29,6 +29,7 @@
29#include "as3514.h" 29#include "as3514.h"
30#include "audiohw.h" 30#include "audiohw.h"
31#include "mmu-arm.h" 31#include "mmu-arm.h"
32#include "pcm-internal.h"
32 33
33#define MAX_TRANSFER (4*((1<<11)-1)) /* maximum data we can transfer via DMA 34#define MAX_TRANSFER (4*((1<<11)-1)) /* maximum data we can transfer via DMA
34 * i.e. 32 bits at once (size of I2SO_DATA) 35 * i.e. 32 bits at once (size of I2SO_DATA)
@@ -104,9 +105,13 @@ static void dma_callback(void)
104 105
105 /* force writeback */ 106 /* force writeback */
106 clean_dcache_range(dma_start_addr, dma_start_size); 107 clean_dcache_range(dma_start_addr, dma_start_size);
108 play_start_pcm();
109 pcm_play_dma_started_callback();
110 }
111 else
112 {
113 play_start_pcm();
107 } 114 }
108
109 play_start_pcm();
110} 115}
111 116
112void pcm_play_dma_start(const void *addr, size_t size) 117void pcm_play_dma_start(const void *addr, size_t size)
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
index c8c1283d12..1f6eef435a 100644
--- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
@@ -26,6 +26,7 @@
26#include "ccm-imx31.h" 26#include "ccm-imx31.h"
27#include "sdma-imx31.h" 27#include "sdma-imx31.h"
28#include "mmu-imx31.h" 28#include "mmu-imx31.h"
29#include "pcm-internal.h"
29 30
30#define DMA_PLAY_CH_NUM 2 31#define DMA_PLAY_CH_NUM 2
31#define DMA_REC_CH_NUM 1 32#define DMA_REC_CH_NUM 1
@@ -105,6 +106,8 @@ static void play_dma_callback(void)
105 dma_play_bd.mode.command = TRANSFER_16BIT; 106 dma_play_bd.mode.command = TRANSFER_16BIT;
106 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; 107 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
107 sdma_channel_run(DMA_PLAY_CH_NUM); 108 sdma_channel_run(DMA_PLAY_CH_NUM);
109
110 pcm_play_dma_started_callback();
108} 111}
109 112
110void pcm_play_lock(void) 113void pcm_play_lock(void)
diff --git a/firmware/target/arm/pcm-mixer-armv4.c b/firmware/target/arm/pcm-mixer-armv4.c
new file mode 100644
index 0000000000..4818544d7b
--- /dev/null
+++ b/firmware/target/arm/pcm-mixer-armv4.c
@@ -0,0 +1,182 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Michael Sevakis
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#define MIXER_OPTIMIZED_WRITE_SAMPLES
23#define MIXER_OPTIMIZED_MIX_SAMPLES
24
25/* Mix channels' samples and apply gain factors */
26static FORCE_INLINE void mix_samples(void *out,
27 void *src0,
28 int32_t src0_amp,
29 void *src1,
30 int32_t src1_amp,
31 size_t size)
32{
33 if (src0_amp == MIX_AMP_UNITY && src1_amp == MIX_AMP_UNITY)
34 {
35 /* Both are unity amplitude */
36 int32_t l0, l1, h0, h1;
37 asm volatile (
38 "1: \n"
39 "ldrsh %4, [%1], #2 \n"
40 "ldrsh %5, [%2], #2 \n"
41 "ldrsh %6, [%1], #2 \n"
42 "ldrsh %7, [%2], #2 \n"
43 "add %4, %4, %5 \n"
44 "add %6, %6, %7 \n"
45 "mov %5, %4, asr #15 \n"
46 "teq %5, %5, asr #31 \n"
47 "eorne %4, %8, %4, asr #31 \n"
48 "mov %7, %6, asr #15 \n"
49 "teq %7, %7, asr #31 \n"
50 "eorne %6, %8, %6, asr #31 \n"
51 "subs %3, %3, #4 \n"
52 "and %4, %4, %8, lsr #16 \n"
53 "orr %6, %4, %6, lsl #16 \n"
54 "str %6, [%0], #4 \n"
55 "bhi 1b \n"
56 : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size),
57 "=&r"(l0), "=&r"(l1), "=&r"(h0), "=&r"(h1)
58 : "r"(0xffff7fff));
59 }
60 else if (src0_amp != MIX_AMP_UNITY && src1_amp != MIX_AMP_UNITY)
61 {
62 /* Neither are unity amplitude */
63 int32_t l0, l1, h0, h1;
64 asm volatile (
65 "1: \n"
66 "ldrsh %4, [%1], #2 \n"
67 "ldrsh %5, [%2], #2 \n"
68 "ldrsh %6, [%1], #2 \n"
69 "ldrsh %7, [%2], #2 \n"
70 "mul %4, %8, %4 \n"
71 "mul %5, %9, %5 \n"
72 "mul %6, %8, %6 \n"
73 "mul %7, %9, %7 \n"
74 "mov %4, %4, asr #16 \n"
75 "add %4, %4, %5, asr #16 \n"
76 "mov %6, %6, asr #16 \n"
77 "add %6, %6, %7, asr #16 \n"
78 "mov %5, %4, asr #15 \n"
79 "teq %5, %5, asr #31 \n"
80 "eorne %4, %10, %4, asr #31 \n"
81 "mov %7, %6, asr #15 \n"
82 "teq %7, %7, asr #31 \n"
83 "eorne %6, %10, %6, asr #31 \n"
84 "subs %3, %3, #4 \n"
85 "and %4, %4, %10, lsr #16 \n"
86 "orr %6, %4, %6, lsl #16 \n"
87 "str %6, [%0], #4 \n"
88 "bhi 1b \n"
89 : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size),
90 "=&r"(l0), "=&r"(l1), "=&r"(h0), "=&r"(h1)
91 : "r"(src0_amp), "r"(src1_amp), "r"(0xffff7fff));
92 }
93 else
94 {
95 /* One is unity amplitude */
96 if (src0_amp != MIX_AMP_UNITY)
97 {
98 /* Keep unity in src0, amp0 */
99 int16_t *src_tmp = src0;
100 src0 = src1;
101 src1 = src_tmp;
102 src1_amp = src0_amp;
103 src0_amp = MIX_AMP_UNITY;
104 }
105
106 int32_t l0, l1, h0, h1;
107 asm volatile (
108 "1: \n"
109 "ldrsh %4, [%1], #2 \n"
110 "ldrsh %5, [%2], #2 \n"
111 "ldrsh %6, [%1], #2 \n"
112 "ldrsh %7, [%2], #2 \n"
113 "mul %5, %8, %5 \n"
114 "mul %7, %8, %7 \n"
115 "add %4, %4, %5, asr #16 \n"
116 "add %6, %6, %7, asr #16 \n"
117 "mov %5, %4, asr #15 \n"
118 "teq %5, %5, asr #31 \n"
119 "eorne %4, %9, %4, asr #31 \n"
120 "mov %7, %6, asr #15 \n"
121 "teq %7, %7, asr #31 \n"
122 "eorne %6, %9, %6, asr #31 \n"
123 "subs %3, %3, #4 \n"
124 "and %4, %4, %9, lsr #16 \n"
125 "orr %6, %4, %6, lsl #16 \n"
126 "str %6, [%0], #4 \n"
127 "bhi 1b \n"
128 : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size),
129 "=&r"(l0), "=&r"(l1), "=&r"(h0), "=&r"(h1)
130 : "r"(src1_amp), "r"(0xffff7fff));
131 }
132}
133
134/* Write channel's samples and apply gain factor */
135static FORCE_INLINE void write_samples(void *out,
136 void *src,
137 int32_t amp,
138 size_t size)
139{
140 if (LIKELY(amp == MIX_AMP_UNITY))
141 {
142 /* Channel is unity amplitude */
143 asm volatile (
144 "ands r1, %2, #0x1f \n"
145 "beq 2f \n"
146 "1: \n"
147 "ldr r0, [%1], #4 \n"
148 "subs r1, r1, #4 \n"
149 "str r0, [%0], #4 \n"
150 "bne 1b \n"
151 "bics %2, %2, #0x1f \n"
152 "beq 3f \n"
153 "2: \n"
154 "ldmia %1!, { r0-r7 } \n"
155 "subs %2, %2, #32 \n"
156 "stmia %0!, { r0-r7 } \n"
157 "bhi 2b \n"
158 "3: \n"
159 : "+r"(out), "+r"(src), "+r"(size)
160 :
161 : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7");
162 }
163 else
164 {
165 /* Channel needs amplitude cut */
166 uint32_t l, h;
167 asm volatile (
168 "1: \n"
169 "ldrsh %3, [%1], #2 \n"
170 "ldrsh %4, [%1], #2 \n"
171 "subs %2, %2, #4 \n"
172 "mul %3, %5, %3 \n"
173 "mul %4, %5, %4 \n"
174 "and %4, %4, %6, lsl #16 \n"
175 "orr %4, %4, %3, lsr #16 \n"
176 "str %4, [%0], #4 \n"
177 "bhi 1b \n"
178 : "+r"(out), "+r"(src), "+r"(size),
179 "=&r"(l), "=&r"(h)
180 : "r"(amp), "r"(0xffffffffu));
181 }
182}
diff --git a/firmware/target/arm/pcm-mixer-armv5.c b/firmware/target/arm/pcm-mixer-armv5.c
new file mode 100644
index 0000000000..64f2c86f52
--- /dev/null
+++ b/firmware/target/arm/pcm-mixer-armv5.c
@@ -0,0 +1,106 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Michael Sevakis
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#define MIXER_OPTIMIZED_WRITE_SAMPLES
23#define MIXER_OPTIMIZED_MIX_SAMPLES
24
25/* Mix channels' samples and apply gain factors */
26static FORCE_INLINE void mix_samples(void *out,
27 void *src0,
28 int32_t src0_amp,
29 void *src1,
30 int32_t src1_amp,
31 size_t size)
32{
33 int32_t s0, s1, tmp;
34 asm volatile (
35 "1: \n"
36 "ldr %4, [%1], #4 \n"
37 "ldr %5, [%2], #4 \n"
38 "smulwb %6, %7, %4 \n"
39 "smulwt %4, %7, %4 \n"
40 "smlawb %6, %8, %5, %6 \n"
41 "smlawt %4, %8, %5, %4 \n"
42 "mov %5, %6, asr #15 \n"
43 "teq %5, %5, asr #31 \n"
44 "eorne %6, %9, %6, asr #31 \n"
45 "mov %5, %4, asr #15 \n"
46 "teq %5, %5, asr #31 \n"
47 "eorne %4, %9, %4, asr #31 \n"
48 "subs %3, %3, #4 \n"
49 "and %6, %6, %9, lsr #16 \n"
50 "orr %6, %6, %4, lsl #16 \n"
51 "str %6, [%0], #4 \n"
52 "bhi 1b \n"
53 : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size),
54 "=&r"(s0), "=&r"(s1), "=&r"(tmp)
55 : "r"(src0_amp), "r"(src1_amp), "r"(0xffff7fff));
56}
57
58/* Write channel's samples and apply gain factor */
59static FORCE_INLINE void write_samples(void *out,
60 void *src,
61 int32_t amp,
62 size_t size)
63{
64 if (LIKELY(amp == MIX_AMP_UNITY))
65 {
66 /* Channel is unity amplitude */
67 asm volatile (
68 "ands r1, %2, #0x1f \n"
69 "beq 2f \n"
70 "1: \n"
71 "ldr r0, [%1], #4 \n"
72 "subs r1, r1, #4 \n"
73 "str r0, [%0], #4 \n"
74 "bne 1b \n"
75 "bics %2, %2, #0x1f \n"
76 "beq 3f \n"
77 "2: \n"
78 "ldmia %1!, { r0-r7 } \n"
79 "subs %2, %2, #32 \n"
80 "stmia %0!, { r0-r7 } \n"
81 "bhi 2b \n"
82 "3: \n"
83 : "+r"(out), "+r"(src), "+r"(size)
84 :
85 : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7");
86 }
87 else
88 {
89 /* Channel needs amplitude cut */
90 uint32_t l, h;
91 asm volatile (
92 "1: \n"
93 "ldr %3, [%1], #4 \n"
94 "subs %2, %2, #4 \n"
95 "smulwt %4, %5, %3 \n"
96 "smulwb %3, %5, %3 \n"
97 "mov %4, %4, lsl #16 \n"
98 "mov %3, %3, lsl #16 \n"
99 "orr %4, %4, %3, lsr #16 \n"
100 "str %4, [%0], #4 \n"
101 "bhi 1b \n"
102 : "+r"(out), "+r"(src), "+r"(size),
103 "=&r"(l), "=&r"(h)
104 : "r"(amp));
105 }
106}
diff --git a/firmware/target/arm/pcm-mixer-armv6.c b/firmware/target/arm/pcm-mixer-armv6.c
new file mode 100644
index 0000000000..94eecd0f90
--- /dev/null
+++ b/firmware/target/arm/pcm-mixer-armv6.c
@@ -0,0 +1,118 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Michael Sevakis
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#define MIXER_OPTIMIZED_MIX_SAMPLES
22#define MIXER_OPTIMIZED_WRITE_SAMPLES
23
24/* Mix channels' samples and apply gain factors */
25static FORCE_INLINE void mix_samples(void *out,
26 void *src0,
27 int32_t src0_amp,
28 void *src1,
29 int32_t src1_amp,
30 size_t size)
31{
32 uint32_t s0, s1;
33
34 if (src0_amp == MIX_AMP_UNITY && src1_amp == MIX_AMP_UNITY)
35 {
36 /* Both are unity amplitude */
37 asm volatile (
38 "1: \n"
39 "ldr %4, [%1], #4 \n"
40 "ldr %5, [%2], #4 \n"
41 "subs %3, %3, #4 \n"
42 "qadd16 %5, %5, %4 \n"
43 "str %5, [%0], #4 \n"
44 "bhi 1b \n"
45 : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size),
46 "=&r"(s0), "=&r"(s1));
47 }
48 else
49 {
50 /* One or neither are unity amplitude */
51 uint32_t tmp;
52 asm volatile (
53 "1: \n"
54 "ldr %4, [%1], #4 \n"
55 "ldr %5, [%2], #4 \n"
56 "subs %3, %3, #4 \n"
57 "smulwb %6, %7, %4 \n"
58 "smulwt %4, %7, %4 \n"
59 "smlawb %6, %8, %5, %6 \n"
60 "smlawt %4, %8, %5, %4 \n"
61 "ssat %6, #16, %6 \n"
62 "ssat %4, #16, %4 \n"
63 "pkhbt %6, %6, %4, asl #16 \n"
64 "str %6, [%0], #4 \n"
65 "bhi 1b \n"
66 : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size),
67 "=&r"(s0), "=&r"(s1), "=&r"(tmp)
68 : "r"(src0_amp), "r"(src1_amp));
69 }
70}
71
72/* Write channel's samples and apply gain factor */
73static FORCE_INLINE void write_samples(void *out,
74 void *src,
75 int32_t amp,
76 size_t size)
77{
78 if (LIKELY(amp == MIX_AMP_UNITY))
79 {
80 /* Channel is unity amplitude */
81 asm volatile (
82 "ands r1, %2, #0x1f \n"
83 "beq 2f \n"
84 "1: \n"
85 "ldr r0, [%1], #4 \n"
86 "subs r1, r1, #4 \n"
87 "str r0, [%0], #4 \n"
88 "bne 1b \n"
89 "bics %2, %2, #0x1f \n"
90 "beq 3f \n"
91 "2: \n"
92 "ldmia %1!, { r0-r7 } \n"
93 "subs %2, %2, #32 \n"
94 "stmia %0!, { r0-r7 } \n"
95 "bhi 2b \n"
96 "3: \n"
97 : "+r"(out), "+r"(src), "+r"(size)
98 :
99 : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7");
100 }
101 else
102 {
103 /* Channel needs amplitude cut */
104 uint32_t s, tmp;
105 asm volatile(
106 "1: \n"
107 "ldr %3, [%1], #4 \n"
108 "subs %2, %2, #4 \n"
109 "smulwt %4, %5, %3 \n"
110 "smulwb %3, %5, %3 \n"
111 "pkhbt %4, %3, %4, asl #16 \n"
112 "str %4, [%0], #4 \n"
113 "bhi 1b \n"
114 : "+r"(out), "+r"(src), "+r"(size),
115 "=&r"(s), "=&r"(tmp)
116 : "r"(amp));
117 }
118}
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c
index c446f98fcf..704296d407 100644
--- a/firmware/target/arm/pcm-pp.c
+++ b/firmware/target/arm/pcm-pp.c
@@ -26,6 +26,7 @@
26#include "sound.h" 26#include "sound.h"
27#include "pcm.h" 27#include "pcm.h"
28#include "pcm_sampr.h" 28#include "pcm_sampr.h"
29#include "pcm-internal.h"
29 30
30/** DMA **/ 31/** DMA **/
31 32
@@ -115,6 +116,7 @@ void pcm_dma_apply_settings(void)
115/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ 116/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
116void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) 117void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void)
117{ 118{
119 bool new_buffer = false;
118 register size_t size; 120 register size_t size;
119 121
120 DMA0_STATUS; /* Clear any pending interrupt */ 122 DMA0_STATUS; /* Clear any pending interrupt */
@@ -136,9 +138,14 @@ void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void)
136 /* Set the new DMA values and activate channel */ 138 /* Set the new DMA values and activate channel */
137 DMA0_RAM_ADDR = dma_play_data.addr; 139 DMA0_RAM_ADDR = dma_play_data.addr;
138 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START; 140 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
141
142 if (new_buffer)
143 pcm_play_dma_started_callback();
139 return; 144 return;
140 } 145 }
141 146
147 new_buffer = true;
148
142 /* Buffer empty. Try to get more. */ 149 /* Buffer empty. Try to get more. */
143 pcm_play_get_more_callback((void **)&dma_play_data.addr, 150 pcm_play_get_more_callback((void **)&dma_play_data.addr,
144 &dma_play_data.size); 151 &dma_play_data.size);
@@ -181,8 +188,9 @@ void fiq_playback(void)
181 * r0-r3 and r12 is a working register. 188 * r0-r3 and r12 is a working register.
182 */ 189 */
183 asm volatile ( 190 asm volatile (
184 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */ 191 "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */
185 192
193 "mov r4, #0 \n" /* Was the callback called? */
186#if CONFIG_CPU == PP5002 194#if CONFIG_CPU == PP5002
187 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */ 195 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */
188 "ldr r12, [r12] \n" 196 "ldr r12, [r12] \n"
@@ -212,16 +220,13 @@ void fiq_playback(void)
212 "tst r1, #1 \n" /* two samples (one word) left? */ 220 "tst r1, #1 \n" /* two samples (one word) left? */
213 "ldrne r12, [r8], #4 \n" /* load two samples */ 221 "ldrne r12, [r8], #4 \n" /* load two samples */
214 "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ 222 "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */
215
216 "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */
217 "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */
218#elif SAMPLE_SIZE == 32 223#elif SAMPLE_SIZE == 32
219 ".check_fifo: \n" 224 ".check_fifo: \n"
220 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ 225 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */
221 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */ 226 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */
222 227
223 "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */ 228 "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */
224 "beq .exit \n" /* no complete pair? -> exit */ 229 "beq .fifo_fill_complete \n" /* no complete pair? -> exit */
225 "cmp r1, r9, lsr #2 \n" /* number of words from source */ 230 "cmp r1, r9, lsr #2 \n" /* number of words from source */
226 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ 231 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */
227 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ 232 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */
@@ -234,11 +239,23 @@ void fiq_playback(void)
234 "subs r1, r1, #1 \n" /* one more loop? */ 239 "subs r1, r1, #1 \n" /* one more loop? */
235 "bgt .fifo_loop \n" /* yes, continue */ 240 "bgt .fifo_loop \n" /* yes, continue */
236 241
242 ".fifo_fill_complete: \n"
243#endif
244 "cmp r4, #0 \n" /* If fill came after get_more... */
245 "beq .still_old_buffer \n"
246 "mov r4, #0 \n"
247 "ldr r2, =pcm_play_dma_started \n"
248 "ldrne r2, [r2] \n"
249 "cmp r2, #0 \n"
250 "movne lr, pc \n"
251 "bxne r2 \n"
252
253 ".still_old_buffer: \n"
237 "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */ 254 "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */
238 "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */ 255 "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */
239#endif
240 256
241 ".more_data: \n" 257 ".more_data: \n"
258 "mov r4, #1 \n" /* Remember we did this */
242 "ldr r2, =pcm_play_get_more_callback \n" 259 "ldr r2, =pcm_play_get_more_callback \n"
243 "mov r0, r11 \n" /* r0 = &p */ 260 "mov r0, r11 \n" /* r0 = &p */
244 "add r1, r11, #4 \n" /* r1 = &size */ 261 "add r1, r11, #4 \n" /* r1 = &size */
@@ -250,7 +267,7 @@ void fiq_playback(void)
250 267
251 ".exit: \n" /* (r9=0 if stopping, look above) */ 268 ".exit: \n" /* (r9=0 if stopping, look above) */
252 "stmia r11, { r8-r9 } \n" /* save p and size */ 269 "stmia r11, { r8-r9 } \n" /* save p and size */
253 "ldmfd sp!, { r0-r3, lr } \n" 270 "ldmfd sp!, { r0-r4, lr } \n"
254 "subs pc, lr, #4 \n" /* FIQ specific return sequence */ 271 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
255 ".ltorg \n" 272 ".ltorg \n"
256 : /* These must only be integers! No regs */ 273 : /* These must only be integers! No regs */
@@ -264,6 +281,8 @@ void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR;
264/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ 281/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
265void fiq_playback(void) 282void fiq_playback(void)
266{ 283{
284 bool new_buffer = false;
285
267#if CONFIG_CPU == PP5002 286#if CONFIG_CPU == PP5002
268 inl(0xcf001040); 287 inl(0xcf001040);
269#endif 288#endif
@@ -271,6 +290,10 @@ void fiq_playback(void)
271 do { 290 do {
272 while (dma_play_data.size > 0) { 291 while (dma_play_data.size > 0) {
273 if (IIS_TX_FREE_COUNT < 2) { 292 if (IIS_TX_FREE_COUNT < 2) {
293 if (new_buffer) {
294 new_buffer = false;
295 pcm_play_dma_started_callback();
296 }
274 return; 297 return;
275 } 298 }
276#if SAMPLE_SIZE == 16 299#if SAMPLE_SIZE == 16
@@ -282,9 +305,15 @@ void fiq_playback(void)
282 dma_play_data.size -= 4; 305 dma_play_data.size -= 4;
283 } 306 }
284 307
308 if (new_buffer) {
309 new_buffer = false;
310 pcm_play_dma_started_callback();
311 }
312
285 /* p is empty, get some more data */ 313 /* p is empty, get some more data */
286 pcm_play_get_more_callback((void **)&dma_play_data.addr, 314 pcm_play_get_more_callback((void **)&dma_play_data.addr,
287 &dma_play_data.size); 315 &dma_play_data.size);
316 new_buffer = true;
288 } while (dma_play_data.size); 317 } while (dma_play_data.size);
289 318
290 /* No more data */ 319 /* No more data */
diff --git a/firmware/target/arm/pcm-telechips.c b/firmware/target/arm/pcm-telechips.c
index 851ebee7de..aff43171f6 100644
--- a/firmware/target/arm/pcm-telechips.c
+++ b/firmware/target/arm/pcm-telechips.c
@@ -27,6 +27,7 @@
27#include "sound.h" 27#include "sound.h"
28#include "i2s.h" 28#include "i2s.h"
29#include "pcm.h" 29#include "pcm.h"
30#include "pcm-internal.h"
30 31
31struct dma_data 32struct dma_data
32{ 33{
@@ -247,6 +248,8 @@ void fiq_handler(void)
247 * r0-r3 and r12 is a working register. 248 * r0-r3 and r12 is a working register.
248 */ 249 */
249 asm volatile ( 250 asm volatile (
251 "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */
252 "mov r4, #0 \n" /* Was the callback called? */
250#if defined(CPU_TCC780X) 253#if defined(CPU_TCC780X)
251 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */ 254 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
252 "ldr r9, =0xf3001004 \n" /* CREQ */ 255 "ldr r9, =0xf3001004 \n" /* CREQ */
@@ -279,33 +282,41 @@ void fiq_handler(void)
279 "sub r9, r9, #0x10 \n" /* 4 words written */ 282 "sub r9, r9, #0x10 \n" /* 4 words written */
280 "stmia r11, { r8-r9 } \n" /* save p and size */ 283 "stmia r11, { r8-r9 } \n" /* save p and size */
281 284
285 "cmp r4, #0 \n" /* Callback called? */
286 "beq .exit \n"
287 /* "mov r4, #0 \n" If get_more could be called multiple times! */
288 "ldr r2, =pcm_play_dma_started\n"
289 "ldr r2, [r2] \n"
290 "cmp r2, #0 \n"
291 "blxne r2 \n"
292
282 ".exit: \n" 293 ".exit: \n"
294 "ldmfd sp!, { r0-r4, lr } \n"
283 "subs pc, lr, #4 \n" /* FIQ specific return sequence */ 295 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
284 296
285 ".more_data: \n" 297 ".more_data: \n"
286 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */ 298 "mov r4, #1 \n" /* Remember we got more data in this FIQ */
287 "ldr r2, =pcm_play_get_more_callback \n" 299 "ldr r2, =pcm_play_get_more_callback \n"
288 "mov r0, r11 \n" /* r0 = &p */ 300 "mov r0, r11 \n" /* r0 = &p */
289 "add r1, r11, #4 \n" /* r1 = &size */ 301 "add r1, r11, #4 \n" /* r1 = &size */
290 "blx r2 \n" /* call pcm_play_get_more_callback */ 302 "blx r2 \n" /* call pcm_play_get_more_callback */
291 "ldmia r11, { r8-r9 } \n" /* load new p and size */ 303 "ldmia r11, { r8-r9 } \n" /* load new p and size */
292 "cmp r9, #0x10 \n" /* did we actually get enough data? */ 304 "cmp r9, #0x10 \n" /* did we actually get enough data? */
293 "ldmfd sp!, { r0-r3, lr } \n"
294 "bpl .fill_fifo \n" /* not stop and enough? refill */ 305 "bpl .fill_fifo \n" /* not stop and enough? refill */
295 "b .exit \n" 306 "b .exit \n"
296 ".ltorg \n" 307 ".ltorg \n"
297 ); 308 );
298} 309}
299#else /* C version for reference */ 310#else /* C version for reference */
300void fiq_handler(void) ICODE_ATTR __attribute__((naked)); 311void fiq_handler(void) ICODE_ATTR;
301void fiq_handler(void) 312void fiq_handler(void)
302{ 313{
303 asm volatile( "stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */ 314 register bool new_buffer = false;
304 "sub sp, sp, #8 \n"); /* Reserve stack */
305 315
306 if (dma_play_data.size < 16) 316 if (dma_play_data.size < 16)
307 { 317 {
308 /* p is empty, get some more data */ 318 /* p is empty, get some more data */
319 new_buffer = true;
309 pcm_play_get_more_callback((void**)&dma_play_data.p, 320 pcm_play_get_more_callback((void**)&dma_play_data.p,
310 &dma_play_data.size); 321 &dma_play_data.size);
311 } 322 }
@@ -327,9 +338,8 @@ void fiq_handler(void)
327 /* Clear FIQ status */ 338 /* Clear FIQ status */
328 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; 339 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK;
329 340
330 asm volatile( "add sp, sp, #8 \n" /* Cleanup stack */ 341 if (new_buffer)
331 "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */ 342 pcm_play_dma_started_callback();
332 "subs pc, lr, #4 \n"); /* Return from FIQ */
333} 343}
334#endif 344#endif
335 345
diff --git a/firmware/target/arm/pnx0101/pcm-pnx0101.c b/firmware/target/arm/pnx0101/pcm-pnx0101.c
index 9d4ffbd773..d4c17454ed 100644
--- a/firmware/target/arm/pnx0101/pcm-pnx0101.c
+++ b/firmware/target/arm/pnx0101/pcm-pnx0101.c
@@ -21,6 +21,7 @@
21#include "system.h" 21#include "system.h"
22#include "audio.h" 22#include "audio.h"
23#include "string.h" 23#include "string.h"
24#include "pcm-internal.h"
24 25
25#define DMA_BUF_SAMPLES 0x100 26#define DMA_BUF_SAMPLES 0x100
26 27
@@ -63,6 +64,8 @@ static inline void fill_dma_buf(int offset)
63 64
64 if (pcm_playing && !pcm_paused) 65 if (pcm_playing && !pcm_paused)
65 { 66 {
67 bool new_buffer =false;
68
66 do 69 do
67 { 70 {
68 int count; 71 int count;
@@ -102,10 +105,20 @@ static inline void fill_dma_buf(int offset)
102 count--; 105 count--;
103 } 106 }
104 p = tmp_p; 107 p = tmp_p;
108
109 if (new_buffer)
110 {
111 new_buffer = false;
112 pcm_play_dma_started_callback();
113 }
114
105 if (l >= lend) 115 if (l >= lend)
106 return; 116 return;
107 117
108 pcm_play_get_more_callback((void**)&p, &p_size); 118 pcm_play_get_more_callback((void**)&p, &p_size);
119
120 if (p_size)
121 new_buffer = true;
109 } 122 }
110 while (p_size); 123 while (p_size);
111 } 124 }
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
index c1c9017fbb..33194ae5d9 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
@@ -25,6 +25,7 @@
25#include "audio.h" 25#include "audio.h"
26#include "sound.h" 26#include "sound.h"
27#include "file.h" 27#include "file.h"
28#include "pcm-internal.h"
28 29
29/* PCM interrupt routine lockout */ 30/* PCM interrupt routine lockout */
30static struct 31static struct
@@ -235,6 +236,8 @@ void fiq_handler(void)
235 236
236 /* Re-Activate the channel */ 237 /* Re-Activate the channel */
237 DMASKTRIG2 = 0x2; 238 DMASKTRIG2 = 0x2;
239
240 pcm_play_dma_started_callback();
238} 241}
239 242
240size_t pcm_get_bytes_waiting(void) 243size_t pcm_get_bytes_waiting(void)
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c
index 01b177da6c..eea4c58e4b 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c
@@ -99,14 +99,15 @@ void TIMER3(void)
99 INTPND = TIMER3_MASK; 99 INTPND = TIMER3_MASK;
100} 100}
101 101
102void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) 102void beep_play(unsigned int frequency, unsigned int duration,
103 unsigned int amplitude)
103{ 104{
104 #define TIMER3_TICK_SEC (TIMER_FREQ / TIMER234_PRESCALE) 105 #define TIMER3_TICK_SEC (TIMER_FREQ / TIMER234_PRESCALE)
105 106
106 unsigned long tcnt, tcmp; 107 unsigned long tcnt, tcmp;
107 int oldstatus; 108 int oldstatus;
108 109
109 if (amplitude <= 0) 110 if (frequency == 0 || duration == 0 || amplitude == 0)
110 { 111 {
111 beep_stop(); /* won't hear it anyway */ 112 beep_stop(); /* won't hear it anyway */
112 return; 113 return;
diff --git a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
index 8a6b62f31f..0c69c1e6d3 100644
--- a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
+++ b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
@@ -26,6 +26,7 @@
26#include "audio.h" 26#include "audio.h"
27#include "sound.h" 27#include "sound.h"
28#include "file.h" 28#include "file.h"
29#include "pcm-internal.h"
29 30
30/* PCM interrupt routine lockout */ 31/* PCM interrupt routine lockout */
31static struct 32static struct
@@ -275,6 +276,8 @@ void fiq_handler(void)
275 276
276 /* Re-Activate the channel */ 277 /* Re-Activate the channel */
277 DMASKTRIG2 = 0x2; 278 DMASKTRIG2 = 0x2;
279
280 pcm_play_dma_started_callback();
278} 281}
279 282
280size_t pcm_get_bytes_waiting(void) 283size_t pcm_get_bytes_waiting(void)
diff --git a/firmware/target/arm/s5l8700/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c
index 08086c37d8..14c515ec47 100644
--- a/firmware/target/arm/s5l8700/pcm-s5l8700.c
+++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c
@@ -27,6 +27,7 @@
27#include "panic.h" 27#include "panic.h"
28#include "audiohw.h" 28#include "audiohw.h"
29#include "pcm.h" 29#include "pcm.h"
30#include "pcm-internal.h"
30#include "pcm_sampr.h" 31#include "pcm_sampr.h"
31#include "dma-target.h" 32#include "dma-target.h"
32#include "mmu-arm.h" 33#include "mmu-arm.h"
@@ -100,6 +101,7 @@ void pcm_play_unlock(void)
100void INT_DMA(void) ICODE_ATTR; 101void INT_DMA(void) ICODE_ATTR;
101void INT_DMA(void) 102void INT_DMA(void)
102{ 103{
104 bool new_buffer = false;
103 DMACOM0 = 7; 105 DMACOM0 = 7;
104 while (!(DMACON0 & (1 << 18))) 106 while (!(DMACON0 & (1 << 18)))
105 { 107 {
@@ -112,8 +114,12 @@ void INT_DMA(void)
112 } 114 }
113 else 115 else
114 { 116 {
115 if (!nextsize) pcm_play_get_more_callback((void**)&nextbuf, &nextsize); 117 if (!nextsize)
116 if (!nextsize) break; 118 {
119 pcm_play_get_more_callback((void**)&nextbuf, &nextsize);
120 if (!nextsize) break;
121 new_buffer = true;
122 }
117 queuedsize = MIN(sizeof(dblbuf), nextsize / 2); 123 queuedsize = MIN(sizeof(dblbuf), nextsize / 2);
118 nextsize -= queuedsize; 124 nextsize -= queuedsize;
119 queuedbuf = nextbuf + nextsize; 125 queuedbuf = nextbuf + nextsize;
@@ -124,7 +130,14 @@ void INT_DMA(void)
124 clean_dcache(); 130 clean_dcache();
125 DMACOM0 = 4; 131 DMACOM0 = 4;
126 DMACOM0 = 7; 132 DMACOM0 = 7;
133
134 if (new_buffer)
135 {
136 pcm_play_dma_started_callback();
137 new_buffer = false;
138 }
127 } 139 }
140
128} 141}
129 142
130void pcm_play_dma_start(const void* addr, size_t size) 143void pcm_play_dma_start(const void* addr, size_t size)
diff --git a/firmware/target/arm/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c
index c0498a9ce2..dbadf3bac0 100644
--- a/firmware/target/arm/s5l8702/pcm-s5l8702.c
+++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c
@@ -27,6 +27,7 @@
27#include "panic.h" 27#include "panic.h"
28#include "audiohw.h" 28#include "audiohw.h"
29#include "pcm.h" 29#include "pcm.h"
30#include "pcm-internal.h"
30#include "pcm_sampr.h" 31#include "pcm_sampr.h"
31#include "mmu-arm.h" 32#include "mmu-arm.h"
32#include "pcm-target.h" 33#include "pcm-target.h"
@@ -113,6 +114,8 @@ void INT_DMAC0C0(void)
113 DMAC0C0CONFIG = 0x8a81; 114 DMAC0C0CONFIG = 0x8a81;
114 } 115 }
115 else DMAC0C0NEXTLLI = pcm_lli; 116 else DMAC0C0NEXTLLI = pcm_lli;
117
118 pcm_play_dma_started_callback();
116} 119}
117 120
118void pcm_play_dma_start(const void* addr, size_t size) 121void pcm_play_dma_start(const void* addr, size_t size)
diff --git a/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c b/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c
index 3c54ce81fb..5ec62cf876 100644
--- a/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c
+++ b/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c
@@ -27,6 +27,7 @@
27#include "dm320.h" 27#include "dm320.h"
28#include "audiohw.h" 28#include "audiohw.h"
29#include "dsp-target.h" 29#include "dsp-target.h"
30#include "pcm-internal.h"
30 31
31void pcm_play_dma_init(void) 32void pcm_play_dma_init(void)
32{ 33{
diff --git a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
index fb94adae71..90c342e868 100644
--- a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
+++ b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
@@ -28,6 +28,7 @@
28#include "dsp-target.h" 28#include "dsp-target.h"
29#include "dsp/ipc.h" 29#include "dsp/ipc.h"
30#include "mmu-arm.h" 30#include "mmu-arm.h"
31#include "pcm-internal.h"
31 32
32/* This is global to save some latency when pcm_play_dma_get_peak_buffer is 33/* This is global to save some latency when pcm_play_dma_get_peak_buffer is
33 * called. 34 * called.
@@ -178,6 +179,8 @@ void DSPHINT(void)
178 179
179 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", 180 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx",
180 (unsigned long)start, (unsigned long)sdem_addr); 181 (unsigned long)start, (unsigned long)sdem_addr);
182
183 pcm_play_dma_started_callback();
181 } 184 }
182 185
183 break; 186 break;
diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c
index a06542c31f..85eeaec815 100644
--- a/firmware/target/coldfire/pcm-coldfire.c
+++ b/firmware/target/coldfire/pcm-coldfire.c
@@ -28,6 +28,7 @@
28#if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT) 28#if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT)
29#include "spdif.h" 29#include "spdif.h"
30#endif 30#endif
31#include "pcm-internal.h"
31 32
32#define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \ 33#define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \
33 (IIS_PLAY & (7 << 8)) | \ 34 (IIS_PLAY & (7 << 8)) | \
@@ -318,6 +319,9 @@ void DMA0(void)
318 SAR0 = (unsigned long)start; /* Source address */ 319 SAR0 = (unsigned long)start; /* Source address */
319 BCR0 = size; /* Bytes to transfer */ 320 BCR0 = size; /* Bytes to transfer */
320 or_l(DMA_EEXT | DMA_INT, &DCR0); /* per request and int ON */ 321 or_l(DMA_EEXT | DMA_INT, &DCR0); /* per request and int ON */
322
323 /* Call buffer callback */
324 pcm_play_dma_started_callback();
321 } 325 }
322 /* else inished playing */ 326 /* else inished playing */
323} /* DMA0 */ 327} /* DMA0 */
diff --git a/firmware/target/coldfire/pcm-mixer-coldfire.c b/firmware/target/coldfire/pcm-mixer-coldfire.c
new file mode 100644
index 0000000000..d8318fffaf
--- /dev/null
+++ b/firmware/target/coldfire/pcm-mixer-coldfire.c
@@ -0,0 +1,134 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Michael Sevakis
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#define MIXER_OPTIMIZED_MIX_SAMPLES
23#define MIXER_OPTIMIZED_WRITE_SAMPLES
24static struct emac_context
25{
26 unsigned long r[4];
27} emac_context IBSS_ATTR;
28
29/* Save emac context affected in ISR */
30static FORCE_INLINE void save_emac_context(void)
31{
32 asm volatile (
33 "move.l %%macsr, %%d0 \n"
34 "move.l %%accext01, %%d1 \n"
35 "movclr.l %%acc0, %%a0 \n"
36 "movclr.l %%acc1, %%a1 \n"
37 "movem.l %%d0-%%d1/%%a0-%%a1, (%0) \n"
38 :
39 : "a"(&emac_context)
40 : "d0", "d1", "a0", "a1");
41}
42
43/* Restore emac context affected in ISR */
44static FORCE_INLINE void restore_emac_context(void)
45{
46 asm volatile (
47 "movem.l (%0), %%d0-%%d1/%%a0-%%a1 \n"
48 "move.l %%a1, %%acc1 \n"
49 "move.l %%a0, %%acc0 \n"
50 "move.l %%d1, %%accext01 \n"
51 "move.l %%d0, %%macsr \n"
52 :
53 : "a"(&emac_context)
54 : "d0", "d1", "a0", "a1");
55}
56
57/* Mix channels' samples and apply gain factors */
58static FORCE_INLINE void mix_samples(void *out,
59 void *src0,
60 int32_t src0_amp,
61 void *src1,
62 int32_t src1_amp,
63 size_t size)
64{
65 uint32_t s0, s1, s2, s3;
66 save_emac_context();
67 coldfire_set_macsr(EMAC_ROUND | EMAC_SATURATE);
68
69 asm volatile (
70 "move.l (%1)+, %5 \n"
71 "1: \n"
72 "movea.w %5, %4 \n"
73 "asr.l %10, %5 \n"
74 "mac.l %4, %8, %%acc0 \n"
75 "mac.l %5, %8, (%2)+, %5, %%acc1 \n"
76 "movea.w %5, %4 \n"
77 "asr.l %10, %5 \n"
78 "mac.l %4, %9, %%acc0 \n"
79 "mac.l %5, %9, (%1)+, %5, %%acc1 \n"
80 "movclr.l %%acc0, %6 \n"
81 "movclr.l %%acc1, %7 \n"
82 "swap.w %6 \n"
83 "move.w %6, %7 \n"
84 "move.l %7, (%0)+ \n"
85 "subq.l #4, %3 \n"
86 "bhi.b 1b \n"
87 : "+a"(out), "+a"(src0), "+a"(src1), "+d"(size),
88 "=&a"(s0), "=&d"(s1), "=&d"(s2), "=&d"(s3)
89 : "r"(src0_amp), "r"(src1_amp), "d"(16)
90 );
91
92 restore_emac_context();
93}
94
95/* Write channel's samples and apply gain factor */
96static FORCE_INLINE void write_samples(void *out,
97 void *src,
98 int32_t amp,
99 size_t size)
100{
101 if (LIKELY(amp == MIX_AMP_UNITY))
102 {
103 /* Channel is unity amplitude */
104 memcpy(out, src, size);
105 }
106 else
107 {
108 /* Channel needs amplitude cut */
109 uint32_t s0, s1, s2, s3;
110 save_emac_context();
111 coldfire_set_macsr(EMAC_ROUND | EMAC_SATURATE);
112
113 asm volatile (
114 "move.l (%1)+, %4 \n"
115 "1: \n"
116 "movea.w %4, %3 \n"
117 "asr.l %8, %4 \n"
118 "mac.l %3, %7, %%acc0 \n"
119 "mac.l %4, %7, (%1)+, %4, %%acc1 \n"
120 "movclr.l %%acc0, %5 \n"
121 "movclr.l %%acc1, %6 \n"
122 "swap.w %5 \n"
123 "move.w %5, %6 \n"
124 "move.l %6, (%0)+ \n"
125 "subq.l #4, %2 \n"
126 "bhi.b 1b \n"
127 : "+a"(out), "+a"(src), "+d"(size),
128 "=&a"(s0), "=&d"(s1), "=&d"(s2), "=&d"(s3)
129 : "r"(amp), "d"(16)
130 );
131
132 restore_emac_context();
133 }
134}
diff --git a/firmware/target/hosted/android/pcm-android.c b/firmware/target/hosted/android/pcm-android.c
index 88792cd76f..cbd6cb3228 100644
--- a/firmware/target/hosted/android/pcm-android.c
+++ b/firmware/target/hosted/android/pcm-android.c
@@ -23,14 +23,18 @@
23#include <stdbool.h> 23#include <stdbool.h>
24#define _SYSTEM_WITH_JNI /* for getJavaEnvironment */ 24#define _SYSTEM_WITH_JNI /* for getJavaEnvironment */
25#include <system.h> 25#include <system.h>
26#include <pthread.h>
26#include "debug.h" 27#include "debug.h"
27#include "pcm.h" 28#include "pcm.h"
29#include "pcm-internal.h"
28 30
29extern JNIEnv *env_ptr; 31extern JNIEnv *env_ptr;
30 32
31/* infos about our pcm chunks */ 33/* infos about our pcm chunks */
32static size_t pcm_data_size; 34static size_t pcm_data_size;
33static char *pcm_data_start; 35static char *pcm_data_start;
36static int audio_locked = 0;
37static pthread_mutex_t audio_lock_mutex = PTHREAD_MUTEX_INITIALIZER;
34 38
35/* cache frequently called methods */ 39/* cache frequently called methods */
36static jmethodID play_pause_method; 40static jmethodID play_pause_method;
@@ -42,6 +46,20 @@ static jobject RockboxPCM_instance;
42 46
43 47
44/* 48/*
49 * mutex lock/unlock wrappers neatness' sake
50 */
51static inline void lock_audio(void)
52{
53 pthread_mutex_lock(&audio_lock_mutex);
54}
55
56static inline void unlock_audio(void)
57{
58 pthread_mutex_unlock(&audio_lock_mutex);
59}
60
61
62/*
45 * write pcm samples to the hardware. Calls AudioTrack.write directly (which 63 * write pcm samples to the hardware. Calls AudioTrack.write directly (which
46 * is usually a blocking call) 64 * is usually a blocking call)
47 * 65 *
@@ -54,10 +72,17 @@ JNIEXPORT jint JNICALL
54Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this, 72Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this,
55 jbyteArray temp_array, jint max_size) 73 jbyteArray temp_array, jint max_size)
56{ 74{
75 bool new_buffer = false;
76
77 lock_audio();
78
57 jint left = max_size; 79 jint left = max_size;
58 80
59 if (!pcm_data_size) /* get some initial data */ 81 if (!pcm_data_size) /* get some initial data */
82 {
83 new_buffer = true;
60 pcm_play_get_more_callback((void**) &pcm_data_start, &pcm_data_size); 84 pcm_play_get_more_callback((void**) &pcm_data_start, &pcm_data_size);
85 }
61 86
62 while(left > 0 && pcm_data_size) 87 while(left > 0 && pcm_data_size)
63 { 88 {
@@ -70,23 +95,49 @@ Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this,
70 95
71 ret = (*env)->CallIntMethod(env, this, write_method, 96 ret = (*env)->CallIntMethod(env, this, write_method,
72 temp_array, 0, transfer_size); 97 temp_array, 0, transfer_size);
98
99 if (new_buffer)
100 {
101 new_buffer = false;
102 pcm_play_dma_started_callback();
103
104 /* NOTE: might need to release the mutex and sleep here if the
105 buffer is shorter than the required buffer (like pcm-sdl.c) to
106 have the mixer clocked at a regular interval */
107 }
108
73 if (ret < 0) 109 if (ret < 0)
110 {
111 unlock_audio();
74 return ret; 112 return ret;
113 }
75 114
76 if (pcm_data_size == 0) /* need new data */ 115 if (pcm_data_size == 0) /* need new data */
116 {
117 new_buffer = true;
77 pcm_play_get_more_callback((void**)&pcm_data_start, &pcm_data_size); 118 pcm_play_get_more_callback((void**)&pcm_data_start, &pcm_data_size);
119 }
78 else /* increment data pointer and feed more */ 120 else /* increment data pointer and feed more */
79 pcm_data_start += transfer_size; 121 pcm_data_start += transfer_size;
80 } 122 }
123
124 if (new_buffer && pcm_data_size)
125 pcm_play_dma_started_callback();
126
127 unlock_audio();
81 return max_size - left; 128 return max_size - left;
82} 129}
83 130
84void pcm_play_lock(void) 131void pcm_play_lock(void)
85{ 132{
133 if (++audio_locked == 1)
134 lock_audio();
86} 135}
87 136
88void pcm_play_unlock(void) 137void pcm_play_unlock(void)
89{ 138{
139 if (--audio_locked == 0)
140 unlock_audio();
90} 141}
91 142
92void pcm_dma_apply_settings(void) 143void pcm_dma_apply_settings(void)
@@ -153,8 +204,6 @@ void pcm_play_dma_init(void)
153 set_volume_method = e->GetMethodID(env_ptr, RockboxPCM_class, "set_volume", "(I)V"); 204 set_volume_method = e->GetMethodID(env_ptr, RockboxPCM_class, "set_volume", "(I)V");
154 stop_method = e->GetMethodID(env_ptr, RockboxPCM_class, "stop", "()V"); 205 stop_method = e->GetMethodID(env_ptr, RockboxPCM_class, "stop", "()V");
155 write_method = e->GetMethodID(env_ptr, RockboxPCM_class, "write", "([BII)I"); 206 write_method = e->GetMethodID(env_ptr, RockboxPCM_class, "write", "([BII)I");
156 /* get initial pcm data, if any */
157 pcm_play_get_more_callback((void*)&pcm_data_start, &pcm_data_size);
158} 207}
159 208
160void pcm_postinit(void) 209void pcm_postinit(void)
@@ -173,6 +222,7 @@ void pcm_shutdown(void)
173 JNIEnv e = *env_ptr; 222 JNIEnv e = *env_ptr;
174 jmethodID release = e->GetMethodID(env_ptr, RockboxPCM_class, "release", "()V"); 223 jmethodID release = e->GetMethodID(env_ptr, RockboxPCM_class, "release", "()V");
175 e->CallVoidMethod(env_ptr, RockboxPCM_instance, release); 224 e->CallVoidMethod(env_ptr, RockboxPCM_instance, release);
225 pthread_mutex_destroy(&audio_lock_mutex);
176} 226}
177 227
178/* Due to limitations of default_event_handler(), parameters gets swallowed when 228/* Due to limitations of default_event_handler(), parameters gets swallowed when
diff --git a/firmware/target/hosted/maemo/pcm-gstreamer.c b/firmware/target/hosted/maemo/pcm-gstreamer.c
index e3e40f0619..6069801fba 100644
--- a/firmware/target/hosted/maemo/pcm-gstreamer.c
+++ b/firmware/target/hosted/maemo/pcm-gstreamer.c
@@ -54,6 +54,7 @@
54#endif 54#endif
55 55
56#include "pcm.h" 56#include "pcm.h"
57#include "pcm-internal.h"
57#include "pcm_sampr.h" 58#include "pcm_sampr.h"
58 59
59/*#define LOGF_ENABLE*/ 60/*#define LOGF_ENABLE*/
@@ -182,6 +183,8 @@ static void feed_data(GstElement * appsrc, guint size_hint, void *unused)
182 183
183 if (ret != 0) 184 if (ret != 0)
184 DEBUGF("push-buffer error result: %d\n", ret); 185 DEBUGF("push-buffer error result: %d\n", ret);
186
187 pcm_play_dma_started_callback();
185 } else 188 } else
186 { 189 {
187 DEBUGF("feed_data: No Data.\n"); 190 DEBUGF("feed_data: No Data.\n");
diff --git a/firmware/target/hosted/sdl/pcm-sdl.c b/firmware/target/hosted/sdl/pcm-sdl.c
index 7780083b99..dfdd90f29b 100644
--- a/firmware/target/hosted/sdl/pcm-sdl.c
+++ b/firmware/target/hosted/sdl/pcm-sdl.c
@@ -30,6 +30,7 @@
30#include "sound.h" 30#include "sound.h"
31#include "audiohw.h" 31#include "audiohw.h"
32#include "system.h" 32#include "system.h"
33#include "panic.h"
33 34
34#ifdef HAVE_RECORDING 35#ifdef HAVE_RECORDING
35#include "audiohw.h" 36#include "audiohw.h"
@@ -39,6 +40,7 @@
39#endif 40#endif
40 41
41#include "pcm.h" 42#include "pcm.h"
43#include "pcm-internal.h"
42#include "pcm_sampr.h" 44#include "pcm_sampr.h"
43 45
44/*#define LOGF_ENABLE*/ 46/*#define LOGF_ENABLE*/
@@ -71,15 +73,19 @@ static struct pcm_udata
71 73
72static SDL_AudioSpec obtained; 74static SDL_AudioSpec obtained;
73static SDL_AudioCVT cvt; 75static SDL_AudioCVT cvt;
76static int audio_locked = 0;
77static SDL_mutex *audio_lock;
74 78
75void pcm_play_lock(void) 79void pcm_play_lock(void)
76{ 80{
77 SDL_LockAudio(); 81 if (++audio_locked == 1)
82 SDL_LockMutex(audio_lock);
78} 83}
79 84
80void pcm_play_unlock(void) 85void pcm_play_unlock(void)
81{ 86{
82 SDL_UnlockAudio(); 87 if (--audio_locked == 0)
88 SDL_UnlockMutex(audio_lock);
83} 89}
84 90
85static void pcm_dma_apply_settings_nolock(void) 91static void pcm_dma_apply_settings_nolock(void)
@@ -227,14 +233,19 @@ static void write_to_soundcard(struct pcm_udata *udata)
227static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len) 233static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
228{ 234{
229 logf("sdl_audio_callback: len %d, pcm %d\n", len, pcm_data_size); 235 logf("sdl_audio_callback: len %d, pcm %d\n", len, pcm_data_size);
236
237 bool new_buffer = false;
230 udata->stream = stream; 238 udata->stream = stream;
231 239
240 SDL_LockMutex(audio_lock);
241
232 /* Write what we have in the PCM buffer */ 242 /* Write what we have in the PCM buffer */
233 if (pcm_data_size > 0) 243 if (pcm_data_size > 0)
234 goto start; 244 goto start;
235 245
236 /* Audio card wants more? Get some more then. */ 246 /* Audio card wants more? Get some more then. */
237 while (len > 0) { 247 while (len > 0) {
248 new_buffer = true;
238 pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size); 249 pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size);
239 start: 250 start:
240 if (pcm_data_size != 0) { 251 if (pcm_data_size != 0) {
@@ -246,6 +257,28 @@ static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
246 udata->num_in *= pcm_sample_bytes; 257 udata->num_in *= pcm_sample_bytes;
247 udata->num_out *= pcm_sample_bytes; 258 udata->num_out *= pcm_sample_bytes;
248 259
260
261 if (new_buffer)
262 {
263 new_buffer = false;
264 pcm_play_dma_started_callback();
265
266 if ((size_t)len > udata->num_out)
267 {
268 int delay = pcm_data_size*250 / pcm_sampr - 1;
269
270 if (delay > 0)
271 {
272 SDL_UnlockMutex(audio_lock);
273 SDL_Delay(delay);
274 SDL_LockMutex(audio_lock);
275
276 if (!pcm_is_playing())
277 break;
278 }
279 }
280 }
281
249 pcm_data += udata->num_in; 282 pcm_data += udata->num_in;
250 pcm_data_size -= udata->num_in; 283 pcm_data_size -= udata->num_in;
251 udata->stream += udata->num_out; 284 udata->stream += udata->num_out;
@@ -255,6 +288,8 @@ static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
255 break; 288 break;
256 } 289 }
257 } 290 }
291
292 SDL_UnlockMutex(audio_lock);
258} 293}
259 294
260const void * pcm_play_dma_get_peak_buffer(int *count) 295const void * pcm_play_dma_get_peak_buffer(int *count)
@@ -320,6 +355,14 @@ void pcm_play_dma_init(void)
320 return; 355 return;
321 } 356 }
322 357
358 audio_lock = SDL_CreateMutex();
359
360 if (!audio_lock)
361 {
362 panicf("Could not create audio_lock\n");
363 return;
364 }
365
323 SDL_AudioSpec wanted_spec; 366 SDL_AudioSpec wanted_spec;
324#ifdef DEBUG 367#ifdef DEBUG
325 udata.debug = NULL; 368 udata.debug = NULL;
diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
index 5cd9c33e18..cfc3c9ef8e 100644
--- a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
+++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
@@ -25,6 +25,7 @@
25#include "audio.h" 25#include "audio.h"
26#include "sound.h" 26#include "sound.h"
27#include "pcm.h" 27#include "pcm.h"
28#include "pcm-internal.h"
28#include "jz4740.h" 29#include "jz4740.h"
29 30
30 31
@@ -109,6 +110,7 @@ static inline void play_dma_callback(void)
109 { 110 {
110 set_dma(start, size); 111 set_dma(start, size);
111 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; 112 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN;
113 pcm_play_dma_started_callback();
112 } 114 }
113} 115}
114 116