mirror of
https://github.com/espressif/esp-idf.git
synced 2026-06-07 05:36:32 +03:00
feat(lcd): support buffer switch interrupt
This commit is contained in:
@@ -465,6 +465,12 @@ esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_
|
||||
gdma_group_t *group = pair->group;
|
||||
gdma_hal_context_t *hal = &group->hal;
|
||||
gdma_tx_channel_t *tx_chan = __containerof(dma_chan, gdma_tx_channel_t, base);
|
||||
bool link_switch_event_supported = gdma_hal_is_tx_link_switch_event_supported(hal);
|
||||
|
||||
if (cbs->on_link_switch) {
|
||||
ESP_RETURN_ON_FALSE(link_switch_event_supported,
|
||||
ESP_ERR_NOT_SUPPORTED, TAG, "on_link_switch not supported");
|
||||
}
|
||||
|
||||
if (dma_chan->flags.isr_cache_safe) {
|
||||
if (cbs->on_trans_eof) {
|
||||
@@ -475,6 +481,10 @@ esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_descr_err), ESP_ERR_INVALID_ARG,
|
||||
TAG, "on_descr_err not in IRAM");
|
||||
}
|
||||
if (cbs->on_link_switch) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_link_switch), ESP_ERR_INVALID_ARG,
|
||||
TAG, "on_link_switch not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG,
|
||||
TAG, "user context not in internal RAM");
|
||||
@@ -488,6 +498,12 @@ esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_
|
||||
portENTER_CRITICAL(&pair->spinlock);
|
||||
gdma_hal_enable_intr(hal, pair->pair_id, GDMA_CHANNEL_DIRECTION_TX, GDMA_LL_EVENT_TX_EOF, cbs->on_trans_eof != NULL);
|
||||
gdma_hal_enable_intr(hal, pair->pair_id, GDMA_CHANNEL_DIRECTION_TX, GDMA_LL_EVENT_TX_DESC_ERROR, cbs->on_descr_err != NULL);
|
||||
#if GDMA_LL_EVENT_TX_LINK_SWITCH
|
||||
if (link_switch_event_supported) {
|
||||
gdma_hal_enable_intr(hal, pair->pair_id, GDMA_CHANNEL_DIRECTION_TX, GDMA_LL_EVENT_TX_LINK_SWITCH,
|
||||
cbs->on_link_switch != NULL);
|
||||
}
|
||||
#endif // GDMA_LL_EVENT_TX_LINK_SWITCH
|
||||
portEXIT_CRITICAL(&pair->spinlock);
|
||||
|
||||
memcpy(&tx_chan->cbs, cbs, sizeof(gdma_tx_event_callbacks_t));
|
||||
@@ -601,6 +617,30 @@ esp_err_t gdma_reset(gdma_channel_handle_t dma_chan)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t gdma_request_link_switch_event(gdma_channel_handle_t dma_tx_chan)
|
||||
{
|
||||
if (!dma_tx_chan || dma_tx_chan->direction != GDMA_CHANNEL_DIRECTION_TX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
gdma_pair_t *pair = dma_tx_chan->pair;
|
||||
gdma_group_t *group = pair->group;
|
||||
gdma_hal_context_t *hal = &group->hal;
|
||||
gdma_tx_channel_t *tx_chan = __containerof(dma_tx_chan, gdma_tx_channel_t, base);
|
||||
|
||||
if (!gdma_hal_is_tx_link_switch_event_supported(hal)) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if (!tx_chan->cbs.on_link_switch) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_SAFE(&dma_tx_chan->spinlock);
|
||||
gdma_hal_request_link_switch_event(hal, pair->pair_id, dma_tx_chan->direction);
|
||||
portEXIT_CRITICAL_SAFE(&dma_tx_chan->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void gdma_release_group_handle(gdma_group_t *group)
|
||||
{
|
||||
int group_id = group->group_id;
|
||||
@@ -868,6 +908,12 @@ void gdma_default_tx_isr(void *args)
|
||||
if ((intr_status & GDMA_LL_EVENT_TX_DESC_ERROR) && tx_chan->cbs.on_descr_err) {
|
||||
need_yield |= tx_chan->cbs.on_descr_err(&tx_chan->base, NULL, tx_chan->user_data);
|
||||
}
|
||||
#if GDMA_LL_EVENT_TX_LINK_SWITCH
|
||||
if (gdma_hal_is_tx_link_switch_event_supported(hal) &&
|
||||
(intr_status & GDMA_LL_EVENT_TX_LINK_SWITCH) && tx_chan->cbs.on_link_switch) {
|
||||
need_yield |= tx_chan->cbs.on_link_switch(&tx_chan->base, NULL, tx_chan->user_data);
|
||||
}
|
||||
#endif // GDMA_LL_EVENT_TX_LINK_SWITCH
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
@@ -890,7 +936,7 @@ static esp_err_t gdma_install_rx_interrupt(gdma_rx_channel_t *rx_chan)
|
||||
#endif
|
||||
intr_handle_t intr = NULL;
|
||||
ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[group->group_id].pairs[pair_id].rx_irq_id, isr_flags,
|
||||
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_RX), GDMA_LL_RX_EVENT_MASK,
|
||||
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_RX), hal->priv_data->rx_event_mask,
|
||||
gdma_default_rx_isr, rx_chan, &intr);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc interrupt failed");
|
||||
rx_chan->base.intr = intr;
|
||||
@@ -922,7 +968,7 @@ static esp_err_t gdma_install_tx_interrupt(gdma_tx_channel_t *tx_chan)
|
||||
#endif
|
||||
intr_handle_t intr = NULL;
|
||||
ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[group->group_id].pairs[pair_id].tx_irq_id, isr_flags,
|
||||
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_TX), GDMA_LL_TX_EVENT_MASK,
|
||||
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_TX), hal->priv_data->tx_event_mask,
|
||||
gdma_default_tx_isr, tx_chan, &intr);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc interrupt failed");
|
||||
tx_chan->base.intr = intr;
|
||||
|
||||
@@ -68,6 +68,7 @@ typedef bool (*gdma_event_callback_t)(gdma_channel_handle_t dma_chan, gdma_event
|
||||
typedef struct {
|
||||
gdma_event_callback_t on_trans_eof; /*!< Invoked when TX engine meets EOF descriptor */
|
||||
gdma_event_callback_t on_descr_err; /*!< Invoked when DMA encounters a descriptor error */
|
||||
gdma_event_callback_t on_link_switch; /*!< Invoked when TX link list switches to a new descriptor chain */
|
||||
} gdma_tx_event_callbacks_t;
|
||||
|
||||
/**
|
||||
@@ -281,6 +282,26 @@ esp_err_t gdma_get_group_channel_id(gdma_channel_handle_t dma_chan, int *group_i
|
||||
*/
|
||||
esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_tx_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Request an interrupt when the TX channel switches to a new descriptor chain
|
||||
*
|
||||
* @note This API is only available on targets that support TX link switch interrupt.
|
||||
* @note Register the `on_link_switch` callback by `gdma_register_tx_event_callbacks()` before calling this API.
|
||||
* @note The TX EOF event indicates that GDMA has reached an EOF descriptor. By contrast, the TX link switch
|
||||
* event indicates that GDMA has started using the next descriptor chain after a link update.
|
||||
* GDMA can prefetch descriptors from the updated link before the EOF event, so EOF doesn't always mean
|
||||
* the previous descriptor chain is no longer referenced by GDMA. The link switch event is needed when
|
||||
* the caller must know that the previous descriptor chain can be reused safely.
|
||||
*
|
||||
* @param[in] dma_tx_chan GDMA TX channel handle
|
||||
* @return
|
||||
* - ESP_OK: Request link switch event successfully
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - ESP_ERR_NOT_SUPPORTED: Link switch event is not supported
|
||||
* - ESP_ERR_INVALID_STATE: Link switch callback is not registered
|
||||
*/
|
||||
esp_err_t gdma_request_link_switch_event(gdma_channel_handle_t dma_tx_chan);
|
||||
|
||||
/**
|
||||
* @brief Set GDMA event callbacks for RX channel
|
||||
* @note This API will install GDMA interrupt service for the channel internally
|
||||
|
||||
@@ -20,24 +20,28 @@ entries:
|
||||
gdma_hal_top: gdma_hal_clear_intr (noflash)
|
||||
gdma_hal_top: gdma_hal_read_intr_status (noflash)
|
||||
gdma_hal_top: gdma_hal_get_eof_desc_addr (noflash)
|
||||
gdma_hal_top: gdma_hal_is_tx_link_switch_event_supported (noflash)
|
||||
|
||||
# GDMA implementation layer for AHB-DMA version 1
|
||||
if SOC_AHB_GDMA_VERSION = 1:
|
||||
gdma_hal_ahb_v1: gdma_ahb_hal_clear_intr (noflash)
|
||||
gdma_hal_ahb_v1: gdma_ahb_hal_read_intr_status (noflash)
|
||||
gdma_hal_ahb_v1: gdma_ahb_hal_get_eof_desc_addr (noflash)
|
||||
gdma_hal_ahb_v1: gdma_ahb_hal_is_tx_link_switch_event_supported (noflash)
|
||||
|
||||
# GDMA implementation layer for AHB-DMA version 2
|
||||
if SOC_AHB_GDMA_VERSION = 2:
|
||||
gdma_hal_ahb_v2: gdma_ahb_hal_clear_intr (noflash)
|
||||
gdma_hal_ahb_v2: gdma_ahb_hal_read_intr_status (noflash)
|
||||
gdma_hal_ahb_v2: gdma_ahb_hal_get_eof_desc_addr (noflash)
|
||||
gdma_hal_ahb_v2: gdma_ahb_hal_is_tx_link_switch_event_supported (noflash)
|
||||
|
||||
# GDMA implementation layer for AXI-DMA
|
||||
if SOC_AXI_GDMA_SUPPORTED = y:
|
||||
gdma_hal_axi: gdma_axi_hal_clear_intr (noflash)
|
||||
gdma_hal_axi: gdma_axi_hal_read_intr_status (noflash)
|
||||
gdma_hal_axi: gdma_axi_hal_get_eof_desc_addr (noflash)
|
||||
gdma_hal_axi: gdma_axi_hal_is_tx_link_switch_event_supported (noflash)
|
||||
|
||||
# put GDMA control HAL functions in IRAM
|
||||
if GDMA_CTRL_FUNC_IN_IRAM = y:
|
||||
|
||||
@@ -41,7 +41,8 @@ struct esp_lcd_dpi_panel_t {
|
||||
SemaphoreHandle_t draw_sem; // A semaphore used to synchronize the draw operations when DMA2D is used
|
||||
esp_pm_lock_handle_t pm_lock; // Power management lock
|
||||
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
|
||||
};
|
||||
|
||||
@@ -83,10 +84,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;
|
||||
}
|
||||
}
|
||||
@@ -109,8 +116,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();
|
||||
}
|
||||
}
|
||||
@@ -626,19 +633,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
|
||||
*/
|
||||
@@ -178,18 +178,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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,14 @@
|
||||
#include "rgb_lcd_rotation_sw.h"
|
||||
#include "esp_private/sleep_retention.h"
|
||||
|
||||
#if SOC_AXI_GDMA_SUPPORTED
|
||||
#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
|
||||
@@ -135,7 +146,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
|
||||
@@ -703,7 +714,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;
|
||||
@@ -947,7 +960,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;
|
||||
@@ -957,6 +970,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
|
||||
@@ -984,11 +1015,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;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,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;
|
||||
@@ -135,7 +135,7 @@ typedef struct {
|
||||
esp_lcd_rgb_panel_bounce_buf_fill_cb_t on_bounce_empty; /*!< Bounce buffer empty callback. */
|
||||
union {
|
||||
esp_lcd_rgb_panel_frame_buf_complete_cb_t on_bounce_frame_finish __attribute__((deprecated)); /*!< Bounce buffer finish 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;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "test_mipi_dsi_board.h"
|
||||
#include "esp_lcd_ek79007.h"
|
||||
|
||||
IRAM_ATTR static bool test_rgb_panel_count_in_callback(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||
IRAM_ATTR static bool test_dpi_panel_count_in_callback(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
uint32_t *count = (uint32_t *)user_ctx;
|
||||
*count = *count + 1;
|
||||
@@ -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_rgb_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));
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ extern "C" {
|
||||
|
||||
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
|
||||
|
||||
#define GDMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define GDMA_LL_TX_EVENT_MASK (0x3F)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x3F)
|
||||
|
||||
// for M2M mode, hardware will automatically assign peri_sel ID depends on the channel number (ch0: 10, ch1: 11, ch2: 12)
|
||||
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0x1C00)
|
||||
|
||||
@@ -23,8 +23,8 @@ extern "C" {
|
||||
|
||||
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
|
||||
|
||||
#define GDMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define GDMA_LL_TX_EVENT_MASK (0x3F)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x3F)
|
||||
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFE75)
|
||||
|
||||
@@ -23,8 +23,8 @@ extern "C" {
|
||||
|
||||
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
|
||||
|
||||
#define GDMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define GDMA_LL_TX_EVENT_MASK (0x3F)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x3F)
|
||||
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFC00)
|
||||
|
||||
@@ -25,6 +25,8 @@ extern "C" {
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFAC2)
|
||||
#define AHB_DMA_LL_INVALID_PERIPH_ID (0x3F)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x1F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x0F)
|
||||
|
||||
///////////////////////////////////// Common /////////////////////////////////////////
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "hal/hal_utils.h"
|
||||
#include "hal/gdma_types.h"
|
||||
#include "hal/gdma_ll.h"
|
||||
#include "hal/config.h"
|
||||
#include "soc/axi_dma_struct.h"
|
||||
#include "soc/axi_dma_reg.h"
|
||||
|
||||
@@ -25,6 +26,13 @@ extern "C" {
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AXI_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFFC0)
|
||||
#define AXI_DMA_LL_INVALID_PERIPH_ID (0x3F)
|
||||
#define AXI_DMA_LL_RX_EVENT_MASK (0x1F)
|
||||
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
|
||||
#define AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT 1
|
||||
#define AXI_DMA_LL_TX_EVENT_MASK (0x40F)
|
||||
#else
|
||||
#define AXI_DMA_LL_TX_EVENT_MASK (0x0F)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////// Common /////////////////////////////////////////
|
||||
/**
|
||||
@@ -468,6 +476,42 @@ static inline void axi_dma_ll_tx_restart(axi_dma_dev_t *dev, uint32_t channel)
|
||||
dev->out[channel].conf.out_link1.outlink_restart_chn = 1;
|
||||
}
|
||||
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
/**
|
||||
* @brief Request link switch done indication for TX channel
|
||||
*/
|
||||
static inline void axi_dma_ll_tx_request_link_switch_event(axi_dma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case 0:
|
||||
dev->link_switch_state.link_switch_state_ch0 = 1;
|
||||
break;
|
||||
case 1:
|
||||
dev->link_switch_state.link_switch_state_ch1 = 1;
|
||||
break;
|
||||
case 2:
|
||||
dev->link_switch_state.link_switch_state_ch2 = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Check if TX link switch done indication is supported
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline bool axi_dma_ll_tx_is_link_switch_event_supported(axi_dma_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
|
||||
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
|
||||
|
||||
#define GDMA_LL_RX_EVENT_MASK (0x1F)
|
||||
#define GDMA_LL_TX_EVENT_MASK (0x0F)
|
||||
|
||||
#define GDMA_LL_INVALID_PERIPH_ID (0x3F)
|
||||
|
||||
// the following event bits are only supported by axi-dma
|
||||
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
|
||||
#define GDMA_LL_EVENT_TX_LINK_SWITCH (1<<10)
|
||||
#endif
|
||||
// the following event bits are identical for ahb-dma and axi-dma
|
||||
#define GDMA_LL_EVENT_TX_TOTAL_EOF (1<<3)
|
||||
#define GDMA_LL_EVENT_TX_DESC_ERROR (1<<2)
|
||||
#define GDMA_LL_EVENT_TX_EOF (1<<1)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
static gdma_hal_priv_data_t gdma_ahb_hal_priv_data = {
|
||||
.m2m_free_periph_mask = GDMA_LL_M2M_FREE_PERIPH_ID_MASK,
|
||||
.tx_event_mask = GDMA_LL_TX_EVENT_MASK,
|
||||
.rx_event_mask = GDMA_LL_RX_EVENT_MASK,
|
||||
};
|
||||
|
||||
void gdma_ahb_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr)
|
||||
@@ -175,6 +177,12 @@ void gdma_ahb_hal_enable_etm_task(gdma_hal_context_t *hal, int chan_id, gdma_cha
|
||||
}
|
||||
#endif // SOC_GDMA_SUPPORT_ETM
|
||||
|
||||
bool gdma_ahb_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal)
|
||||
{
|
||||
(void)hal;
|
||||
return false;
|
||||
}
|
||||
|
||||
void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
{
|
||||
hal->dev = GDMA_LL_GET_HW(config->group_id - GDMA_LL_AHB_GROUP_START_ID);
|
||||
@@ -198,5 +206,6 @@ void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
#if GDMA_LL_AHB_BURST_SIZE_ADJUSTABLE
|
||||
hal->set_burst_size = gdma_ahb_hal_set_burst_size;
|
||||
#endif // GDMA_LL_AHB_BURST_SIZE_ADJUSTABLE
|
||||
hal->is_tx_link_switch_event_supported = gdma_ahb_hal_is_tx_link_switch_event_supported;
|
||||
hal->priv_data = &gdma_ahb_hal_priv_data;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
static gdma_hal_priv_data_t gdma_ahb_hal_priv_data = {
|
||||
.m2m_free_periph_mask = AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK,
|
||||
.tx_event_mask = AHB_DMA_LL_TX_EVENT_MASK,
|
||||
.rx_event_mask = AHB_DMA_LL_RX_EVENT_MASK,
|
||||
};
|
||||
|
||||
void gdma_ahb_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr)
|
||||
@@ -232,6 +234,12 @@ void gdma_ahb_hal_enable_etm_task(gdma_hal_context_t *hal, int chan_id, gdma_cha
|
||||
}
|
||||
#endif // SOC_GDMA_SUPPORT_ETM
|
||||
|
||||
bool gdma_ahb_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal)
|
||||
{
|
||||
(void)hal;
|
||||
return false;
|
||||
}
|
||||
|
||||
void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
{
|
||||
hal->ahb_dma_dev = AHB_DMA_LL_GET_HW(config->group_id - GDMA_LL_AHB_GROUP_START_ID);
|
||||
@@ -261,5 +269,6 @@ void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
#if GDMA_LL_AHB_BURST_SIZE_ADJUSTABLE
|
||||
hal->set_burst_size = gdma_ahb_hal_set_burst_size;
|
||||
#endif // GDMA_LL_AHB_BURST_SIZE_ADJUSTABLE
|
||||
hal->is_tx_link_switch_event_supported = gdma_ahb_hal_is_tx_link_switch_event_supported;
|
||||
ahb_dma_ll_set_default_memory_range(hal->ahb_dma_dev);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
static gdma_hal_priv_data_t gdma_axi_hal_priv_data = {
|
||||
.m2m_free_periph_mask = AXI_DMA_LL_M2M_FREE_PERIPH_ID_MASK,
|
||||
.tx_event_mask = AXI_DMA_LL_TX_EVENT_MASK,
|
||||
.rx_event_mask = AXI_DMA_LL_RX_EVENT_MASK,
|
||||
};
|
||||
|
||||
void gdma_axi_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr)
|
||||
@@ -163,6 +165,15 @@ uint32_t gdma_axi_hal_get_eof_desc_addr(gdma_hal_context_t *hal, int chan_id, gd
|
||||
}
|
||||
}
|
||||
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
void gdma_axi_hal_request_link_switch_event(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir)
|
||||
{
|
||||
if (dir == GDMA_CHANNEL_DIRECTION_TX) {
|
||||
axi_dma_ll_tx_request_link_switch_event(hal->axi_dma_dev, chan_id);
|
||||
}
|
||||
}
|
||||
#endif // AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
|
||||
#if SOC_GDMA_SUPPORT_CRC
|
||||
void gdma_axi_hal_clear_crc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir)
|
||||
{
|
||||
@@ -230,6 +241,11 @@ void gdma_axi_hal_enable_etm_task(gdma_hal_context_t *hal, int chan_id, gdma_cha
|
||||
}
|
||||
#endif // SOC_GDMA_SUPPORT_ETM
|
||||
|
||||
bool gdma_axi_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal)
|
||||
{
|
||||
return axi_dma_ll_tx_is_link_switch_event_supported(hal->axi_dma_dev);
|
||||
}
|
||||
|
||||
void gdma_axi_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
{
|
||||
hal->axi_dma_dev = AXI_DMA_LL_GET_HW(config->group_id - GDMA_LL_AXI_GROUP_START_ID);
|
||||
@@ -257,5 +273,10 @@ void gdma_axi_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
#if SOC_GDMA_SUPPORT_ETM
|
||||
hal->enable_etm_task = gdma_axi_hal_enable_etm_task;
|
||||
#endif // SOC_GDMA_SUPPORT_ETM
|
||||
|
||||
hal->is_tx_link_switch_event_supported = gdma_axi_hal_is_tx_link_switch_event_supported;
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
hal->request_link_switch_event = gdma_axi_hal_request_link_switch_event;
|
||||
#endif // AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
axi_dma_ll_set_default_memory_range(hal->axi_dma_dev);
|
||||
}
|
||||
|
||||
@@ -113,3 +113,13 @@ void gdma_hal_enable_etm_task(gdma_hal_context_t *hal, int chan_id, gdma_channel
|
||||
hal->enable_etm_task(hal, chan_id, dir, en_or_dis);
|
||||
}
|
||||
#endif // SOC_GDMA_SUPPORT_ETM
|
||||
|
||||
void gdma_hal_request_link_switch_event(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir)
|
||||
{
|
||||
hal->request_link_switch_event(hal, chan_id, dir);
|
||||
}
|
||||
|
||||
bool gdma_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal)
|
||||
{
|
||||
return hal->is_tx_link_switch_event_supported(hal);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -49,9 +49,9 @@ typedef struct {
|
||||
typedef struct {
|
||||
// The bitmap of the IDs that can be used by M2M are different between AXI DMA and AHB DMA, so we need to save a copy for each of them
|
||||
uint32_t m2m_free_periph_mask;
|
||||
// TODO: we can add more private data here, e.g. the interrupt event mask of interest
|
||||
// for now, the AXI DMA and AHB DMA are sharing the same interrupt mask, so we don't need to store it here
|
||||
// If one day they become incompatible, we shall save a copy for each of them as a private data
|
||||
// Supported interrupt events can vary across DMA instances (e.g. AHB vs AXI)
|
||||
uint32_t tx_event_mask;
|
||||
uint32_t rx_event_mask;
|
||||
} gdma_hal_priv_data_t;
|
||||
|
||||
/**
|
||||
@@ -95,6 +95,8 @@ struct gdma_hal_context_t {
|
||||
#if SOC_GDMA_SUPPORT_ETM
|
||||
void (*enable_etm_task)(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_or_dis); /// Enable the ETM task
|
||||
#endif // SOC_GDMA_SUPPORT_ETM
|
||||
bool (*is_tx_link_switch_event_supported)(gdma_hal_context_t *hal); /// Check if TX link-switch interrupt is supported
|
||||
void (*request_link_switch_event)(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir); /// Raise the interrupt when tx link switch complete
|
||||
};
|
||||
|
||||
void gdma_hal_deinit(gdma_hal_context_t *hal);
|
||||
@@ -148,6 +150,10 @@ uint32_t gdma_hal_get_crc_result(gdma_hal_context_t *hal, int chan_id, gdma_chan
|
||||
void gdma_hal_enable_etm_task(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_or_dis);
|
||||
#endif
|
||||
|
||||
void gdma_hal_request_link_switch_event(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
bool gdma_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,36 +12,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void gdma_ahb_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr);
|
||||
|
||||
void gdma_ahb_hal_stop(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_ahb_hal_append(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_ahb_hal_reset(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_ahb_hal_set_priority(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t priority);
|
||||
|
||||
void gdma_ahb_hal_connect_peri(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, gdma_trigger_peripheral_t periph, int periph_sub_id);
|
||||
|
||||
void gdma_ahb_hal_disconnect_peri(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_ahb_hal_enable_burst(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_data_burst, bool en_desc_burst);
|
||||
|
||||
void gdma_ahb_hal_set_burst_size(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t burst_sz);
|
||||
|
||||
void gdma_ahb_hal_set_strategy(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_owner_check, bool en_desc_write_back, bool eof_till_popped);
|
||||
|
||||
void gdma_ahb_hal_enable_intr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t intr_event_mask, bool en_or_dis);
|
||||
|
||||
void gdma_ahb_hal_clear_intr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t intr_event_mask);
|
||||
|
||||
uint32_t gdma_ahb_hal_read_intr_status(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool raw);
|
||||
|
||||
uint32_t gdma_ahb_hal_get_intr_status_reg(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
uint32_t gdma_ahb_hal_get_eof_desc_addr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool is_success);
|
||||
|
||||
void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -12,36 +12,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void gdma_axi_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr);
|
||||
|
||||
void gdma_axi_hal_stop(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_axi_hal_append(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_axi_hal_reset(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_axi_hal_set_priority(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t priority);
|
||||
|
||||
void gdma_axi_hal_connect_peri(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, gdma_trigger_peripheral_t periph, int periph_sub_id);
|
||||
|
||||
void gdma_axi_hal_disconnect_peri(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_axi_hal_enable_burst(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_data_burst, bool en_desc_burst);
|
||||
|
||||
void gdma_axi_hal_set_burst_size(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t burst_sz);
|
||||
|
||||
void gdma_axi_hal_set_strategy(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_owner_check, bool en_desc_write_back, bool eof_till_popped);
|
||||
|
||||
void gdma_axi_hal_enable_intr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t intr_event_mask, bool en_or_dis);
|
||||
|
||||
void gdma_axi_hal_clear_intr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t intr_event_mask);
|
||||
|
||||
uint32_t gdma_axi_hal_read_intr_status(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool raw);
|
||||
|
||||
uint32_t gdma_axi_hal_get_intr_status_reg(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
uint32_t gdma_axi_hal_get_eof_desc_addr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool is_success);
|
||||
|
||||
void gdma_axi_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
lvgl/lvgl: "9.4.0"
|
||||
lvgl/lvgl: "9.5.0"
|
||||
esp_lcd_ili9881c: "^1.0.0"
|
||||
esp_lcd_ek79007: "^1.0.0"
|
||||
|
||||
@@ -331,7 +331,7 @@ void app_main(void)
|
||||
esp_lcd_dpi_panel_event_callbacks_t cbs = {
|
||||
.on_color_trans_done = example_notify_lvgl_flush_ready,
|
||||
#if CONFIG_EXAMPLE_MONITOR_REFRESH_BY_GPIO
|
||||
.on_refresh_done = example_monitor_refresh_rate,
|
||||
.on_vsync = example_monitor_refresh_rate,
|
||||
#endif
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, display));
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
idf_component_register(SRCS "rgb_lcd_example_main.c" "lvgl_demo_ui.c"
|
||||
PRIV_REQUIRES esp_lcd
|
||||
PRIV_REQUIRES esp_lcd esp_timer
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
dependencies:
|
||||
lvgl/lvgl: "9.2.0"
|
||||
lvgl/lvgl: "9.5.0"
|
||||
|
||||
@@ -101,15 +101,43 @@ static const char *TAG = "example";
|
||||
|
||||
// LVGL library is not thread-safe, this example will call LVGL APIs from different tasks, so use a mutex to protect it
|
||||
static _lock_t lvgl_api_lock;
|
||||
static TaskHandle_t lvgl_task_handle;
|
||||
|
||||
extern void example_lvgl_demo_ui(lv_display_t *disp);
|
||||
|
||||
#if CONFIG_EXAMPLE_USE_DOUBLE_FB
|
||||
static bool example_on_frame_buf_complete(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_ctx)
|
||||
{
|
||||
(void)panel;
|
||||
(void)event_data;
|
||||
(void)user_ctx;
|
||||
BaseType_t need_yield = pdFALSE;
|
||||
|
||||
if (lvgl_task_handle) {
|
||||
vTaskNotifyGiveFromISR(lvgl_task_handle, &need_yield);
|
||||
}
|
||||
return need_yield == pdTRUE;
|
||||
}
|
||||
|
||||
static void example_lvgl_flush_wait_cb(lv_display_t *disp)
|
||||
{
|
||||
// The flush callback only submits the rendered buffer to the LCD driver.
|
||||
// With direct-mode double buffering, LVGL must wait until the RGB panel has
|
||||
// switched away from the previous frame buffer before rendering into it again.
|
||||
if (lv_display_flush_is_last(disp)) {
|
||||
// Wait until the previous frame buffer is no longer referenced by DMA.
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
}
|
||||
lv_display_flush_ready(disp);
|
||||
}
|
||||
#else
|
||||
static bool example_notify_lvgl_flush_ready(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_ctx)
|
||||
{
|
||||
lv_display_t *disp = (lv_display_t *)user_ctx;
|
||||
lv_display_flush_ready(disp);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
|
||||
{
|
||||
@@ -118,6 +146,20 @@ static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uin
|
||||
int offsetx2 = area->x2;
|
||||
int offsety1 = area->y1;
|
||||
int offsety2 = area->y2;
|
||||
#if CONFIG_EXAMPLE_USE_DOUBLE_FB
|
||||
if (!lv_display_flush_is_last(disp)) {
|
||||
lv_display_flush_ready(disp);
|
||||
return;
|
||||
}
|
||||
// In direct mode, LVGL may flush multiple dirty areas. Switch the RGB panel to the new
|
||||
// frame buffer only after the last dirty area has been rendered.
|
||||
offsetx1 = 0;
|
||||
offsety1 = 0;
|
||||
offsetx2 = EXAMPLE_LCD_H_RES - 1;
|
||||
offsety2 = EXAMPLE_LCD_V_RES - 1;
|
||||
// Clear any stale completion from the previous frame before waiting for this frame.
|
||||
ulTaskNotifyTake(pdTRUE, 0);
|
||||
#endif
|
||||
// pass the draw buffer to the driver
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map);
|
||||
}
|
||||
@@ -264,10 +306,19 @@ void app_main(void)
|
||||
|
||||
// set the callback which can copy the rendered image to an area of the display
|
||||
lv_display_set_flush_cb(display, example_lvgl_flush_cb);
|
||||
#if CONFIG_EXAMPLE_USE_DOUBLE_FB
|
||||
// The wait callback keeps LVGL from reusing a frame buffer until the panel driver
|
||||
// reports that the buffer has finished refreshing.
|
||||
lv_display_set_flush_wait_cb(display, example_lvgl_flush_wait_cb);
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Register event callbacks");
|
||||
esp_lcd_rgb_panel_event_callbacks_t cbs = {
|
||||
#if CONFIG_EXAMPLE_USE_DOUBLE_FB
|
||||
.on_frame_buf_complete = example_on_frame_buf_complete,
|
||||
#else
|
||||
.on_color_trans_done = example_notify_lvgl_flush_ready,
|
||||
#endif
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, display));
|
||||
|
||||
@@ -282,7 +333,7 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
|
||||
|
||||
ESP_LOGI(TAG, "Create LVGL task");
|
||||
xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);
|
||||
xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, &lvgl_task_handle);
|
||||
|
||||
ESP_LOGI(TAG, "Display LVGL UI");
|
||||
// Lock the mutex due to the LVGL APIs are not thread-safe
|
||||
|
||||
Reference in New Issue
Block a user