diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2008-05-03 15:14:52 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2008-05-03 15:14:52 +0000 |
commit | 5df4405317890cc4a84edcfe827a765b52a712c9 (patch) | |
tree | c4293ce39c1d3e6be351670179b8fac9761391db /firmware/drivers/audio | |
parent | d0e32119f1a639ab372258e4e3dbb5349bb086ec (diff) | |
download | rockbox-5df4405317890cc4a84edcfe827a765b52a712c9.tar.gz rockbox-5df4405317890cc4a84edcfe827a765b52a712c9.zip |
Gigabeat S: Man it's so loud in here. We have SOUND! Someone please make keymaps consistent; it's rather messy atm.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17327 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/audio')
-rw-r--r-- | firmware/drivers/audio/wm8978.c | 309 |
1 files changed, 305 insertions, 4 deletions
diff --git a/firmware/drivers/audio/wm8978.c b/firmware/drivers/audio/wm8978.c index 01f3d331bb..f94f1db87f 100644 --- a/firmware/drivers/audio/wm8978.c +++ b/firmware/drivers/audio/wm8978.c | |||
@@ -23,9 +23,20 @@ | |||
23 | #include "audiohw.h" | 23 | #include "audiohw.h" |
24 | #include "wmcodec.h" | 24 | #include "wmcodec.h" |
25 | #include "audio.h" | 25 | #include "audio.h" |
26 | //#define LOGF_ENABLE | ||
27 | #include "logf.h" | ||
26 | 28 | ||
27 | const struct sound_settings_info audiohw_settings[] = { | 29 | #define HW_VOL_MIN 0 |
28 | [SOUND_VOLUME] = {"dB", 0, 1, -58, 6, -25}, | 30 | #define HW_VOL_MUTE 0 |
31 | #define HW_VOL_MAX 96 | ||
32 | |||
33 | /* TODO: Define/refine an API for special hardware steps outside the | ||
34 | * main codec driver such as special GPIO handling. */ | ||
35 | extern void audiohw_enable_headphone_jack(bool enable); | ||
36 | |||
37 | const struct sound_settings_info audiohw_settings[] = | ||
38 | { | ||
39 | [SOUND_VOLUME] = {"dB", 0, 1, -90, 6, -25}, | ||
29 | [SOUND_BASS] = {"dB", 0, 1, -12, 12, 0}, | 40 | [SOUND_BASS] = {"dB", 0, 1, -12, 12, 0}, |
30 | [SOUND_TREBLE] = {"dB", 0, 1, -12, 12, 0}, | 41 | [SOUND_TREBLE] = {"dB", 0, 1, -12, 12, 0}, |
31 | [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0}, | 42 | [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0}, |
@@ -33,28 +44,318 @@ const struct sound_settings_info audiohw_settings[] = { | |||
33 | [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, | 44 | [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, |
34 | [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, | 45 | [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, |
35 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, | 46 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, |
47 | #if 0 | ||
36 | [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, | 48 | [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, |
49 | #endif | ||
37 | #if 0 | 50 | #if 0 |
38 | [SOUND_BASS_CUTOFF] = {"", 0, 1, 1, 4, 1}, | 51 | [SOUND_BASS_CUTOFF] = {"", 0, 1, 1, 4, 1}, |
39 | [SOUND_TREBLE_CUTOFF] = {"", 0, 1, 1, 4, 1}, | 52 | [SOUND_TREBLE_CUTOFF] = {"", 0, 1, 1, 4, 1}, |
40 | #endif | 53 | #endif |
41 | }; | 54 | }; |
42 | 55 | ||
56 | static uint16_t wmc_regs[WMC_NUM_REGISTERS] = | ||
57 | { | ||
58 | /* Initialized with post-reset default values - the 2-wire interface | ||
59 | * cannot be read. Or-in additional bits desired for some registers. */ | ||
60 | [0 ... WMC_NUM_REGISTERS-1] = 0x8000, /* To ID invalids in gaps */ | ||
61 | [WMC_SOFTWARE_RESET] = 0x000, | ||
62 | [WMC_POWER_MANAGEMENT1] = 0x000, | ||
63 | [WMC_POWER_MANAGEMENT2] = 0x000, | ||
64 | [WMC_POWER_MANAGEMENT3] = 0x000, | ||
65 | [WMC_AUDIO_INTERFACE] = 0x050, | ||
66 | [WMC_COMPANDING_CTRL] = 0x000, | ||
67 | [WMC_CLOCK_GEN_CTRL] = 0x140, | ||
68 | [WMC_ADDITIONAL_CTRL] = 0x000, | ||
69 | [WMC_GPIO] = 0x000, | ||
70 | [WMC_JACK_DETECT_CONTROL1] = 0x000, | ||
71 | [WMC_DAC_CONTROL] = 0x000, | ||
72 | [WMC_LEFT_DAC_DIGITAL_VOL] = 0x0ff | WMC_VU, | ||
73 | [WMC_RIGHT_DAC_DIGITAL_VOL] = 0x0ff | WMC_VU, | ||
74 | [WMC_JACK_DETECT_CONTROL2] = 0x000, | ||
75 | [WMC_ADC_CONTROL] = 0x100, | ||
76 | [WMC_LEFT_ADC_DIGITAL_VOL] = 0x0ff | WMC_VU, | ||
77 | [WMC_RIGHT_ADC_DIGITAL_VOL] = 0x0ff | WMC_VU, | ||
78 | [WMC_EQ1_LOW_SHELF] = 0x12c, | ||
79 | [WMC_EQ2_PEAK1] = 0x02c, | ||
80 | [WMC_EQ3_PEAK2] = 0x02c, | ||
81 | [WMC_EQ4_PEAK3] = 0x02c, | ||
82 | [WMC_EQ5_HIGH_SHELF] = 0x02c, | ||
83 | [WMC_DAC_LIMITER1] = 0x032, | ||
84 | [WMC_DAC_LIMITER2] = 0x000, | ||
85 | [WMC_NOTCH_FILTER1] = 0x000, | ||
86 | [WMC_NOTCH_FILTER2] = 0x000, | ||
87 | [WMC_NOTCH_FILTER3] = 0x000, | ||
88 | [WMC_NOTCH_FILTER4] = 0x000, | ||
89 | [WMC_ALC_CONTROL1] = 0x038, | ||
90 | [WMC_ALC_CONTROL2] = 0x00b, | ||
91 | [WMC_ALC_CONTROL3] = 0x032, | ||
92 | [WMC_NOISE_GATE] = 0x000, | ||
93 | [WMC_PLL_N] = 0x008, | ||
94 | [WMC_PLL_K1] = 0x00c, | ||
95 | [WMC_PLL_K2] = 0x093, | ||
96 | [WMC_PLL_K3] = 0x0e9, | ||
97 | [WMC_3D_CONTROL] = 0x000, | ||
98 | [WMC_BEEP_CONTROL] = 0x000, | ||
99 | [WMC_INPUT_CTRL] = 0x033, | ||
100 | [WMC_LEFT_INP_PGA_GAIN_CTRL] = 0x010, | ||
101 | [WMC_RIGHT_INP_PGA_GAIN_CTRL] = 0x010, | ||
102 | [WMC_LEFT_ADC_BOOST_CTRL] = 0x100, | ||
103 | [WMC_RIGHT_ADC_BOOST_CTRL] = 0x100, | ||
104 | [WMC_OUTPUT_CTRL] = 0x002, | ||
105 | [WMC_LEFT_MIXER_CTRL] = 0x001, | ||
106 | [WMC_RIGHT_MIXER_CTRL] = 0x001, | ||
107 | [WMC_LOUT1_HP_VOLUME_CTRL] = 0x039 | WMC_VU | WMC_ZC, | ||
108 | [WMC_ROUT1_HP_VOLUME_CTRL] = 0x039 | WMC_VU | WMC_ZC, | ||
109 | [WMC_LOUT2_SPK_VOLUME_CTRL] = 0x039 | WMC_VU | WMC_ZC, | ||
110 | [WMC_ROUT2_SPK_VOLUME_CTRL] = 0x039 | WMC_VU | WMC_ZC, | ||
111 | [WMC_OUT3_MIXER_CTRL] = 0x001, | ||
112 | [WMC_OUT4_MONO_MIXER_CTRL] = 0x001, | ||
113 | }; | ||
114 | |||
115 | struct | ||
116 | { | ||
117 | int vol_l; | ||
118 | int vol_r; | ||
119 | bool ahw_mute; | ||
120 | } wmc_vol = | ||
121 | { | ||
122 | 0, 0, false | ||
123 | }; | ||
124 | |||
125 | static void wmc_write(unsigned int reg, unsigned int val) | ||
126 | { | ||
127 | if (reg >= WMC_NUM_REGISTERS || (wmc_regs[reg] & 0x8000)) | ||
128 | { | ||
129 | logf("wm8978 invalid register: %d", reg); | ||
130 | return; | ||
131 | } | ||
132 | |||
133 | wmc_regs[reg] = val & ~0x8000; | ||
134 | wmcodec_write(reg, val); | ||
135 | } | ||
136 | |||
137 | static void wmc_set(unsigned int reg, unsigned int bits) | ||
138 | { | ||
139 | wmc_write(reg, wmc_regs[reg] | bits); | ||
140 | } | ||
141 | |||
142 | static void wmc_clear(unsigned int reg, unsigned int bits) | ||
143 | { | ||
144 | wmc_write(reg, wmc_regs[reg] & ~bits); | ||
145 | } | ||
146 | |||
147 | static void wmc_write_masked(unsigned int reg, unsigned int bits, | ||
148 | unsigned int mask) | ||
149 | { | ||
150 | wmc_write(reg, (wmc_regs[reg] & ~mask) | (bits & mask)); | ||
151 | } | ||
152 | |||
153 | /* convert tenth of dB volume (-890..60) to master volume register value | ||
154 | * (000000...111111) */ | ||
155 | int tenthdb2master(int db) | ||
156 | { | ||
157 | /* -90dB to +6dB 1dB steps (96 levels) 7bits */ | ||
158 | /* 1100000 == +6dB (0x60,96) */ | ||
159 | /* 1101010 == 0dB (0x5a,90) */ | ||
160 | /* 1000001 == -57dB (0x21,33,DAC) */ | ||
161 | /* 0000001 == -89dB (0x01,01) */ | ||
162 | /* 0000000 == -90dB (0x00,00,Mute) */ | ||
163 | if (db <= VOLUME_MIN) | ||
164 | { | ||
165 | return 0x0; | ||
166 | } | ||
167 | else | ||
168 | { | ||
169 | return (db - VOLUME_MIN) / 10; | ||
170 | } | ||
171 | } | ||
172 | |||
43 | void audiohw_preinit(void) | 173 | void audiohw_preinit(void) |
44 | { | 174 | { |
45 | wmcodec_write(WM8978_SOFTWARE_RESET, 0); | 175 | /* 1. Turn on external power supplies. Wait for supply voltage to settle. */ |
176 | |||
177 | /* Step 1 should be completed already. Reset and return all registers to | ||
178 | * defaults */ | ||
179 | wmcodec_write(WMC_SOFTWARE_RESET, 0xff); | ||
180 | sleep(HZ/10); | ||
181 | |||
182 | /* 2. Mute all analogue outputs */ | ||
183 | wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
184 | wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
185 | wmc_set(WMC_LOUT2_SPK_VOLUME_CTRL, WMC_MUTE); | ||
186 | wmc_set(WMC_ROUT2_SPK_VOLUME_CTRL, WMC_MUTE); | ||
187 | wmc_set(WMC_OUT3_MIXER_CTRL, WMC_MUTE); | ||
188 | wmc_set(WMC_OUT4_MONO_MIXER_CTRL, WMC_MUTE); | ||
189 | wmc_set(WMC_INPUT_CTRL, 0x000); | ||
190 | |||
191 | /* 3. Set L/RMIXEN = 1 and DACENL/R = 1 in register R3. */ | ||
192 | wmc_write(WMC_POWER_MANAGEMENT3, | ||
193 | WMC_RMIXEN | WMC_LMIXEN | WMC_DACENR | WMC_DACENL); | ||
194 | |||
195 | /* 4. Set BUFIOEN = 1 and VMIDSEL[1:0] to required value in register | ||
196 | * R1. Wait for VMID supply to settle */ | ||
197 | wmc_write(WMC_POWER_MANAGEMENT1, WMC_BUFIOEN | WMC_VMIDSEL_300K); | ||
198 | sleep(HZ/10); | ||
199 | |||
200 | /* 5. Set BIASEN = 1 in register R1. */ | ||
201 | wmc_set(WMC_POWER_MANAGEMENT1, WMC_BIASEN); | ||
46 | } | 202 | } |
47 | 203 | ||
48 | void audiohw_postinit(void) | 204 | void audiohw_postinit(void) |
49 | { | 205 | { |
50 | sleep(HZ); | 206 | sleep(HZ); |
207 | |||
208 | /* 6. Set L/ROUTEN = 1 in register R2. */ | ||
209 | wmc_write(WMC_POWER_MANAGEMENT2, WMC_LOUT1EN | WMC_ROUT1EN); | ||
210 | |||
211 | /* 7. Enable other mixers as required */ | ||
212 | |||
213 | /* 8. Enable other outputs as required */ | ||
214 | |||
215 | /* 9. Set remaining registers */ | ||
216 | wmc_write(WMC_AUDIO_INTERFACE, WMC_WL_16 | WMC_FMT_I2S | ||
217 | | WMC_DACLRSWAP | WMC_ADCLRSWAP); | ||
218 | wmc_write(WMC_DAC_CONTROL, WMC_DACOSR_128 | WMC_AMUTE); | ||
219 | |||
220 | /* Specific to HW clocking */ | ||
221 | wmc_write(WMC_CLOCK_GEN_CTRL, WMC_MCLKDIV_1_5 | WMC_BCLKDIV_8 | WMC_MS); | ||
222 | wmc_write(WMC_ADDITIONAL_CTRL, WMC_SR_48KHZ); /* 44.1 */ | ||
223 | |||
224 | /* Initialize to minimum volume */ | ||
225 | wmc_write_masked(WMC_LEFT_DAC_DIGITAL_VOL, 189, WMC_DVOL); | ||
226 | wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, 0, WMC_AVOL); | ||
227 | wmc_write_masked(WMC_RIGHT_DAC_DIGITAL_VOL, 189, WMC_DVOL); | ||
228 | wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, 0, WMC_AVOL); | ||
229 | |||
230 | /* ADC silenced */ | ||
231 | wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL); | ||
232 | wmc_write_masked(WMC_RIGHT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL); | ||
233 | |||
234 | audiohw_enable_headphone_jack(true); | ||
235 | } | ||
236 | |||
237 | void audiohw_set_headphone_vol(int vol_l, int vol_r) | ||
238 | { | ||
239 | int prev_l = wmc_vol.vol_l; | ||
240 | int prev_r = wmc_vol.vol_r; | ||
241 | int dac_l, dac_r; | ||
242 | |||
243 | wmc_vol.vol_l = vol_l; | ||
244 | wmc_vol.vol_r = vol_r; | ||
245 | |||
246 | /* When analogue volume falls below -57dB (0x00) start attenuating the | ||
247 | * DAC volume */ | ||
248 | if (vol_l >= 33) | ||
249 | { | ||
250 | if (vol_l > HW_VOL_MAX) | ||
251 | vol_l = HW_VOL_MAX; | ||
252 | |||
253 | dac_l = 255; | ||
254 | vol_l -= 33; | ||
255 | } | ||
256 | else | ||
257 | { | ||
258 | if (vol_l < HW_VOL_MIN) | ||
259 | vol_l = HW_VOL_MIN; | ||
260 | |||
261 | dac_l = 2*vol_l + 189; | ||
262 | vol_l = 0; | ||
263 | } | ||
264 | |||
265 | if (vol_r >= 33) | ||
266 | { | ||
267 | if (vol_r > HW_VOL_MAX) | ||
268 | vol_r = HW_VOL_MAX; | ||
269 | |||
270 | dac_r = 255; | ||
271 | vol_r -= 33; | ||
272 | } | ||
273 | else | ||
274 | { | ||
275 | if (vol_r < HW_VOL_MIN) | ||
276 | vol_r = HW_VOL_MIN; | ||
277 | |||
278 | dac_r = 2*vol_r + 189; | ||
279 | vol_r = 0; | ||
280 | } | ||
281 | |||
282 | /* Have to write both channels always to have the latching work */ | ||
283 | wmc_write_masked(WMC_LEFT_DAC_DIGITAL_VOL, dac_l, WMC_DVOL); | ||
284 | wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, vol_l, WMC_AVOL); | ||
285 | wmc_write_masked(WMC_RIGHT_DAC_DIGITAL_VOL, dac_r, WMC_DVOL); | ||
286 | wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, vol_r, WMC_AVOL); | ||
287 | |||
288 | if (wmc_vol.vol_l > HW_VOL_MUTE) | ||
289 | { | ||
290 | /* Not muted and going up from mute level? */ | ||
291 | if (prev_l <= HW_VOL_MUTE && !wmc_vol.ahw_mute) | ||
292 | wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
293 | } | ||
294 | else | ||
295 | { | ||
296 | /* Going to mute level? */ | ||
297 | if (prev_l > HW_VOL_MUTE) | ||
298 | wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
299 | } | ||
300 | |||
301 | if (wmc_vol.vol_r > HW_VOL_MUTE) | ||
302 | { | ||
303 | /* Not muted and going up from mute level? */ | ||
304 | if (prev_r <= HW_VOL_MIN && !wmc_vol.ahw_mute) | ||
305 | wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
306 | } | ||
307 | else | ||
308 | { | ||
309 | /* Going to mute level? */ | ||
310 | if (prev_r > HW_VOL_MUTE) | ||
311 | wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
312 | } | ||
51 | } | 313 | } |
52 | 314 | ||
53 | void audiohw_close(void) | 315 | void audiohw_close(void) |
54 | { | 316 | { |
317 | /* 1. Mute all analogue outputs */ | ||
318 | audiohw_mute(true); | ||
319 | audiohw_enable_headphone_jack(false); | ||
320 | |||
321 | /* 2. Disable power management register 1. R1 = 00 */ | ||
322 | wmc_write(WMC_POWER_MANAGEMENT1, 0x000); | ||
323 | |||
324 | /* 3. Disable power management register 2. R2 = 00 */ | ||
325 | wmc_write(WMC_POWER_MANAGEMENT2, 0x000); | ||
326 | |||
327 | /* 4. Disable power management register 3. R3 = 00 */ | ||
328 | wmc_write(WMC_POWER_MANAGEMENT3, 0x000); | ||
329 | |||
330 | /* 5. Remove external power supplies. */ | ||
55 | } | 331 | } |
56 | 332 | ||
57 | void audiohw_mute(bool mute) | 333 | void audiohw_mute(bool mute) |
58 | { | 334 | { |
59 | (void)mute; | 335 | wmc_vol.ahw_mute = mute; |
336 | |||
337 | /* No DAC mute here, please - take care of each enabled output. */ | ||
338 | if (mute) | ||
339 | { | ||
340 | wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
341 | wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
342 | } | ||
343 | else | ||
344 | { | ||
345 | /* Unmute outputs not at mute level */ | ||
346 | if (wmc_vol.vol_l > HW_VOL_MUTE) | ||
347 | wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
348 | |||
349 | if (wmc_vol.vol_r > HW_VOL_MUTE) | ||
350 | wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | |||
355 | #ifdef HAVE_RECORDING | ||
356 | /* TODO */ | ||
357 | void audiohw_set_recvol(int left, int right, int type) | ||
358 | { | ||
359 | (void)left; (void)right; (void)type; | ||
60 | } | 360 | } |
361 | #endif | ||