use crate::iomuxc::{consts, sai};
use crate::ral;
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ByteOrder {
#[default]
LSB = 0,
MSB = 1,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Mode {
#[default]
Master = 0,
Slave = 1,
BclkMasterFrameSyncSlave = 2,
BclkSlaveFrameSyncMaster = 3,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClockPolarity {
#[default]
ActiveHigh = 0,
ActiveLow = 1,
}
#[allow(non_upper_case_globals)]
impl ClockPolarity {
pub const SampleOnRising: ClockPolarity = ClockPolarity::ActiveLow;
pub const SampleOnFalling: ClockPolarity = ClockPolarity::ActiveHigh;
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum MclkSource {
#[default]
Sysclk = 0,
Select1 = 1,
Select2 = 2,
Select3 = 3,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SyncMode {
#[default]
Async = 0,
TxFollowRx = 1,
RxFollowTx = 2,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SyncWidth {
#[default]
WordSize,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BclkSource {
#[default]
Bus = 0,
Opt1 = 1,
Opt2 = 2,
Opt3 = 3,
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Interrupts : u32 {
const WORD_START = 1 << 12;
const SYNC_ERROR = 1 << 11;
const FIFO_ERROR = 1 << 10;
const FIFO_WARNING = 1 << 9;
const FIFO_REQUEST = 1 << 8;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Status : u32 {
const WORD_START = 1 << 20;
const SYNC_ERROR = 1 << 19;
const FIFO_ERROR = 1 << 18;
const FIFO_WARNING = 1 << 17;
const FIFO_REQUEST = 1 << 16;
}
}
impl Status {
const W1C: Self = Self::from_bits_truncate(
Self::WORD_START.bits() | Self::SYNC_ERROR.bits() | Self::FIFO_ERROR.bits(),
);
}
#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u32)]
pub enum Packing {
#[default]
None = 0b00,
Pack8bit = 0b10,
Pack16bit = 0b11,
}
impl Packing {
const fn is_valid_for_word_size(self, word_size: u8) -> bool {
match self {
Self::None => true,
Self::Pack8bit => word_size == 8,
Self::Pack16bit => word_size == 16,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidPackingError {
pub word_size: u8,
pub packing: Packing,
}
#[allow(non_upper_case_globals)]
impl BclkSource {
pub const MclkDiv: BclkSource = BclkSource::Opt1;
}
fn reset_tx(regs: &ral::sai::RegisterBlock) {
ral::write_reg!(ral::sai, regs, TCSR, SR: 1, FR: 1);
ral::modify_reg!(ral::sai, regs, TCSR, SR: 0);
}
fn reset_rx(regs: &ral::sai::RegisterBlock) {
ral::write_reg!(ral::sai, regs, RCSR, SR: 1, FR: 1);
ral::modify_reg!(ral::sai, regs, RCSR, SR: 0);
}
fn reset(regs: &ral::sai::RegisterBlock) {
reset_tx(regs);
reset_rx(regs);
}
pub struct Pins<Sync, Bclk, Data> {
pub sync: Sync,
pub bclk: Bclk,
pub data: Data,
}
#[derive(Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SaiConfig {
pub mclk_source: MclkSource,
pub tx_fifo_wm: u32,
pub tx_stop_en: bool,
pub tx_debug_en: bool,
pub tx_bclk_div: u32,
pub rx_fifo_wm: u32,
pub rx_stop_en: bool,
pub rx_debug_en: bool,
pub rx_bclk_div: u32,
pub byte_order: ByteOrder,
pub mode: Mode,
pub sync_width: SyncWidth,
pub sync_early: bool,
pub sync_polarity: ClockPolarity,
pub sync_mode: SyncMode,
pub bclk_src_swap: bool,
pub bclk_input_delay: bool,
pub bclk_polarity: ClockPolarity,
}
const MIN_BCLK_DIV: u32 = 2;
const MAX_BCLK_DIV: u32 = 512;
pub fn bclk_div(bclk_div: u32) -> u32 {
bclk_div.clamp(MIN_BCLK_DIV, MAX_BCLK_DIV).div_ceil(2) - 1
}
impl SaiConfig {
pub const fn i2s(bclk_div: u32) -> Self {
Self {
mclk_source: MclkSource::Sysclk,
tx_fifo_wm: 16,
tx_stop_en: false,
tx_debug_en: false,
tx_bclk_div: bclk_div,
rx_fifo_wm: 16,
rx_stop_en: false,
rx_debug_en: false,
rx_bclk_div: bclk_div,
byte_order: ByteOrder::MSB,
mode: Mode::Master,
sync_early: true,
sync_width: SyncWidth::WordSize,
sync_polarity: ClockPolarity::ActiveLow,
sync_mode: SyncMode::Async,
bclk_src_swap: false,
bclk_input_delay: false,
bclk_polarity: ClockPolarity::SampleOnRising,
}
}
}
type AnyInstance = crate::AnyInstance<ral::sai::RegisterBlock>;
pub struct Sai {
sai: AnyInstance,
tx_chan_mask: u32,
rx_chan_mask: u32,
}
impl Sai {
pub fn new<const N: u8, Chan, Mclk, TxSync, TxBclk, TxData, RxSync, RxBclk, RxData>(
sai: ral::sai::Instance<N>,
mut mclk_pin: Mclk,
mut tx_pins: Pins<TxSync, TxBclk, TxData>,
mut rx_pins: Pins<RxSync, RxBclk, RxData>,
) -> Self
where
Mclk: sai::Pin<consts::Const<N>, Signal = sai::Mclk>,
TxSync: sai::Pin<consts::Const<N>, Signal = sai::TxSync>,
TxBclk: sai::Pin<consts::Const<N>, Signal = sai::TxBclk>,
TxData: sai::Pin<consts::Const<N>>,
RxSync: sai::Pin<consts::Const<N>, Signal = sai::RxSync>,
RxBclk: sai::Pin<consts::Const<N>, Signal = sai::RxBclk>,
RxData: sai::Pin<consts::Const<N>>,
Chan: consts::Unsigned,
<TxData as sai::Pin<consts::Const<N>>>::Signal: sai::TxDataSignal<Index = Chan>,
<RxData as sai::Pin<consts::Const<N>>>::Signal: sai::RxDataSignal<Index = Chan>,
{
reset(&sai);
sai::prepare(&mut mclk_pin);
sai::prepare(&mut tx_pins.sync);
sai::prepare(&mut tx_pins.bclk);
sai::prepare(&mut tx_pins.data);
sai::prepare(&mut rx_pins.sync);
sai::prepare(&mut rx_pins.bclk);
sai::prepare(&mut rx_pins.data);
Self {
sai: crate::into_any(sai),
tx_chan_mask: 1 << Chan::to_usize(),
rx_chan_mask: 1 << Chan::to_usize(),
}
}
}
pub struct Tx {
pub(crate) sai: AnyInstance,
word_size: u8,
frame_size: usize,
channel: usize,
}
impl Tx {
pub fn word_size(&self) -> u8 {
self.word_size
}
pub fn frame_size(&self) -> usize {
self.frame_size
}
pub fn set_enable(&mut self, en: bool) {
let mut tcsr = ral::read_reg!(ral::sai, self.sai, TCSR) & !Status::W1C.bits();
if en {
tcsr |= ral::sai::TCSR::TE::mask
} else {
tcsr &= !ral::sai::TCSR::TE::mask
}
ral::write_reg!(ral::sai, self.sai, TCSR, tcsr);
self.clear_status(Status::W1C);
}
pub fn interrupts(&self) -> Interrupts {
let tcsr = ral::read_reg!(ral::sai, self.sai, TCSR);
Interrupts::from_bits_truncate(tcsr)
}
pub fn set_interrupts(&mut self, interrupts: Interrupts) {
ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| {
let tcsr = tcsr & !Interrupts::all().bits();
tcsr | interrupts.bits()
})
}
pub fn status(&mut self) -> Status {
let tcsr = ral::read_reg!(ral::sai, self.sai, TCSR);
Status::from_bits_truncate(tcsr)
}
pub fn clear_status(&mut self, flags: Status) {
let flags = flags & Status::W1C;
ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| { tcsr | flags.bits() });
}
pub fn reg_dump(&mut self) -> [u32; 6] {
[
ral::read_reg!(ral::sai, self.sai, TCR1),
ral::read_reg!(ral::sai, self.sai, TCR2),
ral::read_reg!(ral::sai, self.sai, TCR3),
ral::read_reg!(ral::sai, self.sai, TCR4),
ral::read_reg!(ral::sai, self.sai, TCR5),
ral::read_reg!(ral::sai, self.sai, TCSR),
]
}
pub fn fifo_position(&mut self, chan: usize) -> (u32, u32) {
ral::read_reg!(ral::sai, self.sai, TFR[chan], WFP, RFP)
}
pub fn channel(&self) -> usize {
self.channel
}
pub fn tdr(&self, chan: usize) -> *const u32 {
core::ptr::addr_of!(self.sai.TDR[chan]).cast()
}
pub fn enable_dma_transmit(&mut self) {
ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| {
(tcsr & !Status::W1C.bits()) | ral::sai::TCSR::FWDE::mask
});
}
pub fn disable_dma_transmit(&mut self) {
ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| {
(tcsr & !Status::W1C.bits()) & !ral::sai::TCSR::FWDE::mask
});
}
pub fn write_frame_u32(&mut self, chan: usize, frame: &[u32]) {
for &sample in frame {
ral::write_reg!(ral::sai, self.sai, TDR[chan], sample);
}
}
pub fn write_frame_u16(&mut self, chan: usize, frame: &[u16]) {
for &sample in frame {
ral::write_reg!(ral::sai, self.sai, TDR[chan], sample as u32);
}
}
pub fn write_frame_u8(&mut self, chan: usize, frame: &[u8]) {
for &sample in frame {
ral::write_reg!(ral::sai, self.sai, TDR[chan], sample as u32);
}
}
}
pub struct Rx {
pub(crate) sai: AnyInstance,
word_size: u8,
frame_size: usize,
channel: usize,
}
impl Rx {
pub fn word_size(&self) -> u8 {
self.word_size
}
pub fn frame_size(&self) -> usize {
self.frame_size
}
pub fn set_enable(&mut self, en: bool) {
let mut rcsr = ral::read_reg!(ral::sai, self.sai, RCSR) & !Status::W1C.bits();
if en {
rcsr |= ral::sai::RCSR::RE::mask
} else {
rcsr &= !ral::sai::RCSR::RE::mask
}
ral::write_reg!(ral::sai, self.sai, RCSR, rcsr);
self.clear_status(Status::W1C);
}
pub fn interrupts(&self) -> Interrupts {
let rcsr = ral::read_reg!(ral::sai, self.sai, RCSR);
Interrupts::from_bits_truncate(rcsr)
}
pub fn set_interrupts(&mut self, interrupts: Interrupts) {
ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| {
let rcsr = rcsr & !Interrupts::all().bits();
rcsr | interrupts.bits()
})
}
pub fn status(&mut self) -> Status {
let rcsr = ral::read_reg!(ral::sai, self.sai, RCSR);
Status::from_bits_truncate(rcsr)
}
pub fn clear_status(&mut self, flags: Status) {
let flags = flags & Status::W1C;
ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| { rcsr | flags.bits() });
}
pub fn reg_dump(&mut self) -> [u32; 6] {
[
ral::read_reg!(ral::sai, self.sai, RCR1),
ral::read_reg!(ral::sai, self.sai, RCR2),
ral::read_reg!(ral::sai, self.sai, RCR3),
ral::read_reg!(ral::sai, self.sai, RCR4),
ral::read_reg!(ral::sai, self.sai, RCR5),
ral::read_reg!(ral::sai, self.sai, RCSR),
]
}
pub fn fifo_position(&mut self, chan: usize) -> (u32, u32) {
ral::read_reg!(ral::sai, self.sai, RFR[chan], WFP, RFP)
}
pub fn channel(&self) -> usize {
self.channel
}
pub fn rdr(&self, chan: usize) -> *const u32 {
core::ptr::addr_of!(self.sai.RDR[chan]).cast()
}
pub fn enable_dma_receive(&mut self) {
ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| {
(rcsr & !Status::W1C.bits()) | ral::sai::RCSR::FRDE::mask
});
}
pub fn disable_dma_receive(&mut self) {
ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| {
(rcsr & !Status::W1C.bits()) & !ral::sai::RCSR::FRDE::mask
});
}
pub fn read_frame_u32(&mut self, chan: usize, frame: &mut [u32]) {
for sample in frame {
*sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]);
}
}
pub fn read_frame_u16(&mut self, chan: usize, frame: &mut [u16]) {
for sample in frame {
*sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]) as u16;
}
}
pub fn read_frame_u8(&mut self, chan: usize, frame: &mut [u8]) {
for sample in frame {
*sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]) as u8;
}
}
}
impl Sai {
pub fn from_tx<const N: u8, Chan, Mclk, TxSync, TxBclk, TxData>(
sai: ral::sai::Instance<N>,
mut mclk_pin: Mclk,
mut tx_pins: Pins<TxSync, TxBclk, TxData>,
) -> Self
where
Mclk: sai::Pin<consts::Const<N>, Signal = sai::Mclk>,
TxSync: sai::Pin<consts::Const<N>, Signal = sai::TxSync>,
TxBclk: sai::Pin<consts::Const<N>, Signal = sai::TxBclk>,
TxData: sai::Pin<consts::Const<N>>,
Chan: consts::Unsigned,
<TxData as sai::Pin<consts::Const<N>>>::Signal: sai::TxDataSignal<Index = Chan>,
{
reset(&sai);
sai::prepare(&mut mclk_pin);
sai::prepare(&mut tx_pins.sync);
sai::prepare(&mut tx_pins.bclk);
sai::prepare(&mut tx_pins.data);
Sai {
sai: crate::into_any(sai),
tx_chan_mask: 1 << Chan::to_usize(),
rx_chan_mask: 0,
}
}
pub fn from_rx<const N: u8, Chan, Mclk, RxSync, RxBclk, RxData>(
sai: ral::sai::Instance<N>,
mut mclk_pin: Mclk,
mut rx_pins: Pins<RxSync, RxBclk, RxData>,
) -> Self
where
Mclk: sai::Pin<consts::Const<N>, Signal = sai::Mclk>,
RxSync: sai::Pin<consts::Const<N>, Signal = sai::RxSync>,
RxBclk: sai::Pin<consts::Const<N>, Signal = sai::RxBclk>,
RxData: sai::Pin<consts::Const<N>>,
Chan: consts::Unsigned,
<RxData as sai::Pin<consts::Const<N>>>::Signal: sai::RxDataSignal<Index = Chan>,
{
reset(&sai);
sai::prepare(&mut mclk_pin);
sai::prepare(&mut rx_pins.sync);
sai::prepare(&mut rx_pins.bclk);
sai::prepare(&mut rx_pins.data);
Sai {
sai: crate::into_any(sai),
tx_chan_mask: 0,
rx_chan_mask: 1 << Chan::to_usize(),
}
}
pub fn without_pins<const N: u8>(
sai: ral::sai::Instance<N>,
tx_chan_mask: u32,
rx_chan_mask: u32,
) -> Self {
Sai {
sai: crate::into_any(sai),
tx_chan_mask,
rx_chan_mask,
}
}
pub fn split(
self,
word_size: u8,
frame_size: usize,
packing: Packing,
cfg: &SaiConfig,
) -> Result<(Option<Tx>, Option<Rx>), InvalidPackingError> {
if !packing.is_valid_for_word_size(word_size) {
return Err(InvalidPackingError { word_size, packing });
}
let tx_channel = self.tx_chan_mask.trailing_zeros() as usize;
let rx_channel = self.rx_chan_mask.trailing_zeros() as usize;
let tx = (self.tx_chan_mask != 0).then(|| Tx {
sai: unsafe { AnyInstance::new(&*self.sai) },
word_size,
frame_size,
channel: tx_channel,
});
let rx = (self.rx_chan_mask != 0).then(|| Rx {
sai: unsafe { AnyInstance::new(&*self.sai) },
word_size,
frame_size,
channel: rx_channel,
});
let frame_sync_dir = match cfg.mode {
Mode::Master => 1,
Mode::BclkSlaveFrameSyncMaster => 1,
_ => 0,
};
let bclk_dir = match cfg.mode {
Mode::Master => 1,
Mode::BclkMasterFrameSyncSlave => 1,
_ => 0,
};
let (tx_sync_mode, rx_sync_mode) = match cfg.sync_mode {
SyncMode::Async => (0b00, 0b00),
SyncMode::TxFollowRx => (0b01, 0b00),
SyncMode::RxFollowTx => (0b00, 0b01),
};
let sync_width = match cfg.sync_width {
SyncWidth::WordSize => word_size as u32,
};
if tx.is_some() {
ral::write_reg!(ral::sai, self.sai, TCR1, TFW: cfg.tx_fifo_wm);
if cfg.mode == Mode::Master || cfg.mode == Mode::BclkMasterFrameSyncSlave {
ral::write_reg!(ral::sai, self.sai, TCR2, SYNC: tx_sync_mode,
BCS: cfg.bclk_src_swap as u32, BCI: cfg.bclk_input_delay as u32,
MSEL: 0x11_u32, BCP: cfg.bclk_polarity as u32, BCD: bclk_dir,
DIV: cfg.tx_bclk_div);
} else {
ral::modify_reg!(ral::sai, self.sai, TCR2, BCP: cfg.bclk_polarity as u32);
}
ral::modify_reg!(ral::sai, self.sai, TCR3, TCE: self.tx_chan_mask, WDFL: 0_u32);
ral::write_reg!(ral::sai, self.sai, TCR4, FRSZ: ((frame_size - 1) as u32),
FPACK: packing as u32, SYWD: (sync_width - 1), MF: cfg.byte_order as u32,
FSE: cfg.sync_early as u32, FSP: cfg.sync_polarity as u32, FSD: frame_sync_dir);
ral::write_reg!(ral::sai, self.sai, TCR5, W0W: ((word_size - 1) as u32), WNW: ((word_size - 1) as u32), FBT: (word_size - 1) as u32);
ral::write_reg!(ral::sai, self.sai, TCSR, TE: 0, STOPE: cfg.tx_stop_en as u32,
DBGE: cfg.tx_debug_en as u32, BCE: 1, WSF: 1, SEF: 1, FEF: 1, FWF: 0, FRF: 0,
WSIE: 0, SEIE: 0, FEIE: 0, FWIE: 0, FWDE: 0, FRDE: 0);
}
if rx.is_some() {
ral::write_reg!(ral::sai, self.sai, RCR1, RFW: cfg.rx_fifo_wm);
if cfg.mode == Mode::Master || cfg.mode == Mode::BclkMasterFrameSyncSlave {
ral::write_reg!(ral::sai, self.sai, RCR2, SYNC: rx_sync_mode,
BCS: cfg.bclk_src_swap as u32, BCI: cfg.bclk_input_delay as u32,
MSEL: cfg.mclk_source as u32, BCP: cfg.bclk_polarity as u32, BCD: bclk_dir,
DIV: cfg.rx_bclk_div);
} else {
ral::modify_reg!(ral::sai, self.sai, RCR2, BCP: cfg.bclk_polarity as u32);
}
ral::modify_reg!(ral::sai, self.sai, RCR3, RCE: self.rx_chan_mask);
ral::write_reg!(ral::sai, self.sai, RCR4, FRSZ: ((frame_size - 1) as u32),
FPACK: packing as u32, SYWD: (sync_width - 1), MF: cfg.byte_order as u32,
FSE: cfg.sync_early as u32, FSP: cfg.sync_polarity as u32, FSD: frame_sync_dir);
ral::write_reg!(ral::sai, self.sai, RCR5, W0W: ((word_size - 1) as u32), WNW: ((word_size - 1) as u32), FBT: (word_size - 1) as u32);
ral::write_reg!(ral::sai, self.sai, RCSR, RE: 0, STOPE: cfg.rx_stop_en as u32,
DBGE: cfg.rx_debug_en as u32, BCE: 1, WSF: 1, SEF: 1, FEF: 1, FWF: 0, FRF: 0,
WSIE: 0, SEIE: 0, FEIE: 0, FWIE: 0, FWDE: 0, FRDE: 0);
}
Ok((tx, rx))
}
}