diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2016-08-15 16:32:05 +0100 |
---|---|---|
committer | Gerrit Rockbox <gerrit@rockbox.org> | 2016-08-29 08:16:27 +0200 |
commit | afe7f1b91568e742f3302f8e7b86fc7cd35b390b (patch) | |
tree | 24ad95956838fc2ba77d002bc5664effe99b64ea | |
parent | cbcc8f368f13a699bea563a136304acaa1dc8a8e (diff) | |
download | rockbox-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.c | 81 |
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 */ |
80 | static uint8_t as3514_regs[AS3514_NUM_AUDIO_REGS]; /* 8-bit registers */ | 80 | static uint8_t as3514_regs[AS3514_NUM_AUDIO_REGS]; /* 8-bit registers */ |
81 | /* Keep track of volume */ | ||
82 | static 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 | */ |
486 | void audiohw_set_monitor(bool enable) | 503 | void 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 */ |