diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index 981ae04dd0..615ed67ee0 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -882,15 +882,38 @@ static uint64_t s_i2s_get_pair_chan_gpio_mask(i2s_chan_handle_t handle) return handle->controller->tx_chan ? handle->controller->tx_chan->reserve_gpio_mask : 0; } +static bool s_i2s_gpio_used_by_pair_chan(i2s_chan_handle_t handle, int gpio_num) +{ + if (!handle->controller->full_duplex) { + return false; + } + return !!(s_i2s_get_pair_chan_gpio_mask(handle) & BIT64(gpio_num)); +} + +/** + * @note Call before IO_MUX/matrix reconfiguration. Input pins are not added to the + * global GPIO reserve map (no esp_gpio_reserve), to avoid esp_gpio_revoke() + * clearing bits that MSPI or other subsystems already own. + */ +static void s_i2c_input_gpio_reserve_check(i2s_chan_handle_t handle, int gpio_num) +{ + if (s_i2s_gpio_used_by_pair_chan(handle, gpio_num)) { + return; + } + if (esp_gpio_is_reserved(BIT64(gpio_num))) { + ESP_LOGW(TAG, "GPIO %d is already reserved; selecting GPIO matrix input on this pin may conflict (e.g. MSPI/Flash)", gpio_num); + } +} + void i2s_output_gpio_reserve(i2s_chan_handle_t handle, int gpio_num) { - bool used_by_pair_chan = false; /* If the gpio is used by the pair channel do not show warning for this case */ - if (handle->controller->full_duplex) { - used_by_pair_chan = !!(s_i2s_get_pair_chan_gpio_mask(handle) & BIT64(gpio_num)); + if (s_i2s_gpio_used_by_pair_chan(handle, gpio_num)) { + handle->reserve_gpio_mask |= BIT64(gpio_num); + return; } /* reserve the GPIO output path, because we don't expect another peripheral to signal to the same GPIO */ - if (!used_by_pair_chan && (esp_gpio_reserve(BIT64(gpio_num)) & BIT64(gpio_num))) { + if (esp_gpio_reserve(BIT64(gpio_num)) & BIT64(gpio_num)) { ESP_LOGW(TAG, "GPIO %d is not usable, maybe conflict with others", gpio_num); } handle->reserve_gpio_mask |= BIT64(gpio_num); @@ -913,14 +936,15 @@ void i2s_gpio_check_and_set(i2s_chan_handle_t handle, int gpio, uint32_t signal_ { /* Ignore the pin if pin = I2S_GPIO_UNUSED */ if (gpio != (int)I2S_GPIO_UNUSED) { - gpio_func_sel(gpio, PIN_FUNC_GPIO); + /* Reserve / warn before IO_MUX changes so e.g. MSPI pads are not remuxed with no prior notice */ if (is_input) { - /* Enable the input, for some GPIOs, the input function are not enabled as default */ + s_i2c_input_gpio_reserve_check(handle, gpio); + gpio_func_sel(gpio, PIN_FUNC_GPIO); gpio_input_enable(gpio); esp_rom_gpio_connect_in_signal(gpio, signal_idx, is_invert); } else { i2s_output_gpio_reserve(handle, gpio); - /* output will be enabled in esp_rom_gpio_connect_out_signal */ + gpio_func_sel(gpio, PIN_FUNC_GPIO); esp_rom_gpio_connect_out_signal(gpio, signal_idx, is_invert, 0); } } diff --git a/components/esp_driver_i2s/test_apps/i2s/main/test_app_main.c b/components/esp_driver_i2s/test_apps/i2s/main/test_app_main.c index 9e1f7b5a6c..c05ad2830a 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/test_app_main.c +++ b/components/esp_driver_i2s/test_apps/i2s/main/test_app_main.c @@ -9,7 +9,7 @@ #include "esp_heap_caps.h" // Some resources are lazy allocated in I2S driver, the threshold is left for that case -#define TEST_MEMORY_LEAK_THRESHOLD (-350) +#define TEST_MEMORY_LEAK_THRESHOLD (-360) static size_t before_free_8bit; static size_t before_free_32bit; diff --git a/components/esp_driver_i2s/test_apps/test_inc/test_i2s.h b/components/esp_driver_i2s/test_apps/test_inc/test_i2s.h index 0be715c450..61180ed706 100644 --- a/components/esp_driver_i2s/test_apps/test_inc/test_i2s.h +++ b/components/esp_driver_i2s/test_apps/test_inc/test_i2s.h @@ -22,7 +22,7 @@ extern "C" { #define SLAVE_WS_IO 22 #define DATA_IN_IO 19 #define DATA_OUT_IO 18 -#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H4 #define MASTER_MCK_IO 0 #define MASTER_BCK_IO 4 #define MASTER_WS_IO 5