1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006 by Michael Sevakis
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
27 #define MROBE100_44100HZ (0x40|(0x11 << 1)|1)
33 /* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
34 #define SAMPLE_SIZE 16
36 /* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
37 #define SAMPLE_SIZE 32
42 /* NOTE: The order of size and p is important if you use assembler
43 optimised fiq handler, so don't change it. */
46 #elif SAMPLE_SIZE == 32
57 extern void *fiq_function
;
59 /* Dispatch to the proper handler and leave the main vector table alone */
60 void fiq_handler(void) ICODE_ATTR
__attribute__((naked
));
61 void fiq_handler(void)
64 "ldr pc, [pc, #-4] \n"
70 /* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */
72 /****************************************************************************
73 ** Playback DMA transfer
75 struct dma_data dma_play_data NOCACHEBSS_ATTR
=
77 /* Initialize to a locked, stopped state */
87 static unsigned long pcm_freq NOCACHEDATA_ATTR
= HW_SAMPR_DEFAULT
; /* 44.1 is default */
89 /* Samplerate control for audio codec */
90 static int sr_ctrl
= MROBE100_44100HZ
;
93 void pcm_set_frequency(unsigned int frequency
)
96 pcm_freq
= HW_SAMPR_DEFAULT
;
98 sr_ctrl
= MROBE100_44100HZ
;
102 void pcm_apply_settings(void)
105 audiohw_set_frequency(sr_ctrl
);
107 pcm_curr_sampr
= pcm_freq
;
110 /* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
111 has registers r8-r14 banked, and so does not need to be saved. This routine
112 uses only these registers, and so will never touch the stack unless it
113 actually needs to do so when calling pcm_callback_for_more. C version is
114 still included below for reference and testing.
117 void fiq_playback(void) ICODE_ATTR
__attribute__((naked
));
118 void fiq_playback(void)
120 /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual
121 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
122 * addresses we need are generated by using offsets with these two.
123 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG.
124 * r8 and r9 contains local copies of p and size respectively.
125 * r12 is a working register.
128 #if CONFIG_CPU == PP5002
129 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */
132 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
133 "cmp r9, #0 \n" /* is size 0? */
134 "beq .more_data \n" /* if so, ask pcmbuf for more data */
136 "ldr r12, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */
137 "ands r12, r12, %[mask] \n"
138 "beq .exit \n" /* FIFO full, exit */
139 #if SAMPLE_SIZE == 16
140 "ldr r12, [r8], #4 \n" /* load two samples */
141 "str r12, [r10, %[wr]] \n" /* write them */
142 #elif SAMPLE_SIZE == 32
143 "ldr r12, [r8], #4 \n" /* load two samples */
144 "mov r12, r12, ror #16 \n" /* put left sample at the top bits */
145 "str r12, [r10, %[wr]] \n" /* write top sample, lower sample ignored */
146 "mov r12, r12, lsl #16 \n" /* shift lower sample up */
147 "str r12, [r10, %[wr]] \n" /* then write it */
149 "subs r9, r9, #4 \n" /* check if we have more samples */
150 "bne .fifo_loop \n" /* yes, continue */
152 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
153 "ldr r2, =pcm_callback_for_more \n"
154 "ldr r2, [r2] \n" /* get callback address */
155 "cmp r2, #0 \n" /* check for null pointer */
156 "stmneia r11, { r8-r9 } \n" /* save internal copies of variables back */
157 "movne r0, r11 \n" /* r0 = &p */
158 "addne r1, r11, #4 \n" /* r1 = &size */
159 "movne lr, pc \n" /* call pcm_callback_for_more */
161 "ldmia r11, { r8-r9 } \n" /* reload p and size */
162 "cmp r9, #0 \n" /* did we actually get more data? */
163 "ldmnefd sp!, { r0-r3, lr } \n"
164 "bne .fifo_loop \n" /* yes, continue to try feeding FIFO */
165 "ldr r12, =pcm_play_dma_stop \n"
168 "ldr r12, =pcm_play_dma_stopped_callback \n"
171 "ldmfd sp!, { r0-r3, lr } \n"
172 ".exit: \n" /* (r8=0 if stopping, look above) */
173 "stmia r11, { r8-r9 } \n" /* save p and size */
174 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
176 : /* These must only be integers! No regs */
177 : [mask
]"i"(IIS_TX_FREE_MASK
& (IIS_TX_FREE_MASK
-1)),
178 [cfg
]"i"((int)&IISFIFO_CFG
- (int)&IISCONFIG
),
179 [wr
]"i"((int)&IISFIFO_WR
- (int)&IISCONFIG
)
182 #else /* C version for reference */
183 void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR
;
184 /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
185 void fiq_playback(void)
187 register pcm_more_callback_type get_more
;
189 #if CONFIG_CPU == PP5002
194 while (dma_play_data
.size
> 0) {
195 if (IIS_TX_FREE_COUNT
< 2) {
198 #if SAMPLE_SIZE == 16
199 IISFIFO_WR
= *dma_play_data
.p
++;
200 #elif SAMPLE_SIZE == 32
201 IISFIFO_WR
= *dma_play_data
.p
++ << 16;
202 IISFIFO_WR
= *dma_play_data
.p
++ << 16;
204 dma_play_data
.size
-= 4;
207 /* p is empty, get some more data */
208 get_more
= pcm_callback_for_more
;
210 get_more((unsigned char**)&dma_play_data
.p
,
211 &dma_play_data
.size
);
213 } while (dma_play_data
.size
);
215 /* No more data, so disable the FIFO/interrupt */
217 pcm_play_dma_stopped_callback();
219 #endif /* ASM / C selection */
221 /* For the locks, FIQ must be disabled because the handler manipulates
222 IISCONFIG and the operation is not atomic - dual core support
223 will require other measures */
224 void pcm_play_lock(void)
226 int status
= disable_fiq_save();
228 if (++dma_play_data
.locked
== 1) {
229 IIS_IRQTX_REG
&= ~IIS_IRQTX
;
235 void pcm_play_unlock(void)
237 int status
= disable_fiq_save();
239 if (--dma_play_data
.locked
== 0 && dma_play_data
.state
!= 0) {
240 IIS_IRQTX_REG
|= IIS_IRQTX
;
246 static void play_start_pcm(void)
248 fiq_function
= fiq_playback
;
249 pcm_apply_settings();
251 IISCONFIG
&= ~IIS_TXFIFOEN
; /* Stop transmitting */
252 dma_play_data
.state
= 1;
254 /* Fill the FIFO or start when data is used up */
256 if (IIS_TX_FREE_COUNT
< 2 || dma_play_data
.size
== 0) {
257 IISCONFIG
|= IIS_TXFIFOEN
; /* Start transmitting */
261 #if SAMPLE_SIZE == 16
262 IISFIFO_WR
= *dma_play_data
.p
++;
263 #elif SAMPLE_SIZE == 32
264 IISFIFO_WR
= *dma_play_data
.p
++ << 16;
265 IISFIFO_WR
= *dma_play_data
.p
++ << 16;
267 dma_play_data
.size
-= 4;
271 static void play_stop_pcm(void)
273 /* Disable TX interrupt */
274 IIS_IRQTX_REG
&= ~IIS_IRQTX
;
275 dma_play_data
.state
= 0;
278 void pcm_play_dma_start(const void *addr
, size_t size
)
280 dma_play_data
.p
= (void *)(((uintptr_t)addr
+ 2) & ~3);
281 dma_play_data
.size
= (size
& ~3);
284 /* This will become more important later - and different ! */
285 dma_play_data
.core
= processor_id(); /* save initiating core */
288 CPU_INT_PRIORITY
|= IIS_MASK
; /* FIQ priority for I2S */
289 CPU_INT_EN
= IIS_MASK
;
294 /* Stops the DMA transfer and interrupt */
295 void pcm_play_dma_stop(void)
298 dma_play_data
.size
= 0;
300 dma_play_data
.core
= 0; /* no core in control */
304 void pcm_play_dma_pause(bool pause
)
313 size_t pcm_get_bytes_waiting(void)
315 return dma_play_data
.size
& ~3;
318 void pcm_play_dma_init(void)
320 pcm_set_frequency(SAMPR_44
);
322 /* Initialize default register values. */
325 #if !defined(HAVE_WM8731) && !defined(HAVE_WM8751)
327 audiohw_enable_output(true);
328 /* Unmute the master channel (DAC should be at zero point now). */
332 dma_play_data
.size
= 0;
334 dma_play_data
.core
= 0; /* no core in control */
337 IISCONFIG
|= IIS_TXFIFOEN
;
340 void pcm_postinit(void)
343 pcm_apply_settings();
346 const void * pcm_play_dma_get_peak_buffer(int *count
)
348 unsigned long addr
= (unsigned long)dma_play_data
.p
;
349 size_t cnt
= dma_play_data
.size
;
351 return (void *)((addr
+ 2) & ~3);
354 /****************************************************************************
355 ** Recording DMA transfer
357 #ifdef HAVE_RECORDING
358 /* PCM recording interrupt routine lockout */
359 static struct dma_data dma_rec_data NOCACHEBSS_ATTR
=
361 /* Initialize to a locked, stopped state */
371 /* For the locks, FIQ must be disabled because the handler manipulates
372 IISCONFIG and the operation is not atomic - dual core support
373 will require other measures */
374 void pcm_rec_lock(void)
376 int status
= disable_fiq_save();
378 if (++dma_rec_data
.locked
== 1)
379 IIS_IRQRX_REG
&= ~IIS_IRQRX
;
384 void pcm_rec_unlock(void)
386 int status
= disable_fiq_save();
388 if (--dma_rec_data
.locked
== 0 && dma_rec_data
.state
!= 0)
389 IIS_IRQRX_REG
|= IIS_IRQRX
;
394 /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
395 void fiq_record(void) ICODE_ATTR
__attribute__((interrupt ("FIQ")));
397 #if defined(SANSA_C200) || defined(SANSA_E200)
398 void fiq_record(void)
400 register pcm_more_callback_type2 more_ready
;
401 register int32_t value
;
403 if (audio_channels
== 2) {
405 while (dma_rec_data
.size
> 0) {
406 if (IIS_RX_FULL_COUNT
< 2) {
410 /* Discard every other sample since ADC clock is 1/2 LRCK */
414 *dma_rec_data
.p
++ = value
;
415 dma_rec_data
.size
-= 4;
417 /* TODO: Figure out how to do IIS loopback */
418 if (audio_output_source
!= AUDIO_SRC_PLAYBACK
) {
419 if (IIS_TX_FREE_COUNT
>= 16) {
420 /* Resync the output FIFO - it ran dry */
430 /* RX is left channel mono */
431 while (dma_rec_data
.size
> 0) {
432 if (IIS_RX_FULL_COUNT
< 2) {
436 /* Discard every other sample since ADC clock is 1/2 LRCK */
440 value
= (uint16_t)value
| (value
<< 16);
442 *dma_rec_data
.p
++ = value
;
443 dma_rec_data
.size
-= 4;
445 if (audio_output_source
!= AUDIO_SRC_PLAYBACK
) {
446 if (IIS_TX_FREE_COUNT
>= 16) {
447 /* Resync the output FIFO - it ran dry */
452 value
= *((int32_t *)dma_rec_data
.p
- 1);
459 more_ready
= pcm_callback_more_ready
;
461 if (more_ready
== NULL
|| more_ready(0) < 0) {
462 /* Finished recording */
464 pcm_rec_dma_stopped_callback();
469 void fiq_record(void)
471 register pcm_more_callback_type2 more_ready
;
473 while (dma_rec_data
.size
> 0) {
474 if (IIS_RX_FULL_COUNT
< 2) {
478 #if SAMPLE_SIZE == 16
479 *dma_rec_data
.p
++ = IISFIFO_RD
;
480 #elif SAMPLE_SIZE == 32
481 *dma_rec_data
.p
++ = IISFIFO_RD
>> 16;
482 *dma_rec_data
.p
++ = IISFIFO_RD
>> 16;
484 dma_rec_data
.size
-= 4;
487 more_ready
= pcm_callback_more_ready
;
489 if (more_ready
== NULL
|| more_ready(0) < 0) {
490 /* Finished recording */
492 pcm_rec_dma_stopped_callback();
496 #endif /* SANSA_E200 */
498 /* Continue transferring data in */
499 void pcm_record_more(void *start
, size_t size
)
501 pcm_rec_peak_addr
= start
; /* Start peaking at dest */
502 dma_rec_data
.p
= start
; /* Start of RX buffer */
503 dma_rec_data
.size
= size
; /* Bytes to transfer */
506 void pcm_rec_dma_stop(void)
508 /* disable interrupt */
509 IIS_IRQRX_REG
&= ~IIS_IRQRX
;
511 dma_rec_data
.state
= 0;
512 dma_rec_data
.size
= 0;
514 dma_rec_data
.core
= 0x00;
518 IISCONFIG
&= ~IIS_RXFIFOEN
;
519 IISFIFO_CFG
|= IIS_RXCLR
;
522 void pcm_rec_dma_start(void *addr
, size_t size
)
526 pcm_rec_peak_addr
= addr
;
527 dma_rec_data
.p
= addr
;
528 dma_rec_data
.size
= size
;
530 /* This will become more important later - and different ! */
531 dma_rec_data
.core
= processor_id(); /* save initiating core */
533 /* setup FIQ handler */
534 fiq_function
= fiq_record
;
536 /* interrupt on full fifo, enable record fifo interrupt */
537 dma_rec_data
.state
= 1;
540 IISCONFIG
|= IIS_RXFIFOEN
;
542 /* enable IIS interrupt as FIQ */
543 CPU_INT_PRIORITY
|= IIS_MASK
;
544 CPU_INT_EN
= IIS_MASK
;
547 void pcm_rec_dma_close(void)
550 } /* pcm_close_recording */
552 void pcm_rec_dma_init(void)
554 #if defined(IPOD_COLOR) || defined (IPOD_4G)
555 /* The usual magic from IPL - I'm guessing this configures the headphone
556 socket to be input or output - in this case, input. */
557 GPIOI_OUTPUT_VAL
&= ~0x40;
558 GPIOA_OUTPUT_VAL
&= ~0x4;
564 const void * pcm_rec_dma_get_peak_buffer(int *count
)
566 unsigned long addr
= (unsigned long)pcm_rec_peak_addr
;
567 unsigned long end
= (unsigned long)dma_rec_data
.p
;
568 *count
= (end
>> 2) - (addr
>> 2);
569 return (void *)(addr
& ~3);
570 } /* pcm_rec_dma_get_peak_buffer */
572 #endif /* HAVE_RECORDING */