summaryrefslogtreecommitdiff
path: root/firmware/target/arm/as3525
diff options
context:
space:
mode:
authorRafaël Carré <rafael.carre@gmail.com>2010-05-13 05:26:12 +0000
committerRafaël Carré <rafael.carre@gmail.com>2010-05-13 05:26:12 +0000
commitd556f268209e949b92a44e710e9720b3565dac2e (patch)
treee4c9db8b2ba5d61a016ee1b06002daa4dfe8fd24 /firmware/target/arm/as3525
parent9abd82fc0459cc2c617f2bd521275a887ddcb991 (diff)
downloadrockbox-d556f268209e949b92a44e710e9720b3565dac2e.tar.gz
rockbox-d556f268209e949b92a44e710e9720b3565dac2e.zip
as3525: use DMA for recording
Flyspray: FS#11257 git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25980 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/as3525')
-rw-r--r--firmware/target/arm/as3525/pcm-as3525.c175
1 files changed, 69 insertions, 106 deletions
diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c
index 4d78899398..bdd6a4eca1 100644
--- a/firmware/target/arm/as3525/pcm-as3525.c
+++ b/firmware/target/arm/as3525/pcm-as3525.c
@@ -134,18 +134,19 @@ void pcm_postinit(void)
134 audiohw_postinit(); 134 audiohw_postinit();
135} 135}
136 136
137void pcm_dma_apply_settings(void) 137static unsigned mclk_divider(void)
138{ 138{
139 unsigned long frequency = pcm_sampr;
140
141 /* TODO : use a table ? */ 139 /* TODO : use a table ? */
142 const int divider = ((AS3525_MCLK_FREQ/128) + (frequency/2)) / frequency; 140 return (((AS3525_MCLK_FREQ/128) + (pcm_sampr/2)) / pcm_sampr) - 1;
141}
143 142
143void pcm_dma_apply_settings(void)
144{
144 int cgu_audio = CGU_AUDIO; /* read register */ 145 int cgu_audio = CGU_AUDIO; /* read register */
145 cgu_audio &= ~(3 << 0); /* clear i2sout MCLK_SEL */ 146 cgu_audio &= ~(3 << 0); /* clear i2sout MCLK_SEL */
146 cgu_audio |= (AS3525_MCLK_SEL << 0); /* set i2sout MCLK_SEL */ 147 cgu_audio |= (AS3525_MCLK_SEL << 0); /* set i2sout MCLK_SEL */
147 cgu_audio &= ~(511 << 2); /* clear i2sout divider */ 148 cgu_audio &= ~(511 << 2); /* clear i2sout divider */
148 cgu_audio |= (divider - 1) << 2; /* set new i2sout divider */ 149 cgu_audio |= mclk_divider() << 2; /* set new i2sout divider */
149 CGU_AUDIO = cgu_audio; /* write back register */ 150 CGU_AUDIO = cgu_audio; /* write back register */
150} 151}
151 152
@@ -174,166 +175,128 @@ void * pcm_dma_addr(void *addr)
174 ** Recording DMA transfer 175 ** Recording DMA transfer
175 **/ 176 **/
176#ifdef HAVE_RECORDING 177#ifdef HAVE_RECORDING
177#define I2SIN_RECORDING_MASK ( I2SIN_MASK_POER | I2SIN_MASK_PUER | \
178 I2SIN_MASK_POHF | I2SIN_MASK_POAF | I2SIN_MASK_POF )
179 178
180static int rec_locked = 0; 179static int rec_locked = 0;
181static unsigned int *rec_start_addr; 180static unsigned char *rec_dma_start_addr;
182static size_t rec_size; 181static size_t rec_dma_size;
182static void rec_dma_callback(void);
183
183 184
184void pcm_rec_lock(void) 185void pcm_rec_lock(void)
185{ 186{
186 if(++rec_locked == 1) { 187 if(++rec_locked == 1)
187 int vic_state = disable_irq_save(); 188 VIC_INT_EN_CLEAR = INTERRUPT_DMAC;
188 VIC_INT_EN_CLEAR = INTERRUPT_I2SIN;
189 I2SIN_MASK = 0;
190 restore_irq( vic_state );
191 }
192} 189}
193 190
191
194void pcm_rec_unlock(void) 192void pcm_rec_unlock(void)
195{ 193{
196 if(--rec_locked == 0) { 194 if(--rec_locked == 0)
197 int vic_state = disable_irq_save(); 195 VIC_INT_ENABLE = INTERRUPT_DMAC;
198 VIC_INT_ENABLE = INTERRUPT_I2SIN;
199 I2SIN_MASK = I2SIN_RECORDING_MASK;
200 restore_irq( vic_state );
201 }
202} 196}
203 197
204 198
205void pcm_rec_dma_record_more(void *start, size_t size) 199static void rec_dma_start(void)
206{ 200{
207 rec_start_addr = start; 201 void* addr = rec_dma_start_addr;
208 rec_size = size; 202 size_t size = rec_dma_size;
209}
210 203
204 /* We are limited to 8188 DMA transfers, and the recording core asks for
205 * 8192 bytes. Avoid splitting 8192 bytes transfers in 8188 + 4 */
206 if(size > 4096)
207 size = 4096;
211 208
212void pcm_rec_dma_stop(void) 209 rec_dma_size -= size;
213{ 210 rec_dma_start_addr += size;
214 int vic_state = disable_irq_save();
215 VIC_INT_EN_CLEAR = INTERRUPT_I2SIN;
216 I2SIN_MASK = 0;
217 restore_irq( vic_state );
218 211
219 I2SOUT_CONTROL &= ~(1<<5); /* source = i2soutif fifo */ 212 dma_enable_channel(1, (void*)I2SIN_DATA, addr, DMA_PERI_I2SIN,
220 CGU_AUDIO &= ~((1<<23)|(1<<11)); 213 DMAC_FLOWCTRL_DMAC_PERI_TO_MEM, false, true, size >> 2, DMA_S4,
221 CGU_PERI &= ~(CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE); 214 rec_dma_callback);
222} 215}
223 216
224 217
225void INT_I2SIN(void) 218static void rec_dma_callback(void)
226{ 219{
227 register int status; 220 if(!rec_dma_size)
228 register pcm_more_callback_type2 more_ready;
229
230 status = I2SIN_STATUS;
231
232 if ( status & ((1<<6)|(1<<0)) ) /* errors */
233 panicf("i2sin error: 0x%x = %s %s", status,
234 (status & (1<<6)) ? "push" : "",
235 (status & (1<<0)) ? "pop" : ""
236 );
237
238 /* called at half full so it's safe to pull 16 FIFO reads in one chunk */
239 if( rec_size >= 16*4 )
240 {
241 /* unrolled loop */
242 *rec_start_addr++ = *I2SIN_DATA;
243 *rec_start_addr++ = *I2SIN_DATA;
244 *rec_start_addr++ = *I2SIN_DATA;
245 *rec_start_addr++ = *I2SIN_DATA;
246
247 *rec_start_addr++ = *I2SIN_DATA;
248 *rec_start_addr++ = *I2SIN_DATA;
249 *rec_start_addr++ = *I2SIN_DATA;
250 *rec_start_addr++ = *I2SIN_DATA;
251
252 *rec_start_addr++ = *I2SIN_DATA;
253 *rec_start_addr++ = *I2SIN_DATA;
254 *rec_start_addr++ = *I2SIN_DATA;
255 *rec_start_addr++ = *I2SIN_DATA;
256
257 *rec_start_addr++ = *I2SIN_DATA;
258 *rec_start_addr++ = *I2SIN_DATA;
259 *rec_start_addr++ = *I2SIN_DATA;
260 *rec_start_addr++ = *I2SIN_DATA;
261
262 rec_size -= 16*4; /* 16x4byte reads */
263 }
264
265 /* read out any odd samples left */
266 while (((I2SIN_RAW_STATUS & (1<<5)) == 0) && rec_size)
267 {
268 /* 14 bits per sample = 1 32 bits word */
269 *rec_start_addr++ = *I2SIN_DATA;
270 rec_size -= 4;
271 }
272
273 I2SIN_CLEAR = status;
274
275 if(!rec_size)
276 { 221 {
277 more_ready = pcm_callback_more_ready; 222 register pcm_more_callback_type2 more_ready = pcm_callback_more_ready;
278 if(!more_ready || more_ready(0) < 0) 223 if (!more_ready || more_ready(0) < 0)
279 { 224 {
280 /* Finished recording */ 225 /* Finished recording */
281 pcm_rec_dma_stop(); 226 pcm_rec_dma_stop();
282 pcm_rec_dma_stopped_callback(); 227 pcm_rec_dma_stopped_callback();
228 return;
283 } 229 }
284 } 230 }
231
232 rec_dma_start();
233}
234
235
236void pcm_rec_dma_record_more(void *start, size_t size)
237{
238 dump_dcache_range(start, size);
239 rec_dma_start_addr = start;
240 rec_dma_size = size;
241}
242
243
244void pcm_rec_dma_stop(void)
245{
246 dma_disable_channel(1);
247 rec_dma_size = 0;
248 dma_release();
249
250 I2SOUT_CONTROL &= ~(1<<5); /* source = i2soutif fifo */
251 I2SIN_CONTROL &= ~(1<<11); /* disable dma */
252
253 CGU_AUDIO &= ~((1<<23)|(1<<11));
254 CGU_PERI &= ~(CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE);
285} 255}
286 256
287 257
288void pcm_rec_dma_start(void *addr, size_t size) 258void pcm_rec_dma_start(void *addr, size_t size)
289{ 259{
290 rec_start_addr = addr; 260 dump_dcache_range(addr, size);
291 rec_size = size; 261 rec_dma_start_addr = addr;
262 rec_dma_size = size;
263
264 dma_retain();
292 265
293 CGU_PERI |= CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE; 266 CGU_PERI |= CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE;
294 CGU_AUDIO |= ((1<<23)|(1<<11)); 267 CGU_AUDIO |= ((1<<23)|(1<<11));
295 268
296 I2SOUT_CONTROL |= 1<<5; /* source = loopback from i2sin fifo */ 269 I2SOUT_CONTROL |= 1<<5; /* source = loopback from i2sin fifo */
297 270
298 /* 14 bits samples, i2c clk src = I2SOUTIF, sdata src = AFE, 271 I2SIN_CONTROL |= (1<<11)|(1<<5); /* enable dma, 14bits samples */
299 * data valid at positive edge of SCLK */
300 I2SIN_CONTROL = (1<<5) | (1<<2);
301 272
302 unsigned long tmp; 273 rec_dma_start();
303 while ( ( I2SIN_RAW_STATUS & ( 1<<5 ) ) == 0 )
304 tmp = *I2SIN_DATA; /* FLUSH FIFO */
305 I2SIN_CLEAR = (1<<6)|(1<<0); /* push error, pop error */
306 I2SIN_MASK = I2SIN_RECORDING_MASK;
307
308 VIC_INT_ENABLE = INTERRUPT_I2SIN;
309} 274}
310 275
311 276
312void pcm_rec_dma_close(void) 277void pcm_rec_dma_close(void)
313{ 278{
314 pcm_rec_dma_stop();
315} 279}
316 280
317 281
318void pcm_rec_dma_init(void) 282void pcm_rec_dma_init(void)
319{ 283{
320 unsigned long frequency = pcm_sampr;
321
322 /* TODO : use a table ? */
323 const int divider = ((AS3525_MCLK_FREQ/128) + (frequency/2)) / frequency;
324
325 int cgu_audio = CGU_AUDIO; /* read register */ 284 int cgu_audio = CGU_AUDIO; /* read register */
326 cgu_audio &= ~(3 << 12); /* clear i2sin MCLK_SEL */ 285 cgu_audio &= ~(3 << 12); /* clear i2sin MCLK_SEL */
327 cgu_audio |= (AS3525_MCLK_SEL << 12); /* set i2sin MCLK_SEL */ 286 cgu_audio |= (AS3525_MCLK_SEL << 12); /* set i2sin MCLK_SEL */
328 cgu_audio &= ~(511 << 14); /* clear i2sin divider */ 287 cgu_audio &= ~(511 << 14); /* clear i2sin divider */
329 cgu_audio |= (divider - 1) << 14; /* set new i2sin divider */ 288 cgu_audio |= mclk_divider() << 14; /* set new i2sin divider */
330 CGU_AUDIO = cgu_audio; /* write back register */ 289 CGU_AUDIO = cgu_audio; /* write back register */
290
291 /* i2c clk src = I2SOUTIF, sdata src = AFE,
292 * data valid at positive edge of SCLK */
293 I2SIN_CONTROL = (1<<2);
331} 294}
332 295
333 296
334const void * pcm_rec_dma_get_peak_buffer(void) 297const void * pcm_rec_dma_get_peak_buffer(void)
335{ 298{
336 return (const void*)rec_start_addr; 299 return rec_dma_start_addr;
337} 300}
338 301
339#endif /* HAVE_RECORDING */ 302#endif /* HAVE_RECORDING */