summaryrefslogtreecommitdiffstats
path: root/firmware/drivers
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 /firmware/drivers
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
Diffstat (limited to 'firmware/drivers')
-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 @@
/* 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 */