Skip to content

Commit e686c01

Browse files
committed
Merge branch 'feature/i2s_support_merge_simplex_to_duplex_v5.1' into 'release/v5.1'
feat(i2s): support to lazy constitute full-duplex mode (v5.1) See merge request espressif/esp-idf!41846
2 parents aab1694 + e375ea5 commit e686c01

File tree

9 files changed

+393
-101
lines changed

9 files changed

+393
-101
lines changed

components/driver/i2s/i2s_common.c

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,6 @@ static inline bool i2s_take_available_channel(i2s_controller_t *i2s_obj, uint8_t
211211
{
212212
bool is_available = false;
213213

214-
#if SOC_I2S_HW_VERSION_1
215-
/* In ESP32 and ESP32-S2, tx channel and rx channel are not totally separated
216-
* Take both two channels in case one channel can affect another
217-
*/
218-
chan_search_mask = I2S_DIR_RX | I2S_DIR_TX;
219-
#endif
220214
portENTER_CRITICAL(&g_i2s.spinlock);
221215
if (!(chan_search_mask & i2s_obj->chan_occupancy)) {
222216
i2s_obj->chan_occupancy |= chan_search_mask;
@@ -731,11 +725,11 @@ void i2s_gpio_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool is_input,
731725
if (gpio != I2S_GPIO_UNUSED) {
732726
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
733727
if (is_input) {
734-
/* Set direction, for some GPIOs, the input function are not enabled as default */
735-
gpio_set_direction(gpio, GPIO_MODE_INPUT);
728+
/* Enable the input, for some GPIOs, the input function are not enabled as default */
729+
gpio_ll_input_enable(GPIO_HAL_GET_HW(GPIO_PORT_0), (uint32_t)gpio);
736730
esp_rom_gpio_connect_in_signal(gpio, signal_idx, is_invert);
737731
} else {
738-
gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
732+
/* output will be enabled in esp_rom_gpio_connect_out_signal */
739733
esp_rom_gpio_connect_out_signal(gpio, signal_idx, is_invert, 0);
740734
}
741735
}
@@ -745,7 +739,7 @@ void i2s_gpio_loopback_set(gpio_num_t gpio, uint32_t out_sig_idx, uint32_t in_si
745739
{
746740
if (gpio != I2S_GPIO_UNUSED) {
747741
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
748-
gpio_set_direction(gpio, GPIO_MODE_INPUT_OUTPUT);
742+
gpio_ll_input_enable(GPIO_HAL_GET_HW(GPIO_PORT_0), (uint32_t)gpio);
749743
esp_rom_gpio_connect_out_signal(gpio, out_sig_idx, 0, 0);
750744
esp_rom_gpio_connect_in_signal(gpio, in_sig_idx, 0);
751745
}

components/driver/i2s/i2s_private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ struct i2s_channel_obj_t {
9090
i2s_state_t state; /*!< i2s driver state. Ensuring the driver working in a correct sequence */
9191
/* Stored configurations */
9292
void *mode_info; /*!< Slot, clock and gpio information of each mode */
93+
bool full_duplex_slave; /*!< whether the channel is forced to switch to slave role for full duplex */
9394
#if SOC_I2S_SUPPORTS_APLL
9495
bool apll_en; /*!< Flag of wether APLL enabled */
9596
#endif

components/driver/i2s/i2s_std.c

Lines changed: 85 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -33,9 +33,10 @@ static esp_err_t i2s_std_calculate_clock(i2s_chan_handle_t handle, const i2s_std
3333
uint32_t slot_bits = (slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO) ||
3434
((int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width) ?
3535
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
36+
slot_cfg->slot_bit_width = slot_bits;
3637
/* Calculate multiple
3738
* Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) */
38-
if (handle->role == I2S_ROLE_MASTER) {
39+
if (handle->role == I2S_ROLE_MASTER || handle->full_duplex_slave) {
3940
clk_info->bclk = rate * handle->total_slot * slot_bits;
4041
clk_info->mclk = rate * clk_cfg->mclk_multiple;
4142
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
@@ -99,18 +100,13 @@ static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_c
99100
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, buf_size),
100101
TAG, "allocate memory for dma descriptor failed");
101102
}
102-
bool is_slave = handle->role == I2S_ROLE_SLAVE;
103103
/* Share bck and ws signal in full-duplex mode */
104104
if (handle->controller->full_duplex) {
105105
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
106-
/* Since bck and ws are shared, only tx or rx can be master
107-
Force to set rx as slave to avoid conflict of clock signal */
108-
if (handle->dir == I2S_DIR_RX) {
109-
is_slave = true;
110-
}
111106
} else {
112107
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
113108
}
109+
bool is_slave = handle->role == I2S_ROLE_SLAVE;
114110

115111
portENTER_CRITICAL(&g_i2s.spinlock);
116112
/* Configure the hardware to apply STD format */
@@ -154,46 +150,96 @@ static esp_err_t i2s_std_set_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_c
154150
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false);
155151
}
156152

157-
if (handle->role == I2S_ROLE_SLAVE) {
158-
/* For "tx + slave" mode, select TX signal index for ws and bck */
159-
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
153+
/* Set mclk pin */
154+
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, std_cfg->clk_cfg.clk_src, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
155+
160156
#if SOC_I2S_HW_VERSION_2
157+
/* Bind the MCLK signal to the TX or RX clock source */
158+
if (!handle->controller->full_duplex) {
159+
if (handle->dir == I2S_DIR_TX) {
161160
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
162-
#endif
163-
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
164-
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
165-
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
166161
} else {
167-
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
168-
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
162+
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
169163
}
170-
} else {
171-
/* mclk only available in master mode */
172-
#if SOC_I2S_SUPPORTS_APLL
173-
bool is_apll = std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL;
174-
#else
175-
bool is_apll = false;
176-
#endif
177-
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, is_apll, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
178-
/* For "rx + master" mode, select RX signal index for ws and bck */
179-
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
180-
#if SOC_I2S_HW_VERSION_2
164+
} else if (handle->role == I2S_ROLE_MASTER) {
165+
if (handle->dir == I2S_DIR_TX) {
166+
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
167+
} else {
181168
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
169+
}
170+
}
182171
#endif
183-
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
184-
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
185-
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
172+
173+
uint32_t ws_sig = 0;
174+
uint32_t bck_sig = 0;
175+
bool is_input = handle->role == I2S_ROLE_SLAVE;
176+
if (handle->role == I2S_ROLE_SLAVE) {
177+
// Assign slave signals
178+
if (handle->dir == I2S_DIR_TX) {
179+
ws_sig = i2s_periph_signal[id].s_tx_ws_sig;
180+
bck_sig = i2s_periph_signal[id].s_tx_bck_sig;
186181
} else {
187-
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
188-
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
182+
ws_sig = i2s_periph_signal[id].s_rx_ws_sig;
183+
bck_sig = i2s_periph_signal[id].s_rx_bck_sig;
184+
}
185+
} else {
186+
// Assign master signals
187+
if (handle->dir == I2S_DIR_TX) {
188+
ws_sig = i2s_periph_signal[id].m_tx_ws_sig;
189+
bck_sig = i2s_periph_signal[id].m_tx_bck_sig;
190+
} else {
191+
ws_sig = i2s_periph_signal[id].m_rx_ws_sig;
192+
bck_sig = i2s_periph_signal[id].m_rx_bck_sig;
189193
}
190194
}
195+
i2s_gpio_check_and_set(gpio_cfg->ws, ws_sig, is_input, gpio_cfg->invert_flags.ws_inv);
196+
i2s_gpio_check_and_set(gpio_cfg->bclk, bck_sig, is_input, gpio_cfg->invert_flags.bclk_inv);
197+
191198
/* Update the mode info: gpio configuration */
192199
memcpy(&(std_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_std_gpio_config_t));
193200

194201
return ESP_OK;
195202
}
196203

204+
static esp_err_t s_i2s_channel_try_to_constitude_std_duplex(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
205+
{
206+
/* Get another direction handle */
207+
i2s_chan_handle_t another_handle = handle->dir == I2S_DIR_RX ? handle->controller->tx_chan : handle->controller->rx_chan;
208+
/* Condition: 1. Another direction channel is registered
209+
* 2. Not a full-duplex channel yet
210+
* 3. Another channel is initialized, try to compare the configurations */
211+
if (another_handle && another_handle->state >= I2S_CHAN_STATE_READY) {
212+
/* Judge if the two channels can constitute full-duplex */
213+
if (!handle->controller->full_duplex) {
214+
i2s_std_config_t curr_cfg = *std_cfg;
215+
/* Override the slot bit width to the actual slot bit width */
216+
curr_cfg.slot_cfg.slot_bit_width = (int)curr_cfg.slot_cfg.slot_bit_width < (int)curr_cfg.slot_cfg.data_bit_width ?
217+
curr_cfg.slot_cfg.data_bit_width : curr_cfg.slot_cfg.slot_bit_width;
218+
/* Compare the hardware configurations of the two channels, constitute the full-duplex if they are the same */
219+
if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_std_config_t)) == 0) {
220+
handle->controller->full_duplex = true;
221+
ESP_LOGD(TAG, "Constitude full-duplex on port %d", handle->controller->id);
222+
}
223+
#if SOC_I2S_HW_VERSION_1
224+
else {
225+
ESP_LOGE(TAG, "Can't set different channel configurations on a same port");
226+
return ESP_ERR_INVALID_ARG;
227+
}
228+
#endif
229+
}
230+
/* Switch to the slave role if needed */
231+
if (handle->controller->full_duplex &&
232+
handle->role == I2S_ROLE_MASTER &&
233+
another_handle->role == I2S_ROLE_MASTER) {
234+
/* The later initialized channel must be slave for full duplex */
235+
handle->role = I2S_ROLE_SLAVE;
236+
handle->full_duplex_slave = true;
237+
}
238+
}
239+
240+
return ESP_OK;
241+
}
242+
197243
esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
198244
{
199245
#if CONFIG_I2S_ENABLE_DEBUG_LOG
@@ -211,7 +257,11 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf
211257
handle->mode_info = calloc(1, sizeof(i2s_std_config_t));
212258
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
213259
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_REGISTER, ESP_ERR_INVALID_STATE, err, TAG, "the channel has initialized already");
214-
ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, &std_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
260+
/* Try to constitute full-duplex mode if the STD configuration is totally same as another channel */
261+
ret = s_i2s_channel_try_to_constitude_std_duplex(handle, std_cfg);
262+
#if SOC_I2S_HW_VERSION_1
263+
ESP_GOTO_ON_ERROR(ret, err, TAG, "Failed to constitute full-duplex mode");
264+
#endif
215265
/* i2s_set_std_slot should be called before i2s_set_std_clock while initializing, because clock is relay on the slot */
216266
ESP_GOTO_ON_ERROR(i2s_std_set_slot(handle, &std_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
217267
#if SOC_I2S_SUPPORTS_APLL
@@ -222,6 +272,7 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf
222272
}
223273
#endif
224274
ESP_GOTO_ON_ERROR(i2s_std_set_clock(handle, &std_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock");
275+
ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, &std_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
225276
ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, I2S_INTR_ALLOC_FLAGS), err, TAG, "initialize dma interrupt failed");
226277
#if SOC_I2S_HW_VERSION_2
227278
/* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */

0 commit comments

Comments
 (0)