diff options
Diffstat (limited to 'firmware/target')
21 files changed, 754 insertions, 25 deletions
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 | ||
112 | void pcm_play_dma_start(const void *addr, size_t size) | 117 | void 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 | ||
110 | void pcm_play_lock(void) | 113 | void 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 */ | ||
26 | static 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 */ | ||
135 | static 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 */ | ||
26 | static 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 */ | ||
59 | static 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 */ | ||
25 | static 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 */ | ||
73 | static 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 */ |
116 | void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) | 117 | void 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 */ |
265 | void fiq_playback(void) | 282 | void 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 | ||
31 | struct dma_data | 32 | struct 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 */ |
300 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | 311 | void fiq_handler(void) ICODE_ATTR; |
301 | void fiq_handler(void) | 312 | void 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 */ |
30 | static struct | 31 | static 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 | ||
240 | size_t pcm_get_bytes_waiting(void) | 243 | size_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 | ||
102 | void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) | 102 | void 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 */ |
31 | static struct | 32 | static 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 | ||
280 | size_t pcm_get_bytes_waiting(void) | 283 | size_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) | |||
100 | void INT_DMA(void) ICODE_ATTR; | 101 | void INT_DMA(void) ICODE_ATTR; |
101 | void INT_DMA(void) | 102 | void 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 | ||
130 | void pcm_play_dma_start(const void* addr, size_t size) | 143 | void 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 | ||
118 | void pcm_play_dma_start(const void* addr, size_t size) | 121 | void 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 | ||
31 | void pcm_play_dma_init(void) | 32 | void 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 | ||
24 | static struct emac_context | ||
25 | { | ||
26 | unsigned long r[4]; | ||
27 | } emac_context IBSS_ATTR; | ||
28 | |||
29 | /* Save emac context affected in ISR */ | ||
30 | static 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 */ | ||
44 | static 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 */ | ||
58 | static 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 */ | ||
96 | static 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 | ||
29 | extern JNIEnv *env_ptr; | 31 | extern JNIEnv *env_ptr; |
30 | 32 | ||
31 | /* infos about our pcm chunks */ | 33 | /* infos about our pcm chunks */ |
32 | static size_t pcm_data_size; | 34 | static size_t pcm_data_size; |
33 | static char *pcm_data_start; | 35 | static char *pcm_data_start; |
36 | static int audio_locked = 0; | ||
37 | static pthread_mutex_t audio_lock_mutex = PTHREAD_MUTEX_INITIALIZER; | ||
34 | 38 | ||
35 | /* cache frequently called methods */ | 39 | /* cache frequently called methods */ |
36 | static jmethodID play_pause_method; | 40 | static 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 | */ | ||
51 | static inline void lock_audio(void) | ||
52 | { | ||
53 | pthread_mutex_lock(&audio_lock_mutex); | ||
54 | } | ||
55 | |||
56 | static 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 | |||
54 | Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this, | 72 | Java_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 | ||
84 | void pcm_play_lock(void) | 131 | void pcm_play_lock(void) |
85 | { | 132 | { |
133 | if (++audio_locked == 1) | ||
134 | lock_audio(); | ||
86 | } | 135 | } |
87 | 136 | ||
88 | void pcm_play_unlock(void) | 137 | void pcm_play_unlock(void) |
89 | { | 138 | { |
139 | if (--audio_locked == 0) | ||
140 | unlock_audio(); | ||
90 | } | 141 | } |
91 | 142 | ||
92 | void pcm_dma_apply_settings(void) | 143 | void 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 | ||
160 | void pcm_postinit(void) | 209 | void 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 | ||
72 | static SDL_AudioSpec obtained; | 74 | static SDL_AudioSpec obtained; |
73 | static SDL_AudioCVT cvt; | 75 | static SDL_AudioCVT cvt; |
76 | static int audio_locked = 0; | ||
77 | static SDL_mutex *audio_lock; | ||
74 | 78 | ||
75 | void pcm_play_lock(void) | 79 | void pcm_play_lock(void) |
76 | { | 80 | { |
77 | SDL_LockAudio(); | 81 | if (++audio_locked == 1) |
82 | SDL_LockMutex(audio_lock); | ||
78 | } | 83 | } |
79 | 84 | ||
80 | void pcm_play_unlock(void) | 85 | void pcm_play_unlock(void) |
81 | { | 86 | { |
82 | SDL_UnlockAudio(); | 87 | if (--audio_locked == 0) |
88 | SDL_UnlockMutex(audio_lock); | ||
83 | } | 89 | } |
84 | 90 | ||
85 | static void pcm_dma_apply_settings_nolock(void) | 91 | static void pcm_dma_apply_settings_nolock(void) |
@@ -227,14 +233,19 @@ static void write_to_soundcard(struct pcm_udata *udata) | |||
227 | static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len) | 233 | static 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 | ||
260 | const void * pcm_play_dma_get_peak_buffer(int *count) | 295 | const 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 | ||