1
#![cfg_attr(docsrs, procmacros::doc_replace(
2
    "dma_channel" => {
3
        cfg(i2s_dma_engine = "I2S_DMA")  => "DMA_I2S0",
4
        _ => "DMA_CH0",
5
    },
6
    "mclk" => {
7
        cfg(not(esp32)) => "let i2s = i2s.with_mclk(peripherals.GPIO0);",
8
        _ => ""
9
    },
10
    "tdm_slot_philips" => include_str!("../tdm_slot_philips.svg"),
11
    "tdm_slot_msb" => include_str!("../tdm_slot_msb.svg"),
12
    "tdm_slot_pcm_short" => include_str!("../tdm_slot_pcm_short.svg"),
13
    "tdm_slot_pcm_long" => include_str!("../tdm_slot_pcm_long.svg"),
14
))]
15
//! # Inter-IC Sound (I2S)
16
//!
17
//! ## Overview
18
//!
19
//! I2S (Inter-IC Sound) is a synchronous serial communication protocol usually
20
//! used for transmitting audio data between two digital audio devices.
21
//! Espressif devices may contain more than one I2S peripheral(s). These
22
//! peripherals can be configured to input and output sample data via the I2S
23
//! driver.
24
//!
25
//! ## Configuration
26
//!
27
//! I2S supports different data formats, including varying data and channel
28
//! widths, different standards, such as the Philips standard and configurable
29
//! pin mappings for I2S clock (BCLK), word select (WS), and data input/output
30
//! (DOUT/DIN).
31
//!
32
//! The driver uses DMA (Direct Memory Access) for efficient data transfer and
33
//! supports various configurations, such as different data formats, standards
34
//! (e.g., Philips) and pin configurations. It relies on other peripheral
35
//! modules, such as
36
//!   - `GPIO`
37
//!   - `DMA`
38
//!   - `system` (to configure and enable the I2S peripheral)
39
//!
40
//! ### Standards
41
//!
42
//! I2S supports different standards, which you can access using [`TdmConfig::new_tdm_philips`]
43
//! and related helpers. You can also configure custom data formats using methods such as
44
//! [`TdmConfig::with_msb_shift`], [`TdmConfig::with_ws_width`], [`TdmConfig::with_ws_polarity`],
45
//! etc.
46
//!
47
//! In TDM mode, WS (word select, sometimes called LRCLK or left/right clock) becomes a frame
48
//! synchronization signal that signals the first slot of a frame. The two sides of the TDM link
49
//! must agree on the number of channels, data bit width, and frame synchronization pattern; this
50
//! cannot be determined by examining the signal itself.
51
//!
52
//! #### TDM Philips Standard
53
//!
54
//! TDM Philips mode pulls the WS line low one BCK period before the first data bit of the first
55
//! slot is sent and holds it low for 50% of the frame.
56
//!
57
//! # {tdm_slot_philips}
58
//!
59
//! #### TDM MSB Standard
60
//!
61
//! MSB (most-significant bit) mode is similar to Philips mode, except the WS line is pulled low at
62
//! the same time the first data bit of the first slot is sent. It is held low for 50% of the frame.
63
//!
64
//! # {tdm_slot_msb}
65
//!
66
//! #### TDM PCM Short Standard
67
//!
68
//! PCM (pulse-code modulation) short mode pulls the WS line *high* one BCK period before the first
69
//! data bit of the first slot is sent, keeps it high for one BCK, then pulls it low for the
70
//! remainder of the frame.
71
//!
72
//! # {tdm_slot_pcm_short}
73
//!
74
//! #### TDM PCM Long Standard
75
//!
76
//! PCM long mode pulls the WS line *high* one BCK period before the first data bit of the first
77
//! slot is sent, keeps it high until just before the last data bit of the first slot is sent, then
78
//! pulls it low for the remainder of the frame.
79
//!
80
//! # {tdm_slot_pcm_long}
81
//!
82
//! Diagrams from _ESP-IDF Programming Guide_; rendered by Wavedrom.
83
//!
84
//! ## Examples
85
//!
86
//! ### I2S Read
87
//!
88
//! ```rust, no_run
89
//! # {before_snippet}
90
//! # use esp_hal::i2s::master::{I2s, Channels, DataFormat, TdmConfig};
91
//! # use esp_hal::dma_rx_stream_buffer;
92
//! let rx_buffer = dma_rx_stream_buffer!(4 * 4092, 1024);
93
//!
94
//! let i2s = I2s::new(
95
//!     peripherals.I2S0,
96
//!     peripherals.__dma_channel__,
97
//!     TdmConfig::new_tdm_philips()
98
//!         .with_sample_rate(Rate::from_hz(44100))
99
//!         .with_data_format(DataFormat::Data16Channel16)
100
//!         .with_channels(Channels::STEREO),
101
//! )?;
102
//! # {mclk}
103
//! let mut i2s_rx = i2s
104
//!     .i2s_rx
105
//!     .with_bclk(peripherals.GPIO1)
106
//!     .with_ws(peripherals.GPIO2)
107
//!     .with_din(peripherals.GPIO5)
108
//!     .build();
109
//!
110
//! let mut transfer = i2s_rx.read(rx_buffer)?;
111
//!
112
//! loop {
113
//!     let avail = transfer.available_bytes();
114
//!
115
//!     if avail > 0 {
116
//!         let mut rcv = [0u8; 5000];
117
//!         transfer.pop(&mut rcv[..avail]);
118
//!     }
119
//! }
120
//! # }
121
//! ```
122
#![cfg_attr(
123
    any(i2s_supports_pdm_tx, i2s_supports_pdm_rx),
124
    doc = r"## PDM mode
125

            
126
PDM (pulse-density modulation) is supported on I2S instances where the hardware
127
provides PDM (see per-instance metadata). Use [`I2s::new_pdm`] with [`PdmConfig`].
128
Hardware PCM-to-PDM / PDM-to-PCM conversion is only available on instances that
129
support it (typically I2S0); other instances require [`PdmDataFormat::Raw`].
130
PDM uses a single clock pin (`with_clk` on [`I2s::i2s_tx`] / [`I2s::i2s_rx`])
131
instead of separate BCLK and WS lines. Only simplex operation (TX *or* RX) is
132
supported.
133

            
134
```rust, no_run
135
# {before_snippet}
136
use esp_hal::i2s::master::{I2s, PdmSlotMode, PdmTxConfig, PdmConfig};
137
use esp_hal::time::Rate;
138

            
139
let pdm = PdmConfig::tx_only(PdmTxConfig::new_codec_default(
140
    Rate::from_hz(16_000),
141
    PdmSlotMode::Mono,
142
));
143
let i2s = I2s::new_pdm(peripherals.I2S0, peripherals.__dma_channel__, pdm)?;
144
# {after_snippet}
145
```
146
"
147
)]
148

            
149
use core::mem::ManuallyDrop;
150

            
151
use enumset::{EnumSet, EnumSetType, enum_set};
152
use private::*;
153

            
154
mod low_level;
155

            
156
#[doc(hidden)]
157
pub use low_level::Info;
158

            
159
#[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
160
pub use super::pdm::{
161
    PdmConfig,
162
    PdmDataFormat,
163
    PdmError,
164
    PdmInstance,
165
    PdmRxConfig,
166
    PdmSlotMode,
167
    PdmTxConfig,
168
};
169
/// Clock source for the I2S TX/RX module clocks (after the source mux and MCLK divider).
170
#[cfg(not(i2s_version = "1"))]
171
pub use crate::clock::ll::I2sClkSclk as I2sClockSource;
172
/// Selects whether the MCLK pad outputs the TX or RX module clock.
173
#[cfg(not(i2s_version = "1"))]
174
pub use crate::clock::ll::I2sMclkOutConfig as MclkOut;
175
/// Clock source for the I2S module clock (after the source mux and MCLK divider).
176
#[cfg(i2s_version = "1")]
177
pub use crate::clock::ll::I2sMclkSclk as I2sClockSource;
178
use crate::{
179
    Async,
180
    Blocking,
181
    DriverMode,
182
    clock::dividers::FractionalDivider,
183
    dma::{
184
        Channel,
185
        ChannelRx,
186
        ChannelTx,
187
        DmaChannel,
188
        DmaEligiblePeripheral,
189
        DmaError,
190
        DmaRxBuffer,
191
        DmaRxInterrupt,
192
        DmaTxBuffer,
193
        DmaTxInterrupt,
194
        asynch::{DmaRxFuture, DmaTxFuture},
195
    },
196
    gpio::{
197
        InputConfig,
198
        InputSignal,
199
        OutputConfig,
200
        OutputSignal,
201
        interconnect::{PeripheralInput, PeripheralOutput},
202
    },
203
    i2s::{AnyI2s, any::Inner as AnyI2sInner},
204
    interrupt::{InterruptConfigurable, InterruptHandler},
205
    pac::i2s0::RegisterBlock,
206
    system::PeripheralGuard,
207
    time::Rate,
208
};
209

            
210
/// Returns the frequency of a module clock source, in Hz.
211
///
212
/// The transmitter and the receiver select their source from the same set of clocks, so one
213
/// lookup describes both directions.
214
pub(crate) fn source_frequency(source: I2sClockSource) -> u32 {
215
    cfg_select! {
216
        i2s_version = "1" => crate::clock::ll::I2sInstance::mclk_source_frequency(source),
217
        _ => crate::clock::ll::I2sInstance::tx_clk_source_frequency(source),
218
    }
219
}
220

            
221
/// Returns the largest A coefficient the module-clock divider can hold.
222
pub(crate) fn mclk_max_denominator() -> u32 {
223
    cfg_select! {
224
        i2s_version = "1" => {
225
            let (_, max) = property!("clock_tree.i2s.mclk.div_a");
226
            max
227
        }
228
        _ => {
229
            let (_, max) = property!("clock_tree.i2s.tx_clk.div_a");
230
            max
231
        }
232
    }
233
}
234

            
235
#[derive(Debug, EnumSetType)]
236
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
237
/// Represents the various interrupt types for the I2S peripheral.
238
pub enum I2sInterrupt {
239
    /// Receive buffer hung, indicating a stall in data reception.
240
    RxHung,
241

            
242
    /// Transmit buffer hung, indicating a stall in data transmission.
243
    TxHung,
244

            
245
    #[cfg(not(i2s_version = "1"))]
246
    /// Reception of data is complete.
247
    RxDone,
248

            
249
    #[cfg(not(i2s_version = "1"))]
250
    /// Transmission of data is complete.
251
    TxDone,
252
}
253

            
254
with_i2s_dma_engine! {
255
    ($engine:tt, $any_channel:ident) => {
256
        /// DMA channel trait for I2S master peripherals.
257
        ///
258
        /// Implemented for each channel type that can serve a particular I2S instance `S`.
259
        #[instability::unstable]
260
        #[diagnostic::on_unimplemented(
261
            message = "The DMA channel cannot be used with this I2S peripheral",
262
            label = "This DMA channel",
263
            note = "Use a channel that matches the I2S instance."
264
        )]
