use crate::peripherals::I2s;
use core::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2sMode {
I2s,
Pcm,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum ClockEdge {
Rising,
Falling,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ChannelCount {
Two = 0,
Four = 1,
Eight = 2,
Sixteen = 3,
}
impl ChannelCount {
pub const fn count(self) -> u32 {
match self {
ChannelCount::Two => 2,
ChannelCount::Four => 4,
ChannelCount::Eight => 8,
ChannelCount::Sixteen => 16,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DataWidth {
Bits16 = 1,
Bits18 = 2,
Bits20 = 3,
Bits24 = 4,
Bits32 = 5,
}
impl DataWidth {
pub const fn bits(self) -> u32 {
match self {
DataWidth::Bits16 => 16,
DataWidth::Bits18 => 18,
DataWidth::Bits20 => 20,
DataWidth::Bits24 => 24,
DataWidth::Bits32 => 32,
}
}
}
const I2S_MCLK: u32 = 12288;
const FREQ_OF_NEED: u32 = 32;
const fn derive_dividers(dw: DataWidth, ch: ChannelCount) -> (u32, u32, u32) {
let prod = dw.bits() * ch.count(); let fs_div_num = prod;
let fs_div_ratio = prod / 2;
let d = FREQ_OF_NEED * prod; let bclk = (2 * I2S_MCLK + d) / (2 * d);
let bclk = if bclk == 0 { 1 } else { bclk };
(bclk, fs_div_num, fs_div_ratio)
}
mod sealed {
pub trait Role {}
}
#[derive(Debug)]
pub struct Master;
#[derive(Debug)]
#[instability::unstable]
pub struct Slave;
impl sealed::Role for Master {}
impl sealed::Role for Slave {}
#[derive(Debug, Clone, Copy)]
pub struct MasterConfig {
pub mode: I2sMode,
pub channels: ChannelCount,
pub data_width: DataWidth,
pub tx_fifo_threshold: u8,
pub rx_fifo_threshold: u8,
pub loopback: bool,
}
impl Default for MasterConfig {
fn default() -> Self {
Self {
mode: I2sMode::I2s,
channels: ChannelCount::Two,
data_width: DataWidth::Bits16,
tx_fifo_threshold: 8,
rx_fifo_threshold: 8,
loopback: false,
}
}
}
#[derive(Debug, Clone, Copy)]
#[instability::unstable]
pub struct SlaveConfig {
pub mode: I2sMode,
pub channels: ChannelCount,
pub data_width: DataWidth,
pub clock_edge: ClockEdge,
pub tx_fifo_threshold: u8,
pub rx_fifo_threshold: u8,
pub loopback: bool,
}
impl Default for SlaveConfig {
fn default() -> Self {
Self {
mode: I2sMode::I2s,
channels: ChannelCount::Two,
data_width: DataWidth::Bits16,
clock_edge: ClockEdge::Rising,
tx_fifo_threshold: 8,
rx_fifo_threshold: 8,
loopback: false,
}
}
}
pub struct I2sDriver<'d, R> {
_i2s: I2s<'d>,
_role: PhantomData<R>,
}
const fn mode_bits(mode: I2sMode, channels: ChannelCount, edge: ClockEdge, master: bool) -> u32 {
let mut m = 0u32;
if matches!(mode, I2sMode::Pcm) {
m |= 1 << 0;
}
m |= ((channels as u32) & 0x3) << 4;
if matches!(edge, ClockEdge::Falling) {
m |= 1 << 6;
}
if master {
m |= 1 << 7;
}
m
}
const fn data_width_bits(dw: DataWidth) -> u32 {
let code = (dw as u32) & 0x7;
code | (code << 3)
}
impl<'d> I2sDriver<'d, Master> {
pub fn new_master(i2s: I2s<'d>, config: &MasterConfig) -> Self {
enable_i2s_clock();
let driver = Self { _i2s: i2s, _role: PhantomData };
let r = driver.regs();
let mode = mode_bits(config.mode, config.channels, ClockEdge::Falling, true);
let (bclk, fs_num, fs_ratio) = derive_dividers(config.data_width, config.channels);
unsafe {
r.mode().write(|w| w.bits(mode));
r.i2s_fs_div_num().write(|w| w.bits(fs_num));
r.i2s_fs_div_ratio_num().write(|w| w.bits(fs_ratio));
r.i2s_bclk_div_num().write(|w| w.bits(bclk));
r.i2s_crg().write(|w| w.bits(0b11));
}
driver.apply_common(config.data_width, config.tx_fifo_threshold, config.rx_fifo_threshold, config.loopback);
driver
}
}
impl<'d> I2sDriver<'d, Slave> {
#[instability::unstable]
pub fn new_slave(i2s: I2s<'d>, config: &SlaveConfig) -> Self {
enable_i2s_clock();
let driver = Self { _i2s: i2s, _role: PhantomData };
let r = driver.regs();
let mode = mode_bits(config.mode, config.channels, config.clock_edge, false);
unsafe {
r.mode().write(|w| w.bits(mode));
r.i2s_crg().write(|w| w.bits(0));
}
driver.apply_common(config.data_width, config.tx_fifo_threshold, config.rx_fifo_threshold, config.loopback);
driver
}
}
impl<'d, R: sealed::Role> I2sDriver<'d, R> {
fn regs(&self) -> &'static crate::soc::pac::i2s::RegisterBlock {
unsafe { &*I2s::ptr() }
}
fn apply_common(&self, data_width: DataWidth, tx_thresh: u8, rx_thresh: u8, loopback: bool) {
let r = self.regs();
unsafe {
r.data_width_set().write(|w| w.bits(data_width_bits(data_width)));
let thresh = (tx_thresh as u32) | ((rx_thresh as u32) << 8);
r.fifo_threshold().write(|w| w.bits(thresh));
r.version().write(|w| w.bits(if loopback { 1 << 8 } else { 0 }));
r.signed_ext().write(|w| w.bits(0x01));
r.ct_set().write(|w| w.bits(0x0C));
}
}
#[instability::unstable]
pub fn enable_tx(&mut self) {
unsafe {
self.regs().ct_set().write(|w| w.bits(0x01)); }
}
#[instability::unstable]
pub fn enable_rx(&mut self) {
unsafe {
self.regs().ct_set().write(|w| w.bits(0x02)); }
}
#[instability::unstable]
pub fn disable_tx(&mut self) {
unsafe {
self.regs().ct_clr().write(|w| w.bits(0x01));
}
}
#[instability::unstable]
pub fn disable_rx(&mut self) {
unsafe {
self.regs().ct_clr().write(|w| w.bits(0x02));
}
}
#[instability::unstable]
pub fn reset_tx(&mut self) {
unsafe {
self.regs().ct_set().write(|w| w.bits(0x04));
}
}
#[instability::unstable]
pub fn reset_rx(&mut self) {
unsafe {
self.regs().ct_set().write(|w| w.bits(0x08));
}
}
#[instability::unstable]
pub fn enable_tx_interrupt(&mut self) {
unsafe {
self.regs().ct_set().write(|w| w.bits(0x10));
}
}
#[instability::unstable]
pub fn enable_rx_interrupt(&mut self) {
unsafe {
self.regs().ct_set().write(|w| w.bits(0x20));
}
}
#[instability::unstable]
pub fn write_left(&mut self, data: u32) {
unsafe {
self.regs().left_tx().write(|w| w.bits(data));
}
}
#[instability::unstable]
pub fn write_right(&mut self, data: u32) {
unsafe {
self.regs().right_tx().write(|w| w.bits(data));
}
}
#[instability::unstable]
pub fn read_left(&self) -> u32 {
self.regs().left_rx().read().bits()
}
#[instability::unstable]
pub fn read_right(&self) -> u32 {
self.regs().right_rx().read().bits()
}
#[instability::unstable]
pub fn tx_fifo_left_depth(&self) -> u8 {
(self.regs().tx_sta().read().bits() & 0xFF) as u8
}
#[instability::unstable]
pub fn tx_fifo_right_depth(&self) -> u8 {
((self.regs().tx_sta().read().bits() >> 8) & 0xFF) as u8
}
#[instability::unstable]
pub fn rx_fifo_left_depth(&self) -> u8 {
(self.regs().rx_sta().read().bits() & 0xFF) as u8
}
#[instability::unstable]
pub fn rx_fifo_right_depth(&self) -> u8 {
((self.regs().rx_sta().read().bits() >> 8) & 0xFF) as u8
}
#[instability::unstable]
pub fn interrupt_status(&self) -> (bool, bool, bool, bool) {
let sts = self.regs().intstatus().read().bits();
((sts & 0x01) != 0, (sts & 0x02) != 0, (sts & 0x04) != 0, (sts & 0x08) != 0)
}
#[instability::unstable]
pub fn clear_interrupts(&mut self) {
unsafe {
self.regs().intclr().write(|w| w.bits(0x0F));
}
}
#[instability::unstable]
pub fn set_interrupt_mask(
&mut self,
rx_int_mask: bool,
tx_int_mask: bool,
rx_overflow_mask: bool,
tx_underflow_mask: bool,
) {
let mut val: u32 = 0;
if rx_int_mask {
val |= 0x01;
}
if tx_int_mask {
val |= 0x02;
}
if rx_overflow_mask {
val |= 0x04;
}
if tx_underflow_mask {
val |= 0x08;
}
unsafe {
self.regs().intmask().write(|w| w.bits(val));
}
}
pub fn version(&self) -> u8 {
(self.regs().version().read().bits() & 0xFF) as u8
}
}
#[cfg(feature = "chip-ws63")]
fn enable_i2s_clock() {
let cmu = unsafe { &*crate::soc::pac::Cmu::ptr() };
let cldo = unsafe { &*crate::peripherals::CldoCrg::ptr() };
cmu.cmu_new_cfg0().modify(|_, w| w.cmu_div_ad_rstn_sync().set_bit());
cldo.cken_ctl0().modify(|_, w| w.i2s_cken().set_bit().i2s_bus_cken().set_bit());
}
#[cfg(not(feature = "chip-ws63"))]
fn enable_i2s_clock() {}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod tests {
use super::*;
#[test]
fn channel_count_field_codes() {
assert_eq!(ChannelCount::Two as u32, 0);
assert_eq!(ChannelCount::Four as u32, 1);
assert_eq!(ChannelCount::Eight as u32, 2);
assert_eq!(ChannelCount::Sixteen as u32, 3);
assert_eq!(ChannelCount::Two.count(), 2);
assert_eq!(ChannelCount::Four.count(), 4);
assert_eq!(ChannelCount::Eight.count(), 8);
assert_eq!(ChannelCount::Sixteen.count(), 16);
}
#[test]
fn data_width_field_codes() {
assert_eq!(DataWidth::Bits16 as u32, 1);
assert_eq!(DataWidth::Bits18 as u32, 2);
assert_eq!(DataWidth::Bits20 as u32, 3);
assert_eq!(DataWidth::Bits24 as u32, 4);
assert_eq!(DataWidth::Bits32 as u32, 5);
assert_eq!(DataWidth::Bits16.bits(), 16);
assert_eq!(DataWidth::Bits32.bits(), 32);
}
#[test]
fn mode_encoding_master_default() {
let m = mode_bits(I2sMode::I2s, ChannelCount::Two, ClockEdge::Falling, true);
assert_eq!(m, (1 << 6) | (1 << 7));
}
#[test]
fn mode_encoding_fields_disjoint() {
assert_eq!(mode_bits(I2sMode::Pcm, ChannelCount::Two, ClockEdge::Rising, false), 1 << 0);
assert_eq!(mode_bits(I2sMode::I2s, ChannelCount::Sixteen, ClockEdge::Rising, false), 0b11 << 4);
assert_eq!(mode_bits(I2sMode::I2s, ChannelCount::Two, ClockEdge::Falling, false), 1 << 6);
assert_eq!(mode_bits(I2sMode::I2s, ChannelCount::Two, ClockEdge::Rising, true), 1 << 7);
assert_eq!(mode_bits(I2sMode::I2s, ChannelCount::Eight, ClockEdge::Rising, false), 2 << 4);
}
#[test]
fn data_width_set_encoding() {
assert_eq!(data_width_bits(DataWidth::Bits16), 0x09);
assert_eq!(data_width_bits(DataWidth::Bits32), 0x2D);
}
#[test]
fn derived_dividers_known_value() {
let (bclk, fs_num, fs_ratio) = derive_dividers(DataWidth::Bits16, ChannelCount::Two);
assert_eq!(fs_num, 32);
assert_eq!(fs_ratio, 16);
assert_eq!(bclk, 12);
}
#[test]
fn derived_dividers_in_range() {
let widths = [DataWidth::Bits16, DataWidth::Bits18, DataWidth::Bits20, DataWidth::Bits24, DataWidth::Bits32];
let chans = [ChannelCount::Two, ChannelCount::Four, ChannelCount::Eight, ChannelCount::Sixteen];
for &dw in &widths {
for &ch in &chans {
let (bclk, fs_num, fs_ratio) = derive_dividers(dw, ch);
assert!(bclk >= 1 && bclk <= 0x7F, "bclk {bclk} out of 7-bit range for {dw:?}/{ch:?}");
assert!(fs_num >= 1 && fs_num <= 0x3FF, "fs_num {fs_num} out of 10-bit range");
assert!(fs_ratio >= 1 && fs_ratio <= 0x7FF, "fs_ratio {fs_ratio} out of 11-bit range");
}
}
}
fn encode_int_mask(rx: bool, tx: bool, rx_ovf: bool, tx_unf: bool) -> u32 {
let mut val: u32 = 0;
if rx {
val |= 0x01;
}
if tx {
val |= 0x02;
}
if rx_ovf {
val |= 0x04;
}
if tx_unf {
val |= 0x08;
}
val
}
fn decode_int_status(sts: u32) -> (bool, bool, bool, bool) {
((sts & 0x01) != 0, (sts & 0x02) != 0, (sts & 0x04) != 0, (sts & 0x08) != 0)
}
#[test]
fn interrupt_mask_encoding() {
assert_eq!(encode_int_mask(false, false, false, false), 0x00);
assert_eq!(encode_int_mask(true, false, false, false), 0x01);
assert_eq!(encode_int_mask(false, true, false, false), 0x02);
assert_eq!(encode_int_mask(false, false, true, false), 0x04);
assert_eq!(encode_int_mask(false, false, false, true), 0x08);
assert_eq!(encode_int_mask(true, true, true, true), 0x0F);
}
#[test]
fn interrupt_status_decode_roundtrips_mask() {
for bits in 0u32..16 {
let (rx, tx, ovf, unf) = decode_int_status(bits);
assert_eq!(encode_int_mask(rx, tx, ovf, unf), bits);
}
}
#[test]
fn fifo_threshold_packing() {
let tx: u8 = 0x12;
let rx: u8 = 0x34;
let thresh = (tx as u32) | ((rx as u32) << 8);
assert_eq!(thresh, 0x3412);
}
#[test]
fn default_master_config_is_16bit_stereo_i2s() {
let c = MasterConfig::default();
assert_eq!(c.mode, I2sMode::I2s);
assert_eq!(c.channels, ChannelCount::Two);
assert_eq!(c.data_width, DataWidth::Bits16);
assert!(!c.loopback);
}
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod proptests {
use proptest::prelude::*;
proptest! {
#[test]
fn fifo_threshold_roundtrip(tx in any::<u8>(), rx in any::<u8>()) {
let thresh = (tx as u32) | ((rx as u32) << 8);
prop_assert_eq!((thresh & 0xFF) as u8, tx);
prop_assert_eq!(((thresh >> 8) & 0xFF) as u8, rx);
prop_assert_eq!(thresh & !0xFFFF, 0);
}
}
}