diff options
Diffstat (limited to 'firmware/drivers/audio')
-rw-r--r-- | firmware/drivers/audio/wm8985.c | 296 |
1 files changed, 251 insertions, 45 deletions
diff --git a/firmware/drivers/audio/wm8985.c b/firmware/drivers/audio/wm8985.c index ad19a589fe..163e9079be 100644 --- a/firmware/drivers/audio/wm8985.c +++ b/firmware/drivers/audio/wm8985.c | |||
@@ -7,8 +7,6 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Stubs for WM8985 audio codec, (unwisely?) based on 8975 driver. | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | 10 | * All files in this archive are subject to the GNU General Public License. |
13 | * See the file COPYING in the source tree root for full license agreement. | 11 | * See the file COPYING in the source tree root for full license agreement. |
14 | * | 12 | * |
@@ -25,117 +23,325 @@ | |||
25 | #include "audiohw.h" | 23 | #include "audiohw.h" |
26 | #include "i2s.h" | 24 | #include "i2s.h" |
27 | 25 | ||
28 | /* TODO: fix these values, they're copied straight from WM8975 */ | 26 | /* Register addresses as per datasheet Rev.4.4 */ |
27 | #define RESET 0x00 | ||
28 | #define PWRMGMT1 0x01 | ||
29 | #define PWRMGMT2 0x02 | ||
30 | #define PWRMGMT3 0x03 | ||
31 | #define AINTFCE 0x04 | ||
32 | #define COMPAND 0x05 | ||
33 | #define CLKGEN 0x06 | ||
34 | #define SRATECTRL 0x07 | ||
35 | #define GPIOCTL 0x08 | ||
36 | #define JACKDETECT0 0x09 | ||
37 | #define DACCTRL 0x0a | ||
38 | #define LDACVOL 0x0b | ||
39 | #define RDACVOL 0x0c | ||
40 | #define JACKDETECT1 0x0d | ||
41 | #define ADCCTL 0x0e | ||
42 | #define LADCVOL 0x0f | ||
43 | #define RADCVOL 0x10 | ||
44 | |||
45 | #define EQ1 0x12 | ||
46 | #define EQ2 0x13 | ||
47 | #define EQ3 0x14 | ||
48 | #define EQ4 0x15 | ||
49 | #define EQ5 0x16 | ||
50 | #define EQ_GAIN_MASK 0x001f | ||
51 | #define EQ_CUTOFF_MASK 0x0060 | ||
52 | #define EQ_GAIN_VALUE(x) (((-x) + 12) & 0x1f) | ||
53 | #define EQ_CUTOFF_VALUE(x) ((((x) - 1) & 0x03) << 5) | ||
54 | |||
55 | #define CLASSDCTL 0x17 | ||
56 | #define DACLIMIT1 0x18 | ||
57 | #define DACLIMIT2 0x19 | ||
58 | #define NOTCH1 0x1b | ||
59 | #define NOTCH2 0x1c | ||
60 | #define NOTCH3 0x1d | ||
61 | #define NOTCH4 0x1e | ||
62 | #define ALCCTL1 0x20 | ||
63 | #define ALCCTL2 0x21 | ||
64 | #define ALCCTL3 0x22 | ||
65 | #define NOISEGATE 0x23 | ||
66 | #define PLLN 0x24 | ||
67 | #define PLLK1 0x25 | ||
68 | #define PLLK2 0x26 | ||
69 | #define PLLK3 0x27 | ||
70 | #define THREEDCTL 0x29 | ||
71 | #define OUT4ADC 0x2a | ||
72 | #define BEEPCTRL 0x2b | ||
73 | #define INCTRL 0x2c | ||
74 | #define LINPGAGAIN 0x2d | ||
75 | #define RINPGAGAIN 0x2e | ||
76 | #define LADCBOOST 0x2f | ||
77 | #define RADCBOOST 0x30 | ||
78 | #define OUTCTRL 0x31 | ||
79 | #define LOUTMIX 0x32 | ||
80 | #define ROUTMIX 0x33 | ||
81 | #define LOUT1VOL 0x34 | ||
82 | #define ROUT1VOL 0x35 | ||
83 | #define LOUT2VOL 0x36 | ||
84 | #define ROUT2VOL 0x37 | ||
85 | #define OUT3MIX 0x38 | ||
86 | #define OUT4MIX 0x39 | ||
87 | #define BIASCTL 0x3d | ||
88 | |||
89 | /* Register settings for the supported samplerates: */ | ||
90 | #define WM8985_8000HZ 0x4d | ||
91 | #define WM8985_12000HZ 0x61 | ||
92 | #define WM8985_16000HZ 0x55 | ||
93 | #define WM8985_22050HZ 0x77 | ||
94 | #define WM8985_24000HZ 0x79 | ||
95 | #define WM8985_32000HZ 0x59 | ||
96 | #define WM8985_44100HZ 0x63 | ||
97 | #define WM8985_48000HZ 0x41 | ||
98 | #define WM8985_88200HZ 0x7f | ||
99 | #define WM8985_96000HZ 0x5d | ||
100 | |||
29 | const struct sound_settings_info audiohw_settings[] = { | 101 | const struct sound_settings_info audiohw_settings[] = { |
30 | [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25}, | 102 | [SOUND_VOLUME] = {"dB", 0, 1, -58, 6, -25}, |
31 | [SOUND_BASS] = {"dB", 0, 1, -6, 9, 0}, | 103 | [SOUND_BASS] = {"dB", 0, 1, -12, 12, 0}, |
32 | [SOUND_TREBLE] = {"dB", 0, 1, -6, 9, 0}, | 104 | [SOUND_TREBLE] = {"dB", 0, 1, -12, 12, 0}, |
33 | [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0}, | 105 | [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0}, |
34 | [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, | 106 | [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, |
35 | [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, | 107 | [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, |
36 | [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, | 108 | [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, |
37 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, | 109 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, |
38 | [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, | 110 | [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, |
111 | [SOUND_BASS_CUTOFF] = {"", 0, 1, 1, 4, 1}, | ||
112 | [SOUND_TREBLE_CUTOFF] = {"", 0, 1, 1, 4, 1}, | ||
39 | }; | 113 | }; |
40 | 114 | ||
41 | /* convert tenth of dB volume to master volume register value */ | 115 | /* shadow registers */ |
116 | unsigned int eq1_reg; | ||
117 | unsigned int eq5_reg; | ||
118 | |||
119 | /* convert tenth of dB volume (-57..6) to master volume register value */ | ||
42 | int tenthdb2master(int db) | 120 | int tenthdb2master(int db) |
43 | { | 121 | { |
44 | #warning function not implemented | 122 | /* +6 to -57dB in 1dB steps == 64 levels = 6 bits */ |
45 | 123 | /* 0111111 == +6dB (0x3f) = 63) */ | |
46 | (void)db; | 124 | /* 0111001 == 0dB (0x39) = 57) */ |
47 | return 0; | 125 | /* 0000001 == -56dB (0x01) = */ |
126 | /* 0000000 == -57dB (0x00) */ | ||
127 | |||
128 | /* 1000000 == Mute (0x40) */ | ||
129 | |||
130 | if (db < VOLUME_MIN) { | ||
131 | return 0x40; | ||
132 | } else { | ||
133 | return((db/10)+57); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | /* convert tenth of dB volume (-780..0) to mixer volume register value */ | ||
138 | int tenthdb2mixer(int db) | ||
139 | { | ||
140 | if (db < -660) /* 1.5 dB steps */ | ||
141 | return (2640 - db) / 15; | ||
142 | else if (db < -600) /* 0.75 dB steps */ | ||
143 | return (990 - db) * 2 / 15; | ||
144 | else if (db < -460) /* 0.5 dB steps */ | ||
145 | return (460 - db) / 5; | ||
146 | else /* 0.25 dB steps */ | ||
147 | return -db * 2 / 5; | ||
48 | } | 148 | } |
49 | 149 | ||
50 | /* Silently enable / disable audio output */ | 150 | /* Silently enable / disable audio output */ |
51 | void audiohw_enable_output(bool enable) | 151 | void audiohw_enable_output(bool enable) |
52 | { | 152 | { |
53 | #warning function not implemented | 153 | if (enable) |
54 | 154 | { | |
55 | (void)enable; | 155 | /* TODO: reset the I2S controller into known state */ |
156 | //i2s_reset(); | ||
157 | |||
158 | /* TODO: Review the power-up sequence to prevent pops (see datasheet) */ | ||
159 | |||
160 | wmcodec_write(RESET, 0x1ff); /*Reset*/ | ||
161 | |||
162 | wmcodec_write(PWRMGMT1, 0x2b); | ||
163 | wmcodec_write(PWRMGMT2, 0x180); | ||
164 | wmcodec_write(PWRMGMT3, 0x6f); | ||
165 | |||
166 | wmcodec_write(AINTFCE, 0x10); | ||
167 | wmcodec_write(CLKCTRL, 0x49); | ||
168 | |||
169 | wmcodec_write(OUTCTRL, 1); | ||
170 | |||
171 | /* The iPod can handle multiple frequencies, but fix at 44.1KHz | ||
172 | for now */ | ||
173 | audiohw_set_sample_rate(WM8985_44100HZ); | ||
174 | |||
175 | wmcodec_write(LOUTMIX,0x1); /* Enable mixer */ | ||
176 | wmcodec_write(ROUTMIX,0x1); /* Enable mixer */ | ||
177 | audiohw_mute(0); | ||
178 | } else { | ||
179 | audiohw_mute(1); | ||
180 | } | ||
56 | } | 181 | } |
57 | 182 | ||
58 | void audiohw_set_master_vol(int vol_l, int vol_r) | 183 | void audiohw_set_master_vol(int vol_l, int vol_r) |
59 | { | 184 | { |
60 | #warning function not implemented | 185 | /* OUT1 */ |
61 | 186 | wmcodec_write(LOUT1VOL, 0x080 | vol_l); | |
62 | (void)vol_l; | 187 | wmcodec_write(ROUT1VOL, 0x180 | vol_r); |
63 | (void)vol_r; | ||
64 | } | 188 | } |
65 | 189 | ||
66 | void audiohw_set_lineout_vol(int vol_l, int vol_r) | 190 | void audiohw_set_lineout_vol(int vol_l, int vol_r) |
67 | { | 191 | { |
68 | #warning function not implemented | 192 | /* OUT2 */ |
193 | wmcodec_write(LOUT2VOL, vol_l); | ||
194 | wmcodec_write(ROUT2VOL, 0x100 | vol_r); | ||
195 | } | ||
69 | 196 | ||
70 | (void)vol_l; | 197 | void audiohw_set_mixer_vol(int channel1, int channel2) |
71 | (void)vol_r; | 198 | { |
199 | (void)channel1; | ||
200 | (void)channel2; | ||
72 | } | 201 | } |
73 | 202 | ||
74 | void audiohw_set_bass(int value) | 203 | void audiohw_set_bass(int value) |
75 | { | 204 | { |
76 | #warning function not implemented | 205 | eq1_reg = (eq1_reg & ~EQ_GAIN_MASK) | EQ_GAIN_VALUE(value); |
206 | wmcodec_write(EQ1, 0x100 | eq1_reg); | ||
207 | } | ||
77 | 208 | ||
78 | (void)value; | 209 | void audiohw_set_bass_cutoff(int value) |
210 | { | ||
211 | eq1_reg = (eq1_reg & ~EQ_CUTOFF_MASK) | EQ_CUTOFF_VALUE(value); | ||
212 | wmcodec_write(EQ1, 0x100 | eq1_reg); | ||
79 | } | 213 | } |
80 | 214 | ||
81 | void audiohw_set_treble(int value) | 215 | void audiohw_set_treble(int value) |
82 | { | 216 | { |
83 | #warning function not implemented | 217 | eq5_reg = (eq5_reg & ~EQ_GAIN_MASK) | EQ_GAIN_VALUE(value); |
218 | wmcodec_write(EQ5, eq5_reg); | ||
219 | } | ||
84 | 220 | ||
85 | (void)value; | 221 | void audiohw_set_treble_cutoff(int value) |
222 | { | ||
223 | eq5_reg = (eq5_reg & ~EQ_CUTOFF_MASK) | EQ_CUTOFF_VALUE(value); | ||
224 | wmcodec_write(EQ5, eq5_reg); | ||
86 | } | 225 | } |
87 | 226 | ||
88 | void audiohw_mute(bool mute) | 227 | void audiohw_mute(bool mute) |
89 | { | 228 | { |
90 | #warning function not implemented | 229 | if (mute) |
91 | 230 | { | |
92 | (void)mute; | 231 | /* Set DACMU = 1 to soft-mute the audio DACs. */ |
232 | wmcodec_write(DACCTRL, 0x40); | ||
233 | } else { | ||
234 | /* Set DACMU = 0 to soft-un-mute the audio DACs. */ | ||
235 | wmcodec_write(DACCTRL, 0x0); | ||
236 | } | ||
93 | } | 237 | } |
94 | 238 | ||
239 | /* Nice shutdown of WM8758 codec */ | ||
95 | void audiohw_close(void) | 240 | void audiohw_close(void) |
96 | { | 241 | { |
97 | #warning function not implemented | 242 | audiohw_mute(1); |
243 | |||
244 | wmcodec_write(PWRMGMT3, 0x0); | ||
245 | |||
246 | wmcodec_write(PWRMGMT1, 0x0); | ||
247 | |||
248 | wmcodec_write(PWRMGMT2, 0x40); | ||
98 | } | 249 | } |
99 | 250 | ||
251 | /* Change the order of the noise shaper, 5th order is recommended above 32kHz */ | ||
100 | void audiohw_set_nsorder(int order) | 252 | void audiohw_set_nsorder(int order) |
101 | { | 253 | { |
102 | #warning function not implemented | ||
103 | |||
104 | (void)order; | 254 | (void)order; |
105 | } | 255 | } |
106 | 256 | ||
107 | /* Note: Disable output before calling this function */ | 257 | /* Note: Disable output before calling this function */ |
108 | void audiohw_set_sample_rate(int sampling_control) | 258 | void audiohw_set_sample_rate(int sampling_control) |
109 | { | 259 | { |
110 | #warning function not implemented | 260 | /**** We force 44.1KHz for now. ****/ |
111 | |||
112 | (void)sampling_control; | 261 | (void)sampling_control; |
262 | |||
263 | /* set clock div */ | ||
264 | wmcodec_write(CLKCTRL, 1 | (0 << 2) | (2 << 5)); | ||
265 | |||
266 | /* setup PLL for MHZ=11.2896 */ | ||
267 | wmcodec_write(PLLN, (1 << 4) | 0x7); | ||
268 | wmcodec_write(PLLK1, 0x21); | ||
269 | wmcodec_write(PLLK2, 0x161); | ||
270 | wmcodec_write(PLLK3, 0x26); | ||
271 | |||
272 | /* set clock div */ | ||
273 | wmcodec_write(CLKCTRL, 1 | (1 << 2) | (2 << 5) | (1 << 8)); | ||
274 | |||
275 | /* set srate */ | ||
276 | wmcodec_write(SRATECTRL, (0 << 1)); | ||
113 | } | 277 | } |
114 | 278 | ||
115 | void audiohw_enable_recording(bool source_mic) | 279 | void audiohw_enable_recording(bool source_mic) |
116 | { | 280 | { |
117 | #warning function not implemented | 281 | (void)source_mic; /* We only have a line-in (I think) */ |
282 | |||
283 | /* TODO: reset the I2S controller into known state */ | ||
284 | //i2s_reset(); | ||
285 | |||
286 | wmcodec_write(RESET, 0x1ff); /*Reset*/ | ||
287 | |||
288 | wmcodec_write(PWRMGMT1, 0x2b); | ||
289 | wmcodec_write(PWRMGMT2, 0x18f); /* Enable ADC - 0x0c enables left/right PGA input, and 0x03 turns on power to the ADCs */ | ||
290 | wmcodec_write(PWRMGMT3, 0x6f); | ||
291 | |||
292 | wmcodec_write(AINTFCE, 0x10); | ||
293 | wmcodec_write(CLKCTRL, 0x49); | ||
118 | 294 | ||
119 | (void)source_mic; | 295 | wmcodec_write(OUTCTRL, 1); |
296 | |||
297 | /* The iPod can handle multiple frequencies, but fix at 44.1KHz | ||
298 | for now */ | ||
299 | audiohw_set_sample_rate(WM8985_44100HZ); | ||
300 | |||
301 | wmcodec_write(INCTRL,0x44); /* Connect L2 and R2 inputs */ | ||
302 | |||
303 | /* Set L2/R2_2BOOSTVOL to 0db (bits 4-6) */ | ||
304 | /* 000 = disabled | ||
305 | 001 = -12dB | ||
306 | 010 = -9dB | ||
307 | 011 = -6dB | ||
308 | 100 = -3dB | ||
309 | 101 = 0dB | ||
310 | 110 = 3dB | ||
311 | 111 = 6dB | ||
312 | */ | ||
313 | wmcodec_write(LADCBOOST,0x50); | ||
314 | wmcodec_write(RADCBOOST,0x50); | ||
315 | |||
316 | /* Set L/R input PGA Volume to 0db */ | ||
317 | // wm8758_write(LINPGAVOL,0x3f); | ||
318 | // wm8758_write(RINPGAVOL,0x13f); | ||
319 | |||
320 | /* Enable monitoring */ | ||
321 | wmcodec_write(LOUTMIX,0x17); /* Enable output mixer - BYPL2LMIX @ 0db*/ | ||
322 | wmcodec_write(ROUTMIX,0x17); /* Enable output mixer - BYPR2RMIX @ 0db*/ | ||
323 | |||
324 | audiohw_mute(0); | ||
120 | } | 325 | } |
121 | 326 | ||
122 | void audiohw_disable_recording(void) | 327 | void audiohw_disable_recording(void) { |
123 | { | 328 | audiohw_mute(1); |
124 | #warning function not implemented | 329 | |
330 | wmcodec_write(PWRMGMT3, 0x0); | ||
331 | |||
332 | wmcodec_write(PWRMGMT1, 0x0); | ||
333 | |||
334 | wmcodec_write(PWRMGMT2, 0x40); | ||
125 | } | 335 | } |
126 | 336 | ||
127 | void audiohw_set_recvol(int left, int right, int type) | 337 | void audiohw_set_recvol(int left, int right, int type) { |
128 | { | ||
129 | #warning function not implemented | ||
130 | 338 | ||
131 | (void)left; | 339 | (void)left; |
132 | (void)right; | 340 | (void)right; |
133 | (void)type; | 341 | (void)type; |
134 | } | 342 | } |
135 | 343 | ||
136 | void audiohw_set_monitor(bool enable) | 344 | void audiohw_set_monitor(bool enable) { |
137 | { | ||
138 | #warning function not implemented | ||
139 | 345 | ||
140 | (void)enable; | 346 | (void)enable; |
141 | } | 347 | } |