diff --git a/firmware/drivers/audio/ak4376.c b/firmware/drivers/audio/ak4376.c index 494bbabfa4..11714b210d 100644 --- a/firmware/drivers/audio/ak4376.c +++ b/firmware/drivers/audio/ak4376.c @@ -27,6 +27,18 @@ #include "system.h" #include "i2c-async.h" +/* sample rates supported by the hardware */ +#define CAPS (SAMPR_CAP_192 | SAMPR_CAP_176 | \ + SAMPR_CAP_96 | SAMPR_CAP_88 | SAMPR_CAP_64 | \ + SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) + +/* future proofing */ +#if (HW_SAMPR_CAPS & ~CAPS) != 0 +# error "incorrect HW_SAMPR_CAPS" +#endif + #ifndef HAVE_SW_VOLUME_CONTROL # error "AK4376 requires HAVE_SW_VOLUME_CONTROL!" #endif @@ -40,7 +52,7 @@ */ /* Converts HW_FREQ_XX constants to register values */ -static const int ak4376_fsel_to_hw[] = { +static const uint8_t ak4376_fsel_to_hw[] = { HW_HAVE_192_(AK4376_FS_192,) HW_HAVE_176_(AK4376_FS_176,) HW_HAVE_96_(AK4376_FS_96,) @@ -57,19 +69,13 @@ static const int ak4376_fsel_to_hw[] = { HW_HAVE_8_(AK4376_FS_8,) }; -static struct ak4376 { - int fsel; - int low_mode; - int regs[AK4376_NUM_REGS]; -} ak4376; +static int ak4376_regs[AK4376_NUM_REGS]; -void ak4376_init(void) +void ak4376_open(void) { /* Initialize DAC state */ - ak4376.fsel = HW_FREQ_48; - ak4376.low_mode = 0; for(int i = 0; i < AK4376_NUM_REGS; ++i) - ak4376.regs[i] = -1; + ak4376_regs[i] = -1; /* Initial reset after power-on */ ak4376_set_pdn_pin(0); @@ -102,9 +108,6 @@ void ak4376_init(void) /* Write initial configuration prior to power-up */ for(size_t i = 0; i < ARRAYLEN(init_config); i += 2) ak4376_write(init_config[i], init_config[i+1]); - - /* Initial frequency setting, also handles DAC/amp power-up */ - audiohw_set_frequency(HW_FREQ_48); } void ak4376_close(void) @@ -121,22 +124,22 @@ void ak4376_close(void) void ak4376_write(int reg, int value) { /* Ensure value is sensible and differs from the last set value */ - if((value & 0xff) == value && ak4376.regs[reg] != value) { + if((value & 0xff) == value && ak4376_regs[reg] != value) { int r = i2c_reg_write1(AK4376_BUS, AK4376_ADDR, reg, value); if(r == I2C_STATUS_OK) - ak4376.regs[reg] = value; + ak4376_regs[reg] = value; else - ak4376.regs[reg] = -1; + ak4376_regs[reg] = -1; } } int ak4376_read(int reg) { /* Only read from I2C if we don't already know the value */ - if(ak4376.regs[reg] < 0) - ak4376.regs[reg] = i2c_reg_read1(AK4376_BUS, AK4376_ADDR, reg); + if(ak4376_regs[reg] < 0) + ak4376_regs[reg] = i2c_reg_read1(AK4376_BUS, AK4376_ADDR, reg); - return ak4376.regs[reg]; + return ak4376_regs[reg]; } static int round_step_up(int x, int step) @@ -180,7 +183,7 @@ static int amp_vol_to_hw(int vol) return (vol - AK4376_AMP_VOLUME_MIN) / AK4376_AMP_VOLUME_STEP + 1; } -void audiohw_set_volume(int vol_l, int vol_r) +void ak4376_set_volume(int vol_l, int vol_r) { int amp; int mix_l = AK4376_MIX_LCH, dig_l, sw_l; @@ -210,7 +213,7 @@ void audiohw_set_volume(int vol_l, int vol_r) pcm_set_master_volume(sw_l, sw_r); } -void audiohw_set_filter_roll_off(int val) +void ak4376_set_filter_roll_off(int val) { int reg = ak4376_read(AK4376_REG_FILTER); reg &= ~0xc0; @@ -218,11 +221,8 @@ void audiohw_set_filter_roll_off(int val) ak4376_write(AK4376_REG_FILTER, reg); } -void audiohw_set_frequency(int fsel) +void ak4376_set_freqmode(int fsel, int mult, int power_mode) { - /* Determine master clock multiplier */ - int mult = ak4376_set_mclk_freq(fsel, false); - /* Calculate clock mode for frequency. Multipliers of 32/64 are only * for rates >= 256 KHz which are not supported by Rockbox, so they * are commented out -- but they're in the correct place. */ @@ -248,27 +248,11 @@ void audiohw_set_frequency(int fsel) /* Handle the DSMLP bit in the MODE_CTRL register */ int mode_ctrl = 0x00; - if(ak4376.low_mode || hw_freq_sampr[fsel] <= SAMPR_12) + if(power_mode || hw_freq_sampr[fsel] <= SAMPR_12) mode_ctrl |= 0x40; /* Program the new settings */ ak4376_write(AK4376_REG_CLOCK_MODE, clock_mode); ak4376_write(AK4376_REG_MODE_CTRL, mode_ctrl); - ak4376_write(AK4376_REG_PWR3, ak4376.low_mode ? 0x11 : 0x01); - - /* Enable the master clock */ - ak4376_set_mclk_freq(fsel, true); - - /* Remember the frequency */ - ak4376.fsel = fsel; -} - -void audiohw_set_power_mode(int mode) -{ - /* This is handled via audiohw_set_frequency() since changing LPMODE - * bit requires power-down/power-up & changing other bits as well */ - if(ak4376.low_mode != mode) { - ak4376.low_mode = mode; - audiohw_set_frequency(ak4376.fsel); - } + ak4376_write(AK4376_REG_PWR3, power_mode ? 0x11 : 0x01); } diff --git a/firmware/export/ak4376.h b/firmware/export/ak4376.h index eb06755e92..eab0bc24f3 100644 --- a/firmware/export/ak4376.h +++ b/firmware/export/ak4376.h @@ -104,10 +104,12 @@ AUDIOHW_SETTING(POWER_MODE, "", 0, 1, 0, 1, 0) #define AK4376_FS_176 17 #define AK4376_FS_192 18 -/* Functions to power on / off the DAC which should be called from - * the target's audiohw_init() / audiohw_close() implementation. +/* Functions to power on / off the DAC. + * + * NOTE: Target must call ak4376_set_frequency() after ak4376_open() to + * finish the power-up sequence of the headphone amp. */ -extern void ak4376_init(void); +extern void ak4376_open(void); extern void ak4376_close(void); /* Register read/write. Cached to avoid redundant reads/writes. */ @@ -117,16 +119,17 @@ extern int ak4376_read(int reg); /* Target-specific function to set the PDN pin level. */ extern void ak4376_set_pdn_pin(int level); -/* Target-specific function to control the external master clock frequency. - * This is called by the ak4376's audiohw implementation when switching to - * or from a frequency that is configured to use this clock source. +/* Set overall output volume */ +extern void ak4376_set_volume(int vol_l, int vol_r); + +/* Set the roll-off filter */ +extern void ak4376_set_filter_roll_off(int val); + +/* Set audio sampling frequency and power mode. * - * - hw_freq is the new sample rate -- one of the HW_FREQ_XX constants. - * - enabled is true if clock should be output, false if not. - * - * The return value is the master clock rate as a multiple of the sampling - * frequency. The allowed multiples depend on the sampling frequency, shown - * in the table below. + * If the I2S master clock is being supplied externally, the caller must also + * give the master clock multiplier 'mult'. The accepted values depend on the + * sampling rate, see below: * * +-----------+------------------------+ * | frequency | master clock rate | @@ -137,16 +140,13 @@ extern void ak4376_set_pdn_pin(int level); * | 128 - 192 | 128fs | * +-----------+------------------------+ * - * For example, at 48 KHz you could return either 256 or 512 depending on - * the rate you decided to actually use. + * Switching between high-power and low-power mode requires the same registers + * and power-up / power-down sequences as a frequency switch, so both settings + * are controlled by this function. * - * You need to return a valid master multiplier for supported frequencies - * even when enabled = false, since the driver needs to know the multiplier - * _before_ enabling the clock. - * - * For unsupported frequencies you don't need to return a valid master - * multiplier, because the DAC doesn't need the return value in such cases. + * high power mode -- use power_mode=0 + * low power mode -- use power_mode=1 */ -extern int ak4376_set_mclk_freq(int hw_freq, bool enabled); +extern void ak4376_set_freqmode(int fsel, int mult, int power_mode); #endif /* __AK4376_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c index d1c4d67d33..542d1745dc 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c @@ -22,10 +22,24 @@ #include "audiohw.h" #include "system.h" #include "pcm_sampr.h" -#include "logf.h" #include "aic-x1000.h" #include "i2c-x1000.h" #include "gpio-x1000.h" +#include "logf.h" + +static int cur_fsel = HW_FREQ_48; +static int cur_power_mode = 0; + +static void set_ak_freqmode(void) +{ + int freq = hw_freq_sampr[cur_fsel]; + int mult = freq >= SAMPR_176 ? 128 : 256; + + aic_enable_i2s_bit_clock(false); + aic_set_i2s_clock(X1000_CLK_SCLK_A, freq, mult); + ak4376_set_freqmode(cur_fsel, mult, cur_power_mode); + aic_enable_i2s_bit_clock(true); +} void audiohw_init(void) { @@ -36,7 +50,8 @@ void audiohw_init(void) /* Initialize DAC */ i2c_x1000_set_freq(AK4376_BUS, I2C_FREQ_400K); - ak4376_init(); + ak4376_open(); + set_ak_freqmode(); } void audiohw_postinit(void) @@ -48,22 +63,29 @@ void audiohw_close(void) ak4376_close(); } +void audiohw_set_volume(int vol_l, int vol_r) +{ + ak4376_set_volume(vol_l, vol_r); +} + +void audiohw_set_filter_roll_off(int val) +{ + ak4376_set_filter_roll_off(val); +} + +void audiohw_set_frequency(int fsel) +{ + cur_fsel = fsel; + set_ak_freqmode(); +} + +void audiohw_set_power_mode(int mode) +{ + cur_power_mode = mode; + set_ak_freqmode(); +} + void ak4376_set_pdn_pin(int level) { gpio_config(GPIO_A, 1 << 16, GPIO_OUTPUT(level ? 1 : 0)); } - -int ak4376_set_mclk_freq(int hw_freq, bool enabled) -{ - int freq = hw_freq_sampr[hw_freq]; - int mult = freq >= SAMPR_176 ? 128 : 256; - - if(enabled) { - if(aic_set_i2s_clock(X1000_CLK_SCLK_A, freq, mult)) { - logf("WARNING: unachievable audio rate %d x %d!?", freq, mult); - } - } - - aic_enable_i2s_bit_clock(enabled); - return mult; -}