From afe7f1b91568e742f3302f8e7b86fc7cd35b390b Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Mon, 15 Aug 2016 16:32:05 +0100 Subject: 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 --- firmware/drivers/audio/as3514.c | 81 ++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 30 deletions(-) (limited to 'firmware/drivers') 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 @@ /* Shadow registers */ static uint8_t as3514_regs[AS3514_NUM_AUDIO_REGS]; /* 8-bit registers */ +/* Keep track of volume */ +static int current_vol_l, current_vol_r; /* * little helper method to set register values. @@ -264,6 +266,10 @@ void audiohw_set_volume(int vol_l, int vol_r) unsigned int hph_r, hph_l; unsigned int mix_l, mix_r; + /* remember volume */ + current_vol_l = vol_l; + current_vol_r = vol_r; + vol_l = vol_tenthdb2hw(vol_l); vol_r = vol_tenthdb2hw(vol_r); @@ -273,13 +279,13 @@ void audiohw_set_volume(int vol_l, int vol_r) } /* We combine the mixer/DAC channel volume range with the headphone volume - range - keep first stage as loud as possible */ + * range. We want to keep the mixers volume as high as possible and the + * headphone volume as low as possible. */ -/*AS3543 mixer can go a little louder then the as3514, although - * it might be possible to go louder on the as3514 as well */ - + /* AS3543 mixer can go a little louder then the as3514, although + * it might be possible to go louder on the as3514 as well */ #ifdef HAVE_AS3543 -#define MIXER_MAX_VOLUME 0x1b +#define MIXER_MAX_VOLUME 0x1b /* IMPORTANT corresponds to a volume of 0dB (see below) */ #else /* lets leave the AS3514 alone until its better tested*/ #define MIXER_MAX_VOLUME 0x16 #endif @@ -301,17 +307,28 @@ void audiohw_set_volume(int vol_l, int vol_r) } #ifdef HAVE_AS3543 - /*if not radio or recording*/ - if (!(as3514_regs[AS3514_AUDIOSET1] & (AUDIOSET1_ADC_on | AUDIOSET1_LIN1_on))) { - if (!hph_l || !hph_r) { /*if volume higher, disable the mixer to slightly improve noise*/ - as3514_write(AS3514_AUDIOSET1, AUDIOSET1_DAC_on | AUDIOSET1_DAC_GAIN_on); - as3514_write(AS3514_AUDIOSET2, AUDIOSET2_AGC_off | AUDIOSET2_HPH_QUALITY_LOW_POWER); - as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_SUM, HPH_OUT_R_HP_OUT_MASK); - } else { - as3514_write(AS3514_AUDIOSET1, AUDIOSET1_DAC_on); - as3514_write(AS3514_AUDIOSET2, AUDIOSET2_SUM_off | AUDIOSET2_AGC_off | AUDIOSET2_HPH_QUALITY_LOW_POWER); - as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_DAC, HPH_OUT_R_HP_OUT_MASK); - } + bool dac_only = !(as3514_regs[AS3514_AUDIOSET1] & (AUDIOSET1_ADC_on | AUDIOSET1_LIN1_on)); + if(dac_only && hph_l != 0 && hph_r != 0) + { + /* In DAC only mode, if both left and right volume are higher than + * MIXER_MAX_VOLUME, we disable and bypass the DAC mixer to slightly + * improve noise. + * + * WARNING this works because MIXER_MAX_VOLUME corresponds to a DAC mixer + * volume of 0dB, thus it's the same to bypass the mixer or set its + * level to MIXER_MAX_VOLUME, except that bypassing is less noisy */ + as3514_clear(AS3514_AUDIOSET1, AUDIOSET1_DAC_GAIN_on); + as3514_set(AS3514_AUDIOSET2, AUDIOSET2_SUM_off); + as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_DAC, HPH_OUT_R_HP_OUT_MASK); + } + else + { + /* In all other cases, we have no choice but to go through the main mixer + * (aka SUM) to get the volume we want or to properly route audio from + * line-in/microphone. */ + as3514_set(AS3514_AUDIOSET1, AUDIOSET1_DAC_GAIN_on); + as3514_clear(AS3514_AUDIOSET2, AUDIOSET2_SUM_off); + as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_SUM, HPH_OUT_R_HP_OUT_MASK); } #endif @@ -485,28 +502,32 @@ void audiohw_set_recvol(int left, int right, int type) */ void audiohw_set_monitor(bool enable) { + /* On AS3543 we play with DAC mixer bypass to decrease noise. This means that + * even in DAC mode, the headphone mux might be set to HPH_OUT_R_HP_OUT_SUM or + * HPH_OUT_R_HP_OUT_DAC depending on the volume. Care must be taken when + * changing monitor. + * + * The only safe procedure is to first change the Audioset1 register to enable/disable + * monitor, then call audiohw_set_volume to recompute the audio routing, then + * mute/unmute lines-in. */ if (enable) { -#ifdef HAVE_AS3543 - as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_SUM, HPH_OUT_R_HP_OUT_MASK); -#endif - /* select either LIN1 or LIN2 */ + /* select either LIN1 or LIN2 but keep them muted for now */ as3514_write_masked(AS3514_AUDIOSET1, AUDIOSET1_LIN_on, AUDIOSET1_LIN1_on | AUDIOSET1_LIN2_on); + /* change audio routing */ + audiohw_set_volume(current_vol_l, current_vol_r); + /* finally unmute line-in */ as3514_set(AS3514_LINE_IN_R, LINE_IN1_R_LI1R_MUTE_off); as3514_set(AS3514_LINE_IN_L, LINE_IN1_L_LI1L_MUTE_off); } else { -#ifdef HAVE_AS3543 - as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_DAC, HPH_OUT_R_HP_OUT_MASK); -#endif - /* turn off both LIN1 and LIN2 (if present) */ - as3514_clear(AS3514_LINE_IN1_R, LINE_IN1_R_LI1R_MUTE_off); - as3514_clear(AS3514_LINE_IN1_L, LINE_IN1_L_LI1L_MUTE_off); -#ifndef HAVE_AS3543 - as3514_clear(AS3514_LINE_IN2_R, LINE_IN2_R_LI2R_MUTE_off); - as3514_clear(AS3514_LINE_IN2_L, LINE_IN2_L_LI2L_MUTE_off); -#endif + /* mute line-in */ + as3514_clear(AS3514_LINE_IN_R, LINE_IN1_R_LI1R_MUTE_off); + as3514_clear(AS3514_LINE_IN_L, LINE_IN1_L_LI1L_MUTE_off); + /* disable line-in */ as3514_clear(AS3514_AUDIOSET1, AUDIOSET1_LIN1_on | AUDIOSET1_LIN2_on); + /* change audio routing */ + audiohw_set_volume(current_vol_l, current_vol_r); } } #endif /* HAVE_RECORDING || HAVE_FMRADIO_IN */ -- cgit v1.2.3