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_AIF1_CLOCKING_1: u16 = 0x0200;
const REG_AIF1_CLOCKING_2: u16 = 0x0201;
const REG_CLOCKING_1: u16 = 0x0208;
const REG_CLOCKING_2: u16 = 0x0210;
const REG_AIF1_RATE: u16 = 0x0211;
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_AIF1_CONTROL_1: u16 = 0x0300;
const REG_AIF1_CONTROL_2: u16 = 0x0301;
const REG_AIF1_MASTER_SLAVE: u16 = 0x0302;
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_LEFT_VOL: u16 = 0x0610;
const REG_DAC1_RIGHT_VOL: u16 = 0x0611;
const REG_OUTPUT_MIXER_1: u16 = 0x002D;
const REG_OUTPUT_MIXER_2: u16 = 0x002E;
const REG_LEFT_OUTPUT_VOL: u16 = 0x001C;
const REG_RIGHT_OUTPUT_VOL: u16 = 0x001D;
const REG_SPEAKER_VOL_LEFT: u16 = 0x0026;
const REG_SPEAKER_VOL_RIGHT: u16 = 0x0027;
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_CHARGE_PUMP_1: u16 = 0x004C;
const REG_ANTIPOP_2: u16 = 0x0039;
const REG_SW_RESET: u16 = 0x0000;
const WM8994_ID: u16 = 0x8994;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum OutputDevice {
Headphone,
Speaker,
Both,
}
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 = match sample_rate {
8000 => 0x00,
11025 => 0x01,
12000 => 0x01,
16000 => 0x02,
22050 => 0x03,
24000 => 0x03,
32000 => 0x04,
44100 => 0x05,
48000 => 0x06,
88200 => 0x07,
96000 => 0x07,
_ => 0x06, };
self.write_reg(REG_AIF1_RATE, (rate_bits << 4) | 0x0001)?;
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(())
}
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 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(())
}
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)
}
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)
}
}