Skip to main content

imxrt_hal/chip/drivers/
sai.rs

1//! Synchronous Audio Interface.
2//!
3//! [`Sai`] provides a pair of synchronous audio word streams containing stereo data.
4//!
5//! This driver also exposes the peripheral's lower-level, hardware-dependent audio stream
6//! configuration and FIFO pair.
7//!
8//! Each SAI instance has at minimum a tx and rx data line. Each data line supports up to 32 audio
9//! words per frame. Audio words are 8 to 32 bits. Frames can be used to send multichannel audio
10//! data over a single serial stream such as stereo audio.
11//!
12//! Each data line comes with its own 32x32 FIFO allowing for a full frame to be sent and/or received
13//! without software interaction.
14//!
15//! The configuration of the SAI is encoded in configuration structure that can be used with a singular
16//! configure method.
17//!
18//! ## DMA
19//!
20//! DMA transfers target the lowest-numbered enabled data line for each direction
21//! (TX/RX). Multi-channel frames (e.g. stereo) on a single data line work
22//! naturally, since frame words are interleaved through the same TDR/RDR by the
23//! hardware FIFO. However, multiple data lines are not supported by DMA — only
24//! the lowest-numbered enabled data line is used.
25//!
26//! ## Clock configuration
27//!
28//! Make sure to configure your clocks before using the audio interface. Note that there may be
29//! additional clock settings in `IOMUXC_GPR`.
30
31use crate::iomuxc::{consts, sai};
32use crate::ral;
33
34/// Audio word byte order
35#[derive(Clone, Copy, Default, Eq, PartialEq)]
36#[cfg_attr(feature = "defmt", derive(defmt::Format))]
37pub enum ByteOrder {
38    /// Least significant byte first
39    #[default]
40    LSB = 0,
41    /// Most significant byte first
42    MSB = 1,
43}
44
45/// Mode of operation for the SAI peripheral
46#[derive(Clone, Copy, Default, Eq, PartialEq)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48pub enum Mode {
49    /// Master mode where all clocks are generated from the SAI
50    #[default]
51    Master = 0,
52    /// Slave mode where all audio clocks are expecting to come from external sources
53    Slave = 1,
54    /// Bitclock Master, FrameSync Slave
55    BclkMasterFrameSyncSlave = 2,
56    /// Bitclock Slave, FrameSync Master
57    BclkSlaveFrameSyncMaster = 3,
58}
59
60/// Clock Polarity Options for Bclk/Mclk
61#[derive(Clone, Copy, Default, Eq, PartialEq)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63pub enum ClockPolarity {
64    /// Transmitted clock implies active high
65    #[default]
66    ActiveHigh = 0,
67    /// Transmitted clock implies active low
68    ActiveLow = 1,
69}
70
71#[allow(non_upper_case_globals)]
72impl ClockPolarity {
73    /// Received clock implies sample on rising edge
74    pub const SampleOnRising: ClockPolarity = ClockPolarity::ActiveLow;
75    /// Received clock implies sample on falling edge
76    pub const SampleOnFalling: ClockPolarity = ClockPolarity::ActiveHigh;
77}
78
79/// Mclk source option
80#[derive(Clone, Copy, Default, Eq, PartialEq)]
81#[cfg_attr(feature = "defmt", derive(defmt::Format))]
82pub enum MclkSource {
83    /// Mclk sourced from system clock
84    #[default]
85    Sysclk = 0,
86    /// Select 1, part dependent
87    Select1 = 1,
88    /// Select 2, part dependent
89    Select2 = 2,
90    /// Select 3, part dependent
91    Select3 = 3,
92}
93
94/// Frame sync mode between rx/tx
95#[derive(Clone, Copy, Default, Eq, PartialEq)]
96#[cfg_attr(feature = "defmt", derive(defmt::Format))]
97pub enum SyncMode {
98    /// Both tx/rx are setup as being independent of each other for frame sync
99    #[default]
100    Async = 0,
101    /// Tx synchronously follows Rx frame sync
102    TxFollowRx = 1,
103    /// Rx synchronously follows Tx frame sync
104    RxFollowTx = 2,
105}
106
107/// Frame Sync Width
108#[derive(Clone, Copy, Default, Eq, PartialEq)]
109#[cfg_attr(feature = "defmt", derive(defmt::Format))]
110pub enum SyncWidth {
111    /// Frame sync width is the size of the word
112    #[default]
113    WordSize,
114}
115
116/// Source for Bclk, check part datasheet for details
117#[derive(Clone, Copy, Default, Eq, PartialEq)]
118#[cfg_attr(feature = "defmt", derive(defmt::Format))]
119pub enum BclkSource {
120    /// Bus clock is the source of Bclk
121    #[default]
122    Bus = 0,
123    /// Option 1, part dependent
124    Opt1 = 1,
125    /// Option 2, part dependent
126    Opt2 = 2,
127    /// Option 3, part dependent
128    Opt3 = 3,
129}
130
131bitflags::bitflags! {
132    /// Interrupt settings.
133    ///
134    /// A set bit indicates that the interrupt is enabled.
135    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
136    pub struct Interrupts : u32 {
137        /// Word Start Interrupt Enable.
138        const WORD_START = 1 << 12;
139        /// Sync Error Interrupt Enable.
140        const SYNC_ERROR = 1 << 11;
141        /// FIFO Error Interrupt Enable.
142        const FIFO_ERROR = 1 << 10;
143        /// FIFO Warning Interrupt Enable.
144        const FIFO_WARNING = 1 << 9;
145        /// FIFO Request Interrupt Enable.
146        const FIFO_REQUEST = 1 << 8;
147    }
148}
149
150bitflags::bitflags! {
151    /// Status flags.
152    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
153    pub struct Status : u32 {
154        /// Word Start Flag.
155        ///
156        /// Indicates that the start of the configured word has been detected.
157        const WORD_START = 1 << 20;
158        /// Sync Error Flag.
159        ///
160        /// Indicates that an error in the externally-generated frame sync has been detected.
161        const SYNC_ERROR = 1 << 19;
162        /// FIFO Error Flag.
163        ///
164        /// Indicates that an enabled
165        /// * receive FIFO has overflowed
166        /// * transmit FIFO has underrun
167        const FIFO_ERROR = 1 << 18;
168        /// FIFO Warning Flag.
169        ///
170        /// Indicates that an enabled
171        /// * receive FIFO is full
172        /// * transmit FIFO is empty
173        const FIFO_WARNING = 1 << 17;
174        /// FIFO Request Flag.
175        ///
176        /// Indicates that the number of words in an enabled
177        /// * receive channel FIFO is greater than the receive FIFO watermark
178        /// * transmit channel FIFO is less than or equal to the transmit FIFO watermark
179        const FIFO_REQUEST = 1 << 16;
180    }
181}
182
183impl Status {
184    const W1C: Self = Self::from_bits_truncate(
185        Self::WORD_START.bits() | Self::SYNC_ERROR.bits() | Self::FIFO_ERROR.bits(),
186    );
187}
188
189/// FIFO packing mode for audio words.
190///
191/// Packing allows multiple smaller audio words to be packed into a single
192/// 32-bit FIFO entry.
193#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
194#[cfg_attr(feature = "defmt", derive(defmt::Format))]
195#[repr(u32)]
196pub enum Packing {
197    /// No packing of audio words into a single 32-bit FIFO word.
198    ///
199    /// Each audio word occupies one 32-bit FIFO entry.
200    #[default]
201    None = 0b00,
202    /// 8-bit audio words packed into a single 32-bit FIFO word.
203    ///
204    /// Four 8-bit audio words are packed per FIFO entry. Only valid when
205    /// word size is 8 bits.
206    Pack8bit = 0b10,
207    /// 16-bit audio words packed into a single 32-bit FIFO word.
208    ///
209    /// Two 16-bit audio words are packed per FIFO entry. Only valid when
210    /// word size is 16 bits.
211    Pack16bit = 0b11,
212}
213
214impl Packing {
215    const fn is_valid_for_word_size(self, word_size: u8) -> bool {
216        match self {
217            Self::None => true,
218            Self::Pack8bit => word_size == 8,
219            Self::Pack16bit => word_size == 16,
220        }
221    }
222}
223
224/// Indicates an invalid word size and packing combination.
225///
226/// [`Packing::Pack8bit`] requires a word size of 8, and [`Packing::Pack16bit`]
227/// requires a word size of 16. [`Packing::None`] is valid for any word size.
228#[derive(Debug, Clone, Copy, Eq, PartialEq)]
229#[cfg_attr(feature = "defmt", derive(defmt::Format))]
230pub struct InvalidPackingError {
231    /// The word size that was requested.
232    pub word_size: u8,
233    /// The packing mode that was requested.
234    pub packing: Packing,
235}
236
237#[allow(non_upper_case_globals)]
238impl BclkSource {
239    /// Mclk Divider as Bclk source
240    pub const MclkDiv: BclkSource = BclkSource::Opt1;
241}
242
243fn reset_tx(regs: &ral::sai::RegisterBlock) {
244    ral::write_reg!(ral::sai, regs, TCSR, SR: 1, FR: 1);
245    ral::modify_reg!(ral::sai, regs, TCSR, SR: 0);
246}
247
248fn reset_rx(regs: &ral::sai::RegisterBlock) {
249    ral::write_reg!(ral::sai, regs, RCSR, SR: 1, FR: 1);
250    ral::modify_reg!(ral::sai, regs, RCSR, SR: 0);
251}
252
253fn reset(regs: &ral::sai::RegisterBlock) {
254    reset_tx(regs);
255    reset_rx(regs);
256}
257
258/// A set of Pins for Tx or Rx
259///
260/// NOTE: The Data type *could* be more than a single Pin.
261pub struct Pins<Sync, Bclk, Data> {
262    /// Frame sync pin
263    pub sync: Sync,
264    /// Bit clock pin
265    pub bclk: Bclk,
266    /// Data pin(s)
267    pub data: Data,
268}
269
270/// Configuration for SAI peripheral
271#[derive(Default)]
272#[cfg_attr(feature = "defmt", derive(defmt::Format))]
273pub struct SaiConfig {
274    /// MCLK source
275    pub mclk_source: MclkSource,
276    /// TX fifo watermark
277    pub tx_fifo_wm: u32,
278    /// TX stop enable
279    pub tx_stop_en: bool,
280    /// TX debug enable
281    pub tx_debug_en: bool,
282    /// TX BCLK divider
283    pub tx_bclk_div: u32,
284    /// RX FIFO watermark
285    pub rx_fifo_wm: u32,
286    /// RX stop enable
287    pub rx_stop_en: bool,
288    /// RX debug enable
289    pub rx_debug_en: bool,
290    /// RX BCLK divider
291    pub rx_bclk_div: u32,
292    /// Byte order
293    pub byte_order: ByteOrder,
294    /// Mode
295    pub mode: Mode,
296    /// Sync width
297    pub sync_width: SyncWidth,
298    /// Sync early
299    pub sync_early: bool,
300    /// Sync polarity
301    pub sync_polarity: ClockPolarity,
302    /// Sync mode
303    pub sync_mode: SyncMode,
304    /// BCLK source swap
305    pub bclk_src_swap: bool,
306    /// BCLK input delay
307    pub bclk_input_delay: bool,
308    /// BCLK polarity
309    pub bclk_polarity: ClockPolarity,
310}
311
312const MIN_BCLK_DIV: u32 = 2;
313const MAX_BCLK_DIV: u32 = 512;
314
315/// Compute a bclk divider setting given a desired numerical divider
316pub fn bclk_div(bclk_div: u32) -> u32 {
317    bclk_div.clamp(MIN_BCLK_DIV, MAX_BCLK_DIV).div_ceil(2) - 1
318}
319
320impl SaiConfig {
321    /// Initialize a constant SaiConfig to be used with an i2s signaling scheme
322    pub const fn i2s(bclk_div: u32) -> Self {
323        // Get the bitclock divider for a given integer division. Notably non-even numbers
324        // are rounded up as the bclk divider is *always* an even number from 2 to 512
325        Self {
326            mclk_source: MclkSource::Sysclk,
327            tx_fifo_wm: 16,
328            tx_stop_en: false,
329            tx_debug_en: false,
330            tx_bclk_div: bclk_div,
331            rx_fifo_wm: 16,
332            rx_stop_en: false,
333            rx_debug_en: false,
334            rx_bclk_div: bclk_div,
335            byte_order: ByteOrder::MSB,
336            mode: Mode::Master,
337            sync_early: true,
338            sync_width: SyncWidth::WordSize,
339            sync_polarity: ClockPolarity::ActiveLow,
340            sync_mode: SyncMode::Async,
341            bclk_src_swap: false,
342            bclk_input_delay: false,
343            bclk_polarity: ClockPolarity::SampleOnRising,
344        }
345    }
346}
347
348type AnyInstance = crate::AnyInstance<ral::sai::RegisterBlock>;
349
350/// A SAI peripheral instance.
351pub struct Sai {
352    sai: AnyInstance,
353    tx_chan_mask: u32,
354    rx_chan_mask: u32,
355}
356
357impl Sai {
358    /// Creates SAI instance with single channel RX and TX.
359    pub fn new<const N: u8, Chan, Mclk, TxSync, TxBclk, TxData, RxSync, RxBclk, RxData>(
360        sai: ral::sai::Instance<N>,
361        mut mclk_pin: Mclk,
362        mut tx_pins: Pins<TxSync, TxBclk, TxData>,
363        mut rx_pins: Pins<RxSync, RxBclk, RxData>,
364    ) -> Self
365    where
366        Mclk: sai::Pin<consts::Const<N>, Signal = sai::Mclk>,
367        TxSync: sai::Pin<consts::Const<N>, Signal = sai::TxSync>,
368        TxBclk: sai::Pin<consts::Const<N>, Signal = sai::TxBclk>,
369        TxData: sai::Pin<consts::Const<N>>,
370        RxSync: sai::Pin<consts::Const<N>, Signal = sai::RxSync>,
371        RxBclk: sai::Pin<consts::Const<N>, Signal = sai::RxBclk>,
372        RxData: sai::Pin<consts::Const<N>>,
373        Chan: consts::Unsigned,
374        <TxData as sai::Pin<consts::Const<N>>>::Signal: sai::TxDataSignal<Index = Chan>,
375        <RxData as sai::Pin<consts::Const<N>>>::Signal: sai::RxDataSignal<Index = Chan>,
376    {
377        reset(&sai);
378
379        sai::prepare(&mut mclk_pin);
380        sai::prepare(&mut tx_pins.sync);
381        sai::prepare(&mut tx_pins.bclk);
382        sai::prepare(&mut tx_pins.data);
383        sai::prepare(&mut rx_pins.sync);
384        sai::prepare(&mut rx_pins.bclk);
385        sai::prepare(&mut rx_pins.data);
386
387        Self {
388            sai: crate::into_any(sai),
389            tx_chan_mask: 1 << Chan::to_usize(),
390            rx_chan_mask: 1 << Chan::to_usize(),
391        }
392    }
393}
394
395/// A SAI transmit half.
396pub struct Tx {
397    pub(crate) sai: AnyInstance,
398    word_size: u8,
399    frame_size: usize,
400    channel: usize,
401}
402
403impl Tx {
404    /// Returns the word size in bits.
405    pub fn word_size(&self) -> u8 {
406        self.word_size
407    }
408
409    /// Returns the frame size (number of words per frame).
410    pub fn frame_size(&self) -> usize {
411        self.frame_size
412    }
413
414    /// Enable/Disable transmission
415    pub fn set_enable(&mut self, en: bool) {
416        let mut tcsr = ral::read_reg!(ral::sai, self.sai, TCSR) & !Status::W1C.bits();
417        if en {
418            tcsr |= ral::sai::TCSR::TE::mask
419        } else {
420            tcsr &= !ral::sai::TCSR::TE::mask
421        }
422        ral::write_reg!(ral::sai, self.sai, TCSR, tcsr);
423        self.clear_status(Status::W1C);
424    }
425
426    /// Return the interrupt flags.
427    ///
428    /// The interrupt flags indicate the reasons that this peripheral may generate an interrupt.
429    pub fn interrupts(&self) -> Interrupts {
430        let tcsr = ral::read_reg!(ral::sai, self.sai, TCSR);
431        Interrupts::from_bits_truncate(tcsr)
432    }
433
434    /// Set the interrupt flags for this SAI transmitter.
435    pub fn set_interrupts(&mut self, interrupts: Interrupts) {
436        ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| {
437            let tcsr = tcsr & !Interrupts::all().bits();
438            tcsr | interrupts.bits()
439        })
440    }
441
442    /// Get the status register of the transmitter, this can be used in conjunction with
443    /// status field masks to determine the state of the SAI peripheral.
444    pub fn status(&mut self) -> Status {
445        let tcsr = ral::read_reg!(ral::sai, self.sai, TCSR);
446        Status::from_bits_truncate(tcsr)
447    }
448
449    /// Clear status error flags
450    pub fn clear_status(&mut self, flags: Status) {
451        let flags = flags & Status::W1C;
452        ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| { tcsr | flags.bits() });
453    }
454
455    /// Get a dump of the Tx configuration registers
456    pub fn reg_dump(&mut self) -> [u32; 6] {
457        [
458            ral::read_reg!(ral::sai, self.sai, TCR1),
459            ral::read_reg!(ral::sai, self.sai, TCR2),
460            ral::read_reg!(ral::sai, self.sai, TCR3),
461            ral::read_reg!(ral::sai, self.sai, TCR4),
462            ral::read_reg!(ral::sai, self.sai, TCR5),
463            ral::read_reg!(ral::sai, self.sai, TCSR),
464        ]
465    }
466
467    /// Get the FIFO write and read position
468    ///
469    /// ```no_run
470    /// use imxrt_ral::sai::SAI1;
471    /// use imxrt_hal::sai::{Packing, Sai, SaiConfig};
472    /// let sai = Sai::without_pins(unsafe { SAI1::instance() }, 0, 0);
473    /// let (Some(mut sai_tx), None) = sai.split(16, 2, Packing::None, &SaiConfig::i2s(8)).unwrap() else { panic!() };
474    ///
475    /// let (write_pos, read_pos) = sai_tx.fifo_position(0);
476    /// ```
477    pub fn fifo_position(&mut self, chan: usize) -> (u32, u32) {
478        ral::read_reg!(ral::sai, self.sai, TFR[chan], WFP, RFP)
479    }
480
481    /// Returns the data channel index used by this transmitter.
482    pub fn channel(&self) -> usize {
483        self.channel
484    }
485
486    /// Produces a pointer to the transmit data register for the given channel.
487    ///
488    /// Use this pointer when coordinating a DMA transfer.
489    pub fn tdr(&self, chan: usize) -> *const u32 {
490        core::ptr::addr_of!(self.sai.TDR[chan]).cast()
491    }
492
493    /// Enable DMA request on FIFO warning (FWDE bit in TCSR).
494    ///
495    /// When enabled, the transmit FIFO generates a DMA request whenever
496    /// the number of words in the FIFO falls to or below the watermark.
497    pub fn enable_dma_transmit(&mut self) {
498        ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| {
499            (tcsr & !Status::W1C.bits()) | ral::sai::TCSR::FWDE::mask
500        });
501    }
502
503    /// Disable DMA request on FIFO warning (clear FWDE bit in TCSR).
504    pub fn disable_dma_transmit(&mut self) {
505        ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| {
506            (tcsr & !Status::W1C.bits()) & !ral::sai::TCSR::FWDE::mask
507        });
508    }
509
510    /// Write a single audio frame of 32-bit samples to a channel's FIFO.
511    ///
512    /// This writes samples without checks or blocking.
513    ///
514    /// Your slice is expected to be sized based on the configured frame size.
515    /// For example, if your frame size is 2, then your slice should be two
516    /// elements large.
517    pub fn write_frame_u32(&mut self, chan: usize, frame: &[u32]) {
518        for &sample in frame {
519            ral::write_reg!(ral::sai, self.sai, TDR[chan], sample);
520        }
521    }
522
523    /// Write a single audio frame of 16-bit samples to a channel's FIFO.
524    ///
525    /// This writes samples without checks or blocking.
526    ///
527    /// Your slice is expected to be sized based on the configured frame size.
528    /// For example, if your frame size is 2, then your slice should be two
529    /// elements large.
530    pub fn write_frame_u16(&mut self, chan: usize, frame: &[u16]) {
531        for &sample in frame {
532            ral::write_reg!(ral::sai, self.sai, TDR[chan], sample as u32);
533        }
534    }
535
536    /// Write a single audio frame of 8-bit samples to a channel's FIFO.
537    ///
538    /// This writes samples without checks or blocking.
539    ///
540    /// Your slice is expected to be sized based on the configured frame size.
541    /// For example, if your frame size is 2, then your slice should be two
542    /// elements large.
543    pub fn write_frame_u8(&mut self, chan: usize, frame: &[u8]) {
544        for &sample in frame {
545            ral::write_reg!(ral::sai, self.sai, TDR[chan], sample as u32);
546        }
547    }
548}
549
550/// A SAI receive half.
551pub struct Rx {
552    pub(crate) sai: AnyInstance,
553    word_size: u8,
554    frame_size: usize,
555    channel: usize,
556}
557
558impl Rx {
559    /// Returns the word size in bits.
560    pub fn word_size(&self) -> u8 {
561        self.word_size
562    }
563
564    /// Returns the frame size (number of words per frame).
565    pub fn frame_size(&self) -> usize {
566        self.frame_size
567    }
568
569    /// Enable/Disable reception.
570    pub fn set_enable(&mut self, en: bool) {
571        let mut rcsr = ral::read_reg!(ral::sai, self.sai, RCSR) & !Status::W1C.bits();
572        if en {
573            rcsr |= ral::sai::RCSR::RE::mask
574        } else {
575            rcsr &= !ral::sai::RCSR::RE::mask
576        }
577        ral::write_reg!(ral::sai, self.sai, RCSR, rcsr);
578        self.clear_status(Status::W1C);
579    }
580
581    /// Return the interrupt flags.
582    ///
583    /// The interrupt flags indicate the reasons that this peripheral may generate an interrupt.
584    pub fn interrupts(&self) -> Interrupts {
585        let rcsr = ral::read_reg!(ral::sai, self.sai, RCSR);
586        Interrupts::from_bits_truncate(rcsr)
587    }
588
589    /// Set the interrupt flags for this SAI receiver.
590    pub fn set_interrupts(&mut self, interrupts: Interrupts) {
591        ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| {
592            let rcsr = rcsr & !Interrupts::all().bits();
593            rcsr | interrupts.bits()
594        })
595    }
596
597    /// Get the status register of the receiver, this can be used in conjunction with
598    /// status field masks to determine the state of the SAI peripheral.
599    pub fn status(&mut self) -> Status {
600        let rcsr = ral::read_reg!(ral::sai, self.sai, RCSR);
601        Status::from_bits_truncate(rcsr)
602    }
603
604    /// Clear status error flags
605    pub fn clear_status(&mut self, flags: Status) {
606        let flags = flags & Status::W1C;
607        ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| { rcsr | flags.bits() });
608    }
609
610    /// Get a dump of the Rx configuration registers
611    pub fn reg_dump(&mut self) -> [u32; 6] {
612        [
613            ral::read_reg!(ral::sai, self.sai, RCR1),
614            ral::read_reg!(ral::sai, self.sai, RCR2),
615            ral::read_reg!(ral::sai, self.sai, RCR3),
616            ral::read_reg!(ral::sai, self.sai, RCR4),
617            ral::read_reg!(ral::sai, self.sai, RCR5),
618            ral::read_reg!(ral::sai, self.sai, RCSR),
619        ]
620    }
621
622    /// Get the FIFO write and read position
623    ///
624    /// ```no_run
625    /// use imxrt_ral::sai::SAI1;
626    /// use imxrt_hal::sai::{Packing, Sai, SaiConfig};
627    /// let sai = Sai::without_pins(unsafe { SAI1::instance() }, 0, 0);
628    /// let (None, Some(mut sai_rx)) = sai.split(16, 2, Packing::None, &SaiConfig::i2s(8)).unwrap() else { panic!() };
629    ///
630    /// let (write_pos, read_pos) = sai_rx.fifo_position(0);
631    /// ```
632    pub fn fifo_position(&mut self, chan: usize) -> (u32, u32) {
633        ral::read_reg!(ral::sai, self.sai, RFR[chan], WFP, RFP)
634    }
635
636    /// Returns the data channel index used by this receiver.
637    pub fn channel(&self) -> usize {
638        self.channel
639    }
640
641    /// Produces a pointer to the receive data register for the given channel.
642    ///
643    /// Use this pointer when coordinating a DMA transfer.
644    pub fn rdr(&self, chan: usize) -> *const u32 {
645        core::ptr::addr_of!(self.sai.RDR[chan]).cast()
646    }
647
648    /// Enable DMA request on FIFO request (FRDE bit in RCSR).
649    ///
650    /// When enabled, the receive FIFO generates a DMA request whenever
651    /// the number of words in the FIFO reaches the watermark.
652    pub fn enable_dma_receive(&mut self) {
653        ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| {
654            (rcsr & !Status::W1C.bits()) | ral::sai::RCSR::FRDE::mask
655        });
656    }
657
658    /// Disable DMA request on FIFO request (clear FRDE bit in RCSR).
659    pub fn disable_dma_receive(&mut self) {
660        ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| {
661            (rcsr & !Status::W1C.bits()) & !ral::sai::RCSR::FRDE::mask
662        });
663    }
664
665    /// Read a single audio frame of 32-bit samples from a channel's FIFO.
666    ///
667    /// This reads samples without checks or blocking.
668    ///
669    /// Your slice is expected to be sized based on the configured frame size.
670    /// For example, if your frame size is 2, then your slice should be two
671    /// elements large.
672    pub fn read_frame_u32(&mut self, chan: usize, frame: &mut [u32]) {
673        for sample in frame {
674            *sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]);
675        }
676    }
677
678    /// Read a single audio frame of 16-bit samples from a channel's FIFO.
679    ///
680    /// This reads samples without checks or blocking.
681    ///
682    /// Your slice is expected to be sized based on the configured frame size.
683    /// For example, if your frame size is 2, then your slice should be two
684    /// elements large.
685    pub fn read_frame_u16(&mut self, chan: usize, frame: &mut [u16]) {
686        for sample in frame {
687            *sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]) as u16;
688        }
689    }
690
691    /// Read a single audio frame of 8-bit samples from a channel's FIFO.
692    ///
693    /// This reads samples without checks or blocking.
694    ///
695    /// Your slice is expected to be sized based on the configured frame size.
696    /// For example, if your frame size is 2, then your slice should be two
697    /// elements large.
698    pub fn read_frame_u8(&mut self, chan: usize, frame: &mut [u8]) {
699        for sample in frame {
700            *sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]) as u8;
701        }
702    }
703}
704
705impl Sai {
706    /// Create a Sai instance given a set of transmit pins.
707    pub fn from_tx<const N: u8, Chan, Mclk, TxSync, TxBclk, TxData>(
708        sai: ral::sai::Instance<N>,
709        mut mclk_pin: Mclk,
710        mut tx_pins: Pins<TxSync, TxBclk, TxData>,
711    ) -> Self
712    where
713        Mclk: sai::Pin<consts::Const<N>, Signal = sai::Mclk>,
714        TxSync: sai::Pin<consts::Const<N>, Signal = sai::TxSync>,
715        TxBclk: sai::Pin<consts::Const<N>, Signal = sai::TxBclk>,
716        TxData: sai::Pin<consts::Const<N>>,
717        Chan: consts::Unsigned,
718        <TxData as sai::Pin<consts::Const<N>>>::Signal: sai::TxDataSignal<Index = Chan>,
719    {
720        reset(&sai);
721
722        sai::prepare(&mut mclk_pin);
723        sai::prepare(&mut tx_pins.sync);
724        sai::prepare(&mut tx_pins.bclk);
725        sai::prepare(&mut tx_pins.data);
726
727        Sai {
728            sai: crate::into_any(sai),
729            tx_chan_mask: 1 << Chan::to_usize(),
730            rx_chan_mask: 0,
731        }
732    }
733
734    /// Create a Sai instance given a set of receive pins.
735    pub fn from_rx<const N: u8, Chan, Mclk, RxSync, RxBclk, RxData>(
736        sai: ral::sai::Instance<N>,
737        mut mclk_pin: Mclk,
738        mut rx_pins: Pins<RxSync, RxBclk, RxData>,
739    ) -> Self
740    where
741        Mclk: sai::Pin<consts::Const<N>, Signal = sai::Mclk>,
742        RxSync: sai::Pin<consts::Const<N>, Signal = sai::RxSync>,
743        RxBclk: sai::Pin<consts::Const<N>, Signal = sai::RxBclk>,
744        RxData: sai::Pin<consts::Const<N>>,
745        Chan: consts::Unsigned,
746        <RxData as sai::Pin<consts::Const<N>>>::Signal: sai::RxDataSignal<Index = Chan>,
747    {
748        reset(&sai);
749
750        sai::prepare(&mut mclk_pin);
751        sai::prepare(&mut rx_pins.sync);
752        sai::prepare(&mut rx_pins.bclk);
753        sai::prepare(&mut rx_pins.data);
754
755        Sai {
756            sai: crate::into_any(sai),
757            tx_chan_mask: 0,
758            rx_chan_mask: 1 << Chan::to_usize(),
759        }
760    }
761
762    /// Create a new SAI driver from the RAL SAI instance.
763    ///
764    /// You're responsible for configuring pins, and for making sure
765    /// the pin configuration doesn't change while this driver is in use.
766    /// Setting the channel mask is *also* your responsibility.
767    pub fn without_pins<const N: u8>(
768        sai: ral::sai::Instance<N>,
769        tx_chan_mask: u32,
770        rx_chan_mask: u32,
771    ) -> Self {
772        Sai {
773            sai: crate::into_any(sai),
774            tx_chan_mask,
775            rx_chan_mask,
776        }
777    }
778
779    /// Split the Tx/Rx pair from a SAI.
780    ///
781    /// # Arguments
782    ///
783    /// * `word_size` - Audio word size in bits (8-32)
784    /// * `frame_size` - Number of words per frame
785    /// * `packing` - FIFO packing mode
786    /// * `cfg` - SAI configuration
787    pub fn split(
788        self,
789        word_size: u8,
790        frame_size: usize,
791        packing: Packing,
792        cfg: &SaiConfig,
793    ) -> Result<(Option<Tx>, Option<Rx>), InvalidPackingError> {
794        if !packing.is_valid_for_word_size(word_size) {
795            return Err(InvalidPackingError { word_size, packing });
796        }
797        let tx_channel = self.tx_chan_mask.trailing_zeros() as usize;
798        let rx_channel = self.rx_chan_mask.trailing_zeros() as usize;
799
800        let tx = (self.tx_chan_mask != 0).then(|| Tx {
801            // SAFETY: We're creating an alias to the same register block.
802            // Tx and Rx operate on different parts of the register block.
803            sai: unsafe { AnyInstance::new(&*self.sai) },
804            word_size,
805            frame_size,
806            channel: tx_channel,
807        });
808        let rx = (self.rx_chan_mask != 0).then(|| Rx {
809            // SAFETY: We're creating an alias to the same register block.
810            // Tx and Rx operate on different parts of the register block.
811            sai: unsafe { AnyInstance::new(&*self.sai) },
812            word_size,
813            frame_size,
814            channel: rx_channel,
815        });
816
817        let frame_sync_dir = match cfg.mode {
818            Mode::Master => 1,
819            Mode::BclkSlaveFrameSyncMaster => 1,
820            _ => 0,
821        };
822
823        let bclk_dir = match cfg.mode {
824            Mode::Master => 1,
825            Mode::BclkMasterFrameSyncSlave => 1,
826            _ => 0,
827        };
828
829        let (tx_sync_mode, rx_sync_mode) = match cfg.sync_mode {
830            SyncMode::Async => (0b00, 0b00),
831            SyncMode::TxFollowRx => (0b01, 0b00),
832            SyncMode::RxFollowTx => (0b00, 0b01),
833        };
834
835        let sync_width = match cfg.sync_width {
836            SyncWidth::WordSize => word_size as u32,
837        };
838
839        if tx.is_some() {
840            ral::write_reg!(ral::sai, self.sai, TCR1, TFW: cfg.tx_fifo_wm);
841            if cfg.mode == Mode::Master || cfg.mode == Mode::BclkMasterFrameSyncSlave {
842                ral::write_reg!(ral::sai, self.sai, TCR2, SYNC: tx_sync_mode,
843                    BCS: cfg.bclk_src_swap as u32, BCI: cfg.bclk_input_delay as u32,
844                    MSEL: 0x11_u32, BCP: cfg.bclk_polarity as u32, BCD: bclk_dir,
845                    DIV: cfg.tx_bclk_div);
846            } else {
847                ral::modify_reg!(ral::sai, self.sai, TCR2, BCP: cfg.bclk_polarity as u32);
848            }
849            ral::modify_reg!(ral::sai, self.sai, TCR3, TCE: self.tx_chan_mask, WDFL: 0_u32);
850            ral::write_reg!(ral::sai, self.sai, TCR4, FRSZ: ((frame_size - 1) as u32),
851                FPACK: packing as u32, SYWD: (sync_width - 1), MF: cfg.byte_order as u32,
852                FSE: cfg.sync_early as u32, FSP: cfg.sync_polarity as u32, FSD: frame_sync_dir);
853            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);
854            ral::write_reg!(ral::sai, self.sai, TCSR, TE: 0, STOPE: cfg.tx_stop_en as u32,
855                DBGE: cfg.tx_debug_en as u32, BCE: 1, WSF: 1, SEF: 1, FEF: 1, FWF: 0, FRF: 0,
856                WSIE: 0, SEIE: 0, FEIE: 0, FWIE: 0, FWDE: 0, FRDE: 0);
857        }
858
859        if rx.is_some() {
860            ral::write_reg!(ral::sai, self.sai, RCR1, RFW: cfg.rx_fifo_wm);
861            if cfg.mode == Mode::Master || cfg.mode == Mode::BclkMasterFrameSyncSlave {
862                ral::write_reg!(ral::sai, self.sai, RCR2, SYNC: rx_sync_mode,
863                    BCS: cfg.bclk_src_swap as u32, BCI: cfg.bclk_input_delay as u32,
864                    MSEL: cfg.mclk_source as u32, BCP: cfg.bclk_polarity as u32, BCD: bclk_dir,
865                    DIV: cfg.rx_bclk_div);
866            } else {
867                ral::modify_reg!(ral::sai, self.sai, RCR2, BCP: cfg.bclk_polarity as u32);
868            }
869            ral::modify_reg!(ral::sai, self.sai, RCR3, RCE: self.rx_chan_mask);
870            ral::write_reg!(ral::sai, self.sai, RCR4, FRSZ: ((frame_size - 1) as u32),
871                FPACK: packing as u32, SYWD: (sync_width - 1), MF: cfg.byte_order as u32,
872                FSE: cfg.sync_early as u32, FSP: cfg.sync_polarity as u32, FSD: frame_sync_dir);
873            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);
874            ral::write_reg!(ral::sai, self.sai, RCSR, RE: 0, STOPE: cfg.rx_stop_en as u32,
875                DBGE: cfg.rx_debug_en as u32, BCE: 1, WSF: 1, SEF: 1, FEF: 1, FWF: 0, FRF: 0,
876                WSIE: 0, SEIE: 0, FEIE: 0, FWIE: 0, FWDE: 0, FRDE: 0);
877        }
878
879        Ok((tx, rx))
880    }
881}