feat(lcd): support draw bitmap hook function

Closes https://github.com/espressif/esp-idf/issues/16201
This commit is contained in:
Chen Jichang
2025-10-16 18:39:26 +08:00
parent c231e0e777
commit 20bb7a2977
20 changed files with 678 additions and 80 deletions

View File

@@ -768,7 +768,7 @@ static esp_err_t i3c_master_prepare_transaction(i3c_master_i2c_device_handle_t d
// Check if async transaction requires DMA
if (dev_handle->bus_handle->async_transaction && !dev_handle->bus_handle->dma_initialized) {
ESP_LOGE(TAG, "Async transaction requires DMA, please call i3c_master_bus_decorate_dma() first");
ESP_LOGE(TAG, "Async transaction requires DMA, please call `i3c_master_bus_decorate_dma()` first");
return ESP_ERR_INVALID_STATE;
}

View File

@@ -49,7 +49,7 @@ exit:
static esp_err_t parlio_destroy_tx_unit(parlio_tx_unit_t *tx_unit)
{
if (tx_unit->bs_handle) {
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "please call parlio_tx_unit_undecorate_bitscrambler() before delete the tx unit");
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "please call `parlio_tx_unit_undecorate_bitscrambler()` before deleting the tx unit");
}
if (tx_unit->intr) {
ESP_RETURN_ON_ERROR(esp_intr_free(tx_unit->intr), TAG, "delete interrupt service failed");

View File

@@ -20,6 +20,8 @@ typedef struct esp_lcd_dpi_panel_t esp_lcd_dpi_panel_t;
static esp_err_t dpi_panel_del(esp_lcd_panel_t *panel);
static esp_err_t dpi_panel_init(esp_lcd_panel_t *panel);
static esp_err_t dpi_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
static esp_err_t dpi_panel_draw_bitmap_2d(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data,
size_t src_x_size, size_t src_y_size, int src_x_start, int src_y_start, int src_x_end, int src_y_end);
struct esp_lcd_dpi_panel_t {
esp_lcd_panel_t base; // Base class of generic lcd panel
@@ -37,8 +39,14 @@ struct esp_lcd_dpi_panel_t {
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
// hook fields
esp_lcd_panel_draw_bitmap_hook_t draw_bitmap_hook; // Draw bitmap hook function
void* hook_ctx; // Hook context
bool (*on_hook_end)(esp_lcd_panel_handle_t panel); // Callback to be invoked when the draw bitmap hook completes its operation
esp_async_fbcpy_handle_t fbcpy_handle; // Use DMA2D to do frame buffer copy (only when using DMA2D draw bitmap hook)
SemaphoreHandle_t draw_sem; // A semaphore used to synchronize the draw operations when DMA2D is used
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock; // Power management lock
#endif
@@ -47,7 +55,15 @@ struct esp_lcd_dpi_panel_t {
void *user_ctx; // User context for the callback
};
IRAM_ATTR
static bool dpi_panel_draw_bitmap_hook_end(esp_lcd_panel_t *panel)
{
esp_lcd_dpi_panel_t *dpi_panel = __containerof(panel, esp_lcd_dpi_panel_t, base);
if (dpi_panel->on_color_trans_done) {
return dpi_panel->on_color_trans_done(panel, NULL, dpi_panel->user_ctx);
}
return false;
}
static bool async_fbcpy_done_cb(esp_async_fbcpy_handle_t mcp, esp_async_fbcpy_event_data_t *event, void *cb_args)
{
bool need_yield = false;
@@ -60,8 +76,8 @@ static bool async_fbcpy_done_cb(esp_async_fbcpy_handle_t mcp, esp_async_fbcpy_ev
need_yield = true;
}
if (dpi_panel->on_color_trans_done) {
if (dpi_panel->on_color_trans_done(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
if (dpi_panel->on_hook_end) {
if (dpi_panel->on_hook_end(&dpi_panel->base)) {
need_yield = true;
}
}
@@ -168,13 +184,10 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
{
esp_err_t ret = ESP_OK;
esp_lcd_dpi_panel_t *dpi_panel = NULL;
esp_async_fbcpy_handle_t fbcpy_ctx = NULL;
ESP_RETURN_ON_FALSE(bus && panel_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(panel_config->virtual_channel < 4, ESP_ERR_INVALID_ARG, TAG, "invalid virtual channel %d", panel_config->virtual_channel);
ESP_RETURN_ON_FALSE(panel_config->dpi_clock_freq_mhz, ESP_ERR_INVALID_ARG, TAG, "invalid DPI clock frequency %"PRIu32, panel_config->dpi_clock_freq_mhz);
#if !SOC_DMA2D_SUPPORTED
ESP_RETURN_ON_FALSE(!panel_config->flags.use_dma2d, ESP_ERR_NOT_SUPPORTED, TAG, "DMA2D is not supported");
#endif // !SOC_DMA2D_SUPPORTED
size_t num_fbs = panel_config->num_fbs;
// if the user doesn't specify the number of frame buffers, then fallback to use one frame buffer
if (num_fbs == 0) {
@@ -227,17 +240,6 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
dpi_panel->h_pixels = panel_config->video_timing.h_size;
dpi_panel->v_pixels = panel_config->video_timing.v_size;
#if SOC_DMA2D_SUPPORTED
if (panel_config->flags.use_dma2d) {
esp_async_fbcpy_config_t fbcpy_config = {};
ESP_GOTO_ON_ERROR(esp_async_fbcpy_install(&fbcpy_config, &fbcpy_ctx), err, TAG, "install async memcpy 2d failed");
dpi_panel->fbcpy_handle = fbcpy_ctx;
dpi_panel->draw_sem = xSemaphoreCreateBinaryWithCaps(DSI_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(dpi_panel->draw_sem, ESP_ERR_NO_MEM, err, TAG, "no memory for draw semaphore");
xSemaphoreGive(dpi_panel->draw_sem);
}
#endif // SOC_DMA2D_SUPPORTED
// if the clock source is not assigned, fallback to the default clock source
mipi_dsi_dpi_clock_source_t dpi_clk_src = panel_config->dpi_clk_src;
if (dpi_clk_src == 0) {
@@ -331,6 +333,7 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
dpi_panel->base.del = dpi_panel_del;
dpi_panel->base.init = dpi_panel_init;
dpi_panel->base.draw_bitmap = dpi_panel_draw_bitmap;
dpi_panel->base.draw_bitmap_2d = dpi_panel_draw_bitmap_2d;
*ret_panel = &dpi_panel->base;
ESP_LOGD(TAG, "dpi panel created @%p", dpi_panel);
return ESP_OK;
@@ -347,6 +350,11 @@ static esp_err_t dpi_panel_del(esp_lcd_panel_t *panel)
esp_lcd_dsi_bus_handle_t bus = dpi_panel->bus;
int bus_id = bus->bus_id;
mipi_dsi_hal_context_t *hal = &bus->hal;
// check if the panel is using DMA2D draw bitmap hook
if (dpi_panel->fbcpy_handle) {
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "please call `esp_lcd_dpi_panel_disable_dma2d()` before deleting the panel");
}
// disable the DPI clock
PERIPH_RCC_ATOMIC() {
mipi_dsi_ll_enable_dpi_clock(bus_id, false);
@@ -367,12 +375,6 @@ static esp_err_t dpi_panel_del(esp_lcd_panel_t *panel)
dw_gdma_del_link_list(dpi_panel->link_lists[i]);
}
}
if (dpi_panel->fbcpy_handle) {
esp_async_fbcpy_uninstall(dpi_panel->fbcpy_handle);
}
if (dpi_panel->draw_sem) {
vSemaphoreDeleteWithCaps(dpi_panel->draw_sem);
}
if (dpi_panel->brg_intr) {
esp_intr_free(dpi_panel->brg_intr);
}
@@ -463,12 +465,128 @@ static esp_err_t dpi_panel_init(esp_lcd_panel_t *panel)
return ESP_OK;
}
static esp_err_t dpi_panel_draw_bitmap_dma2d_hook(esp_lcd_panel_t *panel, const esp_lcd_draw_bitmap_hook_data_t *hook_data, void* hook_ctx)
{
ESP_LOGV(TAG, "copy draw buffer by DMA2D");
esp_lcd_dpi_panel_t *dpi_panel = __containerof(panel, esp_lcd_dpi_panel_t, base);
// ensure the previous draw operation is finished
ESP_RETURN_ON_FALSE(xSemaphoreTake(dpi_panel->draw_sem, 0) == pdTRUE, ESP_ERR_INVALID_STATE,
TAG, "previous draw operation is not finished");
esp_async_fbcpy_trans_desc_t fbcpy_trans_config = {
.src_buffer = hook_data->src_data,
.dst_buffer = hook_data->dst_data,
.src_buffer_size_x = hook_data->src_x_size,
.src_buffer_size_y = hook_data->src_y_size,
.dst_buffer_size_x = hook_data->dst_x_size,
.dst_buffer_size_y = hook_data->dst_y_size,
.src_offset_x = hook_data->src_x_start,
.src_offset_y = hook_data->src_y_start,
.dst_offset_x = hook_data->dst_x_start,
.dst_offset_y = hook_data->dst_y_start,
.copy_size_x = hook_data->src_x_end - hook_data->src_x_start,
.copy_size_y = hook_data->src_y_end - hook_data->src_y_start,
.pixel_format_fourcc_id = dpi_panel->in_color_format,
};
// save the on_hook_end callback, and invoke it when the async memcpy is done
dpi_panel->on_hook_end = hook_data->on_hook_end;
ESP_RETURN_ON_ERROR(esp_async_fbcpy(dpi_panel->fbcpy_handle, &fbcpy_trans_config, async_fbcpy_done_cb, dpi_panel), TAG, "async memcpy failed");
return ESP_OK;
}
esp_err_t esp_lcd_dpi_panel_register_hooks(esp_lcd_panel_t *panel, const esp_lcd_panel_hooks_t *hooks, void *hook_ctx)
{
ESP_RETURN_ON_FALSE(panel && hooks, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
esp_lcd_dpi_panel_t *dpi_panel = __containerof(panel, esp_lcd_dpi_panel_t, base);
dpi_panel->draw_bitmap_hook = hooks->draw_bitmap_hook;
dpi_panel->hook_ctx = hook_ctx;
return ESP_OK;
}
esp_err_t esp_lcd_dpi_panel_enable_dma2d(esp_lcd_panel_handle_t panel)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "invalid panel");
esp_lcd_dpi_panel_t *dpi_panel = __containerof(panel, esp_lcd_dpi_panel_t, base);
// Check if built-in DMA2D draw bitmap hook is registered
ESP_RETURN_ON_FALSE(!dpi_panel->fbcpy_handle, ESP_ERR_INVALID_STATE, TAG, "draw bitmap DMA2D hook is already registered");
// Initialize DMA2D resources
esp_async_fbcpy_config_t fbcpy_config = {};
ESP_RETURN_ON_ERROR(esp_async_fbcpy_install(&fbcpy_config, &dpi_panel->fbcpy_handle), TAG, "install async memcpy 2d failed");
dpi_panel->draw_sem = xSemaphoreCreateBinaryWithCaps(DSI_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(dpi_panel->draw_sem, ESP_ERR_NO_MEM, err, TAG, "no memory for draw semaphore");
xSemaphoreGive(dpi_panel->draw_sem);
// Register the DMA2D draw bitmap hook
esp_lcd_panel_hooks_t hooks = {
.draw_bitmap_hook = dpi_panel_draw_bitmap_dma2d_hook,
};
ESP_GOTO_ON_ERROR(esp_lcd_dpi_panel_register_hooks(panel, &hooks, dpi_panel->user_ctx), err, TAG, "register DMA2D draw bitmap hook failed");
return ESP_OK;
err:
if (dpi_panel->fbcpy_handle) {
esp_async_fbcpy_uninstall(dpi_panel->fbcpy_handle);
dpi_panel->fbcpy_handle = NULL;
}
if (dpi_panel->draw_sem) {
vSemaphoreDeleteWithCaps(dpi_panel->draw_sem);
dpi_panel->draw_sem = NULL;
}
return ret;
}
esp_err_t esp_lcd_dpi_panel_disable_dma2d(esp_lcd_panel_handle_t panel)
{
ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
esp_lcd_dpi_panel_t *dpi_panel = __containerof(panel, esp_lcd_dpi_panel_t, base);
// Check if built-in DMA2D draw bitmap hook is registered
ESP_RETURN_ON_FALSE(dpi_panel->fbcpy_handle, ESP_ERR_INVALID_STATE, TAG, "draw bitmap DMA2D hook not registered");
esp_lcd_panel_hooks_t hooks = {
.draw_bitmap_hook = NULL,
};
ESP_RETURN_ON_ERROR(esp_lcd_dpi_panel_register_hooks(panel, &hooks, NULL), TAG, "unregister DMA2D draw bitmap hook failed");
if (dpi_panel->fbcpy_handle) {
ESP_RETURN_ON_ERROR(esp_async_fbcpy_uninstall(dpi_panel->fbcpy_handle), TAG, "uninstall DMA2D failed");
dpi_panel->fbcpy_handle = NULL;
}
if (dpi_panel->draw_sem) {
vSemaphoreDeleteWithCaps(dpi_panel->draw_sem);
dpi_panel->draw_sem = NULL;
}
return ESP_OK;
}
static esp_err_t dpi_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
{
size_t src_x_size = x_end - x_start;
size_t src_y_size = y_end - y_start;
size_t src_x_start = 0;
size_t src_y_start = 0;
size_t src_x_end = src_x_size;
size_t src_y_end = src_y_size;
ESP_RETURN_ON_ERROR(dpi_panel_draw_bitmap_2d(panel, x_start, y_start, x_end, y_end, color_data, src_x_size, src_y_size, src_x_start, src_y_start, src_x_end, src_y_end),
TAG, "draw bitmap failed");
return ESP_OK;
}
static esp_err_t dpi_panel_draw_bitmap_2d(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data,
size_t src_x_size, size_t src_y_size, int src_x_start, int src_y_start, int src_x_end, int src_y_end)
{
esp_lcd_dpi_panel_t *dpi_panel = __containerof(panel, esp_lcd_dpi_panel_t, base);
uint8_t cur_fb_index = dpi_panel->cur_fb_index;
uint8_t *frame_buffer = dpi_panel->fbs[cur_fb_index];
uint8_t *draw_buffer = (uint8_t *)color_data;
const uint8_t *draw_buffer = (const uint8_t *)color_data;
size_t fb_size = dpi_panel->fb_size;
size_t bits_per_pixel = dpi_panel->bits_per_pixel;
@@ -494,7 +612,31 @@ static esp_err_t dpi_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
do_copy = true;
}
if (!do_copy) { // no copy, just do cache memory write back
if (dpi_panel->draw_bitmap_hook) { // copy using draw bitmap hook
ESP_LOGV(TAG, "copy draw buffer by draw bitmap hook");
// Note, whether the previous draw operation is finished should be ensured by the hook
esp_lcd_draw_bitmap_hook_data_t hook_data = {
.dst_data = frame_buffer,
.dst_x_size = dpi_panel->h_pixels,
.dst_y_size = dpi_panel->v_pixels,
.dst_x_start = x_start,
.dst_y_start = y_start,
.dst_x_end = x_end,
.dst_y_end = y_end,
.src_data = draw_buffer,
.src_x_size = src_x_size,
.src_y_size = src_y_size,
.src_x_start = src_x_start,
.src_y_start = src_y_start,
.src_x_end = src_x_end,
.src_y_end = src_y_end,
.bits_per_pixel = bits_per_pixel,
.on_hook_end = dpi_panel_draw_bitmap_hook_end,
};
ESP_RETURN_ON_ERROR(dpi_panel->draw_bitmap_hook(panel, &hook_data, dpi_panel->hook_ctx), TAG, "draw_bitmap_hook failed");
} else if (!do_copy) { // no copy, just do cache memory write back
ESP_LOGV(TAG, "draw buffer is in frame buffer memory range, do cache write back only");
// only write back the LCD lines that updated by the draw buffer
uint8_t *cache_sync_start = dpi_panel->fbs[draw_buf_fb_index] + (y_start * dpi_panel->h_pixels) * bits_per_pixel / 8;
@@ -507,18 +649,19 @@ static esp_err_t dpi_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
if (dpi_panel->on_color_trans_done) {
dpi_panel->on_color_trans_done(&dpi_panel->base, NULL, dpi_panel->user_ctx);
}
} else if (!dpi_panel->fbcpy_handle) { // copy by CPU
} else { // copy by CPU
ESP_LOGV(TAG, "copy draw buffer by CPU");
const uint8_t *from = draw_buffer;
const uint8_t *from = draw_buffer + (src_y_start * src_x_size + src_x_start) * bits_per_pixel / 8;
uint8_t *to = frame_buffer + (y_start * dpi_panel->h_pixels + x_start) * bits_per_pixel / 8;
uint32_t copy_bytes_per_line = (x_end - x_start) * bits_per_pixel / 8;
uint32_t bytes_per_line = bits_per_pixel * dpi_panel->h_pixels / 8;
uint32_t dst_bytes_per_line = bits_per_pixel * dpi_panel->h_pixels / 8;
uint32_t src_bytes_per_line = bits_per_pixel * src_x_size / 8;
// please note, we assume the user provided draw_buffer is compact,
// but the destination is a sub-window of the frame buffer, so we need to skip the stride
for (int y = y_start; y < y_end; y++) {
memcpy(to, from, copy_bytes_per_line);
to += bytes_per_line;
from += copy_bytes_per_line;
to += dst_bytes_per_line;
from += src_bytes_per_line;
}
uint8_t *cache_sync_start = frame_buffer + (y_start * dpi_panel->h_pixels) * bits_per_pixel / 8;
size_t cache_sync_size = (y_end - y_start) * dpi_panel->h_pixels * bits_per_pixel / 8;
@@ -528,33 +671,6 @@ static esp_err_t dpi_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
if (dpi_panel->on_color_trans_done) {
dpi_panel->on_color_trans_done(&dpi_panel->base, NULL, dpi_panel->user_ctx);
}
} else { // copy by DMA2D
ESP_LOGV(TAG, "copy draw buffer by DMA2D");
// ensure the previous draw operation is finished
ESP_RETURN_ON_FALSE(xSemaphoreTake(dpi_panel->draw_sem, 0) == pdTRUE, ESP_ERR_INVALID_STATE,
TAG, "previous draw operation is not finished");
// write back the user's draw buffer, so that the DMA can see the correct data
// Note, the user draw buffer should be 1D array, and contiguous in memory, no stride
size_t color_data_size = (x_end - x_start) * (y_end - y_start) * bits_per_pixel / 8;
esp_cache_msync(draw_buffer, color_data_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
esp_async_fbcpy_trans_desc_t fbcpy_trans_config = {
.src_buffer = draw_buffer,
.dst_buffer = (void *)frame_buffer,
.src_buffer_size_x = x_end - x_start,
.src_buffer_size_y = y_end - y_start,
.dst_buffer_size_x = dpi_panel->h_pixels,
.dst_buffer_size_y = dpi_panel->v_pixels,
.src_offset_x = 0,
.src_offset_y = 0,
.dst_offset_x = x_start,
.dst_offset_y = y_start,
.copy_size_x = x_end - x_start,
.copy_size_y = y_end - y_start,
.pixel_format_fourcc_id = dpi_panel->in_color_format,
};
ESP_RETURN_ON_ERROR(esp_async_fbcpy(dpi_panel->fbcpy_handle, &fbcpy_trans_config, async_fbcpy_done_cb, dpi_panel), TAG, "async memcpy failed");
}
return ESP_OK;

View File

@@ -93,7 +93,6 @@ typedef struct {
esp_lcd_video_timing_t video_timing; /*!< Video timing */
/// Extra configuration flags for MIPI DSI DPI panel
struct extra_dpi_panel_flags {
uint32_t use_dma2d: 1; /*!< Use DMA2D to copy user buffer to the frame buffer when necessary */
uint32_t disable_lp: 1;/*!< Disable low-power for DPI */
} flags; /*!< Extra configuration flags */
} esp_lcd_dpi_panel_config_t;
@@ -200,6 +199,92 @@ typedef struct {
*/
esp_err_t esp_lcd_dpi_panel_register_event_callbacks(esp_lcd_panel_handle_t dpi_panel, const esp_lcd_dpi_panel_event_callbacks_t *cbs, void *user_ctx);
/**
* @brief Type of draw bitmap hook data
*/
typedef struct {
void *dst_data; /*!< Destination buffer (usually frame buffer) */
int dst_x_size; /*!< Destination bitmap width */
int dst_y_size; /*!< Destination bitmap height */
int dst_x_start; /*!< Destination start x coordinate */
int dst_y_start; /*!< Destination start y coordinate */
int dst_x_end; /*!< Destination end x coordinate (exclusive) */
int dst_y_end; /*!< Destination end y coordinate (exclusive) */
const void *src_data; /*!< Source bitmap data */
int src_x_size; /*!< Source bitmap width */
int src_y_size; /*!< Source bitmap height */
int src_x_start; /*!< Source start x coordinate */
int src_y_start; /*!< Source start y coordinate */
int src_x_end; /*!< Source end x coordinate (exclusive) */
int src_y_end; /*!< Source end y coordinate (exclusive) */
int bits_per_pixel; /*!< Bits per pixel */
bool (*on_hook_end)(esp_lcd_panel_handle_t panel); /*!< Callback to be invoked when the hook completes its operation */
} esp_lcd_draw_bitmap_hook_data_t;
/**
* @brief draw bitmap hook function type for custom pixel processing operations
*
* This hook allows users to implement custom operations like scaling, rotation,
* color space conversion, etc. using hardware accelerators like PPA or DMA2D.
*
* @note The hook should ensure the synchronization of draw operations on its own.
*
* @param[in] panel LCD panel handle
* @param[in] hook_data Hook data
* @param[in] hook_ctx Hook context
* @return
* - ESP_OK on success
* - Other error codes on failure
*/
typedef esp_err_t (*esp_lcd_panel_draw_bitmap_hook_t)(esp_lcd_panel_handle_t panel, const esp_lcd_draw_bitmap_hook_data_t *hook_data, void* hook_ctx);
/**
* @brief Type of LCD panel hooks
*/
typedef struct {
esp_lcd_panel_draw_bitmap_hook_t draw_bitmap_hook; /*!< Draw bitmap hook function */
} esp_lcd_panel_hooks_t;
/**
* @brief Register panel hooks to the DPI panel
*
* @note You can register panel hooks to implement custom operations like scaling, rotation, color space conversion, etc.
* with hardware accelerators like PPA or DMA2D.
* The hook will be overridden when this function is called multiple times.
*
* @param[in] dpi_panel LCD DPI panel handle, which is returned from esp_lcd_new_panel_dpi()
* @param[in] hooks Panel hooks
* @param[in] hook_ctx Hook context
* @return
* - ESP_OK: Register hooks successfully
* - Other error codes on failure
*/
esp_err_t esp_lcd_dpi_panel_register_hooks(esp_lcd_panel_handle_t dpi_panel, const esp_lcd_panel_hooks_t *hooks, void *hook_ctx);
/**
* @brief Enable DMA2D for DPI panel
*
* @note The function will register a built-in DMA2D draw bitmap hook to perform draw bitmap operations using DMA2D.
*
* @param[in] dpi_panel LCD DPI panel handle, which is returned from esp_lcd_new_panel_dpi()
* @return
* - ESP_OK: Enable DMA2D successfully
* - Other error codes on failure
*/
esp_err_t esp_lcd_dpi_panel_enable_dma2d(esp_lcd_panel_handle_t dpi_panel);
/**
* @brief Disable DMA2D for DPI panel
*
* @note The function will unregister the built-in DMA2D draw bitmap hook.
*
* @param[in] dpi_panel LCD DPI panel handle, which is returned from esp_lcd_new_panel_dpi()
* @return
* - ESP_OK: Disable DMA2D successfully
* - Other error codes on failure
*/
esp_err_t esp_lcd_dpi_panel_disable_dma2d(esp_lcd_panel_handle_t dpi_panel);
#ifdef __cplusplus
}
#endif

View File

@@ -58,6 +58,31 @@ esp_err_t esp_lcd_panel_del(esp_lcd_panel_handle_t panel);
*/
esp_err_t esp_lcd_panel_draw_bitmap(esp_lcd_panel_handle_t panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
/**
* @brief Draw partial source bitmap on LCD panel
*
* This function allows drawing a portion of a source bitmap to a specific area on the LCD panel.
*
* @param[in] panel LCD panel handle, which is created by other factory API like `esp_lcd_new_panel_st7789()`
* @param[in] x_start Start pixel index in the target frame buffer, on x-axis (x_start is included)
* @param[in] y_start Start pixel index in the target frame buffer, on y-axis (y_start is included)
* @param[in] x_end End pixel index in the target frame buffer, on x-axis (x_end is not included)
* @param[in] y_end End pixel index in the target frame buffer, on y-axis (y_end is not included)
* @param[in] color_data Source bitmap data
* @param[in] src_x_size Size of the source bitmap in the x-axis
* @param[in] src_y_size Size of the source bitmap in the y-axis
* @param[in] src_x_start Start pixel index in the source bitmap, on x-axis (src_x_start is included)
* @param[in] src_y_start Start pixel index in the source bitmap, on y-axis (src_y_start is included)
* @param[in] src_x_end End pixel index in the source bitmap, on x-axis (src_x_end is not included)
* @param[in] src_y_end End pixel index in the source bitmap, on y-axis (src_y_end is not included)
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if parameters are invalid
* - ESP_ERR_NOT_SUPPORTED if this function is not supported by the panel
*/
esp_err_t esp_lcd_panel_draw_bitmap_2d(esp_lcd_panel_handle_t panel, int x_start, int y_start, int x_end, int y_end, const void *color_data,
size_t src_x_size, size_t src_y_size, int src_x_start, int src_y_start, int src_x_end, int src_y_end);
/**
* @brief Mirror the LCD panel on specific axis
*

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -59,6 +59,27 @@ struct esp_lcd_panel_t {
*/
esp_err_t (*draw_bitmap)(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
/**
* @brief Draw partial bitmap on LCD panel
*
* @param[in] panel LCD panel handle, which is created by other factory API like `esp_lcd_new_panel_st7789()`
* @param[in] x_start Start pixel index in the target frame buffer, on x-axis (x_start is included)
* @param[in] y_start Start pixel index in the target frame buffer, on y-axis (y_start is included)
* @param[in] x_end End pixel index in the target frame buffer, on x-axis (x_end is not included)
* @param[in] y_end End pixel index in the target frame buffer, on y-axis (y_end is not included)
* @param[in] src_data Source bitmap data
* @param[in] src_x_size Size of the source bitmap in the x-axis
* @param[in] src_y_size Size of the source bitmap in the y-axis
* @param[in] src_x_start Start pixel index in the source bitmap, on x-axis (src_x_start is included)
* @param[in] src_y_start Start pixel index in the source bitmap, on y-axis (src_y_start is included)
* @param[in] src_x_end End pixel index in the source bitmap, on x-axis (src_x_end is not included)
* @param[in] src_y_end End pixel index in the source bitmap, on y-axis (src_y_end is not included)
* @return
* - ESP_OK on success
*/
esp_err_t (*draw_bitmap_2d)(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end,
const void *src_data, size_t src_x_size, size_t src_y_size, int src_x_start, int src_y_start, int src_x_end, int src_y_end);
/**
* @brief Mirror the LCD panel on specific axis
*

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
*/
@@ -188,6 +188,13 @@ esp_err_t esp_async_fbcpy(esp_async_fbcpy_handle_t mcp, esp_async_fbcpy_trans_de
mcp->memcpy_done_cb = memcpy_done_cb;
mcp->cb_args = cb_args;
// write back the user's draw buffer, so that the DMA can see the correct data
// Note, the user src buffer may not be contiguous, writeback from the head to the tail anyways
size_t bits_per_pixel = color_hal_pixel_format_fourcc_get_bit_depth(transaction->pixel_format_fourcc_id);
size_t copy_head = (transaction->src_offset_x + transaction->src_offset_y * transaction->src_buffer_size_x) * bits_per_pixel / 8;
size_t copy_size = (transaction->copy_size_x + transaction->copy_size_y * transaction->src_buffer_size_x) * bits_per_pixel / 8;
ESP_RETURN_ON_ERROR(esp_cache_msync((void *)transaction->src_buffer + copy_head, copy_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED), TAG, "writeback draw buffer failed");
// mount the data to the DMA descriptor
async_memcpy_setup_dma2d_descriptor(mcp, transaction);

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -37,6 +37,16 @@ esp_err_t esp_lcd_panel_draw_bitmap(esp_lcd_panel_handle_t panel, int x_start, i
return panel->draw_bitmap(panel, x_start, y_start, x_end, y_end, color_data);
}
esp_err_t esp_lcd_panel_draw_bitmap_2d(esp_lcd_panel_handle_t panel, int x_start, int y_start, int x_end, int y_end,
const void *color_data, size_t src_x_size, size_t src_y_size, int src_x_start, int src_y_start, int src_x_end, int src_y_end)
{
ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "invalid panel handle");
ESP_RETURN_ON_FALSE(panel->draw_bitmap_2d, ESP_ERR_NOT_SUPPORTED, TAG, "draw_bitmap_2d is not supported by this panel");
ESP_RETURN_ON_FALSE((x_start < x_end) && (y_start < y_end), ESP_ERR_INVALID_ARG, TAG, "start position must be smaller than end position");
ESP_RETURN_ON_FALSE((src_x_start < src_x_end) && (src_y_start < src_y_end), ESP_ERR_INVALID_ARG, TAG, "source start position must be smaller than end position");
return panel->draw_bitmap_2d(panel, x_start, y_start, x_end, y_end, color_data, src_x_size, src_y_size, src_x_start, src_y_start, src_x_end, src_y_end);
}
esp_err_t esp_lcd_panel_mirror(esp_lcd_panel_handle_t panel, bool mirror_x, bool mirror_y)
{
ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "invalid panel handle");

View File

@@ -9,5 +9,5 @@ endif()
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES esp_lcd unity
PRIV_REQUIRES esp_lcd unity esp_driver_ppa
WHOLE_ARCHIVE)

View File

@@ -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_refresh_done = test_dpi_panel_count_in_callback,
};
TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, &callback_calls));

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
*/
@@ -16,6 +16,7 @@
#include "esp_attr.h"
#include "test_mipi_dsi_board.h"
#include "esp_lcd_ek79007.h"
#include "driver/ppa.h"
TEST_CASE("MIPI DSI Pattern Generator (EK79007)", "[mipi_dsi]")
{
@@ -86,7 +87,7 @@ TEST_CASE("MIPI DSI Pattern Generator (EK79007)", "[mipi_dsi]")
test_bsp_disable_dsi_phy_power();
}
#define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t))
#define TEST_IMG_SIZE (200 * 200 * sizeof(uint16_t))
TEST_CASE("MIPI DSI draw RGB bitmap (EK79007)", "[mipi_dsi]")
{
@@ -162,6 +163,294 @@ TEST_CASE("MIPI DSI draw RGB bitmap (EK79007)", "[mipi_dsi]")
test_bsp_disable_dsi_phy_power();
}
#if SOC_HAS(DMA2D)
TEST_CASE("MIPI DSI use DMA2D (EK79007)", "[mipi_dsi]")
{
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
esp_lcd_panel_io_handle_t mipi_dbi_io;
esp_lcd_panel_handle_t mipi_dpi_panel;
test_bsp_enable_dsi_phy_power();
uint8_t *img = malloc(TEST_IMG_SIZE);
TEST_ASSERT_NOT_NULL(img);
esp_lcd_dsi_bus_config_t bus_config = {
.bus_id = 0,
.num_data_lanes = 2,
.lane_bit_rate_mbps = 1000, // 1000 Mbps
};
TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
esp_lcd_dbi_io_config_t dbi_config = {
.virtual_channel = 0,
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
};
TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
esp_lcd_dpi_panel_config_t dpi_config = {
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = MIPI_DSI_DPI_CLK_MHZ,
.virtual_channel = 0,
.in_color_format = LCD_COLOR_FMT_RGB565,
.video_timing = {
.h_size = MIPI_DSI_LCD_H_RES,
.v_size = MIPI_DSI_LCD_V_RES,
.hsync_back_porch = MIPI_DSI_LCD_HBP,
.hsync_pulse_width = MIPI_DSI_LCD_HSYNC,
.hsync_front_porch = MIPI_DSI_LCD_HFP,
.vsync_back_porch = MIPI_DSI_LCD_VBP,
.vsync_pulse_width = MIPI_DSI_LCD_VSYNC,
.vsync_front_porch = MIPI_DSI_LCD_VFP,
},
};
ek79007_vendor_config_t vendor_config = {
.mipi_config = {
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
},
};
esp_lcd_panel_dev_config_t lcd_dev_config = {
.reset_gpio_num = -1,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16,
.vendor_config = &vendor_config,
};
TEST_ESP_OK(esp_lcd_new_panel_ek79007(mipi_dbi_io, &lcd_dev_config, &mipi_dpi_panel));
TEST_ESP_OK(esp_lcd_panel_reset(mipi_dpi_panel));
TEST_ESP_OK(esp_lcd_panel_init(mipi_dpi_panel));
printf("Draw bitmap 2D by CPU\r\n");
for (int i = 0; i < 100; i++) {
int x_start = rand() % (MIPI_DSI_LCD_H_RES - 100);
int y_start = rand() % (MIPI_DSI_LCD_V_RES - 100);
uint8_t color_byte = rand() & 0xFF;
memset(img, color_byte, TEST_IMG_SIZE / 2);
color_byte = rand() & 0xFF;
memset(img + TEST_IMG_SIZE / 2, color_byte, TEST_IMG_SIZE / 2);
esp_lcd_panel_draw_bitmap_2d(mipi_dpi_panel, x_start, y_start, x_start + 100, y_start + 100, img, 200, 200, 50, 50, 150, 150);
vTaskDelay(pdMS_TO_TICKS(10));
}
vTaskDelay(pdMS_TO_TICKS(1000));
printf("Add Built-in DMA2D draw bitmap hook\r\n");
TEST_ESP_OK(esp_lcd_dpi_panel_enable_dma2d(mipi_dpi_panel));
for (int i = 0; i < 100; i++) {
int x_start = rand() % (MIPI_DSI_LCD_H_RES - 100);
int y_start = rand() % (MIPI_DSI_LCD_V_RES - 100);
uint8_t color_byte = rand() & 0xFF;
memset(img, color_byte, TEST_IMG_SIZE / 2);
color_byte = rand() & 0xFF;
memset(img + TEST_IMG_SIZE / 2, color_byte, TEST_IMG_SIZE / 2);
esp_lcd_panel_draw_bitmap_2d(mipi_dpi_panel, x_start, y_start, x_start + 100, y_start + 100, img, 200, 200, 50, 50, 150, 150);
vTaskDelay(pdMS_TO_TICKS(10));
}
TEST_ESP_OK(esp_lcd_dpi_panel_disable_dma2d(mipi_dpi_panel));
vTaskDelay(pdMS_TO_TICKS(1000));
TEST_ESP_OK(esp_lcd_panel_del(mipi_dpi_panel));
TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io));
TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus));
free(img);
test_bsp_disable_dsi_phy_power();
}
#endif // SOC_HAS(DMA2D)
#if SOC_HAS(PPA)
typedef struct {
ppa_client_handle_t ppa_srm_handle;
esp_lcd_draw_bitmap_hook_data_t hook_data;
SemaphoreHandle_t draw_sem;
esp_lcd_panel_handle_t panel;
} test_dpi_panel_draw_bitmap_hook_ctx_t;
typedef struct {
uint32_t count;
SemaphoreHandle_t draw_sem;
} test_dpi_panel_color_trans_done_callback_ctx_t;
IRAM_ATTR static bool test_ppa_srm_trans_done_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *edata, void *user_ctx)
{
bool need_yield = false;
test_dpi_panel_draw_bitmap_hook_ctx_t *hook_ctx = (test_dpi_panel_draw_bitmap_hook_ctx_t *)user_ctx;
esp_lcd_draw_bitmap_hook_data_t *hook_data = &hook_ctx->hook_data;
if (hook_data->on_hook_end) {
if (hook_data->on_hook_end(hook_ctx->panel)) {
need_yield = true;
}
}
return need_yield;
}
static esp_err_t test_draw_bitmap_hook_ppa(esp_lcd_panel_handle_t panel, const esp_lcd_draw_bitmap_hook_data_t *hook_data, void *user_ctx)
{
test_dpi_panel_draw_bitmap_hook_ctx_t *hook_ctx = (test_dpi_panel_draw_bitmap_hook_ctx_t *)user_ctx;
ppa_client_handle_t ppa_srm_handle = hook_ctx->ppa_srm_handle;
memcpy(&hook_ctx->hook_data, hook_data, sizeof(esp_lcd_draw_bitmap_hook_data_t));
xSemaphoreTake(hook_ctx->draw_sem, portMAX_DELAY);
ppa_srm_oper_config_t srm_config = {
.in.buffer = hook_data->src_data,
.in.pic_w = hook_data->src_x_size,
.in.pic_h = hook_data->src_y_size,
.in.block_w = hook_data->src_x_end - hook_data->src_x_start,
.in.block_h = hook_data->src_y_end - hook_data->src_y_start,
.in.block_offset_x = hook_data->src_x_start,
.in.block_offset_y = hook_data->src_y_start,
.in.srm_cm = PPA_SRM_COLOR_MODE_RGB565,
.out.buffer = hook_data->dst_data,
.out.buffer_size = hook_data->dst_x_size * hook_data->dst_y_size * hook_data->bits_per_pixel / 8,
.out.pic_w = hook_data->dst_x_size,
.out.pic_h = hook_data->dst_y_size,
.out.block_offset_x = hook_data->dst_x_start,
.out.block_offset_y = hook_data->dst_y_start,
.out.srm_cm = PPA_SRM_COLOR_MODE_RGB565,
.rotation_angle = PPA_SRM_ROTATION_ANGLE_90,
.scale_x = 0.5,
.scale_y = 0.5,
.rgb_swap = 0,
.byte_swap = 0,
.mode = PPA_TRANS_MODE_NON_BLOCKING,
.user_data = hook_ctx,
};
ppa_event_callbacks_t ppa_srm_event_callbacks = {
.on_trans_done = test_ppa_srm_trans_done_callback,
};
TEST_ESP_OK(ppa_client_register_event_callbacks(ppa_srm_handle, &ppa_srm_event_callbacks));
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_srm_handle, &srm_config));
return ESP_OK;
}
IRAM_ATTR static bool test_dpi_panel_color_trans_done_count_callback(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
{
BaseType_t task_woken = pdFALSE;
test_dpi_panel_color_trans_done_callback_ctx_t *color_trans_done_ctx = (test_dpi_panel_color_trans_done_callback_ctx_t *)user_ctx;
color_trans_done_ctx->count++;
xSemaphoreGiveFromISR(color_trans_done_ctx->draw_sem, &task_woken);
return task_woken == pdTRUE;
}
TEST_CASE("MIPI DSI use PPA (EK79007)", "[mipi_dsi]")
{
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
esp_lcd_panel_io_handle_t mipi_dbi_io;
esp_lcd_panel_handle_t mipi_dpi_panel;
test_bsp_enable_dsi_phy_power();
uint8_t *img = malloc(TEST_IMG_SIZE);
TEST_ASSERT_NOT_NULL(img);
esp_lcd_dsi_bus_config_t bus_config = {
.bus_id = 0,
.num_data_lanes = 2,
.lane_bit_rate_mbps = 1000, // 1000 Mbps
};
TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
esp_lcd_dbi_io_config_t dbi_config = {
.virtual_channel = 0,
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
};
TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
esp_lcd_dpi_panel_config_t dpi_config = {
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = MIPI_DSI_DPI_CLK_MHZ,
.virtual_channel = 0,
.in_color_format = LCD_COLOR_FMT_RGB565,
.video_timing = {
.h_size = MIPI_DSI_LCD_H_RES,
.v_size = MIPI_DSI_LCD_V_RES,
.hsync_back_porch = MIPI_DSI_LCD_HBP,
.hsync_pulse_width = MIPI_DSI_LCD_HSYNC,
.hsync_front_porch = MIPI_DSI_LCD_HFP,
.vsync_back_porch = MIPI_DSI_LCD_VBP,
.vsync_pulse_width = MIPI_DSI_LCD_VSYNC,
.vsync_front_porch = MIPI_DSI_LCD_VFP,
},
};
ek79007_vendor_config_t vendor_config = {
.mipi_config = {
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
},
};
esp_lcd_panel_dev_config_t lcd_dev_config = {
.reset_gpio_num = -1,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16,
.vendor_config = &vendor_config,
};
TEST_ESP_OK(esp_lcd_new_panel_ek79007(mipi_dbi_io, &lcd_dev_config, &mipi_dpi_panel));
TEST_ESP_OK(esp_lcd_panel_reset(mipi_dpi_panel));
TEST_ESP_OK(esp_lcd_panel_init(mipi_dpi_panel));
SemaphoreHandle_t draw_sem = xSemaphoreCreateBinaryWithCaps(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
assert(draw_sem);
xSemaphoreGive(draw_sem);
// use PPA to scale and rotate the image in draw bitmap hook
ppa_client_handle_t ppa_srm_handle = NULL;
ppa_client_config_t ppa_srm_config = {
.oper_type = PPA_OPERATION_SRM,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_srm_config, &ppa_srm_handle));
esp_lcd_dpi_panel_event_callbacks_t cbs = {
.on_color_trans_done = test_dpi_panel_color_trans_done_count_callback,
};
test_dpi_panel_color_trans_done_callback_ctx_t color_trans_done_ctx = {
.draw_sem = draw_sem,
.count = 0,
};
TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, &color_trans_done_ctx));
printf("Add PPA draw bitmap hook\r\n");
esp_lcd_panel_hooks_t hooks = {
.draw_bitmap_hook = test_draw_bitmap_hook_ppa,
};
test_dpi_panel_draw_bitmap_hook_ctx_t hook_ctx = {
.draw_sem = draw_sem,
.ppa_srm_handle = ppa_srm_handle,
.panel = mipi_dpi_panel,
};
TEST_ESP_OK(esp_lcd_dpi_panel_register_hooks(mipi_dpi_panel, &hooks, &hook_ctx));
for (int i = 0; i < 100; i++) {
int x_start = rand() % (MIPI_DSI_LCD_H_RES - 100);
int y_start = rand() % (MIPI_DSI_LCD_V_RES - 100);
uint8_t color_byte = rand() & 0xFF;
memset(img, color_byte, TEST_IMG_SIZE / 2);
color_byte = rand() & 0xFF;
memset(img + TEST_IMG_SIZE / 2, color_byte, TEST_IMG_SIZE / 2);
esp_lcd_panel_draw_bitmap_2d(mipi_dpi_panel, x_start, y_start, x_start + 50, y_start + 50,
img, 200, 200, 0, 0, 200, 200);
vTaskDelay(pdMS_TO_TICKS(10));
}
TEST_ASSERT_EQUAL_INT(100, color_trans_done_ctx.count);
hooks.draw_bitmap_hook = NULL;
TEST_ESP_OK(esp_lcd_dpi_panel_register_hooks(mipi_dpi_panel, &hooks, NULL));
TEST_ESP_OK(ppa_unregister_client(ppa_srm_handle));
TEST_ESP_OK(esp_lcd_panel_del(mipi_dpi_panel));
TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io));
TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus));
vSemaphoreDelete(draw_sem);
free(img);
test_bsp_disable_dsi_phy_power();
}
#endif // SOC_HAS(PPA)
TEST_CASE("MIPI DSI use multiple frame buffers (EK79007)", "[mipi_dsi]")
{
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;

View File

@@ -61,7 +61,6 @@ MIPI DSI Interfaced LCD
- :cpp:member:`esp_lcd_dpi_panel_config_t::dpi_clock_freq_mhz` sets the DPI clock frequency in MHz. Higher pixel clock frequency results in higher refresh rate, but may cause flickering if the DMA bandwidth is not sufficient or the LCD controller chip does not support high pixel clock frequency.
- :cpp:member:`esp_lcd_dpi_panel_config_t::in_color_format` sets the pixel format of the input pixel data. The available pixel formats are listed in :cpp:type:`lcd_color_format_t`. We usually use **RGB888** for MIPI LCD to get the best color depth.
- :cpp:member:`esp_lcd_dpi_panel_config_t::video_timing` sets the LCD panel specific timing parameters. All required parameters are listed in the :cpp:type:`esp_lcd_video_timing_t`, including the LCD resolution and blanking porches. Please fill them according to the datasheet of your LCD.
- :cpp:member:`esp_lcd_dpi_panel_config_t::extra_dpi_panel_flags::use_dma2d` sets whether to use the 2D DMA peripheral to copy the user data to the frame buffer, asynchronously.
.. code-block:: c
@@ -81,11 +80,27 @@ MIPI DSI Interfaced LCD
.vsync_pulse_width = EXAMPLE_MIPI_DSI_LCD_VSYNC,
.vsync_front_porch = EXAMPLE_MIPI_DSI_LCD_VFP,
},
.flags.use_dma2d = true,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_dpi(mipi_dsi_bus, &dpi_config, &mipi_dpi_panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(mipi_dpi_panel));
#. Configure draw bitmap hook function (optional)
If you want to use DMA2D to implement draw bitmap, the driver has already implemented the DMA2D draw bitmap hook function, you only need to call :func:`esp_lcd_dpi_panel_enable_dma2d` to enable it.
.. code-block:: c
ESP_ERROR_CHECK(esp_lcd_dpi_panel_enable_dma2d(mipi_dpi_panel));
If you need more advanced applications, you can add a custom hook for draw bitmap, such as using PPA to implement rotation, scaling, etc.
.. code-block:: c
esp_lcd_panel_hooks_t hooks = {
.draw_bitmap_hook = custom_draw_bitmap_hook,
};
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_hooks(mipi_dpi_panel, &hooks, &user_ctx));
Power Supply for MIPI DPHY
--------------------------

