diff options
-rw-r--r-- | firmware/SOURCES | 1 | ||||
-rw-r--r-- | firmware/drivers/audio/wm8985.c | 111 | ||||
-rw-r--r-- | firmware/export/wm8985.h | 6 | ||||
-rw-r--r-- | firmware/sound.c | 4 | ||||
-rw-r--r-- | firmware/target/arm/tcc780x/cowond2/audio-cowond2.c | 92 | ||||
-rw-r--r-- | firmware/target/arm/tcc780x/cowond2/power-cowond2.c | 3 | ||||
-rw-r--r-- | firmware/target/arm/tcc780x/crt0.S | 8 | ||||
-rw-r--r-- | firmware/target/arm/tcc780x/pcm-tcc780x.c | 249 |
8 files changed, 383 insertions, 91 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES index 1a01fd5af4..2e2559477f 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES | |||
@@ -1022,6 +1022,7 @@ target/arm/tcc780x/timer-tcc780x.c | |||
1022 | target/arm/wmcodec-telechips.c | 1022 | target/arm/wmcodec-telechips.c |
1023 | target/arm/tcc780x/debug-tcc780x.c | 1023 | target/arm/tcc780x/debug-tcc780x.c |
1024 | target/arm/tcc780x/pcm-tcc780x.c | 1024 | target/arm/tcc780x/pcm-tcc780x.c |
1025 | target/arm/tcc780x/cowond2/audio-cowond2.c | ||
1025 | #endif /* BOOTLOADER */ | 1026 | #endif /* BOOTLOADER */ |
1026 | #endif /* SIMULATOR */ | 1027 | #endif /* SIMULATOR */ |
1027 | #endif /* COWON_D2 */ | 1028 | #endif /* COWON_D2 */ |
diff --git a/firmware/drivers/audio/wm8985.c b/firmware/drivers/audio/wm8985.c index 6f8d65998b..411bd97c59 100644 --- a/firmware/drivers/audio/wm8985.c +++ b/firmware/drivers/audio/wm8985.c | |||
@@ -86,18 +86,6 @@ | |||
86 | #define OUT4MIX 0x39 | 86 | #define OUT4MIX 0x39 |
87 | #define BIASCTL 0x3d | 87 | #define BIASCTL 0x3d |
88 | 88 | ||
89 | /* Register settings for the supported samplerates: */ | ||
90 | #define WM8985_8000HZ 0x4d | ||
91 | #define WM8985_12000HZ 0x61 | ||
92 | #define WM8985_16000HZ 0x55 | ||
93 | #define WM8985_22050HZ 0x77 | ||
94 | #define WM8985_24000HZ 0x79 | ||
95 | #define WM8985_32000HZ 0x59 | ||
96 | #define WM8985_44100HZ 0x63 | ||
97 | #define WM8985_48000HZ 0x41 | ||
98 | #define WM8985_88200HZ 0x7f | ||
99 | #define WM8985_96000HZ 0x5d | ||
100 | |||
101 | const struct sound_settings_info audiohw_settings[] = { | 89 | const struct sound_settings_info audiohw_settings[] = { |
102 | [SOUND_VOLUME] = {"dB", 0, 1, -58, 6, -25}, | 90 | [SOUND_VOLUME] = {"dB", 0, 1, -58, 6, -25}, |
103 | [SOUND_BASS] = {"dB", 0, 1, -12, 12, 0}, | 91 | [SOUND_BASS] = {"dB", 0, 1, -12, 12, 0}, |
@@ -136,19 +124,6 @@ int tenthdb2master(int db) | |||
136 | } | 124 | } |
137 | } | 125 | } |
138 | 126 | ||
139 | /* convert tenth of dB volume (-780..0) to mixer volume register value */ | ||
140 | int tenthdb2mixer(int db) | ||
141 | { | ||
142 | if (db < -660) /* 1.5 dB steps */ | ||
143 | return (2640 - db) / 15; | ||
144 | else if (db < -600) /* 0.75 dB steps */ | ||
145 | return (990 - db) * 2 / 15; | ||
146 | else if (db < -460) /* 0.5 dB steps */ | ||
147 | return (460 - db) / 5; | ||
148 | else /* 0.25 dB steps */ | ||
149 | return -db * 2 / 5; | ||
150 | } | ||
151 | |||
152 | /* Silently enable / disable audio output */ | 127 | /* Silently enable / disable audio output */ |
153 | void audiohw_enable_output(bool enable) | 128 | void audiohw_enable_output(bool enable) |
154 | { | 129 | { |
@@ -157,32 +132,53 @@ void audiohw_enable_output(bool enable) | |||
157 | /* TODO: reset the I2S controller into known state */ | 132 | /* TODO: reset the I2S controller into known state */ |
158 | //i2s_reset(); | 133 | //i2s_reset(); |
159 | 134 | ||
160 | /* TODO: Review the power-up sequence to prevent pops (see datasheet) */ | 135 | wmcodec_write(RESET, 0x1ff); /* Reset */ |
161 | 136 | ||
162 | wmcodec_write(RESET, 0x1ff); /*Reset*/ | 137 | wmcodec_write(BIASCTL, 0x100); /* BIASCUT = 1 */ |
138 | wmcodec_write(OUTCTRL, 0x6); /* Thermal shutdown */ | ||
163 | 139 | ||
164 | wmcodec_write(PWRMGMT1, 0x2b); | 140 | wmcodec_write(PWRMGMT1, 0x8); /* BIASEN = 1 */ |
165 | wmcodec_write(PWRMGMT2, 0x180); | 141 | |
142 | /* Volume zero, mute all outputs */ | ||
143 | wmcodec_write(LOUT1VOL, 0x140); | ||
144 | wmcodec_write(ROUT1VOL, 0x140); | ||
145 | wmcodec_write(LOUT2VOL, 0x140); | ||
146 | wmcodec_write(ROUT2VOL, 0x140); | ||
147 | wmcodec_write(OUT3MIX, 0x40); | ||
148 | wmcodec_write(OUT4MIX, 0x40); | ||
149 | |||
150 | /* DAC softmute, automute, 128OSR */ | ||
151 | wmcodec_write(DACCTRL, 0x4c); | ||
152 | |||
153 | wmcodec_write(OUT4ADC, 0x2); /* POBCTRL = 1 */ | ||
154 | |||
155 | /* Enable output, DAC and mixer */ | ||
166 | wmcodec_write(PWRMGMT3, 0x6f); | 156 | wmcodec_write(PWRMGMT3, 0x6f); |
157 | wmcodec_write(PWRMGMT2, 0x180); | ||
158 | wmcodec_write(PWRMGMT1, 0xd); | ||
159 | wmcodec_write(LOUTMIX, 0x1); | ||
160 | wmcodec_write(ROUTMIX, 0x1); | ||
167 | 161 | ||
168 | wmcodec_write(AINTFCE, 0x10); | 162 | /* Disable clock since we're acting as slave to the SoC */ |
169 | wmcodec_write(CLKCTRL, 0x49); | 163 | wmcodec_write(CLKGEN, 0x0); |
164 | wmcodec_write(AINTFCE, 0x10); /* 16-bit, I2S format */ | ||
170 | 165 | ||
171 | wmcodec_write(OUTCTRL, 1); | 166 | wmcodec_write(LDACVOL, 0x1ff); /* Full DAC digital vol */ |
167 | wmcodec_write(RDACVOL, 0x1ff); | ||
172 | 168 | ||
173 | /* The iPod can handle multiple frequencies, but fix at 44.1KHz | 169 | wmcodec_write(OUT4ADC, 0x0); /* POBCTRL = 0 */ |
174 | for now */ | 170 | |
175 | audiohw_set_sample_rate(WM8985_44100HZ); | 171 | sleep(HZ/2); |
176 | 172 | ||
177 | wmcodec_write(LOUTMIX,0x1); /* Enable mixer */ | ||
178 | wmcodec_write(ROUTMIX,0x1); /* Enable mixer */ | ||
179 | audiohw_mute(0); | 173 | audiohw_mute(0); |
180 | } else { | 174 | } |
175 | else | ||
176 | { | ||
181 | audiohw_mute(1); | 177 | audiohw_mute(1); |
182 | } | 178 | } |
183 | } | 179 | } |
184 | 180 | ||
185 | void audiohw_set_master_vol(int vol_l, int vol_r) | 181 | void audiohw_set_headphone_vol(int vol_l, int vol_r) |
186 | { | 182 | { |
187 | /* OUT1 */ | 183 | /* OUT1 */ |
188 | wmcodec_write(LOUT1VOL, 0x080 | vol_l); | 184 | wmcodec_write(LOUT1VOL, 0x080 | vol_l); |
@@ -196,12 +192,6 @@ void audiohw_set_lineout_vol(int vol_l, int vol_r) | |||
196 | wmcodec_write(ROUT2VOL, 0x100 | vol_r); | 192 | wmcodec_write(ROUT2VOL, 0x100 | vol_r); |
197 | } | 193 | } |
198 | 194 | ||
199 | void audiohw_set_mixer_vol(int channel1, int channel2) | ||
200 | { | ||
201 | (void)channel1; | ||
202 | (void)channel2; | ||
203 | } | ||
204 | |||
205 | void audiohw_set_bass(int value) | 195 | void audiohw_set_bass(int value) |
206 | { | 196 | { |
207 | eq1_reg = (eq1_reg & ~EQ_GAIN_MASK) | EQ_GAIN_VALUE(value); | 197 | eq1_reg = (eq1_reg & ~EQ_GAIN_MASK) | EQ_GAIN_VALUE(value); |
@@ -231,14 +221,14 @@ void audiohw_mute(bool mute) | |||
231 | if (mute) | 221 | if (mute) |
232 | { | 222 | { |
233 | /* Set DACMU = 1 to soft-mute the audio DACs. */ | 223 | /* Set DACMU = 1 to soft-mute the audio DACs. */ |
234 | wmcodec_write(DACCTRL, 0x40); | 224 | wmcodec_write(DACCTRL, 0x4c); |
235 | } else { | 225 | } else { |
236 | /* Set DACMU = 0 to soft-un-mute the audio DACs. */ | 226 | /* Set DACMU = 0 to soft-un-mute the audio DACs. */ |
237 | wmcodec_write(DACCTRL, 0x0); | 227 | wmcodec_write(DACCTRL, 0xc); |
238 | } | 228 | } |
239 | } | 229 | } |
240 | 230 | ||
241 | /* Nice shutdown of WM8758 codec */ | 231 | /* Nice shutdown of WM8985 codec */ |
242 | void audiohw_close(void) | 232 | void audiohw_close(void) |
243 | { | 233 | { |
244 | audiohw_mute(1); | 234 | audiohw_mute(1); |
@@ -250,32 +240,13 @@ void audiohw_close(void) | |||
250 | wmcodec_write(PWRMGMT2, 0x40); | 240 | wmcodec_write(PWRMGMT2, 0x40); |
251 | } | 241 | } |
252 | 242 | ||
253 | /* Change the order of the noise shaper, 5th order is recommended above 32kHz */ | ||
254 | void audiohw_set_nsorder(int order) | ||
255 | { | ||
256 | (void)order; | ||
257 | } | ||
258 | |||
259 | /* Note: Disable output before calling this function */ | 243 | /* Note: Disable output before calling this function */ |
260 | void audiohw_set_sample_rate(int sampling_control) | 244 | void audiohw_set_sample_rate(int sampling_control) |
261 | { | 245 | { |
262 | /**** We force 44.1KHz for now. ****/ | 246 | /* Currently the WM8985 acts as slave to the SoC I2S controller, so no |
247 | setup is needed here. This seems to be in contrast to every other WM | ||
248 | driver in Rockbox, so this may need to change in the future. */ | ||
263 | (void)sampling_control; | 249 | (void)sampling_control; |
264 | |||
265 | /* set clock div */ | ||
266 | wmcodec_write(CLKCTRL, 1 | (0 << 2) | (2 << 5)); | ||
267 | |||
268 | /* setup PLL for MHZ=11.2896 */ | ||
269 | wmcodec_write(PLLN, (1 << 4) | 0x7); | ||
270 | wmcodec_write(PLLK1, 0x21); | ||
271 | wmcodec_write(PLLK2, 0x161); | ||
272 | wmcodec_write(PLLK3, 0x26); | ||
273 | |||
274 | /* set clock div */ | ||
275 | wmcodec_write(CLKCTRL, 1 | (1 << 2) | (2 << 5) | (1 << 8)); | ||
276 | |||
277 | /* set srate */ | ||
278 | wmcodec_write(SRATECTRL, (0 << 1)); | ||
279 | } | 250 | } |
280 | 251 | ||
281 | #ifdef HAVE_RECORDING | 252 | #ifdef HAVE_RECORDING |
diff --git a/firmware/export/wm8985.h b/firmware/export/wm8985.h index f59bc771bd..56c7f8d405 100644 --- a/firmware/export/wm8985.h +++ b/firmware/export/wm8985.h | |||
@@ -27,12 +27,8 @@ | |||
27 | #define AUDIOHW_CAPS (BASS_CAP | TREBLE_CAP | BASS_CUTOFF_CAP | TREBLE_CUTOFF_CAP) | 27 | #define AUDIOHW_CAPS (BASS_CAP | TREBLE_CAP | BASS_CUTOFF_CAP | TREBLE_CUTOFF_CAP) |
28 | 28 | ||
29 | extern int tenthdb2master(int db); | 29 | extern int tenthdb2master(int db); |
30 | extern int tenthdb2mixer(int db); | ||
31 | 30 | ||
32 | extern void audiohw_set_master_vol(int vol_l, int vol_r); | 31 | extern void audiohw_set_headphone_vol(int vol_l, int vol_r); |
33 | extern void audiohw_set_lineout_vol(int vol_l, int vol_r); | 32 | extern void audiohw_set_lineout_vol(int vol_l, int vol_r); |
34 | extern void audiohw_set_mixer_vol(int channel1, int channel2); | ||
35 | extern void audiohw_set_nsorder(int order); | ||
36 | extern void audiohw_set_sample_rate(int sampling_control); | ||
37 | 33 | ||
38 | #endif /* _WM8985_H */ | 34 | #endif /* _WM8985_H */ |
diff --git a/firmware/sound.c b/firmware/sound.c index 7c862db515..6f02b270e7 100644 --- a/firmware/sound.c +++ b/firmware/sound.c | |||
@@ -301,11 +301,11 @@ static void set_prescaled_volume(void) | |||
301 | audiohw_set_master_vol(tenthdb2master(l), tenthdb2master(r)); | 301 | audiohw_set_master_vol(tenthdb2master(l), tenthdb2master(r)); |
302 | #if defined(HAVE_WM8975) || defined(HAVE_WM8758) \ | 302 | #if defined(HAVE_WM8975) || defined(HAVE_WM8758) \ |
303 | || (defined(HAVE_WM8751) && !defined(MROBE_100)) \ | 303 | || (defined(HAVE_WM8751) && !defined(MROBE_100)) \ |
304 | || defined(HAVE_TSC2100) | 304 | || defined(HAVE_TSC2100) || defined(HAVE_WM8985) |
305 | audiohw_set_lineout_vol(tenthdb2master(0), tenthdb2master(0)); | 305 | audiohw_set_lineout_vol(tenthdb2master(0), tenthdb2master(0)); |
306 | #endif | 306 | #endif |
307 | 307 | ||
308 | #elif defined(HAVE_TLV320) || defined(HAVE_WM8978) | 308 | #elif defined(HAVE_TLV320) || defined(HAVE_WM8978) || defined(HAVE_WM8985) |
309 | audiohw_set_headphone_vol(tenthdb2master(l), tenthdb2master(r)); | 309 | audiohw_set_headphone_vol(tenthdb2master(l), tenthdb2master(r)); |
310 | #endif | 310 | #endif |
311 | } | 311 | } |
diff --git a/firmware/target/arm/tcc780x/cowond2/audio-cowond2.c b/firmware/target/arm/tcc780x/cowond2/audio-cowond2.c new file mode 100644 index 0000000000..f73528247c --- /dev/null +++ b/firmware/target/arm/tcc780x/cowond2/audio-cowond2.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Michael Sevakis | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "system.h" | ||
20 | #include "cpu.h" | ||
21 | #include "audio.h" | ||
22 | #include "sound.h" | ||
23 | |||
24 | int audio_channels = 2; | ||
25 | int audio_output_source = AUDIO_SRC_PLAYBACK; | ||
26 | |||
27 | void audio_set_output_source(int source) | ||
28 | { | ||
29 | int oldmode = set_fiq_status(FIQ_DISABLED); | ||
30 | |||
31 | if ((unsigned)source >= AUDIO_NUM_SOURCES) | ||
32 | source = AUDIO_SRC_PLAYBACK; | ||
33 | |||
34 | audio_output_source = source; | ||
35 | |||
36 | /*if (source != AUDIO_SRC_PLAYBACK) | ||
37 | IISCONFIG |= (1 << 29);*/ | ||
38 | |||
39 | set_fiq_status(oldmode); | ||
40 | } | ||
41 | |||
42 | void audio_input_mux(int source, unsigned flags) | ||
43 | { | ||
44 | static int last_source = AUDIO_SRC_PLAYBACK; | ||
45 | static bool last_recording = false; | ||
46 | bool recording = flags & SRCF_RECORDING; | ||
47 | |||
48 | switch (source) | ||
49 | { | ||
50 | default: /* playback - no recording */ | ||
51 | source = AUDIO_SRC_PLAYBACK; | ||
52 | case AUDIO_SRC_PLAYBACK: | ||
53 | audio_channels = 2; | ||
54 | if (source != last_source) | ||
55 | { | ||
56 | /*audiohw_set_monitor(false); | ||
57 | audiohw_disable_recording();*/ | ||
58 | } | ||
59 | break; | ||
60 | |||
61 | case AUDIO_SRC_MIC: /* recording only */ | ||
62 | audio_channels = 1; | ||
63 | if (source != last_source) | ||
64 | { | ||
65 | /*audiohw_set_monitor(false); | ||
66 | audiohw_enable_recording(true); /. source mic */ | ||
67 | } | ||
68 | break; | ||
69 | |||
70 | case AUDIO_SRC_FMRADIO: /* recording and playback */ | ||
71 | audio_channels = 2; | ||
72 | |||
73 | if (source == last_source && recording == last_recording) | ||
74 | break; | ||
75 | |||
76 | last_recording = recording; | ||
77 | |||
78 | if (recording) | ||
79 | { | ||
80 | /*audiohw_set_monitor(false); | ||
81 | audiohw_enable_recording(false);*/ | ||
82 | } | ||
83 | else | ||
84 | { | ||
85 | /*audiohw_disable_recording(); | ||
86 | audiohw_set_monitor(true); /. line 1 analog audio path */ | ||
87 | } | ||
88 | break; | ||
89 | } /* end switch */ | ||
90 | |||
91 | last_source = source; | ||
92 | } /* audio_input_mux */ | ||
diff --git a/firmware/target/arm/tcc780x/cowond2/power-cowond2.c b/firmware/target/arm/tcc780x/cowond2/power-cowond2.c index c7441256c5..504f4e6eb8 100644 --- a/firmware/target/arm/tcc780x/cowond2/power-cowond2.c +++ b/firmware/target/arm/tcc780x/cowond2/power-cowond2.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "power.h" | 22 | #include "power.h" |
23 | #include "pcf50606.h" | 23 | #include "pcf50606.h" |
24 | #include "button-target.h" | 24 | #include "button-target.h" |
25 | #include "tuner.h" | ||
25 | 26 | ||
26 | #ifndef SIMULATOR | 27 | #ifndef SIMULATOR |
27 | 28 | ||
@@ -69,7 +70,6 @@ void EXT3(void) | |||
69 | unsigned char data[3]; /* 0 = INT1, 1 = INT2, 2 = INT3 */ | 70 | unsigned char data[3]; /* 0 = INT1, 1 = INT2, 2 = INT3 */ |
70 | 71 | ||
71 | /* Clear pending interrupts from pcf50606 */ | 72 | /* Clear pending interrupts from pcf50606 */ |
72 | int fiq_status = disable_fiq_save(); | ||
73 | pcf50606_read_multiple(0x02, data, 3); | 73 | pcf50606_read_multiple(0x02, data, 3); |
74 | 74 | ||
75 | if (data[0] & 0x04) | 75 | if (data[0] & 0x04) |
@@ -87,7 +87,6 @@ void EXT3(void) | |||
87 | /* Touchscreen event, do something about it */ | 87 | /* Touchscreen event, do something about it */ |
88 | button_set_touch_available(); | 88 | button_set_touch_available(); |
89 | } | 89 | } |
90 | restore_fiq(fiq_status); | ||
91 | } | 90 | } |
92 | #endif | 91 | #endif |
93 | 92 | ||
diff --git a/firmware/target/arm/tcc780x/crt0.S b/firmware/target/arm/tcc780x/crt0.S index cef27f1051..aaa9517329 100644 --- a/firmware/target/arm/tcc780x/crt0.S +++ b/firmware/target/arm/tcc780x/crt0.S | |||
@@ -100,10 +100,18 @@ copied_start: | |||
100 | mov r0,#0xd2 | 100 | mov r0,#0xd2 |
101 | msr cpsr, r0 | 101 | msr cpsr, r0 |
102 | ldr sp, =irq_stack | 102 | ldr sp, =irq_stack |
103 | |||
103 | /* Set up stack for FIQ mode */ | 104 | /* Set up stack for FIQ mode */ |
104 | mov r0,#0xd1 | 105 | mov r0,#0xd1 |
105 | msr cpsr, r0 | 106 | msr cpsr, r0 |
106 | ldr sp, =fiq_stack | 107 | ldr sp, =fiq_stack |
108 | |||
109 | /* Load the banked FIQ mode registers with useful values here. | ||
110 | These values will be used in the FIQ handler in pcm-tcc780x.c */ | ||
111 | .equ DADO_BASE, 0xF0059020 | ||
112 | |||
113 | ldr r10, =DADO_BASE | ||
114 | ldr r11, =dma_play_data | ||
107 | 115 | ||
108 | /* Let abort and undefined modes use IRQ stack */ | 116 | /* Let abort and undefined modes use IRQ stack */ |
109 | mov r0,#0xd7 | 117 | mov r0,#0xd7 |
diff --git a/firmware/target/arm/tcc780x/pcm-tcc780x.c b/firmware/target/arm/tcc780x/pcm-tcc780x.c index a9312749c8..7db775d4c7 100644 --- a/firmware/target/arm/tcc780x/pcm-tcc780x.c +++ b/firmware/target/arm/tcc780x/pcm-tcc780x.c | |||
@@ -7,7 +7,8 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2007 by Karl Kurbjun | 10 | * Copyright (C) 2006 by Michael Sevakis |
11 | * Copyright (C) 2008 by Rob Purchase | ||
11 | * | 12 | * |
12 | * All files in this archive are subject to the GNU General Public License. | 13 | * All files in this archive are subject to the GNU General Public License. |
13 | * See the file COPYING in the source tree root for full license agreement. | 14 | * See the file COPYING in the source tree root for full license agreement. |
@@ -16,52 +17,176 @@ | |||
16 | * KIND, either express or implied. | 17 | * KIND, either express or implied. |
17 | * | 18 | * |
18 | ****************************************************************************/ | 19 | ****************************************************************************/ |
20 | #include <stdlib.h> | ||
19 | #include "system.h" | 21 | #include "system.h" |
20 | #include "kernel.h" | 22 | #include "kernel.h" |
21 | #include "logf.h" | 23 | #include "logf.h" |
22 | #include "audio.h" | 24 | #include "audio.h" |
23 | #include "sound.h" | 25 | #include "sound.h" |
24 | #include "file.h" | 26 | #include "pcm.h" |
27 | |||
28 | struct dma_data | ||
29 | { | ||
30 | /* NOTE: The order of size and p is important if you use assembler | ||
31 | optimised fiq handler, so don't change it. */ | ||
32 | uint16_t *p; | ||
33 | size_t size; | ||
34 | #if NUM_CORES > 1 | ||
35 | unsigned core; | ||
36 | #endif | ||
37 | int locked; | ||
38 | int state; | ||
39 | }; | ||
40 | |||
41 | /**************************************************************************** | ||
42 | ** Playback DMA transfer | ||
43 | **/ | ||
44 | struct dma_data dma_play_data SHAREDBSS_ATTR = | ||
45 | { | ||
46 | /* Initialize to a locked, stopped state */ | ||
47 | .p = NULL, | ||
48 | .size = 0, | ||
49 | #if NUM_CORES > 1 | ||
50 | .core = 0x00, | ||
51 | #endif | ||
52 | .locked = 0, | ||
53 | .state = 0 | ||
54 | }; | ||
55 | |||
56 | static unsigned long pcm_freq SHAREDDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */ | ||
25 | 57 | ||
26 | void pcm_postinit(void) | 58 | void pcm_postinit(void) |
27 | { | 59 | { |
60 | /*audiohw_postinit();*/ | ||
61 | pcm_apply_settings(); | ||
28 | } | 62 | } |
29 | 63 | ||
30 | const void * pcm_play_dma_get_peak_buffer(int *count) | 64 | const void * pcm_play_dma_get_peak_buffer(int *count) |
31 | { | 65 | { |
32 | (void) count; | 66 | unsigned long addr = (unsigned long)dma_play_data.p; |
33 | return 0; | 67 | size_t cnt = dma_play_data.size; |
68 | *count = cnt >> 2; | ||
69 | return (void *)((addr + 2) & ~3); | ||
34 | } | 70 | } |
35 | 71 | ||
36 | void pcm_play_dma_init(void) | 72 | void pcm_play_dma_init(void) |
37 | { | 73 | { |
74 | /* Set DAI clock divided from PLL0 (192MHz). | ||
75 | The best approximation of 256*44.1kHz is 11.291MHz. */ | ||
76 | BCLKCTR &= ~DEV_DAI; | ||
77 | PCLK_DAI = (1<<28) | 61682; /* DCO mode */ | ||
78 | BCLKCTR |= DEV_DAI; | ||
79 | |||
80 | /* Enable DAI block in Master mode, 256fs->32fs, 16bit LSB */ | ||
81 | DAMR = 0x3c8e80; | ||
82 | DAVC = 0x0; /* Digital Volume = max */ | ||
83 | |||
84 | /* Set DAI interrupts as FIQs */ | ||
85 | IRQSEL = ~(DAI_RX_IRQ_MASK | DAI_TX_IRQ_MASK); | ||
86 | |||
87 | pcm_set_frequency(SAMPR_44); | ||
88 | |||
89 | /* Initialize default register values. */ | ||
90 | audiohw_init(); | ||
91 | |||
92 | /* Power on */ | ||
93 | audiohw_enable_output(true); | ||
94 | |||
95 | /* Unmute the master channel (DAC should be at zero point now). */ | ||
96 | audiohw_mute(false); | ||
97 | |||
98 | dma_play_data.size = 0; | ||
99 | #if NUM_CORES > 1 | ||
100 | dma_play_data.core = 0; /* no core in control */ | ||
101 | #endif | ||
38 | } | 102 | } |
39 | 103 | ||
40 | void pcm_apply_settings(void) | 104 | void pcm_apply_settings(void) |
41 | { | 105 | { |
106 | pcm_curr_sampr = pcm_freq; | ||
42 | } | 107 | } |
43 | 108 | ||
44 | void pcm_set_frequency(unsigned int frequency) | 109 | void pcm_set_frequency(unsigned int frequency) |
45 | { | 110 | { |
46 | (void) frequency; | 111 | (void) frequency; |
112 | pcm_freq = HW_SAMPR_DEFAULT; | ||
113 | } | ||
114 | |||
115 | static void play_start_pcm(void) | ||
116 | { | ||
117 | pcm_apply_settings(); | ||
118 | |||
119 | DAMR &= ~(1<<14); /* disable tx */ | ||
120 | dma_play_data.state = 1; | ||
121 | |||
122 | if (dma_play_data.size >= 16) | ||
123 | { | ||
124 | DADO_L(0) = *dma_play_data.p++; | ||
125 | DADO_R(0) = *dma_play_data.p++; | ||
126 | DADO_L(1) = *dma_play_data.p++; | ||
127 | DADO_R(1) = *dma_play_data.p++; | ||
128 | DADO_L(2) = *dma_play_data.p++; | ||
129 | DADO_R(2) = *dma_play_data.p++; | ||
130 | DADO_L(3) = *dma_play_data.p++; | ||
131 | DADO_R(3) = *dma_play_data.p++; | ||
132 | dma_play_data.size -= 16; | ||
133 | } | ||
134 | |||
135 | DAMR |= (1<<14); /* enable tx */ | ||
136 | } | ||
137 | |||
138 | static void play_stop_pcm(void) | ||
139 | { | ||
140 | DAMR &= ~(1<<14); /* disable tx */ | ||
141 | dma_play_data.state = 0; | ||
47 | } | 142 | } |
48 | 143 | ||
49 | void pcm_play_dma_start(const void *addr, size_t size) | 144 | void pcm_play_dma_start(const void *addr, size_t size) |
50 | { | 145 | { |
51 | (void) addr; | 146 | dma_play_data.p = (void *)(((uintptr_t)addr + 2) & ~3); |
52 | (void) size; | 147 | dma_play_data.size = (size & ~3); |
148 | |||
149 | #if NUM_CORES > 1 | ||
150 | /* This will become more important later - and different ! */ | ||
151 | dma_play_data.core = processor_id(); /* save initiating core */ | ||
152 | #endif | ||
153 | |||
154 | IEN |= DAI_TX_IRQ_MASK; | ||
155 | |||
156 | play_start_pcm(); | ||
53 | } | 157 | } |
54 | 158 | ||
55 | void pcm_play_dma_stop(void) | 159 | void pcm_play_dma_stop(void) |
56 | { | 160 | { |
161 | play_stop_pcm(); | ||
162 | dma_play_data.size = 0; | ||
163 | #if NUM_CORES > 1 | ||
164 | dma_play_data.core = 0; /* no core in control */ | ||
165 | #endif | ||
57 | } | 166 | } |
58 | 167 | ||
59 | void pcm_play_lock(void) | 168 | void pcm_play_lock(void) |
60 | { | 169 | { |
170 | int status = disable_fiq_save(); | ||
171 | |||
172 | if (++dma_play_data.locked == 1) | ||
173 | { | ||
174 | IEN &= ~DAI_TX_IRQ_MASK; | ||
175 | } | ||
176 | |||
177 | restore_fiq(status); | ||
61 | } | 178 | } |
62 | 179 | ||
63 | void pcm_play_unlock(void) | 180 | void pcm_play_unlock(void) |
64 | { | 181 | { |
182 | int status = disable_fiq_save(); | ||
183 | |||
184 | if (--dma_play_data.locked == 0 && dma_play_data.state != 0) | ||
185 | { | ||
186 | IEN |= DAI_TX_IRQ_MASK; | ||
187 | } | ||
188 | |||
189 | restore_fiq(status); | ||
65 | } | 190 | } |
66 | 191 | ||
67 | void pcm_play_dma_pause(bool pause) | 192 | void pcm_play_dma_pause(bool pause) |
@@ -71,16 +196,116 @@ void pcm_play_dma_pause(bool pause) | |||
71 | 196 | ||
72 | size_t pcm_get_bytes_waiting(void) | 197 | size_t pcm_get_bytes_waiting(void) |
73 | { | 198 | { |
74 | return 0; | 199 | return dma_play_data.size & ~3; |
75 | } | 200 | } |
76 | 201 | ||
202 | #if 1 | ||
203 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | ||
77 | void fiq_handler(void) | 204 | void fiq_handler(void) |
78 | { | 205 | { |
79 | /* Clear FIQ status */ | 206 | /* r10 contains DADO_L0 base address (set in crt0.S to minimise code in the |
80 | CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; | 207 | * FIQ handler. r11 contains address of p (also set in crt0.S). Most other |
81 | 208 | * addresses we need are generated by using offsets with these two. | |
82 | /* Return from FIQ */ | 209 | * r8 and r9 contains local copies of p and size respectively. |
210 | * r0-r3 and r12 is a working register. | ||
211 | */ | ||
83 | asm volatile ( | 212 | asm volatile ( |
84 | "subs pc, lr, #4 \r\n" | 213 | "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */ |
214 | |||
215 | "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ | ||
216 | "cmp r9, #0x10 \n" /* is size <16? */ | ||
217 | "blt .more_data \n" /* if so, ask pcmbuf for more data */ | ||
218 | |||
219 | ".fill_fifo: \n" | ||
220 | "ldr r12, [r8], #4 \n" /* load two samples */ | ||
221 | "str r12, [r10, #0x0] \n" /* write top sample to DADO_L0 */ | ||
222 | "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */ | ||
223 | "str r12, [r10, #0x4] \n" /* write low sample to DADO_R0*/ | ||
224 | "ldr r12, [r8], #4 \n" /* load two samples */ | ||
225 | "str r12, [r10, #0x8] \n" /* write top sample to DADO_L1 */ | ||
226 | "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */ | ||
227 | "str r12, [r10, #0xc] \n" /* write low sample to DADO_R1*/ | ||
228 | "ldr r12, [r8], #4 \n" /* load two samples */ | ||
229 | "str r12, [r10, #0x10] \n" /* write top sample to DADO_L2 */ | ||
230 | "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */ | ||
231 | "str r12, [r10, #0x14] \n" /* write low sample to DADO_R2*/ | ||
232 | "ldr r12, [r8], #4 \n" /* load two samples */ | ||
233 | "str r12, [r10, #0x18] \n" /* write top sample to DADO_L3 */ | ||
234 | "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */ | ||
235 | "str r12, [r10, #0x1c] \n" /* write low sample to DADO_R3*/ | ||
236 | "sub r9, r9, #0x10 \n" /* 4 words written */ | ||
237 | "stmia r11, { r8-r9 } \n" /* save p and size */ | ||
238 | |||
239 | ".exit: \n" | ||
240 | "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */ | ||
241 | "ldr r9, =0xf3001004 \n" /* CREQ */ | ||
242 | "str r8, [r9] \n" /* clear DAI IRQs */ | ||
243 | "ldmfd sp!, { r0-r3, lr } \n" | ||
244 | "subs pc, lr, #4 \n" /* FIQ specific return sequence */ | ||
245 | |||
246 | ".more_data: \n" | ||
247 | "ldr r2, =pcm_callback_for_more \n" | ||
248 | "ldr r2, [r2] \n" /* get callback address */ | ||
249 | "cmp r2, #0 \n" /* check for null pointer */ | ||
250 | "movne r0, r11 \n" /* r0 = &p */ | ||
251 | "addne r1, r11, #4 \n" /* r1 = &size */ | ||
252 | "blxne r2 \n" /* call pcm_callback_for_more */ | ||
253 | "ldmia r11, { r8-r9 } \n" /* reload p and size */ | ||
254 | "cmp r9, #0x10 \n" /* did we actually get more data? */ | ||
255 | "bge .fill_fifo \n" /* yes: fill the fifo */ | ||
256 | "ldr r12, =pcm_play_dma_stop \n" | ||
257 | "blx r12 \n" /* no: stop playback */ | ||
258 | "ldr r12, =pcm_play_dma_stopped_callback \n" | ||
259 | "blx r12 \n" | ||
260 | "b .exit \n" | ||
261 | ".ltorg \n" | ||
85 | ); | 262 | ); |
86 | } | 263 | } |
264 | #else /* C version for reference */ | ||
265 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | ||
266 | void fiq_handler(void) | ||
267 | { | ||
268 | asm volatile( "stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */ | ||
269 | "sub sp, sp, #8 \n"); /* Reserve stack */ | ||
270 | |||
271 | register pcm_more_callback_type get_more; | ||
272 | |||
273 | if (dma_play_data.size < 16) | ||
274 | { | ||
275 | /* p is empty, get some more data */ | ||
276 | get_more = pcm_callback_for_more; | ||
277 | if (get_more) | ||
278 | { | ||
279 | get_more((unsigned char**)&dma_play_data.p, | ||
280 | &dma_play_data.size); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | if (dma_play_data.size >= 16) | ||
285 | { | ||
286 | DADO_L(0) = *dma_play_data.p++; | ||
287 | DADO_R(0) = *dma_play_data.p++; | ||
288 | DADO_L(1) = *dma_play_data.p++; | ||
289 | DADO_R(1) = *dma_play_data.p++; | ||
290 | DADO_L(2) = *dma_play_data.p++; | ||
291 | DADO_R(2) = *dma_play_data.p++; | ||
292 | DADO_L(3) = *dma_play_data.p++; | ||
293 | DADO_R(3) = *dma_play_data.p++; | ||
294 | |||
295 | dma_play_data.size -= 16; | ||
296 | } | ||
297 | else | ||
298 | { | ||
299 | /* No more data, so disable the FIFO/interrupt */ | ||
300 | pcm_play_dma_stop(); | ||
301 | pcm_play_dma_stopped_callback(); | ||
302 | } | ||
303 | |||
304 | /* Clear FIQ status */ | ||
305 | CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; | ||
306 | |||
307 | asm volatile( "add sp, sp, #8 \n" /* Cleanup stack */ | ||
308 | "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */ | ||
309 | "subs pc, lr, #4 \n"); /* Return from FIQ */ | ||
310 | } | ||
311 | #endif | ||