fix(esp_lcd): balance clock source references on teardown

Track enabled clock sources in DSI/DPI/i80 drivers and disable them in
delete/error cleanup paths so esp_clk_tree source refcounts stay
balanced.
This commit is contained in:
morris
2026-05-22 18:43:02 +08:00
parent f3ee69582e
commit 6b711f91f5
4 changed files with 33 additions and 2 deletions
@@ -30,6 +30,8 @@ esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lc
esp_lcd_dsi_bus_t *dsi_bus = heap_caps_calloc(1, sizeof(esp_lcd_dsi_bus_t), DSI_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(dsi_bus, ESP_ERR_NO_MEM, TAG, "no memory for DSI bus");
dsi_bus->bus_id = bus_id;
dsi_bus->phy_pllref_clk_src = SOC_MOD_CLK_INVALID;
dsi_bus->phy_cfg_clk_src = SOC_MOD_CLK_INVALID;
// Enable the APB clock for accessing the DSI host and bridge registers
PERIPH_RCC_ATOMIC() {
@@ -47,9 +49,11 @@ esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lc
#endif
}
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)phy_clk_src, true), err, TAG, "clock source enable failed");
dsi_bus->phy_pllref_clk_src = (soc_module_clk_t)phy_clk_src;
// always use the default clock source for the DSI PHY configuration
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)MIPI_DSI_PHY_CFG_CLK_SRC_DEFAULT, true), err, TAG, "clock source enable failed");
dsi_bus->phy_cfg_clk_src = (soc_module_clk_t)MIPI_DSI_PHY_CFG_CLK_SRC_DEFAULT;
// enable the clock source for DSI PHY
PERIPH_RCC_ATOMIC() {
@@ -143,6 +147,14 @@ esp_err_t esp_lcd_del_dsi_bus(esp_lcd_dsi_bus_handle_t bus)
mipi_dsi_ll_enable_phy_pllref_clock(bus_id, false);
mipi_dsi_ll_enable_phy_config_clock(bus_id, false);
}
if (bus->phy_pllref_clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(bus->phy_pllref_clk_src, false);
bus->phy_pllref_clk_src = SOC_MOD_CLK_INVALID;
}
if (bus->phy_cfg_clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(bus->phy_cfg_clk_src, false);
bus->phy_cfg_clk_src = SOC_MOD_CLK_INVALID;
}
// disable the APB clock for accessing the DSI peripheral registers
PERIPH_RCC_ATOMIC() {
mipi_dsi_ll_enable_bus_clock(bus_id, false);
@@ -34,6 +34,7 @@ struct esp_lcd_dpi_panel_t {
uint32_t v_pixels; // Vertical pixels
size_t fb_size; // Frame buffer size, in bytes
size_t bits_per_pixel; // Bits per pixel
soc_module_clk_t clk_src; // DPI clock source, SOC_MOD_CLK_INVALID if not enabled
lcd_color_format_t in_color_format; // Input color format
lcd_color_format_t out_color_format; // Output color format
dw_gdma_channel_handle_t dma_chan; // DMA channel
@@ -215,6 +216,7 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
dpi_panel = heap_caps_calloc(1, sizeof(esp_lcd_dpi_panel_t), DSI_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(dpi_panel, ESP_ERR_NO_MEM, err, TAG, "no memory for DPI panel");
dpi_panel->clk_src = SOC_MOD_CLK_INVALID;
dpi_panel->virtual_channel = panel_config->virtual_channel;
dpi_panel->in_color_format = in_color_format;
dpi_panel->out_color_format = out_color_format;
@@ -253,6 +255,7 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
float dpi_clk_src_freq_mhz = (float)dpi_clk_src_freq_hz / 1000.0f / 1000.0f;
uint32_t dpi_div = mipi_dsi_hal_host_dpi_calculate_divider(hal, dpi_clk_src_freq_mhz, panel_config->dpi_clock_freq_mhz);
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)dpi_clk_src, true), err, TAG, "clock source enable failed");
dpi_panel->clk_src = (soc_module_clk_t)dpi_clk_src;
// set the clock source, set the divider, and enable the dpi clock
PERIPH_RCC_ATOMIC() {
mipi_dsi_ll_set_dpi_clock_source(bus_id, dpi_clk_src);
@@ -360,6 +363,10 @@ static esp_err_t dpi_panel_del(esp_lcd_panel_t *panel)
PERIPH_RCC_ATOMIC() {
mipi_dsi_ll_enable_dpi_clock(bus_id, false);
}
if (dpi_panel->clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(dpi_panel->clk_src, false);
dpi_panel->clk_src = SOC_MOD_CLK_INVALID;
}
// disable the DSI bridge
mipi_dsi_brg_ll_enable(hal->bridge, false);
// free memory
+2
View File
@@ -46,6 +46,8 @@ extern "C" {
typedef struct esp_lcd_dsi_bus_t {
int bus_id;
mipi_dsi_hal_context_t hal;
soc_module_clk_t phy_pllref_clk_src; // PHY PLL reference clock source, SOC_MOD_CLK_INVALID if not enabled
soc_module_clk_t phy_cfg_clk_src; // PHY config clock source, SOC_MOD_CLK_INVALID if not enabled
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif