diff --git a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c index 8fc6b46e16..7430d646b8 100644 --- a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c +++ b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c @@ -6,6 +6,7 @@ #include #include "esp_lcd_panel_interface.h" #include "esp_lcd_mipi_dsi.h" +#include "esp_intr_alloc.h" #include "esp_clk_tree.h" #include "esp_cache.h" #include "mipi_dsi_priv.h" @@ -33,7 +34,8 @@ struct esp_lcd_dpi_panel_t { size_t bits_per_pixel; // Bits per pixel 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 + dw_gdma_channel_handle_t dma_chan; // DMA channel + intr_handle_t brg_intr; // DSI Bridge interrupt handle dw_gdma_link_list_handle_t link_lists[DPI_PANEL_MAX_FB_NUM]; // DMA link list esp_async_fbcpy_handle_t fbcpy_handle; // Use DMA2D to do frame buffer copy SemaphoreHandle_t draw_sem; // A semaphore used to synchronize the draw operations when DMA2D is used @@ -69,20 +71,9 @@ bool mipi_dsi_dma_trans_done_cb(dw_gdma_channel_handle_t chan, const dw_gdma_tra { bool yield_needed = false; esp_lcd_dpi_panel_t *dpi_panel = (esp_lcd_dpi_panel_t *)user_data; - mipi_dsi_hal_context_t *hal = &dpi_panel->bus->hal; uint8_t fb_index = dpi_panel->cur_fb_index; dw_gdma_link_list_handle_t link_list = dpi_panel->link_lists[fb_index]; - // clear the interrupt status - uint32_t error_status = mipi_dsi_brg_ll_get_interrupt_status(hal->bridge); - mipi_dsi_brg_ll_clear_interrupt_status(hal->bridge, error_status); - if (unlikely(error_status & MIPI_DSI_LL_EVENT_UNDERRUN)) { - // when an underrun happens, the LCD display may already becomes blue - // it's too late to recover the display, so we just print an error message - // as a hint to the user that he should optimize the memory bandwidth (with AXI-ICM) - ESP_DRAM_LOGE(TAG, "can't fetch data from external memory fast enough, underrun happens"); - } - // restart the DMA transfer, keep refreshing the LCD dw_gdma_block_markers_t markers = { .is_valid = true, @@ -92,15 +83,40 @@ bool mipi_dsi_dma_trans_done_cb(dw_gdma_channel_handle_t chan, const dw_gdma_tra dw_gdma_channel_use_link_list(chan, link_list); dw_gdma_channel_enable_ctrl(chan, true); +#if !MIPI_DSI_BRG_LL_EVENT_VSYNC // the DMA descriptor is large enough to carry a whole frame buffer, so this event can also be treated as a fake "vsync end" if (dpi_panel->on_refresh_done) { if (dpi_panel->on_refresh_done(&dpi_panel->base, NULL, dpi_panel->user_ctx)) { yield_needed = true; } } +#endif return yield_needed; } +void mipi_dsi_bridge_isr_handler(void *args) +{ + esp_lcd_dpi_panel_t* dpi_panel = (esp_lcd_dpi_panel_t *)args; + mipi_dsi_hal_context_t *hal = &dpi_panel->bus->hal; + // clear the interrupt status + uint32_t intr_status = mipi_dsi_brg_ll_get_interrupt_status(hal->bridge); + mipi_dsi_brg_ll_clear_interrupt_status(hal->bridge, intr_status); + + if (intr_status & MIPI_DSI_BRG_LL_EVENT_UNDERRUN) { + // when an underrun happens, the LCD display may already becomes blue + // it's too late to recover the display, so we just print an error message + // as a hint to the user that he should optimize the memory bandwidth (with AXI-ICM) + ESP_DRAM_LOGE(TAG, "can't fetch data from external memory fast enough, underrun happens"); + } + if (intr_status & MIPI_DSI_BRG_LL_EVENT_VSYNC) { + if (dpi_panel->on_refresh_done) { + if (dpi_panel->on_refresh_done(&dpi_panel->base, NULL, dpi_panel->user_ctx)) { + portYIELD_FROM_ISR(); + } + } + } +} + // Please note, errors happened in this function is just propagated to the caller // dpi_panel_del() is actually doing the error handling static esp_err_t dpi_panel_create_dma_link(esp_lcd_dpi_panel_t *dpi_panel) @@ -267,6 +283,14 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_ esp_pm_lock_acquire(dpi_panel->pm_lock); #endif + // install interrupt service + int isr_flags = ESP_INTR_FLAG_LOWMED; +#if CONFIG_LCD_DSI_ISR_CACHE_SAFE + isr_flags |= ESP_INTR_FLAG_IRAM; +#endif + ESP_GOTO_ON_ERROR(esp_intr_alloc(soc_mipi_dsi_signals[bus_id].brg_irq_id, isr_flags, mipi_dsi_bridge_isr_handler, + dpi_panel, &dpi_panel->brg_intr), err, TAG, "allocate DSI Bridge interrupt failed"); + // create DMA resources ESP_GOTO_ON_ERROR(dpi_panel_create_dma_link(dpi_panel), err, TAG, "initialize DMA link failed"); @@ -366,6 +390,9 @@ static esp_err_t dpi_panel_del(esp_lcd_panel_t *panel) if (dpi_panel->draw_sem) { vSemaphoreDeleteWithCaps(dpi_panel->draw_sem); } + if (dpi_panel->brg_intr) { + esp_intr_free(dpi_panel->brg_intr); + } if (dpi_panel->pm_lock) { esp_pm_lock_release(dpi_panel->pm_lock); esp_pm_lock_delete(dpi_panel->pm_lock); @@ -443,9 +470,8 @@ static esp_err_t dpi_panel_init(esp_lcd_panel_t *panel) mipi_dsi_brg_ll_enable_dpi_output(hal->bridge, true); mipi_dsi_brg_ll_update_dpi_config(hal->bridge); - // enable the underrun interrupt, we use this as a signal of bandwidth shortage - // note, we opt to not install a dedicated interrupt handler just for this error condition, instead, we check it in the DMA callback - mipi_dsi_brg_ll_enable_interrupt(hal->bridge, MIPI_DSI_LL_EVENT_UNDERRUN, true); + // always enable the interrupt to detect the underflow condition + mipi_dsi_brg_ll_enable_interrupt(hal->bridge, MIPI_DSI_BRG_LL_EVENT_UNDERRUN, true); return ESP_OK; } @@ -612,5 +638,8 @@ esp_err_t esp_lcd_dpi_panel_register_event_callbacks(esp_lcd_panel_handle_t pane dpi_panel->on_refresh_done = cbs->on_refresh_done; dpi_panel->user_ctx = user_ctx; + // enable the vsync interrupt if the callback is provided + mipi_dsi_brg_ll_enable_interrupt(dpi_panel->bus->hal.bridge, MIPI_DSI_BRG_LL_EVENT_VSYNC, cbs->on_refresh_done != NULL); + return ESP_OK; } diff --git a/components/esp_lcd/dsi/mipi_dsi_priv.h b/components/esp_lcd/dsi/mipi_dsi_priv.h index ad880e473e..3df7513e60 100644 --- a/components/esp_lcd/dsi/mipi_dsi_priv.h +++ b/components/esp_lcd/dsi/mipi_dsi_priv.h @@ -12,7 +12,7 @@ // Set the maximum log level for gptimer driver #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #endif -#include "soc/soc_caps_full.h" +#include "soc/mipi_dsi_periph.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" diff --git a/components/esp_lcd/linker.lf b/components/esp_lcd/linker.lf index 301f385da5..f69efa92bd 100644 --- a/components/esp_lcd/linker.lf +++ b/components/esp_lcd/linker.lf @@ -3,6 +3,7 @@ archive: libesp_lcd.a entries: if LCD_DSI_ISR_HANDLER_IN_IRAM = y: esp_lcd_panel_dpi: mipi_dsi_dma_trans_done_cb (noflash) + esp_lcd_panel_dpi: mipi_dsi_bridge_isr_handler (noflash) [mapping:esp_lcd_dsi_dma] archive: libesp_hw_support.a diff --git a/components/hal/esp32p4/include/hal/mipi_dsi_brg_ll.h b/components/hal/esp32p4/include/hal/mipi_dsi_brg_ll.h index 1c7fd80e85..fdeeea3411 100644 --- a/components/hal/esp32p4/include/hal/mipi_dsi_brg_ll.h +++ b/components/hal/esp32p4/include/hal/mipi_dsi_brg_ll.h @@ -15,8 +15,13 @@ #include "hal/config.h" #define MIPI_DSI_LL_GET_BRG(bus_id) (bus_id == 0 ? &MIPI_DSI_BRIDGE : NULL) -#define MIPI_DSI_LL_EVENT_UNDERRUN (1 << 0) -#define MIPI_DSI_LL_EVENT_VSYNC (1 << 1) + +#define MIPI_DSI_BRG_LL_EVENT_UNDERRUN (1 << 0) +#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 +#define MIPI_DSI_BRG_LL_EVENT_VSYNC (1 << 1) +#else +#define MIPI_DSI_BRG_LL_EVENT_VSYNC 0 // not supported +#endif #ifdef __cplusplus extern "C" { diff --git a/components/soc/esp32p4/mipi_dsi_periph.c b/components/soc/esp32p4/mipi_dsi_periph.c index ca2eb57f00..b8ae4de768 100644 --- a/components/soc/esp32p4/mipi_dsi_periph.c +++ b/components/soc/esp32p4/mipi_dsi_periph.c @@ -1,10 +1,11 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "soc/mipi_dsi_periph.h" +#include "soc/interrupts.h" const soc_mipi_dsi_phy_pll_freq_range_t soc_mipi_dsi_phy_pll_ranges[] = { {80, 89, 0x00}, // [80,90) Mbps @@ -49,3 +50,9 @@ const soc_mipi_dsi_phy_pll_freq_range_t soc_mipi_dsi_phy_pll_ranges[] = { }; const size_t num_of_soc_mipi_dsi_phy_pll_ranges = sizeof(soc_mipi_dsi_phy_pll_ranges) / sizeof(soc_mipi_dsi_phy_pll_freq_range_t); + +const soc_mipi_dsi_signal_desc_t soc_mipi_dsi_signals[1] = { + [0] = { + .brg_irq_id = ETS_DSI_BRIDGE_INTR_SOURCE, + } +}; diff --git a/components/soc/include/soc/mipi_dsi_periph.h b/components/soc/include/soc/mipi_dsi_periph.h index 1264c517b6..6c48dfa826 100644 --- a/components/soc/include/soc/mipi_dsi_periph.h +++ b/components/soc/include/soc/mipi_dsi_periph.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,11 +8,14 @@ #include #include +#include "soc/soc_caps.h" #ifdef __cplusplus extern "C" { #endif +#if SOC_MIPI_DSI_SUPPORTED + /** * @brief MIPI DSI PHY PLL frequency range */ @@ -25,6 +28,14 @@ typedef struct { extern const soc_mipi_dsi_phy_pll_freq_range_t soc_mipi_dsi_phy_pll_ranges[]; extern const size_t num_of_soc_mipi_dsi_phy_pll_ranges; +typedef struct { + const int brg_irq_id; // interrupt source ID for MIPI DSI Bridge +} soc_mipi_dsi_signal_desc_t; + +extern const soc_mipi_dsi_signal_desc_t soc_mipi_dsi_signals[1]; // only one MIPI DSI peripheral + +#endif // SOC_MIPI_DSI_SUPPORTED + #ifdef __cplusplus } #endif