summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2008-12-07 00:07:47 +0000
committerMichael Sevakis <jethead71@rockbox.org>2008-12-07 00:07:47 +0000
commit89da4328a07c8736c42843607a3f3bf91c17601d (patch)
treecbd5d6580b2c62dad5830c043d9284ff15cf7bdf
parent3648e8705402ce5a0af2125987f12c884b540eea (diff)
downloadrockbox-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
-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,
130 /* Produce keyclick */ 130 /* Produce keyclick */
131 if (global_settings.keyclick && !(button & BUTTON_REL)) 131 if (global_settings.keyclick && !(button & BUTTON_REL))
132 if (!(button & BUTTON_REPEAT) || global_settings.keyclick_repeats) 132 if (!(button & BUTTON_REPEAT) || global_settings.keyclick_repeats)
133 pcmbuf_beep(5000, 2, 2500*global_settings.keyclick); 133 pcmbuf_beep(4000, 2, 2500*global_settings.keyclick);
134#endif 134#endif
135 135
136 if ((context != last_context) && ((last_button & BUTTON_REL) == 0)) 136 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)
954} 954}
955#endif 955#endif
956 956
957#ifndef HAVE_HARDWARE_BEEP
957/* Generates a constant square wave sound with a given frequency 958/* Generates a constant square wave sound with a given frequency
958 in Hertz for a duration in milliseconds. */ 959 in Hertz for a duration in milliseconds. */
959void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) 960void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
960{ 961{
961 unsigned int count = 0; 962 int i;
962 unsigned int i; 963 unsigned int step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
963 unsigned int interval = NATIVE_FREQUENCY / frequency; 964 int32_t phase = 0;
964 unsigned int samples = NATIVE_FREQUENCY / 1000 * duration; 965 int samples = NATIVE_FREQUENCY / 1000 * duration;
965 int32_t sample; 966 int32_t sample;
966 int16_t *bufstart; 967 int16_t *bufstart;
967 int16_t *bufptr; 968 int16_t *bufptr;
@@ -986,21 +987,17 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
986 bufptr = bufstart; 987 bufptr = bufstart;
987 for (i = 0; i < samples; ++i) 988 for (i = 0; i < samples; ++i)
988 { 989 {
990 int32_t amp = (phase >> 31) ^ (int32_t)amplitude;
989 sample = mix ? *bufptr : 0; 991 sample = mix ? *bufptr : 0;
990 *bufptr++ = clip_sample_16(sample + amplitude); 992 *bufptr++ = clip_sample_16(sample + amp);
991 if (bufptr > pcmbuf_end) 993 if (bufptr >= pcmbuf_end)
992 bufptr = (int16_t *)audiobuffer; 994 bufptr = (int16_t *)audiobuffer;
993 sample = mix ? *bufptr : 0; 995 sample = mix ? *bufptr : 0;
994 *bufptr++ = clip_sample_16(sample + amplitude); 996 *bufptr++ = clip_sample_16(sample + amp);
995 if (bufptr > pcmbuf_end) 997 if (bufptr >= pcmbuf_end)
996 bufptr = (int16_t *)audiobuffer; 998 bufptr = (int16_t *)audiobuffer;
997 999
998 /* Toggle square wave edge */ 1000 phase += step;
999 if (++count >= interval)
1000 {
1001 count = 0;
1002 amplitude = -amplitude;
1003 }
1004 } 1001 }
1005 1002
1006 /* Kick off playback if required */ 1003 /* Kick off playback if required */
@@ -1009,7 +1006,7 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
1009 pcm_play_data(NULL, (unsigned char *)bufstart, samples * 4); 1006 pcm_play_data(NULL, (unsigned char *)bufstart, samples * 4);
1010 } 1007 }
1011} 1008}
1012 1009#endif /* HAVE_HARDWARE_BEEP */
1013 1010
1014/* Returns pcm buffer usage in percents (0 to 100). */ 1011/* Returns pcm buffer usage in percents (0 to 100). */
1015int pcmbuf_usage(void) 1012int 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)
694 if (playlist_check(ci.new_track + wps_offset + direction)) 694 if (playlist_check(ci.new_track + wps_offset + direction))
695 { 695 {
696 if (global_settings.beep) 696 if (global_settings.beep)
697 pcmbuf_beep(5000, 100, 2500*global_settings.beep); 697 pcmbuf_beep(2000, 100, 2500*global_settings.beep);
698 698
699 LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", direction); 699 LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", direction);
700 queue_post(&audio_queue, Q_AUDIO_SKIP, direction); 700 queue_post(&audio_queue, Q_AUDIO_SKIP, direction);
@@ -706,7 +706,7 @@ static void audio_skip(int direction)
706 { 706 {
707 /* No more tracks. */ 707 /* No more tracks. */
708 if (global_settings.beep) 708 if (global_settings.beep)
709 pcmbuf_beep(1000, 100, 1000*global_settings.beep); 709 pcmbuf_beep(1000, 100, 1500*global_settings.beep);
710 } 710 }
711} 711}
712 712
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)
152 wmcodec_write(LEFTMIX1, LEFTMIX1_LD2LO | LEFTMIX1_LI2LO_DEFAULT); 152 wmcodec_write(LEFTMIX1, LEFTMIX1_LD2LO | LEFTMIX1_LI2LO_DEFAULT);
153 wmcodec_write(RIGHTMIX2, RIGHTMIX2_RD2RO | RIGHTMIX2_RI2RO_DEFAULT); 153 wmcodec_write(RIGHTMIX2, RIGHTMIX2_RD2RO | RIGHTMIX2_RI2RO_DEFAULT);
154 154
155#ifdef TOSHIBA_GIGABEAT_F
156#ifdef HAVE_HARDWARE_BEEP
157 /* Single-ended mono input */
158 wmcodec_write(MONOMIX1, 0);
159
160 /* Route mono input to both outputs at 0dB */
161 wmcodec_write(LEFTMIX2, LEFTMIX2_MI2LO | LEFTMIX2_MI2LOVOL(2));
162 wmcodec_write(RIGHTMIX1, RIGHTMIX1_MI2RO | RIGHTMIX1_MI2ROVOL(2));
163#endif
164#endif
165
155 audiohw_mute(false); 166 audiohw_mute(false);
156 167
157#ifdef MROBE_100 168#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 @@
52#define LCD_SLEEP_TIMEOUT (5*HZ) 52#define LCD_SLEEP_TIMEOUT (5*HZ)
53 53
54#define HAVE_TOUCHPAD_SENSITIVITY_SETTING 54#define HAVE_TOUCHPAD_SENSITIVITY_SETTING
55
56#ifndef SIMULATOR
57#define HAVE_HARDWARE_BEEP
58#endif
59
55#endif /* BOOTLOADER */ 60#endif /* BOOTLOADER */
56 61
57#define CONFIG_KEYPAD GIGABEAT_PAD 62#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)
32 * Timer input clock frequency = 32 * Timer input clock frequency =
33 * fPCLK / {prescaler value+1} / {divider value} 33 * fPCLK / {prescaler value+1} / {divider value}
34 * TIMER_FREQ = 49156800 / 2 34 * TIMER_FREQ = 49156800 / 2
35 * 13300 = TIMER_FREQ / 231 / 8 35 * 146300 = TIMER_FREQ / 21 / 8
36 * 49156800 = 19*(11)*(7)*7*5*5*(3)*2*2*2*2*2*2 36 * 49156800 = 19*11*(7)*7*5*5*(3)*2*2*2*2*2*2
37 * 231 = 11*7*3 37 * 21 = 7*3
38 */ 38 */
39 39
40 /* stop timer 4 */ 40 /* stop timer 4 */
41 TCON &= ~(1 << 20); 41 TCON &= ~(1 << 20);
42 /* Set the count for timer 4 */ 42 /* Set the count for timer 4 */
43 TCNTB4 = (TIMER_FREQ / 231 / 8) * interval_in_ms / 1000; 43 TCNTB4 = (TIMER_FREQ / TIMER234_PRESCALE / 8) * interval_in_ms / 1000;
44 /* Set the the prescaler value for timers 2,3, and 4 */ 44 /* Set the the prescaler value for timers 2,3, and 4 */
45 TCFG0 = (TCFG0 & ~0xff00) | ((231-1) << 8); 45 TCFG0 = (TCFG0 & ~0xff00) | ((TIMER234_PRESCALE-1) << 8);
46 /* MUX4 = 1/16 */ 46 /* DMA mode off, MUX4 = 1/16 */
47 TCFG1 = (TCFG1 & ~0xff0000) | 0x030000; 47 TCFG1 = (TCFG1 & ~0xff0000) | 0x030000;
48 /* set manual bit */ 48 /* set manual bit */
49 TCON |= 1 << 21; 49 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 @@
23 23
24/* timer is based on PCLK and minimum division is 2 */ 24/* timer is based on PCLK and minimum division is 2 */
25#define TIMER_FREQ (49156800/2) 25#define TIMER_FREQ (49156800/2)
26#define TIMER234_PRESCALE 21
26 27
27bool __timer_set(long cycles, bool set); 28bool __timer_set(long cycles, bool set);
28bool __timer_register(void); 29bool __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 @@
29#include "kernel.h" 29#include "kernel.h"
30#include "sound.h" 30#include "sound.h"
31#include "i2c-meg-fx.h" 31#include "i2c-meg-fx.h"
32#include "system-target.h"
33#include "timer-target.h"
32#include "wmcodec.h" 34#include "wmcodec.h"
33 35
36#ifdef HAVE_HARDWARE_BEEP
37static void beep_stop(void);
38#endif
39
34void audiohw_init(void) 40void audiohw_init(void)
35{ 41{
36 /* GPC5 controls headphone output */ 42 /* GPC5 controls headphone output */
@@ -39,6 +45,14 @@ void audiohw_init(void)
39 GPCDAT |= (1 << 5); 45 GPCDAT |= (1 << 5);
40 46
41 audiohw_preinit(); 47 audiohw_preinit();
48
49#ifdef HAVE_HARDWARE_BEEP
50 /* pin pullup ON */
51 GPBUP &= ~(1 << 3);
52 beep_stop();
53 /* set pin to TIMER3 output (functional TOUT3) */
54 GPBCON = (GPBCON & ~(0x3 << 6)) | (2 << 6);
55#endif
42} 56}
43 57
44void wmcodec_write(int reg, int data) 58void wmcodec_write(int reg, int data)
@@ -48,3 +62,92 @@ void wmcodec_write(int reg, int data)
48 d[1] = data; 62 d[1] = data;
49 i2c_write(0x34, d, 2); 63 i2c_write(0x34, d, 2);
50} 64}
65
66#ifdef HAVE_HARDWARE_BEEP
67/** Beeping via TIMER3 output to codec MONO input **/
68static int beep_cycles = 0;
69static int beep_cycle_count = 0;
70
71static void beep_stop(void)
72{
73 int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS);
74
75 /* stop interrupt */
76 INTMSK |= TIMER3_MASK;
77 /* stop timer */
78 TCON &= ~(1 << 16);
79 /* be sure timer PWM pin is LOW to avoid noise */
80 TCON ^= (GPBDAT & (1 << 3)) << 15;
81 /* clear pending */
82 SRCPND = TIMER3_MASK;
83 INTPND = TIMER3_MASK;
84
85 restore_interrupt(oldstatus);
86}
87
88/* Timer interrupt called on every cycle */
89void TIMER3(void)
90{
91 if (++beep_cycles >= beep_cycle_count)
92 {
93 /* beep is complete */
94 beep_stop();
95 }
96
97 /* clear pending */
98 SRCPND = TIMER3_MASK;
99 INTPND = TIMER3_MASK;
100}
101
102void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
103{
104 #define TIMER3_TICK_SEC (TIMER_FREQ / TIMER234_PRESCALE)
105
106 unsigned long tcnt, tcmp;
107 int oldstatus;
108
109 if (amplitude <= 0)
110 {
111 beep_stop(); /* won't hear it anyway */
112 return;
113 }
114
115 /* pretend this is pcm */
116 if (amplitude > 32767)
117 amplitude = 32767;
118
119 /* limit frequency range to keep math in range */
120 if (frequency > 19506)
121 frequency = 19506;
122 else if (frequency < 18)
123 frequency = 18;
124
125 /* set timer */
126 tcnt = TIMER3_TICK_SEC / frequency;
127
128 oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS);
129
130 beep_cycles = 0;
131 beep_cycle_count = TIMER3_TICK_SEC * duration / (tcnt*1000);
132
133 /* divider = 1/2 */
134 TCFG1 = (TCFG1 & ~(0xf << 12)) | (0 << 12);
135 /* stop TIMER3, inverter OFF */
136 TCON &= ~((1 << 18) | (1 << 16));
137 /* set countdown */
138 TCNTB3 = tcnt;
139 /* set PWM counter - control volume with duty cycle. */
140 tcmp = tcnt*amplitude / (65536*2 - 2*amplitude);
141 TCMPB3 = tcmp < 1 ? 1 : tcmp;
142 /* manual update: on (to reset count), interval mode (auto reload) */
143 TCON |= (1 << 19) | (1 << 17);
144 /* clear manual bit */
145 TCON &= ~(1 << 17);
146 /* start timer */
147 TCON |= (1 << 16);
148 /* enable timer interrupt */
149 INTMSK &= ~TIMER3_MASK;
150
151 restore_interrupt(oldstatus);
152}
153#endif /* HAVE_HARDWARE_BEEP */