summaryrefslogtreecommitdiff
path: root/firmware/drivers/audio/wm8978.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2008-05-03 15:14:52 +0000
committerMichael Sevakis <jethead71@rockbox.org>2008-05-03 15:14:52 +0000
commit5df4405317890cc4a84edcfe827a765b52a712c9 (patch)
treec4293ce39c1d3e6be351670179b8fac9761391db /firmware/drivers/audio/wm8978.c
parentd0e32119f1a639ab372258e4e3dbb5349bb086ec (diff)
downloadrockbox-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/wm8978.c')
-rw-r--r--firmware/drivers/audio/wm8978.c309
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
27const 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. */
35extern void audiohw_enable_headphone_jack(bool enable);
36
37const 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
56static 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
115struct
116{
117 int vol_l;
118 int vol_r;
119 bool ahw_mute;
120} wmc_vol =
121{
122 0, 0, false
123};
124
125static 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
137static void wmc_set(unsigned int reg, unsigned int bits)
138{
139 wmc_write(reg, wmc_regs[reg] | bits);
140}
141
142static void wmc_clear(unsigned int reg, unsigned int bits)
143{
144 wmc_write(reg, wmc_regs[reg] & ~bits);
145}
146
147static 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) */
155int 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
43void audiohw_preinit(void) 173void 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
48void audiohw_postinit(void) 204void 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
237void 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
53void audiohw_close(void) 315void 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
57void audiohw_mute(bool mute) 333void 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 */
357void audiohw_set_recvol(int left, int right, int type)
358{
359 (void)left; (void)right; (void)type;
60} 360}
361#endif