Merge branch 'feat/mipi_dsi_vsync_event' into 'master'

feat(dsi): add vsync interrupt handling

Closes IDF-13503

See merge request espressif/esp-idf!42163
This commit is contained in:
morris
2025-09-29 10:54:58 +08:00
11 changed files with 103 additions and 45 deletions

View File

@@ -6,6 +6,7 @@
#include <sys/param.h>
#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"
@@ -34,6 +35,7 @@ struct esp_lcd_dpi_panel_t {
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
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,11 +188,6 @@ 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;
@@ -190,7 +201,6 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
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;
}

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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" {

View File

@@ -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

View File

@@ -24,6 +24,7 @@ PROVIDE ( AXI_ICM_QOS = 0x500A4400 );
PROVIDE ( HP_PERI_PMS = 0x500A5000 );
PROVIDE ( LP2HP_PERI_PMS = 0x500A5800 );
PROVIDE ( DMA_PMS = 0x500A6000 );
PROVIDE ( AXI_PERF_MON = 0x500A8000 );
PROVIDE ( LEDC = 0x500D3000 );
PROVIDE ( LEDC_GAMMA_RAM = 0x500D3400 );
PROVIDE ( TIMERG0 = 0x500C2000 );

View File

@@ -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,
}
};

View File

@@ -144,12 +144,12 @@ typedef struct {
volatile icm_axi_hw_cfg_reg_reg_t hw_cfg_reg;
volatile icm_axi_cmd_reg_t cmd;
volatile icm_axi_data_reg_t data;
} icm_axi_dev_t;
} axi_icm_qos_dev_t;
extern icm_axi_dev_t ICM_SYS;
extern axi_icm_qos_dev_t AXI_ICM_QOS;
#ifndef __cplusplus
_Static_assert(sizeof(icm_axi_dev_t) == 0x10, "Invalid size of icm_axi_dev_t structure");
_Static_assert(sizeof(axi_icm_qos_dev_t) == 0x10, "Invalid size of axi_icm_qos_dev_t structure");
#endif
#ifdef __cplusplus

View File

@@ -508,12 +508,12 @@ typedef struct {
volatile icm_rdn_eco_cs_reg_t rdn_eco_cs;
volatile icm_rdn_eco_low_reg_t rdn_eco_low;
volatile icm_rdn_eco_high_reg_t rdn_eco_high;
} icm_dev_t;
} axi_icm_dev_t;
extern icm_dev_t ICM_SYS;
extern axi_icm_dev_t AXI_ICM;
#ifndef __cplusplus
_Static_assert(sizeof(icm_dev_t) == 0x5c, "Invalid size of icm_dev_t structure");
_Static_assert(sizeof(axi_icm_dev_t) == 0x5c, "Invalid size of axi_icm_dev_t structure");
#endif
#ifdef __cplusplus

View File

@@ -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 <stdint.h>
#include <stddef.h>
#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