1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Amaury Pouly 2016-08-15 16:32:05 +01:00 committed by Gerrit Rockbox
parent cbcc8f368f
commit afe7f1b915

View file

@ -78,6 +78,8 @@
/* Shadow registers */ /* Shadow registers */
static uint8_t as3514_regs[AS3514_NUM_AUDIO_REGS]; /* 8-bit 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. * 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 hph_r, hph_l;
unsigned int mix_l, mix_r; 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_l = vol_tenthdb2hw(vol_l);
vol_r = vol_tenthdb2hw(vol_r); 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 /* 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 /* AS3543 mixer can go a little louder then the as3514, although
* it might be possible to go louder on the as3514 as well */ * it might be possible to go louder on the as3514 as well */
#ifdef HAVE_AS3543 #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*/ #else /* lets leave the AS3514 alone until its better tested*/
#define MIXER_MAX_VOLUME 0x16 #define MIXER_MAX_VOLUME 0x16
#endif #endif
@ -301,17 +307,28 @@ void audiohw_set_volume(int vol_l, int vol_r)
} }
#ifdef HAVE_AS3543 #ifdef HAVE_AS3543
/*if not radio or recording*/ bool dac_only = !(as3514_regs[AS3514_AUDIOSET1] & (AUDIOSET1_ADC_on | AUDIOSET1_LIN1_on));
if (!(as3514_regs[AS3514_AUDIOSET1] & (AUDIOSET1_ADC_on | AUDIOSET1_LIN1_on))) { if(dac_only && hph_l != 0 && hph_r != 0)
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); /* In DAC only mode, if both left and right volume are higher than
as3514_write(AS3514_AUDIOSET2, AUDIOSET2_AGC_off | AUDIOSET2_HPH_QUALITY_LOW_POWER); * MIXER_MAX_VOLUME, we disable and bypass the DAC mixer to slightly
as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_SUM, HPH_OUT_R_HP_OUT_MASK); * improve noise.
} else { *
as3514_write(AS3514_AUDIOSET1, AUDIOSET1_DAC_on); * WARNING this works because MIXER_MAX_VOLUME corresponds to a DAC mixer
as3514_write(AS3514_AUDIOSET2, AUDIOSET2_SUM_off | AUDIOSET2_AGC_off | AUDIOSET2_HPH_QUALITY_LOW_POWER); * volume of 0dB, thus it's the same to bypass the mixer or set its
as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_DAC, HPH_OUT_R_HP_OUT_MASK); * 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 #endif
@ -485,28 +502,32 @@ void audiohw_set_recvol(int left, int right, int type)
*/ */
void audiohw_set_monitor(bool enable) 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) { if (enable) {
#ifdef HAVE_AS3543 /* select either LIN1 or LIN2 but keep them muted for now */
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 */
as3514_write_masked(AS3514_AUDIOSET1, AUDIOSET1_LIN_on, as3514_write_masked(AS3514_AUDIOSET1, AUDIOSET1_LIN_on,
AUDIOSET1_LIN1_on | AUDIOSET1_LIN2_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_R, LINE_IN1_R_LI1R_MUTE_off);
as3514_set(AS3514_LINE_IN_L, LINE_IN1_L_LI1L_MUTE_off); as3514_set(AS3514_LINE_IN_L, LINE_IN1_L_LI1L_MUTE_off);
} }
else { else {
#ifdef HAVE_AS3543 /* mute line-in */
as3514_write_masked(AS3514_HPH_OUT_R, HPH_OUT_R_HP_OUT_DAC, HPH_OUT_R_HP_OUT_MASK); as3514_clear(AS3514_LINE_IN_R, LINE_IN1_R_LI1R_MUTE_off);
#endif as3514_clear(AS3514_LINE_IN_L, LINE_IN1_L_LI1L_MUTE_off);
/* turn off both LIN1 and LIN2 (if present) */ /* disable line-in */
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
as3514_clear(AS3514_AUDIOSET1, AUDIOSET1_LIN1_on | AUDIOSET1_LIN2_on); 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 */ #endif /* HAVE_RECORDING || HAVE_FMRADIO_IN */