summaryrefslogtreecommitdiff
path: root/firmware/drivers/audio/wm8975.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/audio/wm8975.c')
-rw-r--r--firmware/drivers/audio/wm8975.c317
1 files changed, 166 insertions, 151 deletions
diff --git a/firmware/drivers/audio/wm8975.c b/firmware/drivers/audio/wm8975.c
index 13cfab75ba..6809bf823f 100644
--- a/firmware/drivers/audio/wm8975.c
+++ b/firmware/drivers/audio/wm8975.c
@@ -34,9 +34,6 @@
34#include "audiohw.h" 34#include "audiohw.h"
35#include "i2s.h" 35#include "i2s.h"
36 36
37/* use zero crossing to reduce clicks during volume changes */
38#define VOLUME_ZC_WAIT (1<<7)
39
40const struct sound_settings_info audiohw_settings[] = { 37const struct sound_settings_info audiohw_settings[] = {
41 [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25}, 38 [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25},
42 [SOUND_BASS] = {"dB", 0, 1, -6, 9, 0}, 39 [SOUND_BASS] = {"dB", 0, 1, -6, 9, 0},
@@ -45,12 +42,38 @@ const struct sound_settings_info audiohw_settings[] = {
45 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, 42 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0},
46 [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, 43 [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100},
47#ifdef HAVE_RECORDING 44#ifdef HAVE_RECORDING
48 [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, 45 [SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 63, 23},
49 [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, 46 [SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 63, 23},
50 [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, 47 [SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 63, 0},
51#endif 48#endif
52}; 49};
53 50
51static unsigned short wm8975_regs[] =
52{
53 [LINVOL] = LINVOL_LZCEN | 23, /* 0dB */
54 [RINVOL] = RINVOL_RIVU | RINVOL_RZCEN | 23, /* 0dB */
55 [DAPCTRL] = DAPCTRL_DACMU,
56 [PWRMGMT1] = PWRMGMT1_VMIDSEL_5K | PWRMGMT1_VREF,
57 [PWRMGMT2] = PWRMGMT2_DACL | PWRMGMT2_DACR | PWRMGMT2_LOUT1
58 | PWRMGMT2_ROUT1 | PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2,
59};
60
61static void wm8975_write(int reg, unsigned val)
62{
63 wm8975_regs[reg] = val;
64 wmcodec_write(reg, val);
65}
66
67static void wm8975_write_and(int reg, unsigned bits)
68{
69 wm8975_write(reg, wm8975_regs[reg] & bits);
70}
71
72static void wm8975_write_or(int reg, unsigned bits)
73{
74 wm8975_write(reg, wm8975_regs[reg] | bits);
75}
76
54/* convert tenth of dB volume (-730..60) to master volume register value */ 77/* convert tenth of dB volume (-730..60) to master volume register value */
55int tenthdb2master(int db) 78int tenthdb2master(int db)
56{ 79{
@@ -67,65 +90,83 @@ int tenthdb2master(int db)
67 } 90 }
68} 91}
69 92
70#define IPOD_PCM_LEVEL 0x65 /* -6dB */ 93int sound_val2phys(int setting, int value)
94{
95 int result;
96
97 switch(setting)
98 {
99#ifdef HAVE_RECORDING
100 case SOUND_LEFT_GAIN:
101 case SOUND_RIGHT_GAIN:
102 result = ((value - 23) * 15) / 2;
103 break;
104 case SOUND_MIC_GAIN:
105 result = ((value - 23) * 15) / 2 + 200;
106 break;
107#endif
108 default:
109 result = value;
110 break;
111 }
71 112
113 return result;
114}
72 115
73/* Silently enable / disable audio output */ 116void audiohw_mute(bool mute)
74void audiohw_enable_output(bool enable)
75{ 117{
76 if (enable) 118 if (mute) {
77 { 119 /* Set DACMU = 1 to soft-mute the audio DACs. */
78 /* reset the I2S controller into known state */ 120 wm8975_write_or(DAPCTRL, DAPCTRL_DACMU);
79 i2s_reset(); 121 } else {
80 122 /* Set DACMU = 0 to soft-un-mute the audio DACs. */
81 /* 123 wm8975_write_and(DAPCTRL, ~DAPCTRL_DACMU);
82 * 1. Switch on power supplies. 124 }
83 * By default the WM8750L is in Standby Mode, the DAC is 125}
84 * digitally muted and the Audio Interface, Line outputs
85 * and Headphone outputs are all OFF (DACMU = 1 Power
86 * Management registers 1 and 2 are all zeros).
87 */
88 wmcodec_write(RESET, 0x1ff); /*Reset*/
89 wmcodec_write(RESET, 0x0);
90
91 /* 2. Enable Vmid and VREF. */
92 wmcodec_write(PWRMGMT1, 0xc0); /*Pwr Mgmt(1)*/
93 126
94 /* From app notes: allow Vref to stabilize to reduce clicks */ 127#define IPOD_PCM_LEVEL 0x65 /* -6dB */
95 sleep(HZ/4); 128
96 129void audiohw_preinit(void)
97 /* 3. Enable DACs as required. */ 130{
98 wmcodec_write(PWRMGMT2, 0x180); /*Pwr Mgmt(2)*/ 131 i2s_reset();
99 132
100 /* 4. Enable line and / or headphone output buffers as required. */ 133 /* POWER UP SEQUENCE */
101 wmcodec_write(PWRMGMT2, 0x1f8); /*Pwr Mgmt(2)*/ 134 wmcodec_write(RESET, RESET_RESET);
102 135
103 /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */ 136 /* 2. Enable Vmid and VREF, quick startup. */
104 /* IWL=00(16 bit) FORMAT=10(I2S format) */ 137 wm8975_write(PWRMGMT1, wm8975_regs[PWRMGMT1]);
105 wmcodec_write(AINTFCE, 0x42); 138 sleep(HZ/50);
139 wm8975_regs[PWRMGMT1] &= ~PWRMGMT1_VMIDSEL_MASK;
140 wm8975_write(PWRMGMT1, wm8975_regs[PWRMGMT1] | PWRMGMT1_VMIDSEL_50K);
106 141
107 /* The iPod can handle multiple frequencies, but fix at 44.1KHz for now */ 142 /* 4. Enable DACs, line and headphone output buffers as required. */
108 audiohw_set_sample_rate(WM8975_44100HZ); 143 wm8975_write(PWRMGMT2, wm8975_regs[PWRMGMT2]);
109 144
110 /* set the volume to -6dB */ 145 wmcodec_write(AINTFCE, AINTFCE_MS | AINTFCE_LRP_I2S_RLO
111 wmcodec_write(LOUT1VOL, VOLUME_ZC_WAIT | IPOD_PCM_LEVEL); 146 | AINTFCE_IWL_16BIT | AINTFCE_FORMAT_I2S);
112 wmcodec_write(ROUT1VOL, VOLUME_ZC_WAIT | 0x100 | IPOD_PCM_LEVEL); 147
148 wm8975_write(DAPCTRL, wm8975_regs[DAPCTRL] );
149
150 audiohw_set_sample_rate(WM8975_44100HZ);
113 151
114 wmcodec_write(LOUTMIX1, 0x150); /* Left out Mix(def) */ 152 /* set the volume to -6dB */
115 wmcodec_write(LOUTMIX2, 0x50); 153 wmcodec_write(LOUT1VOL, LOUT1VOL_LO1ZC | IPOD_PCM_LEVEL);
154 wmcodec_write(ROUT1VOL, ROUT1VOL_RO1VU | ROUT1VOL_RO1ZC | IPOD_PCM_LEVEL);
155
156 wmcodec_write(LOUTMIX1, LOUTMIX1_LD2LO| LOUTMIX1_LI2LOVOL(5));
157 wmcodec_write(LOUTMIX2, LOUTMIX2_RI2LOVOL(5));
116 158
117 wmcodec_write(ROUTMIX1, 0x50); /* Right out Mix(def) */ 159 wmcodec_write(ROUTMIX1, ROUTMIX1_LI2ROVOL(5));
118 wmcodec_write(ROUTMIX2, 0x150); 160 wmcodec_write(ROUTMIX2, ROUTMIX2_RD2RO| ROUTMIX2_RI2ROVOL(5));
119 161
120 wmcodec_write(MOUTMIX1, 0x0); /* Mono out Mix */ 162 wmcodec_write(MOUTMIX1, 0);
121 wmcodec_write(MOUTMIX2, 0x0); 163 wmcodec_write(MOUTMIX2, 0);
122
123 audiohw_mute(0);
124 } else {
125 audiohw_mute(1);
126 }
127} 164}
128 165
166void audiohw_postinit(void)
167{
168 audiohw_mute(false);
169}
129 170
130 171
131void audiohw_set_master_vol(int vol_l, int vol_r) 172void audiohw_set_master_vol(int vol_l, int vol_r)
@@ -137,163 +178,137 @@ void audiohw_set_master_vol(int vol_l, int vol_r)
137 /* 0101111 == mute (0x2f) */ 178 /* 0101111 == mute (0x2f) */
138 179
139 /* OUT1 */ 180 /* OUT1 */
140 wmcodec_write(LOUT1VOL, VOLUME_ZC_WAIT | vol_l); 181 wmcodec_write(LOUT1VOL, LOUT1VOL_LO1ZC | vol_l);
141 wmcodec_write(ROUT1VOL, VOLUME_ZC_WAIT | 0x100 | vol_r); 182 wmcodec_write(ROUT1VOL, ROUT1VOL_RO1VU | ROUT1VOL_RO1ZC | vol_r);
142} 183}
143 184
144void audiohw_set_lineout_vol(int vol_l, int vol_r) 185void audiohw_set_lineout_vol(int vol_l, int vol_r)
145{ 186{
146 /* OUT2 */ 187 /* OUT2 */
147 wmcodec_write(LOUT2VOL, VOLUME_ZC_WAIT | vol_l); 188 wmcodec_write(LOUT2VOL, LOUT2VOL_LO2ZC | vol_l);
148 wmcodec_write(ROUT2VOL, VOLUME_ZC_WAIT | 0x100 | vol_r); 189 wmcodec_write(ROUT2VOL, ROUT2VOL_RO2VU | ROUT2VOL_RO2ZC | vol_r);
149} 190}
150 191
151void audiohw_set_bass(int value) 192void audiohw_set_bass(int value)
152{ 193{
153 const int regvalues[] = { 194 const int regvalues[] = {
154 11, 10, 10, 9, 8, 8, 0xf, 6, 6, 5, 4, 4, 3, 2, 1, 0 195 11, 10, 10, 9, 8, 8, 0xf, 6, 6, 5, 4, 4, 3, 2, 2, 1
155 }; 196 };
156 197
157 if ((value >= -6) && (value <= 9)) { 198 if ((value >= -6) && (value <= 9)) {
158 /* We use linear bass control with 200 Hz cutoff */ 199 /* We use linear bass control with 200 Hz cutoff */
159 wmcodec_write(BASSCTRL, regvalues[value + 6] | 0x40); 200 wmcodec_write(BASSCTRL, regvalues[value + 6] | BASSCTRL_BC);
160 } 201 }
161} 202}
162 203
163void audiohw_set_treble(int value) 204void audiohw_set_treble(int value)
164{ 205{
165 const int regvalues[] = { 206 const int regvalues[] = {
166 11, 10, 10, 9, 8, 8, 0xf, 6, 6, 5, 4, 4, 3, 2, 1, 0 207 11, 10, 10, 9, 8, 8, 0xf, 6, 6, 5, 4, 4, 3, 2, 2, 1
167 }; 208 };
168 209
169 if ((value >= -6) && (value <= 9)) { 210 if ((value >= -6) && (value <= 9)) {
170 /* We use linear treble control with 4 kHz cutoff */ 211 /* We use linear treble control with 4 kHz cutoff */
171 wmcodec_write(TREBCTRL, regvalues[value + 6] | 0x40); 212 wmcodec_write(TREBCTRL, regvalues[value + 6] | TREBCTRL_TC);
172 }
173}
174
175void audiohw_mute(bool mute)
176{
177 if (mute)
178 {
179 /* Set DACMU = 1 to soft-mute the audio DACs. */
180 wmcodec_write(DACCTRL, 0x8);
181 } else {
182 /* Set DACMU = 0 to soft-un-mute the audio DACs. */
183 wmcodec_write(DACCTRL, 0x0);
184 } 213 }
185} 214}
186 215
187/* Nice shutdown of WM8975 codec */ 216/* Nice shutdown of WM8975 codec */
188void audiohw_close(void) 217void audiohw_close(void)
189{ 218{
190 /* 1. Set DACMU = 1 to soft-mute the audio DACs. */ 219 audiohw_mute(true);
191 wmcodec_write(DACCTRL, 0x8);
192 220
193 /* 2. Disable all output buffers. */ 221 /* 2. Disable all output buffers. */
194 wmcodec_write(PWRMGMT2, 0x0); /*Pwr Mgmt(2)*/ 222 wmcodec_write(PWRMGMT2, 0x0);
195 223
196 /* 3. Switch off the power supplies. */ 224 /* 3. Switch off the power supplies. */
197 wmcodec_write(PWRMGMT1, 0x0); /*Pwr Mgmt(1)*/ 225 wmcodec_write(PWRMGMT1, 0x0);
198} 226}
199 227
200/* Change the order of the noise shaper, 5th order is recommended above 32kHz */
201void audiohw_set_nsorder(int order) 228void audiohw_set_nsorder(int order)
202{ 229{
203 (void)order; 230 (void)order;
204} 231}
205 232
206/* Note: Disable output before calling this function */ 233/* Note: Disable output before calling this function */
207void audiohw_set_sample_rate(int sampling_control) { 234void audiohw_set_sample_rate(int sampling_control)
208 235{
209 wmcodec_write(0x08, sampling_control); 236 wmcodec_write(SAMPCTRL, sampling_control);
210
211} 237}
212 238
239#ifdef HAVE_RECORDING
213void audiohw_enable_recording(bool source_mic) 240void audiohw_enable_recording(bool source_mic)
214{ 241{
215 (void)source_mic; 242 wm8975_regs[PWRMGMT1] |= PWRMGMT1_AINL | PWRMGMT1_AINR
216 243 | PWRMGMT1_ADCL | PWRMGMT1_ADCR;
217 /* reset the I2S controller into known state */ 244 wm8975_write(PWRMGMT1, wm8975_regs[PWRMGMT1]);
218 i2s_reset();
219
220 /*
221 * 1. Switch on power supplies.
222 * By default the WM8750L is in Standby Mode, the DAC is
223 * digitally muted and the Audio Interface, Line outputs
224 * and Headphone outputs are all OFF (DACMU = 1 Power
225 * Management registers 1 and 2 are all zeros).
226 */
227 wmcodec_write(0x0f, 0x1ff);
228 wmcodec_write(0x0f, 0x000);
229
230 /* 2. Enable Vmid and VREF. */
231 wmcodec_write(0x19, 0xc0); /*Pwr Mgmt(1)*/
232
233 /* 3. Enable ADCs as required. */
234 wmcodec_write(0x19, 0xcc); /*Pwr Mgmt(1)*/
235 wmcodec_write(0x1a, 0x180); /*Pwr Mgmt(2)*/
236
237 /* 4. Enable line and / or headphone output buffers as required. */
238 wmcodec_write(0x19, 0xfc); /*Pwr Mgmt(1)*/
239 245
240 /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */ 246 /* NOTE: When switching to digital monitoring we will not want
241 /* IWL=00(16 bit) FORMAT=10(I2S format) */ 247 * the DACs disabled. Also the outputs shouldn't be disabled
242 wmcodec_write(0x07, 0x42); 248 * when recording from line in (dock connector) - needs testing. */
249 wm8975_regs[PWRMGMT2] &= ~(PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1
250 | PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2);
251 wm8975_write(PWRMGMT2, wm8975_regs[PWRMGMT2]);
243 252
244 /* The iPod can handle multiple frequencies, but fix at 44.1KHz for now */ 253 wm8975_write_or(LINVOL, LINVOL_LINMUTE);
245 audiohw_set_sample_rate(WM8975_44100HZ); 254 wm8975_write_or(RINVOL, RINVOL_RINMUTE);
246
247 /* unmute inputs */
248 wmcodec_write(0x00, 0x17); /* LINVOL (def 0dB) */
249 wmcodec_write(0x01, 0x117); /* RINVOL (def 0dB) */
250 255
251 wmcodec_write(0x15, 0x1d7); /* LADCVOL max vol x was ff */ 256 wmcodec_write(ADDCTRL3, ADDCTRL3_VROI);
252 wmcodec_write(0x16, 0x1d7); /* RADCVOL max vol x was ff */
253 257
254 if (source_mic) { 258 if (source_mic) {
255 /* VSEL=10(def) DATSEL=10 (use right ADC only) */ 259 wmcodec_write(ADDCTRL1, ADDCTRL1_VSEL_LOWBIAS | ADDCTRL1_DATSEL_RADC
256 wmcodec_write(0x17, 0xc9); /* Additional control(1) */ 260 | ADDCTRL1_TOEN);
257 261 wmcodec_write(ADCLPATH, 0);
258 /* VROI=1 (sets output resistance to 40kohms) */ 262 wmcodec_write(ADCRPATH, ADCRPATH_RINSEL_RIN2 | ADCRPATH_RMICBOOST_20dB);
259 wmcodec_write(0x1b, 0x40); /* Additional control(3) */
260
261 /* LINSEL=1 (LINPUT2) LMICBOOST=10 (20dB boost) */
262 wmcodec_write(0x20, 0x60); /* ADCL signal path */
263 wmcodec_write(0x21, 0x60); /* ADCR signal path */
264 } else { 263 } else {
265 /* VSEL=10(def) DATSEL=00 (left->left, right->right) */ 264 wmcodec_write(ADDCTRL1, ADDCTRL1_VSEL_LOWBIAS | ADDCTRL1_DATSEL_NORMAL
266 wmcodec_write(0x17, 0xc1); /* Additional control(1) */ 265 | ADDCTRL1_TOEN);
267 266 wmcodec_write(ADCLPATH, ADCLPATH_LINSEL_LIN1 | ADCLPATH_LMICBOOST_OFF);
268 /* VROI=1 (sets output resistance to 40kohms) */ 267 wmcodec_write(ADCRPATH, ADCRPATH_RINSEL_RIN1 | ADCRPATH_RMICBOOST_OFF);
269 wmcodec_write(0x1b, 0x40); /* Additional control(3) */
270
271 /* LINSEL=0 (LINPUT1) LMICBOOST=00 (bypass boost) */
272 wmcodec_write(0x20, 0x00); /* ADCL signal path */
273 /* RINSEL=0 (RINPUT1) RMICBOOST=00 (bypass boost) */
274 wmcodec_write(0x21, 0x00); /* ADCR signal path */
275 } 268 }
269 wm8975_write_and(LINVOL, ~LINVOL_LINMUTE);
270 wm8975_write_and(RINVOL, ~RINVOL_RINMUTE);
276} 271}
277
278void audiohw_disable_recording(void) {
279 /* 1. Set DACMU = 1 to soft-mute the audio DACs. */
280 wmcodec_write(0x05, 0x8);
281 272
282 /* 2. Disable all output buffers. */ 273void audiohw_disable_recording(void)
283 wmcodec_write(0x1a, 0x0); /*Pwr Mgmt(2)*/ 274{
275 /* mute inputs */
276 wm8975_write_or(LINVOL, LINVOL_LINMUTE);
277 wm8975_write_or(RINVOL, RINVOL_RINMUTE);
284 278
285 /* 3. Switch off the power supplies. */ 279 wmcodec_write(ADDCTRL3, 0);
286 wmcodec_write(0x19, 0x0); /*Pwr Mgmt(1)*/
287}
288 280
289void audiohw_set_recvol(int left, int right, int type) { 281 wm8975_regs[PWRMGMT2] |= PWRMGMT2_DACL | PWRMGMT2_DACR
282 | PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1
283 | PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2;
284 wm8975_write(PWRMGMT2, wm8975_regs[PWRMGMT2]);
290 285
291 (void)left; 286 wm8975_regs[PWRMGMT1] &= ~(PWRMGMT1_AINL | PWRMGMT1_AINR
292 (void)right; 287 | PWRMGMT1_ADCL | PWRMGMT1_ADCR);
293 (void)type; 288 wm8975_write(PWRMGMT1, wm8975_regs[PWRMGMT1]);
294} 289}
295 290
296void audiohw_set_monitor(bool enable) { 291void audiohw_set_recvol(int left, int right, int type)
292{
293 switch (type)
294 {
295 case AUDIO_GAIN_MIC: /* Mic uses right ADC */
296 wm8975_regs[RINVOL] &= ~RINVOL_MASK;
297 wm8975_write_or(RINVOL, left & RINVOL_MASK);
298 break;
299 case AUDIO_GAIN_LINEIN:
300 wm8975_regs[LINVOL] &= ~LINVOL_MASK;
301 wm8975_write_or(LINVOL, left & LINVOL_MASK);
302 wm8975_regs[RINVOL] &= ~RINVOL_MASK;
303 wm8975_write_or(RINVOL, right & RINVOL_MASK);
304 break;
305 default:
306 return;
307 }
308}
297 309
310void audiohw_set_monitor(bool enable)
311{
298 (void)enable; 312 (void)enable;
299} 313}
314#endif /* HAVE_RECORDING */