View File

@@ -53,6 +53,7 @@ LCD Data Panel Operations
* :cpp:func:`esp_lcd_panel_reset` can reset the LCD data panel.
* :cpp:func:`esp_lcd_panel_init` performs a basic initialization of the data panel.
* :cpp:func:`esp_lcd_panel_draw_bitmap` is the function which does the magic to flush the user draw buffer to the LCD screen, where the target draw window is configurable. Please note, this function expects that the draw buffer is a 1-D array and there's no stride in between each lines.
* :cpp:func:`esp_lcd_panel_draw_bitmap_2d` is the function which does the magic to flush the user draw buffer to the LCD screen, where the source and target draw windows are configurable. Please note, the draw buffer can be a 2-D array or a 1-D array with no stride in between each lines.
.. _steps_add_manufacture_init:

View File

@@ -266,6 +266,7 @@ LCD
- ``esp_lcd_dpi_panel_set_color_conversion`` function is replaced by :cpp:func:`esp_lcd_dpi_panel_set_yuv_conversion` to set YUV to RGB color conversion profile.
- :cpp:func:`esp_lcd_rgb_panel_set_yuv_conversion` function has a different signature. The ``esp_lcd_yuv_conv_config_t`` configuration type is now replaced by :cpp:type:`esp_lcd_color_conv_yuv_config_t`.
- The NT35510 LCD device driver has been moved out of ESP-IDF and is now hosted in the `ESP Component Registry <https://components.espressif.com/components/espressif/esp_lcd_nt35510/versions/1.0.0/readme>`__. If your project uses the NT35510 driver, you can add it to your project by running ``idf.py add-dependency "espressif/esp_lcd_nt35510"``.
- The ``use_dma2d`` member in the :cpp:type:`esp_lcd_dpi_panel_config_t` has been removed. Please use the :func:`esp_lcd_dpi_panel_enable_dma2d` function to enable DMA2D for the DPI panel. When not using DMA2D, the binary file size can be reduced by around 10KB.
SPI
---

