1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Driver for WM8758 audio codec - based on datasheet for WM8983
12 * Based on code from the ipodlinux project - http://ipodlinux.org/
13 * Adapted for Rockbox in December 2005
15 * Original file: linux/arch/armnommu/mach-ipod/audio.c
17 * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org)
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation; either version 2
22 * of the License, or (at your option) any later version.
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
25 * KIND, either express or implied.
27 ****************************************************************************/
35 const struct sound_settings_info audiohw_settings
[] = {
36 [SOUND_VOLUME
] = {"dB", 0, 1, -90, 6, -25},
37 [SOUND_BASS
] = {"dB", 0, 1, -12, 12, 0},
38 [SOUND_TREBLE
] = {"dB", 0, 1, -12, 12, 0},
39 [SOUND_BALANCE
] = {"%", 0, 1,-100, 100, 0},
40 [SOUND_CHANNELS
] = {"", 0, 1, 0, 5, 0},
41 [SOUND_STEREO_WIDTH
] = {"%", 0, 5, 0, 250, 100},
43 [SOUND_LEFT_GAIN
] = {"dB", 1, 1, 0, 63, 16},
44 [SOUND_RIGHT_GAIN
] = {"dB", 1, 1, 0, 63, 16},
45 [SOUND_MIC_GAIN
] = {"dB", 1, 1, 0, 63, 16},
47 [SOUND_BASS_CUTOFF
] = {"", 0, 1, 1, 4, 1},
48 [SOUND_TREBLE_CUTOFF
] = {"", 0, 1, 1, 4, 1},
51 /* shadow registers */
52 static unsigned short eq1_reg
= EQ1_EQ3DMODE
| EQ_GAIN_VALUE(0);
53 static unsigned short eq5_reg
= EQ_GAIN_VALUE(0);
55 /* convert tenth of dB volume (-89..6) to master volume register value */
56 int tenthdb2master(int db
)
65 if (db
< VOLUME_MIN
) {
68 return (db
-VOLUME_MIN
)/10 + 1;
72 /* helper function that calculates the register setting for amplifier and
73 DAC volume out of the input from tenthdb2master() */
74 static void get_volume_params(int db
, int *dac
, int *amp
)
76 /* should never happen, set max volume for amp and dac */
81 /* set dac to max and set volume for amp (better snr) */
86 /* set amp to min and reduce dac output */
88 *dac
= (db
-33)*2 + 255;
98 int sound_val2phys(int setting
, int value
)
104 #ifdef HAVE_RECORDING
105 case SOUND_LEFT_GAIN
:
106 case SOUND_RIGHT_GAIN
:
108 result
= ((value
- 16) * 15) / 2;
119 static void audiohw_mute(bool mute
)
122 wmcodec_write(DACCTRL
, DACCTRL_SOFTMUTE
);
124 wmcodec_write(DACCTRL
, 0);
128 void audiohw_preinit(void)
130 wmcodec_write(RESET
, RESET_RESET
);
132 wmcodec_write(PWRMGMT1
, PWRMGMT1_PLLEN
| PWRMGMT1_BIASEN
133 | PWRMGMT1_VMIDSEL_5K
);
134 wmcodec_write(PWRMGMT2
, PWRMGMT2_ROUT1EN
| PWRMGMT2_LOUT1EN
);
135 wmcodec_write(PWRMGMT3
, PWRMGMT3_LOUT2EN
| PWRMGMT3_ROUT2EN
136 | PWRMGMT3_RMIXEN
| PWRMGMT3_LMIXEN
137 | PWRMGMT3_DACENR
| PWRMGMT3_DACENL
);
139 wmcodec_write(AINTFCE
, AINTFCE_IWL_16BIT
| AINTFCE_FORMAT_I2S
);
140 wmcodec_write(OUTCTRL
, OUTCTRL_VROI
);
141 wmcodec_write(CLKCTRL
, CLKCTRL_MS
); /* WM8758 is clock master */
143 audiohw_set_frequency(HW_FREQ_44
);
145 wmcodec_write(LOUTMIX
, LOUTMIX_DACL2LMIX
);
146 wmcodec_write(ROUTMIX
, ROUTMIX_DACR2RMIX
);
149 void audiohw_postinit(void)
151 wmcodec_write(PWRMGMT1
, PWRMGMT1_PLLEN
| PWRMGMT1_BIASEN
152 | PWRMGMT1_VMIDSEL_75K
);
153 /* lower the VMID power consumption */
157 void audiohw_set_master_vol(int vol_l
, int vol_r
)
159 int dac_l
, amp_l
, dac_r
, amp_r
;
160 get_volume_params(vol_l
, &dac_l
, &_l
);
161 get_volume_params(vol_r
, &dac_r
, &_r
);
164 Important: DAC is global and will also affect lineout */
165 wmcodec_write(LDACVOL
, dac_l
);
166 wmcodec_write(RDACVOL
, dac_r
| RDACVOL_DACVU
);
168 /* set headphone amp OUT1 */
169 wmcodec_write(LOUT1VOL
, amp_l
| LOUT1VOL_LOUT1ZC
);
170 wmcodec_write(ROUT1VOL
, amp_r
| ROUT1VOL_ROUT1ZC
| ROUT1VOL_OUT1VU
);
173 void audiohw_set_lineout_vol(int vol_l
, int vol_r
)
175 int dac_l
, amp_l
, dac_r
, amp_r
;
176 get_volume_params(vol_l
, &dac_l
, &_l
);
177 get_volume_params(vol_r
, &dac_r
, &_r
);
179 /* set lineout amp OUT2 */
180 wmcodec_write(LOUT2VOL
, amp_l
| LOUT2VOL_LOUT2ZC
);
181 wmcodec_write(ROUT2VOL
, amp_r
| ROUT2VOL_ROUT2ZC
| ROUT2VOL_OUT2VU
);
184 void audiohw_enable_lineout(bool enable
)
186 /* Initialize data without lineout enabling. */
187 int pwrmgmt3_data
= PWRMGMT3_RMIXEN
| PWRMGMT3_LMIXEN
188 | PWRMGMT3_DACENR
| PWRMGMT3_DACENL
;
189 /* Set lineout (OUT2), if enabled. */
191 pwrmgmt3_data
|= PWRMGMT3_LOUT2EN
| PWRMGMT3_ROUT2EN
;
194 wmcodec_write(PWRMGMT3
, pwrmgmt3_data
);
197 void audiohw_set_bass(int value
)
199 eq1_reg
= (eq1_reg
& ~EQ_GAIN_MASK
) | EQ_GAIN_VALUE(value
);
200 wmcodec_write(EQ1
, eq1_reg
);
203 void audiohw_set_bass_cutoff(int value
)
205 eq1_reg
= (eq1_reg
& ~EQ_CUTOFF_MASK
) | EQ_CUTOFF_VALUE(value
);
206 wmcodec_write(EQ1
, eq1_reg
);
209 void audiohw_set_treble(int value
)
211 eq5_reg
= (eq5_reg
& ~EQ_GAIN_MASK
) | EQ_GAIN_VALUE(value
);
212 wmcodec_write(EQ5
, eq5_reg
);
215 void audiohw_set_treble_cutoff(int value
)
217 eq5_reg
= (eq5_reg
& ~EQ_CUTOFF_MASK
) | EQ_CUTOFF_VALUE(value
);
218 wmcodec_write(EQ5
, eq5_reg
);
221 /* Nice shutdown of WM8758 codec */
222 void audiohw_close(void)
226 wmcodec_write(PWRMGMT3
, 0);
227 wmcodec_write(PWRMGMT1
, 0);
228 wmcodec_write(PWRMGMT2
, PWRMGMT2_SLEEP
);
231 /* Note: Disable output before calling this function */
232 void audiohw_set_frequency(int fsel
)
234 /* CLKCTRL_MCLKDIV_MASK and ADDCTRL_SR_MASK don't overlap,
235 so they can both fit in one byte. Bit 0 selects PLL
236 configuration via pll_setups.
238 static const unsigned char freq_setups
[HW_NUM_FREQ
] =
240 [HW_FREQ_48
] = CLKCTRL_MCLKDIV_2
| ADDCTRL_SR_48kHz
| 1,
241 [HW_FREQ_44
] = CLKCTRL_MCLKDIV_2
| ADDCTRL_SR_48kHz
,
242 [HW_FREQ_32
] = CLKCTRL_MCLKDIV_3
| ADDCTRL_SR_32kHz
| 1,
243 [HW_FREQ_24
] = CLKCTRL_MCLKDIV_4
| ADDCTRL_SR_24kHz
| 1,
244 [HW_FREQ_22
] = CLKCTRL_MCLKDIV_4
| ADDCTRL_SR_24kHz
,
245 [HW_FREQ_16
] = CLKCTRL_MCLKDIV_6
| ADDCTRL_SR_16kHz
| 1,
246 [HW_FREQ_12
] = CLKCTRL_MCLKDIV_8
| ADDCTRL_SR_12kHz
| 1,
247 [HW_FREQ_11
] = CLKCTRL_MCLKDIV_8
| ADDCTRL_SR_12kHz
,
248 [HW_FREQ_8
] = CLKCTRL_MCLKDIV_12
| ADDCTRL_SR_8kHz
| 1
251 /* Each PLL configuration is an array consisting of
252 { PLLN, PLLK1, PLLK2, PLLK3 }. The WM8983 datasheet requires
253 5 < PLLN < 13, and states optimum is PLLN = 8, f2 = 90 MHz
255 static const unsigned short pll_setups
[2][4] =
257 /* f1 = 12 MHz, R = 7.5264, f2 = 90.3168 MHz, fPLLOUT = 22.5792 MHz */
258 { PLLN_PLLPRESCALE
| 0x7, 0x21, 0x161, 0x26 },
259 /* f1 = 12 MHz, R = 8.192, f2 = 98.304 MHz, fPLLOUT = 24.576 MHz */
260 { PLLN_PLLPRESCALE
| 0x8, 0xC, 0x93, 0xE9 }
265 /* PLLN, PLLK1, PLLK2, PLLK3 are contiguous (at 0x24 to 0x27) */
266 for (i
= 0; i
< 4; i
++)
267 wmcodec_write(PLLN
+ i
, pll_setups
[freq_setups
[fsel
] & 1][i
]);
269 /* CLKCTRL_MCLKDIV divides fPLLOUT to get SYSCLK (256 * sample rate) */
270 wmcodec_write(CLKCTRL
, CLKCTRL_CLKSEL
271 | (freq_setups
[fsel
] & CLKCTRL_MCLKDIV_MASK
)
272 | CLKCTRL_BCLKDIV_2
| CLKCTRL_MS
);
274 /* set ADC and DAC filter characteristics according to sample rate */
275 wmcodec_write(ADDCTRL
, (freq_setups
[fsel
] & ADDCTRL_SR_MASK
)
276 | ADDCTRL_SLOWCLKEN
);
277 /* SLOWCLK enabled for zero cross timeout to work */
280 void audiohw_enable_recording(bool source_mic
)
282 (void)source_mic
; /* We only have a line-in (I think) */
284 wmcodec_write(PWRMGMT2
, PWRMGMT2_ROUT1EN
| PWRMGMT2_LOUT1EN
285 | PWRMGMT2_INPGAENR
| PWRMGMT2_INPGAENL
286 | PWRMGMT2_ADCENR
| PWRMGMT2_ADCENL
);
288 wmcodec_write(INCTRL
, INCTRL_R2_2INPGA
| INCTRL_L2_2INPGA
);
290 wmcodec_write(LADCBOOST
, LADCBOOST_L2_2BOOST(5));
291 wmcodec_write(RADCBOOST
, RADCBOOST_R2_2BOOST(5));
293 /* Enable monitoring */
294 wmcodec_write(LOUTMIX
, LOUTMIX_BYP2LMIXVOL(5)
295 | LOUTMIX_BYPL2LMIX
| LOUTMIX_DACL2LMIX
);
296 wmcodec_write(ROUTMIX
, ROUTMIX_BYP2RMIXVOL(5)
297 | ROUTMIX_BYPR2RMIX
| ROUTMIX_DACR2RMIX
);
300 void audiohw_disable_recording(void)
302 wmcodec_write(LOUTMIX
, LOUTMIX_DACL2LMIX
);
303 wmcodec_write(ROUTMIX
, ROUTMIX_DACR2RMIX
);
305 wmcodec_write(PWRMGMT2
, PWRMGMT2_ROUT1EN
| PWRMGMT2_LOUT1EN
);
308 void audiohw_set_recvol(int left
, int right
, int type
)
315 case AUDIO_GAIN_LINEIN
:
316 wmcodec_write(LINPGAVOL
, LINPGAVOL_INPGAZCL
317 | (left
& LINPGAVOL_INPGAVOL_MASK
));
318 wmcodec_write(RINPGAVOL
, RINPGAVOL_INPGAVU
| RINPGAVOL_INPGAZCR
319 | (right
& RINPGAVOL_INPGAVOL_MASK
));
326 void audiohw_set_monitor(bool enable
)