diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2008-12-07 00:07:47 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2008-12-07 00:07:47 +0000 |
commit | 89da4328a07c8736c42843607a3f3bf91c17601d (patch) | |
tree | cbd5d6580b2c62dad5830c043d9284ff15cf7bdf /firmware/target/arm/s3c2440/gigabeat-fx | |
parent | 3648e8705402ce5a0af2125987f12c884b540eea (diff) | |
download | rockbox-89da4328a07c8736c42843607a3f3bf91c17601d.tar.gz rockbox-89da4328a07c8736c42843607a3f3bf91c17601d.zip |
Meg F/X can beep and click using a hardware timer so let us try it out. To match things up better, fix PCM beeping to give correct frequency (and get a pointer wrap bug too). Do some minor adjustments to compensate for corrections.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19355 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/s3c2440/gigabeat-fx')
3 files changed, 110 insertions, 6 deletions
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c index 76917c8c82..5ef8c8023a 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c @@ -32,18 +32,18 @@ void tick_start(unsigned int interval_in_ms) * Timer input clock frequency = * fPCLK / {prescaler value+1} / {divider value} * TIMER_FREQ = 49156800 / 2 - * 13300 = TIMER_FREQ / 231 / 8 - * 49156800 = 19*(11)*(7)*7*5*5*(3)*2*2*2*2*2*2 - * 231 = 11*7*3 + * 146300 = TIMER_FREQ / 21 / 8 + * 49156800 = 19*11*(7)*7*5*5*(3)*2*2*2*2*2*2 + * 21 = 7*3 */ /* stop timer 4 */ TCON &= ~(1 << 20); /* Set the count for timer 4 */ - TCNTB4 = (TIMER_FREQ / 231 / 8) * interval_in_ms / 1000; + TCNTB4 = (TIMER_FREQ / TIMER234_PRESCALE / 8) * interval_in_ms / 1000; /* Set the the prescaler value for timers 2,3, and 4 */ - TCFG0 = (TCFG0 & ~0xff00) | ((231-1) << 8); - /* MUX4 = 1/16 */ + TCFG0 = (TCFG0 & ~0xff00) | ((TIMER234_PRESCALE-1) << 8); + /* DMA mode off, MUX4 = 1/16 */ TCFG1 = (TCFG1 & ~0xff0000) | 0x030000; /* set manual bit */ TCON |= 1 << 21; diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h b/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h index ac195bf9a5..b5652a3365 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h +++ b/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h @@ -23,6 +23,7 @@ /* timer is based on PCLK and minimum division is 2 */ #define TIMER_FREQ (49156800/2) +#define TIMER234_PRESCALE 21 bool __timer_set(long cycles, bool set); bool __timer_register(void); diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c index 957d58b344..de965f0750 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c @@ -29,8 +29,14 @@ #include "kernel.h" #include "sound.h" #include "i2c-meg-fx.h" +#include "system-target.h" +#include "timer-target.h" #include "wmcodec.h" +#ifdef HAVE_HARDWARE_BEEP +static void beep_stop(void); +#endif + void audiohw_init(void) { /* GPC5 controls headphone output */ @@ -39,6 +45,14 @@ void audiohw_init(void) GPCDAT |= (1 << 5); audiohw_preinit(); + +#ifdef HAVE_HARDWARE_BEEP + /* pin pullup ON */ + GPBUP &= ~(1 << 3); + beep_stop(); + /* set pin to TIMER3 output (functional TOUT3) */ + GPBCON = (GPBCON & ~(0x3 << 6)) | (2 << 6); +#endif } void wmcodec_write(int reg, int data) @@ -48,3 +62,92 @@ void wmcodec_write(int reg, int data) d[1] = data; i2c_write(0x34, d, 2); } + +#ifdef HAVE_HARDWARE_BEEP +/** Beeping via TIMER3 output to codec MONO input **/ +static int beep_cycles = 0; +static int beep_cycle_count = 0; + +static void beep_stop(void) +{ + int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); + + /* stop interrupt */ + INTMSK |= TIMER3_MASK; + /* stop timer */ + TCON &= ~(1 << 16); + /* be sure timer PWM pin is LOW to avoid noise */ + TCON ^= (GPBDAT & (1 << 3)) << 15; + /* clear pending */ + SRCPND = TIMER3_MASK; + INTPND = TIMER3_MASK; + + restore_interrupt(oldstatus); +} + +/* Timer interrupt called on every cycle */ +void TIMER3(void) +{ + if (++beep_cycles >= beep_cycle_count) + { + /* beep is complete */ + beep_stop(); + } + + /* clear pending */ + SRCPND = TIMER3_MASK; + INTPND = TIMER3_MASK; +} + +void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) +{ + #define TIMER3_TICK_SEC (TIMER_FREQ / TIMER234_PRESCALE) + + unsigned long tcnt, tcmp; + int oldstatus; + + if (amplitude <= 0) + { + beep_stop(); /* won't hear it anyway */ + return; + } + + /* pretend this is pcm */ + if (amplitude > 32767) + amplitude = 32767; + + /* limit frequency range to keep math in range */ + if (frequency > 19506) + frequency = 19506; + else if (frequency < 18) + frequency = 18; + + /* set timer */ + tcnt = TIMER3_TICK_SEC / frequency; + + oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); + + beep_cycles = 0; + beep_cycle_count = TIMER3_TICK_SEC * duration / (tcnt*1000); + + /* divider = 1/2 */ + TCFG1 = (TCFG1 & ~(0xf << 12)) | (0 << 12); + /* stop TIMER3, inverter OFF */ + TCON &= ~((1 << 18) | (1 << 16)); + /* set countdown */ + TCNTB3 = tcnt; + /* set PWM counter - control volume with duty cycle. */ + tcmp = tcnt*amplitude / (65536*2 - 2*amplitude); + TCMPB3 = tcmp < 1 ? 1 : tcmp; + /* manual update: on (to reset count), interval mode (auto reload) */ + TCON |= (1 << 19) | (1 << 17); + /* clear manual bit */ + TCON &= ~(1 << 17); + /* start timer */ + TCON |= (1 << 16); + /* enable timer interrupt */ + INTMSK &= ~TIMER3_MASK; + + restore_interrupt(oldstatus); +} +#endif /* HAVE_HARDWARE_BEEP */ |