View File

@@ -61,7 +61,6 @@ MIPI DSI 接口的 LCD
- :cpp:member:`esp_lcd_dpi_panel_config_t::dpi_clock_freq_mhz` 设置 DPI 时钟频率 (MHz)。像素时钟频率越高,刷新率越高,但如果 DMA 带宽不足或 LCD 控制器芯片不支持高像素时钟频率,则可能会导致闪烁。
- :cpp:member:`esp_lcd_dpi_panel_config_t::in_color_format` 设置输入的像素数据的格式。可用的像素格式见 :cpp:type:`lcd_color_format_t`。MIPI LCD 通常使用 **RGB888** 来获得最佳色彩深度。
- :cpp:member:`esp_lcd_dpi_panel_config_t::video_timing` 设置 LCD 面板的特定时序参数。包括 LCD 分辨率和消隐间隔在内的必要参数列表见 :cpp:type:`esp_lcd_video_timing_t`,请依据 LCD 技术规格书填写参数。
- :cpp:member:`esp_lcd_dpi_panel_config_t::extra_dpi_panel_flags::use_dma2d` 设置是否用 2D DMA 将用户数据异步复制到帧 buffer 中。
.. code-block:: c
@@ -81,11 +80,27 @@ MIPI DSI 接口的 LCD
.vsync_pulse_width = EXAMPLE_MIPI_DSI_LCD_VSYNC,
.vsync_front_porch = EXAMPLE_MIPI_DSI_LCD_VFP,
},
.flags.use_dma2d = true,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_dpi(mipi_dsi_bus, &dpi_config, &mipi_dpi_panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(mipi_dpi_panel));
#. 配置绘制位图钩子函数(可选)
若想使用 DMA2D 实现绘制位图,驱动程序内部已实现 DMA2D 绘制位图的钩子函数,用户只需调用 :func:`esp_lcd_dpi_panel_enable_dma2d` 即可。
.. code-block:: c
ESP_ERROR_CHECK(esp_lcd_dpi_panel_enable_dma2d(mipi_dpi_panel));
若需更高级的应用,用户可为绘制位图添加自定义钩子,例如通过 PPA 实现旋转、缩放等操作。
.. code-block:: c
esp_lcd_panel_hooks_t hooks = {
.draw_bitmap_hook = custom_draw_bitmap_hook,
};
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_hooks(mipi_dpi_panel, &hooks, &user_ctx));
关于 MIPI DPHY 的供电
---------------------

