From 2e99b3d9318dbc98c242c9ddbbf23d2fcc5bf2bd Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Wed, 31 Dec 2008 01:38:44 +0000 Subject: Gigabeat S: Allow recording from FM. Give FM the same volume range as playback. NOTE: This bumps the si4700 output volume to -0dB so other players with that may need a minor adjustment. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19619 a1c6a512-1295-4272-9138-f99709370657 --- firmware/drivers/audio/wm8978.c | 244 +++++++++++++++++++++++++++------------- firmware/drivers/tuner/si4700.c | 4 +- 2 files changed, 164 insertions(+), 84 deletions(-) (limited to 'firmware/drivers') diff --git a/firmware/drivers/audio/wm8978.c b/firmware/drivers/audio/wm8978.c index ee881fb..da444be 100644 --- a/firmware/drivers/audio/wm8978.c +++ b/firmware/drivers/audio/wm8978.c @@ -28,18 +28,11 @@ //#define LOGF_ENABLE #include "logf.h" -/* #define to help adjust lower volume limit */ -#define HW_VOL_MIN 0 -#define HW_VOL_MUTE 0 -#define HW_VOL_MAX 96 -#define HW_VOL_ANA_MIN 0 -#define HW_VOL_ANA_MAX 63 -#define HW_VOL_DIG_MAX 255 -#define HW_VOL_DIG_THRESHOLD (HW_VOL_MAX - HW_VOL_ANA_MAX) -#define HW_VOL_DIG_MIN (HW_VOL_DIG_MAX - 2*HW_VOL_DIG_THRESHOLD) - /* TODO: Define/refine an API for special hardware steps outside the * main codec driver such as special GPIO handling. */ +/* NOTE: Much of the volume code is very interdependent and calibrated for + * the Gigabeat S. If you change anything for another device that uses this + * file it may break things. */ extern void audiohw_enable_headphone_jack(bool enable); const struct sound_settings_info audiohw_settings[] = @@ -51,10 +44,14 @@ const struct sound_settings_info audiohw_settings[] = [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, #ifdef HAVE_RECORDING - [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, - [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, + /* Digital: -119.0dB to +8.0dB in 0.5dB increments + * Analog: Relegated to volume control + * Circumstances unfortunately do not allow a great deal of positive + * gain. */ + [SOUND_LEFT_GAIN] = {"dB", 1, 1,-238, 16, 0}, + [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-238, 16, 0}, #if 0 - [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, + [SOUND_MIC_GAIN] = {"dB", 1, 1,-238, 16, 0}, #endif #endif #if 0 @@ -107,8 +104,8 @@ static uint16_t wmc_regs[WMC_NUM_REGISTERS] = [WMC_3D_CONTROL] = 0x000, [WMC_BEEP_CONTROL] = 0x000, [WMC_INPUT_CTRL] = 0x033, - [WMC_LEFT_INP_PGA_GAIN_CTRL] = 0x010, - [WMC_RIGHT_INP_PGA_GAIN_CTRL] = 0x010, + [WMC_LEFT_INP_PGA_GAIN_CTRL] = 0x010 | WMC_VU | WMC_ZC, + [WMC_RIGHT_INP_PGA_GAIN_CTRL] = 0x010 | WMC_VU | WMC_ZC, [WMC_LEFT_ADC_BOOST_CTRL] = 0x100, [WMC_RIGHT_ADC_BOOST_CTRL] = 0x100, [WMC_OUTPUT_CTRL] = 0x002, @@ -129,7 +126,7 @@ struct bool ahw_mute; } wmc_vol = { - HW_VOL_MUTE, HW_VOL_MUTE, false + 0, 0, false }; static void wmc_write(unsigned int reg, unsigned int val) @@ -180,6 +177,27 @@ int tenthdb2master(int db) } } +int sound_val2phys(int setting, int value) +{ + int result; + + switch (setting) + { +#ifdef HAVE_RECORDING + case SOUND_LEFT_GAIN: + case SOUND_RIGHT_GAIN: + case SOUND_MIC_GAIN: + result = value * 5; + break; +#endif + + default: + result = value; + } + + return result; +} + void audiohw_preinit(void) { /* 1. Turn on external power supplies. Wait for supply voltage to settle. */ @@ -190,13 +208,12 @@ void audiohw_preinit(void) sleep(HZ/10); /* 2. Mute all analogue outputs */ - wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE | HW_VOL_ANA_MIN); - wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE | HW_VOL_ANA_MIN); + wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); + wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); wmc_set(WMC_LOUT2_SPK_VOLUME_CTRL, WMC_MUTE); wmc_set(WMC_ROUT2_SPK_VOLUME_CTRL, WMC_MUTE); wmc_set(WMC_OUT3_MIXER_CTRL, WMC_MUTE); wmc_set(WMC_OUT4_MONO_MIXER_CTRL, WMC_MUTE); - wmc_set(WMC_INPUT_CTRL, 0x000); /* 3. Set L/RMIXEN = 1 and DACENL/R = 1 in register R3. */ wmc_write(WMC_POWER_MANAGEMENT3, @@ -226,103 +243,105 @@ void audiohw_postinit(void) wmc_write(WMC_AUDIO_INTERFACE, WMC_WL_16 | WMC_FMT_I2S); wmc_write(WMC_DAC_CONTROL, WMC_DACOSR_128 | WMC_AMUTE); - wmc_set(WMC_INPUT_CTRL, WMC_R2_2INPPGA | WMC_L2_2INPPGA); - /* set PGA volumes to 0dB and enable zero cross */ - wmc_set(WMC_LEFT_INP_PGA_GAIN_CTRL, 0x10 | 1 << 7); - wmc_set(WMC_RIGHT_INP_PGA_GAIN_CTRL, 0x10 | 1 << 7); - /* write to INPPGAUPDATE to actually change voulme */ - wmc_set(WMC_LEFT_INP_PGA_GAIN_CTRL, 1 << 8); - wmc_set(WMC_RIGHT_INP_PGA_GAIN_CTRL, 1 << 8); - /* set boost gain to 0dB */ - wmc_set(WMC_LEFT_ADC_BOOST_CTRL, (5 << 4)); - wmc_set(WMC_RIGHT_ADC_BOOST_CTRL, (5 << 4)); + /* No ADC, no HP filter, no popping */ + wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN); + + wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL); + wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR); /* Specific to HW clocking */ wmc_write_masked(WMC_CLOCK_GEN_CTRL, WMC_BCLKDIV_4 | WMC_MS, WMC_BCLKDIV | WMC_MS | WMC_CLKSEL); audiohw_set_frequency(HW_FREQ_DEFAULT); - /* ADC silenced */ - wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL); - wmc_write_masked(WMC_RIGHT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL); - audiohw_enable_headphone_jack(true); } -void audiohw_set_headphone_vol(int vol_l, int vol_r) +static void get_headphone_levels(int val, int *dac_p, int *hp_p, + int *mix_p, int *boost_p) { - int prev_l = wmc_vol.vol_l; - int prev_r = wmc_vol.vol_r; - int dac_l, dac_r; - - wmc_vol.vol_l = vol_l; - wmc_vol.vol_r = vol_r; + int dac, hp, mix, boost; - /* When analogue volume falls below -57dB (0x00) start attenuating the - * DAC volume */ - if (vol_l >= HW_VOL_DIG_THRESHOLD) + if (val >= 33) { - if (vol_l > HW_VOL_MAX) - vol_l = HW_VOL_MAX; - - dac_l = HW_VOL_DIG_MAX; - vol_l -= HW_VOL_DIG_THRESHOLD; + dac = 255; + hp = val - 33; + mix = 7; + boost = 5; + } + else if (val >= 21) + { + dac = 189 + val / 3 * 6; + hp = val % 3; + mix = 7; + boost = (val - 18) / 3; } else { - if (vol_l < HW_VOL_MIN) - vol_l = HW_VOL_MIN; - - dac_l = 2*vol_l + HW_VOL_DIG_MIN; - vol_l = HW_VOL_ANA_MIN; + dac = 189 + val / 3 * 6; + hp = val % 3; + mix = val / 3; + boost = 1; } - if (vol_r >= HW_VOL_DIG_THRESHOLD) - { - if (vol_r > HW_VOL_MAX) - vol_r = HW_VOL_MAX; + *dac_p = dac; + *hp_p = hp; + *mix_p = mix; + *boost_p = boost; +} - dac_r = HW_VOL_DIG_MAX; - vol_r -= HW_VOL_DIG_THRESHOLD; - } - else - { - if (vol_r < HW_VOL_MIN) - vol_r = HW_VOL_MIN; +void audiohw_set_headphone_vol(int vol_l, int vol_r) +{ + int prev_l = wmc_vol.vol_l; + int prev_r = wmc_vol.vol_r; + int dac_l, dac_r, hp_l, hp_r; + int mix_l, mix_r, boost_l, boost_r; - dac_r = 2*vol_r + HW_VOL_DIG_MIN; - vol_r = HW_VOL_ANA_MIN; - } + wmc_vol.vol_l = vol_l; + wmc_vol.vol_r = vol_r; + + /* Mixers are synced to provide full volume range on both the analogue + * and digital pathways */ + get_headphone_levels(vol_l, &dac_l, &hp_l, &mix_l, &boost_l); + get_headphone_levels(vol_r, &dac_r, &hp_r, &mix_r, &boost_r); - /* Have to write both channels always to have the latching work */ + wmc_write_masked(WMC_LEFT_MIXER_CTRL, WMC_BYPLMIXVOLw(mix_l), + WMC_BYPLMIXVOL); + wmc_write_masked(WMC_LEFT_ADC_BOOST_CTRL, + WMC_L2_2BOOSTVOLw(boost_l), WMC_L2_2BOOSTVOL); wmc_write_masked(WMC_LEFT_DAC_DIGITAL_VOL, dac_l, WMC_DVOL); - wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, vol_l, WMC_AVOL); + wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, hp_l, WMC_AVOL); + + wmc_write_masked(WMC_RIGHT_MIXER_CTRL, WMC_BYPRMIXVOLw(mix_r), + WMC_BYPRMIXVOL); + wmc_write_masked(WMC_RIGHT_ADC_BOOST_CTRL, + WMC_R2_2BOOSTVOLw(boost_r), WMC_R2_2BOOSTVOL); wmc_write_masked(WMC_RIGHT_DAC_DIGITAL_VOL, dac_r, WMC_DVOL); - wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, vol_r, WMC_AVOL); + wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, hp_r, WMC_AVOL); - if (wmc_vol.vol_l > HW_VOL_MUTE) + if (vol_l > 0) { /* Not muted and going up from mute level? */ - if (prev_l <= HW_VOL_MUTE && !wmc_vol.ahw_mute) + if (prev_l <= 0 && !wmc_vol.ahw_mute) wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); } else { /* Going to mute level? */ - if (prev_l > HW_VOL_MUTE) + if (prev_l > 0) wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); } - if (wmc_vol.vol_r > HW_VOL_MUTE) + if (vol_r > 0) { /* Not muted and going up from mute level? */ - if (prev_r <= HW_VOL_MIN && !wmc_vol.ahw_mute) + if (prev_r <= 0 && !wmc_vol.ahw_mute) wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); } else { /* Going to mute level? */ - if (prev_r > HW_VOL_MUTE) + if (prev_r > 0) wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); } } @@ -358,10 +377,10 @@ void audiohw_mute(bool mute) else { /* Unmute outputs not at mute level */ - if (wmc_vol.vol_l > HW_VOL_MUTE) + if (wmc_vol.vol_l > 0) wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); - if (wmc_vol.vol_r > HW_VOL_MUTE) + if (wmc_vol.vol_r > 0) wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); } } @@ -491,9 +510,72 @@ void audiohw_set_frequency(int fsel) } #ifdef HAVE_RECORDING -/* TODO */ +void audiohw_set_recsrc(int source, bool recording) +{ + switch (source) + { + case AUDIO_SRC_PLAYBACK: + /* Disable all audio paths but DAC */ + /* Disable ADCs */ + wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN); + wmc_clear(WMC_POWER_MANAGEMENT2, WMC_ADCENL | WMC_ADCENR); + /* Disable bypass */ + wmc_clear(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX); + wmc_clear(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX); + /* Disable IP BOOSTMIX and PGA */ + wmc_clear(WMC_POWER_MANAGEMENT2, WMC_INPPGAENL | WMC_INPPGAENR | + WMC_BOOSTENL | WMC_BOOSTENR); + wmc_clear(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA); + wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL); + wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR); + break; + + case AUDIO_SRC_FMRADIO: + if (recording) + { + /* Disable bypass */ + wmc_clear(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX); + wmc_clear(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX); + /* Enable ADCs, IP BOOSTMIX and PGA, route L/R2 through PGA */ + wmc_set(WMC_POWER_MANAGEMENT2, WMC_ADCENL | WMC_ADCENR | + WMC_BOOSTENL | WMC_BOOSTENR | WMC_INPPGAENL | + WMC_INPPGAENR); + wmc_set(WMC_ADC_CONTROL, WMC_ADCOSR | WMC_HPFEN); + /* PGA at 0dB with +20dB boost */ + wmc_write_masked(WMC_LEFT_INP_PGA_GAIN_CTRL, 0x10, WMC_AVOL); + wmc_write_masked(WMC_RIGHT_INP_PGA_GAIN_CTRL, 0x10, WMC_AVOL); + wmc_set(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL); + wmc_set(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR); + /* Connect L/R2 inputs to PGA */ + wmc_set(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA); + } + else + { + /* Disable PGA and ADC, enable IP BOOSTMIX, route L/R2 directly to + * IP BOOSTMIX */ + wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN); + wmc_write_masked(WMC_POWER_MANAGEMENT2, WMC_BOOSTENL | WMC_BOOSTENR, + WMC_BOOSTENL | WMC_BOOSTENR | WMC_INPPGAENL | + WMC_INPPGAENR | WMC_ADCENL | WMC_ADCENR); + wmc_clear(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA); + wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL); + wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR); + /* Enable bypass to L/R mixers */ + wmc_set(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX); + wmc_set(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX); + } + break; + } +} + void audiohw_set_recvol(int left, int right, int type) { - (void)left; (void)right; (void)type; + switch (type) + { + case AUDIO_GAIN_LINEIN: + wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, left + 239, WMC_DVOL); + wmc_write_masked(WMC_RIGHT_ADC_DIGITAL_VOL, right + 239, WMC_DVOL); + return; + } } -#endif +#endif /* HAVE_RECORDING */ diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c index fb8fc49..1e2c188 100644 --- a/firmware/drivers/tuner/si4700.c +++ b/firmware/drivers/tuner/si4700.c @@ -47,7 +47,6 @@ extern int si4700_st(void); #endif #define SEEK_THRESHOLD 0x16 -#define TUNER_VOLUME 0xC #define I2C_ADR 0x20 @@ -332,10 +331,9 @@ static void si4700_sleep(int snooze) SYSCONFIG1_GPIO1 | SYSCONFIG1_GPIO2 | SYSCONFIG1_GPIO3); #endif - /* -6dB volume, seek threshold 12 */ si4700_write_masked(SYSCONFIG2, SYSCONFIG2_SKEETHw(SEEK_THRESHOLD) | - SYSCONFIG2_VOLUMEw(TUNER_VOLUME), + SYSCONFIG2_VOLUMEw(0xF), SYSCONFIG2_VOLUME | SYSCONFIG2_SEEKTH); } } -- cgit v1.1