AK4376 driver: refactoring

Some audiohw API calls are shared between playback and recording,
eg. frequency settings. Implementing these in the DAC driver won't
work for the M3K, as it uses a separate codec for microphone input.

Change-Id: Ieb0a267f8a81b9e2bbf0bbca951c5778f8dcd203
This commit is contained in:
Aidan MacDonald 2021-05-31 01:26:26 +01:00
parent f63edb52ef
commit 663c5268ac
3 changed files with 87 additions and 81 deletions

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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;
}