use embedded_hal::i2c::{I2c, SevenBitAddress};
const REG_CHIP_ID: u16 = 0x0000;
const REG_PWR_MGMT_1: u16 = 0x0001;
const REG_PWR_MGMT_2: u16 = 0x0002;
const REG_PWR_MGMT_3: u16 = 0x0003;
const REG_PWR_MGMT_4: u16 = 0x0004;
const REG_PWR_MGMT_5: u16 = 0x0005;
const REG_PWR_MGMT_6: u16 = 0x0006;
const REG_LEFT_LINE_IN1_2_VOL: u16 = 0x0018;
const REG_RIGHT_LINE_IN1_2_VOL: u16 = 0x001A;
const REG_LEFT_OUTPUT_VOL: u16 = 0x001C;
const REG_RIGHT_OUTPUT_VOL: u16 = 0x001D;
const REG_LINEOUT_VOL: u16 = 0x001E;
const REG_SPKMIXL_ATT: u16 = 0x0022;
const REG_SPKMIXR_ATT: u16 = 0x0023;
const REG_SPKOUT_MIXERS: u16 = 0x0024;
const REG_CLASS_W: u16 = 0x0025;
const REG_SPEAKER_VOL_LEFT: u16 = 0x0026;
const REG_SPEAKER_VOL_RIGHT: u16 = 0x0027;
const REG_INPUT_MIXER_2: u16 = 0x0028;
const REG_INPUT_MIXER_3: u16 = 0x0029;
const REG_INPUT_MIXER_4: u16 = 0x002A;
const REG_OUTPUT_MIXER_1: u16 = 0x002D;
const REG_OUTPUT_MIXER_2: u16 = 0x002E;
const REG_LINEOUT_MIXER_1: u16 = 0x0033;
const REG_LINEOUT_MIXER_2: u16 = 0x0034;
const REG_ANTIPOP_2: u16 = 0x0039;
const REG_CHARGE_PUMP_1: u16 = 0x004C;
const REG_CLASSW_HP: u16 = 0x0051;
const REG_DC_SERVO_1: u16 = 0x0054;
#[allow(dead_code)]
const REG_DC_SERVO_2: u16 = 0x0055;
const REG_ANALOGUE_HP1: u16 = 0x0060;
const REG_AIF1_CLOCKING_1: u16 = 0x0200;
const REG_AIF1_CLOCKING_2: u16 = 0x0201;
const REG_CLOCKING_1: u16 = 0x0208;
const REG_CLOCKING_2: u16 = 0x0209;
const REG_AIF1_RATE: u16 = 0x0210;
const REG_FLL1_CTRL_1: u16 = 0x0220;
const REG_FLL1_CTRL_2: u16 = 0x0221;
const REG_FLL1_CTRL_3: u16 = 0x0222;
const REG_FLL1_CTRL_4: u16 = 0x0223;
const REG_FLL1_CTRL_5: u16 = 0x0224;
const REG_FLL1_LOCK_STS: u16 = 0x0732;
const FLL1_LOCK_STS_BIT: u16 = 1 << 5;
const REG_AIF1_CONTROL_1: u16 = 0x0300;
const REG_AIF1_CONTROL_2: u16 = 0x0301;
const REG_AIF1_MASTER_SLAVE: u16 = 0x0302;
const REG_AIF1_ADC_LRCLK: u16 = 0x0304;
const REG_AIF1_DAC_LRCLK: u16 = 0x0305;
const REG_AIF1_ADC1_L_VOL: u16 = 0x0400;
const REG_AIF1_ADC1_R_VOL: u16 = 0x0401;
const REG_AIF1_ADC1_FILTERS: u16 = 0x0410;
const REG_AIF1_DAC1_FILTER_1: u16 = 0x0420;
const REG_AIF1_DAC1_LEFT_VOL: u16 = 0x0402;
const REG_AIF1_DAC1_RIGHT_VOL: u16 = 0x0403;
const REG_DAC1_MIXER_VOLUMES: u16 = 0x0600;
const REG_DAC1L_MIXER: u16 = 0x0601;
const REG_DAC1R_MIXER: u16 = 0x0602;
const REG_AIF1_ADC1L_MIXER: u16 = 0x0606;
const REG_AIF1_ADC1R_MIXER: u16 = 0x0607;
const REG_OVERSAMPLING: u16 = 0x0620;
const REG_DAC1_LEFT_VOL: u16 = 0x0610;
const REG_DAC1_RIGHT_VOL: u16 = 0x0611;
const REG_SW_RESET: u16 = 0x0000;
const WM8994_ID: u16 = 0x8994;
#[inline(always)]
fn delay_busy(microseconds: u32) {
let cycles = (microseconds as u64).saturating_mul(400);
let cycles = cycles.min(u32::MAX as u64) as u32;
cortex_m::asm::delay(cycles);
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum OutputDevice {
Headphone,
Speaker,
Both,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum InputDevice {
LineIn1,
LineIn2,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FllLockOutcome {
LockedFirstTry,
LockedAfterRetry {
attempts: u8,
},
Failed,
}
pub struct Wm8994<I2C> {
i2c: I2C,
}
impl<I2C> Wm8994<I2C>
where
I2C: I2c<SevenBitAddress>,
{
const ADDRESS: SevenBitAddress = 0x1A;
pub fn new(i2c: I2C) -> Self {
Self { i2c }
}
pub fn release(self) -> I2C {
self.i2c
}
pub fn reset(&mut self) -> Result<(), I2C::Error> {
self.write_reg(REG_SW_RESET, 0x0000)
}
pub fn read_id(&mut self) -> Result<u16, I2C::Error> {
self.read_reg(REG_CHIP_ID)
}
pub fn verify_id(&mut self) -> Result<bool, I2C::Error> {
Ok(self.read_id()? == WM8994_ID)
}
pub fn init_playback(
&mut self,
sample_rate: u32,
mclk_hz: u32,
output: OutputDevice,
) -> Result<(), I2C::Error> {
self.reset()?;
for _ in 0..400_000u32 {
core::hint::black_box(0u32);
}
self.write_reg(REG_ANTIPOP_2, 0x0048)?;
self.write_reg(REG_PWR_MGMT_1, 0x0003)?;
for _ in 0..20_000_000u32 {
core::hint::black_box(0u32);
}
self.write_reg(REG_CHARGE_PUMP_1, 0x8001)?;
self.configure_fll1(sample_rate, mclk_hz)?;
self.write_reg(REG_AIF1_CLOCKING_1, 0x0011)?;
self.write_reg(REG_CLOCKING_1, 0x000A)?;
let rate_bits: u16 = match sample_rate {
8000 => 0x00,
11025 => 0x01,
12000 => 0x02,
16000 => 0x03,
22050 => 0x04,
24000 => 0x05,
32000 => 0x06,
44100 => 0x07,
48000 => 0x08,
88200 => 0x09,
96000 => 0x0A,
_ => 0x08, };
self.write_reg(REG_AIF1_RATE, (rate_bits << 4) | 0x0003)?;
self.write_reg(REG_AIF1_CONTROL_1, 0x4010)?;
self.write_reg(REG_AIF1_MASTER_SLAVE, 0x0000)?;
self.write_reg(REG_AIF1_CLOCKING_2, 0x0000)?;
self.write_reg(REG_CLOCKING_2, 0x0003)?;
self.configure_output(output)?;
self.write_reg(REG_AIF1_DAC1_FILTER_1, 0x0000)?;
self.write_reg(REG_DAC1_LEFT_VOL, 0x01C0)?; self.write_reg(REG_DAC1_RIGHT_VOL, 0x01C0)?;
self.write_reg(REG_AIF1_DAC1_LEFT_VOL, 0x01C0)?;
self.write_reg(REG_AIF1_DAC1_RIGHT_VOL, 0x01C0)?;
Ok(())
}
pub fn set_headphone_volume(&mut self, vol: u8) -> Result<(), I2C::Error> {
let v = (vol.min(63) as u16) | 0x0140; self.write_reg(REG_LEFT_OUTPUT_VOL, v)?;
self.write_reg(REG_RIGHT_OUTPUT_VOL, v)?;
Ok(())
}
pub fn set_speaker_volume(&mut self, vol: u8) -> Result<(), I2C::Error> {
let v = (vol.min(63) as u16) | 0x0140; self.write_reg(REG_SPEAKER_VOL_LEFT, v)?;
self.write_reg(REG_SPEAKER_VOL_RIGHT, v)?;
Ok(())
}
pub fn mute(&mut self, mute: bool) -> Result<(), I2C::Error> {
if mute {
self.write_reg(REG_AIF1_DAC1_FILTER_1, 0x0200)?; } else {
self.write_reg(REG_AIF1_DAC1_FILTER_1, 0x0000)?; }
Ok(())
}
pub fn init_record(
&mut self,
sample_rate: u32,
_mclk_hz: u32,
input: InputDevice,
) -> Result<FllLockOutcome, I2C::Error> {
self.reset()?;
self.write_reg(REG_ANTIPOP_2, 0x006C)?;
self.write_reg(REG_PWR_MGMT_1, 0x0033)?;
delay_busy(50_000);
self.write_reg(REG_CHARGE_PUMP_1, 0x9F25)?;
delay_busy(15_000);
let pm2: u16 = match input {
InputDevice::LineIn1 => {
(1 << 14) | (1 << 13) | (1 << 9) | (1 << 8) | (1 << 6) | (1 << 4)
}
InputDevice::LineIn2 => {
(1 << 14) | (1 << 13) | (1 << 9) | (1 << 8) | (1 << 7) | (1 << 5)
}
};
self.write_reg(REG_PWR_MGMT_2, pm2)?;
self.write_reg(REG_PWR_MGMT_4, (1 << 1) | 1)?;
self.write_reg(REG_PWR_MGMT_5, 0x0003)?;
self.write_reg(REG_PWR_MGMT_6, 0x0000)?;
let line_vol: u16 = 0x010B;
self.write_reg(REG_LEFT_LINE_IN1_2_VOL, line_vol)?;
self.write_reg(REG_RIGHT_LINE_IN1_2_VOL, line_vol)?;
let input_mix_2: u16 = match input {
InputDevice::LineIn1 | InputDevice::LineIn2 => 0x0033,
};
self.write_reg(REG_INPUT_MIXER_2, input_mix_2)?;
let (mixer3, mixer4) = match input {
InputDevice::LineIn1 => (0x0020u16, 0x0020u16), InputDevice::LineIn2 => (0x0100u16, 0x0100u16), };
self.write_reg(REG_INPUT_MIXER_3, mixer3)?;
self.write_reg(REG_INPUT_MIXER_4, mixer4)?;
self.write_reg(REG_AIF1_ADC1L_MIXER, 0x0002)?;
self.write_reg(REG_AIF1_ADC1R_MIXER, 0x0002)?;
let _ = sample_rate;
const AIF1CLK_FROM_MCLK1: bool = true;
let lock_outcome = if AIF1CLK_FROM_MCLK1 {
FllLockOutcome::LockedFirstTry
} else {
self.write_reg(REG_FLL1_CTRL_5, 0x0003)?; self.write_reg(REG_FLL1_CTRL_4, 0x0800)?; self.write_reg(REG_FLL1_CTRL_3, 0x0000)?; self.write_reg(REG_FLL1_CTRL_2, 7u16 << 8)?; let o = self.fll1_enable_and_wait_lock_bclk1()?;
if matches!(o, FllLockOutcome::Failed) {
return Ok(o);
}
o
};
self.write_reg(REG_AIF1_CONTROL_1, 0x4010)?;
self.write_reg(REG_AIF1_MASTER_SLAVE, 0x0000)?;
const AIF1_RATE32_DIR_OFF: u16 = 0x0020; self.write_reg(REG_AIF1_ADC_LRCLK, AIF1_RATE32_DIR_OFF)?;
self.write_reg(REG_AIF1_DAC_LRCLK, AIF1_RATE32_DIR_OFF)?;
let aif1_rate: u16 = match sample_rate {
8_000 => 0x03,
11_025 => 0x13,
12_000 => 0x23,
16_000 => 0x33,
22_050 => 0x43,
24_000 => 0x53,
32_000 => 0x63,
44_100 => 0x73,
48_000 => 0x83,
88_200 => 0x93,
96_000 => 0xA3,
_ => 0x83,
};
self.write_reg(REG_AIF1_RATE, aif1_rate)?;
self.write_reg(REG_CLOCKING_1, 0x000A)?;
let aif1clk = if AIF1CLK_FROM_MCLK1 { 0x0001 } else { 0x0011 };
self.write_reg(REG_AIF1_CLOCKING_1, aif1clk)?;
delay_busy(5_000);
delay_busy(50_000);
self.write_reg(REG_AIF1_ADC1_L_VOL, 0x01C0)?;
self.write_reg(REG_AIF1_ADC1_R_VOL, 0x01C0)?;
self.write_reg(REG_AIF1_ADC1_FILTERS, 0x1800)?;
self.write_reg(REG_OVERSAMPLING, 0x0003)?;
delay_busy(5_000);
let pm4 = self.read_reg(REG_PWR_MGMT_4)?;
self.write_reg(REG_PWR_MGMT_4, pm4 | (1 << 9) | (1 << 8))?;
let pm5 = self.read_reg(REG_PWR_MGMT_5)?;
self.write_reg(REG_PWR_MGMT_5, pm5 | (1 << 9) | (1 << 8))?;
const AIF1_RATE32_DIR_ON: u16 = 0x0820; self.write_reg(REG_AIF1_ADC_LRCLK, AIF1_RATE32_DIR_ON)?;
self.write_reg(REG_AIF1_DAC_LRCLK, AIF1_RATE32_DIR_ON)?;
delay_busy(50_000);
self.write_reg(REG_AIF1_ADC1_L_VOL, 0x01C0)?;
self.write_reg(REG_AIF1_ADC1_R_VOL, 0x01C0)?;
self.write_reg(REG_AIF1_ADC1_FILTERS, 0x1800)?;
self.write_reg(REG_PWR_MGMT_3, 0x0030)?;
self.write_reg(REG_PWR_MGMT_1, 0x0333)?;
self.write_reg(REG_DAC1_MIXER_VOLUMES, 0x018C)?;
self.write_reg(REG_DAC1L_MIXER, 0x0010)?;
self.write_reg(REG_DAC1R_MIXER, 0x0020)?;
self.write_reg(REG_OUTPUT_MIXER_1, 0x0001)?;
self.write_reg(REG_OUTPUT_MIXER_2, 0x0001)?;
self.write_reg(REG_LINEOUT_VOL, 0x0000)?;
let hpout_vol: u16 = 0x100 | 0x40 | 0x39;
self.write_reg(REG_LEFT_OUTPUT_VOL, hpout_vol)?;
self.write_reg(REG_RIGHT_OUTPUT_VOL, hpout_vol)?;
self.write_reg(REG_AIF1_DAC1_FILTER_1, 0x0000)?;
self.write_reg(REG_DAC1_LEFT_VOL, 0x01C0)?;
self.write_reg(REG_DAC1_RIGHT_VOL, 0x01C0)?;
self.write_reg(REG_AIF1_DAC1_LEFT_VOL, 0x01C0)?;
self.write_reg(REG_AIF1_DAC1_RIGHT_VOL, 0x01C0)?;
self.write_reg(REG_CLASSW_HP, 0x0005)?;
Ok(lock_outcome)
}
pub fn init_hp_output_stage(&mut self) -> Result<(), I2C::Error> {
self.write_reg(REG_ANALOGUE_HP1, 0x0022)?;
self.write_reg(REG_DC_SERVO_1, 0x0033)?;
Ok(())
}
pub fn finish_hp_output_stage(&mut self) -> Result<(), I2C::Error> {
self.write_reg(REG_ANALOGUE_HP1, 0x00EE)?;
Ok(())
}
pub fn readback_critical_regs(&mut self, out: &mut [u32]) -> Result<(), I2C::Error> {
const REGS: [u16; 24] = [
REG_PWR_MGMT_1, REG_PWR_MGMT_2, REG_PWR_MGMT_4, REG_PWR_MGMT_5, REG_PWR_MGMT_6, REG_LEFT_LINE_IN1_2_VOL, REG_RIGHT_LINE_IN1_2_VOL, REG_INPUT_MIXER_2, REG_INPUT_MIXER_3, REG_INPUT_MIXER_4, REG_AIF1_CLOCKING_1, REG_CLOCKING_1, REG_AIF1_RATE, REG_AIF1_CONTROL_1, REG_AIF1_ADC1_L_VOL, REG_AIF1_ADC1_R_VOL, REG_AIF1_ADC1_FILTERS, REG_AIF1_ADC1L_MIXER, REG_AIF1_ADC1R_MIXER, REG_ANTIPOP_2, REG_FLL1_CTRL_1, REG_FLL1_CTRL_2, REG_FLL1_CTRL_4, REG_FLL1_CTRL_5, ];
for (i, ®) in REGS.iter().enumerate() {
if i >= out.len() {
break;
}
let val = self.read_reg(reg)?;
out[i] = ((reg as u32) << 16) | (val as u32);
}
Ok(())
}
pub const READBACK_SLOT_COUNT: usize = 24;
pub fn readback_output_regs(&mut self, out: &mut [u32]) -> Result<(), I2C::Error> {
const REGS: [u16; 22] = [
REG_CHIP_ID, REG_PWR_MGMT_1, REG_PWR_MGMT_3, REG_PWR_MGMT_5, REG_LEFT_OUTPUT_VOL, REG_RIGHT_OUTPUT_VOL, REG_LINEOUT_VOL, REG_OUTPUT_MIXER_1, REG_OUTPUT_MIXER_2, REG_LINEOUT_MIXER_1, REG_LINEOUT_MIXER_2, REG_ANTIPOP_2, REG_CHARGE_PUMP_1, REG_CLASSW_HP, REG_DC_SERVO_1, REG_DC_SERVO_2, REG_ANALOGUE_HP1, REG_AIF1_DAC1_LEFT_VOL, REG_AIF1_DAC1_RIGHT_VOL, REG_AIF1_DAC1_FILTER_1, REG_DAC1L_MIXER, REG_DAC1R_MIXER, ];
for (i, ®) in REGS.iter().enumerate() {
if i >= out.len() {
break;
}
let val = self.read_reg(reg)?;
out[i] = ((reg as u32) << 16) | (val as u32);
}
Ok(())
}
pub const READBACK_OUTPUT_SLOT_COUNT: usize = 22;
fn configure_fll1(&mut self, sample_rate: u32, mclk_hz: u32) -> Result<(), I2C::Error> {
let fll_output = sample_rate * 256;
self.write_reg(REG_FLL1_CTRL_1, 0x0000)?;
let (fll_fratio, fratio_bits) = if mclk_hz < 1_000_000 {
(8u32, 0b011u16)
} else {
(1u32, 0b000u16)
};
let nk_x = (fll_output as u64) * (fll_fratio as u64);
let n = (nk_x / mclk_hz as u64) as u16;
let remainder = (nk_x % mclk_hz as u64) as u64;
let k = ((remainder * 65536) / mclk_hz as u64) as u16;
self.write_reg(REG_FLL1_CTRL_2, n & 0x03FF)?;
self.write_reg(REG_FLL1_CTRL_3, k)?;
self.write_reg(REG_FLL1_CTRL_4, fratio_bits << 0)?;
self.write_reg(REG_FLL1_CTRL_5, 0x0000)?;
let ctrl1 = 0x0001 | if k != 0 { 0x0004 } else { 0x0000 };
self.write_reg(REG_FLL1_CTRL_1, ctrl1)?;
for _ in 0..2_000_000u32 {
core::hint::black_box(0u32);
}
Ok(())
}
fn fll1_enable_and_wait_lock_bclk1(&mut self) -> Result<FllLockOutcome, I2C::Error> {
const PER_ATTEMPT_TIMEOUT_MS: u32 = 100;
const POLL_INTERVAL_MS: u32 = 1;
const RE_ARM_WAIT_MS: u32 = 5;
const MAX_RETRIES: u8 = 3;
let poll_iters = PER_ATTEMPT_TIMEOUT_MS / POLL_INTERVAL_MS;
for attempt in 0..=MAX_RETRIES {
if attempt > 0 {
self.write_reg(REG_FLL1_CTRL_1, 0x0000)?; delay_busy(RE_ARM_WAIT_MS * 1_000);
self.write_reg(REG_FLL1_CTRL_5, 0x0003)?; self.write_reg(REG_FLL1_CTRL_4, 0x0800)?; self.write_reg(REG_FLL1_CTRL_3, 0x0000)?; self.write_reg(REG_FLL1_CTRL_2, 7u16 << 8)?; }
self.write_reg(REG_FLL1_CTRL_1, 0x0001)?;
for _ in 0..poll_iters {
delay_busy(POLL_INTERVAL_MS * 1_000);
let status = self.read_reg(REG_FLL1_LOCK_STS)?;
if status & FLL1_LOCK_STS_BIT != 0 {
return Ok(if attempt == 0 {
FllLockOutcome::LockedFirstTry
} else {
FllLockOutcome::LockedAfterRetry { attempts: attempt }
});
}
}
}
Ok(FllLockOutcome::Failed)
}
fn configure_output(&mut self, output: OutputDevice) -> Result<(), I2C::Error> {
match output {
OutputDevice::Headphone | OutputDevice::Both => {
let pwr1 = self.read_reg(REG_PWR_MGMT_1)?;
self.write_reg(REG_PWR_MGMT_1, pwr1 | 0x0300)?;
self.write_reg(REG_PWR_MGMT_3, 0x0030)?;
self.write_reg(REG_OUTPUT_MIXER_1, 0x0001)?; self.write_reg(REG_OUTPUT_MIXER_2, 0x0001)?;
self.set_headphone_volume(50)?;
self.write_reg(REG_CLASS_W, 0x0005)?;
}
_ => {}
}
match output {
OutputDevice::Speaker | OutputDevice::Both => {
let pwr1 = self.read_reg(REG_PWR_MGMT_1)?;
self.write_reg(REG_PWR_MGMT_1, pwr1 | 0x3000)?;
let pwr3 = self.read_reg(REG_PWR_MGMT_3)?;
self.write_reg(REG_PWR_MGMT_3, pwr3 | 0x0300)?;
self.write_reg(REG_SPKMIXL_ATT, 0x0001)?; self.write_reg(REG_SPKMIXR_ATT, 0x0001)?;
self.write_reg(REG_SPKOUT_MIXERS, 0x0018)?;
self.set_speaker_volume(50)?;
}
_ => {}
}
self.write_reg(REG_PWR_MGMT_4, 0x0300)?;
self.write_reg(REG_PWR_MGMT_5, 0x0303)?;
self.write_reg(REG_PWR_MGMT_6, 0x000C)?;
Ok(())
}
pub fn write_reg(&mut self, reg: u16, val: u16) -> Result<(), I2C::Error> {
let buf = [
(reg >> 8) as u8,
(reg & 0xFF) as u8,
(val >> 8) as u8,
(val & 0xFF) as u8,
];
self.i2c.write(Self::ADDRESS, &buf)
}
pub fn read_reg(&mut self, reg: u16) -> Result<u16, I2C::Error> {
let addr = [(reg >> 8) as u8, (reg & 0xFF) as u8];
let mut buf = [0u8; 2];
self.i2c.write_read(Self::ADDRESS, &addr, &mut buf)?;
Ok(((buf[0] as u16) << 8) | buf[1] as u16)
}
}