265
        pub trait I2sMasterDmaChannel<'d, S>: crate::private::Sealed + Into<crate::dma::$any_channel<'d>> {}
266

            
267
        crate::macros::impl_dma_channel_trait! {
268
            $engine,
269
            any_peri = AnyI2s<'d>,
270
            peris = for_each_i2s,
271
            ($peri:path, $ch:path) => {
272
                impl<'d> I2sMasterDmaChannel<'d, $peri> for $ch {}
273
            }
274
        }
275

            
276
        // Proxy type so that the type-erased DMA channel can be named in the driver, regardless of the DMA engine.
277
        type I2sMasterErased<'d> = crate::dma::$any_channel<'d>;
278
    };
279
}
280

            
281
impl<'d> I2s<'d, crate::Blocking> {
282
    /// Creates a new I2S instance in TDM mode.
283
    pub fn new<I: Instance + 'd>(
284
        i2s: I,
285
        channel: impl I2sMasterDmaChannel<'d, I>,
286
        config: TdmConfig,
287
    ) -> Result<Self, ConfigError> {
288
15
        Self::new_internal(i2s, channel.into(), Config::Tdm(config))
289
    }
290

            
291
    /// Creates a new I2S instance in PDM mode.
292
    #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
293
    pub fn new_pdm<I: Instance + PdmInstance + 'd>(
294
        i2s: I,
295
        channel: impl I2sMasterDmaChannel<'d, I>,
296
        config: PdmConfig,
297
    ) -> Result<Self, ConfigError> {
298
1
        Self::new_internal(i2s, channel.into(), Config::Pdm(config))
299
    }
300
}
301

            
302
/// A structure representing a DMA transfer.
303
///
304
/// Holds references to the driver instance, DMA buffers, and transfer status.
305
#[instability::unstable]
306
pub struct I2sTxDmaTransfer<'d, Dm, Buf>
307
where
308
    Dm: DriverMode,
309
    Buf: DmaTxBuffer,
310
{
311
    i2s_tx: ManuallyDrop<I2sTx<'d, Dm>>,
312
    buffer_view: ManuallyDrop<Buf::View>,
313
    completed: bool,
314
}
315

            
316
impl<'d, Dm, Buf> I2sTxDmaTransfer<'d, Dm, Buf>
317
where
318
    Dm: DriverMode,
319
    Buf: DmaTxBuffer,
320
{
321
    /// Returns whether [`Self::wait`] will not block.
322
    pub fn is_done(&self) -> bool {
323
        self.completed || self.i2s_tx.i2s.info().is_tx_done()
324
    }
325

            
326
    /// Waits for the transfer to finish and returns the peripheral and buffer.
327
    pub fn wait(self) -> (Result<(), DmaError>, I2sTx<'d, Dm>, Buf::Final) {
328
        while !self.is_done() {}
329
        let (i2s_tx, buf) = self.stop();
330

            
331
        if i2s_tx.tx_channel.has_error() {
332
            (Err(DmaError::DescriptorError), i2s_tx, buf)
333
        } else {
334
            (Ok(()), i2s_tx, buf)
335
        }
336
    }
337

            
338
    /// Immediately stops the transfer and returns the peripheral and buffer.
339
    pub fn stop(mut self) -> (I2sTx<'d, Dm>, Buf::Final) {
340
        self.i2s_tx.tx_channel.stop_transfer();
341
        self.i2s_tx.i2s.info().tx_stop();
342

            
343
        let (i2s_tx, buf) = self.release();
344

            
345
        (i2s_tx, Buf::from_view(buf))
346
    }
347

            
348
    fn release(mut self) -> (I2sTx<'d, Dm>, Buf::View) {
349
        let (i2s_tx, buffer_view) = unsafe {
350
            (
351
                ManuallyDrop::take(&mut self.i2s_tx),
352
                ManuallyDrop::take(&mut self.buffer_view),
353
            )
354
        };
355

            
356
        core::mem::forget(self);
357
        (i2s_tx, buffer_view)
358
    }
359
}
360

            
361
impl<'d, Buf> I2sTxDmaTransfer<'d, Async, Buf>
362
where
363
    Buf: DmaTxBuffer,
364
{
365
    /// Waits for the transfer to finish and returns the peripheral and buffer.
366
    pub async fn wait_async(mut self) -> (Result<(), DmaError>, I2sTx<'d, Async>, Buf::Final) {
367
        if !self.completed {
368
            if let Err(err) = DmaTxFuture::new(&mut self.i2s_tx.tx_channel).await {
369
                self.i2s_tx.tx_channel.stop_transfer();
370
                self.i2s_tx.i2s.info().tx_stop();
371
                let (i2s_tx, buf) = self.release();
372
                return (Err(err), i2s_tx, Buf::from_view(buf));
373
            }
374
            while !self.is_done() {}
375
            self.completed = true;
376
        }
377

            
378
        let (i2s_tx, buf) = self.stop();
379

            
380
        if i2s_tx.tx_channel.has_error() {
381
            (Err(DmaError::DescriptorError), i2s_tx, buf)
382
        } else {
383
            (Ok(()), i2s_tx, buf)
384
        }
385
    }
386

            
387
    /// Waits for the transfer to finish.
388
    pub async fn wait_for_done_async(&mut self) -> Result<(), DmaError> {
389
        if !self.completed {
390
            DmaTxFuture::new(&mut self.i2s_tx.tx_channel).await?;
391
            while !self.is_done() {}
392
            self.completed = true;
393
        }
394
        Ok(())
395
    }
396

            
397
    /// Waits for a condition that might indicate more data is available.
398
    pub async fn wait_for_available_async(&mut self) -> Result<(), DmaError> {
399
        DmaTxFuture::new_with_config(
400
            &mut self.i2s_tx.tx_channel,
401
            enum_set!(DmaTxInterrupt::Eof),
402
            enum_set!(DmaTxInterrupt::DescriptorError | DmaTxInterrupt::TotalEof),
403
        )
404
        .await
405
    }
406
}
407

            
408
impl<Dm: DriverMode, BUF: DmaTxBuffer> core::ops::Deref for I2sTxDmaTransfer<'_, Dm, BUF> {
409
    type Target = BUF::View;
410

            
411
    fn deref(&self) -> &Self::Target {
412
        &self.buffer_view
413
    }
414
}
415

            
416
impl<Dm: DriverMode, BUF: DmaTxBuffer> core::ops::DerefMut for I2sTxDmaTransfer<'_, Dm, BUF> {
417
    fn deref_mut(&mut self) -> &mut Self::Target {
418
        &mut self.buffer_view
419
    }
420
}
421

            
422
impl<Dm: DriverMode, BUF: DmaTxBuffer> Drop for I2sTxDmaTransfer<'_, Dm, BUF> {
423
    fn drop(&mut self) {
424
        self.i2s_tx.tx_channel.stop_transfer();
425
        self.i2s_tx.i2s.info().tx_stop();
426

            
427
        // SAFETY: This is Drop, we know that the parts are no longer used
428
        let view = unsafe {
429
            ManuallyDrop::drop(&mut self.i2s_tx);
430
            ManuallyDrop::take(&mut self.buffer_view)
431
        };
432
        let _ = BUF::from_view(view);
433
    }
434
}
435

            
436
/// A structure representing a DMA transfer.
437
///
438
/// Holds references to the driver instance, DMA buffers, and transfer status.
439
#[instability::unstable]
440
pub struct I2sRxDmaTransfer<'d, Dm, Buf>
441
where
442
    Dm: DriverMode,
443
    Buf: DmaRxBuffer,
444
{
445
    i2s_rx: ManuallyDrop<I2sRx<'d, Dm>>,
446
    buffer_view: ManuallyDrop<Buf::View>,
447
    completed: bool,
448
}
449

            
450
impl<'d, Dm, Buf> I2sRxDmaTransfer<'d, Dm, Buf>
451
where
452
    Dm: DriverMode,
453
    Buf: DmaRxBuffer,
454
{
455
    /// Returns whether [`Self::wait`] will not block.
456
    pub fn is_done(&self) -> bool {
457
        self.completed || self.i2s_rx.i2s.info().is_rx_done()
458
    }
459

            
460
    /// Waits for the transfer to finish and returns the peripheral and buffer.
461
    pub fn wait(self) -> (Result<(), DmaError>, I2sRx<'d, Dm>, Buf::Final) {
462
        while !self.is_done() {}
463

            
464
        let (i2s_rx, buf) = self.stop();
465

            
466
        if i2s_rx.rx_channel.has_error() {
467
            (Err(DmaError::DescriptorError), i2s_rx, buf)
468
        } else {
469
            (Ok(()), i2s_rx, buf)
470
        }
471
    }
472

            
473
    /// Immediately stops the transfer and returns the peripheral and buffer.
474
    pub fn stop(mut self) -> (I2sRx<'d, Dm>, Buf::Final) {
475
        self.i2s_rx.i2s.info().rx_stop();
476
        self.i2s_rx.rx_channel.stop_transfer();
477

            
478
        let (i2s_rx, buf) = self.release();
479
        (i2s_rx, Buf::from_view(buf))
480
    }
481

            
482
    fn release(mut self) -> (I2sRx<'d, Dm>, Buf::View) {
483
        let (i2s_rx, buffer_view) = unsafe {
484
            (
485
                ManuallyDrop::take(&mut self.i2s_rx),
486
                ManuallyDrop::take(&mut self.buffer_view),
487
            )
488
        };
489

            
490
        core::mem::forget(self);
491
        (i2s_rx, buffer_view)
492
    }
493
}
494

            
495
impl<'d, Buf> I2sRxDmaTransfer<'d, Async, Buf>
496
where
497
    Buf: DmaRxBuffer,
