summaryrefslogtreecommitdiff
path: root/firmware/drivers/audio/wm8978.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/audio/wm8978.c')
-rw-r--r--firmware/drivers/audio/wm8978.c244
1 files changed, 163 insertions, 81 deletions
diff --git a/firmware/drivers/audio/wm8978.c b/firmware/drivers/audio/wm8978.c
index ee881fbf4a..da444be1bf 100644
--- a/firmware/drivers/audio/wm8978.c
+++ b/firmware/drivers/audio/wm8978.c
@@ -28,18 +28,11 @@
28//#define LOGF_ENABLE 28//#define LOGF_ENABLE
29#include "logf.h" 29#include "logf.h"
30 30
31/* #define to help adjust lower volume limit */
32#define HW_VOL_MIN 0
33#define HW_VOL_MUTE 0
34#define HW_VOL_MAX 96
35#define HW_VOL_ANA_MIN 0
36#define HW_VOL_ANA_MAX 63
37#define HW_VOL_DIG_MAX 255
38#define HW_VOL_DIG_THRESHOLD (HW_VOL_MAX - HW_VOL_ANA_MAX)
39#define HW_VOL_DIG_MIN (HW_VOL_DIG_MAX - 2*HW_VOL_DIG_THRESHOLD)
40
41/* TODO: Define/refine an API for special hardware steps outside the 31/* TODO: Define/refine an API for special hardware steps outside the
42 * main codec driver such as special GPIO handling. */ 32 * main codec driver such as special GPIO handling. */
33/* NOTE: Much of the volume code is very interdependent and calibrated for
34 * the Gigabeat S. If you change anything for another device that uses this
35 * file it may break things. */
43extern void audiohw_enable_headphone_jack(bool enable); 36extern void audiohw_enable_headphone_jack(bool enable);
44 37
45const struct sound_settings_info audiohw_settings[] = 38const struct sound_settings_info audiohw_settings[] =
@@ -51,10 +44,14 @@ const struct sound_settings_info audiohw_settings[] =
51 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, 44 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0},
52 [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, 45 [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100},
53#ifdef HAVE_RECORDING 46#ifdef HAVE_RECORDING
54 [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, 47 /* Digital: -119.0dB to +8.0dB in 0.5dB increments
55 [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, 48 * Analog: Relegated to volume control
49 * Circumstances unfortunately do not allow a great deal of positive
50 * gain. */
51 [SOUND_LEFT_GAIN] = {"dB", 1, 1,-238, 16, 0},
52 [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-238, 16, 0},
56#if 0 53#if 0
57 [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, 54 [SOUND_MIC_GAIN] = {"dB", 1, 1,-238, 16, 0},
58#endif 55#endif
59#endif 56#endif
60#if 0 57#if 0
@@ -107,8 +104,8 @@ static uint16_t wmc_regs[WMC_NUM_REGISTERS] =
107 [WMC_3D_CONTROL] = 0x000, 104 [WMC_3D_CONTROL] = 0x000,
108 [WMC_BEEP_CONTROL] = 0x000, 105 [WMC_BEEP_CONTROL] = 0x000,
109 [WMC_INPUT_CTRL] = 0x033, 106 [WMC_INPUT_CTRL] = 0x033,
110 [WMC_LEFT_INP_PGA_GAIN_CTRL] = 0x010, 107 [WMC_LEFT_INP_PGA_GAIN_CTRL] = 0x010 | WMC_VU | WMC_ZC,
111 [WMC_RIGHT_INP_PGA_GAIN_CTRL] = 0x010, 108 [WMC_RIGHT_INP_PGA_GAIN_CTRL] = 0x010 | WMC_VU | WMC_ZC,
112 [WMC_LEFT_ADC_BOOST_CTRL] = 0x100, 109 [WMC_LEFT_ADC_BOOST_CTRL] = 0x100,
113 [WMC_RIGHT_ADC_BOOST_CTRL] = 0x100, 110 [WMC_RIGHT_ADC_BOOST_CTRL] = 0x100,
114 [WMC_OUTPUT_CTRL] = 0x002, 111 [WMC_OUTPUT_CTRL] = 0x002,
@@ -129,7 +126,7 @@ struct
129 bool ahw_mute; 126 bool ahw_mute;
130} wmc_vol = 127} wmc_vol =
131{ 128{
132 HW_VOL_MUTE, HW_VOL_MUTE, false 129 0, 0, false
133}; 130};
134 131
135static void wmc_write(unsigned int reg, unsigned int val) 132static void wmc_write(unsigned int reg, unsigned int val)
@@ -180,6 +177,27 @@ int tenthdb2master(int db)
180 } 177 }
181} 178}
182 179
180int sound_val2phys(int setting, int value)
181{
182 int result;
183
184 switch (setting)
185 {
186#ifdef HAVE_RECORDING
187 case SOUND_LEFT_GAIN:
188 case SOUND_RIGHT_GAIN:
189 case SOUND_MIC_GAIN:
190 result = value * 5;
191 break;
192#endif
193
194 default:
195 result = value;
196 }
197
198 return result;
199}
200
183void audiohw_preinit(void) 201void audiohw_preinit(void)
184{ 202{
185 /* 1. Turn on external power supplies. Wait for supply voltage to settle. */ 203 /* 1. Turn on external power supplies. Wait for supply voltage to settle. */
@@ -190,13 +208,12 @@ void audiohw_preinit(void)
190 sleep(HZ/10); 208 sleep(HZ/10);
191 209
192 /* 2. Mute all analogue outputs */ 210 /* 2. Mute all analogue outputs */
193 wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE | HW_VOL_ANA_MIN); 211 wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE);
194 wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE | HW_VOL_ANA_MIN); 212 wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE);
195 wmc_set(WMC_LOUT2_SPK_VOLUME_CTRL, WMC_MUTE); 213 wmc_set(WMC_LOUT2_SPK_VOLUME_CTRL, WMC_MUTE);
196 wmc_set(WMC_ROUT2_SPK_VOLUME_CTRL, WMC_MUTE); 214 wmc_set(WMC_ROUT2_SPK_VOLUME_CTRL, WMC_MUTE);
197 wmc_set(WMC_OUT3_MIXER_CTRL, WMC_MUTE); 215 wmc_set(WMC_OUT3_MIXER_CTRL, WMC_MUTE);
198 wmc_set(WMC_OUT4_MONO_MIXER_CTRL, WMC_MUTE); 216 wmc_set(WMC_OUT4_MONO_MIXER_CTRL, WMC_MUTE);
199 wmc_set(WMC_INPUT_CTRL, 0x000);
200 217
201 /* 3. Set L/RMIXEN = 1 and DACENL/R = 1 in register R3. */ 218 /* 3. Set L/RMIXEN = 1 and DACENL/R = 1 in register R3. */
202 wmc_write(WMC_POWER_MANAGEMENT3, 219 wmc_write(WMC_POWER_MANAGEMENT3,
@@ -226,103 +243,105 @@ void audiohw_postinit(void)
226 wmc_write(WMC_AUDIO_INTERFACE, WMC_WL_16 | WMC_FMT_I2S); 243 wmc_write(WMC_AUDIO_INTERFACE, WMC_WL_16 | WMC_FMT_I2S);
227 wmc_write(WMC_DAC_CONTROL, WMC_DACOSR_128 | WMC_AMUTE); 244 wmc_write(WMC_DAC_CONTROL, WMC_DACOSR_128 | WMC_AMUTE);
228 245
229 wmc_set(WMC_INPUT_CTRL, WMC_R2_2INPPGA | WMC_L2_2INPPGA); 246 /* No ADC, no HP filter, no popping */
230 /* set PGA volumes to 0dB and enable zero cross */ 247 wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN);
231 wmc_set(WMC_LEFT_INP_PGA_GAIN_CTRL, 0x10 | 1 << 7); 248
232 wmc_set(WMC_RIGHT_INP_PGA_GAIN_CTRL, 0x10 | 1 << 7); 249 wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL);
233 /* write to INPPGAUPDATE to actually change voulme */ 250 wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR);
234 wmc_set(WMC_LEFT_INP_PGA_GAIN_CTRL, 1 << 8);
235 wmc_set(WMC_RIGHT_INP_PGA_GAIN_CTRL, 1 << 8);
236 /* set boost gain to 0dB */
237 wmc_set(WMC_LEFT_ADC_BOOST_CTRL, (5 << 4));
238 wmc_set(WMC_RIGHT_ADC_BOOST_CTRL, (5 << 4));
239 251
240 /* Specific to HW clocking */ 252 /* Specific to HW clocking */
241 wmc_write_masked(WMC_CLOCK_GEN_CTRL, WMC_BCLKDIV_4 | WMC_MS, 253 wmc_write_masked(WMC_CLOCK_GEN_CTRL, WMC_BCLKDIV_4 | WMC_MS,
242 WMC_BCLKDIV | WMC_MS | WMC_CLKSEL); 254 WMC_BCLKDIV | WMC_MS | WMC_CLKSEL);
243 audiohw_set_frequency(HW_FREQ_DEFAULT); 255 audiohw_set_frequency(HW_FREQ_DEFAULT);
244 256
245 /* ADC silenced */
246 wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL);
247 wmc_write_masked(WMC_RIGHT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL);
248
249 audiohw_enable_headphone_jack(true); 257 audiohw_enable_headphone_jack(true);
250} 258}
251 259
252void audiohw_set_headphone_vol(int vol_l, int vol_r) 260static void get_headphone_levels(int val, int *dac_p, int *hp_p,
261 int *mix_p, int *boost_p)
253{ 262{
254 int prev_l = wmc_vol.vol_l; 263 int dac, hp, mix, boost;
255 int prev_r = wmc_vol.vol_r;
256 int dac_l, dac_r;
257
258 wmc_vol.vol_l = vol_l;
259 wmc_vol.vol_r = vol_r;
260 264
261 /* When analogue volume falls below -57dB (0x00) start attenuating the 265 if (val >= 33)
262 * DAC volume */
263 if (vol_l >= HW_VOL_DIG_THRESHOLD)
264 { 266 {
265 if (vol_l > HW_VOL_MAX) 267 dac = 255;
266 vol_l = HW_VOL_MAX; 268 hp = val - 33;
267 269 mix = 7;
268 dac_l = HW_VOL_DIG_MAX; 270 boost = 5;
269 vol_l -= HW_VOL_DIG_THRESHOLD; 271 }
272 else if (val >= 21)
273 {
274 dac = 189 + val / 3 * 6;
275 hp = val % 3;
276 mix = 7;
277 boost = (val - 18) / 3;
270 } 278 }
271 else 279 else
272 { 280 {
273 if (vol_l < HW_VOL_MIN) 281 dac = 189 + val / 3 * 6;
274 vol_l = HW_VOL_MIN; 282 hp = val % 3;
275 283 mix = val / 3;
276 dac_l = 2*vol_l + HW_VOL_DIG_MIN; 284 boost = 1;
277 vol_l = HW_VOL_ANA_MIN;
278 } 285 }
279 286
280 if (vol_r >= HW_VOL_DIG_THRESHOLD) 287 *dac_p = dac;
281 { 288 *hp_p = hp;
282 if (vol_r > HW_VOL_MAX) 289 *mix_p = mix;
283 vol_r = HW_VOL_MAX; 290 *boost_p = boost;
291}
284 292
285 dac_r = HW_VOL_DIG_MAX; 293void audiohw_set_headphone_vol(int vol_l, int vol_r)
286 vol_r -= HW_VOL_DIG_THRESHOLD; 294{
287 } 295 int prev_l = wmc_vol.vol_l;
288 else 296 int prev_r = wmc_vol.vol_r;
289 { 297 int dac_l, dac_r, hp_l, hp_r;
290 if (vol_r < HW_VOL_MIN) 298 int mix_l, mix_r, boost_l, boost_r;
291 vol_r = HW_VOL_MIN;
292 299
293 dac_r = 2*vol_r + HW_VOL_DIG_MIN; 300 wmc_vol.vol_l = vol_l;
294 vol_r = HW_VOL_ANA_MIN; 301 wmc_vol.vol_r = vol_r;
295 } 302
303 /* Mixers are synced to provide full volume range on both the analogue
304 * and digital pathways */
305 get_headphone_levels(vol_l, &dac_l, &hp_l, &mix_l, &boost_l);
306 get_headphone_levels(vol_r, &dac_r, &hp_r, &mix_r, &boost_r);
296 307
297 /* Have to write both channels always to have the latching work */ 308 wmc_write_masked(WMC_LEFT_MIXER_CTRL, WMC_BYPLMIXVOLw(mix_l),
309 WMC_BYPLMIXVOL);
310 wmc_write_masked(WMC_LEFT_ADC_BOOST_CTRL,
311 WMC_L2_2BOOSTVOLw(boost_l), WMC_L2_2BOOSTVOL);
298 wmc_write_masked(WMC_LEFT_DAC_DIGITAL_VOL, dac_l, WMC_DVOL); 312 wmc_write_masked(WMC_LEFT_DAC_DIGITAL_VOL, dac_l, WMC_DVOL);
299 wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, vol_l, WMC_AVOL); 313 wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, hp_l, WMC_AVOL);
314
315 wmc_write_masked(WMC_RIGHT_MIXER_CTRL, WMC_BYPRMIXVOLw(mix_r),
316 WMC_BYPRMIXVOL);
317 wmc_write_masked(WMC_RIGHT_ADC_BOOST_CTRL,
318 WMC_R2_2BOOSTVOLw(boost_r), WMC_R2_2BOOSTVOL);
300 wmc_write_masked(WMC_RIGHT_DAC_DIGITAL_VOL, dac_r, WMC_DVOL); 319 wmc_write_masked(WMC_RIGHT_DAC_DIGITAL_VOL, dac_r, WMC_DVOL);
301 wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, vol_r, WMC_AVOL); 320 wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, hp_r, WMC_AVOL);
302 321
303 if (wmc_vol.vol_l > HW_VOL_MUTE) 322 if (vol_l > 0)
304 { 323 {
305 /* Not muted and going up from mute level? */ 324 /* Not muted and going up from mute level? */
306 if (prev_l <= HW_VOL_MUTE && !wmc_vol.ahw_mute) 325 if (prev_l <= 0 && !wmc_vol.ahw_mute)
307 wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); 326 wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE);
308 } 327 }
309 else 328 else
310 { 329 {
311 /* Going to mute level? */ 330 /* Going to mute level? */
312 if (prev_l > HW_VOL_MUTE) 331 if (prev_l > 0)
313 wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); 332 wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE);
314 } 333 }
315 334
316 if (wmc_vol.vol_r > HW_VOL_MUTE) 335 if (vol_r > 0)
317 { 336 {
318 /* Not muted and going up from mute level? */ 337 /* Not muted and going up from mute level? */
319 if (prev_r <= HW_VOL_MIN && !wmc_vol.ahw_mute) 338 if (prev_r <= 0 && !wmc_vol.ahw_mute)
320 wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); 339 wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE);
321 } 340 }
322 else 341 else
323 { 342 {
324 /* Going to mute level? */ 343 /* Going to mute level? */
325 if (prev_r > HW_VOL_MUTE) 344 if (prev_r > 0)
326 wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); 345 wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE);
327 } 346 }
328} 347}
@@ -358,10 +377,10 @@ void audiohw_mute(bool mute)
358 else 377 else
359 { 378 {
360 /* Unmute outputs not at mute level */ 379 /* Unmute outputs not at mute level */
361 if (wmc_vol.vol_l > HW_VOL_MUTE) 380 if (wmc_vol.vol_l > 0)
362 wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); 381 wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE);
363 382
364 if (wmc_vol.vol_r > HW_VOL_MUTE) 383 if (wmc_vol.vol_r > 0)
365 wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); 384 wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE);
366 } 385 }
367} 386}
@@ -491,9 +510,72 @@ void audiohw_set_frequency(int fsel)
491} 510}
492 511
493#ifdef HAVE_RECORDING 512#ifdef HAVE_RECORDING
494/* TODO */ 513void audiohw_set_recsrc(int source, bool recording)
514{
515 switch (source)
516 {
517 case AUDIO_SRC_PLAYBACK:
518 /* Disable all audio paths but DAC */
519 /* Disable ADCs */
520 wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN);
521 wmc_clear(WMC_POWER_MANAGEMENT2, WMC_ADCENL | WMC_ADCENR);
522 /* Disable bypass */
523 wmc_clear(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX);
524 wmc_clear(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX);
525 /* Disable IP BOOSTMIX and PGA */
526 wmc_clear(WMC_POWER_MANAGEMENT2, WMC_INPPGAENL | WMC_INPPGAENR |
527 WMC_BOOSTENL | WMC_BOOSTENR);
528 wmc_clear(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA);
529 wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL);
530 wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR);
531 break;
532
533 case AUDIO_SRC_FMRADIO:
534 if (recording)
535 {
536 /* Disable bypass */
537 wmc_clear(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX);
538 wmc_clear(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX);
539 /* Enable ADCs, IP BOOSTMIX and PGA, route L/R2 through PGA */
540 wmc_set(WMC_POWER_MANAGEMENT2, WMC_ADCENL | WMC_ADCENR |
541 WMC_BOOSTENL | WMC_BOOSTENR | WMC_INPPGAENL |
542 WMC_INPPGAENR);
543 wmc_set(WMC_ADC_CONTROL, WMC_ADCOSR | WMC_HPFEN);
544 /* PGA at 0dB with +20dB boost */
545 wmc_write_masked(WMC_LEFT_INP_PGA_GAIN_CTRL, 0x10, WMC_AVOL);
546 wmc_write_masked(WMC_RIGHT_INP_PGA_GAIN_CTRL, 0x10, WMC_AVOL);
547 wmc_set(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL);
548 wmc_set(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR);
549 /* Connect L/R2 inputs to PGA */
550 wmc_set(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA);
551 }
552 else
553 {
554 /* Disable PGA and ADC, enable IP BOOSTMIX, route L/R2 directly to
555 * IP BOOSTMIX */
556 wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN);
557 wmc_write_masked(WMC_POWER_MANAGEMENT2, WMC_BOOSTENL | WMC_BOOSTENR,
558 WMC_BOOSTENL | WMC_BOOSTENR | WMC_INPPGAENL |
559 WMC_INPPGAENR | WMC_ADCENL | WMC_ADCENR);
560 wmc_clear(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA);
561 wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL);
562 wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR);
563 /* Enable bypass to L/R mixers */
564 wmc_set(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX);
565 wmc_set(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX);
566 }
567 break;
568 }
569}
570
495void audiohw_set_recvol(int left, int right, int type) 571void audiohw_set_recvol(int left, int right, int type)
496{ 572{
497 (void)left; (void)right; (void)type; 573 switch (type)
574 {
575 case AUDIO_GAIN_LINEIN:
576 wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, left + 239, WMC_DVOL);
577 wmc_write_masked(WMC_RIGHT_ADC_DIGITAL_VOL, right + 239, WMC_DVOL);
578 return;
579 }
498} 580}
499#endif 581#endif /* HAVE_RECORDING */