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 */
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 */
#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);
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 */