498
{
499
    /// Waits for the transfer to finish and returns the peripheral and buffer.
500
    pub async fn wait_async(mut self) -> (Result<(), DmaError>, I2sRx<'d, Async>, Buf::Final) {
501
        if !self.completed {
502
            // we treat DescriptorEmpty as rx transfer is done
503
            if let Err(err) = DmaRxFuture::new_with_config(
504
                &mut self.i2s_rx.rx_channel,
505
                enum_set!(DmaRxInterrupt::DescriptorEmpty),
506
                enum_set!(DmaRxInterrupt::ErrorEof | DmaRxInterrupt::DescriptorError),
507
            )
508
            .await
509
            {
510
                self.completed = true;
511
                self.i2s_rx.i2s.info().rx_stop();
512
                self.i2s_rx.rx_channel.stop_transfer();
513
                let (i2s_rx, buf) = self.release();
514
                return (Err(err), i2s_rx, Buf::from_view(buf));
515
            }
516
            self.completed = true;
517
        }
518

            
519
        let (i2s_rx, buf) = self.stop();
520

            
521
        if i2s_rx.rx_channel.has_error() {
522
            (Err(DmaError::DescriptorError), i2s_rx, buf)
523
        } else {
524
            (Ok(()), i2s_rx, buf)
525
        }
526
    }
527

            
528
    /// Waits for the transfer to finish and returns the peripheral and buffer.
529
    pub async fn wait_for_done_async(&mut self) -> Result<(), DmaError> {
530
        if !self.completed {
531
            // we treat DescriptorEmpty as rx transfer is done
532
            DmaRxFuture::new_with_config(
533
                &mut self.i2s_rx.rx_channel,
534
                enum_set!(DmaRxInterrupt::DescriptorEmpty),
535
                enum_set!(DmaRxInterrupt::ErrorEof | DmaRxInterrupt::DescriptorError),
536
            )
537
            .await?;
538
            self.completed = true;
539
        }
540
        Ok(())
541
    }
542

            
543
    /// Waits for a condition that might indicate more data is available.
544
    pub async fn wait_for_available_async(&mut self) -> Result<(), DmaError> {
545
        DmaRxFuture::new_with_config(
546
            &mut self.i2s_rx.rx_channel,
547
            enum_set!(DmaRxInterrupt::SuccessfulEof | DmaRxInterrupt::Done),
548
            enum_set!(
549
                DmaRxInterrupt::ErrorEof
550
                    | DmaRxInterrupt::DescriptorError
551
                    | DmaRxInterrupt::DescriptorEmpty
552
            ),
553
        )
554
        .await
555
    }
556
}
557

            
558
impl<Dm: DriverMode, BUF: DmaRxBuffer> core::ops::Deref for I2sRxDmaTransfer<'_, Dm, BUF> {
559
    type Target = BUF::View;
560

            
561
    fn deref(&self) -> &Self::Target {
562
        &self.buffer_view
563
    }
564
}
565

            
566
impl<Dm: DriverMode, BUF: DmaRxBuffer> core::ops::DerefMut for I2sRxDmaTransfer<'_, Dm, BUF> {
567
    fn deref_mut(&mut self) -> &mut Self::Target {
568
        &mut self.buffer_view
569
    }
570
}
571

            
572
impl<Dm: DriverMode, BUF: DmaRxBuffer> Drop for I2sRxDmaTransfer<'_, Dm, BUF> {
573
    fn drop(&mut self) {
574
        self.i2s_rx.rx_channel.stop_transfer();
575
        self.i2s_rx.i2s.info().rx_stop();
576

            
577
        // SAFETY: This is Drop, we know that the parts are no longer used
578
        let view = unsafe {
579
            ManuallyDrop::drop(&mut self.i2s_rx);
580
            ManuallyDrop::take(&mut self.buffer_view)
581
        };
582
        let _ = BUF::from_view(view);
583
    }
