diff options
Diffstat (limited to 'firmware/drivers/audio/wm8731.c')
-rw-r--r-- | firmware/drivers/audio/wm8731.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/firmware/drivers/audio/wm8731.c b/firmware/drivers/audio/wm8731.c new file mode 100644 index 0000000000..2e1a978777 --- /dev/null +++ b/firmware/drivers/audio/wm8731.c | |||
@@ -0,0 +1,324 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Driver for WM8731L audio codec | ||
11 | * | ||
12 | * Based on code from the ipodlinux project - http://ipodlinux.org/ | ||
13 | * Adapted for Rockbox in January 2006 | ||
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 "config.h" | ||
27 | #include "logf.h" | ||
28 | #include "system.h" | ||
29 | #include "string.h" | ||
30 | #include "audio.h" | ||
31 | |||
32 | #include "wmcodec.h" | ||
33 | #include "audiohw.h" | ||
34 | #include "i2s.h" | ||
35 | |||
36 | const struct sound_settings_info audiohw_settings[] = { | ||
37 | [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25}, | ||
38 | /* HAVE_SW_TONE_CONTROLS */ | ||
39 | [SOUND_BASS] = {"dB", 0, 1, -24, 24, 0}, | ||
40 | [SOUND_TREBLE] = {"dB", 0, 1, -24, 24, 0}, | ||
41 | [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0}, | ||
42 | [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, | ||
43 | [SOUND_STEREO_WIDTH] = {"%", 0, 1, 0, 255, 100}, | ||
44 | #ifdef HAVE_RECORDING | ||
45 | [SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 31, 23}, | ||
46 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 31, 23}, | ||
47 | [SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 1, 0}, | ||
48 | #endif | ||
49 | }; | ||
50 | |||
51 | /* Init values/shadows | ||
52 | * Ignore bit 8 since that only specifies "both" for updating | ||
53 | * gains */ | ||
54 | static unsigned char wm8731_regs[7] = | ||
55 | { | ||
56 | [LINVOL] = LINVOL_LINMUTE, | ||
57 | [RINVOL] = RINVOL_RINMUTE, | ||
58 | [LOUTVOL] = ROUTVOL_RZCEN, | ||
59 | [ROUTVOL] = ROUTVOL_RZCEN, | ||
60 | [AAPCTRL] = AAPCTRL_MUTEMIC | AAPCTRL_DACSEL, | ||
61 | [DAPCTRL] = DAPCTRL_DACMU | DAPCTRL_DEEMP_44KHz | DAPCTRL_ADCHPD, | ||
62 | [PDCTRL] = PDCTRL_LINEINPD | PDCTRL_MICPD | PDCTRL_ADCPD | | ||
63 | PDCTRL_OUTPD | PDCTRL_OSCPD | PDCTRL_CLKOUTPD, | ||
64 | }; | ||
65 | |||
66 | static void wm8731_write(int reg, unsigned val) | ||
67 | { | ||
68 | wm8731_regs[reg] = (unsigned char)val; | ||
69 | wmcodec_write(reg, val); | ||
70 | } | ||
71 | |||
72 | static void wm8731_write_and(int reg, unsigned bits) | ||
73 | { | ||
74 | wm8731_write(reg, wm8731_regs[reg] & bits); | ||
75 | } | ||
76 | |||
77 | static void wm8731_write_or(int reg, unsigned bits) | ||
78 | { | ||
79 | wm8731_write(reg, wm8731_regs[reg] | bits); | ||
80 | } | ||
81 | |||
82 | /* convert tenth of dB volume (-730..60) to master volume register value */ | ||
83 | int tenthdb2master(int db) | ||
84 | { | ||
85 | /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */ | ||
86 | /* 1111111 == +6dB (0x7f) */ | ||
87 | /* 1111001 == 0dB (0x79) */ | ||
88 | /* 0110000 == -73dB (0x30 */ | ||
89 | /* 0101111 == mute (0x2f) */ | ||
90 | |||
91 | if (db < VOLUME_MIN) { | ||
92 | return 0x2f; | ||
93 | } else { | ||
94 | return((db/10)+0x30+73); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /* convert tenth of dB volume (-780..0) to mixer volume register value */ | ||
99 | int tenthdb2mixer(int db) | ||
100 | { | ||
101 | if (db < -660) /* 1.5 dB steps */ | ||
102 | return (2640 - db) / 15; | ||
103 | else if (db < -600) /* 0.75 dB steps */ | ||
104 | return (990 - db) * 2 / 15; | ||
105 | else if (db < -460) /* 0.5 dB steps */ | ||
106 | return (460 - db) / 5; | ||
107 | else /* 0.25 dB steps */ | ||
108 | return -db * 2 / 5; | ||
109 | } | ||
110 | |||
111 | int sound_val2phys(int setting, int value) | ||
112 | { | ||
113 | int result; | ||
114 | |||
115 | switch(setting) | ||
116 | { | ||
117 | case SOUND_LEFT_GAIN: | ||
118 | case SOUND_RIGHT_GAIN: | ||
119 | result = (value - 23) * 15; | ||
120 | break; | ||
121 | case SOUND_MIC_GAIN: | ||
122 | result = value * 200; | ||
123 | break; | ||
124 | default: | ||
125 | result = value; | ||
126 | break; | ||
127 | } | ||
128 | |||
129 | return result; | ||
130 | } | ||
131 | |||
132 | void audiohw_mute(bool mute) | ||
133 | { | ||
134 | if (mute) { | ||
135 | /* Set DACMU = 1 to soft-mute the audio DACs. */ | ||
136 | wm8731_write_or(DAPCTRL, DAPCTRL_DACMU); | ||
137 | } else { | ||
138 | /* Set DACMU = 0 to soft-un-mute the audio DACs. */ | ||
139 | wm8731_write_and(DAPCTRL, ~DAPCTRL_DACMU); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | static void codec_set_active(int active) | ||
144 | { | ||
145 | /* set active to 0x0 or 0x1 */ | ||
146 | wmcodec_write(ACTIVECTRL, active ? ACTIVECTRL_ACTIVE : 0); | ||
147 | } | ||
148 | |||
149 | void audiohw_preinit(void) | ||
150 | { | ||
151 | i2s_reset(); | ||
152 | |||
153 | /* POWER UP SEQUENCE */ | ||
154 | /* 1) Switch on power supplies. By default the WM8731 is in Standby Mode, | ||
155 | * the DAC is digitally muted and the Audio Interface and Outputs are | ||
156 | * all OFF. */ | ||
157 | wmcodec_write(RESET, RESET_RESET); | ||
158 | |||
159 | /* 2) Set all required bits in the Power Down register (0Ch) to '0'; | ||
160 | * EXCEPT the OUTPD bit, this should be set to '1' (Default). */ | ||
161 | wm8731_write(PDCTRL, wm8731_regs[PDCTRL]); | ||
162 | |||
163 | /* 3) Set required values in all other registers except 12h (Active). */ | ||
164 | wmcodec_write(AINTFCE, AINTFCE_FORMAT_I2S | AINTFCE_IWL_16BIT | | ||
165 | AINTFCE_MS); | ||
166 | wm8731_write(AAPCTRL, wm8731_regs[AAPCTRL]); | ||
167 | wm8731_write(DAPCTRL, wm8731_regs[DAPCTRL]); | ||
168 | wmcodec_write(SAMPCTRL, WM8731_USB24_44100HZ); | ||
169 | |||
170 | /* 5) The last write of the sequence should be setting OUTPD to '0' | ||
171 | * (active) in register 0Ch, enabling the DAC signal path, free | ||
172 | * of any significant power-up noise. */ | ||
173 | wm8731_write_and(PDCTRL, ~PDCTRL_OUTPD); | ||
174 | } | ||
175 | |||
176 | void audiohw_postinit(void) | ||
177 | { | ||
178 | sleep(HZ); | ||
179 | |||
180 | /* 4) Set the ‘Active’ bit in register 12h. */ | ||
181 | codec_set_active(true); | ||
182 | |||
183 | audiohw_mute(false); | ||
184 | |||
185 | #if defined(IRIVER_H10) || defined(IRIVER_H10_5GB) | ||
186 | /* We need to enable bit 4 of GPIOL for output for sound on H10 */ | ||
187 | GPIOL_OUTPUT_VAL |= 0x10; | ||
188 | #endif | ||
189 | } | ||
190 | |||
191 | int audiohw_set_master_vol(int vol_l, int vol_r) | ||
192 | { | ||
193 | /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */ | ||
194 | /* 1111111 == +6dB */ | ||
195 | /* 1111001 == 0dB */ | ||
196 | /* 0110000 == -73dB */ | ||
197 | /* 0101111 == mute (0x2f) */ | ||
198 | wm8731_write(LOUTVOL, LOUTVOL_LZCEN | (vol_l & LOUTVOL_LHPVOL_MASK)); | ||
199 | wm8731_write(ROUTVOL, ROUTVOL_RZCEN | (vol_r & ROUTVOL_RHPVOL_MASK)); | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | /* Nice shutdown of WM8731 codec */ | ||
204 | void audiohw_close(void) | ||
205 | { | ||
206 | /* POWER DOWN SEQUENCE */ | ||
207 | /* 1) Set the OUTPD bit to '1' (power down). */ | ||
208 | wm8731_write_or(PDCTRL, PDCTRL_OUTPD); | ||
209 | /* 2) Remove the WM8731 supplies. */ | ||
210 | } | ||
211 | |||
212 | void audiohw_set_nsorder(int order) | ||
213 | { | ||
214 | static const unsigned char deemp[4] = | ||
215 | { | ||
216 | DAPCTRL_DEEMP_DISABLE, | ||
217 | DAPCTRL_DEEMP_32KHz, | ||
218 | DAPCTRL_DEEMP_44KHz, | ||
219 | DAPCTRL_DEEMP_48KHz | ||
220 | }; | ||
221 | |||
222 | if ((unsigned)order >= ARRAYLEN(deemp)) | ||
223 | order = 0; | ||
224 | |||
225 | wm8731_write(DAPCTRL, | ||
226 | (wm8731_regs[DAPCTRL] & ~DAPCTRL_DEEMP_MASK) | deemp[order]); | ||
227 | } | ||
228 | |||
229 | void audiohw_set_sample_rate(int sampling_control) | ||
230 | { | ||
231 | codec_set_active(false); | ||
232 | wmcodec_write(SAMPCTRL, sampling_control); | ||
233 | codec_set_active(true); | ||
234 | } | ||
235 | |||
236 | void audiohw_enable_recording(bool source_mic) | ||
237 | { | ||
238 | codec_set_active(false); | ||
239 | |||
240 | wm8731_regs[PDCTRL] &= ~PDCTRL_ADCPD; | ||
241 | wm8731_regs[PDCTRL] |= PDCTRL_DACPD; | ||
242 | wm8731_regs[AAPCTRL] &= ~AAPCTRL_DACSEL; | ||
243 | |||
244 | if (source_mic) { | ||
245 | wm8731_write_or(LINVOL, LINVOL_LINMUTE); | ||
246 | wm8731_write_or(RINVOL, RINVOL_RINMUTE); | ||
247 | wm8731_regs[PDCTRL] &= ~PDCTRL_MICPD; | ||
248 | wm8731_regs[PDCTRL] |= PDCTRL_LINEINPD; | ||
249 | wm8731_regs[AAPCTRL] |= AAPCTRL_INSEL | AAPCTRL_SIDETONE; | ||
250 | wm8731_regs[AAPCTRL] &= ~AAPCTRL_MUTEMIC; | ||
251 | } else { | ||
252 | wm8731_regs[PDCTRL] |= PDCTRL_MICPD; | ||
253 | wm8731_regs[PDCTRL] &= ~PDCTRL_LINEINPD; | ||
254 | wm8731_regs[AAPCTRL] |= AAPCTRL_MUTEMIC | AAPCTRL_BYPASS; | ||
255 | wm8731_regs[AAPCTRL] &= ~(AAPCTRL_INSEL | AAPCTRL_SIDETONE); | ||
256 | } | ||
257 | |||
258 | wm8731_write(PDCTRL, wm8731_regs[PDCTRL]); | ||
259 | wm8731_write(AAPCTRL, wm8731_regs[AAPCTRL]); | ||
260 | |||
261 | if (!source_mic) { | ||
262 | wm8731_regs[AAPCTRL] |= AAPCTRL_INSEL | AAPCTRL_SIDETONE; | ||
263 | wm8731_regs[AAPCTRL] &= ~(AAPCTRL_MUTEMIC | AAPCTRL_BYPASS); | ||
264 | } else { | ||
265 | wm8731_write_and(LINVOL, ~LINVOL_LINMUTE); | ||
266 | wm8731_write_and(RINVOL, ~RINVOL_RINMUTE); | ||
267 | } | ||
268 | |||
269 | codec_set_active(true); | ||
270 | } | ||
271 | |||
272 | void audiohw_disable_recording(void) | ||
273 | { | ||
274 | codec_set_active(false); | ||
275 | |||
276 | /* Mute inputs */ | ||
277 | wm8731_write_or(LINVOL, LINVOL_LINMUTE); | ||
278 | wm8731_write_or(RINVOL, RINVOL_RINMUTE); | ||
279 | wm8731_write_or(AAPCTRL, AAPCTRL_MUTEMIC); | ||
280 | |||
281 | /* Turn off input analog audio paths */ | ||
282 | wm8731_regs[AAPCTRL] &= ~(AAPCTRL_BYPASS | AAPCTRL_SIDETONE); | ||
283 | wm8731_write(AAPCTRL, wm8731_regs[AAPCTRL]); | ||
284 | |||
285 | /* Set power config */ | ||
286 | wm8731_regs[PDCTRL] &= ~PDCTRL_DACPD; | ||
287 | wm8731_regs[PDCTRL] |= PDCTRL_MICPD | PDCTRL_LINEINPD | | ||
288 | PDCTRL_ADCPD; | ||
289 | wm8731_write(PDCTRL, wm8731_regs[PDCTRL]); | ||
290 | |||
291 | /* Select DAC */ | ||
292 | wm8731_write_or(AAPCTRL, AAPCTRL_DACSEL); | ||
293 | |||
294 | codec_set_active(true); | ||
295 | } | ||
296 | |||
297 | void audiohw_set_recvol(int left, int right, int type) | ||
298 | { | ||
299 | switch (type) | ||
300 | { | ||
301 | case AUDIO_GAIN_MIC: | ||
302 | if (left > 0) { | ||
303 | wm8731_write_or(AAPCTRL, AAPCTRL_MIC_BOOST); | ||
304 | } | ||
305 | else { | ||
306 | wm8731_write_and(AAPCTRL, ~AAPCTRL_MIC_BOOST); | ||
307 | } | ||
308 | break; | ||
309 | case AUDIO_GAIN_LINEIN: | ||
310 | wm8731_regs[LINVOL] &= ~LINVOL_MASK; | ||
311 | wm8731_write(LINVOL, wm8731_regs[LINVOL] | (left & LINVOL_MASK)); | ||
312 | wm8731_regs[RINVOL] &= ~RINVOL_MASK; | ||
313 | wm8731_write(RINVOL, wm8731_regs[RINVOL] | (right & RINVOL_MASK)); | ||
314 | break; | ||
315 | default: | ||
316 | return; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | void audiohw_set_monitor(int enable) | ||
321 | { | ||
322 | /* TODO: Implement for FM monitoring */ | ||
323 | (void)enable; | ||
324 | } | ||