View File

@@ -53,6 +53,7 @@ LCD 数据面板操作
* :cpp:func:`esp_lcd_panel_reset` 可以重置 LCD 数据面板。
* :cpp:func:`esp_lcd_panel_init` 执行基本的数据面板初始化。
* :cpp:func:`esp_lcd_panel_draw_bitmap` 可以将绘制 buffer 刷新到 LCD 屏幕上,其中目标绘制窗口是可配置的。请注意,使用该函数需要确保绘制 buffer 是一维数组,且每行像素数据之间没有跨距。
* :cpp:func:`esp_lcd_panel_draw_bitmap_2d` 可以绘制部分位图到 LCD 屏幕上,源窗口和目标绘制窗口都是可配置的。请注意,此时绘制 buffer 可以是二维数组,也可以是每行像素数据之间没有跨距的一维数组。
.. _steps_add_manufacture_init:

View File

@@ -266,6 +266,7 @@ LCD
- ``esp_lcd_dpi_panel_set_color_conversion`` 函数已被 :cpp:func:`esp_lcd_dpi_panel_set_yuv_conversion` 取代,用于设置 YUV 到 RGB 的色彩转换配置。
- :cpp:func:`esp_lcd_rgb_panel_set_yuv_conversion` 函数的签名已改变。原先使用的 ``esp_lcd_yuv_conv_config_t`` 配置类型现已被 :cpp:type:`esp_lcd_color_conv_yuv_config_t` 取代。
- NT35510 LCD 设备驱动已经从 ESP-IDF 中移动到外部仓库,并且托管在了 `ESP Component Registry <https://components.espressif.com/components/espressif/esp_lcd_nt35510/versions/1.0.0/readme>`__ 上。如果你的项目使用到了 NT35510 驱动,你可以通过运行 ``idf.py add-dependency "espressif/esp_lcd_nt35510"`` 将它添加到你的项目中。
- :cpp:type:`esp_lcd_dpi_panel_config_t` 结构体中的 ``use_dma2d`` 成员已被移除。请使用 :func:`esp_lcd_dpi_panel_enable_dma2d` 函数来启用 DMA2D 功能。当不使用 DMA2D 时,可以减小 10KB 左右的二进制文件大小。
SPI
---

