mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-28 16:46:31 +03:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -65,6 +65,7 @@ struct esp_lcd_i80_bus_t {
|
||||
uint8_t *format_buffer; // The driver allocates an internal buffer for DMA to do data format transformer
|
||||
uint8_t *format_buffer_nc; // Non-cacheable version of format buffer
|
||||
size_t resolution_hz; // LCD_CLK resolution, determined by selected clock source
|
||||
soc_module_clk_t clk_src; // peripheral clock source, SOC_MOD_CLK_INVALID if not enabled
|
||||
size_t max_transfer_bytes; // Maximum number of bytes that can be transferred in one transaction
|
||||
gdma_channel_handle_t dma_chan; // DMA channel handle
|
||||
gdma_link_list_handle_t dma_link; // DMA link list handle
|
||||
@@ -139,6 +140,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
ESP_GOTO_ON_FALSE(bus, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 bus");
|
||||
bus->bus_width = bus_config->bus_width;
|
||||
bus->bus_id = -1;
|
||||
bus->clk_src = SOC_MOD_CLK_INVALID;
|
||||
// allocate the format buffer from internal memory, with DMA capability
|
||||
bus->format_buffer = heap_caps_calloc(1, LCD_I80_IO_FORMAT_BUF_SIZE,
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
@@ -262,6 +264,10 @@ err:
|
||||
if (bus->format_buffer) {
|
||||
free(bus->format_buffer);
|
||||
}
|
||||
if (bus->clk_src != SOC_MOD_CLK_INVALID) {
|
||||
esp_clk_tree_enable_src(bus->clk_src, false);
|
||||
bus->clk_src = SOC_MOD_CLK_INVALID;
|
||||
}
|
||||
#if CONFIG_PM_ENABLE
|
||||
if (bus->pm_lock) {
|
||||
esp_pm_lock_delete(bus->pm_lock);
|
||||
@@ -281,6 +287,10 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus)
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
lcd_ll_enable_clock(bus->hal.dev, false);
|
||||
}
|
||||
if (bus->clk_src != SOC_MOD_CLK_INVALID) {
|
||||
esp_clk_tree_enable_src(bus->clk_src, false);
|
||||
bus->clk_src = SOC_MOD_CLK_INVALID;
|
||||
}
|
||||
#if I80_USE_RETENTION_LINK
|
||||
const periph_retention_module_t module_id = soc_i80_lcd_retention_info[bus_id].retention_module;
|
||||
sleep_retention_module_detach(module_id);
|
||||
@@ -631,12 +641,12 @@ static void lcd_i80_create_retention_module(esp_lcd_i80_bus_t *bus)
|
||||
|
||||
static esp_err_t lcd_i80_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_clock_source_t clk_src)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true), TAG, "clock source enable failed");
|
||||
bus->clk_src = (soc_module_clk_t)clk_src;
|
||||
// get clock source frequency
|
||||
uint32_t src_clk_hz = 0;
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_hz),
|
||||
TAG, "get clock source frequency failed");
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true), TAG, "clock source enable failed");
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
lcd_ll_select_clk_src(bus->hal.dev, clk_src);
|
||||
// force to use integer division, as fractional division might lead to clock jitter
|
||||
|
||||
Reference in New Issue
Block a user