From c53f03cf91902723fd35edace13b8c3e5cc0f983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Tue, 25 Aug 2026 11:14:25 +0200 Subject: [PATCH 1/3] Enable I2S for ESP32-S31 --- esp-hal/README.md | 2 +- .../{hp_sys_clkrst.rs => hp_sys_clkrst_p4.rs} | 2 +- esp-hal/src/i2s/hp_sys_clkrst_s31.rs | 150 +++++++++ esp-hal/src/i2s/master/low_level/mod.rs | 4 +- esp-hal/src/i2s/mod.rs | 6 +- esp-hal/src/i2s/pdm/regs_v2.rs | 4 +- esp-hal/src/soc/esp32s31/mod.rs | 4 +- .../src/_build_script_utils.rs | 46 ++- .../src/_generated_esp32s31.rs | 296 +++++++++++++----- esp-metadata/devices/esp32s31/clocks.toml | 12 + esp-metadata/devices/esp32s31/gpio.toml | 46 ++- esp-metadata/devices/esp32s31/soc.toml | 45 ++- 12 files changed, 514 insertions(+), 103 deletions(-) rename esp-hal/src/i2s/{hp_sys_clkrst.rs => hp_sys_clkrst_p4.rs} (99%) create mode 100644 esp-hal/src/i2s/hp_sys_clkrst_s31.rs diff --git a/esp-hal/README.md b/esp-hal/README.md index 1be6778e7dc..ac7fd7f8f33 100644 --- a/esp-hal/README.md +++ b/esp-hal/README.md @@ -95,7 +95,7 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a | SPI slave | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | | LP SPI master | | | | | | | | ❌ | | | | | LP SPI slave | | | | | | | | ❌ | | | | -| I2S | ⚒️ | | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ❌ | +| I2S | ⚒️ | | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | | LP I2S | | | | | | | | ❌ | | | | | PARL_IO | | | | ⚒️ | ⚒️ | | ⚒️ | ❌ | | | ❌ | | RMT | ⚒️ | | ⚒️ | ⚒️ | ⚒️ | | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | diff --git a/esp-hal/src/i2s/hp_sys_clkrst.rs b/esp-hal/src/i2s/hp_sys_clkrst_p4.rs similarity index 99% rename from esp-hal/src/i2s/hp_sys_clkrst.rs rename to esp-hal/src/i2s/hp_sys_clkrst_p4.rs index 65c7ebf5d4a..1388a74c2ed 100644 --- a/esp-hal/src/i2s/hp_sys_clkrst.rs +++ b/esp-hal/src/i2s/hp_sys_clkrst_p4.rs @@ -1,4 +1,4 @@ -//! I2S clock programming via `HP_SYS_CLKRST`. +//! I2S clock programming via `HP_SYS_CLKRST` on ESP32-P4. use super::master::private::I2sClockDividers; use crate::{peripherals::HP_SYS_CLKRST, system::Peripheral}; diff --git a/esp-hal/src/i2s/hp_sys_clkrst_s31.rs b/esp-hal/src/i2s/hp_sys_clkrst_s31.rs new file mode 100644 index 00000000000..90ccedcb6e4 --- /dev/null +++ b/esp-hal/src/i2s/hp_sys_clkrst_s31.rs @@ -0,0 +1,150 @@ +//! I2S clock programming via `HP_SYS_CLKRST` on ESP32-S31. +//! +//! Register layout differs from ESP32-P4: each instance has dedicated +//! `i2sN_{tx,rx}_{ctrl0,div_ctrl0}` registers. Sequence follows ESP-IDF +//! `i2s_ll_{tx,rx}_set_raw_clk_div`. + +use super::master::private::I2sClockDividers; +use crate::{peripherals::HP_SYS_CLKRST, system::Peripheral}; + +pub(crate) fn set_tx_clock(peripheral: Peripheral, clock_settings: &I2sClockDividers) { + let clkm_div = clock_settings.mclk_dividers(); + let clkrst = HP_SYS_CLKRST::regs(); + let clock_source = property!("i2s.default_clock_source"); + + match peripheral { + Peripheral::I2s0 => { + // Bind MCLK to the TX module (`mst_clk_sel` lives on the RX ctrl register). + clkrst + .i2s0_rx_ctrl0() + .modify(|_, w| w.mst_clk_sel().set_bit()); + + // Workaround for the double-division issue documented in esp-idf i2s_ll.h. + clkrst + .i2s0_tx_ctrl0() + .modify(|_, w| unsafe { w.tx_div_n().bits(2) }); + clkrst.i2s0_tx_div_ctrl0().modify(|_, w| { + w.tx_div_yn1().clear_bit(); + unsafe { + w.tx_div_y().bits(1); + w.tx_div_z().bits(0); + w.tx_div_x().bits(0) + } + }); + + clkrst.i2s0_tx_div_ctrl0().modify(|_, w| { + w.tx_div_yn1().bit(clkm_div.yn1); + unsafe { + w.tx_div_z().bits(clkm_div.z as u16); + w.tx_div_y().bits(clkm_div.y as u16); + w.tx_div_x().bits(clkm_div.x as u16) + } + }); + clkrst.i2s0_tx_ctrl0().modify(|_, w| unsafe { + w.tx_div_n().bits(clock_settings.mclk_divider as u8); + w.tx_clk_en().set_bit(); + w.tx_clk_src_sel().bits(clock_source) + }); + } + Peripheral::I2s1 => { + clkrst + .i2s1_rx_ctrl0() + .modify(|_, w| w.mst_clk_sel().set_bit()); + + clkrst + .i2s1_tx_ctrl0() + .modify(|_, w| unsafe { w.tx_div_n().bits(2) }); + clkrst.i2s1_tx_div_ctrl0().modify(|_, w| { + w.tx_div_yn1().clear_bit(); + unsafe { + w.tx_div_y().bits(1); + w.tx_div_z().bits(0); + w.tx_div_x().bits(0) + } + }); + + clkrst.i2s1_tx_div_ctrl0().modify(|_, w| { + w.tx_div_yn1().bit(clkm_div.yn1); + unsafe { + w.tx_div_z().bits(clkm_div.z as u16); + w.tx_div_y().bits(clkm_div.y as u16); + w.tx_div_x().bits(clkm_div.x as u16) + } + }); + clkrst.i2s1_tx_ctrl0().modify(|_, w| unsafe { + w.tx_div_n().bits(clock_settings.mclk_divider as u8); + w.tx_clk_en().set_bit(); + w.tx_clk_src_sel().bits(clock_source) + }); + } + _ => unreachable!(), + } +} + +pub(crate) fn set_rx_clock(peripheral: Peripheral, clock_settings: &I2sClockDividers) { + let clkm_div = clock_settings.mclk_dividers(); + let clkrst = HP_SYS_CLKRST::regs(); + let clock_source = property!("i2s.default_clock_source"); + + match peripheral { + Peripheral::I2s0 => { + // Bind MCLK to the RX module, then apply the double-division workaround + // documented in esp-idf i2s_ll.h (`mst_clk_sel` lives on this register). + clkrst.i2s0_rx_ctrl0().modify(|_, w| { + w.mst_clk_sel().clear_bit(); + unsafe { w.rx_div_n().bits(2) } + }); + clkrst.i2s0_rx_div_ctrl0().modify(|_, w| { + w.rx_div_yn1().clear_bit(); + unsafe { + w.rx_div_y().bits(1); + w.rx_div_z().bits(0); + w.rx_div_x().bits(0) + } + }); + + clkrst.i2s0_rx_div_ctrl0().modify(|_, w| { + w.rx_div_yn1().bit(clkm_div.yn1); + unsafe { + w.rx_div_z().bits(clkm_div.z as u16); + w.rx_div_y().bits(clkm_div.y as u16); + w.rx_div_x().bits(clkm_div.x as u16) + } + }); + clkrst.i2s0_rx_ctrl0().modify(|_, w| unsafe { + w.rx_div_n().bits(clock_settings.mclk_divider as u8); + w.rx_clk_en().set_bit(); + w.rx_clk_src_sel().bits(clock_source) + }); + } + Peripheral::I2s1 => { + clkrst.i2s1_rx_ctrl0().modify(|_, w| { + w.mst_clk_sel().clear_bit(); + unsafe { w.rx_div_n().bits(2) } + }); + clkrst.i2s1_rx_div_ctrl0().modify(|_, w| { + w.rx_div_yn1().clear_bit(); + unsafe { + w.rx_div_y().bits(1); + w.rx_div_z().bits(0); + w.rx_div_x().bits(0) + } + }); + + clkrst.i2s1_rx_div_ctrl0().modify(|_, w| { + w.rx_div_yn1().bit(clkm_div.yn1); + unsafe { + w.rx_div_z().bits(clkm_div.z as u16); + w.rx_div_y().bits(clkm_div.y as u16); + w.rx_div_x().bits(clkm_div.x as u16) + } + }); + clkrst.i2s1_rx_ctrl0().modify(|_, w| unsafe { + w.rx_div_n().bits(clock_settings.mclk_divider as u8); + w.rx_clk_en().set_bit(); + w.rx_clk_src_sel().bits(clock_source) + }); + } + _ => unreachable!(), + } +} diff --git a/esp-hal/src/i2s/master/low_level/mod.rs b/esp-hal/src/i2s/master/low_level/mod.rs index c8c17928efe..89fcb34a8e4 100644 --- a/esp-hal/src/i2s/master/low_level/mod.rs +++ b/esp-hal/src/i2s/master/low_level/mod.rs @@ -25,7 +25,9 @@ pub(crate) struct I2sMclkDividers { impl I2sClockDividers { pub(crate) fn mclk_dividers(&self) -> I2sMclkDividers { let (x, y, z, yn1) = if self.denominator == 0 || self.numerator == 0 { - (0, 0, 0, true) + // IDF `i2s_ll_tx_set_mclk`: no fraction → x/y/z/yn1 all 0. + // `yn1` with z=0 makes the hardware run at N+1. + (0, 0, 0, false) } else if self.numerator > self.denominator / 2 { let x = self .denominator diff --git a/esp-hal/src/i2s/mod.rs b/esp-hal/src/i2s/mod.rs index 8e18d3a9cad..1d3eb7aee5c 100644 --- a/esp-hal/src/i2s/mod.rs +++ b/esp-hal/src/i2s/mod.rs @@ -2,7 +2,11 @@ pub mod master; -#[cfg(i2s_clock_configured_by_hp_sys_clkrst)] +#[cfg(all(i2s_clock_configured_by_hp_sys_clkrst, esp32p4))] +#[path = "hp_sys_clkrst_p4.rs"] +mod hp_sys_clkrst; +#[cfg(all(i2s_clock_configured_by_hp_sys_clkrst, esp32s31))] +#[path = "hp_sys_clkrst_s31.rs"] mod hp_sys_clkrst; #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))] diff --git a/esp-hal/src/i2s/pdm/regs_v2.rs b/esp-hal/src/i2s/pdm/regs_v2.rs index f8edd38f082..3cf4f18cbe2 100644 --- a/esp-hal/src/i2s/pdm/regs_v2.rs +++ b/esp-hal/src/i2s/pdm/regs_v2.rs @@ -207,7 +207,7 @@ fn configure_rx(i2s: &Info, config: &super::PdmRxConfig) -> Result<(), PdmError> i2s.set_rx_clock(clock.dividers); - #[cfg(all(i2s_supports_pdm2pcm, esp32p4))] + #[cfg(all(i2s_supports_pdm2pcm, i2s_supports_pdm_rx_hp_filter))] { let dsr16 = config.clock.downsample_rate == super::PdmDownsampleRate::Dsr16s; let freq_x10 = (config.slot.hp_cut_off_freq_hz * 10.0) as u32; @@ -228,7 +228,7 @@ fn configure_rx(i2s: &Info, config: &super::PdmRxConfig) -> Result<(), PdmError> i2s.regs().rx_conf().modify(|_, w| { w.rx_pdm_en().set_bit(); w.rx_tdm_en().clear_bit(); - #[cfg(all(i2s_supports_pdm2pcm, not(esp32p4)))] + #[cfg(all(i2s_supports_pdm2pcm, not(i2s_supports_pdm_rx_hp_filter)))] { w.rx_pdm2pcm_en().bit(pcm); w.rx_pdm_sinc_dsr_16_en() diff --git a/esp-hal/src/soc/esp32s31/mod.rs b/esp-hal/src/soc/esp32s31/mod.rs index 4a36d8969ab..e676615b5b4 100644 --- a/esp-hal/src/soc/esp32s31/mod.rs +++ b/esp-hal/src/soc/esp32s31/mod.rs @@ -16,7 +16,9 @@ pub(crate) use esp32s31 as pac; #[cfg(i2s_driver_supported)] #[cfg_attr(not(feature = "unstable"), allow(unused))] pub(crate) fn i2s_sclk_frequency() -> u32 { - clocks::pll_f160m_frequency() + // XTAL matches `i2s.default_clock_source` (mux 0). Mux 1 is APLL, which has + // less MCLK jitter; switch both to APLL once the clock tree can configure it. + clocks::xtal_clk_frequency() } pub(crate) fn enable_branch_predictor() { diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index aa7d78978fb..ee81fdc63d9 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -7925,6 +7925,8 @@ impl Chip { "soc_has_hp_sys_clkrst", "soc_has_i2c0", "soc_has_i2c1", + "soc_has_i2s0", + "soc_has_i2s1", "soc_has_interrupt_core0", "soc_has_interrupt_core1", "soc_has_io_mux", @@ -7987,6 +7989,7 @@ impl Chip { "i2c_master_driver_supported", "spi_master_driver_supported", "spi_slave_driver_supported", + "i2s_driver_supported", "rmt_driver_supported", "sdmmc_driver_supported", "usb_otg_hs_driver_supported", @@ -8017,6 +8020,8 @@ impl Chip { "spi_master_spi3", "spi_slave_spi2", "spi_slave_spi3", + "i2s_i2s0", + "i2s_i2s1", "adc_adc1", "adc_adc2", "timergroup_timg0", @@ -8056,6 +8061,20 @@ impl Chip { "spi_master_has_octal", "spi_master_has_app_interrupts", "spi_master_has_dma_segmented_transfer", + "i2s_version=\"3\"", + "i2s_version_is_set", + "i2s_default_clock_source=\"0\"", + "i2s_default_clock_source_is_set", + "i2s_mclk_divider_bit_width=\"8\"", + "i2s_mclk_divider_bit_width_is_set", + "i2s_max_ws_width=\"512\"", + "i2s_max_ws_width_is_set", + "i2s_clock_configured_by_hp_sys_clkrst", + "i2s_supports_pdm_rx_hp_filter", + "i2s_supports_pdm_tx", + "i2s_supports_pdm_rx", + "i2s_supports_pcm2pdm", + "i2s_supports_pdm2pcm", "rmt_ram_start=\"540366848\"", "rmt_channel_ram_size=\"48\"", "rmt_has_tx_immediate_stop", @@ -8115,6 +8134,8 @@ impl Chip { "dma_supports_mem2mem", "uhci_supports_dma", "uhci_dma_engine=\"AHB_GDMA\"", + "i2s_supports_dma", + "i2s_dma_engine=\"AHB_GDMA\"", "spi_master_supports_dma", "spi_master_dma_engine=\"AXI_GDMA\"", "spi_slave_supports_dma", @@ -8215,6 +8236,8 @@ impl Chip { "cargo:rustc-cfg=soc_has_hp_sys_clkrst", "cargo:rustc-cfg=soc_has_i2c0", "cargo:rustc-cfg=soc_has_i2c1", + "cargo:rustc-cfg=soc_has_i2s0", + "cargo:rustc-cfg=soc_has_i2s1", "cargo:rustc-cfg=soc_has_interrupt_core0", "cargo:rustc-cfg=soc_has_interrupt_core1", "cargo:rustc-cfg=soc_has_io_mux", @@ -8277,6 +8300,7 @@ impl Chip { "cargo:rustc-cfg=i2c_master_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", "cargo:rustc-cfg=spi_slave_driver_supported", + "cargo:rustc-cfg=i2s_driver_supported", "cargo:rustc-cfg=rmt_driver_supported", "cargo:rustc-cfg=sdmmc_driver_supported", "cargo:rustc-cfg=usb_otg_hs_driver_supported", @@ -8307,6 +8331,8 @@ impl Chip { "cargo:rustc-cfg=spi_master_spi3", "cargo:rustc-cfg=spi_slave_spi2", "cargo:rustc-cfg=spi_slave_spi3", + "cargo:rustc-cfg=i2s_i2s0", + "cargo:rustc-cfg=i2s_i2s1", "cargo:rustc-cfg=adc_adc1", "cargo:rustc-cfg=adc_adc2", "cargo:rustc-cfg=timergroup_timg0", @@ -8346,6 +8372,20 @@ impl Chip { "cargo:rustc-cfg=spi_master_has_octal", "cargo:rustc-cfg=spi_master_has_app_interrupts", "cargo:rustc-cfg=spi_master_has_dma_segmented_transfer", + "cargo:rustc-cfg=i2s_version=\"3\"", + "cargo:rustc-cfg=i2s_version_is_set", + "cargo:rustc-cfg=i2s_default_clock_source=\"0\"", + "cargo:rustc-cfg=i2s_default_clock_source_is_set", + "cargo:rustc-cfg=i2s_mclk_divider_bit_width=\"8\"", + "cargo:rustc-cfg=i2s_mclk_divider_bit_width_is_set", + "cargo:rustc-cfg=i2s_max_ws_width=\"512\"", + "cargo:rustc-cfg=i2s_max_ws_width_is_set", + "cargo:rustc-cfg=i2s_clock_configured_by_hp_sys_clkrst", + "cargo:rustc-cfg=i2s_supports_pdm_rx_hp_filter", + "cargo:rustc-cfg=i2s_supports_pdm_tx", + "cargo:rustc-cfg=i2s_supports_pdm_rx", + "cargo:rustc-cfg=i2s_supports_pcm2pdm", + "cargo:rustc-cfg=i2s_supports_pdm2pcm", "cargo:rustc-cfg=rmt_ram_start=\"540366848\"", "cargo:rustc-cfg=rmt_channel_ram_size=\"48\"", "cargo:rustc-cfg=rmt_has_tx_immediate_stop", @@ -8405,6 +8445,8 @@ impl Chip { "cargo:rustc-cfg=dma_supports_mem2mem", "cargo:rustc-cfg=uhci_supports_dma", "cargo:rustc-cfg=uhci_dma_engine=\"AHB_GDMA\"", + "cargo:rustc-cfg=i2s_supports_dma", + "cargo:rustc-cfg=i2s_dma_engine=\"AHB_GDMA\"", "cargo:rustc-cfg=spi_master_supports_dma", "cargo:rustc-cfg=spi_master_dma_engine=\"AXI_GDMA\"", "cargo:rustc-cfg=spi_slave_supports_dma", @@ -9353,8 +9395,8 @@ impl Chip { "spi_master_version, values(\"1\",\"3\",\"2\")", "spi_master_fifo_size, values(\"64\",\"72\")", "i2s_version, values(\"1\",\"2\",\"3\")", - "i2s_default_clock_source, values(\"2\",\"1\",\"3\")", - "i2s_mclk_divider_bit_width, values(\"6\",\"9\")", + "i2s_default_clock_source, values(\"2\",\"1\",\"3\",\"0\")", + "i2s_mclk_divider_bit_width, values(\"6\",\"9\",\"8\")", "i2s_max_ws_width, values(\"128\",\"512\")", "rmt_ram_start, \ values(\"1073047552\",\"1610703872\",\"1610638336\",\"1610642432\",\"1342842880\",\"\ diff --git a/esp-metadata-generated/src/_generated_esp32s31.rs b/esp-metadata-generated/src/_generated_esp32s31.rs index 99f7554cf6d..1e218ede1e3 100644 --- a/esp-metadata-generated/src/_generated_esp32s31.rs +++ b/esp-metadata-generated/src/_generated_esp32s31.rs @@ -226,6 +226,51 @@ macro_rules! property { ("spi_master.dma_can_access_flash") => { false }; + ("i2s.version") => { + 3 + }; + ("i2s.version", str) => { + stringify!(3) + }; + ("i2s.default_clock_source") => { + 0 + }; + ("i2s.default_clock_source", str) => { + stringify!(0) + }; + ("i2s.mclk_divider_bit_width") => { + 8 + }; + ("i2s.mclk_divider_bit_width", str) => { + stringify!(8) + }; + ("i2s.max_ws_width") => { + 512 + }; + ("i2s.max_ws_width", str) => { + stringify!(512) + }; + ("i2s.clock_configured_by_pcr") => { + false + }; + ("i2s.clock_configured_by_hp_sys_clkrst") => { + true + }; + ("i2s.supports_pdm_rx_hp_filter") => { + true + }; + ("i2s.supports_pdm_tx") => { + true + }; + ("i2s.supports_pdm_rx") => { + true + }; + ("i2s.supports_pcm2pdm") => { + true + }; + ("i2s.supports_pdm2pcm") => { + true + }; ("rmt.ram_start") => { 540366848 }; @@ -986,16 +1031,16 @@ macro_rules! for_each_dma_channel { _for_each_inner_dma_channel!(("AHB_GDMA", any_channel = AhbGdmaChannel)); _for_each_inner_dma_channel!(("AXI_GDMA", any_channel = AxiGdmaChannel)); _for_each_inner_dma_channel!(("AHB_GDMA", DMA_CH0, 0, interrupt_in = - AHB_PDMA_IN_CH0, interrupt_out = AHB_PDMA_OUT_CH0, compatible = [UHCI0])); - _for_each_inner_dma_channel!(("AHB_GDMA", DMA_CH1, 1, interrupt_in = - AHB_PDMA_IN_CH1, interrupt_out = AHB_PDMA_OUT_CH1, compatible = [UHCI0])); - _for_each_inner_dma_channel!(("AHB_GDMA", DMA_CH2, 2, interrupt_in = - AHB_PDMA_IN_CH2, interrupt_out = AHB_PDMA_OUT_CH2, compatible = [UHCI0])); - _for_each_inner_dma_channel!(("AHB_GDMA", DMA_CH3, 3, interrupt_in = - AHB_PDMA_IN_CH3, interrupt_out = AHB_PDMA_OUT_CH3, compatible = [UHCI0])); - _for_each_inner_dma_channel!(("AHB_GDMA", DMA_CH4, 4, interrupt_in = - AHB_PDMA_IN_CH4, interrupt_out = AHB_PDMA_OUT_CH4, compatible = [UHCI0])); - _for_each_inner_dma_channel!(("AXI_GDMA", DMA_AXI_CH0, 0, interrupt_in = + AHB_PDMA_IN_CH0, interrupt_out = AHB_PDMA_OUT_CH0, compatible = [UHCI0, I2S0, + I2S1])); _for_each_inner_dma_channel!(("AHB_GDMA", DMA_CH1, 1, interrupt_in = + AHB_PDMA_IN_CH1, interrupt_out = AHB_PDMA_OUT_CH1, compatible = [UHCI0, I2S0, + I2S1])); _for_each_inner_dma_channel!(("AHB_GDMA", DMA_CH2, 2, interrupt_in = + AHB_PDMA_IN_CH2, interrupt_out = AHB_PDMA_OUT_CH2, compatible = [UHCI0, I2S0, + I2S1])); _for_each_inner_dma_channel!(("AHB_GDMA", DMA_CH3, 3, interrupt_in = + AHB_PDMA_IN_CH3, interrupt_out = AHB_PDMA_OUT_CH3, compatible = [UHCI0, I2S0, + I2S1])); _for_each_inner_dma_channel!(("AHB_GDMA", DMA_CH4, 4, interrupt_in = + AHB_PDMA_IN_CH4, interrupt_out = AHB_PDMA_OUT_CH4, compatible = [UHCI0, I2S0, + I2S1])); _for_each_inner_dma_channel!(("AXI_GDMA", DMA_AXI_CH0, 0, interrupt_in = AXI_PDMA_IN_CH0, interrupt_out = AXI_PDMA_OUT_CH0, compatible = [SPI2, SPI3, AES, SHA])); _for_each_inner_dma_channel!(("AXI_GDMA", DMA_AXI_CH1, 1, interrupt_in = AXI_PDMA_IN_CH1, interrupt_out = AXI_PDMA_OUT_CH1, compatible = [SPI2, SPI3, AES, @@ -1008,19 +1053,20 @@ macro_rules! for_each_dma_channel { any_channel = AhbGdmaChannel), ("AXI_GDMA", any_channel = AxiGdmaChannel))); _for_each_inner_dma_channel!((shared)); _for_each_inner_dma_channel!((split("AHB_GDMA", DMA_CH0, 0, interrupt_in = - AHB_PDMA_IN_CH0, interrupt_out = AHB_PDMA_OUT_CH0, compatible = [UHCI0]), - ("AHB_GDMA", DMA_CH1, 1, interrupt_in = AHB_PDMA_IN_CH1, interrupt_out = - AHB_PDMA_OUT_CH1, compatible = [UHCI0]), ("AHB_GDMA", DMA_CH2, 2, interrupt_in = - AHB_PDMA_IN_CH2, interrupt_out = AHB_PDMA_OUT_CH2, compatible = [UHCI0]), - ("AHB_GDMA", DMA_CH3, 3, interrupt_in = AHB_PDMA_IN_CH3, interrupt_out = - AHB_PDMA_OUT_CH3, compatible = [UHCI0]), ("AHB_GDMA", DMA_CH4, 4, interrupt_in = - AHB_PDMA_IN_CH4, interrupt_out = AHB_PDMA_OUT_CH4, compatible = [UHCI0]), - ("AXI_GDMA", DMA_AXI_CH0, 0, interrupt_in = AXI_PDMA_IN_CH0, interrupt_out = - AXI_PDMA_OUT_CH0, compatible = [SPI2, SPI3, AES, SHA]), ("AXI_GDMA", DMA_AXI_CH1, - 1, interrupt_in = AXI_PDMA_IN_CH1, interrupt_out = AXI_PDMA_OUT_CH1, compatible = - [SPI2, SPI3, AES, SHA]), ("AXI_GDMA", DMA_AXI_CH2, 2, interrupt_in = - AXI_PDMA_IN_CH2, interrupt_out = AXI_PDMA_OUT_CH2, compatible = [SPI2, SPI3, AES, - SHA]))); _for_each_inner_dma_channel!((no_own_interrupt)); + AHB_PDMA_IN_CH0, interrupt_out = AHB_PDMA_OUT_CH0, compatible = [UHCI0, I2S0, + I2S1]), ("AHB_GDMA", DMA_CH1, 1, interrupt_in = AHB_PDMA_IN_CH1, interrupt_out = + AHB_PDMA_OUT_CH1, compatible = [UHCI0, I2S0, I2S1]), ("AHB_GDMA", DMA_CH2, 2, + interrupt_in = AHB_PDMA_IN_CH2, interrupt_out = AHB_PDMA_OUT_CH2, compatible = + [UHCI0, I2S0, I2S1]), ("AHB_GDMA", DMA_CH3, 3, interrupt_in = AHB_PDMA_IN_CH3, + interrupt_out = AHB_PDMA_OUT_CH3, compatible = [UHCI0, I2S0, I2S1]), ("AHB_GDMA", + DMA_CH4, 4, interrupt_in = AHB_PDMA_IN_CH4, interrupt_out = AHB_PDMA_OUT_CH4, + compatible = [UHCI0, I2S0, I2S1]), ("AXI_GDMA", DMA_AXI_CH0, 0, interrupt_in = + AXI_PDMA_IN_CH0, interrupt_out = AXI_PDMA_OUT_CH0, compatible = [SPI2, SPI3, AES, + SHA]), ("AXI_GDMA", DMA_AXI_CH1, 1, interrupt_in = AXI_PDMA_IN_CH1, interrupt_out + = AXI_PDMA_OUT_CH1, compatible = [SPI2, SPI3, AES, SHA]), ("AXI_GDMA", + DMA_AXI_CH2, 2, interrupt_in = AXI_PDMA_IN_CH2, interrupt_out = AXI_PDMA_OUT_CH2, + compatible = [SPI2, SPI3, AES, SHA]))); + _for_each_inner_dma_channel!((no_own_interrupt)); }; } #[macro_export] @@ -1029,10 +1075,20 @@ macro_rules! for_each_dma_channel_peri_pair { ($($pattern:tt => $code:tt;)*) => { macro_rules! _for_each_inner_dma_channel_peri_pair { $(($pattern) => $code;)* ($other : tt) => {} } _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", - DMA_CH0, UHCI0)); _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH1, - UHCI0)); _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH2, UHCI0)); + DMA_CH0, UHCI0)); _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH0, + I2S0)); _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH0, I2S1)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH1, UHCI0)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH1, I2S0)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH1, I2S1)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH2, UHCI0)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH2, I2S0)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH2, I2S1)); _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH3, UHCI0)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH3, I2S0)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH3, I2S1)); _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH4, UHCI0)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH4, I2S0)); + _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", DMA_CH4, I2S1)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", DMA_AXI_CH0, SPI2)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", DMA_AXI_CH0, SPI3)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", DMA_AXI_CH0, AES)); @@ -1046,24 +1102,32 @@ macro_rules! for_each_dma_channel_peri_pair { _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", DMA_AXI_CH2, AES)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", DMA_AXI_CH2, SHA)); _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", any_channel = AhbGdmaChannel, - UHCI0)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", any_channel = - AxiGdmaChannel, SPI2)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", - any_channel = AxiGdmaChannel, SPI3)); + UHCI0)); _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", any_channel = + AhbGdmaChannel, I2S0)); _for_each_inner_dma_channel_peri_pair!(("AHB_GDMA", + any_channel = AhbGdmaChannel, I2S1)); + _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", any_channel = AxiGdmaChannel, + SPI2)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", any_channel = + AxiGdmaChannel, SPI3)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", + any_channel = AxiGdmaChannel, AES)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", any_channel = AxiGdmaChannel, - AES)); _for_each_inner_dma_channel_peri_pair!(("AXI_GDMA", any_channel = - AxiGdmaChannel, SHA)); - _for_each_inner_dma_channel_peri_pair!((channels("AHB_GDMA", DMA_CH0, UHCI0), - ("AHB_GDMA", DMA_CH1, UHCI0), ("AHB_GDMA", DMA_CH2, UHCI0), ("AHB_GDMA", DMA_CH3, - UHCI0), ("AHB_GDMA", DMA_CH4, UHCI0), ("AXI_GDMA", DMA_AXI_CH0, SPI2), - ("AXI_GDMA", DMA_AXI_CH0, SPI3), ("AXI_GDMA", DMA_AXI_CH0, AES), ("AXI_GDMA", - DMA_AXI_CH0, SHA), ("AXI_GDMA", DMA_AXI_CH1, SPI2), ("AXI_GDMA", DMA_AXI_CH1, - SPI3), ("AXI_GDMA", DMA_AXI_CH1, AES), ("AXI_GDMA", DMA_AXI_CH1, SHA), - ("AXI_GDMA", DMA_AXI_CH2, SPI2), ("AXI_GDMA", DMA_AXI_CH2, SPI3), ("AXI_GDMA", - DMA_AXI_CH2, AES), ("AXI_GDMA", DMA_AXI_CH2, SHA))); + SHA)); _for_each_inner_dma_channel_peri_pair!((channels("AHB_GDMA", DMA_CH0, + UHCI0), ("AHB_GDMA", DMA_CH0, I2S0), ("AHB_GDMA", DMA_CH0, I2S1), ("AHB_GDMA", + DMA_CH1, UHCI0), ("AHB_GDMA", DMA_CH1, I2S0), ("AHB_GDMA", DMA_CH1, I2S1), + ("AHB_GDMA", DMA_CH2, UHCI0), ("AHB_GDMA", DMA_CH2, I2S0), ("AHB_GDMA", DMA_CH2, + I2S1), ("AHB_GDMA", DMA_CH3, UHCI0), ("AHB_GDMA", DMA_CH3, I2S0), ("AHB_GDMA", + DMA_CH3, I2S1), ("AHB_GDMA", DMA_CH4, UHCI0), ("AHB_GDMA", DMA_CH4, I2S0), + ("AHB_GDMA", DMA_CH4, I2S1), ("AXI_GDMA", DMA_AXI_CH0, SPI2), ("AXI_GDMA", + DMA_AXI_CH0, SPI3), ("AXI_GDMA", DMA_AXI_CH0, AES), ("AXI_GDMA", DMA_AXI_CH0, + SHA), ("AXI_GDMA", DMA_AXI_CH1, SPI2), ("AXI_GDMA", DMA_AXI_CH1, SPI3), + ("AXI_GDMA", DMA_AXI_CH1, AES), ("AXI_GDMA", DMA_AXI_CH1, SHA), ("AXI_GDMA", + DMA_AXI_CH2, SPI2), ("AXI_GDMA", DMA_AXI_CH2, SPI3), ("AXI_GDMA", DMA_AXI_CH2, + AES), ("AXI_GDMA", DMA_AXI_CH2, SHA))); _for_each_inner_dma_channel_peri_pair!((any_channels("AHB_GDMA", any_channel = - AhbGdmaChannel, UHCI0), ("AXI_GDMA", any_channel = AxiGdmaChannel, SPI2), - ("AXI_GDMA", any_channel = AxiGdmaChannel, SPI3), ("AXI_GDMA", any_channel = - AxiGdmaChannel, AES), ("AXI_GDMA", any_channel = AxiGdmaChannel, SHA))); + AhbGdmaChannel, UHCI0), ("AHB_GDMA", any_channel = AhbGdmaChannel, I2S0), + ("AHB_GDMA", any_channel = AhbGdmaChannel, I2S1), ("AXI_GDMA", any_channel = + AxiGdmaChannel, SPI2), ("AXI_GDMA", any_channel = AxiGdmaChannel, SPI3), + ("AXI_GDMA", any_channel = AxiGdmaChannel, AES), ("AXI_GDMA", any_channel = + AxiGdmaChannel, SHA))); }; } #[macro_export] @@ -1100,6 +1164,14 @@ macro_rules! with_aes_dma_engine { } #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! with_i2s_dma_engine { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _with_inner_i2s_dma_engine { $(($pattern) => $code;)* ($other : tt) + => {} } _with_inner_i2s_dma_engine!(("AHB_GDMA", AhbGdmaChannel)); + }; +} +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] macro_rules! with_spi_master_dma_engine { ($($pattern:tt => $code:tt;)*) => { macro_rules! _with_inner_spi_master_dma_engine { $(($pattern) => $code;)* ($other @@ -3806,6 +3878,10 @@ macro_rules! implement_peripheral_clocks { I2c0, /// I2C1 peripheral clock signal I2c1, + /// I2S0 peripheral clock signal + I2s0, + /// I2S1 peripheral clock signal + I2s1, /// RMT peripheral clock signal Rmt, /// RSA peripheral clock signal @@ -3852,6 +3928,8 @@ macro_rules! implement_peripheral_clocks { Self::GpioSd, Self::I2c0, Self::I2c1, + Self::I2s0, + Self::I2s1, Self::Rmt, Self::Rsa, Self::SdioHost, @@ -3912,6 +3990,22 @@ macro_rules! implement_peripheral_clocks { .i2c_ctrl0(1) .modify(|_, w| w.apb_clk_en().bit(enable).clk_en().bit(enable)); } + Peripheral::I2s0 => { + crate::peripherals::HP_SYS_CLKRST::regs() + .i2s0_ctrl0() + .modify(|_, w| w.apb_clk_en().bit(enable)); + crate::peripherals::HP_ALIVE_SYS::regs() + .hp_pad_i2s0_ctrl() + .modify(|_, w| w.hp_pad_i2s0_mclk_en().bit(enable)); + } + Peripheral::I2s1 => { + crate::peripherals::HP_SYS_CLKRST::regs() + .i2s1_ctrl0() + .modify(|_, w| w.apb_clk_en().bit(enable)); + crate::peripherals::HP_ALIVE_SYS::regs() + .hp_pad_i2s1_ctrl() + .modify(|_, w| w.hp_pad_i2s1_mclk_en().bit(enable)); + } Peripheral::Rmt => { crate::peripherals::HP_SYS_CLKRST::regs() .rmt_ctrl0() @@ -4037,6 +4131,16 @@ macro_rules! implement_peripheral_clocks { .i2c_ctrl0(1) .modify(|_, w| w.rst_en().bit(reset)); } + Peripheral::I2s0 => { + crate::peripherals::HP_SYS_CLKRST::regs() + .i2s0_ctrl0() + .modify(|_, w| w.apb_rst_en().bit(reset)); + } + Peripheral::I2s1 => { + crate::peripherals::HP_SYS_CLKRST::regs() + .i2s1_ctrl0() + .modify(|_, w| w.apb_rst_en().bit(reset)); + } Peripheral::Rmt => { crate::peripherals::HP_SYS_CLKRST::regs() .rmt_ctrl0() @@ -4212,7 +4316,16 @@ macro_rules! for_each_i2c_master { macro_rules! for_each_i2s { ($($pattern:tt => $code:tt;)*) => { macro_rules! _for_each_inner_i2s { $(($pattern) => $code;)* ($other : tt) => {} } - _for_each_inner_i2s!((names)); _for_each_inner_i2s!((all)); + _for_each_inner_i2s!((I2S0)); _for_each_inner_i2s!((I2S1)); + _for_each_inner_i2s!((I2S0, I2s0, I2S0_MCLK, I2S0O_BCK, I2S0O_WS, I2S0I_BCK, + I2S0I_WS, [I2S0O_SD, I2S0O_SD1], [I2S0I_SD, I2S0I_SD1, I2S0I_SD2, I2S0I_SD3], + true, true, true, true)); _for_each_inner_i2s!((I2S1, I2s1, I2S1_MCLK, I2S1O_BCK, + I2S1O_WS, I2S1I_BCK, I2S1I_WS, [I2S1O_SD], [I2S1I_SD], true, true, false, + false)); _for_each_inner_i2s!((names(I2S0), (I2S1))); + _for_each_inner_i2s!((all(I2S0, I2s0, I2S0_MCLK, I2S0O_BCK, I2S0O_WS, I2S0I_BCK, + I2S0I_WS, [I2S0O_SD, I2S0O_SD1], [I2S0I_SD, I2S0I_SD1, I2S0I_SD2, I2S0I_SD3], + true, true, true, true), (I2S1, I2s1, I2S1_MCLK, I2S1O_BCK, I2S1O_WS, I2S1I_BCK, + I2S1I_WS, [I2S1O_SD], [I2S1I_SD], true, true, false, false))); }; } /// This macro can be used to generate code for each peripheral instance of the UART driver. @@ -4608,8 +4721,13 @@ macro_rules! for_each_peripheral { _for_each_inner_peripheral!((@ peri_type #[doc = "I2C1 peripheral singleton"] I2C1 <= I2C1(I2C1 : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }))); _for_each_inner_peripheral!((@ peri_type #[doc = - "INTERRUPT_CORE0 peripheral singleton"] INTERRUPT_CORE0 <= INTERRUPT_CORE0() - (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = + "I2S0 peripheral singleton"] I2S0 <= I2S0(I2S0 : { bind_peri_interrupt, + enable_peri_interrupt, disable_peri_interrupt }) (unstable))); + _for_each_inner_peripheral!((@ peri_type #[doc = "I2S1 peripheral singleton"] + I2S1 <= I2S1(I2S1 : { bind_peri_interrupt, enable_peri_interrupt, + disable_peri_interrupt }) (unstable))); _for_each_inner_peripheral!((@ peri_type + #[doc = "INTERRUPT_CORE0 peripheral singleton"] INTERRUPT_CORE0 <= + INTERRUPT_CORE0() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "INTERRUPT_CORE1 peripheral singleton"] INTERRUPT_CORE1 <= INTERRUPT_CORE1() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "IO_MUX peripheral singleton"] IO_MUX <= IO_MUX() (unstable))); @@ -4766,6 +4884,8 @@ macro_rules! for_each_peripheral { _for_each_inner_peripheral!((HP_ALIVE_SYS(unstable))); _for_each_inner_peripheral!((HP_SYS_CLKRST(unstable))); _for_each_inner_peripheral!((I2C0)); _for_each_inner_peripheral!((I2C1)); + _for_each_inner_peripheral!((I2S0(unstable))); + _for_each_inner_peripheral!((I2S1(unstable))); _for_each_inner_peripheral!((INTERRUPT_CORE0(unstable))); _for_each_inner_peripheral!((INTERRUPT_CORE1(unstable))); _for_each_inner_peripheral!((IO_MUX(unstable))); @@ -4815,9 +4935,11 @@ macro_rules! for_each_peripheral { _for_each_inner_peripheral!((FROM_CPU_INTR2(unstable))); _for_each_inner_peripheral!((FROM_CPU_INTR3(unstable))); _for_each_inner_peripheral!((UHCI0, Uhci0, 0, AhbGdmaChannel)); + _for_each_inner_peripheral!((I2S0, I2s0, 1, AhbGdmaChannel)); _for_each_inner_peripheral!((SPI2, Spi2, 1, AxiGdmaChannel)); _for_each_inner_peripheral!((SPI3, Spi3, 2, AxiGdmaChannel)); _for_each_inner_peripheral!((AES, Aes, 4, AxiGdmaChannel)); + _for_each_inner_peripheral!((I2S1, I2s1, 5, AhbGdmaChannel)); _for_each_inner_peripheral!((SHA, Sha, 5, AxiGdmaChannel)); _for_each_inner_peripheral!((all(@ peri_type #[doc = "GPIO0 peripheral singleton"] GPIO0 <= virtual()), (@ peri_type #[doc = @@ -5052,8 +5174,12 @@ macro_rules! for_each_peripheral { : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt })), (@ peri_type #[doc = "I2C1 peripheral singleton"] I2C1 <= I2C1(I2C1 : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt })), (@ - peri_type #[doc = "INTERRUPT_CORE0 peripheral singleton"] INTERRUPT_CORE0 <= - INTERRUPT_CORE0() (unstable)), (@ peri_type #[doc = + peri_type #[doc = "I2S0 peripheral singleton"] I2S0 <= I2S0(I2S0 : { + bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) + (unstable)), (@ peri_type #[doc = "I2S1 peripheral singleton"] I2S1 <= I2S1(I2S1 + : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) + (unstable)), (@ peri_type #[doc = "INTERRUPT_CORE0 peripheral singleton"] + INTERRUPT_CORE0 <= INTERRUPT_CORE0() (unstable)), (@ peri_type #[doc = "INTERRUPT_CORE1 peripheral singleton"] INTERRUPT_CORE1 <= INTERRUPT_CORE1() (unstable)), (@ peri_type #[doc = "IO_MUX peripheral singleton"] IO_MUX <= IO_MUX() (unstable)), (@ peri_type #[doc = "IOMUX_MSPI_PIN peripheral singleton"] @@ -5144,23 +5270,25 @@ macro_rules! for_each_peripheral { (CLIC(unstable)), (ECC(unstable)), (GPIO(unstable)), (GPIO_SD(unstable)), (HP_APM(unstable)), (HP_MEM_APM(unstable)), (HP_SYS(unstable)), (HP_ALIVE_SYS(unstable)), (HP_SYS_CLKRST(unstable)), (I2C0), (I2C1), - (INTERRUPT_CORE0(unstable)), (INTERRUPT_CORE1(unstable)), (IO_MUX(unstable)), - (LP_AON_CLK_RST(unstable)), (LP_APM(unstable)), (LP_GPIO(unstable)), - (LP_IO_MUX(unstable)), (LP_PERI(unstable)), (LP_SYS(unstable)), - (LP_TEE(unstable)), (LP_WDT(unstable)), (LPWR(unstable)), - (MEM_MONITOR(unstable)), (MODEM_LPCON(unstable)), (MODEM_SYSCON(unstable)), - (PAU(unstable)), (PMU(unstable)), (RTC_TIMER(unstable)), (RNG(unstable)), - (RMT(unstable)), (RSA(unstable)), (SPI0(unstable)), (SPI1(unstable)), (SPI2), - (SPI3), (AXI_GDMA(unstable)), (DMA(unstable)), (SHA(unstable)), - (SYSTEM(unstable)), (SYSTIMER(unstable)), (SDHOST(unstable)), (TEE(unstable)), - (TIMG0(unstable)), (TIMG1(unstable)), (UART0), (UART1), (UART2), (UART3), - (UHCI0(unstable)), (USB_DEVICE(unstable)), (USB_HS(unstable)), (ADC1(unstable)), - (ADC2(unstable)), (FLASH(unstable)), (PSRAM(unstable)), - (GPIO_DEDICATED(unstable)), (CPU_CTRL(unstable)), (FROM_CPU_INTR0(unstable)), - (FROM_CPU_INTR1(unstable)), (FROM_CPU_INTR2(unstable)), - (FROM_CPU_INTR3(unstable)))); _for_each_inner_peripheral!((dma_eligible(UHCI0, - Uhci0, 0, AhbGdmaChannel), (SPI2, Spi2, 1, AxiGdmaChannel), (SPI3, Spi3, 2, - AxiGdmaChannel), (AES, Aes, 4, AxiGdmaChannel), (SHA, Sha, 5, AxiGdmaChannel))); + (I2S0(unstable)), (I2S1(unstable)), (INTERRUPT_CORE0(unstable)), + (INTERRUPT_CORE1(unstable)), (IO_MUX(unstable)), (LP_AON_CLK_RST(unstable)), + (LP_APM(unstable)), (LP_GPIO(unstable)), (LP_IO_MUX(unstable)), + (LP_PERI(unstable)), (LP_SYS(unstable)), (LP_TEE(unstable)), (LP_WDT(unstable)), + (LPWR(unstable)), (MEM_MONITOR(unstable)), (MODEM_LPCON(unstable)), + (MODEM_SYSCON(unstable)), (PAU(unstable)), (PMU(unstable)), + (RTC_TIMER(unstable)), (RNG(unstable)), (RMT(unstable)), (RSA(unstable)), + (SPI0(unstable)), (SPI1(unstable)), (SPI2), (SPI3), (AXI_GDMA(unstable)), + (DMA(unstable)), (SHA(unstable)), (SYSTEM(unstable)), (SYSTIMER(unstable)), + (SDHOST(unstable)), (TEE(unstable)), (TIMG0(unstable)), (TIMG1(unstable)), + (UART0), (UART1), (UART2), (UART3), (UHCI0(unstable)), (USB_DEVICE(unstable)), + (USB_HS(unstable)), (ADC1(unstable)), (ADC2(unstable)), (FLASH(unstable)), + (PSRAM(unstable)), (GPIO_DEDICATED(unstable)), (CPU_CTRL(unstable)), + (FROM_CPU_INTR0(unstable)), (FROM_CPU_INTR1(unstable)), + (FROM_CPU_INTR2(unstable)), (FROM_CPU_INTR3(unstable)))); + _for_each_inner_peripheral!((dma_eligible(UHCI0, Uhci0, 0, AhbGdmaChannel), + (I2S0, I2s0, 1, AhbGdmaChannel), (SPI2, Spi2, 1, AxiGdmaChannel), (SPI3, Spi3, 2, + AxiGdmaChannel), (AES, Aes, 4, AxiGdmaChannel), (I2S1, I2s1, 5, AhbGdmaChannel), + (SHA, Sha, 5, AxiGdmaChannel))); }; } /// This macro can be used to generate code for each `GPIOn` instance. @@ -6309,12 +6437,21 @@ macro_rules! define_io_mux_signals { U3RXD = 137, U3CTS = 138, U3DSR = 139, - I2SO_BCK = 25, - I2S_MCLK = 26, - I2SO_WS = 27, - I2SI_SD = 28, - I2SI_BCK = 29, - I2SI_WS = 30, + I2S0O_BCK = 25, + I2S0_MCLK = 26, + I2S0O_WS = 27, + I2S0I_SD = 28, + I2S0I_BCK = 29, + I2S0I_WS = 30, + I2S0I_SD1 = 43, + I2S0I_SD2 = 44, + I2S0I_SD3 = 45, + I2S1O_BCK = 31, + I2S1_MCLK = 32, + I2S1O_WS = 33, + I2S1I_SD = 34, + I2S1I_BCK = 35, + I2S1I_WS = 36, FSPICLK = 53, FSPIQ = 54, FSPID = 55, @@ -6330,8 +6467,6 @@ macro_rules! define_io_mux_signals { FSPICS3 = 65, FSPICS4 = 66, FSPICS5 = 67, - SPI3_CS2 = 45, - SPI3_CS1 = 46, SPI3_CK = 47, SPI3_Q = 48, SPI3_D = 49, @@ -6505,12 +6640,19 @@ macro_rules! define_io_mux_signals { U3TXD = 137, U3RTS = 138, U3DTR = 116, - I2SO_BCK = 25, - I2S_MCLK = 26, - I2SO_WS = 27, - I2SO_SD = 28, - I2SI_BCK = 29, - I2SI_WS = 30, + I2S0O_BCK = 25, + I2S0_MCLK = 26, + I2S0O_WS = 27, + I2S0O_SD = 28, + I2S0I_BCK = 29, + I2S0I_WS = 30, + I2S0O_SD1 = 43, + I2S1O_BCK = 31, + I2S1_MCLK = 32, + I2S1O_WS = 33, + I2S1O_SD = 34, + I2S1I_BCK = 35, + I2S1I_WS = 36, FSPICLK = 53, FSPIQ = 54, FSPID = 55, diff --git a/esp-metadata/devices/esp32s31/clocks.toml b/esp-metadata/devices/esp32s31/clocks.toml index 0d9d8d843ee..a63a669042f 100644 --- a/esp-metadata/devices/esp32s31/clocks.toml +++ b/esp-metadata/devices/esp32s31/clocks.toml @@ -149,6 +149,18 @@ peripheral_clocks = { templates = [ { name = "Spi3", template_params = { peripheral = "gpspi3" } }, { name = "I2c0", template_params = { clk_en = "{{apb_clk}} {{peri_clk}}", ctrl_reg = "i2c_ctrl0(0)" } }, { name = "I2c1", template_params = { clk_en = "{{apb_clk}} {{peri_clk}}", ctrl_reg = "i2c_ctrl0(1)" } }, + { name = "I2s0", template_params = { + clk_en = "{{apb_clk}}", + rst_en = ".apb_rst_en().bit(reset)", + ctrl_reg = "i2s0_ctrl0()", + clk_en_template = "{{default_clk_template}} crate::peripherals::HP_ALIVE_SYS::regs().hp_pad_i2s0_ctrl().modify(|_, w| w.hp_pad_i2s0_mclk_en().bit(enable));" + } }, + { name = "I2s1", template_params = { + clk_en = "{{apb_clk}}", + rst_en = ".apb_rst_en().bit(reset)", + ctrl_reg = "i2s1_ctrl0()", + clk_en_template = "{{default_clk_template}} crate::peripherals::HP_ALIVE_SYS::regs().hp_pad_i2s1_ctrl().modify(|_, w| w.hp_pad_i2s1_mclk_en().bit(enable));" + } }, { name = "SdioHost", template_params = { clk_en = "{{sys_clk}}", rst_template = "crate::peripherals::CNNT_SYS::regs().sys_hp_sdmmc_ctrl().modify(|_, w| w.sys_sdmmc_rst_en().bit(reset));" } }, # DMA engines { name = "AxiGdma", template_params = { peripheral = "axi_pdma", clk_en = "{{sys_clk}}" } }, diff --git a/esp-metadata/devices/esp32s31/gpio.toml b/esp-metadata/devices/esp32s31/gpio.toml index 58a221a4dca..0616b2a342c 100644 --- a/esp-metadata/devices/esp32s31/gpio.toml +++ b/esp-metadata/devices/esp32s31/gpio.toml @@ -83,12 +83,22 @@ input_signals = [ { name = "U3CTS", id = 138 }, { name = "U3DSR", id = 139 }, # I2S0 - { name = "I2SO_BCK", id = 25 }, - { name = "I2S_MCLK", id = 26 }, - { name = "I2SO_WS", id = 27 }, - { name = "I2SI_SD", id = 28 }, - { name = "I2SI_BCK", id = 29 }, - { name = "I2SI_WS", id = 30 }, + { name = "I2S0O_BCK", id = 25 }, + { name = "I2S0_MCLK", id = 26 }, + { name = "I2S0O_WS", id = 27 }, + { name = "I2S0I_SD", id = 28 }, + { name = "I2S0I_BCK", id = 29 }, + { name = "I2S0I_WS", id = 30 }, + { name = "I2S0I_SD1", id = 43 }, + { name = "I2S0I_SD2", id = 44 }, + { name = "I2S0I_SD3", id = 45 }, + # I2S1 + { name = "I2S1O_BCK", id = 31 }, + { name = "I2S1_MCLK", id = 32 }, + { name = "I2S1O_WS", id = 33 }, + { name = "I2S1I_SD", id = 34 }, + { name = "I2S1I_BCK", id = 35 }, + { name = "I2S1I_WS", id = 36 }, # SPI2 (FSPI) { name = "FSPICLK", id = 53 }, { name = "FSPIQ", id = 54 }, @@ -105,9 +115,7 @@ input_signals = [ { name = "FSPICS3", id = 65 }, { name = "FSPICS4", id = 66 }, { name = "FSPICS5", id = 67 }, - # SPI3 - { name = "SPI3_CS2", id = 45 }, - { name = "SPI3_CS1", id = 46 }, + # SPI3 (CS1/CS2 are output-only) { name = "SPI3_CK", id = 47 }, { name = "SPI3_Q", id = 48 }, { name = "SPI3_D", id = 49 }, @@ -289,12 +297,20 @@ output_signals = [ { name = "U3RTS", id = 138 }, { name = "U3DTR", id = 116 }, # I2S0 - { name = "I2SO_BCK", id = 25 }, - { name = "I2S_MCLK", id = 26 }, - { name = "I2SO_WS", id = 27 }, - { name = "I2SO_SD", id = 28 }, - { name = "I2SI_BCK", id = 29 }, - { name = "I2SI_WS", id = 30 }, + { name = "I2S0O_BCK", id = 25 }, + { name = "I2S0_MCLK", id = 26 }, + { name = "I2S0O_WS", id = 27 }, + { name = "I2S0O_SD", id = 28 }, + { name = "I2S0I_BCK", id = 29 }, + { name = "I2S0I_WS", id = 30 }, + { name = "I2S0O_SD1", id = 43 }, + # I2S1 + { name = "I2S1O_BCK", id = 31 }, + { name = "I2S1_MCLK", id = 32 }, + { name = "I2S1O_WS", id = 33 }, + { name = "I2S1O_SD", id = 34 }, + { name = "I2S1I_BCK", id = 35 }, + { name = "I2S1I_WS", id = 36 }, # SPI2 { name = "FSPICLK", id = 53 }, { name = "FSPIQ", id = 54 }, diff --git a/esp-metadata/devices/esp32s31/soc.toml b/esp-metadata/devices/esp32s31/soc.toml index 143912ffe91..7a89b6b61ad 100644 --- a/esp-metadata/devices/esp32s31/soc.toml +++ b/esp-metadata/devices/esp32s31/soc.toml @@ -61,6 +61,8 @@ peripherals = [ { name = "HP_SYS_CLKRST" }, { name = "I2C0", interrupts = { peri = "I2C0" }, clock_group = "I2C", stable = true }, { name = "I2C1", interrupts = { peri = "I2C1" }, clock_group = "I2C", stable = true }, + { name = "I2S0", interrupts = { peri = "I2S0" } }, + { name = "I2S1", interrupts = { peri = "I2S1" } }, { name = "INTERRUPT_CORE0" }, { name = "INTERRUPT_CORE1" }, { name = "IO_MUX" }, @@ -279,9 +281,11 @@ engines = [ name = "AHB_GDMA", max_priority = 15, burst_sizes = [0, 4, 16, 32, 64], - drivers = ["uhci"], + drivers = ["uhci", "i2s"], peripheral_instances = [ { name = "UHCI0", dma_id = 0 }, + { name = "I2S0", dma_id = 1 }, + { name = "I2S1", dma_id = 5 }, ], channels = [ { name = "DMA_CH0", mem2mem = true, mem2mem_id = 9, interrupt_in = "AHB_PDMA_IN_CH0", interrupt_out = "AHB_PDMA_OUT_CH0" }, @@ -400,7 +404,44 @@ support_status = "not_supported" [device.i2c_slave] support_status = "not_supported" [device.i2s] -support_status = "not_supported" +support_status = "partial" +version = 3 +# 0 = XTAL. Prefer APLL (1) once the clock tree can configure it. +default_clock_source = 0 +clock_configured_by_hp_sys_clkrst = true +mclk_divider_bit_width = 8 +max_ws_width = 512 +supports_pdm_rx_hp_filter = true +instances = [ + { + name = "i2s0", + sys_instance = "I2s0", + mclk = "I2S0_MCLK", + bclk = "I2S0O_BCK", + ws = "I2S0O_WS", + bclk_rx = "I2S0I_BCK", + ws_rx = "I2S0I_WS", + dout = ["I2S0O_SD", "I2S0O_SD1"], + din = ["I2S0I_SD", "I2S0I_SD1", "I2S0I_SD2", "I2S0I_SD3"], + pdm_tx = true, + pdm_rx = true, + pcm2pdm = true, + pdm2pcm = true, + }, + { + name = "i2s1", + sys_instance = "I2s1", + mclk = "I2S1_MCLK", + bclk = "I2S1O_BCK", + ws = "I2S1O_WS", + bclk_rx = "I2S1I_BCK", + ws_rx = "I2S1I_WS", + dout = ["I2S1O_SD"], + din = ["I2S1I_SD"], + pdm_tx = true, + pdm_rx = true, + }, +] [device.ieee802154] support_status = "not_supported" [device.key_manager] From d14961ddfd7aad37811fb0ef19d25768d336f89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Tue, 25 Aug 2026 13:10:51 +0200 Subject: [PATCH 2/3] use achievable rate --- hil-test/src/bin/i2s.rs | 10 ++++++---- qa-test/src/bin/embassy_i2s_pdm_rx.rs | 9 +++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hil-test/src/bin/i2s.rs b/hil-test/src/bin/i2s.rs index e9b003cc961..a8331644867 100644 --- a/hil-test/src/bin/i2s.rs +++ b/hil-test/src/bin/i2s.rs @@ -996,7 +996,8 @@ mod pdm_tx_tests { let tx_cfg = if info.pcm2pdm { PdmTxConfig::new_codec_default(Rate::from_hz(16_000), PdmSlotMode::Mono) } else { - PdmTxConfig::new_raw_default(Rate::from_hz(2_048_000), PdmSlotMode::Mono) + // 1.024 MHz: raw MCLK is sample_rate * 16; 2.048 MHz exceeds S31's 40 MHz XTAL. + PdmTxConfig::new_raw_default(Rate::from_hz(1_024_000), PdmSlotMode::Mono) }; assert!(tx_cfg.validate(info).is_ok()); } @@ -1011,7 +1012,7 @@ mod pdm_tx_tests { let tx_cfg = if info.pcm2pdm { PdmTxConfig::new_codec_default(Rate::from_hz(16_000), PdmSlotMode::Mono) } else { - PdmTxConfig::new_raw_default(Rate::from_hz(2_048_000), PdmSlotMode::Mono) + PdmTxConfig::new_raw_default(Rate::from_hz(1_024_000), PdmSlotMode::Mono) }; let pdm_cfg = PdmConfig::tx_only(tx_cfg); @@ -1095,7 +1096,8 @@ mod pdm_rx_tests { let rx_cfg = if info.pdm2pcm { PdmRxConfig::new_pcm_default(Rate::from_hz(16_000), PdmSlotMode::Mono) } else { - PdmRxConfig::new_raw_default(Rate::from_hz(2_048_000), PdmSlotMode::Mono) + // 1.024 MHz: raw MCLK is sample_rate * 16; 2.048 MHz exceeds S31's 40 MHz XTAL. + PdmRxConfig::new_raw_default(Rate::from_hz(1_024_000), PdmSlotMode::Mono) }; assert!(rx_cfg.validate(info).is_ok()); } @@ -1110,7 +1112,7 @@ mod pdm_rx_tests { let rx_cfg = if info.pdm2pcm { PdmRxConfig::new_pcm_default(Rate::from_hz(16_000), PdmSlotMode::Mono) } else { - PdmRxConfig::new_raw_default(Rate::from_hz(2_048_000), PdmSlotMode::Mono) + PdmRxConfig::new_raw_default(Rate::from_hz(1_024_000), PdmSlotMode::Mono) }; let pdm_cfg = PdmConfig::rx_only(rx_cfg); diff --git a/qa-test/src/bin/embassy_i2s_pdm_rx.rs b/qa-test/src/bin/embassy_i2s_pdm_rx.rs index 110152cb0aa..93e827260b7 100644 --- a/qa-test/src/bin/embassy_i2s_pdm_rx.rs +++ b/qa-test/src/bin/embassy_i2s_pdm_rx.rs @@ -10,8 +10,9 @@ //! 3V3 ────────────── VCC //! ``` //! -//! Reads 16 kHz mono PCM on chips with a hardware PDM2PCM filter (ESP32, ESP32-S3, …), -//! or 2.048 MHz raw PDM oversampling on chips without one (ESP32-C6, …), matching ESP-IDF. +//! Reads 16 kHz mono PCM on chips with a hardware PDM2PCM filter (ESP32, ESP32-S3, +//! ESP32-S31, …), or 2.048 MHz raw PDM oversampling on chips without one +//! (ESP32-C6, …), matching ESP-IDF. //! Values are printed as `i16` like IDF's example — on raw-PDM targets that means line //! bitstream words, not decoded PCM. @@ -79,7 +80,7 @@ async fn main(_spawner: Spawner) { }; cfg_select! { - any(feature = "esp32", feature = "esp32s3", feature = "esp32p4") => { + i2s_supports_pdm2pcm => { println!("embassy_i2s_pdm_rx: PDM RX in PCM format"); } _ => { @@ -88,7 +89,7 @@ async fn main(_spawner: Spawner) { } let rx_cfg = cfg_select! { - any(feature = "esp32", feature = "esp32s3", feature = "esp32p4") => { + i2s_supports_pdm2pcm => { PdmRxConfig::new_pcm_default(Rate::from_hz(16_000), PdmSlotMode::Mono) } _ => PdmRxConfig::new_raw_default(Rate::from_hz(2_048_000), PdmSlotMode::Mono), From a3565cbff0fe787787e99c493d12df5672eaecff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Tue, 25 Aug 2026 14:59:29 +0200 Subject: [PATCH 3/3] Simplify P4/S31 path cfg --- esp-hal/src/i2s/mod.rs | 8 +++----- esp-hal/src/soc/esp32s31/mod.rs | 2 +- esp-metadata/devices/esp32s31/soc.toml | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/esp-hal/src/i2s/mod.rs b/esp-hal/src/i2s/mod.rs index 1d3eb7aee5c..af96523ff13 100644 --- a/esp-hal/src/i2s/mod.rs +++ b/esp-hal/src/i2s/mod.rs @@ -2,11 +2,9 @@ pub mod master; -#[cfg(all(i2s_clock_configured_by_hp_sys_clkrst, esp32p4))] -#[path = "hp_sys_clkrst_p4.rs"] -mod hp_sys_clkrst; -#[cfg(all(i2s_clock_configured_by_hp_sys_clkrst, esp32s31))] -#[path = "hp_sys_clkrst_s31.rs"] +#[cfg(i2s_clock_configured_by_hp_sys_clkrst)] +#[cfg_attr(esp32p4, path = "hp_sys_clkrst_p4.rs")] +#[cfg_attr(esp32s31, path = "hp_sys_clkrst_s31.rs")] mod hp_sys_clkrst; #[cfg(any(i2s_supports_pdm_tx, i2s_supports_pdm_rx))] diff --git a/esp-hal/src/soc/esp32s31/mod.rs b/esp-hal/src/soc/esp32s31/mod.rs index e676615b5b4..9a2ef766335 100644 --- a/esp-hal/src/soc/esp32s31/mod.rs +++ b/esp-hal/src/soc/esp32s31/mod.rs @@ -17,7 +17,7 @@ pub(crate) use esp32s31 as pac; #[cfg_attr(not(feature = "unstable"), allow(unused))] pub(crate) fn i2s_sclk_frequency() -> u32 { // XTAL matches `i2s.default_clock_source` (mux 0). Mux 1 is APLL, which has - // less MCLK jitter; switch both to APLL once the clock tree can configure it. + // less MCLK jitter; switch both to APLL once it's modelled in the clock tree. clocks::xtal_clk_frequency() } diff --git a/esp-metadata/devices/esp32s31/soc.toml b/esp-metadata/devices/esp32s31/soc.toml index 7a89b6b61ad..c0091cb7829 100644 --- a/esp-metadata/devices/esp32s31/soc.toml +++ b/esp-metadata/devices/esp32s31/soc.toml @@ -406,7 +406,7 @@ support_status = "not_supported" [device.i2s] support_status = "partial" version = 3 -# 0 = XTAL. Prefer APLL (1) once the clock tree can configure it. +# 0 = XTAL. Prefer APLL (1) once it's modelled in the clock tree. default_clock_source = 0 clock_configured_by_hp_sys_clkrst = true mclk_divider_bit_width = 8