diff options
Diffstat (limited to 'firmware/drivers/audio/wm8975.c')
-rw-r--r-- | firmware/drivers/audio/wm8975.c | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/firmware/drivers/audio/wm8975.c b/firmware/drivers/audio/wm8975.c new file mode 100644 index 0000000000..9052236131 --- /dev/null +++ b/firmware/drivers/audio/wm8975.c | |||
@@ -0,0 +1,318 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Driver for WM8975 audio codec | ||
11 | * | ||
12 | * Based on code from the ipodlinux project - http://ipodlinux.org/ | ||
13 | * Adapted for Rockbox in December 2005 | ||
14 | * | ||
15 | * Original file: linux/arch/armnommu/mach-ipod/audio.c | ||
16 | * | ||
17 | * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org) | ||
18 | * | ||
19 | * All files in this archive are subject to the GNU General Public License. | ||
20 | * See the file COPYING in the source tree root for full license agreement. | ||
21 | * | ||
22 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
23 | * KIND, either express or implied. | ||
24 | * | ||
25 | ****************************************************************************/ | ||
26 | #include "lcd.h" | ||
27 | #include "cpu.h" | ||
28 | #include "kernel.h" | ||
29 | #include "thread.h" | ||
30 | #include "power.h" | ||
31 | #include "debug.h" | ||
32 | #include "system.h" | ||
33 | #include "sprintf.h" | ||
34 | #include "button.h" | ||
35 | #include "string.h" | ||
36 | #include "file.h" | ||
37 | #include "buffer.h" | ||
38 | #include "audio.h" | ||
39 | |||
40 | #include "wmcodec.h" | ||
41 | #include "wm8975.h" | ||
42 | #include "i2s.h" | ||
43 | |||
44 | /* use zero crossing to reduce clicks during volume changes */ | ||
45 | #define VOLUME_ZC_WAIT (1<<7) | ||
46 | |||
47 | |||
48 | |||
49 | /* convert tenth of dB volume (-730..60) to master volume register value */ | ||
50 | int tenthdb2master(int db) | ||
51 | { | ||
52 | /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */ | ||
53 | /* 1111111 == +6dB (0x7f) */ | ||
54 | /* 1111001 == 0dB (0x79) */ | ||
55 | /* 0110000 == -73dB (0x30 */ | ||
56 | /* 0101111..0000000 == mute (0x2f) */ | ||
57 | |||
58 | if (db < VOLUME_MIN) { | ||
59 | return 0x0; | ||
60 | } else { | ||
61 | return((db/10)+73+0x30); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | /* convert tenth of dB volume (-780..0) to mixer volume register value */ | ||
66 | int tenthdb2mixer(int db) | ||
67 | { | ||
68 | (void)db; | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | |||
73 | void audiohw_reset(void); | ||
74 | |||
75 | #define IPOD_PCM_LEVEL 0x65 /* -6dB */ | ||
76 | |||
77 | |||
78 | /* Silently enable / disable audio output */ | ||
79 | void audiohw_enable_output(bool enable) | ||
80 | { | ||
81 | if (enable) | ||
82 | { | ||
83 | /* reset the I2S controller into known state */ | ||
84 | i2s_reset(); | ||
85 | |||
86 | /* | ||
87 | * 1. Switch on power supplies. | ||
88 | * By default the WM8750L is in Standby Mode, the DAC is | ||
89 | * digitally muted and the Audio Interface, Line outputs | ||
90 | * and Headphone outputs are all OFF (DACMU = 1 Power | ||
91 | * Management registers 1 and 2 are all zeros). | ||
92 | */ | ||
93 | wmcodec_write(RESET, 0x1ff); /*Reset*/ | ||
94 | wmcodec_write(RESET, 0x0); | ||
95 | |||
96 | /* 2. Enable Vmid and VREF. */ | ||
97 | wmcodec_write(PWRMGMT1, 0xc0); /*Pwr Mgmt(1)*/ | ||
98 | |||
99 | /* From app notes: allow Vref to stabilize to reduce clicks */ | ||
100 | sleep(HZ/4); | ||
101 | |||
102 | /* 3. Enable DACs as required. */ | ||
103 | wmcodec_write(PWRMGMT2, 0x180); /*Pwr Mgmt(2)*/ | ||
104 | |||
105 | /* 4. Enable line and / or headphone output buffers as required. */ | ||
106 | wmcodec_write(PWRMGMT2, 0x1f8); /*Pwr Mgmt(2)*/ | ||
107 | |||
108 | /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */ | ||
109 | /* IWL=00(16 bit) FORMAT=10(I2S format) */ | ||
110 | wmcodec_write(AINTFCE, 0x42); | ||
111 | |||
112 | /* The iPod can handle multiple frequencies, but fix at 44.1KHz for now */ | ||
113 | audiohw_set_sample_rate(WM8975_44100HZ); | ||
114 | |||
115 | /* set the volume to -6dB */ | ||
116 | wmcodec_write(LOUT1VOL, VOLUME_ZC_WAIT | IPOD_PCM_LEVEL); | ||
117 | wmcodec_write(ROUT1VOL, VOLUME_ZC_WAIT | 0x100 | IPOD_PCM_LEVEL); | ||
118 | |||
119 | wmcodec_write(LOUTMIX1, 0x150); /* Left out Mix(def) */ | ||
120 | wmcodec_write(LOUTMIX2, 0x50); | ||
121 | |||
122 | wmcodec_write(ROUTMIX1, 0x50); /* Right out Mix(def) */ | ||
123 | wmcodec_write(ROUTMIX2, 0x150); | ||
124 | |||
125 | wmcodec_write(MOUTMIX1, 0x0); /* Mono out Mix */ | ||
126 | wmcodec_write(MOUTMIX2, 0x0); | ||
127 | |||
128 | audiohw_mute(0); | ||
129 | } else { | ||
130 | audiohw_mute(1); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | |||
135 | |||
136 | int audiohw_set_master_vol(int vol_l, int vol_r) | ||
137 | { | ||
138 | /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */ | ||
139 | /* 1111111 == +6dB */ | ||
140 | /* 1111001 == 0dB */ | ||
141 | /* 0110000 == -73dB */ | ||
142 | /* 0101111 == mute (0x2f) */ | ||
143 | |||
144 | /* OUT1 */ | ||
145 | wmcodec_write(LOUT1VOL, VOLUME_ZC_WAIT | vol_l); | ||
146 | wmcodec_write(ROUT1VOL, VOLUME_ZC_WAIT | 0x100 | vol_r); | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | int audiohw_set_lineout_vol(int vol_l, int vol_r) | ||
152 | { | ||
153 | /* OUT2 */ | ||
154 | wmcodec_write(LOUT2VOL, VOLUME_ZC_WAIT | vol_l); | ||
155 | wmcodec_write(ROUT2VOL, VOLUME_ZC_WAIT | 0x100 | vol_r); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | int audiohw_set_mixer_vol(int channel1, int channel2) | ||
161 | { | ||
162 | (void)channel1; | ||
163 | (void)channel2; | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | void audiohw_set_bass(int value) | ||
169 | { | ||
170 | const int regvalues[] = { | ||
171 | 11, 10, 10, 9, 8, 8, 0xf, 6, 6, 5, 4, 4, 3, 2, 1, 0 | ||
172 | }; | ||
173 | |||
174 | if ((value >= -6) && (value <= 9)) { | ||
175 | /* We use linear bass control with 200 Hz cutoff */ | ||
176 | wmcodec_write(BASSCTRL, regvalues[value + 6] | 0x40); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | void audiohw_set_treble(int value) | ||
181 | { | ||
182 | const int regvalues[] = { | ||
183 | 11, 10, 10, 9, 8, 8, 0xf, 6, 6, 5, 4, 4, 3, 2, 1, 0 | ||
184 | }; | ||
185 | |||
186 | if ((value >= -6) && (value <= 9)) { | ||
187 | /* We use linear treble control with 4 kHz cutoff */ | ||
188 | wmcodec_write(TREBCTRL, regvalues[value + 6] | 0x40); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | int audiohw_mute(int mute) | ||
193 | { | ||
194 | if (mute) | ||
195 | { | ||
196 | /* Set DACMU = 1 to soft-mute the audio DACs. */ | ||
197 | wmcodec_write(DACCTRL, 0x8); | ||
198 | } else { | ||
199 | /* Set DACMU = 0 to soft-un-mute the audio DACs. */ | ||
200 | wmcodec_write(DACCTRL, 0x0); | ||
201 | } | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | /* Nice shutdown of WM8975 codec */ | ||
207 | void audiohw_close(void) | ||
208 | { | ||
209 | /* 1. Set DACMU = 1 to soft-mute the audio DACs. */ | ||
210 | wmcodec_write(DACCTRL, 0x8); | ||
211 | |||
212 | /* 2. Disable all output buffers. */ | ||
213 | wmcodec_write(PWRMGMT2, 0x0); /*Pwr Mgmt(2)*/ | ||
214 | |||
215 | /* 3. Switch off the power supplies. */ | ||
216 | wmcodec_write(PWRMGMT1, 0x0); /*Pwr Mgmt(1)*/ | ||
217 | } | ||
218 | |||
219 | /* Change the order of the noise shaper, 5th order is recommended above 32kHz */ | ||
220 | void audiohw_set_nsorder(int order) | ||
221 | { | ||
222 | (void)order; | ||
223 | } | ||
224 | |||
225 | /* Note: Disable output before calling this function */ | ||
226 | void audiohw_set_sample_rate(int sampling_control) { | ||
227 | |||
228 | wmcodec_write(0x08, sampling_control); | ||
229 | |||
230 | } | ||
231 | |||
232 | void audiohw_enable_recording(bool source_mic) | ||
233 | { | ||
234 | (void)source_mic; | ||
235 | |||
236 | /* reset the I2S controller into known state */ | ||
237 | i2s_reset(); | ||
238 | |||
239 | /* | ||
240 | * 1. Switch on power supplies. | ||
241 | * By default the WM8750L is in Standby Mode, the DAC is | ||
242 | * digitally muted and the Audio Interface, Line outputs | ||
243 | * and Headphone outputs are all OFF (DACMU = 1 Power | ||
244 | * Management registers 1 and 2 are all zeros). | ||
245 | */ | ||
246 | wmcodec_write(0x0f, 0x1ff); | ||
247 | wmcodec_write(0x0f, 0x000); | ||
248 | |||
249 | /* 2. Enable Vmid and VREF. */ | ||
250 | wmcodec_write(0x19, 0xc0); /*Pwr Mgmt(1)*/ | ||
251 | |||
252 | /* 3. Enable ADCs as required. */ | ||
253 | wmcodec_write(0x19, 0xcc); /*Pwr Mgmt(1)*/ | ||
254 | wmcodec_write(0x1a, 0x180); /*Pwr Mgmt(2)*/ | ||
255 | |||
256 | /* 4. Enable line and / or headphone output buffers as required. */ | ||
257 | wmcodec_write(0x19, 0xfc); /*Pwr Mgmt(1)*/ | ||
258 | |||
259 | /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */ | ||
260 | /* IWL=00(16 bit) FORMAT=10(I2S format) */ | ||
261 | wmcodec_write(0x07, 0x42); | ||
262 | |||
263 | /* The iPod can handle multiple frequencies, but fix at 44.1KHz for now */ | ||
264 | audiohw_set_sample_rate(WM8975_44100HZ); | ||
265 | |||
266 | /* unmute inputs */ | ||
267 | wmcodec_write(0x00, 0x17); /* LINVOL (def 0dB) */ | ||
268 | wmcodec_write(0x01, 0x117); /* RINVOL (def 0dB) */ | ||
269 | |||
270 | wmcodec_write(0x15, 0x1d7); /* LADCVOL max vol x was ff */ | ||
271 | wmcodec_write(0x16, 0x1d7); /* RADCVOL max vol x was ff */ | ||
272 | |||
273 | if (source_mic) { | ||
274 | /* VSEL=10(def) DATSEL=10 (use right ADC only) */ | ||
275 | wmcodec_write(0x17, 0xc9); /* Additional control(1) */ | ||
276 | |||
277 | /* VROI=1 (sets output resistance to 40kohms) */ | ||
278 | wmcodec_write(0x1b, 0x40); /* Additional control(3) */ | ||
279 | |||
280 | /* LINSEL=1 (LINPUT2) LMICBOOST=10 (20dB boost) */ | ||
281 | wmcodec_write(0x20, 0x60); /* ADCL signal path */ | ||
282 | wmcodec_write(0x21, 0x60); /* ADCR signal path */ | ||
283 | } else { | ||
284 | /* VSEL=10(def) DATSEL=00 (left->left, right->right) */ | ||
285 | wmcodec_write(0x17, 0xc1); /* Additional control(1) */ | ||
286 | |||
287 | /* VROI=1 (sets output resistance to 40kohms) */ | ||
288 | wmcodec_write(0x1b, 0x40); /* Additional control(3) */ | ||
289 | |||
290 | /* LINSEL=0 (LINPUT1) LMICBOOST=00 (bypass boost) */ | ||
291 | wmcodec_write(0x20, 0x00); /* ADCL signal path */ | ||
292 | /* RINSEL=0 (RINPUT1) RMICBOOST=00 (bypass boost) */ | ||
293 | wmcodec_write(0x21, 0x00); /* ADCR signal path */ | ||
294 | } | ||
295 | } | ||
296 | |||
297 | void audiohw_disable_recording(void) { | ||
298 | /* 1. Set DACMU = 1 to soft-mute the audio DACs. */ | ||
299 | wmcodec_write(0x05, 0x8); | ||
300 | |||
301 | /* 2. Disable all output buffers. */ | ||
302 | wmcodec_write(0x1a, 0x0); /*Pwr Mgmt(2)*/ | ||
303 | |||
304 | /* 3. Switch off the power supplies. */ | ||
305 | wmcodec_write(0x19, 0x0); /*Pwr Mgmt(1)*/ | ||
306 | } | ||
307 | |||
308 | void audiohw_set_recvol(int left, int right, int type) { | ||
309 | |||
310 | (void)left; | ||
311 | (void)right; | ||
312 | (void)type; | ||
313 | } | ||
314 | |||
315 | void audiohw_set_monitor(int enable) { | ||
316 | |||
317 | (void)enable; | ||
318 | } | ||