imxrt_hal/chip/imxrt10xx/
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//! ## Clock configuration
19//!
20//! Make sure to configure your clocks before using the audio interface. Note that there may be
21//! additional clock settings in `IOMUXC_GPR`.
22
23use crate::iomuxc::{consts, sai};
24use crate::ral;
25use core::marker::PhantomData;
26
27/// Audio word byte order
28#[derive(Clone, Copy, Default, Eq, PartialEq)]
29pub enum ByteOrder {
30    /// Least significant byte first
31    #[default]
32    LSB = 0,
33    /// Most significant byte first
34    MSB = 1,
35}
36
37/// Mode of operation for the SAI peripheral
38#[derive(Clone, Copy, Default, Eq, PartialEq)]
39pub enum Mode {
40    /// Master mode where all clocks are generated from the SAI
41    #[default]
42    Master = 0,
43    /// Slave mode where all audio clocks are expecting to come from external sources
44    Slave = 1,
45    /// Bitclock Master, FrameSync Slave
46    BclkMasterFrameSyncSlave = 2,
47    /// Bitclock Slave, FrameSync Master
48    BclkSlaveFrameSyncMaster = 3,
49}
50
51/// Clock Polarity Options for Bclk/Mclk
52#[derive(Clone, Copy, Default, Eq, PartialEq)]
53pub enum ClockPolarity {
54    /// Transmitted clock implies active high
55    #[default]
56    ActiveHigh = 0,
57    /// Transmitted clock implies active low
58    ActiveLow = 1,
59}
60
61#[allow(non_upper_case_globals)]
62impl ClockPolarity {
63    /// Received clock implies sample on rising edge
64    pub const SampleOnRising: ClockPolarity = ClockPolarity::ActiveLow;
65    /// Received clock implies sample on falling edge
66    pub const SampleOnFalling: ClockPolarity = ClockPolarity::ActiveHigh;
67}
68
69/// Mclk source option
70#[derive(Clone, Copy, Default, Eq, PartialEq)]
71pub enum MclkSource {
72    /// Mclk sourced from system clock
73    #[default]
74    Sysclk = 0,
75    /// Select 1, part dependent
76    Select1 = 1,
77    /// Select 2, part dependent
78    Select2 = 2,
79    /// Select 3, part dependent
80    Select3 = 3,
81}
82
83/// Frame sync mode between rx/tx
84#[derive(Clone, Copy, Default, Eq, PartialEq)]
85pub enum SyncMode {
86    /// Both tx/rx are setup as being independent of each other for frame sync
87    #[default]
88    Async = 0,
89    /// Tx synchronously follows Rx frame sync
90    TxFollowRx = 1,
91    /// Rx synchronously follows Tx frame sync
92    RxFollowTx = 2,
93}
94
95/// Frame Sync Width
96#[derive(Clone, Copy, Default, Eq, PartialEq)]
97pub enum SyncWidth {
98    /// Frame sync width is the size of the word
99    #[default]
100    WordSize,
101}
102
103/// Source for Bclk, check part datasheet for details
104#[derive(Clone, Copy, Default, Eq, PartialEq)]
105pub enum BclkSource {
106    /// Bus clock is the source of Bclk
107    #[default]
108    Bus = 0,
109    /// Option 1, part dependent
110    Opt1 = 1,
111    /// Option 2, part dependent
112    Opt2 = 2,
113    /// Option 3, part dependent
114    Opt3 = 3,
115}
116
117bitflags::bitflags! {
118    /// Interrupt settings.
119    ///
120    /// A set bit indicates that the interrupt is enabled.
121    pub struct Interrupts : u32 {
122        /// Word Start Interrupt Enable.
123        const WORD_START = 1 << 12;
124        /// Sync Error Interrupt Enable.
125        const SYNC_ERROR = 1 << 11;
126        /// FIFO Error Interrupt Enable.
127        const FIFO_ERROR = 1 << 10;
128        /// FIFO Warning Interrupt Enable.
129        const FIFO_WARNING = 1 << 9;
130        /// FIFO Request Interrupt Enable.
131        const FIFO_REQUEST = 1 << 8;
132    }
133}
134
135bitflags::bitflags! {
136    /// Status flags.
137    pub struct Status : u32 {
138        /// Word Start Flag.
139        ///
140        /// Indicates that the start of the configured word has been detected.
141        const WORD_START = 1 << 20;
142        /// Sync Error Flag.
143        ///
144        /// Indicates that an error in the externally-generated frame sync has been detected.
145        const SYNC_ERROR = 1 << 19;
146        /// FIFO Error Flag.
147        ///
148        /// Indicates that an enabled
149        /// * receive FIFO has overflowed
150        /// * transmit FIFO has underrun
151        const FIFO_ERROR = 1 << 18;
152        /// FIFO Warning Flag.
153        ///
154        /// Indicates that an enabled
155        /// * receive FIFO is full
156        /// * transmit FIFO is empty
157        const FIFO_WARNING = 1 << 17;
158        /// FIFO Request Flag.
159        ///
160        /// Indicates that the number of words in an enabled
161        /// * receive channel FIFO is greater than the receive FIFO watermark
162        /// * transmit channel FIFO is less than or equal to the transmit FIFO watermark
163        const FIFO_REQUEST = 1 << 16;
164    }
165}
166
167impl Status {
168    const W1C: Self = Self::from_bits_truncate(
169        Self::WORD_START.bits() | Self::SYNC_ERROR.bits() | Self::FIFO_ERROR.bits(),
170    );
171}
172
173mod private {
174    pub trait Sealed {}
175}
176
177/// Packing is useful to have function variants over so is provided as a trait
178pub trait Packing<const WORD_SIZE: u8>: private::Sealed {
179    /// FPACK register field value to set the appropriate byte packing of the FIFO
180    const FPACK: u32;
181}
182
183/// Indicates No packing of audio words into a single 32bit FIFO word
184pub struct PackingNone;
185
186/// Indicates 8bit audio words packed into a single 32bit FIFO word
187pub struct Packing8bit;
188
189/// Indicates 16bit audio words packed into a single 32bit FIFO word
190pub struct Packing16bit;
191
192impl<const WORD_SIZE: u8> Packing<WORD_SIZE> for PackingNone {
193    const FPACK: u32 = 0b00;
194}
195
196impl private::Sealed for PackingNone {}
197
198impl Packing<8> for Packing8bit {
199    const FPACK: u32 = 0b01;
200}
201
202impl private::Sealed for Packing8bit {}
203
204impl Packing<16> for Packing16bit {
205    const FPACK: u32 = 0b10;
206}
207
208impl private::Sealed for Packing16bit {}
209
210#[allow(non_upper_case_globals)]
211impl BclkSource {
212    /// Mclk Divider as Bclk source
213    pub const MclkDiv: BclkSource = BclkSource::Opt1;
214}
215
216fn reset_tx(regs: &ral::sai::RegisterBlock) {
217    ral::write_reg!(ral::sai, regs, TCSR, SR: 1, FR: 1);
218    ral::modify_reg!(ral::sai, regs, TCSR, SR: 0);
219}
220
221fn reset_rx(regs: &ral::sai::RegisterBlock) {
222    ral::write_reg!(ral::sai, regs, RCSR, SR: 1, FR: 1);
223    ral::modify_reg!(ral::sai, regs, RCSR, SR: 0);
224}
225
226fn reset(regs: &ral::sai::RegisterBlock) {
227    reset_tx(regs);
228    reset_rx(regs);
229}
230
231/// A set of Pins for Tx or Rx
232///
233/// NOTE: The Data type *could* be more than a single Pin.
234pub struct Pins<Sync, Bclk, Data> {
235    /// Frame sync pin
236    pub sync: Sync,
237    /// Bit clock pin
238    pub bclk: Bclk,
239    /// Data pin(s)
240    pub data: Data,
241}
242
243/// Configuration for SAI peripheral
244#[derive(Default)]
245pub struct SaiConfig {
246    /// MCLK source
247    pub mclk_source: MclkSource,
248    /// TX fifo watermark
249    pub tx_fifo_wm: u32,
250    /// TX stop enable
251    pub tx_stop_en: bool,
252    /// TX debug enable
253    pub tx_debug_en: bool,
254    /// TX BCLK divider
255    pub tx_bclk_div: u32,
256    /// RX FIFO watermark
257    pub rx_fifo_wm: u32,
258    /// RX stop enable
259    pub rx_stop_en: bool,
260    /// RX debug enable
261    pub rx_debug_en: bool,
262    /// RX BCLK divider
263    pub rx_bclk_div: u32,
264    /// Byte order
265    pub byte_order: ByteOrder,
266    /// Mode
267    pub mode: Mode,
268    /// Sync width
269    pub sync_width: SyncWidth,
270    /// Sync early
271    pub sync_early: bool,
272    /// Sync polarity
273    pub sync_polarity: ClockPolarity,
274    /// Sync mode
275    pub sync_mode: SyncMode,
276    /// BCLK source swap
277    pub bclk_src_swap: bool,
278    /// BCLK input delay
279    pub bclk_input_delay: bool,
280    /// BCLK polarity
281    pub bclk_polarity: ClockPolarity,
282}
283
284const MIN_BCLK_DIV: u32 = 2;
285const MAX_BCLK_DIV: u32 = 512;
286
287/// Compute a bclk divider setting given a desired numerical divider
288pub fn bclk_div(bclk_div: u32) -> u32 {
289    bclk_div.clamp(MIN_BCLK_DIV, MAX_BCLK_DIV).div_ceil(2) - 1
290}
291
292impl SaiConfig {
293    /// Initialize a constant SaiConfig to be used with an i2s signaling scheme
294    pub const fn i2s(bclk_div: u32) -> Self {
295        // Get the bitclock divider for a given integer division. Notably non-even numbers
296        // are rounded up as the bclk divider is *always* an even number from 2 to 512
297        Self {
298            mclk_source: MclkSource::Sysclk,
299            tx_fifo_wm: 16,
300            tx_stop_en: false,
301            tx_debug_en: false,
302            tx_bclk_div: bclk_div,
303            rx_fifo_wm: 16,
304            rx_stop_en: false,
305            rx_debug_en: false,
306            rx_bclk_div: bclk_div,
307            byte_order: ByteOrder::MSB,
308            mode: Mode::Master,
309            sync_early: true,
310            sync_width: SyncWidth::WordSize,
311            sync_polarity: ClockPolarity::ActiveLow,
312            sync_mode: SyncMode::Async,
313            bclk_src_swap: false,
314            bclk_input_delay: false,
315            bclk_polarity: ClockPolarity::SampleOnRising,
316        }
317    }
318}
319
320/// A SAI peripheral instance
321pub struct Sai<const N: u8, MclkPin, TxPins, RxPins> {
322    pub(super) sai: ral::sai::Instance<N>,
323    _mclk_pin: MclkPin,
324    tx_pins: Option<TxPins>,
325    rx_pins: Option<RxPins>,
326    tx_chan_mask: u32,
327    rx_chan_mask: u32,
328}
329
330impl<const N: u8, Chan, Mclk, TxSync, TxBclk, TxData, RxSync, RxBclk, RxData>
331    Sai<N, Mclk, Pins<TxSync, TxBclk, TxData>, Pins<RxSync, RxBclk, RxData>>
332where
333    Mclk: sai::Pin<consts::Const<N>, Signal = sai::Mclk>,
334    TxSync: sai::Pin<consts::Const<N>, Signal = sai::TxSync>,
335    TxBclk: sai::Pin<consts::Const<N>, Signal = sai::TxBclk>,
336    TxData: sai::Pin<consts::Const<N>>,
337    RxSync: sai::Pin<consts::Const<N>, Signal = sai::RxSync>,
338    RxBclk: sai::Pin<consts::Const<N>, Signal = sai::RxBclk>,
339    RxData: sai::Pin<consts::Const<N>>,
340    Chan: consts::Unsigned,
341    <TxData as sai::Pin<consts::Const<N>>>::Signal: sai::TxDataSignal<Index = Chan>,
342    <RxData as sai::Pin<consts::Const<N>>>::Signal: sai::RxDataSignal<Index = Chan>,
343{
344    /// Creates SAI instance with single channel RX and TX.
345    pub fn new(
346        sai: ral::sai::Instance<N>,
347        mut mclk_pin: Mclk,
348        mut tx_pins: Pins<TxSync, TxBclk, TxData>,
349        mut rx_pins: Pins<RxSync, RxBclk, RxData>,
350    ) -> Self {
351        reset(&sai);
352
353        sai::prepare(&mut mclk_pin);
354        sai::prepare(&mut tx_pins.sync);
355        sai::prepare(&mut tx_pins.bclk);
356        sai::prepare(&mut tx_pins.data);
357        sai::prepare(&mut rx_pins.sync);
358        sai::prepare(&mut rx_pins.bclk);
359        sai::prepare(&mut rx_pins.data);
360
361        Self {
362            sai,
363            _mclk_pin: mclk_pin,
364            tx_pins: Some(tx_pins),
365            rx_pins: Some(rx_pins),
366            tx_chan_mask: 1 << Chan::to_usize(),
367            rx_chan_mask: 1 << Chan::to_usize(),
368        }
369    }
370}
371
372/// A SAI transmit half
373pub struct Tx<
374    const N: u8,
375    const WORD_SIZE: u8,
376    const FRAME_SIZE: usize,
377    PACKING: Packing<WORD_SIZE>,
378> {
379    sai: ral::sai::Instance<N>,
380    _packing: PhantomData<PACKING>,
381}
382
383impl<const N: u8, const WORD_SIZE: u8, const FRAME_SIZE: usize, PACKING: Packing<WORD_SIZE>>
384    Tx<N, WORD_SIZE, FRAME_SIZE, PACKING>
385{
386    /// Enable/Disable transmission
387    pub fn set_enable(&mut self, en: bool) {
388        let mut tcsr = ral::read_reg!(ral::sai, self.sai, TCSR) & !Status::W1C.bits();
389        if en {
390            tcsr |= ral::sai::TCSR::TE::mask
391        } else {
392            tcsr &= !ral::sai::TCSR::TE::mask
393        }
394        ral::write_reg!(ral::sai, self.sai, TCSR, tcsr);
395        self.clear_status(Status::W1C);
396    }
397
398    /// Return the interrupt flags.
399    ///
400    /// The interrupt flags indicate the reasons that this peripheral may generate an interrupt.
401    pub fn interrupts(&self) -> Interrupts {
402        let tcsr = ral::read_reg!(ral::sai, self.sai, TCSR);
403        Interrupts::from_bits_truncate(tcsr)
404    }
405
406    /// Set the interrupt flags for this SAI transmitter.
407    pub fn set_interrupts(&mut self, interrutps: Interrupts) {
408        ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| {
409            let tcsr = tcsr & !Interrupts::all().bits();
410            tcsr | interrutps.bits()
411        })
412    }
413
414    /// Get the status register of the transmitter, this can be used in conjunction with
415    /// status field masks to determine the state of the SAI peripheral.
416    pub fn status(&mut self) -> Status {
417        let tcsr = ral::read_reg!(ral::sai, self.sai, TCSR);
418        Status::from_bits_truncate(tcsr)
419    }
420
421    /// Clear status error flags
422    pub fn clear_status(&mut self, flags: Status) {
423        let flags = flags & Status::W1C;
424        ral::modify_reg!(ral::sai, self.sai, TCSR, |tcsr| { tcsr | flags.bits() });
425    }
426
427    /// Get a dump of the Tx configuration registers
428    pub fn reg_dump(&mut self) -> [u32; 6] {
429        [
430            ral::read_reg!(ral::sai, self.sai, TCR1),
431            ral::read_reg!(ral::sai, self.sai, TCR2),
432            ral::read_reg!(ral::sai, self.sai, TCR3),
433            ral::read_reg!(ral::sai, self.sai, TCR4),
434            ral::read_reg!(ral::sai, self.sai, TCR5),
435            ral::read_reg!(ral::sai, self.sai, TCSR),
436        ]
437    }
438
439    /// Get the FIFO write and read position
440    ///
441    /// ```no_run
442    /// use imxrt_ral::sai::{SAI1, TCSR};
443    /// use imxrt_hal::sai::{Tx, PackingNone, Sai, SaiConfig};
444    /// let sai = Sai::without_pins(unsafe { SAI1::instance() }, 0, 0);
445    /// let (Some(mut sai_tx), None) = sai.split::<16, 2, PackingNone>(&SaiConfig::i2s(8)) else { panic!() };
446    ///
447    /// let (write_pos, read_pos) = sai_tx.fifo_position(0);
448    /// ```
449    pub fn fifo_position(&mut self, chan: usize) -> (u32, u32) {
450        ral::read_reg!(ral::sai, self.sai, TFR[chan], WFP, RFP)
451    }
452}
453
454impl<const N: u8, const FRAME_SIZE: usize> Tx<N, 32, FRAME_SIZE, PackingNone> {
455    /// Write without checks or blocking a single audio frame to channels FIFO
456    pub fn write_frame(&mut self, chan: usize, frame: [u32; FRAME_SIZE]) {
457        for sample in frame {
458            ral::write_reg!(ral::sai, self.sai, TDR[chan], sample);
459        }
460    }
461}
462
463impl<const N: u8, const FRAME_SIZE: usize> Tx<N, 16, FRAME_SIZE, PackingNone> {
464    /// Write without checks or blocking a single audio frame to channels FIFO
465    pub fn write_frame(&mut self, chan: usize, frame: [u16; FRAME_SIZE]) {
466        for sample in frame {
467            ral::write_reg!(ral::sai, self.sai, TDR[chan], sample as u32);
468        }
469    }
470}
471
472impl<const N: u8, const FRAME_SIZE: usize> Tx<N, 8, FRAME_SIZE, PackingNone> {
473    /// Write without checks or blocking a single audio frame to channels FIFO
474    pub fn write_frame(&mut self, chan: usize, frame: [u8; FRAME_SIZE]) {
475        for sample in frame {
476            ral::write_reg!(ral::sai, self.sai, TDR[chan], sample as u32);
477        }
478    }
479}
480
481/// A SAI receive half
482pub struct Rx<
483    const N: u8,
484    const WORD_SIZE: u8,
485    const FRAME_SIZE: usize,
486    PACKING: Packing<WORD_SIZE>,
487> {
488    sai: ral::sai::Instance<N>,
489    _packing: PhantomData<PACKING>,
490}
491
492impl<const N: u8, const WORD_SIZE: u8, const FRAME_SIZE: usize, PACKING: Packing<WORD_SIZE>>
493    Rx<N, WORD_SIZE, FRAME_SIZE, PACKING>
494{
495    /// Enable/Disable transmission
496    pub fn set_enable(&mut self, en: bool) {
497        let mut rcsr = ral::read_reg!(ral::sai, self.sai, RCSR) & !Status::W1C.bits();
498        if en {
499            rcsr |= ral::sai::RCSR::RE::mask
500        } else {
501            rcsr &= !ral::sai::RCSR::RE::mask
502        }
503        ral::write_reg!(ral::sai, self.sai, RCSR, rcsr);
504        self.clear_status(Status::W1C);
505    }
506
507    /// Return the interrupt flags.
508    ///
509    /// The interrupt flags indicate the reasons that this peripheral may generate an interrupt.
510    pub fn interrupts(&self) -> Interrupts {
511        let rcsr = ral::read_reg!(ral::sai, self.sai, RCSR);
512        Interrupts::from_bits_truncate(rcsr)
513    }
514
515    /// Set the interrupt flags for this SAI transmitter.
516    pub fn set_interrupts(&mut self, interrutps: Interrupts) {
517        ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| {
518            let rcsr = rcsr & !Interrupts::all().bits();
519            rcsr | interrutps.bits()
520        })
521    }
522
523    /// Get the status register of the transmitter, this can be used in conjunction with
524    /// status field masks to determine the state of the SAI peripheral.
525    pub fn status(&mut self) -> Status {
526        let rcsr = ral::read_reg!(ral::sai, self.sai, RCSR);
527        Status::from_bits_truncate(rcsr)
528    }
529
530    /// Clear status error flags
531    pub fn clear_status(&mut self, flags: Status) {
532        let flags = flags & Status::W1C;
533        ral::modify_reg!(ral::sai, self.sai, RCSR, |rcsr| { rcsr | flags.bits() });
534    }
535
536    /// Get a dump of the Rx configuration registers
537    pub fn reg_dump(&mut self) -> [u32; 6] {
538        [
539            ral::read_reg!(ral::sai, self.sai, RCR1),
540            ral::read_reg!(ral::sai, self.sai, RCR2),
541            ral::read_reg!(ral::sai, self.sai, RCR3),
542            ral::read_reg!(ral::sai, self.sai, RCR4),
543            ral::read_reg!(ral::sai, self.sai, RCR5),
544            ral::read_reg!(ral::sai, self.sai, RCSR),
545        ]
546    }
547
548    /// Get the FIFO write and read position
549    ///
550    /// ```no_run
551    /// use imxrt_ral::sai::{SAI1, RCSR};
552    /// use imxrt_hal::sai::{Rx, PackingNone, Sai, SaiConfig};
553    /// let sai = Sai::without_pins(unsafe { SAI1::instance() }, 0, 0);
554    /// let (Some(mut sai_rx), None) = sai.split::<16, 2, PackingNone>(&SaiConfig::i2s(8)) else { panic!() };
555    ///
556    /// let (write_pos, read_pos) = sai_rx.fifo_position(0);
557    /// ```
558    pub fn fifo_position(&mut self, chan: usize) -> (u32, u32) {
559        ral::read_reg!(ral::sai, self.sai, RFR[chan], WFP, RFP)
560    }
561}
562
563impl<const N: u8, const FRAME_SIZE: usize> Rx<N, 32, FRAME_SIZE, PackingNone> {
564    /// Read without checks or blocking a single audio frame from channels FIFO
565    pub fn read_frame(&mut self, chan: usize, frame: &mut [u32; FRAME_SIZE]) {
566        for sample in frame {
567            *sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]);
568        }
569    }
570}
571
572impl<const N: u8, const FRAME_SIZE: usize> Rx<N, 16, FRAME_SIZE, PackingNone> {
573    /// Read without checks or blocking a single audio frame from channels FIFO
574    pub fn read_frame(&mut self, chan: usize, frame: &mut [u16; FRAME_SIZE]) {
575        for sample in frame {
576            *sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]) as u16;
577        }
578    }
579}
580
581impl<const N: u8, const FRAME_SIZE: usize> Rx<N, 8, FRAME_SIZE, PackingNone> {
582    /// Read without checks or blocking a single audio frame from channels FIFO
583    pub fn read_frame(&mut self, chan: usize, frame: &mut [u8; FRAME_SIZE]) {
584        for sample in frame {
585            *sample = ral::read_reg!(ral::sai, self.sai, RDR[chan]) as u8;
586        }
587    }
588}
589
590impl<const N: u8, Chan, Mclk, TxSync, TxBclk, TxData> Sai<N, Mclk, Pins<TxSync, TxBclk, TxData>, ()>
591where
592    Mclk: sai::Pin<consts::Const<N>, Signal = sai::Mclk>,
593    TxSync: sai::Pin<consts::Const<N>, Signal = sai::TxSync>,
594    TxBclk: sai::Pin<consts::Const<N>, Signal = sai::TxBclk>,
595    TxData: sai::Pin<consts::Const<N>>,
596    Chan: consts::Unsigned,
597    <TxData as sai::Pin<consts::Const<N>>>::Signal: sai::TxDataSignal<Index = Chan>,
598{
599    /// The peripheral instance.
600    pub const N: u8 = N;
601
602    /// Create a Sai instance given a set of transmit pins
603    pub fn from_tx(
604        sai: ral::sai::Instance<N>,
605        mut mclk_pin: Mclk,
606        mut tx_pins: Pins<TxSync, TxBclk, TxData>,
607    ) -> Self {
608        reset(&sai);
609
610        sai::prepare(&mut mclk_pin);
611        sai::prepare(&mut tx_pins.sync);
612        sai::prepare(&mut tx_pins.bclk);
613        sai::prepare(&mut tx_pins.data);
614
615        Sai {
616            sai,
617            _mclk_pin: mclk_pin,
618            tx_pins: Some(tx_pins),
619            rx_pins: None,
620            tx_chan_mask: 1 << Chan::to_usize(),
621            rx_chan_mask: 0,
622        }
623    }
624}
625
626impl<const N: u8, Chan, Mclk, RxSync, RxBclk, RxData> Sai<N, Mclk, (), Pins<RxSync, RxBclk, RxData>>
627where
628    Mclk: sai::Pin<consts::Const<N>, Signal = sai::Mclk>,
629    RxSync: sai::Pin<consts::Const<N>, Signal = sai::RxSync>,
630    RxBclk: sai::Pin<consts::Const<N>, Signal = sai::RxBclk>,
631    RxData: sai::Pin<consts::Const<N>>,
632    Chan: consts::Unsigned,
633    <RxData as sai::Pin<consts::Const<N>>>::Signal: sai::RxDataSignal<Index = Chan>,
634{
635    /// Create a Sai instance given a set of receive pins
636    pub fn from_rx(
637        sai: ral::sai::Instance<N>,
638        mut mclk_pin: Mclk,
639        mut rx_pins: Pins<RxSync, RxBclk, RxData>,
640    ) -> Self {
641        reset(&sai);
642
643        sai::prepare(&mut mclk_pin);
644        sai::prepare(&mut rx_pins.sync);
645        sai::prepare(&mut rx_pins.bclk);
646        sai::prepare(&mut rx_pins.data);
647
648        Sai {
649            sai,
650            _mclk_pin: mclk_pin,
651            tx_pins: None,
652            rx_pins: Some(rx_pins),
653            tx_chan_mask: 0,
654            rx_chan_mask: 1 << Chan::to_usize(),
655        }
656    }
657}
658
659impl<const N: u8> Sai<N, (), (), ()> {
660    /// Create a new SAI driver from the RAL SAI instance.
661    ///
662    /// You're responsible for configuring pins, and for making sure
663    /// the pin configuration doesn't change while this driver is in use.
664    /// Setting the channel mask is *also* your responsibility
665    pub fn without_pins(sai: ral::sai::Instance<N>, tx_chan_mask: u32, rx_chan_mask: u32) -> Self {
666        Sai {
667            sai,
668            _mclk_pin: (),
669            tx_pins: None,
670            rx_pins: None,
671            tx_chan_mask,
672            rx_chan_mask,
673        }
674    }
675}
676
677impl<const N: u8, Mclk, TxPins, RxPins> Sai<N, Mclk, TxPins, RxPins> {
678    /// Split the Tx/Rx pair from a SAI, with word, frame, and packing options as type parameters
679    pub fn split<const WORD_SIZE: u8, const FRAME_SIZE: usize, PACKING: Packing<WORD_SIZE>>(
680        self,
681        cfg: &SaiConfig,
682    ) -> (
683        Option<Tx<N, WORD_SIZE, FRAME_SIZE, PACKING>>,
684        Option<Rx<N, WORD_SIZE, FRAME_SIZE, PACKING>>,
685    ) {
686        let tx = self.tx_pins.map(|_| Tx {
687            // Safety: create instance
688            sai: unsafe { ral::sai::Instance::<N>::new(&*self.sai) },
689            _packing: PhantomData::<PACKING>,
690        });
691
692        let rx = self.rx_pins.map(|_| Rx {
693            // Safety: create instance
694            sai: unsafe { ral::sai::Instance::<N>::new(&*self.sai) },
695            _packing: PhantomData::<PACKING>,
696        });
697
698        let frame_sync_dir = match cfg.mode {
699            Mode::Master => 1,
700            Mode::BclkSlaveFrameSyncMaster => 1,
701            _ => 0,
702        };
703
704        let bclk_dir = match cfg.mode {
705            Mode::Master => 1,
706            Mode::BclkMasterFrameSyncSlave => 1,
707            _ => 0,
708        };
709
710        let (tx_sync_mode, rx_sync_mode) = match cfg.sync_mode {
711            SyncMode::Async => (0b00, 0b00),
712            SyncMode::TxFollowRx => (0b01, 0b00),
713            SyncMode::RxFollowTx => (0b00, 0b01),
714        };
715
716        let sync_width = match cfg.sync_width {
717            SyncWidth::WordSize => WORD_SIZE as u32,
718        };
719
720        if tx.is_some() {
721            ral::write_reg!(ral::sai, self.sai, TCR1, TFW: cfg.tx_fifo_wm);
722            if cfg.mode == Mode::Master || cfg.mode == Mode::BclkMasterFrameSyncSlave {
723                ral::write_reg!(ral::sai, self.sai, TCR2, SYNC: tx_sync_mode,
724                    BCS: cfg.bclk_src_swap as u32, BCI: cfg.bclk_input_delay as u32,
725                    MSEL: 0x11_u32, BCP: cfg.bclk_polarity as u32, BCD: bclk_dir,
726                    DIV: cfg.tx_bclk_div);
727            } else {
728                ral::modify_reg!(ral::sai, self.sai, TCR2, BCP: cfg.bclk_polarity as u32);
729            }
730            ral::modify_reg!(ral::sai, self.sai, TCR3, TCE: self.tx_chan_mask, WDFL: 0_u32);
731            ral::write_reg!(ral::sai, self.sai, TCR4, FRSZ: ((FRAME_SIZE - 1) as u32),
732                FPACK: 0_u32, SYWD: (sync_width - 1), MF: cfg.byte_order as u32,
733                FSE: cfg.sync_early as u32, FSP: cfg.sync_polarity as u32, FSD: frame_sync_dir);
734            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);
735            ral::write_reg!(ral::sai, self.sai, TCSR, TE: 0, STOPE: cfg.tx_stop_en as u32,
736                DBGE: cfg.tx_debug_en as u32, BCE: 1, WSF: 1, SEF: 1, FEF: 1, FWF: 0, FRF: 0,
737                WSIE: 0, SEIE: 0, FEIE: 0, FWIE: 0, FWDE: 0, FRDE: 0);
738        }
739
740        if rx.is_some() {
741            ral::write_reg!(ral::sai, self.sai, RCR1, RFW: cfg.rx_fifo_wm);
742            if cfg.mode == Mode::Master || cfg.mode == Mode::BclkMasterFrameSyncSlave {
743                ral::write_reg!(ral::sai, self.sai, RCR2, SYNC: rx_sync_mode,
744                    BCS: cfg.bclk_src_swap as u32, BCI: cfg.bclk_input_delay as u32,
745                    MSEL: cfg.mclk_source as u32, BCP: cfg.bclk_polarity as u32, BCD: bclk_dir,
746                    DIV: cfg.rx_bclk_div);
747            } else {
748                ral::modify_reg!(ral::sai, self.sai, RCR2, BCP: cfg.bclk_polarity as u32);
749            }
750            ral::modify_reg!(ral::sai, self.sai, RCR3, RCE: self.rx_chan_mask);
751            ral::write_reg!(ral::sai, self.sai, RCR4, FRSZ: ((FRAME_SIZE - 1) as u32),
752                FPACK: PACKING::FPACK, SYWD: (sync_width - 1), MF: cfg.byte_order as u32,
753                FSE: cfg.sync_early as u32, FSP: cfg.sync_polarity as u32, FSD: frame_sync_dir);
754            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);
755            ral::write_reg!(ral::sai, self.sai, RCSR, RE: 0, STOPE: cfg.rx_stop_en as u32,
756                DBGE: cfg.rx_debug_en as u32, BCE: 1, WSF: 1, SEF: 1, FEF: 1, FWF: 0, FRF: 0,
757                WSIE: 0, SEIE: 0, FEIE: 0, FWIE: 0, FWDE: 0, FRDE: 0);
758        }
759
760        (tx, rx)
761    }
762}