mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
feat(lcd): support buffer switch interrupt
This commit is contained in:
@@ -51,7 +51,8 @@ struct esp_lcd_dpi_panel_t {
|
||||
esp_pm_lock_handle_t pm_lock; // Power management lock
|
||||
#endif
|
||||
esp_lcd_dpi_panel_color_trans_done_cb_t on_color_trans_done; // Callback invoked when color data transfer has finished
|
||||
esp_lcd_dpi_panel_refresh_done_cb_t on_refresh_done; // Callback invoked when one refresh operation finished (kinda like a vsync end)
|
||||
esp_lcd_dpi_panel_frame_buf_complete_cb_t on_frame_buf_complete; // Callback invoked when the frame buffer can be reused safely
|
||||
esp_lcd_dpi_panel_vsync_cb_t on_vsync; // VSYNC event callback
|
||||
void *user_ctx; // User context for the callback
|
||||
};
|
||||
|
||||
@@ -101,10 +102,16 @@ 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 (dpi_panel->on_frame_buf_complete) {
|
||||
if (dpi_panel->on_frame_buf_complete(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
|
||||
yield_needed = 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)) {
|
||||
if (dpi_panel->on_vsync) {
|
||||
if (dpi_panel->on_vsync(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
|
||||
yield_needed = true;
|
||||
}
|
||||
}
|
||||
@@ -127,8 +134,8 @@ void mipi_dsi_bridge_isr_handler(void *args)
|
||||
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)) {
|
||||
if (dpi_panel->on_vsync) {
|
||||
if (dpi_panel->on_vsync(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
@@ -719,19 +726,23 @@ esp_err_t esp_lcd_dpi_panel_register_event_callbacks(esp_lcd_panel_handle_t pane
|
||||
if (cbs->on_color_trans_done) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_color_trans_done), ESP_ERR_INVALID_ARG, TAG, "on_color_trans_done callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_refresh_done) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_refresh_done), ESP_ERR_INVALID_ARG, TAG, "on_refresh_done callback not in IRAM");
|
||||
if (cbs->on_vsync) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_vsync), ESP_ERR_INVALID_ARG, TAG, "on_vsync callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_frame_buf_complete) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_frame_buf_complete), ESP_ERR_INVALID_ARG, TAG, "on_frame_buf_complete callback not in IRAM");
|
||||
}
|
||||
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_CACHE_SAFE
|
||||
dpi_panel->on_color_trans_done = cbs->on_color_trans_done;
|
||||
dpi_panel->on_refresh_done = cbs->on_refresh_done;
|
||||
dpi_panel->on_vsync = cbs->on_vsync;
|
||||
dpi_panel->on_frame_buf_complete = cbs->on_frame_buf_complete;
|
||||
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);
|
||||
mipi_dsi_brg_ll_enable_interrupt(dpi_panel->bus->hal.bridge, MIPI_DSI_BRG_LL_EVENT_VSYNC, cbs->on_vsync != NULL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -177,18 +177,36 @@ typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_color_trans_done_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Declare the prototype of the function that will be invoked
|
||||
* when driver finishes refreshing the frame buffer to the screen
|
||||
* when the frame buffer can be reused safely
|
||||
*
|
||||
* @deprecated Use esp_lcd_dpi_panel_frame_buf_complete_cb_t instead.
|
||||
*/
|
||||
typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_refresh_done_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Declare the prototype of the function that will be invoked when the LCD controller sends the VSYNC signal.
|
||||
*/
|
||||
typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_vsync_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Declare the prototype of the function that will be invoked
|
||||
* when the frame buffer can be reused safely
|
||||
*/
|
||||
typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_frame_buf_complete_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Type of LCD DPI panel callbacks
|
||||
*/
|
||||
typedef struct {
|
||||
esp_lcd_dpi_panel_color_trans_done_cb_t on_color_trans_done; /*!< Invoked when user's color buffer copied to the internal frame buffer.
|
||||
esp_lcd_dpi_panel_color_trans_done_cb_t on_color_trans_done; /*!< Invoked when user's draw buffer copied to the frame buffer.
|
||||
This is an indicator that the draw buffer can be recycled safely.
|
||||
But doesn't mean the draw buffer finishes the refreshing to the screen. */
|
||||
esp_lcd_dpi_panel_refresh_done_cb_t on_refresh_done; /*!< Invoked when the internal frame buffer finishes refreshing to the screen */
|
||||
union {
|
||||
esp_lcd_dpi_panel_refresh_done_cb_t on_refresh_done __attribute__((deprecated("Deprecated, use on_frame_buf_complete instead"))); /*!< Deprecated, use on_frame_buf_complete instead */
|
||||
esp_lcd_dpi_panel_frame_buf_complete_cb_t on_frame_buf_complete; /*!< Invoked when the frame buffer can be reused safely
|
||||
when the frame buffer is the draw buffer. */
|
||||
};
|
||||
esp_lcd_dpi_panel_vsync_cb_t on_vsync; /*!< VSYNC event callback */
|
||||
} esp_lcd_dpi_panel_event_callbacks_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,6 +50,14 @@
|
||||
#include "rgb_lcd_rotation_sw.h"
|
||||
#include "esp_private/sleep_retention.h"
|
||||
|
||||
#if SOC_HAS(AXI_DMA)
|
||||
#include "hal/axi_dma_ll.h"
|
||||
#endif
|
||||
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
#define RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT 1
|
||||
#endif
|
||||
|
||||
// hardware issue workaround
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
#define RGB_LCD_NEEDS_SEPARATE_RESTART_LINK 1
|
||||
@@ -95,6 +103,9 @@ static esp_err_t lcd_rgb_panel_configure_gpio(esp_rgb_panel_t *rgb_panel, const
|
||||
static void lcd_rgb_panel_release_gpio(esp_rgb_panel_t *rgb_panel);
|
||||
static void lcd_rgb_panel_start_transmission(esp_rgb_panel_t *rgb_panel);
|
||||
static void rgb_lcd_default_isr_handler(void *args);
|
||||
#if RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
static bool lcd_rgb_panel_link_switch_handler(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
||||
#endif // RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
|
||||
struct esp_rgb_panel_t {
|
||||
esp_lcd_panel_t base; // Base class of generic lcd panel
|
||||
@@ -139,7 +150,7 @@ struct esp_rgb_panel_t {
|
||||
size_t bb_eof_count; // record the number we received the DMA EOF event, compare with `expect_eof_count` in the VSYNC_END ISR
|
||||
size_t expect_eof_count; // record the number of DMA EOF event we expected to receive
|
||||
esp_lcd_rgb_panel_draw_buf_complete_cb_t on_color_trans_done; // draw buffer completes
|
||||
esp_lcd_rgb_panel_frame_buf_complete_cb_t on_frame_buf_complete; // callback used to notify when the bounce buffer finish copying the entire frame
|
||||
esp_lcd_rgb_panel_frame_buf_complete_cb_t on_frame_buf_complete; // callback used to notify when the buffer can be reused safely
|
||||
esp_lcd_rgb_panel_vsync_cb_t on_vsync; // VSYNC event callback
|
||||
esp_lcd_rgb_panel_bounce_buf_fill_cb_t on_bounce_empty; // callback used to fill a bounce buffer rather than copying from the frame buffer
|
||||
void *user_ctx; // Reserved user's data of callback functions
|
||||
@@ -738,7 +749,9 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
|
||||
// it's hard to know the time when the new frame buffer starts
|
||||
gdma_link_concat(rgb_panel->dma_fb_links[i], -1, rgb_panel->dma_fb_links[rgb_panel->cur_fb_index], 0);
|
||||
}
|
||||
|
||||
#if RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
ESP_RETURN_ON_ERROR(gdma_request_link_switch_event(rgb_panel->dma_chan), TAG, "request link switch event failed");
|
||||
#endif // RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -977,7 +990,7 @@ static IRAM_ATTR bool lcd_rgb_panel_eof_handler(gdma_channel_handle_t dma_chan,
|
||||
portEXIT_CRITICAL_ISR(&rgb_panel->spinlock);
|
||||
need_yield = lcd_rgb_panel_fill_bounce_buffer(rgb_panel, rgb_panel->bounce_buffer[bb]);
|
||||
} else {
|
||||
// if not bounce buffer, the DMA EOF event means the end of a frame has been sent out to the LCD controller
|
||||
// Once the preload has already done, the buffer complete callback is not reliable.
|
||||
if (rgb_panel->on_frame_buf_complete) {
|
||||
if (rgb_panel->on_frame_buf_complete(&rgb_panel->base, NULL, rgb_panel->user_ctx)) {
|
||||
need_yield = true;
|
||||
@@ -987,6 +1000,24 @@ static IRAM_ATTR bool lcd_rgb_panel_eof_handler(gdma_channel_handle_t dma_chan,
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
#if RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
static IRAM_ATTR bool lcd_rgb_panel_link_switch_handler(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
(void)dma_chan;
|
||||
(void)event_data;
|
||||
bool need_yield = false;
|
||||
esp_rgb_panel_t *rgb_panel = (esp_rgb_panel_t *)user_data;
|
||||
|
||||
if (rgb_panel->on_frame_buf_complete) {
|
||||
if (rgb_panel->on_frame_buf_complete(&rgb_panel->base, NULL, rgb_panel->user_ctx)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
#endif // RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
|
||||
static esp_err_t lcd_rgb_create_dma_channel(esp_rgb_panel_t *rgb_panel)
|
||||
{
|
||||
// alloc DMA channel and connect to LCD peripheral
|
||||
@@ -1013,11 +1044,19 @@ static esp_err_t lcd_rgb_create_dma_channel(esp_rgb_panel_t *rgb_panel)
|
||||
// get the memory alignment required by the DMA
|
||||
gdma_get_alignment_constraints(rgb_panel->dma_chan, &rgb_panel->int_mem_align, &rgb_panel->ext_mem_align);
|
||||
|
||||
// register DMA EOF callback
|
||||
// register DMA event callbacks
|
||||
gdma_tx_event_callbacks_t cbs = {
|
||||
#if RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
// if no bounce buffer, the DMA EOF event means the end of a frame has been sent out to the LCD controller.
|
||||
// But the dma link may have preloaded the next frame with the current buffer.
|
||||
// So we need to wait for the GDMA link switch event to invoke the on_frame_buf_complete callback.
|
||||
.on_trans_eof = (rgb_panel->flags.stream_mode && !rgb_panel->bb_size) ? NULL : lcd_rgb_panel_eof_handler,
|
||||
.on_link_switch = lcd_rgb_panel_link_switch_handler,
|
||||
#else
|
||||
.on_trans_eof = lcd_rgb_panel_eof_handler,
|
||||
#endif // RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(rgb_panel->dma_chan, &cbs, rgb_panel), TAG, "register DMA EOF callback failed");
|
||||
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(rgb_panel->dma_chan, &cbs, rgb_panel), TAG, "register DMA event callbacks failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ typedef bool (*esp_lcd_rgb_panel_general_cb_t)(esp_lcd_panel_handle_t panel, con
|
||||
typedef esp_lcd_rgb_panel_general_cb_t esp_lcd_rgb_panel_draw_buf_complete_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Declare the prototype of the function that will be invoked when a whole frame buffer is sent to the LCD DMA.
|
||||
* @brief Declare the prototype of the function that will be invoked when a whole frame buffer can be reused safely.
|
||||
* The LCD hardware may still need some blank time to finish the refresh.
|
||||
*/
|
||||
typedef esp_lcd_rgb_panel_general_cb_t esp_lcd_rgb_panel_frame_buf_complete_cb_t;
|
||||
@@ -132,7 +132,7 @@ typedef struct {
|
||||
But doesn't mean the draw buffer finishes the refreshing to the screen. */
|
||||
esp_lcd_rgb_panel_vsync_cb_t on_vsync; /*!< VSYNC event callback */
|
||||
esp_lcd_rgb_panel_bounce_buf_fill_cb_t on_bounce_empty; /*!< Bounce buffer empty callback. */
|
||||
esp_lcd_rgb_panel_frame_buf_complete_cb_t on_frame_buf_complete; /*!< A whole frame buffer was just sent to the LCD DMA */
|
||||
esp_lcd_rgb_panel_frame_buf_complete_cb_t on_frame_buf_complete; /*!< Invoked when the frame buffer can be reused safely */
|
||||
} esp_lcd_rgb_panel_event_callbacks_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,7 +91,7 @@ TEST_CASE("MIPI DSI draw bitmap (EK79007) IRAM Safe", "[mipi_dsi]")
|
||||
|
||||
uint32_t callback_calls = 0;
|
||||
esp_lcd_dpi_panel_event_callbacks_t cbs = {
|
||||
.on_refresh_done = test_dpi_panel_count_in_callback,
|
||||
.on_frame_buf_complete = test_dpi_panel_count_in_callback,
|
||||
};
|
||||
TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, &callback_calls));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user