diff --git a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c index 2b546f39a2..40fa13b796 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 @@ -71,20 +73,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, @@ -94,15 +85,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) @@ -172,25 +188,19 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_ // the deprecated way to set the pixel format #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - bool has_pixel_fmt = panel_config->pixel_format != 0; - bool has_in_fmt = panel_config->in_color_format != 0; - ESP_RETURN_ON_FALSE(has_pixel_fmt ^ has_in_fmt, ESP_ERR_INVALID_ARG, TAG, - "must set exactly one of pixel_format or in_color_format"); - if (panel_config->pixel_format) { - switch (panel_config->pixel_format) { - case LCD_COLOR_PIXEL_FORMAT_RGB565: - bits_per_pixel = 16; - break; - case LCD_COLOR_PIXEL_FORMAT_RGB666: - // RGB data in the memory must be constructed in 6-6-6 (18 bits) for each pixel - bits_per_pixel = 18; - break; - case LCD_COLOR_PIXEL_FORMAT_RGB888: - bits_per_pixel = 24; - break; - } - in_color_format = COLOR_TYPE_ID(COLOR_SPACE_RGB, panel_config->pixel_format); + switch (panel_config->pixel_format) { + case LCD_COLOR_PIXEL_FORMAT_RGB565: + bits_per_pixel = 16; + break; + case LCD_COLOR_PIXEL_FORMAT_RGB666: + // RGB data in the memory must be constructed in 6-6-6 (18 bits) for each pixel + bits_per_pixel = 18; + break; + case LCD_COLOR_PIXEL_FORMAT_RGB888: + bits_per_pixel = 24; + break; } + in_color_format = COLOR_TYPE_ID(COLOR_SPACE_RGB, panel_config->pixel_format); #pragma GCC diagnostic pop // the recommended way to set the input color format if (panel_config->in_color_format) { @@ -278,6 +288,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"); @@ -377,6 +395,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 CONFIG_PM_ENABLE if (dpi_panel->pm_lock) { esp_pm_lock_release(dpi_panel->pm_lock); @@ -458,9 +479,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; } @@ -622,10 +642,13 @@ esp_err_t esp_lcd_dpi_panel_register_event_callbacks(esp_lcd_panel_handle_t pane if (user_ctx) { ESP_RETURN_ON_FALSE(esp_ptr_internal(user_ctx), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM"); } -#endif // CONFIG_LCD_DSI_ISR_HANDLER_IN_IRAM +#endif // CONFIG_LCD_DSI_ISR_CACHE_SAFE dpi_panel->on_color_trans_done = cbs->on_color_trans_done; 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 9e107eb649..d0350141df 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/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_panel.c b/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_panel.c index 90f4fc2249..5719c2ef23 100644 --- a/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_panel.c +++ b/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_panel.c @@ -330,6 +330,8 @@ TEST_CASE("MIPI DSI draw YUV422 image (EK79007)", "[mipi_dsi]") test_bsp_disable_dsi_phy_power(); } +#if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_REV_MIN_FULL < 300) + TEST_CASE("MIPI DSI draw Gray8 image (EK79007)", "[mipi_dsi]") { esp_lcd_dsi_bus_handle_t mipi_dsi_bus; @@ -407,3 +409,5 @@ TEST_CASE("MIPI DSI draw Gray8 image (EK79007)", "[mipi_dsi]") test_bsp_disable_dsi_phy_power(); } + +#endif 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/include/soc/soc_caps_full.h b/components/soc/esp32p4/include/soc/soc_caps_full.h index 7ccb01cd48..2d08cd4501 100644 --- a/components/soc/esp32p4/include/soc/soc_caps_full.h +++ b/components/soc/esp32p4/include/soc/soc_caps_full.h @@ -37,3 +37,6 @@ /*--------------------------- ETM (Event Task Matrix) ----------------------------*/ #define _SOC_CAPS_ETM_INST_NUM 1 // Number of ETM instances #define _SOC_CAPS_ETM_CHANS_PER_INST 50 // Number of channels in each ETM instance + +/*--------------------------- MIPI DSI -------------------------------------------*/ +#define _SOC_CAPS_MIPI_DSI_INST_NUM 1 // Number of MIPI DSI instances 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..831b985add 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,17 @@ #include #include +#include "soc/soc_caps_full.h" + +// helper macros to access module attributes +#define SOC_MIPI_DSI_ATTR(_attr) SOC_MODULE_ATTR(MIPI_DSI, _attr) #ifdef __cplusplus extern "C" { #endif +#if SOC_HAS(MIPI_DSI) + /** * @brief MIPI DSI PHY PLL frequency range */ @@ -25,6 +31,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[SOC_MIPI_DSI_ATTR(INST_NUM)]; + +#endif // SOC_HAS(MIPI_DSI) + #ifdef __cplusplus } #endif