embassy_stm32/sai/
mod.rs

1//! Serial Audio Interface (SAI)
2#![macro_use]
3#![cfg_attr(gpdma, allow(unused))]
4
5use core::marker::PhantomData;
6
7use embassy_hal_internal::{into_ref, PeripheralRef};
8
9pub use crate::dma::word;
10#[cfg(not(gpdma))]
11use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer};
12use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
13use crate::pac::sai::{vals, Sai as Regs};
14use crate::rcc::{self, RccPeripheral};
15use crate::{peripherals, Peripheral};
16
17/// SAI error
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub enum Error {
21    /// `write` called on a SAI in receive mode.
22    NotATransmitter,
23    /// `read` called on a SAI in transmit mode.
24    NotAReceiver,
25    /// Overrun
26    Overrun,
27}
28
29#[cfg(not(gpdma))]
30impl From<ringbuffer::Error> for Error {
31    fn from(#[allow(unused)] err: ringbuffer::Error) -> Self {
32        #[cfg(feature = "defmt")]
33        {
34            if err == ringbuffer::Error::DmaUnsynced {
35                defmt::error!("Ringbuffer broken invariants detected!");
36            }
37        }
38        Self::Overrun
39    }
40}
41
42/// Master/slave mode.
43#[derive(Copy, Clone)]
44#[allow(missing_docs)]
45pub enum Mode {
46    Master,
47    Slave,
48}
49
50impl Mode {
51    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
52    const fn mode(&self, tx_rx: TxRx) -> vals::Mode {
53        match tx_rx {
54            TxRx::Transmitter => match self {
55                Mode::Master => vals::Mode::MASTER_TX,
56                Mode::Slave => vals::Mode::SLAVE_TX,
57            },
58            TxRx::Receiver => match self {
59                Mode::Master => vals::Mode::MASTER_RX,
60                Mode::Slave => vals::Mode::SLAVE_RX,
61            },
62        }
63    }
64}
65
66/// Direction: transmit or receive
67#[derive(Copy, Clone)]
68#[allow(missing_docs)]
69pub enum TxRx {
70    Transmitter,
71    Receiver,
72}
73
74/// Data slot size.
75#[derive(Copy, Clone)]
76#[allow(missing_docs)]
77pub enum SlotSize {
78    DataSize,
79    /// 16 bit data length on 16 bit wide channel
80    Channel16,
81    /// 16 bit data length on 32 bit wide channel
82    Channel32,
83}
84
85impl SlotSize {
86    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
87    const fn slotsz(&self) -> vals::Slotsz {
88        match self {
89            SlotSize::DataSize => vals::Slotsz::DATA_SIZE,
90            SlotSize::Channel16 => vals::Slotsz::BIT16,
91            SlotSize::Channel32 => vals::Slotsz::BIT32,
92        }
93    }
94}
95
96/// Data size.
97#[derive(Copy, Clone)]
98#[allow(missing_docs)]
99pub enum DataSize {
100    Data8,
101    Data10,
102    Data16,
103    Data20,
104    Data24,
105    Data32,
106}
107
108impl DataSize {
109    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
110    const fn ds(&self) -> vals::Ds {
111        match self {
112            DataSize::Data8 => vals::Ds::BIT8,
113            DataSize::Data10 => vals::Ds::BIT10,
114            DataSize::Data16 => vals::Ds::BIT16,
115            DataSize::Data20 => vals::Ds::BIT20,
116            DataSize::Data24 => vals::Ds::BIT24,
117            DataSize::Data32 => vals::Ds::BIT32,
118        }
119    }
120}
121
122/// FIFO threshold level.
123#[derive(Copy, Clone)]
124#[allow(missing_docs)]
125pub enum FifoThreshold {
126    Empty,
127    Quarter,
128    Half,
129    ThreeQuarters,
130    Full,
131}
132
133impl FifoThreshold {
134    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
135    const fn fth(&self) -> vals::Fth {
136        match self {
137            FifoThreshold::Empty => vals::Fth::EMPTY,
138            FifoThreshold::Quarter => vals::Fth::QUARTER1,
139            FifoThreshold::Half => vals::Fth::QUARTER2,
140            FifoThreshold::ThreeQuarters => vals::Fth::QUARTER3,
141            FifoThreshold::Full => vals::Fth::FULL,
142        }
143    }
144}
145
146/// Output value on mute.
147#[derive(Copy, Clone)]
148#[allow(missing_docs)]
149pub enum MuteValue {
150    Zero,
151    LastValue,
152}
153
154impl MuteValue {
155    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
156    const fn muteval(&self) -> vals::Muteval {
157        match self {
158            MuteValue::Zero => vals::Muteval::SEND_ZERO,
159            MuteValue::LastValue => vals::Muteval::SEND_LAST,
160        }
161    }
162}
163
164/// Protocol variant to use.
165#[derive(Copy, Clone)]
166#[allow(missing_docs)]
167pub enum Protocol {
168    Free,
169    Spdif,
170    Ac97,
171}
172
173impl Protocol {
174    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
175    const fn prtcfg(&self) -> vals::Prtcfg {
176        match self {
177            Protocol::Free => vals::Prtcfg::FREE,
178            Protocol::Spdif => vals::Prtcfg::SPDIF,
179            Protocol::Ac97 => vals::Prtcfg::AC97,
180        }
181    }
182}
183
184/// Sync input between SAI units/blocks.
185#[derive(Copy, Clone, PartialEq)]
186#[allow(missing_docs)]
187pub enum SyncInput {
188    /// Not synced to any other SAI unit.
189    None,
190    /// Syncs with the other A/B sub-block within the SAI unit
191    Internal,
192    /// Syncs with a sub-block in the other SAI unit
193    #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))]
194    External(SyncInputInstance),
195}
196
197impl SyncInput {
198    const fn syncen(&self) -> vals::Syncen {
199        match self {
200            SyncInput::None => vals::Syncen::ASYNCHRONOUS,
201            SyncInput::Internal => vals::Syncen::INTERNAL,
202            #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))]
203            SyncInput::External(_) => vals::Syncen::EXTERNAL,
204        }
205    }
206}
207
208/// SAI instance to sync from.
209#[cfg(any(sai_v4_2pdm, sai_v4_4pdm))]
210#[derive(Copy, Clone, PartialEq)]
211#[allow(missing_docs)]
212pub enum SyncInputInstance {
213    #[cfg(peri_sai1)]
214    Sai1 = 0,
215    #[cfg(peri_sai2)]
216    Sai2 = 1,
217    #[cfg(peri_sai3)]
218    Sai3 = 2,
219    #[cfg(peri_sai4)]
220    Sai4 = 3,
221}
222
223/// Channels (stereo or mono).
224#[derive(Copy, Clone, PartialEq)]
225#[allow(missing_docs)]
226pub enum StereoMono {
227    Stereo,
228    Mono,
229}
230
231impl StereoMono {
232    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
233    const fn mono(&self) -> vals::Mono {
234        match self {
235            StereoMono::Stereo => vals::Mono::STEREO,
236            StereoMono::Mono => vals::Mono::MONO,
237        }
238    }
239}
240
241/// Bit order
242#[derive(Copy, Clone)]
243pub enum BitOrder {
244    /// Least significant bit first.
245    LsbFirst,
246    /// Most significant bit first.
247    MsbFirst,
248}
249
250impl BitOrder {
251    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
252    const fn lsbfirst(&self) -> vals::Lsbfirst {
253        match self {
254            BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST,
255            BitOrder::MsbFirst => vals::Lsbfirst::MSB_FIRST,
256        }
257    }
258}
259
260/// Frame sync offset.
261#[derive(Copy, Clone)]
262pub enum FrameSyncOffset {
263    /// This is used in modes other than standard I2S phillips mode
264    OnFirstBit,
265    /// This is used in standard I2S phillips mode
266    BeforeFirstBit,
267}
268
269impl FrameSyncOffset {
270    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
271    const fn fsoff(&self) -> vals::Fsoff {
272        match self {
273            FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST,
274            FrameSyncOffset::BeforeFirstBit => vals::Fsoff::BEFORE_FIRST,
275        }
276    }
277}
278
279/// Frame sync polarity
280#[derive(Copy, Clone)]
281pub enum FrameSyncPolarity {
282    /// Sync signal is active low.
283    ActiveLow,
284    /// Sync signal is active high
285    ActiveHigh,
286}
287
288impl FrameSyncPolarity {
289    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
290    const fn fspol(&self) -> vals::Fspol {
291        match self {
292            FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE,
293            FrameSyncPolarity::ActiveHigh => vals::Fspol::RISING_EDGE,
294        }
295    }
296}
297
298/// Sync definition.
299#[derive(Copy, Clone)]
300#[allow(missing_docs)]
301pub enum FrameSyncDefinition {
302    StartOfFrame,
303    ChannelIdentification,
304}
305
306impl FrameSyncDefinition {
307    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
308    const fn fsdef(&self) -> bool {
309        match self {
310            FrameSyncDefinition::StartOfFrame => false,
311            FrameSyncDefinition::ChannelIdentification => true,
312        }
313    }
314}
315
316/// Clock strobe.
317#[derive(Copy, Clone)]
318#[allow(missing_docs)]
319pub enum ClockStrobe {
320    Falling,
321    Rising,
322}
323
324impl ClockStrobe {
325    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
326    const fn ckstr(&self) -> vals::Ckstr {
327        match self {
328            ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE,
329            ClockStrobe::Rising => vals::Ckstr::RISING_EDGE,
330        }
331    }
332}
333
334/// Complements format for negative samples.
335#[derive(Copy, Clone)]
336#[allow(missing_docs)]
337pub enum ComplementFormat {
338    OnesComplement,
339    TwosComplement,
340}
341
342impl ComplementFormat {
343    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
344    const fn cpl(&self) -> vals::Cpl {
345        match self {
346            ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT,
347            ComplementFormat::TwosComplement => vals::Cpl::TWOS_COMPLEMENT,
348        }
349    }
350}
351
352/// Companding setting.
353#[derive(Copy, Clone)]
354#[allow(missing_docs)]
355pub enum Companding {
356    None,
357    MuLaw,
358    ALaw,
359}
360
361impl Companding {
362    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
363    const fn comp(&self) -> vals::Comp {
364        match self {
365            Companding::None => vals::Comp::NO_COMPANDING,
366            Companding::MuLaw => vals::Comp::MU_LAW,
367            Companding::ALaw => vals::Comp::ALAW,
368        }
369    }
370}
371
372/// Output drive
373#[derive(Copy, Clone)]
374#[allow(missing_docs)]
375pub enum OutputDrive {
376    OnStart,
377    Immediately,
378}
379
380impl OutputDrive {
381    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
382    const fn outdriv(&self) -> vals::Outdriv {
383        match self {
384            OutputDrive::OnStart => vals::Outdriv::ON_START,
385            OutputDrive::Immediately => vals::Outdriv::IMMEDIATELY,
386        }
387    }
388}
389
390/// Master clock divider.
391#[derive(Copy, Clone, PartialEq)]
392#[allow(missing_docs)]
393#[cfg(any(sai_v1, sai_v2))]
394pub enum MasterClockDivider {
395    MasterClockDisabled,
396    Div1,
397    Div2,
398    Div4,
399    Div6,
400    Div8,
401    Div10,
402    Div12,
403    Div14,
404    Div16,
405    Div18,
406    Div20,
407    Div22,
408    Div24,
409    Div26,
410    Div28,
411    Div30,
412}
413
414/// Master clock divider.
415#[derive(Copy, Clone, PartialEq)]
416#[allow(missing_docs)]
417#[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
418pub enum MasterClockDivider {
419    MasterClockDisabled,
420    Div1,
421    Div2,
422    Div3,
423    Div4,
424    Div5,
425    Div6,
426    Div7,
427    Div8,
428    Div9,
429    Div10,
430    Div11,
431    Div12,
432    Div13,
433    Div14,
434    Div15,
435    Div16,
436    Div17,
437    Div18,
438    Div19,
439    Div20,
440    Div21,
441    Div22,
442    Div23,
443    Div24,
444    Div25,
445    Div26,
446    Div27,
447    Div28,
448    Div29,
449    Div30,
450    Div31,
451    Div32,
452    Div33,
453    Div34,
454    Div35,
455    Div36,
456    Div37,
457    Div38,
458    Div39,
459    Div40,
460    Div41,
461    Div42,
462    Div43,
463    Div44,
464    Div45,
465    Div46,
466    Div47,
467    Div48,
468    Div49,
469    Div50,
470    Div51,
471    Div52,
472    Div53,
473    Div54,
474    Div55,
475    Div56,
476    Div57,
477    Div58,
478    Div59,
479    Div60,
480    Div61,
481    Div62,
482    Div63,
483}
484
485impl MasterClockDivider {
486    #[cfg(any(sai_v1, sai_v2))]
487    const fn mckdiv(&self) -> u8 {
488        match self {
489            MasterClockDivider::MasterClockDisabled => 0,
490            MasterClockDivider::Div1 => 0,
491            MasterClockDivider::Div2 => 1,
492            MasterClockDivider::Div4 => 2,
493            MasterClockDivider::Div6 => 3,
494            MasterClockDivider::Div8 => 4,
495            MasterClockDivider::Div10 => 5,
496            MasterClockDivider::Div12 => 6,
497            MasterClockDivider::Div14 => 7,
498            MasterClockDivider::Div16 => 8,
499            MasterClockDivider::Div18 => 9,
500            MasterClockDivider::Div20 => 10,
501            MasterClockDivider::Div22 => 11,
502            MasterClockDivider::Div24 => 12,
503            MasterClockDivider::Div26 => 13,
504            MasterClockDivider::Div28 => 14,
505            MasterClockDivider::Div30 => 15,
506        }
507    }
508
509    #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
510    const fn mckdiv(&self) -> u8 {
511        match self {
512            MasterClockDivider::MasterClockDisabled => 0,
513            MasterClockDivider::Div1 => 1,
514            MasterClockDivider::Div2 => 2,
515            MasterClockDivider::Div3 => 3,
516            MasterClockDivider::Div4 => 4,
517            MasterClockDivider::Div5 => 5,
518            MasterClockDivider::Div6 => 6,
519            MasterClockDivider::Div7 => 7,
520            MasterClockDivider::Div8 => 8,
521            MasterClockDivider::Div9 => 9,
522            MasterClockDivider::Div10 => 10,
523            MasterClockDivider::Div11 => 11,
524            MasterClockDivider::Div12 => 12,
525            MasterClockDivider::Div13 => 13,
526            MasterClockDivider::Div14 => 14,
527            MasterClockDivider::Div15 => 15,
528            MasterClockDivider::Div16 => 16,
529            MasterClockDivider::Div17 => 17,
530            MasterClockDivider::Div18 => 18,
531            MasterClockDivider::Div19 => 19,
532            MasterClockDivider::Div20 => 20,
533            MasterClockDivider::Div21 => 21,
534            MasterClockDivider::Div22 => 22,
535            MasterClockDivider::Div23 => 23,
536            MasterClockDivider::Div24 => 24,
537            MasterClockDivider::Div25 => 25,
538            MasterClockDivider::Div26 => 26,
539            MasterClockDivider::Div27 => 27,
540            MasterClockDivider::Div28 => 28,
541            MasterClockDivider::Div29 => 29,
542            MasterClockDivider::Div30 => 30,
543            MasterClockDivider::Div31 => 31,
544            MasterClockDivider::Div32 => 32,
545            MasterClockDivider::Div33 => 33,
546            MasterClockDivider::Div34 => 34,
547            MasterClockDivider::Div35 => 35,
548            MasterClockDivider::Div36 => 36,
549            MasterClockDivider::Div37 => 37,
550            MasterClockDivider::Div38 => 38,
551            MasterClockDivider::Div39 => 39,
552            MasterClockDivider::Div40 => 40,
553            MasterClockDivider::Div41 => 41,
554            MasterClockDivider::Div42 => 42,
555            MasterClockDivider::Div43 => 43,
556            MasterClockDivider::Div44 => 44,
557            MasterClockDivider::Div45 => 45,
558            MasterClockDivider::Div46 => 46,
559            MasterClockDivider::Div47 => 47,
560            MasterClockDivider::Div48 => 48,
561            MasterClockDivider::Div49 => 49,
562            MasterClockDivider::Div50 => 50,
563            MasterClockDivider::Div51 => 51,
564            MasterClockDivider::Div52 => 52,
565            MasterClockDivider::Div53 => 53,
566            MasterClockDivider::Div54 => 54,
567            MasterClockDivider::Div55 => 55,
568            MasterClockDivider::Div56 => 56,
569            MasterClockDivider::Div57 => 57,
570            MasterClockDivider::Div58 => 58,
571            MasterClockDivider::Div59 => 59,
572            MasterClockDivider::Div60 => 60,
573            MasterClockDivider::Div61 => 61,
574            MasterClockDivider::Div62 => 62,
575            MasterClockDivider::Div63 => 63,
576        }
577    }
578}
579
580/// [`SAI`] configuration.
581#[allow(missing_docs)]
582#[non_exhaustive]
583#[derive(Copy, Clone)]
584pub struct Config {
585    pub mode: Mode,
586    pub tx_rx: TxRx,
587    pub sync_input: SyncInput,
588    pub sync_output: bool,
589    pub protocol: Protocol,
590    pub slot_size: SlotSize,
591    pub slot_count: word::U4,
592    pub slot_enable: u16,
593    pub first_bit_offset: word::U5,
594    pub data_size: DataSize,
595    pub stereo_mono: StereoMono,
596    pub bit_order: BitOrder,
597    pub frame_sync_offset: FrameSyncOffset,
598    pub frame_sync_polarity: FrameSyncPolarity,
599    pub frame_sync_active_level_length: word::U7,
600    pub frame_sync_definition: FrameSyncDefinition,
601    pub frame_length: u8,
602    pub clock_strobe: ClockStrobe,
603    pub output_drive: OutputDrive,
604    pub master_clock_divider: MasterClockDivider,
605    pub is_high_impedance_on_inactive_slot: bool,
606    pub fifo_threshold: FifoThreshold,
607    pub companding: Companding,
608    pub complement_format: ComplementFormat,
609    pub mute_value: MuteValue,
610    pub mute_detection_counter: word::U5,
611}
612
613impl Default for Config {
614    fn default() -> Self {
615        Self {
616            mode: Mode::Master,
617            tx_rx: TxRx::Transmitter,
618            sync_output: false,
619            sync_input: SyncInput::None,
620            protocol: Protocol::Free,
621            slot_size: SlotSize::DataSize,
622            slot_count: word::U4(2),
623            first_bit_offset: word::U5(0),
624            slot_enable: 0b11,
625            data_size: DataSize::Data16,
626            stereo_mono: StereoMono::Stereo,
627            bit_order: BitOrder::LsbFirst,
628            frame_sync_offset: FrameSyncOffset::BeforeFirstBit,
629            frame_sync_polarity: FrameSyncPolarity::ActiveLow,
630            frame_sync_active_level_length: word::U7(16),
631            frame_sync_definition: FrameSyncDefinition::ChannelIdentification,
632            frame_length: 32,
633            master_clock_divider: MasterClockDivider::MasterClockDisabled,
634            clock_strobe: ClockStrobe::Rising,
635            output_drive: OutputDrive::Immediately,
636            is_high_impedance_on_inactive_slot: false,
637            fifo_threshold: FifoThreshold::ThreeQuarters,
638            companding: Companding::None,
639            complement_format: ComplementFormat::TwosComplement,
640            mute_value: MuteValue::Zero,
641            mute_detection_counter: word::U5(4),
642        }
643    }
644}
645
646impl Config {
647    /// Create a new config with all default values.
648    pub fn new() -> Self {
649        return Default::default();
650    }
651}
652
653#[cfg(not(gpdma))]
654enum RingBuffer<'d, W: word::Word> {
655    Writable(WritableRingBuffer<'d, W>),
656    Readable(ReadableRingBuffer<'d, W>),
657}
658
659fn dr<W: word::Word>(w: crate::pac::sai::Sai, sub_block: WhichSubBlock) -> *mut W {
660    let ch = w.ch(sub_block as usize);
661    ch.dr().as_ptr() as _
662}
663
664// return the type for (sd, sck)
665fn get_af_types(mode: Mode, tx_rx: TxRx) -> (AfType, AfType) {
666    (
667        //sd is defined by tx/rx mode
668        match tx_rx {
669            TxRx::Transmitter => AfType::output(OutputType::PushPull, Speed::VeryHigh),
670            TxRx::Receiver => AfType::input(Pull::Down), // Ensure mute level when no input is connected.
671        },
672        //clocks (mclk, sck and fs) are defined by master/slave
673        match mode {
674            Mode::Master => AfType::output(OutputType::PushPull, Speed::VeryHigh),
675            Mode::Slave => AfType::input(Pull::Down), // Ensure no clocks when no input is connected.
676        },
677    )
678}
679
680#[cfg(not(gpdma))]
681fn get_ring_buffer<'d, T: Instance, W: word::Word>(
682    dma: impl Peripheral<P = impl Channel> + 'd,
683    dma_buf: &'d mut [W],
684    request: Request,
685    sub_block: WhichSubBlock,
686    tx_rx: TxRx,
687) -> RingBuffer<'d, W> {
688    let opts = TransferOptions {
689        half_transfer_ir: true,
690        //the new_write() and new_read() always use circular mode
691        ..Default::default()
692    };
693    match tx_rx {
694        TxRx::Transmitter => RingBuffer::Writable(unsafe {
695            WritableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
696        }),
697        TxRx::Receiver => RingBuffer::Readable(unsafe {
698            ReadableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
699        }),
700    }
701}
702
703fn update_synchronous_config(config: &mut Config) {
704    config.mode = Mode::Slave;
705    config.sync_output = false;
706
707    #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm))]
708    {
709        config.sync_input = SyncInput::Internal;
710    }
711
712    #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))]
713    {
714        //this must either be Internal or External
715        //The asynchronous sub-block on the same SAI needs to enable sync_output
716        assert!(config.sync_input != SyncInput::None);
717    }
718}
719
720/// SAI subblock instance.
721pub struct SubBlock<'d, T, S: SubBlockInstance> {
722    peri: PeripheralRef<'d, T>,
723    _phantom: PhantomData<S>,
724}
725
726/// Split the main SAIx peripheral into the two subblocks.
727///
728/// You can then create a [`Sai`] driver for each each half.
729pub fn split_subblocks<'d, T: Instance>(peri: impl Peripheral<P = T> + 'd) -> (SubBlock<'d, T, A>, SubBlock<'d, T, B>) {
730    into_ref!(peri);
731    rcc::enable_and_reset::<T>();
732
733    (
734        SubBlock {
735            peri: unsafe { peri.clone_unchecked() },
736            _phantom: PhantomData,
737        },
738        SubBlock {
739            peri,
740            _phantom: PhantomData,
741        },
742    )
743}
744
745/// SAI sub-block driver.
746pub struct Sai<'d, T: Instance, W: word::Word> {
747    _peri: PeripheralRef<'d, T>,
748    sd: Option<PeripheralRef<'d, AnyPin>>,
749    fs: Option<PeripheralRef<'d, AnyPin>>,
750    sck: Option<PeripheralRef<'d, AnyPin>>,
751    mclk: Option<PeripheralRef<'d, AnyPin>>,
752    #[cfg(gpdma)]
753    ring_buffer: PhantomData<W>,
754    #[cfg(not(gpdma))]
755    ring_buffer: RingBuffer<'d, W>,
756    sub_block: WhichSubBlock,
757}
758
759#[cfg(not(gpdma))]
760impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
761    /// Create a new SAI driver in asynchronous mode with MCLK.
762    ///
763    /// You can obtain the [`SubBlock`] with [`split_subblocks`].
764    pub fn new_asynchronous_with_mclk<S: SubBlockInstance>(
765        peri: SubBlock<'d, T, S>,
766        sck: impl Peripheral<P = impl SckPin<T, S>> + 'd,
767        sd: impl Peripheral<P = impl SdPin<T, S>> + 'd,
768        fs: impl Peripheral<P = impl FsPin<T, S>> + 'd,
769        mclk: impl Peripheral<P = impl MclkPin<T, S>> + 'd,
770        dma: impl Peripheral<P = impl Channel + Dma<T, S>> + 'd,
771        dma_buf: &'d mut [W],
772        mut config: Config,
773    ) -> Self {
774        into_ref!(mclk);
775
776        let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);
777        mclk.set_as_af(mclk.af_num(), ck_af_type);
778
779        if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
780            config.master_clock_divider = MasterClockDivider::Div1;
781        }
782
783        Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config)
784    }
785
786    /// Create a new SAI driver in asynchronous mode without MCLK.
787    ///
788    /// You can obtain the [`SubBlock`] with [`split_subblocks`].
789    pub fn new_asynchronous<S: SubBlockInstance>(
790        peri: SubBlock<'d, T, S>,
791        sck: impl Peripheral<P = impl SckPin<T, S>> + 'd,
792        sd: impl Peripheral<P = impl SdPin<T, S>> + 'd,
793        fs: impl Peripheral<P = impl FsPin<T, S>> + 'd,
794        dma: impl Peripheral<P = impl Channel + Dma<T, S>> + 'd,
795        dma_buf: &'d mut [W],
796        config: Config,
797    ) -> Self {
798        let peri = peri.peri;
799        into_ref!(peri, dma, sck, sd, fs);
800
801        let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);
802        sd.set_as_af(sd.af_num(), sd_af_type);
803        sck.set_as_af(sck.af_num(), ck_af_type);
804        fs.set_as_af(fs.af_num(), ck_af_type);
805
806        let sub_block = S::WHICH;
807        let request = dma.request();
808
809        Self::new_inner(
810            peri,
811            sub_block,
812            Some(sck.map_into()),
813            None,
814            Some(sd.map_into()),
815            Some(fs.map_into()),
816            get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx),
817            config,
818        )
819    }
820
821    /// Create a new SAI driver in synchronous mode.
822    ///
823    /// You can obtain the [`SubBlock`] with [`split_subblocks`].
824    pub fn new_synchronous<S: SubBlockInstance>(
825        peri: SubBlock<'d, T, S>,
826        sd: impl Peripheral<P = impl SdPin<T, S>> + 'd,
827        dma: impl Peripheral<P = impl Channel + Dma<T, S>> + 'd,
828        dma_buf: &'d mut [W],
829        mut config: Config,
830    ) -> Self {
831        update_synchronous_config(&mut config);
832
833        let peri = peri.peri;
834        into_ref!(dma, peri, sd);
835
836        let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx);
837        sd.set_as_af(sd.af_num(), sd_af_type);
838
839        let sub_block = S::WHICH;
840        let request = dma.request();
841
842        Self::new_inner(
843            peri,
844            sub_block,
845            None,
846            None,
847            Some(sd.map_into()),
848            None,
849            get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx),
850            config,
851        )
852    }
853
854    fn new_inner(
855        peri: impl Peripheral<P = T> + 'd,
856        sub_block: WhichSubBlock,
857        sck: Option<PeripheralRef<'d, AnyPin>>,
858        mclk: Option<PeripheralRef<'d, AnyPin>>,
859        sd: Option<PeripheralRef<'d, AnyPin>>,
860        fs: Option<PeripheralRef<'d, AnyPin>>,
861        ring_buffer: RingBuffer<'d, W>,
862        config: Config,
863    ) -> Self {
864        let ch = T::REGS.ch(sub_block as usize);
865
866        #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
867        {
868            ch.cr1().modify(|w| w.set_saien(false));
869        }
870
871        ch.cr2().modify(|w| w.set_fflush(true));
872
873        #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))]
874        {
875            if let SyncInput::External(i) = config.sync_input {
876                T::REGS.gcr().modify(|w| {
877                    w.set_syncin(i as u8);
878                });
879            }
880
881            if config.sync_output {
882                let syncout: u8 = match sub_block {
883                    WhichSubBlock::A => 0b01,
884                    WhichSubBlock::B => 0b10,
885                };
886                T::REGS.gcr().modify(|w| {
887                    w.set_syncout(syncout);
888                });
889            }
890        }
891
892        #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
893        {
894            ch.cr1().modify(|w| {
895                w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) {
896                    TxRx::Transmitter
897                } else {
898                    TxRx::Receiver
899                }));
900                w.set_prtcfg(config.protocol.prtcfg());
901                w.set_ds(config.data_size.ds());
902                w.set_lsbfirst(config.bit_order.lsbfirst());
903                w.set_ckstr(config.clock_strobe.ckstr());
904                w.set_syncen(config.sync_input.syncen());
905                w.set_mono(config.stereo_mono.mono());
906                w.set_outdriv(config.output_drive.outdriv());
907                w.set_mckdiv(config.master_clock_divider.mckdiv());
908                w.set_nodiv(
909                    if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
910                        vals::Nodiv::NO_DIV
911                    } else {
912                        vals::Nodiv::MASTER_CLOCK
913                    },
914                );
915                w.set_dmaen(true);
916            });
917
918            ch.cr2().modify(|w| {
919                w.set_fth(config.fifo_threshold.fth());
920                w.set_comp(config.companding.comp());
921                w.set_cpl(config.complement_format.cpl());
922                w.set_muteval(config.mute_value.muteval());
923                w.set_mutecnt(config.mute_detection_counter.0 as u8);
924                w.set_tris(config.is_high_impedance_on_inactive_slot);
925            });
926
927            ch.frcr().modify(|w| {
928                w.set_fsoff(config.frame_sync_offset.fsoff());
929                w.set_fspol(config.frame_sync_polarity.fspol());
930                w.set_fsdef(config.frame_sync_definition.fsdef());
931                w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1);
932                w.set_frl(config.frame_length - 1);
933            });
934
935            ch.slotr().modify(|w| {
936                w.set_nbslot(config.slot_count.0 as u8 - 1);
937                w.set_slotsz(config.slot_size.slotsz());
938                w.set_fboff(config.first_bit_offset.0 as u8);
939                w.set_sloten(vals::Sloten(config.slot_enable as u16));
940            });
941
942            ch.cr1().modify(|w| w.set_saien(true));
943
944            if ch.cr1().read().saien() == false {
945                panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)");
946            }
947        }
948
949        Self {
950            _peri: peri.into_ref(),
951            sub_block,
952            sck,
953            mclk,
954            sd,
955            fs,
956            ring_buffer,
957        }
958    }
959
960    /// Start the SAI driver.
961    ///
962    /// Only receivers can be started. Transmitters are started on the first writing operation.
963    pub fn start(&mut self) -> Result<(), Error> {
964        match self.ring_buffer {
965            RingBuffer::Writable(_) => Err(Error::NotAReceiver),
966            RingBuffer::Readable(ref mut rb) => {
967                rb.start();
968                Ok(())
969            }
970        }
971    }
972
973    fn is_transmitter(ring_buffer: &RingBuffer<W>) -> bool {
974        match ring_buffer {
975            RingBuffer::Writable(_) => true,
976            _ => false,
977        }
978    }
979
980    /// Reset SAI operation.
981    pub fn reset() {
982        rcc::enable_and_reset::<T>();
983    }
984
985    /// Enable or disable mute.
986    pub fn set_mute(&mut self, value: bool) {
987        let ch = T::REGS.ch(self.sub_block as usize);
988        ch.cr2().modify(|w| w.set_mute(value));
989    }
990
991    /// Determine the mute state of the receiver.
992    ///
993    /// Clears the mute state flag in the status register.
994    pub fn is_muted(&self) -> Result<bool, Error> {
995        match &self.ring_buffer {
996            RingBuffer::Readable(_) => {
997                let ch = T::REGS.ch(self.sub_block as usize);
998                let mute_state = ch.sr().read().mutedet();
999                ch.clrfr().write(|w| w.set_cmutedet(true));
1000                Ok(mute_state)
1001            }
1002            _ => Err(Error::NotAReceiver),
1003        }
1004    }
1005
1006    /// Wait until any SAI write error occurs.
1007    ///
1008    /// One useful application for this is stopping playback as soon as the SAI
1009    /// experiences an overrun of the ring buffer. Then, instead of letting
1010    /// the SAI peripheral play the last written buffer over and over again, SAI
1011    /// can be muted or dropped instead.
1012    pub async fn wait_write_error(&mut self) -> Result<(), Error> {
1013        match &mut self.ring_buffer {
1014            RingBuffer::Writable(buffer) => {
1015                buffer.wait_write_error().await?;
1016                Ok(())
1017            }
1018            _ => return Err(Error::NotATransmitter),
1019        }
1020    }
1021
1022    /// Write data to the SAI ringbuffer.
1023    ///
1024    /// The first write starts the DMA after filling the ring buffer with the provided data.
1025    /// This ensures that the DMA does not run before data is available in the ring buffer.
1026    ///
1027    /// This appends the data to the buffer and returns immediately. The
1028    /// data will be transmitted in the background.
1029    ///
1030    /// If there's no space in the buffer, this waits until there is.
1031    pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
1032        match &mut self.ring_buffer {
1033            RingBuffer::Writable(buffer) => {
1034                if buffer.is_running() {
1035                    buffer.write_exact(data).await?;
1036                } else {
1037                    buffer.write_immediate(data)?;
1038                    buffer.start();
1039                }
1040                Ok(())
1041            }
1042            _ => return Err(Error::NotATransmitter),
1043        }
1044    }
1045
1046    /// Read data from the SAI ringbuffer.
1047    ///
1048    /// SAI is always receiving data in the background. This function pops already-received
1049    /// data from the buffer.
1050    ///
1051    /// If there's less than `data.len()` data in the buffer, this waits until there is.
1052    pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
1053        match &mut self.ring_buffer {
1054            RingBuffer::Readable(buffer) => {
1055                buffer.read_exact(data).await?;
1056                Ok(())
1057            }
1058            _ => Err(Error::NotAReceiver),
1059        }
1060    }
1061}
1062
1063impl<'d, T: Instance, W: word::Word> Drop for Sai<'d, T, W> {
1064    fn drop(&mut self) {
1065        let ch = T::REGS.ch(self.sub_block as usize);
1066        ch.cr1().modify(|w| w.set_saien(false));
1067        ch.cr2().modify(|w| w.set_fflush(true));
1068        self.fs.as_ref().map(|x| x.set_as_disconnected());
1069        self.sd.as_ref().map(|x| x.set_as_disconnected());
1070        self.sck.as_ref().map(|x| x.set_as_disconnected());
1071        self.mclk.as_ref().map(|x| x.set_as_disconnected());
1072    }
1073}
1074
1075trait SealedInstance {
1076    const REGS: Regs;
1077}
1078
1079#[derive(Copy, Clone)]
1080enum WhichSubBlock {
1081    A = 0,
1082    B = 1,
1083}
1084
1085trait SealedSubBlock {
1086    const WHICH: WhichSubBlock;
1087}
1088
1089/// Sub-block instance trait.
1090#[allow(private_bounds)]
1091pub trait SubBlockInstance: SealedSubBlock {}
1092
1093/// Sub-block A.
1094pub enum A {}
1095impl SealedSubBlock for A {
1096    const WHICH: WhichSubBlock = WhichSubBlock::A;
1097}
1098impl SubBlockInstance for A {}
1099
1100/// Sub-block B.
1101pub enum B {}
1102impl SealedSubBlock for B {
1103    const WHICH: WhichSubBlock = WhichSubBlock::B;
1104}
1105impl SubBlockInstance for B {}
1106
1107/// SAI instance trait.
1108#[allow(private_bounds)]
1109pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {}
1110
1111pin_trait!(SckPin, Instance, SubBlockInstance);
1112pin_trait!(FsPin, Instance, SubBlockInstance);
1113pin_trait!(SdPin, Instance, SubBlockInstance);
1114pin_trait!(MclkPin, Instance, SubBlockInstance);
1115
1116dma_trait!(Dma, Instance, SubBlockInstance);
1117
1118foreach_peripheral!(
1119    (sai, $inst:ident) => {
1120        impl SealedInstance for peripherals::$inst {
1121            const REGS: Regs = crate::pac::$inst;
1122        }
1123
1124        impl Instance for peripherals::$inst {}
1125    };
1126);