use crate::iomuxc::{consts, sai};
use crate::ral;
use core::marker::PhantomData;
#[derive(Clone, Copy, Default, Eq, PartialEq)]
pub enum ByteOrder {
#[default]
LSB = 0,
MSB = 1,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
pub enum Mode {
#[default]
Master = 0,
Slave = 1,
BclkMasterFrameSyncSlave = 2,
BclkSlaveFrameSyncMaster = 3,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
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)]
pub enum MclkSource {
#[default]
Sysclk = 0,
Select1 = 1,
Select2 = 2,
Select3 = 3,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
pub enum SyncMode {
#[default]
Async = 0,
TxFollowRx = 1,
RxFollowTx = 2,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
pub enum SyncWidth {
#[default]
WordSize,
}
#[derive(Clone, Copy, Default, Eq, PartialEq)]
pub enum BclkSource {
#[default]
Bus = 0,
Opt1 = 1,
Opt2 = 2,
Opt3 = 3,
}
bitflags::bitflags! {
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! {
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(),
);
}
mod private {
pub trait Sealed {}
}
pub trait Packing<const WORD_SIZE: u8>: private::Sealed {
const FPACK: u32;
}
pub struct PackingNone;
pub struct Packing8bit;
pub struct Packing16bit;
impl<const WORD_SIZE: u8> Packing<WORD_SIZE> for PackingNone {
const FPACK: u32 = 0b00;
}
impl private::Sealed for PackingNone {}
impl Packing<8> for Packing8bit {
const FPACK: u32 = 0b01;
}
impl private::Sealed for Packing8bit {}
impl Packing<16> for Packing16bit {
const FPACK: u32 = 0b10;
}
impl private::Sealed for Packing16bit {}
#[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)]
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,
}
}
}
pub struct Sai<const N: u8, MclkPin, TxPins, RxPins> {
pub(super) sai: ral::sai::Instance<N>,
_mclk_pin: MclkPin,
tx_pins: Option<TxPins>,
rx_pins: Option<RxPins>,
tx_chan_mask: u32,
rx_chan_mask: u32,
}
impl<const N: u8, Chan, Mclk, TxSync, TxBclk, TxData, RxSync, RxBclk, RxData>
Sai<N, Mclk, Pins<TxSync, TxBclk, TxData>, Pins<RxSync, RxBclk, RxData>>
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>,
{
pub fn new(
sai: ral::sai::Instance<N>,
mut mclk_pin: Mclk,
mut tx_pins: Pins<TxSync, TxBclk, TxData>,
mut rx_pins: Pins<RxSync, RxBclk, RxData>,
) -> Self {
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,
_mclk_pin: mclk_pin,
tx_pins: Some(tx_pins),
rx_pins: Some(rx_pins),
tx_chan_mask: 1 << Chan::to_usize(),
rx_chan_mask: 1 << Chan::to_usize(),
}
}
}
pub struct Tx<
const N: u8,
const WORD_SIZE: u8,
const FRAME_SIZE: usize,
PACKING: Packing<WORD_SIZE>,
> {
sai: ral::sai::Instance<N>,
_packing: PhantomData<PACKING>,
}
impl<const N: u8, const WORD_SIZE: u8, const FRAME_SIZE: usize, PACKING: Packing<WORD_SIZE>>
Tx<N, WORD_SIZE, FRAME_SIZE, PACKING>
{
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, interrutps: Interrupts) {
ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| {
let tcsr = tcsr & !Interrupts::all().bits();
tcsr | interrutps.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)
}
}
impl<const N: u8, const FRAME_SIZE: usize> Tx<N, 32, FRAME_SIZE, PackingNone> {
pub fn write_frame(&mut self, chan: usize, frame: [u32; FRAME_SIZE]) {
for sample in frame {
ral::write_reg!(ral::sai, self.sai, TDR[chan], sample);
}
}
}
impl<const N: u8, const FRAME_SIZE: usize> Tx<N, 16, FRAME_SIZE, PackingNone> {
pub fn write_frame(&mut self, chan: usize, frame: [u16; FRAME_SIZE]) {
for sample in frame {
ral::write_reg!(ral::sai, self.sai, TDR[chan], sample as u32);
}
}
}
impl<const N: u8, const FRAME_SIZE: usize> Tx<N, 8, FRAME_SIZE, PackingNone> {
pub fn write_frame(&mut self, chan: usize, frame: [u8; FRAME_SIZE]) {
for sample in frame {
ral::write_reg!(ral::sai, self.sai, TDR[chan], sample as u32);
}
}
}
pub struct Rx<
const N: u8,
const WORD_SIZE: u8,
const FRAME_SIZE: usize,
PACKING: Packing<WORD_SIZE>,
> {
sai: ral::sai::Instance<N>,
_packing: PhantomData<PACKING>,
}
impl<const N: u8, const WORD_SIZE: u8, const FRAME_SIZE: usize, PACKING: Packing<WORD_SIZE>>
Rx<N, WORD_SIZE, FRAME_SIZE, PACKING>
{
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, interrutps: Interrupts) {
ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| {
let rcsr = rcsr & !Interrupts::all().bits();
rcsr | interrutps.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)
}
}
impl<const N: u8, const FRAME_SIZE: usize> Rx<N, 32, FRAME_SIZE, PackingNone> {
pub fn read_frame(&mut self, chan: usize, frame: &mut [u32; FRAME_SIZE]) {
for sample in frame {
*sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]);
}
}
}
impl<const N: u8, const FRAME_SIZE: usize> Rx<N, 16, FRAME_SIZE, PackingNone> {
pub fn read_frame(&mut self, chan: usize, frame: &mut [u16; FRAME_SIZE]) {
for sample in frame {
*sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]) as u16;
}
}
}
impl<const N: u8, const FRAME_SIZE: usize> Rx<N, 8, FRAME_SIZE, PackingNone> {
pub fn read_frame(&mut self, chan: usize, frame: &mut [u8; FRAME_SIZE]) {
for sample in frame {
*sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]) as u8;
}
}
}
impl<const N: u8, Chan, Mclk, TxSync, TxBclk, TxData> Sai<N, Mclk, Pins<TxSync, TxBclk, TxData>, ()>
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>,
{
pub const N: u8 = N;
pub fn from_tx(
sai: ral::sai::Instance<N>,
mut mclk_pin: Mclk,
mut tx_pins: Pins<TxSync, TxBclk, TxData>,
) -> Self {
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,
_mclk_pin: mclk_pin,
tx_pins: Some(tx_pins),
rx_pins: None,
tx_chan_mask: 1 << Chan::to_usize(),
rx_chan_mask: 0,
}
}
}
impl<const N: u8, Chan, Mclk, RxSync, RxBclk, RxData> Sai<N, Mclk, (), Pins<RxSync, RxBclk, RxData>>
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>,
{
pub fn from_rx(
sai: ral::sai::Instance<N>,
mut mclk_pin: Mclk,
mut rx_pins: Pins<RxSync, RxBclk, RxData>,
) -> Self {
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,
_mclk_pin: mclk_pin,
tx_pins: None,
rx_pins: Some(rx_pins),
tx_chan_mask: 0,
rx_chan_mask: 1 << Chan::to_usize(),
}
}
}
impl<const N: u8> Sai<N, (), (), ()> {
pub fn without_pins(sai: ral::sai::Instance<N>, tx_chan_mask: u32, rx_chan_mask: u32) -> Self {
Sai {
sai,
_mclk_pin: (),
tx_pins: None,
rx_pins: None,
tx_chan_mask,
rx_chan_mask,
}
}
}
impl<const N: u8, Mclk, TxPins, RxPins> Sai<N, Mclk, TxPins, RxPins> {
pub fn split<const WORD_SIZE: u8, const FRAME_SIZE: usize, PACKING: Packing<WORD_SIZE>>(
self,
cfg: &SaiConfig,
) -> (
Option<Tx<N, WORD_SIZE, FRAME_SIZE, PACKING>>,
Option<Rx<N, WORD_SIZE, FRAME_SIZE, PACKING>>,
) {
let tx = self.tx_pins.map(|_| Tx {
sai: unsafe { ral::sai::Instance::<N>::new(&*self.sai) },
_packing: PhantomData::<PACKING>,
});
let rx = self.rx_pins.map(|_| Rx {
sai: unsafe { ral::sai::Instance::<N>::new(&*self.sai) },
_packing: PhantomData::<PACKING>,
});
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: 0_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::FPACK, 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);
}
(tx, rx)
}
}