View File

@@ -69,9 +69,6 @@ void example_dsi_resource_alloc(const example_dsi_alloc_config_t *config,
.vsync_pulse_width = EXAMPLE_MIPI_DSI_IMAGE_VSYNC,
.vsync_front_porch = EXAMPLE_MIPI_DSI_IMAGE_VFP,
},
#if CONFIG_EXAMPLE_MIPI_DSI_DISP_USE_DMA2D
.flags.use_dma2d = true,
#endif
};
#if CONFIG_EXAMPLE_LCD_PATTERN_ILI9881C
@@ -105,6 +102,12 @@ void example_dsi_resource_alloc(const example_dsi_alloc_config_t *config,
ESP_ERROR_CHECK(esp_lcd_new_panel_ek79007(*mipi_dbi_io, &lcd_dev_config, mipi_dpi_panel));
#endif
#if CONFIG_EXAMPLE_MIPI_DSI_DISP_USE_DMA2D
// use DMA2D to copy draw buffer into frame buffer
ESP_ERROR_CHECK(esp_lcd_dpi_panel_enable_dma2d(*mipi_dpi_panel));
ESP_LOGI(TAG, "DPI panel added DMA2D draw bitmap hook");
#endif
// Get frame buffer addresses
if (fb0 != NULL) {
if (num_fbs == 2) {
@@ -122,6 +125,7 @@ void example_dsi_resource_alloc(const example_dsi_alloc_config_t *config,
ESP_LOGD(TAG, "Frame buffer[0] allocated at: %p", *fb0);
}
}
}
void example_dpi_panel_reset(esp_lcd_panel_handle_t mipi_dpi_panel)

View File

@@ -228,9 +228,6 @@ void app_main(void)
.vsync_pulse_width = EXAMPLE_MIPI_DSI_LCD_VSYNC,
.vsync_front_porch = EXAMPLE_MIPI_DSI_LCD_VFP,
},
#if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME
.flags.use_dma2d = true, // use DMA2D to copy draw buffer into frame buffer
#endif
};
#if CONFIG_EXAMPLE_LCD_USE_ILI9881C
@@ -264,6 +261,12 @@ void app_main(void)
ESP_ERROR_CHECK(esp_lcd_new_panel_ek79007(mipi_dbi_io, &lcd_dev_config, &mipi_dpi_panel));
#endif
#if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME
// use DMA2D to copy draw buffer into frame buffer
ESP_ERROR_CHECK(esp_lcd_dpi_panel_enable_dma2d(mipi_dpi_panel));
ESP_LOGI(TAG, "DPI panel added DMA2D draw bitmap hook");
#endif
ESP_ERROR_CHECK(esp_lcd_panel_reset(mipi_dpi_panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(mipi_dpi_panel));
// turn on backlight

View File

@@ -742,6 +742,10 @@
re_variables: ['w5500']
hint_variables: ['W5500', 'w5500']
-
re: "error: 'struct extra_dpi_panel_flags' has no member named 'use_dma2d'"
hint: "The `use_dma2d` member has been removed in ESP-IDF v6.0. Please use the `esp_lcd_dpi_panel_enable_dma2d()` function to enable DMA2D for the DPI panel."
-
re: "error: 'MALLOC_CAP_EXEC' undeclared \\(first use in this function\\)"
hint: "MALLOC_CAP_EXEC capability cannot be used if CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT or CONFIG_ESP_SYSTEM_MEMPROT_FEATURE is enabled. \nFor further information about those configurations, run 'idf.py docs -sp api-reference/kconfig-reference.html#memory-protection'"