Skip to main content

esp_hal/dma/engine/
i2s.rs

1use enumset::EnumSet;
2use portable_atomic::Ordering;
3
4use crate::{
5    RegisterToggle,
6    asynch::AtomicWaker,
7    dma::{
8        BurstConfig,
9        DmaChannel,
10        DmaRxChannel,
11        DmaRxInterrupt,
12        DmaTxChannel,
13        DmaTxInterrupt,
14        InterruptAccess,
15        RegisterAccess,
16        RxRegisterAccess,
17        TxRegisterAccess,
18    },
19    interrupt::InterruptHandler,
20    peripherals::Interrupt,
21    system::PeripheralGuard,
22};
23
24/// Immutable per-channel metadata.
25#[doc(hidden)]
26pub struct ChannelInfo {
27    pub(crate) peripheral_interrupt: Interrupt,
28
29    pub(crate) async_handler: InterruptHandler,
30
31    /// Peripheral IDs this channel can serve. An empty slice means no runtime check is needed.
32    pub(crate) compatible_peripherals: &'static [u8],
33}
34
35/// Mutable per-channel runtime state (wakers and async-mode flags).
36pub(crate) struct ChannelState {
37    /// Async waker for the TX (out) half of this channel.
38    pub(crate) tx_waker: AtomicWaker,
39
40    /// Async waker for the RX (in) half of this channel.
41    pub(crate) rx_waker: AtomicWaker,
42
43    /// Whether the TX half is currently in async mode.
44    pub(crate) tx_async_flag: portable_atomic::AtomicBool,
45
46    /// Whether the RX half is currently in async mode.
47    pub(crate) rx_async_flag: portable_atomic::AtomicBool,
48}
49
50pub(super) type I2sRegisterBlock = crate::pac::i2s0::RegisterBlock;
51
52/// The RX half of an arbitrary I2S DMA channel.
53#[derive(Debug)]
54#[cfg_attr(feature = "defmt", derive(defmt::Format))]
55pub struct I2sDmaRxChannel<'d>(pub(crate) I2sDmaChannel<'d>);
56
57impl I2sDmaRxChannel<'_> {
58    fn regs(&self) -> &I2sRegisterBlock {
59        self.0.register_block()
60    }
61}
62
63impl crate::private::Sealed for I2sDmaRxChannel<'_> {}
64impl DmaRxChannel for I2sDmaRxChannel<'_> {}
65
66/// The TX half of an arbitrary I2S DMA channel.
67#[derive(Debug)]
68#[cfg_attr(feature = "defmt", derive(defmt::Format))]
69pub struct I2sDmaTxChannel<'d>(pub(crate) I2sDmaChannel<'d>);
70
71impl I2sDmaTxChannel<'_> {
72    fn regs(&self) -> &I2sRegisterBlock {
73        self.0.register_block()
74    }
75}
76
77impl crate::private::Sealed for I2sDmaTxChannel<'_> {}
78impl DmaTxChannel for I2sDmaTxChannel<'_> {}
79
80impl RegisterAccess for I2sDmaTxChannel<'_> {
81    #[allow(private_interfaces)]
82    fn enable(&self) -> Option<PeripheralGuard> {
83        None
84    }
85
86    fn reset(&self) {
87        self.regs().lc_conf().toggle(|w, bit| w.out_rst().bit(bit));
88    }
89
90    fn set_burst_mode(&self, burst_mode: BurstConfig) {
91        self.regs()
92            .lc_conf()
93            .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled()));
94    }
95
96    fn set_descr_burst_mode(&self, burst_mode: bool) {
97        self.regs()
98            .lc_conf()
99            .modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
100    }
101
102    fn set_link_addr(&self, address: u32) {
103        self.regs()
104            .out_link()
105            .modify(|_, w| unsafe { w.outlink_addr().bits(address) });
106    }
107
108    fn start(&self) {
109        self.regs()
110            .out_link()
111            .modify(|_, w| w.outlink_start().set_bit());
112    }
113
114    fn stop(&self) {
115        self.regs()
116            .out_link()
117            .modify(|_, w| w.outlink_stop().set_bit());
118    }
119
120    fn restart(&self) {
121        self.regs()
122            .out_link()
123            .modify(|_, w| w.outlink_restart().set_bit());
124    }
125
126    fn set_check_owner(&self, check_owner: Option<bool>) {
127        self.regs()
128            .lc_conf()
129            .modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true)));
130    }
131
132    #[cfg(dma_ext_mem_configurable_block_size)]
133    fn set_ext_mem_block_size(&self, size: crate::dma::DmaExtMemBKSize) {
134        self.regs()
135            .lc_conf()
136            .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
137    }
138
139    #[cfg(dma_can_access_psram)]
140    fn can_access_psram(&self) -> bool {
141        matches!(self.0, I2sDmaChannel(any::Inner::I2s0(_)))
142    }
143
144    fn compatible_peripherals(&self) -> &[u8] {
145        self.0.info().compatible_peripherals
146    }
147}
148
149impl TxRegisterAccess for I2sDmaTxChannel<'_> {
150    fn is_fifo_empty(&self) -> bool {
151        cfg_select! {
152            esp32 => self.regs().lc_state0().read().bits() & 0x80000000 != 0,
153            _ => self.regs().lc_state0().read().out_empty().bit_is_set(),
154        }
155    }
156
157    fn set_auto_write_back(&self, enable: bool) {
158        self.regs()
159            .lc_conf()
160            .modify(|_, w| w.out_auto_wrback().bit(enable));
161    }
162
163    fn last_dscr_address(&self) -> usize {
164        self.regs()
165            .out_eof_des_addr()
166            .read()
167            .out_eof_des_addr()
168            .bits() as usize
169    }
170
171    fn peripheral_interrupt(&self) -> Option<Interrupt> {
172        Some(self.0.info().peripheral_interrupt)
173    }
174
175    fn async_handler(&self) -> Option<InterruptHandler> {
176        Some(self.0.info().async_handler)
177    }
178}
179
180impl InterruptAccess<DmaTxInterrupt> for I2sDmaTxChannel<'_> {
181    fn enable_listen(&self, interrupts: EnumSet<DmaTxInterrupt>, enable: bool) {
182        self.regs().int_ena().modify(|_, w| {
183            for interrupt in interrupts {
184                match interrupt {
185                    DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable),
186                    DmaTxInterrupt::DescriptorError => w.out_dscr_err().bit(enable),
187                    DmaTxInterrupt::Eof => w.out_eof().bit(enable),
188                    DmaTxInterrupt::Done => w.out_done().bit(enable),
189                };
190            }
191            w
192        });
193    }
194
195    fn is_listening(&self) -> EnumSet<DmaTxInterrupt> {
196        let mut result = EnumSet::new();
197
198        let int_ena = self.regs().int_ena().read();
199        if int_ena.out_total_eof().bit_is_set() {
200            result |= DmaTxInterrupt::TotalEof;
201        }
202        if int_ena.out_dscr_err().bit_is_set() {
203            result |= DmaTxInterrupt::DescriptorError;
204        }
205        if int_ena.out_eof().bit_is_set() {
206            result |= DmaTxInterrupt::Eof;
207        }
208        if int_ena.out_done().bit_is_set() {
209            result |= DmaTxInterrupt::Done;
210        }
211
212        result
213    }
214
215    fn pending_interrupts(&self) -> EnumSet<DmaTxInterrupt> {
216        let mut result = EnumSet::new();
217
218        let int_raw = self.regs().int_raw().read();
219        if int_raw.out_total_eof().bit_is_set() {
220            result |= DmaTxInterrupt::TotalEof;
221        }
222        if int_raw.out_dscr_err().bit_is_set() {
223            result |= DmaTxInterrupt::DescriptorError;
224        }
225        if int_raw.out_eof().bit_is_set() {
226            result |= DmaTxInterrupt::Eof;
227        }
228        if int_raw.out_done().bit_is_set() {
229            result |= DmaTxInterrupt::Done;
230        }
231
232        result
233    }
234
235    fn clear(&self, interrupts: impl Into<EnumSet<DmaTxInterrupt>>) {
236        self.regs().int_clr().write(|w| {
237            for interrupt in interrupts.into() {
238                match interrupt {
239                    DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(),
240                    DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(),
241                    DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(),
242                    DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(),
243                };
244            }
245            w
246        });
247    }
248
249    fn waker(&self) -> &'static AtomicWaker {
250        &self.0.state().tx_waker
251    }
252
253    fn is_async(&self) -> bool {
254        self.0.state().tx_async_flag.load(Ordering::Relaxed)
255    }
256
257    fn set_async(&self, is_async: bool) {
258        self.0
259            .state()
260            .tx_async_flag
261            .store(is_async, Ordering::Relaxed);
262    }
263}
264
265impl RegisterAccess for I2sDmaRxChannel<'_> {
266    #[allow(private_interfaces)]
267    fn enable(&self) -> Option<PeripheralGuard> {
268        None
269    }
270
271    fn reset(&self) {
272        self.regs().lc_conf().toggle(|w, bit| w.in_rst().bit(bit));
273    }
274
275    fn set_burst_mode(&self, _burst_mode: BurstConfig) {}
276
277    fn set_descr_burst_mode(&self, burst_mode: bool) {
278        self.regs()
279            .lc_conf()
280            .modify(|_, w| w.indscr_burst_en().bit(burst_mode));
281    }
282
283    fn set_link_addr(&self, address: u32) {
284        self.regs()
285            .in_link()
286            .modify(|_, w| unsafe { w.inlink_addr().bits(address) });
287    }
288
289    fn start(&self) {
290        self.regs()
291            .in_link()
292            .modify(|_, w| w.inlink_start().set_bit());
293    }
294
295    fn stop(&self) {
296        self.regs()
297            .in_link()
298            .modify(|_, w| w.inlink_stop().set_bit());
299    }
300
301    fn restart(&self) {
302        self.regs()
303            .in_link()
304            .modify(|_, w| w.inlink_restart().set_bit());
305    }
306
307    fn set_check_owner(&self, check_owner: Option<bool>) {
308        self.regs()
309            .lc_conf()
310            .modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true)));
311    }
312
313    #[cfg(dma_ext_mem_configurable_block_size)]
314    fn set_ext_mem_block_size(&self, size: crate::dma::DmaExtMemBKSize) {
315        self.regs()
316            .lc_conf()
317            .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
318    }
319
320    #[cfg(dma_can_access_psram)]
321    fn can_access_psram(&self) -> bool {
322        matches!(self.0, I2sDmaChannel(any::Inner::I2s0(_)))
323    }
324
325    fn compatible_peripherals(&self) -> &[u8] {
326        self.0.info().compatible_peripherals
327    }
328}
329
330impl RxRegisterAccess for I2sDmaRxChannel<'_> {
331    #[cfg(dma_supports_mem2mem)]
332    fn set_mem2mem_mode(&self, en: bool) {
333        self.regs()
334            .lc_conf()
335            .modify(|_, w| w.mem_trans_en().bit(en));
336    }
337
338    fn peripheral_interrupt(&self) -> Option<Interrupt> {
339        Some(self.0.info().peripheral_interrupt)
340    }
341
342    fn async_handler(&self) -> Option<InterruptHandler> {
343        Some(self.0.info().async_handler)
344    }
345}
346
347impl InterruptAccess<DmaRxInterrupt> for I2sDmaRxChannel<'_> {
348    fn enable_listen(&self, interrupts: EnumSet<DmaRxInterrupt>, enable: bool) {
349        self.regs().int_ena().modify(|_, w| {
350            for interrupt in interrupts {
351                match interrupt {
352                    DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable),
353                    DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable),
354                    DmaRxInterrupt::DescriptorError => w.in_dscr_err().bit(enable),
355                    DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().bit(enable),
356                    DmaRxInterrupt::Done => w.in_done().bit(enable),
357                };
358            }
359            w
360        });
361    }
362
363    fn is_listening(&self) -> EnumSet<DmaRxInterrupt> {
364        let mut result = EnumSet::new();
365
366        let int_ena = self.regs().int_ena().read();
367        if int_ena.in_dscr_err().bit_is_set() {
368            result |= DmaRxInterrupt::DescriptorError;
369        }
370        if int_ena.in_dscr_empty().bit_is_set() {
371            result |= DmaRxInterrupt::DescriptorEmpty;
372        }
373        if int_ena.in_suc_eof().bit_is_set() {
374            result |= DmaRxInterrupt::SuccessfulEof;
375        }
376        if int_ena.in_err_eof().bit_is_set() {
377            result |= DmaRxInterrupt::ErrorEof;
378        }
379        if int_ena.in_done().bit_is_set() {
380            result |= DmaRxInterrupt::Done;
381        }
382
383        result
384    }
385
386    fn pending_interrupts(&self) -> EnumSet<DmaRxInterrupt> {
387        let mut result = EnumSet::new();
388
389        let int_raw = self.regs().int_raw().read();
390        if int_raw.in_dscr_err().bit_is_set() {
391            result |= DmaRxInterrupt::DescriptorError;
392        }
393        if int_raw.in_dscr_empty().bit_is_set() {
394            result |= DmaRxInterrupt::DescriptorEmpty;
395        }
396        if int_raw.in_suc_eof().bit_is_set() {
397            result |= DmaRxInterrupt::SuccessfulEof;
398        }
399        if int_raw.in_err_eof().bit_is_set() {
400            result |= DmaRxInterrupt::ErrorEof;
401        }
402        if int_raw.in_done().bit_is_set() {
403            result |= DmaRxInterrupt::Done;
404        }
405
406        result
407    }
408
409    fn clear(&self, interrupts: impl Into<EnumSet<DmaRxInterrupt>>) {
410        self.regs().int_clr().write(|w| {
411            for interrupt in interrupts.into() {
412                match interrupt {
413                    DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(),
414                    DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(),
415                    DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(),
416                    DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(),
417                    DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(),
418                };
419            }
420            w
421        });
422    }
423
424    fn waker(&self) -> &'static AtomicWaker {
425        &self.0.state().rx_waker
426    }
427
428    fn is_async(&self) -> bool {
429        self.0.state().rx_async_flag.load(Ordering::Relaxed)
430    }
431
432    fn set_async(&self, is_async: bool) {
433        self.0
434            .state()
435            .rx_async_flag
436            .store(is_async, Ordering::Relaxed);
437    }
438}
439
440crate::any_peripheral! {
441    /// An I2S-compatible type-erased DMA channel.
442    pub peripheral I2sDmaChannel<'d> {
443        #[cfg(soc_has_i2s0)]
444        I2s0(DMA_I2S0<'d>),
445        #[cfg(soc_has_i2s1)]
446        I2s1(DMA_I2S1<'d>),
447    }
448}
449
450impl<'d> DmaChannel for I2sDmaChannel<'d> {
451    type Rx = I2sDmaRxChannel<'d>;
452    type Tx = I2sDmaTxChannel<'d>;
453
454    unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) {
455        (
456            I2sDmaRxChannel(unsafe { self.clone_unchecked() }),
457            I2sDmaTxChannel(self),
458        )
459    }
460}
461
462impl I2sDmaChannel<'_> {
463    delegate::delegate! {
464        to match &self.0 {
465            #[cfg(soc_has_i2s0)]
466            any::Inner::I2s0(channel) => channel,
467            #[cfg(soc_has_i2s1)]
468            any::Inner::I2s1(channel) => channel,
469        } {
470            fn register_block(&self) -> &I2sRegisterBlock;
471            fn info(&self) -> &'static ChannelInfo;
472            fn state(&self) -> &'static ChannelState;
473        }
474    }
475}
476
477// Convert erased channel into erased TX/RX half structs
478impl<'d> From<I2sDmaChannel<'d>> for I2sDmaRxChannel<'d> {
479    fn from(this: I2sDmaChannel<'d>) -> I2sDmaRxChannel<'d> {
480        I2sDmaRxChannel(this)
481    }
482}
483
484impl<'d> From<I2sDmaChannel<'d>> for I2sDmaTxChannel<'d> {
485    fn from(this: I2sDmaChannel<'d>) -> I2sDmaTxChannel<'d> {
486        I2sDmaTxChannel(this)
487    }
488}
489
490for_each_dma_channel_peri_pair! {
491    ("I2S_DMA", $dma_peri:ident, $peri:ident) => {
492        use crate::peripherals::$dma_peri;
493        impl $dma_peri<'_> {
494            pub(super) fn info(&self) -> &'static ChannelInfo {
495                #[crate::handler(priority = crate::interrupt::Priority::max())]
496                fn interrupt_handler() {
497                    crate::dma::asynch::handle_in_interrupt::<$dma_peri<'static>>();
498                    crate::dma::asynch::handle_out_interrupt::<$dma_peri<'static>>();
499                }
500
501                static INFO: ChannelInfo = ChannelInfo {
502                    peripheral_interrupt: Interrupt::$peri,
503                    async_handler: interrupt_handler,
504                    compatible_peripherals: &[crate::dma::DmaPeripheral::$peri.0],
505                };
506                &INFO
507            }
508
509            pub(super) fn state(&self) -> &'static ChannelState {
510                static STATE: ChannelState = ChannelState {
511                    tx_waker: AtomicWaker::new(),
512                    rx_waker: AtomicWaker::new(),
513                    tx_async_flag: portable_atomic::AtomicBool::new(false),
514                    rx_async_flag: portable_atomic::AtomicBool::new(false),
515                };
516                &STATE
517            }
518        }
519
520        crate::dma::impl_channel_common!(I2sDma, $dma_peri);
521    };
522}