summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-05-31 01:26:26 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-05-31 23:00:14 +0100
commit663c5268ace48cc4be03b4c43355038f06d4b3c5 (patch)
tree192b2189580357cc2f886bd248c99572b8a21cda
parentf63edb52ef8ecf18520926b40b3c61db37081a9d (diff)
downloadrockbox-663c5268ac.tar.gz
rockbox-663c5268ac.zip
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
-rw-r--r--firmware/drivers/audio/ak4376.c70
-rw-r--r--firmware/export/ak4376.h42
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c50
3 files changed, 84 insertions, 78 deletions
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.
- *
- * - 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.
+/* 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.
*
- * 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.
- *
- * 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.
+ * 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.
*
- * 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 ak4376_set_pdn_pin(int level)
+void audiohw_set_volume(int vol_l, int vol_r)
{
- gpio_config(GPIO_A, 1 << 16, GPIO_OUTPUT(level ? 1 : 0));
+ ak4376_set_volume(vol_l, vol_r);
}
-int ak4376_set_mclk_freq(int hw_freq, bool enabled)
+void audiohw_set_filter_roll_off(int val)
{
- int freq = hw_freq_sampr[hw_freq];
- int mult = freq >= SAMPR_176 ? 128 : 256;
+ ak4376_set_filter_roll_off(val);
+}
+
+void audiohw_set_frequency(int fsel)
+{
+ cur_fsel = fsel;
+ set_ak_freqmode();
+}
- if(enabled) {
- if(aic_set_i2s_clock(X1000_CLK_SCLK_A, freq, mult)) {
- logf("WARNING: unachievable audio rate %d x %d!?", freq, mult);
- }
- }
+void audiohw_set_power_mode(int mode)
+{
+ cur_power_mode = mode;
+ set_ak_freqmode();
+}
- aic_enable_i2s_bit_clock(enabled);
- return mult;
+void ak4376_set_pdn_pin(int level)
+{
+ gpio_config(GPIO_A, 1 << 16, GPIO_OUTPUT(level ? 1 : 0));
}