summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/action.c2
-rw-r--r--apps/pcmbuf.c27
-rw-r--r--apps/playback.c4
-rw-r--r--firmware/drivers/audio/wm8751.c11
-rw-r--r--firmware/export/config-gigabeat.h5
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c12
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h1
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c103
8 files changed, 141 insertions, 24 deletions
diff --git a/apps/action.c b/apps/action.c
index 5ceeeb896f..5f845ab272 100644
--- a/apps/action.c
+++ b/apps/action.c
@@ -130,7 +130,7 @@ static int get_action_worker(int context, int timeout,
/* Produce keyclick */
if (global_settings.keyclick && !(button & BUTTON_REL))
if (!(button & BUTTON_REPEAT) || global_settings.keyclick_repeats)
- pcmbuf_beep(5000, 2, 2500*global_settings.keyclick);
+ pcmbuf_beep(4000, 2, 2500*global_settings.keyclick);
#endif
if ((context != last_context) && ((last_button & BUTTON_REL) == 0))
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 61c6c45de2..c7db4d3101 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -954,14 +954,15 @@ bool pcmbuf_insert_buffer(char *buf, int count)
}
#endif
+#ifndef HAVE_HARDWARE_BEEP
/* Generates a constant square wave sound with a given frequency
in Hertz for a duration in milliseconds. */
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
{
- unsigned int count = 0;
- unsigned int i;
- unsigned int interval = NATIVE_FREQUENCY / frequency;
- unsigned int samples = NATIVE_FREQUENCY / 1000 * duration;
+ int i;
+ unsigned int step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
+ int32_t phase = 0;
+ int samples = NATIVE_FREQUENCY / 1000 * duration;
int32_t sample;
int16_t *bufstart;
int16_t *bufptr;
@@ -986,21 +987,17 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
bufptr = bufstart;
for (i = 0; i < samples; ++i)
{
+ int32_t amp = (phase >> 31) ^ (int32_t)amplitude;
sample = mix ? *bufptr : 0;
- *bufptr++ = clip_sample_16(sample + amplitude);
- if (bufptr > pcmbuf_end)
+ *bufptr++ = clip_sample_16(sample + amp);
+ if (bufptr >= pcmbuf_end)
bufptr = (int16_t *)audiobuffer;
sample = mix ? *bufptr : 0;
- *bufptr++ = clip_sample_16(sample + amplitude);
- if (bufptr > pcmbuf_end)
+ *bufptr++ = clip_sample_16(sample + amp);
+ if (bufptr >= pcmbuf_end)
bufptr = (int16_t *)audiobuffer;
- /* Toggle square wave edge */
- if (++count >= interval)
- {
- count = 0;
- amplitude = -amplitude;
- }
+ phase += step;
}
/* Kick off playback if required */
@@ -1009,7 +1006,7 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
pcm_play_data(NULL, (unsigned char *)bufstart, samples * 4);
}
}
-
+#endif /* HAVE_HARDWARE_BEEP */
/* Returns pcm buffer usage in percents (0 to 100). */
int pcmbuf_usage(void)
diff --git a/apps/playback.c b/apps/playback.c
index b21a3c1d46..50c4017200 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -694,7 +694,7 @@ static void audio_skip(int direction)
if (playlist_check(ci.new_track + wps_offset + direction))
{
if (global_settings.beep)
- pcmbuf_beep(5000, 100, 2500*global_settings.beep);
+ pcmbuf_beep(2000, 100, 2500*global_settings.beep);
LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", direction);
queue_post(&audio_queue, Q_AUDIO_SKIP, direction);
@@ -706,7 +706,7 @@ static void audio_skip(int direction)
{
/* No more tracks. */
if (global_settings.beep)
- pcmbuf_beep(1000, 100, 1000*global_settings.beep);
+ pcmbuf_beep(1000, 100, 1500*global_settings.beep);
}
}
diff --git a/firmware/drivers/audio/wm8751.c b/firmware/drivers/audio/wm8751.c
index 9d552b505f..2e0eb06dbf 100644
--- a/firmware/drivers/audio/wm8751.c
+++ b/firmware/drivers/audio/wm8751.c
@@ -152,6 +152,17 @@ void audiohw_postinit(void)
wmcodec_write(LEFTMIX1, LEFTMIX1_LD2LO | LEFTMIX1_LI2LO_DEFAULT);
wmcodec_write(RIGHTMIX2, RIGHTMIX2_RD2RO | RIGHTMIX2_RI2RO_DEFAULT);
+#ifdef TOSHIBA_GIGABEAT_F
+#ifdef HAVE_HARDWARE_BEEP
+ /* Single-ended mono input */
+ wmcodec_write(MONOMIX1, 0);
+
+ /* Route mono input to both outputs at 0dB */
+ wmcodec_write(LEFTMIX2, LEFTMIX2_MI2LO | LEFTMIX2_MI2LOVOL(2));
+ wmcodec_write(RIGHTMIX1, RIGHTMIX1_MI2RO | RIGHTMIX1_MI2ROVOL(2));
+#endif
+#endif
+
audiohw_mute(false);
#ifdef MROBE_100
diff --git a/firmware/export/config-gigabeat.h b/firmware/export/config-gigabeat.h
index 313bdadcdd..664befd54e 100644
--- a/firmware/export/config-gigabeat.h
+++ b/firmware/export/config-gigabeat.h
@@ -52,6 +52,11 @@
#define LCD_SLEEP_TIMEOUT (5*HZ)
#define HAVE_TOUCHPAD_SENSITIVITY_SETTING
+
+#ifndef SIMULATOR
+#define HAVE_HARDWARE_BEEP
+#endif
+
#endif /* BOOTLOADER */
#define CONFIG_KEYPAD GIGABEAT_PAD
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 */