summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2016-08-15 16:32:05 +0100
committerGerrit Rockbox <gerrit@rockbox.org>2016-08-29 08:16:27 +0200
commitafe7f1b91568e742f3302f8e7b86fc7cd35b390b (patch)
tree24ad95956838fc2ba77d002bc5664effe99b64ea
parentcbcc8f368f13a699bea563a136304acaa1dc8a8e (diff)
downloadrockbox-afe7f1b91568e742f3302f8e7b86fc7cd35b390b.tar.gz
rockbox-afe7f1b91568e742f3302f8e7b86fc7cd35b390b.zip
as3543: fix audio gap when switching from dac to line-in/recording
Also clarity parts of the code. The old code suffered from two defects: - it was very unclear because it made changes to whole registers (using as3514_write) instead of fields (using as3514_set/clear/write_masked). Also the routing code was spread accross several functions which made it hard to follow. - it did not properly reroute audio on monitor changes. In particular, the following could happen: when switching from DAC to radio, the code would fail to clear SUM_off, resulting in a weird situation where the main mixer was off (SUM_off) but the headphone where using the main mixer as input. Incredibly this worked anyway (at least on AMSv2 and YP-R0) but resulted in strange volume gaps between DAC and radio mode. Change-Id: I7826835fdb59c21f6483b223883ca9289e85caca
-rw-r--r--firmware/drivers/audio/as3514.c81
1 files changed, 51 insertions, 30 deletions
diff --git a/firmware/drivers/audio/as3514.c b/firmware/drivers/audio/as3514.c
index 167dd85abb..c55c7ba0a2 100644
--- a/firmware/drivers/audio/as3514.c
+++ b/firmware/drivers/audio/as3514.c
@@ -78,6 +78,8 @@
78 78
79/* Shadow registers */ 79/* Shadow registers */
80static uint8_t as3514_regs[AS3514_NUM_AUDIO_REGS]; /* 8-bit registers */ 80static uint8_t as3514_regs[AS3514_NUM_AUDIO_REGS]; /* 8-bit registers */
81/* Keep track of volume */
82static int current_vol_l, current_vol_r;
81 83
82/* 84/*
83 * little helper method to set register values. 85 * little helper method to set register values.
@@ -264,6 +266,10 @@ void audiohw_set_volume(int vol_l, int vol_r)
264 unsigned int hph_r, hph_l; 266 unsigned int hph_r, hph_l;
265 unsigned int mix_l, mix_r; 267 unsigned int mix_l, mix_r;
266 268
269 /* remember volume */
270 current_vol_l = vol_l;
271 current_vol_r = vol_r;
272
267 vol_l = vol_tenthdb2hw(vol_l); 273 vol_l = vol_tenthdb2hw(vol_l);
268 vol_r = vol_tenthdb2hw(vol_r); 274 vol_r = vol_tenthdb2hw(vol_r);
269 275
@@ -273,13 +279,13 @@ void audiohw_set_volume(int vol_l, int vol_r)
273 } 279 }
274 280
275 /* We combine the mixer/DAC channel volume range with the headphone volume 281 /* We combine the mixer/DAC channel volume range with the headphone volume
276 range - keep first stage as loud as possible */ 282 * range. We want to keep the mixers volume as high as possible and the
283 * headphone volume as low as possible. */
277 284
278/*AS3543 mixer can go a little louder then the as3514, although 285 /* AS3543 mixer can go a little louder then the as3514, although
279 * it might be possible to go louder on the as3514 as well */ 286 * it might be possible to go louder on the as3514 as well */
280
281#ifdef HAVE_AS3543 287#ifdef HAVE_AS3543
282#define MIXER_MAX_VOLUME 0x1b 288#define MIXER_MAX_VOLUME 0x1b /* IMPORTANT corresponds to a volume of 0dB (see below) */
283#else /* lets leave the AS3514 alone until its better tested*/ 289#else /* lets leave the AS3514 alone until its better tested*/
284#define MIXER_MAX_VOLUME 0x16 290#define MIXER_MAX_VOLUME 0x16
285#endif 291#endif
@@ -301,17 +307,28 @@ void audiohw_set_volume(int vol_l, int vol_r)
301 } 307 }
302 308
303#ifdef HAVE_AS3543 309#ifdef HAVE_AS3543
304 /*if not radio or recording*/ 310 bool dac_only = !(as3514_regs[AS3514_AUDIOSET1] & (AUDIOSET1_ADC_on | AUDIOSET1_LIN1_on));
305 if (!(as3514_regs[AS3514_AUDIOSET1] & (AUDIOSET1_ADC_on | AUDIOSET1_LIN1_on))) { 311 if(dac_only && hph_l != 0 && hph_r != 0)
306 if (!hph_l || !hph_r) { /*if volume higher, disable the mixer to slightly improve noise*/ 312 {
307 as3514_write(AS3514_AUDIOSET1, AUDIOSET1_DAC_on | AUDIOSET1_DAC_GAIN_on); 313 /* In DAC only mode, if both left and right volume are higher than
308 as3514_write(AS3514_AUDIOSET2, AUDIOSET2_AGC_off | AUDIOSET2_HPH_QUALITY_LOW_POWER); 314 * MIXER_MAX_VOLUME, we disable and bypass the DAC mixer to slightly
309 as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_SUM, HPH_OUT_R_HP_OUT_MASK); 315 * improve noise.
310 } else { 316 *
311 as3514_write(AS3514_AUDIOSET1, AUDIOSET1_DAC_on); 317 * WARNING this works because MIXER_MAX_VOLUME corresponds to a DAC mixer
312 as3514_write(AS3514_AUDIOSET2, AUDIOSET2_SUM_off | AUDIOSET2_AGC_off | AUDIOSET2_HPH_QUALITY_LOW_POWER); 318 * volume of 0dB, thus it's the same to bypass the mixer or set its
313 as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_DAC, HPH_OUT_R_HP_OUT_MASK); 319 * level to MIXER_MAX_VOLUME, except that bypassing is less noisy */
314 } 320 as3514_clear(AS3514_AUDIOSET1, AUDIOSET1_DAC_GAIN_on);
321 as3514_set(AS3514_AUDIOSET2, AUDIOSET2_SUM_off);
322 as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_DAC, HPH_OUT_R_HP_OUT_MASK);
323 }
324 else
325 {
326 /* In all other cases, we have no choice but to go through the main mixer
327 * (aka SUM) to get the volume we want or to properly route audio from
328 * line-in/microphone. */
329 as3514_set(AS3514_AUDIOSET1, AUDIOSET1_DAC_GAIN_on);
330 as3514_clear(AS3514_AUDIOSET2, AUDIOSET2_SUM_off);
331 as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_SUM, HPH_OUT_R_HP_OUT_MASK);
315 } 332 }
316#endif 333#endif
317 334
@@ -485,28 +502,32 @@ void audiohw_set_recvol(int left, int right, int type)
485 */ 502 */
486void audiohw_set_monitor(bool enable) 503void audiohw_set_monitor(bool enable)
487{ 504{
505 /* On AS3543 we play with DAC mixer bypass to decrease noise. This means that
506 * even in DAC mode, the headphone mux might be set to HPH_OUT_R_HP_OUT_SUM or
507 * HPH_OUT_R_HP_OUT_DAC depending on the volume. Care must be taken when
508 * changing monitor.
509 *
510 * The only safe procedure is to first change the Audioset1 register to enable/disable
511 * monitor, then call audiohw_set_volume to recompute the audio routing, then
512 * mute/unmute lines-in. */
488 if (enable) { 513 if (enable) {
489#ifdef HAVE_AS3543 514 /* select either LIN1 or LIN2 but keep them muted for now */
490 as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_SUM, HPH_OUT_R_HP_OUT_MASK);
491#endif
492 /* select either LIN1 or LIN2 */
493 as3514_write_masked(AS3514_AUDIOSET1, AUDIOSET1_LIN_on, 515 as3514_write_masked(AS3514_AUDIOSET1, AUDIOSET1_LIN_on,
494 AUDIOSET1_LIN1_on | AUDIOSET1_LIN2_on); 516 AUDIOSET1_LIN1_on | AUDIOSET1_LIN2_on);
517 /* change audio routing */
518 audiohw_set_volume(current_vol_l, current_vol_r);
519 /* finally unmute line-in */
495 as3514_set(AS3514_LINE_IN_R, LINE_IN1_R_LI1R_MUTE_off); 520 as3514_set(AS3514_LINE_IN_R, LINE_IN1_R_LI1R_MUTE_off);
496 as3514_set(AS3514_LINE_IN_L, LINE_IN1_L_LI1L_MUTE_off); 521 as3514_set(AS3514_LINE_IN_L, LINE_IN1_L_LI1L_MUTE_off);
497 } 522 }
498 else { 523 else {
499#ifdef HAVE_AS3543 524 /* mute line-in */
500 as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_DAC, HPH_OUT_R_HP_OUT_MASK); 525 as3514_clear(AS3514_LINE_IN_R, LINE_IN1_R_LI1R_MUTE_off);
501#endif 526 as3514_clear(AS3514_LINE_IN_L, LINE_IN1_L_LI1L_MUTE_off);
502 /* turn off both LIN1 and LIN2 (if present) */ 527 /* disable line-in */
503 as3514_clear(AS3514_LINE_IN1_R, LINE_IN1_R_LI1R_MUTE_off);
504 as3514_clear(AS3514_LINE_IN1_L, LINE_IN1_L_LI1L_MUTE_off);
505#ifndef HAVE_AS3543
506 as3514_clear(AS3514_LINE_IN2_R, LINE_IN2_R_LI2R_MUTE_off);
507 as3514_clear(AS3514_LINE_IN2_L, LINE_IN2_L_LI2L_MUTE_off);
508#endif
509 as3514_clear(AS3514_AUDIOSET1, AUDIOSET1_LIN1_on | AUDIOSET1_LIN2_on); 528 as3514_clear(AS3514_AUDIOSET1, AUDIOSET1_LIN1_on | AUDIOSET1_LIN2_on);
529 /* change audio routing */
530 audiohw_set_volume(current_vol_l, current_vol_r);
510 } 531 }
511} 532}
512#endif /* HAVE_RECORDING || HAVE_FMRADIO_IN */ 533#endif /* HAVE_RECORDING || HAVE_FMRADIO_IN */