584
}
585

            
586
/// I2S Error
587
#[derive(Debug, Clone, Copy, PartialEq)]
588
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
589
#[allow(clippy::enum_variant_names, reason = "peripheral is unstable")]
590
pub enum Error {
591
    /// An unspecified or unknown error occurred during an I2S operation.
592
    Unknown,
593
    /// A DMA-related error occurred during I2S operations.
594
    DmaError(DmaError),
595
    /// An illegal or invalid argument was passed to an I2S function or method.
596
    IllegalArgument,
597
}
598

            
599
impl From<DmaError> for Error {
600
    fn from(value: DmaError) -> Self {
601
        Error::DmaError(value)
602
    }
603
}
604

            
605
/// Supported I2S data formats.
606
///
607
/// Each format defines two dimensions:
608
///
609
/// - **`Data{X}` (on-wire slot width)**: The number of bit-clock (BCLK) cycles allocated to each
610
///   channel slot on the physical line ([`Self::data_bits`]). This determines the BCLK frequency
611
///   (`BCLK = sample_rate * channels * data_bits`) and the time duration of each channel slot.
612
/// - **`Channel{Y}` (in-memory sample width)**: The number of active audio data bits per sample
613
///   transferred to or from memory via DMA ([`Self::channel_bits`]).
614
///
615
/// ### In-Memory Representation
616
///
617
/// In the DMA memory buffer, samples are stored with a size corresponding to `Channel{Y}`:
618
/// - 32-bit (`Channel32`): 4 bytes per sample (e.g. `i32` / `u32`).
619
/// - 24-bit (`Channel24`): 3 bytes per sample (packed 24-bit PCM).
620
/// - 16-bit (`Channel16`): 2 bytes per sample (e.g. `i16` / `u16`).
621
/// - 8-bit (`Channel8`): 1 byte per sample (e.g. `i8` / `u8`).
622
///
623
/// ### On-Wire Representation and Padding
624
///
625
/// On the physical I2S lines, each channel occupies `Data{X}` clock cycles:
626
/// - When `X == Y` (e.g. [`Data16Channel16`], [`Data32Channel32`]), the entire slot carries sample
627
///   data with no padding.
628
/// - When `X > Y` (e.g. [`Data32Channel16`]), the `Y`-bit sample occupies `Y` bits of the `X`-bit
629
///   slot on the wire, and the remaining `X - Y` bits are padded or discarded according to
630
///   [`Alignment`]:
631
///   - [`Alignment::Left`] (default):
632
///     - **TX**: The `Y` sample bits are transmitted first (MSB-aligned within the slot), followed
633
///       by `X - Y` zero bits.
634
///     - **RX**: The first `Y` bits from the wire are captured into memory; trailing bits in the
635
///       slot are discarded.
636
///   - [`Alignment::Right`]:
637
///     - **TX**: `X - Y` zero bits are transmitted first, followed by the `Y` sample bits
638
///       (LSB-aligned within the slot).
639
///     - **RX**: The leading `X - Y` bits are discarded; the last `Y` bits of the slot are captured
640
///       into memory.
641
///
642
/// Note: In this driver's naming scheme, `Data{X}` refers to the on-wire slot width, and
643
/// `Channel{Y}` refers to the in-memory sample data width. (This differs from some datasheets where
644
/// the on-wire slot is called "slot width" or "channel width", and the sample size is called "data
645
/// width".)
646
///
647
/// [`Data16Channel16`]: DataFormat::Data16Channel16
648
/// [`Data32Channel16`]: DataFormat::Data32Channel16
649
/// [`Data32Channel32`]: DataFormat::Data32Channel32
650
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
651
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
652
#[cfg(not(i2s_version = "1"))]
653
pub enum DataFormat {
654
    /// 32-bit on-wire slot width (32 BCLK cycles) and 32-bit in-memory sample width (4 bytes per
655
    /// sample).
656
    Data32Channel32,
657
    /// 32-bit on-wire slot width (32 BCLK cycles) and 24-bit in-memory sample width (3 bytes packed
658
    /// per sample).
659
    ///
660
    /// In TX, 24-bit samples from memory are padded to 32 bits on the wire according to
661
    /// [`Alignment`]. In RX, 24 bits are extracted from each 32-bit slot on the wire into
662
    /// memory.
663
    Data32Channel24,
664
    /// 32-bit on-wire slot width (32 BCLK cycles) and 16-bit in-memory sample width (2 bytes per
665
    /// sample).
666
    ///
667
    /// In TX, 16-bit samples from memory are padded to 32 bits on the wire according to
668
    /// [`Alignment`]. In RX, 16 bits are extracted from each 32-bit slot on the wire into
669
    /// memory.
670
    Data32Channel16,
671
    /// 32-bit on-wire slot width (32 BCLK cycles) and 8-bit in-memory sample width (1 byte per
672
    /// sample).
673
    ///
674
    /// In TX, 8-bit samples from memory are padded to 32 bits on the wire according to
675
    /// [`Alignment`]. In RX, 8 bits are extracted from each 32-bit slot on the wire into
676
    /// memory.
677
    Data32Channel8,
678
    /// 16-bit on-wire slot width (16 BCLK cycles) and 16-bit in-memory sample width (2 bytes per
679
    /// sample).
680
    Data16Channel16,
681
    /// 16-bit on-wire slot width (16 BCLK cycles) and 8-bit in-memory sample width (1 byte per
682
    /// sample).
683
    ///
684
    /// In TX, 8-bit samples from memory are padded to 16 bits on the wire according to
685
    /// [`Alignment`]. In RX, 8 bits are extracted from each 16-bit slot on the wire into
686
    /// memory.
687
    Data16Channel8,
688
    /// 8-bit on-wire slot width (8 BCLK cycles) and 8-bit in-memory sample width (1 byte per
689
    /// sample).
690
    Data8Channel8,
691
}
692

            
693
/// Supported I2S data formats.
694
///
695
/// Each format defines the on-wire slot width (`Data{X}`) and the in-memory sample width
696
/// (`Channel{Y}`). On the ESP32-S2, each slot on the wire matches the sample data width in memory
697
/// (`X == Y`).
698
///
699
/// - **`Data{X}` (on-wire slot width)**: The number of bit-clock (BCLK) cycles allocated to each
700
///   channel slot on the physical line ([`Self::data_bits`]).
701
/// - **`Channel{Y}` (in-memory sample width)**: The number of audio data bits per sample
702
///   transferred to or from memory via DMA ([`Self::channel_bits`]).
703
///
704
/// In the DMA memory buffer, samples occupy:
705
/// - 32-bit ([`Data32Channel32`]): 4 bytes per sample.
706
/// - 24-bit ([`Data24Channel24`]): 3 bytes per sample.
707
/// - 16-bit ([`Data16Channel16`]): 2 bytes per sample.
708
/// - 8-bit ([`Data8Channel8`]): 1 byte per sample.
709
///
710
/// [`Data32Channel32`]: DataFormat::Data32Channel32
711
/// [`Data24Channel24`]: DataFormat::Data24Channel24
712
/// [`Data16Channel16`]: DataFormat::Data16Channel16
713
/// [`Data8Channel8`]: DataFormat::Data8Channel8
714
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
715
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
716
#[cfg(esp32s2)]
717
pub enum DataFormat {
718
    /// 32-bit on-wire slot width (32 BCLK cycles) and 32-bit in-memory sample width (4 bytes per
719
    /// sample).
720
    Data32Channel32,
721
    /// 24-bit on-wire slot width (24 BCLK cycles) and 24-bit in-memory sample width (3 bytes packed
722
    /// per sample).
723
    Data24Channel24,
724
    /// 16-bit on-wire slot width (16 BCLK cycles) and 16-bit in-memory sample width (2 bytes per
725
    /// sample).
726
    Data16Channel16,
727
    /// 8-bit on-wire slot width (8 BCLK cycles) and 8-bit in-memory sample width (1 byte per
728
    /// sample).
729
    Data8Channel8,
730
}
731

            
732
/// Supported I2S data formats.
733
///
734
/// Each format defines the on-wire slot width (`Data{X}`) and the in-memory sample width
735
/// (`Channel{Y}`). On the ESP32, each slot on the wire matches the sample data width in memory (`X
736
/// == Y`).
737
///
738
/// - **`Data{X}` (on-wire slot width)**: The number of bit-clock (BCLK) cycles allocated to each
739
///   channel slot on the physical line ([`Self::data_bits`]).
740
/// - **`Channel{Y}` (in-memory sample width)**: The number of audio data bits per sample
741
///   transferred to or from memory via DMA ([`Self::channel_bits`]).
742
///
743
/// In the DMA memory buffer, samples occupy:
744
/// - 32-bit ([`Data32Channel32`]): 4 bytes per sample.
745
/// - 16-bit ([`Data16Channel16`]): 2 bytes per sample.
746
///
747
/// [`Data32Channel32`]: DataFormat::Data32Channel32
748
/// [`Data16Channel16`]: DataFormat::Data16Channel16
749
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
750
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
751
#[cfg(esp32)]
752
pub enum DataFormat {
753
    /// 32-bit on-wire slot width (32 BCLK cycles) and 32-bit in-memory sample width (4 bytes per
754
    /// sample).
755
    Data32Channel32,
756
    /// 16-bit on-wire slot width (16 BCLK cycles) and 16-bit in-memory sample width (2 bytes per
757
    /// sample).
758
    Data16Channel16,
759
}
760

            
761
#[cfg(not(i2s_version = "1"))]
762
impl DataFormat {
763
    /// Returns the on-wire slot width in bits (number of BCLK cycles per channel slot).
764
    pub fn data_bits(&self) -> u8 {
765
24
        match self {
766
            DataFormat::Data32Channel32 => 32,
767
            DataFormat::Data32Channel24 => 32,
768
            DataFormat::Data32Channel16 => 32,
769
            DataFormat::Data32Channel8 => 32,
770
            DataFormat::Data16Channel16 => 16,
771
            DataFormat::Data16Channel8 => 16,
772
8
            DataFormat::Data8Channel8 => 8,
773
        }
774
    }
775

            
776
    /// Returns the in-memory sample width in bits (active audio data bits per sample transferred
777
    /// via DMA).
778
    pub fn channel_bits(&self) -> u8 {
779
24
        match self {
780
            DataFormat::Data32Channel32 => 32,
781
            DataFormat::Data32Channel24 => 24,
782
            DataFormat::Data32Channel16 => 16,
783
            DataFormat::Data32Channel8 => 8,
784
            DataFormat::Data16Channel16 => 16,
785
            DataFormat::Data16Channel8 => 8,
786
            DataFormat::Data8Channel8 => 8,
787
        }
788
    }
789
}
790

            
791
#[cfg(esp32s2)]
792
impl DataFormat {
793
    /// Returns the on-wire slot width in bits (number of BCLK cycles per channel slot).
794
    pub fn data_bits(&self) -> u8 {
795
        match self {
796
            DataFormat::Data32Channel32 => 32,
797
            DataFormat::Data24Channel24 => 24,
798
            DataFormat::Data16Channel16 => 16,
799
            DataFormat::Data8Channel8 => 8,
800
        }
801
    }
802

            
803
    /// Returns the in-memory sample width in bits (active audio data bits per sample transferred
804
    /// via DMA).
805
    pub fn channel_bits(&self) -> u8 {
806
        match self {
807
            DataFormat::Data32Channel32 => 32,
808
            DataFormat::Data24Channel24 => 24,
809
            DataFormat::Data16Channel16 => 16,
810
            DataFormat::Data8Channel8 => 8,
811
        }
812
    }
813
}
814

            
815
#[cfg(esp32)]
816
impl DataFormat {
817
    /// Returns the on-wire slot width in bits (number of BCLK cycles per channel slot).
818
    pub fn data_bits(&self) -> u8 {
819
        match self {
820
            DataFormat::Data32Channel32 => 32,
821
            DataFormat::Data16Channel16 => 16,
822
        }
823
    }
824

            
825
    /// Returns the in-memory sample width in bits (active audio data bits per sample transferred
826
    /// via DMA).
827
    pub fn channel_bits(&self) -> u8 {
828
        match self {
829
            DataFormat::Data32Channel32 => 32,
830
            DataFormat::Data16Channel16 => 16,
831
        }
832
    }
833
}
834

            
835
/// I2S bit order
836
24
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
837
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
838
pub enum BitOrder {
839
    /// Most Significant Bit (MSB) is transmitted first.
840
    #[default]
841
    MsbFirst,
842
    /// Least Significant Bit (LSB) is transmitted first.
843
    LsbFirst,
844
}
845

            
846
/// I2S endianness
847
24
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
848
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
849
pub enum Endianness {
850
    /// Most Significant Byte (MSB) is transmitted first.
851
    #[default]
852
    LittleEndian,
853
    /// Least Significant Byte (LSB) is transmitted first.
854
    BigEndian,
855
}
856

            
857
/// I2S word select signal width
858
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
859
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
860
pub enum WsWidth {
861
    /// Word select signal will be kept active for half of the frame.
862
    #[default]
863
    HalfFrame,
864
    /// Word select signal will be kept active for the length of the first channel (PCM long frame
865
    /// standard)
866
    #[cfg(not(i2s_version = "1"))]
867
    OneChannel,
868
    /// Word select signal will be kept active for a single BCLK cycle (PCM short frame standard)
869
    Bit,
870
    /// Word select signal will be kept active for the specified amount of bits(BCLK cycles)
871
    #[cfg(not(i2s_version = "1"))]
872
    Bits(u16),
873
}
874

            
875
/// Represents the polarity of a signal.
876
24
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
877
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
878
pub enum Polarity {
879
    /// The signal is high when active.
880
    #[default]
881
    ActiveHigh,
882
    /// The signal is low when active.
883
    ActiveLow,
884
}
885

            
886
/// Alignment of sample data within the on-wire slot when the in-memory sample width is less than
887
/// the on-wire slot width.
888
///
889
/// See [`DataFormat`] for more details on padding and on-wire slot alignment.
890
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
891
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
892
pub enum Alignment {
893
    #[default]
894
    /// Left aligned (MSB-aligned within the on-wire slot).
895
    Left,
896
    /// Right aligned (LSB-aligned within the on-wire slot).
897
    Right,
898
}
899

            
900
/// I2S channels configuration
901
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
902
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
903
pub struct Channels {
904
    count: u8,
905
    mask: u16,
906
    fill: Option<u32>,
907
}
908

            
909
impl Channels {
910
    /// Two channels will use different data.
911
    pub const STEREO: Channels = Channels::new_impl(2, 0b11, None);
912
    /// Two channels will use the same data.
913
    pub const MONO: Channels = Channels::new_impl(2, 0b01, None);
914
    /// Two channels. Left(first) channel will contain data. Right(second) channel will contain
915
    /// zeros.
916
    pub const LEFT: Channels = Channels::new_impl(2, 0b01, Some(0));
917
    /// Two channels. Right(second) channel will contain data. Left(first) channel will contain
918
    /// zeros.
919
    pub const RIGHT: Channels = Channels::new_impl(2, 0b10, Some(0));
920

            
921
    #[procmacros::doc_replace]
922
    /// Creates arbitrary configuration for I2S channels.
923
    ///
924
    /// - `count` the total number of channels. Must be at least 1 and no more than 16.
925
    /// - `mask` determines which channels will be active, with the least significant bit
926
    ///   representing first channel. Setting the bit at the nth position means nth channel is
927
    ///   active. Inactive channels do not consume or write data in the DMA buffer.
928
    /// - `fill` determines the behavior of inactive channels. `Some(n)` will make all inactive
929
    ///   channel send out specified value, truncated to the channel width. `None` will make
930
    ///   disabled channels repeat the data from the last active channel. This field is ignored in
931
    ///   the receiver unit.
932
    ///
933
    /// # Examples
934
    ///
935
    /// The following example prepares configuration for 6 channels. Only 1st and 4th channels
936
    /// are active. Channels 2-3 will use the same data as the 1st, and channels 5-6 will use the
937
    /// data from the 4th.
938
    ///
939
    /// ```rust, no_run
940
    /// # {before_snippet}
941
    /// use esp_hal::i2s::master::Channels;
942
    ///
943
    /// let channels = Channels::new(6, 0b_001_001, None);
944
    /// # {after_snippet}
945
    /// ```
946
    #[cfg(not(i2s_version = "1"))]
947
    pub const fn new(count: u8, mask: u16, fill: Option<u32>) -> Self {
948
        Self::new_impl(count, mask, fill)
949
    }
950

            
951
    const fn new_impl(count: u8, mut mask: u16, fill: Option<u32>) -> Self {
952
        mask &= (1 << count) - 1;
953

            
954
        Self { count, mask, fill }
955
    }
956

            
957
    #[cfg(not(i2s_version = "1"))]
958
    fn active_count(&self) -> u8 {
959
24
        self.mask.count_ones() as u8
960
    }
961
}
962

            
963
/// Internal I2S peripheral configuration (TDM or PDM mode).
964
#[derive(Clone, Copy, Debug, PartialEq)]
965
#[cfg_attr(not(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx)), derive(Eq, Hash))]
966
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
967
#[non_exhaustive]
968
pub(crate) enum Config {
969
    /// Time-division multiplexed (TDM) configuration.
970
    Tdm(TdmConfig),
971
    /// Pulse-density modulation (PDM) configuration.
972
    #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
973
    Pdm(PdmConfig),
974
}
975

            
976
/// TDM mode peripheral configuration.
977
15
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, procmacros::BuilderLite)]
978
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
979
#[non_exhaustive]
980
pub struct TdmConfig {
981
    /// Receiver unit config.
982
    rx_config: TdmUnitConfig,
983

            
984
    /// Transmitter unit config.
985
    tx_config: TdmUnitConfig,
986

            
987
    /// Sets `I2S_SIG_LOOPBACK`: TX and RX share the same WS and BCK.
988
    signal_loopback: bool,
989

            
990
    /// The target sample rate.
991
    #[cfg(i2s_version = "1")]
992
    sample_rate: Rate,
993

            
994
    /// Source clock for the shared module clock.
995
    #[cfg(i2s_version = "1")]
996
    clock_source: I2sClockSource,
997

            
998
    /// Selects which module clock is routed to the MCLK pad.
999
    #[cfg(not(i2s_version = "1"))]
    mclk_out: MclkOut,
    /// Data format configuring on-wire slot width and in-memory sample width.
    #[cfg(i2s_version = "1")]
    data_format: DataFormat,
}
impl Config {
    fn validate(&self, _info: &Info) -> Result<(), ConfigError> {
26
        match self {
            Self::Tdm(c) => c.validate(),
            #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
            Self::Pdm(c) => c.validate(_info).map_err(ConfigError::Pdm),
        }
    }
}
impl TdmConfig {
    /// TDM Philips standard configuration with two 16-bit active channels.
24
    pub fn new_tdm_philips() -> Self {
24
        Self {
            rx_config: TdmUnitConfig::new_tdm_philips(),
            tx_config: TdmUnitConfig::new_tdm_philips(),
            ..Default::default()
        }
24
    }
    /// TDM MSB standard configuration with two 16-bit active channels.
    pub fn new_tdm_msb() -> Self {
        Self {
            rx_config: TdmUnitConfig::new_tdm_msb(),
            tx_config: TdmUnitConfig::new_tdm_msb(),
            ..Default::default()
        }
    }
    /// TDM PCM short frame standard configuration with two 16-bit active channels.
    pub fn new_tdm_pcm_short() -> Self {
        Self {
            rx_config: TdmUnitConfig::new_tdm_pcm_short(),
            tx_config: TdmUnitConfig::new_tdm_pcm_short(),
            ..Default::default()
        }
    }
    /// TDM PCM long frame standard configuration with two 16-bit active channels.
    #[cfg(not(i2s_version = "1"))]
    pub fn new_tdm_pcm_long() -> Self {
        Self {
            rx_config: TdmUnitConfig::new_tdm_pcm_long(),
            tx_config: TdmUnitConfig::new_tdm_pcm_long(),
            ..Default::default()
        }
    }
24
    fn validate(&self) -> Result<(), ConfigError> {
        self.rx_config.validate()?;
        self.tx_config.validate()?;
        Ok(())
    }
    #[cfg(i2s_version = "1")]
    fn calculate_clock(&self) -> I2sClockDividers {
        I2sClockDividers::new(
            self.sample_rate,
            2,
            self.data_format.data_bits(),
            self.clock_source,
        )
    }
    /// Assigns the given value to the `sample_rate` field in both units.
    #[must_use]
    #[cfg(not(i2s_version = "1"))]
    pub fn with_sample_rate(self, sample_rate: Rate) -> Self {
1
        Self {
1
            rx_config: self.rx_config.with_sample_rate(sample_rate),
1
            tx_config: self.tx_config.with_sample_rate(sample_rate),
            ..self
        }
    }
    /// Assigns the given value to the `channels` field in both units.
    #[must_use]
    pub fn with_channels(self, channels: Channels) -> Self {
1
        Self {
            rx_config: self.rx_config.with_channels(channels),
            tx_config: self.tx_config.with_channels(channels),
            ..self
        }
    }
    /// Assigns the given data format to both units.
    ///
    /// See [`DataFormat`] for details on on-wire slot width and in-memory sample width.
    #[must_use]
    #[cfg(not(i2s_version = "1"))]
    pub fn with_data_format(self, data_format: DataFormat) -> Self {
        Self {
1
            rx_config: self.rx_config.with_data_format(data_format),
1
            tx_config: self.tx_config.with_data_format(data_format),
            ..self
        }
    }
    /// Assigns the given value to the `ws_width` field in both units.
    #[must_use]
    pub fn with_ws_width(self, ws_width: WsWidth) -> Self {
        Self {
            rx_config: self.rx_config.with_ws_width(ws_width),
            tx_config: self.tx_config.with_ws_width(ws_width),
            ..self
        }
    }
    /// Assigns the given value to the `ws_polarity` field in both units.
    #[must_use]
    pub fn with_ws_polarity(self, ws_polarity: Polarity) -> Self {
        Self {
            rx_config: self.rx_config.with_ws_polarity(ws_polarity),
            tx_config: self.tx_config.with_ws_polarity(ws_polarity),
            ..self
        }
    }
    /// Assigns the given value to the `msb_shift` field in both units.
    #[must_use]
    pub fn with_msb_shift(self, msb_shift: bool) -> Self {
        Self {
            rx_config: self.rx_config.with_msb_shift(msb_shift),
            tx_config: self.tx_config.with_msb_shift(msb_shift),
            ..self
        }
    }
    /// Assigns the given value to the `endianness` field in both units.
    #[cfg(not(esp32))]
    #[must_use]
    pub fn with_endianness(self, endianness: Endianness) -> Self {
        Self {
            rx_config: self.rx_config.with_endianness(endianness),
            tx_config: self.tx_config.with_endianness(endianness),
            ..self
        }
    }
    /// Assigns the given value to the `bit_order` field in both units.
    #[cfg(not(i2s_version = "1"))]
    #[must_use]
    pub fn with_bit_order(self, bit_order: BitOrder) -> Self {
        Self {
            rx_config: self.rx_config.with_bit_order(bit_order),
            tx_config: self.tx_config.with_bit_order(bit_order),
            ..self
        }
    }
    /// Assigns the given value to the `alignment` field in both units.
    #[cfg(not(i2s_version = "1"))]
    #[must_use]
    pub fn with_alignment(self, alignment: Alignment) -> Self {
        Self {
            rx_config: self.rx_config.with_alignment(alignment),
            tx_config: self.tx_config.with_alignment(alignment),
            ..self
        }
    }
}
#[allow(clippy::derivable_impls)]
impl Default for TdmConfig {
    fn default() -> Self {
        Self {
            rx_config: TdmUnitConfig::new_tdm_philips(),
            tx_config: TdmUnitConfig::new_tdm_philips(),
            signal_loopback: false,
            #[cfg(i2s_version = "1")]
            sample_rate: Rate::from_hz(44100),
            #[cfg(i2s_version = "1")]
            clock_source: I2sClockSource::default(),
            #[cfg(not(i2s_version = "1"))]
            mclk_out: MclkOut::default(),
            #[cfg(i2s_version = "1")]
            data_format: DataFormat::Data16Channel16,
        }
    }
}
/// I2S receiver/transmitter unit configuration (TDM mode).
24
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct TdmUnitConfig {
    /// The target sample rate.
    #[cfg(not(i2s_version = "1"))]
    sample_rate: Rate,
    /// Source clock for this unit's module clock.
    #[cfg(not(i2s_version = "1"))]
    clock_source: I2sClockSource,
    /// I2S channels configuration.
    channels: Channels,
    /// Data format configuring on-wire slot width and in-memory sample width.
    #[cfg(not(i2s_version = "1"))]
    data_format: DataFormat,
    /// Duration for which WS signal is kept active.
    ws_width: WsWidth,
    /// Polarity of WS signal.
    ws_polarity: Polarity,
    /// Data signal will lag by one bit relative to the WS signal.
    msb_shift: bool,
    /// Byte order of the data.
    #[cfg(not(esp32))]
    endianness: Endianness,
    /// Bit order of the data.
    #[cfg(not(i2s_version = "1"))]
    bit_order: BitOrder,
    /// Alignment of sample data within the on-wire slot. Only relevant when sample width is less
    /// than slot width.
    #[cfg(not(i2s_version = "1"))]
    alignment: Alignment,
}
/// Alias for [`TdmUnitConfig`] (TDM mode unit configuration).
pub type UnitConfig = TdmUnitConfig;
impl TdmUnitConfig {
    /// TDM Philips standard configuration with two 16-bit active channels.
30
    pub fn new_tdm_philips() -> Self {
30
        Self {
            #[cfg(not(i2s_version = "1"))]
            sample_rate: Rate::from_hz(44100),
            #[cfg(not(i2s_version = "1"))]
            clock_source: I2sClockSource::default(),
            channels: Channels::STEREO,
            #[cfg(not(i2s_version = "1"))]
            data_format: DataFormat::Data16Channel16,
            ws_width: WsWidth::HalfFrame,
            ws_polarity: Polarity::ActiveLow,
            msb_shift: true,
            #[cfg(not(esp32))]
            endianness: Endianness::LittleEndian,
            #[cfg(not(i2s_version = "1"))]
            bit_order: BitOrder::MsbFirst,
            #[cfg(not(i2s_version = "1"))]
            alignment: Alignment::Left,
        }
30
    }
    /// TDM MSB standard configuration with two 16-bit active channels.
    pub fn new_tdm_msb() -> Self {
        Self::new_tdm_philips().with_msb_shift(false)
    }
    /// TDM PCM short frame standard configuration with two 16-bit active channels.
    pub fn new_tdm_pcm_short() -> Self {
        Self::new_tdm_philips()
            .with_ws_width(WsWidth::Bit)
            .with_ws_polarity(Polarity::ActiveHigh)
    }
    /// TDM PCM long frame standard configuration with two 16-bit active channels.
    #[cfg(not(i2s_version = "1"))]
    pub fn new_tdm_pcm_long() -> Self {
        Self::new_tdm_philips()
            .with_ws_width(WsWidth::OneChannel)
            .with_ws_polarity(Polarity::ActiveHigh)
    }
    fn validate(&self) -> Result<(), ConfigError> {
        #[cfg(not(i2s_version = "1"))]
24
        if self.channels.active_count() == 0 || self.channels.count > 16 {
            return Err(ConfigError::ChannelsOutOfRange);
        }
        Ok(())
    }
    #[cfg(not(i2s_version = "1"))]
    fn calculate_ws_width(&self) -> Result<u16, ConfigError> {
24
        let ws_width = match self.ws_width {
            WsWidth::HalfFrame => {
24
                self.data_format.data_bits() as u16 * self.channels.count as u16 / 2
            }
            WsWidth::Bit => 1,
            WsWidth::OneChannel => self.data_format.data_bits() as u16,
            WsWidth::Bits(bits) => bits,
        };
        const MAX_WS_WIDTH: u16 = property!("i2s.max_ws_width");
        if !(1..=MAX_WS_WIDTH).contains(&ws_width)
24
            || ws_width > self.data_format.data_bits() as u16 * self.channels.count as u16
        {
            return Err(ConfigError::WsWidthOutOfRange);
        }
        Ok(ws_width)
    }
    #[cfg(not(i2s_version = "1"))]
    fn calculate_clock(&self) -> I2sClockDividers {
        I2sClockDividers::new(
24
            self.sample_rate,
            self.channels.count,
            self.data_format.data_bits(),
            self.clock_source,
        )
    }
}
impl Default for TdmUnitConfig {
    fn default() -> Self {
        Self::new_tdm_philips()
    }
}
/// Configuration errors.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigError {
    /// Provided [Channels] configuration has no active channels or has over 16 total channels.
    #[cfg(not(i2s_version = "1"))]
    ChannelsOutOfRange,
    /// Requested WS signal width is out of range.
    #[cfg(not(i2s_version = "1"))]
    WsWidthOutOfRange,
    /// PDM configuration error.
    #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
    Pdm(PdmError),
}
#[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
impl From<PdmError> for ConfigError {
    fn from(err: PdmError) -> Self {
        Self::Pdm(err)
    }
}
impl core::error::Error for ConfigError {}
impl core::fmt::Display for ConfigError {
    #[allow(unused_variables)]
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match *self {
            #[cfg(not(i2s_version = "1"))]
            ConfigError::ChannelsOutOfRange => {
                write!(
                    f,
                    "Provided channels configuration has no active channels or has over 16 total channels"
                )
            }
            #[cfg(not(i2s_version = "1"))]
            ConfigError::WsWidthOutOfRange => {
                write!(
                    f,
                    "The requested WS signal width is out of supported range (1..=128)"
                )
            }
            #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
            ConfigError::Pdm(err) => write!(f, "{err}"),
        }
    }
}
/// Instance of the I2S peripheral driver.
#[non_exhaustive]
pub struct I2s<'d, Dm>
where
    Dm: DriverMode,
{
    /// Handles the reception (RX) side of the I2S peripheral.
    pub i2s_rx: RxCreator<'d, Dm>,
    /// Handles the transmission (TX) side of the I2S peripheral.
    pub i2s_tx: TxCreator<'d, Dm>,
}
impl<Dm> I2s<'_, Dm>
where
    Dm: DriverMode,
{
    #[cfg_attr(
        not(multi_core),
        doc = "Registers an interrupt handler for the peripheral."
    )]
    #[cfg_attr(
        multi_core,
        doc = "Registers an interrupt handler for the peripheral on the current core."
    )]
    #[doc = ""]
    /// Replaces any previously registered interrupt handlers.
    ///
    /// The default/unhandled interrupt handler can be restored with
    /// [crate::interrupt::DEFAULT_INTERRUPT_HANDLER]
    #[instability::unstable]
    pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
        // tx.i2s and rx.i2s is the same, we could use either one
        self.i2s_tx.i2s.set_interrupt_handler(handler);
    }
    /// Listens for the given interrupts.
    #[instability::unstable]
    pub fn listen(&mut self, interrupts: impl Into<EnumSet<I2sInterrupt>>) {
        // tx.i2s and rx.i2s is the same, we could use either one
        self.i2s_tx
            .i2s
            .info()
            .enable_listen(interrupts.into(), true);
    }
    /// Unlistens from the given interrupts.
    #[instability::unstable]
    pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<I2sInterrupt>>) {
        // tx.i2s and rx.i2s is the same, we could use either one
        self.i2s_tx
            .i2s
            .info()
            .enable_listen(interrupts.into(), false);
    }
    /// Returns the asserted interrupts.
    #[instability::unstable]
    pub fn interrupts(&mut self) -> EnumSet<I2sInterrupt> {
        // tx.i2s and rx.i2s is the same, we could use either one
        self.i2s_tx.i2s.info().interrupts()
    }
    /// Resets asserted interrupts.
    #[instability::unstable]
    pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<I2sInterrupt>>) {
        // tx.i2s and rx.i2s is the same, we could use either one
        self.i2s_tx.i2s.info().clear_interrupts(interrupts.into());
    }
}
impl<Dm> crate::private::Sealed for I2s<'_, Dm> where Dm: DriverMode {}
impl<Dm> InterruptConfigurable for I2s<'_, Dm>
where
    Dm: DriverMode,
{
    fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) {
        I2s::set_interrupt_handler(self, handler);
    }
}
impl<'d> I2s<'d, Blocking> {
    #[cfg(i2s_driver_supported)]
24
    fn new_internal(
        i2s: impl Instance + 'd,
        channel: I2sMasterErased<'d>,
        config: Config,
    ) -> Result<Self, ConfigError> {
24
        let channel = Channel::new(channel);
        let i2s = i2s.degrade();
24
        channel.runtime_ensure_compatible(i2s.dma_peripheral());
        // on ESP32-C3 / ESP32-S3 and later RX and TX are independent and
        // could be configured totally independently but for now handle all
        // the targets the same and force same configuration for both, TX and RX
        // make sure the peripheral is enabled before configuring it
24
        let peripheral = i2s.info().peripheral;
24
        let rx_guard = PeripheralGuard::new(peripheral);
24
        let tx_guard = PeripheralGuard::new(peripheral);
24
        i2s.info().set_master();
24
        i2s.info().configure(&config)?;
        let clock = i2s.info().clock_instance;
24
        let (req_tx, req_rx) = match &config {
            Config::Tdm(_) => (true, true),
            #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
            Config::Pdm(c) => (c.tx.is_some(), c.rx.is_some()),
        };
        // `tx_update` / `rx_update` copy APB registers into the I2S clock domain. The
        // module clock of that direction must run, or the self-clearing bit never
        // clears.
24
        let rx_clk_guard = req_rx.then(|| I2sDirClkGuard::request_rx(clock));
24
        let tx_clk_guard = req_tx.then(|| I2sDirClkGuard::request_tx(clock));
        if req_tx {
24
            i2s.info().update_tx();
        }
1
        if req_rx {
1
            i2s.info().update_rx();
        }
        Ok(Self {
            i2s_rx: RxCreator {
                i2s: unsafe { i2s.clone_unchecked() },
                rx_channel: channel.rx,
                guard: rx_guard,
                clk_guard: rx_clk_guard,
                #[cfg(not(i2s_version = "1"))]
                mclk_out_guard: None,
                #[cfg(i2s_version = "1")]
                data_format: match config {
                    Config::Tdm(c) => c.data_format,
                    #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
                    Config::Pdm(_) => DataFormat::Data16Channel16,
                },
            },
            i2s_tx: TxCreator {
                i2s,
                tx_channel: channel.tx,
                guard: tx_guard,
                clk_guard: tx_clk_guard,
                #[cfg(not(i2s_version = "1"))]
                mclk_out_guard: None,
                #[cfg(i2s_version = "1")]
                data_format: match config {
                    Config::Tdm(c) => c.data_format,
                    #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))]
                    Config::Pdm(_) => DataFormat::Data16Channel16,
                },
            },
        })
    }
    /// Converts the I2S instance into async mode.
    pub fn into_async(self) -> I2s<'d, Async> {
        I2s {
            i2s_rx: RxCreator {
                i2s: self.i2s_rx.i2s,
                rx_channel: self.i2s_rx.rx_channel.into_async(),
                guard: self.i2s_rx.guard,
                clk_guard: self.i2s_rx.clk_guard,
                #[cfg(not(i2s_version = "1"))]
                mclk_out_guard: self.i2s_rx.mclk_out_guard,
                #[cfg(i2s_version = "1")]
                data_format: self.i2s_rx.data_format,
            },
            i2s_tx: TxCreator {
                i2s: self.i2s_tx.i2s,
                tx_channel: self.i2s_tx.tx_channel.into_async(),
                guard: self.i2s_tx.guard,
                clk_guard: self.i2s_tx.clk_guard,
                #[cfg(not(i2s_version = "1"))]
                mclk_out_guard: self.i2s_tx.mclk_out_guard,
                #[cfg(i2s_version = "1")]
                data_format: self.i2s_tx.data_format,
            },
        }
    }
}
#[cfg(esp32)]
mod esp32 {
    use super::*;
    use crate::gpio::OutputSignal;
    /// Pins that can be used as clock outputs.
    pub trait ClkPin<'d>: PeripheralOutput<'d> {
        #[doc(hidden)]
        fn signal(&self) -> OutputSignal;
    }
    // TODO: implementations should be generated. This is the same problem as SDIO pins -
    // alternate function without GPIO matrix option. S2 and S3 also have the CLK_OUTn functions,
    // although they don't seem to need the same mechanism.
    impl<'d> ClkPin<'d> for crate::peripherals::GPIO0<'d> {
        fn signal(&self) -> OutputSignal {
            OutputSignal::CLK_OUT1
        }
    }
    impl<'d> ClkPin<'d> for crate::peripherals::GPIO1<'d> {
        fn signal(&self) -> OutputSignal {
            OutputSignal::CLK_OUT3
        }
    }
    impl<'d> ClkPin<'d> for crate::peripherals::GPIO3<'d> {
        fn signal(&self) -> OutputSignal {
            OutputSignal::CLK_OUT2
        }
    }
}
#[cfg(esp32)]
pub use esp32::ClkPin;
impl<'d, Dm> I2s<'d, Dm>
where
    Dm: DriverMode,
{
    /// Configures the I2S peripheral to use a master clock (MCLK) output pin.
    #[cfg(not(esp32))]
    pub fn with_mclk(self, mclk: impl PeripheralOutput<'d>) -> Self {
        let mclk = mclk.into();
        mclk.apply_output_config(&OutputConfig::default());
        mclk.set_output_enable(true);
        self.i2s_tx.i2s.info().mclk.connect_to(&mclk);
        self.request_mclk_out()
    }
    /// Requests the MCLK pad output, on the chips that gate it as a separate clock.
    #[cfg(all(not(esp32), i2s_version = "1"))]
    fn request_mclk_out(self) -> Self {
        self
    }
    /// Requests the MCLK pad output, on the chips that gate it as a separate clock.
    #[cfg(all(not(esp32), not(i2s_version = "1")))]
    fn request_mclk_out(mut self) -> Self {
        let guard = I2sMclkOutGuard::request(self.i2s_tx.i2s.info().clock_instance);
        self.i2s_tx.mclk_out_guard = Some(guard.clone());
        self.i2s_rx.mclk_out_guard = Some(guard);
        self
    }
    /// Configures the I2S peripheral to output its clock to a pin.
    #[cfg(esp32)]
    pub fn with_mclk(self, mclk: impl ClkPin<'d>) -> Self {
        use crate::{gpio::OutputSignal, peripherals::IO_MUX};
        let clk_signal = mclk.signal();
        let mclk = mclk.into();
        mclk.apply_output_config(&OutputConfig::default());
        mclk.set_output_enable(true);
        // We need to do two things:
        // - Configure the IO_MUX_PIN_CTRL register
        // - Select the correct pin function
        let selector = match self.i2s_rx.i2s.0 {
            super::any::Inner::I2s0(_) => 0x0,
            super::any::Inner::I2s1(_) => 0xF,
        };
        // Route the appropriate I2S clock output to the selected signal.
        IO_MUX::regs().pin_ctrl().modify(|_, w| unsafe {
            match clk_signal {
                OutputSignal::CLK_OUT1 => w.clk1().bits(selector),
                OutputSignal::CLK_OUT2 => w.clk2().bits(selector),
                OutputSignal::CLK_OUT3 => w.clk3().bits(selector),
                _ => unreachable!(),
            }
        });
        // Connect the clock signal to the selected pin.
        clk_signal.connect_to(&mclk);
        // I think it's okay to leave the configuration untouched when dropping the driver. We'll
        // gate the clock source, which should also stop the clock output. Reusing the pins will
        // remove the output signal assignment.
        self
    }
}
/// I2S TX channel.
pub struct I2sTx<'d, Dm>
where
    Dm: DriverMode,
{
    i2s: AnyI2s<'d>,
    tx_channel: ChannelTx<Dm, <I2sMasterErased<'d> as DmaChannel>::Tx>,
    _guard: PeripheralGuard,
    _clk_guard: Option<I2sDirClkGuard>,
    #[cfg(not(i2s_version = "1"))]
    _mclk_out_guard: Option<I2sMclkOutGuard>,
    #[cfg(i2s_version = "1")]
    data_format: DataFormat,
}
impl<Dm> core::fmt::Debug for I2sTx<'_, Dm>
where
    Dm: DriverMode,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("I2sTx").finish()
    }
}
impl<'d, Dm> I2sTx<'d, Dm>
where
    Dm: DriverMode,
{
    /// Performs a DMA write.
    #[allow(clippy::type_complexity)]
    #[instability::unstable]
    pub fn write<TX: DmaTxBuffer>(
        mut self,
        mut buffer: TX,
    ) -> Result<I2sTxDmaTransfer<'d, Dm, TX>, (Error, Self, TX)> {
        self.i2s.info().reset_tx();
        let res = unsafe {
            self.tx_channel
                .prepare_transfer(self.i2s.dma_peripheral(), &mut buffer)
                .and_then(|_| self.tx_channel.start_transfer())
        };
        if let Err(err) = res {
            return Err((Error::DmaError(err), self, buffer));
        }
        self.i2s.info().tx_start();
        Ok(I2sTxDmaTransfer {
            i2s_tx: ManuallyDrop::new(self),
            buffer_view: ManuallyDrop::new(buffer.into_view()),
            completed: false,
        })
    }
    /// Changes the I2S Tx unit configuration.
    pub fn apply_config(&mut self, tx_config: &UnitConfig) -> Result<(), ConfigError> {
        tx_config.validate()?;
        self.i2s.info().configure_tx(
            tx_config,
            #[cfg(i2s_version = "1")]
            self.data_format,
        )
    }
}
/// I2S RX channel.
pub struct I2sRx<'d, Dm>
where
    Dm: DriverMode,
{
    i2s: AnyI2s<'d>,
    rx_channel: ChannelRx<Dm, <I2sMasterErased<'d> as DmaChannel>::Rx>,
    _guard: PeripheralGuard,
    _clk_guard: Option<I2sDirClkGuard>,
    #[cfg(not(i2s_version = "1"))]
    _mclk_out_guard: Option<I2sMclkOutGuard>,
    #[cfg(i2s_version = "1")]
    data_format: DataFormat,
}
impl<Dm> core::fmt::Debug for I2sRx<'_, Dm>
where
    Dm: DriverMode,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("I2sRx").finish()
    }
}
impl<'d, Dm> I2sRx<'d, Dm>
where
    Dm: DriverMode,
{
    /// Performs a DMA read.
    ///
    /// The number of read bytes might be less than the capacity of the provided buffer since the
    /// peripheral might not completely fill each descriptor's buffer.
    ///
    /// Returns an [`I2sRxDmaTransfer`].
    pub fn read<BUF>(
        mut self,
        mut buffer: BUF,
    ) -> Result<I2sRxDmaTransfer<'d, Dm, BUF>, (Error, Self, BUF)>
    where
        BUF: DmaRxBuffer,
    {
        // Reset RX unit and RX FIFO
        self.i2s.info().reset_rx();
        let res = unsafe {
            self.rx_channel
                .prepare_transfer(self.i2s.dma_peripheral(), &mut buffer)
                .and_then(|_| self.rx_channel.start_transfer())
        };
        if let Err(err) = res {
            return Err((Error::DmaError(err), self, buffer));
        }
        // start: set I2S_RX_START
        self.i2s.info().rx_start(usize::MAX); // really limited by exhausting DMA rx buffer
        Ok(I2sRxDmaTransfer {
            i2s_rx: ManuallyDrop::new(self),
            buffer_view: ManuallyDrop::new(buffer.into_view()),
            completed: false,
        })
    }
    /// Changes the I2S Rx unit configuration.
    pub fn apply_config(&mut self, rx_config: &UnitConfig) -> Result<(), ConfigError> {
        rx_config.validate()?;
        self.i2s.info().configure_rx(
            rx_config,
            #[cfg(i2s_version = "1")]
            self.data_format,
        )
    }
}
/// A peripheral singleton compatible with the I2S master driver.
pub trait Instance: crate::private::Sealed + super::any::Degrade {
    /// Returns the peripheral data describing this instance.
    #[doc(hidden)]
    fn info(&self) -> &'static Info;
}
for_each_i2s! {
    (
        $instance:ident, $sys:ident, $mclk:ident,
        $bclk:ident, $ws:ident, $bclk_rx:ident, $ws_rx:ident,
        [$($dout:ident),+], [$($din:ident),+], $pdm_tx:literal, $pdm_rx:literal,
        $pcm2pdm:literal, $pdm2pcm:literal
    ) => {
        impl Instance for crate::peripherals::$instance<'_> {
            fn info(&self) -> &'static Info {
                static INFO: Info = Info {
                    register_block: crate::peripherals::$instance::PTR.cast::<RegisterBlock>(),
                    peripheral: crate::system::Peripheral::$sys,
                    // MCLK on ESP32 requires special handling, so it has no signal here.
                    #[cfg(not(esp32))]
                    mclk: OutputSignal::$mclk,
                    bclk: OutputSignal::$bclk,
                    ws: OutputSignal::$ws,
                    bclk_rx: OutputSignal::$bclk_rx,
                    ws_rx: OutputSignal::$ws_rx,
                    dout_lines: &[$(OutputSignal::$dout),+],
                    din_lines: &[$(InputSignal::$din),+],
                    pdm_tx: $pdm_tx,
                    pdm_rx: $pdm_rx,
                    pcm2pdm: $pcm2pdm,
                    pdm2pcm: $pdm2pcm,
                    clock_instance: crate::clock::ll::I2sInstance::$sys,
                };
                &INFO
            }
        }
    };
}
impl Instance for AnyI2s<'_> {
    fn info(&self) -> &'static Info {
        match &self.0 {
            #[cfg(soc_has_i2s0)]
            AnyI2sInner::I2s0(i2s) => i2s.info(),
            #[cfg(soc_has_i2s1)]
            AnyI2sInner::I2s1(i2s) => i2s.info(),
            #[cfg(soc_has_i2s2)]
            AnyI2sInner::I2s2(i2s) => i2s.info(),
        }
    }
}
impl AnyI2s<'_> {
    delegate::delegate! {
        to match &self.0 {
            #[cfg(soc_has_i2s0)]
            AnyI2sInner::I2s0(i2s) => i2s,
            #[cfg(soc_has_i2s1)]
            AnyI2sInner::I2s1(i2s) => i2s,
            #[cfg(soc_has_i2s2)]
            AnyI2sInner::I2s2(i2s) => i2s,
        } {
            fn bind_peri_interrupt(&self, handler: InterruptHandler);
            fn disable_peri_interrupt_on_all_cores(&self);
        }
    }
    pub(super) fn set_interrupt_handler(&self, handler: InterruptHandler) {
        self.disable_peri_interrupt_on_all_cores();
        self.bind_peri_interrupt(handler);
    }
}
/// Keeps the module clock of one direction enabled.
///
/// Both directions share a single module clock on `i2s_version = "1"`, and the clock tree counts
/// the requests, so a guard per direction is correct on every chip.
pub(crate) struct I2sDirClkGuard {
    clock: crate::clock::ll::I2sInstance,
    #[cfg(not(i2s_version = "1"))]
    tx: bool,
}
impl I2sDirClkGuard {
25
    pub(crate) fn request_tx(clock: crate::clock::ll::I2sInstance) -> Self {
25
        Self::request(clock, true)
    }
25
    fn request_rx(clock: crate::clock::ll::I2sInstance) -> Self {
25
        Self::request(clock, false)
    }
50
    fn request(clock: crate::clock::ll::I2sInstance, tx: bool) -> Self {
        crate::clock::ll::ClockTree::with(|clocks| {
            cfg_select! {
                i2s_version = "1" => {
                    let _ = tx;
                    clock.request_mclk(clocks);
                }
                _ => {
50
                    if tx {
                        clock.request_tx_clk(clocks);
                    } else {
                        clock.request_rx_clk(clocks);
                    }
                }
            }
        });
        Self {
            clock,
            #[cfg(not(i2s_version = "1"))]
            tx,
        }
50
    }
}
impl Drop for I2sDirClkGuard {
    fn drop(&mut self) {
        crate::clock::ll::ClockTree::with(|clocks| {
            cfg_select! {
                i2s_version = "1" => self.clock.release_mclk(clocks),
                _ => {
                    if self.tx {
                        self.clock.release_tx_clk(clocks);
                    } else {
                        self.clock.release_rx_clk(clocks);
                    }
                }
            }
        });
    }
}
/// Keeps the MCLK pad output enabled.
#[cfg(not(i2s_version = "1"))]
pub(crate) struct I2sMclkOutGuard {
    clock: crate::clock::ll::I2sInstance,
}
#[cfg(not(i2s_version = "1"))]
impl I2sMclkOutGuard {
    fn request(clock: crate::clock::ll::I2sInstance) -> Self {
        crate::clock::ll::ClockTree::with(|clocks| clock.request_mclk_out(clocks));
        Self { clock }
    }
}
#[cfg(not(i2s_version = "1"))]
impl Clone for I2sMclkOutGuard {
    fn clone(&self) -> Self {
        Self::request(self.clock)
    }
}
#[cfg(not(i2s_version = "1"))]
impl Drop for I2sMclkOutGuard {
    fn drop(&mut self) {
        crate::clock::ll::ClockTree::with(|clocks| self.clock.release_mclk_out(clocks));
    }
}
pub(crate) mod private {
    use super::*;
    pub struct TxCreator<'d, Dm>
    where
        Dm: DriverMode,
    {
        pub i2s: AnyI2s<'d>,
        pub tx_channel: ChannelTx<Dm, <I2sMasterErased<'d> as DmaChannel>::Tx>,
        pub(crate) guard: PeripheralGuard,
        pub(crate) clk_guard: Option<I2sDirClkGuard>,
        #[cfg(not(i2s_version = "1"))]
        pub(crate) mclk_out_guard: Option<I2sMclkOutGuard>,
        #[cfg(i2s_version = "1")]
        pub(crate) data_format: DataFormat,
    }
    impl<'d, Dm> TxCreator<'d, Dm>
    where
        Dm: DriverMode,
    {
        pub fn build(self) -> I2sTx<'d, Dm> {
            let peripheral = self.i2s.info().peripheral;
            I2sTx {
                i2s: self.i2s,
                tx_channel: self.tx_channel,
                _guard: PeripheralGuard::new(peripheral),
                _clk_guard: self.clk_guard,
                #[cfg(not(i2s_version = "1"))]
                _mclk_out_guard: self.mclk_out_guard,
                #[cfg(i2s_version = "1")]
                data_format: self.data_format,
            }
        }
        pub fn with_bclk(self, bclk: impl PeripheralOutput<'d>) -> Self {
            let bclk = bclk.into();
            bclk.apply_output_config(&OutputConfig::default());
            bclk.set_output_enable(true);
            self.i2s.info().bclk.connect_to(&bclk);
            self
        }
        pub fn with_ws(self, ws: impl PeripheralOutput<'d>) -> Self {
            let ws = ws.into();
            ws.apply_output_config(&OutputConfig::default());
            ws.set_output_enable(true);
            self.i2s.info().ws.connect_to(&ws);
            self
        }
        pub fn with_dout(self, dout: impl PeripheralOutput<'d>) -> Self {
            let dout = dout.into();
            dout.apply_output_config(&OutputConfig::default());
            dout.set_output_enable(true);
            unwrap!(self.i2s.info().dout(0)).connect_to(&dout);
            self
        }
        /// Connects the PDM clock pin (maps to the WS output signal).
        pub fn with_clk(self, clk: impl PeripheralOutput<'d>) -> Self {
            self.with_ws(clk)
        }
        /// Connects a second PDM TX data line (line 1, two-line DAC mode, HW v2+).
        #[cfg(all(i2s_supports_pdm_tx, not(i2s_version = "1")))]
        pub fn with_dout2(self, dout: impl PeripheralOutput<'d>) -> Result<Self, ConfigError> {
            let dout = dout.into();
            dout.apply_output_config(&OutputConfig::default());
            dout.set_output_enable(true);
            let signal = self.i2s.info().dout(1).ok_or(PdmError::InvalidLine)?;
            signal.connect_to(&dout);
            Ok(self)
        }
    }
    pub struct RxCreator<'d, Dm>
    where
        Dm: DriverMode,
    {
        pub i2s: AnyI2s<'d>,
        pub rx_channel: ChannelRx<Dm, <I2sMasterErased<'d> as DmaChannel>::Rx>,
        pub(crate) guard: PeripheralGuard,
        pub(crate) clk_guard: Option<I2sDirClkGuard>,
        #[cfg(not(i2s_version = "1"))]
        pub(crate) mclk_out_guard: Option<I2sMclkOutGuard>,
        #[cfg(i2s_version = "1")]
        pub(crate) data_format: DataFormat,
    }
    impl<'d, Dm> RxCreator<'d, Dm>
    where
        Dm: DriverMode,
    {
        pub fn build(self) -> I2sRx<'d, Dm> {
            let peripheral = self.i2s.info().peripheral;
            I2sRx {
                i2s: self.i2s,
                rx_channel: self.rx_channel,
                _guard: PeripheralGuard::new(peripheral),
                _clk_guard: self.clk_guard,
                #[cfg(not(i2s_version = "1"))]
                _mclk_out_guard: self.mclk_out_guard,
                #[cfg(i2s_version = "1")]
                data_format: self.data_format,
            }
        }
        pub fn with_bclk(self, bclk: impl PeripheralOutput<'d>) -> Self {
            let bclk = bclk.into();
            bclk.apply_output_config(&OutputConfig::default());
            bclk.set_output_enable(true);
            self.i2s.info().bclk_rx.connect_to(&bclk);
            self
        }
        pub fn with_ws(self, ws: impl PeripheralOutput<'d>) -> Self {
            let ws = ws.into();
            ws.apply_output_config(&OutputConfig::default());
            ws.set_output_enable(true);
            self.i2s.info().ws_rx.connect_to(&ws);
            self
        }
        pub fn with_din(self, din: impl PeripheralInput<'d>) -> Self {
            let din = din.into();
            din.apply_input_config(&InputConfig::default());
            din.set_input_enable(true);
            unwrap!(self.i2s.info().din(0)).connect_to(&din);
            self
        }
        /// Connects the PDM clock pin (maps to the WS output signal).
        pub fn with_clk(self, clk: impl PeripheralOutput<'d>) -> Self {
            self.with_ws(clk)
        }
        /// Connects a PDM RX data line (`line` 0..=`pdm_max_rx_lines`-1).
        #[cfg(i2s_supports_pdm_rx)]
        pub fn with_din_line(
            self,
            line: u8,
            din: impl PeripheralInput<'d>,
        ) -> Result<Self, ConfigError> {
            let din = din.into();
            din.apply_input_config(&InputConfig::default());
            din.set_input_enable(true);
            let signal = self.i2s.info().din(line).ok_or(PdmError::InvalidLine)?;
            signal.connect_to(&din);
            Ok(self)
        }
    }
    /// The divider from the module clock source to MCLK and BCLK.
    ///
    /// MCLK is `sclk / (mclk_divider + numerator / denominator)`, and BCLK is
    /// `MCLK / bclk_divider`.
    pub struct I2sClockDividers {
        pub(crate) mclk_divider: u32,
        pub(crate) bclk_divider: u32,
        pub(crate) denominator: u32,
        pub(crate) numerator: u32,
    }
    impl I2sClockDividers {
        pub fn new(
            sample_rate: Rate,
            channels: u8,
            data_bits: u8,
            source: I2sClockSource,
        ) -> I2sClockDividers {
            // this loosely corresponds to `i2s_std_calculate_clock` and
            // `i2s_ll_tx_set_mclk` in esp-idf
            // If data_bits is a power of two, use 256 as the mclk_multiple
            // If data_bits is 24, use 192 (24 * 8) as the mclk_multiple
            let mclk_multiple = if data_bits == 24 { 192 } else { 256 };
            let rate = sample_rate.as_hz();
24
            let bclk = rate * channels as u32 * data_bits as u32;
24
            let mclk = rate * mclk_multiple;
24
            I2sClockDividers::from_frequencies(source_frequency(source), mclk, mclk / bclk)
        }
50
        pub(crate) fn from_frequencies(sclk: u32, mclk: u32, bclk_divider: u32) -> Self {
            let divider = FractionalDivider::new(sclk, mclk, super::mclk_max_denominator());
50
            I2sClockDividers {
                mclk_divider: divider.integer,
                bclk_divider,
                // An integer divider is described as `0 / 1`, not as `0 / 0`.
                denominator: divider.denominator.max(1),
                numerator: divider.numerator,
            }
50
        }
    }
}