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::PeripheralType;
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, Peri};
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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v3_2pdm, sai_v3_4pdm, 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_v3_2pdm, sai_v3_4pdm, 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_v3_2pdm, sai_v3_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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_v1_4pdm, 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 nodiv: bool,
606    pub is_high_impedance_on_inactive_slot: bool,
607    pub fifo_threshold: FifoThreshold,
608    pub companding: Companding,
609    pub complement_format: ComplementFormat,
610    pub mute_value: MuteValue,
611    pub mute_detection_counter: word::U5,
612}
613
614impl Default for Config {
615    fn default() -> Self {
616        Self {
617            mode: Mode::Master,
618            tx_rx: TxRx::Transmitter,
619            sync_output: false,
620            sync_input: SyncInput::None,
621            protocol: Protocol::Free,
622            slot_size: SlotSize::DataSize,
623            slot_count: word::U4(2),
624            first_bit_offset: word::U5(0),
625            slot_enable: 0b11,
626            data_size: DataSize::Data16,
627            stereo_mono: StereoMono::Stereo,
628            bit_order: BitOrder::LsbFirst,
629            frame_sync_offset: FrameSyncOffset::BeforeFirstBit,
630            frame_sync_polarity: FrameSyncPolarity::ActiveLow,
631            frame_sync_active_level_length: word::U7(16),
632            frame_sync_definition: FrameSyncDefinition::ChannelIdentification,
633            frame_length: 32,
634            master_clock_divider: MasterClockDivider::MasterClockDisabled,
635            nodiv: false,
636            clock_strobe: ClockStrobe::Rising,
637            output_drive: OutputDrive::Immediately,
638            is_high_impedance_on_inactive_slot: false,
639            fifo_threshold: FifoThreshold::ThreeQuarters,
640            companding: Companding::None,
641            complement_format: ComplementFormat::TwosComplement,
642            mute_value: MuteValue::Zero,
643            mute_detection_counter: word::U5(4),
644        }
645    }
646}
647
648impl Config {
649    /// Create a new config with all default values.
650    pub fn new() -> Self {
651        return Default::default();
652    }
653}
654
655#[cfg(not(gpdma))]
656enum RingBuffer<'d, W: word::Word> {
657    Writable(WritableRingBuffer<'d, W>),
658    Readable(ReadableRingBuffer<'d, W>),
659}
660
661fn dr<W: word::Word>(w: crate::pac::sai::Sai, sub_block: WhichSubBlock) -> *mut W {
662    let ch = w.ch(sub_block as usize);
663    ch.dr().as_ptr() as _
664}
665
666// return the type for (sd, sck)
667fn get_af_types(mode: Mode, tx_rx: TxRx) -> (AfType, AfType) {
668    (
669        //sd is defined by tx/rx mode
670        match tx_rx {
671            TxRx::Transmitter => AfType::output(OutputType::PushPull, Speed::VeryHigh),
672            TxRx::Receiver => AfType::input(Pull::Down), // Ensure mute level when no input is connected.
673        },
674        //clocks (mclk, sck and fs) are defined by master/slave
675        match mode {
676            Mode::Master => AfType::output(OutputType::PushPull, Speed::VeryHigh),
677            Mode::Slave => AfType::input(Pull::Down), // Ensure no clocks when no input is connected.
678        },
679    )
680}
681
682#[cfg(not(gpdma))]
683fn get_ring_buffer<'d, T: Instance, W: word::Word>(
684    dma: Peri<'d, impl Channel>,
685    dma_buf: &'d mut [W],
686    request: Request,
687    sub_block: WhichSubBlock,
688    tx_rx: TxRx,
689) -> RingBuffer<'d, W> {
690    let opts = TransferOptions {
691        half_transfer_ir: true,
692        //the new_write() and new_read() always use circular mode
693        ..Default::default()
694    };
695    match tx_rx {
696        TxRx::Transmitter => RingBuffer::Writable(unsafe {
697            WritableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
698        }),
699        TxRx::Receiver => RingBuffer::Readable(unsafe {
700            ReadableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
701        }),
702    }
703}
704
705fn update_synchronous_config(config: &mut Config) {
706    config.mode = Mode::Slave;
707    config.sync_output = false;
708
709    #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2))]
710    {
711        config.sync_input = SyncInput::Internal;
712    }
713
714    #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
715    {
716        //this must either be Internal or External
717        //The asynchronous sub-block on the same SAI needs to enable sync_output
718        assert!(config.sync_input != SyncInput::None);
719    }
720}
721
722/// SAI subblock instance.
723pub struct SubBlock<'d, T: Instance, S: SubBlockInstance> {
724    peri: Peri<'d, T>,
725    _phantom: PhantomData<S>,
726}
727
728/// Split the main SAIx peripheral into the two subblocks.
729///
730/// You can then create a [`Sai`] driver for each each half.
731pub fn split_subblocks<'d, T: Instance>(peri: Peri<'d, T>) -> (SubBlock<'d, T, A>, SubBlock<'d, T, B>) {
732    rcc::enable_and_reset::<T>();
733
734    (
735        SubBlock {
736            peri: unsafe { peri.clone_unchecked() },
737            _phantom: PhantomData,
738        },
739        SubBlock {
740            peri,
741            _phantom: PhantomData,
742        },
743    )
744}
745
746/// SAI sub-block driver.
747pub struct Sai<'d, T: Instance, W: word::Word> {
748    _peri: Peri<'d, T>,
749    sd: Option<Peri<'d, AnyPin>>,
750    fs: Option<Peri<'d, AnyPin>>,
751    sck: Option<Peri<'d, AnyPin>>,
752    mclk: Option<Peri<'d, AnyPin>>,
753    #[cfg(gpdma)]
754    ring_buffer: PhantomData<W>,
755    #[cfg(not(gpdma))]
756    ring_buffer: RingBuffer<'d, W>,
757    sub_block: WhichSubBlock,
758}
759
760#[cfg(not(gpdma))]
761impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
762    /// Create a new SAI driver in asynchronous mode with MCLK.
763    ///
764    /// You can obtain the [`SubBlock`] with [`split_subblocks`].
765    pub fn new_asynchronous_with_mclk<S: SubBlockInstance>(
766        peri: SubBlock<'d, T, S>,
767        sck: Peri<'d, impl SckPin<T, S>>,
768        sd: Peri<'d, impl SdPin<T, S>>,
769        fs: Peri<'d, impl FsPin<T, S>>,
770        mclk: Peri<'d, impl MclkPin<T, S>>,
771        dma: Peri<'d, impl Channel + Dma<T, S>>,
772        dma_buf: &'d mut [W],
773        mut config: Config,
774    ) -> Self {
775        let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);
776        mclk.set_as_af(mclk.af_num(), ck_af_type);
777
778        if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
779            config.master_clock_divider = MasterClockDivider::Div1;
780        }
781
782        Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config)
783    }
784
785    /// Create a new SAI driver in asynchronous mode without MCLK.
786    ///
787    /// You can obtain the [`SubBlock`] with [`split_subblocks`].
788    pub fn new_asynchronous<S: SubBlockInstance>(
789        peri: SubBlock<'d, T, S>,
790        sck: Peri<'d, impl SckPin<T, S>>,
791        sd: Peri<'d, impl SdPin<T, S>>,
792        fs: Peri<'d, impl FsPin<T, S>>,
793        dma: Peri<'d, impl Channel + Dma<T, S>>,
794        dma_buf: &'d mut [W],
795        config: Config,
796    ) -> Self {
797        let peri = peri.peri;
798
799        let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);
800        sd.set_as_af(sd.af_num(), sd_af_type);
801        sck.set_as_af(sck.af_num(), ck_af_type);
802        fs.set_as_af(fs.af_num(), ck_af_type);
803
804        let sub_block = S::WHICH;
805        let request = dma.request();
806
807        Self::new_inner(
808            peri,
809            sub_block,
810            Some(sck.into()),
811            None,
812            Some(sd.into()),
813            Some(fs.into()),
814            get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx),
815            config,
816        )
817    }
818
819    /// Create a new SAI driver in synchronous mode.
820    ///
821    /// You can obtain the [`SubBlock`] with [`split_subblocks`].
822    pub fn new_synchronous<S: SubBlockInstance>(
823        peri: SubBlock<'d, T, S>,
824        sd: Peri<'d, impl SdPin<T, S>>,
825        dma: Peri<'d, impl Channel + Dma<T, S>>,
826        dma_buf: &'d mut [W],
827        mut config: Config,
828    ) -> Self {
829        update_synchronous_config(&mut config);
830
831        let peri = peri.peri;
832
833        let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx);
834        sd.set_as_af(sd.af_num(), sd_af_type);
835
836        let sub_block = S::WHICH;
837        let request = dma.request();
838
839        Self::new_inner(
840            peri,
841            sub_block,
842            None,
843            None,
844            Some(sd.into()),
845            None,
846            get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx),
847            config,
848        )
849    }
850
851    fn new_inner(
852        peri: Peri<'d, T>,
853        sub_block: WhichSubBlock,
854        sck: Option<Peri<'d, AnyPin>>,
855        mclk: Option<Peri<'d, AnyPin>>,
856        sd: Option<Peri<'d, AnyPin>>,
857        fs: Option<Peri<'d, AnyPin>>,
858        ring_buffer: RingBuffer<'d, W>,
859        config: Config,
860    ) -> Self {
861        let ch = T::REGS.ch(sub_block as usize);
862
863        #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
864        {
865            ch.cr1().modify(|w| w.set_saien(false));
866        }
867
868        ch.cr2().modify(|w| w.set_fflush(true));
869
870        #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
871        {
872            if let SyncInput::External(i) = config.sync_input {
873                T::REGS.gcr().modify(|w| {
874                    w.set_syncin(i as u8);
875                });
876            }
877
878            if config.sync_output {
879                let syncout: u8 = match sub_block {
880                    WhichSubBlock::A => 0b01,
881                    WhichSubBlock::B => 0b10,
882                };
883                T::REGS.gcr().modify(|w| {
884                    w.set_syncout(syncout);
885                });
886            }
887        }
888
889        #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
890        {
891            ch.cr1().modify(|w| {
892                w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) {
893                    TxRx::Transmitter
894                } else {
895                    TxRx::Receiver
896                }));
897                w.set_prtcfg(config.protocol.prtcfg());
898                w.set_ds(config.data_size.ds());
899                w.set_lsbfirst(config.bit_order.lsbfirst());
900                w.set_ckstr(config.clock_strobe.ckstr());
901                w.set_syncen(config.sync_input.syncen());
902                w.set_mono(config.stereo_mono.mono());
903                w.set_outdriv(config.output_drive.outdriv());
904                w.set_mckdiv(config.master_clock_divider.mckdiv().into());
905                w.set_nodiv(config.nodiv);
906                w.set_dmaen(true);
907            });
908
909            ch.cr2().modify(|w| {
910                w.set_fth(config.fifo_threshold.fth());
911                w.set_comp(config.companding.comp());
912                w.set_cpl(config.complement_format.cpl());
913                w.set_muteval(config.mute_value.muteval());
914                w.set_mutecnt(config.mute_detection_counter.0 as u8);
915                w.set_tris(config.is_high_impedance_on_inactive_slot);
916            });
917
918            ch.frcr().modify(|w| {
919                w.set_fsoff(config.frame_sync_offset.fsoff());
920                w.set_fspol(config.frame_sync_polarity.fspol());
921                w.set_fsdef(config.frame_sync_definition.fsdef());
922                w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1);
923                w.set_frl(config.frame_length - 1);
924            });
925
926            ch.slotr().modify(|w| {
927                w.set_nbslot(config.slot_count.0 as u8 - 1);
928                w.set_slotsz(config.slot_size.slotsz());
929                w.set_fboff(config.first_bit_offset.0 as u8);
930                w.set_sloten(vals::Sloten::from_bits(config.slot_enable as u16));
931            });
932
933            ch.cr1().modify(|w| w.set_saien(true));
934
935            if ch.cr1().read().saien() == false {
936                panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)");
937            }
938        }
939
940        Self {
941            _peri: peri,
942            sub_block,
943            sck,
944            mclk,
945            sd,
946            fs,
947            ring_buffer,
948        }
949    }
950
951    /// Start the SAI driver.
952    ///
953    /// Only receivers can be started. Transmitters are started on the first writing operation.
954    pub fn start(&mut self) -> Result<(), Error> {
955        match self.ring_buffer {
956            RingBuffer::Writable(_) => Err(Error::NotAReceiver),
957            RingBuffer::Readable(ref mut rb) => {
958                rb.start();
959                Ok(())
960            }
961        }
962    }
963
964    fn is_transmitter(ring_buffer: &RingBuffer<W>) -> bool {
965        match ring_buffer {
966            RingBuffer::Writable(_) => true,
967            _ => false,
968        }
969    }
970
971    /// Reset SAI operation.
972    pub fn reset() {
973        rcc::enable_and_reset::<T>();
974    }
975
976    /// Enable or disable mute.
977    pub fn set_mute(&mut self, value: bool) {
978        let ch = T::REGS.ch(self.sub_block as usize);
979        ch.cr2().modify(|w| w.set_mute(value));
980    }
981
982    /// Determine the mute state of the receiver.
983    ///
984    /// Clears the mute state flag in the status register.
985    pub fn is_muted(&self) -> Result<bool, Error> {
986        match &self.ring_buffer {
987            RingBuffer::Readable(_) => {
988                let ch = T::REGS.ch(self.sub_block as usize);
989                let mute_state = ch.sr().read().mutedet();
990                ch.clrfr().write(|w| w.set_cmutedet(true));
991                Ok(mute_state)
992            }
993            _ => Err(Error::NotAReceiver),
994        }
995    }
996
997    /// Wait until any SAI write error occurs.
998    ///
999    /// One useful application for this is stopping playback as soon as the SAI
1000    /// experiences an overrun of the ring buffer. Then, instead of letting
1001    /// the SAI peripheral play the last written buffer over and over again, SAI
1002    /// can be muted or dropped instead.
1003    pub async fn wait_write_error(&mut self) -> Result<(), Error> {
1004        match &mut self.ring_buffer {
1005            RingBuffer::Writable(buffer) => {
1006                buffer.wait_write_error().await?;
1007                Ok(())
1008            }
1009            _ => return Err(Error::NotATransmitter),
1010        }
1011    }
1012
1013    /// Write data to the SAI ringbuffer.
1014    ///
1015    /// The first write starts the DMA after filling the ring buffer with the provided data.
1016    /// This ensures that the DMA does not run before data is available in the ring buffer.
1017    ///
1018    /// This appends the data to the buffer and returns immediately. The
1019    /// data will be transmitted in the background.
1020    ///
1021    /// If there's no space in the buffer, this waits until there is.
1022    pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
1023        match &mut self.ring_buffer {
1024            RingBuffer::Writable(buffer) => {
1025                if buffer.is_running() {
1026                    buffer.write_exact(data).await?;
1027                } else {
1028                    buffer.write_immediate(data)?;
1029                    buffer.start();
1030                }
1031                Ok(())
1032            }
1033            _ => return Err(Error::NotATransmitter),
1034        }
1035    }
1036
1037    /// Read data from the SAI ringbuffer.
1038    ///
1039    /// SAI is always receiving data in the background. This function pops already-received
1040    /// data from the buffer.
1041    ///
1042    /// If there's less than `data.len()` data in the buffer, this waits until there is.
1043    pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
1044        match &mut self.ring_buffer {
1045            RingBuffer::Readable(buffer) => {
1046                buffer.read_exact(data).await?;
1047                Ok(())
1048            }
1049            _ => Err(Error::NotAReceiver),
1050        }
1051    }
1052}
1053
1054impl<'d, T: Instance, W: word::Word> Drop for Sai<'d, T, W> {
1055    fn drop(&mut self) {
1056        let ch = T::REGS.ch(self.sub_block as usize);
1057        ch.cr1().modify(|w| w.set_saien(false));
1058        ch.cr2().modify(|w| w.set_fflush(true));
1059        self.fs.as_ref().map(|x| x.set_as_disconnected());
1060        self.sd.as_ref().map(|x| x.set_as_disconnected());
1061        self.sck.as_ref().map(|x| x.set_as_disconnected());
1062        self.mclk.as_ref().map(|x| x.set_as_disconnected());
1063    }
1064}
1065
1066trait SealedInstance {
1067    const REGS: Regs;
1068}
1069
1070#[derive(Copy, Clone)]
1071enum WhichSubBlock {
1072    A = 0,
1073    B = 1,
1074}
1075
1076trait SealedSubBlock {
1077    const WHICH: WhichSubBlock;
1078}
1079
1080/// Sub-block instance trait.
1081#[allow(private_bounds)]
1082pub trait SubBlockInstance: SealedSubBlock {}
1083
1084/// Sub-block A.
1085pub enum A {}
1086impl SealedSubBlock for A {
1087    const WHICH: WhichSubBlock = WhichSubBlock::A;
1088}
1089impl SubBlockInstance for A {}
1090
1091/// Sub-block B.
1092pub enum B {}
1093impl SealedSubBlock for B {
1094    const WHICH: WhichSubBlock = WhichSubBlock::B;
1095}
1096impl SubBlockInstance for B {}
1097
1098/// SAI instance trait.
1099#[allow(private_bounds)]
1100pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {}
1101
1102pin_trait!(SckPin, Instance, SubBlockInstance);
1103pin_trait!(FsPin, Instance, SubBlockInstance);
1104pin_trait!(SdPin, Instance, SubBlockInstance);
1105pin_trait!(MclkPin, Instance, SubBlockInstance);
1106
1107dma_trait!(Dma, Instance, SubBlockInstance);
1108
1109foreach_peripheral!(
1110    (sai, $inst:ident) => {
1111        impl SealedInstance for peripherals::$inst {
1112            const REGS: Regs = crate::pac::$inst;
1113        }
1114
1115        impl Instance for peripherals::$inst {}
1116    };
1117);