summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c')
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c103
1 files changed, 103 insertions, 0 deletions
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 */