summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c')
-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 */