summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s3c2440/gigabeat-fx
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-10-06 22:27:27 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-10-06 22:27:27 +0000
commit6077e5b7c85c0d6f5963e4aadb215faf2c4d10d2 (patch)
treea6bc91ee4168e83617e942eeaea46e5523e82420 /firmware/target/arm/s3c2440/gigabeat-fx
parentf6de0d4083a4fcb6da57f271e1f8ccaf715e571d (diff)
downloadrockbox-6077e5b7c85c0d6f5963e4aadb215faf2c4d10d2.tar.gz
rockbox-6077e5b7c85c0d6f5963e4aadb215faf2c4d10d2.zip
Unify PCM interface just above the hardware driver level for all targets including the sims. Perform lockout of audio callback when changing states. Weird new playback or recording trouble? Check before and after this revision first though things seem quite sound.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15006 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/s3c2440/gigabeat-fx')
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c374
1 files changed, 150 insertions, 224 deletions
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 57873faaff..a38b4e424e 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
@@ -25,31 +25,42 @@
25#include "file.h" 25#include "file.h"
26#include "mmu-meg-fx.h" 26#include "mmu-meg-fx.h"
27 27
28/* All exact rates for 16.9344MHz clock */
28#define GIGABEAT_11025HZ (0x19 << 1) 29#define GIGABEAT_11025HZ (0x19 << 1)
29#define GIGABEAT_22050HZ (0x1b << 1) 30#define GIGABEAT_22050HZ (0x1b << 1)
30#define GIGABEAT_44100HZ (0x11 << 1) 31#define GIGABEAT_44100HZ (0x11 << 1)
31#define GIGABEAT_88200HZ (0x1f << 1) 32#define GIGABEAT_88200HZ (0x1f << 1)
32 33
33static int pcm_freq = 0; /* 44.1 is default */ 34/* PCM interrupt routine lockout */
35static struct
36{
37 int locked;
38 unsigned long state;
39} dma_play_lock =
40{
41 .locked = 0,
42 .state = (0<<19)
43};
44
45/* Last samplerate set by pcm_set_frequency */
46static unsigned long pcm_freq = 0; /* 44.1 is default */
47/* Samplerate control for audio codec */
34static int sr_ctrl = 0; 48static int sr_ctrl = 0;
35#define FIFO_COUNT ((IISFCON >> 6) & 0x01F) 49
50#define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
36 51
37/* Setup for the DMA controller */ 52/* Setup for the DMA controller */
38#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20)) 53#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
39 54
40/* DMA count has hit zero - no more data */ 55/* DMA count has hit zero - no more data */
41/* Get more data from the callback and top off the FIFO */ 56/* Get more data from the callback and top off the FIFO */
42/* Uses explicitly coded prologue/epilogue code to get around complier bugs 57void fiq_handler(void) __attribute__((interrupt ("FIQ")));
43 in order to be able to use the stack */
44void fiq_handler(void) __attribute__((naked));
45 58
46static void _pcm_apply_settings(void) 59static void _pcm_apply_settings(void)
47{ 60{
48 static int last_freqency = 0; 61 if (pcm_freq != pcm_curr_sampr)
49
50 if (pcm_freq != last_freqency)
51 { 62 {
52 last_freqency = pcm_freq; 63 pcm_curr_sampr = pcm_freq;
53 audiohw_set_frequency(sr_ctrl); 64 audiohw_set_frequency(sr_ctrl);
54 } 65 }
55} 66}
@@ -61,29 +72,50 @@ void pcm_apply_settings(void)
61 set_fiq_status(oldstatus); 72 set_fiq_status(oldstatus);
62} 73}
63 74
64void pcm_init(void) 75/* For the locks, DMA interrupt must be disabled because the handler
76 manipulates INTMSK and the operation is not atomic */
77void pcm_play_lock(void)
78{
79 int status = set_fiq_status(FIQ_DISABLED);
80 if (++dma_play_lock.locked == 1)
81 INTMSK |= (1<<19); /* Mask the DMA interrupt */
82 set_fiq_status(status);
83}
84
85void pcm_play_unlock(void)
65{ 86{
66 pcm_playing = false; 87 int status = set_fiq_status(FIQ_DISABLED);
67 pcm_paused = false; 88 if (--dma_play_lock.locked == 0)
68 pcm_callback_for_more = NULL; 89 INTMSK &= ~dma_play_lock.state; /* Unmask the DMA interrupt if enabled */
90 set_fiq_status(status);
91}
69 92
93void pcm_play_dma_init(void)
94{
70 pcm_set_frequency(SAMPR_44); 95 pcm_set_frequency(SAMPR_44);
71 96
72 /* slave */ 97 /* slave */
73 IISMOD |= (1<<8); 98 IISMOD |= (1<<8);
74 99
100 /* RX,TX off,idle */
101 IISCON |= (1<<3) | (1<<2);
102
75 audiohw_init(); 103 audiohw_init();
76 104
77 /* init GPIO */ 105 /* init GPIO */
78 GPCCON = (GPCCON & ~(3<<14)) | (1<<14); 106 GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
79 GPCDAT |= 1<<7; 107 GPCDAT |= (1<<7);
80 GPECON |= 0x2aa; 108 /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
109 GPECON = (GPECON & ~0x3ff) | 0x2aa;
81 110
82 /* Do not service DMA requests, yet */ 111 /* Do not service DMA requests, yet */
112
83 /* clear any pending int and mask it */ 113 /* clear any pending int and mask it */
84 INTMSK |= (1<<19); /* mask the interrupt */ 114 INTMSK |= (1<<19);
85 SRCPND = (1<<19); /* clear any pending interrupts */ 115 SRCPND = (1<<19);
86 INTMOD |= (1<<19); /* connect to FIQ */ 116
117 /* connect to FIQ */
118 INTMOD |= (1<<19);
87} 119}
88 120
89void pcm_postinit(void) 121void pcm_postinit(void)
@@ -92,21 +124,69 @@ void pcm_postinit(void)
92 pcm_apply_settings(); 124 pcm_apply_settings();
93} 125}
94 126
95void pcm_play_dma_start(const void *addr, size_t size) 127/* Connect the DMA and start filling the FIFO */
128static void play_start_pcm(void)
129{
130 /* clear pending DMA interrupt */
131 SRCPND = (1<<19);
132
133 _pcm_apply_settings();
134
135 /* Flush any pending writes */
136 clean_dcache_range((void*)DISRC2, (DCON2 & 0xFFFFF) * 2);
137
138 /* unmask DMA interrupt when unlocking */
139 dma_play_lock.state = (1<<19);
140
141 /* turn on the request */
142 IISCON |= (1<<5);
143
144 /* Activate the channel */
145 DMASKTRIG2 = 0x2;
146
147 /* turn off the idle */
148 IISCON &= ~(1<<3);
149
150 /* start the IIS */
151 IISCON |= (1<<0);
152}
153
154/* Disconnect the DMA and wait for the FIFO to clear */
155static void play_stop_pcm(void)
96{ 156{
97 addr = (void *)((unsigned long)addr & ~3); /* Align data */ 157 /* Mask DMA interrupt */
98 size &= ~3; /* Size must be multiple of 4 */ 158 INTMSK |= (1<<19);
159
160 /* De-Activate the DMA channel */
161 DMASKTRIG2 = 0x4;
162
163 /* are we playing? wait for the chunk to finish */
164 if (dma_play_lock.state != 0)
165 {
166 /* wait for the FIFO to empty and DMA to stop */
167 while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
168 }
169
170 /* Keep interrupt masked when unlocking */
171 dma_play_lock.state = 0;
172
173 /* turn off the request */
174 IISCON &= ~(1<<5);
99 175
100 /* sanity check: bad pointer or too small file */ 176 /* turn on the idle */
101 if (NULL == addr || size == 0) return; 177 IISCON |= (1<<3);
102 178
103 disable_fiq(); 179 /* stop the IIS */
180 IISCON &= ~(1<<0);
181}
104 182
183void pcm_play_dma_start(const void *addr, size_t size)
184{
105 /* Enable the IIS clock */ 185 /* Enable the IIS clock */
106 CLKCON |= (1<<17); 186 CLKCON |= (1<<17);
107 187
108 /* IIS interface setup and set to idle */ 188 /* stop any DMA in progress - idle IIS */
109 IISCON = (1<<5) | (1<<3); 189 play_stop_pcm();
110 190
111 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz - 191 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
112 BCLK 32fs */ 192 BCLK 32fs */
@@ -116,155 +196,80 @@ void pcm_play_dma_start(const void *addr, size_t size)
116 IISFCON = (1<<15) | (1<<13); 196 IISFCON = (1<<15) | (1<<13);
117 197
118 /* set DMA dest */ 198 /* set DMA dest */
119 DIDST2 = (int)&IISFIFO; 199 DIDST2 = (unsigned int)&IISFIFO;
120 200
121 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */ 201 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
122 DIDSTC2 = 0x03; 202 DIDSTC2 = 0x03;
123 203
204 /* set DMA source and options */
205 DISRC2 = (unsigned int)addr + 0x30000000;
124 /* How many transfers to make - we transfer half-word at a time = 2 bytes */ 206 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
125 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */ 207 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
126 /* no auto-reload, half-word (16bit) */ 208 /* no auto-reload, half-word (16bit) */
127 DCON2 = DMA_CONTROL_SETUP | (size / 2); 209 DCON2 = DMA_CONTROL_SETUP | (size / 2);
128
129 /* set DMA source and options */
130 DISRC2 = (unsigned long)addr + 0x30000000;
131 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */ 210 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
132 211
133 /* clear pending DMA interrupt */ 212 play_start_pcm();
134 SRCPND = 1<<19; 213}
135
136 pcm_playing = true;
137
138 _pcm_apply_settings();
139
140 /* unmask the DMA interrupt */
141 INTMSK &= ~(1<<19);
142
143 /* Flush any pending writes */
144 clean_dcache_range(addr, size);
145
146 /* Activate the channel */
147 DMASKTRIG2 = 0x2;
148
149 /* turn off the idle */
150 IISCON &= ~(1<<3);
151 214
152 /* start the IIS */ 215/* Promptly stop DMA transfers and stop IIS */
153 IISCON |= (1<<0); 216void pcm_play_dma_stop(void)
217{
218 play_stop_pcm();
154 219
155 enable_fiq(); 220 /* Disconnect the IIS clock */
221 CLKCON &= ~(1<<17);
156} 222}
157 223
158static void pcm_play_dma_stop_fiq(void) 224void pcm_play_dma_pause(bool pause)
159{ 225{
160 INTMSK |= (1<<19); /* mask the DMA interrupt */ 226 if (pause)
161 IISCON &= ~(1<<5); /* disable fifo request */
162 DMASKTRIG2 = 0x4; /* De-Activate the DMA channel */
163
164 /* are we playing? wait for the chunk to finish */
165 if (pcm_playing)
166 { 227 {
167 /* wait for the FIFO to empty before turning things off */ 228 /* pause playback on current buffer */
168 while (IISCON & (1<<7)) ; 229 play_stop_pcm();
169 230 }
170 pcm_playing = false; 231 else
171 if (!audio_status()) 232 {
172 pcm_paused = false; 233 /* restart playback on current buffer */
234 /* make sure we're aligned on left channel - skip any right
235 channel sample left waiting */
236 DISRC2 = (DCSRC2 + 2) & ~0x3;
237 DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE);
238 play_start_pcm();
173 } 239 }
174
175 /* Disconnect the IIS clock */
176 CLKCON &= ~(1<<17);
177} 240}
178 241
179void fiq_handler(void) 242void fiq_handler(void)
180{ 243{
181 /* r0-r7 are probably not all used by GCC but there's no way to know 244 static unsigned char *start;
182 otherwise this whole thing must be assembly */ 245 static size_t size;
183 asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
184 "sub sp, sp, #8 \n"); /* Reserve stack */
185 register pcm_more_callback_type get_more; /* No stack for this */ 246 register pcm_more_callback_type get_more; /* No stack for this */
186 unsigned char *next_start; /* sp + #0 */
187 size_t next_size; /* sp + #4 */
188 247
189 /* clear any pending interrupt */ 248 /* clear any pending interrupt */
190 SRCPND = (1<<19); 249 SRCPND = (1<<19);
191 250
192 /* Buffer empty. Try to get more. */ 251 /* Buffer empty. Try to get more. */
193 get_more = pcm_callback_for_more; 252 get_more = pcm_callback_for_more;
194 if (get_more == NULL) 253 size = 0;
195 {
196 /* Callback missing */
197 pcm_play_dma_stop_fiq();
198 goto fiq_exit;
199 }
200
201 next_size = 0;
202 get_more(&next_start, &next_size);
203 254
204 if (next_size == 0) 255 if (get_more == NULL || (get_more(&start, &size), size == 0))
205 { 256 {
206 /* No more DMA to do */ 257 /* Callback missing or no more DMA to do */
207 pcm_play_dma_stop_fiq(); 258 pcm_play_dma_stop();
208 goto fiq_exit; 259 pcm_play_dma_stopped_callback();
209 } 260 }
210 261 else
211 /* Flush any pending cache writes */
212 clean_dcache_range(next_start, next_size);
213
214 /* set the new DMA values */
215 DCON2 = DMA_CONTROL_SETUP | (next_size >> 1);
216 DISRC2 = (unsigned long)next_start + 0x30000000;
217
218 /* Re-Activate the channel */
219 DMASKTRIG2 = 0x2;
220
221fiq_exit:
222 asm volatile("add sp, sp, #8 \n" /* Cleanup stack */
223 "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
224 "subs pc, lr, #4 \n"); /* Return from FIQ */
225}
226
227/* Disconnect the DMA and wait for the FIFO to clear */
228void pcm_play_dma_stop(void)
229{
230 disable_fiq();
231 pcm_play_dma_stop_fiq();
232}
233
234void pcm_play_pause_pause(void)
235{
236 /* stop servicing refills */
237 int oldstatus = set_fiq_status(FIQ_DISABLED);
238 INTMSK |= (1<<19); /* mask interrupt request */
239 IISCON &= ~(1<<5); /* turn off FIFO request */
240 DMASKTRIG2 = 0x4; /* stop DMA at end of atomic transfer */
241
242 if (pcm_playing)
243 { 262 {
244 /* playing - wait for the FIFO to empty */ 263 /* Flush any pending cache writes */
245 while (IISCON & (1<<7)) ; 264 clean_dcache_range(start, size);
246 }
247 265
248 set_fiq_status(oldstatus); 266 /* set the new DMA values */
249} 267 DCON2 = DMA_CONTROL_SETUP | (size >> 1);
268 DISRC2 = (unsigned int)start + 0x30000000;
250 269
251void pcm_play_pause_unpause(void) 270 /* Re-Activate the channel */
252{ 271 DMASKTRIG2 = 0x2;
253 /* refill buffer and keep going */
254 int oldstatus = set_fiq_status(FIQ_DISABLED);
255 _pcm_apply_settings();
256 if (pcm_playing)
257 {
258 /* make sure we're aligned on left channel - skip any right channel
259 sample left waiting */
260 DISRC2 = (DCSRC2 + 2) & ~0x3;
261 DCON2 = (DSTAT2 & 0xFFFFE);
262
263 SRCPND = (1<<19); /* clear pending DMA interrupt */
264 INTMSK &= ~(1<<19); /* unmask interrupt request */
265 IISCON |= (1<<5); /* enable FIFO request */
266 } 272 }
267 set_fiq_status(oldstatus);
268} 273}
269 274
270void pcm_set_frequency(unsigned int frequency) 275void pcm_set_frequency(unsigned int frequency)
@@ -296,89 +301,10 @@ size_t pcm_get_bytes_waiting(void)
296 return (DSTAT2 & 0xFFFFE) * 2; 301 return (DSTAT2 & 0xFFFFE) * 2;
297} 302}
298 303
299/** **/ 304const void * pcm_play_dma_get_peak_buffer(int *count)
300
301void pcm_mute(bool mute)
302{ 305{
303 audiohw_mute(mute); 306 unsigned long addr = DCSRC2;
304 if (mute) 307 int cnt = DSTAT2;
305 sleep(HZ/16); 308 *count = (cnt & 0xFFFFF) >> 1;
309 return (void *)((addr + 2) & ~3);
306} 310}
307
308/**
309 * Return playback peaks - Peaks ahead in the DMA buffer based upon the
310 * calling period to attempt to compensate for
311 * delay.
312 */
313void pcm_calculate_peaks(int *left, int *right)
314{
315 static unsigned long last_peak_tick = 0;
316 static unsigned long frame_period = 0;
317 static int peaks_l = 0, peaks_r = 0;
318
319 /* Throttled peak ahead based on calling period */
320 unsigned long period = current_tick - last_peak_tick;
321
322 /* Keep reasonable limits on period */
323 if (period < 1)
324 period = 1;
325 else if (period > HZ/5)
326 period = HZ/5;
327
328 frame_period = (3*frame_period + period) >> 2;
329
330 last_peak_tick = current_tick;
331
332 if (pcm_playing && !pcm_paused)
333 {
334 unsigned long *addr = (unsigned long *)DCSRC2;
335 long samples = DSTAT2;
336 long samp_frames;
337
338 addr = (unsigned long *)((unsigned long)addr & ~3);
339 samples &= 0xFFFFE;
340 samp_frames = frame_period*pcm_freq/(HZ/2);
341 samples = MIN(samp_frames, samples) >> 1;
342
343 if (samples > 0)
344 {
345 long peak_l = 0, peak_r = 0;
346 long peaksq_l = 0, peaksq_r = 0;
347
348 addr -= 0x30000000 >> 2;
349 addr = (long *)((long)addr & ~3);
350
351 do
352 {
353 long value = *addr;
354 long ch, chsq;
355
356 ch = (int16_t)value;
357 chsq = ch*ch;
358 if (chsq > peaksq_l)
359 peak_l = ch, peaksq_l = chsq;
360
361 ch = value >> 16;
362 chsq = ch*ch;
363 if (chsq > peaksq_r)
364 peak_r = ch, peaksq_r = chsq;
365
366 addr += 4;
367 }
368 while ((samples -= 4) > 0);
369
370 peaks_l = abs(peak_l);
371 peaks_r = abs(peak_r);
372 }
373 }
374 else
375 {
376 peaks_l = peaks_r = 0;
377 }
378
379 if (left)
380 *left = peaks_l;
381
382 if (right)
383 *right = peaks_r;
384} /* pcm_calculate_peaks */