feat(hal): graudate the I2S hal driver into a new component

This commit is contained in:
laokaiyao
2025-12-11 10:25:40 +08:00
parent bf9070c030
commit fd918efe43
47 changed files with 454 additions and 462 deletions
+25
View File
@@ -0,0 +1,25 @@
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator
endif()
set(srcs)
set(includes "include")
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${target}/include")
list(APPEND includes "${target}/include")
endif()
# I2S related source files
if(CONFIG_SOC_I2S_SUPPORTED)
list(APPEND srcs "i2s_hal.c" "${target}/i2s_periph.c")
endif()
if(CONFIG_SOC_LP_I2S_SUPPORTED)
list(APPEND srcs "lp_i2s_hal.c")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${includes}
REQUIRES soc hal)
+48
View File
@@ -0,0 +1,48 @@
# ESP Hardware Abstraction Layer for I2S Peripheral(s)
> [!NOTE]
> This component is currently in beta. Its API, behavior, and compatibility may change at any time and without notice; backward compatibility is not guaranteed. Use caution when integrating into production systems.
## Overview
The `esp_hal_i2s` component provides a **Hardware Abstraction Layer** for Inter-IC Sound (I2S) controllers across all ESP-IDF supported targets. I2S is a serial bus interface standard used for connecting digital audio devices, enabling high-quality audio data transmission between integrated circuits.
## Architecture
The I2S HAL is structured in two main sub-layers:
1. **HAL Layer (Upper)**: Defines the operational steps and data structures required to control I2S peripherals (e.g., initialization, clock configuration, STD/TDM/PDM slot configuration, channel enable/disable).
2. **Low-Level Layer (Bottom)**: Serves as a translation layer between the HAL and the register files defined in the `soc` component, handling target-specific register configurations.
## Supported I2S Controllers
This HAL supports two I2S controller types depending on the ESP chip:
- **Standard I2S**: Traditional I2S interface supporting master/slave modes. And the different hardware versions are supported regarding different chips.
- **Low-Power I2S (LP I2S)**: Power-efficient I2S controller for low-power applications (on supported chips)
## Features
- **Multiple Audio Formats**:
- Standard I2S (STD) mode
- Pulse Density Modulation (PDM) mode
- Time Division Multiplexing (TDM) mode
- **Master/Slave Mode Support**: Configurable as master or slave device
- **Bidirectional Communication**: Independent TX and RX channel configuration
- **Clock Configuration**: Precise master clock (MCLK) and bit clock (BCLK) division
- **Slot Configuration**: Mono/stereo mode, slot selection, bit width configuration
- **PDM/PCM Converter Features**: PCM-to-PDM and PDM-to-PCM conversion, high-pass filtering (on supported chips)
- **TDM Features**: Multi-slot TDM support with configurable slot masks
- **Signal Connection Management**: GPIO signal mapping and interrupt configuration
## Usage
The HAL functions primarily serve ESP-IDF peripheral drivers such as `esp_driver_i2s`.
Advanced developers can use these interfaces directly when implementing custom drivers, with the understanding that API stability is not guaranteed.
## Dependencies
- `soc`: Provides chip-specific register definitions
- `hal`: Core hardware abstraction utilities and macros
+52
View File
@@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
{
.mck_out_sig = -1, // Unavailable
.mck_in_sig = -1, // Unavailable
.m_tx_bck_sig = I2S0O_BCK_OUT_IDX,
.m_rx_bck_sig = I2S0I_BCK_OUT_IDX,
.m_tx_ws_sig = I2S0O_WS_OUT_IDX,
.m_rx_ws_sig = I2S0I_WS_OUT_IDX,
.s_tx_bck_sig = I2S0O_BCK_IN_IDX,
.s_rx_bck_sig = I2S0I_BCK_IN_IDX,
.s_tx_ws_sig = I2S0O_WS_IN_IDX,
.s_rx_ws_sig = I2S0I_WS_IN_IDX,
.data_out_sig = I2S0O_DATA_OUT23_IDX,
.data_in_sig = I2S0I_DATA_IN15_IDX,
.irq = ETS_I2S0_INTR_SOURCE,
},
{
.mck_out_sig = -1, // Unavailable
.mck_in_sig = -1, // Unavailable
.m_tx_bck_sig = I2S1O_BCK_OUT_IDX,
.m_rx_bck_sig = I2S1I_BCK_OUT_IDX,
.m_tx_ws_sig = I2S1O_WS_OUT_IDX,
.m_rx_ws_sig = I2S1I_WS_OUT_IDX,
.s_tx_bck_sig = I2S1O_BCK_IN_IDX,
.s_rx_bck_sig = I2S1I_BCK_IN_IDX,
.s_tx_ws_sig = I2S1O_WS_IN_IDX,
.s_rx_ws_sig = I2S1I_WS_IN_IDX,
.data_out_sig = I2S1O_DATA_OUT23_IDX,
.data_in_sig = I2S1I_DATA_IN15_IDX,
.irq = ETS_I2S1_INTR_SOURCE,
}
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
{
.mck_out_sig = I2S_MCLK_OUT_IDX,
.mck_in_sig = I2S_MCLK_IN_IDX,
.m_tx_bck_sig = I2SO_BCK_OUT_IDX,
.m_rx_bck_sig = I2SI_BCK_OUT_IDX,
.m_tx_ws_sig = I2SO_WS_OUT_IDX,
.m_rx_ws_sig = I2SI_WS_OUT_IDX,
.s_tx_bck_sig = I2SO_BCK_IN_IDX,
.s_rx_bck_sig = I2SI_BCK_IN_IDX,
.s_tx_ws_sig = I2SO_WS_IN_IDX,
.s_rx_ws_sig = I2SI_WS_IN_IDX,
.data_out_sigs[0] = I2SO_SD_OUT_IDX,
.data_out_sigs[1] = I2SO_SD1_OUT_IDX,
.data_in_sig = I2SI_SD_IN_IDX,
.irq = ETS_I2S0_INTR_SOURCE,
}
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/i2s_reg.h"
#include "soc/gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
{
.mck_out_sig = I2S_MCLK_OUT_IDX,
.mck_in_sig = I2S_MCLK_IN_IDX,
.m_tx_bck_sig = I2SO_BCK_OUT_IDX,
.m_rx_bck_sig = I2SI_BCK_OUT_IDX,
.m_tx_ws_sig = I2SO_WS_OUT_IDX,
.m_rx_ws_sig = I2SI_WS_OUT_IDX,
.s_tx_bck_sig = I2SO_BCK_IN_IDX,
.s_rx_bck_sig = I2SI_BCK_IN_IDX,
.s_tx_ws_sig = I2SO_WS_IN_IDX,
.s_rx_ws_sig = I2SI_WS_IN_IDX,
.data_out_sigs[0] = I2SO_SD_OUT_IDX,
.data_out_sigs[1] = I2SO_SD1_OUT_IDX,
.data_in_sig = I2SI_SD_IN_IDX,
.irq = ETS_I2S0_INTR_SOURCE,
}
};
/**
* I2S Registers to be saved during sleep retention
* - I2S_RX_CONF_REG
* - I2S_TX_CONF_REG
* - I2S_RX_CONF1_REG
* - I2S_TX_CONF1_REG
* - I2S_TX_PCM2PDM_CONF_REG
* - I2S_TX_PCM2PDM_CONF1_REG
* - I2S_RX_TDM_CTRL_REG
* - I2S_TX_TDM_CTRL_REG
* - I2S_RXEOF_NUM_REG
* - I2S_ETM_CONF_REG
*/
#define I2S_RETENTION_REGS_CNT 10
#define I2S_RETENTION_REGS_BASE(i) I2S_RX_CONF_REG(i)
static const uint32_t i2s_regs_map[4] = {0x12360f, 0x0, 0x0, 0x0};
#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \
/* Save/restore the register values */ \
[0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
REGDMA_I2S_LINK(0x00), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_CNT, 0, 0, \
i2s_regs_map[0], i2s_regs_map[1], \
i2s_regs_map[2], i2s_regs_map[3]), \
.owner = ENTRY(0) | ENTRY(2)}, \
};
static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0);
const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_ATTR(INST_NUM)] = {
[0] = {
.retention_module = SLEEP_RETENTION_MODULE_I2S0,
.entry_array = i2s0_regs_retention,
.array_size = ARRAY_SIZE(i2s0_regs_retention)
},
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/i2s_reg.h"
#include "soc/gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
{
.mck_out_sig = I2S_MCLK_OUT_IDX,
.mck_in_sig = I2S_MCLK_IN_IDX,
.m_tx_bck_sig = I2SO_BCK_OUT_IDX,
.m_rx_bck_sig = I2SI_BCK_OUT_IDX,
.m_tx_ws_sig = I2SO_WS_OUT_IDX,
.m_rx_ws_sig = I2SI_WS_OUT_IDX,
.s_tx_bck_sig = I2SO_BCK_IN_IDX,
.s_rx_bck_sig = I2SI_BCK_IN_IDX,
.s_tx_ws_sig = I2SO_WS_IN_IDX,
.s_rx_ws_sig = I2SI_WS_IN_IDX,
.data_out_sigs[0] = I2SO_SD_OUT_IDX,
.data_out_sigs[1] = I2SO_SD1_OUT_IDX,
.data_in_sig = I2SI_SD_IN_IDX,
.irq = ETS_I2S0_INTR_SOURCE,
}
};
/**
* I2S Registers to be saved during sleep retention
* - I2S_RX_CONF_REG
* - I2S_TX_CONF_REG
* - I2S_RX_CONF1_REG
* - I2S_TX_CONF1_REG
* - I2S_TX_PCM2PDM_CONF_REG
* - I2S_TX_PCM2PDM_CONF1_REG
* - I2S_RX_TDM_CTRL_REG
* - I2S_TX_TDM_CTRL_REG
* - I2S_RXEOF_NUM_REG
* - I2S_ETM_CONF_REG
*/
#define I2S_RETENTION_REGS_CNT 10
#define I2S_RETENTION_REGS_BASE(i) I2S_RX_CONF_REG(i)
static const uint32_t i2s_regs_map[4] = {0x12330f, 0x0, 0x0, 0x0};
#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \
/* Save/restore the register values */ \
[0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
REGDMA_I2S_LINK(0x00), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_CNT, 0, 0, \
i2s_regs_map[0], i2s_regs_map[1], \
i2s_regs_map[2], i2s_regs_map[3]), \
.owner = ENTRY(0) | ENTRY(2)}, \
};
static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0);
const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_ATTR(INST_NUM)] = {
[0] = {
.retention_module = SLEEP_RETENTION_MODULE_I2S0,
.entry_array = i2s0_regs_retention,
.array_size = ARRAY_SIZE(i2s0_regs_retention)
},
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/i2s_reg.h"
#include "soc/gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
{
.mck_out_sig = I2S_MCLK_OUT_IDX,
.mck_in_sig = I2S_MCLK_IN_IDX,
.m_tx_bck_sig = I2SO_BCK_OUT_IDX,
.m_rx_bck_sig = I2SI_BCK_OUT_IDX,
.m_tx_ws_sig = I2SO_WS_OUT_IDX,
.m_rx_ws_sig = I2SI_WS_OUT_IDX,
.s_tx_bck_sig = I2SO_BCK_IN_IDX,
.s_rx_bck_sig = I2SI_BCK_IN_IDX,
.s_tx_ws_sig = I2SO_WS_IN_IDX,
.s_rx_ws_sig = I2SI_WS_IN_IDX,
.data_out_sigs[0] = I2SO_SD_OUT_IDX,
.data_out_sigs[1] = I2SO_SD1_OUT_IDX,
.data_in_sig = I2SI_SD_IN_IDX,
.irq = ETS_I2S0_INTR_SOURCE,
}
};
/**
* I2S Registers to be saved during sleep retention
* - I2S_RX_CONF_REG
* - I2S_TX_CONF_REG
* - I2S_RX_CONF1_REG
* - I2S_TX_CONF1_REG
* - I2S_TX_PCM2PDM_CONF_REG
* - I2S_TX_PCM2PDM_CONF1_REG
* - I2S_RX_TDM_CTRL_REG
* - I2S_TX_TDM_CTRL_REG
* - I2S_RXEOF_NUM_REG
* - I2S_ETM_CONF_REG
*/
#define I2S_RETENTION_REGS_CNT 10
#define I2S_RETENTION_REGS_BASE(i) I2S_RX_CONF_REG
static const uint32_t i2s_regs_map[4] = {0x12360f, 0x0, 0x0, 0x0};
#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \
/* Save/restore the register values */ \
[0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
REGDMA_I2S_LINK(0x00), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_CNT, 0, 0, \
i2s_regs_map[0], i2s_regs_map[1], \
i2s_regs_map[2], i2s_regs_map[3]), \
.owner = ENTRY(0) | ENTRY(2)}, \
};
static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0);
const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_ATTR(INST_NUM)] = {
[0] = {
.retention_module = SLEEP_RETENTION_MODULE_I2S0,
.entry_array = i2s0_regs_retention,
.array_size = ARRAY_SIZE(i2s0_regs_retention)
},
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,72 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/i2s_reg.h"
#include "soc/gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
{
.mck_out_sig = I2S_MCLK_OUT_IDX,
.mck_in_sig = I2S_MCLK_IN_IDX,
.m_tx_bck_sig = I2SO_BCK_OUT_IDX,
.m_rx_bck_sig = I2SI_BCK_OUT_IDX,
.m_tx_ws_sig = I2SO_WS_OUT_IDX,
.m_rx_ws_sig = I2SI_WS_OUT_IDX,
.s_tx_bck_sig = I2SO_BCK_IN_IDX,
.s_rx_bck_sig = I2SI_BCK_IN_IDX,
.s_tx_ws_sig = I2SO_WS_IN_IDX,
.s_rx_ws_sig = I2SI_WS_IN_IDX,
.data_out_sig = I2SO_SD_OUT_IDX,
.data_in_sig = I2SI_SD_IN_IDX,
.irq = ETS_I2S0_INTR_SOURCE,
}
};
/**
* I2S Registers to be saved during sleep retention
* - I2S_RX_CONF_REG
* - I2S_TX_CONF_REG
* - I2S_RX_CONF1_REG
* - I2S_TX_CONF1_REG
* - I2S_TX_PCM2PDM_CONF_REG
* - I2S_TX_PCM2PDM_CONF1_REG
* - I2S_RX_TDM_CTRL_REG
* - I2S_TX_TDM_CTRL_REG
* - I2S_RXEOF_NUM_REG
* - I2S_ETM_CONF_REG
*/
#define I2S_RETENTION_REGS_CNT 10
#define I2S_RETENTION_REGS_BASE(i) I2S_RX_CONF_REG
static const uint32_t i2s_regs_map[4] = {0x12330f, 0x0, 0x0, 0x0};
#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \
/* Save/restore the register values */ \
[0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
REGDMA_I2S_LINK(0x00), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_CNT, 0, 0, \
i2s_regs_map[0], i2s_regs_map[1], \
i2s_regs_map[2], i2s_regs_map[3]), \
.owner = ENTRY(0) | ENTRY(2)}, \
};
static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0);
const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_ATTR(INST_NUM)] = {
[0] = {
.retention_module = SLEEP_RETENTION_MODULE_I2S0,
.entry_array = i2s0_regs_retention,
.array_size = ARRAY_SIZE(i2s0_regs_retention)
},
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,77 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/i2s_reg.h"
#include "soc/gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
{
.mck_out_sig = I2S_MCLK_OUT_IDX,
.mck_in_sig = I2S_MCLK_IN_IDX,
.m_tx_bck_sig = I2SO_BCK_OUT_IDX,
.m_rx_bck_sig = I2SI_BCK_OUT_IDX,
.m_tx_ws_sig = I2SO_WS_OUT_IDX,
.m_rx_ws_sig = I2SI_WS_OUT_IDX,
.s_tx_bck_sig = I2SO_BCK_IN_IDX,
.s_rx_bck_sig = I2SI_BCK_IN_IDX,
.s_tx_ws_sig = I2SO_WS_IN_IDX,
.s_rx_ws_sig = I2SI_WS_IN_IDX,
.data_out_sig = I2SO_SD_OUT_IDX,
.data_in_sig = I2SI_SD_IN_IDX,
.irq = ETS_I2S_INTR_SOURCE,
}
};
/**
* I2S Registers to be saved during sleep retention
* - I2S_INT_ENA_REG
* - I2S_RX_CONF_REG
* - I2S_TX_CONF_REG
* - I2S_RX_CONF1_REG
* - I2S_TX_CONF1_REG
* - I2S_TX_PCM2PDM_CONF_REG
* - I2S_TX_PCM2PDM_CONF1_REG
* - I2S_RX_TDM_CTRL_REG
* - I2S_TX_TDM_CTRL_REG
* - I2S_RXEOF_NUM_REG
* - I2S_ETM_CONF_REG
* - I2S_IDEAL_CNT_REG
* - I2S_SYNC_SW_THRES_REG
* - I2S_SYNC_HW_THRES_REG
* - I2S_HW_SYNC_CONF_REG
* - I2S_HW_SYNC_DATA_REG
*/
#define I2S_RETENTION_REGS_CNT 16
#define I2S_RETENTION_REGS_BASE(i) (I2S_INT_ENA_REG(i))
static const uint32_t i2s_regs_map[4] = {0xf191b079, 0x0, 0x0, 0x0};
#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \
[0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
REGDMA_I2S_LINK(0x00), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_CNT, 0, 0, \
i2s_regs_map[0], i2s_regs_map[1], \
i2s_regs_map[2], i2s_regs_map[3]), \
.owner = ENTRY(0) | ENTRY(2) }, \
};
static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0);
const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_ATTR(INST_NUM)] = {
[0] = {
.retention_module = SLEEP_RETENTION_MODULE_I2S0,
.entry_array = i2s0_regs_retention,
.array_size = ARRAY_SIZE(i2s0_regs_retention)
},
};
File diff suppressed because it is too large Load Diff
+162
View File
@@ -0,0 +1,162 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/i2s_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/lp_gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
[0] = {
.mck_out_sig = I2S0_MCLK_PAD_OUT_IDX,
.mck_in_sig = I2S0_MCLK_PAD_IN_IDX,
.m_tx_bck_sig = I2S0_O_BCK_PAD_OUT_IDX,
.m_rx_bck_sig = I2S0_I_BCK_PAD_OUT_IDX,
.m_tx_ws_sig = I2S0_O_WS_PAD_OUT_IDX,
.m_rx_ws_sig = I2S0_I_WS_PAD_OUT_IDX,
.s_tx_bck_sig = I2S0_O_BCK_PAD_IN_IDX,
.s_rx_bck_sig = I2S0_I_BCK_PAD_IN_IDX,
.s_tx_ws_sig = I2S0_O_WS_PAD_IN_IDX,
.s_rx_ws_sig = I2S0_I_WS_PAD_IN_IDX,
.data_out_sigs[0] = I2S0_O_SD_PAD_OUT_IDX,
.data_out_sigs[1] = I2S0_O_SD1_PAD_OUT_IDX,
.data_in_sigs[0] = I2S0_I_SD_PAD_IN_IDX,
.data_in_sigs[1] = I2S0_I_SD1_PAD_IN_IDX,
.data_in_sigs[2] = I2S0_I_SD2_PAD_IN_IDX,
.data_in_sigs[3] = I2S0_I_SD3_PAD_IN_IDX,
.irq = ETS_I2S0_INTR_SOURCE,
},
[1] = {
.mck_out_sig = I2S1_MCLK_PAD_OUT_IDX,
.mck_in_sig = I2S1_MCLK_PAD_IN_IDX,
.m_tx_bck_sig = I2S1_O_BCK_PAD_OUT_IDX,
.m_rx_bck_sig = I2S1_I_BCK_PAD_OUT_IDX,
.m_tx_ws_sig = I2S1_O_WS_PAD_OUT_IDX,
.m_rx_ws_sig = I2S1_I_WS_PAD_OUT_IDX,
.s_tx_bck_sig = I2S1_O_BCK_PAD_IN_IDX,
.s_rx_bck_sig = I2S1_I_BCK_PAD_IN_IDX,
.s_tx_ws_sig = I2S1_O_WS_PAD_IN_IDX,
.s_rx_ws_sig = I2S1_I_WS_PAD_IN_IDX,
.data_out_sigs[0] = I2S1_O_SD_PAD_OUT_IDX,
.data_out_sigs[1] = -1,
.data_in_sigs[0] = I2S1_I_SD_PAD_IN_IDX,
.data_in_sigs[1] = -1,
.data_in_sigs[2] = -1,
.data_in_sigs[3] = -1,
.irq = ETS_I2S1_INTR_SOURCE,
},
[2] = {
.mck_out_sig = I2S2_MCLK_PAD_OUT_IDX,
.mck_in_sig = I2S2_MCLK_PAD_IN_IDX,
.m_tx_bck_sig = I2S2_O_BCK_PAD_OUT_IDX,
.m_rx_bck_sig = I2S2_I_BCK_PAD_OUT_IDX,
.m_tx_ws_sig = I2S2_O_WS_PAD_OUT_IDX,
.m_rx_ws_sig = I2S2_I_WS_PAD_OUT_IDX,
.s_tx_bck_sig = I2S2_O_BCK_PAD_IN_IDX,
.s_rx_bck_sig = I2S2_I_BCK_PAD_IN_IDX,
.s_tx_ws_sig = I2S2_O_WS_PAD_IN_IDX,
.s_rx_ws_sig = I2S2_I_WS_PAD_IN_IDX,
.data_out_sigs[0] = I2S2_O_SD_PAD_OUT_IDX,
.data_out_sigs[1] = -1,
.data_in_sigs[0] = I2S2_I_SD_PAD_IN_IDX,
.data_in_sigs[1] = -1,
.data_in_sigs[2] = -1,
.data_in_sigs[3] = -1,
.irq = ETS_I2S2_INTR_SOURCE,
},
};
const i2s_signal_conn_t lp_i2s_periph_signal[SOC_LP_I2S_NUM] = {
[0] = {
.mck_out_sig = -1,
.mck_in_sig = -1,
.m_tx_bck_sig = LP_I2S_O_BCK_PAD_OUT_IDX,
.m_rx_bck_sig = LP_I2S_I_BCK_PAD_OUT_IDX,
.m_tx_ws_sig = LP_I2S_O_WS_PAD_OUT_IDX,
.m_rx_ws_sig = LP_I2S_I_WS_PAD_OUT_IDX,
.s_tx_bck_sig = LP_I2S_O_BCK_PAD_IN_IDX,
.s_rx_bck_sig = LP_I2S_I_BCK_PAD_IN_IDX,
.s_tx_ws_sig = LP_I2S_O_WS_PAD_IN_IDX,
.s_rx_ws_sig = LP_I2S_I_WS_PAD_IN_IDX,
.data_out_sigs[0] = LP_I2S_O_SD_PAD_OUT_IDX,
.data_out_sigs[1] = -1,
.data_in_sigs[0] = LP_I2S_I_SD_PAD_IN_IDX,
.data_in_sigs[1] = -1,
.data_in_sigs[2] = -1,
.data_in_sigs[3] = -1,
.irq = ETS_LP_I2S_INTR_SOURCE,
},
};
/**
* I2S Registers to be saved during sleep retention
* - I2S_RX_CONF_REG
* - I2S_TX_CONF_REG
* - I2S_RX_CONF1_REG
* - I2S_TX_CONF1_REG
* - I2S_TX_PCM2PDM_CONF_REG
* - I2S_TX_PCM2PDM_CONF1_REG
* - I2S_RX_PDM2PCM_CONF_REG
* - I2S_RX_TDM_CTRL_REG
* - I2S_TX_TDM_CTRL_REG
* - I2S_RXEOF_NUM_REG
* - I2S_ETM_CONF_REG
*/
#define I2S_RETENTION_REGS_CNT 11
#define I2S_RETENTION_REGS_BASE(i) I2S_RX_CONF_REG(i)
static const uint32_t i2s_regs_map[4] = {0x12370f, 0x0, 0x0, 0x0};
#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \
/* Save/restore the register values */ \
[0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
REGDMA_I2S_LINK(0x00), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_BASE(i2s_port), \
I2S_RETENTION_REGS_CNT, 0, 0, \
i2s_regs_map[0], i2s_regs_map[1], \
i2s_regs_map[2], i2s_regs_map[3]), \
.owner = ENTRY(0)}, \
};
static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0);
static const regdma_entries_config_t i2s1_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(1);
static const regdma_entries_config_t i2s2_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(2);
const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_ATTR(INST_NUM)] = {
[0] = {
.retention_module = SLEEP_RETENTION_MODULE_I2S0,
.entry_array = i2s0_regs_retention,
.array_size = ARRAY_SIZE(i2s0_regs_retention)
},
[1] = {
.retention_module = SLEEP_RETENTION_MODULE_I2S1,
.entry_array = i2s1_regs_retention,
.array_size = ARRAY_SIZE(i2s1_regs_retention)
},
[2] = {
.retention_module = SLEEP_RETENTION_MODULE_I2S2,
.entry_array = i2s2_regs_retention,
.array_size = ARRAY_SIZE(i2s2_regs_retention)
},
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,847 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The LL layer for LP I2S register operations
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
#pragma once
#include <stdbool.h>
#include <math.h>
#include "hal/misc.h"
#include "hal/assert.h"
#include "hal/i2s_types.h"
#include "soc/lp_i2s_struct.h"
#include "soc/lpperi_struct.h"
#include "soc/lp_system_struct.h"
#include "soc/clk_tree_defs.h"
#include "soc/reg_base.h"
#ifdef __cplusplus
extern "C" {
#endif
#define LP_I2S_LL_GET_HW(num) (((num) == 0)? (&LP_I2S) : NULL)
#define LP_I2S_LL_EVENT_RX_DONE_INT (1<<0)
#define LP_I2S_LL_EVENT_RX_HUNG_INT_INT (1<<1)
#define LP_I2S_LL_EVENT_RX_FIFOMEM_UDF_INT (1<<2)
#define LP_I2S_LL_EVENT_VAD_DONE_INT (1<<3)
#define LP_I2S_LL_EVENT_VAD_RESET_DONE_INT (1<<4)
#define LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT (1<<5)
#define LP_I2S_LL_TDM_CH_MASK (0x03UL)
#define LP_I2S_LL_RX_SUPPORTED 1
#define LP_I2S_LL_TDM_MAX_DATA_BIT_WIDTH 16
#define LP_I2S_LL_TDM_MAX_CHAN_BIT_WIDTH 16
#define LP_I2S_LL_RX_MEM_THRESH_BYTES_MAX 1020
#define LP_I2S_LL_RX_MEM_POP_BYTES 4
#define LP_I2S_LL_CUT_OFF_COEF_X_NUM 21
#define LP_I2S_LL_CUT_OFF_COEF_Y_NUM 3
/* PDM high pass filter cut-off frequency and coefficients list
* [0]: cut-off frequency * 10; [1]: param0; [2]: param5
* NOTE: the cut-off frequency was timed 10 to use integer type */
#define LP_I2S_LL_CUT_OFF_COEF {{1850, 0, 0}, {1720, 0, 1}, {1600, 1, 1}, \
{1500, 1, 2}, {1370, 2, 2}, {1260, 2, 3}, \
{1200, 0, 3}, {1150, 3, 3}, {1060, 1, 7}, \
{1040, 2, 4}, {920, 4, 4}, {915, 2, 7}, \
{810, 4, 5}, {772, 3, 7}, {690, 5, 5}, \
{630, 4, 7}, {580, 5, 6}, {490, 5, 7}, \
{460, 6, 6}, {355, 6, 7}, {233, 7, 7}}
/**
* @brief LP I2S rx stop mode enum type
*/
typedef enum {
LP_I2S_LL_RX_STOP_MODE_START_CLEAR,
LP_I2S_LL_RX_STOP_MODE_START_CLEAR_EOF_NUM,
} lp_i2s_ll_rx_stop_mode_t;
/**
* @brief LP I2S rx polarity mode enum type
*/
typedef enum {
LP_I2S_LL_RX_WS_POL_NEGATIVE,
LP_I2S_LL_RX_WS_POL_POSITIVE,
} lp_i2s_ll_rx_ws_pol_t;
/**
* @brief LP I2S rx bit order enum type
*/
typedef enum {
LP_I2S_LL_RX_BIT_ORDER_BIG_ENDIAN,
LP_I2S_LL_RX_BIT_ORDER_SMALL_ENDIAN,
} lp_i2s_ll_rx_bit_order_t;
/**
* @brief LP I2S rx align enum type
*/
typedef enum {
LP_I2S_LL_RX_RIGHT_ALIGN,
LP_I2S_LL_RX_LEFT_ALIGN,
} lp_i2s_ll_rx_alignment_t;
/**
* @brief LP I2S rx endian enum type
*/
typedef enum {
LP_I2S_LL_RX_SMALL_ENDIAN,
LP_I2S_LL_RX_BIG_ENDIAN,
} lp_i2s_ll_rx_endian_t;
/*---------------------------------------------------------------
Mem
---------------------------------------------------------------*/
/**
* @brief Enable the internal memory for LP I2S module
*
* @param id Instance id
* @param en enable / disable
*/
static inline void lp_i2s_ll_enable_mem(int id, bool en)
{
LP_SYS.lp_mem_aux_ctrl.lp_mem_aux_ctrl = !en;
}
static inline void lp_i2s_ll_set_rx_mem_threshold(lp_i2s_dev_t *hw, uint32_t words)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_mem_conf, rx_mem_threshold, words);
}
/*---------------------------------------------------------------
Clock
---------------------------------------------------------------*/
/**
* @brief Enable the bus clock for LP I2S module
*
* @param id Instance id
* @param en enable / disable
*/
static inline void lp_i2s_ll_enable_module_clock(int id, bool en)
{
LPPERI.clk_en.ck_en_lp_i2s = en;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define lp_i2s_ll_enable_module_clock(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
lp_i2s_ll_enable_module_clock(__VA_ARGS__); \
} while(0)
/**
* @brief Reset the LP I2S module
*
* @param hw Hardware instance address
*/
static inline void lp_i2s_ll_reset_module_clock(int id)
{
LPPERI.reset_en.rst_en_lp_i2s = 1;
LPPERI.reset_en.rst_en_lp_i2s = 0;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define lp_i2s_ll_reset_module_clock(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
lp_i2s_ll_reset_module_clock(__VA_ARGS__); \
} while(0)
/**
* @brief Enable the bus clock for LP I2S RX module
*
* @param id Instance id
* @param en enable / disable
*/
static inline void lp_i2s_ll_enable_rx_module_clock(int id, bool en)
{
LPPERI.clk_en.ck_en_lp_i2s_rx = en;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define lp_i2s_ll_enable_rx_module_clock(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
lp_i2s_ll_enable_rx_module_clock(__VA_ARGS__); \
} while(0)
/**
* @brief Select ISP clock source
*
* @param id Instance id
* @param clk_src clock source, see valid sources in type `soc_periph_lp_i2s_clk_src_t`
*/
static inline void lp_i2s_ll_select_rx_clk_source(int id, soc_periph_lp_i2s_clk_src_t clk_src)
{
uint8_t clk_val = 0;
switch (clk_src) {
case LP_I2S_CLK_SRC_LP_PERI:
clk_val = 0;
break;
case LP_I2S_CLK_SRC_XTAL_D2:
clk_val = 1;
break;
default:
HAL_ASSERT(false);
break;
}
LPPERI.core_clk_sel.lp_i2s_rx_clk_sel = clk_val;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define lp_i2s_ll_select_rx_clk_source(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
lp_i2s_ll_select_rx_clk_source(__VA_ARGS__); \
} while(0)
/**
* @brief Set LP I2S clock source div num
*
* @note f_LP_I2S_CLK_S / (N + b / a) = f_LP_I2S_RX_CLK
*
* @param id Instance id
* @param val value to set div num
*/
static inline void lp_i2s_ll_clk_source_div_num(int id, uint32_t val)
{
if (val == 256) {
HAL_FORCE_MODIFY_U32_REG_FIELD(LPPERI.lp_i2s_rxclk_div_num, lp_i2s_rx_clkm_div_num, 0);
} else if (val == 2) {
HAL_FORCE_MODIFY_U32_REG_FIELD(LPPERI.lp_i2s_rxclk_div_num, lp_i2s_rx_clkm_div_num, 1);
} else {
HAL_FORCE_MODIFY_U32_REG_FIELD(LPPERI.lp_i2s_rxclk_div_num, lp_i2s_rx_clkm_div_num, val);
}
}
/**
* @brief Set LP I2S rx raw clock division
*
* @param id Instance id
* @param a div a
* @param b div b
*/
static inline void lp_i2s_ll_rx_set_raw_clk_div(int id, uint32_t a, uint32_t b)
{
HAL_ASSERT(b > 0);
if (b <= a / 2) {
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_yn1 = 0;
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_x = floor(a / b) - 1;
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_y = a % b;
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_z = b;
} else {
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_yn1 = 1;
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_x = floor(a / (a - b)) - 1;
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_y = a % (a - b);
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_z = a - b;
}
if (a == 0 && b == 0) {
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_x = 0;
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_z = 0;
LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_y = 1;
}
}
/**
* @brief Set LP I2S rx bck div num
*
* @param hw LP I2S hardware instance
* @param val value to set rx bck div num
*/
static inline void lp_i2s_ll_rx_set_bck_div_num(lp_i2s_dev_t *hw, uint32_t val)
{
hw->rx_conf1.rx_bck_div_num = val - 1;
}
/**
* @brief Enable/Disable clock gate
*
* @param hw LP I2S hardware instance
* @param enable enable or disable
*/
static inline void lp_i2s_ll_clk_gate_en(lp_i2s_dev_t *hw, bool enable)
{
hw->clk_gate.clk_en = enable;
}
/**
* @brief Enable/Disable rx mem clock gate
*
* @param hw LP I2S hardware instance
* @param enable enable or disable
*/
static inline void lp_i2s_ll_rx_mem_clk_gate_en(lp_i2s_dev_t *hw, bool enable)
{
hw->clk_gate.rx_mem_cg_force_on = enable;
}
/**
* @brief Enable/Disable rx reg clock gate
*
* @param hw LP I2S hardware instance
* @param enable enable or disable
*/
static inline void lp_i2s_ll_rx_reg_clk_gate_en(lp_i2s_dev_t *hw, bool enable)
{
hw->clk_gate.rx_reg_cg_force_on = enable;
}
/*---------------------------------------------------------------
Reset
---------------------------------------------------------------*/
/**
* @brief Reset LP I2S RX module
*
* @param hw LP I2S hardware instance
*/
static inline void lp_i2s_ll_rx_reset(lp_i2s_dev_t *hw)
{
hw->rx_conf.rx_reset = 1;
hw->rx_conf.rx_reset = 0;
}
/**
* @brief Reset LP I2S RX FIFO
*
* @param hw LP I2S hardware instance
*/
static inline void lp_i2s_ll_rx_reset_fifo(lp_i2s_dev_t *hw)
{
hw->rx_conf.rx_fifo_reset = 1;
hw->rx_conf.rx_fifo_reset = 0;
}
/**
* @brief Reset LP I2S RX FIFO mem
*
* @param hw LP I2S hardware instance
*/
static inline void lp_i2s_ll_rx_reset_fifo_mem(lp_i2s_dev_t *hw)
{
hw->rx_conf.rx_fifomem_reset = 1;
hw->rx_conf.rx_fifomem_reset = 0;
}
/*---------------------------------------------------------------
Master/Slave
---------------------------------------------------------------*/
/**
* @brief Start LP I2S RX
*
* @param hw LP I2S hardware instance
*/
static inline void lp_i2s_ll_rx_start(lp_i2s_dev_t *hw)
{
hw->rx_conf.rx_update = 1;
while (hw->rx_conf.rx_update);
hw->rx_conf.rx_start = 1;
}
/**
* @brief Set LP I2S RX stop mode
*
* @param hw LP I2S hardware instance
*/
static inline void lp_i2s_ll_set_rx_stop_mode(lp_i2s_dev_t *hw, lp_i2s_ll_rx_stop_mode_t rx_stop_mode)
{
hw->rx_conf.rx_stop_mode = rx_stop_mode;
}
/**
* @brief Configure the received length to trigger in_suc_eof interrupt
*
* @param hw LP I2S hardware instance
* @param eof_num the byte length to trigger in_suc_eof interrupt
*/
static inline void lp_i2s_ll_rx_set_eof_num(lp_i2s_dev_t *hw, int eof_num)
{
hw->rxeof_num.rx_eof_num = eof_num;
}
/**
* @brief Stop LP I2S RX
*
* @param hw LP I2S hardware instance
*/
static inline void lp_i2s_ll_rx_stop(lp_i2s_dev_t *hw)
{
hw->rx_conf.rx_start = 0;
}
/**
* @brief Stop LP I2S RX
*
* @param hw LP I2S hardware instance
* @param is_slave Is slave or master
*/
static inline void lp_i2s_ll_set_rx_master_slave_mode(lp_i2s_dev_t *hw, bool is_slave)
{
hw->rx_conf.rx_slave_mod = is_slave;
}
/*---------------------------------------------------------------
Receive
---------------------------------------------------------------*/
/*--------------------------------------------------
TDM
--------------------------------------------------*/
/**
* @brief Enable LP I2S RX TDM mode
*
* @param hw LP I2S hardware instance
*/
static inline void lp_i2s_ll_rx_enable_tdm(lp_i2s_dev_t *hw)
{
hw->rx_conf.rx_pdm_en = false;
hw->rx_conf.rx_tdm_en = true;
}
/**
* @brief Configure RX total chan number under TDM mode
*
* @param hw LP I2S hardware instance
* @param total_num Total chan number
*/
static inline void lp_i2s_ll_rx_set_tdm_chan_num(lp_i2s_dev_t *hw, int total_num)
{
hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = total_num - 1;
}
/*--------------------------------------------------
PDM
--------------------------------------------------*/
/**
* @brief Enable LP RX PDM mode.
*
* @param hw LP I2S hardware instance
*/
static inline void lp_i2s_ll_rx_enable_pdm(lp_i2s_dev_t *hw)
{
hw->rx_conf.rx_pdm_en = 1;
hw->rx_conf.rx_tdm_en = 0;
}
/**
* @brief Enable LP I2S RX PDM high pass filter
*
* @param hw LP I2S hardware instance
* @param enable Set true to enable I2S RX PDM high pass filter, set false to bypass it
*/
static inline void lp_i2s_ll_rx_enable_pdm_hp_filter(lp_i2s_dev_t *hw, bool enable)
{
hw->rx_pdm_conf.rx_pdm_hp_bypass = !enable;
}
/**
* @brief Set LP_I2S RX PDM high pass filter param0
*
* @param hw Peripheral LP_I2S hardware instance address.
* @param param The fourth parameter of PDM RX IIR_HP filter stage 1 is (504 + LP_I2S_RX_IIR_HP_MULT12_0[2:0])
*/
static inline void lp_i2s_ll_rx_set_pdm_hp_filter_param0(lp_i2s_dev_t *hw, uint32_t param)
{
hw->rx_pdm_conf.rx_iir_hp_mult12_0 = param;
}
/**
* @brief Set LP I2S RX PDM high pass filter param5
*
* @param hw Peripheral LP_I2S hardware instance address.
* @param param The fourth parameter of PDM RX IIR_HP filter stage 2 is (504 + LP_I2S_RX_IIR_HP_MULT12_5[2:0])
*/
static inline void lp_i2s_ll_rx_set_pdm_hp_filter_param5(lp_i2s_dev_t *hw, uint32_t param)
{
hw->rx_pdm_conf.rx_iir_hp_mult12_5 = param;
}
/**
* @brief Configure RX PDM amplify number
* @note This is the amplification number of the digital amplifier,
* which is added after the PDM to PCM conversion result and mainly used for
* amplify the small PDM signal under the VAD scenario
* pcm_result = pdm_input * amplify_num
* pcm_result = 0 if amplify_num = 0
*
* @param hw Peripheral LP_I2S hardware instance address.
* @param amp_num PDM RX amplify number
*/
static inline void lp_i2s_ll_rx_set_pdm_amplify_num(lp_i2s_dev_t *hw, uint32_t amp_num)
{
hw->rx_pdm_conf.rx_pdm2pcm_amplify_num = amp_num;
}
/*---------------------------------------------------------------
General
---------------------------------------------------------------*/
/**
* @brief Configure LP I2S rx channel bits and bits mode
*
* @param hw LP I2S hardware instance
* @param chan_bits Chan bits
* @param bits_mode Bits mode
*/
static inline void lp_i2s_ll_rx_set_sample_bit(lp_i2s_dev_t *hw, int chan_bits, int bits_mode)
{
hw->rx_conf1.rx_tdm_chan_bits = chan_bits - 1;
hw->rx_conf1.rx_bits_mod = bits_mode - 1;
}
/**
* @brief Set LP I2S rx ws polarity
*
* @param hw LP I2S hardware instance
* @param pol ws polarity
*/
static inline void lp_i2s_ll_rx_set_ws_pol(lp_i2s_dev_t *hw, lp_i2s_ll_rx_ws_pol_t pol)
{
hw->rx_conf.rx_ws_idle_pol = pol;
}
/**
* @brief Set LP I2S rx ws width
*
* @param hw LP I2S hardware instance
* @param width ws width
*/
static inline void lp_i2s_ll_rx_set_ws_width(lp_i2s_dev_t *hw, int width)
{
hw->rx_conf1.rx_tdm_ws_width = width - 1;
}
/**
* @brief Set LP I2S rx half sample bits
*
* @param hw LP I2S hardware instance
* @param half_sample_bits Half sample bits
*/
static inline void lp_i2s_ll_rx_set_half_sample_bits(lp_i2s_dev_t *hw, int half_sample_bits)
{
hw->rx_conf1.rx_half_sample_bits = half_sample_bits - 1;
}
/**
* @brief Enable LP I2S RX set active channul
*
* @param hw LP I2S hardware instance
* @param chanid channel id
* @param enable enable or disable
*/
static inline void lp_i2s_ll_rx_set_active_chan(lp_i2s_dev_t *hw, uint32_t chan_mask)
{
uint32_t tdm_ctrl = hw->rx_tdm_ctrl.val;
tdm_ctrl &= ~LP_I2S_LL_TDM_CH_MASK;
tdm_ctrl |= (chan_mask & LP_I2S_LL_TDM_CH_MASK);
hw->rx_tdm_ctrl.val = tdm_ctrl;
}
/**
* @brief Set LP I2S rx std channels
*
* @param hw LP I2S hardware instance
* @param chan_mask select chan to receive data
*/
static inline void lp_i2s_ll_rx_select_std_chan(lp_i2s_dev_t *hw, i2s_std_slot_mask_t chan_mask)
{
hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = 1;
uint32_t mask = 0;
switch (chan_mask) {
case I2S_STD_SLOT_LEFT:
mask = 0x01;
break;
case I2S_STD_SLOT_RIGHT:
mask = 0x02;
break;
case I2S_STD_SLOT_BOTH:
mask = 0x03;
break;
default:
HAL_ASSERT(false);
break;
}
lp_i2s_ll_rx_set_active_chan(hw, mask);
}
/**
* @brief Set LP I2S rx pdm2pcm enable
*
* @param hw LP I2S hardware instance
* @param en enable or disable
*/
static inline void lp_i2s_ll_rx_set_pdm2pcm_en(lp_i2s_dev_t *hw, bool en)
{
hw->rx_pdm_conf.rx_pdm2pcm_en = en;
}
/**
* @brief Set LP I2S rx bit order
*
* @param hw LP I2S hardware instance
* @param bit_order bit order
*/
static inline void lp_i2s_ll_rx_set_bit_order(lp_i2s_dev_t *hw, lp_i2s_ll_rx_bit_order_t bit_order)
{
hw->rx_conf.rx_bit_order = bit_order;
}
/**
* @brief Enable LP I2S 24 fill
*
* @param hw LP I2S hardware instance
* @param en enable or disable
*/
static inline void lp_i2s_ll_rx_enable_24_fill(lp_i2s_dev_t *hw, bool en)
{
hw->rx_conf.rx_24_fill_en = en;
}
/**
* @brief Set LP I2S rx alignment mode
*
* @param hw LP I2S hardware instance
* @param align align
*/
static inline void lp_i2s_ll_rx_set_alignment_mode(lp_i2s_dev_t *hw, lp_i2s_ll_rx_alignment_t align)
{
hw->rx_conf.rx_left_align = align;
}
/**
* @brief Set LP I2S rx endian
*
* @param hw LP I2S hardware instance
* @param endian endian
*/
static inline void lp_i2s_ll_rx_set_endian(lp_i2s_dev_t *hw, lp_i2s_ll_rx_endian_t endian)
{
hw->rx_conf.rx_big_endian = endian;
}
/**
* @brief Enable RX mono mode
*
* @param hw LP I2S hardware instance
* @param mono_ena Set true to enable mono mde.
*/
static inline void lp_i2s_ll_rx_enable_mono_mode(lp_i2s_dev_t *hw, bool mono_ena)
{
hw->rx_conf.rx_mono = mono_ena;
hw->rx_conf.rx_mono_fst_vld = mono_ena;
}
/**
* @brief Enable RX MSB shift, the data will be launch at the first BCK clock
*
* @param hw LP I2S hardware instance
* @param msb_shift_enable Set true to enable MSB shift
*/
static inline void lp_i2s_ll_rx_enable_msb_shift(lp_i2s_dev_t *hw, bool msb_shift_enable)
{
hw->rx_conf1.rx_msb_shift = msb_shift_enable;
}
/**
* Read from LP I2S hardware data buffer.
*
* @param hw Beginning address of the peripheral registers.
* @param buffer_to_rcv Address of a buffer to read data from hardware data buffer
* @param byte_len Length to read, in bytes.
*/
static inline void lp_i2s_ll_read_buffer(lp_i2s_dev_t *hw, void *buffer_to_rcv, size_t byte_len)
{
for (int i = 0; i < byte_len; i += 4) {
*(uint32_t *)(buffer_to_rcv + i) = *((uint32_t *)LP_I2S_RAM_BASE);
}
}
/**
* Write from LP I2S hardware data buffer.
*
* @param hw Beginning address of the peripheral registers.
* @param buffer_to_rcv Address of a buffer to write data from hardware data buffer
* @param bitlbyte_lenen Length to write, in bytes.
*/
static inline void lp_i2s_ll_write_buffer(lp_i2s_dev_t *hw, const void *buffer_to_send, size_t byte_len)
{
for (int i = 0; i < byte_len; i += 4) {
//Use memcpy to get around alignment issues for txdata
*((uint32_t *)LP_I2S_RAM_BASE) = *(uint32_t *)(buffer_to_send + i);
}
}
/**
* Get RX mem fifo cnt, in bytes
*
* @param hw Beginning address of the peripheral registers.
*/
static inline uint32_t lp_i2s_ll_get_rx_mem_fifo_cnt(lp_i2s_dev_t *hw)
{
return hw->rx_mem_conf.rx_mem_fifo_cnt * 4;
}
/*---------------------------------------------------------------
Interrupt
---------------------------------------------------------------*/
/**
* @brief Get LP I2S RX interrupt status word
*
* @param hw LP I2S hardware instance
* @param raw raw or status
*/
__attribute__((always_inline))
static inline uint32_t lp_i2s_ll_rx_get_interrupt_status(lp_i2s_dev_t *hw, bool raw)
{
if (raw) {
return hw->int_raw.val;
} else {
return hw->int_st.val;
}
}
/**
* @brief Get interrupt status reg address
*
* @param[in] hw LP I2S hardware instance
*
* @return Interrupt status reg address
*/
static inline uint32_t lp_i2s_ll_get_intr_status_reg_addr(lp_i2s_dev_t *hw)
{
return (uint32_t) & (hw->int_st);
}
/**
* @brief Enable LP I2S RX channel interrupt
*
* @param[in] hw LP I2S hardware instance
* @param[in] mask mask
* @param[in] enable enable or disable
*/
static inline void lp_i2s_ll_rx_enable_interrupt(lp_i2s_dev_t *hw, uint32_t mask, bool enable)
{
uint32_t int_ena = hw->int_ena.val;
if (enable) {
int_ena |= mask;
} else {
int_ena &= ~mask;
}
hw->int_ena.val = int_ena;
}
/**
* @brief Clear LP I2S RX channel interrupt
*
* @param[in] hw LP I2S hardware instance
* @param[in] mask mask
*/
__attribute__((always_inline))
static inline void lp_i2s_ll_rx_clear_interrupt_status(lp_i2s_dev_t *hw, uint32_t mask)
{
hw->int_clr.val = mask;
}
/*---------------------------------------------------------------
VAD
---------------------------------------------------------------*/
#define LP_VAD_LL_INIT_FRAME_MIN 100
#define LP_VAD_LL_INIT_FRAME_MAX 200
/**
* @brief Set VAD init frame number
*
* @param[in] hw LP I2S hardware instance
* @param[in] frame_num Frame number
*/
static inline void lp_vad_ll_set_init_frame_num(lp_i2s_dev_t *hw, int frame_num)
{
hw->vad_param0.param_init_frame_num = frame_num;
}
/**
* @brief Set VAD min energy
*
* @param[in] hw LP I2S hardware instance
* @param[in] min_energy Min energy
*/
static inline void lp_vad_ll_set_init_min_energy(lp_i2s_dev_t *hw, int min_energy)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->vad_param0, param_min_energy, min_energy);
}
/**
* @brief Set VAD speak activity thresh
*
* @param[in] hw LP I2S hardware instance
* @param[in] thresh Threshold
*/
static inline void lp_vad_ll_set_speak_activity_thresh(lp_i2s_dev_t *hw, int thresh)
{
hw->vad_param1.param_hangover_speech = thresh;
}
/**
* @brief Set VAD non speak activity thresh
*
* @param[in] hw LP I2S hardware instance
* @param[in] thresh Threshold
*/
static inline void lp_vad_ll_set_non_speak_activity_thresh(lp_i2s_dev_t *hw, int thresh)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->vad_param1, param_hangover_silent, thresh);
}
/**
* @brief Set VAD min speak activity thresh
*
* @param[in] hw LP I2S hardware instance
* @param[in] thresh Threshold
*/
static inline void lp_vad_ll_set_min_speak_activity_thresh(lp_i2s_dev_t *hw, int thresh)
{
hw->vad_param1.param_min_speech_count = thresh;
}
/**
* @brief Set VAD max speak activity thresh
*
* @param[in] hw LP I2S hardware instance
* @param[in] thresh Threshold
*/
static inline void lp_vad_ll_set_max_speak_activity_thresh(lp_i2s_dev_t *hw, int thresh)
{
hw->vad_param1.param_max_speech_count = thresh;
}
/**
* @brief Skip band energy check
*
* @param[in] hw LP I2S hardware instance
* @param[in] skip 1: skip; 0: not skip
*/
static inline void lp_vad_ll_skip_band_energy(lp_i2s_dev_t *hw, bool skip)
{
hw->vad_param1.param_skip_band_energy = skip;
}
/**
* @brief Enable LP I2S 24 fill
*
* @param[in] hw LP I2S hardware instance
* @param[in] en enable or disable
*/
static inline void lp_vad_ll_enable(lp_i2s_dev_t *hw, bool en)
{
hw->vad_conf.vad_en = en;
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
{
.mck_out_sig = CLK_I2S_MUX_IDX,
.mck_in_sig = -1, // Unavailable
.m_tx_bck_sig = I2S0O_BCK_OUT_IDX,
.m_rx_bck_sig = I2S0I_BCK_OUT_IDX,
.m_tx_ws_sig = I2S0O_WS_OUT_IDX,
.m_rx_ws_sig = I2S0I_WS_OUT_IDX,
.s_tx_bck_sig = I2S0O_BCK_IN_IDX,
.s_rx_bck_sig = I2S0I_BCK_IN_IDX,
.s_tx_ws_sig = I2S0O_WS_IN_IDX,
.s_rx_ws_sig = I2S0I_WS_IN_IDX,
.data_out_sig = I2S0O_DATA_OUT23_IDX,
.data_in_sig = I2S0I_DATA_IN15_IDX,
.irq = ETS_I2S0_INTR_SOURCE,
}
};
@@ -0,0 +1,998 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
// The LL layer for ESP32-S2 I2S register operations
#pragma once
#include <stdbool.h>
#include "hal/misc.h"
#include "hal/i2s_periph.h"
#include "soc/i2s_struct.h"
#include "soc/system_reg.h"
#include "soc/dport_access.h"
#include "hal/i2s_types.h"
#include "hal/hal_utils.h"
#include "hal/assert.h"
#define I2S_LL_GET(_attr) I2S_LL_ ## _attr
#define I2S_LL_BUS_WIDTH 24
#define I2S_LL_INST_NUM 1
#ifdef __cplusplus
extern "C" {
#endif
// Get I2S hardware instance with giving i2s num
#define I2S_LL_GET_HW(num) (((num) == 0) ? (&I2S0) : NULL)
#define I2S_LL_BCK_MAX_PRESCALE (64)
#define I2S_LL_CLK_FRAC_DIV_N_MAX 256 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the N register is 8 bit-width
#define I2S_LL_CLK_FRAC_DIV_AB_MAX 64 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the a/b register is 6 bit-width
#define I2S_LL_EVENT_RX_EOF BIT(9)
#define I2S_LL_EVENT_TX_EOF BIT(12)
#define I2S_LL_EVENT_RX_DSCR_ERR BIT(13)
#define I2S_LL_EVENT_TX_DSCR_ERR BIT(14)
#define I2S_INTR_MAX (UINT32_MAX)
#define I2S_LL_TX_EVENT_MASK I2S_LL_EVENT_TX_EOF
#define I2S_LL_RX_EVENT_MASK I2S_LL_EVENT_RX_EOF
#define I2S_LL_DEFAULT_CLK_FREQ (160 * 1000000) // PLL_F160M_CLK: 160MHz
/**
* @brief Enable DMA descriptor owner check
*
* @param hw Peripheral I2S hardware instance address.
* @param en whether to enable owner check
*/
static inline void i2s_ll_dma_enable_owner_check(i2s_dev_t *hw, bool en)
{
hw->lc_conf.check_owner = en;
}
/**
* @brief Enable DMA descriptor write back
*
* @param hw Peripheral I2S hardware instance address.
* @param en whether to enable write back
*/
static inline void i2s_ll_dma_enable_auto_write_back(i2s_dev_t *hw, bool en)
{
hw->lc_conf.out_auto_wrback = en;
}
/**
* @brief I2S DMA generate EOF event on data in FIFO popped out
*
* @param hw Peripheral I2S hardware instance address.
* @param en True to enable, False to disable
*/
static inline void i2s_ll_dma_enable_eof_on_fifo_empty(i2s_dev_t *hw, bool en)
{
hw->lc_conf.out_eof_mode = en;
}
/**
* @brief Enable the bus clock for I2S module
*
* @param i2s_id The port id of I2S
* @param enable Set true to enable the buf clock
*/
static inline void i2s_ll_enable_bus_clock(int i2s_id, bool enable)
{
(void) i2s_id;
if (enable) {
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
} else {
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
}
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define i2s_ll_enable_bus_clock(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
i2s_ll_enable_bus_clock(__VA_ARGS__); \
} while(0)
/**
* @brief Reset the I2S module
*
* @param i2s_id The port id of I2S
*/
static inline void i2s_ll_reset_register(int i2s_id)
{
(void) i2s_id;
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN0_REG, DPORT_I2S0_RST);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN0_REG, DPORT_I2S0_RST);
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define i2s_ll_reset_register(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
i2s_ll_reset_register(__VA_ARGS__); \
} while(0)
/**
* @brief I2S module general init, enable I2S clock.
*
* @param hw Peripheral I2S hardware instance address.
* @param enable set true to enable the core clock
*/
static inline void i2s_ll_enable_core_clock(i2s_dev_t *hw, bool enable)
{
if (enable && !hw->clkm_conf.clk_en) {
hw->clkm_conf.clk_sel = 2;
hw->clkm_conf.clk_en = 1;
hw->conf2.val = 0;
} else if (!enable && hw->clkm_conf.clk_en) {
hw->clkm_conf.clk_en = 0;
}
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define i2s_ll_enable_core_clock(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
i2s_ll_enable_core_clock(__VA_ARGS__); \
} while(0)
/**
* @brief I2S tx msb right enable
*
* @param hw Peripheral I2S hardware instance address.
* @param enable Set true to enable tx msb right
*/
static inline void i2s_ll_tx_enable_msb_right(i2s_dev_t *hw, bool enable)
{
hw->conf.tx_msb_right = enable;
}
/**
* @brief I2S rx msb right enable
*
* @param hw Peripheral I2S hardware instance address.
* @param enable Set true to enable rx msb right
*/
static inline void i2s_ll_rx_enable_msb_right(i2s_dev_t *hw, bool enable)
{
hw->conf.rx_msb_right = enable;
}
/**
* @brief I2S tx right channel first
*
* @param hw Peripheral I2S hardware instance address.
* @param enable Set true to enable send right channel first
*/
static inline void i2s_ll_tx_enable_right_first(i2s_dev_t *hw, bool enable)
{
hw->conf.tx_right_first = enable;
}
/**
* @brief I2S rx right channel first
*
* @param hw Peripheral I2S hardware instance address.
* @param enable Set true to enable receive right channel first
*/
static inline void i2s_ll_rx_enable_right_first(i2s_dev_t *hw, bool enable)
{
hw->conf.rx_right_first = enable;
}
/**
* @brief I2S tx fifo module force enable
*
* @param hw Peripheral I2S hardware instance address.
* @param enable Set true to enable tx fifo module
*/
static inline void i2s_ll_tx_force_enable_fifo_mod(i2s_dev_t *hw, bool enable)
{
hw->fifo_conf.tx_fifo_mod_force_en = enable;
}
/**
* @brief I2S rx fifo module force enable
*
* @param hw Peripheral I2S hardware instance address.
* @param enable Set true to enable rx fifo module
*/
static inline void i2s_ll_rx_force_enable_fifo_mod(i2s_dev_t *hw, bool enable)
{
hw->fifo_conf.rx_fifo_mod_force_en = enable;
}
/**
* @brief Enable I2S TX slave mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slave_en Set true to enable slave mode
*/
static inline void i2s_ll_tx_set_slave_mod(i2s_dev_t *hw, bool slave_en)
{
hw->conf.tx_slave_mod = slave_en;
}
/**
* @brief Enable I2S RX slave mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slave_en Set true to enable slave mode
*/
static inline void i2s_ll_rx_set_slave_mod(i2s_dev_t *hw, bool slave_en)
{
hw->conf.rx_slave_mod = slave_en;
}
/**
* @brief Reset TX module
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_reset(i2s_dev_t *hw)
{
hw->conf.tx_reset = 1;
hw->conf.tx_reset = 0;
}
/**
* @brief Reset RX module
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_reset(i2s_dev_t *hw)
{
hw->conf.rx_reset = 1;
hw->conf.rx_reset = 0;
}
/**
* @brief Reset TX FIFO
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_reset_fifo(i2s_dev_t *hw)
{
hw->conf.tx_fifo_reset = 1;
hw->conf.tx_fifo_reset = 0;
}
/**
* @brief Reset RX FIFO
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw)
{
hw->conf.rx_fifo_reset = 1;
hw->conf.rx_fifo_reset = 0;
}
/**
* @brief Set TX source clock
*
* @param hw Peripheral I2S hardware instance address.
* @param src I2S source clock
*/
static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
{
hw->clkm_conf.clk_sel = (src == I2S_CLK_SRC_APLL) ? 1 : 2;
}
/**
* @brief Set RX source clock
*
* @param hw Peripheral I2S hardware instance address.
* @param src I2S source clock
*/
static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
{
hw->clkm_conf.clk_sel = (src == I2S_CLK_SRC_APLL) ? 1 : 2;
}
/**
* @brief Set I2S tx bck div num
*
* @param hw Peripheral I2S hardware instance address.
* @param val value to set tx bck div num
*/
static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
{
hw->sample_rate_conf.tx_bck_div_num = val;
}
/**
* @brief Configure I2S module clock divider
* @note mclk on ESP32 is shared by both TX and RX channel
* mclk = sclk / (mclk_div + b/a)
*
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div integer part of the division from sclk to mclk
* @param a Denominator of decimal part
* @param b Numerator of decimal part
*/
static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div);
hw->clkm_conf.clkm_div_b = b;
hw->clkm_conf.clkm_div_a = a;
}
/**
* @brief Configure I2S TX module clock divider
* @note mclk on ESP32 is shared by both TX and RX channel
*
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
i2s_ll_set_raw_mclk_div(hw, mclk_div->integer, mclk_div->denominator, mclk_div->numerator);
}
/**
* @brief Set I2S rx bck div num
*
* @param hw Peripheral I2S hardware instance address.
* @param val value to set rx bck div num
*/
static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
{
hw->sample_rate_conf.rx_bck_div_num = val;
}
/**
* @brief Configure I2S RX module clock divider
* @note mclk on ESP32S2 is shared by both TX and RX channel
*
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
// TX and RX channel on ESP32 shares a same mclk
i2s_ll_tx_set_mclk(hw, mclk_div);
}
/**
* @brief Enable interrupt by mask
*
* @param hw Peripheral I2S hardware instance address.
* @param mask Interrupt event mask
* @param en true to enable, false to disable
*/
static inline void i2s_ll_enable_intr(i2s_dev_t *hw, uint32_t mask, bool en)
{
uint32_t int_ena_mask = hw->int_ena.val;
if (en) {
int_ena_mask |= mask;
} else {
int_ena_mask &= ~mask;
}
hw->int_ena.val = int_ena_mask;
}
/**
* @brief Enable TX interrupt
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_enable_intr(i2s_dev_t *hw)
{
hw->int_ena.out_eof = 1;
}
/**
* @brief Disable TX interrupt
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_disable_intr(i2s_dev_t *hw)
{
hw->int_ena.out_eof = 0;
}
/**
* @brief Enable RX interrupt
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_enable_intr(i2s_dev_t *hw)
{
hw->int_ena.in_suc_eof = 1;
}
/**
* @brief Disable RX interrupt
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_disable_intr(i2s_dev_t *hw)
{
hw->int_ena.in_suc_eof = 0;
}
/**
* @brief Get interrupt status register address
*
* @param hw Peripheral I2S hardware instance address.
* @return interrupt status register address
*/
static inline volatile void *i2s_ll_get_intr_status_reg(i2s_dev_t *hw)
{
return &hw->int_st;
}
/**
* @brief Get I2S interrupt status
*
* @param hw Peripheral I2S hardware instance address.
* @return
* - module interrupt status
*/
__attribute__((always_inline))
static inline uint32_t i2s_ll_get_intr_status(i2s_dev_t *hw)
{
return hw->int_st.val;
}
/**
* @brief Get DMA interrupt status register address
*/
static inline volatile void *i2s_ll_get_interrupt_status_reg(i2s_dev_t *hw)
{
return (volatile void *)(&hw->int_st);
}
/**
* @brief Clear I2S interrupt status
*
* @param hw Peripheral I2S hardware instance address.
* @param clr_mask Interrupt mask to be cleared.
*/
__attribute__((always_inline))
static inline void i2s_ll_clear_intr_status(i2s_dev_t *hw, uint32_t clr_mask)
{
hw->int_clr.val = clr_mask;
}
/**
* @brief Reset dma out
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_reset_dma(i2s_dev_t *hw)
{
hw->lc_conf.out_rst = 1;
hw->lc_conf.out_rst = 0;
}
/**
* @brief Reset dma in
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_reset_dma(i2s_dev_t *hw)
{
hw->lc_conf.in_rst = 1;
hw->lc_conf.in_rst = 0;
}
/**
* @brief Enable I2S TX STD mode
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_enable_std(i2s_dev_t *hw)
{
hw->conf2.val = 0;
}
/**
* @brief Enable I2S RX STD mode
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_enable_std(i2s_dev_t *hw)
{
hw->conf2.val = 0;
}
/**
* @brief Enable I2S TX PDM mode
* @note ESP32-S2 doesn't support pdm
* This function is used to be compatible with those support pdm
* @param hw Peripheral I2S hardware instance address.
* @param pcm2pdm_en Set true to enable TX PCM to PDM filter
*/
static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pcm2pdm_en)
{
// remain empty
(void)hw;
(void)pcm2pdm_en;
}
/**
* @brief Enable I2S RX PDM mode
* @note ESP32-S2 doesn't support pdm
* This function is used to be compatible with those support pdm
* @param hw Peripheral I2S hardware instance address.
* @param pdm2pcm_en Set true to enable RX PDM to PCM filter
*/
static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm2pcm_en)
{
// remain empty
(void)hw;
(void)pdm2pcm_en;
}
/**
* @brief Start out link
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_start_out_link(i2s_dev_t *hw)
{
hw->out_link.start = 1;
}
/**
* @brief Set I2S out link address
*
* @param hw Peripheral I2S hardware instance address.
* @param val value to set out link address
*/
static inline void i2s_ll_set_out_link_addr(i2s_dev_t *hw, uint32_t val)
{
hw->out_link.addr = val;
}
/**
* @brief Start TX module
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_start(i2s_dev_t *hw)
{
hw->conf.tx_start = 1;
}
/**
* @brief Start RX module
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_start(i2s_dev_t *hw)
{
hw->conf.rx_start = 1;
}
/**
* @brief Configure TX DMA descriptor address and start TX DMA
*
* @param hw Peripheral I2S hardware instance address.
* @param link_addr DMA descriptor link address.
*/
static inline void i2s_ll_tx_start_link(i2s_dev_t *hw, uint32_t link_addr)
{
i2s_ll_set_out_link_addr(hw, link_addr);
i2s_ll_start_out_link(hw);
}
/**
* @brief Configure RX DMA descriptor address and start TX DMA
*
* @param hw Peripheral I2S hardware instance address.
* @param link_addr DMA descriptor link address.
*/
static inline void i2s_ll_rx_start_link(i2s_dev_t *hw, uint32_t link_addr)
{
hw->in_link.addr = link_addr;
hw->in_link.start = 1;
}
/**
* @brief Stop TX module
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_stop(i2s_dev_t *hw)
{
hw->conf.tx_start = 0;
}
/**
* @brief Stop RX module
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_stop(i2s_dev_t *hw)
{
hw->conf.rx_start = 0;
}
/**
* @brief Stop out link
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_stop_link(i2s_dev_t *hw)
{
hw->out_link.stop = 1;
}
/**
* @brief Stop in link
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_stop_link(i2s_dev_t *hw)
{
hw->in_link.stop = 1;
}
/**
* @brief Get I2S out eof descriptor address
*
* @param hw Peripheral I2S hardware instance address.
* @param eof_addr Pointer to accept out eof des address
*/
__attribute__((always_inline))
static inline void i2s_ll_tx_get_eof_des_addr(i2s_dev_t *hw, uint32_t *eof_addr)
{
*eof_addr = hw->out_eof_des_addr;
}
/**
* @brief Get I2S in eof descriptor address
*
* @param hw Peripheral I2S hardware instance address.
* @param eof_addr Pointer to accept in eof des address
*/
__attribute__((always_inline))
static inline void i2s_ll_rx_get_eof_des_addr(i2s_dev_t *hw, uint32_t *eof_addr)
{
*eof_addr = hw->in_eof_des_addr;
}
/**
* @brief Configure the received length to trigger in_suc_eof interrupt
*
* @param hw Peripheral I2S hardware instance address.
* @param eof_num the byte length to trigger in_suc_eof interrupt
*/
static inline void i2s_ll_rx_set_eof_num(i2s_dev_t *hw, uint32_t eof_num)
{
hw->rx_eof_num = eof_num;
}
/**
* @brief Configure TX chan bit and audio data bit, on ESP32-S2, sample_bit should equals to data_bit
*
* @param hw Peripheral I2S hardware instance address.
* @param chan_bit The chan bit width
* @param data_bit The audio data bit width
*/
static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit)
{
hw->fifo_conf.tx_fifo_mod = (chan_bit <= I2S_DATA_BIT_WIDTH_16BIT ? 0 : 2);
hw->sample_rate_conf.tx_bits_mod = data_bit;
}
/**
* @brief Configure RX chan bit and audio data bit, on ESP32-S2, sample_bit should equals to data_bit
*
* @param hw Peripheral I2S hardware instance address.
* @param chan_bit The chan bit width
* @param data_bit The audio data bit width
*/
static inline void i2s_ll_rx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit)
{
hw->fifo_conf.rx_fifo_mod = (chan_bit <= I2S_DATA_BIT_WIDTH_16BIT ? 0 : 2);
hw->sample_rate_conf.rx_bits_mod = data_bit;
}
/**
* @brief Enable I2S DMA
*
* @param hw Peripheral I2S hardware instance address.
* @param ena Set true to enable DMA
*/
static inline void i2s_ll_enable_dma(i2s_dev_t *hw, bool ena)
{
hw->fifo_conf.dscr_en = ena;
}
/**
* @brief Set I2S TX to philips standard
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_set_format_philips(i2s_dev_t *hw)
{
hw->conf.tx_short_sync = 0;
hw->conf.tx_msb_shift = 1;
}
/**
* @brief Set I2S RX to philips standard
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_set_format_philips(i2s_dev_t *hw)
{
hw->conf.rx_short_sync = 0;
hw->conf.rx_msb_shift = 1;
}
/**
* @brief Set I2S TX to MSB Alignment Standard
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_set_format_msb_align(i2s_dev_t *hw)
{
hw->conf.tx_short_sync = 0;
hw->conf.tx_msb_shift = 0;
}
/**
* @brief Set I2S RX to MSB Alignment Standard
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_set_format_msb_align(i2s_dev_t *hw)
{
hw->conf.rx_short_sync = 0;
hw->conf.rx_msb_shift = 0;
}
/**
* @brief Set I2S TX to PCM short standard
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_set_pcm_short(i2s_dev_t *hw)
{
hw->conf.tx_short_sync = 1;
hw->conf.tx_msb_shift = 0;
}
/**
* @brief Set I2S RX to PCM short standard
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_set_pcm_short(i2s_dev_t *hw)
{
hw->conf.rx_short_sync = 1;
hw->conf.rx_msb_shift = 0;
}
/**
* @brief Set I2S TX to PCM long standard
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_tx_set_pcm_long(i2s_dev_t *hw)
{
hw->conf.tx_short_sync = 0;
hw->conf.tx_msb_shift = 0;
}
/**
* @brief Set I2S RX to PCM long standard
*
* @param hw Peripheral I2S hardware instance address.
*/
static inline void i2s_ll_rx_set_pcm_long(i2s_dev_t *hw)
{
hw->conf.rx_short_sync = 0;
hw->conf.rx_msb_shift = 0;
}
/**
* @brief Configure TX WS signal width
*
* @param hw Peripheral I2S hardware instance address.
* @param width WS width in BCK cycle
*/
static inline void i2s_ll_tx_set_ws_width(i2s_dev_t *hw, int width)
{
hw->conf.tx_short_sync = width == 1 ? 1 : 0;
}
/**
* @brief Configure RX WS signal width
*
* @param hw Peripheral I2S hardware instance address.
* @param width WS width in BCK cycle
*/
static inline void i2s_ll_rx_set_ws_width(i2s_dev_t *hw, int width)
{
hw->conf.rx_short_sync = width == 1 ? 1 : 0;
}
/**
* @brief Enable TX MSB shift, the data will be launch at the first BCK clock
*
* @param hw Peripheral I2S hardware instance address.
* @param msb_shift_enable Set true to enable MSB shift
*/
static inline void i2s_ll_tx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable)
{
hw->conf.tx_msb_shift = msb_shift_enable;
}
/**
* @brief Enable RX MSB shift, the data will be launch at the first BCK clock
*
* @param hw Peripheral I2S hardware instance address.
* @param msb_shift_enable Set true to enable MSB shift
*/
static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable)
{
hw->conf.rx_msb_shift = msb_shift_enable;
}
/**
* @brief Set I2S tx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to send data
* @param is_mono is mono mode
*/
static inline void i2s_ll_tx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_mono)
{
if (is_mono) {
switch (slot_mask) {
case I2S_STD_SLOT_RIGHT:
hw->conf_chan.tx_chan_mod = 3;
break;
case I2S_STD_SLOT_LEFT:
hw->conf_chan.tx_chan_mod = 4;
break;
case I2S_STD_SLOT_BOTH:
hw->conf_chan.tx_chan_mod = 1; // 1 & 2 has same effect
break;
default:
break;
}
} else {
switch (slot_mask) {
case I2S_STD_SLOT_RIGHT:
hw->conf_chan.tx_chan_mod = 1;
break;
case I2S_STD_SLOT_LEFT:
hw->conf_chan.tx_chan_mod = 2;
break;
case I2S_STD_SLOT_BOTH:
hw->conf_chan.tx_chan_mod = 0;
break;
default:
break;
}
}
}
/**
* @brief Set I2S rx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to receive data
*/
static inline void i2s_ll_rx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
{
switch (slot_mask) {
case I2S_STD_SLOT_RIGHT:
hw->conf_chan.rx_chan_mod = is_msb_right ? 1 : 2;
break;
case I2S_STD_SLOT_LEFT:
hw->conf_chan.rx_chan_mod = is_msb_right ? 2 : 1;
break;
case I2S_STD_SLOT_BOTH:
hw->conf_chan.rx_chan_mod = 0;
break;
default:
break;
}
}
/**
* @brief Set I2S tx bits mod
*
* @param hw Peripheral I2S hardware instance address.
* @param val value to set tx bits mod
*/
static inline void i2s_ll_tx_set_bits_mod(i2s_dev_t *hw, uint32_t val)
{
hw->sample_rate_conf.tx_bits_mod = val;
}
/**
* @brief Set I2S tx dma equal
*
* @param hw Peripheral I2S hardware instance address.
* @param val value to set tx dma equal
*/
static inline void i2s_ll_tx_enable_dma_equal(i2s_dev_t *hw, bool en)
{
hw->conf.tx_dma_equal = en;
}
/**
* @brief Enable TX mono mode
*
* @param hw Peripheral I2S hardware instance address.
* @param mono_ena Set true to enable mono mde.
*/
static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena)
{
int data_bit = hw->sample_rate_conf.tx_bits_mod;
hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
hw->conf.tx_dma_equal = mono_ena;
}
/**
* @brief Enable RX mono mode
*
* @param hw Peripheral I2S hardware instance address.
* @param mono_ena Set true to enable mono mde.
*/
static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena)
{
int data_bit = hw->sample_rate_conf.rx_bits_mod;
hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
hw->conf.rx_dma_equal = mono_ena;
}
/**
* @brief Enable I2S loopback mode
*
* @param hw Peripheral I2S hardware instance address.
* @param loopback_en Set true to share BCK and WS signal for tx module and rx module.
*/
static inline void i2s_ll_share_bck_ws(i2s_dev_t *hw, bool loopback_en)
{
hw->conf.sig_loopback = loopback_en;
}
/**
* @brief Enable I2S LCD mode
*
* @param hw Peripheral I2S hardware instance address.
* @param enable Set true to enable LCD mode.
*/
static inline void i2s_ll_enable_lcd(i2s_dev_t *hw, bool enable)
{
hw->conf2.lcd_en = enable;
}
/**
* @brief Set whether to continue I2S signal on bus when TX FIFO is empty
*
* @param hw Peripheral I2S hardware instance address.
* @param en whether to stop when tx fifo is empty
*/
static inline void i2s_ll_tx_stop_on_fifo_empty(i2s_dev_t *hw, bool en)
{
hw->conf1.tx_stop_en = en;
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/i2s_periph.h"
#include "soc/gpio_sig_map.h"
/*
Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc
*/
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)] = {
{
.mck_out_sig = I2S0_MCLK_OUT_IDX,
.mck_in_sig = I2S0_MCLK_IN_IDX,
.m_tx_bck_sig = I2S0O_BCK_OUT_IDX,
.m_rx_bck_sig = I2S0I_BCK_OUT_IDX,
.m_tx_ws_sig = I2S0O_WS_OUT_IDX,
.m_rx_ws_sig = I2S0I_WS_OUT_IDX,
.s_tx_bck_sig = I2S0O_BCK_IN_IDX,
.s_rx_bck_sig = I2S0I_BCK_IN_IDX,
.s_tx_ws_sig = I2S0O_WS_IN_IDX,
.s_rx_ws_sig = I2S0I_WS_IN_IDX,
.data_out_sigs[0] = I2S0O_SD_OUT_IDX,
.data_out_sigs[1] = I2S0O_SD1_OUT_IDX,
.data_in_sigs[0] = I2S0I_SD_IN_IDX,
.data_in_sigs[1] = I2S0I_SD1_IN_IDX,
.data_in_sigs[2] = I2S0I_SD2_IN_IDX,
.data_in_sigs[3] = I2S0I_SD3_IN_IDX,
.irq = ETS_I2S0_INTR_SOURCE,
},
{
.mck_out_sig = I2S1_MCLK_OUT_IDX,
.mck_in_sig = I2S1_MCLK_IN_IDX,
.m_tx_bck_sig = I2S1O_BCK_OUT_IDX,
.m_rx_bck_sig = I2S1I_BCK_OUT_IDX,
.m_tx_ws_sig = I2S1O_WS_OUT_IDX,
.m_rx_ws_sig = I2S1I_WS_OUT_IDX,
.s_tx_bck_sig = I2S1O_BCK_IN_IDX,
.s_rx_bck_sig = I2S1I_BCK_IN_IDX,
.s_tx_ws_sig = I2S1O_WS_IN_IDX,
.s_rx_ws_sig = I2S1I_WS_IN_IDX,
.data_out_sigs[0] = I2S1O_SD_OUT_IDX,
.data_out_sigs[1] = -1,
.data_in_sigs[0] = I2S1I_SD_IN_IDX,
.data_in_sigs[1] = -1,
.data_in_sigs[2] = -1,
.data_in_sigs[3] = -1,
.irq = ETS_I2S1_INTR_SOURCE,
}
};
File diff suppressed because it is too large Load Diff
+405
View File
@@ -0,0 +1,405 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The HAL layer for I2S (common part)
#include "soc/soc.h"
#include "hal/assert.h"
#include "hal/i2s_hal.h"
#if SOC_I2S_HW_VERSION_2 && (SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX_HP_FILTER)
/* PDM tx high pass filter cut-off frequency and coefficients list
* [0]: cut-off frequency * 10; [1]: param0; [2]: param5
* NOTE: the cut-off frequency was timed 10 to use integer type */
static const uint32_t cut_off_coef[21][3] = {
{1850, 0, 0}, {1720, 0, 1}, {1600, 1, 1},
{1500, 1, 2}, {1370, 2, 2}, {1260, 2, 3},
{1200, 0, 3}, {1150, 3, 3}, {1060, 1, 7},
{1040, 2, 4}, {920, 4, 4}, {915, 2, 7},
{810, 4, 5}, {772, 3, 7}, {690, 5, 5},
{630, 4, 7}, {580, 5, 6}, {490, 5, 7},
{460, 6, 6}, {355, 6, 7}, {233, 7, 7}
};
static void s_i2s_hal_get_cut_off_coef(uint32_t freq, uint32_t *param0, uint32_t *param5)
{
uint8_t cnt = 0;
uint32_t min = 10000;
/* Find the closest cut-off frequency and its coefficients */
for (int i = 0; i < 21; i++) {
uint32_t tmp = cut_off_coef[i][0] < freq ? freq - cut_off_coef[i][0] : cut_off_coef[i][0] - freq;
if (tmp < min) {
min = tmp;
cnt = i;
}
}
*param0 = (uint32_t)cut_off_coef[cnt][1];
*param5 = (uint32_t)cut_off_coef[cnt][2];
}
#endif
/**
* @brief Calculate the precise mclk division by sclk and mclk
*
* @param sclk system clock
* @param mclk module clock
* @param mclk_div output the mclk division coefficients
*/
void i2s_hal_calc_mclk_precise_division(uint32_t sclk, uint32_t mclk, hal_utils_clk_div_t *mclk_div)
{
hal_utils_clk_info_t i2s_clk_info = {
.src_freq_hz = sclk,
.exp_freq_hz = mclk,
.max_integ = I2S_LL_CLK_FRAC_DIV_N_MAX,
.min_integ = 1,
.max_fract = I2S_LL_CLK_FRAC_DIV_AB_MAX,
};
hal_utils_calc_clk_div_frac_accurate(&i2s_clk_info, mclk_div);
}
void i2s_hal_init(i2s_hal_context_t *hal, int port_id)
{
/* Get hardware instance */
hal->dev = I2S_LL_GET_HW(port_id);
}
#if SOC_PERIPH_CLK_CTRL_SHARED
void _i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div)
{
if (clk_info) {
hal_utils_clk_div_t mclk_div = {};
hal_utils_clk_div_t *mclk_div_ptr = ret_mclk_div ? ret_mclk_div : &mclk_div;
#if SOC_I2S_HW_VERSION_2
_i2s_ll_tx_enable_clock(hal->dev);
_i2s_ll_mclk_bind_to_tx_clk(hal->dev);
#endif
_i2s_ll_tx_clk_set_src(hal->dev, clk_src);
i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, mclk_div_ptr);
_i2s_ll_tx_set_mclk(hal->dev, mclk_div_ptr);
i2s_ll_tx_set_bck_div_num(hal->dev, clk_info->bclk_div);
} else {
_i2s_ll_tx_clk_set_src(hal->dev, clk_src);
}
}
void _i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div)
{
if (clk_info) {
hal_utils_clk_div_t mclk_div = {};
hal_utils_clk_div_t *mclk_div_ptr = ret_mclk_div ? ret_mclk_div : &mclk_div;
#if SOC_I2S_HW_VERSION_2
_i2s_ll_rx_enable_clock(hal->dev);
_i2s_ll_mclk_bind_to_rx_clk(hal->dev);
#endif
_i2s_ll_rx_clk_set_src(hal->dev, clk_src);
i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, mclk_div_ptr);
_i2s_ll_rx_set_mclk(hal->dev, mclk_div_ptr);
i2s_ll_rx_set_bck_div_num(hal->dev, clk_info->bclk_div);
} else {
_i2s_ll_rx_clk_set_src(hal->dev, clk_src);
}
}
#else
void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div)
{
if (clk_info) {
hal_utils_clk_div_t mclk_div = {};
hal_utils_clk_div_t *mclk_div_ptr = ret_mclk_div ? ret_mclk_div : &mclk_div;
#if SOC_I2S_HW_VERSION_2
i2s_ll_tx_enable_clock(hal->dev);
i2s_ll_mclk_bind_to_tx_clk(hal->dev);
#endif
i2s_ll_tx_clk_set_src(hal->dev, clk_src);
i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, mclk_div_ptr);
i2s_ll_tx_set_mclk(hal->dev, mclk_div_ptr);
i2s_ll_tx_set_bck_div_num(hal->dev, clk_info->bclk_div);
} else {
i2s_ll_tx_clk_set_src(hal->dev, clk_src);
}
}
void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div)
{
if (clk_info) {
hal_utils_clk_div_t mclk_div = {};
hal_utils_clk_div_t *mclk_div_ptr = ret_mclk_div ? ret_mclk_div : &mclk_div;
#if SOC_I2S_HW_VERSION_2
i2s_ll_rx_enable_clock(hal->dev);
i2s_ll_mclk_bind_to_rx_clk(hal->dev);
#endif
i2s_ll_rx_clk_set_src(hal->dev, clk_src);
i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, mclk_div_ptr);
i2s_ll_rx_set_mclk(hal->dev, mclk_div_ptr);
i2s_ll_rx_set_bck_div_num(hal->dev, clk_info->bclk_div);
} else {
i2s_ll_rx_clk_set_src(hal->dev, clk_src);
}
}
#endif // SOC_PERIPH_CLK_CTRL_SHARED
/*-------------------------------------------------------------------------
| STD Specific Slot Configurations |
-------------------------------------------------------------------------*/
void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
{
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
i2s_ll_tx_reset(hal->dev);
i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave
i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->std.bit_shift);
i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->std.ws_width);
#if SOC_I2S_HW_VERSION_1
i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
i2s_ll_tx_select_std_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
// According to the test, the behavior of tx_msb_right is opposite with TRM, TRM is wrong?
i2s_ll_tx_enable_msb_right(hal->dev, slot_cfg->std.msb_right);
i2s_ll_tx_enable_right_first(hal->dev, slot_cfg->std.ws_pol);
/* Should always enable fifo */
i2s_ll_tx_force_enable_fifo_mod(hal->dev, true);
#elif SOC_I2S_HW_VERSION_2
bool is_copy_mono = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO && slot_cfg->std.slot_mask == I2S_STD_SLOT_BOTH;
i2s_ll_tx_enable_mono_mode(hal->dev, is_copy_mono);
i2s_ll_tx_select_std_slot(hal->dev, is_copy_mono ? I2S_STD_SLOT_LEFT : slot_cfg->std.slot_mask);
i2s_ll_tx_set_skip_mask(hal->dev, (slot_cfg->std.slot_mask != I2S_STD_SLOT_BOTH) &&
(slot_cfg->slot_mode == I2S_SLOT_MODE_STEREO));
i2s_ll_tx_set_half_sample_bit(hal->dev, slot_bit_width);
i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->std.ws_pol);
i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->std.bit_order_lsb);
i2s_ll_tx_enable_left_align(hal->dev, slot_cfg->std.left_align);
i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->std.big_endian);
#endif
}
void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
{
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
i2s_ll_rx_reset(hal->dev);
i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave
i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->std.bit_shift);
i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->std.ws_width);
#if SOC_I2S_HW_VERSION_1
i2s_ll_rx_select_std_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->std.msb_right);
i2s_ll_rx_enable_msb_right(hal->dev, slot_cfg->std.msb_right);
i2s_ll_rx_enable_right_first(hal->dev, slot_cfg->std.ws_pol);
/* Should always enable fifo */
i2s_ll_rx_force_enable_fifo_mod(hal->dev, true);
#elif SOC_I2S_HW_VERSION_2
i2s_ll_rx_select_std_slot(hal->dev, slot_cfg->std.slot_mask);
i2s_ll_rx_set_half_sample_bit(hal->dev, slot_bit_width);
i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->std.ws_pol);
i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->std.bit_order_lsb);
i2s_ll_rx_enable_left_align(hal->dev, slot_cfg->std.left_align);
i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->std.big_endian);
#endif
}
void i2s_hal_std_enable_tx_channel(i2s_hal_context_t *hal)
{
i2s_ll_tx_enable_std(hal->dev);
}
void i2s_hal_std_enable_rx_channel(i2s_hal_context_t *hal)
{
i2s_ll_rx_enable_std(hal->dev);
}
/*-------------------------------------------------------------------------
| PDM Specific Slot Configurations |
-------------------------------------------------------------------------*/
#if SOC_I2S_SUPPORTS_PDM_TX
void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
{
bool is_mono = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO;
i2s_ll_tx_reset(hal->dev);
i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave
i2s_ll_tx_enable_msb_shift(hal->dev, false);
if (slot_cfg->pdm_tx.data_fmt == I2S_PDM_DATA_FMT_PCM) {
i2s_ll_tx_set_pdm_prescale(hal->dev, slot_cfg->pdm_tx.sd_prescale);
i2s_ll_tx_set_pdm_hp_scale(hal->dev, slot_cfg->pdm_tx.hp_scale);
i2s_ll_tx_set_pdm_lp_scale(hal->dev, slot_cfg->pdm_tx.lp_scale);
i2s_ll_tx_set_pdm_sinc_scale(hal->dev, slot_cfg->pdm_tx.sinc_scale);
i2s_ll_tx_set_pdm_sd_scale(hal->dev, slot_cfg->pdm_tx.sd_scale);
}
#if SOC_I2S_HW_VERSION_1
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
i2s_ll_tx_force_enable_fifo_mod(hal->dev, true);
i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
i2s_ll_tx_enable_mono_mode(hal->dev, is_mono);
i2s_ll_tx_select_pdm_slot(hal->dev, slot_cfg->pdm_tx.slot_mask & I2S_STD_SLOT_BOTH, is_mono);
i2s_ll_tx_enable_msb_right(hal->dev, false);
i2s_ll_tx_enable_right_first(hal->dev, false);
#elif SOC_I2S_HW_VERSION_2
/* PDM TX line mode */
i2s_ll_tx_pdm_line_mode(hal->dev, slot_cfg->pdm_tx.line_mode);
/* Force use 32 bit in PDM TX stereo mode to satisfy the frequency */
uint32_t slot_bit_width = is_mono ? 16 : 32;
i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_bit_width);
i2s_ll_tx_set_half_sample_bit(hal->dev, 16); // Fixed to 16 in PDM mode
/* By default, taking the DMA data at the first half period of WS */
i2s_ll_tx_pdm_dma_take_mode(hal->dev, is_mono, true);
i2s_ll_tx_set_ws_idle_pol(hal->dev, false);
/* Slot mode seems not take effect according to the test, leave it default here */
i2s_ll_tx_pdm_slot_mode(hal->dev, is_mono, false, I2S_PDM_SLOT_BOTH);
if (slot_cfg->pdm_tx.data_fmt == I2S_PDM_DATA_FMT_PCM) {
uint32_t param0;
uint32_t param5;
s_i2s_hal_get_cut_off_coef(slot_cfg->pdm_tx.hp_cut_off_freq_hzx10, &param0, &param5);
i2s_ll_tx_enable_pdm_hp_filter(hal->dev, slot_cfg->pdm_tx.hp_en);
i2s_ll_tx_set_pdm_hp_filter_param0(hal->dev, param0);
i2s_ll_tx_set_pdm_hp_filter_param5(hal->dev, param5);
i2s_ll_tx_set_pdm_sd_dither(hal->dev, slot_cfg->pdm_tx.sd_dither);
i2s_ll_tx_set_pdm_sd_dither2(hal->dev, slot_cfg->pdm_tx.sd_dither2);
}
#endif
}
void i2s_hal_pdm_enable_tx_channel(i2s_hal_context_t *hal, bool pcm2pdm_en)
{
#if !SOC_I2S_SUPPORTS_PCM2PDM
HAL_ASSERT(!pcm2pdm_en);
#endif
i2s_ll_tx_enable_pdm(hal->dev, pcm2pdm_en);
}
#endif
#if SOC_I2S_SUPPORTS_PDM_RX
void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
{
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
i2s_ll_rx_reset(hal->dev);
i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave
i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
#if SOC_I2S_HW_VERSION_1
i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
i2s_ll_rx_select_pdm_slot(hal->dev, slot_cfg->pdm_rx.slot_mask);
i2s_ll_rx_force_enable_fifo_mod(hal->dev, true);
i2s_ll_rx_enable_msb_right(hal->dev, false);
i2s_ll_rx_enable_right_first(hal->dev, false);
#elif SOC_I2S_HW_VERSION_2
i2s_ll_rx_set_half_sample_bit(hal->dev, 16);
i2s_ll_rx_enable_mono_mode(hal->dev, false);
#if SOC_I2S_PDM_MAX_RX_LINES > 1
uint32_t slot_mask = (slot_cfg->slot_mode == I2S_SLOT_MODE_STEREO && slot_cfg->pdm_rx.slot_mask <= I2S_PDM_SLOT_BOTH) ?
I2S_PDM_SLOT_BOTH : slot_cfg->pdm_rx.slot_mask;
#else
/* Set the channel mask to enable corresponding slots, always enable two slots for stereo mode */
uint32_t slot_mask = slot_cfg->slot_mode == I2S_SLOT_MODE_STEREO ? I2S_PDM_SLOT_BOTH : slot_cfg->pdm_rx.slot_mask;
#endif // SOC_I2S_SUPPORTS_PDM_RX > 1
i2s_ll_rx_set_active_chan_mask(hal->dev, slot_mask);
#endif // SOC_I2S_HW_VERSION_1
#if SOC_I2S_SUPPORTS_PDM_RX_HP_FILTER
if (slot_cfg->pdm_rx.data_fmt != I2S_PDM_DATA_FMT_RAW) {
uint32_t param0;
uint32_t param5;
s_i2s_hal_get_cut_off_coef(slot_cfg->pdm_rx.hp_cut_off_freq_hzx10, &param0, &param5);
i2s_ll_rx_enable_pdm_hp_filter(hal->dev, slot_cfg->pdm_rx.hp_en);
i2s_ll_rx_set_pdm_hp_filter_param0(hal->dev, param0);
i2s_ll_rx_set_pdm_hp_filter_param5(hal->dev, param5);
/* Set the amplification number, the default and the minimum value is 1. 0 will mute the channel */
i2s_ll_rx_set_pdm_amplify_num(hal->dev, slot_cfg->pdm_rx.amplify_num ? slot_cfg->pdm_rx.amplify_num : 1);
}
#endif // SOC_I2S_SUPPORTS_PDM_RX_HP_FILTER
}
void i2s_hal_pdm_enable_rx_channel(i2s_hal_context_t *hal, bool pdm2pcm_en)
{
#if !SOC_I2S_SUPPORTS_PDM2PCM
HAL_ASSERT(!pdm2pcm_en);
#endif
i2s_ll_rx_enable_pdm(hal->dev, pdm2pcm_en);
}
#endif
/*-------------------------------------------------------------------------
| TDM Specific Slot Configurations |
-------------------------------------------------------------------------*/
#if SOC_I2S_SUPPORTS_TDM
void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
{
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
uint32_t cnt;
uint32_t msk = slot_cfg->tdm.slot_mask;
/* Get the maximum slot number */
cnt = 32 - __builtin_clz(msk);
/* Except PCM short format (ws_width = 1), there should be at least 2 slots in total even for mono mode */
cnt = ((cnt < 2) && (slot_cfg->tdm.ws_width != 1)) ? 2 : cnt;
uint32_t total_slot = slot_cfg->tdm.total_slot > cnt ? slot_cfg->tdm.total_slot : cnt;
i2s_ll_tx_reset(hal->dev);
i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave
i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->tdm.bit_shift);
if (slot_cfg->tdm.ws_width == 0) { // 0: I2S_TDM_AUTO_WS_WIDTH
i2s_ll_tx_set_ws_width(hal->dev, (total_slot * slot_bit_width) / 2);
} else {
i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->tdm.ws_width);
}
i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->tdm.ws_pol);
i2s_ll_tx_set_chan_num(hal->dev, total_slot);
/* In mono mode, there only should be one slot enabled, other inactive slots will transmit same data as enabled slot */
i2s_ll_tx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ?
I2S_TDM_SLOT0 : (uint32_t)slot_cfg->tdm.slot_mask);
i2s_ll_tx_set_skip_mask(hal->dev, slot_cfg->tdm.skip_mask);
i2s_ll_tx_set_half_sample_bit(hal->dev, total_slot * slot_bit_width / 2);
i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->tdm.bit_order_lsb);
i2s_ll_tx_enable_left_align(hal->dev, slot_cfg->tdm.left_align);
i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->tdm.big_endian);
}
void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
{
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
uint32_t cnt;
uint32_t msk = slot_cfg->tdm.slot_mask;
/* Get the maximum slot number */
cnt = 32 - __builtin_clz(msk);
/* Except PCM short format (ws_width = 1), there should be at least 2 slots in total even for mono mode */
cnt = ((cnt < 2) && (slot_cfg->tdm.ws_width != 1)) ? 2 : cnt;
uint32_t total_slot = slot_cfg->tdm.total_slot > cnt ? slot_cfg->tdm.total_slot : cnt;
i2s_ll_rx_reset(hal->dev);
i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave
i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->tdm.bit_shift);
if (slot_cfg->tdm.ws_width == 0) { // 0: I2S_TDM_AUTO_WS_WIDTH
i2s_ll_rx_set_ws_width(hal->dev, (total_slot * slot_bit_width) / 2);
} else {
i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->tdm.ws_width);
}
i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->tdm.ws_pol);
i2s_ll_rx_set_chan_num(hal->dev, total_slot);
/* In mono mode, there only should be one slot enabled, other inactive slots will transmit same data as enabled slot */
i2s_ll_rx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ?
I2S_TDM_SLOT0 : (uint32_t)slot_cfg->tdm.slot_mask);
i2s_ll_rx_set_half_sample_bit(hal->dev, total_slot * slot_bit_width / 2);
i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->tdm.bit_order_lsb);
i2s_ll_rx_enable_left_align(hal->dev, slot_cfg->tdm.left_align);
i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->tdm.big_endian);
}
void i2s_hal_tdm_enable_tx_channel(i2s_hal_context_t *hal)
{
i2s_ll_tx_enable_tdm(hal->dev);
}
void i2s_hal_tdm_enable_rx_channel(i2s_hal_context_t *hal)
{
i2s_ll_rx_enable_tdm(hal->dev);
}
#endif
@@ -0,0 +1,82 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/soc.h"
#include "soc/interrupts.h"
#include "soc/soc_caps.h"
#include "soc/soc_caps_full.h"
#include "soc/regdma.h"
#if SOC_HAS(PAU)
#include "soc/retention_periph_defs.h"
#endif
#if SOC_HAS(I2S)
#include "soc/i2s_struct.h"
#include "soc/i2s_reg.h"
#endif
#define SOC_I2S_ATTR(_attr) SOC_MODULE_ATTR(I2S, _attr)
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_HAS(I2S)
/*
Stores a bunch of per-I2S-peripheral data.
*/
typedef struct {
const uint8_t mck_out_sig;
const uint8_t mck_in_sig;
const uint8_t m_tx_bck_sig;
const uint8_t m_rx_bck_sig;
const uint8_t m_tx_ws_sig;
const uint8_t m_rx_ws_sig;
const uint8_t s_tx_bck_sig;
const uint8_t s_rx_bck_sig;
const uint8_t s_tx_ws_sig;
const uint8_t s_rx_ws_sig;
union {
const uint8_t data_out_sig;
#if SOC_I2S_PDM_MAX_TX_LINES
const uint8_t data_out_sigs[SOC_I2S_PDM_MAX_TX_LINES]; // Only valid in version 2
#endif
};
union {
const uint8_t data_in_sig;
#if SOC_I2S_PDM_MAX_RX_LINES
const uint8_t data_in_sigs[SOC_I2S_PDM_MAX_RX_LINES];
#endif
};
const uint8_t irq;
} i2s_signal_conn_t;
extern const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_ATTR(INST_NUM)];
#if SOC_LP_I2S_SUPPORTED
extern const i2s_signal_conn_t lp_i2s_periph_signal[SOC_LP_I2S_NUM];
#endif
#if SOC_HAS(PAU)
typedef struct {
const periph_retention_module_t retention_module;
const regdma_entries_config_t *entry_array;
uint32_t array_size;
} i2s_reg_retention_info_t;
extern const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_ATTR(INST_NUM)];
#endif // SOC_HAS(PAU)
#endif // SOC_HAS(I2S)
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,243 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#include "esp_bit_defs.h"
#include "soc/soc_caps.h"
#include "soc/clk_tree_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief I2S channel slot mode
*/
typedef enum {
I2S_SLOT_MODE_MONO = 1, /*!< I2S channel slot format mono, transmit same data in all slots for tx mode, only receive the data in the first slots for rx mode. */
I2S_SLOT_MODE_STEREO = 2, /*!< I2S channel slot format stereo, transmit different data in different slots for tx mode, receive the data in all slots for rx mode. */
} i2s_slot_mode_t;
/**
* @brief I2S channel direction
*/
typedef enum {
I2S_DIR_RX = BIT(0), /*!< I2S channel direction RX */
I2S_DIR_TX = BIT(1), /*!< I2S channel direction TX */
} i2s_dir_t;
/**
* @brief I2S controller role
*/
typedef enum {
I2S_ROLE_MASTER, /*!< I2S controller master role, bclk and ws signal will be set to output */
I2S_ROLE_SLAVE /*!< I2S controller slave role, bclk and ws signal will be set to input */
} i2s_role_t;
/**
* @brief Available data bit width in one slot
*/
typedef enum {
I2S_DATA_BIT_WIDTH_8BIT = 8, /*!< I2S channel data bit-width: 8 */
I2S_DATA_BIT_WIDTH_16BIT = 16, /*!< I2S channel data bit-width: 16 */
I2S_DATA_BIT_WIDTH_24BIT = 24, /*!< I2S channel data bit-width: 24 */
I2S_DATA_BIT_WIDTH_32BIT = 32, /*!< I2S channel data bit-width: 32 */
} i2s_data_bit_width_t;
/**
* @brief Total slot bit width in one slot
*
*/
typedef enum {
I2S_SLOT_BIT_WIDTH_AUTO = (0), /*!< I2S channel slot bit-width equals to data bit-width */
I2S_SLOT_BIT_WIDTH_8BIT = (8), /*!< I2S channel slot bit-width: 8 */
I2S_SLOT_BIT_WIDTH_16BIT = (16), /*!< I2S channel slot bit-width: 16 */
I2S_SLOT_BIT_WIDTH_24BIT = (24), /*!< I2S channel slot bit-width: 24 */
I2S_SLOT_BIT_WIDTH_32BIT = (32), /*!< I2S channel slot bit-width: 32 */
} i2s_slot_bit_width_t;
#if SOC_HAS(I2S)
typedef soc_periph_i2s_clk_src_t i2s_clock_src_t; /*!< I2S clock source */
#else
typedef int i2s_clock_src_t; /*!< Define a default type to avoid compiling warnings */
#endif
#if SOC_I2S_SUPPORTS_PCM
/**
* @brief A/U-law decompress or compress configuration.
*
*/
typedef enum {
I2S_PCM_DISABLE = 0, /*!< Disable A/U law decompress or compress*/
I2S_PCM_A_DECOMPRESS, /*!< A-law decompress*/
I2S_PCM_A_COMPRESS, /*!< A-law compress*/
I2S_PCM_U_DECOMPRESS, /*!< U-law decompress*/
I2S_PCM_U_COMPRESS, /*!< U-law compress*/
} i2s_pcm_compress_t;
#endif // SOC_I2S_SUPPORTS_PCM
/**
* @brief I2S PDM data format
*
*/
typedef enum {
I2S_PDM_DATA_FMT_PCM = 0, /*!< PDM RX:
* Enable the hardware PDM to PCM filter to convert the inputted PDM data on the line into PCM format in software,
* so that the read data in software is PCM format data already, no need additional software filter.
* PCM data format is only available when PCM2PDM filter is supported in hardware.
* PDM TX:
* Enable the hardware PCM to PDM filter to convert the written PCM data in software into PDM format on the line,
* so that we only need to write the PCM data in software, no need to prepare raw PDM data in software.
* PCM data format is only available when PDM2PCM filter is supported in hardware.
*/
I2S_PDM_DATA_FMT_RAW = 1, /*!< PDM RX:
* Read the raw PDM data directly in software, without the hardware PDM to PCM filter.
* You may need a software PDM to PCM filter to convert the raw PDM data that read into PCM format.
* PDM TX:
* Write the raw PDM data directly in software, without the hardware PCM to PDM filter.
* You may need to prepare the raw PDM data in software to output the PDM format data on the line.
*/
} i2s_pdm_data_fmt_t;
#if SOC_I2S_SUPPORTS_PDM_RX
/**
* @brief I2S PDM RX down-sampling mode
*/
typedef enum {
I2S_PDM_DSR_8S = 0, /*!< downsampling number is 8 for PDM RX mode*/
I2S_PDM_DSR_16S, /*!< downsampling number is 16 for PDM RX mode*/
I2S_PDM_DSR_MAX,
} i2s_pdm_dsr_t;
#endif // SOC_I2S_SUPPORTS_PDM_RX
#if SOC_I2S_SUPPORTS_PDM_TX
/**
* @brief pdm tx signal scaling mode
*/
typedef enum {
I2S_PDM_SIG_SCALING_DIV_2 = 0, /*!< I2S TX PDM signal scaling: /2 */
I2S_PDM_SIG_SCALING_MUL_1 = 1, /*!< I2S TX PDM signal scaling: x1 */
I2S_PDM_SIG_SCALING_MUL_2 = 2, /*!< I2S TX PDM signal scaling: x2 */
I2S_PDM_SIG_SCALING_MUL_4 = 3, /*!< I2S TX PDM signal scaling: x4 */
} i2s_pdm_sig_scale_t;
#if SOC_I2S_HW_VERSION_2
/**
* @brief PDM TX line mode
* @note For the standard codec mode, PDM pins are connect to a codec which requires both clock signal and data signal
* For the DAC output mode, PDM data signal can be connected to a power amplifier directly with a low-pass filter,
* normally, DAC output mode doesn't need the clock signal.
*/
typedef enum {
I2S_PDM_TX_ONE_LINE_CODEC, /*!< Standard PDM format output, left and right slot data on a single line */
I2S_PDM_TX_ONE_LINE_DAC, /*!< PDM DAC format output, left or right slot data on a single line */
I2S_PDM_TX_TWO_LINE_DAC, /*!< PDM DAC format output, left and right slot data on separated lines */
} i2s_pdm_tx_line_mode_t;
#endif // SOC_I2S_HW_VERSION_2
#endif // SOC_I2S_SUPPORTS_PDM_TX
/**
* @brief I2S slot select in standard mode
* @note It has different meanings in tx/rx/mono/stereo mode, and it may have different behaviors on different targets
* For the details, please refer to the I2S API reference
*/
typedef enum {
I2S_STD_SLOT_LEFT = BIT(0), /*!< I2S transmits or receives left slot */
I2S_STD_SLOT_RIGHT = BIT(1), /*!< I2S transmits or receives right slot */
I2S_STD_SLOT_BOTH = BIT(0) | BIT(1), /*!< I2S transmits or receives both left and right slot */
} i2s_std_slot_mask_t;
/**
* @brief I2S slot select in PDM mode
*
*/
typedef enum {
I2S_PDM_SLOT_RIGHT = BIT(0), /*!< I2S PDM only transmits or receives the PDM device whose 'select' pin is pulled up */
I2S_PDM_SLOT_LEFT = BIT(1), /*!< I2S PDM only transmits or receives the PDM device whose 'select' pin is pulled down */
I2S_PDM_SLOT_BOTH = BIT(0) | BIT(1), /*!< I2S PDM transmits or receives both two slots */
#if SOC_I2S_PDM_MAX_RX_LINES > 1
/* The following enumerators are only used in multi-line PDM RX mode */
I2S_PDM_RX_LINE0_SLOT_RIGHT = I2S_PDM_SLOT_RIGHT, /*!< I2S PDM receives the right slot on line 0 */
I2S_PDM_RX_LINE0_SLOT_LEFT = I2S_PDM_SLOT_LEFT, /*!< I2S PDM receives the left slot on line 0 */
I2S_PDM_RX_LINE1_SLOT_RIGHT = BIT(2), /*!< I2S PDM receives the right slot on line 1 */
I2S_PDM_RX_LINE1_SLOT_LEFT = BIT(3), /*!< I2S PDM receives the left slot on line 1 */
I2S_PDM_RX_LINE2_SLOT_RIGHT = BIT(4), /*!< I2S PDM receives the right slot on line 2 */
I2S_PDM_RX_LINE2_SLOT_LEFT = BIT(5), /*!< I2S PDM receives the left slot on line 2 */
I2S_PDM_RX_LINE3_SLOT_RIGHT = BIT(6), /*!< I2S PDM receives the right slot on line 3 */
I2S_PDM_RX_LINE3_SLOT_LEFT = BIT(7), /*!< I2S PDM receives the left slot on line 3 */
I2S_PDM_LINE_SLOT_ALL = 0x00ff, /*!< I2S PDM receives all slots */
#endif
} i2s_pdm_slot_mask_t;
#if SOC_I2S_SUPPORTS_TDM
/**
* @brief tdm slot number
* @note Multiple slots in TDM mode.
* For TX module, only the active slot send the audio data, the inactive slot send a constant or will be skipped if 'skip_msk' is set.
* For RX module, only receive the audio data in active slots, the data in inactive slots will be ignored.
* the bit map of active slot can not exceed (0x1<<total_slot_num).
* e.g: slot_mask = (I2S_TDM_SLOT0 | I2S_TDM_SLOT3), here the active slot number is 2 and total_slot is not supposed to be smaller than 4.
*/
typedef enum {
I2S_TDM_SLOT0 = BIT(0), /*!< I2S slot 0 enabled */
I2S_TDM_SLOT1 = BIT(1), /*!< I2S slot 1 enabled */
I2S_TDM_SLOT2 = BIT(2), /*!< I2S slot 2 enabled */
I2S_TDM_SLOT3 = BIT(3), /*!< I2S slot 3 enabled */
I2S_TDM_SLOT4 = BIT(4), /*!< I2S slot 4 enabled */
I2S_TDM_SLOT5 = BIT(5), /*!< I2S slot 5 enabled */
I2S_TDM_SLOT6 = BIT(6), /*!< I2S slot 6 enabled */
I2S_TDM_SLOT7 = BIT(7), /*!< I2S slot 7 enabled */
I2S_TDM_SLOT8 = BIT(8), /*!< I2S slot 8 enabled */
I2S_TDM_SLOT9 = BIT(9), /*!< I2S slot 9 enabled */
I2S_TDM_SLOT10 = BIT(10), /*!< I2S slot 10 enabled */
I2S_TDM_SLOT11 = BIT(11), /*!< I2S slot 11 enabled */
I2S_TDM_SLOT12 = BIT(12), /*!< I2S slot 12 enabled */
I2S_TDM_SLOT13 = BIT(13), /*!< I2S slot 13 enabled */
I2S_TDM_SLOT14 = BIT(14), /*!< I2S slot 14 enabled */
I2S_TDM_SLOT15 = BIT(15), /*!< I2S slot 15 enabled */
} i2s_tdm_slot_mask_t;
#endif // SOC_I2S_SUPPORTS_TDM
/**
* @brief I2S channel events that supported by the ETM module
*/
typedef enum {
/** Trigger condition:
* TX: no data to send in the TX FIFO, i.e., DMA need to stop (next desc is NULL)
* RX: 1. If rx_stop_mode = 0, this event will trigger when DMA is stopped (next desc is NULL)
* 2. If rx_stop_mode = 1, this event will trigger when DMA in_suc_eof.
* 3. If rx_stop_mode = 2, this event will trigger when RX FIFO is full.
*/
I2S_ETM_EVENT_DONE, /*!< Event that I2S TX or RX stopped */
/** Trigger condition:
* TX: the sent words(in 32-bit) number reach the threshold that configured in `etm_tx_send_word_num`
* RX: the received words(in 32-bit) number reach the threshold that configured in `etm_rx_receive_word_num`
* and `etm_rx_receive_word_num` should be smaller than the size of the DMA buffer in one `in_suc_eof` event.
*/
I2S_ETM_EVENT_REACH_THRESH, /*!< Event that the I2S sent or received data reached the threshold */
I2S_ETM_EVENT_MAX, /*!< Maximum number of events */
} i2s_etm_event_type_t;
/**
* @brief I2S channel tasks that supported by the ETM module
*/
typedef enum {
I2S_ETM_TASK_START, /*!< Start the I2S channel */
I2S_ETM_TASK_STOP, /*!< Stop the I2S channel */
#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC
I2S_ETM_TASK_SYNC_FIFO, /*!< Check the I2S TX channel sync status */
#endif
I2S_ETM_TASK_MAX, /*!< Maximum number of tasks */
} i2s_etm_task_type_t;
#ifdef __cplusplus
}
#endif
+16
View File
@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The HAL layer for LP I2S
#include "soc/soc.h"
#include "hal/lp_i2s_hal.h"
#include "hal/lp_i2s_ll.h"
void lp_i2s_hal_init(lp_i2s_hal_context_t *hal, int group_id)
{
hal->dev = LP_I2S_LL_GET_HW(group_id);
}