diff --git a/components/esp_driver_dma/CMakeLists.txt b/components/esp_driver_dma/CMakeLists.txt index 3d520c3e9f4..46a442371ba 100644 --- a/components/esp_driver_dma/CMakeLists.txt +++ b/components/esp_driver_dma/CMakeLists.txt @@ -35,7 +35,8 @@ if(CONFIG_SOC_DW_GDMA_SUPPORTED) endif() if(CONFIG_SOC_DMA2D_SUPPORTED) - list(APPEND srcs "src/dma2d.c" "src/esp_async_color_convert.c" "src/async_color_convert_dma2d.c") + list(APPEND srcs "src/dma2d.c" "src/esp_async_color_convert.c" "src/async_color_convert_dma2d.c" + "src/async_memcpy_dma2d.c") endif() idf_component_register(SRCS ${srcs} diff --git a/components/esp_driver_dma/include/esp_private/async_memcpy_dma2d.h b/components/esp_driver_dma/include/esp_private/async_memcpy_dma2d.h new file mode 100644 index 00000000000..03e024f2651 --- /dev/null +++ b/components/esp_driver_dma/include/esp_private/async_memcpy_dma2d.h @@ -0,0 +1,150 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// DO NOT USE THESE APIS IN ANY APPLICATIONS +// DMA2D async 2D memcpy is a private helper built on async color convert +// for same-format window copy (e.g. LCD frame buffer blit). + +#pragma once + +#include +#include +#include +#include "esp_err.h" +#include "hal/color_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Opaque handle of DMA2D async 2D memcpy driver instance + * + * @note Internally this is a thin wrapper over async color convert with + * source/destination formats forced to the same value (copy-only path). + */ +typedef struct async_memcpy_dma2d_t *async_memcpy_dma2d_handle_t; + +/** + * @brief DMA2D async 2D memcpy event data + */ +typedef struct { +} async_memcpy_dma2d_event_data_t; + +/** + * @brief DMA2D async 2D memcpy ISR callback + * + * @note This callback runs in ISR context. + * + * @param[in] mcp Driver handle that produced this event + * @param[in] edata Event data for the completed request + * @param[in] cb_args User context passed to :cpp:func:`esp_async_memcpy_dma2d` + * + * @return + * - true: a higher-priority task was woken and a yield is requested + * - false: no yield request + */ +typedef bool (*async_memcpy_dma2d_isr_cb_t)(async_memcpy_dma2d_handle_t mcp, + async_memcpy_dma2d_event_data_t *edata, + void *cb_args); + +/** + * @brief DMA2D async 2D memcpy driver configuration + */ +typedef struct { + uint32_t backlog; /*!< Number of in-flight/pending requests. 0 means driver default. */ + size_t dma_burst_size; /*!< DMA burst length in bytes. 0 means driver default. */ + uint32_t intr_priority; /*!< Interrupt priority. 0 means default low/medium priority. */ +} async_memcpy_dma2d_config_t; + +/** + * @brief DMA2D async 2D memcpy transaction descriptor + * + * Coordinates and size are in pixels. + * + * The source and destination windows are: + * - source: [src_x, src_x + copy_width) x [src_y, src_y + copy_height) + * - destination: [dst_x, dst_x + copy_width) x [dst_y, dst_y + copy_height) + * + * Both windows must be fully inside their corresponding image bounds. + * + * This API only performs same-format 2D window copy (no color conversion). + */ +typedef struct { + const void *src_buffer; /*!< Source picture base address */ + uint32_t src_stride; /*!< Source picture row stride in pixels */ + uint32_t src_height; /*!< Source picture height in pixels */ + uint32_t src_x; /*!< Source window x offset in pixels */ + uint32_t src_y; /*!< Source window y offset in pixels */ + + void *dst_buffer; /*!< Destination picture base address */ + uint32_t dst_stride; /*!< Destination picture row stride in pixels */ + uint32_t dst_height; /*!< Destination picture height in pixels */ + uint32_t dst_x; /*!< Destination window x offset in pixels */ + uint32_t dst_y; /*!< Destination window y offset in pixels */ + + uint32_t copy_width; /*!< Copy window width in pixels */ + uint32_t copy_height; /*!< Copy window height in pixels */ + + esp_color_fourcc_t pixel_format; /*!< Pixel format of both source and destination */ +} async_memcpy_dma2d_trans_desc_t; + +/** + * @brief Install DMA2D async 2D memcpy driver + * + * This is a thin wrapper over :cpp:func:`esp_async_color_convert_install_dma2d`. + * 2D window copy is implemented as a same-format color-convert request. + * + * @param[in] config Driver configuration + * @param[out] ret_hdl Returned driver handle + * + * @return + * - ESP_OK: Driver installed successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_NO_MEM: Out of memory + * - ESP_ERR_NOT_FOUND: Required DMA2D resource is unavailable + * - others: Error from lower-level driver + */ +esp_err_t esp_async_memcpy_install_dma2d(const async_memcpy_dma2d_config_t *config, + async_memcpy_dma2d_handle_t *ret_hdl); + +/** + * @brief Uninstall DMA2D async 2D memcpy driver + * + * @param[in] mcp Driver handle returned by :cpp:func:`esp_async_memcpy_install_dma2d` + * + * @return + * - ESP_OK: Driver uninstalled successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: There are pending requests in the queue + */ +esp_err_t esp_async_memcpy_uninstall_dma2d(async_memcpy_dma2d_handle_t mcp); + +/** + * @brief Submit an asynchronous 2D memory copy request via DMA2D + * + * The request is enqueued and completed later in DMA2D interrupt context. + * The callback can be NULL if no completion notification is needed. + * + * @param[in] mcp Driver handle returned by :cpp:func:`esp_async_memcpy_install_dma2d` + * @param[in] trans 2D memcpy transaction descriptor + * @param[in] cb_isr ISR callback invoked on copy completion, can be NULL + * @param[in] cb_args User context passed to @p cb_isr + * + * @return + * - ESP_OK: Request accepted + * - ESP_ERR_INVALID_ARG: Invalid argument or invalid request fields + * - ESP_ERR_INVALID_STATE: No free internal transaction slot (queue full) + * - others: Error from lower-level driver + */ +esp_err_t esp_async_memcpy_dma2d(async_memcpy_dma2d_handle_t mcp, + const async_memcpy_dma2d_trans_desc_t *trans, + async_memcpy_dma2d_isr_cb_t cb_isr, + void *cb_args); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_dma/src/async_memcpy_dma2d.c b/components/esp_driver_dma/src/async_memcpy_dma2d.c new file mode 100644 index 00000000000..da24d8087da --- /dev/null +++ b/components/esp_driver_dma/src/async_memcpy_dma2d.c @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_check.h" +#include "esp_async_color_convert.h" +#include "esp_private/async_memcpy_dma2d.h" + +ESP_LOG_ATTR_TAG(TAG, "async_memcpy_dma2d"); + +/* + * DMA2D 2D window copy is a simplified same-format color-convert request. + * Keep this file as a thin naming/API wrapper so LCD and other drivers can + * express "2D memcpy" without calling color-convert APIs directly. + */ + +esp_err_t esp_async_memcpy_install_dma2d(const async_memcpy_dma2d_config_t *config, + async_memcpy_dma2d_handle_t *ret_hdl) +{ + ESP_RETURN_ON_FALSE(config && ret_hdl, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + async_color_convert_config_t conv_config = { + .backlog = config->backlog, + .dma_burst_size = config->dma_burst_size, + .intr_priority = config->intr_priority, + }; + return esp_async_color_convert_install_dma2d(&conv_config, (async_color_convert_handle_t *)ret_hdl); +} + +esp_err_t esp_async_memcpy_uninstall_dma2d(async_memcpy_dma2d_handle_t mcp) +{ + return esp_async_color_convert_uninstall((async_color_convert_handle_t)mcp); +} + +esp_err_t esp_async_memcpy_dma2d(async_memcpy_dma2d_handle_t mcp, + const async_memcpy_dma2d_trans_desc_t *trans, + async_memcpy_dma2d_isr_cb_t cb_isr, + void *cb_args) +{ + ESP_RETURN_ON_FALSE(mcp && trans, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + async_color_convert_request_t request = { + .src_buffer = trans->src_buffer, + .dst_buffer = trans->dst_buffer, + .src_stride = trans->src_stride, + .src_height = trans->src_height, + .dst_stride = trans->dst_stride, + .dst_height = trans->dst_height, + .src_x = trans->src_x, + .src_y = trans->src_y, + .dst_x = trans->dst_x, + .dst_y = trans->dst_y, + .copy_width = trans->copy_width, + .copy_height = trans->copy_height, + // Same source/destination format disables CSC and performs 2D window copy only. + .src_color_format = trans->pixel_format, + .dst_color_format = trans->pixel_format, + }; + + // Callback prototypes are ABI-compatible: opaque handle pointer + empty event struct + user args. + return esp_async_color_convert((async_color_convert_handle_t)mcp, + &request, + (async_color_convert_isr_cb_t)cb_isr, + cb_args); +} diff --git a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c index a9ba69dee4b..b9e901fca38 100644 --- a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c +++ b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c @@ -6,12 +6,12 @@ #include #include "esp_lcd_panel_interface.h" #include "esp_lcd_mipi_dsi.h" -#include "esp_async_color_convert.h" #include "esp_intr_alloc.h" #include "esp_clk_tree.h" #include "esp_cache.h" #include "mipi_dsi_priv.h" #include "esp_memory_utils.h" +#include "esp_private/async_memcpy_dma2d.h" #include "esp_private/dw_gdma.h" #include "hal/color_hal.h" @@ -44,7 +44,7 @@ struct esp_lcd_dpi_panel_t { 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 - async_color_convert_handle_t fbcpy_handle; // Async color convert handle used for same-format DMA2D frame buffer copy + async_memcpy_dma2d_handle_t fbcpy_handle; // DMA2D async 2D memcpy handle used for same-format frame buffer copy #if CONFIG_PM_ENABLE esp_pm_lock_handle_t pm_lock; // Power management lock @@ -64,11 +64,11 @@ static bool dpi_panel_draw_bitmap_hook_end(esp_lcd_panel_t *panel) return false; } -static bool async_fbcpy_done_cb(async_color_convert_handle_t conv_hdl, async_color_convert_event_data_t *event, void *cb_args) +static bool async_fbcpy_done_cb(async_memcpy_dma2d_handle_t mcp, async_memcpy_dma2d_event_data_t *event, void *cb_args) { bool need_yield = false; esp_lcd_dpi_panel_t *dpi_panel = (esp_lcd_dpi_panel_t *)cb_args; - (void)conv_hdl; + (void)mcp; (void)event; if (dpi_panel->on_hook_end) { @@ -470,7 +470,9 @@ static esp_err_t dpi_panel_draw_bitmap_dma2d_hook(esp_lcd_panel_t *panel, const esp_lcd_dpi_panel_t *dpi_panel = __containerof(panel, esp_lcd_dpi_panel_t, base); (void)hook_ctx; - async_color_convert_request_t fbcpy_trans_config = { + // Built-in DMA2D draw hook only needs same-format 2D window copy. + // Use the private DMA2D async memcpy wrapper (thin layer over color convert). + async_memcpy_dma2d_trans_desc_t fbcpy_trans_config = { .src_buffer = hook_data->src_data, .dst_buffer = hook_data->dst_data, .src_stride = hook_data->src_x_size, @@ -483,16 +485,13 @@ static esp_err_t dpi_panel_draw_bitmap_dma2d_hook(esp_lcd_panel_t *panel, const .dst_y = hook_data->dst_y_start, .copy_width = hook_data->src_x_end - hook_data->src_x_start, .copy_height = hook_data->src_y_end - hook_data->src_y_start, - // For this DMA2D hook we only do window copy from draw buffer to frame buffer. - // Source and destination color formats are intentionally set to the same value to disable CSC. - .src_color_format = dpi_panel->in_color_format, - .dst_color_format = dpi_panel->in_color_format, + .pixel_format = dpi_panel->in_color_format, }; - // The async color convert backend owns source/destination cache sync for the - // DMA2D copy path, so the LCD driver should not perform extra cache sync here. + // The DMA2D async 2D memcpy backend owns source/destination cache sync for the + // copy path, so the LCD driver should not perform extra cache sync here. // Save the completion callback and invoke it when the async frame buffer copy finishes. dpi_panel->on_hook_end = hook_data->on_hook_end; - ESP_RETURN_ON_ERROR(esp_async_color_convert(dpi_panel->fbcpy_handle, &fbcpy_trans_config, async_fbcpy_done_cb, dpi_panel), TAG, "async frame buffer copy failed"); + ESP_RETURN_ON_ERROR(esp_async_memcpy_dma2d(dpi_panel->fbcpy_handle, &fbcpy_trans_config, async_fbcpy_done_cb, dpi_panel), TAG, "async frame buffer copy failed"); return ESP_OK; } @@ -517,12 +516,12 @@ esp_err_t esp_lcd_dpi_panel_enable_dma2d(esp_lcd_panel_handle_t panel) // 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 the async color convert backend used by the built-in DMA2D copy hook. + // Initialize the DMA2D async 2D memcpy backend used by the built-in copy hook. // Use its default backlog to queue multiple frame buffer copy requests. - async_color_convert_config_t fbcpy_config = { + async_memcpy_dma2d_config_t fbcpy_config = { .dma_burst_size = 128, // for better performance }; - ESP_RETURN_ON_ERROR(esp_async_color_convert_install_dma2d(&fbcpy_config, &dpi_panel->fbcpy_handle), TAG, "install async frame buffer copy backend failed"); + ESP_RETURN_ON_ERROR(esp_async_memcpy_install_dma2d(&fbcpy_config, &dpi_panel->fbcpy_handle), TAG, "install async frame buffer copy backend failed"); // Register the DMA2D draw bitmap hook esp_lcd_panel_hooks_t hooks = { @@ -534,7 +533,7 @@ esp_err_t esp_lcd_dpi_panel_enable_dma2d(esp_lcd_panel_handle_t panel) err: if (dpi_panel->fbcpy_handle) { - esp_async_color_convert_uninstall(dpi_panel->fbcpy_handle); + esp_async_memcpy_uninstall_dma2d(dpi_panel->fbcpy_handle); dpi_panel->fbcpy_handle = NULL; } dpi_panel->on_hook_end = NULL; @@ -554,13 +553,7 @@ esp_err_t esp_lcd_dpi_panel_disable_dma2d(esp_lcd_panel_handle_t panel) .draw_bitmap_hook = NULL, }; ESP_RETURN_ON_ERROR(esp_lcd_dpi_panel_register_hooks(panel, &hooks, NULL), TAG, "unregister DMA2D draw bitmap hook failed"); - esp_err_t ret = esp_async_color_convert_uninstall(dpi_panel->fbcpy_handle); - if (ret != ESP_OK) { - // Restore the hook so draw_bitmap_hook and fbcpy_handle stay consistent on failure - hooks.draw_bitmap_hook = dpi_panel_draw_bitmap_dma2d_hook; - esp_lcd_dpi_panel_register_hooks(panel, &hooks, NULL); - ESP_RETURN_ON_ERROR(ret, TAG, "uninstall DMA2D failed"); - } + ESP_RETURN_ON_ERROR(esp_async_memcpy_uninstall_dma2d(dpi_panel->fbcpy_handle), TAG, "uninstall DMA2D failed"); dpi_panel->fbcpy_handle = NULL; dpi_panel->on_hook_end = NULL; @@ -651,7 +644,7 @@ static esp_err_t dpi_panel_draw_bitmap_2d(esp_lcd_panel_t *panel, int x_start, i ESP_LOGV(TAG, "copy draw buffer by draw bitmap hook"); // Note, whether the previous draw operation is finished should be ensured by the hook. // For the built-in DMA2D hook, cache maintenance of the source and destination - // buffers is handled inside the async color convert driver. + // buffers is handled inside the DMA2D async 2D memcpy driver. esp_lcd_draw_bitmap_hook_data_t hook_data = { .dst_data = frame_buffer, diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index 283a6d62c56..cfe03db34be 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -49,7 +49,7 @@ #include "hal/color_hal.h" #include "rgb_lcd_rotation_sw.h" #include "esp_private/sleep_retention.h" -#include "esp_async_color_convert.h" +#include "esp_private/async_memcpy_dma2d.h" #if SOC_HAS(AXI_GDMA) #include "hal/axi_dma_ll.h" @@ -174,7 +174,7 @@ struct esp_rgb_panel_t { 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 - async_color_convert_handle_t fbcpy_handle; // Async color convert handle used for same-format DMA2D frame buffer copy + async_memcpy_dma2d_handle_t fbcpy_handle; // DMA2D async 2D memcpy handle used for same-format frame buffer copy }; static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, const esp_lcd_rgb_panel_config_t *panel_config) @@ -708,11 +708,11 @@ static bool rgb_panel_draw_bitmap_hook_end(esp_lcd_panel_t *panel) } #if SOC_HAS(DMA2D) -static bool async_fbcpy_done_cb(async_color_convert_handle_t conv_hdl, async_color_convert_event_data_t *event, void *cb_args) +static bool async_fbcpy_done_cb(async_memcpy_dma2d_handle_t mcp, async_memcpy_dma2d_event_data_t *event, void *cb_args) { bool need_yield = false; esp_rgb_panel_t *rgb_panel = (esp_rgb_panel_t *)cb_args; - (void)conv_hdl; + (void)mcp; (void)event; if (rgb_panel->on_hook_end) { @@ -800,7 +800,7 @@ static esp_err_t rgb_panel_draw_bitmap_2d(esp_lcd_panel_t *panel, int x_start, i ESP_LOGV(TAG, "copy draw buffer by draw bitmap hook"); // Note, whether the previous draw operation is finished should be ensured by the hook. // For the built-in DMA2D hook, cache maintenance of the source and destination - // buffers is handled inside the async color convert driver. + // buffers is handled inside the DMA2D async 2D memcpy driver. esp_lcd_draw_bitmap_hook_data_t hook_data = { .dst_data = frame_buffer, @@ -1447,7 +1447,9 @@ static esp_err_t rgb_panel_draw_bitmap_dma2d_hook(esp_lcd_panel_t *panel, const esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base); (void)hook_ctx; - async_color_convert_request_t fbcpy_trans_config = { + // Built-in DMA2D draw hook only needs same-format 2D window copy. + // Use the private DMA2D async memcpy wrapper (thin layer over color convert). + async_memcpy_dma2d_trans_desc_t fbcpy_trans_config = { .src_buffer = hook_data->src_data, .dst_buffer = hook_data->dst_data, .src_stride = hook_data->src_x_size, @@ -1460,16 +1462,13 @@ static esp_err_t rgb_panel_draw_bitmap_dma2d_hook(esp_lcd_panel_t *panel, const .dst_y = hook_data->dst_y_start, .copy_width = hook_data->src_x_end - hook_data->src_x_start, .copy_height = hook_data->src_y_end - hook_data->src_y_start, - // For this DMA2D hook we only do window copy from draw buffer to frame buffer. - // Source and destination color formats are intentionally set to the same value to disable CSC. - .src_color_format = rgb_panel->in_color_format, - .dst_color_format = rgb_panel->in_color_format, + .pixel_format = rgb_panel->in_color_format, }; - // The async color convert backend owns source/destination cache sync for the - // DMA2D copy path, so the LCD driver should not perform extra cache sync here. + // The DMA2D async 2D memcpy backend owns source/destination cache sync for the + // copy path, so the LCD driver should not perform extra cache sync here. // Save the completion callback and invoke it when the async frame buffer copy finishes. rgb_panel->on_hook_end = hook_data->on_hook_end; - ESP_RETURN_ON_ERROR(esp_async_color_convert(rgb_panel->fbcpy_handle, &fbcpy_trans_config, async_fbcpy_done_cb, rgb_panel), TAG, "async frame buffer copy failed"); + ESP_RETURN_ON_ERROR(esp_async_memcpy_dma2d(rgb_panel->fbcpy_handle, &fbcpy_trans_config, async_fbcpy_done_cb, rgb_panel), TAG, "async frame buffer copy failed"); return ESP_OK; } @@ -1482,12 +1481,12 @@ esp_err_t esp_lcd_rgb_panel_enable_dma2d(esp_lcd_panel_handle_t panel) // Check if built-in DMA2D draw bitmap hook is registered ESP_RETURN_ON_FALSE(!rgb_panel->fbcpy_handle, ESP_ERR_INVALID_STATE, TAG, "draw bitmap DMA2D hook is already registered"); - // Initialize the async color convert backend used by the built-in DMA2D copy hook. + // Initialize the DMA2D async 2D memcpy backend used by the built-in copy hook. // Use its default backlog to queue multiple frame buffer copy requests. - async_color_convert_config_t fbcpy_config = { + async_memcpy_dma2d_config_t fbcpy_config = { .dma_burst_size = 128, // for better performance }; - ESP_RETURN_ON_ERROR(esp_async_color_convert_install_dma2d(&fbcpy_config, &rgb_panel->fbcpy_handle), TAG, "install async frame buffer copy backend failed"); + ESP_RETURN_ON_ERROR(esp_async_memcpy_install_dma2d(&fbcpy_config, &rgb_panel->fbcpy_handle), TAG, "install async frame buffer copy backend failed"); // Register the DMA2D draw bitmap hook esp_lcd_panel_hooks_t hooks = { @@ -1499,7 +1498,7 @@ esp_err_t esp_lcd_rgb_panel_enable_dma2d(esp_lcd_panel_handle_t panel) err: if (rgb_panel->fbcpy_handle) { - esp_async_color_convert_uninstall(rgb_panel->fbcpy_handle); + esp_async_memcpy_uninstall_dma2d(rgb_panel->fbcpy_handle); rgb_panel->fbcpy_handle = NULL; } rgb_panel->on_hook_end = NULL; @@ -1519,13 +1518,7 @@ esp_err_t esp_lcd_rgb_panel_disable_dma2d(esp_lcd_panel_handle_t panel) .draw_bitmap_hook = NULL, }; ESP_RETURN_ON_ERROR(esp_lcd_rgb_panel_register_hooks(panel, &hooks, NULL), TAG, "unregister DMA2D draw bitmap hook failed"); - esp_err_t ret = esp_async_color_convert_uninstall(rgb_panel->fbcpy_handle); - if (ret != ESP_OK) { - // Restore the hook so draw_bitmap_hook and fbcpy_handle stay consistent on failure - hooks.draw_bitmap_hook = rgb_panel_draw_bitmap_dma2d_hook; - esp_lcd_rgb_panel_register_hooks(panel, &hooks, NULL); - ESP_RETURN_ON_ERROR(ret, TAG, "uninstall DMA2D failed"); - } + ESP_RETURN_ON_ERROR(esp_async_memcpy_uninstall_dma2d(rgb_panel->fbcpy_handle), TAG, "uninstall DMA2D failed"); rgb_panel->fbcpy_handle = NULL; rgb_panel->on_hook_end = NULL;