From 03d37ac9b440b65990132b6dc445f644d4c4db6a Mon Sep 17 00:00:00 2001 From: LailaTheElf Date: Sun, 15 Jun 2025 00:23:43 +0200 Subject: [PATCH] start at TLV320AIC2354 driver --- .gitignore | 1 + Cargo.lock | 65 +++++ Cargo.toml | 7 + src/lib.rs | 8 + src/registers.rs | 642 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 723 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/registers.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ec93363 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,65 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "TLV320AIC2354" +version = "0.1.0" +dependencies = [ + "with_builtin_macros", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "with_builtin_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24deb3cd6e530e7617b12b1f0f1ce160a3a71d92feb351c4db5156d1d10e398a" +dependencies = [ + "with_builtin_macros-proc_macros", +] + +[[package]] +name = "with_builtin_macros-proc_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2259ae9b1285596f1ee52ce8f627013c65853d4d7f271cb10bfe2d048769804a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0df2b18 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "TLV320AIC2354" +version = "0.1.0" +edition = "2024" + +[dependencies] +with_builtin_macros = "0.1.0" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..45366e8 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,8 @@ +mod registers; + +fn i2c_read(page: u16, reg: u16) -> Result{ + Err(()) +} +fn i2c_write(page: u16, reg: u16, value: u16) -> Result<(), _>{ + Err(()) +} diff --git a/src/registers.rs b/src/registers.rs new file mode 100644 index 0000000..ba07720 --- /dev/null +++ b/src/registers.rs @@ -0,0 +1,642 @@ +mod regs { + use with_builtin_macros::with_builtin; + + trait RegisterRW { + const PAGE: u8; + const REG: u8; + + fn get_reg(&self) -> u8; + fn set_reg(&mut self, value: u8); + + fn load(&mut self) { + println!("load reg {} from page {}", Self::REG, Self::PAGE); + todo!(); + } + fn store(&mut self) { + println!("store reg {} from page {}", Self::REG, Self::PAGE); + todo!(); + } + } + trait RegisterRO { + const PAGE: u8; + const REG: u8; + + fn get_reg(&self) -> u8; + + fn load(&mut self) { + println!("load reg {} from page {}", Self::REG, Self::PAGE); + todo!(); + } + } + trait RegisterWO { + const PAGE: u8; + const REG: u8; + + fn set_reg(&mut self, value: u8); + + fn store(&mut self) { + println!("store reg {} from page {}", Self::REG, Self::PAGE); + todo!(); + } + } + + macro_rules! register_rw { + ($name:ident, $page:expr, $reg:expr) => { + struct $name { + value: u8, + changed: bool + } + impl RegisterRW for $name { + const PAGE: u8 = $page; + const REG: u8 = $reg; + + fn get_reg(&self) -> u8 { + self.value + } + fn set_reg(&mut self, value: u8) { + if self.value != value { + self.changed = true; + } + self.value = value; + } + + fn load(&mut self) { + self.changed = false; + todo!(); + } + fn store(&mut self) { + self.changed = false; + todo!(); + } + } + }; + } + macro_rules! register_ro { + ($name:ident, $page:expr, $reg:expr) => { + struct $name { + value: u8, + } + impl RegisterRO for $name { + const PAGE: u8 = $page; + const REG: u8 = $reg; + + fn get_reg(&self) -> u8 { + self.value + } + + fn load(&mut self) { + todo!(); + } + } + }; + } + macro_rules! register_wo { + ($name:ident, $page:expr, $reg:expr) => { + struct $name { + value: u8, + changed: bool + } + impl RegisterWO for $name { + const PAGE: u8 = $page; + const REG: u8 = $reg; + + fn set_reg(&mut self, value: u8) { + self.changed = true; + self.value = value; + } + + fn store(&mut self) { + self.changed = false; + todo!(); + } + } + }; + } + + macro_rules! bitmask_rw { + ($name:ident, $mask:expr, $shift:expr) => { + with_builtin!(let $fname = concat_idents!(get_, $name) in { + fn $fname (&mut self, $name: u8) { + self.value = (($name << $shift) & $mask) | (self.value & !$mask); + } + }); + with_builtin!(let $fname = concat_idents!(set_, $name) in { + fn $fname (&mut self) -> u8 { + (self.value & $mask) >> $shift + } + }); + }; + } + macro_rules! bitmask_ro { + ($name:ident, $mask:expr, $shrift:expr) => { + with_builtin!(let $fname = concat_idents!(get_, $name) in { + fn $fname (&mut self, $name: u8) { + self.value = (($name << $shift) & $mask) | (self.value & !$mask); + } + }); + }; + } + macro_rules! bitmask_wo { + ($name:ident, $mask:expr, $shrift:expr) => { + with_builtin!(let $fname = concat_idents!(set_, $name) in { + fn $fname (&mut self) -> u8 { + (self.value & $mask) >> $shift + } + }); + }; + } + + macro_rules! back_to_enum { + ($(#[$meta:meta])* $vis:vis enum $name:ident { + $($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)* + }) => { + $(#[$meta])* + $vis enum $name { + $($(#[$vmeta])* $vname $(= $val)?,)* + } + + impl std::convert::TryFrom for $name { + type Error = u8; + + fn try_from(v: u8) -> Result { + match v { + $(x if x == $name::$vname as u8 => Ok($name::$vname),)* + _ => Err(v), + } + } + } + } + } + + macro_rules! bitmask_enum_rw { + ($name:ident, $mask:expr, $shift:expr, $enum:ident) => { + with_builtin!(let $fname = concat_idents!(get_, $name) in { + fn $fname (&mut self, value: $enum) { + self.value = (((value as u8) << $shift) & $mask) | (self.value & !$mask); + } + }); + with_builtin!(let $fname = concat_idents!(set_, $name) in { + fn $fname (&mut self) -> Result<$enum, u8> { + $enum::try_from((self.value & $mask) >> $shift) + } + }); + }; + } + + macro_rules! bit_rw { + ($name:ident, $shift:expr) => { + with_builtin!(let $fname = concat_idents!(get_, $name) in { + fn $fname (&mut self, $name: bool) { + let bit: u8 = 0b1 << $shift; + if $name { + self.value &= bit; + } + else { + self.value |= !bit; + } + } + }); + with_builtin!(let $fname = concat_idents!(set_, $name) in { + fn $fname (&mut self) -> bool { + (self.value >> $shift) & 0b1 == 0b1 + } + }); + }; + } + macro_rules! bit_ro { + ($name:ident, $shift:expr) => { + with_builtin!(let $fname = concat_idents!(get_, $name) in { + fn $fname (&mut self, $name: bool) { + let bit: u8 = 0b1 << $shift; + if $name { + self.value &= bit; + } + else { + self.value |= !bit; + } + } + }); + }; + } + macro_rules! bit_wo { + ($name:ident, $shift:expr) => { + with_builtin!(let $fname = concat_idents!(set_, $name) in { + fn $fname (&mut self) -> bool { + (self.value >> $shift) & 0b1 == 0b1 + } + }); + }; + } + + register_rw!(PSEL, 0, 0); + + register_wo!(RESET, 0, 1); + impl RESET { + bit_wo!(software_reset, 0); + } + register_ro!(CLKMUX, 0, 4); + impl CLKMUX { + bitmask_enum_rw!(pllrange, 0b0100_0000, 6, ClkMuxPllrange); + bitmask_enum_rw!(pll_clock_in, 0b0000_1100, 2, ClkMuxPllClockIn); + bitmask_enum_rw!(codec_clock_in, 0b0000_0011, 0, ClkMuxCodecClockIn); + } + back_to_enum! { + pub enum ClkMuxPllrange { + LowClockRange = 0b0, + HighClockRange = 0b1, + } + } + back_to_enum! { + pub enum ClkMuxPllClockIn { + MCLK = 0x0, + BCLK = 0x1, + GPIO = 0x2, + DIN = 0x3, + } + } + back_to_enum! { + pub enum ClkMuxCodecClockIn { + MCLK = 0x0, + BCLK = 0x1, + GPIO = 0x2, + PLL = 0x3, + } + } + + register_rw!(PLLPR, 0, 4); + impl PLLPR { + bit_rw!(enable, 7); + // 0 -> 8, n if not 0 -> n + bitmask_rw!(p, 0b0111_0000, 4); + // 0 -> don't use, n if in range(1..=4) -> n, n if >= 5 -> don't use + bitmask_rw!(r, 0b0000_1111, 0); + } + register_rw!(PLLJ, 0, 6); + impl PLLJ { + // n if in range(0..=3) -> don't use, n if in range(4..=63) -> n + bitmask_rw!(j, 0b0011_1111, 0); + } + register_rw!(PLLDMSB, 0, 7); + register_rw!(PLLDLSB, 0, 8); + struct PLLD { + regs: (PLLDMSB, PLLDLSB), + } + impl PLLD { + fn get_reg(&self) -> u16 { + ((self.regs.1.value as u16) << 8) | (self.regs.0.value as u16) + } + fn set_reg(&mut self, value: u16) { + self.regs.0.value = (value & 0xFF) as u8; + self.regs.1.value = ((value >> 8) & 0xFF) as u8; + } + + fn load(&mut self) { + self.regs.1.load(); + self.regs.0.load(); + } + fn store(&mut self) { + self.regs.1.store(); + self.regs.0.store(); + } + } + impl PLLD { + // n if in range(0..=9999) -> n + fn get_d(&self) -> u16 { + self.get_reg() & 0x7FFF + } + // n if in range(0..=9999) -> n + fn set_d(&mut self, value: u16) { + self.set_reg(value & 0x7FFF); + } + } + register_rw!(NDAC, 0, 11); + impl NDAC { + bit_rw!(enable, 7); + // 0 -> 128, n if in range(1..=127) -> n + bitmask_rw!(ndac, 0b0111_1111, 0); + } + register_rw!(MDAC, 0, 12); + impl MDAC { + bit_rw!(enable, 7); + // 0 -> 128, n if in range(1..=127) -> n + bitmask_rw!(mdac, 0b0111_1111, 0); + } + register_rw!(DOSRMSB, 0, 13); + register_rw!(DOSRLSB, 0, 14); + struct DOSR { + regs: (DOSRMSB, DOSRLSB), + } + impl DOSR { + fn get_reg(&self) -> u16 { + ((self.regs.1.value as u16) << 8) | (self.regs.0.value as u16) + } + fn set_reg(&mut self, value: u16) { + self.regs.0.value = (value & 0xFF) as u8; + self.regs.1.value = ((value >> 8) & 0xFF) as u8; + } + + fn load(&mut self) { + self.regs.1.load(); + self.regs.0.load(); + } + fn store(&mut self) { + self.regs.1.store(); + self.regs.0.store(); + } + } + impl DOSR { + // 0 -> 1024, n if in range(1..=1023) -> n + fn get_osr(&self) -> u16 { + self.get_reg() & 0x7FFF + } + // 0 -> 1024, n if in range(1..=1023) -> n + fn set_osr(&mut self, value: u16) { + self.set_reg(value & 0x3FFF); + } + } + register_rw!(DSPDIDEC1, 0, 15); + register_rw!(DSPDIDEC2, 0, 16); + register_rw!(DSPDINTERP, 0, 17); + register_rw!(NADC, 0, 18); + impl NADC { + bit_rw!(enable, 7); + // 0 -> 128, n if in range(1..=127) -> n + bitmask_rw!(nadc, 0b0111_1111, 0); + } + register_rw!(MADC, 0, 19); + impl MADC { + bit_rw!(enable, 7); + // 0 -> 128, n if in range(1..=127) -> n + bitmask_rw!(nadc, 0b0111_1111, 0); + } + // 0 -> 256, n if in range(1..=255) -> n + register_rw!(AOSR, 0, 20); + register_rw!(DSPAIDEC1, 0, 21); + register_rw!(DSPAIDEC2, 0, 22); + register_rw!(DSPADECIMATION, 0, 23); + register_rw!(CLKMUX2, 0, 25); + impl CLKMUX2 { + bitmask_enum_rw!(clkin, 0b0000_0111, 0, ClkMux2Clock); + } + back_to_enum!{ + pub enum ClkMux2Clock { + MClk = 0b000, + BClk = 0b001, + DIn = 0b010, + PLLClk = 0b011, + DACClk = 0b100, + DACModClk = 0b101, + ADCClk = 0b110, + ADCModClk = 0b111, + } + } + register_rw!(CLKOUTM, 0, 26); + impl CLKOUTM { + bit_rw!(enable, 7); + // 0 -> 128, n if in range(1..=127) -> n + bitmask_rw!(m, 0b0111_1111, 0); + } + register_rw!(IFACE1, 0, 27); + impl IFACE1 { + bitmask_enum_rw!(datatype, 0b1100_0000, 6, IfaceDatetype); + bitmask_enum_rw!(datalen, 0b0011_0000, 4, IfaceWordlen); + bitmask_enum_rw!(wclk_dir, 0b1<<3, 3, IfaceClkDir); + bitmask_enum_rw!(bclk_dir, 0b1<<2, 2, IfaceClkDir); + bitmask_enum_rw!(dout_inpedance, 0b1, 0, IfaceInpedance); + } + back_to_enum! { + pub enum IfaceDatetype { + I2s = 0x0, + DSP = 0x1, + RightJustified = 0x2, + LeftJustified = 0x3, + } + } + back_to_enum! { + pub enum IfaceWordlen { + B16 = 0x0, + B20 = 0x1, + B24 = 0x2, + B32 = 0x3, + } + } + back_to_enum! { + pub enum IfaceClkDir { + Input = 0x0, + Output = 0x1, + } + } + back_to_enum! { + pub enum IfaceInpedance { + High = 0x0, + Low = 0x1, + } + } + register_rw!(IFACE2, 0, 28); + impl IFACE2 { + // n if in range(0..=255) -> n + bitmask_rw!(data_offset, 0b0111_1111, 0); + } + register_rw!(IFACE3, 0, 29); + impl IFACE3 { + bit_rw!(data_loopback_enable, 5); + bit_rw!(analog_loopback_enable, 4); + bit_rw!(bclk_invert, 3); + bit_rw!(bclk_wclk_pwr_ctrl, 2); + bitmask_enum_rw!(bdiv_in_mux, 0b0000_0011, 0, IfaceBDivInMux); + } + back_to_enum! { + pub enum IfaceBDivInMux { + DacClk = 0x0, + DacModClk = 0x1, + AdcClk = 0x2, + AdcModClk = 0x3, + } + } + register_rw!(BCLKN, 0, 30); + impl BCLKN { + bit_rw!(enable, 7); + bitmask_rw!(n, 0b0111_1111, 0); + } + register_rw!(IFACE4, 0, 31); + impl IFACE4 { + bitmask_enum_rw!(secd_bclk_mux, 0b0110_0000, 5, IfaceSecdClkMux); + bitmask_enum_rw!(secd_wclk_mux, 0b0001_1000, 5, IfaceSecdClkMux); + bitmask_enum_rw!(adc_wclk_mux, 0b0000_0110, 5, IfaceAdcWClkInMux); + bitmask_enum_rw!(data_in_mux, 0b0000_0001, 5, IfaceSecdDInMux); + } + back_to_enum! { + pub enum IfaceSecdClkMux { + GPIO = 0x0, + SClk = 0x1, + MISO = 0x2, + DOut = 0x3, + } + } + back_to_enum! { + pub enum IfaceAdcWClkInMux { + GPIO = 0x0, + SClk = 0x1, + MISO = 0x2, + } + } + back_to_enum! { + pub enum IfaceSecdDInMux { + GPIO = 0x0, + SClk = 0x1, + MISO = 0x2, + } + } + register_rw!(IFACE5, 0, 32); + impl IFACE5 { + bitmask_enum_rw!(audioif_bclk, 0b0000_1000, 3, IfaceAudioIfClk); + bitmask_enum_rw!(audioif_wclk, 0b0000_0100, 2, IfaceAudioIfClk); + bitmask_enum_rw!(adc_wclk, 0b0000_0010, 1, IfaceAudioIfAdcWClk); + bitmask_enum_rw!(din, 0b0000_0001, 0, IfaceAudioIfClk); + } + back_to_enum! { + pub enum IfaceAudioIfClk { + Primary = 0x0, + Secondary = 0x1, + } + } + back_to_enum! { + pub enum IfaceAudioIfAdcWClk { + DacWClk = 0x0, + Secondary = 0x1, + } + } + register_rw!(IFACE6, 0, 33); + impl IFACE6 { + bit_rw!(bclk_output_ctrl, 7); + bit_rw!(secd_bclk_output_ctrl, 6); + bitmask_rw!(wclk_output_ctrl, 0b0011_0000, 4); + bitmask_rw!(secd_wclk_output_ctrl, 0b0000_1100, 2); + bit_rw!(prim_data_output_ctrl, 1); + bit_rw!(secd_data_output_ctrl, 0); + } + register_rw!(GPIOCTL, 0, 52); + register_rw!(DOUTCTL, 0, 53); + register_rw!(DINCTL, 0, 54); + register_rw!(MISOCTL, 0, 55); + register_rw!(SCLKCTL, 0, 56); + register_rw!(DACSPB, 0, 60); + register_rw!(ADCSPB, 0, 61); + register_rw!(DACSETUP, 0, 63); + impl DACSETUP { + bitmask_rw!(dac_channel, 0b0011_1100, 2); + bit_rw!(ldac2r_enable, 5); + bit_rw!(ldac2l_enable, 4); + bit_rw!(rdac2l_enable, 3); + bit_rw!(rdac2r_enable, 2); + } + register_rw!(DACMUTE, 0, 64); + impl DACMUTE { + bitmask_rw!(mute, 0b0000_1100, 2); + } + register_rw!(LDACVOL, 0, 65); + register_rw!(RDACVOL, 0, 66); + register_rw!(ADCSETUP, 0, 81); + impl ADCSETUP { + bitmask_rw!(dac_channel, 0b0110_0000, 6); + bit_rw!(ladc_enable, 7); + bit_rw!(radc_enable, 6); + } + register_rw!(ADCFGA, 0, 82); + register_rw!(LADCVOL, 0, 83); + register_rw!(RADCVOL, 0, 84); + register_rw!(LAGC1, 0, 86); + register_rw!(LAGC2, 0, 87); + register_rw!(LAGC3, 0, 88); + register_rw!(LAGC4, 0, 89); + register_rw!(LAGC5, 0, 90); + register_rw!(LAGC6, 0, 91); + register_rw!(LAGC7, 0, 92); + register_rw!(RAGC1, 0, 94); + register_rw!(RAGC2, 0, 95); + register_rw!(RAGC3, 0, 96); + register_rw!(RAGC4, 0, 97); + register_rw!(RAGC5, 0, 98); + register_rw!(RAGC6, 0, 99); + register_rw!(RAGC7, 0, 100); + register_rw!(PWRCFG, 1, 1); + impl PWRCFG { + bit_rw!(avdd_weak_disable, 3); + } + register_rw!(LDOCTL, 1, 2); + impl LDOCTL { + bit_rw!(enable, 0); + } + register_rw!(LPLAYBACK, 1, 3); + register_rw!(RPLAYBACK, 1, 4); + register_rw!(OUTPWRCTL, 1, 9); + register_rw!(CMMODE, 1, 10); + impl CMMODE { + bit_rw!(ldoin_to_hpvdd, 1); + bit_rw!(ldoin_18_36, 0); + } + register_rw!(HPLROUTE, 1, 12); + register_rw!(HPRROUTE, 1, 13); + register_rw!(LOLROUTE, 1, 14); + register_rw!(LORROUTE, 1, 15); + register_rw!(HPLGAIN, 1, 16); + register_rw!(HPRGAIN, 1, 17); + register_rw!(LOLGAIN, 1, 18); + register_rw!(LORGAIN, 1, 19); + register_rw!(HEADSTART, 1, 20); + register_rw!(SPK, 1, 45); + register_rw!(SPKVOL1, 1, 46); + register_rw!(SPKVOL2, 1, 48); + register_rw!(MICBIAS, 1, 51); + impl MICBIAS { + bitmask_rw!(bias, 0b0111_1000, 3); + } + + pub enum Micbias { + LDOIN = 0b001, + V2075 = 0b110 + } + + register_rw!(LMICPGAPIN, 1, 52); + register_rw!(LMICPGANIN, 1, 54); + impl LMICPGANIN { + bit_rw!(in2r_10k, 4); + bit_rw!(cm1l_10k, 6); + } + register_rw!(RMICPGAPIN, 1, 55); + register_rw!(RMICPGANIN, 1, 57); + impl RMICPGANIN { + bit_rw!(in1l_10k, 4); + bit_rw!(cm1r_10k, 6); + } + register_rw!(FLOATINGINP, 1, 58); + register_rw!(LMICPGAVOL, 1, 59); + register_rw!(RMICPGAVOL, 1, 60); + // register_rw!(REFPOWERUP, 1, 122); // TAS2505 + register_rw!(REFPOWERUP, 1, 123); + impl REFPOWERUP { + bitmask_rw!(speed, 0b1111_0000, 4); + } + + /* AIC32X4_REFPOWERUP */ + pub enum RefPowerup { + RefSlow = 0x04, + Ref40ms = 0x05, + Ref80ms = 0x06, + Ref120ms = 0x07, + } + + /* Bits, masks, and shifts */ + + /* Common mask and enable for all of the dividers */ + static mut AIC32X4_DIVEN: u8 = 0x01 << 7; // BIT(7) + static mut AIC32X4_DIV_MASK: u8 = 0b0111_1111; // GENMASK(6, 0) + static mut AIC32X4_DIV_MAX: u8 = 128; + + /* Clock Limits */ + static mut AIC32X4_MAX_DOSR_FREQ: u32 = 6200000; + static mut AIC32X4_MIN_DOSR_FREQ: u32 = 2800000; + static mut AIC32X4_MAX_CODEC_CLKIN_FREQ: u32 = 110000000; + static mut AIC32X4_MAX_PLL_CLKIN: u32 = 20000000; + +} \ No newline at end of file