diff --git a/components/esp_hw_support/include/esp_private/periph_ctrl.h b/components/esp_hw_support/include/esp_private/periph_ctrl.h index c73c789cf7..99ee3d6d99 100644 --- a/components/esp_hw_support/include/esp_private/periph_ctrl.h +++ b/components/esp_hw_support/include/esp_private/periph_ctrl.h @@ -34,7 +34,7 @@ extern "C" { * * @note User code protected by this macro should be as short as possible, because it's a critical section * @note This macro will decrease the reference lock of that peripheral. - * You can get the value before the increment from the `rc_name` local variable + * You can get the value after the decrease from the `rc_name` local variable */ #define PERIPH_RCC_RELEASE_ATOMIC(periph, rc_name) \ for (uint8_t rc_name, i = 1, __DECLARE_RCC_RC_ATOMIC_ENV; \ diff --git a/components/esp_lcd/src/esp_lcd_panel_io_i80.c b/components/esp_lcd/src/esp_lcd_panel_io_i80.c index 955f5853f8..39cd7fd65a 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i80.c @@ -144,7 +144,12 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc ESP_GOTO_ON_FALSE(bus_id >= 0, ESP_ERR_NOT_FOUND, err, TAG, "no free i80 bus slot"); bus->bus_id = bus_id; // enable APB to access LCD registers - periph_module_enable(lcd_periph_signals.buses[bus_id].module); + PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_signals.panels[bus_id].module, ref_count) { + if (ref_count == 0) { + lcd_ll_enable_bus_clock(bus_id, true); + lcd_ll_reset_register(bus_id); + } + } // initialize HAL layer, so we can call LL APIs later lcd_hal_init(&bus->hal, bus_id); // reset peripheral and FIFO @@ -199,7 +204,11 @@ err: gdma_del_channel(bus->dma_chan); } if (bus->bus_id >= 0) { - periph_module_disable(lcd_periph_signals.buses[bus->bus_id].module); + PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[bus->bus_id].module, ref_count) { + if (ref_count == 0) { + lcd_ll_enable_bus_clock(bus->bus_id, false); + } + } lcd_com_remove_device(LCD_COM_DEVICE_TYPE_I80, bus->bus_id); } if (bus->format_buffer) { @@ -220,7 +229,11 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus) ESP_GOTO_ON_FALSE(LIST_EMPTY(&bus->device_list), ESP_ERR_INVALID_STATE, err, TAG, "device list not empty"); int bus_id = bus->bus_id; lcd_com_remove_device(LCD_COM_DEVICE_TYPE_I80, bus_id); - periph_module_disable(lcd_periph_signals.buses[bus_id].module); + PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[bus_id].module, ref_count) { + if (ref_count == 0) { + lcd_ll_enable_bus_clock(bus_id, false); + } + } gdma_disconnect(bus->dma_chan); gdma_del_channel(bus->dma_chan); esp_intr_free(bus->intr); diff --git a/components/esp_lcd/src/esp_lcd_panel_rgb.c b/components/esp_lcd/src/esp_lcd_panel_rgb.c index e7aec6b122..2a5c57d0da 100644 --- a/components/esp_lcd/src/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/src/esp_lcd_panel_rgb.c @@ -178,7 +178,11 @@ static esp_err_t lcd_rgb_panel_destory(esp_rgb_panel_t *rgb_panel) { lcd_ll_enable_clock(rgb_panel->hal.dev, false); if (rgb_panel->panel_id >= 0) { - periph_module_disable(lcd_periph_signals.panels[rgb_panel->panel_id].module); + PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[rgb_panel->panel_id].module, ref_count) { + if (ref_count == 0) { + lcd_ll_enable_bus_clock(rgb_panel->panel_id, false); + } + } lcd_com_remove_device(LCD_COM_DEVICE_TYPE_RGB, rgb_panel->panel_id); } for (size_t i = 0; i < rgb_panel->num_fbs; i++) { @@ -283,8 +287,12 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf rgb_panel->panel_id = panel_id; // enable APB to access LCD registers - periph_module_enable(lcd_periph_signals.panels[panel_id].module); - periph_module_reset(lcd_periph_signals.panels[panel_id].module); + PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_signals.panels[panel_id].module, ref_count) { + if (ref_count == 0) { + lcd_ll_enable_bus_clock(panel_id, true); + lcd_ll_reset_register(panel_id); + } + } // allocate frame buffers + bounce buffers ESP_GOTO_ON_ERROR(lcd_rgb_panel_alloc_frame_buffers(rgb_panel_config, rgb_panel), err, TAG, "alloc frame buffers failed"); diff --git a/components/hal/esp32s3/include/hal/lcd_ll.h b/components/hal/esp32s3/include/hal/lcd_ll.h index 2287df966d..2f30e5b210 100644 --- a/components/hal/esp32s3/include/hal/lcd_ll.h +++ b/components/hal/esp32s3/include/hal/lcd_ll.h @@ -13,6 +13,7 @@ #include "soc/lcd_cam_struct.h" #include "hal/assert.h" #include "hal/lcd_types.h" +#include "soc/system_struct.h" #ifdef __cplusplus extern "C" { @@ -596,6 +597,35 @@ static inline volatile void *lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev) return &dev->lc_dma_int_st; } +/** + * @brief Enable or disable the bus clock for the LCD module + * + * @param set_bit True to set bit, false to clear bit + */ +static inline void lcd_ll_enable_bus_clock(int group_id, bool enable) +{ + (void)group_id; + SYSTEM.perip_clk_en1.lcd_cam_clk_en = enable; +} + +/// 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_RC_ATOMIC_ENV variable in advance +#define lcd_ll_enable_bus_clock(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; lcd_ll_enable_bus_clock(__VA_ARGS__) + +/** + * @brief Reset the LCD module + */ +static inline void lcd_ll_reset_register(int group_id) +{ + (void)group_id; + SYSTEM.perip_rst_en1.lcd_cam_rst = 0x01; + SYSTEM.perip_rst_en1.lcd_cam_rst = 0x00; +} + +/// 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_RC_ATOMIC_ENV variable in advance +#define lcd_ll_reset_register(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; lcd_ll_reset_register(__VA_ARGS__) + #ifdef __cplusplus } #endif