From 36732fbcb4eba77ed383a09d344594293214a1c7 Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Mon, 5 Feb 2024 22:04:37 +0800 Subject: [PATCH 01/15] feat(ppa): add PPA driver support for ESP32P4 --- components/esp_driver_ppa/CMakeLists.txt | 11 + .../esp_driver_ppa/include/driver/ppa.h | 144 +++ components/esp_driver_ppa/src/ppa.c | 966 ++++++++++++++++++ components/hal/esp32p4/include/hal/ppa_ll.h | 66 +- components/hal/include/hal/dma2d_types.h | 4 + components/hal/include/hal/ppa_types.h | 15 + .../esp32p4/include/soc/Kconfig.soc_caps.in | 4 + .../soc/esp32p4/include/soc/dma2d_channel.h | 4 +- components/soc/esp32p4/include/soc/soc_caps.h | 2 +- 9 files changed, 1178 insertions(+), 38 deletions(-) create mode 100644 components/esp_driver_ppa/CMakeLists.txt create mode 100644 components/esp_driver_ppa/include/driver/ppa.h create mode 100644 components/esp_driver_ppa/src/ppa.c diff --git a/components/esp_driver_ppa/CMakeLists.txt b/components/esp_driver_ppa/CMakeLists.txt new file mode 100644 index 0000000000..5647fac3cb --- /dev/null +++ b/components/esp_driver_ppa/CMakeLists.txt @@ -0,0 +1,11 @@ +set(srcs) +set(public_include "include") +if(CONFIG_SOC_PPA_SUPPORTED) + list(APPEND srcs "src/ppa.c") +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${public_include} + PRIV_REQUIRES esp_mm + # LDFRAGMENTS "linker.lf" + ) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h new file mode 100644 index 0000000000..781ff5e07a --- /dev/null +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "hal/ppa_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Type of PPA engine handle + */ +typedef struct ppa_engine_t *ppa_engine_handle_t; + +typedef struct { + ppa_engine_type_t engine; +} ppa_engine_config_t; + +esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_handle_t *ret_engine); + +esp_err_t ppa_engine_release(ppa_engine_handle_t ppa_engine); + +typedef enum { + PPA_TRANS_MODE_BLOCKING, + PPA_TRANS_MODE_NON_BLOCKING, +} ppa_trans_mode_t; + +typedef struct { + void *in_buffer; // TODO: could be a buffer list, link descriptors together, process a batch + // uint32_t batch_num; // However, is it necessary? psram can not store too many pictures + uint32_t in_pic_w; + uint32_t in_pic_h; + uint32_t in_block_w; + uint32_t in_block_h; + uint32_t in_block_offset_x; + uint32_t in_block_offset_y; + + void *out_buffer; // TODO: alignment restriction + uint32_t out_pic_w; + uint32_t out_pic_h; + uint32_t out_block_offset_x; + uint32_t out_block_offset_y; + + ppa_sr_rotation_angle_t rotation_angle; + float scale_x; + float scale_y; + bool mirror_x; + bool mirror_y; + + struct { + ppa_sr_color_mode_t mode; + color_range_t yuv_range; + color_conv_std_rgb_yuv_t yuv_std; + bool rgb_swap; + bool byte_swap; + } in_color; + + struct { + ppa_sr_color_mode_t mode; + color_range_t yuv_range; + color_conv_std_rgb_yuv_t yuv_std; + } out_color; + + ppa_alpha_mode_t alpha_mode; + uint32_t alpha_value; /*!< When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) + When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) + When other alpha modes are selected, this field is not used*/ +} ppa_sr_trans_config_t; + +/** + * @brief Perform a scaling-and-rotating (SR) operation to a picture + * + * @param[in] ppa_engine PPA engine handle with `PPA_ENGINE_TYPE_SR` as the engine type + * @param[in] config Pointer to a collection of configurations for the SR operation, ppa_sr_trans_config_t + * @param[in] mode Select one mode from ppa_trans_mode_t + * + * @return + * - ESP_OK: + * - ESP_ERR_INVALID_ARG: + * - ESP_ERR_NO_MEM: + * - ESP_FAIL: + */ +esp_err_t ppa_do_scale_and_rotate(ppa_engine_handle_t ppa_engine, const ppa_sr_trans_config_t *config, ppa_trans_mode_t mode); + +typedef struct { + void *in_bg_buffer; + void *in_fg_buffer; + void *out_buffer; +} ppa_blend_trans_config_t; + +/** + * @brief Perform a blending operation to a picture + * + * @param[in] ppa_engine PPA engine handle with `PPA_ENGINE_TYPE_BLEND` as the engine type + * @param[in] config Pointer to a collection of configurations for the blending operation, ppa_blend_trans_config_t + * @param[in] mode Select one mode from ppa_trans_mode_t + * + * @return + * - ESP_OK: + * - ESP_ERR_INVALID_ARG: + * - ESP_ERR_NO_MEM: + * - ESP_FAIL: + */ +esp_err_t ppa_do_blend(ppa_engine_handle_t ppa_engine, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode); + +typedef struct { + void *out_buffer; +} ppa_fill_trans_config_t; + +/** + * @brief Perform a filling operation to a picture + * + * @param[in] ppa_engine PPA engine handle with `PPA_ENGINE_TYPE_BLEND` as the engine type + * @param[in] config Pointer to a collection of configurations for the filling operation, ppa_fill_trans_config_t + * @param[in] mode Select one mode from ppa_trans_mode_t + * + * @return + * - ESP_OK: + * - ESP_ERR_INVALID_ARG: + * - ESP_ERR_NO_MEM: + * - ESP_FAIL: + */ +esp_err_t ppa_do_fill(ppa_engine_handle_t ppa_engine, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode); + +// SR and Blending are independent, can work at the same time +// Fill is in blend, so fill and blend cannot work at the same time + +// Consider blocking and non-blocking options +// Non-blocking may require notification of process done event + +// dma2d done/eof callback, when/how to free dma2d transaction content + +// cache writeback/invalidate + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c new file mode 100644 index 0000000000..71b2365508 --- /dev/null +++ b/components/esp_driver_ppa/src/ppa.c @@ -0,0 +1,966 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "esp_check.h" +#include "esp_log.h" +#include "freertos/portmacro.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_heap_caps.h" +#include "esp_cache.h" +#include "hal/cache_hal.h" +#include "hal/cache_ll.h" +#include "driver/ppa.h" +#include "esp_private/dma2d.h" +#include "hal/dma2d_ll.h" +#include "soc/dma2d_channel.h" +#include "hal/ppa_ll.h" +#include "hal/ppa_types.h" +#include "hal/color_types.h" +#include "hal/color_hal.h" +#include "esp_private/periph_ctrl.h" + +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + +static const char *TAG = "ppa"; + +typedef struct ppa_dev_t *ppa_soc_handle_t; // PPA SOC layer handle + +typedef struct { + ppa_soc_handle_t dev; +} ppa_hal_context_t; + +void ppa_hal_init(ppa_hal_context_t *hal) +{ + hal->dev = PPA_LL_GET_HW; +} + +void ppa_hal_deinit(ppa_hal_context_t *hal) +{ + hal->dev = NULL; +} + +// PPA module contains SR engine and Blending engine + +// typedef struct ppa_group_t ppa_group_t; +typedef struct ppa_engine_t ppa_engine_t; + +typedef ppa_sr_trans_config_t ppa_sr_transaction_t; + +typedef struct { + +} ppa_blend_transaction_t; + +typedef struct ppa_trans_s { + STAILQ_ENTRY(ppa_trans_s) entry; // link entry + // union { + // const ppa_sr_transaction_t *sr_desc; + // const ppa_blending_transaction_t *blending_desc; + // }; + dma2d_trans_config_t *trans_desc; + dma2d_trans_t *dma_trans_placeholder; + SemaphoreHandle_t sem; +} ppa_trans_t; + +typedef struct { + union { + ppa_sr_transaction_t *sr_desc; + ppa_blend_transaction_t *blend_desc; + }; + ppa_engine_t *ppa_engine; + ppa_trans_t *trans_elm; + dma2d_trigger_peripheral_t trigger_periph; + // dma2d_csc_config_t + // dma2d_strategy_config_t *dma_strategy; + // dma2d_transfer_ability_t *dma_transfer_ability; + // dma2d_rx_event_callbacks_t *event_cbs; +} ppa_dma2d_trans_on_picked_config_t; + +struct ppa_engine_t { + // ppa_group_t *group; + ppa_engine_type_t type; + portMUX_TYPE spinlock; + SemaphoreHandle_t sem; + bool in_accepting_trans_state; + // pending transactions queue? union ppa_sr_trans_config_t, ppa_blending_trans_config_t? handle when to free (at trans start or at trans end?) + STAILQ_HEAD(trans, ppa_trans_s) trans_stailq; // link head of pending transactions for the PPA engine + // callback func? Here or in the struct above? + // dma2d_rx_event_callbacks_t event_cbs; +}; + +typedef struct ppa_sr_engine_t { + ppa_engine_t base; + dma2d_descriptor_t *dma_tx_desc; + dma2d_descriptor_t *dma_rx_desc; +} ppa_sr_engine_t; + +typedef struct ppa_blend_engine_t { + ppa_engine_t base; + dma2d_descriptor_t *dma_tx_bg_desc; + dma2d_descriptor_t *dma_tx_fg_desc; + dma2d_descriptor_t *dma_rx_desc; +} ppa_blend_engine_t; + +// how to free and how to push next trans into dma2d queue after engine is free (callback triggered) +// + +// struct ppa_group_t { +// int group_id; +// portMUX_TYPE spinlock; +// ppa_hal_context_t hal; +// dma2d_pool_handle_t dma2d_pool_handle; +// ppa_sr_engine_t *sr; +// ppa_blend_engine_t *blending; +// uint32_t sr_engine_ref_count; +// uint32_t blend_engine_ref_count; +// }; + +typedef struct ppa_platform_t { + _lock_t mutex; // platform level mutex lock to protect the ppa_module_acquire/ppa_module_release process + portMUX_TYPE spinlock; // platform level spinlock + // ppa_group_t *group[PPA_LL_GROUPS]; // array of PPA group instances + ppa_hal_context_t hal; + dma2d_pool_handle_t dma2d_pool_handle; + ppa_sr_engine_t *sr; + ppa_blend_engine_t *blending; + uint32_t sr_engine_ref_count; + uint32_t blend_engine_ref_count; + uint32_t dma_desc_mem_size; +} ppa_platform_t; + +// PPA driver platform +static ppa_platform_t s_platform = { + .spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED, + // .group = {}, +}; + +#define PPA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) // TODO... + +// TODO: acquire pm_lock? +esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_handle_t *ret_engine) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(config && ret_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(config->engine == PPA_ENGINE_TYPE_SR || config->engine == PPA_ENGINE_TYPE_BLEND, ESP_ERR_INVALID_ARG, TAG, "invalid engine"); + + *ret_engine = NULL; + + uint32_t data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); + size_t alignment = MAX(DMA2D_LL_DESC_ALIGNMENT, data_cache_line_size); + + _lock_acquire(&s_platform.mutex); + if (s_platform.dma_desc_mem_size == 0) { + s_platform.dma_desc_mem_size = ALIGN_UP(sizeof(dma2d_descriptor_align8_t), alignment); + } + + if (config->engine == PPA_ENGINE_TYPE_SR) { + if (!s_platform.sr) { + ppa_sr_engine_t *sr_engine = heap_caps_calloc(1, sizeof(ppa_sr_engine_t), PPA_MEM_ALLOC_CAPS); + SemaphoreHandle_t sr_sem = xSemaphoreCreateBinary(); + dma2d_descriptor_t *sr_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *sr_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + if (sr_engine && sr_sem && sr_tx_dma_desc && sr_rx_dma_desc) { + sr_engine->dma_tx_desc = sr_tx_dma_desc; + sr_engine->dma_rx_desc = sr_rx_dma_desc; + // sr_engine->base.group = s_platform.group[group_id]; + sr_engine->base.type = PPA_ENGINE_TYPE_SR; + sr_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + sr_engine->base.sem = sr_sem; + xSemaphoreGive(sr_engine->base.sem); + sr_engine->base.in_accepting_trans_state = true; + STAILQ_INIT(&sr_engine->base.trans_stailq); + // sr_engine->base.event_cbs + s_platform.sr = sr_engine; + s_platform.sr_engine_ref_count++; + *ret_engine = &sr_engine->base; + } else { + ret = ESP_ERR_NO_MEM; + ESP_LOGE(TAG, "no mem to register PPA SR engine"); + free(sr_engine); + if (sr_sem) { + vSemaphoreDelete(sr_sem); + } + free(sr_tx_dma_desc); + free(sr_rx_dma_desc); + } + } else { + // SR engine already registered + s_platform.sr_engine_ref_count++; + *ret_engine = &s_platform.sr->base; + } + } else if (config->engine == PPA_ENGINE_TYPE_BLEND) { + if (!s_platform.blending) { + ppa_blend_engine_t *blending_engine = heap_caps_calloc(1, sizeof(ppa_blend_engine_t), PPA_MEM_ALLOC_CAPS); + SemaphoreHandle_t blending_sem = xSemaphoreCreateBinary(); + dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + if (blending_engine && blending_sem && blending_tx_bg_dma_desc && blending_tx_fg_dma_desc && blending_rx_dma_desc) { + blending_engine->dma_tx_bg_desc = blending_tx_bg_dma_desc; + blending_engine->dma_tx_fg_desc = blending_tx_fg_dma_desc; + blending_engine->dma_rx_desc = blending_rx_dma_desc; + // blending_engine->base.group = s_platform.group[group_id]; + blending_engine->base.type = PPA_ENGINE_TYPE_BLEND; + blending_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + blending_engine->base.sem = blending_sem; + xSemaphoreGive(blending_engine->base.sem); + blending_engine->base.in_accepting_trans_state = true; + STAILQ_INIT(&blending_engine->base.trans_stailq); + // blending_engine->base.event_cbs + s_platform.blending = blending_engine; + s_platform.blend_engine_ref_count++; + *ret_engine = &blending_engine->base; + } else { + ret = ESP_ERR_NO_MEM; + ESP_LOGE(TAG, "no mem to register PPA Blending engine"); + free(blending_engine); + if (blending_sem) { + vSemaphoreDelete(blending_sem); + } + free(blending_tx_bg_dma_desc); + free(blending_tx_fg_dma_desc); + free(blending_rx_dma_desc); + } + } else { + // Blending engine already registered + s_platform.blend_engine_ref_count++; + *ret_engine = &s_platform.blending->base; + } + } + + if (ret == ESP_OK) { + if (!s_platform.hal.dev) { + assert(!s_platform.dma2d_pool_handle); + + // Enable the bus clock to access PPA registers + PERIPH_RCC_ATOMIC() { + ppa_ll_enable_bus_clock(true); + ppa_ll_reset_register(); + } + + ppa_hal_init(&s_platform.hal); // initialize HAL context + + // Get 2D-DMA pool handle + dma2d_pool_config_t dma2d_config = { + .pool_id = 0, + }; + ret = dma2d_acquire_pool(&dma2d_config, &s_platform.dma2d_pool_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "install 2D-DMA failed"); + } + } + } + _lock_release(&s_platform.mutex); + + if (ret != ESP_OK && *ret_engine != NULL) { + ppa_engine_release(*ret_engine); + } + + return ret; +} + +esp_err_t ppa_engine_release(ppa_engine_handle_t ppa_engine) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(ppa_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + _lock_acquire(&s_platform.mutex); + if (ppa_engine->type == PPA_ENGINE_TYPE_SR) { + ppa_sr_engine_t *sr_engine = __containerof(ppa_engine, ppa_sr_engine_t, base); + s_platform.sr_engine_ref_count--; + if (s_platform.sr_engine_ref_count == 0) { + // Stop accepting new transactions to SR engine + portENTER_CRITICAL(&sr_engine->base.spinlock); + sr_engine->base.in_accepting_trans_state = false; + portEXIT_CRITICAL(&sr_engine->base.spinlock); + // Wait until all transactions get processed + while (!STAILQ_EMPTY(&sr_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide + // Now, time to free + s_platform.sr = NULL; + free(sr_engine->dma_tx_desc); + free(sr_engine->dma_rx_desc); + vSemaphoreDelete(sr_engine->base.sem); + free(sr_engine); + } + } else if (ppa_engine->type == PPA_ENGINE_TYPE_BLEND) { + ppa_blend_engine_t *blending_engine = __containerof(ppa_engine, ppa_blend_engine_t, base); + s_platform.blend_engine_ref_count--; + if (s_platform.blend_engine_ref_count == 0) { + // Stop accepting new transactions to blending engine + portENTER_CRITICAL(&blending_engine->base.spinlock); + blending_engine->base.in_accepting_trans_state = false; + portEXIT_CRITICAL(&blending_engine->base.spinlock); + // Wait until all transactions get processed + while (!STAILQ_EMPTY(&blending_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide + // Now, time to free + s_platform.blending = NULL; + free(blending_engine->dma_tx_bg_desc); + free(blending_engine->dma_tx_fg_desc); + free(blending_engine->dma_rx_desc); + vSemaphoreDelete(blending_engine->base.sem); + free(blending_engine); + } + } + + if (!s_platform.sr && !s_platform.blending) { + assert(s_platform.sr_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0); + + if (s_platform.dma2d_pool_handle) { + dma2d_release_pool(s_platform.dma2d_pool_handle); // TODO: check return value. If not ESP_OK, then must be error on other 2D-DMA clients :( Give a warning log? + s_platform.dma2d_pool_handle = NULL; + } + + ppa_hal_deinit(&s_platform.hal); // De-initialize HAL context + + // Disable the bus clock to access PPA registers + PERIPH_RCC_ATOMIC() { + ppa_ll_enable_bus_clock(false); + } + } + _lock_release(&s_platform.mutex); + return ret; +} + +// // TODO: pm lock? +// esp_err_t ppa_module_acquire(const ppa_group_alloc_config_t *config, ppa_group_handle_t *ret_group) +// { +// esp_err_t ret = ESP_OK; +// ESP_RETURN_ON_FALSE(config && ret_group, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); +// ESP_RETURN_ON_FALSE(config->group_id < PPA_LL_GROUPS, ESP_ERR_INVALID_ARG, TAG, "invalid group_id"); + +// int group_id = config->group_id; + +// // _lock_acquire(&s_platform.mutex); +// // if (!s_platform.group[group_id]) { +// // ppa_group_t *pre_alloc_group = heap_caps_calloc(1, sizeof(ppa_group_t), PPA_MEM_ALLOC_CAPS); +// // if (pre_alloc_group) { +// // ppa_hal_init(&pre_alloc_group->hal, group_id); // initialize HAL context +// // pre_alloc_group->group_id = group_id; +// // pre_alloc_group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +// // s_platform.group[group_id] = pre_alloc_group; // register to platform +// // // Enable the bus clock to access PPA registers +// // PERIPH_RCC_ATOMIC() { +// // ppa_ll_enable_bus_clock(group_id, true); +// // ppa_ll_reset_register(group_id); +// // } + +// // // Get 2D-DMA pool handle +// // dma2d_pool_config_t dma2d_config = { +// // .pool_id = 0, +// // }; +// // ret = dma2d_acquire_pool(&dma2d_config, &s_platform.group[group_id]->dma2d_pool_handle); +// // if (ret != ESP_OK) { +// // ESP_LOGE(TAG, "install 2D-DMA failed"); +// // // free(pre_alloc_group); +// // // s_platform.group[group_id] = NULL; +// // } +// // } else { +// // ret = ESP_ERR_NO_MEM; +// // } +// // } + +// // // Register PPA SR engine +// // if (ret == ESP_OK && config->sr_engine_en && !s_platform.group[group_id]->sr) { +// // ppa_sr_engine_t *sr_engine = heap_caps_calloc(1, sizeof(ppa_sr_engine_t), PPA_MEM_ALLOC_CAPS); +// // SemaphoreHandle_t sr_sem = xSemaphoreCreateBinary(); +// // dma2d_descriptor_t *sr_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API +// // dma2d_descriptor_t *sr_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); +// // if (sr_engine && sr_sem && sr_tx_dma_desc && sr_rx_dma_desc) { +// // sr_engine->dma_tx_desc = sr_tx_dma_desc; +// // sr_engine->dma_rx_desc = sr_rx_dma_desc; +// // sr_engine->base.group = s_platform.group[group_id]; +// // sr_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +// // sr_engine->base.sem = sr_sem; +// // xSemaphoreGive(sr_engine->base.sem); +// // sr_engine->base.in_accepting_trans_state = true; +// // STAILQ_INIT(&sr_engine->base.trans_stailq); +// // // sr_engine->base.event_cbs +// // s_platform.group[group_id]->sr = sr_engine; +// // } else { +// // ret = ESP_ERR_NO_MEM; +// // ESP_LOGE(TAG, "no mem to register PPA SR engine"); +// // free(sr_engine); +// // if (sr_sem) vSemaphoreDelete(sr_sem); +// // free(sr_tx_dma_desc); +// // free(sr_rx_dma_desc); +// // } +// // } + +// // // Register PPA Blending engine +// // if (ret == ESP_OK && config->blending_engine_en && !s_platform.group[group_id]->blending) { +// // ppa_blending_engine_t *blending_engine = heap_caps_calloc(1, sizeof(ppa_blending_engine_t), PPA_MEM_ALLOC_CAPS); +// // SemaphoreHandle_t blending_sem = xSemaphoreCreateBinary(); +// // dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API +// // dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); +// // dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); +// // if (blending_engine && blending_sem && blending_tx_bg_dma_desc && blending_tx_fg_dma_desc && blending_rx_dma_desc) { +// // blending_engine->dma_tx_bg_desc = blending_tx_bg_dma_desc; +// // blending_engine->dma_tx_fg_desc = blending_tx_fg_dma_desc; +// // blending_engine->dma_rx_desc = blending_rx_dma_desc; +// // blending_engine->base.group = s_platform.group[group_id]; +// // blending_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +// // blending_engine->base.sem = blending_sem; +// // xSemaphoreGive(blending_engine->base.sem); +// // blending_engine->base.in_accepting_trans_state = true; +// // STAILQ_INIT(&blending_engine->base.trans_stailq); +// // // blending_engine->base.event_cbs +// // s_platform.group[group_id]->blending = blending_engine; +// // } else { +// // ret = ESP_ERR_NO_MEM; +// // ESP_LOGE(TAG, "no mem to register PPA Blending engine"); +// // free(blending_engine); +// // if (blending_sem) vSemaphoreDelete(blending_sem); +// // free(blending_tx_bg_dma_desc); +// // free(blending_tx_fg_dma_desc); +// // free(blending_rx_dma_desc); +// // } +// // } +// // _lock_release(&s_platform.mutex); + +// // ppa_module_release + +// bool new_group = false; +// bool new_sr_engine = false; +// bool new_blending_engine = false; +// ppa_group_t *pre_alloc_group = heap_caps_calloc(1, sizeof(ppa_group_t), PPA_MEM_ALLOC_CAPS); +// ppa_sr_engine_t *sr_engine = NULL; +// ppa_blend_engine_t *blending_engine = NULL; +// SemaphoreHandle_t sr_sem = NULL, blending_sem = NULL; + +// // portENTER_CRITICAL(&s_platform.spinlock); +// if (!s_platform.group[group_id]) { +// if (pre_alloc_group) { +// new_group = true; +// ppa_hal_init(&pre_alloc_group->hal, group_id); // initialize HAL context +// pre_alloc_group->group_id = group_id; +// pre_alloc_group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +// s_platform.group[group_id] = pre_alloc_group; // register to platform +// // Enable the bus clock to access PPA registers +// PERIPH_RCC_ATOMIC() { +// ppa_ll_enable_bus_clock(group_id, true); +// ppa_ll_reset_register(group_id); +// } +// } else { +// ret = ESP_ERR_NO_MEM; +// } +// } +// // portEXIT_CRITICAL(&s_platform.spinlock); + +// if (new_group) { +// // Get 2D-DMA pool handle +// dma2d_pool_config_t dma2d_config = { +// .pool_id = 0, +// }; +// ret = dma2d_acquire_pool(&dma2d_config, &s_platform.group[group_id]->dma2d_pool_handle); +// if (ret != ESP_OK) { +// ESP_LOGE(TAG, "install 2D-DMA failed"); +// goto err; +// } +// } + +// if (ret == ESP_OK && config->sr_engine_en) { +// sr_engine = heap_caps_calloc(1, sizeof(ppa_sr_engine_t), PPA_MEM_ALLOC_CAPS); +// sr_sem = xSemaphoreCreateBinary(); +// dma2d_descriptor_t *sr_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API +// dma2d_descriptor_t *sr_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); +// // Register PPA SR engine +// portENTER_CRITICAL(&s_platform.group[group_id]->spinlock); +// if (!s_platform.group[group_id]->sr) { +// if (sr_engine && sr_sem && sr_tx_dma_desc && sr_rx_dma_desc) { +// new_sr_engine = true; +// sr_engine->dma_tx_desc = sr_tx_dma_desc; +// sr_engine->dma_rx_desc = sr_rx_dma_desc; +// sr_engine->base.group = s_platform.group[group_id]; +// sr_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +// sr_engine->base.sem = sr_sem; +// xSemaphoreGive(sr_engine->base.sem); +// sr_engine->base.in_accepting_trans_state = true; +// STAILQ_INIT(&sr_engine->base.trans_stailq); +// // sr_engine->base.event_cbs +// s_platform.group[group_id]->sr = sr_engine; +// } else { +// ret = ESP_ERR_NO_MEM; +// } +// } +// portEXIT_CRITICAL(&s_platform.group[group_id]->spinlock); +// if (ret == ESP_ERR_NO_MEM) { +// ESP_LOGE(TAG, "no mem to register PPA SR engine"); +// } +// } + +// if (ret == ESP_OK && config->blending_engine_en) { +// blending_engine = heap_caps_calloc(1, sizeof(ppa_blend_engine_t), PPA_MEM_ALLOC_CAPS); +// blending_sem = xSemaphoreCreateBinary(); +// dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API +// dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); +// dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); +// // Register PPA Blending engine +// portENTER_CRITICAL(&s_platform.group[group_id]->spinlock); +// if (!s_platform.group[group_id]->blending) { +// if (blending_engine && blending_sem && blending_tx_bg_dma_desc && blending_tx_fg_dma_desc && blending_rx_dma_desc) { +// new_blending_engine = true; +// blending_engine->dma_tx_bg_desc = blending_tx_bg_dma_desc; +// blending_engine->dma_tx_fg_desc = blending_tx_fg_dma_desc; +// blending_engine->dma_rx_desc = blending_rx_dma_desc; +// blending_engine->base.group = s_platform.group[group_id]; +// blending_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +// blending_engine->base.sem = blending_sem; +// xSemaphoreGive(blending_engine->base.sem); +// blending_engine->base.in_accepting_trans_state = true; +// STAILQ_INIT(&blending_engine->base.trans_stailq); +// // blending_engine->base.event_cbs +// s_platform.group[group_id]->blending = blending_engine; +// } else { +// ret = ESP_ERR_NO_MEM; +// } +// } +// portEXIT_CRITICAL(&s_platform.group[group_id]->spinlock); +// if (ret == ESP_ERR_NO_MEM) { +// ESP_LOGE(TAG, "no mem to register PPA Blending engine"); +// } +// } + +// if (!new_sr_engine) { +// free(sr_engine); +// if (sr_sem) vSemaphoreDelete(sr_sem); +// // TODO: free desc +// } +// if (!new_blending_engine) { +// free(blending_engine); +// if (blending_sem) vSemaphoreDelete(blending_sem); +// // TODO: free desc +// } +// err: +// if (ret != ESP_OK) { +// if (new_group) { +// ppa_module_release(s_platform.group[group_id]); +// } +// } +// if (!new_group) { +// free(pre_alloc_group); +// } +// *ret_group = s_platform.group[group_id]; +// return ret; +// } + +// esp_err_t ppa_module_release(ppa_group_handle_t ppa_group) +// { +// esp_err_t ret = ESP_OK; +// ESP_RETURN_ON_FALSE(ppa_group, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + +// bool do_deinitialize = false; +// int group_id = ppa_group->group_id; +// ppa_sr_engine_t *sr_engine = ppa_group->sr; +// ppa_blend_engine_t *blending_engine = ppa_group->blending; +// bool sr_no_waiting_trans = true; +// bool blending_no_waiting_trans = true; + +// // portENTER_CRITICAL(&s_platform.spinlock); +// portENTER_CRITICAL(&ppa_group->spinlock); +// if (sr_engine) { +// sr_engine->base.in_accepting_trans_state = false; +// portENTER_CRITICAL(&sr_engine->base.spinlock); +// sr_no_waiting_trans = STAILQ_EMPTY(&sr_engine->base.trans_stailq); +// portEXIT_CRITICAL(&sr_engine->base.spinlock); +// } +// if (blending_engine) { +// blending_engine->base.in_accepting_trans_state = false; +// portENTER_CRITICAL(&blending_engine->base.spinlock); +// blending_no_waiting_trans = STAILQ_EMPTY(&blending_engine->base.trans_stailq); +// portEXIT_CRITICAL(&blending_engine->base.spinlock); +// } +// portEXIT_CRITICAL(&ppa_group->spinlock); +// if (sr_no_waiting_trans && blending_no_waiting_trans) { +// do_deinitialize = true; +// ppa_group->sr = NULL; +// ppa_group->blending = NULL; +// s_platform.group[group_id] = NULL; +// } else { +// ret = ESP_FAIL; +// } +// // portEXIT_CRITICAL(&s_platform.spinlock); + +// if (do_deinitialize) { +// if (sr_engine) { +// free(sr_engine->dma_tx_desc); +// free(sr_engine->dma_rx_desc); +// vSemaphoreDelete(sr_engine->base.sem); +// free(sr_engine); +// } +// if (blending_engine) { +// free(blending_engine->dma_tx_bg_desc); +// free(blending_engine->dma_tx_fg_desc); +// free(blending_engine->dma_rx_desc); +// vSemaphoreDelete(blending_engine->base.sem); +// free(blending_engine); +// } +// dma2d_release_pool(ppa_group->dma2d_pool_handle); +// // Disable the bus clock to access PPA registers +// PERIPH_RCC_ATOMIC() { +// ppa_ll_enable_bus_clock(group_id, false); +// } +// free(ppa_group); +// } +// return ret; +// } + +// Each PPA engine should only have one transaction being pushed to 2D-DMA queue, the rest transactions should stay in engine's own transaction queue. +// This is to avoid 2D-DMA channels being hold, but not actually being used (waiting for PPA engine to be free) +static esp_err_t ppa_dma2d_enqueue(const ppa_trans_t *trans_elm) +{ + return dma2d_enqueue(s_platform.dma2d_pool_handle, trans_elm->trans_desc, trans_elm->dma_trans_placeholder); +} + +static void ppa_recycle_transaction(ppa_trans_t *trans_elm) +{ + if (trans_elm) { + if (trans_elm->trans_desc) { + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; + if (trans_on_picked_desc) { + if (trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SR) { + free(trans_on_picked_desc->sr_desc); + } else if (trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND) { + free(trans_on_picked_desc->blend_desc); + } + free(trans_on_picked_desc); + } + free(trans_elm->trans_desc); + } + if (trans_elm->sem) { + vSemaphoreDelete(trans_elm->sem); + } + free(trans_elm); + } +} + +static bool ppa_sr_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) +{ + bool need_yield = false; + BaseType_t HPTaskAwoken; + ppa_trans_t *trans_elm = (ppa_trans_t *)user_data; + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; + assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SR); + ppa_sr_engine_t *sr_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_sr_engine_t, base); + // ppa_group_t *ppa_group = sr_engine->base.group; + + ppa_trans_t *next_start_trans = NULL; + portENTER_CRITICAL_ISR(&sr_engine->base.spinlock); + // Remove this transaction from transaction queue + STAILQ_REMOVE(&sr_engine->base.trans_stailq, trans_elm, ppa_trans_s, entry); + next_start_trans = STAILQ_FIRST(&sr_engine->base.trans_stailq); + portEXIT_CRITICAL_ISR(&sr_engine->base.spinlock); + + // If there is next trans in PPA engine queue, send it to DMA queue; otherwise, release the engine semaphore + if (next_start_trans) { + esp_rom_printf("from ISR -"); + ppa_dma2d_enqueue(next_start_trans); + } else { + xSemaphoreGiveFromISR(sr_engine->base.sem, &HPTaskAwoken); + need_yield |= (HPTaskAwoken == pdTRUE); + } + esp_rom_printf("trans addr: 0x%x\n", trans_elm); + // Recycle transaction or give transaction semaphore + if (trans_elm->sem != NULL) { + xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); + need_yield |= (HPTaskAwoken == pdTRUE); + } else { + ppa_recycle_transaction(trans_elm); + } + + // TODO: how to notify non-blocking transaction + + return need_yield; +} + +static bool ppa_sr_transaction_on_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +{ + assert(channel_num == 2 && dma2d_chans && user_config); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; + assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SR && trans_on_picked_desc->sr_desc && trans_on_picked_desc->ppa_engine); + + ppa_sr_transaction_t *sr_trans_desc = trans_on_picked_desc->sr_desc; + ppa_sr_engine_t *sr_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_sr_engine_t, base); + // ppa_group_t *ppa_group = sr_engine->base.group; + + // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) + free(trans_on_picked_desc->trans_elm->dma_trans_placeholder); + + // Get the required 2D-DMA channel handles + uint32_t dma2d_tx_chan_idx = 0; + uint32_t dma2d_rx_chan_idx = 1; + if (dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX) { + dma2d_tx_chan_idx = 1; + dma2d_rx_chan_idx = 0; + } + dma2d_channel_handle_t dma2d_tx_chan = dma2d_chans[dma2d_tx_chan_idx].chan; + dma2d_channel_handle_t dma2d_rx_chan = dma2d_chans[dma2d_rx_chan_idx].chan; + + // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) + // Write back in_buffer + uint32_t in_buffer_len = sr_trans_desc->in_pic_w * sr_trans_desc->in_pic_h * color_hal_pixel_format_get_bit_depth((color_space_pixel_format_t) { + .color_type_id = sr_trans_desc->in_color.mode + }) / 8; + esp_cache_msync(sr_trans_desc->in_buffer, in_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + // Invalidate out_buffer + uint32_t out_buffer_len = sr_trans_desc->out_pic_w * sr_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth((color_space_pixel_format_t) { + .color_type_id = sr_trans_desc->out_color.mode + }) / 8; + esp_cache_msync(sr_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + + // Fill 2D-DMA descriptors + sr_engine->dma_tx_desc->vb_size = sr_trans_desc->in_block_h; + sr_engine->dma_tx_desc->hb_length = sr_trans_desc->in_block_w; + sr_engine->dma_tx_desc->err_eof = 0; + sr_engine->dma_tx_desc->dma2d_en = 1; + sr_engine->dma_tx_desc->suc_eof = 1; + sr_engine->dma_tx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + sr_engine->dma_tx_desc->va_size = sr_trans_desc->in_pic_h; + sr_engine->dma_tx_desc->ha_length = sr_trans_desc->in_pic_w; + sr_engine->dma_tx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value((color_space_pixel_format_t) { + .color_type_id = sr_trans_desc->in_color.mode + }); // check in 912 whether this field can be ignored (911 seems cannot) No! Why? + sr_engine->dma_tx_desc->y = sr_trans_desc->in_block_offset_y; + sr_engine->dma_tx_desc->x = sr_trans_desc->in_block_offset_x; + sr_engine->dma_tx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + sr_engine->dma_tx_desc->buffer = (void *)sr_trans_desc->in_buffer; + sr_engine->dma_tx_desc->next = NULL; + + // sr_engine->dma_rx_desc->vb_size = sr_trans_desc->in_block_h; // check in 912 whether this field can be ignored (911 seems cannot) No! Why? + // sr_engine->dma_rx_desc->hb_length = sr_trans_desc->in_block_w; // check in 912 whether this field can be ignored (911 seems cannot) No! Why? + sr_engine->dma_rx_desc->vb_size = 1; + sr_engine->dma_rx_desc->hb_length = 1; + sr_engine->dma_rx_desc->err_eof = 0; + sr_engine->dma_rx_desc->dma2d_en = 1; + sr_engine->dma_rx_desc->suc_eof = 1; + sr_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + sr_engine->dma_rx_desc->va_size = sr_trans_desc->out_pic_h; + sr_engine->dma_rx_desc->ha_length = sr_trans_desc->out_pic_w; + sr_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value((color_space_pixel_format_t) { + .color_type_id = sr_trans_desc->out_color.mode + }); // check in 912 whether this field can be ignored (911 seems cannot) No! Why? + sr_engine->dma_rx_desc->y = sr_trans_desc->out_block_offset_y; + sr_engine->dma_rx_desc->x = sr_trans_desc->out_block_offset_x; + sr_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + sr_engine->dma_rx_desc->buffer = (void *)sr_trans_desc->out_buffer; + sr_engine->dma_rx_desc->next = NULL; + + esp_cache_msync((void *)sr_engine->dma_tx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)sr_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + + // Configure 2D-DMA channels + dma2d_trigger_t trig_periph = { + .periph = DMA2D_TRIG_PERIPH_PPA_SR, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SR_TX, + }; + dma2d_connect(dma2d_tx_chan, &trig_periph); + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SR_RX; + dma2d_connect(dma2d_rx_chan, &trig_periph); + + dma2d_transfer_ability_t dma_transfer_ability = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + }; + dma2d_set_transfer_ability(dma2d_tx_chan, &dma_transfer_ability); + dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); + + // TODO: configuring this doesn't seem helping anything? Shouldn't it related to descriptor pbyte? + dma2d_strategy_config_t dma_strategy = { + .auto_update_desc = true, + }; + dma2d_apply_strategy(dma2d_tx_chan, &dma_strategy); + + // YUV444 and YUV422 are not supported by PPA module, need to utilize 2D-DMA color space conversion feature to do a conversion + ppa_sr_color_mode_t ppa_in_color_mode = sr_trans_desc->in_color.mode; + if (ppa_in_color_mode == PPA_SR_COLOR_MODE_YUV444) { + ppa_in_color_mode = PPA_SR_COLOR_MODE_RGB888; + dma2d_csc_config_t dma_tx_csc = {0}; + if (sr_trans_desc->in_color.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_601; + } else { + dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_709; + } + dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); + } else if (ppa_in_color_mode == PPA_SR_COLOR_MODE_YUV422) { + ppa_in_color_mode = PPA_SR_COLOR_MODE_RGB888; + dma2d_csc_config_t dma_tx_csc = {0}; + if (sr_trans_desc->in_color.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_601; + } else { + dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_709; + } + dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); + } + + ppa_sr_color_mode_t ppa_out_color_mode = sr_trans_desc->out_color.mode; + if (ppa_out_color_mode == PPA_SR_COLOR_MODE_YUV444) { + ppa_out_color_mode = PPA_SR_COLOR_MODE_YUV420; + dma2d_csc_config_t dma_rx_csc = { + .rx_csc_option = DMA2D_CSC_RX_YUV420_TO_YUV444, + }; + dma2d_configure_color_space_conversion(dma2d_rx_chan, &dma_rx_csc); + } + + dma2d_rx_event_callbacks_t dma_event_cbs = { + .on_recv_eof = ppa_sr_transaction_done_cb, + }; + dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); + + ppa_ll_sr_reset(s_platform.hal.dev); + + dma2d_set_desc_addr(dma2d_tx_chan, (intptr_t)sr_engine->dma_tx_desc); + dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)sr_engine->dma_rx_desc); + dma2d_start(dma2d_tx_chan); + dma2d_start(dma2d_rx_chan); + + // Configure PPA SR engine + ppa_ll_sr_set_rx_color_mode(s_platform.hal.dev, ppa_in_color_mode); + if (COLOR_SPACE_TYPE(ppa_in_color_mode) == COLOR_SPACE_YUV) { + ppa_ll_sr_set_rx_yuv_range(s_platform.hal.dev, sr_trans_desc->in_color.yuv_range); + ppa_ll_sr_set_yuv2rgb_std(s_platform.hal.dev, sr_trans_desc->in_color.yuv_std); + } + ppa_ll_sr_enable_rx_byte_swap(s_platform.hal.dev, sr_trans_desc->in_color.byte_swap); + ppa_ll_sr_enable_rx_rgb_swap(s_platform.hal.dev, sr_trans_desc->in_color.rgb_swap); + + ppa_ll_sr_set_tx_color_mode(s_platform.hal.dev, ppa_out_color_mode); + if (COLOR_SPACE_TYPE(ppa_out_color_mode) == COLOR_SPACE_YUV) { + ppa_ll_sr_set_rx_yuv_range(s_platform.hal.dev, sr_trans_desc->out_color.yuv_range); + ppa_ll_sr_set_yuv2rgb_std(s_platform.hal.dev, sr_trans_desc->out_color.yuv_std); + } + + ppa_ll_sr_configure_rx_alpha(s_platform.hal.dev, sr_trans_desc->alpha_mode, sr_trans_desc->alpha_value); + + // TODO: sr_macro_bk_ro_bypass + ppa_ll_sr_set_rotation_angle(s_platform.hal.dev, sr_trans_desc->rotation_angle); + ppa_ll_sr_set_scaling_x(s_platform.hal.dev, (uint32_t)sr_trans_desc->scale_x, (uint32_t)(sr_trans_desc->scale_x * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX); + ppa_ll_sr_set_scaling_y(s_platform.hal.dev, (uint32_t)sr_trans_desc->scale_y, (uint32_t)(sr_trans_desc->scale_y * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX); + ppa_ll_sr_enable_mirror_x(s_platform.hal.dev, sr_trans_desc->mirror_x); + ppa_ll_sr_enable_mirror_y(s_platform.hal.dev, sr_trans_desc->mirror_y); + + ppa_ll_sr_start(s_platform.hal.dev); + + // dma2d_start(dma2d_tx_chan); + // dma2d_start(dma2d_rx_chan); + + // No need to yield + return false; +} + +esp_err_t ppa_do_scale_and_rotate(ppa_engine_handle_t ppa_engine, const ppa_sr_trans_config_t *config, ppa_trans_mode_t mode) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(ppa_engine && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_engine->type == PPA_ENGINE_TYPE_SR, ESP_ERR_INVALID_ARG, TAG, "wrong engine handle"); + ESP_RETURN_ON_FALSE(mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + // Any restrictions on in/out buffer address? alignment? alignment restriction comes from cache, its addr and size need to be aligned to cache line size on 912! + // buffer on stack/heap + // ESP_RETURN_ON_FALSE(config->rotation_angle) + // ESP_RETURN_ON_FALSE(config->in/out_color_mode) + // what if in_color is YUV420, out is RGB, what is out RGB range? Full range? + ESP_RETURN_ON_FALSE(config->scale_x < (PPA_LL_SR_SCALING_INT_MAX + 1) && config->scale_x >= (1.0 / PPA_LL_SR_SCALING_FRAG_MAX) && + config->scale_y < (PPA_LL_SR_SCALING_INT_MAX + 1) && config->scale_y >= (1.0 / PPA_LL_SR_SCALING_FRAG_MAX), + ESP_ERR_INVALID_ARG, TAG, "invalid scale"); + // byte/rgb swap with color mode only to (A)RGB color space? + + ppa_sr_engine_t *sr_engine = __containerof(ppa_engine, ppa_sr_engine_t, base); + // ESP_RETURN_ON_FALSE(sr_engine, ESP_FAIL, TAG, "SR engine not registered, please register through ppa_module_acquire first"); + + ppa_trans_t *new_trans_elm = (ppa_trans_t *)heap_caps_calloc(1, sizeof(ppa_trans_t), PPA_MEM_ALLOC_CAPS); + dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, PPA_MEM_ALLOC_CAPS); + dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)heap_caps_calloc(1, sizeof(dma2d_trans_config_t), PPA_MEM_ALLOC_CAPS); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)heap_caps_calloc(1, sizeof(ppa_dma2d_trans_on_picked_config_t), PPA_MEM_ALLOC_CAPS); + ppa_sr_transaction_t *ppa_trans_desc = (ppa_sr_transaction_t *)heap_caps_calloc(1, sizeof(ppa_sr_transaction_t), PPA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(new_trans_elm && dma_trans_elm && dma_trans_desc && trans_on_picked_desc && ppa_trans_desc, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); + if (mode == PPA_TRANS_MODE_BLOCKING) { + new_trans_elm->sem = xSemaphoreCreateBinary(); + ESP_GOTO_ON_FALSE(new_trans_elm->sem, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); + } + esp_rom_printf("new trans addr: 0x%x\n", new_trans_elm); + memcpy(ppa_trans_desc, config, sizeof(ppa_sr_trans_config_t)); + + trans_on_picked_desc->sr_desc = ppa_trans_desc; + trans_on_picked_desc->ppa_engine = &sr_engine->base; + trans_on_picked_desc->trans_elm = new_trans_elm; + trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_SR; + + dma_trans_desc->tx_channel_num = 1; + dma_trans_desc->rx_channel_num = 1; + // TODO: reserved channels + dma_trans_desc->user_config = (void *)trans_on_picked_desc; + dma_trans_desc->on_job_picked = ppa_sr_transaction_on_picked; + + new_trans_elm->trans_desc = dma_trans_desc; + new_trans_elm->dma_trans_placeholder = dma_trans_elm; + + portENTER_CRITICAL(&sr_engine->base.spinlock); + if (sr_engine->base.in_accepting_trans_state) { + // Send transaction into PPA SR engine queue + STAILQ_INSERT_TAIL(&sr_engine->base.trans_stailq, new_trans_elm, entry); + } else { + ret = ESP_FAIL; + } + portEXIT_CRITICAL(&sr_engine->base.spinlock); + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "SR engine cannot accept transaction now"); + goto err; + } + + TickType_t ticks_to_wait = (mode == PPA_TRANS_MODE_NON_BLOCKING) ? 0 : portMAX_DELAY; + if (xSemaphoreTake(sr_engine->base.sem, ticks_to_wait) == pdTRUE) { + // Check if the transaction has already been started from the ISR + // If so, then the transaction should have been removed from queue at this moment (transaction completed) + bool found = false; + ppa_trans_t *temp = NULL; + portENTER_CRITICAL(&sr_engine->base.spinlock); + STAILQ_FOREACH(temp, &sr_engine->base.trans_stailq, entry) { + if (temp == new_trans_elm) { + found = true; + break; + } + } + portEXIT_CRITICAL(&sr_engine->base.spinlock); + if (found) { + ret = ppa_dma2d_enqueue(new_trans_elm); + if (ret != ESP_OK) { + portENTER_CRITICAL(&sr_engine->base.spinlock); + STAILQ_REMOVE(&sr_engine->base.trans_stailq, new_trans_elm, ppa_trans_s, entry); + portEXIT_CRITICAL(&sr_engine->base.spinlock); + xSemaphoreGive(sr_engine->base.sem); + goto err; + } + } else { + xSemaphoreGive(sr_engine->base.sem); + } + } + + if (mode == PPA_TRANS_MODE_BLOCKING) { + xSemaphoreTake(new_trans_elm->sem, portMAX_DELAY); // Given in the ISR + // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) + ppa_recycle_transaction(new_trans_elm); + } + +err: + if (ret != ESP_OK) { + ppa_recycle_transaction(new_trans_elm); + } + return ret; +} + +esp_err_t ppa_do_blend(ppa_engine_handle_t ppa_engine, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode) +{ + return ESP_OK; +} + +esp_err_t ppa_do_fill(ppa_engine_handle_t ppa_engine, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode) +{ + return ESP_OK; +} diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index 7ab8249bbd..0b3e82f4d7 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -24,15 +24,8 @@ extern "C" { #define PPA_LL_BLEND0_CLUT_MEM_ADDR_OFFSET 0x400 #define PPA_LL_BLEND1_CLUT_MEM_ADDR_OFFSET 0x800 -/** - * @brief Enumeration of alpha value transformation mode - */ -typedef enum { - PPA_LL_RX_ALPHA_NO_CHANGE, /*!< Do not replace alpha value. If input format does not contain alpha info, alpha value 255 will be used. */ - PPA_LL_RX_ALPHA_FIX_VALUE, /*!< Replace the alpha value in received pixel with a new, fixed alpha value */ - PPA_LL_RX_ALPHA_SCALE, /*!< Scale the alpha value in received pixel to be a new alpha value */ - PPA_LL_RX_ALPHA_INVERT, /*!< Invert the alpha value in received pixel */ -} ppa_ll_rx_alpha_mode_t; +#define PPA_LL_SR_SCALING_INT_MAX PPA_SR_SCAL_X_INT_V +#define PPA_LL_SR_SCALING_FRAG_MAX PPA_SR_SCAL_X_FRAG_V /** * @brief Enumeration of PPA blending mode @@ -231,7 +224,7 @@ static inline void ppa_ll_sr_set_tx_color_mode(ppa_dev_t *dev, ppa_sr_color_mode } /** - * @brief Set YUV to RGB protocol when PPA SR pixel color space conversion from RX to TX is YUV to RGB + * @brief Set YUV to RGB protocol when PPA SR RX pixel color space is YUV * * @param dev Peripheral instance address * @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t @@ -252,7 +245,7 @@ static inline void ppa_ll_sr_set_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_rgb_ } /** - * @brief Set RGB to YUV protocol when PPA SR pixel color space conversion from RX to TX is RGB to YUV + * @brief Set RGB to YUV protocol when PPA SR TX pixel color space is YUV * * @param dev Peripheral instance address * @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t @@ -342,31 +335,32 @@ static inline void ppa_ll_sr_enable_rx_byte_swap(ppa_dev_t *dev, bool enable) * @brief Configure PPA SR alpha value transformation mode * * @param dev Peripheral instance address - * @param mode Alpha value transformation mode, one of the values in ppa_ll_rx_alpha_mode_t - * @param val When PPA_LL_RX_ALPHA_FIX_VALUE mode is selected, val is the alpha value to be replaced with (output_alpha = val) - * When PPA_LL_RX_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) + * @param mode Alpha value transformation mode, one of the values in ppa_alpha_mode_t + * @param val When PPA_ALPHA_FIX_VALUE mode is selected, val is the alpha value to be replaced with (output_alpha = val) + * When PPA_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) * When other modes are selected, this field is not used */ -static inline void ppa_ll_sr_configure_rx_alpha(ppa_dev_t *dev, ppa_ll_rx_alpha_mode_t mode, uint32_t val) +static inline void ppa_ll_sr_configure_rx_alpha(ppa_dev_t *dev, ppa_alpha_mode_t mode, uint32_t val) { switch (mode) { - case PPA_LL_RX_ALPHA_NO_CHANGE: + case PPA_ALPHA_NO_CHANGE: dev->sr_fix_alpha.sr_rx_alpha_mod = 0; dev->sr_fix_alpha.sr_rx_alpha_inv = 0; break; - case PPA_LL_RX_ALPHA_FIX_VALUE: + case PPA_ALPHA_FIX_VALUE: dev->sr_fix_alpha.sr_rx_alpha_mod = 1; HAL_FORCE_MODIFY_U32_REG_FIELD(dev->sr_fix_alpha, sr_rx_fix_alpha, val); dev->sr_fix_alpha.sr_rx_alpha_inv = 0; break; - case PPA_LL_RX_ALPHA_SCALE: + case PPA_ALPHA_SCALE: dev->sr_fix_alpha.sr_rx_alpha_mod = 2; HAL_FORCE_MODIFY_U32_REG_FIELD(dev->sr_fix_alpha, sr_rx_fix_alpha, val); dev->sr_fix_alpha.sr_rx_alpha_inv = 0; break; - case PPA_LL_RX_ALPHA_INVERT: + case PPA_ALPHA_INVERT: dev->sr_fix_alpha.sr_rx_alpha_mod = 0; dev->sr_fix_alpha.sr_rx_alpha_inv = 1; + break; default: // Unsupported alpha transformation mode abort(); @@ -570,31 +564,32 @@ static inline void ppa_ll_blend_enable_rx_fg_byte_swap(ppa_dev_t *dev, bool enab * @brief Configure PPA blending input background alpha value transformation mode * * @param dev Peripheral instance address - * @param mode Alpha value transformation mode, one of the values in ppa_ll_rx_alpha_mode_t - * @param val When PPA_LL_RX_ALPHA_FIX_VALUE mode is selected, val is the alpha value to be replaced with (output_alpha = val) - * When PPA_LL_RX_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) + * @param mode Alpha value transformation mode, one of the values in ppa_alpha_mode_t + * @param val When PPA_ALPHA_FIX_VALUE mode is selected, val is the alpha value to be replaced with (output_alpha = val) + * When PPA_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) * When other modes are selected, this field is not used */ -static inline void ppa_ll_blend_configure_rx_bg_alpha(ppa_dev_t *dev, ppa_ll_rx_alpha_mode_t mode, uint32_t val) +static inline void ppa_ll_blend_configure_rx_bg_alpha(ppa_dev_t *dev, ppa_alpha_mode_t mode, uint32_t val) { switch (mode) { - case PPA_LL_RX_ALPHA_NO_CHANGE: + case PPA_ALPHA_NO_CHANGE: dev->blend_fix_alpha.blend0_rx_alpha_mod = 0; dev->blend_fix_alpha.blend0_rx_alpha_inv = 0; break; - case PPA_LL_RX_ALPHA_FIX_VALUE: + case PPA_ALPHA_FIX_VALUE: dev->blend_fix_alpha.blend0_rx_alpha_mod = 1; HAL_FORCE_MODIFY_U32_REG_FIELD(dev->blend_fix_alpha, blend0_rx_fix_alpha, val); dev->blend_fix_alpha.blend0_rx_alpha_inv = 0; break; - case PPA_LL_RX_ALPHA_SCALE: + case PPA_ALPHA_SCALE: dev->blend_fix_alpha.blend0_rx_alpha_mod = 2; HAL_FORCE_MODIFY_U32_REG_FIELD(dev->blend_fix_alpha, blend0_rx_fix_alpha, val); dev->blend_fix_alpha.blend0_rx_alpha_inv = 0; break; - case PPA_LL_RX_ALPHA_INVERT: + case PPA_ALPHA_INVERT: dev->blend_fix_alpha.blend0_rx_alpha_mod = 0; dev->blend_fix_alpha.blend0_rx_alpha_inv = 1; + break; default: // Unsupported alpha transformation mode abort(); @@ -605,31 +600,32 @@ static inline void ppa_ll_blend_configure_rx_bg_alpha(ppa_dev_t *dev, ppa_ll_rx_ * @brief Configure PPA blending input foreground alpha value transformation mode * * @param dev Peripheral instance address - * @param mode Alpha value transformation mode, one of the values in ppa_ll_rx_alpha_mode_t - * @param val When PPA_LL_RX_ALPHA_FIX_VALUE mode is selected, val is the alpha value to be replaced with (output_alpha = val) - * When PPA_LL_RX_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) + * @param mode Alpha value transformation mode, one of the values in ppa_alpha_mode_t + * @param val When PPA_ALPHA_FIX_VALUE mode is selected, val is the alpha value to be replaced with (output_alpha = val) + * When PPA_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) * When other modes are selected, this field is not used */ -static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_ll_rx_alpha_mode_t mode, uint32_t val) +static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_alpha_mode_t mode, uint32_t val) { switch (mode) { - case PPA_LL_RX_ALPHA_NO_CHANGE: + case PPA_ALPHA_NO_CHANGE: dev->blend_fix_alpha.blend1_rx_alpha_mod = 0; dev->blend_fix_alpha.blend1_rx_alpha_inv = 0; break; - case PPA_LL_RX_ALPHA_FIX_VALUE: + case PPA_ALPHA_FIX_VALUE: dev->blend_fix_alpha.blend1_rx_alpha_mod = 1; HAL_FORCE_MODIFY_U32_REG_FIELD(dev->blend_fix_alpha, blend1_rx_fix_alpha, val); dev->blend_fix_alpha.blend1_rx_alpha_inv = 0; break; - case PPA_LL_RX_ALPHA_SCALE: + case PPA_ALPHA_SCALE: dev->blend_fix_alpha.blend1_rx_alpha_mod = 2; HAL_FORCE_MODIFY_U32_REG_FIELD(dev->blend_fix_alpha, blend1_rx_fix_alpha, val); dev->blend_fix_alpha.blend1_rx_alpha_inv = 0; break; - case PPA_LL_RX_ALPHA_INVERT: + case PPA_ALPHA_INVERT: dev->blend_fix_alpha.blend1_rx_alpha_mod = 0; dev->blend_fix_alpha.blend1_rx_alpha_inv = 1; + break; default: // Unsupported alpha transformation mode abort(); diff --git a/components/hal/include/hal/dma2d_types.h b/components/hal/include/hal/dma2d_types.h index 835fcf139c..f19ef2d816 100644 --- a/components/hal/include/hal/dma2d_types.h +++ b/components/hal/include/hal/dma2d_types.h @@ -174,6 +174,10 @@ typedef enum { // B = 1.164 *(Y - 16) + 2.114 *(Cb - 128) // //*********************BT709***********************************// +// R/G/B [0 ... 255] +// Y [16 ... 235] +// Cb/Cr [16 ... 240] + // 256 * Q = A[9:0] * x + B[10:0] * y + C[9:0] * z + D[17:0] #define DMA2D_COLOR_SPACE_CONV_PARAM_RGB2YUV_BT601 \ diff --git a/components/hal/include/hal/ppa_types.h b/components/hal/include/hal/ppa_types.h index 1be3e6c9e8..2f9d2678b5 100644 --- a/components/hal/include/hal/ppa_types.h +++ b/components/hal/include/hal/ppa_types.h @@ -39,6 +39,11 @@ typedef enum { PPA_SR_COLOR_MODE_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< PPA SR color mode: RGB888 */ PPA_SR_COLOR_MODE_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< PPA SR color mode: RGB565 */ PPA_SR_COLOR_MODE_YUV420 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV420), /*!< PPA SR color mode: YUV420 */ + PPA_SR_COLOR_MODE_YUV444 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV444), /*!< PPA SR color mode: YUV444 (limited range only)*/ + PPA_SR_COLOR_MODE_YUV422 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV422), /*!< PPA SR color mode: YUV422 (input only, limited range only) */ + // YUV444 and YUV422 not supported by PPA hardware, but seems like we can use 2D-DMA to do conversion before sending into and after coming out from the PPA module + // If in_pic is YUV444/422, then TX DMA channnel could do DMA2D_CSC_TX_YUV444/422_TO_RGB888_601/709, so PPA in_color_mode is RGB888 + // If out_pic is YUV444, then RX DMA channel could do DMA2D_CSC_RX_YUV420_TO_YUV444, so PPA out_color_mode is YUV420 } ppa_sr_color_mode_t; /** @@ -54,6 +59,16 @@ typedef enum { PPA_BLEND_COLOR_MODE_A4 = COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A4), /*!< PPA Blending color mode: A4, only available on blending foreground input */ } ppa_blend_color_mode_t; +/** + * @brief Enumeration of PPA alpha compositing mode + */ +typedef enum { + PPA_ALPHA_NO_CHANGE = 0, /*!< Do not replace alpha value. If input format does not contain alpha info, alpha value 255 will be used. */ + PPA_ALPHA_FIX_VALUE, /*!< Replace the alpha value in received pixel with a new, fixed alpha value */ + PPA_ALPHA_SCALE, /*!< Scale the alpha value in received pixel to be a new alpha value */ + PPA_ALPHA_INVERT, /*!< Invert the alpha value in received pixel */ +} ppa_alpha_mode_t; + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index caea62cf0a..d84bf8885a 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -267,6 +267,10 @@ config SOC_GP_LDO_SUPPORTED bool default y +config SOC_PPA_SUPPORTED + bool + default y + config SOC_LIGHT_SLEEP_SUPPORTED bool default y diff --git a/components/soc/esp32p4/include/soc/dma2d_channel.h b/components/soc/esp32p4/include/soc/dma2d_channel.h index f960fb1c4a..ed4ed0c8c5 100644 --- a/components/soc/esp32p4/include/soc/dma2d_channel.h +++ b/components/soc/esp32p4/include/soc/dma2d_channel.h @@ -14,6 +14,6 @@ #define SOC_DMA2D_TRIG_PERIPH_JPEG_TX (0) #define SOC_DMA2D_TRIG_PERIPH_PPA_SR_TX (1) -#define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_FG_TX (2) -#define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_BG_TX (3) +#define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_BG_TX (2) +#define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_FG_TX (3) #define SOC_DMA2D_TRIG_PERIPH_M2M_TX (-1) // Any value of 4 ~ 7, TX and RX do not have to use same ID value for M2M diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 069710449f..389059740b 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -88,7 +88,7 @@ // #define SOC_TOUCH_SENSOR_SUPPORTED 1 //TODO: IDF-7477 #define SOC_RNG_SUPPORTED 1 #define SOC_GP_LDO_SUPPORTED 1 // General purpose LDO -// #define SOC_PPA_SUPPORTED 1 //TODO: IDF-6878 +#define SOC_PPA_SUPPORTED 1 #define SOC_LIGHT_SLEEP_SUPPORTED 1 #define SOC_DEEP_SLEEP_SUPPORTED 1 #define SOC_PM_SUPPORTED 1 From 715004f07e86186094c82c79f3214a76db71769a Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Mon, 26 Feb 2024 11:12:51 +0800 Subject: [PATCH 02/15] feat(ppa): add PPA driver support for ESP32P4 Add invoker concept Add blend operation --- .../esp_driver_ppa/include/driver/ppa.h | 112 +++- components/esp_driver_ppa/src/ppa.c | 498 +++++++++++++++--- 2 files changed, 521 insertions(+), 89 deletions(-) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 781ff5e07a..9f343d8d21 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -15,23 +15,63 @@ extern "C" { #endif /** - * @brief Type of PPA engine handle + * @brief Type of PPA invoker handle */ -typedef struct ppa_engine_t *ppa_engine_handle_t; +typedef struct ppa_invoker_t *ppa_invoker_handle_t; +/** + * @brief PPA engine type flags + * + * These flags are supposed to be used to specify the PPA engines that are required by the invoker, so that the engines + * can be acquired when registering the invoker with `ppa_register_invoker`. + */ +#define PPA_ENGINE_FLAG_SR (1 << PPA_ENGINE_TYPE_SR) +#define PPA_ENGINE_FLAG_BLEND (1 << PPA_ENGINE_TYPE_BLEND) + +/** + * @brief A collection of configuration items that used for registering a PPA invoker + */ typedef struct { - ppa_engine_type_t engine; -} ppa_engine_config_t; + // ppa_engine_handle_t sr_engine; + // ppa_engine_handle_t blend_engine; + uint32_t engine_flag; /*!< Bitwise OR of `PPA_ENGINE_FLAG_*` flags indicating the required engines for the invoker */ + // done_cb + // user_data +} ppa_invoker_config_t; -esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_handle_t *ret_engine); +/** + * @brief Register a PPA invoker + * + * @param[in] config Pointer to a collection of configurations for the invoker + * @param[out] ret_invoker Returned invoker handle + * @return + * - ESP_OK: Register the PPA invoker successfully + * - ESP_ERR_INVALID_ARG: Register the PPA invoker failed because of invalid argument + * - ESP_ERR_NO_MEM: Register the PPA invoker failed because out of memory + * - ESP_FAIL: Register the PPA invoker failed because of other error + */ +esp_err_t ppa_register_invoker(const ppa_invoker_config_t *config, ppa_invoker_handle_t *ret_invoker); -esp_err_t ppa_engine_release(ppa_engine_handle_t ppa_engine); +/** + * @brief Unregister a PPA invoker + * + * @param[in] ppa_invoker PPA invoker handle, allocated by `ppa_register_invoker` + * @return + * - ESP_OK: Unregister the PPA invoker successfully + */ +esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker); +/** + * @brief Modes to perform the PPA operations + */ typedef enum { - PPA_TRANS_MODE_BLOCKING, - PPA_TRANS_MODE_NON_BLOCKING, + PPA_TRANS_MODE_BLOCKING, /*!< `ppa_do_xxx` function will block until the PPA operation is finished */ + PPA_TRANS_MODE_NON_BLOCKING, /*!< `ppa_do_xxx` function will return immediately after the PPA operation is pushed to the internal queue */ } ppa_trans_mode_t; +/** + * @brief A collection of configuration items to perform a PPA SR operation + */ typedef struct { void *in_buffer; // TODO: could be a buffer list, link descriptors together, process a batch // uint32_t batch_num; // However, is it necessary? psram can not store too many pictures @@ -77,7 +117,7 @@ typedef struct { /** * @brief Perform a scaling-and-rotating (SR) operation to a picture * - * @param[in] ppa_engine PPA engine handle with `PPA_ENGINE_TYPE_SR` as the engine type + * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA SR engine * @param[in] config Pointer to a collection of configurations for the SR operation, ppa_sr_trans_config_t * @param[in] mode Select one mode from ppa_trans_mode_t * @@ -87,18 +127,60 @@ typedef struct { * - ESP_ERR_NO_MEM: * - ESP_FAIL: */ -esp_err_t ppa_do_scale_and_rotate(ppa_engine_handle_t ppa_engine, const ppa_sr_trans_config_t *config, ppa_trans_mode_t mode); +esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr_trans_config_t *config, ppa_trans_mode_t mode); typedef struct { void *in_bg_buffer; + uint32_t in_bg_pic_w; + uint32_t in_bg_pic_h; + uint32_t in_bg_block_offset_x; + uint32_t in_bg_block_offset_y; + void *in_fg_buffer; + uint32_t in_fg_pic_w; + uint32_t in_fg_pic_h; + uint32_t in_fg_block_offset_x; + uint32_t in_fg_block_offset_y; + + uint32_t in_bg_fg_block_w; + uint32_t in_bg_fg_block_h; + void *out_buffer; + uint32_t out_pic_w; + uint32_t out_pic_h; + uint32_t out_block_offset_x; + uint32_t out_block_offset_y; + //out_block_w (auto or max/min(bg, fg)) + //out_block_h + + struct { + ppa_blend_color_mode_t mode; + bool rgb_swap; + bool byte_swap; + ppa_alpha_mode_t alpha_mode; + uint32_t alpha_value; + } in_bg_color; + + struct { + ppa_blend_color_mode_t mode; + bool rgb_swap; + bool byte_swap; + ppa_alpha_mode_t alpha_mode; + uint32_t alpha_value; + uint32_t fix_rgb_val; /*!< When in_fg_color.mode is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground. In RGB888 format (R[23:16], G[15: 8], B[7:0]). */ + } in_fg_color; + + struct { + ppa_blend_color_mode_t mode; + } out_color; + + // TODO: colorkey } ppa_blend_trans_config_t; /** * @brief Perform a blending operation to a picture * - * @param[in] ppa_engine PPA engine handle with `PPA_ENGINE_TYPE_BLEND` as the engine type + * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA Blend engine * @param[in] config Pointer to a collection of configurations for the blending operation, ppa_blend_trans_config_t * @param[in] mode Select one mode from ppa_trans_mode_t * @@ -108,7 +190,7 @@ typedef struct { * - ESP_ERR_NO_MEM: * - ESP_FAIL: */ -esp_err_t ppa_do_blend(ppa_engine_handle_t ppa_engine, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode); +esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode); typedef struct { void *out_buffer; @@ -117,7 +199,7 @@ typedef struct { /** * @brief Perform a filling operation to a picture * - * @param[in] ppa_engine PPA engine handle with `PPA_ENGINE_TYPE_BLEND` as the engine type + * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA Blend engine * @param[in] config Pointer to a collection of configurations for the filling operation, ppa_fill_trans_config_t * @param[in] mode Select one mode from ppa_trans_mode_t * @@ -127,7 +209,9 @@ typedef struct { * - ESP_ERR_NO_MEM: * - ESP_FAIL: */ -esp_err_t ppa_do_fill(ppa_engine_handle_t ppa_engine, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode); +esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode); + +// argb color conversion (bypass blend) // SR and Blending are independent, can work at the same time // Fill is in blend, so fill and blend cannot work at the same time diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c index 71b2365508..8c6de3cf64 100644 --- a/components/esp_driver_ppa/src/ppa.c +++ b/components/esp_driver_ppa/src/ppa.c @@ -50,14 +50,13 @@ void ppa_hal_deinit(ppa_hal_context_t *hal) // PPA module contains SR engine and Blending engine -// typedef struct ppa_group_t ppa_group_t; typedef struct ppa_engine_t ppa_engine_t; +typedef struct ppa_invoker_t ppa_invoker_t; + typedef ppa_sr_trans_config_t ppa_sr_transaction_t; -typedef struct { - -} ppa_blend_transaction_t; +typedef ppa_blend_trans_config_t ppa_blend_transaction_t; typedef struct ppa_trans_s { STAILQ_ENTRY(ppa_trans_s) entry; // link entry @@ -68,12 +67,14 @@ typedef struct ppa_trans_s { dma2d_trans_config_t *trans_desc; dma2d_trans_t *dma_trans_placeholder; SemaphoreHandle_t sem; + ppa_invoker_t *invoker; } ppa_trans_t; typedef struct { union { ppa_sr_transaction_t *sr_desc; ppa_blend_transaction_t *blend_desc; + // ppa_fill_transaction_t }; ppa_engine_t *ppa_engine; ppa_trans_t *trans_elm; @@ -85,11 +86,10 @@ typedef struct { } ppa_dma2d_trans_on_picked_config_t; struct ppa_engine_t { - // ppa_group_t *group; ppa_engine_type_t type; portMUX_TYPE spinlock; SemaphoreHandle_t sem; - bool in_accepting_trans_state; + // bool in_accepting_trans_state; // pending transactions queue? union ppa_sr_trans_config_t, ppa_blending_trans_config_t? handle when to free (at trans start or at trans end?) STAILQ_HEAD(trans, ppa_trans_s) trans_stailq; // link head of pending transactions for the PPA engine // callback func? Here or in the struct above? @@ -109,24 +109,23 @@ typedef struct ppa_blend_engine_t { dma2d_descriptor_t *dma_rx_desc; } ppa_blend_engine_t; +struct ppa_invoker_t { + ppa_engine_t *sr_engine; + ppa_engine_t *blending_engine; + uint32_t sr_trans_cnt; + uint32_t blending_trans_cnt; + portMUX_TYPE spinlock; + bool in_accepting_trans_state; + // done_cb + // user_data +}; + // how to free and how to push next trans into dma2d queue after engine is free (callback triggered) // -// struct ppa_group_t { -// int group_id; -// portMUX_TYPE spinlock; -// ppa_hal_context_t hal; -// dma2d_pool_handle_t dma2d_pool_handle; -// ppa_sr_engine_t *sr; -// ppa_blend_engine_t *blending; -// uint32_t sr_engine_ref_count; -// uint32_t blend_engine_ref_count; -// }; - typedef struct ppa_platform_t { _lock_t mutex; // platform level mutex lock to protect the ppa_module_acquire/ppa_module_release process portMUX_TYPE spinlock; // platform level spinlock - // ppa_group_t *group[PPA_LL_GROUPS]; // array of PPA group instances ppa_hal_context_t hal; dma2d_pool_handle_t dma2d_pool_handle; ppa_sr_engine_t *sr; @@ -139,13 +138,19 @@ typedef struct ppa_platform_t { // PPA driver platform static ppa_platform_t s_platform = { .spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED, - // .group = {}, }; #define PPA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) // TODO... +typedef struct { + ppa_engine_type_t engine; +} ppa_engine_config_t; + +static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine); +static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine); + // TODO: acquire pm_lock? -esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_handle_t *ret_engine) +static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine) { esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(config && ret_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); @@ -170,12 +175,11 @@ esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_handl if (sr_engine && sr_sem && sr_tx_dma_desc && sr_rx_dma_desc) { sr_engine->dma_tx_desc = sr_tx_dma_desc; sr_engine->dma_rx_desc = sr_rx_dma_desc; - // sr_engine->base.group = s_platform.group[group_id]; sr_engine->base.type = PPA_ENGINE_TYPE_SR; sr_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; sr_engine->base.sem = sr_sem; xSemaphoreGive(sr_engine->base.sem); - sr_engine->base.in_accepting_trans_state = true; + // sr_engine->base.in_accepting_trans_state = true; STAILQ_INIT(&sr_engine->base.trans_stailq); // sr_engine->base.event_cbs s_platform.sr = sr_engine; @@ -207,12 +211,11 @@ esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_handl blending_engine->dma_tx_bg_desc = blending_tx_bg_dma_desc; blending_engine->dma_tx_fg_desc = blending_tx_fg_dma_desc; blending_engine->dma_rx_desc = blending_rx_dma_desc; - // blending_engine->base.group = s_platform.group[group_id]; blending_engine->base.type = PPA_ENGINE_TYPE_BLEND; blending_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; blending_engine->base.sem = blending_sem; xSemaphoreGive(blending_engine->base.sem); - blending_engine->base.in_accepting_trans_state = true; + // blending_engine->base.in_accepting_trans_state = true; STAILQ_INIT(&blending_engine->base.trans_stailq); // blending_engine->base.event_cbs s_platform.blending = blending_engine; @@ -267,7 +270,7 @@ esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_handl return ret; } -esp_err_t ppa_engine_release(ppa_engine_handle_t ppa_engine) +static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) { esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(ppa_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); @@ -277,12 +280,13 @@ esp_err_t ppa_engine_release(ppa_engine_handle_t ppa_engine) ppa_sr_engine_t *sr_engine = __containerof(ppa_engine, ppa_sr_engine_t, base); s_platform.sr_engine_ref_count--; if (s_platform.sr_engine_ref_count == 0) { - // Stop accepting new transactions to SR engine - portENTER_CRITICAL(&sr_engine->base.spinlock); - sr_engine->base.in_accepting_trans_state = false; - portEXIT_CRITICAL(&sr_engine->base.spinlock); - // Wait until all transactions get processed - while (!STAILQ_EMPTY(&sr_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide + // // Stop accepting new transactions to SR engine + // portENTER_CRITICAL(&sr_engine->base.spinlock); + // sr_engine->base.in_accepting_trans_state = false; + // portEXIT_CRITICAL(&sr_engine->base.spinlock); + // // Wait until all transactions get processed + // while (!STAILQ_EMPTY(&sr_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide + assert(STAILQ_EMPTY(&sr_engine->base.trans_stailq)); // Now, time to free s_platform.sr = NULL; free(sr_engine->dma_tx_desc); @@ -294,12 +298,13 @@ esp_err_t ppa_engine_release(ppa_engine_handle_t ppa_engine) ppa_blend_engine_t *blending_engine = __containerof(ppa_engine, ppa_blend_engine_t, base); s_platform.blend_engine_ref_count--; if (s_platform.blend_engine_ref_count == 0) { - // Stop accepting new transactions to blending engine - portENTER_CRITICAL(&blending_engine->base.spinlock); - blending_engine->base.in_accepting_trans_state = false; - portEXIT_CRITICAL(&blending_engine->base.spinlock); - // Wait until all transactions get processed - while (!STAILQ_EMPTY(&blending_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide + // // Stop accepting new transactions to blending engine + // portENTER_CRITICAL(&blending_engine->base.spinlock); + // blending_engine->base.in_accepting_trans_state = false; + // portEXIT_CRITICAL(&blending_engine->base.spinlock); + // // Wait until all transactions get processed + // while (!STAILQ_EMPTY(&blending_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide + assert(STAILQ_EMPTY(&blending_engine->base.trans_stailq)); // Now, time to free s_platform.blending = NULL; free(blending_engine->dma_tx_bg_desc); @@ -329,6 +334,60 @@ esp_err_t ppa_engine_release(ppa_engine_handle_t ppa_engine) return ret; } +esp_err_t ppa_register_invoker(const ppa_invoker_config_t *config, ppa_invoker_handle_t *ret_invoker) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(config && ret_invoker, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + ppa_invoker_t *invoker = (ppa_invoker_t *)heap_caps_calloc(1, sizeof(ppa_invoker_t), PPA_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(invoker, ESP_ERR_NO_MEM, TAG, "no mem to register invoker"); + + invoker->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + invoker->in_accepting_trans_state = true; + if (config->engine_flag & PPA_ENGINE_FLAG_SR) { + ppa_engine_config_t engine_config = { + .engine = PPA_ENGINE_TYPE_SR, + }; + ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->sr_engine), err, TAG, "unable to acquire SR engine"); + } + if (config->engine_flag & PPA_ENGINE_FLAG_BLEND) { + ppa_engine_config_t engine_config = { + .engine = PPA_ENGINE_TYPE_BLEND, + }; + ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->blending_engine), err, TAG, "unable to acquire Blending engine"); + } + *ret_invoker = invoker; + +err: + if (ret != ESP_OK) { + ppa_unregister_invoker(invoker); + } + return ret; +} + +esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) +{ + ESP_RETURN_ON_FALSE(ppa_invoker, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + bool do_unregister = false; + portENTER_CRITICAL(&ppa_invoker->spinlock); + if (ppa_invoker->sr_trans_cnt == 0 && ppa_invoker->blending_trans_cnt == 0) { + ppa_invoker->in_accepting_trans_state = false; + do_unregister = true; + } + portEXIT_CRITICAL(&ppa_invoker->spinlock); + ESP_RETURN_ON_FALSE(do_unregister, ESP_ERR_INVALID_STATE, TAG, "invoker still has unprocessed trans"); + + if (ppa_invoker->sr_engine) { + ppa_engine_release(ppa_invoker->sr_engine); + } + if (ppa_invoker->blending_engine) { + ppa_engine_release(ppa_invoker->blending_engine); + } + free(ppa_invoker); + return ESP_OK; +} + // // TODO: pm lock? // esp_err_t ppa_module_acquire(const ppa_group_alloc_config_t *config, ppa_group_handle_t *ret_group) // { @@ -646,10 +705,10 @@ static bool ppa_sr_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_ bool need_yield = false; BaseType_t HPTaskAwoken; ppa_trans_t *trans_elm = (ppa_trans_t *)user_data; + ppa_invoker_t *invoker = trans_elm->invoker; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SR); ppa_sr_engine_t *sr_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_sr_engine_t, base); - // ppa_group_t *ppa_group = sr_engine->base.group; ppa_trans_t *next_start_trans = NULL; portENTER_CRITICAL_ISR(&sr_engine->base.spinlock); @@ -675,20 +734,23 @@ static bool ppa_sr_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_ ppa_recycle_transaction(trans_elm); } + portENTER_CRITICAL_ISR(&invoker->spinlock); + invoker->sr_trans_cnt--; + portEXIT_CRITICAL_ISR(&invoker->spinlock); + // TODO: how to notify non-blocking transaction return need_yield; } -static bool ppa_sr_transaction_on_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) { - assert(channel_num == 2 && dma2d_chans && user_config); + assert(num_chans == 2 && dma2d_chans && user_config); ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SR && trans_on_picked_desc->sr_desc && trans_on_picked_desc->ppa_engine); ppa_sr_transaction_t *sr_trans_desc = trans_on_picked_desc->sr_desc; ppa_sr_engine_t *sr_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_sr_engine_t, base); - // ppa_group_t *ppa_group = sr_engine->base.group; // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) free(trans_on_picked_desc->trans_elm->dma_trans_placeholder); @@ -705,14 +767,16 @@ static bool ppa_sr_transaction_on_picked(uint32_t channel_num, const dma2d_trans // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) // Write back in_buffer - uint32_t in_buffer_len = sr_trans_desc->in_pic_w * sr_trans_desc->in_pic_h * color_hal_pixel_format_get_bit_depth((color_space_pixel_format_t) { - .color_type_id = sr_trans_desc->in_color.mode - }) / 8; + color_space_pixel_format_t in_pixel_format = { + .color_type_id = sr_trans_desc->in_color.mode, + }; + uint32_t in_buffer_len = sr_trans_desc->in_pic_w * sr_trans_desc->in_pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; esp_cache_msync(sr_trans_desc->in_buffer, in_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); // Invalidate out_buffer - uint32_t out_buffer_len = sr_trans_desc->out_pic_w * sr_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth((color_space_pixel_format_t) { - .color_type_id = sr_trans_desc->out_color.mode - }) / 8; + color_space_pixel_format_t out_pixel_format = { + .color_type_id = sr_trans_desc->out_color.mode, + }; + uint32_t out_buffer_len = sr_trans_desc->out_pic_w * sr_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; esp_cache_msync(sr_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); // Fill 2D-DMA descriptors @@ -724,17 +788,14 @@ static bool ppa_sr_transaction_on_picked(uint32_t channel_num, const dma2d_trans sr_engine->dma_tx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; sr_engine->dma_tx_desc->va_size = sr_trans_desc->in_pic_h; sr_engine->dma_tx_desc->ha_length = sr_trans_desc->in_pic_w; - sr_engine->dma_tx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value((color_space_pixel_format_t) { - .color_type_id = sr_trans_desc->in_color.mode - }); // check in 912 whether this field can be ignored (911 seems cannot) No! Why? + sr_engine->dma_tx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_pixel_format); sr_engine->dma_tx_desc->y = sr_trans_desc->in_block_offset_y; sr_engine->dma_tx_desc->x = sr_trans_desc->in_block_offset_x; sr_engine->dma_tx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; sr_engine->dma_tx_desc->buffer = (void *)sr_trans_desc->in_buffer; sr_engine->dma_tx_desc->next = NULL; - // sr_engine->dma_rx_desc->vb_size = sr_trans_desc->in_block_h; // check in 912 whether this field can be ignored (911 seems cannot) No! Why? - // sr_engine->dma_rx_desc->hb_length = sr_trans_desc->in_block_w; // check in 912 whether this field can be ignored (911 seems cannot) No! Why? + // vb_size, hb_length can be any value (auto writeback). However, if vb_size/hb_length is 0, it triggers 2D-DMA DESC_ERROR interrupt, and dma2d driver will automatically ends the transaction. Therefore, to avoid this, we set them to 1. sr_engine->dma_rx_desc->vb_size = 1; sr_engine->dma_rx_desc->hb_length = 1; sr_engine->dma_rx_desc->err_eof = 0; @@ -743,9 +804,7 @@ static bool ppa_sr_transaction_on_picked(uint32_t channel_num, const dma2d_trans sr_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; sr_engine->dma_rx_desc->va_size = sr_trans_desc->out_pic_h; sr_engine->dma_rx_desc->ha_length = sr_trans_desc->out_pic_w; - sr_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value((color_space_pixel_format_t) { - .color_type_id = sr_trans_desc->out_color.mode - }); // check in 912 whether this field can be ignored (911 seems cannot) No! Why? + // pbyte can be any value sr_engine->dma_rx_desc->y = sr_trans_desc->out_block_offset_y; sr_engine->dma_rx_desc->x = sr_trans_desc->out_block_offset_x; sr_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; @@ -755,6 +814,9 @@ static bool ppa_sr_transaction_on_picked(uint32_t channel_num, const dma2d_trans esp_cache_msync((void *)sr_engine->dma_tx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); esp_cache_msync((void *)sr_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + // printf("desc addr: %p\n", sr_engine->dma_rx_desc); + // printf("desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)sr_engine->dma_rx_desc, *(uint32_t *)((uint32_t)sr_engine->dma_rx_desc + 4), *(uint32_t *)((uint32_t)sr_engine->dma_rx_desc + 8), *(uint32_t *)((uint32_t)sr_engine->dma_rx_desc + 12), *(uint32_t *)((uint32_t)sr_engine->dma_rx_desc + 16)); + // Configure 2D-DMA channels dma2d_trigger_t trig_periph = { .periph = DMA2D_TRIG_PERIPH_PPA_SR, @@ -772,12 +834,6 @@ static bool ppa_sr_transaction_on_picked(uint32_t channel_num, const dma2d_trans dma2d_set_transfer_ability(dma2d_tx_chan, &dma_transfer_ability); dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); - // TODO: configuring this doesn't seem helping anything? Shouldn't it related to descriptor pbyte? - dma2d_strategy_config_t dma_strategy = { - .auto_update_desc = true, - }; - dma2d_apply_strategy(dma2d_tx_chan, &dma_strategy); - // YUV444 and YUV422 are not supported by PPA module, need to utilize 2D-DMA color space conversion feature to do a conversion ppa_sr_color_mode_t ppa_in_color_mode = sr_trans_desc->in_color.mode; if (ppa_in_color_mode == PPA_SR_COLOR_MODE_YUV444) { @@ -839,6 +895,7 @@ static bool ppa_sr_transaction_on_picked(uint32_t channel_num, const dma2d_trans ppa_ll_sr_configure_rx_alpha(s_platform.hal.dev, sr_trans_desc->alpha_mode, sr_trans_desc->alpha_value); // TODO: sr_macro_bk_ro_bypass + // PPA.sr_byte_order.sr_macro_bk_ro_bypass = 1; ppa_ll_sr_set_rotation_angle(s_platform.hal.dev, sr_trans_desc->rotation_angle); ppa_ll_sr_set_scaling_x(s_platform.hal.dev, (uint32_t)sr_trans_desc->scale_x, (uint32_t)(sr_trans_desc->scale_x * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX); ppa_ll_sr_set_scaling_y(s_platform.hal.dev, (uint32_t)sr_trans_desc->scale_y, (uint32_t)(sr_trans_desc->scale_y * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX); @@ -847,18 +904,15 @@ static bool ppa_sr_transaction_on_picked(uint32_t channel_num, const dma2d_trans ppa_ll_sr_start(s_platform.hal.dev); - // dma2d_start(dma2d_tx_chan); - // dma2d_start(dma2d_rx_chan); - // No need to yield return false; } -esp_err_t ppa_do_scale_and_rotate(ppa_engine_handle_t ppa_engine, const ppa_sr_trans_config_t *config, ppa_trans_mode_t mode) +esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr_trans_config_t *config, ppa_trans_mode_t mode) { esp_err_t ret = ESP_OK; - ESP_RETURN_ON_FALSE(ppa_engine && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_engine->type == PPA_ENGINE_TYPE_SR, ESP_ERR_INVALID_ARG, TAG, "wrong engine handle"); + ESP_RETURN_ON_FALSE(ppa_invoker && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_invoker->sr_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to SR engine"); ESP_RETURN_ON_FALSE(mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // Any restrictions on in/out buffer address? alignment? alignment restriction comes from cache, its addr and size need to be aligned to cache line size on 912! // buffer on stack/heap @@ -870,7 +924,7 @@ esp_err_t ppa_do_scale_and_rotate(ppa_engine_handle_t ppa_engine, const ppa_sr_t ESP_ERR_INVALID_ARG, TAG, "invalid scale"); // byte/rgb swap with color mode only to (A)RGB color space? - ppa_sr_engine_t *sr_engine = __containerof(ppa_engine, ppa_sr_engine_t, base); + ppa_sr_engine_t *sr_engine = __containerof(ppa_invoker->sr_engine, ppa_sr_engine_t, base); // ESP_RETURN_ON_FALSE(sr_engine, ESP_FAIL, TAG, "SR engine not registered, please register through ppa_module_acquire first"); ppa_trans_t *new_trans_elm = (ppa_trans_t *)heap_caps_calloc(1, sizeof(ppa_trans_t), PPA_MEM_ALLOC_CAPS); @@ -899,18 +953,20 @@ esp_err_t ppa_do_scale_and_rotate(ppa_engine_handle_t ppa_engine, const ppa_sr_t new_trans_elm->trans_desc = dma_trans_desc; new_trans_elm->dma_trans_placeholder = dma_trans_elm; + new_trans_elm->invoker = ppa_invoker; - portENTER_CRITICAL(&sr_engine->base.spinlock); - if (sr_engine->base.in_accepting_trans_state) { + portENTER_CRITICAL(&ppa_invoker->spinlock); + if (ppa_invoker->in_accepting_trans_state) { // Send transaction into PPA SR engine queue STAILQ_INSERT_TAIL(&sr_engine->base.trans_stailq, new_trans_elm, entry); + ppa_invoker->sr_trans_cnt++; } else { ret = ESP_FAIL; } - portEXIT_CRITICAL(&sr_engine->base.spinlock); + portEXIT_CRITICAL(&ppa_invoker->spinlock); if (ret != ESP_OK) { - ESP_LOGE(TAG, "SR engine cannot accept transaction now"); + ESP_LOGE(TAG, "The invoker cannot accept transaction now"); goto err; } @@ -943,6 +999,9 @@ esp_err_t ppa_do_scale_and_rotate(ppa_engine_handle_t ppa_engine, const ppa_sr_t } if (mode == PPA_TRANS_MODE_BLOCKING) { + // while (1) { + // printf("ppa intr: %ld\n", PPA.int_raw.val); + // } xSemaphoreTake(new_trans_elm->sem, portMAX_DELAY); // Given in the ISR // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) ppa_recycle_transaction(new_trans_elm); @@ -955,12 +1014,301 @@ err: return ret; } -esp_err_t ppa_do_blend(ppa_engine_handle_t ppa_engine, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode) +static bool ppa_blend_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) { - return ESP_OK; + bool need_yield = false; + BaseType_t HPTaskAwoken; + ppa_trans_t *trans_elm = (ppa_trans_t *)user_data; + ppa_invoker_t *invoker = trans_elm->invoker; + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; + assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND); + ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); + + ppa_trans_t *next_start_trans = NULL; + portENTER_CRITICAL_ISR(&blend_engine->base.spinlock); + // Remove this transaction from transaction queue + STAILQ_REMOVE(&blend_engine->base.trans_stailq, trans_elm, ppa_trans_s, entry); + next_start_trans = STAILQ_FIRST(&blend_engine->base.trans_stailq); + portEXIT_CRITICAL_ISR(&blend_engine->base.spinlock); + + // If there is next trans in PPA engine queue, send it to DMA queue; otherwise, release the engine semaphore + if (next_start_trans) { + ppa_dma2d_enqueue(next_start_trans); + } else { + xSemaphoreGiveFromISR(blend_engine->base.sem, &HPTaskAwoken); + need_yield |= (HPTaskAwoken == pdTRUE); + } + // Recycle transaction or give transaction semaphore + if (trans_elm->sem != NULL) { + xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); + need_yield |= (HPTaskAwoken == pdTRUE); + } else { + ppa_recycle_transaction(trans_elm); + } + + portENTER_CRITICAL_ISR(&invoker->spinlock); + invoker->blending_trans_cnt--; + portEXIT_CRITICAL_ISR(&invoker->spinlock); + + // TODO: how to notify non-blocking transaction + + return need_yield; } -esp_err_t ppa_do_fill(ppa_engine_handle_t ppa_engine, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode) +static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +{ + assert(num_chans == 3 && dma2d_chans && user_config); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; + assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->blend_desc && trans_on_picked_desc->ppa_engine); + + ppa_blend_transaction_t *blend_trans_desc = trans_on_picked_desc->blend_desc; + ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); + + // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) + free(trans_on_picked_desc->trans_elm->dma_trans_placeholder); + + // Get the required 2D-DMA channel handles + dma2d_channel_handle_t dma2d_tx_bg_chan = NULL; + dma2d_channel_handle_t dma2d_tx_fg_chan = NULL; + dma2d_channel_handle_t dma2d_rx_chan = NULL; + for (uint32_t i = 0; i < num_chans; i++) { + if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_TX) { + if (!dma2d_tx_bg_chan) { + dma2d_tx_bg_chan = dma2d_chans[i].chan; + } else { + dma2d_tx_fg_chan = dma2d_chans[i].chan; + } + } + if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_RX) { + dma2d_rx_chan = dma2d_chans[i].chan; + } + } + assert(dma2d_tx_bg_chan && dma2d_tx_fg_chan && dma2d_rx_chan); + + // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) + // Write back in_bg_buffer, in_fg_buffer + color_space_pixel_format_t in_bg_pixel_format = { + .color_type_id = blend_trans_desc->in_bg_color.mode, + }; + uint32_t in_bg_buffer_len = blend_trans_desc->in_bg_pic_w * blend_trans_desc->in_bg_pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; + esp_cache_msync(blend_trans_desc->in_bg_buffer, in_bg_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + color_space_pixel_format_t in_fg_pixel_format = { + .color_type_id = blend_trans_desc->in_fg_color.mode, + }; + uint32_t in_fg_buffer_len = blend_trans_desc->in_fg_pic_w * blend_trans_desc->in_fg_pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; + esp_cache_msync(blend_trans_desc->in_fg_buffer, in_fg_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + // Invalidate out_buffer + color_space_pixel_format_t out_pixel_format = { + .color_type_id = blend_trans_desc->out_color.mode, + }; + uint32_t out_buffer_len = blend_trans_desc->out_pic_w * blend_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + esp_cache_msync(blend_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + + // Fill 2D-DMA descriptors + blend_engine->dma_tx_bg_desc->vb_size = blend_trans_desc->in_bg_fg_block_h; + blend_engine->dma_tx_bg_desc->hb_length = blend_trans_desc->in_bg_fg_block_w; + blend_engine->dma_tx_bg_desc->err_eof = 0; + blend_engine->dma_tx_bg_desc->dma2d_en = 1; + blend_engine->dma_tx_bg_desc->suc_eof = 1; + blend_engine->dma_tx_bg_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + blend_engine->dma_tx_bg_desc->va_size = blend_trans_desc->in_bg_pic_h; + blend_engine->dma_tx_bg_desc->ha_length = blend_trans_desc->in_bg_pic_w; + blend_engine->dma_tx_bg_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_bg_pixel_format); + blend_engine->dma_tx_bg_desc->y = blend_trans_desc->in_bg_block_offset_y; + blend_engine->dma_tx_bg_desc->x = blend_trans_desc->in_bg_block_offset_x; + blend_engine->dma_tx_bg_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + blend_engine->dma_tx_bg_desc->buffer = (void *)blend_trans_desc->in_bg_buffer; + blend_engine->dma_tx_bg_desc->next = NULL; + + blend_engine->dma_tx_fg_desc->vb_size = blend_trans_desc->in_bg_fg_block_h; + blend_engine->dma_tx_fg_desc->hb_length = blend_trans_desc->in_bg_fg_block_w; + blend_engine->dma_tx_fg_desc->err_eof = 0; + blend_engine->dma_tx_fg_desc->dma2d_en = 1; + blend_engine->dma_tx_fg_desc->suc_eof = 1; + blend_engine->dma_tx_fg_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + blend_engine->dma_tx_fg_desc->va_size = blend_trans_desc->in_fg_pic_h; + blend_engine->dma_tx_fg_desc->ha_length = blend_trans_desc->in_fg_pic_w; + blend_engine->dma_tx_fg_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_fg_pixel_format); + blend_engine->dma_tx_fg_desc->y = blend_trans_desc->in_fg_block_offset_y; + blend_engine->dma_tx_fg_desc->x = blend_trans_desc->in_fg_block_offset_x; + blend_engine->dma_tx_fg_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + blend_engine->dma_tx_fg_desc->buffer = (void *)blend_trans_desc->in_fg_buffer; + blend_engine->dma_tx_fg_desc->next = NULL; + + blend_engine->dma_rx_desc->vb_size = blend_trans_desc->in_bg_fg_block_h; + blend_engine->dma_rx_desc->hb_length = blend_trans_desc->in_bg_fg_block_w; + blend_engine->dma_rx_desc->err_eof = 0; + blend_engine->dma_rx_desc->dma2d_en = 1; + blend_engine->dma_rx_desc->suc_eof = 1; + blend_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + blend_engine->dma_rx_desc->va_size = blend_trans_desc->out_pic_h; + blend_engine->dma_rx_desc->ha_length = blend_trans_desc->out_pic_w; + blend_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format); + blend_engine->dma_rx_desc->y = blend_trans_desc->out_block_offset_y; + blend_engine->dma_rx_desc->x = blend_trans_desc->out_block_offset_x; + blend_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + blend_engine->dma_rx_desc->buffer = (void *)blend_trans_desc->out_buffer; + blend_engine->dma_rx_desc->next = NULL; + + esp_cache_msync((void *)blend_engine->dma_tx_bg_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)blend_engine->dma_tx_fg_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)blend_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + + // printf("tx bg desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)blend_engine->dma_tx_bg_desc, *(uint32_t *)((uint32_t)blend_engine->dma_tx_bg_desc + 4), *(uint32_t *)((uint32_t)blend_engine->dma_tx_bg_desc + 8), *(uint32_t *)((uint32_t)blend_engine->dma_tx_bg_desc + 12), *(uint32_t *)((uint32_t)blend_engine->dma_tx_bg_desc + 16)); + // printf("tx fg desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)blend_engine->dma_tx_fg_desc, *(uint32_t *)((uint32_t)blend_engine->dma_tx_fg_desc + 4), *(uint32_t *)((uint32_t)blend_engine->dma_tx_fg_desc + 8), *(uint32_t *)((uint32_t)blend_engine->dma_tx_fg_desc + 12), *(uint32_t *)((uint32_t)blend_engine->dma_tx_fg_desc + 16)); + // printf("rx desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)blend_engine->dma_rx_desc, *(uint32_t *)((uint32_t)blend_engine->dma_rx_desc + 4), *(uint32_t *)((uint32_t)blend_engine->dma_rx_desc + 8), *(uint32_t *)((uint32_t)blend_engine->dma_rx_desc + 12), *(uint32_t *)((uint32_t)blend_engine->dma_rx_desc + 16)); + + // Configure 2D-DMA channels + dma2d_trigger_t trig_periph = { + .periph = DMA2D_TRIG_PERIPH_PPA_BLEND, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_BG_TX, + }; + dma2d_connect(dma2d_tx_bg_chan, &trig_periph); + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_FG_TX; + dma2d_connect(dma2d_tx_fg_chan, &trig_periph); + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_RX; + dma2d_connect(dma2d_rx_chan, &trig_periph); + + dma2d_transfer_ability_t dma_transfer_ability = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + }; + dma2d_set_transfer_ability(dma2d_tx_bg_chan, &dma_transfer_ability); + dma2d_set_transfer_ability(dma2d_tx_fg_chan, &dma_transfer_ability); + dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); + + dma2d_rx_event_callbacks_t dma_event_cbs = { + .on_recv_eof = ppa_blend_transaction_done_cb, + }; + dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); + + ppa_ll_blend_reset(s_platform.hal.dev); + + dma2d_set_desc_addr(dma2d_tx_bg_chan, (intptr_t)blend_engine->dma_tx_bg_desc); + dma2d_set_desc_addr(dma2d_tx_fg_chan, (intptr_t)blend_engine->dma_tx_fg_desc); + dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)blend_engine->dma_rx_desc); + dma2d_start(dma2d_tx_bg_chan); + dma2d_start(dma2d_tx_fg_chan); + dma2d_start(dma2d_rx_chan); + + // Configure PPA Blending engine + ppa_ll_blend_set_rx_bg_color_mode(s_platform.hal.dev, blend_trans_desc->in_bg_color.mode); + ppa_ll_blend_enable_rx_bg_byte_swap(s_platform.hal.dev, blend_trans_desc->in_bg_color.byte_swap); + ppa_ll_blend_enable_rx_bg_rgb_swap(s_platform.hal.dev, blend_trans_desc->in_bg_color.rgb_swap); + ppa_ll_blend_configure_rx_bg_alpha(s_platform.hal.dev, blend_trans_desc->in_bg_color.alpha_mode, blend_trans_desc->in_bg_color.alpha_value); + + ppa_ll_blend_set_rx_fg_color_mode(s_platform.hal.dev, blend_trans_desc->in_fg_color.mode); + if (COLOR_SPACE_TYPE(blend_trans_desc->in_fg_color.mode) == COLOR_SPACE_ALPHA) { + ppa_ll_blend_set_rx_fg_fix_rgb(s_platform.hal.dev, blend_trans_desc->in_fg_color.fix_rgb_val); + } + ppa_ll_blend_enable_rx_fg_byte_swap(s_platform.hal.dev, blend_trans_desc->in_fg_color.byte_swap); + ppa_ll_blend_enable_rx_fg_rgb_swap(s_platform.hal.dev, blend_trans_desc->in_fg_color.rgb_swap); + ppa_ll_blend_configure_rx_fg_alpha(s_platform.hal.dev, blend_trans_desc->in_fg_color.alpha_mode, blend_trans_desc->in_fg_color.alpha_value); + + ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, blend_trans_desc->out_color.mode); + + ppa_ll_blend_start(s_platform.hal.dev, PPA_LL_BLEND_TRANS_MODE_BLEND); + + // No need to yield + return false; +} + +esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(ppa_invoker && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); + ESP_RETURN_ON_FALSE(mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + + ppa_blend_engine_t *blend_engine = __containerof(ppa_invoker->blending_engine, ppa_blend_engine_t, base); + + ppa_trans_t *new_trans_elm = (ppa_trans_t *)heap_caps_calloc(1, sizeof(ppa_trans_t), PPA_MEM_ALLOC_CAPS); + dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, PPA_MEM_ALLOC_CAPS); + dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)heap_caps_calloc(1, sizeof(dma2d_trans_config_t), PPA_MEM_ALLOC_CAPS); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)heap_caps_calloc(1, sizeof(ppa_dma2d_trans_on_picked_config_t), PPA_MEM_ALLOC_CAPS); + ppa_blend_transaction_t *ppa_trans_desc = (ppa_blend_transaction_t *)heap_caps_calloc(1, sizeof(ppa_blend_transaction_t), PPA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(new_trans_elm && dma_trans_elm && dma_trans_desc && trans_on_picked_desc && ppa_trans_desc, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); + if (mode == PPA_TRANS_MODE_BLOCKING) { + new_trans_elm->sem = xSemaphoreCreateBinary(); + ESP_GOTO_ON_FALSE(new_trans_elm->sem, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); + } + memcpy(ppa_trans_desc, config, sizeof(ppa_blend_trans_config_t)); + + trans_on_picked_desc->blend_desc = ppa_trans_desc; + trans_on_picked_desc->ppa_engine = &blend_engine->base; + trans_on_picked_desc->trans_elm = new_trans_elm; + trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; + + dma_trans_desc->tx_channel_num = 2; + dma_trans_desc->rx_channel_num = 1; + dma_trans_desc->user_config = (void *)trans_on_picked_desc; + dma_trans_desc->on_job_picked = ppa_blend_transaction_on_picked; + + new_trans_elm->trans_desc = dma_trans_desc; + new_trans_elm->dma_trans_placeholder = dma_trans_elm; + new_trans_elm->invoker = ppa_invoker; + + portENTER_CRITICAL(&ppa_invoker->spinlock); + if (ppa_invoker->in_accepting_trans_state) { + // Send transaction into PPA blending engine queue + STAILQ_INSERT_TAIL(&blend_engine->base.trans_stailq, new_trans_elm, entry); + ppa_invoker->blending_trans_cnt++; + } else { + ret = ESP_FAIL; + } + portEXIT_CRITICAL(&ppa_invoker->spinlock); + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "The invoker cannot accept transaction now"); + goto err; + } + + TickType_t ticks_to_wait = (mode == PPA_TRANS_MODE_NON_BLOCKING) ? 0 : portMAX_DELAY; + if (xSemaphoreTake(blend_engine->base.sem, ticks_to_wait) == pdTRUE) { + // Check if the transaction has already been started from the ISR + // If so, then the transaction should have been removed from queue at this moment (transaction completed) + bool found = false; + ppa_trans_t *temp = NULL; + portENTER_CRITICAL(&blend_engine->base.spinlock); + STAILQ_FOREACH(temp, &blend_engine->base.trans_stailq, entry) { + if (temp == new_trans_elm) { + found = true; + break; + } + } + portEXIT_CRITICAL(&blend_engine->base.spinlock); + if (found) { + ret = ppa_dma2d_enqueue(new_trans_elm); + if (ret != ESP_OK) { + portENTER_CRITICAL(&blend_engine->base.spinlock); + STAILQ_REMOVE(&blend_engine->base.trans_stailq, new_trans_elm, ppa_trans_s, entry); + portEXIT_CRITICAL(&blend_engine->base.spinlock); + xSemaphoreGive(blend_engine->base.sem); + goto err; + } + } else { + xSemaphoreGive(blend_engine->base.sem); + } + } + + if (mode == PPA_TRANS_MODE_BLOCKING) { + // while (1) { + // printf("ppa intr: %ld\n", PPA.int_raw.val); + // } + xSemaphoreTake(new_trans_elm->sem, portMAX_DELAY); // Given in the ISR + // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) + ppa_recycle_transaction(new_trans_elm); + } + +err: + if (ret != ESP_OK) { + ppa_recycle_transaction(new_trans_elm); + } + return ret; +} + +esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode) { return ESP_OK; } From fd19979d881326f3e1ef65b21baf94d30975ee6c Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Tue, 27 Feb 2024 20:50:34 +0800 Subject: [PATCH 03/15] feat(ppa): add PPA driver support for ESP32P4 cleaned up a bit, unify to some static functions --- .../esp_driver_ppa/include/driver/ppa.h | 12 +- components/esp_driver_ppa/src/ppa.c | 399 +++++++----------- 2 files changed, 157 insertions(+), 254 deletions(-) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 9f343d8d21..69103f3ab9 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -100,6 +100,10 @@ typedef struct { color_conv_std_rgb_yuv_t yuv_std; bool rgb_swap; bool byte_swap; + ppa_alpha_mode_t alpha_mode; + uint32_t alpha_value; /*!< When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) + When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) + When other alpha modes are selected, this field is not used */ } in_color; struct { @@ -107,11 +111,6 @@ typedef struct { color_range_t yuv_range; color_conv_std_rgb_yuv_t yuv_std; } out_color; - - ppa_alpha_mode_t alpha_mode; - uint32_t alpha_value; /*!< When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) - When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) - When other alpha modes are selected, this field is not used*/ } ppa_sr_trans_config_t; /** @@ -192,6 +191,7 @@ typedef struct { */ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode); +// TODO: FILL typedef struct { void *out_buffer; } ppa_fill_trans_config_t; @@ -219,8 +219,6 @@ esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_trans_con // Consider blocking and non-blocking options // Non-blocking may require notification of process done event -// dma2d done/eof callback, when/how to free dma2d transaction content - // cache writeback/invalidate #ifdef __cplusplus diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c index 8c6de3cf64..3c5bcd5b82 100644 --- a/components/esp_driver_ppa/src/ppa.c +++ b/components/esp_driver_ppa/src/ppa.c @@ -14,6 +14,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" +#include "freertos/idf_additions.h" #include "esp_heap_caps.h" #include "esp_cache.h" #include "hal/cache_hal.h" @@ -60,10 +61,6 @@ typedef ppa_blend_trans_config_t ppa_blend_transaction_t; typedef struct ppa_trans_s { STAILQ_ENTRY(ppa_trans_s) entry; // link entry - // union { - // const ppa_sr_transaction_t *sr_desc; - // const ppa_blending_transaction_t *blending_desc; - // }; dma2d_trans_config_t *trans_desc; dma2d_trans_t *dma_trans_placeholder; SemaphoreHandle_t sem; @@ -79,10 +76,6 @@ typedef struct { ppa_engine_t *ppa_engine; ppa_trans_t *trans_elm; dma2d_trigger_peripheral_t trigger_periph; - // dma2d_csc_config_t - // dma2d_strategy_config_t *dma_strategy; - // dma2d_transfer_ability_t *dma_transfer_ability; - // dma2d_rx_event_callbacks_t *event_cbs; } ppa_dma2d_trans_on_picked_config_t; struct ppa_engine_t { @@ -120,8 +113,11 @@ struct ppa_invoker_t { // user_data }; -// how to free and how to push next trans into dma2d queue after engine is free (callback triggered) -// +typedef enum { + PPA_OPERATION_SR, + PPA_OPERATION_BLEND, + PPA_OPERATION_FILL, +} ppa_operation_t; typedef struct ppa_platform_t { _lock_t mutex; // platform level mutex lock to protect the ppa_module_acquire/ppa_module_release process @@ -148,6 +144,20 @@ typedef struct { static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine); static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine); +static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); +static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); + +extern uint32_t dma2d_tx_channel_reserved_mask[SOC_DMA2D_GROUPS]; +extern uint32_t dma2d_rx_channel_reserved_mask[SOC_DMA2D_GROUPS]; +static uint32_t ppa_specified_tx_channel_mask = 0; +static uint32_t ppa_specified_rx_channel_mask = 0; +static __attribute__((constructor)) void ppa_reserve_dma2d_channels(void) +{ + // ppa_specified_tx_channel_mask = 1 << 0; + // ppa_specified_rx_channel_mask = 1 << 0; + dma2d_tx_channel_reserved_mask[0] |= ppa_specified_tx_channel_mask; + dma2d_rx_channel_reserved_mask[0] |= ppa_specified_rx_channel_mask; +} // TODO: acquire pm_lock? static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine) @@ -169,7 +179,7 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin if (config->engine == PPA_ENGINE_TYPE_SR) { if (!s_platform.sr) { ppa_sr_engine_t *sr_engine = heap_caps_calloc(1, sizeof(ppa_sr_engine_t), PPA_MEM_ALLOC_CAPS); - SemaphoreHandle_t sr_sem = xSemaphoreCreateBinary(); + SemaphoreHandle_t sr_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); dma2d_descriptor_t *sr_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); dma2d_descriptor_t *sr_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); if (sr_engine && sr_sem && sr_tx_dma_desc && sr_rx_dma_desc) { @@ -190,7 +200,7 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin ESP_LOGE(TAG, "no mem to register PPA SR engine"); free(sr_engine); if (sr_sem) { - vSemaphoreDelete(sr_sem); + vSemaphoreDeleteWithCaps(sr_sem); } free(sr_tx_dma_desc); free(sr_rx_dma_desc); @@ -203,7 +213,7 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin } else if (config->engine == PPA_ENGINE_TYPE_BLEND) { if (!s_platform.blending) { ppa_blend_engine_t *blending_engine = heap_caps_calloc(1, sizeof(ppa_blend_engine_t), PPA_MEM_ALLOC_CAPS); - SemaphoreHandle_t blending_sem = xSemaphoreCreateBinary(); + SemaphoreHandle_t blending_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); @@ -226,7 +236,7 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin ESP_LOGE(TAG, "no mem to register PPA Blending engine"); free(blending_engine); if (blending_sem) { - vSemaphoreDelete(blending_sem); + vSemaphoreDeleteWithCaps(blending_sem); } free(blending_tx_bg_dma_desc); free(blending_tx_fg_dma_desc); @@ -291,7 +301,7 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) s_platform.sr = NULL; free(sr_engine->dma_tx_desc); free(sr_engine->dma_rx_desc); - vSemaphoreDelete(sr_engine->base.sem); + vSemaphoreDeleteWithCaps(sr_engine->base.sem); free(sr_engine); } } else if (ppa_engine->type == PPA_ENGINE_TYPE_BLEND) { @@ -310,7 +320,7 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) free(blending_engine->dma_tx_bg_desc); free(blending_engine->dma_tx_fg_desc); free(blending_engine->dma_rx_desc); - vSemaphoreDelete(blending_engine->base.sem); + vSemaphoreDeleteWithCaps(blending_engine->base.sem); free(blending_engine); } } @@ -694,38 +704,139 @@ static void ppa_recycle_transaction(ppa_trans_t *trans_elm) free(trans_elm->trans_desc); } if (trans_elm->sem) { - vSemaphoreDelete(trans_elm->sem); + vSemaphoreDeleteWithCaps(trans_elm->sem); } free(trans_elm); } } -static bool ppa_sr_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) +static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t *ppa_engine_base, ppa_operation_t ppa_operation, const void *config, ppa_trans_mode_t mode) +{ + esp_err_t ret = ESP_OK; + ppa_engine_type_t engine_type = ppa_engine_base->type; + + ppa_trans_t *new_trans_elm = (ppa_trans_t *)heap_caps_calloc(1, sizeof(ppa_trans_t), PPA_MEM_ALLOC_CAPS); + dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, PPA_MEM_ALLOC_CAPS); + dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)heap_caps_calloc(1, sizeof(dma2d_trans_config_t), PPA_MEM_ALLOC_CAPS); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)heap_caps_calloc(1, sizeof(ppa_dma2d_trans_on_picked_config_t), PPA_MEM_ALLOC_CAPS); + size_t ppa_trans_desc_size = (ppa_operation == PPA_OPERATION_SR) ? sizeof(ppa_sr_transaction_t) : + (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_transaction_t) : 0; + assert(ppa_trans_desc_size != 0); + void *ppa_trans_desc = heap_caps_calloc(1, ppa_trans_desc_size, PPA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(new_trans_elm && dma_trans_elm && dma_trans_desc && trans_on_picked_desc && ppa_trans_desc, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); + if (mode == PPA_TRANS_MODE_BLOCKING) { + new_trans_elm->sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(new_trans_elm->sem, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); + } + memcpy(ppa_trans_desc, config, ppa_trans_desc_size); + + trans_on_picked_desc->blend_desc = ppa_trans_desc; + trans_on_picked_desc->ppa_engine = ppa_engine_base; + trans_on_picked_desc->trans_elm = new_trans_elm; + trans_on_picked_desc->trigger_periph = (engine_type == PPA_ENGINE_TYPE_SR) ? DMA2D_TRIG_PERIPH_PPA_SR : DMA2D_TRIG_PERIPH_PPA_BLEND; + + dma_trans_desc->tx_channel_num = (ppa_operation == PPA_OPERATION_SR) ? 1 : + (ppa_operation == PPA_OPERATION_BLEND) ? 2 : 0; + dma_trans_desc->rx_channel_num = 1; + if (ppa_operation == PPA_OPERATION_SR) { + dma_trans_desc->specified_tx_channel_mask = ppa_specified_tx_channel_mask; + dma_trans_desc->specified_rx_channel_mask = ppa_specified_rx_channel_mask; + } + dma_trans_desc->user_config = (void *)trans_on_picked_desc; + dma_trans_desc->on_job_picked = (ppa_operation == PPA_OPERATION_SR) ? ppa_sr_transaction_on_picked : + (ppa_operation == PPA_OPERATION_BLEND) ? ppa_blend_transaction_on_picked : NULL; + + new_trans_elm->trans_desc = dma_trans_desc; + new_trans_elm->dma_trans_placeholder = dma_trans_elm; + new_trans_elm->invoker = ppa_invoker; + + portENTER_CRITICAL(&ppa_invoker->spinlock); + if (ppa_invoker->in_accepting_trans_state) { + // Send transaction into PPA engine queue + STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, new_trans_elm, entry); + if (engine_type == PPA_ENGINE_TYPE_SR) { + ppa_invoker->sr_trans_cnt++; + } else { + ppa_invoker->blending_trans_cnt++; + } + } else { + ret = ESP_FAIL; + } + portEXIT_CRITICAL(&ppa_invoker->spinlock); + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "The invoker cannot accept transaction now"); + goto err; + } + + TickType_t ticks_to_wait = (mode == PPA_TRANS_MODE_NON_BLOCKING) ? 0 : portMAX_DELAY; + if (xSemaphoreTake(ppa_engine_base->sem, ticks_to_wait) == pdTRUE) { + // Check if the transaction has already been started from the ISR + // If so, then the transaction should have been removed from queue at this moment (transaction completed) + bool found = false; + ppa_trans_t *temp = NULL; + portENTER_CRITICAL(&ppa_engine_base->spinlock); + STAILQ_FOREACH(temp, &ppa_engine_base->trans_stailq, entry) { + if (temp == new_trans_elm) { + found = true; + break; + } + } + portEXIT_CRITICAL(&ppa_engine_base->spinlock); + if (found) { + ret = ppa_dma2d_enqueue(new_trans_elm); + if (ret != ESP_OK) { + portENTER_CRITICAL(&ppa_engine_base->spinlock); + STAILQ_REMOVE(&ppa_engine_base->trans_stailq, new_trans_elm, ppa_trans_s, entry); + portEXIT_CRITICAL(&ppa_engine_base->spinlock); + xSemaphoreGive(ppa_engine_base->sem); + goto err; + } + } else { + xSemaphoreGive(ppa_engine_base->sem); + } + } + + if (mode == PPA_TRANS_MODE_BLOCKING) { + // while (1) { + // printf("ppa intr: %ld\n", PPA.int_raw.val); + // } + xSemaphoreTake(new_trans_elm->sem, portMAX_DELAY); // Given in the ISR + // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) + ppa_recycle_transaction(new_trans_elm); + } + +err: + if (ret != ESP_OK) { + ppa_recycle_transaction(new_trans_elm); + } + return ret; +} + +static bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) { bool need_yield = false; BaseType_t HPTaskAwoken; ppa_trans_t *trans_elm = (ppa_trans_t *)user_data; ppa_invoker_t *invoker = trans_elm->invoker; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; - assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SR); - ppa_sr_engine_t *sr_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_sr_engine_t, base); + ppa_engine_t *engine_base = trans_on_picked_desc->ppa_engine; + ppa_engine_type_t engine_type = engine_base->type; ppa_trans_t *next_start_trans = NULL; - portENTER_CRITICAL_ISR(&sr_engine->base.spinlock); + portENTER_CRITICAL_ISR(&engine_base->spinlock); // Remove this transaction from transaction queue - STAILQ_REMOVE(&sr_engine->base.trans_stailq, trans_elm, ppa_trans_s, entry); - next_start_trans = STAILQ_FIRST(&sr_engine->base.trans_stailq); - portEXIT_CRITICAL_ISR(&sr_engine->base.spinlock); + STAILQ_REMOVE(&engine_base->trans_stailq, trans_elm, ppa_trans_s, entry); + next_start_trans = STAILQ_FIRST(&engine_base->trans_stailq); + portEXIT_CRITICAL_ISR(&engine_base->spinlock); // If there is next trans in PPA engine queue, send it to DMA queue; otherwise, release the engine semaphore if (next_start_trans) { - esp_rom_printf("from ISR -"); ppa_dma2d_enqueue(next_start_trans); } else { - xSemaphoreGiveFromISR(sr_engine->base.sem, &HPTaskAwoken); + xSemaphoreGiveFromISR(engine_base->sem, &HPTaskAwoken); need_yield |= (HPTaskAwoken == pdTRUE); } - esp_rom_printf("trans addr: 0x%x\n", trans_elm); // Recycle transaction or give transaction semaphore if (trans_elm->sem != NULL) { xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); @@ -735,7 +846,11 @@ static bool ppa_sr_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_ } portENTER_CRITICAL_ISR(&invoker->spinlock); - invoker->sr_trans_cnt--; + if (engine_type == PPA_ENGINE_TYPE_SR) { + invoker->sr_trans_cnt--; + } else { + invoker->blending_trans_cnt--; + } portEXIT_CRITICAL_ISR(&invoker->spinlock); // TODO: how to notify non-blocking transaction @@ -862,11 +977,11 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c dma2d_csc_config_t dma_rx_csc = { .rx_csc_option = DMA2D_CSC_RX_YUV420_TO_YUV444, }; - dma2d_configure_color_space_conversion(dma2d_rx_chan, &dma_rx_csc); + dma2d_configure_color_space_conversion(dma2d_rx_chan, &dma_rx_csc); // TODO: RX CSC only on channel 0 } dma2d_rx_event_callbacks_t dma_event_cbs = { - .on_recv_eof = ppa_sr_transaction_done_cb, + .on_recv_eof = ppa_transaction_done_cb, }; dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); @@ -885,6 +1000,7 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c } ppa_ll_sr_enable_rx_byte_swap(s_platform.hal.dev, sr_trans_desc->in_color.byte_swap); ppa_ll_sr_enable_rx_rgb_swap(s_platform.hal.dev, sr_trans_desc->in_color.rgb_swap); + ppa_ll_sr_configure_rx_alpha(s_platform.hal.dev, sr_trans_desc->in_color.alpha_mode, sr_trans_desc->in_color.alpha_value); ppa_ll_sr_set_tx_color_mode(s_platform.hal.dev, ppa_out_color_mode); if (COLOR_SPACE_TYPE(ppa_out_color_mode) == COLOR_SPACE_YUV) { @@ -892,8 +1008,6 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c ppa_ll_sr_set_yuv2rgb_std(s_platform.hal.dev, sr_trans_desc->out_color.yuv_std); } - ppa_ll_sr_configure_rx_alpha(s_platform.hal.dev, sr_trans_desc->alpha_mode, sr_trans_desc->alpha_value); - // TODO: sr_macro_bk_ro_bypass // PPA.sr_byte_order.sr_macro_bk_ro_bypass = 1; ppa_ll_sr_set_rotation_angle(s_platform.hal.dev, sr_trans_desc->rotation_angle); @@ -910,7 +1024,6 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr_trans_config_t *config, ppa_trans_mode_t mode) { - esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(ppa_invoker && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(ppa_invoker->sr_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to SR engine"); ESP_RETURN_ON_FALSE(mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); @@ -924,135 +1037,9 @@ esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr ESP_ERR_INVALID_ARG, TAG, "invalid scale"); // byte/rgb swap with color mode only to (A)RGB color space? - ppa_sr_engine_t *sr_engine = __containerof(ppa_invoker->sr_engine, ppa_sr_engine_t, base); - // ESP_RETURN_ON_FALSE(sr_engine, ESP_FAIL, TAG, "SR engine not registered, please register through ppa_module_acquire first"); + // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? - ppa_trans_t *new_trans_elm = (ppa_trans_t *)heap_caps_calloc(1, sizeof(ppa_trans_t), PPA_MEM_ALLOC_CAPS); - dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, PPA_MEM_ALLOC_CAPS); - dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)heap_caps_calloc(1, sizeof(dma2d_trans_config_t), PPA_MEM_ALLOC_CAPS); - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)heap_caps_calloc(1, sizeof(ppa_dma2d_trans_on_picked_config_t), PPA_MEM_ALLOC_CAPS); - ppa_sr_transaction_t *ppa_trans_desc = (ppa_sr_transaction_t *)heap_caps_calloc(1, sizeof(ppa_sr_transaction_t), PPA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(new_trans_elm && dma_trans_elm && dma_trans_desc && trans_on_picked_desc && ppa_trans_desc, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); - if (mode == PPA_TRANS_MODE_BLOCKING) { - new_trans_elm->sem = xSemaphoreCreateBinary(); - ESP_GOTO_ON_FALSE(new_trans_elm->sem, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); - } - esp_rom_printf("new trans addr: 0x%x\n", new_trans_elm); - memcpy(ppa_trans_desc, config, sizeof(ppa_sr_trans_config_t)); - - trans_on_picked_desc->sr_desc = ppa_trans_desc; - trans_on_picked_desc->ppa_engine = &sr_engine->base; - trans_on_picked_desc->trans_elm = new_trans_elm; - trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_SR; - - dma_trans_desc->tx_channel_num = 1; - dma_trans_desc->rx_channel_num = 1; - // TODO: reserved channels - dma_trans_desc->user_config = (void *)trans_on_picked_desc; - dma_trans_desc->on_job_picked = ppa_sr_transaction_on_picked; - - new_trans_elm->trans_desc = dma_trans_desc; - new_trans_elm->dma_trans_placeholder = dma_trans_elm; - new_trans_elm->invoker = ppa_invoker; - - portENTER_CRITICAL(&ppa_invoker->spinlock); - if (ppa_invoker->in_accepting_trans_state) { - // Send transaction into PPA SR engine queue - STAILQ_INSERT_TAIL(&sr_engine->base.trans_stailq, new_trans_elm, entry); - ppa_invoker->sr_trans_cnt++; - } else { - ret = ESP_FAIL; - } - portEXIT_CRITICAL(&ppa_invoker->spinlock); - - if (ret != ESP_OK) { - ESP_LOGE(TAG, "The invoker cannot accept transaction now"); - goto err; - } - - TickType_t ticks_to_wait = (mode == PPA_TRANS_MODE_NON_BLOCKING) ? 0 : portMAX_DELAY; - if (xSemaphoreTake(sr_engine->base.sem, ticks_to_wait) == pdTRUE) { - // Check if the transaction has already been started from the ISR - // If so, then the transaction should have been removed from queue at this moment (transaction completed) - bool found = false; - ppa_trans_t *temp = NULL; - portENTER_CRITICAL(&sr_engine->base.spinlock); - STAILQ_FOREACH(temp, &sr_engine->base.trans_stailq, entry) { - if (temp == new_trans_elm) { - found = true; - break; - } - } - portEXIT_CRITICAL(&sr_engine->base.spinlock); - if (found) { - ret = ppa_dma2d_enqueue(new_trans_elm); - if (ret != ESP_OK) { - portENTER_CRITICAL(&sr_engine->base.spinlock); - STAILQ_REMOVE(&sr_engine->base.trans_stailq, new_trans_elm, ppa_trans_s, entry); - portEXIT_CRITICAL(&sr_engine->base.spinlock); - xSemaphoreGive(sr_engine->base.sem); - goto err; - } - } else { - xSemaphoreGive(sr_engine->base.sem); - } - } - - if (mode == PPA_TRANS_MODE_BLOCKING) { - // while (1) { - // printf("ppa intr: %ld\n", PPA.int_raw.val); - // } - xSemaphoreTake(new_trans_elm->sem, portMAX_DELAY); // Given in the ISR - // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) - ppa_recycle_transaction(new_trans_elm); - } - -err: - if (ret != ESP_OK) { - ppa_recycle_transaction(new_trans_elm); - } - return ret; -} - -static bool ppa_blend_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) -{ - bool need_yield = false; - BaseType_t HPTaskAwoken; - ppa_trans_t *trans_elm = (ppa_trans_t *)user_data; - ppa_invoker_t *invoker = trans_elm->invoker; - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; - assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND); - ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); - - ppa_trans_t *next_start_trans = NULL; - portENTER_CRITICAL_ISR(&blend_engine->base.spinlock); - // Remove this transaction from transaction queue - STAILQ_REMOVE(&blend_engine->base.trans_stailq, trans_elm, ppa_trans_s, entry); - next_start_trans = STAILQ_FIRST(&blend_engine->base.trans_stailq); - portEXIT_CRITICAL_ISR(&blend_engine->base.spinlock); - - // If there is next trans in PPA engine queue, send it to DMA queue; otherwise, release the engine semaphore - if (next_start_trans) { - ppa_dma2d_enqueue(next_start_trans); - } else { - xSemaphoreGiveFromISR(blend_engine->base.sem, &HPTaskAwoken); - need_yield |= (HPTaskAwoken == pdTRUE); - } - // Recycle transaction or give transaction semaphore - if (trans_elm->sem != NULL) { - xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); - need_yield |= (HPTaskAwoken == pdTRUE); - } else { - ppa_recycle_transaction(trans_elm); - } - - portENTER_CRITICAL_ISR(&invoker->spinlock); - invoker->blending_trans_cnt--; - portEXIT_CRITICAL_ISR(&invoker->spinlock); - - // TODO: how to notify non-blocking transaction - - return need_yield; + return ppa_do_operation(ppa_invoker, ppa_invoker->sr_engine, PPA_OPERATION_SR, (void *)config, mode); } static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) @@ -1179,7 +1166,7 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); dma2d_rx_event_callbacks_t dma_event_cbs = { - .on_recv_eof = ppa_blend_transaction_done_cb, + .on_recv_eof = ppa_transaction_done_cb, }; dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); @@ -1216,96 +1203,14 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode) { - esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(ppa_invoker && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); ESP_RETURN_ON_FALSE(mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + // TODO: ARG CHECK - ppa_blend_engine_t *blend_engine = __containerof(ppa_invoker->blending_engine, ppa_blend_engine_t, base); + // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? - ppa_trans_t *new_trans_elm = (ppa_trans_t *)heap_caps_calloc(1, sizeof(ppa_trans_t), PPA_MEM_ALLOC_CAPS); - dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, PPA_MEM_ALLOC_CAPS); - dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)heap_caps_calloc(1, sizeof(dma2d_trans_config_t), PPA_MEM_ALLOC_CAPS); - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)heap_caps_calloc(1, sizeof(ppa_dma2d_trans_on_picked_config_t), PPA_MEM_ALLOC_CAPS); - ppa_blend_transaction_t *ppa_trans_desc = (ppa_blend_transaction_t *)heap_caps_calloc(1, sizeof(ppa_blend_transaction_t), PPA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(new_trans_elm && dma_trans_elm && dma_trans_desc && trans_on_picked_desc && ppa_trans_desc, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); - if (mode == PPA_TRANS_MODE_BLOCKING) { - new_trans_elm->sem = xSemaphoreCreateBinary(); - ESP_GOTO_ON_FALSE(new_trans_elm->sem, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); - } - memcpy(ppa_trans_desc, config, sizeof(ppa_blend_trans_config_t)); - - trans_on_picked_desc->blend_desc = ppa_trans_desc; - trans_on_picked_desc->ppa_engine = &blend_engine->base; - trans_on_picked_desc->trans_elm = new_trans_elm; - trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; - - dma_trans_desc->tx_channel_num = 2; - dma_trans_desc->rx_channel_num = 1; - dma_trans_desc->user_config = (void *)trans_on_picked_desc; - dma_trans_desc->on_job_picked = ppa_blend_transaction_on_picked; - - new_trans_elm->trans_desc = dma_trans_desc; - new_trans_elm->dma_trans_placeholder = dma_trans_elm; - new_trans_elm->invoker = ppa_invoker; - - portENTER_CRITICAL(&ppa_invoker->spinlock); - if (ppa_invoker->in_accepting_trans_state) { - // Send transaction into PPA blending engine queue - STAILQ_INSERT_TAIL(&blend_engine->base.trans_stailq, new_trans_elm, entry); - ppa_invoker->blending_trans_cnt++; - } else { - ret = ESP_FAIL; - } - portEXIT_CRITICAL(&ppa_invoker->spinlock); - - if (ret != ESP_OK) { - ESP_LOGE(TAG, "The invoker cannot accept transaction now"); - goto err; - } - - TickType_t ticks_to_wait = (mode == PPA_TRANS_MODE_NON_BLOCKING) ? 0 : portMAX_DELAY; - if (xSemaphoreTake(blend_engine->base.sem, ticks_to_wait) == pdTRUE) { - // Check if the transaction has already been started from the ISR - // If so, then the transaction should have been removed from queue at this moment (transaction completed) - bool found = false; - ppa_trans_t *temp = NULL; - portENTER_CRITICAL(&blend_engine->base.spinlock); - STAILQ_FOREACH(temp, &blend_engine->base.trans_stailq, entry) { - if (temp == new_trans_elm) { - found = true; - break; - } - } - portEXIT_CRITICAL(&blend_engine->base.spinlock); - if (found) { - ret = ppa_dma2d_enqueue(new_trans_elm); - if (ret != ESP_OK) { - portENTER_CRITICAL(&blend_engine->base.spinlock); - STAILQ_REMOVE(&blend_engine->base.trans_stailq, new_trans_elm, ppa_trans_s, entry); - portEXIT_CRITICAL(&blend_engine->base.spinlock); - xSemaphoreGive(blend_engine->base.sem); - goto err; - } - } else { - xSemaphoreGive(blend_engine->base.sem); - } - } - - if (mode == PPA_TRANS_MODE_BLOCKING) { - // while (1) { - // printf("ppa intr: %ld\n", PPA.int_raw.val); - // } - xSemaphoreTake(new_trans_elm->sem, portMAX_DELAY); // Given in the ISR - // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) - ppa_recycle_transaction(new_trans_elm); - } - -err: - if (ret != ESP_OK) { - ppa_recycle_transaction(new_trans_elm); - } - return ret; + return ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_BLEND, (void *)config, mode); } esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode) From 776c3ef392acb4f8a7bcc4968d8fb2d383bbae4e Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Fri, 1 Mar 2024 21:52:56 +0800 Subject: [PATCH 04/15] feat(ppa): add PPA driver support for ESP32P4 Add fill operation Split ppa_do_operation, pre-process some trans config --- .../esp_driver_ppa/include/driver/ppa.h | 99 ++++---- components/esp_driver_ppa/src/ppa.c | 228 ++++++++++++++---- components/hal/esp32p4/include/hal/ppa_ll.h | 2 +- 3 files changed, 243 insertions(+), 86 deletions(-) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 69103f3ab9..e468770b10 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -32,8 +32,6 @@ typedef struct ppa_invoker_t *ppa_invoker_handle_t; * @brief A collection of configuration items that used for registering a PPA invoker */ typedef struct { - // ppa_engine_handle_t sr_engine; - // ppa_engine_handle_t blend_engine; uint32_t engine_flag; /*!< Bitwise OR of `PPA_ENGINE_FLAG_*` flags indicating the required engines for the invoker */ // done_cb // user_data @@ -69,49 +67,51 @@ typedef enum { PPA_TRANS_MODE_NON_BLOCKING, /*!< `ppa_do_xxx` function will return immediately after the PPA operation is pushed to the internal queue */ } ppa_trans_mode_t; +#define PPA_SR_TRANS_CONFIG struct { \ + void *in_buffer; /*!< TODO: could be a buffer list, link descriptors together, process a batch + uint32_t batch_num; However, is it necessary? psram can not store too many pictures */ \ + uint32_t in_pic_w; \ + uint32_t in_pic_h; \ + uint32_t in_block_w; \ + uint32_t in_block_h; \ + uint32_t in_block_offset_x; \ + uint32_t in_block_offset_y; \ + \ + void *out_buffer; /*!< TODO: alignment restriction */ \ + uint32_t out_pic_w; \ + uint32_t out_pic_h; \ + uint32_t out_block_offset_x; \ + uint32_t out_block_offset_y; \ + \ + ppa_sr_rotation_angle_t rotation_angle; \ + float scale_x; \ + float scale_y; \ + bool mirror_x; \ + bool mirror_y; \ + \ + struct { \ + ppa_sr_color_mode_t mode; \ + color_range_t yuv_range; \ + color_conv_std_rgb_yuv_t yuv_std; \ + bool rgb_swap; \ + bool byte_swap; \ + ppa_alpha_mode_t alpha_mode; \ + uint32_t alpha_value; /*!< When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) + When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) + When other alpha modes are selected, this field is not used */ \ + } in_color; \ + \ + struct { \ + ppa_sr_color_mode_t mode; \ + color_range_t yuv_range; \ + color_conv_std_rgb_yuv_t yuv_std; \ + } out_color; \ +} + /** * @brief A collection of configuration items to perform a PPA SR operation */ -typedef struct { - void *in_buffer; // TODO: could be a buffer list, link descriptors together, process a batch - // uint32_t batch_num; // However, is it necessary? psram can not store too many pictures - uint32_t in_pic_w; - uint32_t in_pic_h; - uint32_t in_block_w; - uint32_t in_block_h; - uint32_t in_block_offset_x; - uint32_t in_block_offset_y; - - void *out_buffer; // TODO: alignment restriction - uint32_t out_pic_w; - uint32_t out_pic_h; - uint32_t out_block_offset_x; - uint32_t out_block_offset_y; - - ppa_sr_rotation_angle_t rotation_angle; - float scale_x; - float scale_y; - bool mirror_x; - bool mirror_y; - - struct { - ppa_sr_color_mode_t mode; - color_range_t yuv_range; - color_conv_std_rgb_yuv_t yuv_std; - bool rgb_swap; - bool byte_swap; - ppa_alpha_mode_t alpha_mode; - uint32_t alpha_value; /*!< When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) - When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) - When other alpha modes are selected, this field is not used */ - } in_color; - - struct { - ppa_sr_color_mode_t mode; - color_range_t yuv_range; - color_conv_std_rgb_yuv_t yuv_std; - } out_color; -} ppa_sr_trans_config_t; +typedef PPA_SR_TRANS_CONFIG ppa_sr_trans_config_t; /** * @brief Perform a scaling-and-rotating (SR) operation to a picture @@ -191,9 +191,22 @@ typedef struct { */ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode); -// TODO: FILL typedef struct { + uint32_t fill_block_w; + uint32_t fill_block_h; + uint32_t fill_argb_color; /*!< The color to be filled, in ARGB8888 format ((A[31:24], R[23:16], G[15: 8], B[7:0])) */ + void *out_buffer; + uint32_t out_pic_w; + uint32_t out_pic_h; + uint32_t out_block_offset_x; + uint32_t out_block_offset_y; + + struct { + ppa_blend_color_mode_t mode; + } out_color; + + // colorkey??? } ppa_fill_trans_config_t; /** diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c index 3c5bcd5b82..4db3fd4148 100644 --- a/components/esp_driver_ppa/src/ppa.c +++ b/components/esp_driver_ppa/src/ppa.c @@ -55,10 +55,18 @@ typedef struct ppa_engine_t ppa_engine_t; typedef struct ppa_invoker_t ppa_invoker_t; -typedef ppa_sr_trans_config_t ppa_sr_transaction_t; +typedef struct { + PPA_SR_TRANS_CONFIG; + uint32_t scale_x_int; + uint32_t scale_x_frag; + uint32_t scale_y_int; + uint32_t scale_y_frag; +} ppa_sr_transaction_t; typedef ppa_blend_trans_config_t ppa_blend_transaction_t; +typedef ppa_fill_trans_config_t ppa_fill_transaction_t; + typedef struct ppa_trans_s { STAILQ_ENTRY(ppa_trans_s) entry; // link entry dma2d_trans_config_t *trans_desc; @@ -71,7 +79,8 @@ typedef struct { union { ppa_sr_transaction_t *sr_desc; ppa_blend_transaction_t *blend_desc; - // ppa_fill_transaction_t + ppa_fill_transaction_t *fill_desc; + void *op_desc; }; ppa_engine_t *ppa_engine; ppa_trans_t *trans_elm; @@ -146,18 +155,19 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine); static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); +static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); -extern uint32_t dma2d_tx_channel_reserved_mask[SOC_DMA2D_GROUPS]; -extern uint32_t dma2d_rx_channel_reserved_mask[SOC_DMA2D_GROUPS]; -static uint32_t ppa_specified_tx_channel_mask = 0; -static uint32_t ppa_specified_rx_channel_mask = 0; -static __attribute__((constructor)) void ppa_reserve_dma2d_channels(void) -{ - // ppa_specified_tx_channel_mask = 1 << 0; - // ppa_specified_rx_channel_mask = 1 << 0; - dma2d_tx_channel_reserved_mask[0] |= ppa_specified_tx_channel_mask; - dma2d_rx_channel_reserved_mask[0] |= ppa_specified_rx_channel_mask; -} +// extern uint32_t dma2d_tx_channel_reserved_mask[SOC_DMA2D_GROUPS]; +// extern uint32_t dma2d_rx_channel_reserved_mask[SOC_DMA2D_GROUPS]; +// static uint32_t ppa_specified_tx_channel_mask = 0; +// static uint32_t ppa_specified_rx_channel_mask = 0; +// static __attribute__((constructor)) void ppa_reserve_dma2d_channels(void) +// { +// // ppa_specified_tx_channel_mask = 1 << 0; +// // ppa_specified_rx_channel_mask = 1 << 0; +// dma2d_tx_channel_reserved_mask[0] |= ppa_specified_tx_channel_mask; +// dma2d_rx_channel_reserved_mask[0] |= ppa_specified_rx_channel_mask; +// } // TODO: acquire pm_lock? static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine) @@ -694,11 +704,7 @@ static void ppa_recycle_transaction(ppa_trans_t *trans_elm) if (trans_elm->trans_desc) { ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; if (trans_on_picked_desc) { - if (trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SR) { - free(trans_on_picked_desc->sr_desc); - } else if (trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND) { - free(trans_on_picked_desc->blend_desc); - } + free(trans_on_picked_desc->op_desc); free(trans_on_picked_desc); } free(trans_elm->trans_desc); @@ -710,7 +716,7 @@ static void ppa_recycle_transaction(ppa_trans_t *trans_elm) } } -static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t *ppa_engine_base, ppa_operation_t ppa_operation, const void *config, ppa_trans_mode_t mode) +static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_engine_t *ppa_engine_base, ppa_operation_t ppa_operation, const void *config, ppa_trans_t **trans_elm, ppa_trans_mode_t mode) { esp_err_t ret = ESP_OK; ppa_engine_type_t engine_type = ppa_engine_base->type; @@ -720,7 +726,8 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)heap_caps_calloc(1, sizeof(dma2d_trans_config_t), PPA_MEM_ALLOC_CAPS); ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)heap_caps_calloc(1, sizeof(ppa_dma2d_trans_on_picked_config_t), PPA_MEM_ALLOC_CAPS); size_t ppa_trans_desc_size = (ppa_operation == PPA_OPERATION_SR) ? sizeof(ppa_sr_transaction_t) : - (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_transaction_t) : 0; + (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_transaction_t) : + (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_transaction_t) : 0; assert(ppa_trans_desc_size != 0); void *ppa_trans_desc = heap_caps_calloc(1, ppa_trans_desc_size, PPA_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(new_trans_elm && dma_trans_elm && dma_trans_desc && trans_on_picked_desc && ppa_trans_desc, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); @@ -728,32 +735,51 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t new_trans_elm->sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(new_trans_elm->sem, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); } - memcpy(ppa_trans_desc, config, ppa_trans_desc_size); - trans_on_picked_desc->blend_desc = ppa_trans_desc; + size_t cpy_size = (ppa_operation == PPA_OPERATION_SR) ? sizeof(ppa_sr_trans_config_t) : + (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_trans_config_t) : + (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_trans_config_t) : 0; + memcpy(ppa_trans_desc, config, cpy_size); + + trans_on_picked_desc->op_desc = ppa_trans_desc; trans_on_picked_desc->ppa_engine = ppa_engine_base; trans_on_picked_desc->trans_elm = new_trans_elm; trans_on_picked_desc->trigger_periph = (engine_type == PPA_ENGINE_TYPE_SR) ? DMA2D_TRIG_PERIPH_PPA_SR : DMA2D_TRIG_PERIPH_PPA_BLEND; dma_trans_desc->tx_channel_num = (ppa_operation == PPA_OPERATION_SR) ? 1 : - (ppa_operation == PPA_OPERATION_BLEND) ? 2 : 0; + (ppa_operation == PPA_OPERATION_BLEND) ? 2 : 0; // PPA_OPERATION_FILL does not have data input dma_trans_desc->rx_channel_num = 1; - if (ppa_operation == PPA_OPERATION_SR) { - dma_trans_desc->specified_tx_channel_mask = ppa_specified_tx_channel_mask; - dma_trans_desc->specified_rx_channel_mask = ppa_specified_rx_channel_mask; - } + + // dma_trans_desc->specified_tx_channel_mask = ppa_specified_tx_channel_mask; + // dma_trans_desc->specified_rx_channel_mask = ppa_specified_rx_channel_mask; + dma_trans_desc->user_config = (void *)trans_on_picked_desc; dma_trans_desc->on_job_picked = (ppa_operation == PPA_OPERATION_SR) ? ppa_sr_transaction_on_picked : - (ppa_operation == PPA_OPERATION_BLEND) ? ppa_blend_transaction_on_picked : NULL; + (ppa_operation == PPA_OPERATION_BLEND) ? ppa_blend_transaction_on_picked : + (ppa_operation == PPA_OPERATION_FILL) ? ppa_fill_transaction_on_picked : NULL; new_trans_elm->trans_desc = dma_trans_desc; new_trans_elm->dma_trans_placeholder = dma_trans_elm; new_trans_elm->invoker = ppa_invoker; + *trans_elm = new_trans_elm; +err: + if (ret != ESP_OK) { + ppa_recycle_transaction(new_trans_elm); + *trans_elm = NULL; + } + return ret; +} + +static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t *ppa_engine_base, ppa_trans_t *trans_elm, ppa_trans_mode_t mode) +{ + esp_err_t ret = ESP_OK; + ppa_engine_type_t engine_type = ppa_engine_base->type; + portENTER_CRITICAL(&ppa_invoker->spinlock); if (ppa_invoker->in_accepting_trans_state) { // Send transaction into PPA engine queue - STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, new_trans_elm, entry); + STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry); if (engine_type == PPA_ENGINE_TYPE_SR) { ppa_invoker->sr_trans_cnt++; } else { @@ -777,17 +803,17 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t ppa_trans_t *temp = NULL; portENTER_CRITICAL(&ppa_engine_base->spinlock); STAILQ_FOREACH(temp, &ppa_engine_base->trans_stailq, entry) { - if (temp == new_trans_elm) { + if (temp == trans_elm) { found = true; break; } } portEXIT_CRITICAL(&ppa_engine_base->spinlock); if (found) { - ret = ppa_dma2d_enqueue(new_trans_elm); + ret = ppa_dma2d_enqueue(trans_elm); if (ret != ESP_OK) { portENTER_CRITICAL(&ppa_engine_base->spinlock); - STAILQ_REMOVE(&ppa_engine_base->trans_stailq, new_trans_elm, ppa_trans_s, entry); + STAILQ_REMOVE(&ppa_engine_base->trans_stailq, trans_elm, ppa_trans_s, entry); portEXIT_CRITICAL(&ppa_engine_base->spinlock); xSemaphoreGive(ppa_engine_base->sem); goto err; @@ -801,15 +827,12 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t // while (1) { // printf("ppa intr: %ld\n", PPA.int_raw.val); // } - xSemaphoreTake(new_trans_elm->sem, portMAX_DELAY); // Given in the ISR + xSemaphoreTake(trans_elm->sem, portMAX_DELAY); // Given in the ISR // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) - ppa_recycle_transaction(new_trans_elm); + ppa_recycle_transaction(trans_elm); } err: - if (ret != ESP_OK) { - ppa_recycle_transaction(new_trans_elm); - } return ret; } @@ -1011,8 +1034,8 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c // TODO: sr_macro_bk_ro_bypass // PPA.sr_byte_order.sr_macro_bk_ro_bypass = 1; ppa_ll_sr_set_rotation_angle(s_platform.hal.dev, sr_trans_desc->rotation_angle); - ppa_ll_sr_set_scaling_x(s_platform.hal.dev, (uint32_t)sr_trans_desc->scale_x, (uint32_t)(sr_trans_desc->scale_x * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX); - ppa_ll_sr_set_scaling_y(s_platform.hal.dev, (uint32_t)sr_trans_desc->scale_y, (uint32_t)(sr_trans_desc->scale_y * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX); + ppa_ll_sr_set_scaling_x(s_platform.hal.dev, sr_trans_desc->scale_x_int, sr_trans_desc->scale_x_frag); + ppa_ll_sr_set_scaling_y(s_platform.hal.dev, sr_trans_desc->scale_y_int, sr_trans_desc->scale_y_frag); ppa_ll_sr_enable_mirror_x(s_platform.hal.dev, sr_trans_desc->mirror_x); ppa_ll_sr_enable_mirror_y(s_platform.hal.dev, sr_trans_desc->mirror_y); @@ -1039,7 +1062,25 @@ esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? - return ppa_do_operation(ppa_invoker, ppa_invoker->sr_engine, PPA_OPERATION_SR, (void *)config, mode); + ppa_trans_t *trans_elm = NULL; + esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->sr_engine, PPA_OPERATION_SR, (void *)config, &trans_elm, mode); + if (ret == ESP_OK) { + assert(trans_elm); + + // Pre-process some data + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = trans_elm->trans_desc->user_config; + ppa_sr_transaction_t *sr_trans_desc = trans_on_picked_desc->sr_desc; + sr_trans_desc->scale_x_int = (uint32_t)sr_trans_desc->scale_x; + sr_trans_desc->scale_x_frag = (uint32_t)(sr_trans_desc->scale_x * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX; + sr_trans_desc->scale_y_int = (uint32_t)sr_trans_desc->scale_y; + sr_trans_desc->scale_y_frag = (uint32_t)(sr_trans_desc->scale_y * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX; + + ret = ppa_do_operation(ppa_invoker, ppa_invoker->sr_engine, trans_elm, mode); + if (ret != ESP_OK) { + ppa_recycle_transaction(trans_elm); + } + } + return ret; } static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) @@ -1210,10 +1251,113 @@ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_trans_c // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? - return ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_BLEND, (void *)config, mode); + ppa_trans_t *trans_elm = NULL; + esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_BLEND, (void *)config, &trans_elm, mode); + if (ret == ESP_OK) { + assert(trans_elm); + + ret = ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, trans_elm, mode); + if (ret != ESP_OK) { + ppa_recycle_transaction(trans_elm); + } + } + return ret; +} + +static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +{ + assert(num_chans == 1 && dma2d_chans && user_config); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; + assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->fill_desc && trans_on_picked_desc->ppa_engine); + + ppa_fill_transaction_t *fill_trans_desc = trans_on_picked_desc->fill_desc; + ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); + + // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) + free(trans_on_picked_desc->trans_elm->dma_trans_placeholder); + + // Get the required 2D-DMA channel handles + assert(dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX); + dma2d_channel_handle_t dma2d_rx_chan = dma2d_chans[0].chan; + + // Invalidate is performed on the entire picture (the window content is not continuous in the buffer) + color_space_pixel_format_t out_pixel_format = { + .color_type_id = fill_trans_desc->out_color.mode, + }; + uint32_t out_buffer_len = fill_trans_desc->out_pic_w * fill_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + esp_cache_msync(fill_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + + // Fill 2D-DMA descriptors + blend_engine->dma_rx_desc->vb_size = fill_trans_desc->fill_block_h; + blend_engine->dma_rx_desc->hb_length = fill_trans_desc->fill_block_w; + blend_engine->dma_rx_desc->err_eof = 0; + blend_engine->dma_rx_desc->dma2d_en = 1; + blend_engine->dma_rx_desc->suc_eof = 1; + blend_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + blend_engine->dma_rx_desc->va_size = fill_trans_desc->out_pic_h; + blend_engine->dma_rx_desc->ha_length = fill_trans_desc->out_pic_w; + blend_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format); + blend_engine->dma_rx_desc->y = fill_trans_desc->out_block_offset_y; + blend_engine->dma_rx_desc->x = fill_trans_desc->out_block_offset_x; + blend_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + blend_engine->dma_rx_desc->buffer = (void *)fill_trans_desc->out_buffer; + blend_engine->dma_rx_desc->next = NULL; + + esp_cache_msync((void *)blend_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + + // Configure 2D-DMA channels + dma2d_trigger_t trig_periph = { + .periph = DMA2D_TRIG_PERIPH_PPA_BLEND, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_RX, + }; + dma2d_connect(dma2d_rx_chan, &trig_periph); + + dma2d_transfer_ability_t dma_transfer_ability = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + }; + dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); + + dma2d_rx_event_callbacks_t dma_event_cbs = { + .on_recv_eof = ppa_transaction_done_cb, + }; + dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); + + ppa_ll_blend_reset(s_platform.hal.dev); + + dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)blend_engine->dma_rx_desc); + dma2d_start(dma2d_rx_chan); + + // Configure PPA Blending engine + ppa_ll_blend_configure_filling_block(s_platform.hal.dev, fill_trans_desc->fill_argb_color, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h); + ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, fill_trans_desc->out_color.mode); + + ppa_ll_blend_start(s_platform.hal.dev, PPA_LL_BLEND_TRANS_MODE_FILL); + + // No need to yield + return false; } esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode) { - return ESP_OK; + ESP_RETURN_ON_FALSE(ppa_invoker && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); + ESP_RETURN_ON_FALSE(mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + // TODO: ARG CHECK + // fill_block_w <= PPA_BLEND_HB_V, fill_block_h <= PPA_BLEND_VB_V + + // TODO: Maybe do buffer invalidation here, instead of in on_picked? + + ppa_trans_t *trans_elm = NULL; + esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_FILL, (void *)config, &trans_elm, mode); + if (ret == ESP_OK) { + assert(trans_elm); + + ret = ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, trans_elm, mode); + if (ret != ESP_OK) { + ppa_recycle_transaction(trans_elm); + } + } + return ret; } diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index 0b3e82f4d7..33e851d92b 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -638,7 +638,7 @@ static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_alpha_ * @param dev Peripheral instance address * @param data The fix data to be filled to the image block pixels in ARGB8888 format * @param hb The horizontal width of image block that would be filled in fix pixel filling mode. The unit is pixel. - * @param vb The vertical width of image block that would be filled in fix pixel filling mode. The unit is pixel. + * @param vb The vertical height of image block that would be filled in fix pixel filling mode. The unit is pixel. */ static inline void ppa_ll_blend_configure_filling_block(ppa_dev_t *dev, uint32_t data, uint32_t hb, uint32_t vb) { From 368ef8b472dca79f9549dc8b25b585e0d90f4da1 Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Mon, 4 Mar 2024 17:39:03 +0800 Subject: [PATCH 05/15] feat(ppa): add PPA driver support for ESP32P4 Improved/Refactored on the APIs --- .../esp_driver_ppa/include/driver/ppa.h | 46 ++++++----- components/esp_driver_ppa/src/ppa.c | 79 +++++++++---------- 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index e468770b10..062c839305 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -20,19 +20,20 @@ extern "C" { typedef struct ppa_invoker_t *ppa_invoker_handle_t; /** - * @brief PPA engine type flags + * @brief PPA operation type flags * - * These flags are supposed to be used to specify the PPA engines that are required by the invoker, so that the engines - * can be acquired when registering the invoker with `ppa_register_invoker`. + * These flags are supposed to be used to specify the PPA operations that are going to be used by the invoker, so that + * the corresponding engines can be acquired when registering the invoker with `ppa_register_invoker`. */ -#define PPA_ENGINE_FLAG_SR (1 << PPA_ENGINE_TYPE_SR) -#define PPA_ENGINE_FLAG_BLEND (1 << PPA_ENGINE_TYPE_BLEND) +#define PPA_OPERATION_FLAG_SR (1 << 0) +#define PPA_OPERATION_FLAG_BLEND (1 << 1) +#define PPA_OPERATION_FLAG_FILL (1 << 2) /** * @brief A collection of configuration items that used for registering a PPA invoker */ typedef struct { - uint32_t engine_flag; /*!< Bitwise OR of `PPA_ENGINE_FLAG_*` flags indicating the required engines for the invoker */ + uint32_t operation_flag; /*!< Bitwise OR of `PPA_OPERATION_FLAG_*` flags indicating the required operations for the invoker */ // done_cb // user_data } ppa_invoker_config_t; @@ -67,7 +68,14 @@ typedef enum { PPA_TRANS_MODE_NON_BLOCKING, /*!< `ppa_do_xxx` function will return immediately after the PPA operation is pushed to the internal queue */ } ppa_trans_mode_t; -#define PPA_SR_TRANS_CONFIG struct { \ +/** + * @brief A collection of configuration items to perform a PPA transaction + */ +typedef struct { + ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ +} ppa_trans_config_t; + +#define PPA_SR_OPERATION_CONFIG struct { \ void *in_buffer; /*!< TODO: could be a buffer list, link descriptors together, process a batch uint32_t batch_num; However, is it necessary? psram can not store too many pictures */ \ uint32_t in_pic_w; \ @@ -111,14 +119,14 @@ typedef enum { /** * @brief A collection of configuration items to perform a PPA SR operation */ -typedef PPA_SR_TRANS_CONFIG ppa_sr_trans_config_t; +typedef PPA_SR_OPERATION_CONFIG ppa_sr_operation_config_t; /** * @brief Perform a scaling-and-rotating (SR) operation to a picture * * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA SR engine - * @param[in] config Pointer to a collection of configurations for the SR operation, ppa_sr_trans_config_t - * @param[in] mode Select one mode from ppa_trans_mode_t + * @param[in] oper_config Pointer to a collection of configurations for the SR operation, ppa_sr_operation_config_t + * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t * * @return * - ESP_OK: @@ -126,7 +134,7 @@ typedef PPA_SR_TRANS_CONFIG ppa_sr_trans_config_t; * - ESP_ERR_NO_MEM: * - ESP_FAIL: */ -esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr_trans_config_t *config, ppa_trans_mode_t mode); +esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); typedef struct { void *in_bg_buffer; @@ -174,14 +182,14 @@ typedef struct { } out_color; // TODO: colorkey -} ppa_blend_trans_config_t; +} ppa_blend_operation_config_t; /** * @brief Perform a blending operation to a picture * * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA Blend engine - * @param[in] config Pointer to a collection of configurations for the blending operation, ppa_blend_trans_config_t - * @param[in] mode Select one mode from ppa_trans_mode_t + * @param[in] oper_config Pointer to a collection of configurations for the blending operation, ppa_blend_operation_config_t + * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t * * @return * - ESP_OK: @@ -189,7 +197,7 @@ typedef struct { * - ESP_ERR_NO_MEM: * - ESP_FAIL: */ -esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode); +esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); typedef struct { uint32_t fill_block_w; @@ -207,14 +215,14 @@ typedef struct { } out_color; // colorkey??? -} ppa_fill_trans_config_t; +} ppa_fill_operation_config_t; /** * @brief Perform a filling operation to a picture * * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA Blend engine - * @param[in] config Pointer to a collection of configurations for the filling operation, ppa_fill_trans_config_t - * @param[in] mode Select one mode from ppa_trans_mode_t + * @param[in] oper_config Pointer to a collection of configurations for the filling operation, ppa_fill_operation_config_t + * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t * * @return * - ESP_OK: @@ -222,7 +230,7 @@ typedef struct { * - ESP_ERR_NO_MEM: * - ESP_FAIL: */ -esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode); +esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); // argb color conversion (bypass blend) diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c index 4db3fd4148..83dd39a5e8 100644 --- a/components/esp_driver_ppa/src/ppa.c +++ b/components/esp_driver_ppa/src/ppa.c @@ -56,16 +56,16 @@ typedef struct ppa_engine_t ppa_engine_t; typedef struct ppa_invoker_t ppa_invoker_t; typedef struct { - PPA_SR_TRANS_CONFIG; + PPA_SR_OPERATION_CONFIG; uint32_t scale_x_int; uint32_t scale_x_frag; uint32_t scale_y_int; uint32_t scale_y_frag; -} ppa_sr_transaction_t; +} ppa_sr_oper_t; -typedef ppa_blend_trans_config_t ppa_blend_transaction_t; +typedef ppa_blend_operation_config_t ppa_blend_oper_t; -typedef ppa_fill_trans_config_t ppa_fill_transaction_t; +typedef ppa_fill_operation_config_t ppa_fill_oper_t; typedef struct ppa_trans_s { STAILQ_ENTRY(ppa_trans_s) entry; // link entry @@ -77,9 +77,9 @@ typedef struct ppa_trans_s { typedef struct { union { - ppa_sr_transaction_t *sr_desc; - ppa_blend_transaction_t *blend_desc; - ppa_fill_transaction_t *fill_desc; + ppa_sr_oper_t *sr_desc; + ppa_blend_oper_t *blend_desc; + ppa_fill_oper_t *fill_desc; void *op_desc; }; ppa_engine_t *ppa_engine; @@ -92,7 +92,6 @@ struct ppa_engine_t { portMUX_TYPE spinlock; SemaphoreHandle_t sem; // bool in_accepting_trans_state; - // pending transactions queue? union ppa_sr_trans_config_t, ppa_blending_trans_config_t? handle when to free (at trans start or at trans end?) STAILQ_HEAD(trans, ppa_trans_s) trans_stailq; // link head of pending transactions for the PPA engine // callback func? Here or in the struct above? // dma2d_rx_event_callbacks_t event_cbs; @@ -169,7 +168,7 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans // dma2d_rx_channel_reserved_mask[0] |= ppa_specified_rx_channel_mask; // } -// TODO: acquire pm_lock? +// TODO: acquire pm_lock per transaction? static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine) { esp_err_t ret = ESP_OK; @@ -364,13 +363,13 @@ esp_err_t ppa_register_invoker(const ppa_invoker_config_t *config, ppa_invoker_h invoker->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; invoker->in_accepting_trans_state = true; - if (config->engine_flag & PPA_ENGINE_FLAG_SR) { + if (config->operation_flag & PPA_OPERATION_FLAG_SR) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_SR, }; ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->sr_engine), err, TAG, "unable to acquire SR engine"); } - if (config->engine_flag & PPA_ENGINE_FLAG_BLEND) { + if (config->operation_flag & PPA_OPERATION_FLAG_BLEND || config->operation_flag & PPA_OPERATION_FLAG_FILL) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_BLEND, }; @@ -716,7 +715,7 @@ static void ppa_recycle_transaction(ppa_trans_t *trans_elm) } } -static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_engine_t *ppa_engine_base, ppa_operation_t ppa_operation, const void *config, ppa_trans_t **trans_elm, ppa_trans_mode_t mode) +static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_engine_t *ppa_engine_base, ppa_operation_t ppa_operation, const void *oper_config, ppa_trans_mode_t mode, ppa_trans_t **trans_elm) { esp_err_t ret = ESP_OK; ppa_engine_type_t engine_type = ppa_engine_base->type; @@ -725,9 +724,9 @@ static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_eng dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, PPA_MEM_ALLOC_CAPS); dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)heap_caps_calloc(1, sizeof(dma2d_trans_config_t), PPA_MEM_ALLOC_CAPS); ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)heap_caps_calloc(1, sizeof(ppa_dma2d_trans_on_picked_config_t), PPA_MEM_ALLOC_CAPS); - size_t ppa_trans_desc_size = (ppa_operation == PPA_OPERATION_SR) ? sizeof(ppa_sr_transaction_t) : - (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_transaction_t) : - (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_transaction_t) : 0; + size_t ppa_trans_desc_size = (ppa_operation == PPA_OPERATION_SR) ? sizeof(ppa_sr_oper_t) : + (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_oper_t) : + (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_oper_t) : 0; assert(ppa_trans_desc_size != 0); void *ppa_trans_desc = heap_caps_calloc(1, ppa_trans_desc_size, PPA_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(new_trans_elm && dma_trans_elm && dma_trans_desc && trans_on_picked_desc && ppa_trans_desc, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); @@ -736,10 +735,10 @@ static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_eng ESP_GOTO_ON_FALSE(new_trans_elm->sem, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); } - size_t cpy_size = (ppa_operation == PPA_OPERATION_SR) ? sizeof(ppa_sr_trans_config_t) : - (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_trans_config_t) : - (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_trans_config_t) : 0; - memcpy(ppa_trans_desc, config, cpy_size); + size_t cpy_size = (ppa_operation == PPA_OPERATION_SR) ? sizeof(ppa_sr_operation_config_t) : + (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_operation_config_t) : + (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_operation_config_t) : 0; + memcpy(ppa_trans_desc, oper_config, cpy_size); trans_on_picked_desc->op_desc = ppa_trans_desc; trans_on_picked_desc->ppa_engine = ppa_engine_base; @@ -887,7 +886,7 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SR && trans_on_picked_desc->sr_desc && trans_on_picked_desc->ppa_engine); - ppa_sr_transaction_t *sr_trans_desc = trans_on_picked_desc->sr_desc; + ppa_sr_oper_t *sr_trans_desc = trans_on_picked_desc->sr_desc; ppa_sr_engine_t *sr_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_sr_engine_t, base); // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) @@ -1045,37 +1044,37 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c return false; } -esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr_trans_config_t *config, ppa_trans_mode_t mode) +esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) { - ESP_RETURN_ON_FALSE(ppa_invoker && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(ppa_invoker->sr_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to SR engine"); - ESP_RETURN_ON_FALSE(mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // Any restrictions on in/out buffer address? alignment? alignment restriction comes from cache, its addr and size need to be aligned to cache line size on 912! // buffer on stack/heap // ESP_RETURN_ON_FALSE(config->rotation_angle) // ESP_RETURN_ON_FALSE(config->in/out_color_mode) // what if in_color is YUV420, out is RGB, what is out RGB range? Full range? - ESP_RETURN_ON_FALSE(config->scale_x < (PPA_LL_SR_SCALING_INT_MAX + 1) && config->scale_x >= (1.0 / PPA_LL_SR_SCALING_FRAG_MAX) && - config->scale_y < (PPA_LL_SR_SCALING_INT_MAX + 1) && config->scale_y >= (1.0 / PPA_LL_SR_SCALING_FRAG_MAX), + ESP_RETURN_ON_FALSE(oper_config->scale_x < (PPA_LL_SR_SCALING_INT_MAX + 1) && oper_config->scale_x >= (1.0 / PPA_LL_SR_SCALING_FRAG_MAX) && + oper_config->scale_y < (PPA_LL_SR_SCALING_INT_MAX + 1) && oper_config->scale_y >= (1.0 / PPA_LL_SR_SCALING_FRAG_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid scale"); // byte/rgb swap with color mode only to (A)RGB color space? // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? ppa_trans_t *trans_elm = NULL; - esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->sr_engine, PPA_OPERATION_SR, (void *)config, &trans_elm, mode); + esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->sr_engine, PPA_OPERATION_SR, (void *)oper_config, trans_config->mode, &trans_elm); if (ret == ESP_OK) { assert(trans_elm); // Pre-process some data ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = trans_elm->trans_desc->user_config; - ppa_sr_transaction_t *sr_trans_desc = trans_on_picked_desc->sr_desc; + ppa_sr_oper_t *sr_trans_desc = trans_on_picked_desc->sr_desc; sr_trans_desc->scale_x_int = (uint32_t)sr_trans_desc->scale_x; sr_trans_desc->scale_x_frag = (uint32_t)(sr_trans_desc->scale_x * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX; sr_trans_desc->scale_y_int = (uint32_t)sr_trans_desc->scale_y; sr_trans_desc->scale_y_frag = (uint32_t)(sr_trans_desc->scale_y * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX; - ret = ppa_do_operation(ppa_invoker, ppa_invoker->sr_engine, trans_elm, mode); + ret = ppa_do_operation(ppa_invoker, ppa_invoker->sr_engine, trans_elm, trans_config->mode); if (ret != ESP_OK) { ppa_recycle_transaction(trans_elm); } @@ -1089,7 +1088,7 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->blend_desc && trans_on_picked_desc->ppa_engine); - ppa_blend_transaction_t *blend_trans_desc = trans_on_picked_desc->blend_desc; + ppa_blend_oper_t *blend_trans_desc = trans_on_picked_desc->blend_desc; ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) @@ -1242,21 +1241,21 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran return false; } -esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_trans_config_t *config, ppa_trans_mode_t mode) +esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) { - ESP_RETURN_ON_FALSE(ppa_invoker && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); - ESP_RETURN_ON_FALSE(mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // TODO: ARG CHECK // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? ppa_trans_t *trans_elm = NULL; - esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_BLEND, (void *)config, &trans_elm, mode); + esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_BLEND, (void *)oper_config, trans_config->mode, &trans_elm); if (ret == ESP_OK) { assert(trans_elm); - ret = ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, trans_elm, mode); + ret = ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, trans_elm, trans_config->mode); if (ret != ESP_OK) { ppa_recycle_transaction(trans_elm); } @@ -1270,7 +1269,7 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->fill_desc && trans_on_picked_desc->ppa_engine); - ppa_fill_transaction_t *fill_trans_desc = trans_on_picked_desc->fill_desc; + ppa_fill_oper_t *fill_trans_desc = trans_on_picked_desc->fill_desc; ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) @@ -1339,22 +1338,22 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans return false; } -esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_trans_config_t *config, ppa_trans_mode_t mode) +esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) { - ESP_RETURN_ON_FALSE(ppa_invoker && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); - ESP_RETURN_ON_FALSE(mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // TODO: ARG CHECK // fill_block_w <= PPA_BLEND_HB_V, fill_block_h <= PPA_BLEND_VB_V // TODO: Maybe do buffer invalidation here, instead of in on_picked? ppa_trans_t *trans_elm = NULL; - esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_FILL, (void *)config, &trans_elm, mode); + esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_FILL, (void *)oper_config, trans_config->mode, &trans_elm); if (ret == ESP_OK) { assert(trans_elm); - ret = ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, trans_elm, mode); + ret = ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, trans_elm, trans_config->mode); if (ret != ESP_OK) { ppa_recycle_transaction(trans_elm); } From a86e03cba30bc8ade84ac38578513ec995681196 Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Mon, 4 Mar 2024 20:50:29 +0800 Subject: [PATCH 06/15] feat(ppa): add PPA driver support for ESP32P4 Renamed SR to SRM --- .../esp_driver_ppa/include/driver/ppa.h | 24 +- components/esp_driver_ppa/src/ppa.c | 453 +++++++++--------- components/esp_hw_support/dma/dma2d.c | 4 +- .../dma/include/esp_private/dma2d.h | 4 +- components/hal/esp32p4/include/hal/ppa_ll.h | 94 ++-- components/hal/include/hal/dma2d_types.h | 2 +- components/hal/include/hal/ppa_types.h | 30 +- .../soc/esp32p4/include/soc/dma2d_channel.h | 4 +- 8 files changed, 313 insertions(+), 302 deletions(-) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 062c839305..8847525d66 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -25,7 +25,7 @@ typedef struct ppa_invoker_t *ppa_invoker_handle_t; * These flags are supposed to be used to specify the PPA operations that are going to be used by the invoker, so that * the corresponding engines can be acquired when registering the invoker with `ppa_register_invoker`. */ -#define PPA_OPERATION_FLAG_SR (1 << 0) +#define PPA_OPERATION_FLAG_SRM (1 << 0) #define PPA_OPERATION_FLAG_BLEND (1 << 1) #define PPA_OPERATION_FLAG_FILL (1 << 2) @@ -75,7 +75,7 @@ typedef struct { ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ } ppa_trans_config_t; -#define PPA_SR_OPERATION_CONFIG struct { \ +#define PPA_SRM_OPERATION_CONFIG struct { \ void *in_buffer; /*!< TODO: could be a buffer list, link descriptors together, process a batch uint32_t batch_num; However, is it necessary? psram can not store too many pictures */ \ uint32_t in_pic_w; \ @@ -91,14 +91,14 @@ typedef struct { uint32_t out_block_offset_x; \ uint32_t out_block_offset_y; \ \ - ppa_sr_rotation_angle_t rotation_angle; \ + ppa_srm_rotation_angle_t rotation_angle; \ float scale_x; \ float scale_y; \ bool mirror_x; \ bool mirror_y; \ \ struct { \ - ppa_sr_color_mode_t mode; \ + ppa_srm_color_mode_t mode; \ color_range_t yuv_range; \ color_conv_std_rgb_yuv_t yuv_std; \ bool rgb_swap; \ @@ -110,22 +110,22 @@ typedef struct { } in_color; \ \ struct { \ - ppa_sr_color_mode_t mode; \ + ppa_srm_color_mode_t mode; \ color_range_t yuv_range; \ color_conv_std_rgb_yuv_t yuv_std; \ } out_color; \ } /** - * @brief A collection of configuration items to perform a PPA SR operation + * @brief A collection of configuration items to perform a PPA SRM operation */ -typedef PPA_SR_OPERATION_CONFIG ppa_sr_operation_config_t; +typedef PPA_SRM_OPERATION_CONFIG ppa_srm_operation_config_t; /** - * @brief Perform a scaling-and-rotating (SR) operation to a picture + * @brief Perform a scaling-rotating-mirroring (SRM) operation to a picture * - * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA SR engine - * @param[in] oper_config Pointer to a collection of configurations for the SR operation, ppa_sr_operation_config_t + * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA SRM engine + * @param[in] oper_config Pointer to a collection of configurations for the SRM operation, ppa_srm_operation_config_t * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t * * @return @@ -134,7 +134,7 @@ typedef PPA_SR_OPERATION_CONFIG ppa_sr_operation_config_t; * - ESP_ERR_NO_MEM: * - ESP_FAIL: */ -esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); +esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa_srm_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); typedef struct { void *in_bg_buffer; @@ -234,7 +234,7 @@ esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation // argb color conversion (bypass blend) -// SR and Blending are independent, can work at the same time +// SRM and Blending are independent, can work at the same time // Fill is in blend, so fill and blend cannot work at the same time // Consider blocking and non-blocking options diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c index 83dd39a5e8..fbd2f82c8d 100644 --- a/components/esp_driver_ppa/src/ppa.c +++ b/components/esp_driver_ppa/src/ppa.c @@ -49,19 +49,19 @@ void ppa_hal_deinit(ppa_hal_context_t *hal) hal->dev = NULL; } -// PPA module contains SR engine and Blending engine +// PPA module contains SRM engine and Blending engine typedef struct ppa_engine_t ppa_engine_t; typedef struct ppa_invoker_t ppa_invoker_t; typedef struct { - PPA_SR_OPERATION_CONFIG; + PPA_SRM_OPERATION_CONFIG; uint32_t scale_x_int; uint32_t scale_x_frag; uint32_t scale_y_int; uint32_t scale_y_frag; -} ppa_sr_oper_t; +} ppa_srm_oper_t; typedef ppa_blend_operation_config_t ppa_blend_oper_t; @@ -77,7 +77,7 @@ typedef struct ppa_trans_s { typedef struct { union { - ppa_sr_oper_t *sr_desc; + ppa_srm_oper_t *srm_desc; ppa_blend_oper_t *blend_desc; ppa_fill_oper_t *fill_desc; void *op_desc; @@ -97,11 +97,11 @@ struct ppa_engine_t { // dma2d_rx_event_callbacks_t event_cbs; }; -typedef struct ppa_sr_engine_t { +typedef struct ppa_srm_engine_t { ppa_engine_t base; dma2d_descriptor_t *dma_tx_desc; dma2d_descriptor_t *dma_rx_desc; -} ppa_sr_engine_t; +} ppa_srm_engine_t; typedef struct ppa_blend_engine_t { ppa_engine_t base; @@ -111,9 +111,9 @@ typedef struct ppa_blend_engine_t { } ppa_blend_engine_t; struct ppa_invoker_t { - ppa_engine_t *sr_engine; + ppa_engine_t *srm_engine; ppa_engine_t *blending_engine; - uint32_t sr_trans_cnt; + uint32_t srm_trans_cnt; uint32_t blending_trans_cnt; portMUX_TYPE spinlock; bool in_accepting_trans_state; @@ -122,9 +122,10 @@ struct ppa_invoker_t { }; typedef enum { - PPA_OPERATION_SR, + PPA_OPERATION_SRM, PPA_OPERATION_BLEND, PPA_OPERATION_FILL, + PPA_OPERATION_NUM, } ppa_operation_t; typedef struct ppa_platform_t { @@ -132,9 +133,9 @@ typedef struct ppa_platform_t { portMUX_TYPE spinlock; // platform level spinlock ppa_hal_context_t hal; dma2d_pool_handle_t dma2d_pool_handle; - ppa_sr_engine_t *sr; + ppa_srm_engine_t *srm; ppa_blend_engine_t *blending; - uint32_t sr_engine_ref_count; + uint32_t srm_engine_ref_count; uint32_t blend_engine_ref_count; uint32_t dma_desc_mem_size; } ppa_platform_t; @@ -152,10 +153,16 @@ typedef struct { static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine); static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine); -static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); +static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); +const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATION_NUM] = { + ppa_srm_transaction_on_picked, + ppa_blend_transaction_on_picked, + ppa_fill_transaction_on_picked, +}; + // extern uint32_t dma2d_tx_channel_reserved_mask[SOC_DMA2D_GROUPS]; // extern uint32_t dma2d_rx_channel_reserved_mask[SOC_DMA2D_GROUPS]; // static uint32_t ppa_specified_tx_channel_mask = 0; @@ -173,7 +180,7 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin { esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(config && ret_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(config->engine == PPA_ENGINE_TYPE_SR || config->engine == PPA_ENGINE_TYPE_BLEND, ESP_ERR_INVALID_ARG, TAG, "invalid engine"); + ESP_RETURN_ON_FALSE(config->engine == PPA_ENGINE_TYPE_SRM || config->engine == PPA_ENGINE_TYPE_BLEND, ESP_ERR_INVALID_ARG, TAG, "invalid engine"); *ret_engine = NULL; @@ -185,39 +192,42 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin s_platform.dma_desc_mem_size = ALIGN_UP(sizeof(dma2d_descriptor_align8_t), alignment); } - if (config->engine == PPA_ENGINE_TYPE_SR) { - if (!s_platform.sr) { - ppa_sr_engine_t *sr_engine = heap_caps_calloc(1, sizeof(ppa_sr_engine_t), PPA_MEM_ALLOC_CAPS); - SemaphoreHandle_t sr_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *sr_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *sr_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - if (sr_engine && sr_sem && sr_tx_dma_desc && sr_rx_dma_desc) { - sr_engine->dma_tx_desc = sr_tx_dma_desc; - sr_engine->dma_rx_desc = sr_rx_dma_desc; - sr_engine->base.type = PPA_ENGINE_TYPE_SR; - sr_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; - sr_engine->base.sem = sr_sem; - xSemaphoreGive(sr_engine->base.sem); - // sr_engine->base.in_accepting_trans_state = true; - STAILQ_INIT(&sr_engine->base.trans_stailq); - // sr_engine->base.event_cbs - s_platform.sr = sr_engine; - s_platform.sr_engine_ref_count++; - *ret_engine = &sr_engine->base; + if (config->engine == PPA_ENGINE_TYPE_SRM) { + if (!s_platform.srm) { + ppa_srm_engine_t *srm_engine = heap_caps_calloc(1, sizeof(ppa_srm_engine_t), PPA_MEM_ALLOC_CAPS); + SemaphoreHandle_t srm_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *srm_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *srm_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + if (srm_engine && srm_sem && srm_tx_dma_desc && srm_rx_dma_desc) { + srm_engine->dma_tx_desc = srm_tx_dma_desc; + srm_engine->dma_rx_desc = srm_rx_dma_desc; + srm_engine->base.type = PPA_ENGINE_TYPE_SRM; + srm_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + srm_engine->base.sem = srm_sem; + xSemaphoreGive(srm_engine->base.sem); + // srm_engine->base.in_accepting_trans_state = true; + STAILQ_INIT(&srm_engine->base.trans_stailq); + // srm_engine->base.event_cbs + s_platform.srm = srm_engine; + s_platform.srm_engine_ref_count++; + *ret_engine = &srm_engine->base; + + // TODO: Register PPA interrupt? Useful for SRM parameter error. If SRM parameter error, blocks at 2D-DMA, transaction can never finish, stuck... + // need a way to force end } else { ret = ESP_ERR_NO_MEM; - ESP_LOGE(TAG, "no mem to register PPA SR engine"); - free(sr_engine); - if (sr_sem) { - vSemaphoreDeleteWithCaps(sr_sem); + ESP_LOGE(TAG, "no mem to register PPA SRM engine"); + free(srm_engine); + if (srm_sem) { + vSemaphoreDeleteWithCaps(srm_sem); } - free(sr_tx_dma_desc); - free(sr_rx_dma_desc); + free(srm_tx_dma_desc); + free(srm_rx_dma_desc); } } else { - // SR engine already registered - s_platform.sr_engine_ref_count++; - *ret_engine = &s_platform.sr->base; + // SRM engine already registered + s_platform.srm_engine_ref_count++; + *ret_engine = &s_platform.srm->base; } } else if (config->engine == PPA_ENGINE_TYPE_BLEND) { if (!s_platform.blending) { @@ -295,23 +305,23 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) ESP_RETURN_ON_FALSE(ppa_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); _lock_acquire(&s_platform.mutex); - if (ppa_engine->type == PPA_ENGINE_TYPE_SR) { - ppa_sr_engine_t *sr_engine = __containerof(ppa_engine, ppa_sr_engine_t, base); - s_platform.sr_engine_ref_count--; - if (s_platform.sr_engine_ref_count == 0) { - // // Stop accepting new transactions to SR engine - // portENTER_CRITICAL(&sr_engine->base.spinlock); - // sr_engine->base.in_accepting_trans_state = false; - // portEXIT_CRITICAL(&sr_engine->base.spinlock); + if (ppa_engine->type == PPA_ENGINE_TYPE_SRM) { + ppa_srm_engine_t *srm_engine = __containerof(ppa_engine, ppa_srm_engine_t, base); + s_platform.srm_engine_ref_count--; + if (s_platform.srm_engine_ref_count == 0) { + // // Stop accepting new transactions to SRM engine + // portENTER_CRITICAL(&srm_engine->base.spinlock); + // srm_engine->base.in_accepting_trans_state = false; + // portEXIT_CRITICAL(&srm_engine->base.spinlock); // // Wait until all transactions get processed - // while (!STAILQ_EMPTY(&sr_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide - assert(STAILQ_EMPTY(&sr_engine->base.trans_stailq)); + // while (!STAILQ_EMPTY(&srm_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide + assert(STAILQ_EMPTY(&srm_engine->base.trans_stailq)); // Now, time to free - s_platform.sr = NULL; - free(sr_engine->dma_tx_desc); - free(sr_engine->dma_rx_desc); - vSemaphoreDeleteWithCaps(sr_engine->base.sem); - free(sr_engine); + s_platform.srm = NULL; + free(srm_engine->dma_tx_desc); + free(srm_engine->dma_rx_desc); + vSemaphoreDeleteWithCaps(srm_engine->base.sem); + free(srm_engine); } } else if (ppa_engine->type == PPA_ENGINE_TYPE_BLEND) { ppa_blend_engine_t *blending_engine = __containerof(ppa_engine, ppa_blend_engine_t, base); @@ -334,8 +344,8 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) } } - if (!s_platform.sr && !s_platform.blending) { - assert(s_platform.sr_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0); + if (!s_platform.srm && !s_platform.blending) { + assert(s_platform.srm_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0); if (s_platform.dma2d_pool_handle) { dma2d_release_pool(s_platform.dma2d_pool_handle); // TODO: check return value. If not ESP_OK, then must be error on other 2D-DMA clients :( Give a warning log? @@ -363,11 +373,11 @@ esp_err_t ppa_register_invoker(const ppa_invoker_config_t *config, ppa_invoker_h invoker->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; invoker->in_accepting_trans_state = true; - if (config->operation_flag & PPA_OPERATION_FLAG_SR) { + if (config->operation_flag & PPA_OPERATION_FLAG_SRM) { ppa_engine_config_t engine_config = { - .engine = PPA_ENGINE_TYPE_SR, + .engine = PPA_ENGINE_TYPE_SRM, }; - ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->sr_engine), err, TAG, "unable to acquire SR engine"); + ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->srm_engine), err, TAG, "unable to acquire SRM engine"); } if (config->operation_flag & PPA_OPERATION_FLAG_BLEND || config->operation_flag & PPA_OPERATION_FLAG_FILL) { ppa_engine_config_t engine_config = { @@ -390,15 +400,15 @@ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) bool do_unregister = false; portENTER_CRITICAL(&ppa_invoker->spinlock); - if (ppa_invoker->sr_trans_cnt == 0 && ppa_invoker->blending_trans_cnt == 0) { + if (ppa_invoker->srm_trans_cnt == 0 && ppa_invoker->blending_trans_cnt == 0) { ppa_invoker->in_accepting_trans_state = false; do_unregister = true; } portEXIT_CRITICAL(&ppa_invoker->spinlock); ESP_RETURN_ON_FALSE(do_unregister, ESP_ERR_INVALID_STATE, TAG, "invoker still has unprocessed trans"); - if (ppa_invoker->sr_engine) { - ppa_engine_release(ppa_invoker->sr_engine); + if (ppa_invoker->srm_engine) { + ppa_engine_release(ppa_invoker->srm_engine); } if (ppa_invoker->blending_engine) { ppa_engine_release(ppa_invoker->blending_engine); @@ -445,30 +455,30 @@ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) // // } // // } -// // // Register PPA SR engine -// // if (ret == ESP_OK && config->sr_engine_en && !s_platform.group[group_id]->sr) { -// // ppa_sr_engine_t *sr_engine = heap_caps_calloc(1, sizeof(ppa_sr_engine_t), PPA_MEM_ALLOC_CAPS); -// // SemaphoreHandle_t sr_sem = xSemaphoreCreateBinary(); -// // dma2d_descriptor_t *sr_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API -// // dma2d_descriptor_t *sr_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); -// // if (sr_engine && sr_sem && sr_tx_dma_desc && sr_rx_dma_desc) { -// // sr_engine->dma_tx_desc = sr_tx_dma_desc; -// // sr_engine->dma_rx_desc = sr_rx_dma_desc; -// // sr_engine->base.group = s_platform.group[group_id]; -// // sr_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; -// // sr_engine->base.sem = sr_sem; -// // xSemaphoreGive(sr_engine->base.sem); -// // sr_engine->base.in_accepting_trans_state = true; -// // STAILQ_INIT(&sr_engine->base.trans_stailq); -// // // sr_engine->base.event_cbs -// // s_platform.group[group_id]->sr = sr_engine; +// // // Register PPA SRM engine +// // if (ret == ESP_OK && config->srm_engine_en && !s_platform.group[group_id]->srm) { +// // ppa_srm_engine_t *srm_engine = heap_caps_calloc(1, sizeof(ppa_srm_engine_t), PPA_MEM_ALLOC_CAPS); +// // SemaphoreHandle_t srm_sem = xSemaphoreCreateBinary(); +// // dma2d_descriptor_t *srm_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API +// // dma2d_descriptor_t *srm_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); +// // if (srm_engine && srm_sem && srm_tx_dma_desc && srm_rx_dma_desc) { +// // srm_engine->dma_tx_desc = srm_tx_dma_desc; +// // srm_engine->dma_rx_desc = srm_rx_dma_desc; +// // srm_engine->base.group = s_platform.group[group_id]; +// // srm_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +// // srm_engine->base.sem = srm_sem; +// // xSemaphoreGive(srm_engine->base.sem); +// // srm_engine->base.in_accepting_trans_state = true; +// // STAILQ_INIT(&srm_engine->base.trans_stailq); +// // // srm_engine->base.event_cbs +// // s_platform.group[group_id]->srm = srm_engine; // // } else { // // ret = ESP_ERR_NO_MEM; -// // ESP_LOGE(TAG, "no mem to register PPA SR engine"); -// // free(sr_engine); -// // if (sr_sem) vSemaphoreDelete(sr_sem); -// // free(sr_tx_dma_desc); -// // free(sr_rx_dma_desc); +// // ESP_LOGE(TAG, "no mem to register PPA SRM engine"); +// // free(srm_engine); +// // if (srm_sem) vSemaphoreDelete(srm_sem); +// // free(srm_tx_dma_desc); +// // free(srm_rx_dma_desc); // // } // // } @@ -506,12 +516,12 @@ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) // // ppa_module_release // bool new_group = false; -// bool new_sr_engine = false; +// bool new_srm_engine = false; // bool new_blending_engine = false; // ppa_group_t *pre_alloc_group = heap_caps_calloc(1, sizeof(ppa_group_t), PPA_MEM_ALLOC_CAPS); -// ppa_sr_engine_t *sr_engine = NULL; +// ppa_srm_engine_t *srm_engine = NULL; // ppa_blend_engine_t *blending_engine = NULL; -// SemaphoreHandle_t sr_sem = NULL, blending_sem = NULL; +// SemaphoreHandle_t srm_sem = NULL, blending_sem = NULL; // // portENTER_CRITICAL(&s_platform.spinlock); // if (!s_platform.group[group_id]) { @@ -544,33 +554,33 @@ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) // } // } -// if (ret == ESP_OK && config->sr_engine_en) { -// sr_engine = heap_caps_calloc(1, sizeof(ppa_sr_engine_t), PPA_MEM_ALLOC_CAPS); -// sr_sem = xSemaphoreCreateBinary(); -// dma2d_descriptor_t *sr_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API -// dma2d_descriptor_t *sr_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); -// // Register PPA SR engine +// if (ret == ESP_OK && config->srm_engine_en) { +// srm_engine = heap_caps_calloc(1, sizeof(ppa_srm_engine_t), PPA_MEM_ALLOC_CAPS); +// srm_sem = xSemaphoreCreateBinary(); +// dma2d_descriptor_t *srm_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API +// dma2d_descriptor_t *srm_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); +// // Register PPA SRM engine // portENTER_CRITICAL(&s_platform.group[group_id]->spinlock); -// if (!s_platform.group[group_id]->sr) { -// if (sr_engine && sr_sem && sr_tx_dma_desc && sr_rx_dma_desc) { -// new_sr_engine = true; -// sr_engine->dma_tx_desc = sr_tx_dma_desc; -// sr_engine->dma_rx_desc = sr_rx_dma_desc; -// sr_engine->base.group = s_platform.group[group_id]; -// sr_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; -// sr_engine->base.sem = sr_sem; -// xSemaphoreGive(sr_engine->base.sem); -// sr_engine->base.in_accepting_trans_state = true; -// STAILQ_INIT(&sr_engine->base.trans_stailq); -// // sr_engine->base.event_cbs -// s_platform.group[group_id]->sr = sr_engine; +// if (!s_platform.group[group_id]->srm) { +// if (srm_engine && srm_sem && srm_tx_dma_desc && srm_rx_dma_desc) { +// new_srm_engine = true; +// srm_engine->dma_tx_desc = srm_tx_dma_desc; +// srm_engine->dma_rx_desc = srm_rx_dma_desc; +// srm_engine->base.group = s_platform.group[group_id]; +// srm_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +// srm_engine->base.sem = srm_sem; +// xSemaphoreGive(srm_engine->base.sem); +// srm_engine->base.in_accepting_trans_state = true; +// STAILQ_INIT(&srm_engine->base.trans_stailq); +// // srm_engine->base.event_cbs +// s_platform.group[group_id]->srm = srm_engine; // } else { // ret = ESP_ERR_NO_MEM; // } // } // portEXIT_CRITICAL(&s_platform.group[group_id]->spinlock); // if (ret == ESP_ERR_NO_MEM) { -// ESP_LOGE(TAG, "no mem to register PPA SR engine"); +// ESP_LOGE(TAG, "no mem to register PPA SRM engine"); // } // } @@ -606,9 +616,9 @@ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) // } // } -// if (!new_sr_engine) { -// free(sr_engine); -// if (sr_sem) vSemaphoreDelete(sr_sem); +// if (!new_srm_engine) { +// free(srm_engine); +// if (srm_sem) vSemaphoreDelete(srm_sem); // // TODO: free desc // } // if (!new_blending_engine) { @@ -636,18 +646,18 @@ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) // bool do_deinitialize = false; // int group_id = ppa_group->group_id; -// ppa_sr_engine_t *sr_engine = ppa_group->sr; +// ppa_srm_engine_t *srm_engine = ppa_group->srm; // ppa_blend_engine_t *blending_engine = ppa_group->blending; -// bool sr_no_waiting_trans = true; +// bool srm_no_waiting_trans = true; // bool blending_no_waiting_trans = true; // // portENTER_CRITICAL(&s_platform.spinlock); // portENTER_CRITICAL(&ppa_group->spinlock); -// if (sr_engine) { -// sr_engine->base.in_accepting_trans_state = false; -// portENTER_CRITICAL(&sr_engine->base.spinlock); -// sr_no_waiting_trans = STAILQ_EMPTY(&sr_engine->base.trans_stailq); -// portEXIT_CRITICAL(&sr_engine->base.spinlock); +// if (srm_engine) { +// srm_engine->base.in_accepting_trans_state = false; +// portENTER_CRITICAL(&srm_engine->base.spinlock); +// srm_no_waiting_trans = STAILQ_EMPTY(&srm_engine->base.trans_stailq); +// portEXIT_CRITICAL(&srm_engine->base.spinlock); // } // if (blending_engine) { // blending_engine->base.in_accepting_trans_state = false; @@ -656,9 +666,9 @@ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) // portEXIT_CRITICAL(&blending_engine->base.spinlock); // } // portEXIT_CRITICAL(&ppa_group->spinlock); -// if (sr_no_waiting_trans && blending_no_waiting_trans) { +// if (srm_no_waiting_trans && blending_no_waiting_trans) { // do_deinitialize = true; -// ppa_group->sr = NULL; +// ppa_group->srm = NULL; // ppa_group->blending = NULL; // s_platform.group[group_id] = NULL; // } else { @@ -667,11 +677,11 @@ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) // // portEXIT_CRITICAL(&s_platform.spinlock); // if (do_deinitialize) { -// if (sr_engine) { -// free(sr_engine->dma_tx_desc); -// free(sr_engine->dma_rx_desc); -// vSemaphoreDelete(sr_engine->base.sem); -// free(sr_engine); +// if (srm_engine) { +// free(srm_engine->dma_tx_desc); +// free(srm_engine->dma_rx_desc); +// vSemaphoreDelete(srm_engine->base.sem); +// free(srm_engine); // } // if (blending_engine) { // free(blending_engine->dma_tx_bg_desc); @@ -724,7 +734,7 @@ static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_eng dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, PPA_MEM_ALLOC_CAPS); dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)heap_caps_calloc(1, sizeof(dma2d_trans_config_t), PPA_MEM_ALLOC_CAPS); ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)heap_caps_calloc(1, sizeof(ppa_dma2d_trans_on_picked_config_t), PPA_MEM_ALLOC_CAPS); - size_t ppa_trans_desc_size = (ppa_operation == PPA_OPERATION_SR) ? sizeof(ppa_sr_oper_t) : + size_t ppa_trans_desc_size = (ppa_operation == PPA_OPERATION_SRM) ? sizeof(ppa_srm_oper_t) : (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_oper_t) : (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_oper_t) : 0; assert(ppa_trans_desc_size != 0); @@ -735,7 +745,7 @@ static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_eng ESP_GOTO_ON_FALSE(new_trans_elm->sem, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); } - size_t cpy_size = (ppa_operation == PPA_OPERATION_SR) ? sizeof(ppa_sr_operation_config_t) : + size_t cpy_size = (ppa_operation == PPA_OPERATION_SRM) ? sizeof(ppa_srm_operation_config_t) : (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_operation_config_t) : (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_operation_config_t) : 0; memcpy(ppa_trans_desc, oper_config, cpy_size); @@ -743,9 +753,9 @@ static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_eng trans_on_picked_desc->op_desc = ppa_trans_desc; trans_on_picked_desc->ppa_engine = ppa_engine_base; trans_on_picked_desc->trans_elm = new_trans_elm; - trans_on_picked_desc->trigger_periph = (engine_type == PPA_ENGINE_TYPE_SR) ? DMA2D_TRIG_PERIPH_PPA_SR : DMA2D_TRIG_PERIPH_PPA_BLEND; + trans_on_picked_desc->trigger_periph = (engine_type == PPA_ENGINE_TYPE_SRM) ? DMA2D_TRIG_PERIPH_PPA_SRM : DMA2D_TRIG_PERIPH_PPA_BLEND; - dma_trans_desc->tx_channel_num = (ppa_operation == PPA_OPERATION_SR) ? 1 : + dma_trans_desc->tx_channel_num = (ppa_operation == PPA_OPERATION_SRM) ? 1 : (ppa_operation == PPA_OPERATION_BLEND) ? 2 : 0; // PPA_OPERATION_FILL does not have data input dma_trans_desc->rx_channel_num = 1; @@ -753,9 +763,7 @@ static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_eng // dma_trans_desc->specified_rx_channel_mask = ppa_specified_rx_channel_mask; dma_trans_desc->user_config = (void *)trans_on_picked_desc; - dma_trans_desc->on_job_picked = (ppa_operation == PPA_OPERATION_SR) ? ppa_sr_transaction_on_picked : - (ppa_operation == PPA_OPERATION_BLEND) ? ppa_blend_transaction_on_picked : - (ppa_operation == PPA_OPERATION_FILL) ? ppa_fill_transaction_on_picked : NULL; + dma_trans_desc->on_job_picked = ppa_oper_trans_on_picked_func[ppa_operation]; new_trans_elm->trans_desc = dma_trans_desc; new_trans_elm->dma_trans_placeholder = dma_trans_elm; @@ -779,8 +787,8 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t if (ppa_invoker->in_accepting_trans_state) { // Send transaction into PPA engine queue STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry); - if (engine_type == PPA_ENGINE_TYPE_SR) { - ppa_invoker->sr_trans_cnt++; + if (engine_type == PPA_ENGINE_TYPE_SRM) { + ppa_invoker->srm_trans_cnt++; } else { ppa_invoker->blending_trans_cnt++; } @@ -868,8 +876,8 @@ static bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_eve } portENTER_CRITICAL_ISR(&invoker->spinlock); - if (engine_type == PPA_ENGINE_TYPE_SR) { - invoker->sr_trans_cnt--; + if (engine_type == PPA_ENGINE_TYPE_SRM) { + invoker->srm_trans_cnt--; } else { invoker->blending_trans_cnt--; } @@ -880,14 +888,14 @@ static bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_eve return need_yield; } -static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) { assert(num_chans == 2 && dma2d_chans && user_config); ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; - assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SR && trans_on_picked_desc->sr_desc && trans_on_picked_desc->ppa_engine); + assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SRM && trans_on_picked_desc->srm_desc && trans_on_picked_desc->ppa_engine); - ppa_sr_oper_t *sr_trans_desc = trans_on_picked_desc->sr_desc; - ppa_sr_engine_t *sr_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_sr_engine_t, base); + ppa_srm_oper_t *srm_trans_desc = trans_on_picked_desc->srm_desc; + ppa_srm_engine_t *srm_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_srm_engine_t, base); // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) free(trans_on_picked_desc->trans_elm->dma_trans_placeholder); @@ -905,62 +913,62 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) // Write back in_buffer color_space_pixel_format_t in_pixel_format = { - .color_type_id = sr_trans_desc->in_color.mode, + .color_type_id = srm_trans_desc->in_color.mode, }; - uint32_t in_buffer_len = sr_trans_desc->in_pic_w * sr_trans_desc->in_pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; - esp_cache_msync(sr_trans_desc->in_buffer, in_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + uint32_t in_buffer_len = srm_trans_desc->in_pic_w * srm_trans_desc->in_pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; + esp_cache_msync(srm_trans_desc->in_buffer, in_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); // Invalidate out_buffer color_space_pixel_format_t out_pixel_format = { - .color_type_id = sr_trans_desc->out_color.mode, + .color_type_id = srm_trans_desc->out_color.mode, }; - uint32_t out_buffer_len = sr_trans_desc->out_pic_w * sr_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - esp_cache_msync(sr_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + uint32_t out_buffer_len = srm_trans_desc->out_pic_w * srm_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + esp_cache_msync(srm_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); // Fill 2D-DMA descriptors - sr_engine->dma_tx_desc->vb_size = sr_trans_desc->in_block_h; - sr_engine->dma_tx_desc->hb_length = sr_trans_desc->in_block_w; - sr_engine->dma_tx_desc->err_eof = 0; - sr_engine->dma_tx_desc->dma2d_en = 1; - sr_engine->dma_tx_desc->suc_eof = 1; - sr_engine->dma_tx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - sr_engine->dma_tx_desc->va_size = sr_trans_desc->in_pic_h; - sr_engine->dma_tx_desc->ha_length = sr_trans_desc->in_pic_w; - sr_engine->dma_tx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_pixel_format); - sr_engine->dma_tx_desc->y = sr_trans_desc->in_block_offset_y; - sr_engine->dma_tx_desc->x = sr_trans_desc->in_block_offset_x; - sr_engine->dma_tx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - sr_engine->dma_tx_desc->buffer = (void *)sr_trans_desc->in_buffer; - sr_engine->dma_tx_desc->next = NULL; + srm_engine->dma_tx_desc->vb_size = srm_trans_desc->in_block_h; + srm_engine->dma_tx_desc->hb_length = srm_trans_desc->in_block_w; + srm_engine->dma_tx_desc->err_eof = 0; + srm_engine->dma_tx_desc->dma2d_en = 1; + srm_engine->dma_tx_desc->suc_eof = 1; + srm_engine->dma_tx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + srm_engine->dma_tx_desc->va_size = srm_trans_desc->in_pic_h; + srm_engine->dma_tx_desc->ha_length = srm_trans_desc->in_pic_w; + srm_engine->dma_tx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_pixel_format); + srm_engine->dma_tx_desc->y = srm_trans_desc->in_block_offset_y; + srm_engine->dma_tx_desc->x = srm_trans_desc->in_block_offset_x; + srm_engine->dma_tx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + srm_engine->dma_tx_desc->buffer = (void *)srm_trans_desc->in_buffer; + srm_engine->dma_tx_desc->next = NULL; // vb_size, hb_length can be any value (auto writeback). However, if vb_size/hb_length is 0, it triggers 2D-DMA DESC_ERROR interrupt, and dma2d driver will automatically ends the transaction. Therefore, to avoid this, we set them to 1. - sr_engine->dma_rx_desc->vb_size = 1; - sr_engine->dma_rx_desc->hb_length = 1; - sr_engine->dma_rx_desc->err_eof = 0; - sr_engine->dma_rx_desc->dma2d_en = 1; - sr_engine->dma_rx_desc->suc_eof = 1; - sr_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - sr_engine->dma_rx_desc->va_size = sr_trans_desc->out_pic_h; - sr_engine->dma_rx_desc->ha_length = sr_trans_desc->out_pic_w; + srm_engine->dma_rx_desc->vb_size = 1; + srm_engine->dma_rx_desc->hb_length = 1; + srm_engine->dma_rx_desc->err_eof = 0; + srm_engine->dma_rx_desc->dma2d_en = 1; + srm_engine->dma_rx_desc->suc_eof = 1; + srm_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + srm_engine->dma_rx_desc->va_size = srm_trans_desc->out_pic_h; + srm_engine->dma_rx_desc->ha_length = srm_trans_desc->out_pic_w; // pbyte can be any value - sr_engine->dma_rx_desc->y = sr_trans_desc->out_block_offset_y; - sr_engine->dma_rx_desc->x = sr_trans_desc->out_block_offset_x; - sr_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - sr_engine->dma_rx_desc->buffer = (void *)sr_trans_desc->out_buffer; - sr_engine->dma_rx_desc->next = NULL; + srm_engine->dma_rx_desc->y = srm_trans_desc->out_block_offset_y; + srm_engine->dma_rx_desc->x = srm_trans_desc->out_block_offset_x; + srm_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + srm_engine->dma_rx_desc->buffer = (void *)srm_trans_desc->out_buffer; + srm_engine->dma_rx_desc->next = NULL; - esp_cache_msync((void *)sr_engine->dma_tx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - esp_cache_msync((void *)sr_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)srm_engine->dma_tx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)srm_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - // printf("desc addr: %p\n", sr_engine->dma_rx_desc); - // printf("desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)sr_engine->dma_rx_desc, *(uint32_t *)((uint32_t)sr_engine->dma_rx_desc + 4), *(uint32_t *)((uint32_t)sr_engine->dma_rx_desc + 8), *(uint32_t *)((uint32_t)sr_engine->dma_rx_desc + 12), *(uint32_t *)((uint32_t)sr_engine->dma_rx_desc + 16)); + // printf("desc addr: %p\n", srm_engine->dma_rx_desc); + // printf("desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)srm_engine->dma_rx_desc, *(uint32_t *)((uint32_t)srm_engine->dma_rx_desc + 4), *(uint32_t *)((uint32_t)srm_engine->dma_rx_desc + 8), *(uint32_t *)((uint32_t)srm_engine->dma_rx_desc + 12), *(uint32_t *)((uint32_t)srm_engine->dma_rx_desc + 16)); // Configure 2D-DMA channels dma2d_trigger_t trig_periph = { - .periph = DMA2D_TRIG_PERIPH_PPA_SR, - .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SR_TX, + .periph = DMA2D_TRIG_PERIPH_PPA_SRM, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SRM_TX, }; dma2d_connect(dma2d_tx_chan, &trig_periph); - trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SR_RX; + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SRM_RX; dma2d_connect(dma2d_rx_chan, &trig_periph); dma2d_transfer_ability_t dma_transfer_ability = { @@ -972,20 +980,20 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); // YUV444 and YUV422 are not supported by PPA module, need to utilize 2D-DMA color space conversion feature to do a conversion - ppa_sr_color_mode_t ppa_in_color_mode = sr_trans_desc->in_color.mode; - if (ppa_in_color_mode == PPA_SR_COLOR_MODE_YUV444) { - ppa_in_color_mode = PPA_SR_COLOR_MODE_RGB888; + ppa_srm_color_mode_t ppa_in_color_mode = srm_trans_desc->in_color.mode; + if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV444) { + ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; dma2d_csc_config_t dma_tx_csc = {0}; - if (sr_trans_desc->in_color.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + if (srm_trans_desc->in_color.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_601; } else { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_709; } dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); - } else if (ppa_in_color_mode == PPA_SR_COLOR_MODE_YUV422) { - ppa_in_color_mode = PPA_SR_COLOR_MODE_RGB888; + } else if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV422) { + ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; dma2d_csc_config_t dma_tx_csc = {0}; - if (sr_trans_desc->in_color.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + if (srm_trans_desc->in_color.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_601; } else { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_709; @@ -993,9 +1001,9 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); } - ppa_sr_color_mode_t ppa_out_color_mode = sr_trans_desc->out_color.mode; - if (ppa_out_color_mode == PPA_SR_COLOR_MODE_YUV444) { - ppa_out_color_mode = PPA_SR_COLOR_MODE_YUV420; + ppa_srm_color_mode_t ppa_out_color_mode = srm_trans_desc->out_color.mode; + if (ppa_out_color_mode == PPA_SRM_COLOR_MODE_YUV444) { + ppa_out_color_mode = PPA_SRM_COLOR_MODE_YUV420; dma2d_csc_config_t dma_rx_csc = { .rx_csc_option = DMA2D_CSC_RX_YUV420_TO_YUV444, }; @@ -1007,74 +1015,76 @@ static bool ppa_sr_transaction_on_picked(uint32_t num_chans, const dma2d_trans_c }; dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); - ppa_ll_sr_reset(s_platform.hal.dev); + ppa_ll_srm_reset(s_platform.hal.dev); - dma2d_set_desc_addr(dma2d_tx_chan, (intptr_t)sr_engine->dma_tx_desc); - dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)sr_engine->dma_rx_desc); + dma2d_set_desc_addr(dma2d_tx_chan, (intptr_t)srm_engine->dma_tx_desc); + dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)srm_engine->dma_rx_desc); dma2d_start(dma2d_tx_chan); dma2d_start(dma2d_rx_chan); - // Configure PPA SR engine - ppa_ll_sr_set_rx_color_mode(s_platform.hal.dev, ppa_in_color_mode); + // Configure PPA SRM engine + ppa_ll_srm_set_rx_color_mode(s_platform.hal.dev, ppa_in_color_mode); if (COLOR_SPACE_TYPE(ppa_in_color_mode) == COLOR_SPACE_YUV) { - ppa_ll_sr_set_rx_yuv_range(s_platform.hal.dev, sr_trans_desc->in_color.yuv_range); - ppa_ll_sr_set_yuv2rgb_std(s_platform.hal.dev, sr_trans_desc->in_color.yuv_std); + ppa_ll_srm_set_rx_yuv_range(s_platform.hal.dev, srm_trans_desc->in_color.yuv_range); + ppa_ll_srm_set_yuv2rgb_std(s_platform.hal.dev, srm_trans_desc->in_color.yuv_std); } - ppa_ll_sr_enable_rx_byte_swap(s_platform.hal.dev, sr_trans_desc->in_color.byte_swap); - ppa_ll_sr_enable_rx_rgb_swap(s_platform.hal.dev, sr_trans_desc->in_color.rgb_swap); - ppa_ll_sr_configure_rx_alpha(s_platform.hal.dev, sr_trans_desc->in_color.alpha_mode, sr_trans_desc->in_color.alpha_value); + ppa_ll_srm_enable_rx_byte_swap(s_platform.hal.dev, srm_trans_desc->in_color.byte_swap); + ppa_ll_srm_enable_rx_rgb_swap(s_platform.hal.dev, srm_trans_desc->in_color.rgb_swap); + ppa_ll_srm_configure_rx_alpha(s_platform.hal.dev, srm_trans_desc->in_color.alpha_mode, srm_trans_desc->in_color.alpha_value); - ppa_ll_sr_set_tx_color_mode(s_platform.hal.dev, ppa_out_color_mode); + ppa_ll_srm_set_tx_color_mode(s_platform.hal.dev, ppa_out_color_mode); if (COLOR_SPACE_TYPE(ppa_out_color_mode) == COLOR_SPACE_YUV) { - ppa_ll_sr_set_rx_yuv_range(s_platform.hal.dev, sr_trans_desc->out_color.yuv_range); - ppa_ll_sr_set_yuv2rgb_std(s_platform.hal.dev, sr_trans_desc->out_color.yuv_std); + ppa_ll_srm_set_rx_yuv_range(s_platform.hal.dev, srm_trans_desc->out_color.yuv_range); + ppa_ll_srm_set_yuv2rgb_std(s_platform.hal.dev, srm_trans_desc->out_color.yuv_std); } // TODO: sr_macro_bk_ro_bypass // PPA.sr_byte_order.sr_macro_bk_ro_bypass = 1; - ppa_ll_sr_set_rotation_angle(s_platform.hal.dev, sr_trans_desc->rotation_angle); - ppa_ll_sr_set_scaling_x(s_platform.hal.dev, sr_trans_desc->scale_x_int, sr_trans_desc->scale_x_frag); - ppa_ll_sr_set_scaling_y(s_platform.hal.dev, sr_trans_desc->scale_y_int, sr_trans_desc->scale_y_frag); - ppa_ll_sr_enable_mirror_x(s_platform.hal.dev, sr_trans_desc->mirror_x); - ppa_ll_sr_enable_mirror_y(s_platform.hal.dev, sr_trans_desc->mirror_y); + ppa_ll_srm_set_rotation_angle(s_platform.hal.dev, srm_trans_desc->rotation_angle); + ppa_ll_srm_set_scaling_x(s_platform.hal.dev, srm_trans_desc->scale_x_int, srm_trans_desc->scale_x_frag); + ppa_ll_srm_set_scaling_y(s_platform.hal.dev, srm_trans_desc->scale_y_int, srm_trans_desc->scale_y_frag); + ppa_ll_srm_enable_mirror_x(s_platform.hal.dev, srm_trans_desc->mirror_x); + ppa_ll_srm_enable_mirror_y(s_platform.hal.dev, srm_trans_desc->mirror_y); - ppa_ll_sr_start(s_platform.hal.dev); + ppa_ll_srm_start(s_platform.hal.dev); // No need to yield return false; } -esp_err_t ppa_do_scale_and_rotate(ppa_invoker_handle_t ppa_invoker, const ppa_sr_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) +esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa_srm_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) { ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_invoker->sr_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to SR engine"); + ESP_RETURN_ON_FALSE(ppa_invoker->srm_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to SRM engine"); ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // Any restrictions on in/out buffer address? alignment? alignment restriction comes from cache, its addr and size need to be aligned to cache line size on 912! // buffer on stack/heap // ESP_RETURN_ON_FALSE(config->rotation_angle) // ESP_RETURN_ON_FALSE(config->in/out_color_mode) // what if in_color is YUV420, out is RGB, what is out RGB range? Full range? - ESP_RETURN_ON_FALSE(oper_config->scale_x < (PPA_LL_SR_SCALING_INT_MAX + 1) && oper_config->scale_x >= (1.0 / PPA_LL_SR_SCALING_FRAG_MAX) && - oper_config->scale_y < (PPA_LL_SR_SCALING_INT_MAX + 1) && oper_config->scale_y >= (1.0 / PPA_LL_SR_SCALING_FRAG_MAX), + ESP_RETURN_ON_FALSE(oper_config->scale_x < (PPA_LL_SRM_SCALING_INT_MAX + 1) && oper_config->scale_x >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX) && + oper_config->scale_y < (PPA_LL_SRM_SCALING_INT_MAX + 1) && oper_config->scale_y >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid scale"); // byte/rgb swap with color mode only to (A)RGB color space? + // YUV420: in desc, ha/hb/va/vb/x/y must be even number + // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? ppa_trans_t *trans_elm = NULL; - esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->sr_engine, PPA_OPERATION_SR, (void *)oper_config, trans_config->mode, &trans_elm); + esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->srm_engine, PPA_OPERATION_SRM, (void *)oper_config, trans_config->mode, &trans_elm); if (ret == ESP_OK) { assert(trans_elm); // Pre-process some data ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = trans_elm->trans_desc->user_config; - ppa_sr_oper_t *sr_trans_desc = trans_on_picked_desc->sr_desc; - sr_trans_desc->scale_x_int = (uint32_t)sr_trans_desc->scale_x; - sr_trans_desc->scale_x_frag = (uint32_t)(sr_trans_desc->scale_x * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX; - sr_trans_desc->scale_y_int = (uint32_t)sr_trans_desc->scale_y; - sr_trans_desc->scale_y_frag = (uint32_t)(sr_trans_desc->scale_y * (PPA_LL_SR_SCALING_FRAG_MAX + 1)) & PPA_LL_SR_SCALING_FRAG_MAX; + ppa_srm_oper_t *srm_trans_desc = trans_on_picked_desc->srm_desc; + srm_trans_desc->scale_x_int = (uint32_t)srm_trans_desc->scale_x; + srm_trans_desc->scale_x_frag = (uint32_t)(srm_trans_desc->scale_x * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; + srm_trans_desc->scale_y_int = (uint32_t)srm_trans_desc->scale_y; + srm_trans_desc->scale_y_frag = (uint32_t)(srm_trans_desc->scale_y * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; - ret = ppa_do_operation(ppa_invoker, ppa_invoker->sr_engine, trans_elm, trans_config->mode); + ret = ppa_do_operation(ppa_invoker, ppa_invoker->srm_engine, trans_elm, trans_config->mode); if (ret != ESP_OK) { ppa_recycle_transaction(trans_elm); } @@ -1247,6 +1257,7 @@ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operati ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // TODO: ARG CHECK + // 当输入类型为 L4、A4 时,图像块的尺寸 hb 以及在图像中的偏移 x 必须为偶数 // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? diff --git a/components/esp_hw_support/dma/dma2d.c b/components/esp_hw_support/dma/dma2d.c index dab310d02c..1145f35575 100644 --- a/components/esp_hw_support/dma/dma2d.c +++ b/components/esp_hw_support/dma/dma2d.c @@ -571,7 +571,7 @@ esp_err_t dma2d_connect(dma2d_channel_handle_t dma2d_chan, const dma2d_trigger_t // Configure reorder functionality dma2d_ll_tx_enable_reorder(group->hal.dev, channel_id, dma2d_chan->status.reorder_en); // Assume dscr_port enable or not can be directly derived from trig_periph - dma2d_ll_tx_enable_dscr_port(group->hal.dev, channel_id, trig_periph->periph == DMA2D_TRIG_PERIPH_PPA_SR); + dma2d_ll_tx_enable_dscr_port(group->hal.dev, channel_id, trig_periph->periph == DMA2D_TRIG_PERIPH_PPA_SRM); // Reset to certain settings dma2d_ll_tx_enable_owner_check(group->hal.dev, channel_id, false); @@ -596,7 +596,7 @@ esp_err_t dma2d_connect(dma2d_channel_handle_t dma2d_chan, const dma2d_trigger_t // Configure reorder functionality dma2d_ll_rx_enable_reorder(group->hal.dev, channel_id, dma2d_chan->status.reorder_en); // Assume dscr_port enable or not can be directly derived from trig_periph - dma2d_ll_rx_enable_dscr_port(group->hal.dev, channel_id, trig_periph->periph == DMA2D_TRIG_PERIPH_PPA_SR); + dma2d_ll_rx_enable_dscr_port(group->hal.dev, channel_id, trig_periph->periph == DMA2D_TRIG_PERIPH_PPA_SRM); // Reset to certain settings dma2d_ll_rx_enable_owner_check(group->hal.dev, channel_id, false); diff --git a/components/esp_hw_support/dma/include/esp_private/dma2d.h b/components/esp_hw_support/dma/include/esp_private/dma2d.h index 33cd74ad56..fb983c1679 100644 --- a/components/esp_hw_support/dma/include/esp_private/dma2d.h +++ b/components/esp_hw_support/dma/include/esp_private/dma2d.h @@ -104,7 +104,7 @@ typedef struct { * @return Whether a task switch is needed after the callback function returns, * this is usually due to the callback wakes up some high priority task. */ -typedef bool (*dma2d_trans_callback_t)(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); +typedef bool (*dma2d_trans_on_picked_callback_t)(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); /** * @brief 2D-DMA channel special function flags @@ -131,7 +131,7 @@ typedef struct { uint32_t specified_tx_channel_mask; /*!< Bit mask of the specific TX channels to be used, the specified TX channels should have been reserved */ uint32_t specified_rx_channel_mask; /*!< Bit mask of the specific RX channels to be used, the specified RX channels should have been reserved */ - dma2d_trans_callback_t on_job_picked; /*!< Callback function to be called when all necessary channels to do the transaction have been acquired */ + dma2d_trans_on_picked_callback_t on_job_picked; /*!< Callback function to be called when all necessary channels to do the transaction have been acquired */ void *user_config; /*!< User registered data to be passed into `on_job_picked` callback */ } dma2d_trans_config_t; diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index 33e851d92b..3d1813b7a6 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -24,8 +24,8 @@ extern "C" { #define PPA_LL_BLEND0_CLUT_MEM_ADDR_OFFSET 0x400 #define PPA_LL_BLEND1_CLUT_MEM_ADDR_OFFSET 0x800 -#define PPA_LL_SR_SCALING_INT_MAX PPA_SR_SCAL_X_INT_V -#define PPA_LL_SR_SCALING_FRAG_MAX PPA_SR_SCAL_X_FRAG_V +#define PPA_LL_SRM_SCALING_INT_MAX PPA_SR_SCAL_X_INT_V +#define PPA_LL_SRM_SCALING_FRAG_MAX PPA_SR_SCAL_X_FRAG_V /** * @brief Enumeration of PPA blending mode @@ -64,13 +64,13 @@ static inline void ppa_ll_reset_register(void) /// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance #define ppa_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; ppa_ll_reset_register(__VA_ARGS__) -/////////////////////////// Scaling and Rotating (SR) //////////////////////////////// +///////////////////////// Scaling, Rotating, Mirroring (SRM) ////////////////////////////// /** - * @brief Reset PPA scaling and rotating engine + * @brief Reset PPA scaling-rotating-mirroring engine * * @param dev Peripheral instance address */ -static inline void ppa_ll_sr_reset(ppa_dev_t *dev) +static inline void ppa_ll_srm_reset(ppa_dev_t *dev) { dev->sr_scal_rotate.scal_rotate_rst = 1; dev->sr_scal_rotate.scal_rotate_rst = 0; @@ -83,7 +83,7 @@ static inline void ppa_ll_sr_reset(ppa_dev_t *dev) * @param x_int The integrated part of scaling coefficient in X direction, 0 - 255 * @param x_frag The fragment part of scaling coefficient in X direction, 0 - 15. Corresponding fractional value is x_frag/16. */ -static inline void ppa_ll_sr_set_scaling_x(ppa_dev_t *dev, uint32_t x_int, uint32_t x_frag) +static inline void ppa_ll_srm_set_scaling_x(ppa_dev_t *dev, uint32_t x_int, uint32_t x_frag) { HAL_ASSERT(x_int <= PPA_SR_SCAL_X_INT_V && x_frag <= PPA_SR_SCAL_X_FRAG_V); HAL_FORCE_MODIFY_U32_REG_FIELD(dev->sr_scal_rotate, sr_scal_x_int, x_int); @@ -97,7 +97,7 @@ static inline void ppa_ll_sr_set_scaling_x(ppa_dev_t *dev, uint32_t x_int, uint3 * @param y_int The integrated part of scaling coefficient in Y direction, 0 - 255 * @param y_frag The fragment part of scaling coefficient in Y direction, 0 - 15. Corresponding fractional value is y_frag/16. */ -static inline void ppa_ll_sr_set_scaling_y(ppa_dev_t *dev, uint32_t y_int, uint32_t y_frag) +static inline void ppa_ll_srm_set_scaling_y(ppa_dev_t *dev, uint32_t y_int, uint32_t y_frag) { HAL_ASSERT(y_int <= PPA_SR_SCAL_Y_INT_V && y_frag <= PPA_SR_SCAL_Y_FRAG_V); HAL_FORCE_MODIFY_U32_REG_FIELD(dev->sr_scal_rotate, sr_scal_y_int, y_int); @@ -108,22 +108,22 @@ static inline void ppa_ll_sr_set_scaling_y(ppa_dev_t *dev, uint32_t y_int, uint3 * @brief Set PPA rotation angle (in the counterclockwise direction) * * @param dev Peripheral instance address - * @param angle One of the values in ppa_sr_rotation_angle_t + * @param angle One of the values in ppa_srm_rotation_angle_t */ -static inline void ppa_ll_sr_set_rotation_angle(ppa_dev_t *dev, ppa_sr_rotation_angle_t angle) +static inline void ppa_ll_srm_set_rotation_angle(ppa_dev_t *dev, ppa_srm_rotation_angle_t angle) { uint32_t val = 0; switch (angle) { - case PPA_SR_ROTATION_ANGLE_0: + case PPA_SRM_ROTATION_ANGLE_0: val = 0; break; - case PPA_SR_ROTATION_ANGLE_90: + case PPA_SRM_ROTATION_ANGLE_90: val = 1; break; - case PPA_SR_ROTATION_ANGLE_180: + case PPA_SRM_ROTATION_ANGLE_180: val = 2; break; - case PPA_SR_ROTATION_ANGLE_270: + case PPA_SRM_ROTATION_ANGLE_270: val = 3; break; default: @@ -139,7 +139,7 @@ static inline void ppa_ll_sr_set_rotation_angle(ppa_dev_t *dev, ppa_sr_rotation_ * @param dev Peripheral instance address * @param enable True to enable; False to disable */ -static inline void ppa_ll_sr_enable_mirror_x(ppa_dev_t *dev, bool enable) +static inline void ppa_ll_srm_enable_mirror_x(ppa_dev_t *dev, bool enable) { dev->sr_scal_rotate.sr_mirror_x = enable; } @@ -150,86 +150,86 @@ static inline void ppa_ll_sr_enable_mirror_x(ppa_dev_t *dev, bool enable) * @param dev Peripheral instance address * @param enable True to enable; False to disable */ -static inline void ppa_ll_sr_enable_mirror_y(ppa_dev_t *dev, bool enable) +static inline void ppa_ll_srm_enable_mirror_y(ppa_dev_t *dev, bool enable) { dev->sr_scal_rotate.sr_mirror_y = enable; } /** - * @brief Start PPA scaling and rotating engine to perform PPA SR + * @brief Start PPA scaling and rotating engine to perform PPA SRM * * @param dev Peripheral instance address */ -static inline void ppa_ll_sr_start(ppa_dev_t *dev) +static inline void ppa_ll_srm_start(ppa_dev_t *dev) { dev->sr_scal_rotate.scal_rotate_start = 1; } /** - * @brief Set the source image color mode for PPA Scaling and Rotating engine RX + * @brief Set the source image color mode for PPA Scaling-Rotating-Mirroring engine RX * * @param dev Peripheral instance address - * @param color_mode One of the values in ppa_sr_color_mode_t + * @param color_mode One of the values in ppa_srm_color_mode_t */ -static inline void ppa_ll_sr_set_rx_color_mode(ppa_dev_t *dev, ppa_sr_color_mode_t color_mode) +static inline void ppa_ll_srm_set_rx_color_mode(ppa_dev_t *dev, ppa_srm_color_mode_t color_mode) { uint32_t val = 0; switch (color_mode) { - case PPA_SR_COLOR_MODE_ARGB8888: + case PPA_SRM_COLOR_MODE_ARGB8888: val = 0; break; - case PPA_SR_COLOR_MODE_RGB888: + case PPA_SRM_COLOR_MODE_RGB888: val = 1; break; - case PPA_SR_COLOR_MODE_RGB565: + case PPA_SRM_COLOR_MODE_RGB565: val = 2; break; - case PPA_SR_COLOR_MODE_YUV420: + case PPA_SRM_COLOR_MODE_YUV420: val = 8; break; default: - // Unsupported SR rx color mode + // Unsupported SRM rx color mode abort(); } dev->sr_color_mode.sr_rx_cm = val; } /** - * @brief Set the destination image color mode for PPA Scaling and Rotating engine TX + * @brief Set the destination image color mode for PPA Scaling-Rotating-Mirroring engine TX * * @param dev Peripheral instance address - * @param color_mode One of the values in ppa_sr_color_mode_t + * @param color_mode One of the values in ppa_srm_color_mode_t */ -static inline void ppa_ll_sr_set_tx_color_mode(ppa_dev_t *dev, ppa_sr_color_mode_t color_mode) +static inline void ppa_ll_srm_set_tx_color_mode(ppa_dev_t *dev, ppa_srm_color_mode_t color_mode) { uint32_t val = 0; switch (color_mode) { - case PPA_SR_COLOR_MODE_ARGB8888: + case PPA_SRM_COLOR_MODE_ARGB8888: val = 0; break; - case PPA_SR_COLOR_MODE_RGB888: + case PPA_SRM_COLOR_MODE_RGB888: val = 1; break; - case PPA_SR_COLOR_MODE_RGB565: + case PPA_SRM_COLOR_MODE_RGB565: val = 2; break; - case PPA_SR_COLOR_MODE_YUV420: + case PPA_SRM_COLOR_MODE_YUV420: val = 8; break; default: - // Unsupported SR tx color mode + // Unsupported SRM tx color mode abort(); } dev->sr_color_mode.sr_tx_cm = val; } /** - * @brief Set YUV to RGB protocol when PPA SR RX pixel color space is YUV + * @brief Set YUV to RGB protocol when PPA SRM RX pixel color space is YUV * * @param dev Peripheral instance address * @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t */ -static inline void ppa_ll_sr_set_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) +static inline void ppa_ll_srm_set_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) { switch (std) { case COLOR_CONV_STD_RGB_YUV_BT601: @@ -245,12 +245,12 @@ static inline void ppa_ll_sr_set_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_rgb_ } /** - * @brief Set RGB to YUV protocol when PPA SR TX pixel color space is YUV + * @brief Set RGB to YUV protocol when PPA SRM TX pixel color space is YUV * * @param dev Peripheral instance address * @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t */ -static inline void ppa_ll_sr_set_rgb2yuv_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) +static inline void ppa_ll_srm_set_rgb2yuv_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) { switch (std) { case COLOR_CONV_STD_RGB_YUV_BT601: @@ -266,12 +266,12 @@ static inline void ppa_ll_sr_set_rgb2yuv_std(ppa_dev_t *dev, color_conv_std_rgb_ } /** - * @brief Set PPA SR YUV input range + * @brief Set PPA SRM YUV input range * * @param dev Peripheral instance address * @param range One of color range options in color_range_t */ -static inline void ppa_ll_sr_set_rx_yuv_range(ppa_dev_t *dev, color_range_t range) +static inline void ppa_ll_srm_set_rx_yuv_range(ppa_dev_t *dev, color_range_t range) { switch (range) { case COLOR_RANGE_LIMIT: @@ -287,12 +287,12 @@ static inline void ppa_ll_sr_set_rx_yuv_range(ppa_dev_t *dev, color_range_t rang } /** - * @brief Set PPA SR YUV output range + * @brief Set PPA SRM YUV output range * * @param dev Peripheral instance address * @param range One of color range options in color_range_t */ -static inline void ppa_ll_sr_set_tx_yuv_range(ppa_dev_t *dev, color_range_t range) +static inline void ppa_ll_srm_set_tx_yuv_range(ppa_dev_t *dev, color_range_t range) { switch (range) { case COLOR_RANGE_LIMIT: @@ -308,31 +308,31 @@ static inline void ppa_ll_sr_set_tx_yuv_range(ppa_dev_t *dev, color_range_t rang } /** - * @brief Enable PPA SR input data wrap in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) + * @brief Enable PPA SRM input data wrap in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) * * @param dev Peripheral instance address * @param enable True to enable; False to disable */ -static inline void ppa_ll_sr_enable_rx_rgb_swap(ppa_dev_t *dev, bool enable) +static inline void ppa_ll_srm_enable_rx_rgb_swap(ppa_dev_t *dev, bool enable) { dev->sr_byte_order.sr_rx_rgb_swap_en = enable; } /** - * @brief Enable PPA SR input data swap in byte (The Byte0 and Byte1 would be swapped while byte 2 and byte 3 would be swappped) + * @brief Enable PPA SRM input data swap in byte (The Byte0 and Byte1 would be swapped while byte 2 and byte 3 would be swappped) * * Only supported when input color mode is ARGB8888 or RGB565. * * @param dev Peripheral instance address * @param enable True to enable; False to disable */ -static inline void ppa_ll_sr_enable_rx_byte_swap(ppa_dev_t *dev, bool enable) +static inline void ppa_ll_srm_enable_rx_byte_swap(ppa_dev_t *dev, bool enable) { dev->sr_byte_order.sr_rx_byte_swap_en = enable; } /** - * @brief Configure PPA SR alpha value transformation mode + * @brief Configure PPA SRM alpha value transformation mode * * @param dev Peripheral instance address * @param mode Alpha value transformation mode, one of the values in ppa_alpha_mode_t @@ -340,7 +340,7 @@ static inline void ppa_ll_sr_enable_rx_byte_swap(ppa_dev_t *dev, bool enable) * When PPA_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) * When other modes are selected, this field is not used */ -static inline void ppa_ll_sr_configure_rx_alpha(ppa_dev_t *dev, ppa_alpha_mode_t mode, uint32_t val) +static inline void ppa_ll_srm_configure_rx_alpha(ppa_dev_t *dev, ppa_alpha_mode_t mode, uint32_t val) { switch (mode) { case PPA_ALPHA_NO_CHANGE: diff --git a/components/hal/include/hal/dma2d_types.h b/components/hal/include/hal/dma2d_types.h index f19ef2d816..c88a6773a4 100644 --- a/components/hal/include/hal/dma2d_types.h +++ b/components/hal/include/hal/dma2d_types.h @@ -104,7 +104,7 @@ typedef enum { DMA2D_TRIG_PERIPH_M2M, /*!< 2D-DMA trigger peripheral: M2M */ DMA2D_TRIG_PERIPH_JPEG_ENCODER, /*!< 2D-DMA trigger peripheral: JPEG Encoder */ DMA2D_TRIG_PERIPH_JPEG_DECODER, /*!< 2D-DMA trigger peripheral: JPEG Decoder */ - DMA2D_TRIG_PERIPH_PPA_SR, /*!< 2D-DMA trigger peripheral: PPA SR engine */ + DMA2D_TRIG_PERIPH_PPA_SRM, /*!< 2D-DMA trigger peripheral: PPA SRM engine */ DMA2D_TRIG_PERIPH_PPA_BLEND, /*!< 2D-DMA trigger peripheral: PPA Blending engine */ } dma2d_trigger_peripheral_t; diff --git a/components/hal/include/hal/ppa_types.h b/components/hal/include/hal/ppa_types.h index 2f9d2678b5..cac7bd0ab4 100644 --- a/components/hal/include/hal/ppa_types.h +++ b/components/hal/include/hal/ppa_types.h @@ -17,34 +17,34 @@ extern "C" { * @brief Enumeration of engines in PPA modules */ typedef enum { - PPA_ENGINE_TYPE_SR, /*!< PPA Scaling and Rotating (SR) engine, used to perform scale_and_rotate */ + PPA_ENGINE_TYPE_SRM, /*!< PPA Scaling-Rotating-Mirroring (SRM) engine, used to perform scale, rotate, mirror */ PPA_ENGINE_TYPE_BLEND, /*!< PPA Blending engine, used to perform blend or fill */ } ppa_engine_type_t; /** - * @brief Enumeration of PPA Scaling and Rotating available rotation angle (in the counterclockwise direction) + * @brief Enumeration of PPA Scaling-Rotating-Mirroring available rotation angle (in the counterclockwise direction) */ typedef enum { - PPA_SR_ROTATION_ANGLE_0, /*!< Picture does no rotation */ - PPA_SR_ROTATION_ANGLE_90, /*!< Picture rotates 90 degrees CCW */ - PPA_SR_ROTATION_ANGLE_180, /*!< Picture rotates 180 degrees CCW */ - PPA_SR_ROTATION_ANGLE_270, /*!< Picture rotates 270 degrees CCW */ -} ppa_sr_rotation_angle_t; + PPA_SRM_ROTATION_ANGLE_0, /*!< Picture does no rotation */ + PPA_SRM_ROTATION_ANGLE_90, /*!< Picture rotates 90 degrees CCW */ + PPA_SRM_ROTATION_ANGLE_180, /*!< Picture rotates 180 degrees CCW */ + PPA_SRM_ROTATION_ANGLE_270, /*!< Picture rotates 270 degrees CCW */ +} ppa_srm_rotation_angle_t; /** - * @brief Enumeration of PPA Scaling and Rotating available color mode + * @brief Enumeration of PPA Scaling-Rotating-Mirroring available color mode */ typedef enum { - PPA_SR_COLOR_MODE_ARGB8888 = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888), /*!< PPA SR color mode: ARGB8888 */ - PPA_SR_COLOR_MODE_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< PPA SR color mode: RGB888 */ - PPA_SR_COLOR_MODE_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< PPA SR color mode: RGB565 */ - PPA_SR_COLOR_MODE_YUV420 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV420), /*!< PPA SR color mode: YUV420 */ - PPA_SR_COLOR_MODE_YUV444 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV444), /*!< PPA SR color mode: YUV444 (limited range only)*/ - PPA_SR_COLOR_MODE_YUV422 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV422), /*!< PPA SR color mode: YUV422 (input only, limited range only) */ + PPA_SRM_COLOR_MODE_ARGB8888 = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888), /*!< PPA SRM color mode: ARGB8888 */ + PPA_SRM_COLOR_MODE_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< PPA SRM color mode: RGB888 */ + PPA_SRM_COLOR_MODE_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< PPA SRM color mode: RGB565 */ + PPA_SRM_COLOR_MODE_YUV420 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV420), /*!< PPA SRM color mode: YUV420 */ + PPA_SRM_COLOR_MODE_YUV444 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV444), /*!< PPA SRM color mode: YUV444 (limited range only)*/ + PPA_SRM_COLOR_MODE_YUV422 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV422), /*!< PPA SRM color mode: YUV422 (input only, limited range only) */ // YUV444 and YUV422 not supported by PPA hardware, but seems like we can use 2D-DMA to do conversion before sending into and after coming out from the PPA module // If in_pic is YUV444/422, then TX DMA channnel could do DMA2D_CSC_TX_YUV444/422_TO_RGB888_601/709, so PPA in_color_mode is RGB888 // If out_pic is YUV444, then RX DMA channel could do DMA2D_CSC_RX_YUV420_TO_YUV444, so PPA out_color_mode is YUV420 -} ppa_sr_color_mode_t; +} ppa_srm_color_mode_t; /** * @brief Enumeration of PPA Blending available color mode diff --git a/components/soc/esp32p4/include/soc/dma2d_channel.h b/components/soc/esp32p4/include/soc/dma2d_channel.h index ed4ed0c8c5..00ba9dbbda 100644 --- a/components/soc/esp32p4/include/soc/dma2d_channel.h +++ b/components/soc/esp32p4/include/soc/dma2d_channel.h @@ -8,12 +8,12 @@ // The following macros are matched with the 2D-DMA peri_sel field peripheral selection ID #define SOC_DMA2D_TRIG_PERIPH_JPEG_RX (0) -#define SOC_DMA2D_TRIG_PERIPH_PPA_SR_RX (1) +#define SOC_DMA2D_TRIG_PERIPH_PPA_SRM_RX (1) #define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_RX (2) #define SOC_DMA2D_TRIG_PERIPH_M2M_RX (-1) // Any value of 3 ~ 7, TX and RX do not have to use same ID value for M2M #define SOC_DMA2D_TRIG_PERIPH_JPEG_TX (0) -#define SOC_DMA2D_TRIG_PERIPH_PPA_SR_TX (1) +#define SOC_DMA2D_TRIG_PERIPH_PPA_SRM_TX (1) #define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_BG_TX (2) #define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_FG_TX (3) #define SOC_DMA2D_TRIG_PERIPH_M2M_TX (-1) // Any value of 4 ~ 7, TX and RX do not have to use same ID value for M2M From 963c893cc70d93684f126d25b61330899395de56 Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Fri, 15 Mar 2024 19:13:57 +0800 Subject: [PATCH 07/15] feat(ppa): add PPA driver support for ESP32P4 Fix bugs found in AE lvgl demo examples Add color keying feature to blending --- .../esp_driver_ppa/include/driver/ppa.h | 14 ++++---- components/esp_driver_ppa/src/ppa.c | 36 +++++++++++++++++-- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 8847525d66..e0234cf95c 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -157,8 +157,6 @@ typedef struct { uint32_t out_pic_h; uint32_t out_block_offset_x; uint32_t out_block_offset_y; - //out_block_w (auto or max/min(bg, fg)) - //out_block_h struct { ppa_blend_color_mode_t mode; @@ -166,6 +164,9 @@ typedef struct { bool byte_swap; ppa_alpha_mode_t alpha_mode; uint32_t alpha_value; + bool ck_en; + uint32_t ck_rgb_low_thres; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + uint32_t ck_rgb_high_thres; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ } in_bg_color; struct { @@ -175,13 +176,16 @@ typedef struct { ppa_alpha_mode_t alpha_mode; uint32_t alpha_value; uint32_t fix_rgb_val; /*!< When in_fg_color.mode is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground. In RGB888 format (R[23:16], G[15: 8], B[7:0]). */ + bool ck_en; + uint32_t ck_rgb_low_thres; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + uint32_t ck_rgb_high_thres; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ } in_fg_color; struct { ppa_blend_color_mode_t mode; + uint32_t ck_rgb_default_val; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + bool ck_reverse_bg2fg; /*!< If this bit is set, in color keying, for the pixel, where its BG element is in the color range, but its FG element is not in the color range, it will output the FG element instead of the BG element */ } out_color; - - // TODO: colorkey } ppa_blend_operation_config_t; /** @@ -213,8 +217,6 @@ typedef struct { struct { ppa_blend_color_mode_t mode; } out_color; - - // colorkey??? } ppa_fill_operation_config_t; /** diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c index fbd2f82c8d..40f2b400c5 100644 --- a/components/esp_driver_ppa/src/ppa.c +++ b/components/esp_driver_ppa/src/ppa.c @@ -17,6 +17,7 @@ #include "freertos/idf_additions.h" #include "esp_heap_caps.h" #include "esp_cache.h" +#include "esp_private/esp_cache_private.h" #include "hal/cache_hal.h" #include "hal/cache_ll.h" #include "driver/ppa.h" @@ -28,6 +29,7 @@ #include "hal/color_types.h" #include "hal/color_hal.h" #include "esp_private/periph_ctrl.h" +#include "esp_memory_utils.h" #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) @@ -137,6 +139,7 @@ typedef struct ppa_platform_t { ppa_blend_engine_t *blending; uint32_t srm_engine_ref_count; uint32_t blend_engine_ref_count; + size_t buf_alignment_size; uint32_t dma_desc_mem_size; } ppa_platform_t; @@ -191,6 +194,9 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin if (s_platform.dma_desc_mem_size == 0) { s_platform.dma_desc_mem_size = ALIGN_UP(sizeof(dma2d_descriptor_align8_t), alignment); } + if (s_platform.buf_alignment_size == 0) { + esp_cache_get_alignment(ESP_CACHE_MALLOC_FLAG_PSRAM | ESP_CACHE_MALLOC_FLAG_DMA, &s_platform.buf_alignment_size); + } if (config->engine == PPA_ENGINE_TYPE_SRM) { if (!s_platform.srm) { @@ -916,12 +922,13 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ .color_type_id = srm_trans_desc->in_color.mode, }; uint32_t in_buffer_len = srm_trans_desc->in_pic_w * srm_trans_desc->in_pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; - esp_cache_msync(srm_trans_desc->in_buffer, in_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync(srm_trans_desc->in_buffer, in_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); // Invalidate out_buffer color_space_pixel_format_t out_pixel_format = { .color_type_id = srm_trans_desc->out_color.mode, }; uint32_t out_buffer_len = srm_trans_desc->out_pic_w * srm_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + out_buffer_len = ALIGN_UP(out_buffer_len, s_platform.buf_alignment_size); esp_cache_msync(srm_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); // Fill 2D-DMA descriptors @@ -1057,6 +1064,9 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(ppa_invoker->srm_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to SRM engine"); ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region + ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); + ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0, ESP_ERR_INVALID_ARG, TAG, "out_buffer not aligned to cache line size"); // Any restrictions on in/out buffer address? alignment? alignment restriction comes from cache, its addr and size need to be aligned to cache line size on 912! // buffer on stack/heap // ESP_RETURN_ON_FALSE(config->rotation_angle) @@ -1128,7 +1138,7 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran .color_type_id = blend_trans_desc->in_bg_color.mode, }; uint32_t in_bg_buffer_len = blend_trans_desc->in_bg_pic_w * blend_trans_desc->in_bg_pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; - esp_cache_msync(blend_trans_desc->in_bg_buffer, in_bg_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync(blend_trans_desc->in_bg_buffer, in_bg_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); color_space_pixel_format_t in_fg_pixel_format = { .color_type_id = blend_trans_desc->in_fg_color.mode, }; @@ -1139,6 +1149,7 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran .color_type_id = blend_trans_desc->out_color.mode, }; uint32_t out_buffer_len = blend_trans_desc->out_pic_w * blend_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + out_buffer_len = ALIGN_UP(out_buffer_len, s_platform.buf_alignment_size); esp_cache_msync(blend_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); // Fill 2D-DMA descriptors @@ -1245,6 +1256,16 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, blend_trans_desc->out_color.mode); + // Color keying + ppa_ll_blend_configure_rx_bg_ck_range(s_platform.hal.dev, + blend_trans_desc->in_bg_color.ck_en ? blend_trans_desc->in_bg_color.ck_rgb_low_thres : 0xFFFFFF, + blend_trans_desc->in_bg_color.ck_en ? blend_trans_desc->in_bg_color.ck_rgb_high_thres : 0); + ppa_ll_blend_configure_rx_fg_ck_range(s_platform.hal.dev, + blend_trans_desc->in_fg_color.ck_en ? blend_trans_desc->in_fg_color.ck_rgb_low_thres : 0xFFFFFF, + blend_trans_desc->in_fg_color.ck_en ? blend_trans_desc->in_fg_color.ck_rgb_high_thres : 0); + ppa_ll_blend_set_ck_default_rgb(s_platform.hal.dev, (blend_trans_desc->in_bg_color.ck_en && blend_trans_desc->in_fg_color.ck_en) ? blend_trans_desc->out_color.ck_rgb_default_val : 0); + ppa_ll_blend_enable_ck_fg_bg_reverse(s_platform.hal.dev, blend_trans_desc->out_color.ck_reverse_bg2fg); + ppa_ll_blend_start(s_platform.hal.dev, PPA_LL_BLEND_TRANS_MODE_BLEND); // No need to yield @@ -1256,6 +1277,10 @@ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operati ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region + ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); + ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0, ESP_ERR_INVALID_ARG, TAG, "out_buffer not aligned to cache line size"); + // TODO: Check out_buffer size alignment to cacheline? Since invalidate cannot use ESP_CACHE_MSYNC_FLAG_UNALIGNED. // TODO: ARG CHECK // 当输入类型为 L4、A4 时,图像块的尺寸 hb 以及在图像中的偏移 x 必须为偶数 @@ -1295,6 +1320,8 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans .color_type_id = fill_trans_desc->out_color.mode, }; uint32_t out_buffer_len = fill_trans_desc->out_pic_w * fill_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + esp_cache_msync(fill_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + out_buffer_len = ALIGN_UP(out_buffer_len, s_platform.buf_alignment_size); esp_cache_msync(fill_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); // Fill 2D-DMA descriptors @@ -1354,10 +1381,13 @@ esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + // out_buffer ptr cannot in flash region + ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); + ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0, ESP_ERR_INVALID_ARG, TAG, "out_buffer not aligned to cache line size"); // TODO: ARG CHECK // fill_block_w <= PPA_BLEND_HB_V, fill_block_h <= PPA_BLEND_VB_V - // TODO: Maybe do buffer invalidation here, instead of in on_picked? + // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? ppa_trans_t *trans_elm = NULL; esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_FILL, (void *)oper_config, trans_config->mode, &trans_elm); From 95ee1595f958cd4210da914e5ddd542b29c575fe Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Thu, 21 Mar 2024 20:23:18 +0800 Subject: [PATCH 08/15] feat(ppa): add PPA driver support for ESP32P4 Add out_buffer_size field to all oper_config_t Add strict check for out buffer addr and size alignment --- .../esp_driver_ppa/include/driver/ppa.h | 11 ++- components/esp_driver_ppa/src/ppa.c | 87 +++++++++++-------- components/hal/esp32p4/include/hal/ppa_ll.h | 24 ++--- components/hal/include/hal/ppa_types.h | 4 +- 4 files changed, 71 insertions(+), 55 deletions(-) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index e0234cf95c..6e8f6b428c 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -85,7 +85,8 @@ typedef struct { uint32_t in_block_offset_x; \ uint32_t in_block_offset_y; \ \ - void *out_buffer; /*!< TODO: alignment restriction */ \ + void *out_buffer; \ + uint32_t out_buffer_size; \ uint32_t out_pic_w; \ uint32_t out_pic_h; \ uint32_t out_block_offset_x; \ @@ -103,7 +104,7 @@ typedef struct { color_conv_std_rgb_yuv_t yuv_std; \ bool rgb_swap; \ bool byte_swap; \ - ppa_alpha_mode_t alpha_mode; \ + ppa_alpha_update_mode_t alpha_update_mode; \ uint32_t alpha_value; /*!< When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) When other alpha modes are selected, this field is not used */ \ @@ -153,6 +154,7 @@ typedef struct { uint32_t in_bg_fg_block_h; void *out_buffer; + uint32_t out_buffer_size; uint32_t out_pic_w; uint32_t out_pic_h; uint32_t out_block_offset_x; @@ -162,7 +164,7 @@ typedef struct { ppa_blend_color_mode_t mode; bool rgb_swap; bool byte_swap; - ppa_alpha_mode_t alpha_mode; + ppa_alpha_update_mode_t alpha_update_mode; uint32_t alpha_value; bool ck_en; uint32_t ck_rgb_low_thres; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ @@ -173,7 +175,7 @@ typedef struct { ppa_blend_color_mode_t mode; bool rgb_swap; bool byte_swap; - ppa_alpha_mode_t alpha_mode; + ppa_alpha_update_mode_t alpha_update_mode; uint32_t alpha_value; uint32_t fix_rgb_val; /*!< When in_fg_color.mode is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground. In RGB888 format (R[23:16], G[15: 8], B[7:0]). */ bool ck_en; @@ -209,6 +211,7 @@ typedef struct { uint32_t fill_argb_color; /*!< The color to be filled, in ARGB8888 format ((A[31:24], R[23:16], G[15: 8], B[7:0])) */ void *out_buffer; + uint32_t out_buffer_size; uint32_t out_pic_w; uint32_t out_pic_h; uint32_t out_block_offset_x; diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c index 40f2b400c5..ef4103f8bf 100644 --- a/components/esp_driver_ppa/src/ppa.c +++ b/components/esp_driver_ppa/src/ppa.c @@ -916,20 +916,9 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ dma2d_channel_handle_t dma2d_tx_chan = dma2d_chans[dma2d_tx_chan_idx].chan; dma2d_channel_handle_t dma2d_rx_chan = dma2d_chans[dma2d_rx_chan_idx].chan; - // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - // Write back in_buffer color_space_pixel_format_t in_pixel_format = { .color_type_id = srm_trans_desc->in_color.mode, }; - uint32_t in_buffer_len = srm_trans_desc->in_pic_w * srm_trans_desc->in_pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; - esp_cache_msync(srm_trans_desc->in_buffer, in_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - // Invalidate out_buffer - color_space_pixel_format_t out_pixel_format = { - .color_type_id = srm_trans_desc->out_color.mode, - }; - uint32_t out_buffer_len = srm_trans_desc->out_pic_w * srm_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - out_buffer_len = ALIGN_UP(out_buffer_len, s_platform.buf_alignment_size); - esp_cache_msync(srm_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); // Fill 2D-DMA descriptors srm_engine->dma_tx_desc->vb_size = srm_trans_desc->in_block_h; @@ -1037,7 +1026,7 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ } ppa_ll_srm_enable_rx_byte_swap(s_platform.hal.dev, srm_trans_desc->in_color.byte_swap); ppa_ll_srm_enable_rx_rgb_swap(s_platform.hal.dev, srm_trans_desc->in_color.rgb_swap); - ppa_ll_srm_configure_rx_alpha(s_platform.hal.dev, srm_trans_desc->in_color.alpha_mode, srm_trans_desc->in_color.alpha_value); + ppa_ll_srm_configure_rx_alpha(s_platform.hal.dev, srm_trans_desc->in_color.alpha_update_mode, srm_trans_desc->in_color.alpha_value); ppa_ll_srm_set_tx_color_mode(s_platform.hal.dev, ppa_out_color_mode); if (COLOR_SPACE_TYPE(ppa_out_color_mode) == COLOR_SPACE_YUV) { @@ -1066,9 +1055,15 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); - ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0, ESP_ERR_INVALID_ARG, TAG, "out_buffer not aligned to cache line size"); - // Any restrictions on in/out buffer address? alignment? alignment restriction comes from cache, its addr and size need to be aligned to cache line size on 912! + ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0 && oper_config->out_buffer_size % s_platform.buf_alignment_size == 0, + ESP_ERR_INVALID_ARG, TAG, "out_buffer addr or size not aligned to cache line size"); + color_space_pixel_format_t out_pixel_format = { + .color_type_id = oper_config->out_color.mode, + }; + uint32_t out_pic_len = oper_config->out_pic_w * oper_config->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + ESP_RETURN_ON_FALSE(out_pic_len <= oper_config->out_buffer_size, ESP_ERR_INVALID_ARG, TAG, "out_pic_w/h mismatch with out_buffer_size"); // buffer on stack/heap + // check scale w/ (out_pic_w/h - out_pic_offset_x/y)? // ESP_RETURN_ON_FALSE(config->rotation_angle) // ESP_RETURN_ON_FALSE(config->in/out_color_mode) // what if in_color is YUV420, out is RGB, what is out RGB range? Full range? @@ -1079,7 +1074,15 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa // YUV420: in desc, ha/hb/va/vb/x/y must be even number - // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? + // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) + // Write back in_buffer + color_space_pixel_format_t in_pixel_format = { + .color_type_id = oper_config->in_color.mode, + }; + uint32_t in_pic_len = oper_config->in_pic_w * oper_config->in_pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; + esp_cache_msync(oper_config->in_buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + // Invalidate out_buffer + esp_cache_msync(oper_config->out_buffer, oper_config->out_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); ppa_trans_t *trans_elm = NULL; esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->srm_engine, PPA_OPERATION_SRM, (void *)oper_config, trans_config->mode, &trans_elm); @@ -1132,25 +1135,15 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran } assert(dma2d_tx_bg_chan && dma2d_tx_fg_chan && dma2d_rx_chan); - // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - // Write back in_bg_buffer, in_fg_buffer color_space_pixel_format_t in_bg_pixel_format = { .color_type_id = blend_trans_desc->in_bg_color.mode, }; - uint32_t in_bg_buffer_len = blend_trans_desc->in_bg_pic_w * blend_trans_desc->in_bg_pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; - esp_cache_msync(blend_trans_desc->in_bg_buffer, in_bg_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); color_space_pixel_format_t in_fg_pixel_format = { .color_type_id = blend_trans_desc->in_fg_color.mode, }; - uint32_t in_fg_buffer_len = blend_trans_desc->in_fg_pic_w * blend_trans_desc->in_fg_pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; - esp_cache_msync(blend_trans_desc->in_fg_buffer, in_fg_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - // Invalidate out_buffer color_space_pixel_format_t out_pixel_format = { .color_type_id = blend_trans_desc->out_color.mode, }; - uint32_t out_buffer_len = blend_trans_desc->out_pic_w * blend_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - out_buffer_len = ALIGN_UP(out_buffer_len, s_platform.buf_alignment_size); - esp_cache_msync(blend_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); // Fill 2D-DMA descriptors blend_engine->dma_tx_bg_desc->vb_size = blend_trans_desc->in_bg_fg_block_h; @@ -1244,7 +1237,7 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran ppa_ll_blend_set_rx_bg_color_mode(s_platform.hal.dev, blend_trans_desc->in_bg_color.mode); ppa_ll_blend_enable_rx_bg_byte_swap(s_platform.hal.dev, blend_trans_desc->in_bg_color.byte_swap); ppa_ll_blend_enable_rx_bg_rgb_swap(s_platform.hal.dev, blend_trans_desc->in_bg_color.rgb_swap); - ppa_ll_blend_configure_rx_bg_alpha(s_platform.hal.dev, blend_trans_desc->in_bg_color.alpha_mode, blend_trans_desc->in_bg_color.alpha_value); + ppa_ll_blend_configure_rx_bg_alpha(s_platform.hal.dev, blend_trans_desc->in_bg_color.alpha_update_mode, blend_trans_desc->in_bg_color.alpha_value); ppa_ll_blend_set_rx_fg_color_mode(s_platform.hal.dev, blend_trans_desc->in_fg_color.mode); if (COLOR_SPACE_TYPE(blend_trans_desc->in_fg_color.mode) == COLOR_SPACE_ALPHA) { @@ -1252,7 +1245,7 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran } ppa_ll_blend_enable_rx_fg_byte_swap(s_platform.hal.dev, blend_trans_desc->in_fg_color.byte_swap); ppa_ll_blend_enable_rx_fg_rgb_swap(s_platform.hal.dev, blend_trans_desc->in_fg_color.rgb_swap); - ppa_ll_blend_configure_rx_fg_alpha(s_platform.hal.dev, blend_trans_desc->in_fg_color.alpha_mode, blend_trans_desc->in_fg_color.alpha_value); + ppa_ll_blend_configure_rx_fg_alpha(s_platform.hal.dev, blend_trans_desc->in_fg_color.alpha_update_mode, blend_trans_desc->in_fg_color.alpha_value); ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, blend_trans_desc->out_color.mode); @@ -1279,12 +1272,30 @@ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operati ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); - ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0, ESP_ERR_INVALID_ARG, TAG, "out_buffer not aligned to cache line size"); - // TODO: Check out_buffer size alignment to cacheline? Since invalidate cannot use ESP_CACHE_MSYNC_FLAG_UNALIGNED. + ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0 && oper_config->out_buffer_size % s_platform.buf_alignment_size == 0, + ESP_ERR_INVALID_ARG, TAG, "out_buffer addr or size not aligned to cache line size"); + color_space_pixel_format_t out_pixel_format = { + .color_type_id = oper_config->out_color.mode, + }; + uint32_t out_pic_len = oper_config->out_pic_w * oper_config->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + ESP_RETURN_ON_FALSE(out_pic_len <= oper_config->out_buffer_size, ESP_ERR_INVALID_ARG, TAG, "out_pic_w/h mismatch with out_buffer_size"); // TODO: ARG CHECK // 当输入类型为 L4、A4 时,图像块的尺寸 hb 以及在图像中的偏移 x 必须为偶数 - // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? + // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) + // Write back in_bg_buffer, in_fg_buffer + color_space_pixel_format_t in_bg_pixel_format = { + .color_type_id = oper_config->in_bg_color.mode, + }; + uint32_t in_bg_pic_len = oper_config->in_bg_pic_w * oper_config->in_bg_pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; + esp_cache_msync(oper_config->in_bg_buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + color_space_pixel_format_t in_fg_pixel_format = { + .color_type_id = oper_config->in_fg_color.mode, + }; + uint32_t in_fg_pic_len = oper_config->in_fg_pic_w * oper_config->in_fg_pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; + esp_cache_msync(oper_config->in_fg_buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + // Invalidate out_buffer + esp_cache_msync(oper_config->out_buffer, oper_config->out_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); ppa_trans_t *trans_elm = NULL; esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_BLEND, (void *)oper_config, trans_config->mode, &trans_elm); @@ -1315,14 +1326,9 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans assert(dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX); dma2d_channel_handle_t dma2d_rx_chan = dma2d_chans[0].chan; - // Invalidate is performed on the entire picture (the window content is not continuous in the buffer) color_space_pixel_format_t out_pixel_format = { .color_type_id = fill_trans_desc->out_color.mode, }; - uint32_t out_buffer_len = fill_trans_desc->out_pic_w * fill_trans_desc->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - esp_cache_msync(fill_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - out_buffer_len = ALIGN_UP(out_buffer_len, s_platform.buf_alignment_size); - esp_cache_msync(fill_trans_desc->out_buffer, out_buffer_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); // Fill 2D-DMA descriptors blend_engine->dma_rx_desc->vb_size = fill_trans_desc->fill_block_h; @@ -1383,11 +1389,18 @@ esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // out_buffer ptr cannot in flash region ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); - ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0, ESP_ERR_INVALID_ARG, TAG, "out_buffer not aligned to cache line size"); + ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0 && oper_config->out_buffer_size % s_platform.buf_alignment_size == 0, + ESP_ERR_INVALID_ARG, TAG, "out_buffer addr or size not aligned to cache line size"); + color_space_pixel_format_t out_pixel_format = { + .color_type_id = oper_config->out_color.mode, + }; + uint32_t out_pic_len = oper_config->out_pic_w * oper_config->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + ESP_RETURN_ON_FALSE(out_pic_len <= oper_config->out_buffer_size, ESP_ERR_INVALID_ARG, TAG, "out_pic_w/h mismatch with out_buffer_size"); // TODO: ARG CHECK // fill_block_w <= PPA_BLEND_HB_V, fill_block_h <= PPA_BLEND_VB_V - // TODO: Maybe do buffer writeback and invalidation here, instead of in on_picked? + // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) + esp_cache_msync(oper_config->out_buffer, oper_config->out_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); ppa_trans_t *trans_elm = NULL; esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_FILL, (void *)oper_config, trans_config->mode, &trans_elm); diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index 3d1813b7a6..31612960ef 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -332,15 +332,15 @@ static inline void ppa_ll_srm_enable_rx_byte_swap(ppa_dev_t *dev, bool enable) } /** - * @brief Configure PPA SRM alpha value transformation mode + * @brief Configure PPA SRM alpha value update mode * * @param dev Peripheral instance address - * @param mode Alpha value transformation mode, one of the values in ppa_alpha_mode_t + * @param mode Alpha value update mode, one of the values in ppa_alpha_update_mode_t * @param val When PPA_ALPHA_FIX_VALUE mode is selected, val is the alpha value to be replaced with (output_alpha = val) * When PPA_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) * When other modes are selected, this field is not used */ -static inline void ppa_ll_srm_configure_rx_alpha(ppa_dev_t *dev, ppa_alpha_mode_t mode, uint32_t val) +static inline void ppa_ll_srm_configure_rx_alpha(ppa_dev_t *dev, ppa_alpha_update_mode_t mode, uint32_t val) { switch (mode) { case PPA_ALPHA_NO_CHANGE: @@ -362,7 +362,7 @@ static inline void ppa_ll_srm_configure_rx_alpha(ppa_dev_t *dev, ppa_alpha_mode_ dev->sr_fix_alpha.sr_rx_alpha_inv = 1; break; default: - // Unsupported alpha transformation mode + // Unsupported alpha update mode abort(); } } @@ -561,15 +561,15 @@ static inline void ppa_ll_blend_enable_rx_fg_byte_swap(ppa_dev_t *dev, bool enab } /** - * @brief Configure PPA blending input background alpha value transformation mode + * @brief Configure PPA blending input background alpha value update mode * * @param dev Peripheral instance address - * @param mode Alpha value transformation mode, one of the values in ppa_alpha_mode_t + * @param mode Alpha value update mode, one of the values in ppa_alpha_update_mode_t * @param val When PPA_ALPHA_FIX_VALUE mode is selected, val is the alpha value to be replaced with (output_alpha = val) * When PPA_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) * When other modes are selected, this field is not used */ -static inline void ppa_ll_blend_configure_rx_bg_alpha(ppa_dev_t *dev, ppa_alpha_mode_t mode, uint32_t val) +static inline void ppa_ll_blend_configure_rx_bg_alpha(ppa_dev_t *dev, ppa_alpha_update_mode_t mode, uint32_t val) { switch (mode) { case PPA_ALPHA_NO_CHANGE: @@ -591,21 +591,21 @@ static inline void ppa_ll_blend_configure_rx_bg_alpha(ppa_dev_t *dev, ppa_alpha_ dev->blend_fix_alpha.blend0_rx_alpha_inv = 1; break; default: - // Unsupported alpha transformation mode + // Unsupported alpha update mode abort(); } } /** - * @brief Configure PPA blending input foreground alpha value transformation mode + * @brief Configure PPA blending input foreground alpha value update mode * * @param dev Peripheral instance address - * @param mode Alpha value transformation mode, one of the values in ppa_alpha_mode_t + * @param mode Alpha value update mode, one of the values in ppa_alpha_update_mode_t * @param val When PPA_ALPHA_FIX_VALUE mode is selected, val is the alpha value to be replaced with (output_alpha = val) * When PPA_ALPHA_SCALE mode is selected, val/256 is the multiplier to the input alpha value (output_alpha = input_alpha * val / 256) * When other modes are selected, this field is not used */ -static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_alpha_mode_t mode, uint32_t val) +static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_alpha_update_mode_t mode, uint32_t val) { switch (mode) { case PPA_ALPHA_NO_CHANGE: @@ -627,7 +627,7 @@ static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_alpha_ dev->blend_fix_alpha.blend1_rx_alpha_inv = 1; break; default: - // Unsupported alpha transformation mode + // Unsupported alpha update mode abort(); } } diff --git a/components/hal/include/hal/ppa_types.h b/components/hal/include/hal/ppa_types.h index cac7bd0ab4..f08d2b2bb9 100644 --- a/components/hal/include/hal/ppa_types.h +++ b/components/hal/include/hal/ppa_types.h @@ -60,14 +60,14 @@ typedef enum { } ppa_blend_color_mode_t; /** - * @brief Enumeration of PPA alpha compositing mode + * @brief Enumeration of PPA alpha compositing update mode */ typedef enum { PPA_ALPHA_NO_CHANGE = 0, /*!< Do not replace alpha value. If input format does not contain alpha info, alpha value 255 will be used. */ PPA_ALPHA_FIX_VALUE, /*!< Replace the alpha value in received pixel with a new, fixed alpha value */ PPA_ALPHA_SCALE, /*!< Scale the alpha value in received pixel to be a new alpha value */ PPA_ALPHA_INVERT, /*!< Invert the alpha value in received pixel */ -} ppa_alpha_mode_t; +} ppa_alpha_update_mode_t; #ifdef __cplusplus } From 346bc077c508234bcb38571332858fd92ae9592a Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Mon, 8 Apr 2024 16:41:19 +0800 Subject: [PATCH 09/15] feat(ppa): add PPA driver support for ESP32P4 Added doxygen Refactored driver to malloc trans_elm memory when registering the client Cleaned up driver Added API check One client now only responsible for one operation --- components/esp_driver_ppa/CMakeLists.txt | 2 +- .../esp_driver_ppa/include/driver/ppa.h | 309 +++++-- components/esp_driver_ppa/src/ppa.c | 874 ++++++------------ components/esp_driver_ppa/src/ppa_priv.h | 153 +++ components/hal/CMakeLists.txt | 4 + components/hal/esp32p4/include/hal/ppa_ll.h | 2 +- components/hal/include/hal/ppa_hal.h | 44 + components/hal/include/hal/ppa_types.h | 36 +- components/hal/ppa_hal.c | 19 + 9 files changed, 781 insertions(+), 662 deletions(-) create mode 100644 components/esp_driver_ppa/src/ppa_priv.h create mode 100644 components/hal/include/hal/ppa_hal.h create mode 100644 components/hal/ppa_hal.c diff --git a/components/esp_driver_ppa/CMakeLists.txt b/components/esp_driver_ppa/CMakeLists.txt index 5647fac3cb..093679e143 100644 --- a/components/esp_driver_ppa/CMakeLists.txt +++ b/components/esp_driver_ppa/CMakeLists.txt @@ -6,6 +6,6 @@ endif() idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${public_include} - PRIV_REQUIRES esp_mm + PRIV_REQUIRES esp_mm esp_pm # LDFRAGMENTS "linker.lf" ) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 6e8f6b428c..ea2f9ca438 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -14,32 +14,57 @@ extern "C" { #endif +/** + * @brief Enumeration of all PPA available operations + */ +typedef enum { + PPA_OPERATION_SRM, /*!< Do scale-rotate-mirror operation */ + PPA_OPERATION_BLEND, /*!< Do blend operation */ + PPA_OPERATION_FILL, /*!< Do fill operation */ + PPA_OPERATION_NUM, /*!< Quantity of PPA operations */ +} ppa_operation_t; + /** * @brief Type of PPA invoker handle */ typedef struct ppa_invoker_t *ppa_invoker_handle_t; /** - * @brief PPA operation type flags - * - * These flags are supposed to be used to specify the PPA operations that are going to be used by the invoker, so that - * the corresponding engines can be acquired when registering the invoker with `ppa_register_invoker`. + * @brief Type of PPA transaction handle */ -#define PPA_OPERATION_FLAG_SRM (1 << 0) -#define PPA_OPERATION_FLAG_BLEND (1 << 1) -#define PPA_OPERATION_FLAG_FILL (1 << 2) +typedef struct ppa_trans_t *ppa_trans_handle_t; + +/** + * @brief Type of PPA event data + */ +typedef struct { + ; +} ppa_event_data_t; + +/** + * @brief Type of PPA event callback + * + * @param ppa_invoker PPA invoker handle + * @param event_data PPA event data + * @param user_data User registered data from calling `ppa_do_xxx` to perform an operation + * + * @return Whether a task switch is needed after the callback function returns, this is usually due to the callback + * wakes up some high priority task. + */ +typedef bool (*ppa_event_callback_t)(ppa_invoker_handle_t ppa_invoker, ppa_event_data_t *event_data, void *user_data); /** * @brief A collection of configuration items that used for registering a PPA invoker */ typedef struct { - uint32_t operation_flag; /*!< Bitwise OR of `PPA_OPERATION_FLAG_*` flags indicating the required operations for the invoker */ - // done_cb - // user_data + ppa_operation_t oper_type; /*!< The desired PPA operation for the invoker */ + ppa_event_callback_t done_cb; /*!< Callback function to be executed when a PPA transaction finishes */ + uint32_t max_pending_trans_num; /*!< The maximum number of pending transactions for the invoker. + By default, it will be 1, which is sufficient if all transactions are performed with `PPA_TRANS_MODE_BLOCKING` */ } ppa_invoker_config_t; /** - * @brief Register a PPA invoker + * @brief Register a PPA invoker to do a specific PPA operation * * @param[in] config Pointer to a collection of configurations for the invoker * @param[out] ret_invoker Returned invoker handle @@ -57,9 +82,50 @@ esp_err_t ppa_register_invoker(const ppa_invoker_config_t *config, ppa_invoker_h * @param[in] ppa_invoker PPA invoker handle, allocated by `ppa_register_invoker` * @return * - ESP_OK: Unregister the PPA invoker successfully + * - ESP_ERR_INVALID_ARG: Unregister the PPA invoker failed because of invalid argument + * - ESP_ERR_INVALID_STATE: Unregister the PPA invoker failed because there are unfinished transactions */ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker); +/** + * @brief A collection of configuration items for an input picture and the target block inside the picture + */ +typedef struct { + void *buffer; /*!< Pointer to the input picture buffer */ + uint32_t pic_w; /*!< Input picture width (unit: pixel) */ + uint32_t pic_h; /*!< Input picture height (unit: pixel) */ + uint32_t block_w; /*!< Target block width (unit: pixel) */ + uint32_t block_h; /*!< Target block height (unit: pixel) */ + uint32_t block_offset_x; /*!< Target block offset in x direction in the picture (unit: pixel) */ + uint32_t block_offset_y; /*!< Target block offset in y direction in the picture (unit: pixel) */ + union { + ppa_srm_color_mode_t srm_cm; /*!< Color mode of the picture in a PPA SRM operation. Supported color mode in `ppa_srm_color_mode_t` */ + ppa_blend_color_mode_t blend_cm; /*!< Color mode of the picture in a PPA blend operation. Supported color mode in `ppa_blend_color_mode_t` */ + ppa_fill_color_mode_t fill_cm; /*!< Color mode of the picture in a PPA fill operation. Supported color mode in `ppa_fill_color_mode_t` */ + }; + color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */ + color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */ +} ppa_in_pic_blk_config_t; + +/** + * @brief A collection of configuration items for an output picture and the target block inside the picture + */ +typedef struct { + void *buffer; /*!< Pointer to the output picture buffer (requires align to the cache line size) */ + uint32_t buffer_size; /*!< Size of the output picture buffer (requires align to the cache line size) */ + uint32_t pic_w; /*!< Output picture width (unit: pixel) */ + uint32_t pic_h; /*!< Output picture height (unit: pixel) */ + uint32_t block_offset_x; /*!< Target block offset in x direction in the picture (unit: pixel) */ + uint32_t block_offset_y; /*!< Target block offset in y direction in the picture (unit: pixel) */ + union { + ppa_srm_color_mode_t srm_cm; /*!< Color mode of the picture in a PPA SRM operation. Supported color mode in `ppa_srm_color_mode_t` */ + ppa_blend_color_mode_t blend_cm; /*!< Color mode of the picture in a PPA blend operation. Supported color mode in `ppa_blend_color_mode_t` */ + ppa_fill_color_mode_t fill_cm; /*!< Color mode of the picture in a PPA fill operation. Supported color mode in `ppa_fill_color_mode_t` */ + }; + color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */ + color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */ +} ppa_out_pic_blk_config_t; + /** * @brief Modes to perform the PPA operations */ @@ -68,75 +134,148 @@ typedef enum { PPA_TRANS_MODE_NON_BLOCKING, /*!< `ppa_do_xxx` function will return immediately after the PPA operation is pushed to the internal queue */ } ppa_trans_mode_t; +/** + * @brief A collection of configuration items to do a PPA SRM operation transaction + */ +typedef struct { + ppa_in_pic_blk_config_t in; /*!< Information of the input picture and the target block */ + ppa_out_pic_blk_config_t out; /*!< Information of the output picture and the target block */ + + // scale-rotate-mirror manipulation + ppa_srm_rotation_angle_t rotation_angle; /*!< Rotation (counter-clockwise) to the target block, select from `ppa_srm_rotation_angle_t` */ + float scale_x; /*!< Scaling factor to the target block in the x direction */ + float scale_y; /*!< Scaling factor to the target block in the y direction */ + bool mirror_x; /*!< Whether to mirror the target block in the x direction */ + bool mirror_y; /*!< Whether to mirror the target block in the y direction */ + + // input data manipulation + bool rgb_swap; /*!< Whether to swap the input data in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) */ + bool byte_swap; /*!< Whether to swap the input data in byte. Only available feature if input picture color mode is ARGB8888 or RGB565 */ + ppa_alpha_update_mode_t alpha_update_mode; /*!< Select whether the alpha channel of the input picture needs update */ + uint32_t alpha_value; /*!< Range: 0 ~ 255 + When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) + When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) + When other alpha modes are selected, this field is not used */ + + ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ + // uint32_t timeout; + void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ +} ppa_srm_oper_trans_config_t; + /** * @brief A collection of configuration items to perform a PPA transaction */ typedef struct { ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ + void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ } ppa_trans_config_t; -#define PPA_SRM_OPERATION_CONFIG struct { \ - void *in_buffer; /*!< TODO: could be a buffer list, link descriptors together, process a batch - uint32_t batch_num; However, is it necessary? psram can not store too many pictures */ \ - uint32_t in_pic_w; \ - uint32_t in_pic_h; \ - uint32_t in_block_w; \ - uint32_t in_block_h; \ - uint32_t in_block_offset_x; \ - uint32_t in_block_offset_y; \ - \ - void *out_buffer; \ - uint32_t out_buffer_size; \ - uint32_t out_pic_w; \ - uint32_t out_pic_h; \ - uint32_t out_block_offset_x; \ - uint32_t out_block_offset_y; \ - \ - ppa_srm_rotation_angle_t rotation_angle; \ - float scale_x; \ - float scale_y; \ - bool mirror_x; \ - bool mirror_y; \ - \ - struct { \ - ppa_srm_color_mode_t mode; \ - color_range_t yuv_range; \ - color_conv_std_rgb_yuv_t yuv_std; \ - bool rgb_swap; \ - bool byte_swap; \ - ppa_alpha_update_mode_t alpha_update_mode; \ - uint32_t alpha_value; /*!< When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) - When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) - When other alpha modes are selected, this field is not used */ \ - } in_color; \ - \ - struct { \ - ppa_srm_color_mode_t mode; \ - color_range_t yuv_range; \ - color_conv_std_rgb_yuv_t yuv_std; \ - } out_color; \ -} - /** * @brief A collection of configuration items to perform a PPA SRM operation */ -typedef PPA_SRM_OPERATION_CONFIG ppa_srm_operation_config_t; +typedef struct { + void *in_buffer; /*!< TODO: could be a buffer list, link descriptors together, process a batch + uint32_t batch_num; However, is it necessary? psram can not store too many pictures */ + uint32_t in_pic_w; + uint32_t in_pic_h; + uint32_t in_block_w; + uint32_t in_block_h; + uint32_t in_block_offset_x; + uint32_t in_block_offset_y; + + void *out_buffer; + uint32_t out_buffer_size; + uint32_t out_pic_w; + uint32_t out_pic_h; + uint32_t out_block_offset_x; + uint32_t out_block_offset_y; + + ppa_srm_rotation_angle_t rotation_angle; + float scale_x; + float scale_y; + bool mirror_x; + bool mirror_y; + + struct { + ppa_srm_color_mode_t mode; + color_range_t yuv_range; + color_conv_std_rgb_yuv_t yuv_std; + bool rgb_swap; + bool byte_swap; + ppa_alpha_update_mode_t alpha_update_mode; + uint32_t alpha_value; /*!< When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) + When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) + When other alpha modes are selected, this field is not used */ + } in_color; + + struct { + ppa_srm_color_mode_t mode; + color_range_t yuv_range; + color_conv_std_rgb_yuv_t yuv_std; + } out_color; +} ppa_srm_operation_config_t; /** * @brief Perform a scaling-rotating-mirroring (SRM) operation to a picture * - * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA SRM engine + * @param[in] ppa_invoker PPA invoker handle that has been registered to do SRM operations * @param[in] oper_config Pointer to a collection of configurations for the SRM operation, ppa_srm_operation_config_t * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t * * @return - * - ESP_OK: - * - ESP_ERR_INVALID_ARG: - * - ESP_ERR_NO_MEM: - * - ESP_FAIL: + * - ESP_OK: Perform a SRM operation successfully + * - ESP_ERR_INVALID_ARG: Perform a SRM operation failed because of invalid argument + * - ESP_ERR_NO_MEM: Perform a SRM operation failed because out of memory + * - ESP_FAIL: Perform a SRM operation failed because the invoker cannot accept transaction now */ esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa_srm_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); +/** + * @brief A collection of configuration items to do a PPA blend operation transaction + */ +typedef struct { + ppa_in_pic_blk_config_t in_bg; /*!< Information of the input background picture and the target block */ + ppa_in_pic_blk_config_t in_fg; /*!< Information of the input foreground picture and the target block */ + ppa_out_pic_blk_config_t out; /*!< Information of the output picture and the target block */ + + // input data manipulation + bool bg_rgb_swap; /*!< Whether to swap the background input data in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) */ + bool bg_byte_swap; /*!< Whether to swap the background input data in byte. Only available feature if input BG picture color mode is ARGB8888 or RGB565 */ + ppa_alpha_update_mode_t bg_alpha_update_mode; /*!< Select whether the alpha channel of the input background picture needs update */ + uint32_t bg_alpha_value; /*!< Range: 0 ~ 255 + When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) + When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) + When other alpha modes are selected, this field is not used */ + bool fg_rgb_swap; /*!< Whether to swap the foreground input data in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) */ + bool fg_byte_swap; /*!< Whether to swap the foreground input data in byte. Only available feature if input FG picture color mode is ARGB8888 or RGB565 */ + ppa_alpha_update_mode_t fg_alpha_update_mode; /*!< Select whether the alpha channel of the input foreground picture needs update */ + uint32_t fg_alpha_value; /*!< Range: 0 ~ 255 + When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) + When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) + When other alpha modes are selected, this field is not used */ + uint32_t fg_fix_rgb_val; /*!< When in_fg.blend_cm is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + + // color-keying + // A pixel, where its background element and foreground element are both out of their color-keying ranges, will follow Alpha Blending + bool bg_ck_en; /*!< Whether to enable color keying for background + If not enabled, all background pixels are considered as out of the color-keying range */ + uint32_t bg_ck_rgb_low_thres; /*!< The lower threshold of the color-keying range for the background, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + uint32_t bg_ck_rgb_high_thres; /*!< The higher threshold of the color-keying range for the background, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + bool fg_ck_en; /*!< Whether to enable color keying for foreground + If not enabled, all foreground pixels are considered as out of the color-keying range */ + uint32_t fg_ck_rgb_low_thres; /*!< The lower threshold of the color-keying range for the foreground, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + uint32_t fg_ck_rgb_high_thres; /*!< The higher threshold of the color-keying range for the foreground, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + uint32_t ck_rgb_default_val; /*!< The color to overwrite when a pixel, where its background element and foreground element are both within their color-keying ranges, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + bool ck_reverse_bg2fg; /*!< If this bit is set, in color-keying, for the pixel, where its background element is in the color range, but its foreground element is not in the color range, it will output the foreground element instead of the background element */ + + ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ + // uint32_t timeout; + void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ +} ppa_blend_oper_trans_config_t; + +/** + * @brief A collection of configuration items to perform a PPA blend operation + */ typedef struct { void *in_bg_buffer; uint32_t in_bg_pic_w; @@ -193,18 +332,36 @@ typedef struct { /** * @brief Perform a blending operation to a picture * - * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA Blend engine - * @param[in] oper_config Pointer to a collection of configurations for the blending operation, ppa_blend_operation_config_t + * @param[in] ppa_invoker PPA invoker handle that has been registered to do blend operations + * @param[in] oper_config Pointer to a collection of configurations for the blend operation, ppa_blend_operation_config_t * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t * * @return - * - ESP_OK: - * - ESP_ERR_INVALID_ARG: - * - ESP_ERR_NO_MEM: - * - ESP_FAIL: + * - ESP_OK: Perform a blend operation successfully + * - ESP_ERR_INVALID_ARG: Perform a blend operation failed because of invalid argument + * - ESP_ERR_NO_MEM: Perform a blend operation failed because out of memory + * - ESP_FAIL: Perform a blend operation failed because the invoker cannot accept transaction now */ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); +/** + * @brief A collection of configuration items to do a PPA fill operation transaction + */ +typedef struct { + ppa_out_pic_blk_config_t out; /*!< Information of the output picture and the target block */ + + uint32_t fill_block_w; /*!< The width of the block to be filled (unit: pixel) */ + uint32_t fill_block_h; /*!< The height of the block to be filled (unit: pixel) */ + uint32_t fill_argb_color; /*!< The color to be filled, in ARGB8888 format ((A[31:24], R[23:16], G[15: 8], B[7:0])) */ + + ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ + // uint32_t timeout; + void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ +} ppa_fill_oper_trans_config_t; + +/** + * @brief A collection of configuration items to perform a PPA fill operation + */ typedef struct { uint32_t fill_block_w; uint32_t fill_block_h; @@ -225,28 +382,18 @@ typedef struct { /** * @brief Perform a filling operation to a picture * - * @param[in] ppa_invoker PPA invoker handle that has acquired the PPA Blend engine - * @param[in] oper_config Pointer to a collection of configurations for the filling operation, ppa_fill_operation_config_t + * @param[in] ppa_invoker PPA invoker handle that has been registered to do fill operations + * @param[in] oper_config Pointer to a collection of configurations for the fill operation, ppa_fill_operation_config_t * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t * * @return - * - ESP_OK: - * - ESP_ERR_INVALID_ARG: - * - ESP_ERR_NO_MEM: - * - ESP_FAIL: + * - ESP_OK: Perform a fill operation successfully + * - ESP_ERR_INVALID_ARG: Perform a fill operation failed because of invalid argument + * - ESP_ERR_NO_MEM: Perform a fill operation failed because out of memory + * - ESP_FAIL: Perform a fill operation failed because the invoker cannot accept transaction now */ esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); -// argb color conversion (bypass blend) - -// SRM and Blending are independent, can work at the same time -// Fill is in blend, so fill and blend cannot work at the same time - -// Consider blocking and non-blocking options -// Non-blocking may require notification of process done event - -// cache writeback/invalidate - #ifdef __cplusplus } #endif diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c index ef4103f8bf..6cf0e524c1 100644 --- a/components/esp_driver_ppa/src/ppa.c +++ b/components/esp_driver_ppa/src/ppa.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include "sdkconfig.h" #include "esp_check.h" #include "esp_log.h" #include "freertos/portmacro.h" @@ -15,15 +17,18 @@ #include "freertos/task.h" #include "freertos/semphr.h" #include "freertos/idf_additions.h" +#include "freertos/queue.h" #include "esp_heap_caps.h" #include "esp_cache.h" #include "esp_private/esp_cache_private.h" #include "hal/cache_hal.h" #include "hal/cache_ll.h" #include "driver/ppa.h" +#include "ppa_priv.h" #include "esp_private/dma2d.h" #include "hal/dma2d_ll.h" #include "soc/dma2d_channel.h" +#include "hal/ppa_hal.h" #include "hal/ppa_ll.h" #include "hal/ppa_types.h" #include "hal/color_types.h" @@ -35,112 +40,20 @@ static const char *TAG = "ppa"; -typedef struct ppa_dev_t *ppa_soc_handle_t; // PPA SOC layer handle - -typedef struct { - ppa_soc_handle_t dev; -} ppa_hal_context_t; - -void ppa_hal_init(ppa_hal_context_t *hal) -{ - hal->dev = PPA_LL_GET_HW; -} - -void ppa_hal_deinit(ppa_hal_context_t *hal) -{ - hal->dev = NULL; -} - -// PPA module contains SRM engine and Blending engine - -typedef struct ppa_engine_t ppa_engine_t; - -typedef struct ppa_invoker_t ppa_invoker_t; - -typedef struct { - PPA_SRM_OPERATION_CONFIG; - uint32_t scale_x_int; - uint32_t scale_x_frag; - uint32_t scale_y_int; - uint32_t scale_y_frag; -} ppa_srm_oper_t; - -typedef ppa_blend_operation_config_t ppa_blend_oper_t; - -typedef ppa_fill_operation_config_t ppa_fill_oper_t; - -typedef struct ppa_trans_s { - STAILQ_ENTRY(ppa_trans_s) entry; // link entry - dma2d_trans_config_t *trans_desc; - dma2d_trans_t *dma_trans_placeholder; - SemaphoreHandle_t sem; - ppa_invoker_t *invoker; -} ppa_trans_t; - -typedef struct { - union { - ppa_srm_oper_t *srm_desc; - ppa_blend_oper_t *blend_desc; - ppa_fill_oper_t *fill_desc; - void *op_desc; - }; - ppa_engine_t *ppa_engine; - ppa_trans_t *trans_elm; - dma2d_trigger_peripheral_t trigger_periph; -} ppa_dma2d_trans_on_picked_config_t; - -struct ppa_engine_t { - ppa_engine_type_t type; - portMUX_TYPE spinlock; - SemaphoreHandle_t sem; - // bool in_accepting_trans_state; - STAILQ_HEAD(trans, ppa_trans_s) trans_stailq; // link head of pending transactions for the PPA engine - // callback func? Here or in the struct above? - // dma2d_rx_event_callbacks_t event_cbs; -}; - -typedef struct ppa_srm_engine_t { - ppa_engine_t base; - dma2d_descriptor_t *dma_tx_desc; - dma2d_descriptor_t *dma_rx_desc; -} ppa_srm_engine_t; - -typedef struct ppa_blend_engine_t { - ppa_engine_t base; - dma2d_descriptor_t *dma_tx_bg_desc; - dma2d_descriptor_t *dma_tx_fg_desc; - dma2d_descriptor_t *dma_rx_desc; -} ppa_blend_engine_t; - -struct ppa_invoker_t { - ppa_engine_t *srm_engine; - ppa_engine_t *blending_engine; - uint32_t srm_trans_cnt; - uint32_t blending_trans_cnt; - portMUX_TYPE spinlock; - bool in_accepting_trans_state; - // done_cb - // user_data -}; - -typedef enum { - PPA_OPERATION_SRM, - PPA_OPERATION_BLEND, - PPA_OPERATION_FILL, - PPA_OPERATION_NUM, -} ppa_operation_t; - typedef struct ppa_platform_t { - _lock_t mutex; // platform level mutex lock to protect the ppa_module_acquire/ppa_module_release process - portMUX_TYPE spinlock; // platform level spinlock - ppa_hal_context_t hal; - dma2d_pool_handle_t dma2d_pool_handle; - ppa_srm_engine_t *srm; - ppa_blend_engine_t *blending; - uint32_t srm_engine_ref_count; - uint32_t blend_engine_ref_count; - size_t buf_alignment_size; - uint32_t dma_desc_mem_size; + _lock_t mutex; // Platform level mutex lock to protect the ppa_engine_acquire/ppa_engine_release process + portMUX_TYPE spinlock; // Platform level spinlock + ppa_hal_context_t hal; // PPA HAL context + dma2d_pool_handle_t dma2d_pool_handle; // Pointer to the acquired 2D-DMA pool + ppa_srm_engine_t *srm; // Pointer to the PPA SRM engine + ppa_blend_engine_t *blending; // Pointer to the PPA blending engine + uint32_t srm_engine_ref_count; // Reference count used to protect PPA SRM engine acquire and release + uint32_t blend_engine_ref_count; // Reference count used to protect PPA blending engine acquire and release + size_t buf_alignment_size; // Alignment requirement for the outgoing buffer addr and size to satisfy cache line size + uint32_t dma_desc_mem_size; // Alignment requirement for the 2D-DMA descriptor to satisfy cache line size +#if CONFIG_PM_ENABLE + esp_pm_lock_handle_t pm_lock; // Power management lock +#endif } ppa_platform_t; // PPA driver platform @@ -148,14 +61,10 @@ static ppa_platform_t s_platform = { .spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED, }; -#define PPA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) // TODO... - -typedef struct { - ppa_engine_type_t engine; -} ppa_engine_config_t; - static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine); static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine); +static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t trans_elm_num, ppa_operation_t oper_type); +static void ppa_free_transaction(ppa_trans_t *trans_elm); static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); @@ -166,19 +75,6 @@ const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATI ppa_fill_transaction_on_picked, }; -// extern uint32_t dma2d_tx_channel_reserved_mask[SOC_DMA2D_GROUPS]; -// extern uint32_t dma2d_rx_channel_reserved_mask[SOC_DMA2D_GROUPS]; -// static uint32_t ppa_specified_tx_channel_mask = 0; -// static uint32_t ppa_specified_rx_channel_mask = 0; -// static __attribute__((constructor)) void ppa_reserve_dma2d_channels(void) -// { -// // ppa_specified_tx_channel_mask = 1 << 0; -// // ppa_specified_rx_channel_mask = 1 << 0; -// dma2d_tx_channel_reserved_mask[0] |= ppa_specified_tx_channel_mask; -// dma2d_rx_channel_reserved_mask[0] |= ppa_specified_rx_channel_mask; -// } - -// TODO: acquire pm_lock per transaction? static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine) { esp_err_t ret = ESP_OK; @@ -211,9 +107,7 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin srm_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; srm_engine->base.sem = srm_sem; xSemaphoreGive(srm_engine->base.sem); - // srm_engine->base.in_accepting_trans_state = true; STAILQ_INIT(&srm_engine->base.trans_stailq); - // srm_engine->base.event_cbs s_platform.srm = srm_engine; s_platform.srm_engine_ref_count++; *ret_engine = &srm_engine->base; @@ -230,6 +124,13 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin free(srm_tx_dma_desc); free(srm_rx_dma_desc); } + +#if CONFIG_PM_ENABLE + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_srm", &srm_engine->base.pm_lock); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "create pm lock failed"); + } +#endif } else { // SRM engine already registered s_platform.srm_engine_ref_count++; @@ -250,9 +151,7 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin blending_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; blending_engine->base.sem = blending_sem; xSemaphoreGive(blending_engine->base.sem); - // blending_engine->base.in_accepting_trans_state = true; STAILQ_INIT(&blending_engine->base.trans_stailq); - // blending_engine->base.event_cbs s_platform.blending = blending_engine; s_platform.blend_engine_ref_count++; *ret_engine = &blending_engine->base; @@ -267,6 +166,13 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin free(blending_tx_fg_dma_desc); free(blending_rx_dma_desc); } + +#if CONFIG_PM_ENABLE + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_blending", &blending_engine->base.pm_lock); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "create pm lock failed"); + } +#endif } else { // Blending engine already registered s_platform.blend_engine_ref_count++; @@ -293,9 +199,21 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin ret = dma2d_acquire_pool(&dma2d_config, &s_platform.dma2d_pool_handle); if (ret != ESP_OK) { ESP_LOGE(TAG, "install 2D-DMA failed"); + goto wrap_up; } + +#if CONFIG_PM_ENABLE + assert(!s_platform.pm_lock); + // Create and acquire the PM lock + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa", &s_platform.pm_lock); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "create pm lock failed"); + goto wrap_up; + } +#endif } } +wrap_up: _lock_release(&s_platform.mutex); if (ret != ESP_OK && *ret_engine != NULL) { @@ -315,30 +233,23 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) ppa_srm_engine_t *srm_engine = __containerof(ppa_engine, ppa_srm_engine_t, base); s_platform.srm_engine_ref_count--; if (s_platform.srm_engine_ref_count == 0) { - // // Stop accepting new transactions to SRM engine - // portENTER_CRITICAL(&srm_engine->base.spinlock); - // srm_engine->base.in_accepting_trans_state = false; - // portEXIT_CRITICAL(&srm_engine->base.spinlock); - // // Wait until all transactions get processed - // while (!STAILQ_EMPTY(&srm_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide assert(STAILQ_EMPTY(&srm_engine->base.trans_stailq)); // Now, time to free s_platform.srm = NULL; free(srm_engine->dma_tx_desc); free(srm_engine->dma_rx_desc); vSemaphoreDeleteWithCaps(srm_engine->base.sem); +#if CONFIG_PM_ENABLE + if (srm_engine->base.pm_lock) { + assert(esp_pm_lock_delete(srm_engine->base.pm_lock) == ESP_OK); + } +#endif free(srm_engine); } } else if (ppa_engine->type == PPA_ENGINE_TYPE_BLEND) { ppa_blend_engine_t *blending_engine = __containerof(ppa_engine, ppa_blend_engine_t, base); s_platform.blend_engine_ref_count--; if (s_platform.blend_engine_ref_count == 0) { - // // Stop accepting new transactions to blending engine - // portENTER_CRITICAL(&blending_engine->base.spinlock); - // blending_engine->base.in_accepting_trans_state = false; - // portEXIT_CRITICAL(&blending_engine->base.spinlock); - // // Wait until all transactions get processed - // while (!STAILQ_EMPTY(&blending_engine->base.trans_stailq)); // TODO: Think twice, looks like I am not able to use engine semaphore to decide assert(STAILQ_EMPTY(&blending_engine->base.trans_stailq)); // Now, time to free s_platform.blending = NULL; @@ -346,6 +257,11 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) free(blending_engine->dma_tx_fg_desc); free(blending_engine->dma_rx_desc); vSemaphoreDeleteWithCaps(blending_engine->base.sem); +#if CONFIG_PM_ENABLE + if (blending_engine->base.pm_lock) { + assert(esp_pm_lock_delete(blending_engine->base.pm_lock) == ESP_OK); + } +#endif free(blending_engine); } } @@ -353,6 +269,12 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) if (!s_platform.srm && !s_platform.blending) { assert(s_platform.srm_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0); +#if CONFIG_PM_ENABLE + if (s_platform.pm_lock) { + assert(esp_pm_lock_delete(s_platform.pm_lock) == ESP_OK); + } +#endif + if (s_platform.dma2d_pool_handle) { dma2d_release_pool(s_platform.dma2d_pool_handle); // TODO: check return value. If not ESP_OK, then must be error on other 2D-DMA clients :( Give a warning log? s_platform.dma2d_pool_handle = NULL; @@ -373,23 +295,30 @@ esp_err_t ppa_register_invoker(const ppa_invoker_config_t *config, ppa_invoker_h { esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(config && ret_invoker, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(config->oper_type < PPA_OPERATION_NUM, ESP_ERR_INVALID_ARG, TAG, "unknown operation"); ppa_invoker_t *invoker = (ppa_invoker_t *)heap_caps_calloc(1, sizeof(ppa_invoker_t), PPA_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(invoker, ESP_ERR_NO_MEM, TAG, "no mem to register invoker"); + uint32_t ring_buf_size = MAX(1, config->max_pending_trans_num); + invoker->trans_elm_ptr_queue = xQueueCreateWithCaps(ring_buf_size, sizeof(uint32_t), PPA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(invoker->trans_elm_ptr_queue && ppa_malloc_transaction(invoker->trans_elm_ptr_queue, ring_buf_size, config->oper_type), + ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); + + invoker->oper_type = config->oper_type; + invoker->done_cb = config->done_cb; invoker->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; invoker->in_accepting_trans_state = true; - if (config->operation_flag & PPA_OPERATION_FLAG_SRM) { + if (config->oper_type == PPA_OPERATION_SRM) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_SRM, }; - ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->srm_engine), err, TAG, "unable to acquire SRM engine"); - } - if (config->operation_flag & PPA_OPERATION_FLAG_BLEND || config->operation_flag & PPA_OPERATION_FLAG_FILL) { + ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->engine), err, TAG, "unable to acquire SRM engine"); + } else if (config->oper_type == PPA_OPERATION_BLEND || config->oper_type == PPA_OPERATION_FILL) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_BLEND, }; - ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->blending_engine), err, TAG, "unable to acquire Blending engine"); + ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->engine), err, TAG, "unable to acquire Blending engine"); } *ret_invoker = invoker; @@ -406,306 +335,28 @@ esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) bool do_unregister = false; portENTER_CRITICAL(&ppa_invoker->spinlock); - if (ppa_invoker->srm_trans_cnt == 0 && ppa_invoker->blending_trans_cnt == 0) { + if (ppa_invoker->trans_cnt == 0) { ppa_invoker->in_accepting_trans_state = false; do_unregister = true; } portEXIT_CRITICAL(&ppa_invoker->spinlock); ESP_RETURN_ON_FALSE(do_unregister, ESP_ERR_INVALID_STATE, TAG, "invoker still has unprocessed trans"); - if (ppa_invoker->srm_engine) { - ppa_engine_release(ppa_invoker->srm_engine); + if (ppa_invoker->engine) { + ppa_engine_release(ppa_invoker->engine); } - if (ppa_invoker->blending_engine) { - ppa_engine_release(ppa_invoker->blending_engine); + + if (ppa_invoker->trans_elm_ptr_queue) { + ppa_trans_t *trans_elm = NULL; + while (xQueueReceive(ppa_invoker->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + ppa_free_transaction(trans_elm); + } + vQueueDeleteWithCaps(ppa_invoker->trans_elm_ptr_queue); } free(ppa_invoker); return ESP_OK; } -// // TODO: pm lock? -// esp_err_t ppa_module_acquire(const ppa_group_alloc_config_t *config, ppa_group_handle_t *ret_group) -// { -// esp_err_t ret = ESP_OK; -// ESP_RETURN_ON_FALSE(config && ret_group, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); -// ESP_RETURN_ON_FALSE(config->group_id < PPA_LL_GROUPS, ESP_ERR_INVALID_ARG, TAG, "invalid group_id"); - -// int group_id = config->group_id; - -// // _lock_acquire(&s_platform.mutex); -// // if (!s_platform.group[group_id]) { -// // ppa_group_t *pre_alloc_group = heap_caps_calloc(1, sizeof(ppa_group_t), PPA_MEM_ALLOC_CAPS); -// // if (pre_alloc_group) { -// // ppa_hal_init(&pre_alloc_group->hal, group_id); // initialize HAL context -// // pre_alloc_group->group_id = group_id; -// // pre_alloc_group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; -// // s_platform.group[group_id] = pre_alloc_group; // register to platform -// // // Enable the bus clock to access PPA registers -// // PERIPH_RCC_ATOMIC() { -// // ppa_ll_enable_bus_clock(group_id, true); -// // ppa_ll_reset_register(group_id); -// // } - -// // // Get 2D-DMA pool handle -// // dma2d_pool_config_t dma2d_config = { -// // .pool_id = 0, -// // }; -// // ret = dma2d_acquire_pool(&dma2d_config, &s_platform.group[group_id]->dma2d_pool_handle); -// // if (ret != ESP_OK) { -// // ESP_LOGE(TAG, "install 2D-DMA failed"); -// // // free(pre_alloc_group); -// // // s_platform.group[group_id] = NULL; -// // } -// // } else { -// // ret = ESP_ERR_NO_MEM; -// // } -// // } - -// // // Register PPA SRM engine -// // if (ret == ESP_OK && config->srm_engine_en && !s_platform.group[group_id]->srm) { -// // ppa_srm_engine_t *srm_engine = heap_caps_calloc(1, sizeof(ppa_srm_engine_t), PPA_MEM_ALLOC_CAPS); -// // SemaphoreHandle_t srm_sem = xSemaphoreCreateBinary(); -// // dma2d_descriptor_t *srm_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API -// // dma2d_descriptor_t *srm_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); -// // if (srm_engine && srm_sem && srm_tx_dma_desc && srm_rx_dma_desc) { -// // srm_engine->dma_tx_desc = srm_tx_dma_desc; -// // srm_engine->dma_rx_desc = srm_rx_dma_desc; -// // srm_engine->base.group = s_platform.group[group_id]; -// // srm_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; -// // srm_engine->base.sem = srm_sem; -// // xSemaphoreGive(srm_engine->base.sem); -// // srm_engine->base.in_accepting_trans_state = true; -// // STAILQ_INIT(&srm_engine->base.trans_stailq); -// // // srm_engine->base.event_cbs -// // s_platform.group[group_id]->srm = srm_engine; -// // } else { -// // ret = ESP_ERR_NO_MEM; -// // ESP_LOGE(TAG, "no mem to register PPA SRM engine"); -// // free(srm_engine); -// // if (srm_sem) vSemaphoreDelete(srm_sem); -// // free(srm_tx_dma_desc); -// // free(srm_rx_dma_desc); -// // } -// // } - -// // // Register PPA Blending engine -// // if (ret == ESP_OK && config->blending_engine_en && !s_platform.group[group_id]->blending) { -// // ppa_blending_engine_t *blending_engine = heap_caps_calloc(1, sizeof(ppa_blending_engine_t), PPA_MEM_ALLOC_CAPS); -// // SemaphoreHandle_t blending_sem = xSemaphoreCreateBinary(); -// // dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API -// // dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); -// // dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); -// // if (blending_engine && blending_sem && blending_tx_bg_dma_desc && blending_tx_fg_dma_desc && blending_rx_dma_desc) { -// // blending_engine->dma_tx_bg_desc = blending_tx_bg_dma_desc; -// // blending_engine->dma_tx_fg_desc = blending_tx_fg_dma_desc; -// // blending_engine->dma_rx_desc = blending_rx_dma_desc; -// // blending_engine->base.group = s_platform.group[group_id]; -// // blending_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; -// // blending_engine->base.sem = blending_sem; -// // xSemaphoreGive(blending_engine->base.sem); -// // blending_engine->base.in_accepting_trans_state = true; -// // STAILQ_INIT(&blending_engine->base.trans_stailq); -// // // blending_engine->base.event_cbs -// // s_platform.group[group_id]->blending = blending_engine; -// // } else { -// // ret = ESP_ERR_NO_MEM; -// // ESP_LOGE(TAG, "no mem to register PPA Blending engine"); -// // free(blending_engine); -// // if (blending_sem) vSemaphoreDelete(blending_sem); -// // free(blending_tx_bg_dma_desc); -// // free(blending_tx_fg_dma_desc); -// // free(blending_rx_dma_desc); -// // } -// // } -// // _lock_release(&s_platform.mutex); - -// // ppa_module_release - -// bool new_group = false; -// bool new_srm_engine = false; -// bool new_blending_engine = false; -// ppa_group_t *pre_alloc_group = heap_caps_calloc(1, sizeof(ppa_group_t), PPA_MEM_ALLOC_CAPS); -// ppa_srm_engine_t *srm_engine = NULL; -// ppa_blend_engine_t *blending_engine = NULL; -// SemaphoreHandle_t srm_sem = NULL, blending_sem = NULL; - -// // portENTER_CRITICAL(&s_platform.spinlock); -// if (!s_platform.group[group_id]) { -// if (pre_alloc_group) { -// new_group = true; -// ppa_hal_init(&pre_alloc_group->hal, group_id); // initialize HAL context -// pre_alloc_group->group_id = group_id; -// pre_alloc_group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; -// s_platform.group[group_id] = pre_alloc_group; // register to platform -// // Enable the bus clock to access PPA registers -// PERIPH_RCC_ATOMIC() { -// ppa_ll_enable_bus_clock(group_id, true); -// ppa_ll_reset_register(group_id); -// } -// } else { -// ret = ESP_ERR_NO_MEM; -// } -// } -// // portEXIT_CRITICAL(&s_platform.spinlock); - -// if (new_group) { -// // Get 2D-DMA pool handle -// dma2d_pool_config_t dma2d_config = { -// .pool_id = 0, -// }; -// ret = dma2d_acquire_pool(&dma2d_config, &s_platform.group[group_id]->dma2d_pool_handle); -// if (ret != ESP_OK) { -// ESP_LOGE(TAG, "install 2D-DMA failed"); -// goto err; -// } -// } - -// if (ret == ESP_OK && config->srm_engine_en) { -// srm_engine = heap_caps_calloc(1, sizeof(ppa_srm_engine_t), PPA_MEM_ALLOC_CAPS); -// srm_sem = xSemaphoreCreateBinary(); -// dma2d_descriptor_t *srm_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API -// dma2d_descriptor_t *srm_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); -// // Register PPA SRM engine -// portENTER_CRITICAL(&s_platform.group[group_id]->spinlock); -// if (!s_platform.group[group_id]->srm) { -// if (srm_engine && srm_sem && srm_tx_dma_desc && srm_rx_dma_desc) { -// new_srm_engine = true; -// srm_engine->dma_tx_desc = srm_tx_dma_desc; -// srm_engine->dma_rx_desc = srm_rx_dma_desc; -// srm_engine->base.group = s_platform.group[group_id]; -// srm_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; -// srm_engine->base.sem = srm_sem; -// xSemaphoreGive(srm_engine->base.sem); -// srm_engine->base.in_accepting_trans_state = true; -// STAILQ_INIT(&srm_engine->base.trans_stailq); -// // srm_engine->base.event_cbs -// s_platform.group[group_id]->srm = srm_engine; -// } else { -// ret = ESP_ERR_NO_MEM; -// } -// } -// portEXIT_CRITICAL(&s_platform.group[group_id]->spinlock); -// if (ret == ESP_ERR_NO_MEM) { -// ESP_LOGE(TAG, "no mem to register PPA SRM engine"); -// } -// } - -// if (ret == ESP_OK && config->blending_engine_en) { -// blending_engine = heap_caps_calloc(1, sizeof(ppa_blend_engine_t), PPA_MEM_ALLOC_CAPS); -// blending_sem = xSemaphoreCreateBinary(); -// dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); // TODO: get cache line size by API -// dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); -// dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, 1, 64, PPA_MEM_ALLOC_CAPS); -// // Register PPA Blending engine -// portENTER_CRITICAL(&s_platform.group[group_id]->spinlock); -// if (!s_platform.group[group_id]->blending) { -// if (blending_engine && blending_sem && blending_tx_bg_dma_desc && blending_tx_fg_dma_desc && blending_rx_dma_desc) { -// new_blending_engine = true; -// blending_engine->dma_tx_bg_desc = blending_tx_bg_dma_desc; -// blending_engine->dma_tx_fg_desc = blending_tx_fg_dma_desc; -// blending_engine->dma_rx_desc = blending_rx_dma_desc; -// blending_engine->base.group = s_platform.group[group_id]; -// blending_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; -// blending_engine->base.sem = blending_sem; -// xSemaphoreGive(blending_engine->base.sem); -// blending_engine->base.in_accepting_trans_state = true; -// STAILQ_INIT(&blending_engine->base.trans_stailq); -// // blending_engine->base.event_cbs -// s_platform.group[group_id]->blending = blending_engine; -// } else { -// ret = ESP_ERR_NO_MEM; -// } -// } -// portEXIT_CRITICAL(&s_platform.group[group_id]->spinlock); -// if (ret == ESP_ERR_NO_MEM) { -// ESP_LOGE(TAG, "no mem to register PPA Blending engine"); -// } -// } - -// if (!new_srm_engine) { -// free(srm_engine); -// if (srm_sem) vSemaphoreDelete(srm_sem); -// // TODO: free desc -// } -// if (!new_blending_engine) { -// free(blending_engine); -// if (blending_sem) vSemaphoreDelete(blending_sem); -// // TODO: free desc -// } -// err: -// if (ret != ESP_OK) { -// if (new_group) { -// ppa_module_release(s_platform.group[group_id]); -// } -// } -// if (!new_group) { -// free(pre_alloc_group); -// } -// *ret_group = s_platform.group[group_id]; -// return ret; -// } - -// esp_err_t ppa_module_release(ppa_group_handle_t ppa_group) -// { -// esp_err_t ret = ESP_OK; -// ESP_RETURN_ON_FALSE(ppa_group, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - -// bool do_deinitialize = false; -// int group_id = ppa_group->group_id; -// ppa_srm_engine_t *srm_engine = ppa_group->srm; -// ppa_blend_engine_t *blending_engine = ppa_group->blending; -// bool srm_no_waiting_trans = true; -// bool blending_no_waiting_trans = true; - -// // portENTER_CRITICAL(&s_platform.spinlock); -// portENTER_CRITICAL(&ppa_group->spinlock); -// if (srm_engine) { -// srm_engine->base.in_accepting_trans_state = false; -// portENTER_CRITICAL(&srm_engine->base.spinlock); -// srm_no_waiting_trans = STAILQ_EMPTY(&srm_engine->base.trans_stailq); -// portEXIT_CRITICAL(&srm_engine->base.spinlock); -// } -// if (blending_engine) { -// blending_engine->base.in_accepting_trans_state = false; -// portENTER_CRITICAL(&blending_engine->base.spinlock); -// blending_no_waiting_trans = STAILQ_EMPTY(&blending_engine->base.trans_stailq); -// portEXIT_CRITICAL(&blending_engine->base.spinlock); -// } -// portEXIT_CRITICAL(&ppa_group->spinlock); -// if (srm_no_waiting_trans && blending_no_waiting_trans) { -// do_deinitialize = true; -// ppa_group->srm = NULL; -// ppa_group->blending = NULL; -// s_platform.group[group_id] = NULL; -// } else { -// ret = ESP_FAIL; -// } -// // portEXIT_CRITICAL(&s_platform.spinlock); - -// if (do_deinitialize) { -// if (srm_engine) { -// free(srm_engine->dma_tx_desc); -// free(srm_engine->dma_rx_desc); -// vSemaphoreDelete(srm_engine->base.sem); -// free(srm_engine); -// } -// if (blending_engine) { -// free(blending_engine->dma_tx_bg_desc); -// free(blending_engine->dma_tx_fg_desc); -// free(blending_engine->dma_rx_desc); -// vSemaphoreDelete(blending_engine->base.sem); -// free(blending_engine); -// } -// dma2d_release_pool(ppa_group->dma2d_pool_handle); -// // Disable the bus clock to access PPA registers -// PERIPH_RCC_ATOMIC() { -// ppa_ll_enable_bus_clock(group_id, false); -// } -// free(ppa_group); -// } -// return ret; -// } - // Each PPA engine should only have one transaction being pushed to 2D-DMA queue, the rest transactions should stay in engine's own transaction queue. // This is to avoid 2D-DMA channels being hold, but not actually being used (waiting for PPA engine to be free) static esp_err_t ppa_dma2d_enqueue(const ppa_trans_t *trans_elm) @@ -713,17 +364,53 @@ static esp_err_t ppa_dma2d_enqueue(const ppa_trans_t *trans_elm) return dma2d_enqueue(s_platform.dma2d_pool_handle, trans_elm->trans_desc, trans_elm->dma_trans_placeholder); } -static void ppa_recycle_transaction(ppa_trans_t *trans_elm) +static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t trans_elm_num, ppa_operation_t oper_type) +{ + bool res = true; + size_t ppa_trans_desc_size = (oper_type == PPA_OPERATION_SRM) ? sizeof(ppa_srm_oper_t) : + (oper_type == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_oper_t) : + (oper_type == PPA_OPERATION_FILL) ? sizeof(ppa_fill_oper_t) : 0; + assert(ppa_trans_desc_size != 0); + size_t trans_elm_storage_size = sizeof(ppa_trans_t) + SIZEOF_DMA2D_TRANS_T + sizeof(dma2d_trans_config_t) + sizeof(ppa_dma2d_trans_on_picked_config_t) + ppa_trans_desc_size; + for (int i = 0; i < trans_elm_num; i++) { + void *trans_elm_storage = heap_caps_calloc(1, trans_elm_storage_size, PPA_MEM_ALLOC_CAPS); + SemaphoreHandle_t ppa_trans_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); + + if (!trans_elm_storage || !ppa_trans_sem) { + if (trans_elm_storage) { + free(trans_elm_storage); + } + if (ppa_trans_sem) { + vSemaphoreDeleteWithCaps(ppa_trans_sem); + } + res = false; + break; + } + + // Construct trans_elm + ppa_trans_t *new_trans_elm = (ppa_trans_t *)trans_elm_storage; + dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)((uint32_t)trans_elm_storage + sizeof(ppa_trans_t)); + dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)((uint32_t)dma_trans_elm + SIZEOF_DMA2D_TRANS_T); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)((uint32_t)dma_trans_desc + sizeof(dma2d_trans_config_t)); + void *ppa_trans_desc = (void *)((uint32_t)trans_on_picked_desc + sizeof(ppa_dma2d_trans_on_picked_config_t)); + + trans_on_picked_desc->op_desc = ppa_trans_desc; + trans_on_picked_desc->trans_elm = new_trans_elm; + dma_trans_desc->user_config = (void *)trans_on_picked_desc; + dma_trans_desc->on_job_picked = ppa_oper_trans_on_picked_func[oper_type]; // TODO: This maybe better to be in the ppa_do_xxx function + new_trans_elm->trans_desc = dma_trans_desc; + new_trans_elm->dma_trans_placeholder = dma_trans_elm; + new_trans_elm->sem = ppa_trans_sem; + + // Fill the ring buffer with allocated transaction element pointer + assert(xQueueSend(trans_elm_ptr_queue, &new_trans_elm, 0)); + } + return res; +} + +static void ppa_free_transaction(ppa_trans_t *trans_elm) { if (trans_elm) { - if (trans_elm->trans_desc) { - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; - if (trans_on_picked_desc) { - free(trans_on_picked_desc->op_desc); - free(trans_on_picked_desc); - } - free(trans_elm->trans_desc); - } if (trans_elm->sem) { vSemaphoreDeleteWithCaps(trans_elm->sem); } @@ -731,73 +418,29 @@ static void ppa_recycle_transaction(ppa_trans_t *trans_elm) } } -static esp_err_t ppa_prepare_trans_elm(ppa_invoker_handle_t ppa_invoker, ppa_engine_t *ppa_engine_base, ppa_operation_t ppa_operation, const void *oper_config, ppa_trans_mode_t mode, ppa_trans_t **trans_elm) +static bool ppa_recycle_transaction(ppa_invoker_handle_t ppa_invoker, ppa_trans_t *trans_elm) { - esp_err_t ret = ESP_OK; - ppa_engine_type_t engine_type = ppa_engine_base->type; - - ppa_trans_t *new_trans_elm = (ppa_trans_t *)heap_caps_calloc(1, sizeof(ppa_trans_t), PPA_MEM_ALLOC_CAPS); - dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, PPA_MEM_ALLOC_CAPS); - dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)heap_caps_calloc(1, sizeof(dma2d_trans_config_t), PPA_MEM_ALLOC_CAPS); - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)heap_caps_calloc(1, sizeof(ppa_dma2d_trans_on_picked_config_t), PPA_MEM_ALLOC_CAPS); - size_t ppa_trans_desc_size = (ppa_operation == PPA_OPERATION_SRM) ? sizeof(ppa_srm_oper_t) : - (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_oper_t) : - (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_oper_t) : 0; - assert(ppa_trans_desc_size != 0); - void *ppa_trans_desc = heap_caps_calloc(1, ppa_trans_desc_size, PPA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(new_trans_elm && dma_trans_elm && dma_trans_desc && trans_on_picked_desc && ppa_trans_desc, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); - if (mode == PPA_TRANS_MODE_BLOCKING) { - new_trans_elm->sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(new_trans_elm->sem, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); - } - - size_t cpy_size = (ppa_operation == PPA_OPERATION_SRM) ? sizeof(ppa_srm_operation_config_t) : - (ppa_operation == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_operation_config_t) : - (ppa_operation == PPA_OPERATION_FILL) ? sizeof(ppa_fill_operation_config_t) : 0; - memcpy(ppa_trans_desc, oper_config, cpy_size); - - trans_on_picked_desc->op_desc = ppa_trans_desc; - trans_on_picked_desc->ppa_engine = ppa_engine_base; - trans_on_picked_desc->trans_elm = new_trans_elm; - trans_on_picked_desc->trigger_periph = (engine_type == PPA_ENGINE_TYPE_SRM) ? DMA2D_TRIG_PERIPH_PPA_SRM : DMA2D_TRIG_PERIPH_PPA_BLEND; - - dma_trans_desc->tx_channel_num = (ppa_operation == PPA_OPERATION_SRM) ? 1 : - (ppa_operation == PPA_OPERATION_BLEND) ? 2 : 0; // PPA_OPERATION_FILL does not have data input - dma_trans_desc->rx_channel_num = 1; - - // dma_trans_desc->specified_tx_channel_mask = ppa_specified_tx_channel_mask; - // dma_trans_desc->specified_rx_channel_mask = ppa_specified_rx_channel_mask; - - dma_trans_desc->user_config = (void *)trans_on_picked_desc; - dma_trans_desc->on_job_picked = ppa_oper_trans_on_picked_func[ppa_operation]; - - new_trans_elm->trans_desc = dma_trans_desc; - new_trans_elm->dma_trans_placeholder = dma_trans_elm; - new_trans_elm->invoker = ppa_invoker; - - *trans_elm = new_trans_elm; -err: - if (ret != ESP_OK) { - ppa_recycle_transaction(new_trans_elm); - *trans_elm = NULL; - } - return ret; + // Reset transaction and send back to invoker's trans_elm_ptr_queue + // TODO: To be very safe, we shall memset all to 0, and reconnect necessary pointers? + BaseType_t HPTaskAwoken; + assert(xQueueSendFromISR(ppa_invoker->trans_elm_ptr_queue, &trans_elm, &HPTaskAwoken)); + return HPTaskAwoken; } static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t *ppa_engine_base, ppa_trans_t *trans_elm, ppa_trans_mode_t mode) { esp_err_t ret = ESP_OK; - ppa_engine_type_t engine_type = ppa_engine_base->type; + +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_acquire(s_platform.pm_lock) == ESP_OK) && "acquire pm_lock failed"); +#endif portENTER_CRITICAL(&ppa_invoker->spinlock); + // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! if (ppa_invoker->in_accepting_trans_state) { // Send transaction into PPA engine queue STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry); - if (engine_type == PPA_ENGINE_TYPE_SRM) { - ppa_invoker->srm_trans_cnt++; - } else { - ppa_invoker->blending_trans_cnt++; - } + ppa_invoker->trans_cnt++; } else { ret = ESP_FAIL; } @@ -823,12 +466,21 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t } portEXIT_CRITICAL(&ppa_engine_base->spinlock); if (found) { +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_acquire(ppa_engine_base->pm_lock) == ESP_OK) && "acquire pm_lock failed"); +#endif ret = ppa_dma2d_enqueue(trans_elm); if (ret != ESP_OK) { portENTER_CRITICAL(&ppa_engine_base->spinlock); STAILQ_REMOVE(&ppa_engine_base->trans_stailq, trans_elm, ppa_trans_s, entry); portEXIT_CRITICAL(&ppa_engine_base->spinlock); +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_release(ppa_engine_base->pm_lock) == ESP_OK) && "release pm_lock failed"); +#endif xSemaphoreGive(ppa_engine_base->sem); + portENTER_CRITICAL(&ppa_invoker->spinlock); + ppa_invoker->trans_cnt--; + portEXIT_CRITICAL(&ppa_invoker->spinlock); goto err; } } else { @@ -842,9 +494,13 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t // } xSemaphoreTake(trans_elm->sem, portMAX_DELAY); // Given in the ISR // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) - ppa_recycle_transaction(trans_elm); + // ppa_recycle_transaction(ppa_invoker, trans_elm); // TODO: Do we need it to be here or can be at the end of done_cb? } +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_release(s_platform.pm_lock) == ESP_OK) && "release pm_lock failed"); +#endif + err: return ret; } @@ -857,7 +513,11 @@ static bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_eve ppa_invoker_t *invoker = trans_elm->invoker; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; ppa_engine_t *engine_base = trans_on_picked_desc->ppa_engine; - ppa_engine_type_t engine_type = engine_base->type; + + if (invoker->done_cb) { + ppa_event_data_t edata = {}; + need_yield |= invoker->done_cb(invoker, &edata, trans_elm->user_data); + } ppa_trans_t *next_start_trans = NULL; portENTER_CRITICAL_ISR(&engine_base->spinlock); @@ -870,27 +530,24 @@ static bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_eve if (next_start_trans) { ppa_dma2d_enqueue(next_start_trans); } else { +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_release(engine_base->pm_lock) == ESP_OK)); +#endif xSemaphoreGiveFromISR(engine_base->sem, &HPTaskAwoken); need_yield |= (HPTaskAwoken == pdTRUE); } - // Recycle transaction or give transaction semaphore - if (trans_elm->sem != NULL) { - xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); - need_yield |= (HPTaskAwoken == pdTRUE); - } else { - ppa_recycle_transaction(trans_elm); - } + // Recycle transaction and release transaction semaphore + // if (trans_elm->sem != NULL) { + xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); + need_yield |= (HPTaskAwoken == pdTRUE); + // } + // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! portENTER_CRITICAL_ISR(&invoker->spinlock); - if (engine_type == PPA_ENGINE_TYPE_SRM) { - invoker->srm_trans_cnt--; - } else { - invoker->blending_trans_cnt--; - } + need_yield |= ppa_recycle_transaction(invoker, trans_elm); + invoker->trans_cnt--; portEXIT_CRITICAL_ISR(&invoker->spinlock); - // TODO: how to notify non-blocking transaction - return need_yield; } @@ -900,12 +557,9 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SRM && trans_on_picked_desc->srm_desc && trans_on_picked_desc->ppa_engine); - ppa_srm_oper_t *srm_trans_desc = trans_on_picked_desc->srm_desc; + ppa_srm_oper_t *srm_trans_desc = (ppa_srm_oper_t *)trans_on_picked_desc->srm_desc; ppa_srm_engine_t *srm_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_srm_engine_t, base); - // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) - free(trans_on_picked_desc->trans_elm->dma_trans_placeholder); - // Get the required 2D-DMA channel handles uint32_t dma2d_tx_chan_idx = 0; uint32_t dma2d_rx_chan_idx = 1; @@ -955,9 +609,6 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ esp_cache_msync((void *)srm_engine->dma_tx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); esp_cache_msync((void *)srm_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - // printf("desc addr: %p\n", srm_engine->dma_rx_desc); - // printf("desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)srm_engine->dma_rx_desc, *(uint32_t *)((uint32_t)srm_engine->dma_rx_desc + 4), *(uint32_t *)((uint32_t)srm_engine->dma_rx_desc + 8), *(uint32_t *)((uint32_t)srm_engine->dma_rx_desc + 12), *(uint32_t *)((uint32_t)srm_engine->dma_rx_desc + 16)); - // Configure 2D-DMA channels dma2d_trigger_t trig_periph = { .periph = DMA2D_TRIG_PERIPH_PPA_SRM, @@ -997,13 +648,15 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); } + // TODO: YUV420 needs to change dev->out_channel[channel].out_dscr_port_blk.out_dscr_port_blk_h/v_chn to 20 + ppa_srm_color_mode_t ppa_out_color_mode = srm_trans_desc->out_color.mode; if (ppa_out_color_mode == PPA_SRM_COLOR_MODE_YUV444) { ppa_out_color_mode = PPA_SRM_COLOR_MODE_YUV420; dma2d_csc_config_t dma_rx_csc = { .rx_csc_option = DMA2D_CSC_RX_YUV420_TO_YUV444, }; - dma2d_configure_color_space_conversion(dma2d_rx_chan, &dma_rx_csc); // TODO: RX CSC only on channel 0 + dma2d_configure_color_space_conversion(dma2d_rx_chan, &dma_rx_csc); } dma2d_rx_event_callbacks_t dma_event_cbs = { @@ -1051,7 +704,7 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa_srm_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) { ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_invoker->srm_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to SRM engine"); + ESP_RETURN_ON_FALSE(ppa_invoker->oper_type == PPA_OPERATION_SRM, ESP_ERR_INVALID_ARG, TAG, "invoker is not for SRM operations"); ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); @@ -1062,17 +715,25 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa }; uint32_t out_pic_len = oper_config->out_pic_w * oper_config->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; ESP_RETURN_ON_FALSE(out_pic_len <= oper_config->out_buffer_size, ESP_ERR_INVALID_ARG, TAG, "out_pic_w/h mismatch with out_buffer_size"); - // buffer on stack/heap - // check scale w/ (out_pic_w/h - out_pic_offset_x/y)? - // ESP_RETURN_ON_FALSE(config->rotation_angle) - // ESP_RETURN_ON_FALSE(config->in/out_color_mode) - // what if in_color is YUV420, out is RGB, what is out RGB range? Full range? ESP_RETURN_ON_FALSE(oper_config->scale_x < (PPA_LL_SRM_SCALING_INT_MAX + 1) && oper_config->scale_x >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX) && oper_config->scale_y < (PPA_LL_SRM_SCALING_INT_MAX + 1) && oper_config->scale_y >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid scale"); - // byte/rgb swap with color mode only to (A)RGB color space? - + ESP_RETURN_ON_FALSE((uint32_t)(oper_config->scale_x * oper_config->in_block_w) <= (oper_config->out_pic_w - oper_config->out_block_offset_x) && + (uint32_t)(oper_config->scale_y * oper_config->in_block_h) <= (oper_config->out_pic_h - oper_config->out_block_offset_y), + ESP_ERR_INVALID_ARG, TAG, "scale does not fit in the out pic"); + if (oper_config->in_color.byte_swap) { + ESP_RETURN_ON_FALSE(oper_config->in_color.mode == PPA_SRM_COLOR_MODE_ARGB8888 || oper_config->in_color.mode == PPA_SRM_COLOR_MODE_RGB565, + ESP_ERR_INVALID_ARG, TAG, "in_color_mode does not support byte_swap"); + } + if (oper_config->in_color.rgb_swap) { + ESP_RETURN_ON_FALSE(COLOR_SPACE_TYPE((uint32_t)oper_config->in_color.mode) == COLOR_SPACE_ARGB || COLOR_SPACE_TYPE((uint32_t)oper_config->in_color.mode) == COLOR_SPACE_RGB, + ESP_ERR_INVALID_ARG, TAG, "in_color_mode does not support rgb_swap"); + } + ESP_RETURN_ON_FALSE(oper_config->in_color.alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_value"); + // To reduce complexity, rotation_angle, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions + // TODO: // YUV420: in desc, ha/hb/va/vb/x/y must be even number + // What for YUV444/YUV422 // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) // Write back in_buffer @@ -1084,23 +745,46 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa // Invalidate out_buffer esp_cache_msync(oper_config->out_buffer, oper_config->out_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->srm_engine, PPA_OPERATION_SRM, (void *)oper_config, trans_config->mode, &trans_elm); - if (ret == ESP_OK) { - assert(trans_elm); + if (xQueueReceive(ppa_invoker->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; - // Pre-process some data - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = trans_elm->trans_desc->user_config; - ppa_srm_oper_t *srm_trans_desc = trans_on_picked_desc->srm_desc; + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; + + ppa_srm_oper_t *srm_trans_desc = (ppa_srm_oper_t *)trans_on_picked_desc->srm_desc; + memcpy(srm_trans_desc, oper_config, sizeof(ppa_srm_operation_config_t)); srm_trans_desc->scale_x_int = (uint32_t)srm_trans_desc->scale_x; srm_trans_desc->scale_x_frag = (uint32_t)(srm_trans_desc->scale_x * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; srm_trans_desc->scale_y_int = (uint32_t)srm_trans_desc->scale_y; srm_trans_desc->scale_y_frag = (uint32_t)(srm_trans_desc->scale_y * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; - ret = ppa_do_operation(ppa_invoker, ppa_invoker->srm_engine, trans_elm, trans_config->mode); - if (ret != ESP_OK) { - ppa_recycle_transaction(trans_elm); + trans_on_picked_desc->ppa_engine = ppa_invoker->engine; + trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_SRM; + + dma_trans_desc->tx_channel_num = 1; + dma_trans_desc->rx_channel_num = 1; + dma_trans_desc->channel_flags = 0; + if (oper_config->in_color.mode == PPA_SRM_COLOR_MODE_YUV422 || oper_config->in_color.mode == PPA_SRM_COLOR_MODE_YUV444) { + dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC; } + if (oper_config->out_color.mode == PPA_SRM_COLOR_MODE_YUV444) { + dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC; + } + dma_trans_desc->specified_tx_channel_mask = 0; + dma_trans_desc->specified_rx_channel_mask = 0; + + trans_elm->invoker = ppa_invoker; + trans_elm->user_data = trans_config->user_data; + xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semphore before transaction starts + + ret = ppa_do_operation(ppa_invoker, ppa_invoker->engine, trans_elm, trans_config->mode); + if (ret != ESP_OK) { + ppa_recycle_transaction(ppa_invoker, trans_elm); + } + } else { + ret = ESP_FAIL; + ESP_LOGE(TAG, "exceed maximum pending transactions for the invoker, consider increase max_pending_trans_num"); } return ret; } @@ -1111,12 +795,9 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->blend_desc && trans_on_picked_desc->ppa_engine); - ppa_blend_oper_t *blend_trans_desc = trans_on_picked_desc->blend_desc; + ppa_blend_oper_t *blend_trans_desc = (ppa_blend_oper_t *)trans_on_picked_desc->blend_desc; ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); - // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) - free(trans_on_picked_desc->trans_elm->dma_trans_placeholder); - // Get the required 2D-DMA channel handles dma2d_channel_handle_t dma2d_tx_bg_chan = NULL; dma2d_channel_handle_t dma2d_tx_fg_chan = NULL; @@ -1195,10 +876,6 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran esp_cache_msync((void *)blend_engine->dma_tx_fg_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); esp_cache_msync((void *)blend_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - // printf("tx bg desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)blend_engine->dma_tx_bg_desc, *(uint32_t *)((uint32_t)blend_engine->dma_tx_bg_desc + 4), *(uint32_t *)((uint32_t)blend_engine->dma_tx_bg_desc + 8), *(uint32_t *)((uint32_t)blend_engine->dma_tx_bg_desc + 12), *(uint32_t *)((uint32_t)blend_engine->dma_tx_bg_desc + 16)); - // printf("tx fg desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)blend_engine->dma_tx_fg_desc, *(uint32_t *)((uint32_t)blend_engine->dma_tx_fg_desc + 4), *(uint32_t *)((uint32_t)blend_engine->dma_tx_fg_desc + 8), *(uint32_t *)((uint32_t)blend_engine->dma_tx_fg_desc + 12), *(uint32_t *)((uint32_t)blend_engine->dma_tx_fg_desc + 16)); - // printf("rx desc content: %08lX, %08lX, %08lX, %08lX, %08lX\n", *(uint32_t *)blend_engine->dma_rx_desc, *(uint32_t *)((uint32_t)blend_engine->dma_rx_desc + 4), *(uint32_t *)((uint32_t)blend_engine->dma_rx_desc + 8), *(uint32_t *)((uint32_t)blend_engine->dma_rx_desc + 12), *(uint32_t *)((uint32_t)blend_engine->dma_rx_desc + 16)); - // Configure 2D-DMA channels dma2d_trigger_t trig_periph = { .periph = DMA2D_TRIG_PERIPH_PPA_BLEND, @@ -1268,7 +945,7 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) { ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); + ESP_RETURN_ON_FALSE(ppa_invoker->oper_type == PPA_OPERATION_BLEND, ESP_ERR_INVALID_ARG, TAG, "invoker is not for blend operations"); ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); @@ -1279,8 +956,33 @@ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operati }; uint32_t out_pic_len = oper_config->out_pic_w * oper_config->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; ESP_RETURN_ON_FALSE(out_pic_len <= oper_config->out_buffer_size, ESP_ERR_INVALID_ARG, TAG, "out_pic_w/h mismatch with out_buffer_size"); - // TODO: ARG CHECK - // 当输入类型为 L4、A4 时,图像块的尺寸 hb 以及在图像中的偏移 x 必须为偶数 + if (oper_config->in_bg_color.byte_swap) { + ESP_RETURN_ON_FALSE(oper_config->in_bg_color.mode == PPA_BLEND_COLOR_MODE_ARGB8888 || oper_config->in_bg_color.mode == PPA_BLEND_COLOR_MODE_RGB565, + ESP_ERR_INVALID_ARG, TAG, "in_bg_color_mode does not support byte_swap"); + } + if (oper_config->in_bg_color.rgb_swap) { + ESP_RETURN_ON_FALSE(COLOR_SPACE_TYPE((uint32_t)oper_config->in_bg_color.mode) == COLOR_SPACE_ARGB || COLOR_SPACE_TYPE((uint32_t)oper_config->in_bg_color.mode) == COLOR_SPACE_RGB, + ESP_ERR_INVALID_ARG, TAG, "in_bg_color_mode does not support rgb_swap"); + } + if (oper_config->in_fg_color.byte_swap) { + ESP_RETURN_ON_FALSE(oper_config->in_fg_color.mode == PPA_BLEND_COLOR_MODE_ARGB8888 || oper_config->in_fg_color.mode == PPA_BLEND_COLOR_MODE_RGB565, + ESP_ERR_INVALID_ARG, TAG, "in_fg_color_mode does not support byte_swap"); + } + if (oper_config->in_fg_color.rgb_swap) { + ESP_RETURN_ON_FALSE(COLOR_SPACE_TYPE((uint32_t)oper_config->in_fg_color.mode) == COLOR_SPACE_ARGB || COLOR_SPACE_TYPE((uint32_t)oper_config->in_fg_color.mode) == COLOR_SPACE_RGB, + ESP_ERR_INVALID_ARG, TAG, "in_fg_color_mode does not support rgb_swap"); + } + ESP_RETURN_ON_FALSE(oper_config->in_bg_color.alpha_value <= 0xFF && oper_config->in_fg_color.alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_value"); + if (oper_config->in_bg_color.mode == PPA_BLEND_COLOR_MODE_L4) { + ESP_RETURN_ON_FALSE(oper_config->in_bg_fg_block_h % 2 == 0 && oper_config->in_bg_block_offset_x % 2 == 0, + ESP_ERR_INVALID_ARG, TAG, "bg_block_h and bg_block_offset_x must be even"); + } + if (oper_config->in_fg_color.mode == PPA_BLEND_COLOR_MODE_A4 || oper_config->in_fg_color.mode == PPA_BLEND_COLOR_MODE_L4) { + ESP_RETURN_ON_FALSE(oper_config->in_bg_fg_block_h % 2 == 0 && oper_config->in_fg_block_offset_x % 2 == 0, + ESP_ERR_INVALID_ARG, TAG, "fg_block_h and fg_block_offset_x must be even"); + } + // To reduce complexity, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions + // TODO: Check in_bg.block_w == in_fg.block_w && in_bg.block_h == in_fg.block_h // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) // Write back in_bg_buffer, in_fg_buffer @@ -1297,15 +999,36 @@ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operati // Invalidate out_buffer esp_cache_msync(oper_config->out_buffer, oper_config->out_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_BLEND, (void *)oper_config, trans_config->mode, &trans_elm); - if (ret == ESP_OK) { - assert(trans_elm); + if (xQueueReceive(ppa_invoker->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; - ret = ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, trans_elm, trans_config->mode); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; + + ppa_blend_oper_t *blend_trans_desc = (ppa_blend_oper_t *)trans_on_picked_desc->blend_desc; + memcpy(blend_trans_desc, oper_config, sizeof(ppa_blend_operation_config_t)); + + trans_on_picked_desc->ppa_engine = ppa_invoker->engine; + trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; + + dma_trans_desc->tx_channel_num = 2; + dma_trans_desc->rx_channel_num = 1; + dma_trans_desc->channel_flags = 0; + dma_trans_desc->specified_tx_channel_mask = 0; + dma_trans_desc->specified_rx_channel_mask = 0; + + trans_elm->invoker = ppa_invoker; + trans_elm->user_data = trans_config->user_data; + xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semphore before transaction starts + + ret = ppa_do_operation(ppa_invoker, ppa_invoker->engine, trans_elm, trans_config->mode); if (ret != ESP_OK) { - ppa_recycle_transaction(trans_elm); + ppa_recycle_transaction(ppa_invoker, trans_elm); } + } else { + ret = ESP_FAIL; + ESP_LOGE(TAG, "exceed maximum pending transactions for the invoker, consider increase max_pending_trans_num"); } return ret; } @@ -1316,12 +1039,9 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->fill_desc && trans_on_picked_desc->ppa_engine); - ppa_fill_oper_t *fill_trans_desc = trans_on_picked_desc->fill_desc; + ppa_fill_oper_t *fill_trans_desc = (ppa_fill_oper_t *)trans_on_picked_desc->fill_desc; ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); - // Free 2D-DMA transaction placeholder (transaction has already been moved out from 2D-DMA queue) - free(trans_on_picked_desc->trans_elm->dma_trans_placeholder); - // Get the required 2D-DMA channel handles assert(dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX); dma2d_channel_handle_t dma2d_rx_chan = dma2d_chans[0].chan; @@ -1385,7 +1105,7 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) { ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_invoker->blending_engine, ESP_ERR_INVALID_ARG, TAG, "invoker did not register to Blending engine"); + ESP_RETURN_ON_FALSE(ppa_invoker->oper_type == PPA_OPERATION_FILL, ESP_ERR_INVALID_ARG, TAG, "invoker is not for fill operations"); ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // out_buffer ptr cannot in flash region ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); @@ -1396,21 +1116,41 @@ esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation }; uint32_t out_pic_len = oper_config->out_pic_w * oper_config->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; ESP_RETURN_ON_FALSE(out_pic_len <= oper_config->out_buffer_size, ESP_ERR_INVALID_ARG, TAG, "out_pic_w/h mismatch with out_buffer_size"); - // TODO: ARG CHECK - // fill_block_w <= PPA_BLEND_HB_V, fill_block_h <= PPA_BLEND_VB_V + // To reduce complexity, color_mode, fill_block_w/h correctness are checked in their corresponding LL functions // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) esp_cache_msync(oper_config->out_buffer, oper_config->out_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - esp_err_t ret = ppa_prepare_trans_elm(ppa_invoker, ppa_invoker->blending_engine, PPA_OPERATION_FILL, (void *)oper_config, trans_config->mode, &trans_elm); - if (ret == ESP_OK) { - assert(trans_elm); + if (xQueueReceive(ppa_invoker->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; - ret = ppa_do_operation(ppa_invoker, ppa_invoker->blending_engine, trans_elm, trans_config->mode); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; + + ppa_fill_oper_t *fill_trans_desc = (ppa_fill_oper_t *)trans_on_picked_desc->fill_desc; + memcpy(fill_trans_desc, oper_config, sizeof(ppa_fill_operation_config_t)); + + trans_on_picked_desc->ppa_engine = ppa_invoker->engine; + trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; + + dma_trans_desc->tx_channel_num = 0; + dma_trans_desc->rx_channel_num = 1; + dma_trans_desc->channel_flags = 0; + dma_trans_desc->specified_tx_channel_mask = 0; + dma_trans_desc->specified_rx_channel_mask = 0; + + trans_elm->invoker = ppa_invoker; + trans_elm->user_data = trans_config->user_data; + xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semphore before transaction starts + + ret = ppa_do_operation(ppa_invoker, ppa_invoker->engine, trans_elm, trans_config->mode); if (ret != ESP_OK) { - ppa_recycle_transaction(trans_elm); + ppa_recycle_transaction(ppa_invoker, trans_elm); } + } else { + ret = ESP_FAIL; + ESP_LOGE(TAG, "exceed maximum pending transactions for the invoker, consider increase max_pending_trans_num"); } return ret; } diff --git a/components/esp_driver_ppa/src/ppa_priv.h b/components/esp_driver_ppa/src/ppa_priv.h new file mode 100644 index 0000000000..7e11fae305 --- /dev/null +++ b/components/esp_driver_ppa/src/ppa_priv.h @@ -0,0 +1,153 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "sdkconfig.h" +#include "driver/ppa.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_private/dma2d.h" +#include "hal/dma2d_types.h" +#include "hal/ppa_types.h" +#include "esp_pm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PPA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) // TODO... + +#define PPA_PM_LOCK_NAME_LEN_MAX 16 + +/******************************** ENGINE *************************************/ +// PPA module contains SRM engine and Blending engine + +typedef struct ppa_engine_t ppa_engine_t; + +struct ppa_engine_t { + ppa_engine_type_t type; // Type of the PPA engine + portMUX_TYPE spinlock; // Engine level spinlock + SemaphoreHandle_t sem; // Semaphore for whether the engine is processing a transaction + STAILQ_HEAD(trans, ppa_trans_s) trans_stailq; // link head of pending transactions for the PPA engine +#if CONFIG_PM_ENABLE + esp_pm_lock_handle_t pm_lock; // Power management lock +#endif +}; + +typedef struct ppa_srm_engine_t { + ppa_engine_t base; // PPA engine base structure + dma2d_descriptor_t *dma_tx_desc; // Into PPA SRM engine direction 2D-DMA descriptor + dma2d_descriptor_t *dma_rx_desc; // Out from PPA SRM engine direction 2D-DMA descriptor +} ppa_srm_engine_t; + +typedef struct ppa_blend_engine_t { + ppa_engine_t base; // PPA engine base structure + dma2d_descriptor_t *dma_tx_bg_desc; // Into PPA Blending engine direction background channel 2D-DMA descriptor + dma2d_descriptor_t *dma_tx_fg_desc; // Into PPA Blending engine direction foreground channel 2D-DMA descriptor + dma2d_descriptor_t *dma_rx_desc; // Out from PPA blending engine direction 2D-DMA descriptor +} ppa_blend_engine_t; + +typedef struct { + ppa_engine_type_t engine; // Engine type +} ppa_engine_config_t; + +/******************************** CLIENT *************************************/ + +typedef struct ppa_invoker_t ppa_invoker_t; + +struct ppa_invoker_t { + ppa_operation_t oper_type; // The PPA operation type that the invoker wants to do in speciality + ppa_engine_t *engine; // Pointer to the PPA engine that in charge of performing the PPA operation + uint32_t trans_cnt; // Number of pending PPA transactions + portMUX_TYPE spinlock; // Client level spinlock + bool in_accepting_trans_state; // Indicates whether the invoker can accept new PPA transaction requests now + ppa_event_callback_t done_cb; // Transaction done callback + QueueHandle_t trans_elm_ptr_queue; // Queue that contains the pointers to the allocated memory to save the transaction contexts +}; + +/****************************** OPERATION ************************************/ + +// The elements in this structure listed first are identical to the elements in structure `ppa_blend_operation_config_t` +// With adding a few extra elements in the end +// This allows memcpy +typedef struct { + void *in_buffer; + uint32_t in_pic_w; + uint32_t in_pic_h; + uint32_t in_block_w; + uint32_t in_block_h; + uint32_t in_block_offset_x; + uint32_t in_block_offset_y; + + void *out_buffer; + uint32_t out_buffer_size; + uint32_t out_pic_w; + uint32_t out_pic_h; + uint32_t out_block_offset_x; + uint32_t out_block_offset_y; + + ppa_srm_rotation_angle_t rotation_angle; + float scale_x; + float scale_y; + bool mirror_x; + bool mirror_y; + + struct { + ppa_srm_color_mode_t mode; + color_range_t yuv_range; + color_conv_std_rgb_yuv_t yuv_std; + bool rgb_swap; + bool byte_swap; + ppa_alpha_update_mode_t alpha_update_mode; + uint32_t alpha_value; + } in_color; + + struct { + ppa_srm_color_mode_t mode; + color_range_t yuv_range; + color_conv_std_rgb_yuv_t yuv_std; + } out_color; + + uint32_t scale_x_int; + uint32_t scale_x_frag; + uint32_t scale_y_int; + uint32_t scale_y_frag; +} ppa_srm_oper_t; + +typedef ppa_blend_operation_config_t ppa_blend_oper_t; + +typedef ppa_fill_operation_config_t ppa_fill_oper_t; + +/***************************** TRANSACTION ***********************************/ + +// PPA transaction element +typedef struct ppa_trans_s { + STAILQ_ENTRY(ppa_trans_s) entry; // Link entry + dma2d_trans_config_t *trans_desc; // Pointer to the structure containing the configurations for a 2D-DMA transaction + dma2d_trans_t *dma_trans_placeholder; // Pointer to the memory to store the 2D-DMA transaction context + SemaphoreHandle_t sem; // Semaphore to block when the transaction has not finished + ppa_invoker_t *invoker; // Pointer to the invoker who requested the transaction + void *user_data; // User registered event data (per transaction) +} ppa_trans_t; + +typedef struct { + union { + ppa_srm_oper_t *srm_desc; // Pointer to the structure containing the configurations for a PPA SRM operation transaction + ppa_blend_oper_t *blend_desc; // Pointer to the structure containing the configurations for a PPA blend operation transaction + ppa_fill_oper_t *fill_desc; // Pointer to the structure containing the configurations for a PPA fill operation transaction + void *op_desc; // General pointer to the structure containing the configurations for a PPA transaction + }; + ppa_engine_t *ppa_engine; // Pointer to the PPA engine + ppa_trans_t *trans_elm; // Pointer to the PPA transaction element + dma2d_trigger_peripheral_t trigger_periph; // The 2D-DMA trigger peripheral +} ppa_dma2d_trans_on_picked_config_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index 5e2312da4f..7e9c0fe2c4 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -222,6 +222,10 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "jpeg_hal.c") endif() + if(CONFIG_SOC_PPA_SUPPORTED) + list(APPEND srcs "ppa_hal.c") + endif() + if(CONFIG_SOC_GPSPI_SUPPORTED) list(APPEND srcs "spi_hal.c" diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index 31612960ef..54b07c6c63 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -308,7 +308,7 @@ static inline void ppa_ll_srm_set_tx_yuv_range(ppa_dev_t *dev, color_range_t ran } /** - * @brief Enable PPA SRM input data wrap in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) + * @brief Enable PPA SRM input data swap in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) * * @param dev Peripheral instance address * @param enable True to enable; False to disable diff --git a/components/hal/include/hal/ppa_hal.h b/components/hal/include/hal/ppa_hal.h new file mode 100644 index 0000000000..1cbfc30005 --- /dev/null +++ b/components/hal/include/hal/ppa_hal.h @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The HAL is not public api, don't use in application code. + * See readme.md in soc/README.md + ******************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ppa_dev_t *ppa_soc_handle_t; // PPA SOC layer handle + +/** + * Context that should be maintained by both the driver and the HAL + */ +typedef struct { + ppa_soc_handle_t dev; // PPA SOC layer handle (i.e. register base address) +} ppa_hal_context_t; + +/** + * @brief Init the PPA hal. This function should be called first before other hal layer function is called + * + * @param hal Context of the HAL layer + */ +void ppa_hal_init(ppa_hal_context_t *hal); + +/** + * @brief De-init the PPA hal + * + * @param hal Context of the HAL layer + */ +void ppa_hal_deinit(ppa_hal_context_t *hal); + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/ppa_types.h b/components/hal/include/hal/ppa_types.h index f08d2b2bb9..2aa0c42106 100644 --- a/components/hal/include/hal/ppa_types.h +++ b/components/hal/include/hal/ppa_types.h @@ -47,26 +47,38 @@ typedef enum { } ppa_srm_color_mode_t; /** - * @brief Enumeration of PPA Blending available color mode + * @brief Enumeration of PPA blend available color mode */ typedef enum { - PPA_BLEND_COLOR_MODE_ARGB8888 = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888), /*!< PPA Blending color mode: ARGB8888 */ - PPA_BLEND_COLOR_MODE_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< PPA Blending color mode: RGB888 */ - PPA_BLEND_COLOR_MODE_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< PPA Blending color mode: RGB565 */ - PPA_BLEND_COLOR_MODE_L8 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L8), /*!< PPA Blending color mode: L8, only available on blending inputs */ - PPA_BLEND_COLOR_MODE_L4 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L4), /*!< PPA Blending color mode: L4, only available on blending inputs */ - PPA_BLEND_COLOR_MODE_A8 = COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A8), /*!< PPA Blending color mode: A8, only available on blending foreground input */ - PPA_BLEND_COLOR_MODE_A4 = COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A4), /*!< PPA Blending color mode: A4, only available on blending foreground input */ + PPA_BLEND_COLOR_MODE_ARGB8888 = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888), /*!< PPA blend color mode: ARGB8888 */ + PPA_BLEND_COLOR_MODE_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< PPA blend color mode: RGB888 */ + PPA_BLEND_COLOR_MODE_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< PPA blend color mode: RGB565 */ + PPA_BLEND_COLOR_MODE_L8 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L8), /*!< PPA blend color mode: L8, only available on blend inputs */ + PPA_BLEND_COLOR_MODE_L4 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L4), /*!< PPA blend color mode: L4, only available on blend inputs */ + PPA_BLEND_COLOR_MODE_A8 = COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A8), /*!< PPA blend color mode: A8, only available on blend foreground input */ + PPA_BLEND_COLOR_MODE_A4 = COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A4), /*!< PPA blend color mode: A4, only available on blend foreground input */ } ppa_blend_color_mode_t; +/** + * @brief Enumeration of PPA fill available color mode + */ +typedef enum { + PPA_FILL_COLOR_MODE_ARGB8888 = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888), /*!< PPA fill color mode: ARGB8888 */ + PPA_FILL_COLOR_MODE_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< PPA fill color mode: RGB888 */ + PPA_FILL_COLOR_MODE_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< PPA fill color mode: RGB565 */ +} ppa_fill_color_mode_t; + /** * @brief Enumeration of PPA alpha compositing update mode */ typedef enum { - PPA_ALPHA_NO_CHANGE = 0, /*!< Do not replace alpha value. If input format does not contain alpha info, alpha value 255 will be used. */ - PPA_ALPHA_FIX_VALUE, /*!< Replace the alpha value in received pixel with a new, fixed alpha value */ - PPA_ALPHA_SCALE, /*!< Scale the alpha value in received pixel to be a new alpha value */ - PPA_ALPHA_INVERT, /*!< Invert the alpha value in received pixel */ + PPA_ALPHA_NO_CHANGE = 0, /*!< Do not replace alpha value (A' = A). + If input format does not contain alpha info, alpha value 255 will be used. */ + PPA_ALPHA_FIX_VALUE, /*!< Replace the alpha value in received pixel with a new, fixed alpha value (A' = val) */ + PPA_ALPHA_SCALE, /*!< Scale the alpha value in received pixel to be a new alpha value (A' = (A * val) >> 8). + If input format does not contain alpha info, A' = (255 * val) >> 8. */ + PPA_ALPHA_INVERT, /*!< Invert the alpha value in received pixel (A' = 255 - A). + If input format does not contain alpha info, A' = 0, i.e. a layer with 0% opacity. */ } ppa_alpha_update_mode_t; #ifdef __cplusplus diff --git a/components/hal/ppa_hal.c b/components/hal/ppa_hal.c new file mode 100644 index 0000000000..a2cd073fe8 --- /dev/null +++ b/components/hal/ppa_hal.c @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "hal/ppa_hal.h" +#include "hal/ppa_ll.h" + +void ppa_hal_init(ppa_hal_context_t *hal) +{ + hal->dev = PPA_LL_GET_HW; +} + +void ppa_hal_deinit(ppa_hal_context_t *hal) +{ + hal->dev = NULL; +} From fd1a718f62287950f0849f2c9daaa9d0e8117a7e Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Tue, 9 Apr 2024 22:23:06 +0800 Subject: [PATCH 10/15] feat(ppa): add PPA driver support for ESP32P4 Modified API operation configuration structure Rename invoker to client Support YUV420 color mode for SRM Move PPA srm/blending engine reset ahead of any 2D-DMA channel configurations --- .../esp_driver_ppa/include/driver/ppa.h | 203 ++------ components/esp_driver_ppa/src/ppa.c | 489 +++++++++--------- components/esp_driver_ppa/src/ppa_priv.h | 63 +-- components/esp_hw_support/dma/dma2d.c | 3 + .../dma/include/esp_private/dma2d.h | 4 +- components/hal/esp32p4/include/hal/dma2d_ll.h | 16 +- components/hal/esp32p4/include/hal/ppa_ll.h | 7 +- components/hal/include/hal/dma2d_types.h | 2 +- components/hal/include/hal/ppa_types.h | 2 +- .../soc/esp32p4/include/soc/dma2d_reg.h | 20 +- .../soc/esp32p4/include/soc/dma2d_struct.h | 8 +- 11 files changed, 350 insertions(+), 467 deletions(-) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index ea2f9ca438..8b6f3d4ec0 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -25,9 +25,9 @@ typedef enum { } ppa_operation_t; /** - * @brief Type of PPA invoker handle + * @brief Type of PPA client handle */ -typedef struct ppa_invoker_t *ppa_invoker_handle_t; +typedef struct ppa_client_t *ppa_client_handle_t; /** * @brief Type of PPA transaction handle @@ -44,48 +44,48 @@ typedef struct { /** * @brief Type of PPA event callback * - * @param ppa_invoker PPA invoker handle + * @param ppa_client PPA client handle * @param event_data PPA event data * @param user_data User registered data from calling `ppa_do_xxx` to perform an operation * * @return Whether a task switch is needed after the callback function returns, this is usually due to the callback * wakes up some high priority task. */ -typedef bool (*ppa_event_callback_t)(ppa_invoker_handle_t ppa_invoker, ppa_event_data_t *event_data, void *user_data); +typedef bool (*ppa_event_callback_t)(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data); /** - * @brief A collection of configuration items that used for registering a PPA invoker + * @brief A collection of configuration items that used for registering a PPA client */ typedef struct { - ppa_operation_t oper_type; /*!< The desired PPA operation for the invoker */ + ppa_operation_t oper_type; /*!< The desired PPA operation for the client */ ppa_event_callback_t done_cb; /*!< Callback function to be executed when a PPA transaction finishes */ - uint32_t max_pending_trans_num; /*!< The maximum number of pending transactions for the invoker. + uint32_t max_pending_trans_num; /*!< The maximum number of pending transactions for the client. By default, it will be 1, which is sufficient if all transactions are performed with `PPA_TRANS_MODE_BLOCKING` */ -} ppa_invoker_config_t; +} ppa_client_config_t; /** - * @brief Register a PPA invoker to do a specific PPA operation + * @brief Register a PPA client to do a specific PPA operation * - * @param[in] config Pointer to a collection of configurations for the invoker - * @param[out] ret_invoker Returned invoker handle + * @param[in] config Pointer to a collection of configurations for the client + * @param[out] ret_client Returned client handle * @return - * - ESP_OK: Register the PPA invoker successfully - * - ESP_ERR_INVALID_ARG: Register the PPA invoker failed because of invalid argument - * - ESP_ERR_NO_MEM: Register the PPA invoker failed because out of memory - * - ESP_FAIL: Register the PPA invoker failed because of other error + * - ESP_OK: Register the PPA client successfully + * - ESP_ERR_INVALID_ARG: Register the PPA client failed because of invalid argument + * - ESP_ERR_NO_MEM: Register the PPA client failed because out of memory + * - ESP_FAIL: Register the PPA client failed because of other error */ -esp_err_t ppa_register_invoker(const ppa_invoker_config_t *config, ppa_invoker_handle_t *ret_invoker); +esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_handle_t *ret_client); /** - * @brief Unregister a PPA invoker + * @brief Unregister a PPA client * - * @param[in] ppa_invoker PPA invoker handle, allocated by `ppa_register_invoker` + * @param[in] ppa_client PPA client handle, allocated by `ppa_register_client` * @return - * - ESP_OK: Unregister the PPA invoker successfully - * - ESP_ERR_INVALID_ARG: Unregister the PPA invoker failed because of invalid argument - * - ESP_ERR_INVALID_STATE: Unregister the PPA invoker failed because there are unfinished transactions + * - ESP_OK: Unregister the PPA client successfully + * - ESP_ERR_INVALID_ARG: Unregister the PPA client failed because of invalid argument + * - ESP_ERR_INVALID_STATE: Unregister the PPA client failed because there are unfinished transactions */ -esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker); +esp_err_t ppa_unregister_client(ppa_client_handle_t ppa_client); /** * @brief A collection of configuration items for an input picture and the target block inside the picture @@ -158,77 +158,22 @@ typedef struct { When other alpha modes are selected, this field is not used */ ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ - // uint32_t timeout; void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ } ppa_srm_oper_trans_config_t; -/** - * @brief A collection of configuration items to perform a PPA transaction - */ -typedef struct { - ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ - void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ -} ppa_trans_config_t; - -/** - * @brief A collection of configuration items to perform a PPA SRM operation - */ -typedef struct { - void *in_buffer; /*!< TODO: could be a buffer list, link descriptors together, process a batch - uint32_t batch_num; However, is it necessary? psram can not store too many pictures */ - uint32_t in_pic_w; - uint32_t in_pic_h; - uint32_t in_block_w; - uint32_t in_block_h; - uint32_t in_block_offset_x; - uint32_t in_block_offset_y; - - void *out_buffer; - uint32_t out_buffer_size; - uint32_t out_pic_w; - uint32_t out_pic_h; - uint32_t out_block_offset_x; - uint32_t out_block_offset_y; - - ppa_srm_rotation_angle_t rotation_angle; - float scale_x; - float scale_y; - bool mirror_x; - bool mirror_y; - - struct { - ppa_srm_color_mode_t mode; - color_range_t yuv_range; - color_conv_std_rgb_yuv_t yuv_std; - bool rgb_swap; - bool byte_swap; - ppa_alpha_update_mode_t alpha_update_mode; - uint32_t alpha_value; /*!< When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) - When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) - When other alpha modes are selected, this field is not used */ - } in_color; - - struct { - ppa_srm_color_mode_t mode; - color_range_t yuv_range; - color_conv_std_rgb_yuv_t yuv_std; - } out_color; -} ppa_srm_operation_config_t; - /** * @brief Perform a scaling-rotating-mirroring (SRM) operation to a picture * - * @param[in] ppa_invoker PPA invoker handle that has been registered to do SRM operations - * @param[in] oper_config Pointer to a collection of configurations for the SRM operation, ppa_srm_operation_config_t - * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t + * @param[in] ppa_client PPA client handle that has been registered to do SRM operations + * @param[in] config Pointer to a collection of configurations for the SRM operation transaction, ppa_srm_oper_trans_config_t * * @return * - ESP_OK: Perform a SRM operation successfully * - ESP_ERR_INVALID_ARG: Perform a SRM operation failed because of invalid argument * - ESP_ERR_NO_MEM: Perform a SRM operation failed because out of memory - * - ESP_FAIL: Perform a SRM operation failed because the invoker cannot accept transaction now + * - ESP_FAIL: Perform a SRM operation failed because the client cannot accept transaction now */ -esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa_srm_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); +esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_srm_oper_trans_config_t *config); /** * @brief A collection of configuration items to do a PPA blend operation transaction @@ -269,80 +214,22 @@ typedef struct { bool ck_reverse_bg2fg; /*!< If this bit is set, in color-keying, for the pixel, where its background element is in the color range, but its foreground element is not in the color range, it will output the foreground element instead of the background element */ ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ - // uint32_t timeout; void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ } ppa_blend_oper_trans_config_t; -/** - * @brief A collection of configuration items to perform a PPA blend operation - */ -typedef struct { - void *in_bg_buffer; - uint32_t in_bg_pic_w; - uint32_t in_bg_pic_h; - uint32_t in_bg_block_offset_x; - uint32_t in_bg_block_offset_y; - - void *in_fg_buffer; - uint32_t in_fg_pic_w; - uint32_t in_fg_pic_h; - uint32_t in_fg_block_offset_x; - uint32_t in_fg_block_offset_y; - - uint32_t in_bg_fg_block_w; - uint32_t in_bg_fg_block_h; - - void *out_buffer; - uint32_t out_buffer_size; - uint32_t out_pic_w; - uint32_t out_pic_h; - uint32_t out_block_offset_x; - uint32_t out_block_offset_y; - - struct { - ppa_blend_color_mode_t mode; - bool rgb_swap; - bool byte_swap; - ppa_alpha_update_mode_t alpha_update_mode; - uint32_t alpha_value; - bool ck_en; - uint32_t ck_rgb_low_thres; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ - uint32_t ck_rgb_high_thres; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ - } in_bg_color; - - struct { - ppa_blend_color_mode_t mode; - bool rgb_swap; - bool byte_swap; - ppa_alpha_update_mode_t alpha_update_mode; - uint32_t alpha_value; - uint32_t fix_rgb_val; /*!< When in_fg_color.mode is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground. In RGB888 format (R[23:16], G[15: 8], B[7:0]). */ - bool ck_en; - uint32_t ck_rgb_low_thres; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ - uint32_t ck_rgb_high_thres; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ - } in_fg_color; - - struct { - ppa_blend_color_mode_t mode; - uint32_t ck_rgb_default_val; /*!< In RGB888 format (R[23:16], G[15: 8], B[7:0]) */ - bool ck_reverse_bg2fg; /*!< If this bit is set, in color keying, for the pixel, where its BG element is in the color range, but its FG element is not in the color range, it will output the FG element instead of the BG element */ - } out_color; -} ppa_blend_operation_config_t; - /** * @brief Perform a blending operation to a picture * - * @param[in] ppa_invoker PPA invoker handle that has been registered to do blend operations - * @param[in] oper_config Pointer to a collection of configurations for the blend operation, ppa_blend_operation_config_t - * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t + * @param[in] ppa_client PPA client handle that has been registered to do blend operations + * @param[in] config Pointer to a collection of configurations for the blend operation transaction, ppa_blend_oper_trans_config_t * * @return * - ESP_OK: Perform a blend operation successfully * - ESP_ERR_INVALID_ARG: Perform a blend operation failed because of invalid argument * - ESP_ERR_NO_MEM: Perform a blend operation failed because out of memory - * - ESP_FAIL: Perform a blend operation failed because the invoker cannot accept transaction now + * - ESP_FAIL: Perform a blend operation failed because the client cannot accept transaction now */ -esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); +esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_trans_config_t *config); /** * @brief A collection of configuration items to do a PPA fill operation transaction @@ -355,44 +242,22 @@ typedef struct { uint32_t fill_argb_color; /*!< The color to be filled, in ARGB8888 format ((A[31:24], R[23:16], G[15: 8], B[7:0])) */ ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ - // uint32_t timeout; void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ } ppa_fill_oper_trans_config_t; -/** - * @brief A collection of configuration items to perform a PPA fill operation - */ -typedef struct { - uint32_t fill_block_w; - uint32_t fill_block_h; - uint32_t fill_argb_color; /*!< The color to be filled, in ARGB8888 format ((A[31:24], R[23:16], G[15: 8], B[7:0])) */ - - void *out_buffer; - uint32_t out_buffer_size; - uint32_t out_pic_w; - uint32_t out_pic_h; - uint32_t out_block_offset_x; - uint32_t out_block_offset_y; - - struct { - ppa_blend_color_mode_t mode; - } out_color; -} ppa_fill_operation_config_t; - /** * @brief Perform a filling operation to a picture * - * @param[in] ppa_invoker PPA invoker handle that has been registered to do fill operations - * @param[in] oper_config Pointer to a collection of configurations for the fill operation, ppa_fill_operation_config_t - * @param[in] trans_config Pointer to a collection of configurations for the transaction, ppa_trans_config_t + * @param[in] ppa_client PPA client handle that has been registered to do fill operations + * @param[in] config Pointer to a collection of configurations for the fill operation transaction, ppa_fill_oper_trans_config_t * * @return * - ESP_OK: Perform a fill operation successfully * - ESP_ERR_INVALID_ARG: Perform a fill operation failed because of invalid argument * - ESP_ERR_NO_MEM: Perform a fill operation failed because out of memory - * - ESP_FAIL: Perform a fill operation failed because the invoker cannot accept transaction now + * - ESP_FAIL: Perform a fill operation failed because the client cannot accept transaction now */ -esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation_config_t *oper_config, const ppa_trans_config_t *trans_config); +esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_trans_config_t *config); #ifdef __cplusplus } diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c index 6cf0e524c1..fb304877b1 100644 --- a/components/esp_driver_ppa/src/ppa.c +++ b/components/esp_driver_ppa/src/ppa.c @@ -291,69 +291,69 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) return ret; } -esp_err_t ppa_register_invoker(const ppa_invoker_config_t *config, ppa_invoker_handle_t *ret_invoker) +esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_handle_t *ret_client) { esp_err_t ret = ESP_OK; - ESP_RETURN_ON_FALSE(config && ret_invoker, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(config && ret_client, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(config->oper_type < PPA_OPERATION_NUM, ESP_ERR_INVALID_ARG, TAG, "unknown operation"); - ppa_invoker_t *invoker = (ppa_invoker_t *)heap_caps_calloc(1, sizeof(ppa_invoker_t), PPA_MEM_ALLOC_CAPS); - ESP_RETURN_ON_FALSE(invoker, ESP_ERR_NO_MEM, TAG, "no mem to register invoker"); + ppa_client_t *client = (ppa_client_t *)heap_caps_calloc(1, sizeof(ppa_client_t), PPA_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(client, ESP_ERR_NO_MEM, TAG, "no mem to register client"); uint32_t ring_buf_size = MAX(1, config->max_pending_trans_num); - invoker->trans_elm_ptr_queue = xQueueCreateWithCaps(ring_buf_size, sizeof(uint32_t), PPA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(invoker->trans_elm_ptr_queue && ppa_malloc_transaction(invoker->trans_elm_ptr_queue, ring_buf_size, config->oper_type), + client->trans_elm_ptr_queue = xQueueCreateWithCaps(ring_buf_size, sizeof(uint32_t), PPA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(client->trans_elm_ptr_queue && ppa_malloc_transaction(client->trans_elm_ptr_queue, ring_buf_size, config->oper_type), ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); - invoker->oper_type = config->oper_type; - invoker->done_cb = config->done_cb; - invoker->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; - invoker->in_accepting_trans_state = true; + client->oper_type = config->oper_type; + client->done_cb = config->done_cb; + client->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + client->in_accepting_trans_state = true; if (config->oper_type == PPA_OPERATION_SRM) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_SRM, }; - ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->engine), err, TAG, "unable to acquire SRM engine"); + ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire SRM engine"); } else if (config->oper_type == PPA_OPERATION_BLEND || config->oper_type == PPA_OPERATION_FILL) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_BLEND, }; - ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &invoker->engine), err, TAG, "unable to acquire Blending engine"); + ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire Blending engine"); } - *ret_invoker = invoker; + *ret_client = client; err: if (ret != ESP_OK) { - ppa_unregister_invoker(invoker); + ppa_unregister_client(client); } return ret; } -esp_err_t ppa_unregister_invoker(ppa_invoker_handle_t ppa_invoker) +esp_err_t ppa_unregister_client(ppa_client_handle_t ppa_client) { - ESP_RETURN_ON_FALSE(ppa_invoker, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_client, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); bool do_unregister = false; - portENTER_CRITICAL(&ppa_invoker->spinlock); - if (ppa_invoker->trans_cnt == 0) { - ppa_invoker->in_accepting_trans_state = false; + portENTER_CRITICAL(&ppa_client->spinlock); + if (ppa_client->trans_cnt == 0) { + ppa_client->in_accepting_trans_state = false; do_unregister = true; } - portEXIT_CRITICAL(&ppa_invoker->spinlock); - ESP_RETURN_ON_FALSE(do_unregister, ESP_ERR_INVALID_STATE, TAG, "invoker still has unprocessed trans"); + portEXIT_CRITICAL(&ppa_client->spinlock); + ESP_RETURN_ON_FALSE(do_unregister, ESP_ERR_INVALID_STATE, TAG, "client still has unprocessed trans"); - if (ppa_invoker->engine) { - ppa_engine_release(ppa_invoker->engine); + if (ppa_client->engine) { + ppa_engine_release(ppa_client->engine); } - if (ppa_invoker->trans_elm_ptr_queue) { + if (ppa_client->trans_elm_ptr_queue) { ppa_trans_t *trans_elm = NULL; - while (xQueueReceive(ppa_invoker->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + while (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { ppa_free_transaction(trans_elm); } - vQueueDeleteWithCaps(ppa_invoker->trans_elm_ptr_queue); + vQueueDeleteWithCaps(ppa_client->trans_elm_ptr_queue); } - free(ppa_invoker); + free(ppa_client); return ESP_OK; } @@ -418,16 +418,16 @@ static void ppa_free_transaction(ppa_trans_t *trans_elm) } } -static bool ppa_recycle_transaction(ppa_invoker_handle_t ppa_invoker, ppa_trans_t *trans_elm) +static bool ppa_recycle_transaction(ppa_client_handle_t ppa_client, ppa_trans_t *trans_elm) { - // Reset transaction and send back to invoker's trans_elm_ptr_queue + // Reset transaction and send back to client's trans_elm_ptr_queue // TODO: To be very safe, we shall memset all to 0, and reconnect necessary pointers? BaseType_t HPTaskAwoken; - assert(xQueueSendFromISR(ppa_invoker->trans_elm_ptr_queue, &trans_elm, &HPTaskAwoken)); + assert(xQueueSendFromISR(ppa_client->trans_elm_ptr_queue, &trans_elm, &HPTaskAwoken)); return HPTaskAwoken; } -static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t *ppa_engine_base, ppa_trans_t *trans_elm, ppa_trans_mode_t mode) +static esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_engine_base, ppa_trans_t *trans_elm, ppa_trans_mode_t mode) { esp_err_t ret = ESP_OK; @@ -435,19 +435,19 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t assert((esp_pm_lock_acquire(s_platform.pm_lock) == ESP_OK) && "acquire pm_lock failed"); #endif - portENTER_CRITICAL(&ppa_invoker->spinlock); + portENTER_CRITICAL(&ppa_client->spinlock); // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! - if (ppa_invoker->in_accepting_trans_state) { + if (ppa_client->in_accepting_trans_state) { // Send transaction into PPA engine queue STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry); - ppa_invoker->trans_cnt++; + ppa_client->trans_cnt++; } else { ret = ESP_FAIL; } - portEXIT_CRITICAL(&ppa_invoker->spinlock); + portEXIT_CRITICAL(&ppa_client->spinlock); if (ret != ESP_OK) { - ESP_LOGE(TAG, "The invoker cannot accept transaction now"); + ESP_LOGE(TAG, "The client cannot accept transaction now"); goto err; } @@ -478,9 +478,9 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t assert((esp_pm_lock_release(ppa_engine_base->pm_lock) == ESP_OK) && "release pm_lock failed"); #endif xSemaphoreGive(ppa_engine_base->sem); - portENTER_CRITICAL(&ppa_invoker->spinlock); - ppa_invoker->trans_cnt--; - portEXIT_CRITICAL(&ppa_invoker->spinlock); + portENTER_CRITICAL(&ppa_client->spinlock); + ppa_client->trans_cnt--; + portEXIT_CRITICAL(&ppa_client->spinlock); goto err; } } else { @@ -494,7 +494,7 @@ static esp_err_t ppa_do_operation(ppa_invoker_handle_t ppa_invoker, ppa_engine_t // } xSemaphoreTake(trans_elm->sem, portMAX_DELAY); // Given in the ISR // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) - // ppa_recycle_transaction(ppa_invoker, trans_elm); // TODO: Do we need it to be here or can be at the end of done_cb? + // ppa_recycle_transaction(ppa_client, trans_elm); // TODO: Do we need it to be here or can be at the end of done_cb? } #if CONFIG_PM_ENABLE @@ -510,13 +510,13 @@ static bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_eve bool need_yield = false; BaseType_t HPTaskAwoken; ppa_trans_t *trans_elm = (ppa_trans_t *)user_data; - ppa_invoker_t *invoker = trans_elm->invoker; + ppa_client_t *client = trans_elm->client; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; ppa_engine_t *engine_base = trans_on_picked_desc->ppa_engine; - if (invoker->done_cb) { + if (client->done_cb) { ppa_event_data_t edata = {}; - need_yield |= invoker->done_cb(invoker, &edata, trans_elm->user_data); + need_yield |= client->done_cb(client, &edata, trans_elm->user_data); } ppa_trans_t *next_start_trans = NULL; @@ -543,10 +543,10 @@ static bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_eve // } // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! - portENTER_CRITICAL_ISR(&invoker->spinlock); - need_yield |= ppa_recycle_transaction(invoker, trans_elm); - invoker->trans_cnt--; - portEXIT_CRITICAL_ISR(&invoker->spinlock); + portENTER_CRITICAL_ISR(&client->spinlock); + need_yield |= ppa_recycle_transaction(client, trans_elm); + client->trans_cnt--; + portEXIT_CRITICAL_ISR(&client->spinlock); return need_yield; } @@ -560,34 +560,40 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ ppa_srm_oper_t *srm_trans_desc = (ppa_srm_oper_t *)trans_on_picked_desc->srm_desc; ppa_srm_engine_t *srm_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_srm_engine_t, base); + // Reset SRM engine + ppa_ll_srm_reset(s_platform.hal.dev); + // Get the required 2D-DMA channel handles - uint32_t dma2d_tx_chan_idx = 0; - uint32_t dma2d_rx_chan_idx = 1; - if (dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX) { - dma2d_tx_chan_idx = 1; - dma2d_rx_chan_idx = 0; + dma2d_channel_handle_t dma2d_tx_chan = NULL; + dma2d_channel_handle_t dma2d_rx_chan = NULL; + for (uint32_t i = 0; i < num_chans; i++) { + if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_TX) { + dma2d_tx_chan = dma2d_chans[i].chan; + } + if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_RX) { + dma2d_rx_chan = dma2d_chans[i].chan; + } } - dma2d_channel_handle_t dma2d_tx_chan = dma2d_chans[dma2d_tx_chan_idx].chan; - dma2d_channel_handle_t dma2d_rx_chan = dma2d_chans[dma2d_rx_chan_idx].chan; + assert(dma2d_tx_chan && dma2d_rx_chan); color_space_pixel_format_t in_pixel_format = { - .color_type_id = srm_trans_desc->in_color.mode, + .color_type_id = srm_trans_desc->in.srm_cm, }; // Fill 2D-DMA descriptors - srm_engine->dma_tx_desc->vb_size = srm_trans_desc->in_block_h; - srm_engine->dma_tx_desc->hb_length = srm_trans_desc->in_block_w; + srm_engine->dma_tx_desc->vb_size = srm_trans_desc->in.block_h; + srm_engine->dma_tx_desc->hb_length = srm_trans_desc->in.block_w; srm_engine->dma_tx_desc->err_eof = 0; srm_engine->dma_tx_desc->dma2d_en = 1; srm_engine->dma_tx_desc->suc_eof = 1; srm_engine->dma_tx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - srm_engine->dma_tx_desc->va_size = srm_trans_desc->in_pic_h; - srm_engine->dma_tx_desc->ha_length = srm_trans_desc->in_pic_w; + srm_engine->dma_tx_desc->va_size = srm_trans_desc->in.pic_h; + srm_engine->dma_tx_desc->ha_length = srm_trans_desc->in.pic_w; srm_engine->dma_tx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_pixel_format); - srm_engine->dma_tx_desc->y = srm_trans_desc->in_block_offset_y; - srm_engine->dma_tx_desc->x = srm_trans_desc->in_block_offset_x; + srm_engine->dma_tx_desc->y = srm_trans_desc->in.block_offset_y; + srm_engine->dma_tx_desc->x = srm_trans_desc->in.block_offset_x; srm_engine->dma_tx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - srm_engine->dma_tx_desc->buffer = (void *)srm_trans_desc->in_buffer; + srm_engine->dma_tx_desc->buffer = (void *)srm_trans_desc->in.buffer; srm_engine->dma_tx_desc->next = NULL; // vb_size, hb_length can be any value (auto writeback). However, if vb_size/hb_length is 0, it triggers 2D-DMA DESC_ERROR interrupt, and dma2d driver will automatically ends the transaction. Therefore, to avoid this, we set them to 1. @@ -597,13 +603,13 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ srm_engine->dma_rx_desc->dma2d_en = 1; srm_engine->dma_rx_desc->suc_eof = 1; srm_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - srm_engine->dma_rx_desc->va_size = srm_trans_desc->out_pic_h; - srm_engine->dma_rx_desc->ha_length = srm_trans_desc->out_pic_w; + srm_engine->dma_rx_desc->va_size = srm_trans_desc->out.pic_h; + srm_engine->dma_rx_desc->ha_length = srm_trans_desc->out.pic_w; // pbyte can be any value - srm_engine->dma_rx_desc->y = srm_trans_desc->out_block_offset_y; - srm_engine->dma_rx_desc->x = srm_trans_desc->out_block_offset_x; + srm_engine->dma_rx_desc->y = srm_trans_desc->out.block_offset_y; + srm_engine->dma_rx_desc->x = srm_trans_desc->out.block_offset_x; srm_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - srm_engine->dma_rx_desc->buffer = (void *)srm_trans_desc->out_buffer; + srm_engine->dma_rx_desc->buffer = (void *)srm_trans_desc->out.buffer; srm_engine->dma_rx_desc->next = NULL; esp_cache_msync((void *)srm_engine->dma_tx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); @@ -622,16 +628,19 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, .desc_burst_en = true, .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + // Configure the block size to be received by the SRM engine, which is passed from the 2D-DMA TX channel (i.e. 2D-DMA dscr-port mode) + .dscr_port_block_h = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, + .dscr_port_block_v = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, }; dma2d_set_transfer_ability(dma2d_tx_chan, &dma_transfer_ability); dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); // YUV444 and YUV422 are not supported by PPA module, need to utilize 2D-DMA color space conversion feature to do a conversion - ppa_srm_color_mode_t ppa_in_color_mode = srm_trans_desc->in_color.mode; + ppa_srm_color_mode_t ppa_in_color_mode = srm_trans_desc->in.srm_cm; if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV444) { ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; dma2d_csc_config_t dma_tx_csc = {0}; - if (srm_trans_desc->in_color.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_601; } else { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_709; @@ -640,7 +649,7 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ } else if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV422) { ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; dma2d_csc_config_t dma_tx_csc = {0}; - if (srm_trans_desc->in_color.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_601; } else { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_709; @@ -648,9 +657,7 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); } - // TODO: YUV420 needs to change dev->out_channel[channel].out_dscr_port_blk.out_dscr_port_blk_h/v_chn to 20 - - ppa_srm_color_mode_t ppa_out_color_mode = srm_trans_desc->out_color.mode; + ppa_srm_color_mode_t ppa_out_color_mode = srm_trans_desc->out.srm_cm; if (ppa_out_color_mode == PPA_SRM_COLOR_MODE_YUV444) { ppa_out_color_mode = PPA_SRM_COLOR_MODE_YUV420; dma2d_csc_config_t dma_rx_csc = { @@ -664,8 +671,6 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ }; dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); - ppa_ll_srm_reset(s_platform.hal.dev); - dma2d_set_desc_addr(dma2d_tx_chan, (intptr_t)srm_engine->dma_tx_desc); dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)srm_engine->dma_rx_desc); dma2d_start(dma2d_tx_chan); @@ -673,22 +678,20 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ // Configure PPA SRM engine ppa_ll_srm_set_rx_color_mode(s_platform.hal.dev, ppa_in_color_mode); - if (COLOR_SPACE_TYPE(ppa_in_color_mode) == COLOR_SPACE_YUV) { - ppa_ll_srm_set_rx_yuv_range(s_platform.hal.dev, srm_trans_desc->in_color.yuv_range); - ppa_ll_srm_set_yuv2rgb_std(s_platform.hal.dev, srm_trans_desc->in_color.yuv_std); + if (COLOR_SPACE_TYPE((uint32_t)ppa_in_color_mode) == COLOR_SPACE_YUV) { + ppa_ll_srm_set_rx_yuv_range(s_platform.hal.dev, srm_trans_desc->in.yuv_range); + ppa_ll_srm_set_rx_yuv2rgb_std(s_platform.hal.dev, srm_trans_desc->in.yuv_std); } - ppa_ll_srm_enable_rx_byte_swap(s_platform.hal.dev, srm_trans_desc->in_color.byte_swap); - ppa_ll_srm_enable_rx_rgb_swap(s_platform.hal.dev, srm_trans_desc->in_color.rgb_swap); - ppa_ll_srm_configure_rx_alpha(s_platform.hal.dev, srm_trans_desc->in_color.alpha_update_mode, srm_trans_desc->in_color.alpha_value); + ppa_ll_srm_enable_rx_byte_swap(s_platform.hal.dev, srm_trans_desc->byte_swap); + ppa_ll_srm_enable_rx_rgb_swap(s_platform.hal.dev, srm_trans_desc->rgb_swap); + ppa_ll_srm_configure_rx_alpha(s_platform.hal.dev, srm_trans_desc->alpha_update_mode, srm_trans_desc->alpha_value); ppa_ll_srm_set_tx_color_mode(s_platform.hal.dev, ppa_out_color_mode); - if (COLOR_SPACE_TYPE(ppa_out_color_mode) == COLOR_SPACE_YUV) { - ppa_ll_srm_set_rx_yuv_range(s_platform.hal.dev, srm_trans_desc->out_color.yuv_range); - ppa_ll_srm_set_yuv2rgb_std(s_platform.hal.dev, srm_trans_desc->out_color.yuv_std); + if (COLOR_SPACE_TYPE((uint32_t)ppa_out_color_mode) == COLOR_SPACE_YUV) { + ppa_ll_srm_set_tx_yuv_range(s_platform.hal.dev, srm_trans_desc->out.yuv_range); + ppa_ll_srm_set_tx_rgb2yuv_std(s_platform.hal.dev, srm_trans_desc->out.yuv_std); } - // TODO: sr_macro_bk_ro_bypass - // PPA.sr_byte_order.sr_macro_bk_ro_bypass = 1; ppa_ll_srm_set_rotation_angle(s_platform.hal.dev, srm_trans_desc->rotation_angle); ppa_ll_srm_set_scaling_x(s_platform.hal.dev, srm_trans_desc->scale_x_int, srm_trans_desc->scale_x_frag); ppa_ll_srm_set_scaling_y(s_platform.hal.dev, srm_trans_desc->scale_y_int, srm_trans_desc->scale_y_frag); @@ -701,35 +704,42 @@ static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_ return false; } -esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa_srm_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) +esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_srm_oper_trans_config_t *config) { - ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_invoker->oper_type == PPA_OPERATION_SRM, ESP_ERR_INVALID_ARG, TAG, "invoker is not for SRM operations"); - ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + ESP_RETURN_ON_FALSE(ppa_client && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_client->oper_type == PPA_OPERATION_SRM, ESP_ERR_INVALID_ARG, TAG, "client is not for SRM operations"); + ESP_RETURN_ON_FALSE(config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region - ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); - ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0 && oper_config->out_buffer_size % s_platform.buf_alignment_size == 0, - ESP_ERR_INVALID_ARG, TAG, "out_buffer addr or size not aligned to cache line size"); + ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); + ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % s_platform.buf_alignment_size == 0 && config->out.buffer_size % s_platform.buf_alignment_size == 0, + ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); color_space_pixel_format_t out_pixel_format = { - .color_type_id = oper_config->out_color.mode, + .color_type_id = config->out.srm_cm, }; - uint32_t out_pic_len = oper_config->out_pic_w * oper_config->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - ESP_RETURN_ON_FALSE(out_pic_len <= oper_config->out_buffer_size, ESP_ERR_INVALID_ARG, TAG, "out_pic_w/h mismatch with out_buffer_size"); - ESP_RETURN_ON_FALSE(oper_config->scale_x < (PPA_LL_SRM_SCALING_INT_MAX + 1) && oper_config->scale_x >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX) && - oper_config->scale_y < (PPA_LL_SRM_SCALING_INT_MAX + 1) && oper_config->scale_y >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX), + uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); + ESP_RETURN_ON_FALSE(config->scale_x < (PPA_LL_SRM_SCALING_INT_MAX + 1) && config->scale_x >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX) && + config->scale_y < (PPA_LL_SRM_SCALING_INT_MAX + 1) && config->scale_y >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid scale"); - ESP_RETURN_ON_FALSE((uint32_t)(oper_config->scale_x * oper_config->in_block_w) <= (oper_config->out_pic_w - oper_config->out_block_offset_x) && - (uint32_t)(oper_config->scale_y * oper_config->in_block_h) <= (oper_config->out_pic_h - oper_config->out_block_offset_y), + uint32_t new_block_w = 0; + uint32_t new_block_h = 0; + if (config->rotation_angle == PPA_SRM_ROTATION_ANGLE_0 || config->rotation_angle == PPA_SRM_ROTATION_ANGLE_180) { + new_block_w = (uint32_t)(config->scale_x * config->in.block_w); + new_block_h = (uint32_t)(config->scale_y * config->in.block_h); + } else { + new_block_w = (uint32_t)(config->scale_y * config->in.block_h); + new_block_h = (uint32_t)(config->scale_x * config->in.block_w); + } + ESP_RETURN_ON_FALSE(new_block_w <= (config->out.pic_w - config->out.block_offset_x) && + new_block_h <= (config->out.pic_h - config->out.block_offset_y), ESP_ERR_INVALID_ARG, TAG, "scale does not fit in the out pic"); - if (oper_config->in_color.byte_swap) { - ESP_RETURN_ON_FALSE(oper_config->in_color.mode == PPA_SRM_COLOR_MODE_ARGB8888 || oper_config->in_color.mode == PPA_SRM_COLOR_MODE_RGB565, - ESP_ERR_INVALID_ARG, TAG, "in_color_mode does not support byte_swap"); + if (config->byte_swap) { + PPA_CHECK_CM_SUPPORT_BYTE_SWAP("in.srm", (uint32_t)config->in.srm_cm); } - if (oper_config->in_color.rgb_swap) { - ESP_RETURN_ON_FALSE(COLOR_SPACE_TYPE((uint32_t)oper_config->in_color.mode) == COLOR_SPACE_ARGB || COLOR_SPACE_TYPE((uint32_t)oper_config->in_color.mode) == COLOR_SPACE_RGB, - ESP_ERR_INVALID_ARG, TAG, "in_color_mode does not support rgb_swap"); + if (config->rgb_swap) { + PPA_CHECK_CM_SUPPORT_RGB_SWAP("in.srm", (uint32_t)config->in.srm_cm); } - ESP_RETURN_ON_FALSE(oper_config->in_color.alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_value"); + ESP_RETURN_ON_FALSE(config->alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_value"); // To reduce complexity, rotation_angle, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions // TODO: // YUV420: in desc, ha/hb/va/vb/x/y must be even number @@ -738,53 +748,53 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_invoker_handle_t ppa_invoker, const ppa // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) // Write back in_buffer color_space_pixel_format_t in_pixel_format = { - .color_type_id = oper_config->in_color.mode, + .color_type_id = config->in.srm_cm, }; - uint32_t in_pic_len = oper_config->in_pic_w * oper_config->in_pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; - esp_cache_msync(oper_config->in_buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + uint32_t in_pic_len = config->in.pic_w * config->in.pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; + esp_cache_msync(config->in.buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); // Invalidate out_buffer - esp_cache_msync(oper_config->out_buffer, oper_config->out_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - if (xQueueReceive(ppa_invoker->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; ppa_srm_oper_t *srm_trans_desc = (ppa_srm_oper_t *)trans_on_picked_desc->srm_desc; - memcpy(srm_trans_desc, oper_config, sizeof(ppa_srm_operation_config_t)); + memcpy(srm_trans_desc, config, sizeof(ppa_srm_oper_trans_config_t)); srm_trans_desc->scale_x_int = (uint32_t)srm_trans_desc->scale_x; srm_trans_desc->scale_x_frag = (uint32_t)(srm_trans_desc->scale_x * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; srm_trans_desc->scale_y_int = (uint32_t)srm_trans_desc->scale_y; srm_trans_desc->scale_y_frag = (uint32_t)(srm_trans_desc->scale_y * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; - trans_on_picked_desc->ppa_engine = ppa_invoker->engine; + trans_on_picked_desc->ppa_engine = ppa_client->engine; trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_SRM; dma_trans_desc->tx_channel_num = 1; dma_trans_desc->rx_channel_num = 1; dma_trans_desc->channel_flags = 0; - if (oper_config->in_color.mode == PPA_SRM_COLOR_MODE_YUV422 || oper_config->in_color.mode == PPA_SRM_COLOR_MODE_YUV444) { + if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV422 || config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC; } - if (oper_config->out_color.mode == PPA_SRM_COLOR_MODE_YUV444) { + if (config->out.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC; } dma_trans_desc->specified_tx_channel_mask = 0; dma_trans_desc->specified_rx_channel_mask = 0; - trans_elm->invoker = ppa_invoker; - trans_elm->user_data = trans_config->user_data; - xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semphore before transaction starts + trans_elm->client = ppa_client; + trans_elm->user_data = config->user_data; + xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semaphore before transaction starts - ret = ppa_do_operation(ppa_invoker, ppa_invoker->engine, trans_elm, trans_config->mode); + ret = ppa_do_operation(ppa_client, ppa_client->engine, trans_elm, config->mode); if (ret != ESP_OK) { - ppa_recycle_transaction(ppa_invoker, trans_elm); + ppa_recycle_transaction(ppa_client, trans_elm); } } else { ret = ESP_FAIL; - ESP_LOGE(TAG, "exceed maximum pending transactions for the invoker, consider increase max_pending_trans_num"); + ESP_LOGE(TAG, "exceed maximum pending transactions for the client, consider increase max_pending_trans_num"); } return ret; } @@ -798,6 +808,9 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran ppa_blend_oper_t *blend_trans_desc = (ppa_blend_oper_t *)trans_on_picked_desc->blend_desc; ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); + // Reset blending engine + ppa_ll_blend_reset(s_platform.hal.dev); + // Get the required 2D-DMA channel handles dma2d_channel_handle_t dma2d_tx_bg_chan = NULL; dma2d_channel_handle_t dma2d_tx_fg_chan = NULL; @@ -817,59 +830,59 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran assert(dma2d_tx_bg_chan && dma2d_tx_fg_chan && dma2d_rx_chan); color_space_pixel_format_t in_bg_pixel_format = { - .color_type_id = blend_trans_desc->in_bg_color.mode, + .color_type_id = blend_trans_desc->in_bg.blend_cm, }; color_space_pixel_format_t in_fg_pixel_format = { - .color_type_id = blend_trans_desc->in_fg_color.mode, + .color_type_id = blend_trans_desc->in_fg.blend_cm, }; color_space_pixel_format_t out_pixel_format = { - .color_type_id = blend_trans_desc->out_color.mode, + .color_type_id = blend_trans_desc->out.blend_cm, }; // Fill 2D-DMA descriptors - blend_engine->dma_tx_bg_desc->vb_size = blend_trans_desc->in_bg_fg_block_h; - blend_engine->dma_tx_bg_desc->hb_length = blend_trans_desc->in_bg_fg_block_w; + blend_engine->dma_tx_bg_desc->vb_size = blend_trans_desc->in_bg.block_h; + blend_engine->dma_tx_bg_desc->hb_length = blend_trans_desc->in_bg.block_w; blend_engine->dma_tx_bg_desc->err_eof = 0; blend_engine->dma_tx_bg_desc->dma2d_en = 1; blend_engine->dma_tx_bg_desc->suc_eof = 1; blend_engine->dma_tx_bg_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - blend_engine->dma_tx_bg_desc->va_size = blend_trans_desc->in_bg_pic_h; - blend_engine->dma_tx_bg_desc->ha_length = blend_trans_desc->in_bg_pic_w; + blend_engine->dma_tx_bg_desc->va_size = blend_trans_desc->in_bg.pic_h; + blend_engine->dma_tx_bg_desc->ha_length = blend_trans_desc->in_bg.pic_w; blend_engine->dma_tx_bg_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_bg_pixel_format); - blend_engine->dma_tx_bg_desc->y = blend_trans_desc->in_bg_block_offset_y; - blend_engine->dma_tx_bg_desc->x = blend_trans_desc->in_bg_block_offset_x; + blend_engine->dma_tx_bg_desc->y = blend_trans_desc->in_bg.block_offset_y; + blend_engine->dma_tx_bg_desc->x = blend_trans_desc->in_bg.block_offset_x; blend_engine->dma_tx_bg_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - blend_engine->dma_tx_bg_desc->buffer = (void *)blend_trans_desc->in_bg_buffer; + blend_engine->dma_tx_bg_desc->buffer = (void *)blend_trans_desc->in_bg.buffer; blend_engine->dma_tx_bg_desc->next = NULL; - blend_engine->dma_tx_fg_desc->vb_size = blend_trans_desc->in_bg_fg_block_h; - blend_engine->dma_tx_fg_desc->hb_length = blend_trans_desc->in_bg_fg_block_w; + blend_engine->dma_tx_fg_desc->vb_size = blend_trans_desc->in_fg.block_h; + blend_engine->dma_tx_fg_desc->hb_length = blend_trans_desc->in_fg.block_w; blend_engine->dma_tx_fg_desc->err_eof = 0; blend_engine->dma_tx_fg_desc->dma2d_en = 1; blend_engine->dma_tx_fg_desc->suc_eof = 1; blend_engine->dma_tx_fg_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - blend_engine->dma_tx_fg_desc->va_size = blend_trans_desc->in_fg_pic_h; - blend_engine->dma_tx_fg_desc->ha_length = blend_trans_desc->in_fg_pic_w; + blend_engine->dma_tx_fg_desc->va_size = blend_trans_desc->in_fg.pic_h; + blend_engine->dma_tx_fg_desc->ha_length = blend_trans_desc->in_fg.pic_w; blend_engine->dma_tx_fg_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_fg_pixel_format); - blend_engine->dma_tx_fg_desc->y = blend_trans_desc->in_fg_block_offset_y; - blend_engine->dma_tx_fg_desc->x = blend_trans_desc->in_fg_block_offset_x; + blend_engine->dma_tx_fg_desc->y = blend_trans_desc->in_fg.block_offset_y; + blend_engine->dma_tx_fg_desc->x = blend_trans_desc->in_fg.block_offset_x; blend_engine->dma_tx_fg_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - blend_engine->dma_tx_fg_desc->buffer = (void *)blend_trans_desc->in_fg_buffer; + blend_engine->dma_tx_fg_desc->buffer = (void *)blend_trans_desc->in_fg.buffer; blend_engine->dma_tx_fg_desc->next = NULL; - blend_engine->dma_rx_desc->vb_size = blend_trans_desc->in_bg_fg_block_h; - blend_engine->dma_rx_desc->hb_length = blend_trans_desc->in_bg_fg_block_w; + blend_engine->dma_rx_desc->vb_size = blend_trans_desc->in_fg.block_h; // in_bg.block_h == in_fg.block_h + blend_engine->dma_rx_desc->hb_length = blend_trans_desc->in_fg.block_w; // in_bg.block_w == in_fg.block_w blend_engine->dma_rx_desc->err_eof = 0; blend_engine->dma_rx_desc->dma2d_en = 1; blend_engine->dma_rx_desc->suc_eof = 1; blend_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - blend_engine->dma_rx_desc->va_size = blend_trans_desc->out_pic_h; - blend_engine->dma_rx_desc->ha_length = blend_trans_desc->out_pic_w; + blend_engine->dma_rx_desc->va_size = blend_trans_desc->out.pic_h; + blend_engine->dma_rx_desc->ha_length = blend_trans_desc->out.pic_w; blend_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format); - blend_engine->dma_rx_desc->y = blend_trans_desc->out_block_offset_y; - blend_engine->dma_rx_desc->x = blend_trans_desc->out_block_offset_x; + blend_engine->dma_rx_desc->y = blend_trans_desc->out.block_offset_y; + blend_engine->dma_rx_desc->x = blend_trans_desc->out.block_offset_x; blend_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - blend_engine->dma_rx_desc->buffer = (void *)blend_trans_desc->out_buffer; + blend_engine->dma_rx_desc->buffer = (void *)blend_trans_desc->out.buffer; blend_engine->dma_rx_desc->next = NULL; esp_cache_msync((void *)blend_engine->dma_tx_bg_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); @@ -901,8 +914,6 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran }; dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); - ppa_ll_blend_reset(s_platform.hal.dev); - dma2d_set_desc_addr(dma2d_tx_bg_chan, (intptr_t)blend_engine->dma_tx_bg_desc); dma2d_set_desc_addr(dma2d_tx_fg_chan, (intptr_t)blend_engine->dma_tx_fg_desc); dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)blend_engine->dma_rx_desc); @@ -911,30 +922,30 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran dma2d_start(dma2d_rx_chan); // Configure PPA Blending engine - ppa_ll_blend_set_rx_bg_color_mode(s_platform.hal.dev, blend_trans_desc->in_bg_color.mode); - ppa_ll_blend_enable_rx_bg_byte_swap(s_platform.hal.dev, blend_trans_desc->in_bg_color.byte_swap); - ppa_ll_blend_enable_rx_bg_rgb_swap(s_platform.hal.dev, blend_trans_desc->in_bg_color.rgb_swap); - ppa_ll_blend_configure_rx_bg_alpha(s_platform.hal.dev, blend_trans_desc->in_bg_color.alpha_update_mode, blend_trans_desc->in_bg_color.alpha_value); + ppa_ll_blend_set_rx_bg_color_mode(s_platform.hal.dev, blend_trans_desc->in_bg.blend_cm); + ppa_ll_blend_enable_rx_bg_byte_swap(s_platform.hal.dev, blend_trans_desc->bg_byte_swap); + ppa_ll_blend_enable_rx_bg_rgb_swap(s_platform.hal.dev, blend_trans_desc->bg_rgb_swap); + ppa_ll_blend_configure_rx_bg_alpha(s_platform.hal.dev, blend_trans_desc->bg_alpha_update_mode, blend_trans_desc->bg_alpha_value); - ppa_ll_blend_set_rx_fg_color_mode(s_platform.hal.dev, blend_trans_desc->in_fg_color.mode); - if (COLOR_SPACE_TYPE(blend_trans_desc->in_fg_color.mode) == COLOR_SPACE_ALPHA) { - ppa_ll_blend_set_rx_fg_fix_rgb(s_platform.hal.dev, blend_trans_desc->in_fg_color.fix_rgb_val); + ppa_ll_blend_set_rx_fg_color_mode(s_platform.hal.dev, blend_trans_desc->in_fg.blend_cm); + if (COLOR_SPACE_TYPE((uint32_t)blend_trans_desc->in_fg.blend_cm) == COLOR_SPACE_ALPHA) { + ppa_ll_blend_set_rx_fg_fix_rgb(s_platform.hal.dev, blend_trans_desc->fg_fix_rgb_val); } - ppa_ll_blend_enable_rx_fg_byte_swap(s_platform.hal.dev, blend_trans_desc->in_fg_color.byte_swap); - ppa_ll_blend_enable_rx_fg_rgb_swap(s_platform.hal.dev, blend_trans_desc->in_fg_color.rgb_swap); - ppa_ll_blend_configure_rx_fg_alpha(s_platform.hal.dev, blend_trans_desc->in_fg_color.alpha_update_mode, blend_trans_desc->in_fg_color.alpha_value); + ppa_ll_blend_enable_rx_fg_byte_swap(s_platform.hal.dev, blend_trans_desc->fg_byte_swap); + ppa_ll_blend_enable_rx_fg_rgb_swap(s_platform.hal.dev, blend_trans_desc->fg_rgb_swap); + ppa_ll_blend_configure_rx_fg_alpha(s_platform.hal.dev, blend_trans_desc->fg_alpha_update_mode, blend_trans_desc->fg_alpha_value); - ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, blend_trans_desc->out_color.mode); + ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, blend_trans_desc->out.blend_cm); // Color keying ppa_ll_blend_configure_rx_bg_ck_range(s_platform.hal.dev, - blend_trans_desc->in_bg_color.ck_en ? blend_trans_desc->in_bg_color.ck_rgb_low_thres : 0xFFFFFF, - blend_trans_desc->in_bg_color.ck_en ? blend_trans_desc->in_bg_color.ck_rgb_high_thres : 0); + blend_trans_desc->bg_ck_en ? blend_trans_desc->bg_ck_rgb_low_thres : 0xFFFFFF, + blend_trans_desc->bg_ck_en ? blend_trans_desc->bg_ck_rgb_high_thres : 0); ppa_ll_blend_configure_rx_fg_ck_range(s_platform.hal.dev, - blend_trans_desc->in_fg_color.ck_en ? blend_trans_desc->in_fg_color.ck_rgb_low_thres : 0xFFFFFF, - blend_trans_desc->in_fg_color.ck_en ? blend_trans_desc->in_fg_color.ck_rgb_high_thres : 0); - ppa_ll_blend_set_ck_default_rgb(s_platform.hal.dev, (blend_trans_desc->in_bg_color.ck_en && blend_trans_desc->in_fg_color.ck_en) ? blend_trans_desc->out_color.ck_rgb_default_val : 0); - ppa_ll_blend_enable_ck_fg_bg_reverse(s_platform.hal.dev, blend_trans_desc->out_color.ck_reverse_bg2fg); + blend_trans_desc->fg_ck_en ? blend_trans_desc->fg_ck_rgb_low_thres : 0xFFFFFF, + blend_trans_desc->fg_ck_en ? blend_trans_desc->fg_ck_rgb_high_thres : 0); + ppa_ll_blend_set_ck_default_rgb(s_platform.hal.dev, (blend_trans_desc->bg_ck_en && blend_trans_desc->fg_ck_en) ? blend_trans_desc->ck_rgb_default_val : 0); + ppa_ll_blend_enable_ck_fg_bg_reverse(s_platform.hal.dev, blend_trans_desc->ck_reverse_bg2fg); ppa_ll_blend_start(s_platform.hal.dev, PPA_LL_BLEND_TRANS_MODE_BLEND); @@ -942,74 +953,71 @@ static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_tran return false; } -esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) +esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_trans_config_t *config) { - ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_invoker->oper_type == PPA_OPERATION_BLEND, ESP_ERR_INVALID_ARG, TAG, "invoker is not for blend operations"); - ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + ESP_RETURN_ON_FALSE(ppa_client && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_client->oper_type == PPA_OPERATION_BLEND, ESP_ERR_INVALID_ARG, TAG, "client is not for blend operations"); + ESP_RETURN_ON_FALSE(config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region - ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); - ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0 && oper_config->out_buffer_size % s_platform.buf_alignment_size == 0, - ESP_ERR_INVALID_ARG, TAG, "out_buffer addr or size not aligned to cache line size"); + ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); + ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % s_platform.buf_alignment_size == 0 && config->out.buffer_size % s_platform.buf_alignment_size == 0, + ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); color_space_pixel_format_t out_pixel_format = { - .color_type_id = oper_config->out_color.mode, + .color_type_id = config->out.blend_cm, }; - uint32_t out_pic_len = oper_config->out_pic_w * oper_config->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - ESP_RETURN_ON_FALSE(out_pic_len <= oper_config->out_buffer_size, ESP_ERR_INVALID_ARG, TAG, "out_pic_w/h mismatch with out_buffer_size"); - if (oper_config->in_bg_color.byte_swap) { - ESP_RETURN_ON_FALSE(oper_config->in_bg_color.mode == PPA_BLEND_COLOR_MODE_ARGB8888 || oper_config->in_bg_color.mode == PPA_BLEND_COLOR_MODE_RGB565, - ESP_ERR_INVALID_ARG, TAG, "in_bg_color_mode does not support byte_swap"); + uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); + ESP_RETURN_ON_FALSE(config->in_bg.block_w == config->in_fg.block_w && config->in_bg.block_h == config->in_fg.block_h, + ESP_ERR_INVALID_ARG, TAG, "in_bg.block_w/h must be equal to in_fg.block_w/h"); + if (config->bg_byte_swap) { + PPA_CHECK_CM_SUPPORT_BYTE_SWAP("in_bg.blend", (uint32_t)config->in_bg.blend_cm); } - if (oper_config->in_bg_color.rgb_swap) { - ESP_RETURN_ON_FALSE(COLOR_SPACE_TYPE((uint32_t)oper_config->in_bg_color.mode) == COLOR_SPACE_ARGB || COLOR_SPACE_TYPE((uint32_t)oper_config->in_bg_color.mode) == COLOR_SPACE_RGB, - ESP_ERR_INVALID_ARG, TAG, "in_bg_color_mode does not support rgb_swap"); + if (config->bg_rgb_swap) { + PPA_CHECK_CM_SUPPORT_RGB_SWAP("in_bg.blend", (uint32_t)config->in_bg.blend_cm); } - if (oper_config->in_fg_color.byte_swap) { - ESP_RETURN_ON_FALSE(oper_config->in_fg_color.mode == PPA_BLEND_COLOR_MODE_ARGB8888 || oper_config->in_fg_color.mode == PPA_BLEND_COLOR_MODE_RGB565, - ESP_ERR_INVALID_ARG, TAG, "in_fg_color_mode does not support byte_swap"); + if (config->fg_byte_swap) { + PPA_CHECK_CM_SUPPORT_BYTE_SWAP("in_fg.blend", (uint32_t)config->in_fg.blend_cm); } - if (oper_config->in_fg_color.rgb_swap) { - ESP_RETURN_ON_FALSE(COLOR_SPACE_TYPE((uint32_t)oper_config->in_fg_color.mode) == COLOR_SPACE_ARGB || COLOR_SPACE_TYPE((uint32_t)oper_config->in_fg_color.mode) == COLOR_SPACE_RGB, - ESP_ERR_INVALID_ARG, TAG, "in_fg_color_mode does not support rgb_swap"); + if (config->fg_rgb_swap) { + PPA_CHECK_CM_SUPPORT_RGB_SWAP("in_fg.blend", (uint32_t)config->in_fg.blend_cm); } - ESP_RETURN_ON_FALSE(oper_config->in_bg_color.alpha_value <= 0xFF && oper_config->in_fg_color.alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_value"); - if (oper_config->in_bg_color.mode == PPA_BLEND_COLOR_MODE_L4) { - ESP_RETURN_ON_FALSE(oper_config->in_bg_fg_block_h % 2 == 0 && oper_config->in_bg_block_offset_x % 2 == 0, - ESP_ERR_INVALID_ARG, TAG, "bg_block_h and bg_block_offset_x must be even"); + ESP_RETURN_ON_FALSE(config->bg_alpha_value <= 0xFF && config->fg_alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid bg/fg_alpha_value"); + if (config->in_bg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { + ESP_RETURN_ON_FALSE(config->in_bg.block_w % 2 == 0 && config->in_bg.block_offset_x % 2 == 0, + ESP_ERR_INVALID_ARG, TAG, "in_bg.block_w and in_bg.block_offset_x must be even"); } - if (oper_config->in_fg_color.mode == PPA_BLEND_COLOR_MODE_A4 || oper_config->in_fg_color.mode == PPA_BLEND_COLOR_MODE_L4) { - ESP_RETURN_ON_FALSE(oper_config->in_bg_fg_block_h % 2 == 0 && oper_config->in_fg_block_offset_x % 2 == 0, - ESP_ERR_INVALID_ARG, TAG, "fg_block_h and fg_block_offset_x must be even"); + if (config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_A4 || config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { + ESP_RETURN_ON_FALSE(config->in_fg.block_w % 2 == 0 && config->in_fg.block_offset_x % 2 == 0, + ESP_ERR_INVALID_ARG, TAG, "in_fg.block_w and in_fg.block_offset_x must be even"); } // To reduce complexity, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions - // TODO: Check in_bg.block_w == in_fg.block_w && in_bg.block_h == in_fg.block_h // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) // Write back in_bg_buffer, in_fg_buffer color_space_pixel_format_t in_bg_pixel_format = { - .color_type_id = oper_config->in_bg_color.mode, + .color_type_id = config->in_bg.blend_cm, }; - uint32_t in_bg_pic_len = oper_config->in_bg_pic_w * oper_config->in_bg_pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; - esp_cache_msync(oper_config->in_bg_buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + uint32_t in_bg_pic_len = config->in_bg.pic_w * config->in_bg.pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; + esp_cache_msync(config->in_bg.buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); color_space_pixel_format_t in_fg_pixel_format = { - .color_type_id = oper_config->in_fg_color.mode, + .color_type_id = config->in_fg.blend_cm, }; - uint32_t in_fg_pic_len = oper_config->in_fg_pic_w * oper_config->in_fg_pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; - esp_cache_msync(oper_config->in_fg_buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + uint32_t in_fg_pic_len = config->in_fg.pic_w * config->in_fg.pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; + esp_cache_msync(config->in_fg.buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); // Invalidate out_buffer - esp_cache_msync(oper_config->out_buffer, oper_config->out_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - if (xQueueReceive(ppa_invoker->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; ppa_blend_oper_t *blend_trans_desc = (ppa_blend_oper_t *)trans_on_picked_desc->blend_desc; - memcpy(blend_trans_desc, oper_config, sizeof(ppa_blend_operation_config_t)); + memcpy(blend_trans_desc, config, sizeof(ppa_blend_oper_trans_config_t)); - trans_on_picked_desc->ppa_engine = ppa_invoker->engine; + trans_on_picked_desc->ppa_engine = ppa_client->engine; trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; dma_trans_desc->tx_channel_num = 2; @@ -1018,17 +1026,17 @@ esp_err_t ppa_do_blend(ppa_invoker_handle_t ppa_invoker, const ppa_blend_operati dma_trans_desc->specified_tx_channel_mask = 0; dma_trans_desc->specified_rx_channel_mask = 0; - trans_elm->invoker = ppa_invoker; - trans_elm->user_data = trans_config->user_data; - xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semphore before transaction starts + trans_elm->client = ppa_client; + trans_elm->user_data = config->user_data; + xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semaphore before transaction starts - ret = ppa_do_operation(ppa_invoker, ppa_invoker->engine, trans_elm, trans_config->mode); + ret = ppa_do_operation(ppa_client, ppa_client->engine, trans_elm, config->mode); if (ret != ESP_OK) { - ppa_recycle_transaction(ppa_invoker, trans_elm); + ppa_recycle_transaction(ppa_client, trans_elm); } } else { ret = ESP_FAIL; - ESP_LOGE(TAG, "exceed maximum pending transactions for the invoker, consider increase max_pending_trans_num"); + ESP_LOGE(TAG, "exceed maximum pending transactions for the client, consider increase max_pending_trans_num"); } return ret; } @@ -1042,12 +1050,15 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans ppa_fill_oper_t *fill_trans_desc = (ppa_fill_oper_t *)trans_on_picked_desc->fill_desc; ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); + // Reset blending engine + ppa_ll_blend_reset(s_platform.hal.dev); + // Get the required 2D-DMA channel handles assert(dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX); dma2d_channel_handle_t dma2d_rx_chan = dma2d_chans[0].chan; color_space_pixel_format_t out_pixel_format = { - .color_type_id = fill_trans_desc->out_color.mode, + .color_type_id = fill_trans_desc->out.fill_cm, }; // Fill 2D-DMA descriptors @@ -1057,13 +1068,13 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans blend_engine->dma_rx_desc->dma2d_en = 1; blend_engine->dma_rx_desc->suc_eof = 1; blend_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - blend_engine->dma_rx_desc->va_size = fill_trans_desc->out_pic_h; - blend_engine->dma_rx_desc->ha_length = fill_trans_desc->out_pic_w; + blend_engine->dma_rx_desc->va_size = fill_trans_desc->out.pic_h; + blend_engine->dma_rx_desc->ha_length = fill_trans_desc->out.pic_w; blend_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format); - blend_engine->dma_rx_desc->y = fill_trans_desc->out_block_offset_y; - blend_engine->dma_rx_desc->x = fill_trans_desc->out_block_offset_x; + blend_engine->dma_rx_desc->y = fill_trans_desc->out.block_offset_y; + blend_engine->dma_rx_desc->x = fill_trans_desc->out.block_offset_x; blend_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - blend_engine->dma_rx_desc->buffer = (void *)fill_trans_desc->out_buffer; + blend_engine->dma_rx_desc->buffer = (void *)fill_trans_desc->out.buffer; blend_engine->dma_rx_desc->next = NULL; esp_cache_msync((void *)blend_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); @@ -1087,14 +1098,12 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans }; dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); - ppa_ll_blend_reset(s_platform.hal.dev); - dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)blend_engine->dma_rx_desc); dma2d_start(dma2d_rx_chan); // Configure PPA Blending engine ppa_ll_blend_configure_filling_block(s_platform.hal.dev, fill_trans_desc->fill_argb_color, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h); - ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, fill_trans_desc->out_color.mode); + ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, fill_trans_desc->out.fill_cm); ppa_ll_blend_start(s_platform.hal.dev, PPA_LL_BLEND_TRANS_MODE_FILL); @@ -1102,36 +1111,36 @@ static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans return false; } -esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation_config_t *oper_config, const ppa_trans_config_t *trans_config) +esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_trans_config_t *config) { - ESP_RETURN_ON_FALSE(ppa_invoker && oper_config && trans_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_invoker->oper_type == PPA_OPERATION_FILL, ESP_ERR_INVALID_ARG, TAG, "invoker is not for fill operations"); - ESP_RETURN_ON_FALSE(trans_config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + ESP_RETURN_ON_FALSE(ppa_client && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_client->oper_type == PPA_OPERATION_FILL, ESP_ERR_INVALID_ARG, TAG, "client is not for fill operations"); + ESP_RETURN_ON_FALSE(config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); // out_buffer ptr cannot in flash region - ESP_RETURN_ON_FALSE(esp_ptr_internal(oper_config->out_buffer) || esp_ptr_external_ram(oper_config->out_buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out_buffer addr"); - ESP_RETURN_ON_FALSE((uintptr_t)oper_config->out_buffer % s_platform.buf_alignment_size == 0 && oper_config->out_buffer_size % s_platform.buf_alignment_size == 0, - ESP_ERR_INVALID_ARG, TAG, "out_buffer addr or size not aligned to cache line size"); + ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); + ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % s_platform.buf_alignment_size == 0 && config->out.buffer_size % s_platform.buf_alignment_size == 0, + ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); color_space_pixel_format_t out_pixel_format = { - .color_type_id = oper_config->out_color.mode, + .color_type_id = config->out.fill_cm, }; - uint32_t out_pic_len = oper_config->out_pic_w * oper_config->out_pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - ESP_RETURN_ON_FALSE(out_pic_len <= oper_config->out_buffer_size, ESP_ERR_INVALID_ARG, TAG, "out_pic_w/h mismatch with out_buffer_size"); + uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); // To reduce complexity, color_mode, fill_block_w/h correctness are checked in their corresponding LL functions // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - esp_cache_msync(oper_config->out_buffer, oper_config->out_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - if (xQueueReceive(ppa_invoker->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; ppa_fill_oper_t *fill_trans_desc = (ppa_fill_oper_t *)trans_on_picked_desc->fill_desc; - memcpy(fill_trans_desc, oper_config, sizeof(ppa_fill_operation_config_t)); + memcpy(fill_trans_desc, config, sizeof(ppa_fill_oper_trans_config_t)); - trans_on_picked_desc->ppa_engine = ppa_invoker->engine; + trans_on_picked_desc->ppa_engine = ppa_client->engine; trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; dma_trans_desc->tx_channel_num = 0; @@ -1140,17 +1149,17 @@ esp_err_t ppa_do_fill(ppa_invoker_handle_t ppa_invoker, const ppa_fill_operation dma_trans_desc->specified_tx_channel_mask = 0; dma_trans_desc->specified_rx_channel_mask = 0; - trans_elm->invoker = ppa_invoker; - trans_elm->user_data = trans_config->user_data; - xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semphore before transaction starts + trans_elm->client = ppa_client; + trans_elm->user_data = config->user_data; + xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semaphore before transaction starts - ret = ppa_do_operation(ppa_invoker, ppa_invoker->engine, trans_elm, trans_config->mode); + ret = ppa_do_operation(ppa_client, ppa_client->engine, trans_elm, config->mode); if (ret != ESP_OK) { - ppa_recycle_transaction(ppa_invoker, trans_elm); + ppa_recycle_transaction(ppa_client, trans_elm); } } else { ret = ESP_FAIL; - ESP_LOGE(TAG, "exceed maximum pending transactions for the invoker, consider increase max_pending_trans_num"); + ESP_LOGE(TAG, "exceed maximum pending transactions for the client, consider increase max_pending_trans_num"); } return ret; } diff --git a/components/esp_driver_ppa/src/ppa_priv.h b/components/esp_driver_ppa/src/ppa_priv.h index 7e11fae305..3e1a003976 100644 --- a/components/esp_driver_ppa/src/ppa_priv.h +++ b/components/esp_driver_ppa/src/ppa_priv.h @@ -25,6 +25,14 @@ extern "C" { #define PPA_PM_LOCK_NAME_LEN_MAX 16 +#define PPA_CHECK_CM_SUPPORT_BYTE_SWAP(str, color_type_id) \ + ESP_RETURN_ON_FALSE(color_type_id == COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888) || color_type_id == COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), \ + ESP_ERR_INVALID_ARG, TAG, str "_cm does not support byte_swap"); + +#define PPA_CHECK_CM_SUPPORT_RGB_SWAP(str, color_type_id) \ + ESP_RETURN_ON_FALSE(COLOR_SPACE_TYPE(color_type_id) == COLOR_SPACE_ARGB || COLOR_SPACE_TYPE(color_type_id) == COLOR_SPACE_RGB, \ + ESP_ERR_INVALID_ARG, TAG, str "_cm does not support rgb_swap"); + /******************************** ENGINE *************************************/ // PPA module contains SRM engine and Blending engine @@ -59,60 +67,43 @@ typedef struct { /******************************** CLIENT *************************************/ -typedef struct ppa_invoker_t ppa_invoker_t; +typedef struct ppa_client_t ppa_client_t; -struct ppa_invoker_t { - ppa_operation_t oper_type; // The PPA operation type that the invoker wants to do in speciality +struct ppa_client_t { + ppa_operation_t oper_type; // The PPA operation type that the client wants to do in speciality ppa_engine_t *engine; // Pointer to the PPA engine that in charge of performing the PPA operation uint32_t trans_cnt; // Number of pending PPA transactions portMUX_TYPE spinlock; // Client level spinlock - bool in_accepting_trans_state; // Indicates whether the invoker can accept new PPA transaction requests now + bool in_accepting_trans_state; // Indicates whether the client can accept new PPA transaction requests now ppa_event_callback_t done_cb; // Transaction done callback QueueHandle_t trans_elm_ptr_queue; // Queue that contains the pointers to the allocated memory to save the transaction contexts }; /****************************** OPERATION ************************************/ -// The elements in this structure listed first are identical to the elements in structure `ppa_blend_operation_config_t` +// The elements in this structure listed first are identical to the elements in structure `ppa_srm_oper_trans_config_t` // With adding a few extra elements in the end // This allows memcpy typedef struct { - void *in_buffer; - uint32_t in_pic_w; - uint32_t in_pic_h; - uint32_t in_block_w; - uint32_t in_block_h; - uint32_t in_block_offset_x; - uint32_t in_block_offset_y; - - void *out_buffer; - uint32_t out_buffer_size; - uint32_t out_pic_w; - uint32_t out_pic_h; - uint32_t out_block_offset_x; - uint32_t out_block_offset_y; + ppa_in_pic_blk_config_t in; + ppa_out_pic_blk_config_t out; + // scale-rotate-mirror manipulation ppa_srm_rotation_angle_t rotation_angle; float scale_x; float scale_y; bool mirror_x; bool mirror_y; - struct { - ppa_srm_color_mode_t mode; - color_range_t yuv_range; - color_conv_std_rgb_yuv_t yuv_std; - bool rgb_swap; - bool byte_swap; - ppa_alpha_update_mode_t alpha_update_mode; - uint32_t alpha_value; - } in_color; + // input data manipulation + bool rgb_swap; + bool byte_swap; + ppa_alpha_update_mode_t alpha_update_mode; + uint32_t alpha_value; - struct { - ppa_srm_color_mode_t mode; - color_range_t yuv_range; - color_conv_std_rgb_yuv_t yuv_std; - } out_color; + ppa_trans_mode_t mode; + // uint32_t timeout; + void *user_data; uint32_t scale_x_int; uint32_t scale_x_frag; @@ -120,9 +111,9 @@ typedef struct { uint32_t scale_y_frag; } ppa_srm_oper_t; -typedef ppa_blend_operation_config_t ppa_blend_oper_t; +typedef ppa_blend_oper_trans_config_t ppa_blend_oper_t; -typedef ppa_fill_operation_config_t ppa_fill_oper_t; +typedef ppa_fill_oper_trans_config_t ppa_fill_oper_t; /***************************** TRANSACTION ***********************************/ @@ -132,7 +123,7 @@ typedef struct ppa_trans_s { dma2d_trans_config_t *trans_desc; // Pointer to the structure containing the configurations for a 2D-DMA transaction dma2d_trans_t *dma_trans_placeholder; // Pointer to the memory to store the 2D-DMA transaction context SemaphoreHandle_t sem; // Semaphore to block when the transaction has not finished - ppa_invoker_t *invoker; // Pointer to the invoker who requested the transaction + ppa_client_t *client; // Pointer to the client who requested the transaction void *user_data; // User registered event data (per transaction) } ppa_trans_t; diff --git a/components/esp_hw_support/dma/dma2d.c b/components/esp_hw_support/dma/dma2d.c index 1145f35575..6a16a78118 100644 --- a/components/esp_hw_support/dma/dma2d.c +++ b/components/esp_hw_support/dma/dma2d.c @@ -828,6 +828,9 @@ esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dm dma2d_ll_tx_enable_descriptor_burst(group->hal.dev, channel_id, ability->desc_burst_en); dma2d_ll_tx_set_data_burst_length(group->hal.dev, channel_id, ability->data_burst_length); dma2d_ll_tx_set_macro_block_size(group->hal.dev, channel_id, ability->mb_size); + if (ability->dscr_port_block_h && ability->dscr_port_block_v) { + dma2d_ll_tx_set_dscr_port_block_size(group->hal.dev, channel_id, ability->dscr_port_block_h, ability->dscr_port_block_v); + } } else { dma2d_ll_rx_enable_descriptor_burst(group->hal.dev, channel_id, ability->desc_burst_en); dma2d_ll_rx_set_data_burst_length(group->hal.dev, channel_id, ability->data_burst_length); diff --git a/components/esp_hw_support/dma/include/esp_private/dma2d.h b/components/esp_hw_support/dma/include/esp_private/dma2d.h index fb983c1679..8034c8353e 100644 --- a/components/esp_hw_support/dma/include/esp_private/dma2d.h +++ b/components/esp_hw_support/dma/include/esp_private/dma2d.h @@ -220,7 +220,7 @@ typedef struct { esp_err_t dma2d_apply_strategy(dma2d_channel_handle_t dma2d_chan, const dma2d_strategy_config_t *config); /** - * @brief A collection of transfer ability items that each 2D-DMA channel could apply to improve transfer efficiency + * @brief A collection of transfer ability items that each 2D-DMA channel could apply to improve transfer efficiency or to ensure desired transfer block size * * @note The 2D-DMA driver has no knowledge about the DMA buffer (address and size) used by upper layer. * So it's the responsibility of the **upper layer** to take care of the buffer address and size. @@ -231,6 +231,8 @@ typedef struct { bool desc_burst_en; /*!< If set / clear, DMA channel enables / disables burst reading descriptor link */ dma2d_data_burst_length_t data_burst_length; /*!< Configure the DMA channel burst reading data length */ dma2d_macro_block_size_t mb_size; /*!< Configure the DMA channel macro block size (only useful in DMA2D_DESCRIPTOR_BLOCK_RW_MODE_MULTIPLE mode) */ + uint32_t dscr_port_block_h; /*!< Configure the DMA TX channel horizontal width of the block in dscr-port mode (unit: pixel) */ + uint32_t dscr_port_block_v; /*!< Configure the DMA TX channel vertical height of the block in dscr-port mode (unit: pixel) */ } dma2d_transfer_ability_t; /** diff --git a/components/hal/esp32p4/include/hal/dma2d_ll.h b/components/hal/esp32p4/include/hal/dma2d_ll.h index 56209130da..8ec6f91ea4 100644 --- a/components/hal/esp32p4/include/hal/dma2d_ll.h +++ b/components/hal/esp32p4/include/hal/dma2d_ll.h @@ -55,7 +55,7 @@ extern "C" { #define DMA2D_LL_EVENT_TX_EOF (1<<1) #define DMA2D_LL_EVENT_TX_DONE (1<<0) -// Bit masks that are used to indicate availbility of some sub-features in the channels +// Bit masks that are used to indicate availability of some sub-features in the channels #define DMA2D_LL_TX_CHANNEL_SUPPORT_RO_MASK (0U | BIT0) // TX channels that support reorder feature #define DMA2D_LL_TX_CHANNEL_SUPPORT_CSC_MASK (0U | BIT0 | BIT1 | BIT2) // TX channels that support color space conversion feature @@ -479,7 +479,7 @@ static inline void dma2d_ll_rx_enable_reorder(dma2d_dev_t *dev, uint32_t channel reg->in_reorder_en_chn = enable; } -// COLOR SPACE CONVERTION FUNCTION +// COLOR SPACE CONVERSION FUNCTION /** * @brief Configure 2D-DMA RX channel color space conversion parameters @@ -825,6 +825,16 @@ static inline void dma2d_ll_tx_enable_dscr_port(dma2d_dev_t *dev, uint32_t chann dev->out_channel[channel].out_conf0.out_dscr_port_en_chn = enable; } +/** + * @brief Set 2D-DMA TX channel block size in dscr-port mode + */ +__attribute__((always_inline)) +static inline void dma2d_ll_tx_set_dscr_port_block_size(dma2d_dev_t *dev, uint32_t channel, uint32_t blk_h, uint32_t blk_v) +{ + dev->out_channel[channel].out_dscr_port_blk.out_dscr_port_blk_h_chn = blk_h; + dev->out_channel[channel].out_dscr_port_blk.out_dscr_port_blk_v_chn = blk_v; +} + /** * @brief Select 2D-DMA TX channel macro block size */ @@ -973,7 +983,7 @@ static inline void dma2d_ll_tx_enable_reorder(dma2d_dev_t *dev, uint32_t channel dev->out_channel[channel].out_conf0.out_reorder_en_chn = enable; } -// COLOR SPACE CONVERTION FUNCTION +// COLOR SPACE CONVERSION FUNCTION /** * @brief Configure 2D-DMA TX channel color space conversion parameters diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index 54b07c6c63..60ab9766b1 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -27,6 +27,9 @@ extern "C" { #define PPA_LL_SRM_SCALING_INT_MAX PPA_SR_SCAL_X_INT_V #define PPA_LL_SRM_SCALING_FRAG_MAX PPA_SR_SCAL_X_FRAG_V +#define PPA_LL_SRM_DEFAULT_BLOCK_SIZE 18 // 18 x 18 block size +#define PPA_LL_SRM_YUV420_BLOCK_SIZE 20 // 20 x 20 block size + /** * @brief Enumeration of PPA blending mode */ @@ -229,7 +232,7 @@ static inline void ppa_ll_srm_set_tx_color_mode(ppa_dev_t *dev, ppa_srm_color_mo * @param dev Peripheral instance address * @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t */ -static inline void ppa_ll_srm_set_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) +static inline void ppa_ll_srm_set_rx_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) { switch (std) { case COLOR_CONV_STD_RGB_YUV_BT601: @@ -250,7 +253,7 @@ static inline void ppa_ll_srm_set_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_rgb * @param dev Peripheral instance address * @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t */ -static inline void ppa_ll_srm_set_rgb2yuv_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) +static inline void ppa_ll_srm_set_tx_rgb2yuv_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) { switch (std) { case COLOR_CONV_STD_RGB_YUV_BT601: diff --git a/components/hal/include/hal/dma2d_types.h b/components/hal/include/hal/dma2d_types.h index c88a6773a4..6ea7e76a4c 100644 --- a/components/hal/include/hal/dma2d_types.h +++ b/components/hal/include/hal/dma2d_types.h @@ -31,7 +31,7 @@ struct dma2d_descriptor_align8_s { uint32_t dma2d_en : 1; /*!< Whether to enable 2D functionality */ uint32_t suc_eof : 1; /*!< Whether the descriptor is the last one in the link */ uint32_t owner : 1; /*!< Who is allowed to access the buffer that this descriptor points to, select DMA2D_DESCRIPTOR_BUFFER_OWNER_CPU or DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA - When owner is chosen to be DMA, after DMA finishs with the descriptor, it will clear this bit + When owner is chosen to be DMA, after DMA finishes with the descriptor, it will clear this bit For data transfer, the bit won't be cleared unless DMA2D_OUT_AUTO_WRBACK is enabled */ }; /*!< Descriptor Word 0 */ struct { diff --git a/components/hal/include/hal/ppa_types.h b/components/hal/include/hal/ppa_types.h index 2aa0c42106..8bbe0fd5c9 100644 --- a/components/hal/include/hal/ppa_types.h +++ b/components/hal/include/hal/ppa_types.h @@ -42,7 +42,7 @@ typedef enum { PPA_SRM_COLOR_MODE_YUV444 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV444), /*!< PPA SRM color mode: YUV444 (limited range only)*/ PPA_SRM_COLOR_MODE_YUV422 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV422), /*!< PPA SRM color mode: YUV422 (input only, limited range only) */ // YUV444 and YUV422 not supported by PPA hardware, but seems like we can use 2D-DMA to do conversion before sending into and after coming out from the PPA module - // If in_pic is YUV444/422, then TX DMA channnel could do DMA2D_CSC_TX_YUV444/422_TO_RGB888_601/709, so PPA in_color_mode is RGB888 + // If in_pic is YUV444/422, then TX DMA channel could do DMA2D_CSC_TX_YUV444/422_TO_RGB888_601/709, so PPA in_color_mode is RGB888 // If out_pic is YUV444, then RX DMA channel could do DMA2D_CSC_RX_YUV420_TO_YUV444, so PPA out_color_mode is YUV420 } ppa_srm_color_mode_t; diff --git a/components/soc/esp32p4/include/soc/dma2d_reg.h b/components/soc/esp32p4/include/soc/dma2d_reg.h index 5a9acb85ae..2885106a17 100644 --- a/components/soc/esp32p4/include/soc/dma2d_reg.h +++ b/components/soc/esp32p4/include/soc/dma2d_reg.h @@ -899,7 +899,7 @@ extern "C" { #define DMA2D_OUT_COLOR_OUTPUT_SEL_CH0_V 0x00000003U #define DMA2D_OUT_COLOR_OUTPUT_SEL_CH0_S 0 /** DMA2D_OUT_COLOR_3B_PROC_EN_CH0 : R/W; bitpos: [2]; default: 0; - * Enable generic color convert modlue between color input & color output, need to + * Enable generic color convert module between color input & color output, need to * configure parameter. */ #define DMA2D_OUT_COLOR_3B_PROC_EN_CH0 (BIT(2)) @@ -1032,14 +1032,14 @@ extern "C" { */ #define DMA2D_OUT_DSCR_PORT_BLK_CH0_REG (DR_REG_DMA2D_BASE + 0x6c) /** DMA2D_OUT_DSCR_PORT_BLK_H_CH0 : R/W; bitpos: [13:0]; default: 18; - * Set the vertical height of tx block size in dscr port mode + * Set the horizontal width of tx block size in dscr port mode */ #define DMA2D_OUT_DSCR_PORT_BLK_H_CH0 0x00003FFFU #define DMA2D_OUT_DSCR_PORT_BLK_H_CH0_M (DMA2D_OUT_DSCR_PORT_BLK_H_CH0_V << DMA2D_OUT_DSCR_PORT_BLK_H_CH0_S) #define DMA2D_OUT_DSCR_PORT_BLK_H_CH0_V 0x00003FFFU #define DMA2D_OUT_DSCR_PORT_BLK_H_CH0_S 0 /** DMA2D_OUT_DSCR_PORT_BLK_V_CH0 : R/W; bitpos: [27:14]; default: 18; - * Set the horizontal width of tx block size in dscr port mode + * Set the vertical height of tx block size in dscr port mode */ #define DMA2D_OUT_DSCR_PORT_BLK_V_CH0 0x00003FFFU #define DMA2D_OUT_DSCR_PORT_BLK_V_CH0_M (DMA2D_OUT_DSCR_PORT_BLK_V_CH0_V << DMA2D_OUT_DSCR_PORT_BLK_V_CH0_S) @@ -1907,7 +1907,7 @@ extern "C" { #define DMA2D_OUT_COLOR_OUTPUT_SEL_CH1_V 0x00000003U #define DMA2D_OUT_COLOR_OUTPUT_SEL_CH1_S 0 /** DMA2D_OUT_COLOR_3B_PROC_EN_CH1 : R/W; bitpos: [2]; default: 0; - * Enable generic color convert modlue between color input & color output, need to + * Enable generic color convert module between color input & color output, need to * configure parameter. */ #define DMA2D_OUT_COLOR_3B_PROC_EN_CH1 (BIT(2)) @@ -2040,14 +2040,14 @@ extern "C" { */ #define DMA2D_OUT_DSCR_PORT_BLK_CH1_REG (DR_REG_DMA2D_BASE + 0x16c) /** DMA2D_OUT_DSCR_PORT_BLK_H_CH1 : R/W; bitpos: [13:0]; default: 18; - * Set the vertical height of tx block size in dscr port mode + * Set the horizontal width of tx block size in dscr port mode */ #define DMA2D_OUT_DSCR_PORT_BLK_H_CH1 0x00003FFFU #define DMA2D_OUT_DSCR_PORT_BLK_H_CH1_M (DMA2D_OUT_DSCR_PORT_BLK_H_CH1_V << DMA2D_OUT_DSCR_PORT_BLK_H_CH1_S) #define DMA2D_OUT_DSCR_PORT_BLK_H_CH1_V 0x00003FFFU #define DMA2D_OUT_DSCR_PORT_BLK_H_CH1_S 0 /** DMA2D_OUT_DSCR_PORT_BLK_V_CH1 : R/W; bitpos: [27:14]; default: 18; - * Set the horizontal width of tx block size in dscr port mode + * Set the vertical height of tx block size in dscr port mode */ #define DMA2D_OUT_DSCR_PORT_BLK_V_CH1 0x00003FFFU #define DMA2D_OUT_DSCR_PORT_BLK_V_CH1_M (DMA2D_OUT_DSCR_PORT_BLK_V_CH1_V << DMA2D_OUT_DSCR_PORT_BLK_V_CH1_S) @@ -2915,7 +2915,7 @@ extern "C" { #define DMA2D_OUT_COLOR_OUTPUT_SEL_CH2_V 0x00000003U #define DMA2D_OUT_COLOR_OUTPUT_SEL_CH2_S 0 /** DMA2D_OUT_COLOR_3B_PROC_EN_CH2 : R/W; bitpos: [2]; default: 0; - * Enable generic color convert modlue between color input & color output, need to + * Enable generic color convert module between color input & color output, need to * configure parameter. */ #define DMA2D_OUT_COLOR_3B_PROC_EN_CH2 (BIT(2)) @@ -3048,14 +3048,14 @@ extern "C" { */ #define DMA2D_OUT_DSCR_PORT_BLK_CH2_REG (DR_REG_DMA2D_BASE + 0x26c) /** DMA2D_OUT_DSCR_PORT_BLK_H_CH2 : R/W; bitpos: [13:0]; default: 18; - * Set the vertical height of tx block size in dscr port mode + * Set the horizontal width of tx block size in dscr port mode */ #define DMA2D_OUT_DSCR_PORT_BLK_H_CH2 0x00003FFFU #define DMA2D_OUT_DSCR_PORT_BLK_H_CH2_M (DMA2D_OUT_DSCR_PORT_BLK_H_CH2_V << DMA2D_OUT_DSCR_PORT_BLK_H_CH2_S) #define DMA2D_OUT_DSCR_PORT_BLK_H_CH2_V 0x00003FFFU #define DMA2D_OUT_DSCR_PORT_BLK_H_CH2_S 0 /** DMA2D_OUT_DSCR_PORT_BLK_V_CH2 : R/W; bitpos: [27:14]; default: 18; - * Set the horizontal width of tx block size in dscr port mode + * Set the vertical height of tx block size in dscr port mode */ #define DMA2D_OUT_DSCR_PORT_BLK_V_CH2 0x00003FFFU #define DMA2D_OUT_DSCR_PORT_BLK_V_CH2_M (DMA2D_OUT_DSCR_PORT_BLK_V_CH2_V << DMA2D_OUT_DSCR_PORT_BLK_V_CH2_S) @@ -3990,7 +3990,7 @@ extern "C" { #define DMA2D_IN_COLOR_OUTPUT_SEL_CH0_V 0x00000003U #define DMA2D_IN_COLOR_OUTPUT_SEL_CH0_S 0 /** DMA2D_IN_COLOR_3B_PROC_EN_CH0 : R/W; bitpos: [2]; default: 0; - * Enable generic color convert modlue between color input & color output, need to + * Enable generic color convert module between color input & color output, need to * configure parameter. */ #define DMA2D_IN_COLOR_3B_PROC_EN_CH0 (BIT(2)) diff --git a/components/soc/esp32p4/include/soc/dma2d_struct.h b/components/soc/esp32p4/include/soc/dma2d_struct.h index 03a51a6743..b3f91e92b3 100644 --- a/components/soc/esp32p4/include/soc/dma2d_struct.h +++ b/components/soc/esp32p4/include/soc/dma2d_struct.h @@ -660,7 +660,7 @@ typedef union { */ uint32_t out_color_output_sel_chn:2; /** out_color_3b_proc_en_chn : R/W; bitpos: [2]; default: 0; - * Enable generic color convert modlue between color input & color output, need to + * Enable generic color convert module between color input & color output, need to * configure parameter. */ uint32_t out_color_3b_proc_en_chn:1; @@ -718,11 +718,11 @@ typedef union { typedef union { struct { /** out_dscr_port_blk_h_chn : R/W; bitpos: [13:0]; default: 18; - * Set the vertical height of tx block size in dscr port mode + * Set the horizontal width of tx block size in dscr port mode */ uint32_t out_dscr_port_blk_h_chn:14; /** out_dscr_port_blk_v_chn : R/W; bitpos: [27:14]; default: 18; - * Set the horizontal width of tx block size in dscr port mode + * Set the vertical height of tx block size in dscr port mode */ uint32_t out_dscr_port_blk_v_chn:14; uint32_t reserved_28:4; @@ -1411,7 +1411,7 @@ typedef union { */ uint32_t in_color_output_sel_chn:2; /** in_color_3b_proc_en_chn : R/W; bitpos: [2]; default: 0; - * Enable generic color convert modlue between color input & color output, need to + * Enable generic color convert module between color input & color output, need to * configure parameter. */ uint32_t in_color_3b_proc_en_chn:1; From 01bc7dcc56684c8b109e71d7db47483a48ecdc3e Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Mon, 15 Apr 2024 19:05:31 +0800 Subject: [PATCH 11/15] feat(ppa): add PPA driver support for ESP32P4 Separate callback registration to individual API Fix YUV420 output Separate source files into operations --- components/esp_driver_ppa/CMakeLists.txt | 5 +- .../esp_driver_ppa/include/driver/ppa.h | 88 +- components/esp_driver_ppa/src/ppa.c | 1165 ----------------- components/esp_driver_ppa/src/ppa_blend.c | 260 ++++ components/esp_driver_ppa/src/ppa_core.c | 538 ++++++++ components/esp_driver_ppa/src/ppa_fill.c | 141 ++ components/esp_driver_ppa/src/ppa_priv.h | 38 +- components/esp_driver_ppa/src/ppa_srm.c | 269 ++++ 8 files changed, 1300 insertions(+), 1204 deletions(-) delete mode 100644 components/esp_driver_ppa/src/ppa.c create mode 100644 components/esp_driver_ppa/src/ppa_blend.c create mode 100644 components/esp_driver_ppa/src/ppa_core.c create mode 100644 components/esp_driver_ppa/src/ppa_fill.c create mode 100644 components/esp_driver_ppa/src/ppa_srm.c diff --git a/components/esp_driver_ppa/CMakeLists.txt b/components/esp_driver_ppa/CMakeLists.txt index 093679e143..643a7b49f9 100644 --- a/components/esp_driver_ppa/CMakeLists.txt +++ b/components/esp_driver_ppa/CMakeLists.txt @@ -1,7 +1,10 @@ set(srcs) set(public_include "include") if(CONFIG_SOC_PPA_SUPPORTED) - list(APPEND srcs "src/ppa.c") + list(APPEND srcs "src/ppa_core.c" + "src/ppa_srm.c" + "src/ppa_blend.c" + "src/ppa_fill.c") endif() idf_component_register(SRCS ${srcs} diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 8b6f3d4ec0..1d783e9555 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -20,7 +20,7 @@ extern "C" { typedef enum { PPA_OPERATION_SRM, /*!< Do scale-rotate-mirror operation */ PPA_OPERATION_BLEND, /*!< Do blend operation */ - PPA_OPERATION_FILL, /*!< Do fill operation */ + PPA_OPERATION_FILL, /*!< Do fill operation, use one constant pixel to fill a target window */ PPA_OPERATION_NUM, /*!< Quantity of PPA operations */ } ppa_operation_t; @@ -29,36 +29,11 @@ typedef enum { */ typedef struct ppa_client_t *ppa_client_handle_t; -/** - * @brief Type of PPA transaction handle - */ -typedef struct ppa_trans_t *ppa_trans_handle_t; - -/** - * @brief Type of PPA event data - */ -typedef struct { - ; -} ppa_event_data_t; - -/** - * @brief Type of PPA event callback - * - * @param ppa_client PPA client handle - * @param event_data PPA event data - * @param user_data User registered data from calling `ppa_do_xxx` to perform an operation - * - * @return Whether a task switch is needed after the callback function returns, this is usually due to the callback - * wakes up some high priority task. - */ -typedef bool (*ppa_event_callback_t)(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data); - /** * @brief A collection of configuration items that used for registering a PPA client */ typedef struct { ppa_operation_t oper_type; /*!< The desired PPA operation for the client */ - ppa_event_callback_t done_cb; /*!< Callback function to be executed when a PPA transaction finishes */ uint32_t max_pending_trans_num; /*!< The maximum number of pending transactions for the client. By default, it will be 1, which is sufficient if all transactions are performed with `PPA_TRANS_MODE_BLOCKING` */ } ppa_client_config_t; @@ -68,6 +43,7 @@ typedef struct { * * @param[in] config Pointer to a collection of configurations for the client * @param[out] ret_client Returned client handle + * * @return * - ESP_OK: Register the PPA client successfully * - ESP_ERR_INVALID_ARG: Register the PPA client failed because of invalid argument @@ -80,6 +56,7 @@ esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_hand * @brief Unregister a PPA client * * @param[in] ppa_client PPA client handle, allocated by `ppa_register_client` + * * @return * - ESP_OK: Unregister the PPA client successfully * - ESP_ERR_INVALID_ARG: Unregister the PPA client failed because of invalid argument @@ -87,6 +64,47 @@ esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_hand */ esp_err_t ppa_unregister_client(ppa_client_handle_t ppa_client); +/** + * @brief Type of PPA event data + */ +typedef struct { + ; +} ppa_event_data_t; + +/** + * @brief Type of PPA event callback + * + * @param[in] ppa_client PPA client handle + * @param[in] event_data PPA event data + * @param[in] user_data User registered data from calling `ppa_do_xxx` to perform an operation + * + * @return Whether a task switch is needed after the callback function returns, this is usually due to the callback + * wakes up some high priority task. + */ +typedef bool (*ppa_event_callback_t)(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data); + +/** + * @brief Group of supported PPA callbacks + */ +typedef struct { + ppa_event_callback_t on_trans_done; /*! Invoked when a PPA transaction finishes */ +} ppa_event_callbacks_t; + +/** + * @brief Register event callbacks for a PPA client + * + * @param[in] ppa_client PPA client handle + * @param[in] cbs Structure with all PPA callbacks + * + * @note Any user private data that wants to be passed directly to callback's user_data is provided per PPA transaction. + * Please check the `user_data` field in `ppa_xxx_oper_config_t` structure. + * + * @return + * - ESP_OK: Register event callbacks for the PPA client successfully + * - ESP_ERR_INVALID_ARG: Register event callbacks for the PPA client failed because of invalid argument + */ +esp_err_t ppa_client_register_event_callbacks(ppa_client_handle_t ppa_client, const ppa_event_callbacks_t *cbs); + /** * @brief A collection of configuration items for an input picture and the target block inside the picture */ @@ -159,13 +177,13 @@ typedef struct { ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ -} ppa_srm_oper_trans_config_t; +} ppa_srm_oper_config_t; /** * @brief Perform a scaling-rotating-mirroring (SRM) operation to a picture * * @param[in] ppa_client PPA client handle that has been registered to do SRM operations - * @param[in] config Pointer to a collection of configurations for the SRM operation transaction, ppa_srm_oper_trans_config_t + * @param[in] config Pointer to a collection of configurations for the SRM operation transaction, ppa_srm_oper_config_t * * @return * - ESP_OK: Perform a SRM operation successfully @@ -173,7 +191,7 @@ typedef struct { * - ESP_ERR_NO_MEM: Perform a SRM operation failed because out of memory * - ESP_FAIL: Perform a SRM operation failed because the client cannot accept transaction now */ -esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_srm_oper_trans_config_t *config); +esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_srm_oper_config_t *config); /** * @brief A collection of configuration items to do a PPA blend operation transaction @@ -215,13 +233,13 @@ typedef struct { ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ -} ppa_blend_oper_trans_config_t; +} ppa_blend_oper_config_t; /** * @brief Perform a blending operation to a picture * * @param[in] ppa_client PPA client handle that has been registered to do blend operations - * @param[in] config Pointer to a collection of configurations for the blend operation transaction, ppa_blend_oper_trans_config_t + * @param[in] config Pointer to a collection of configurations for the blend operation transaction, ppa_blend_oper_config_t * * @return * - ESP_OK: Perform a blend operation successfully @@ -229,7 +247,7 @@ typedef struct { * - ESP_ERR_NO_MEM: Perform a blend operation failed because out of memory * - ESP_FAIL: Perform a blend operation failed because the client cannot accept transaction now */ -esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_trans_config_t *config); +esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_config_t *config); /** * @brief A collection of configuration items to do a PPA fill operation transaction @@ -243,13 +261,13 @@ typedef struct { ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ -} ppa_fill_oper_trans_config_t; +} ppa_fill_oper_config_t; /** * @brief Perform a filling operation to a picture * * @param[in] ppa_client PPA client handle that has been registered to do fill operations - * @param[in] config Pointer to a collection of configurations for the fill operation transaction, ppa_fill_oper_trans_config_t + * @param[in] config Pointer to a collection of configurations for the fill operation transaction, ppa_fill_oper_config_t * * @return * - ESP_OK: Perform a fill operation successfully @@ -257,7 +275,7 @@ typedef struct { * - ESP_ERR_NO_MEM: Perform a fill operation failed because out of memory * - ESP_FAIL: Perform a fill operation failed because the client cannot accept transaction now */ -esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_trans_config_t *config); +esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_config_t *config); #ifdef __cplusplus } diff --git a/components/esp_driver_ppa/src/ppa.c b/components/esp_driver_ppa/src/ppa.c deleted file mode 100644 index fb304877b1..0000000000 --- a/components/esp_driver_ppa/src/ppa.c +++ /dev/null @@ -1,1165 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include "sdkconfig.h" -#include "esp_check.h" -#include "esp_log.h" -#include "freertos/portmacro.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/idf_additions.h" -#include "freertos/queue.h" -#include "esp_heap_caps.h" -#include "esp_cache.h" -#include "esp_private/esp_cache_private.h" -#include "hal/cache_hal.h" -#include "hal/cache_ll.h" -#include "driver/ppa.h" -#include "ppa_priv.h" -#include "esp_private/dma2d.h" -#include "hal/dma2d_ll.h" -#include "soc/dma2d_channel.h" -#include "hal/ppa_hal.h" -#include "hal/ppa_ll.h" -#include "hal/ppa_types.h" -#include "hal/color_types.h" -#include "hal/color_hal.h" -#include "esp_private/periph_ctrl.h" -#include "esp_memory_utils.h" - -#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) - -static const char *TAG = "ppa"; - -typedef struct ppa_platform_t { - _lock_t mutex; // Platform level mutex lock to protect the ppa_engine_acquire/ppa_engine_release process - portMUX_TYPE spinlock; // Platform level spinlock - ppa_hal_context_t hal; // PPA HAL context - dma2d_pool_handle_t dma2d_pool_handle; // Pointer to the acquired 2D-DMA pool - ppa_srm_engine_t *srm; // Pointer to the PPA SRM engine - ppa_blend_engine_t *blending; // Pointer to the PPA blending engine - uint32_t srm_engine_ref_count; // Reference count used to protect PPA SRM engine acquire and release - uint32_t blend_engine_ref_count; // Reference count used to protect PPA blending engine acquire and release - size_t buf_alignment_size; // Alignment requirement for the outgoing buffer addr and size to satisfy cache line size - uint32_t dma_desc_mem_size; // Alignment requirement for the 2D-DMA descriptor to satisfy cache line size -#if CONFIG_PM_ENABLE - esp_pm_lock_handle_t pm_lock; // Power management lock -#endif -} ppa_platform_t; - -// PPA driver platform -static ppa_platform_t s_platform = { - .spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED, -}; - -static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine); -static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine); -static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t trans_elm_num, ppa_operation_t oper_type); -static void ppa_free_transaction(ppa_trans_t *trans_elm); -static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); -static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); -static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); - -const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATION_NUM] = { - ppa_srm_transaction_on_picked, - ppa_blend_transaction_on_picked, - ppa_fill_transaction_on_picked, -}; - -static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine) -{ - esp_err_t ret = ESP_OK; - ESP_RETURN_ON_FALSE(config && ret_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(config->engine == PPA_ENGINE_TYPE_SRM || config->engine == PPA_ENGINE_TYPE_BLEND, ESP_ERR_INVALID_ARG, TAG, "invalid engine"); - - *ret_engine = NULL; - - uint32_t data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); - size_t alignment = MAX(DMA2D_LL_DESC_ALIGNMENT, data_cache_line_size); - - _lock_acquire(&s_platform.mutex); - if (s_platform.dma_desc_mem_size == 0) { - s_platform.dma_desc_mem_size = ALIGN_UP(sizeof(dma2d_descriptor_align8_t), alignment); - } - if (s_platform.buf_alignment_size == 0) { - esp_cache_get_alignment(ESP_CACHE_MALLOC_FLAG_PSRAM | ESP_CACHE_MALLOC_FLAG_DMA, &s_platform.buf_alignment_size); - } - - if (config->engine == PPA_ENGINE_TYPE_SRM) { - if (!s_platform.srm) { - ppa_srm_engine_t *srm_engine = heap_caps_calloc(1, sizeof(ppa_srm_engine_t), PPA_MEM_ALLOC_CAPS); - SemaphoreHandle_t srm_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *srm_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *srm_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - if (srm_engine && srm_sem && srm_tx_dma_desc && srm_rx_dma_desc) { - srm_engine->dma_tx_desc = srm_tx_dma_desc; - srm_engine->dma_rx_desc = srm_rx_dma_desc; - srm_engine->base.type = PPA_ENGINE_TYPE_SRM; - srm_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; - srm_engine->base.sem = srm_sem; - xSemaphoreGive(srm_engine->base.sem); - STAILQ_INIT(&srm_engine->base.trans_stailq); - s_platform.srm = srm_engine; - s_platform.srm_engine_ref_count++; - *ret_engine = &srm_engine->base; - - // TODO: Register PPA interrupt? Useful for SRM parameter error. If SRM parameter error, blocks at 2D-DMA, transaction can never finish, stuck... - // need a way to force end - } else { - ret = ESP_ERR_NO_MEM; - ESP_LOGE(TAG, "no mem to register PPA SRM engine"); - free(srm_engine); - if (srm_sem) { - vSemaphoreDeleteWithCaps(srm_sem); - } - free(srm_tx_dma_desc); - free(srm_rx_dma_desc); - } - -#if CONFIG_PM_ENABLE - ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_srm", &srm_engine->base.pm_lock); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "create pm lock failed"); - } -#endif - } else { - // SRM engine already registered - s_platform.srm_engine_ref_count++; - *ret_engine = &s_platform.srm->base; - } - } else if (config->engine == PPA_ENGINE_TYPE_BLEND) { - if (!s_platform.blending) { - ppa_blend_engine_t *blending_engine = heap_caps_calloc(1, sizeof(ppa_blend_engine_t), PPA_MEM_ALLOC_CAPS); - SemaphoreHandle_t blending_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - if (blending_engine && blending_sem && blending_tx_bg_dma_desc && blending_tx_fg_dma_desc && blending_rx_dma_desc) { - blending_engine->dma_tx_bg_desc = blending_tx_bg_dma_desc; - blending_engine->dma_tx_fg_desc = blending_tx_fg_dma_desc; - blending_engine->dma_rx_desc = blending_rx_dma_desc; - blending_engine->base.type = PPA_ENGINE_TYPE_BLEND; - blending_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; - blending_engine->base.sem = blending_sem; - xSemaphoreGive(blending_engine->base.sem); - STAILQ_INIT(&blending_engine->base.trans_stailq); - s_platform.blending = blending_engine; - s_platform.blend_engine_ref_count++; - *ret_engine = &blending_engine->base; - } else { - ret = ESP_ERR_NO_MEM; - ESP_LOGE(TAG, "no mem to register PPA Blending engine"); - free(blending_engine); - if (blending_sem) { - vSemaphoreDeleteWithCaps(blending_sem); - } - free(blending_tx_bg_dma_desc); - free(blending_tx_fg_dma_desc); - free(blending_rx_dma_desc); - } - -#if CONFIG_PM_ENABLE - ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_blending", &blending_engine->base.pm_lock); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "create pm lock failed"); - } -#endif - } else { - // Blending engine already registered - s_platform.blend_engine_ref_count++; - *ret_engine = &s_platform.blending->base; - } - } - - if (ret == ESP_OK) { - if (!s_platform.hal.dev) { - assert(!s_platform.dma2d_pool_handle); - - // Enable the bus clock to access PPA registers - PERIPH_RCC_ATOMIC() { - ppa_ll_enable_bus_clock(true); - ppa_ll_reset_register(); - } - - ppa_hal_init(&s_platform.hal); // initialize HAL context - - // Get 2D-DMA pool handle - dma2d_pool_config_t dma2d_config = { - .pool_id = 0, - }; - ret = dma2d_acquire_pool(&dma2d_config, &s_platform.dma2d_pool_handle); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "install 2D-DMA failed"); - goto wrap_up; - } - -#if CONFIG_PM_ENABLE - assert(!s_platform.pm_lock); - // Create and acquire the PM lock - ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa", &s_platform.pm_lock); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "create pm lock failed"); - goto wrap_up; - } -#endif - } - } -wrap_up: - _lock_release(&s_platform.mutex); - - if (ret != ESP_OK && *ret_engine != NULL) { - ppa_engine_release(*ret_engine); - } - - return ret; -} - -static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) -{ - esp_err_t ret = ESP_OK; - ESP_RETURN_ON_FALSE(ppa_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - - _lock_acquire(&s_platform.mutex); - if (ppa_engine->type == PPA_ENGINE_TYPE_SRM) { - ppa_srm_engine_t *srm_engine = __containerof(ppa_engine, ppa_srm_engine_t, base); - s_platform.srm_engine_ref_count--; - if (s_platform.srm_engine_ref_count == 0) { - assert(STAILQ_EMPTY(&srm_engine->base.trans_stailq)); - // Now, time to free - s_platform.srm = NULL; - free(srm_engine->dma_tx_desc); - free(srm_engine->dma_rx_desc); - vSemaphoreDeleteWithCaps(srm_engine->base.sem); -#if CONFIG_PM_ENABLE - if (srm_engine->base.pm_lock) { - assert(esp_pm_lock_delete(srm_engine->base.pm_lock) == ESP_OK); - } -#endif - free(srm_engine); - } - } else if (ppa_engine->type == PPA_ENGINE_TYPE_BLEND) { - ppa_blend_engine_t *blending_engine = __containerof(ppa_engine, ppa_blend_engine_t, base); - s_platform.blend_engine_ref_count--; - if (s_platform.blend_engine_ref_count == 0) { - assert(STAILQ_EMPTY(&blending_engine->base.trans_stailq)); - // Now, time to free - s_platform.blending = NULL; - free(blending_engine->dma_tx_bg_desc); - free(blending_engine->dma_tx_fg_desc); - free(blending_engine->dma_rx_desc); - vSemaphoreDeleteWithCaps(blending_engine->base.sem); -#if CONFIG_PM_ENABLE - if (blending_engine->base.pm_lock) { - assert(esp_pm_lock_delete(blending_engine->base.pm_lock) == ESP_OK); - } -#endif - free(blending_engine); - } - } - - if (!s_platform.srm && !s_platform.blending) { - assert(s_platform.srm_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0); - -#if CONFIG_PM_ENABLE - if (s_platform.pm_lock) { - assert(esp_pm_lock_delete(s_platform.pm_lock) == ESP_OK); - } -#endif - - if (s_platform.dma2d_pool_handle) { - dma2d_release_pool(s_platform.dma2d_pool_handle); // TODO: check return value. If not ESP_OK, then must be error on other 2D-DMA clients :( Give a warning log? - s_platform.dma2d_pool_handle = NULL; - } - - ppa_hal_deinit(&s_platform.hal); // De-initialize HAL context - - // Disable the bus clock to access PPA registers - PERIPH_RCC_ATOMIC() { - ppa_ll_enable_bus_clock(false); - } - } - _lock_release(&s_platform.mutex); - return ret; -} - -esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_handle_t *ret_client) -{ - esp_err_t ret = ESP_OK; - ESP_RETURN_ON_FALSE(config && ret_client, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(config->oper_type < PPA_OPERATION_NUM, ESP_ERR_INVALID_ARG, TAG, "unknown operation"); - - ppa_client_t *client = (ppa_client_t *)heap_caps_calloc(1, sizeof(ppa_client_t), PPA_MEM_ALLOC_CAPS); - ESP_RETURN_ON_FALSE(client, ESP_ERR_NO_MEM, TAG, "no mem to register client"); - - uint32_t ring_buf_size = MAX(1, config->max_pending_trans_num); - client->trans_elm_ptr_queue = xQueueCreateWithCaps(ring_buf_size, sizeof(uint32_t), PPA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(client->trans_elm_ptr_queue && ppa_malloc_transaction(client->trans_elm_ptr_queue, ring_buf_size, config->oper_type), - ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); - - client->oper_type = config->oper_type; - client->done_cb = config->done_cb; - client->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; - client->in_accepting_trans_state = true; - if (config->oper_type == PPA_OPERATION_SRM) { - ppa_engine_config_t engine_config = { - .engine = PPA_ENGINE_TYPE_SRM, - }; - ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire SRM engine"); - } else if (config->oper_type == PPA_OPERATION_BLEND || config->oper_type == PPA_OPERATION_FILL) { - ppa_engine_config_t engine_config = { - .engine = PPA_ENGINE_TYPE_BLEND, - }; - ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire Blending engine"); - } - *ret_client = client; - -err: - if (ret != ESP_OK) { - ppa_unregister_client(client); - } - return ret; -} - -esp_err_t ppa_unregister_client(ppa_client_handle_t ppa_client) -{ - ESP_RETURN_ON_FALSE(ppa_client, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - - bool do_unregister = false; - portENTER_CRITICAL(&ppa_client->spinlock); - if (ppa_client->trans_cnt == 0) { - ppa_client->in_accepting_trans_state = false; - do_unregister = true; - } - portEXIT_CRITICAL(&ppa_client->spinlock); - ESP_RETURN_ON_FALSE(do_unregister, ESP_ERR_INVALID_STATE, TAG, "client still has unprocessed trans"); - - if (ppa_client->engine) { - ppa_engine_release(ppa_client->engine); - } - - if (ppa_client->trans_elm_ptr_queue) { - ppa_trans_t *trans_elm = NULL; - while (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { - ppa_free_transaction(trans_elm); - } - vQueueDeleteWithCaps(ppa_client->trans_elm_ptr_queue); - } - free(ppa_client); - return ESP_OK; -} - -// Each PPA engine should only have one transaction being pushed to 2D-DMA queue, the rest transactions should stay in engine's own transaction queue. -// This is to avoid 2D-DMA channels being hold, but not actually being used (waiting for PPA engine to be free) -static esp_err_t ppa_dma2d_enqueue(const ppa_trans_t *trans_elm) -{ - return dma2d_enqueue(s_platform.dma2d_pool_handle, trans_elm->trans_desc, trans_elm->dma_trans_placeholder); -} - -static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t trans_elm_num, ppa_operation_t oper_type) -{ - bool res = true; - size_t ppa_trans_desc_size = (oper_type == PPA_OPERATION_SRM) ? sizeof(ppa_srm_oper_t) : - (oper_type == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_oper_t) : - (oper_type == PPA_OPERATION_FILL) ? sizeof(ppa_fill_oper_t) : 0; - assert(ppa_trans_desc_size != 0); - size_t trans_elm_storage_size = sizeof(ppa_trans_t) + SIZEOF_DMA2D_TRANS_T + sizeof(dma2d_trans_config_t) + sizeof(ppa_dma2d_trans_on_picked_config_t) + ppa_trans_desc_size; - for (int i = 0; i < trans_elm_num; i++) { - void *trans_elm_storage = heap_caps_calloc(1, trans_elm_storage_size, PPA_MEM_ALLOC_CAPS); - SemaphoreHandle_t ppa_trans_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); - - if (!trans_elm_storage || !ppa_trans_sem) { - if (trans_elm_storage) { - free(trans_elm_storage); - } - if (ppa_trans_sem) { - vSemaphoreDeleteWithCaps(ppa_trans_sem); - } - res = false; - break; - } - - // Construct trans_elm - ppa_trans_t *new_trans_elm = (ppa_trans_t *)trans_elm_storage; - dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)((uint32_t)trans_elm_storage + sizeof(ppa_trans_t)); - dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)((uint32_t)dma_trans_elm + SIZEOF_DMA2D_TRANS_T); - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)((uint32_t)dma_trans_desc + sizeof(dma2d_trans_config_t)); - void *ppa_trans_desc = (void *)((uint32_t)trans_on_picked_desc + sizeof(ppa_dma2d_trans_on_picked_config_t)); - - trans_on_picked_desc->op_desc = ppa_trans_desc; - trans_on_picked_desc->trans_elm = new_trans_elm; - dma_trans_desc->user_config = (void *)trans_on_picked_desc; - dma_trans_desc->on_job_picked = ppa_oper_trans_on_picked_func[oper_type]; // TODO: This maybe better to be in the ppa_do_xxx function - new_trans_elm->trans_desc = dma_trans_desc; - new_trans_elm->dma_trans_placeholder = dma_trans_elm; - new_trans_elm->sem = ppa_trans_sem; - - // Fill the ring buffer with allocated transaction element pointer - assert(xQueueSend(trans_elm_ptr_queue, &new_trans_elm, 0)); - } - return res; -} - -static void ppa_free_transaction(ppa_trans_t *trans_elm) -{ - if (trans_elm) { - if (trans_elm->sem) { - vSemaphoreDeleteWithCaps(trans_elm->sem); - } - free(trans_elm); - } -} - -static bool ppa_recycle_transaction(ppa_client_handle_t ppa_client, ppa_trans_t *trans_elm) -{ - // Reset transaction and send back to client's trans_elm_ptr_queue - // TODO: To be very safe, we shall memset all to 0, and reconnect necessary pointers? - BaseType_t HPTaskAwoken; - assert(xQueueSendFromISR(ppa_client->trans_elm_ptr_queue, &trans_elm, &HPTaskAwoken)); - return HPTaskAwoken; -} - -static esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_engine_base, ppa_trans_t *trans_elm, ppa_trans_mode_t mode) -{ - esp_err_t ret = ESP_OK; - -#if CONFIG_PM_ENABLE - assert((esp_pm_lock_acquire(s_platform.pm_lock) == ESP_OK) && "acquire pm_lock failed"); -#endif - - portENTER_CRITICAL(&ppa_client->spinlock); - // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! - if (ppa_client->in_accepting_trans_state) { - // Send transaction into PPA engine queue - STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry); - ppa_client->trans_cnt++; - } else { - ret = ESP_FAIL; - } - portEXIT_CRITICAL(&ppa_client->spinlock); - - if (ret != ESP_OK) { - ESP_LOGE(TAG, "The client cannot accept transaction now"); - goto err; - } - - TickType_t ticks_to_wait = (mode == PPA_TRANS_MODE_NON_BLOCKING) ? 0 : portMAX_DELAY; - if (xSemaphoreTake(ppa_engine_base->sem, ticks_to_wait) == pdTRUE) { - // Check if the transaction has already been started from the ISR - // If so, then the transaction should have been removed from queue at this moment (transaction completed) - bool found = false; - ppa_trans_t *temp = NULL; - portENTER_CRITICAL(&ppa_engine_base->spinlock); - STAILQ_FOREACH(temp, &ppa_engine_base->trans_stailq, entry) { - if (temp == trans_elm) { - found = true; - break; - } - } - portEXIT_CRITICAL(&ppa_engine_base->spinlock); - if (found) { -#if CONFIG_PM_ENABLE - assert((esp_pm_lock_acquire(ppa_engine_base->pm_lock) == ESP_OK) && "acquire pm_lock failed"); -#endif - ret = ppa_dma2d_enqueue(trans_elm); - if (ret != ESP_OK) { - portENTER_CRITICAL(&ppa_engine_base->spinlock); - STAILQ_REMOVE(&ppa_engine_base->trans_stailq, trans_elm, ppa_trans_s, entry); - portEXIT_CRITICAL(&ppa_engine_base->spinlock); -#if CONFIG_PM_ENABLE - assert((esp_pm_lock_release(ppa_engine_base->pm_lock) == ESP_OK) && "release pm_lock failed"); -#endif - xSemaphoreGive(ppa_engine_base->sem); - portENTER_CRITICAL(&ppa_client->spinlock); - ppa_client->trans_cnt--; - portEXIT_CRITICAL(&ppa_client->spinlock); - goto err; - } - } else { - xSemaphoreGive(ppa_engine_base->sem); - } - } - - if (mode == PPA_TRANS_MODE_BLOCKING) { - // while (1) { - // printf("ppa intr: %ld\n", PPA.int_raw.val); - // } - xSemaphoreTake(trans_elm->sem, portMAX_DELAY); // Given in the ISR - // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) - // ppa_recycle_transaction(ppa_client, trans_elm); // TODO: Do we need it to be here or can be at the end of done_cb? - } - -#if CONFIG_PM_ENABLE - assert((esp_pm_lock_release(s_platform.pm_lock) == ESP_OK) && "release pm_lock failed"); -#endif - -err: - return ret; -} - -static bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) -{ - bool need_yield = false; - BaseType_t HPTaskAwoken; - ppa_trans_t *trans_elm = (ppa_trans_t *)user_data; - ppa_client_t *client = trans_elm->client; - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; - ppa_engine_t *engine_base = trans_on_picked_desc->ppa_engine; - - if (client->done_cb) { - ppa_event_data_t edata = {}; - need_yield |= client->done_cb(client, &edata, trans_elm->user_data); - } - - ppa_trans_t *next_start_trans = NULL; - portENTER_CRITICAL_ISR(&engine_base->spinlock); - // Remove this transaction from transaction queue - STAILQ_REMOVE(&engine_base->trans_stailq, trans_elm, ppa_trans_s, entry); - next_start_trans = STAILQ_FIRST(&engine_base->trans_stailq); - portEXIT_CRITICAL_ISR(&engine_base->spinlock); - - // If there is next trans in PPA engine queue, send it to DMA queue; otherwise, release the engine semaphore - if (next_start_trans) { - ppa_dma2d_enqueue(next_start_trans); - } else { -#if CONFIG_PM_ENABLE - assert((esp_pm_lock_release(engine_base->pm_lock) == ESP_OK)); -#endif - xSemaphoreGiveFromISR(engine_base->sem, &HPTaskAwoken); - need_yield |= (HPTaskAwoken == pdTRUE); - } - // Recycle transaction and release transaction semaphore - // if (trans_elm->sem != NULL) { - xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); - need_yield |= (HPTaskAwoken == pdTRUE); - // } - - // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! - portENTER_CRITICAL_ISR(&client->spinlock); - need_yield |= ppa_recycle_transaction(client, trans_elm); - client->trans_cnt--; - portEXIT_CRITICAL_ISR(&client->spinlock); - - return need_yield; -} - -static bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) -{ - assert(num_chans == 2 && dma2d_chans && user_config); - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; - assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SRM && trans_on_picked_desc->srm_desc && trans_on_picked_desc->ppa_engine); - - ppa_srm_oper_t *srm_trans_desc = (ppa_srm_oper_t *)trans_on_picked_desc->srm_desc; - ppa_srm_engine_t *srm_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_srm_engine_t, base); - - // Reset SRM engine - ppa_ll_srm_reset(s_platform.hal.dev); - - // Get the required 2D-DMA channel handles - dma2d_channel_handle_t dma2d_tx_chan = NULL; - dma2d_channel_handle_t dma2d_rx_chan = NULL; - for (uint32_t i = 0; i < num_chans; i++) { - if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_TX) { - dma2d_tx_chan = dma2d_chans[i].chan; - } - if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_RX) { - dma2d_rx_chan = dma2d_chans[i].chan; - } - } - assert(dma2d_tx_chan && dma2d_rx_chan); - - color_space_pixel_format_t in_pixel_format = { - .color_type_id = srm_trans_desc->in.srm_cm, - }; - - // Fill 2D-DMA descriptors - srm_engine->dma_tx_desc->vb_size = srm_trans_desc->in.block_h; - srm_engine->dma_tx_desc->hb_length = srm_trans_desc->in.block_w; - srm_engine->dma_tx_desc->err_eof = 0; - srm_engine->dma_tx_desc->dma2d_en = 1; - srm_engine->dma_tx_desc->suc_eof = 1; - srm_engine->dma_tx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - srm_engine->dma_tx_desc->va_size = srm_trans_desc->in.pic_h; - srm_engine->dma_tx_desc->ha_length = srm_trans_desc->in.pic_w; - srm_engine->dma_tx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_pixel_format); - srm_engine->dma_tx_desc->y = srm_trans_desc->in.block_offset_y; - srm_engine->dma_tx_desc->x = srm_trans_desc->in.block_offset_x; - srm_engine->dma_tx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - srm_engine->dma_tx_desc->buffer = (void *)srm_trans_desc->in.buffer; - srm_engine->dma_tx_desc->next = NULL; - - // vb_size, hb_length can be any value (auto writeback). However, if vb_size/hb_length is 0, it triggers 2D-DMA DESC_ERROR interrupt, and dma2d driver will automatically ends the transaction. Therefore, to avoid this, we set them to 1. - srm_engine->dma_rx_desc->vb_size = 1; - srm_engine->dma_rx_desc->hb_length = 1; - srm_engine->dma_rx_desc->err_eof = 0; - srm_engine->dma_rx_desc->dma2d_en = 1; - srm_engine->dma_rx_desc->suc_eof = 1; - srm_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - srm_engine->dma_rx_desc->va_size = srm_trans_desc->out.pic_h; - srm_engine->dma_rx_desc->ha_length = srm_trans_desc->out.pic_w; - // pbyte can be any value - srm_engine->dma_rx_desc->y = srm_trans_desc->out.block_offset_y; - srm_engine->dma_rx_desc->x = srm_trans_desc->out.block_offset_x; - srm_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - srm_engine->dma_rx_desc->buffer = (void *)srm_trans_desc->out.buffer; - srm_engine->dma_rx_desc->next = NULL; - - esp_cache_msync((void *)srm_engine->dma_tx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - esp_cache_msync((void *)srm_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - - // Configure 2D-DMA channels - dma2d_trigger_t trig_periph = { - .periph = DMA2D_TRIG_PERIPH_PPA_SRM, - .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SRM_TX, - }; - dma2d_connect(dma2d_tx_chan, &trig_periph); - trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SRM_RX; - dma2d_connect(dma2d_rx_chan, &trig_periph); - - dma2d_transfer_ability_t dma_transfer_ability = { - .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, - .desc_burst_en = true, - .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, - // Configure the block size to be received by the SRM engine, which is passed from the 2D-DMA TX channel (i.e. 2D-DMA dscr-port mode) - .dscr_port_block_h = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, - .dscr_port_block_v = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, - }; - dma2d_set_transfer_ability(dma2d_tx_chan, &dma_transfer_ability); - dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); - - // YUV444 and YUV422 are not supported by PPA module, need to utilize 2D-DMA color space conversion feature to do a conversion - ppa_srm_color_mode_t ppa_in_color_mode = srm_trans_desc->in.srm_cm; - if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV444) { - ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; - dma2d_csc_config_t dma_tx_csc = {0}; - if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { - dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_601; - } else { - dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_709; - } - dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); - } else if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV422) { - ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; - dma2d_csc_config_t dma_tx_csc = {0}; - if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { - dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_601; - } else { - dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_709; - } - dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); - } - - ppa_srm_color_mode_t ppa_out_color_mode = srm_trans_desc->out.srm_cm; - if (ppa_out_color_mode == PPA_SRM_COLOR_MODE_YUV444) { - ppa_out_color_mode = PPA_SRM_COLOR_MODE_YUV420; - dma2d_csc_config_t dma_rx_csc = { - .rx_csc_option = DMA2D_CSC_RX_YUV420_TO_YUV444, - }; - dma2d_configure_color_space_conversion(dma2d_rx_chan, &dma_rx_csc); - } - - dma2d_rx_event_callbacks_t dma_event_cbs = { - .on_recv_eof = ppa_transaction_done_cb, - }; - dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); - - dma2d_set_desc_addr(dma2d_tx_chan, (intptr_t)srm_engine->dma_tx_desc); - dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)srm_engine->dma_rx_desc); - dma2d_start(dma2d_tx_chan); - dma2d_start(dma2d_rx_chan); - - // Configure PPA SRM engine - ppa_ll_srm_set_rx_color_mode(s_platform.hal.dev, ppa_in_color_mode); - if (COLOR_SPACE_TYPE((uint32_t)ppa_in_color_mode) == COLOR_SPACE_YUV) { - ppa_ll_srm_set_rx_yuv_range(s_platform.hal.dev, srm_trans_desc->in.yuv_range); - ppa_ll_srm_set_rx_yuv2rgb_std(s_platform.hal.dev, srm_trans_desc->in.yuv_std); - } - ppa_ll_srm_enable_rx_byte_swap(s_platform.hal.dev, srm_trans_desc->byte_swap); - ppa_ll_srm_enable_rx_rgb_swap(s_platform.hal.dev, srm_trans_desc->rgb_swap); - ppa_ll_srm_configure_rx_alpha(s_platform.hal.dev, srm_trans_desc->alpha_update_mode, srm_trans_desc->alpha_value); - - ppa_ll_srm_set_tx_color_mode(s_platform.hal.dev, ppa_out_color_mode); - if (COLOR_SPACE_TYPE((uint32_t)ppa_out_color_mode) == COLOR_SPACE_YUV) { - ppa_ll_srm_set_tx_yuv_range(s_platform.hal.dev, srm_trans_desc->out.yuv_range); - ppa_ll_srm_set_tx_rgb2yuv_std(s_platform.hal.dev, srm_trans_desc->out.yuv_std); - } - - ppa_ll_srm_set_rotation_angle(s_platform.hal.dev, srm_trans_desc->rotation_angle); - ppa_ll_srm_set_scaling_x(s_platform.hal.dev, srm_trans_desc->scale_x_int, srm_trans_desc->scale_x_frag); - ppa_ll_srm_set_scaling_y(s_platform.hal.dev, srm_trans_desc->scale_y_int, srm_trans_desc->scale_y_frag); - ppa_ll_srm_enable_mirror_x(s_platform.hal.dev, srm_trans_desc->mirror_x); - ppa_ll_srm_enable_mirror_y(s_platform.hal.dev, srm_trans_desc->mirror_y); - - ppa_ll_srm_start(s_platform.hal.dev); - - // No need to yield - return false; -} - -esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_srm_oper_trans_config_t *config) -{ - ESP_RETURN_ON_FALSE(ppa_client && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_client->oper_type == PPA_OPERATION_SRM, ESP_ERR_INVALID_ARG, TAG, "client is not for SRM operations"); - ESP_RETURN_ON_FALSE(config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); - // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region - ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); - ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % s_platform.buf_alignment_size == 0 && config->out.buffer_size % s_platform.buf_alignment_size == 0, - ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); - color_space_pixel_format_t out_pixel_format = { - .color_type_id = config->out.srm_cm, - }; - uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); - ESP_RETURN_ON_FALSE(config->scale_x < (PPA_LL_SRM_SCALING_INT_MAX + 1) && config->scale_x >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX) && - config->scale_y < (PPA_LL_SRM_SCALING_INT_MAX + 1) && config->scale_y >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX), - ESP_ERR_INVALID_ARG, TAG, "invalid scale"); - uint32_t new_block_w = 0; - uint32_t new_block_h = 0; - if (config->rotation_angle == PPA_SRM_ROTATION_ANGLE_0 || config->rotation_angle == PPA_SRM_ROTATION_ANGLE_180) { - new_block_w = (uint32_t)(config->scale_x * config->in.block_w); - new_block_h = (uint32_t)(config->scale_y * config->in.block_h); - } else { - new_block_w = (uint32_t)(config->scale_y * config->in.block_h); - new_block_h = (uint32_t)(config->scale_x * config->in.block_w); - } - ESP_RETURN_ON_FALSE(new_block_w <= (config->out.pic_w - config->out.block_offset_x) && - new_block_h <= (config->out.pic_h - config->out.block_offset_y), - ESP_ERR_INVALID_ARG, TAG, "scale does not fit in the out pic"); - if (config->byte_swap) { - PPA_CHECK_CM_SUPPORT_BYTE_SWAP("in.srm", (uint32_t)config->in.srm_cm); - } - if (config->rgb_swap) { - PPA_CHECK_CM_SUPPORT_RGB_SWAP("in.srm", (uint32_t)config->in.srm_cm); - } - ESP_RETURN_ON_FALSE(config->alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_value"); - // To reduce complexity, rotation_angle, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions - // TODO: - // YUV420: in desc, ha/hb/va/vb/x/y must be even number - // What for YUV444/YUV422 - - // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - // Write back in_buffer - color_space_pixel_format_t in_pixel_format = { - .color_type_id = config->in.srm_cm, - }; - uint32_t in_pic_len = config->in.pic_w * config->in.pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; - esp_cache_msync(config->in.buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - // Invalidate out_buffer - esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - - esp_err_t ret = ESP_OK; - ppa_trans_t *trans_elm = NULL; - if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { - dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; - - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; - - ppa_srm_oper_t *srm_trans_desc = (ppa_srm_oper_t *)trans_on_picked_desc->srm_desc; - memcpy(srm_trans_desc, config, sizeof(ppa_srm_oper_trans_config_t)); - srm_trans_desc->scale_x_int = (uint32_t)srm_trans_desc->scale_x; - srm_trans_desc->scale_x_frag = (uint32_t)(srm_trans_desc->scale_x * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; - srm_trans_desc->scale_y_int = (uint32_t)srm_trans_desc->scale_y; - srm_trans_desc->scale_y_frag = (uint32_t)(srm_trans_desc->scale_y * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; - - trans_on_picked_desc->ppa_engine = ppa_client->engine; - trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_SRM; - - dma_trans_desc->tx_channel_num = 1; - dma_trans_desc->rx_channel_num = 1; - dma_trans_desc->channel_flags = 0; - if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV422 || config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { - dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC; - } - if (config->out.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { - dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC; - } - dma_trans_desc->specified_tx_channel_mask = 0; - dma_trans_desc->specified_rx_channel_mask = 0; - - trans_elm->client = ppa_client; - trans_elm->user_data = config->user_data; - xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semaphore before transaction starts - - ret = ppa_do_operation(ppa_client, ppa_client->engine, trans_elm, config->mode); - if (ret != ESP_OK) { - ppa_recycle_transaction(ppa_client, trans_elm); - } - } else { - ret = ESP_FAIL; - ESP_LOGE(TAG, "exceed maximum pending transactions for the client, consider increase max_pending_trans_num"); - } - return ret; -} - -static bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) -{ - assert(num_chans == 3 && dma2d_chans && user_config); - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; - assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->blend_desc && trans_on_picked_desc->ppa_engine); - - ppa_blend_oper_t *blend_trans_desc = (ppa_blend_oper_t *)trans_on_picked_desc->blend_desc; - ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); - - // Reset blending engine - ppa_ll_blend_reset(s_platform.hal.dev); - - // Get the required 2D-DMA channel handles - dma2d_channel_handle_t dma2d_tx_bg_chan = NULL; - dma2d_channel_handle_t dma2d_tx_fg_chan = NULL; - dma2d_channel_handle_t dma2d_rx_chan = NULL; - for (uint32_t i = 0; i < num_chans; i++) { - if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_TX) { - if (!dma2d_tx_bg_chan) { - dma2d_tx_bg_chan = dma2d_chans[i].chan; - } else { - dma2d_tx_fg_chan = dma2d_chans[i].chan; - } - } - if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_RX) { - dma2d_rx_chan = dma2d_chans[i].chan; - } - } - assert(dma2d_tx_bg_chan && dma2d_tx_fg_chan && dma2d_rx_chan); - - color_space_pixel_format_t in_bg_pixel_format = { - .color_type_id = blend_trans_desc->in_bg.blend_cm, - }; - color_space_pixel_format_t in_fg_pixel_format = { - .color_type_id = blend_trans_desc->in_fg.blend_cm, - }; - color_space_pixel_format_t out_pixel_format = { - .color_type_id = blend_trans_desc->out.blend_cm, - }; - - // Fill 2D-DMA descriptors - blend_engine->dma_tx_bg_desc->vb_size = blend_trans_desc->in_bg.block_h; - blend_engine->dma_tx_bg_desc->hb_length = blend_trans_desc->in_bg.block_w; - blend_engine->dma_tx_bg_desc->err_eof = 0; - blend_engine->dma_tx_bg_desc->dma2d_en = 1; - blend_engine->dma_tx_bg_desc->suc_eof = 1; - blend_engine->dma_tx_bg_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - blend_engine->dma_tx_bg_desc->va_size = blend_trans_desc->in_bg.pic_h; - blend_engine->dma_tx_bg_desc->ha_length = blend_trans_desc->in_bg.pic_w; - blend_engine->dma_tx_bg_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_bg_pixel_format); - blend_engine->dma_tx_bg_desc->y = blend_trans_desc->in_bg.block_offset_y; - blend_engine->dma_tx_bg_desc->x = blend_trans_desc->in_bg.block_offset_x; - blend_engine->dma_tx_bg_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - blend_engine->dma_tx_bg_desc->buffer = (void *)blend_trans_desc->in_bg.buffer; - blend_engine->dma_tx_bg_desc->next = NULL; - - blend_engine->dma_tx_fg_desc->vb_size = blend_trans_desc->in_fg.block_h; - blend_engine->dma_tx_fg_desc->hb_length = blend_trans_desc->in_fg.block_w; - blend_engine->dma_tx_fg_desc->err_eof = 0; - blend_engine->dma_tx_fg_desc->dma2d_en = 1; - blend_engine->dma_tx_fg_desc->suc_eof = 1; - blend_engine->dma_tx_fg_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - blend_engine->dma_tx_fg_desc->va_size = blend_trans_desc->in_fg.pic_h; - blend_engine->dma_tx_fg_desc->ha_length = blend_trans_desc->in_fg.pic_w; - blend_engine->dma_tx_fg_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_fg_pixel_format); - blend_engine->dma_tx_fg_desc->y = blend_trans_desc->in_fg.block_offset_y; - blend_engine->dma_tx_fg_desc->x = blend_trans_desc->in_fg.block_offset_x; - blend_engine->dma_tx_fg_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - blend_engine->dma_tx_fg_desc->buffer = (void *)blend_trans_desc->in_fg.buffer; - blend_engine->dma_tx_fg_desc->next = NULL; - - blend_engine->dma_rx_desc->vb_size = blend_trans_desc->in_fg.block_h; // in_bg.block_h == in_fg.block_h - blend_engine->dma_rx_desc->hb_length = blend_trans_desc->in_fg.block_w; // in_bg.block_w == in_fg.block_w - blend_engine->dma_rx_desc->err_eof = 0; - blend_engine->dma_rx_desc->dma2d_en = 1; - blend_engine->dma_rx_desc->suc_eof = 1; - blend_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - blend_engine->dma_rx_desc->va_size = blend_trans_desc->out.pic_h; - blend_engine->dma_rx_desc->ha_length = blend_trans_desc->out.pic_w; - blend_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format); - blend_engine->dma_rx_desc->y = blend_trans_desc->out.block_offset_y; - blend_engine->dma_rx_desc->x = blend_trans_desc->out.block_offset_x; - blend_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - blend_engine->dma_rx_desc->buffer = (void *)blend_trans_desc->out.buffer; - blend_engine->dma_rx_desc->next = NULL; - - esp_cache_msync((void *)blend_engine->dma_tx_bg_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - esp_cache_msync((void *)blend_engine->dma_tx_fg_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - esp_cache_msync((void *)blend_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - - // Configure 2D-DMA channels - dma2d_trigger_t trig_periph = { - .periph = DMA2D_TRIG_PERIPH_PPA_BLEND, - .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_BG_TX, - }; - dma2d_connect(dma2d_tx_bg_chan, &trig_periph); - trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_FG_TX; - dma2d_connect(dma2d_tx_fg_chan, &trig_periph); - trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_RX; - dma2d_connect(dma2d_rx_chan, &trig_periph); - - dma2d_transfer_ability_t dma_transfer_ability = { - .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, - .desc_burst_en = true, - .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, - }; - dma2d_set_transfer_ability(dma2d_tx_bg_chan, &dma_transfer_ability); - dma2d_set_transfer_ability(dma2d_tx_fg_chan, &dma_transfer_ability); - dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); - - dma2d_rx_event_callbacks_t dma_event_cbs = { - .on_recv_eof = ppa_transaction_done_cb, - }; - dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); - - dma2d_set_desc_addr(dma2d_tx_bg_chan, (intptr_t)blend_engine->dma_tx_bg_desc); - dma2d_set_desc_addr(dma2d_tx_fg_chan, (intptr_t)blend_engine->dma_tx_fg_desc); - dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)blend_engine->dma_rx_desc); - dma2d_start(dma2d_tx_bg_chan); - dma2d_start(dma2d_tx_fg_chan); - dma2d_start(dma2d_rx_chan); - - // Configure PPA Blending engine - ppa_ll_blend_set_rx_bg_color_mode(s_platform.hal.dev, blend_trans_desc->in_bg.blend_cm); - ppa_ll_blend_enable_rx_bg_byte_swap(s_platform.hal.dev, blend_trans_desc->bg_byte_swap); - ppa_ll_blend_enable_rx_bg_rgb_swap(s_platform.hal.dev, blend_trans_desc->bg_rgb_swap); - ppa_ll_blend_configure_rx_bg_alpha(s_platform.hal.dev, blend_trans_desc->bg_alpha_update_mode, blend_trans_desc->bg_alpha_value); - - ppa_ll_blend_set_rx_fg_color_mode(s_platform.hal.dev, blend_trans_desc->in_fg.blend_cm); - if (COLOR_SPACE_TYPE((uint32_t)blend_trans_desc->in_fg.blend_cm) == COLOR_SPACE_ALPHA) { - ppa_ll_blend_set_rx_fg_fix_rgb(s_platform.hal.dev, blend_trans_desc->fg_fix_rgb_val); - } - ppa_ll_blend_enable_rx_fg_byte_swap(s_platform.hal.dev, blend_trans_desc->fg_byte_swap); - ppa_ll_blend_enable_rx_fg_rgb_swap(s_platform.hal.dev, blend_trans_desc->fg_rgb_swap); - ppa_ll_blend_configure_rx_fg_alpha(s_platform.hal.dev, blend_trans_desc->fg_alpha_update_mode, blend_trans_desc->fg_alpha_value); - - ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, blend_trans_desc->out.blend_cm); - - // Color keying - ppa_ll_blend_configure_rx_bg_ck_range(s_platform.hal.dev, - blend_trans_desc->bg_ck_en ? blend_trans_desc->bg_ck_rgb_low_thres : 0xFFFFFF, - blend_trans_desc->bg_ck_en ? blend_trans_desc->bg_ck_rgb_high_thres : 0); - ppa_ll_blend_configure_rx_fg_ck_range(s_platform.hal.dev, - blend_trans_desc->fg_ck_en ? blend_trans_desc->fg_ck_rgb_low_thres : 0xFFFFFF, - blend_trans_desc->fg_ck_en ? blend_trans_desc->fg_ck_rgb_high_thres : 0); - ppa_ll_blend_set_ck_default_rgb(s_platform.hal.dev, (blend_trans_desc->bg_ck_en && blend_trans_desc->fg_ck_en) ? blend_trans_desc->ck_rgb_default_val : 0); - ppa_ll_blend_enable_ck_fg_bg_reverse(s_platform.hal.dev, blend_trans_desc->ck_reverse_bg2fg); - - ppa_ll_blend_start(s_platform.hal.dev, PPA_LL_BLEND_TRANS_MODE_BLEND); - - // No need to yield - return false; -} - -esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_trans_config_t *config) -{ - ESP_RETURN_ON_FALSE(ppa_client && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_client->oper_type == PPA_OPERATION_BLEND, ESP_ERR_INVALID_ARG, TAG, "client is not for blend operations"); - ESP_RETURN_ON_FALSE(config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); - // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region - ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); - ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % s_platform.buf_alignment_size == 0 && config->out.buffer_size % s_platform.buf_alignment_size == 0, - ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); - color_space_pixel_format_t out_pixel_format = { - .color_type_id = config->out.blend_cm, - }; - uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); - ESP_RETURN_ON_FALSE(config->in_bg.block_w == config->in_fg.block_w && config->in_bg.block_h == config->in_fg.block_h, - ESP_ERR_INVALID_ARG, TAG, "in_bg.block_w/h must be equal to in_fg.block_w/h"); - if (config->bg_byte_swap) { - PPA_CHECK_CM_SUPPORT_BYTE_SWAP("in_bg.blend", (uint32_t)config->in_bg.blend_cm); - } - if (config->bg_rgb_swap) { - PPA_CHECK_CM_SUPPORT_RGB_SWAP("in_bg.blend", (uint32_t)config->in_bg.blend_cm); - } - if (config->fg_byte_swap) { - PPA_CHECK_CM_SUPPORT_BYTE_SWAP("in_fg.blend", (uint32_t)config->in_fg.blend_cm); - } - if (config->fg_rgb_swap) { - PPA_CHECK_CM_SUPPORT_RGB_SWAP("in_fg.blend", (uint32_t)config->in_fg.blend_cm); - } - ESP_RETURN_ON_FALSE(config->bg_alpha_value <= 0xFF && config->fg_alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid bg/fg_alpha_value"); - if (config->in_bg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { - ESP_RETURN_ON_FALSE(config->in_bg.block_w % 2 == 0 && config->in_bg.block_offset_x % 2 == 0, - ESP_ERR_INVALID_ARG, TAG, "in_bg.block_w and in_bg.block_offset_x must be even"); - } - if (config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_A4 || config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { - ESP_RETURN_ON_FALSE(config->in_fg.block_w % 2 == 0 && config->in_fg.block_offset_x % 2 == 0, - ESP_ERR_INVALID_ARG, TAG, "in_fg.block_w and in_fg.block_offset_x must be even"); - } - // To reduce complexity, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions - - // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - // Write back in_bg_buffer, in_fg_buffer - color_space_pixel_format_t in_bg_pixel_format = { - .color_type_id = config->in_bg.blend_cm, - }; - uint32_t in_bg_pic_len = config->in_bg.pic_w * config->in_bg.pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; - esp_cache_msync(config->in_bg.buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - color_space_pixel_format_t in_fg_pixel_format = { - .color_type_id = config->in_fg.blend_cm, - }; - uint32_t in_fg_pic_len = config->in_fg.pic_w * config->in_fg.pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; - esp_cache_msync(config->in_fg.buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - // Invalidate out_buffer - esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - - esp_err_t ret = ESP_OK; - ppa_trans_t *trans_elm = NULL; - if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { - dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; - - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; - - ppa_blend_oper_t *blend_trans_desc = (ppa_blend_oper_t *)trans_on_picked_desc->blend_desc; - memcpy(blend_trans_desc, config, sizeof(ppa_blend_oper_trans_config_t)); - - trans_on_picked_desc->ppa_engine = ppa_client->engine; - trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; - - dma_trans_desc->tx_channel_num = 2; - dma_trans_desc->rx_channel_num = 1; - dma_trans_desc->channel_flags = 0; - dma_trans_desc->specified_tx_channel_mask = 0; - dma_trans_desc->specified_rx_channel_mask = 0; - - trans_elm->client = ppa_client; - trans_elm->user_data = config->user_data; - xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semaphore before transaction starts - - ret = ppa_do_operation(ppa_client, ppa_client->engine, trans_elm, config->mode); - if (ret != ESP_OK) { - ppa_recycle_transaction(ppa_client, trans_elm); - } - } else { - ret = ESP_FAIL; - ESP_LOGE(TAG, "exceed maximum pending transactions for the client, consider increase max_pending_trans_num"); - } - return ret; -} - -static bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) -{ - assert(num_chans == 1 && dma2d_chans && user_config); - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; - assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->fill_desc && trans_on_picked_desc->ppa_engine); - - ppa_fill_oper_t *fill_trans_desc = (ppa_fill_oper_t *)trans_on_picked_desc->fill_desc; - ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); - - // Reset blending engine - ppa_ll_blend_reset(s_platform.hal.dev); - - // Get the required 2D-DMA channel handles - assert(dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX); - dma2d_channel_handle_t dma2d_rx_chan = dma2d_chans[0].chan; - - color_space_pixel_format_t out_pixel_format = { - .color_type_id = fill_trans_desc->out.fill_cm, - }; - - // Fill 2D-DMA descriptors - blend_engine->dma_rx_desc->vb_size = fill_trans_desc->fill_block_h; - blend_engine->dma_rx_desc->hb_length = fill_trans_desc->fill_block_w; - blend_engine->dma_rx_desc->err_eof = 0; - blend_engine->dma_rx_desc->dma2d_en = 1; - blend_engine->dma_rx_desc->suc_eof = 1; - blend_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; - blend_engine->dma_rx_desc->va_size = fill_trans_desc->out.pic_h; - blend_engine->dma_rx_desc->ha_length = fill_trans_desc->out.pic_w; - blend_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format); - blend_engine->dma_rx_desc->y = fill_trans_desc->out.block_offset_y; - blend_engine->dma_rx_desc->x = fill_trans_desc->out.block_offset_x; - blend_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; - blend_engine->dma_rx_desc->buffer = (void *)fill_trans_desc->out.buffer; - blend_engine->dma_rx_desc->next = NULL; - - esp_cache_msync((void *)blend_engine->dma_rx_desc, s_platform.dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - - // Configure 2D-DMA channels - dma2d_trigger_t trig_periph = { - .periph = DMA2D_TRIG_PERIPH_PPA_BLEND, - .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_RX, - }; - dma2d_connect(dma2d_rx_chan, &trig_periph); - - dma2d_transfer_ability_t dma_transfer_ability = { - .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, - .desc_burst_en = true, - .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, - }; - dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); - - dma2d_rx_event_callbacks_t dma_event_cbs = { - .on_recv_eof = ppa_transaction_done_cb, - }; - dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); - - dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)blend_engine->dma_rx_desc); - dma2d_start(dma2d_rx_chan); - - // Configure PPA Blending engine - ppa_ll_blend_configure_filling_block(s_platform.hal.dev, fill_trans_desc->fill_argb_color, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h); - ppa_ll_blend_set_tx_color_mode(s_platform.hal.dev, fill_trans_desc->out.fill_cm); - - ppa_ll_blend_start(s_platform.hal.dev, PPA_LL_BLEND_TRANS_MODE_FILL); - - // No need to yield - return false; -} - -esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_trans_config_t *config) -{ - ESP_RETURN_ON_FALSE(ppa_client && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ppa_client->oper_type == PPA_OPERATION_FILL, ESP_ERR_INVALID_ARG, TAG, "client is not for fill operations"); - ESP_RETURN_ON_FALSE(config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); - // out_buffer ptr cannot in flash region - ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); - ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % s_platform.buf_alignment_size == 0 && config->out.buffer_size % s_platform.buf_alignment_size == 0, - ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); - color_space_pixel_format_t out_pixel_format = { - .color_type_id = config->out.fill_cm, - }; - uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; - ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); - // To reduce complexity, color_mode, fill_block_w/h correctness are checked in their corresponding LL functions - - // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); - - esp_err_t ret = ESP_OK; - ppa_trans_t *trans_elm = NULL; - if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { - dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; - - ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; - - ppa_fill_oper_t *fill_trans_desc = (ppa_fill_oper_t *)trans_on_picked_desc->fill_desc; - memcpy(fill_trans_desc, config, sizeof(ppa_fill_oper_trans_config_t)); - - trans_on_picked_desc->ppa_engine = ppa_client->engine; - trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; - - dma_trans_desc->tx_channel_num = 0; - dma_trans_desc->rx_channel_num = 1; - dma_trans_desc->channel_flags = 0; - dma_trans_desc->specified_tx_channel_mask = 0; - dma_trans_desc->specified_rx_channel_mask = 0; - - trans_elm->client = ppa_client; - trans_elm->user_data = config->user_data; - xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semaphore before transaction starts - - ret = ppa_do_operation(ppa_client, ppa_client->engine, trans_elm, config->mode); - if (ret != ESP_OK) { - ppa_recycle_transaction(ppa_client, trans_elm); - } - } else { - ret = ESP_FAIL; - ESP_LOGE(TAG, "exceed maximum pending transactions for the client, consider increase max_pending_trans_num"); - } - return ret; -} diff --git a/components/esp_driver_ppa/src/ppa_blend.c b/components/esp_driver_ppa/src/ppa_blend.c new file mode 100644 index 0000000000..a7a91200cb --- /dev/null +++ b/components/esp_driver_ppa/src/ppa_blend.c @@ -0,0 +1,260 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_check.h" +#include "driver/ppa.h" +#include "ppa_priv.h" +#include "esp_private/dma2d.h" +#include "hal/ppa_ll.h" +#include "esp_cache.h" +#include "esp_memory_utils.h" +#include "soc/dma2d_channel.h" + +static const char *TAG = "ppa_blend"; + +bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +{ + assert(num_chans == 3 && dma2d_chans && user_config); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; + assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->blend_desc && trans_on_picked_desc->ppa_engine); + + ppa_blend_oper_t *blend_trans_desc = (ppa_blend_oper_t *)trans_on_picked_desc->blend_desc; + ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); + ppa_platform_t *platform = blend_engine->base.platform; + + // Reset blending engine + ppa_ll_blend_reset(platform->hal.dev); + + // Get the required 2D-DMA channel handles + dma2d_channel_handle_t dma2d_tx_bg_chan = NULL; + dma2d_channel_handle_t dma2d_tx_fg_chan = NULL; + dma2d_channel_handle_t dma2d_rx_chan = NULL; + for (uint32_t i = 0; i < num_chans; i++) { + if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_TX) { + if (!dma2d_tx_bg_chan) { + dma2d_tx_bg_chan = dma2d_chans[i].chan; + } else { + dma2d_tx_fg_chan = dma2d_chans[i].chan; + } + } + if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_RX) { + dma2d_rx_chan = dma2d_chans[i].chan; + } + } + assert(dma2d_tx_bg_chan && dma2d_tx_fg_chan && dma2d_rx_chan); + + color_space_pixel_format_t in_bg_pixel_format = { + .color_type_id = blend_trans_desc->in_bg.blend_cm, + }; + color_space_pixel_format_t in_fg_pixel_format = { + .color_type_id = blend_trans_desc->in_fg.blend_cm, + }; + color_space_pixel_format_t out_pixel_format = { + .color_type_id = blend_trans_desc->out.blend_cm, + }; + + // Fill 2D-DMA descriptors + blend_engine->dma_tx_bg_desc->vb_size = blend_trans_desc->in_bg.block_h; + blend_engine->dma_tx_bg_desc->hb_length = blend_trans_desc->in_bg.block_w; + blend_engine->dma_tx_bg_desc->err_eof = 0; + blend_engine->dma_tx_bg_desc->dma2d_en = 1; + blend_engine->dma_tx_bg_desc->suc_eof = 1; + blend_engine->dma_tx_bg_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + blend_engine->dma_tx_bg_desc->va_size = blend_trans_desc->in_bg.pic_h; + blend_engine->dma_tx_bg_desc->ha_length = blend_trans_desc->in_bg.pic_w; + blend_engine->dma_tx_bg_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_bg_pixel_format); + blend_engine->dma_tx_bg_desc->y = blend_trans_desc->in_bg.block_offset_y; + blend_engine->dma_tx_bg_desc->x = blend_trans_desc->in_bg.block_offset_x; + blend_engine->dma_tx_bg_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + blend_engine->dma_tx_bg_desc->buffer = (void *)blend_trans_desc->in_bg.buffer; + blend_engine->dma_tx_bg_desc->next = NULL; + + blend_engine->dma_tx_fg_desc->vb_size = blend_trans_desc->in_fg.block_h; + blend_engine->dma_tx_fg_desc->hb_length = blend_trans_desc->in_fg.block_w; + blend_engine->dma_tx_fg_desc->err_eof = 0; + blend_engine->dma_tx_fg_desc->dma2d_en = 1; + blend_engine->dma_tx_fg_desc->suc_eof = 1; + blend_engine->dma_tx_fg_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + blend_engine->dma_tx_fg_desc->va_size = blend_trans_desc->in_fg.pic_h; + blend_engine->dma_tx_fg_desc->ha_length = blend_trans_desc->in_fg.pic_w; + blend_engine->dma_tx_fg_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_fg_pixel_format); + blend_engine->dma_tx_fg_desc->y = blend_trans_desc->in_fg.block_offset_y; + blend_engine->dma_tx_fg_desc->x = blend_trans_desc->in_fg.block_offset_x; + blend_engine->dma_tx_fg_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + blend_engine->dma_tx_fg_desc->buffer = (void *)blend_trans_desc->in_fg.buffer; + blend_engine->dma_tx_fg_desc->next = NULL; + + blend_engine->dma_rx_desc->vb_size = blend_trans_desc->in_fg.block_h; // in_bg.block_h == in_fg.block_h + blend_engine->dma_rx_desc->hb_length = blend_trans_desc->in_fg.block_w; // in_bg.block_w == in_fg.block_w + blend_engine->dma_rx_desc->err_eof = 0; + blend_engine->dma_rx_desc->dma2d_en = 1; + blend_engine->dma_rx_desc->suc_eof = 1; + blend_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + blend_engine->dma_rx_desc->va_size = blend_trans_desc->out.pic_h; + blend_engine->dma_rx_desc->ha_length = blend_trans_desc->out.pic_w; + blend_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format); + blend_engine->dma_rx_desc->y = blend_trans_desc->out.block_offset_y; + blend_engine->dma_rx_desc->x = blend_trans_desc->out.block_offset_x; + blend_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + blend_engine->dma_rx_desc->buffer = (void *)blend_trans_desc->out.buffer; + blend_engine->dma_rx_desc->next = NULL; + + esp_cache_msync((void *)blend_engine->dma_tx_bg_desc, platform->dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)blend_engine->dma_tx_fg_desc, platform->dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)blend_engine->dma_rx_desc, platform->dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + + // Configure 2D-DMA channels + dma2d_trigger_t trig_periph = { + .periph = DMA2D_TRIG_PERIPH_PPA_BLEND, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_BG_TX, + }; + dma2d_connect(dma2d_tx_bg_chan, &trig_periph); + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_FG_TX; + dma2d_connect(dma2d_tx_fg_chan, &trig_periph); + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_RX; + dma2d_connect(dma2d_rx_chan, &trig_periph); + + dma2d_transfer_ability_t dma_transfer_ability = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + }; + dma2d_set_transfer_ability(dma2d_tx_bg_chan, &dma_transfer_ability); + dma2d_set_transfer_ability(dma2d_tx_fg_chan, &dma_transfer_ability); + dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); + + dma2d_rx_event_callbacks_t dma_event_cbs = { + .on_recv_eof = ppa_transaction_done_cb, + }; + dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); + + dma2d_set_desc_addr(dma2d_tx_bg_chan, (intptr_t)blend_engine->dma_tx_bg_desc); + dma2d_set_desc_addr(dma2d_tx_fg_chan, (intptr_t)blend_engine->dma_tx_fg_desc); + dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)blend_engine->dma_rx_desc); + dma2d_start(dma2d_tx_bg_chan); + dma2d_start(dma2d_tx_fg_chan); + dma2d_start(dma2d_rx_chan); + + // Configure PPA Blending engine + ppa_ll_blend_set_rx_bg_color_mode(platform->hal.dev, blend_trans_desc->in_bg.blend_cm); + ppa_ll_blend_enable_rx_bg_byte_swap(platform->hal.dev, blend_trans_desc->bg_byte_swap); + ppa_ll_blend_enable_rx_bg_rgb_swap(platform->hal.dev, blend_trans_desc->bg_rgb_swap); + ppa_ll_blend_configure_rx_bg_alpha(platform->hal.dev, blend_trans_desc->bg_alpha_update_mode, blend_trans_desc->bg_alpha_value); + + ppa_ll_blend_set_rx_fg_color_mode(platform->hal.dev, blend_trans_desc->in_fg.blend_cm); + if (COLOR_SPACE_TYPE((uint32_t)blend_trans_desc->in_fg.blend_cm) == COLOR_SPACE_ALPHA) { + ppa_ll_blend_set_rx_fg_fix_rgb(platform->hal.dev, blend_trans_desc->fg_fix_rgb_val); + } + ppa_ll_blend_enable_rx_fg_byte_swap(platform->hal.dev, blend_trans_desc->fg_byte_swap); + ppa_ll_blend_enable_rx_fg_rgb_swap(platform->hal.dev, blend_trans_desc->fg_rgb_swap); + ppa_ll_blend_configure_rx_fg_alpha(platform->hal.dev, blend_trans_desc->fg_alpha_update_mode, blend_trans_desc->fg_alpha_value); + + ppa_ll_blend_set_tx_color_mode(platform->hal.dev, blend_trans_desc->out.blend_cm); + + // Color keying + ppa_ll_blend_configure_rx_bg_ck_range(platform->hal.dev, + blend_trans_desc->bg_ck_en ? blend_trans_desc->bg_ck_rgb_low_thres : 0xFFFFFF, + blend_trans_desc->bg_ck_en ? blend_trans_desc->bg_ck_rgb_high_thres : 0); + ppa_ll_blend_configure_rx_fg_ck_range(platform->hal.dev, + blend_trans_desc->fg_ck_en ? blend_trans_desc->fg_ck_rgb_low_thres : 0xFFFFFF, + blend_trans_desc->fg_ck_en ? blend_trans_desc->fg_ck_rgb_high_thres : 0); + ppa_ll_blend_set_ck_default_rgb(platform->hal.dev, (blend_trans_desc->bg_ck_en && blend_trans_desc->fg_ck_en) ? blend_trans_desc->ck_rgb_default_val : 0); + ppa_ll_blend_enable_ck_fg_bg_reverse(platform->hal.dev, blend_trans_desc->ck_reverse_bg2fg); + + ppa_ll_blend_start(platform->hal.dev, PPA_LL_BLEND_TRANS_MODE_BLEND); + + // No need to yield + return false; +} + +esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_config_t *config) +{ + ESP_RETURN_ON_FALSE(ppa_client && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_client->oper_type == PPA_OPERATION_BLEND, ESP_ERR_INVALID_ARG, TAG, "client is not for blend operations"); + ESP_RETURN_ON_FALSE(config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region + ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); + uint32_t buf_alignment_size = (uint32_t)ppa_client->engine->platform->buf_alignment_size; + ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % buf_alignment_size == 0 && config->out.buffer_size % buf_alignment_size == 0, + ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); + color_space_pixel_format_t out_pixel_format = { + .color_type_id = config->out.blend_cm, + }; + uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); + ESP_RETURN_ON_FALSE(config->in_bg.block_w == config->in_fg.block_w && config->in_bg.block_h == config->in_fg.block_h, + ESP_ERR_INVALID_ARG, TAG, "in_bg.block_w/h must be equal to in_fg.block_w/h"); + if (config->bg_byte_swap) { + PPA_CHECK_CM_SUPPORT_BYTE_SWAP("in_bg.blend", (uint32_t)config->in_bg.blend_cm); + } + if (config->bg_rgb_swap) { + PPA_CHECK_CM_SUPPORT_RGB_SWAP("in_bg.blend", (uint32_t)config->in_bg.blend_cm); + } + if (config->fg_byte_swap) { + PPA_CHECK_CM_SUPPORT_BYTE_SWAP("in_fg.blend", (uint32_t)config->in_fg.blend_cm); + } + if (config->fg_rgb_swap) { + PPA_CHECK_CM_SUPPORT_RGB_SWAP("in_fg.blend", (uint32_t)config->in_fg.blend_cm); + } + ESP_RETURN_ON_FALSE(config->bg_alpha_value <= 0xFF && config->fg_alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid bg/fg_alpha_value"); + if (config->in_bg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { + ESP_RETURN_ON_FALSE(config->in_bg.block_w % 2 == 0 && config->in_bg.block_offset_x % 2 == 0, + ESP_ERR_INVALID_ARG, TAG, "in_bg.block_w and in_bg.block_offset_x must be even"); + } + if (config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_A4 || config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { + ESP_RETURN_ON_FALSE(config->in_fg.block_w % 2 == 0 && config->in_fg.block_offset_x % 2 == 0, + ESP_ERR_INVALID_ARG, TAG, "in_fg.block_w and in_fg.block_offset_x must be even"); + } + // To reduce complexity, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions + + // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) + // Write back in_bg_buffer, in_fg_buffer + color_space_pixel_format_t in_bg_pixel_format = { + .color_type_id = config->in_bg.blend_cm, + }; + uint32_t in_bg_pic_len = config->in_bg.pic_w * config->in_bg.pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; + esp_cache_msync(config->in_bg.buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + color_space_pixel_format_t in_fg_pixel_format = { + .color_type_id = config->in_fg.blend_cm, + }; + uint32_t in_fg_pic_len = config->in_fg.pic_w * config->in_fg.pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; + esp_cache_msync(config->in_fg.buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + // Invalidate out_buffer + esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + + esp_err_t ret = ESP_OK; + ppa_trans_t *trans_elm = NULL; + if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; + + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; + + ppa_blend_oper_t *blend_trans_desc = (ppa_blend_oper_t *)trans_on_picked_desc->blend_desc; + memcpy(blend_trans_desc, config, sizeof(ppa_blend_oper_config_t)); + + trans_on_picked_desc->ppa_engine = ppa_client->engine; + trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; + + dma_trans_desc->tx_channel_num = 2; + dma_trans_desc->rx_channel_num = 1; + dma_trans_desc->channel_flags = 0; + dma_trans_desc->specified_tx_channel_mask = 0; + dma_trans_desc->specified_rx_channel_mask = 0; + + trans_elm->client = ppa_client; + trans_elm->user_data = config->user_data; + xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semaphore before transaction starts + + ret = ppa_do_operation(ppa_client, ppa_client->engine, trans_elm, config->mode); + if (ret != ESP_OK) { + ppa_recycle_transaction(ppa_client, trans_elm); + } + } else { + ret = ESP_FAIL; + ESP_LOGE(TAG, "exceed maximum pending transactions for the client, consider increase max_pending_trans_num"); + } + return ret; +} diff --git a/components/esp_driver_ppa/src/ppa_core.c b/components/esp_driver_ppa/src/ppa_core.c new file mode 100644 index 0000000000..b08b4a3987 --- /dev/null +++ b/components/esp_driver_ppa/src/ppa_core.c @@ -0,0 +1,538 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_check.h" +#include "esp_log.h" +#include "freertos/portmacro.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/idf_additions.h" +#include "freertos/queue.h" +#include "esp_heap_caps.h" +#include "esp_cache.h" +#include "esp_private/esp_cache_private.h" +#include "hal/cache_hal.h" +#include "hal/cache_ll.h" +#include "driver/ppa.h" +#include "ppa_priv.h" +#include "esp_private/dma2d.h" +#include "hal/dma2d_ll.h" +#include "hal/ppa_hal.h" +#include "hal/ppa_ll.h" +#include "hal/ppa_types.h" +#include "esp_private/periph_ctrl.h" + +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + +static const char *TAG = "ppa_core"; + +// PPA driver platform +static ppa_platform_t s_platform = { + .spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED, +}; + +static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine); +static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine); +static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t trans_elm_num, ppa_operation_t oper_type); +static void ppa_free_transaction(ppa_trans_t *trans_elm); + +const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATION_NUM] = { + ppa_srm_transaction_on_picked, + ppa_blend_transaction_on_picked, + ppa_fill_transaction_on_picked, +}; + +static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(config && ret_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(config->engine == PPA_ENGINE_TYPE_SRM || config->engine == PPA_ENGINE_TYPE_BLEND, ESP_ERR_INVALID_ARG, TAG, "invalid engine"); + + *ret_engine = NULL; + + uint32_t data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); + size_t alignment = MAX(DMA2D_LL_DESC_ALIGNMENT, data_cache_line_size); + + _lock_acquire(&s_platform.mutex); + if (s_platform.dma_desc_mem_size == 0) { + s_platform.dma_desc_mem_size = ALIGN_UP(sizeof(dma2d_descriptor_align8_t), alignment); + } + if (s_platform.buf_alignment_size == 0) { + esp_cache_get_alignment(MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA, &s_platform.buf_alignment_size); + } + + if (config->engine == PPA_ENGINE_TYPE_SRM) { + if (!s_platform.srm) { + ppa_srm_engine_t *srm_engine = heap_caps_calloc(1, sizeof(ppa_srm_engine_t), PPA_MEM_ALLOC_CAPS); + SemaphoreHandle_t srm_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *srm_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *srm_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + if (srm_engine && srm_sem && srm_tx_dma_desc && srm_rx_dma_desc) { + srm_engine->dma_tx_desc = srm_tx_dma_desc; + srm_engine->dma_rx_desc = srm_rx_dma_desc; + srm_engine->base.platform = &s_platform; + srm_engine->base.type = PPA_ENGINE_TYPE_SRM; + srm_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + srm_engine->base.sem = srm_sem; + xSemaphoreGive(srm_engine->base.sem); + STAILQ_INIT(&srm_engine->base.trans_stailq); + s_platform.srm = srm_engine; + s_platform.srm_engine_ref_count++; + *ret_engine = &srm_engine->base; + + // TODO: Register PPA interrupt? Useful for SRM parameter error. If SRM parameter error, blocks at 2D-DMA, transaction can never finish, stuck... + // need a way to force end + } else { + ret = ESP_ERR_NO_MEM; + ESP_LOGE(TAG, "no mem to register PPA SRM engine"); + free(srm_engine); + if (srm_sem) { + vSemaphoreDeleteWithCaps(srm_sem); + } + free(srm_tx_dma_desc); + free(srm_rx_dma_desc); + } + +#if CONFIG_PM_ENABLE + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_srm", &srm_engine->base.pm_lock); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "create pm lock failed"); + } +#endif + } else { + // SRM engine already registered + s_platform.srm_engine_ref_count++; + *ret_engine = &s_platform.srm->base; + } + } else if (config->engine == PPA_ENGINE_TYPE_BLEND) { + if (!s_platform.blending) { + ppa_blend_engine_t *blending_engine = heap_caps_calloc(1, sizeof(ppa_blend_engine_t), PPA_MEM_ALLOC_CAPS); + SemaphoreHandle_t blending_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + if (blending_engine && blending_sem && blending_tx_bg_dma_desc && blending_tx_fg_dma_desc && blending_rx_dma_desc) { + blending_engine->dma_tx_bg_desc = blending_tx_bg_dma_desc; + blending_engine->dma_tx_fg_desc = blending_tx_fg_dma_desc; + blending_engine->dma_rx_desc = blending_rx_dma_desc; + blending_engine->base.platform = &s_platform; + blending_engine->base.type = PPA_ENGINE_TYPE_BLEND; + blending_engine->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + blending_engine->base.sem = blending_sem; + xSemaphoreGive(blending_engine->base.sem); + STAILQ_INIT(&blending_engine->base.trans_stailq); + s_platform.blending = blending_engine; + s_platform.blend_engine_ref_count++; + *ret_engine = &blending_engine->base; + } else { + ret = ESP_ERR_NO_MEM; + ESP_LOGE(TAG, "no mem to register PPA Blending engine"); + free(blending_engine); + if (blending_sem) { + vSemaphoreDeleteWithCaps(blending_sem); + } + free(blending_tx_bg_dma_desc); + free(blending_tx_fg_dma_desc); + free(blending_rx_dma_desc); + } + +#if CONFIG_PM_ENABLE + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_blending", &blending_engine->base.pm_lock); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "create pm lock failed"); + } +#endif + } else { + // Blending engine already registered + s_platform.blend_engine_ref_count++; + *ret_engine = &s_platform.blending->base; + } + } + + if (ret == ESP_OK) { + if (!s_platform.hal.dev) { + assert(!s_platform.dma2d_pool_handle); + + // Enable the bus clock to access PPA registers + PERIPH_RCC_ATOMIC() { + ppa_ll_enable_bus_clock(true); + ppa_ll_reset_register(); + } + + ppa_hal_init(&s_platform.hal); // initialize HAL context + + // Get 2D-DMA pool handle + dma2d_pool_config_t dma2d_config = { + .pool_id = 0, + }; + ret = dma2d_acquire_pool(&dma2d_config, &s_platform.dma2d_pool_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "install 2D-DMA failed"); + goto wrap_up; + } + +#if CONFIG_PM_ENABLE + assert(!s_platform.pm_lock); + // Create and acquire the PM lock + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa", &s_platform.pm_lock); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "create pm lock failed"); + goto wrap_up; + } +#endif + } + } +wrap_up: + _lock_release(&s_platform.mutex); + + if (ret != ESP_OK && *ret_engine != NULL) { + ppa_engine_release(*ret_engine); + } + + return ret; +} + +static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(ppa_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + _lock_acquire(&s_platform.mutex); + if (ppa_engine->type == PPA_ENGINE_TYPE_SRM) { + ppa_srm_engine_t *srm_engine = __containerof(ppa_engine, ppa_srm_engine_t, base); + s_platform.srm_engine_ref_count--; + if (s_platform.srm_engine_ref_count == 0) { + assert(STAILQ_EMPTY(&srm_engine->base.trans_stailq)); + // Now, time to free + s_platform.srm = NULL; + free(srm_engine->dma_tx_desc); + free(srm_engine->dma_rx_desc); + vSemaphoreDeleteWithCaps(srm_engine->base.sem); +#if CONFIG_PM_ENABLE + if (srm_engine->base.pm_lock) { + assert(esp_pm_lock_delete(srm_engine->base.pm_lock) == ESP_OK); + } +#endif + free(srm_engine); + } + } else if (ppa_engine->type == PPA_ENGINE_TYPE_BLEND) { + ppa_blend_engine_t *blending_engine = __containerof(ppa_engine, ppa_blend_engine_t, base); + s_platform.blend_engine_ref_count--; + if (s_platform.blend_engine_ref_count == 0) { + assert(STAILQ_EMPTY(&blending_engine->base.trans_stailq)); + // Now, time to free + s_platform.blending = NULL; + free(blending_engine->dma_tx_bg_desc); + free(blending_engine->dma_tx_fg_desc); + free(blending_engine->dma_rx_desc); + vSemaphoreDeleteWithCaps(blending_engine->base.sem); +#if CONFIG_PM_ENABLE + if (blending_engine->base.pm_lock) { + assert(esp_pm_lock_delete(blending_engine->base.pm_lock) == ESP_OK); + } +#endif + free(blending_engine); + } + } + + if (!s_platform.srm && !s_platform.blending) { + assert(s_platform.srm_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0); + +#if CONFIG_PM_ENABLE + if (s_platform.pm_lock) { + assert(esp_pm_lock_delete(s_platform.pm_lock) == ESP_OK); + } +#endif + + if (s_platform.dma2d_pool_handle) { + dma2d_release_pool(s_platform.dma2d_pool_handle); // TODO: check return value. If not ESP_OK, then must be error on other 2D-DMA clients :( Give a warning log? + s_platform.dma2d_pool_handle = NULL; + } + + ppa_hal_deinit(&s_platform.hal); // De-initialize HAL context + + // Disable the bus clock to access PPA registers + PERIPH_RCC_ATOMIC() { + ppa_ll_enable_bus_clock(false); + } + } + _lock_release(&s_platform.mutex); + return ret; +} + +esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_handle_t *ret_client) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(config && ret_client, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(config->oper_type < PPA_OPERATION_NUM, ESP_ERR_INVALID_ARG, TAG, "unknown operation"); + + ppa_client_t *client = (ppa_client_t *)heap_caps_calloc(1, sizeof(ppa_client_t), PPA_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(client, ESP_ERR_NO_MEM, TAG, "no mem to register client"); + + uint32_t ring_buf_size = MAX(1, config->max_pending_trans_num); + client->trans_elm_ptr_queue = xQueueCreateWithCaps(ring_buf_size, sizeof(uint32_t), PPA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(client->trans_elm_ptr_queue && ppa_malloc_transaction(client->trans_elm_ptr_queue, ring_buf_size, config->oper_type), + ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); + + client->oper_type = config->oper_type; + client->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + client->in_accepting_trans_state = true; + if (config->oper_type == PPA_OPERATION_SRM) { + ppa_engine_config_t engine_config = { + .engine = PPA_ENGINE_TYPE_SRM, + }; + ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire SRM engine"); + } else if (config->oper_type == PPA_OPERATION_BLEND || config->oper_type == PPA_OPERATION_FILL) { + ppa_engine_config_t engine_config = { + .engine = PPA_ENGINE_TYPE_BLEND, + }; + ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire Blending engine"); + } + *ret_client = client; + +err: + if (ret != ESP_OK) { + ppa_unregister_client(client); + } + return ret; +} + +esp_err_t ppa_unregister_client(ppa_client_handle_t ppa_client) +{ + ESP_RETURN_ON_FALSE(ppa_client, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + bool do_unregister = false; + portENTER_CRITICAL(&ppa_client->spinlock); + if (ppa_client->trans_cnt == 0) { + ppa_client->in_accepting_trans_state = false; + do_unregister = true; + } + portEXIT_CRITICAL(&ppa_client->spinlock); + ESP_RETURN_ON_FALSE(do_unregister, ESP_ERR_INVALID_STATE, TAG, "client still has unprocessed trans"); + + if (ppa_client->engine) { + ppa_engine_release(ppa_client->engine); + } + + if (ppa_client->trans_elm_ptr_queue) { + ppa_trans_t *trans_elm = NULL; + while (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + ppa_free_transaction(trans_elm); + } + vQueueDeleteWithCaps(ppa_client->trans_elm_ptr_queue); + } + free(ppa_client); + return ESP_OK; +} + +esp_err_t ppa_client_register_event_callbacks(ppa_client_handle_t ppa_client, const ppa_event_callbacks_t *cbs) +{ + ESP_RETURN_ON_FALSE(ppa_client && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + ppa_client->done_cb = cbs->on_trans_done; + return ESP_OK; +} + +// Each PPA engine should only have one transaction being pushed to 2D-DMA queue, the rest transactions should stay in engine's own transaction queue. +// This is to avoid 2D-DMA channels being hold, but not actually being used (waiting for PPA engine to be free) +static esp_err_t ppa_dma2d_enqueue(const ppa_trans_t *trans_elm) +{ + return dma2d_enqueue(s_platform.dma2d_pool_handle, trans_elm->trans_desc, trans_elm->dma_trans_placeholder); +} + +static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t trans_elm_num, ppa_operation_t oper_type) +{ + bool res = true; + size_t ppa_trans_desc_size = (oper_type == PPA_OPERATION_SRM) ? sizeof(ppa_srm_oper_t) : + (oper_type == PPA_OPERATION_BLEND) ? sizeof(ppa_blend_oper_t) : + (oper_type == PPA_OPERATION_FILL) ? sizeof(ppa_fill_oper_t) : 0; + assert(ppa_trans_desc_size != 0); + size_t trans_elm_storage_size = sizeof(ppa_trans_t) + SIZEOF_DMA2D_TRANS_T + sizeof(dma2d_trans_config_t) + sizeof(ppa_dma2d_trans_on_picked_config_t) + ppa_trans_desc_size; + for (int i = 0; i < trans_elm_num; i++) { + void *trans_elm_storage = heap_caps_calloc(1, trans_elm_storage_size, PPA_MEM_ALLOC_CAPS); + SemaphoreHandle_t ppa_trans_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); + + if (!trans_elm_storage || !ppa_trans_sem) { + if (trans_elm_storage) { + free(trans_elm_storage); + } + if (ppa_trans_sem) { + vSemaphoreDeleteWithCaps(ppa_trans_sem); + } + res = false; + break; + } + + // Construct trans_elm + ppa_trans_t *new_trans_elm = (ppa_trans_t *)trans_elm_storage; + dma2d_trans_t *dma_trans_elm = (dma2d_trans_t *)((uint32_t)trans_elm_storage + sizeof(ppa_trans_t)); + dma2d_trans_config_t *dma_trans_desc = (dma2d_trans_config_t *)((uint32_t)dma_trans_elm + SIZEOF_DMA2D_TRANS_T); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)((uint32_t)dma_trans_desc + sizeof(dma2d_trans_config_t)); + void *ppa_trans_desc = (void *)((uint32_t)trans_on_picked_desc + sizeof(ppa_dma2d_trans_on_picked_config_t)); + + trans_on_picked_desc->op_desc = ppa_trans_desc; + trans_on_picked_desc->trans_elm = new_trans_elm; + dma_trans_desc->user_config = (void *)trans_on_picked_desc; + dma_trans_desc->on_job_picked = ppa_oper_trans_on_picked_func[oper_type]; // TODO: This maybe better to be in the ppa_do_xxx function + new_trans_elm->trans_desc = dma_trans_desc; + new_trans_elm->dma_trans_placeholder = dma_trans_elm; + new_trans_elm->sem = ppa_trans_sem; + + // Fill the ring buffer with allocated transaction element pointer + assert(xQueueSend(trans_elm_ptr_queue, &new_trans_elm, 0)); + } + return res; +} + +static void ppa_free_transaction(ppa_trans_t *trans_elm) +{ + if (trans_elm) { + if (trans_elm->sem) { + vSemaphoreDeleteWithCaps(trans_elm->sem); + } + free(trans_elm); + } +} + +bool ppa_recycle_transaction(ppa_client_handle_t ppa_client, ppa_trans_t *trans_elm) +{ + // Reset transaction and send back to client's trans_elm_ptr_queue + // TODO: To be very safe, we shall memset all to 0, and reconnect necessary pointers? + BaseType_t HPTaskAwoken; + assert(xQueueSendFromISR(ppa_client->trans_elm_ptr_queue, &trans_elm, &HPTaskAwoken)); + return HPTaskAwoken; +} + +esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_engine_base, ppa_trans_t *trans_elm, ppa_trans_mode_t mode) +{ + esp_err_t ret = ESP_OK; + +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_acquire(s_platform.pm_lock) == ESP_OK) && "acquire pm_lock failed"); +#endif + + portENTER_CRITICAL(&ppa_client->spinlock); + // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! + if (ppa_client->in_accepting_trans_state) { + // Send transaction into PPA engine queue + STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry); + ppa_client->trans_cnt++; + } else { + ret = ESP_FAIL; + } + portEXIT_CRITICAL(&ppa_client->spinlock); + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "The client cannot accept transaction now"); + goto err; + } + + TickType_t ticks_to_wait = (mode == PPA_TRANS_MODE_NON_BLOCKING) ? 0 : portMAX_DELAY; + if (xSemaphoreTake(ppa_engine_base->sem, ticks_to_wait) == pdTRUE) { + // Check if the transaction has already been started from the ISR + // If so, then the transaction should have been removed from queue at this moment (transaction completed) + bool found = false; + ppa_trans_t *temp = NULL; + portENTER_CRITICAL(&ppa_engine_base->spinlock); + STAILQ_FOREACH(temp, &ppa_engine_base->trans_stailq, entry) { + if (temp == trans_elm) { + found = true; + break; + } + } + portEXIT_CRITICAL(&ppa_engine_base->spinlock); + if (found) { +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_acquire(ppa_engine_base->pm_lock) == ESP_OK) && "acquire pm_lock failed"); +#endif + ret = ppa_dma2d_enqueue(trans_elm); + if (ret != ESP_OK) { + portENTER_CRITICAL(&ppa_engine_base->spinlock); + STAILQ_REMOVE(&ppa_engine_base->trans_stailq, trans_elm, ppa_trans_s, entry); + portEXIT_CRITICAL(&ppa_engine_base->spinlock); +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_release(ppa_engine_base->pm_lock) == ESP_OK) && "release pm_lock failed"); +#endif + xSemaphoreGive(ppa_engine_base->sem); + portENTER_CRITICAL(&ppa_client->spinlock); + ppa_client->trans_cnt--; + portEXIT_CRITICAL(&ppa_client->spinlock); + goto err; + } + } else { + xSemaphoreGive(ppa_engine_base->sem); + } + } + + if (mode == PPA_TRANS_MODE_BLOCKING) { + // while (1) { + // printf("ppa intr: %ld\n", PPA.int_raw.val); + // } + xSemaphoreTake(trans_elm->sem, portMAX_DELAY); // Given in the ISR + // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) + // ppa_recycle_transaction(ppa_client, trans_elm); // TODO: Do we need it to be here or can be at the end of done_cb? + } + +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_release(s_platform.pm_lock) == ESP_OK) && "release pm_lock failed"); +#endif + +err: + return ret; +} + +bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) +{ + bool need_yield = false; + BaseType_t HPTaskAwoken; + ppa_trans_t *trans_elm = (ppa_trans_t *)user_data; + ppa_client_t *client = trans_elm->client; + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; + ppa_engine_t *engine_base = trans_on_picked_desc->ppa_engine; + + if (client->done_cb) { + ppa_event_data_t edata = {}; + need_yield |= client->done_cb(client, &edata, trans_elm->user_data); + } + + ppa_trans_t *next_start_trans = NULL; + portENTER_CRITICAL_ISR(&engine_base->spinlock); + // Remove this transaction from transaction queue + STAILQ_REMOVE(&engine_base->trans_stailq, trans_elm, ppa_trans_s, entry); + next_start_trans = STAILQ_FIRST(&engine_base->trans_stailq); + portEXIT_CRITICAL_ISR(&engine_base->spinlock); + + // If there is next trans in PPA engine queue, send it to DMA queue; otherwise, release the engine semaphore + if (next_start_trans) { + ppa_dma2d_enqueue(next_start_trans); + } else { +#if CONFIG_PM_ENABLE + assert((esp_pm_lock_release(engine_base->pm_lock) == ESP_OK)); +#endif + xSemaphoreGiveFromISR(engine_base->sem, &HPTaskAwoken); + need_yield |= (HPTaskAwoken == pdTRUE); + } + // Recycle transaction and release transaction semaphore + // if (trans_elm->sem != NULL) { + xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); + need_yield |= (HPTaskAwoken == pdTRUE); + // } + + // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! + portENTER_CRITICAL_ISR(&client->spinlock); + need_yield |= ppa_recycle_transaction(client, trans_elm); + client->trans_cnt--; + portEXIT_CRITICAL_ISR(&client->spinlock); + + return need_yield; +} diff --git a/components/esp_driver_ppa/src/ppa_fill.c b/components/esp_driver_ppa/src/ppa_fill.c new file mode 100644 index 0000000000..476b4f36cf --- /dev/null +++ b/components/esp_driver_ppa/src/ppa_fill.c @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_check.h" +#include "driver/ppa.h" +#include "ppa_priv.h" +#include "esp_private/dma2d.h" +#include "hal/ppa_ll.h" +#include "esp_cache.h" +#include "esp_memory_utils.h" +#include "soc/dma2d_channel.h" + +static const char *TAG = "ppa_fill"; + +bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +{ + assert(num_chans == 1 && dma2d_chans && user_config); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; + assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_BLEND && trans_on_picked_desc->fill_desc && trans_on_picked_desc->ppa_engine); + + ppa_fill_oper_t *fill_trans_desc = (ppa_fill_oper_t *)trans_on_picked_desc->fill_desc; + ppa_blend_engine_t *blend_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_blend_engine_t, base); + ppa_platform_t *platform = blend_engine->base.platform; + + // Reset blending engine + ppa_ll_blend_reset(platform->hal.dev); + + // Get the required 2D-DMA channel handles + assert(dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX); + dma2d_channel_handle_t dma2d_rx_chan = dma2d_chans[0].chan; + + color_space_pixel_format_t out_pixel_format = { + .color_type_id = fill_trans_desc->out.fill_cm, + }; + + // Fill 2D-DMA descriptors + blend_engine->dma_rx_desc->vb_size = fill_trans_desc->fill_block_h; + blend_engine->dma_rx_desc->hb_length = fill_trans_desc->fill_block_w; + blend_engine->dma_rx_desc->err_eof = 0; + blend_engine->dma_rx_desc->dma2d_en = 1; + blend_engine->dma_rx_desc->suc_eof = 1; + blend_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + blend_engine->dma_rx_desc->va_size = fill_trans_desc->out.pic_h; + blend_engine->dma_rx_desc->ha_length = fill_trans_desc->out.pic_w; + blend_engine->dma_rx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format); + blend_engine->dma_rx_desc->y = fill_trans_desc->out.block_offset_y; + blend_engine->dma_rx_desc->x = fill_trans_desc->out.block_offset_x; + blend_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + blend_engine->dma_rx_desc->buffer = (void *)fill_trans_desc->out.buffer; + blend_engine->dma_rx_desc->next = NULL; + + esp_cache_msync((void *)blend_engine->dma_rx_desc, platform->dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + + // Configure 2D-DMA channels + dma2d_trigger_t trig_periph = { + .periph = DMA2D_TRIG_PERIPH_PPA_BLEND, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_RX, + }; + dma2d_connect(dma2d_rx_chan, &trig_periph); + + dma2d_transfer_ability_t dma_transfer_ability = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + }; + dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); + + dma2d_rx_event_callbacks_t dma_event_cbs = { + .on_recv_eof = ppa_transaction_done_cb, + }; + dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); + + dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)blend_engine->dma_rx_desc); + dma2d_start(dma2d_rx_chan); + + // Configure PPA Blending engine + ppa_ll_blend_configure_filling_block(platform->hal.dev, fill_trans_desc->fill_argb_color, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h); + ppa_ll_blend_set_tx_color_mode(platform->hal.dev, fill_trans_desc->out.fill_cm); + + ppa_ll_blend_start(platform->hal.dev, PPA_LL_BLEND_TRANS_MODE_FILL); + + // No need to yield + return false; +} + +esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_config_t *config) +{ + ESP_RETURN_ON_FALSE(ppa_client && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_client->oper_type == PPA_OPERATION_FILL, ESP_ERR_INVALID_ARG, TAG, "client is not for fill operations"); + ESP_RETURN_ON_FALSE(config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + // out_buffer ptr cannot in flash region + ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); + uint32_t buf_alignment_size = (uint32_t)ppa_client->engine->platform->buf_alignment_size; + ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % buf_alignment_size == 0 && config->out.buffer_size % buf_alignment_size == 0, + ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); + color_space_pixel_format_t out_pixel_format = { + .color_type_id = config->out.fill_cm, + }; + uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); + // To reduce complexity, color_mode, fill_block_w/h correctness are checked in their corresponding LL functions + + // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) + esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + + esp_err_t ret = ESP_OK; + ppa_trans_t *trans_elm = NULL; + if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; + + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; + + ppa_fill_oper_t *fill_trans_desc = (ppa_fill_oper_t *)trans_on_picked_desc->fill_desc; + memcpy(fill_trans_desc, config, sizeof(ppa_fill_oper_config_t)); + + trans_on_picked_desc->ppa_engine = ppa_client->engine; + trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; + + dma_trans_desc->tx_channel_num = 0; + dma_trans_desc->rx_channel_num = 1; + dma_trans_desc->channel_flags = 0; + dma_trans_desc->specified_tx_channel_mask = 0; + dma_trans_desc->specified_rx_channel_mask = 0; + + trans_elm->client = ppa_client; + trans_elm->user_data = config->user_data; + xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semaphore before transaction starts + + ret = ppa_do_operation(ppa_client, ppa_client->engine, trans_elm, config->mode); + if (ret != ESP_OK) { + ppa_recycle_transaction(ppa_client, trans_elm); + } + } else { + ret = ESP_FAIL; + ESP_LOGE(TAG, "exceed maximum pending transactions for the client, consider increase max_pending_trans_num"); + } + return ret; +} diff --git a/components/esp_driver_ppa/src/ppa_priv.h b/components/esp_driver_ppa/src/ppa_priv.h index 3e1a003976..e55fa89954 100644 --- a/components/esp_driver_ppa/src/ppa_priv.h +++ b/components/esp_driver_ppa/src/ppa_priv.h @@ -15,6 +15,7 @@ #include "esp_private/dma2d.h" #include "hal/dma2d_types.h" #include "hal/ppa_types.h" +#include "hal/ppa_hal.h" #include "esp_pm.h" #ifdef __cplusplus @@ -33,12 +34,15 @@ extern "C" { ESP_RETURN_ON_FALSE(COLOR_SPACE_TYPE(color_type_id) == COLOR_SPACE_ARGB || COLOR_SPACE_TYPE(color_type_id) == COLOR_SPACE_RGB, \ ESP_ERR_INVALID_ARG, TAG, str "_cm does not support rgb_swap"); +typedef struct ppa_platform_t ppa_platform_t; + /******************************** ENGINE *************************************/ // PPA module contains SRM engine and Blending engine typedef struct ppa_engine_t ppa_engine_t; struct ppa_engine_t { + ppa_platform_t *platform; // PPA driver platform ppa_engine_type_t type; // Type of the PPA engine portMUX_TYPE spinlock; // Engine level spinlock SemaphoreHandle_t sem; // Semaphore for whether the engine is processing a transaction @@ -81,7 +85,7 @@ struct ppa_client_t { /****************************** OPERATION ************************************/ -// The elements in this structure listed first are identical to the elements in structure `ppa_srm_oper_trans_config_t` +// The elements in this structure listed first are identical to the elements in structure `ppa_srm_oper_config_t` // With adding a few extra elements in the end // This allows memcpy typedef struct { @@ -111,9 +115,9 @@ typedef struct { uint32_t scale_y_frag; } ppa_srm_oper_t; -typedef ppa_blend_oper_trans_config_t ppa_blend_oper_t; +typedef ppa_blend_oper_config_t ppa_blend_oper_t; -typedef ppa_fill_oper_trans_config_t ppa_fill_oper_t; +typedef ppa_fill_oper_config_t ppa_fill_oper_t; /***************************** TRANSACTION ***********************************/ @@ -139,6 +143,34 @@ typedef struct { dma2d_trigger_peripheral_t trigger_periph; // The 2D-DMA trigger peripheral } ppa_dma2d_trans_on_picked_config_t; +bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); +bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); +bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); + +esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_engine_base, ppa_trans_t *trans_elm, ppa_trans_mode_t mode); + +bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data); + +bool ppa_recycle_transaction(ppa_client_handle_t ppa_client, ppa_trans_t *trans_elm); + +/****************************** PPA DRIVER ***********************************/ + +struct ppa_platform_t { + _lock_t mutex; // Platform level mutex lock to protect the ppa_engine_acquire/ppa_engine_release process + portMUX_TYPE spinlock; // Platform level spinlock + ppa_hal_context_t hal; // PPA HAL context + dma2d_pool_handle_t dma2d_pool_handle; // Pointer to the acquired 2D-DMA pool + ppa_srm_engine_t *srm; // Pointer to the PPA SRM engine + ppa_blend_engine_t *blending; // Pointer to the PPA blending engine + uint32_t srm_engine_ref_count; // Reference count used to protect PPA SRM engine acquire and release + uint32_t blend_engine_ref_count; // Reference count used to protect PPA blending engine acquire and release + size_t buf_alignment_size; // Alignment requirement for the outgoing buffer addr and size to satisfy cache line size + uint32_t dma_desc_mem_size; // Alignment requirement for the 2D-DMA descriptor to satisfy cache line size +#if CONFIG_PM_ENABLE + esp_pm_lock_handle_t pm_lock; // Power management lock +#endif +}; + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_ppa/src/ppa_srm.c b/components/esp_driver_ppa/src/ppa_srm.c new file mode 100644 index 0000000000..80b5198aad --- /dev/null +++ b/components/esp_driver_ppa/src/ppa_srm.c @@ -0,0 +1,269 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_check.h" +#include "driver/ppa.h" +#include "ppa_priv.h" +#include "esp_private/dma2d.h" +#include "hal/ppa_ll.h" +#include "esp_cache.h" +#include "esp_memory_utils.h" +#include "soc/dma2d_channel.h" + +static const char *TAG = "ppa_srm"; + +bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +{ + assert(num_chans == 2 && dma2d_chans && user_config); + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)user_config; + assert(trans_on_picked_desc->trigger_periph == DMA2D_TRIG_PERIPH_PPA_SRM && trans_on_picked_desc->srm_desc && trans_on_picked_desc->ppa_engine); + + ppa_srm_oper_t *srm_trans_desc = (ppa_srm_oper_t *)trans_on_picked_desc->srm_desc; + ppa_srm_engine_t *srm_engine = __containerof(trans_on_picked_desc->ppa_engine, ppa_srm_engine_t, base); + ppa_platform_t *platform = srm_engine->base.platform; + + // Reset SRM engine + ppa_ll_srm_reset(platform->hal.dev); + + // Get the required 2D-DMA channel handles + dma2d_channel_handle_t dma2d_tx_chan = NULL; + dma2d_channel_handle_t dma2d_rx_chan = NULL; + for (uint32_t i = 0; i < num_chans; i++) { + if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_TX) { + dma2d_tx_chan = dma2d_chans[i].chan; + } + if (dma2d_chans[i].dir == DMA2D_CHANNEL_DIRECTION_RX) { + dma2d_rx_chan = dma2d_chans[i].chan; + } + } + assert(dma2d_tx_chan && dma2d_rx_chan); + + color_space_pixel_format_t in_pixel_format = { + .color_type_id = srm_trans_desc->in.srm_cm, + }; + + // Fill 2D-DMA descriptors + srm_engine->dma_tx_desc->vb_size = srm_trans_desc->in.block_h; + srm_engine->dma_tx_desc->hb_length = srm_trans_desc->in.block_w; + srm_engine->dma_tx_desc->err_eof = 0; + srm_engine->dma_tx_desc->dma2d_en = 1; + srm_engine->dma_tx_desc->suc_eof = 1; + srm_engine->dma_tx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + srm_engine->dma_tx_desc->va_size = srm_trans_desc->in.pic_h; + srm_engine->dma_tx_desc->ha_length = srm_trans_desc->in.pic_w; + srm_engine->dma_tx_desc->pbyte = dma2d_desc_pixel_format_to_pbyte_value(in_pixel_format); + srm_engine->dma_tx_desc->y = srm_trans_desc->in.block_offset_y; + srm_engine->dma_tx_desc->x = srm_trans_desc->in.block_offset_x; + srm_engine->dma_tx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + srm_engine->dma_tx_desc->buffer = (void *)srm_trans_desc->in.buffer; + srm_engine->dma_tx_desc->next = NULL; + + // vb_size, hb_length can be any value (auto writeback) + // However, if vb_size/hb_length is 0, it triggers 2D-DMA DESC_ERROR interrupt, and dma2d driver will automatically ends the transaction + // Moreover, for YUV420, hb/vb have to be even + // Therefore, we set them to 2 + srm_engine->dma_rx_desc->vb_size = 2; + srm_engine->dma_rx_desc->hb_length = 2; + srm_engine->dma_rx_desc->err_eof = 0; + srm_engine->dma_rx_desc->dma2d_en = 1; + srm_engine->dma_rx_desc->suc_eof = 1; + srm_engine->dma_rx_desc->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + srm_engine->dma_rx_desc->va_size = srm_trans_desc->out.pic_h; + srm_engine->dma_rx_desc->ha_length = srm_trans_desc->out.pic_w; + // pbyte can be any value + srm_engine->dma_rx_desc->y = srm_trans_desc->out.block_offset_y; + srm_engine->dma_rx_desc->x = srm_trans_desc->out.block_offset_x; + srm_engine->dma_rx_desc->mode = DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE; + srm_engine->dma_rx_desc->buffer = (void *)srm_trans_desc->out.buffer; + srm_engine->dma_rx_desc->next = NULL; + + esp_cache_msync((void *)srm_engine->dma_tx_desc, platform->dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)srm_engine->dma_rx_desc, platform->dma_desc_mem_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + + // Configure 2D-DMA channels + dma2d_trigger_t trig_periph = { + .periph = DMA2D_TRIG_PERIPH_PPA_SRM, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SRM_TX, + }; + dma2d_connect(dma2d_tx_chan, &trig_periph); + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_PPA_SRM_RX; + dma2d_connect(dma2d_rx_chan, &trig_periph); + + dma2d_transfer_ability_t dma_transfer_ability = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + // Configure the block size to be received by the SRM engine, which is passed from the 2D-DMA TX channel (i.e. 2D-DMA dscr-port mode) + .dscr_port_block_h = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, + .dscr_port_block_v = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, + }; + dma2d_set_transfer_ability(dma2d_tx_chan, &dma_transfer_ability); + dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); + + // YUV444 and YUV422 are not supported by PPA module, need to utilize 2D-DMA color space conversion feature to do a conversion + ppa_srm_color_mode_t ppa_in_color_mode = srm_trans_desc->in.srm_cm; + if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV444) { + ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; + dma2d_csc_config_t dma_tx_csc = {0}; + if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_601; + } else { + dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_709; + } + dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); + } else if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV422) { + ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; + dma2d_csc_config_t dma_tx_csc = {0}; + if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_601; + } else { + dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_709; + } + dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); + } + + ppa_srm_color_mode_t ppa_out_color_mode = srm_trans_desc->out.srm_cm; + if (ppa_out_color_mode == PPA_SRM_COLOR_MODE_YUV444) { + ppa_out_color_mode = PPA_SRM_COLOR_MODE_YUV420; + dma2d_csc_config_t dma_rx_csc = { + .rx_csc_option = DMA2D_CSC_RX_YUV420_TO_YUV444, + }; + dma2d_configure_color_space_conversion(dma2d_rx_chan, &dma_rx_csc); + } + + dma2d_rx_event_callbacks_t dma_event_cbs = { + .on_recv_eof = ppa_transaction_done_cb, + }; + dma2d_register_rx_event_callbacks(dma2d_rx_chan, &dma_event_cbs, (void *)trans_on_picked_desc->trans_elm); + + dma2d_set_desc_addr(dma2d_tx_chan, (intptr_t)srm_engine->dma_tx_desc); + dma2d_set_desc_addr(dma2d_rx_chan, (intptr_t)srm_engine->dma_rx_desc); + dma2d_start(dma2d_tx_chan); + dma2d_start(dma2d_rx_chan); + + // Configure PPA SRM engine + ppa_ll_srm_set_rx_color_mode(platform->hal.dev, ppa_in_color_mode); + if (COLOR_SPACE_TYPE((uint32_t)ppa_in_color_mode) == COLOR_SPACE_YUV) { + ppa_ll_srm_set_rx_yuv_range(platform->hal.dev, srm_trans_desc->in.yuv_range); + ppa_ll_srm_set_rx_yuv2rgb_std(platform->hal.dev, srm_trans_desc->in.yuv_std); + } + ppa_ll_srm_enable_rx_byte_swap(platform->hal.dev, srm_trans_desc->byte_swap); + ppa_ll_srm_enable_rx_rgb_swap(platform->hal.dev, srm_trans_desc->rgb_swap); + ppa_ll_srm_configure_rx_alpha(platform->hal.dev, srm_trans_desc->alpha_update_mode, srm_trans_desc->alpha_value); + + ppa_ll_srm_set_tx_color_mode(platform->hal.dev, ppa_out_color_mode); + if (COLOR_SPACE_TYPE((uint32_t)ppa_out_color_mode) == COLOR_SPACE_YUV) { + ppa_ll_srm_set_tx_yuv_range(platform->hal.dev, srm_trans_desc->out.yuv_range); + ppa_ll_srm_set_tx_rgb2yuv_std(platform->hal.dev, srm_trans_desc->out.yuv_std); + } + + ppa_ll_srm_set_rotation_angle(platform->hal.dev, srm_trans_desc->rotation_angle); + ppa_ll_srm_set_scaling_x(platform->hal.dev, srm_trans_desc->scale_x_int, srm_trans_desc->scale_x_frag); + ppa_ll_srm_set_scaling_y(platform->hal.dev, srm_trans_desc->scale_y_int, srm_trans_desc->scale_y_frag); + ppa_ll_srm_enable_mirror_x(platform->hal.dev, srm_trans_desc->mirror_x); + ppa_ll_srm_enable_mirror_y(platform->hal.dev, srm_trans_desc->mirror_y); + + ppa_ll_srm_start(platform->hal.dev); + + // No need to yield + return false; +} + +esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_srm_oper_config_t *config) +{ + ESP_RETURN_ON_FALSE(ppa_client && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ppa_client->oper_type == PPA_OPERATION_SRM, ESP_ERR_INVALID_ARG, TAG, "client is not for SRM operations"); + ESP_RETURN_ON_FALSE(config->mode <= PPA_TRANS_MODE_NON_BLOCKING, ESP_ERR_INVALID_ARG, TAG, "invalid mode"); + // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region + ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); + uint32_t buf_alignment_size = (uint32_t)ppa_client->engine->platform->buf_alignment_size; + ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % buf_alignment_size == 0 && config->out.buffer_size % buf_alignment_size == 0, + ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); + color_space_pixel_format_t out_pixel_format = { + .color_type_id = config->out.srm_cm, + }; + uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); + ESP_RETURN_ON_FALSE(config->scale_x < (PPA_LL_SRM_SCALING_INT_MAX + 1) && config->scale_x >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX) && + config->scale_y < (PPA_LL_SRM_SCALING_INT_MAX + 1) && config->scale_y >= (1.0 / PPA_LL_SRM_SCALING_FRAG_MAX), + ESP_ERR_INVALID_ARG, TAG, "invalid scale"); + uint32_t new_block_w = 0; + uint32_t new_block_h = 0; + if (config->rotation_angle == PPA_SRM_ROTATION_ANGLE_0 || config->rotation_angle == PPA_SRM_ROTATION_ANGLE_180) { + new_block_w = (uint32_t)(config->scale_x * config->in.block_w); + new_block_h = (uint32_t)(config->scale_y * config->in.block_h); + } else { + new_block_w = (uint32_t)(config->scale_y * config->in.block_h); + new_block_h = (uint32_t)(config->scale_x * config->in.block_w); + } + ESP_RETURN_ON_FALSE(new_block_w <= (config->out.pic_w - config->out.block_offset_x) && + new_block_h <= (config->out.pic_h - config->out.block_offset_y), + ESP_ERR_INVALID_ARG, TAG, "scale does not fit in the out pic"); + if (config->byte_swap) { + PPA_CHECK_CM_SUPPORT_BYTE_SWAP("in.srm", (uint32_t)config->in.srm_cm); + } + if (config->rgb_swap) { + PPA_CHECK_CM_SUPPORT_RGB_SWAP("in.srm", (uint32_t)config->in.srm_cm); + } + ESP_RETURN_ON_FALSE(config->alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_value"); + // To reduce complexity, rotation_angle, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions + // TODO: + // YUV420: in desc, ha/hb/va/vb/x/y must be even number + // What for YUV444/YUV422 + + // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) + // Write back in_buffer + color_space_pixel_format_t in_pixel_format = { + .color_type_id = config->in.srm_cm, + }; + uint32_t in_pic_len = config->in.pic_w * config->in.pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; + esp_cache_msync(config->in.buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + // Invalidate out_buffer + esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + + esp_err_t ret = ESP_OK; + ppa_trans_t *trans_elm = NULL; + if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; + + ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; + + ppa_srm_oper_t *srm_trans_desc = (ppa_srm_oper_t *)trans_on_picked_desc->srm_desc; + memcpy(srm_trans_desc, config, sizeof(ppa_srm_oper_config_t)); + srm_trans_desc->scale_x_int = (uint32_t)srm_trans_desc->scale_x; + srm_trans_desc->scale_x_frag = (uint32_t)(srm_trans_desc->scale_x * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; + srm_trans_desc->scale_y_int = (uint32_t)srm_trans_desc->scale_y; + srm_trans_desc->scale_y_frag = (uint32_t)(srm_trans_desc->scale_y * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; + + trans_on_picked_desc->ppa_engine = ppa_client->engine; + trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_SRM; + + dma_trans_desc->tx_channel_num = 1; + dma_trans_desc->rx_channel_num = 1; + dma_trans_desc->channel_flags = 0; + if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV422 || config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { + dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC; + } + if (config->out.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { + dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC; + } + dma_trans_desc->specified_tx_channel_mask = 0; + dma_trans_desc->specified_rx_channel_mask = 0; + + trans_elm->client = ppa_client; + trans_elm->user_data = config->user_data; + xSemaphoreTake(trans_elm->sem, 0); // Ensure no transaction semaphore before transaction starts + + ret = ppa_do_operation(ppa_client, ppa_client->engine, trans_elm, config->mode); + if (ret != ESP_OK) { + ppa_recycle_transaction(ppa_client, trans_elm); + } + } else { + ret = ESP_FAIL; + ESP_LOGE(TAG, "exceed maximum pending transactions for the client, consider increase max_pending_trans_num"); + } + return ret; +} From 916c0ef8d13bbc600c6faeedc592655f7f39b982 Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Thu, 18 Apr 2024 20:35:33 +0800 Subject: [PATCH 12/15] feat(ppa): add PPA driver support for ESP32P4 Fix pm_lock assert and assign to NULL Alpha value union Tested with CONFIG_PM_ENABLE --- .../esp_driver_ppa/include/driver/ppa.h | 35 +++++++---- components/esp_driver_ppa/src/ppa_blend.c | 19 +++++- components/esp_driver_ppa/src/ppa_core.c | 32 ++++++---- components/esp_driver_ppa/src/ppa_priv.h | 59 ++++++++++++++++--- components/esp_driver_ppa/src/ppa_srm.c | 10 +++- 5 files changed, 123 insertions(+), 32 deletions(-) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 1d783e9555..f903dc49b4 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -55,6 +55,8 @@ esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_hand /** * @brief Unregister a PPA client * + * @note This will also free the resources occupied by the client + * * @param[in] ppa_client PPA client handle, allocated by `ppa_register_client` * * @return @@ -170,10 +172,13 @@ typedef struct { bool rgb_swap; /*!< Whether to swap the input data in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) */ bool byte_swap; /*!< Whether to swap the input data in byte. Only available feature if input picture color mode is ARGB8888 or RGB565 */ ppa_alpha_update_mode_t alpha_update_mode; /*!< Select whether the alpha channel of the input picture needs update */ - uint32_t alpha_value; /*!< Range: 0 ~ 255 - When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) - When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) - When other alpha modes are selected, this field is not used */ + union { + uint32_t alpha_fix_val; /*!< Range: [0, 255] + When PPA_ALPHA_FIX_VALUE mode is selected, alpha_fix_val is the new alpha value to replace the input alpha value (output_alpha = alpha_fix_val) */ + float alpha_scale_ratio; /*!< Range: (0, 1) + When PPA_ALPHA_SCALE mode is selected, alpha_scale_ratio is the multiplier to the input alpha value (output_alpha = alpha_scale_ratio * input_alpha) + Ratio resolution is 1/256 */ + }; ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ @@ -205,17 +210,23 @@ typedef struct { bool bg_rgb_swap; /*!< Whether to swap the background input data in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) */ bool bg_byte_swap; /*!< Whether to swap the background input data in byte. Only available feature if input BG picture color mode is ARGB8888 or RGB565 */ ppa_alpha_update_mode_t bg_alpha_update_mode; /*!< Select whether the alpha channel of the input background picture needs update */ - uint32_t bg_alpha_value; /*!< Range: 0 ~ 255 - When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) - When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) - When other alpha modes are selected, this field is not used */ + union { + uint32_t bg_alpha_fix_val; /*!< Range: [0, 255] + When PPA_ALPHA_FIX_VALUE mode is selected, alpha_fix_val is the new alpha value to replace the input alpha value (output_alpha = alpha_fix_val) */ + float bg_alpha_scale_ratio; /*!< Range: (0, 1) + When PPA_ALPHA_SCALE mode is selected, alpha_scale_ratio is the multiplier to the input alpha value (output_alpha = alpha_scale_ratio * input_alpha) + Ratio resolution is 1/256 */ + }; bool fg_rgb_swap; /*!< Whether to swap the foreground input data in RGB (e.g. ARGB becomes BGRA, RGB becomes BGR) */ bool fg_byte_swap; /*!< Whether to swap the foreground input data in byte. Only available feature if input FG picture color mode is ARGB8888 or RGB565 */ ppa_alpha_update_mode_t fg_alpha_update_mode; /*!< Select whether the alpha channel of the input foreground picture needs update */ - uint32_t fg_alpha_value; /*!< Range: 0 ~ 255 - When PPA_ALPHA_FIX_VALUE mode is selected, alpha_value is the alpha value to be replaced with (output_alpha = alpha_value) - When PPA_ALPHA_SCALE mode is selected, alpha_value/256 is the multiplier to the input alpha value (output_alpha = input_alpha * alpha_value / 256) - When other alpha modes are selected, this field is not used */ + union { + uint32_t fg_alpha_fix_val; /*!< Range: [0, 255] + When PPA_ALPHA_FIX_VALUE mode is selected, alpha_fix_val is the new alpha value to replace the input alpha value (output_alpha = alpha_fix_val) */ + float fg_alpha_scale_ratio; /*!< Range: (0, 1) + When PPA_ALPHA_SCALE mode is selected, alpha_scale_ratio is the multiplier to the input alpha value (output_alpha = alpha_scale_ratio * input_alpha) + Ratio resolution is 1/256 */ + }; uint32_t fg_fix_rgb_val; /*!< When in_fg.blend_cm is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ // color-keying diff --git a/components/esp_driver_ppa/src/ppa_blend.c b/components/esp_driver_ppa/src/ppa_blend.c index a7a91200cb..420a091160 100644 --- a/components/esp_driver_ppa/src/ppa_blend.c +++ b/components/esp_driver_ppa/src/ppa_blend.c @@ -199,7 +199,22 @@ esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_conf if (config->fg_rgb_swap) { PPA_CHECK_CM_SUPPORT_RGB_SWAP("in_fg.blend", (uint32_t)config->in_fg.blend_cm); } - ESP_RETURN_ON_FALSE(config->bg_alpha_value <= 0xFF && config->fg_alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid bg/fg_alpha_value"); + uint32_t new_bg_alpha_value = 0; + if (config->bg_alpha_update_mode == PPA_ALPHA_FIX_VALUE) { + ESP_RETURN_ON_FALSE(config->bg_alpha_fix_val <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid bg_alpha_fix_val"); + new_bg_alpha_value = config->bg_alpha_fix_val; + } else if (config->bg_alpha_update_mode == PPA_ALPHA_SCALE) { + ESP_RETURN_ON_FALSE(config->bg_alpha_scale_ratio > 0 && config->bg_alpha_scale_ratio < 1, ESP_ERR_INVALID_ARG, TAG, "invalid bg_alpha_scale_ratio"); + new_bg_alpha_value = (uint32_t)(config->bg_alpha_scale_ratio * 256); + } + uint32_t new_fg_alpha_value = 0; + if (config->fg_alpha_update_mode == PPA_ALPHA_FIX_VALUE) { + ESP_RETURN_ON_FALSE(config->fg_alpha_fix_val <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid fg_alpha_fix_val"); + new_fg_alpha_value = config->fg_alpha_fix_val; + } else if (config->fg_alpha_update_mode == PPA_ALPHA_SCALE) { + ESP_RETURN_ON_FALSE(config->fg_alpha_scale_ratio > 0 && config->fg_alpha_scale_ratio < 1, ESP_ERR_INVALID_ARG, TAG, "invalid fg_alpha_scale_ratio"); + new_fg_alpha_value = (uint32_t)(config->fg_alpha_scale_ratio * 256); + } if (config->in_bg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { ESP_RETURN_ON_FALSE(config->in_bg.block_w % 2 == 0 && config->in_bg.block_offset_x % 2 == 0, ESP_ERR_INVALID_ARG, TAG, "in_bg.block_w and in_bg.block_offset_x must be even"); @@ -234,6 +249,8 @@ esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_conf ppa_blend_oper_t *blend_trans_desc = (ppa_blend_oper_t *)trans_on_picked_desc->blend_desc; memcpy(blend_trans_desc, config, sizeof(ppa_blend_oper_config_t)); + blend_trans_desc->bg_alpha_value = new_bg_alpha_value; + blend_trans_desc->fg_alpha_value = new_fg_alpha_value; trans_on_picked_desc->ppa_engine = ppa_client->engine; trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; diff --git a/components/esp_driver_ppa/src/ppa_core.c b/components/esp_driver_ppa/src/ppa_core.c index b08b4a3987..dc08fdced7 100644 --- a/components/esp_driver_ppa/src/ppa_core.c +++ b/components/esp_driver_ppa/src/ppa_core.c @@ -220,7 +220,8 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) vSemaphoreDeleteWithCaps(srm_engine->base.sem); #if CONFIG_PM_ENABLE if (srm_engine->base.pm_lock) { - assert(esp_pm_lock_delete(srm_engine->base.pm_lock) == ESP_OK); + ret = esp_pm_lock_delete(srm_engine->base.pm_lock); + assert(ret == ESP_OK); } #endif free(srm_engine); @@ -238,7 +239,8 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) vSemaphoreDeleteWithCaps(blending_engine->base.sem); #if CONFIG_PM_ENABLE if (blending_engine->base.pm_lock) { - assert(esp_pm_lock_delete(blending_engine->base.pm_lock) == ESP_OK); + ret = esp_pm_lock_delete(blending_engine->base.pm_lock); + assert(ret == ESP_OK); } #endif free(blending_engine); @@ -250,7 +252,9 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) #if CONFIG_PM_ENABLE if (s_platform.pm_lock) { - assert(esp_pm_lock_delete(s_platform.pm_lock) == ESP_OK); + ret = esp_pm_lock_delete(s_platform.pm_lock); + assert(ret == ESP_OK); + s_platform.pm_lock = NULL; } #endif @@ -389,7 +393,8 @@ static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t t new_trans_elm->sem = ppa_trans_sem; // Fill the ring buffer with allocated transaction element pointer - assert(xQueueSend(trans_elm_ptr_queue, &new_trans_elm, 0)); + BaseType_t sent = xQueueSend(trans_elm_ptr_queue, &new_trans_elm, 0); + assert(sent); } return res; } @@ -409,16 +414,19 @@ bool ppa_recycle_transaction(ppa_client_handle_t ppa_client, ppa_trans_t *trans_ // Reset transaction and send back to client's trans_elm_ptr_queue // TODO: To be very safe, we shall memset all to 0, and reconnect necessary pointers? BaseType_t HPTaskAwoken; - assert(xQueueSendFromISR(ppa_client->trans_elm_ptr_queue, &trans_elm, &HPTaskAwoken)); + BaseType_t sent = xQueueSendFromISR(ppa_client->trans_elm_ptr_queue, &trans_elm, &HPTaskAwoken); + assert(sent); return HPTaskAwoken; } esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_engine_base, ppa_trans_t *trans_elm, ppa_trans_mode_t mode) { esp_err_t ret = ESP_OK; + esp_err_t pm_lock_ret __attribute__((unused)); #if CONFIG_PM_ENABLE - assert((esp_pm_lock_acquire(s_platform.pm_lock) == ESP_OK) && "acquire pm_lock failed"); + pm_lock_ret = esp_pm_lock_acquire(s_platform.pm_lock); + assert((pm_lock_ret == ESP_OK) && "acquire pm_lock failed"); #endif portENTER_CRITICAL(&ppa_client->spinlock); @@ -453,7 +461,8 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng portEXIT_CRITICAL(&ppa_engine_base->spinlock); if (found) { #if CONFIG_PM_ENABLE - assert((esp_pm_lock_acquire(ppa_engine_base->pm_lock) == ESP_OK) && "acquire pm_lock failed"); + pm_lock_ret = esp_pm_lock_acquire(ppa_engine_base->pm_lock); + assert((pm_lock_ret == ESP_OK) && "acquire pm_lock failed"); #endif ret = ppa_dma2d_enqueue(trans_elm); if (ret != ESP_OK) { @@ -461,7 +470,8 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng STAILQ_REMOVE(&ppa_engine_base->trans_stailq, trans_elm, ppa_trans_s, entry); portEXIT_CRITICAL(&ppa_engine_base->spinlock); #if CONFIG_PM_ENABLE - assert((esp_pm_lock_release(ppa_engine_base->pm_lock) == ESP_OK) && "release pm_lock failed"); + pm_lock_ret = esp_pm_lock_release(ppa_engine_base->pm_lock); + assert((pm_lock_ret == ESP_OK) && "release pm_lock failed"); #endif xSemaphoreGive(ppa_engine_base->sem); portENTER_CRITICAL(&ppa_client->spinlock); @@ -484,7 +494,8 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng } #if CONFIG_PM_ENABLE - assert((esp_pm_lock_release(s_platform.pm_lock) == ESP_OK) && "release pm_lock failed"); + pm_lock_ret = esp_pm_lock_release(s_platform.pm_lock); + assert((pm_lock_ret == ESP_OK) && "release pm_lock failed"); #endif err: @@ -517,7 +528,8 @@ bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data ppa_dma2d_enqueue(next_start_trans); } else { #if CONFIG_PM_ENABLE - assert((esp_pm_lock_release(engine_base->pm_lock) == ESP_OK)); + esp_err_t pm_lock_ret = esp_pm_lock_release(engine_base->pm_lock); + assert(pm_lock_ret == ESP_OK); #endif xSemaphoreGiveFromISR(engine_base->sem, &HPTaskAwoken); need_yield |= (HPTaskAwoken == pdTRUE); diff --git a/components/esp_driver_ppa/src/ppa_priv.h b/components/esp_driver_ppa/src/ppa_priv.h index e55fa89954..827276b238 100644 --- a/components/esp_driver_ppa/src/ppa_priv.h +++ b/components/esp_driver_ppa/src/ppa_priv.h @@ -86,7 +86,7 @@ struct ppa_client_t { /****************************** OPERATION ************************************/ // The elements in this structure listed first are identical to the elements in structure `ppa_srm_oper_config_t` -// With adding a few extra elements in the end +// With adding a few extra elements at the end // This allows memcpy typedef struct { ppa_in_pic_blk_config_t in; @@ -103,19 +103,62 @@ typedef struct { bool rgb_swap; bool byte_swap; ppa_alpha_update_mode_t alpha_update_mode; - uint32_t alpha_value; + union { + uint32_t alpha_fix_val; + float alpha_scale_ratio; + }; ppa_trans_mode_t mode; - // uint32_t timeout; void *user_data; - uint32_t scale_x_int; - uint32_t scale_x_frag; - uint32_t scale_y_int; - uint32_t scale_y_frag; + uint32_t scale_x_int; // Calculation result for the integral part of the scale_x to be directly written to register + uint32_t scale_x_frag; // Calculation result for the fractional part of the scale_x to be directly written to register + uint32_t scale_y_int; // Calculation result for the integral part of the scale_y to be directly written to register + uint32_t scale_y_frag; // Calculation result for the fractional part of the scale_y to be directly written to register + uint32_t alpha_value; // Calculation result for the fix alpha value to be directly written to register } ppa_srm_oper_t; -typedef ppa_blend_oper_config_t ppa_blend_oper_t; +// The elements in this structure listed first are identical to the elements in structure `ppa_blend_oper_config_t` +// With adding a few extra elements at the end +// This allows memcpy +typedef struct { + ppa_in_pic_blk_config_t in_bg; + ppa_in_pic_blk_config_t in_fg; + ppa_out_pic_blk_config_t out; + + // input data manipulation + bool bg_rgb_swap; + bool bg_byte_swap; + ppa_alpha_update_mode_t bg_alpha_update_mode; + union { + uint32_t bg_alpha_fix_val; + float bg_alpha_scale_ratio; + }; + bool fg_rgb_swap; + bool fg_byte_swap; + ppa_alpha_update_mode_t fg_alpha_update_mode; + union { + uint32_t fg_alpha_fix_val; + float fg_alpha_scale_ratio; + }; + uint32_t fg_fix_rgb_val; + + // color-keying + bool bg_ck_en; + uint32_t bg_ck_rgb_low_thres; + uint32_t bg_ck_rgb_high_thres; + bool fg_ck_en; + uint32_t fg_ck_rgb_low_thres; + uint32_t fg_ck_rgb_high_thres; + uint32_t ck_rgb_default_val; + bool ck_reverse_bg2fg; + + ppa_trans_mode_t mode; + void *user_data; + + uint32_t bg_alpha_value; // Calculation result for the fix alpha value for BG to be directly written to register + uint32_t fg_alpha_value; // Calculation result for the fix alpha value for FG to be directly written to register +} ppa_blend_oper_t; typedef ppa_fill_oper_config_t ppa_fill_oper_t; diff --git a/components/esp_driver_ppa/src/ppa_srm.c b/components/esp_driver_ppa/src/ppa_srm.c index 80b5198aad..bbde15789a 100644 --- a/components/esp_driver_ppa/src/ppa_srm.c +++ b/components/esp_driver_ppa/src/ppa_srm.c @@ -208,7 +208,14 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s if (config->rgb_swap) { PPA_CHECK_CM_SUPPORT_RGB_SWAP("in.srm", (uint32_t)config->in.srm_cm); } - ESP_RETURN_ON_FALSE(config->alpha_value <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_value"); + uint32_t new_alpha_value = 0; + if (config->alpha_update_mode == PPA_ALPHA_FIX_VALUE) { + ESP_RETURN_ON_FALSE(config->alpha_fix_val <= 0xFF, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_fix_val"); + new_alpha_value = config->alpha_fix_val; + } else if (config->alpha_update_mode == PPA_ALPHA_SCALE) { + ESP_RETURN_ON_FALSE(config->alpha_scale_ratio > 0 && config->alpha_scale_ratio < 1, ESP_ERR_INVALID_ARG, TAG, "invalid alpha_scale_ratio"); + new_alpha_value = (uint32_t)(config->alpha_scale_ratio * 256); + } // To reduce complexity, rotation_angle, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions // TODO: // YUV420: in desc, ha/hb/va/vb/x/y must be even number @@ -237,6 +244,7 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s srm_trans_desc->scale_x_frag = (uint32_t)(srm_trans_desc->scale_x * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; srm_trans_desc->scale_y_int = (uint32_t)srm_trans_desc->scale_y; srm_trans_desc->scale_y_frag = (uint32_t)(srm_trans_desc->scale_y * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; + srm_trans_desc->alpha_value = new_alpha_value; trans_on_picked_desc->ppa_engine = ppa_client->engine; trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_SRM; From 8464fac84575752f3465b0450a5f1ff139276b9d Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Thu, 25 Apr 2024 22:18:38 +0800 Subject: [PATCH 13/15] feat(ppa): add PPA driver support for ESP32P4 Remove the check for in_accepting_trans_state Add color_pixel_xxxx_data_t structures to color_types.h Fix PM lock protection (Tested, now works well) * CPU_MAX, PM lock and semaphore order * Remove ppa_driver PM lock Modify concurrency (queue, trans recycle, semaphore, ...) Add programming guide Add test apps --- .../esp_driver_ppa/include/driver/ppa.h | 27 +- components/esp_driver_ppa/src/ppa_blend.c | 13 +- components/esp_driver_ppa/src/ppa_core.c | 128 ++--- components/esp_driver_ppa/src/ppa_fill.c | 9 +- components/esp_driver_ppa/src/ppa_priv.h | 6 +- components/esp_driver_ppa/src/ppa_srm.c | 13 +- .../test_apps/.build-test-rules.yml | 7 + .../esp_driver_ppa/test_apps/CMakeLists.txt | 9 + components/esp_driver_ppa/test_apps/README.md | 2 + .../test_apps/main/CMakeLists.txt | 9 + .../test_apps/main/idf_component.yml | 2 + .../test_apps/main/test_app_main.c | 42 ++ .../esp_driver_ppa/test_apps/main/test_ppa.c | 489 ++++++++++++++++++ .../esp_driver_ppa/test_apps/pytest_ppa.py | 17 + .../test_apps/sdkconfig.ci.release | 6 + .../test_apps/sdkconfig.defaults | 3 + .../test_apps/sdkconfig.defaults.esp32p4 | 3 + components/hal/esp32p4/include/hal/ppa_ll.h | 42 +- components/hal/include/hal/color_types.h | 38 ++ components/hal/include/hal/ppa_types.h | 16 + docs/_static/diagrams/ppa/pic_blk_concept.png | Bin 0 -> 26960 bytes docs/conf_common.py | 3 + docs/doxygen/Doxyfile_esp32p4 | 2 + docs/en/api-reference/peripherals/index.rst | 1 + docs/en/api-reference/peripherals/ppa.rst | 149 ++++++ .../zh_CN/api-reference/peripherals/index.rst | 1 + docs/zh_CN/api-reference/peripherals/ppa.rst | 1 + 27 files changed, 906 insertions(+), 132 deletions(-) create mode 100644 components/esp_driver_ppa/test_apps/.build-test-rules.yml create mode 100644 components/esp_driver_ppa/test_apps/CMakeLists.txt create mode 100644 components/esp_driver_ppa/test_apps/README.md create mode 100644 components/esp_driver_ppa/test_apps/main/CMakeLists.txt create mode 100644 components/esp_driver_ppa/test_apps/main/idf_component.yml create mode 100644 components/esp_driver_ppa/test_apps/main/test_app_main.c create mode 100644 components/esp_driver_ppa/test_apps/main/test_ppa.c create mode 100644 components/esp_driver_ppa/test_apps/pytest_ppa.py create mode 100644 components/esp_driver_ppa/test_apps/sdkconfig.ci.release create mode 100644 components/esp_driver_ppa/test_apps/sdkconfig.defaults create mode 100644 components/esp_driver_ppa/test_apps/sdkconfig.defaults.esp32p4 create mode 100644 docs/_static/diagrams/ppa/pic_blk_concept.png create mode 100644 docs/en/api-reference/peripherals/ppa.rst create mode 100644 docs/zh_CN/api-reference/peripherals/ppa.rst diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index f903dc49b4..543a6664ba 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -21,7 +21,7 @@ typedef enum { PPA_OPERATION_SRM, /*!< Do scale-rotate-mirror operation */ PPA_OPERATION_BLEND, /*!< Do blend operation */ PPA_OPERATION_FILL, /*!< Do fill operation, use one constant pixel to fill a target window */ - PPA_OPERATION_NUM, /*!< Quantity of PPA operations */ + PPA_OPERATION_INVALID, /*!< Invalid PPA operations, indicates the quantity of available PPA operations */ } ppa_operation_t; /** @@ -89,7 +89,7 @@ typedef bool (*ppa_event_callback_t)(ppa_client_handle_t ppa_client, ppa_event_d * @brief Group of supported PPA callbacks */ typedef struct { - ppa_event_callback_t on_trans_done; /*! Invoked when a PPA transaction finishes */ + ppa_event_callback_t on_trans_done; /*!< Invoked when a PPA transaction finishes */ } ppa_event_callbacks_t; /** @@ -111,7 +111,7 @@ esp_err_t ppa_client_register_event_callbacks(ppa_client_handle_t ppa_client, co * @brief A collection of configuration items for an input picture and the target block inside the picture */ typedef struct { - void *buffer; /*!< Pointer to the input picture buffer */ + const void *buffer; /*!< Pointer to the input picture buffer */ uint32_t pic_w; /*!< Input picture width (unit: pixel) */ uint32_t pic_h; /*!< Input picture height (unit: pixel) */ uint32_t block_w; /*!< Target block width (unit: pixel) */ @@ -123,8 +123,8 @@ typedef struct { ppa_blend_color_mode_t blend_cm; /*!< Color mode of the picture in a PPA blend operation. Supported color mode in `ppa_blend_color_mode_t` */ ppa_fill_color_mode_t fill_cm; /*!< Color mode of the picture in a PPA fill operation. Supported color mode in `ppa_fill_color_mode_t` */ }; - color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */ - color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */ + ppa_color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */ + ppa_color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */ } ppa_in_pic_blk_config_t; /** @@ -142,8 +142,8 @@ typedef struct { ppa_blend_color_mode_t blend_cm; /*!< Color mode of the picture in a PPA blend operation. Supported color mode in `ppa_blend_color_mode_t` */ ppa_fill_color_mode_t fill_cm; /*!< Color mode of the picture in a PPA fill operation. Supported color mode in `ppa_fill_color_mode_t` */ }; - color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */ - color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */ + ppa_color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */ + ppa_color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */ } ppa_out_pic_blk_config_t; /** @@ -193,8 +193,7 @@ typedef struct { * @return * - ESP_OK: Perform a SRM operation successfully * - ESP_ERR_INVALID_ARG: Perform a SRM operation failed because of invalid argument - * - ESP_ERR_NO_MEM: Perform a SRM operation failed because out of memory - * - ESP_FAIL: Perform a SRM operation failed because the client cannot accept transaction now + * - ESP_FAIL: Perform a SRM operation failed because the client's pending transactions has reached its maximum capacity */ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_srm_oper_config_t *config); @@ -227,7 +226,7 @@ typedef struct { When PPA_ALPHA_SCALE mode is selected, alpha_scale_ratio is the multiplier to the input alpha value (output_alpha = alpha_scale_ratio * input_alpha) Ratio resolution is 1/256 */ }; - uint32_t fg_fix_rgb_val; /*!< When in_fg.blend_cm is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + color_pixel_rgb888_data_t fg_fix_rgb_val; /*!< When in_fg.blend_cm is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground, in RGB888 format */ // color-keying // A pixel, where its background element and foreground element are both out of their color-keying ranges, will follow Alpha Blending @@ -255,8 +254,7 @@ typedef struct { * @return * - ESP_OK: Perform a blend operation successfully * - ESP_ERR_INVALID_ARG: Perform a blend operation failed because of invalid argument - * - ESP_ERR_NO_MEM: Perform a blend operation failed because out of memory - * - ESP_FAIL: Perform a blend operation failed because the client cannot accept transaction now + * - ESP_FAIL: Perform a blend operation failed because the client's pending transactions has reached its maximum capacity */ esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_config_t *config); @@ -268,7 +266,7 @@ typedef struct { uint32_t fill_block_w; /*!< The width of the block to be filled (unit: pixel) */ uint32_t fill_block_h; /*!< The height of the block to be filled (unit: pixel) */ - uint32_t fill_argb_color; /*!< The color to be filled, in ARGB8888 format ((A[31:24], R[23:16], G[15: 8], B[7:0])) */ + color_pixel_argb8888_data_t fill_argb_color; /*!< The color to be filled, in ARGB8888 format */ ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ void *user_data; /*!< User registered data to be passed into `done_cb` callback function */ @@ -283,8 +281,7 @@ typedef struct { * @return * - ESP_OK: Perform a fill operation successfully * - ESP_ERR_INVALID_ARG: Perform a fill operation failed because of invalid argument - * - ESP_ERR_NO_MEM: Perform a fill operation failed because out of memory - * - ESP_FAIL: Perform a fill operation failed because the client cannot accept transaction now + * - ESP_FAIL: Perform a fill operation failed because the client's pending transactions has reached its maximum capacity */ esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_config_t *config); diff --git a/components/esp_driver_ppa/src/ppa_blend.c b/components/esp_driver_ppa/src/ppa_blend.c index 420a091160..fc4c6c5a3c 100644 --- a/components/esp_driver_ppa/src/ppa_blend.c +++ b/components/esp_driver_ppa/src/ppa_blend.c @@ -146,7 +146,7 @@ bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_chann ppa_ll_blend_set_rx_fg_color_mode(platform->hal.dev, blend_trans_desc->in_fg.blend_cm); if (COLOR_SPACE_TYPE((uint32_t)blend_trans_desc->in_fg.blend_cm) == COLOR_SPACE_ALPHA) { - ppa_ll_blend_set_rx_fg_fix_rgb(platform->hal.dev, blend_trans_desc->fg_fix_rgb_val); + ppa_ll_blend_set_rx_fg_fix_rgb(platform->hal.dev, &blend_trans_desc->fg_fix_rgb_val); } ppa_ll_blend_enable_rx_fg_byte_swap(platform->hal.dev, blend_trans_desc->fg_byte_swap); ppa_ll_blend_enable_rx_fg_rgb_swap(platform->hal.dev, blend_trans_desc->fg_rgb_swap); @@ -231,18 +231,21 @@ esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_conf .color_type_id = config->in_bg.blend_cm, }; uint32_t in_bg_pic_len = config->in_bg.pic_w * config->in_bg.pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; - esp_cache_msync(config->in_bg.buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + esp_cache_msync((void *)config->in_bg.buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); color_space_pixel_format_t in_fg_pixel_format = { .color_type_id = config->in_fg.blend_cm, }; uint32_t in_fg_pic_len = config->in_fg.pic_w * config->in_fg.pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; - esp_cache_msync(config->in_fg.buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + esp_cache_msync((void *)config->in_fg.buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); // Invalidate out_buffer - esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + portENTER_CRITICAL(&ppa_client->spinlock); + bool trans_elm_acquired = xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0); + portEXIT_CRITICAL(&ppa_client->spinlock); + if (trans_elm_acquired) { dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; diff --git a/components/esp_driver_ppa/src/ppa_core.c b/components/esp_driver_ppa/src/ppa_core.c index dc08fdced7..c1f3bdf2a7 100644 --- a/components/esp_driver_ppa/src/ppa_core.c +++ b/components/esp_driver_ppa/src/ppa_core.c @@ -46,10 +46,10 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine); static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t trans_elm_num, ppa_operation_t oper_type); static void ppa_free_transaction(ppa_trans_t *trans_elm); -const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATION_NUM] = { - ppa_srm_transaction_on_picked, - ppa_blend_transaction_on_picked, - ppa_fill_transaction_on_picked, +const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATION_INVALID] = { + [PPA_OPERATION_SRM] = ppa_srm_transaction_on_picked, + [PPA_OPERATION_BLEND] = ppa_blend_transaction_on_picked, + [PPA_OPERATION_FILL] = ppa_fill_transaction_on_picked, }; static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine) @@ -104,9 +104,11 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin } #if CONFIG_PM_ENABLE - ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_srm", &srm_engine->base.pm_lock); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "create pm lock failed"); + if (ret == ESP_OK) { + ret = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "ppa_srm", &srm_engine->base.pm_lock); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "create pm lock failed"); + } } #endif } else { @@ -147,9 +149,11 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin } #if CONFIG_PM_ENABLE - ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_blending", &blending_engine->base.pm_lock); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "create pm lock failed"); + if (ret == ESP_OK) { + ret = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "ppa_blending", &blending_engine->base.pm_lock); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "create pm lock failed"); + } } #endif } else { @@ -180,16 +184,6 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin ESP_LOGE(TAG, "install 2D-DMA failed"); goto wrap_up; } - -#if CONFIG_PM_ENABLE - assert(!s_platform.pm_lock); - // Create and acquire the PM lock - ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa", &s_platform.pm_lock); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "create pm lock failed"); - goto wrap_up; - } -#endif } } wrap_up: @@ -250,14 +244,6 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) if (!s_platform.srm && !s_platform.blending) { assert(s_platform.srm_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0); -#if CONFIG_PM_ENABLE - if (s_platform.pm_lock) { - ret = esp_pm_lock_delete(s_platform.pm_lock); - assert(ret == ESP_OK); - s_platform.pm_lock = NULL; - } -#endif - if (s_platform.dma2d_pool_handle) { dma2d_release_pool(s_platform.dma2d_pool_handle); // TODO: check return value. If not ESP_OK, then must be error on other 2D-DMA clients :( Give a warning log? s_platform.dma2d_pool_handle = NULL; @@ -278,19 +264,19 @@ esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_hand { esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(config && ret_client, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(config->oper_type < PPA_OPERATION_NUM, ESP_ERR_INVALID_ARG, TAG, "unknown operation"); + ESP_RETURN_ON_FALSE(config->oper_type < PPA_OPERATION_INVALID, ESP_ERR_INVALID_ARG, TAG, "unknown operation"); ppa_client_t *client = (ppa_client_t *)heap_caps_calloc(1, sizeof(ppa_client_t), PPA_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(client, ESP_ERR_NO_MEM, TAG, "no mem to register client"); - uint32_t ring_buf_size = MAX(1, config->max_pending_trans_num); - client->trans_elm_ptr_queue = xQueueCreateWithCaps(ring_buf_size, sizeof(uint32_t), PPA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(client->trans_elm_ptr_queue && ppa_malloc_transaction(client->trans_elm_ptr_queue, ring_buf_size, config->oper_type), + // Allocate memory for storing transaction contexts and create a queue to save these trans_elm_ptr + uint32_t queue_size = MAX(1, config->max_pending_trans_num); + client->trans_elm_ptr_queue = xQueueCreateWithCaps(queue_size, sizeof(uint32_t), PPA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(client->trans_elm_ptr_queue && ppa_malloc_transaction(client->trans_elm_ptr_queue, queue_size, config->oper_type), ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage"); client->oper_type = config->oper_type; client->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; - client->in_accepting_trans_state = true; if (config->oper_type == PPA_OPERATION_SRM) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_SRM, @@ -318,7 +304,6 @@ esp_err_t ppa_unregister_client(ppa_client_handle_t ppa_client) bool do_unregister = false; portENTER_CRITICAL(&ppa_client->spinlock); if (ppa_client->trans_cnt == 0) { - ppa_client->in_accepting_trans_state = false; do_unregister = true; } portEXIT_CRITICAL(&ppa_client->spinlock); @@ -387,12 +372,12 @@ static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t t trans_on_picked_desc->op_desc = ppa_trans_desc; trans_on_picked_desc->trans_elm = new_trans_elm; dma_trans_desc->user_config = (void *)trans_on_picked_desc; - dma_trans_desc->on_job_picked = ppa_oper_trans_on_picked_func[oper_type]; // TODO: This maybe better to be in the ppa_do_xxx function + dma_trans_desc->on_job_picked = ppa_oper_trans_on_picked_func[oper_type]; new_trans_elm->trans_desc = dma_trans_desc; new_trans_elm->dma_trans_placeholder = dma_trans_elm; new_trans_elm->sem = ppa_trans_sem; - // Fill the ring buffer with allocated transaction element pointer + // Fill the queue with allocated transaction element pointer BaseType_t sent = xQueueSend(trans_elm_ptr_queue, &new_trans_elm, 0); assert(sent); } @@ -424,27 +409,14 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng esp_err_t ret = ESP_OK; esp_err_t pm_lock_ret __attribute__((unused)); -#if CONFIG_PM_ENABLE - pm_lock_ret = esp_pm_lock_acquire(s_platform.pm_lock); - assert((pm_lock_ret == ESP_OK) && "acquire pm_lock failed"); -#endif - portENTER_CRITICAL(&ppa_client->spinlock); - // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! - if (ppa_client->in_accepting_trans_state) { - // Send transaction into PPA engine queue - STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry); - ppa_client->trans_cnt++; - } else { - ret = ESP_FAIL; - } + // Send transaction into PPA engine queue + portENTER_CRITICAL(&ppa_engine_base->spinlock); + STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry); + portEXIT_CRITICAL(&ppa_engine_base->spinlock); + ppa_client->trans_cnt++; portEXIT_CRITICAL(&ppa_client->spinlock); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "The client cannot accept transaction now"); - goto err; - } - TickType_t ticks_to_wait = (mode == PPA_TRANS_MODE_NON_BLOCKING) ? 0 : portMAX_DELAY; if (xSemaphoreTake(ppa_engine_base->sem, ticks_to_wait) == pdTRUE) { // Check if the transaction has already been started from the ISR @@ -469,11 +441,11 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng portENTER_CRITICAL(&ppa_engine_base->spinlock); STAILQ_REMOVE(&ppa_engine_base->trans_stailq, trans_elm, ppa_trans_s, entry); portEXIT_CRITICAL(&ppa_engine_base->spinlock); + xSemaphoreGive(ppa_engine_base->sem); #if CONFIG_PM_ENABLE pm_lock_ret = esp_pm_lock_release(ppa_engine_base->pm_lock); assert((pm_lock_ret == ESP_OK) && "release pm_lock failed"); #endif - xSemaphoreGive(ppa_engine_base->sem); portENTER_CRITICAL(&ppa_client->spinlock); ppa_client->trans_cnt--; portEXIT_CRITICAL(&ppa_client->spinlock); @@ -489,15 +461,9 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng // printf("ppa intr: %ld\n", PPA.int_raw.val); // } xSemaphoreTake(trans_elm->sem, portMAX_DELAY); // Given in the ISR - // Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) - // ppa_recycle_transaction(ppa_client, trans_elm); // TODO: Do we need it to be here or can be at the end of done_cb? + // TODO: Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) } -#if CONFIG_PM_ENABLE - pm_lock_ret = esp_pm_lock_release(s_platform.pm_lock); - assert((pm_lock_ret == ESP_OK) && "release pm_lock failed"); -#endif - err: return ret; } @@ -510,11 +476,9 @@ bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data ppa_client_t *client = trans_elm->client; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config; ppa_engine_t *engine_base = trans_on_picked_desc->ppa_engine; - - if (client->done_cb) { - ppa_event_data_t edata = {}; - need_yield |= client->done_cb(client, &edata, trans_elm->user_data); - } + // Save callback contexts + ppa_event_callback_t done_cb = client->done_cb; + void *trans_elm_user_data = trans_elm->user_data; ppa_trans_t *next_start_trans = NULL; portENTER_CRITICAL_ISR(&engine_base->spinlock); @@ -523,28 +487,34 @@ bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data next_start_trans = STAILQ_FIRST(&engine_base->trans_stailq); portEXIT_CRITICAL_ISR(&engine_base->spinlock); + portENTER_CRITICAL_ISR(&client->spinlock); + // Release transaction semaphore to unblock ppa_do_operation + xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); + need_yield |= (HPTaskAwoken == pdTRUE); + + // Then recycle transaction elm + need_yield |= ppa_recycle_transaction(client, trans_elm); + + client->trans_cnt--; + portEXIT_CRITICAL_ISR(&client->spinlock); + // If there is next trans in PPA engine queue, send it to DMA queue; otherwise, release the engine semaphore if (next_start_trans) { ppa_dma2d_enqueue(next_start_trans); } else { + xSemaphoreGiveFromISR(engine_base->sem, &HPTaskAwoken); + need_yield |= (HPTaskAwoken == pdTRUE); #if CONFIG_PM_ENABLE esp_err_t pm_lock_ret = esp_pm_lock_release(engine_base->pm_lock); assert(pm_lock_ret == ESP_OK); #endif - xSemaphoreGiveFromISR(engine_base->sem, &HPTaskAwoken); - need_yield |= (HPTaskAwoken == pdTRUE); } - // Recycle transaction and release transaction semaphore - // if (trans_elm->sem != NULL) { - xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken); - need_yield |= (HPTaskAwoken == pdTRUE); - // } - // TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!! - portENTER_CRITICAL_ISR(&client->spinlock); - need_yield |= ppa_recycle_transaction(client, trans_elm); - client->trans_cnt--; - portEXIT_CRITICAL_ISR(&client->spinlock); + // Process last transaction's callback + if (done_cb) { + ppa_event_data_t edata = {}; + need_yield |= done_cb(client, &edata, trans_elm_user_data); + } return need_yield; } diff --git a/components/esp_driver_ppa/src/ppa_fill.c b/components/esp_driver_ppa/src/ppa_fill.c index 476b4f36cf..8582e9d3a5 100644 --- a/components/esp_driver_ppa/src/ppa_fill.c +++ b/components/esp_driver_ppa/src/ppa_fill.c @@ -77,7 +77,7 @@ bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channe dma2d_start(dma2d_rx_chan); // Configure PPA Blending engine - ppa_ll_blend_configure_filling_block(platform->hal.dev, fill_trans_desc->fill_argb_color, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h); + ppa_ll_blend_configure_filling_block(platform->hal.dev, &fill_trans_desc->fill_argb_color, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h); ppa_ll_blend_set_tx_color_mode(platform->hal.dev, fill_trans_desc->out.fill_cm); ppa_ll_blend_start(platform->hal.dev, PPA_LL_BLEND_TRANS_MODE_FILL); @@ -104,11 +104,14 @@ esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_config // To reduce complexity, color_mode, fill_block_w/h correctness are checked in their corresponding LL functions // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + portENTER_CRITICAL(&ppa_client->spinlock); + bool trans_elm_acquired = xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0); + portEXIT_CRITICAL(&ppa_client->spinlock); + if (trans_elm_acquired) { dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; diff --git a/components/esp_driver_ppa/src/ppa_priv.h b/components/esp_driver_ppa/src/ppa_priv.h index 827276b238..fdcf7f6bd8 100644 --- a/components/esp_driver_ppa/src/ppa_priv.h +++ b/components/esp_driver_ppa/src/ppa_priv.h @@ -78,7 +78,6 @@ struct ppa_client_t { ppa_engine_t *engine; // Pointer to the PPA engine that in charge of performing the PPA operation uint32_t trans_cnt; // Number of pending PPA transactions portMUX_TYPE spinlock; // Client level spinlock - bool in_accepting_trans_state; // Indicates whether the client can accept new PPA transaction requests now ppa_event_callback_t done_cb; // Transaction done callback QueueHandle_t trans_elm_ptr_queue; // Queue that contains the pointers to the allocated memory to save the transaction contexts }; @@ -141,7 +140,7 @@ typedef struct { uint32_t fg_alpha_fix_val; float fg_alpha_scale_ratio; }; - uint32_t fg_fix_rgb_val; + color_pixel_rgb888_data_t fg_fix_rgb_val; // color-keying bool bg_ck_en; @@ -209,9 +208,6 @@ struct ppa_platform_t { uint32_t blend_engine_ref_count; // Reference count used to protect PPA blending engine acquire and release size_t buf_alignment_size; // Alignment requirement for the outgoing buffer addr and size to satisfy cache line size uint32_t dma_desc_mem_size; // Alignment requirement for the 2D-DMA descriptor to satisfy cache line size -#if CONFIG_PM_ENABLE - esp_pm_lock_handle_t pm_lock; // Power management lock -#endif }; #ifdef __cplusplus diff --git a/components/esp_driver_ppa/src/ppa_srm.c b/components/esp_driver_ppa/src/ppa_srm.c index bbde15789a..331faea3d6 100644 --- a/components/esp_driver_ppa/src/ppa_srm.c +++ b/components/esp_driver_ppa/src/ppa_srm.c @@ -108,7 +108,7 @@ bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV444) { ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; dma2d_csc_config_t dma_tx_csc = {0}; - if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + if (srm_trans_desc->in.yuv_std == PPA_COLOR_CONV_STD_RGB_YUV_BT601) { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_601; } else { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_709; @@ -117,7 +117,7 @@ bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel } else if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV422) { ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; dma2d_csc_config_t dma_tx_csc = {0}; - if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) { + if (srm_trans_desc->in.yuv_std == PPA_COLOR_CONV_STD_RGB_YUV_BT601) { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_601; } else { dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_709; @@ -227,13 +227,16 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s .color_type_id = config->in.srm_cm, }; uint32_t in_pic_len = config->in.pic_w * config->in.pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; - esp_cache_msync(config->in.buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + esp_cache_msync((void *)config->in.buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); // Invalidate out_buffer - esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { + portENTER_CRITICAL(&ppa_client->spinlock); + bool trans_elm_acquired = xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0); + portEXIT_CRITICAL(&ppa_client->spinlock); + if (trans_elm_acquired) { dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; diff --git a/components/esp_driver_ppa/test_apps/.build-test-rules.yml b/components/esp_driver_ppa/test_apps/.build-test-rules.yml new file mode 100644 index 0000000000..7767104a7f --- /dev/null +++ b/components/esp_driver_ppa/test_apps/.build-test-rules.yml @@ -0,0 +1,7 @@ +# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps + +components/esp_driver_ppa/test_apps: + disable: + - if: SOC_PPA_SUPPORTED != 1 + depends_components: + - esp_driver_ppa diff --git a/components/esp_driver_ppa/test_apps/CMakeLists.txt b/components/esp_driver_ppa/test_apps/CMakeLists.txt new file mode 100644 index 0000000000..dde4141943 --- /dev/null +++ b/components/esp_driver_ppa/test_apps/CMakeLists.txt @@ -0,0 +1,9 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +set(COMPONENTS main) + +project(ppa_test) diff --git a/components/esp_driver_ppa/test_apps/README.md b/components/esp_driver_ppa/test_apps/README.md new file mode 100644 index 0000000000..909282018f --- /dev/null +++ b/components/esp_driver_ppa/test_apps/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | diff --git a/components/esp_driver_ppa/test_apps/main/CMakeLists.txt b/components/esp_driver_ppa/test_apps/main/CMakeLists.txt new file mode 100644 index 0000000000..25e3680685 --- /dev/null +++ b/components/esp_driver_ppa/test_apps/main/CMakeLists.txt @@ -0,0 +1,9 @@ +set(srcs "test_app_main.c" + "test_ppa.c") + +# 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} + INCLUDE_DIRS "." + PRIV_REQUIRES esp_driver_ppa esp_psram unity + WHOLE_ARCHIVE) diff --git a/components/esp_driver_ppa/test_apps/main/idf_component.yml b/components/esp_driver_ppa/test_apps/main/idf_component.yml new file mode 100644 index 0000000000..2ae836a935 --- /dev/null +++ b/components/esp_driver_ppa/test_apps/main/idf_component.yml @@ -0,0 +1,2 @@ +dependencies: + ccomp_timer: "^1.0.0" diff --git a/components/esp_driver_ppa/test_apps/main/test_app_main.c b/components/esp_driver_ppa/test_apps/main/test_app_main.c new file mode 100644 index 0000000000..bdc0d7ecb3 --- /dev/null +++ b/components/esp_driver_ppa/test_apps/main/test_app_main.c @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "esp_heap_caps.h" +#include "unity_test_utils.h" + +// Some resources are lazy allocated in the driver, the threshold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + esp_reent_cleanup(); //clean up some of the newlib's lazy allocations + unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + printf(" ________ \n"); + printf("|___ ___ \\ \n"); + printf(" | \\_/ | \n"); + printf(" '.___.' \n"); + printf(" ________ \n"); + printf("|___ ___ \\ \n"); + printf(" | \\_/ | \n"); + printf(" '.___.' \n"); + printf(" _______ \n"); + printf("|__. _ '. \n"); + printf(" __||_/ / \n"); + printf("|______.' \n"); + + unity_run_menu(); +} diff --git a/components/esp_driver_ppa/test_apps/main/test_ppa.c b/components/esp_driver_ppa/test_apps/main/test_ppa.c new file mode 100644 index 0000000000..2d92e4afba --- /dev/null +++ b/components/esp_driver_ppa/test_apps/main/test_ppa.c @@ -0,0 +1,489 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "driver/ppa.h" +#include "esp_heap_caps.h" +#include "esp_err.h" +#include "ccomp_timer.h" +#include "hal/color_hal.h" + +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + +TEST_CASE("ppa_client_do_ppa_operation", "[PPA]") +{ + const uint32_t w = 480; + const uint32_t h = 480; + const uint32_t buf_1_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888); + const uint32_t buf_2_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888); + + color_space_pixel_format_t buf_1_cm = { + .color_type_id = buf_1_color_type_id, + }; + color_space_pixel_format_t buf_2_cm = { + .color_type_id = buf_2_color_type_id, + }; + + uint32_t buf_1_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(buf_1_cm) / 8, 64); + uint32_t buf_2_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(buf_2_cm) / 8, 64); + uint8_t *buf_1 = heap_caps_aligned_calloc(64, buf_1_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(buf_1); + uint8_t *buf_2 = heap_caps_aligned_calloc(64, buf_2_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(buf_2); + + // Register different types of PPA clients + ppa_client_handle_t ppa_client_a_handle; + ppa_client_handle_t ppa_client_b_handle; + ppa_client_handle_t ppa_client_c_handle; + ppa_client_handle_t ppa_client_d_handle; + ppa_client_config_t ppa_client_config = { + .oper_type = PPA_OPERATION_SRM, + }; + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_a_handle)); + ppa_client_config.oper_type = PPA_OPERATION_BLEND; + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_b_handle)); + ppa_client_config.oper_type = PPA_OPERATION_FILL; + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_c_handle)); + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_d_handle)); + + ppa_srm_oper_config_t srm_oper_config = { + .in.buffer = buf_1, + .in.pic_w = w, + .in.pic_h = h, + .in.block_w = w, + .in.block_h = h, + .in.block_offset_x = 0, + .in.block_offset_y = 0, + .in.srm_cm = buf_1_color_type_id, + + .out.buffer = buf_2, + .out.buffer_size = buf_2_size, + .out.pic_w = w, + .out.pic_h = h, + .out.block_offset_x = 0, + .out.block_offset_y = 0, + .out.srm_cm = buf_2_color_type_id, + + .rotation_angle = PPA_SRM_ROTATION_ANGLE_0, + .scale_x = 1.0, + .scale_y = 1.0, + + .mode = PPA_TRANS_MODE_BLOCKING, + }; + // A SRM client can request to do a SRM operation + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_a_handle, &srm_oper_config)); + // A non-SRM client can not request to do a SRM operation + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_scale_rotate_mirror(ppa_client_b_handle, &srm_oper_config)); + + ppa_blend_oper_config_t blend_oper_config = { + .in_bg.buffer = buf_1, + .in_bg.pic_w = w, + .in_bg.pic_h = h, + .in_bg.block_w = w, + .in_bg.block_h = h, + .in_bg.block_offset_x = 0, + .in_bg.block_offset_y = 0, + .in_bg.blend_cm = buf_1_color_type_id, + + .in_fg.buffer = buf_2, + .in_fg.pic_w = w, + .in_fg.pic_h = h, + .in_fg.block_w = w, + .in_fg.block_h = h, + .in_fg.block_offset_x = 0, + .in_fg.block_offset_y = 0, + .in_fg.blend_cm = buf_2_color_type_id, + + .out.buffer = buf_1, + .out.buffer_size = buf_1_size, + .out.pic_w = w, + .out.pic_h = h, + .out.block_offset_x = 0, + .out.block_offset_y = 0, + .out.blend_cm = buf_1_color_type_id, + + .mode = PPA_TRANS_MODE_BLOCKING, + }; + // A blend client can request to do a blend operation + TEST_ESP_OK(ppa_do_blend(ppa_client_b_handle, &blend_oper_config)); + // A non-blend client can not request to do a blend operation + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_blend(ppa_client_d_handle, &blend_oper_config)); + + ppa_fill_oper_config_t fill_oper_config = { + .out.buffer = buf_1, + .out.buffer_size = buf_1_size, + .out.pic_w = w, + .out.pic_h = h, + .out.block_offset_x = 0, + .out.block_offset_y = 0, + .out.fill_cm = buf_1_color_type_id, + + .fill_block_w = w, + .fill_block_h = h, + .fill_argb_color = { + .val = 0xFF00FF00, + }, + + .mode = PPA_TRANS_MODE_NON_BLOCKING, + }; + // A fill client can request to do a fill operation + TEST_ESP_OK(ppa_do_fill(ppa_client_c_handle, &fill_oper_config)); + // Another fill client can also request another fill operation at the same time + TEST_ESP_OK(ppa_do_fill(ppa_client_d_handle, &fill_oper_config)); + + vTaskDelay(pdMS_TO_TICKS(500)); + + // Unregister all PPA clients + TEST_ESP_OK(ppa_unregister_client(ppa_client_a_handle)); + TEST_ESP_OK(ppa_unregister_client(ppa_client_b_handle)); + TEST_ESP_OK(ppa_unregister_client(ppa_client_c_handle)); + TEST_ESP_OK(ppa_unregister_client(ppa_client_d_handle)); + + free(buf_1); + free(buf_2); +} + +static bool ppa_trans_done_cb(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + SemaphoreHandle_t sem = (SemaphoreHandle_t)user_data; + xSemaphoreGiveFromISR(sem, &xHigherPriorityTaskWoken); + return (xHigherPriorityTaskWoken == pdTRUE); +} + +TEST_CASE("ppa_pending_transactions_in_queue", "[PPA]") +{ + // A big picture block takes longer time to process, desired for this test case + const uint32_t w = 1920; + const uint32_t h = 1080; + const uint32_t buf_1_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888); + const uint32_t buf_2_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888); + + color_space_pixel_format_t buf_1_cm = { + .color_type_id = buf_1_color_type_id, + }; + color_space_pixel_format_t buf_2_cm = { + .color_type_id = buf_2_color_type_id, + }; + + uint32_t buf_1_size = w * h * color_hal_pixel_format_get_bit_depth(buf_1_cm) / 8; + uint32_t buf_2_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(buf_2_cm) / 8, 64); + uint8_t *buf_1 = heap_caps_aligned_calloc(64, buf_1_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(buf_1); + uint8_t *buf_2 = heap_caps_aligned_calloc(64, buf_2_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(buf_2); + + // Register two PPA SRM clients with different max_pending_trans_num + ppa_client_handle_t ppa_client_a_handle; + ppa_client_handle_t ppa_client_b_handle; + ppa_client_config_t ppa_client_config = { + .oper_type = PPA_OPERATION_SRM, + }; + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_a_handle)); + ppa_client_config.max_pending_trans_num = 3; + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_b_handle)); + + ppa_event_callbacks_t cbs = { + .on_trans_done = ppa_trans_done_cb, + }; + ppa_client_register_event_callbacks(ppa_client_a_handle, &cbs); + + SemaphoreHandle_t sem = xSemaphoreCreateBinary(); + + ppa_srm_oper_config_t oper_config = { + .in.buffer = buf_1, + .in.pic_w = w, + .in.pic_h = h, + .in.block_w = w, + .in.block_h = h, + .in.block_offset_x = 0, + .in.block_offset_y = 0, + .in.srm_cm = buf_1_color_type_id, + + .out.buffer = buf_2, + .out.buffer_size = buf_2_size, + .out.pic_w = w, + .out.pic_h = h, + .out.block_offset_x = 0, + .out.block_offset_y = 0, + .out.srm_cm = buf_2_color_type_id, + + .rotation_angle = PPA_SRM_ROTATION_ANGLE_0, + .scale_x = 1.0, + .scale_y = 1.0, + + .user_data = (void *)sem, + .mode = PPA_TRANS_MODE_NON_BLOCKING, + }; + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_a_handle, &oper_config)); + + // Another transaction cannot be accept since client_a can only hold one transaction + TEST_ESP_ERR(ESP_FAIL, ppa_do_scale_rotate_mirror(ppa_client_a_handle, &oper_config)); + + // Wait for the last transaction finishes + xSemaphoreTake(sem, portMAX_DELAY); + // Then a new transaction can be accepted again + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_a_handle, &oper_config)); + + // Client can not be unregistered when there are unfinished transactions + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ppa_unregister_client(ppa_client_a_handle)); + + oper_config.mode = PPA_TRANS_MODE_BLOCKING; + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config)); + // Every PPA engine can only process one operation at a time + // Transactions are being processed with First-In-First-Out + // So, at the moment, the new transaction requested by client_b has finished, the last transaction requested by client_a for sure has finished + TEST_ASSERT(xSemaphoreTake(sem, 0) == pdTRUE); + // client_b can accept more than one transactions + oper_config.mode = PPA_TRANS_MODE_NON_BLOCKING; + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config)); + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config)); + oper_config.mode = PPA_TRANS_MODE_BLOCKING; + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config)); + // The last transaction requested is with BLOCKING mode, so the last call to ppa_do_scale_rotate_mirror returned means all transactions finished + + // Unregister all PPA clients + TEST_ESP_OK(ppa_unregister_client(ppa_client_a_handle)); + TEST_ESP_OK(ppa_unregister_client(ppa_client_b_handle)); + + vSemaphoreDelete(sem); + free(buf_1); + free(buf_2); +} + +TEST_CASE("ppa_srm_performance", "[PPA][ignore]") +{ + const uint32_t w = 1920; // 1920 / 1280 / 800 / 640 + const uint32_t h = 1080; // 1080 / 720 / 480 + const uint32_t block_w = w; + const uint32_t block_h = h; + const ppa_srm_color_mode_t in_cm = PPA_SRM_COLOR_MODE_ARGB8888; + const ppa_srm_color_mode_t out_cm = PPA_SRM_COLOR_MODE_YUV420; + const ppa_srm_rotation_angle_t rotation = PPA_SRM_ROTATION_ANGLE_0; + const float scale_x = 1.0; + const float scale_y = 1.0; + + color_space_pixel_format_t in_pixel_format = { + .color_type_id = in_cm, + }; + color_space_pixel_format_t out_pixel_format = { + .color_type_id = out_cm, + }; + + uint32_t in_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; + uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64); + uint8_t *out_buf = heap_caps_aligned_calloc(64, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(out_buf); + uint8_t *in_buf = heap_caps_aligned_calloc(64, in_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(in_buf); + + uint8_t *ptr = in_buf; + for (int x = 0; x < in_buf_size; x++) { + ptr[x] = x; + } + + ppa_client_handle_t ppa_client_handle; + ppa_client_config_t ppa_client_config = { + .oper_type = PPA_OPERATION_SRM, + .max_pending_trans_num = 1, + }; + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle)); + + uint32_t out_pic_w = (rotation == PPA_SRM_ROTATION_ANGLE_0 || rotation == PPA_SRM_ROTATION_ANGLE_180) ? w : h; + uint32_t out_pic_h = (rotation == PPA_SRM_ROTATION_ANGLE_0 || rotation == PPA_SRM_ROTATION_ANGLE_180) ? h : w; + ppa_srm_oper_config_t oper_config = { + .in.buffer = in_buf, + .in.pic_w = w, + .in.pic_h = h, + .in.block_w = block_w, + .in.block_h = block_h, + .in.block_offset_x = 0, + .in.block_offset_y = 0, + .in.srm_cm = in_cm, + + .out.buffer = out_buf, + .out.buffer_size = out_buf_size, + .out.pic_w = out_pic_w, + .out.pic_h = out_pic_h, + .out.block_offset_x = 0, + .out.block_offset_y = 0, + .out.srm_cm = out_cm, + + .rotation_angle = rotation, + .scale_x = scale_x, + .scale_y = scale_y, + + .rgb_swap = 0, + .byte_swap = 0, + + .mode = PPA_TRANS_MODE_BLOCKING, + }; + + ccomp_timer_start(); + + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config)); + + int64_t oper_time = ccomp_timer_stop(); + printf("Time passed: %lld us\n", oper_time); + + TEST_ESP_OK(ppa_unregister_client(ppa_client_handle)); + + free(in_buf); + free(out_buf); +} + +TEST_CASE("ppa_blend_performance", "[PPA][ignore]") +{ + const uint32_t w = 1280; + const uint32_t h = 720; + const uint32_t block_w = w; + const uint32_t block_h = h; + const ppa_blend_color_mode_t in_bg_cm = PPA_BLEND_COLOR_MODE_ARGB8888; + const ppa_blend_color_mode_t in_fg_cm = PPA_BLEND_COLOR_MODE_ARGB8888; + const ppa_blend_color_mode_t out_cm = PPA_BLEND_COLOR_MODE_ARGB8888; + + color_space_pixel_format_t in_bg_pixel_format = { + .color_type_id = in_bg_cm, + }; + color_space_pixel_format_t in_fg_pixel_format = { + .color_type_id = in_fg_cm, + }; + color_space_pixel_format_t out_pixel_format = { + .color_type_id = out_cm, + }; + + uint32_t in_bg_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; + uint32_t in_fg_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; + uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64); + uint8_t *out_buf = heap_caps_aligned_calloc(64, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(out_buf); + uint8_t *in_bg_buf = heap_caps_aligned_calloc(64, in_bg_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(in_bg_buf); + uint8_t *in_fg_buf = heap_caps_aligned_calloc(64, in_fg_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(in_fg_buf); + + uint8_t *ptr = in_bg_buf; + for (int x = 0; x < in_bg_buf_size; x++) { + ptr[x] = x & 0x55; + } + ptr = in_fg_buf; + for (int x = 0; x < in_fg_buf_size; x++) { + ptr[x] = x & 0xAA; + } + + ppa_client_handle_t ppa_client_handle; + ppa_client_config_t ppa_client_config = { + .oper_type = PPA_OPERATION_BLEND, + .max_pending_trans_num = 1, + }; + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle)); + + ppa_blend_oper_config_t oper_config = { + .in_bg.buffer = in_bg_buf, + .in_bg.pic_w = w, + .in_bg.pic_h = h, + .in_bg.block_w = block_w, + .in_bg.block_h = block_h, + .in_bg.block_offset_x = 0, + .in_bg.block_offset_y = 0, + .in_bg.blend_cm = in_bg_cm, + + .in_fg.buffer = in_fg_buf, + .in_fg.pic_w = w, + .in_fg.pic_h = h, + .in_fg.block_w = block_w, + .in_fg.block_h = block_h, + .in_fg.block_offset_x = 0, + .in_fg.block_offset_y = 0, + .in_fg.blend_cm = in_fg_cm, + + .out.buffer = out_buf, + .out.buffer_size = out_buf_size, + .out.pic_w = w, + .out.pic_h = h, + .out.block_offset_x = 0, + .out.block_offset_y = 0, + .out.blend_cm = out_cm, + + .bg_ck_en = false, + .fg_ck_en = false, + + .mode = PPA_TRANS_MODE_BLOCKING, + }; + + ccomp_timer_start(); + + TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &oper_config)); + + int64_t oper_time = ccomp_timer_stop(); + printf("Time passed: %lld us\n", oper_time); + + TEST_ESP_OK(ppa_unregister_client(ppa_client_handle)); + + free(in_bg_buf); + free(in_fg_buf); + free(out_buf); +} + +TEST_CASE("ppa_fill_performance", "[PPA][ignore]") +{ + const uint32_t w = 1280; + const uint32_t h = 720; + const uint32_t block_w = 800; + const uint32_t block_h = 480; + const ppa_fill_color_mode_t out_cm = PPA_FILL_COLOR_MODE_RGB565; + + color_space_pixel_format_t out_pixel_format = { + .color_type_id = out_cm, + }; + + uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64); + uint8_t *out_buf = heap_caps_aligned_calloc(64, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(out_buf); + + ppa_client_handle_t ppa_client_handle; + ppa_client_config_t ppa_client_config = { + .oper_type = PPA_OPERATION_FILL, + .max_pending_trans_num = 1, + }; + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle)); + + ppa_fill_oper_config_t oper_config = { + .out.buffer = out_buf, + .out.buffer_size = out_buf_size, + .out.pic_w = w, + .out.pic_h = h, + .out.block_offset_x = 0, + .out.block_offset_y = 0, + .out.fill_cm = out_cm, + + .fill_block_w = block_w, + .fill_block_h = block_h, + .fill_argb_color = { + .val = 0xFF00FFFF, + }, + + .mode = PPA_TRANS_MODE_BLOCKING, + }; + + ccomp_timer_start(); + + TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config)); + + int64_t oper_time = ccomp_timer_stop(); + printf("Time passed: %lld us\n", oper_time); + + TEST_ESP_OK(ppa_unregister_client(ppa_client_handle)); + + free(out_buf); +} diff --git a/components/esp_driver_ppa/test_apps/pytest_ppa.py b/components/esp_driver_ppa/test_apps/pytest_ppa.py new file mode 100644 index 0000000000..534d32cee1 --- /dev/null +++ b/components/esp_driver_ppa/test_apps/pytest_ppa.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32p4 +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'release', + ], + indirect=True, +) +def test_ppa(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_driver_ppa/test_apps/sdkconfig.ci.release b/components/esp_driver_ppa/test_apps/sdkconfig.ci.release new file mode 100644 index 0000000000..199b0cf97c --- /dev/null +++ b/components/esp_driver_ppa/test_apps/sdkconfig.ci.release @@ -0,0 +1,6 @@ +CONFIG_PM_ENABLE=y +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_PM_DFS_INIT_AUTO=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/esp_driver_ppa/test_apps/sdkconfig.defaults b/components/esp_driver_ppa/test_apps/sdkconfig.defaults new file mode 100644 index 0000000000..1ee5d718bc --- /dev/null +++ b/components/esp_driver_ppa/test_apps/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/components/esp_driver_ppa/test_apps/sdkconfig.defaults.esp32p4 b/components/esp_driver_ppa/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000000..702fac3342 --- /dev/null +++ b/components/esp_driver_ppa/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,3 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_HEX=y +CONFIG_SPIRAM_SPEED_200M=y diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index 60ab9766b1..e5ccd126e8 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -230,15 +230,15 @@ static inline void ppa_ll_srm_set_tx_color_mode(ppa_dev_t *dev, ppa_srm_color_mo * @brief Set YUV to RGB protocol when PPA SRM RX pixel color space is YUV * * @param dev Peripheral instance address - * @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t + * @param std One of the RGB-YUV conversion standards in ppa_color_conv_std_rgb_yuv_t */ -static inline void ppa_ll_srm_set_rx_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) +static inline void ppa_ll_srm_set_rx_yuv2rgb_std(ppa_dev_t *dev, ppa_color_conv_std_rgb_yuv_t std) { switch (std) { - case COLOR_CONV_STD_RGB_YUV_BT601: + case PPA_COLOR_CONV_STD_RGB_YUV_BT601: dev->sr_color_mode.yuv2rgb_protocol = 0; break; - case COLOR_CONV_STD_RGB_YUV_BT709: + case PPA_COLOR_CONV_STD_RGB_YUV_BT709: dev->sr_color_mode.yuv2rgb_protocol = 1; break; default: @@ -251,15 +251,15 @@ static inline void ppa_ll_srm_set_rx_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_ * @brief Set RGB to YUV protocol when PPA SRM TX pixel color space is YUV * * @param dev Peripheral instance address - * @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t + * @param std One of the RGB-YUV conversion standards in ppa_color_conv_std_rgb_yuv_t */ -static inline void ppa_ll_srm_set_tx_rgb2yuv_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std) +static inline void ppa_ll_srm_set_tx_rgb2yuv_std(ppa_dev_t *dev, ppa_color_conv_std_rgb_yuv_t std) { switch (std) { - case COLOR_CONV_STD_RGB_YUV_BT601: + case PPA_COLOR_CONV_STD_RGB_YUV_BT601: dev->sr_color_mode.rgb2yuv_protocol = 0; break; - case COLOR_CONV_STD_RGB_YUV_BT709: + case PPA_COLOR_CONV_STD_RGB_YUV_BT709: dev->sr_color_mode.rgb2yuv_protocol = 1; break; default: @@ -272,15 +272,15 @@ static inline void ppa_ll_srm_set_tx_rgb2yuv_std(ppa_dev_t *dev, color_conv_std_ * @brief Set PPA SRM YUV input range * * @param dev Peripheral instance address - * @param range One of color range options in color_range_t + * @param range One of color range options in ppa_color_range_t */ -static inline void ppa_ll_srm_set_rx_yuv_range(ppa_dev_t *dev, color_range_t range) +static inline void ppa_ll_srm_set_rx_yuv_range(ppa_dev_t *dev, ppa_color_range_t range) { switch (range) { - case COLOR_RANGE_LIMIT: + case PPA_COLOR_RANGE_LIMIT: dev->sr_color_mode.yuv_rx_range = 0; break; - case COLOR_RANGE_FULL: + case PPA_COLOR_RANGE_FULL: dev->sr_color_mode.yuv_rx_range = 1; break; default: @@ -293,15 +293,15 @@ static inline void ppa_ll_srm_set_rx_yuv_range(ppa_dev_t *dev, color_range_t ran * @brief Set PPA SRM YUV output range * * @param dev Peripheral instance address - * @param range One of color range options in color_range_t + * @param range One of color range options in ppa_color_range_t */ -static inline void ppa_ll_srm_set_tx_yuv_range(ppa_dev_t *dev, color_range_t range) +static inline void ppa_ll_srm_set_tx_yuv_range(ppa_dev_t *dev, ppa_color_range_t range) { switch (range) { - case COLOR_RANGE_LIMIT: + case PPA_COLOR_RANGE_LIMIT: dev->sr_color_mode.yuv_tx_range = 0; break; - case COLOR_RANGE_FULL: + case PPA_COLOR_RANGE_FULL: dev->sr_color_mode.yuv_tx_range = 1; break; default: @@ -643,10 +643,10 @@ static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_alpha_ * @param hb The horizontal width of image block that would be filled in fix pixel filling mode. The unit is pixel. * @param vb The vertical height of image block that would be filled in fix pixel filling mode. The unit is pixel. */ -static inline void ppa_ll_blend_configure_filling_block(ppa_dev_t *dev, uint32_t data, uint32_t hb, uint32_t vb) +static inline void ppa_ll_blend_configure_filling_block(ppa_dev_t *dev, color_pixel_argb8888_data_t *data, uint32_t hb, uint32_t vb) { HAL_ASSERT(hb <= PPA_BLEND_HB_V && vb <= PPA_BLEND_VB_V); - dev->blend_fix_pixel.blend_tx_fix_pixel = data; + dev->blend_fix_pixel.blend_tx_fix_pixel = data->val; dev->blend_tx_size.blend_hb = hb; dev->blend_tx_size.blend_vb = vb; } @@ -657,9 +657,11 @@ static inline void ppa_ll_blend_configure_filling_block(ppa_dev_t *dev, uint32_t * @param dev Peripheral instance address * @param rgb RGB color for A4/A8 mode in RGB888 format */ -static inline void ppa_ll_blend_set_rx_fg_fix_rgb(ppa_dev_t *dev, uint32_t rgb) +static inline void ppa_ll_blend_set_rx_fg_fix_rgb(ppa_dev_t *dev, color_pixel_rgb888_data_t *rgb) { - dev->blend_rgb.val = rgb; + dev->blend_rgb.blend1_rx_b = rgb->b; + dev->blend_rgb.blend1_rx_g = rgb->g; + dev->blend_rgb.blend1_rx_r = rgb->r; } /* diff --git a/components/hal/include/hal/color_types.h b/components/hal/include/hal/color_types.h index e129bd09db..32c2d2b96f 100644 --- a/components/hal/include/hal/color_types.h +++ b/components/hal/include/hal/color_types.h @@ -150,6 +150,44 @@ typedef enum { COLOR_RGB_ELEMENT_ORDER_BGR, /*!< RGB element order: BGR */ } color_rgb_element_order_t; +/*--------------------------------------------------------------- + Data Structure for Color Pixel Unit +---------------------------------------------------------------*/ + +/** + * @brief Data structure for ARGB8888 pixel unit + */ +typedef union { + struct { + uint32_t b: 8; /*!< B component [0, 255] */ + uint32_t g: 8; /*!< G component [0, 255] */ + uint32_t r: 8; /*!< R component [0, 255] */ + uint32_t a: 8; /*!< A component [0, 255] */ + }; + uint32_t val; /*!< 32-bit ARGB8888 value */ +} color_pixel_argb8888_data_t; + +/** + * @brief Data structure for RGB888 pixel unit + */ +typedef struct { + uint8_t b; /*!< B component [0, 255] */ + uint8_t g; /*!< G component [0, 255] */ + uint8_t r; /*!< R component [0, 255] */ +} color_pixel_rgb888_data_t; + +/** + * @brief Data structure for RGB565 pixel unit + */ +typedef union { + struct { + uint16_t b: 5; /*!< B component [0, 31] */ + uint16_t g: 6; /*!< G component [0, 63] */ + uint16_t r: 5; /*!< R component [0, 31] */ + }; + uint16_t val; /*!< 16-bit RGB565 value */ +} color_pixel_rgb565_data_t; + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/ppa_types.h b/components/hal/include/hal/ppa_types.h index 8bbe0fd5c9..30fba5ebc9 100644 --- a/components/hal/include/hal/ppa_types.h +++ b/components/hal/include/hal/ppa_types.h @@ -81,6 +81,22 @@ typedef enum { If input format does not contain alpha info, A' = 0, i.e. a layer with 0% opacity. */ } ppa_alpha_update_mode_t; +/** + * @brief Enumeration of PPA supported color conversion standard between RGB and YUV (determines the YUV<->RGB conversion equation) + */ +typedef enum { + PPA_COLOR_CONV_STD_RGB_YUV_BT601 = COLOR_CONV_STD_RGB_YUV_BT601, /*!< YUV<->RGB conversion standard: BT.601 */ + PPA_COLOR_CONV_STD_RGB_YUV_BT709 = COLOR_CONV_STD_RGB_YUV_BT709, /*!< YUV<->RGB conversion standard: BT.709 */ +} ppa_color_conv_std_rgb_yuv_t; + +/** + * @brief Enumeration of PPA supported color range (determines the YUV<->RGB conversion equation) + */ +typedef enum { + PPA_COLOR_RANGE_LIMIT = COLOR_RANGE_LIMIT, /*!< Limited color range, 16 is the darkest black and 235 is the brightest white */ + PPA_COLOR_RANGE_FULL = COLOR_RANGE_FULL, /*!< Full color range, 0 is the darkest black and 255 is the brightest white */ +} ppa_color_range_t; + #ifdef __cplusplus } #endif diff --git a/docs/_static/diagrams/ppa/pic_blk_concept.png b/docs/_static/diagrams/ppa/pic_blk_concept.png new file mode 100644 index 0000000000000000000000000000000000000000..21eadd08264db764dfc3fdd8a413ea30219375db GIT binary patch literal 26960 zcmeAS@N?(olHy`uVBq!ia0y~yVD@ETU|hk$#=yWZOO(%qfq{Xuz$3Dlfq`2Xgc%uT z&5>YWP+;(MaSW-L^X6`4MR4jpyARghmzQ{cZ@siPV8NRXj*@}^M%EOEznolM+^^2; zF<#%-{Q7}WpYf{~7J8d^oUwR+V#j6+jTH(_3r-{je)G-HT*`g-a$4r3%TMOqmtVNt zck(h%PG9f$=SvH3&Y5HLd^Xdh-RD2o^gB&b;dB9mCzFltunGu*z}GY$U$B@lJAk>oOj<#TQ@yn4su9VagPd3lb2EYwxU!-Oa#|nwolJYqq$W z|2!Uszj_XqGJHR)F9upTO}?0M5x21oc+yAdzet-YP#qP@1*3or)vajozn47bg#mI-P-m#qq$uzrT0a_~zxUJN~$to!d`#TYfcb;nl2*SFW%uzSwc& zM#Pch$E$mDH%2@tv3h#^<;$0L*%^Q1ZseH#v|4*S>gbUpKkh7lTwoCp9Q?Rk-haUy zKXn~3?kicQJZ#Kw%B)wdUcK=8>xmO4JTR?${e5w0RMf2VQc=ghezjw9oRD6Xl$>lk z&EIPh%jA?ZrVgPYAtz?PwAQ_7~x>I&L_xaw;L%-ke?|=3zEvEc# z>4}pkC-&F>c-U?r)jPwwT<_DTPYevdzr8(tZmzXq<|UPo(9nfjwuIQ(+uO?;+e5^vo45OjhB{-~ZR_ zOj@zWv@dfli`iC(uXl@zlKS-N(}%Cu<4>PIdi?m~S=sA8u1GvT&-TZ++xfP~I{NyK z#g^ZFT6iXJ`{TRi_gfV>W>^#|E%TYlbl~svgtN0uSFBm%QdMR3?EHNF_GQP8xvh!c zFBcjbDwiBvcvMuvJnxR>-t)yr!NtIp{w?zZKW+WL@@wIW@AjHgz0S-s<^KQg`~JYl z$WGhpZwZH5I4z2w@o;f-Gctt7*Osn-YgGQ^gy4*`X-AG9Km4#@!OoqM$NOZR!@|Ve z`eZccPoFuHb8j3!!-{q5+HU52lk#2}lJw%j!cUgIzP=q@U7xsC@0+&Aa;{(HnQJ$9 z2CP`U+Br5>cG03m6Q)jWoviMED8nS_>@3qAf4|*+nlNL+1O;}l?xT-H3!)@>*v`zc z6n=eet#e|cVo`B1DDL)Fe_ym|laN#|+vfE1(~g%~&5hVwwe`=vpy1$(7cMY33T#L{ zEq3I{k%S8i92KkgA56+vBzN(b{Zm$Mu{qP{#|a7w27aij`X8K=qmy=aRx7WxS;FmY zxgOr$hue6i85kZP?|*-_y8cYsW*cTzPoc22QL5)}Y~}Y|Yplia;jH=ngzxX}#wCLS zZKg3h!-m@5WtJf|c(J(OO4e9kpMSAi@1cdx?Hdvgvz?h|8$IvjEX!iG zMT-|dE#{ZE-GAL`S*tQ2KYxA-+WF&0g^HTmqJ8`1OifLTGPtfiogUw}X_JwO zsp-RJemjN-?{aPw?ztwpI&iVu$ORP85R{a>`8MhttF!Rej(bcyE?4|} zyz;UYF5F~4dT&+f%eeBe(2hkfE+(E!+W6y*%k|5*u3rhv=P!NU?6;izq=nve?|-+l zwjO#|P*LiU!@F?byZKkDF5fEnCud+1*gwOvSdCBKjwkw*#GBw=#z}``1;3{CwS6j> z?rYHAsNQki?nci^DmDLpJ{MqdwEK25eg4C1(fPbL z+rO;aQum|GLgv%PZ@gkvLD>;6)+}`L7jnJ0SUBM||K8-Jq+Pbl&ZZq+KEF=OUW|oN z+C1;b*6VSv4U4Bwn9y+PQqYoR%aR@*YLz=*x~#UN$U>%Qhn2jb zhF7m&2Zn^S+_`f{FFk}$&ZcA0BBhw3ld6ih?R7br`s8f2%%`tdx$@x0$Hy6WSl=wY zE*@Xg`0QEQlI6=EpVD4`E}sG9~Kjl`9FaudQWh&|bes$k5PmhHbUk zy{gx`BI4r7|Ns4!ur7OJ8LKQ%Sy`!}uYY`H@bV9*wAV8f=reZr_qSinII^qswL#h$ z37`3Pwkry~y}dW4o)-JL@oE<913t^Oi6s{nILcL5ZQ8tf;l&IE4yJ2squCiee0*AN z<{bO-^72!^(zPs(2bTHHX4sJ)E~XbF@nrY=JI8vZHcZ@F`Fvhp&&xm_0*yH{VeDR~hfV_zq8dt2_| z?f2`tPn__WaW+jSZjZ#tlMAD79f8yt{$E`JvQ)d2?0&y7W_ZWHYwdSUFCU*HTQV$17Cma^Jdr@r7NZr(O_Hs?ZJzSiy7{0_G@_m+aS|K3e>P%w20~E&6~0N zt~+F!NX69se0pP7>1vr7MMXsgS3|>3z7L$VMDPB5x5bPP^4@Hy{G7&c02HK=izh0( zFIuvMMcr@Cgz2f<&T*WbV<{{tDG6$7o=MyMhr8f?_2;L@?f)72s0puHwMv1bY0sO@ zOP4;KvtRwK<`V9uPuag_tqxy*?3e9Qjn0`3FZOso)NAkPSP@?N%E-6>tO={ol!kqk zw|TmcHkse6IQ+cWd%E6Nt7j!v7w_EBd4DrAGqX{}Ghk&%Vy2Y>2h*(CvlTssl$4eK zzPq?FYV9ZAlJl;Sk&&Pr8Hy8LoV^CZ9JuQz4S zJDWCH+ULdBs(yprd3UWuL`5t2K6|x#{jm*+hnZHj8-pAA;yb?mo?_LuG~i9}tJPPs zwz^MlzP60#;i8KfPiD?q*P>+bb5VfCliIL{OTt#$elsa`U4EJMu7}EmG~ps!$6g;$ zYrfXaUvK|wULLmQ^OCBs?ms&>xB6T%q#YgW@GCk%=@loW*)HC2b#-|Bgx^zM{PFVh zJ63u<_PT!lJ!zgcHKj#%2LkID`Zh6lXhz7q3+h-QUiEV6bQ`(v&uhIzWY>&zUo) zVEV<^h9-i7Um5#0F>iQNB|dRxvcOD3wL4D*0`-k6Kc&36wYB^7`;K$Mkanrvx&;du zSXo&g>NUS;61^Ag59z&pErPhlg`6(Vm3E6$d@Z|Kl#~|LwVQ8OReHs&WPF@~A&E<@ zeW#F;(xQLu3p+bj@OPY%Xg<+n_)xF8qhm$Dbds7nj;O0r70@8=114l}c7RTiNBUkA8f5i#wbsfEf zO_h>@7y6!ic`^I9vZqpx=<$U0x{WvPa7P_g`#IsxJ7kHF|Gfe^rtC zmA018k&CA9%6-2e@3*zbyejKU>m#o%zW%N!VU5Gf-}@W4?zr%{;+^dJ>Q=SD4GW9z z&JWoB*tY-i$?3X|SN7bW^F7M-AltzZ=A~Y|zBRup_sa#rfAwZ!MQBm?K7 z#ge7B@AH;>?rl@?)TsT=7~%bmf#H$Rf57 z$e3Kb_k3x@!xegMt9J^{T9V_UmgclD2`uH(&9#}jOS&b?l|YfBB!?*EUs%~BQIdi$Q+)8pqW<}5!i+yBP3zDdnc zn4#d6{r8HReAk0r$@P0qJvN5Th_d|^l72IjUdB?9res!;Wm#7*Ifwdm7h0XyMBc^&pe*kV;l?) z#V_kb_Et%|EWG*t>#XkmtPl9O@A6h1ymIH%(-H=TDN?c1AA6q@`@Xa8mGE4quK$I* zWoCTY`B>0A&kU4U4Hu^=Sk`B~>GF7-X1h2cowEWs}~TwKJN z&Vh4Q~q%b z5Au2@Y<*#=A;NVX1)c5t%k9SKO zb4y;gZ~T=X{_sq?K2w2yC-a@P_xNoC73Qz*^K$ri_}#C!TU9d*cTK;LdjFHqqvNq} zw?Dh~KK7Joxnk+Hy#cFCH(a0g;Kt_73EK8dpFhfWNS0{^rQhXdF!=kS_YQ~B^J&v} zf8F~^$GV#eQO_e{MouQgPq~~8Ta4S`jSO@ zhh8k-_jPy5nap+0FQdOZtHiT<=lL0IBc;A#QPM*1)9q_NNzCaDb?jH4 zbN4wj!>=_PKV8pS>nOMVX-+Up;D(dN+J?%@D|?p)xc=DFR(L+jMDC8leldnkd3B7p zj;vYmns?i?IY;$RoDW~~S2=5cRNoR!bLGeX|6a?!v!tojLo0%VA;V^i@f}%|JbG-O z+~wyr|JYoAnH`dAAD(^p^JTBBLi=e`-W7|y+7_@R$Zj( zn9k2R6WFuNIg(|6in?|IK&bmmLjx{Ly=5XUnY#3{%hT zt~~m4g2l@!hK83f!Z>`d7DTUL|fNj4Dd3HW$?hm`&$hGX*yamz|*zcaH zHYj*zTX0Kv#m!IpS8}R&zgmay`p)Z`vCua!`{Uf5ANmQqQ z@YzNZzZ``VY=6r3++Xi{OoX z)!!=1SKQ&JW$!b5-#a_%c&@&ZnP{M-@&eyeZojs^OD#OR^!LRL)xO*Bm2WVeuRrND z=kmj^3VlRxUSn|Dd#Rky!|E&siA zm-5C0L52l&*STi&7V=by{e0gxD6zO&f?2+`zUSS`eMU@s7>y6^LD+Uc!$?+EhawN1?$zivBem{w&Z1v9jAIX#x#+4thl$^hv{ zPd^*yS8&u23(p>X8?;cK8<;np-6sIyYBDYFqrWDR1A=7kfU*?X1JC zJM9a6PksBjYRgW3M}x2hKfgGBKXd#h|CglQl9P5A8c*E)M0Cp9Dj7d-W|xCpOWpdP zXuorK_2vIZ34hs@?*D4D3)B9d&iL^zxuZ$++bpG@mCKi|dz-R!Q|-%5jo0m%F3*4F zAhKq~uScROF~2otY1cEjypPoC&vrk0>bpAEvS(&peE+{b&T+R}I)87V-{#+YyP87d z#5i_X%N+Bc(Z1!Tx9z#>OZkp=xSK8fGui%N$$?sbmFUXobf@KqUtQekw=h5<`C{>k z+uy`q?W?^0*s7?QZ@ECgbr;{7UG5g4StTAiI~cdd@8x8;z(4DO;bvEtEApBuo|6v6 zvc&!fTOZf^zowY$+7)ksx#pU8o(g=mTx-7F)MuZhA#1noyvZ)&F1uOvc1tb_ZV1YD za4FezCvUg5U$*j`ZE6~(RxU2;rMsq|$TV~+;q2(}h{8Xu*$P*tD?abL zF7SM@vQq1{LxNd4QU~X!3V#jzbKBG9%5lvpTA+fw>s4I4=~g?v1*rvZr%z@zS*K{I z!pkMZEp{N)__wY5{I}KfgEpA9Pcm}i_MKoZ!ggn~$E+Ds4y<2%?Z+J7knF`14L<(T zi(l{grDpAg3bv>6_RI_S@d$og*}=PECA;9oR&Wa!+_BiQ^!4Fbw}>U@qs86{{Zc6{ z5!#ejqqd+}wl_WC``03q>GF+RcL+38YbL&XyZ+bQr%Fn%^ncVlf%+`xQgdD2hM#_V zeAkwm6ula;z?X*)hl>aX>K_92)UTY+e8i^o_y1op!9wPRz9(DFEY0tz3JQMJ;_N2zL`x9uiolz{pK!PUmx||V5iU3 zhZ^l29UTWQxY(}w`9e)&mKNiJ(*ch(>;*p5X3B`NGp|#eU>JRh59EgF4-W>`F??Ok z#GNEKGkJo||DAg0SI7P>IPl)O@L+N5wTxRo`E+*Q8QmMDQf(;n;CnP-AuxAKvc+6_GTf%1r@00WSDbFM#%Jz15l-1ka`hRcB zzkge=h#lVYbv{Fb)@|higAZRUeGmP9sHd2dWZEqtUKtEd24-z)uL}2;ep|4+l+~(P zXx&F8<6ZL%U+cv$RJic8Yp$WsKFbv|53gg9Dq!NB7(4g+ubKmjmIvhdT+(_RT|Omv zFpC?(d0mVLVo0<~W%3{up=gJ0cRojfNn6fDwCG)Jx>EB}eE@`KD`r?j`4yR^N zkB>i7zD1&|qobpWExHfVzyEn~rrNFtoL^H5B(xWpsW?ryT5|cbif7Z(9Rdwc3hzMs z(xDr#Pg}5brIg^tl^4{We|fR>??e^BZC%M5u1|Bw$!j?^fur`E>xCnHA1e3lT_^JG zRg#7Dd54v~ZDkJ(T$b{_ez>Z$qod;-FKC!}R@%zflg~~5_1@m?>+^c&KSw`LsJ;Ao z?mYFKEF$~MR$u=Z=>M+e)`S8(?=L~y-^yOet!FIR=M6N3Sa=+3_wq+jHW| z!MNw)`9G)2%vkifo~QN~$BsEni`xIKaDTORN=^Ud|9gb9oSEEgcW!oH=jr0&BCgk_ zwrI16mg%Kucla3ue75rVzK!s!Q)gecXZQVP#qW>&&WpCDh2;OSW=MGOW~qe11EuUgq=BRH&%5ka+zfWe_qh(`SMGQ=cEfs>skY< zxr7*Ee{rnX+Sag6xzo==N{fMEBBx}4MyPGj=Ech-7#5tDeQ$2SIN>#O!;_WwPRm*s z&3gU7U|!?tYb+V9Lc|YWy~E)fWTqr+yf9$P*Uf^nRw-_~ zaq&_JD}w+l`*+ePhQY>r95)__1{@G%qKM+WLLIZS)F`rxp1M(q8Ck3;@n&9 z^j>W2?C4lgo}96$&RNJ+=KpKn486Ph^4@BPBAuCEq(tF8JrP3^C|!oU!CGubfXmNOQZpYYktQ*D18~$41#r*(DLt&3$o4J+sSn;pS<3l;&%j zgg$P6{`^!wZ?pR0rv?YZ>R8S${T=w_Rj$OYlBxF>T#Wgn%TONiBJTqCvS(p>%b6J# zTzRoeCcbFfj`LrG=f4mM_|t#;Vp7R@j+e#9m6ggEM6`Hg__ViZHrr(XE1N1aV~UjR z7Us_vVs^=2n`Em|`8r(U*K7WT0UCnFC0r&SGcz(*O`9$Z>SG&DQ&U`g`ul^^4XfX| zUEa7pQQN-h)6{A;#mCjk^tE+$;I>DB zGx&0yijM}mEahis2w7V%+3?%>|ApWGw6_awpL45w^8X#eC&P6e3_pNDc3=Vg{m0#RZzt<-2&Wy+tJN|KAso%|&ZdWbXQ0*!6 zAn$A4i%zDWF{L3)pXAG~=dahD$Q=AlS6{L2&egi?yubIB#uSQ!I@C*}n5%O+`)sYb zw|!DJkiXwJBXPP?>9xHKx61pP6)iW2nr;{bu5C86M30nNj~<)%l#O#NR91>!$Zuo-Ng=eI3W~HTB)=Sexya zi=9I^zYtO2;U$j%!--u#Sv5ig zZG)VbRF(9{fNJeq1-~+pwi4hhBKhng0DJLxWxRnm=2k zvL_tRwY)9jD3bH(jMteHnQ_Ub-x_c4NY|C(&zlg>y~BP%w(#brb>=r_6^rdxum_D# zt@xW|IceFQo}T~Tqt_Qs<`Pp{nV`t9;Nyj~IaQyhUrxJETn#y;qpTL zr;$6(wAQznY3@G0&fmqw#kPN8=P%ySmTi}xi`30~{l#?Ew*7W`%bDN)zTv6+E>hI< zrJei2JZ@L7(g(&uYmIf@z6@$-iOigxkY#tDE$*xLk>0&ry}z#={&Uz2Gh34M^DE5a6iJgf6wW{ zXz4HuUEYGIR}0Nl+SWXKwfVtoOExIDUu1%%$Q}HB~laIHF3(oFZ0V*_8mH$S4-?Zb|rN5xj%joyk zH)fp|Kl!xfa^hEuWnDShO^RG$9emOZ4!IsKA=|1o6Mwe*AE-%EcR6^?Y}$)YQv3hT zTe&MYzjRl;R>ZZ->;Yd-erCAy_UPngI&2J6_EpE9Sm)^C;bNBu>U+`-3^T5p?Lsxvcm|+-}_~LK2;8ZgnuBlR) z_LTz5|9#e;(U*U`G4=6+D=(h9ad-xcRll+In)#7i;`_Sd^I~f&L{hcoTFq4X7oBEY zxQ{3E^EAb$n{HdLwa>d!a)f6t)8&tES44a^Npkn&|K+yo;I*245vTbUy~yWYc;&^y zPG3e5EsoywCsvC-FL%oAn125Jo`;%$KKzLRjiUVd^_Jnz@A)xi#xCumi|5^WX;|m- zhT&&*=+>8|A-Pqux1PjUH9nE3?8$JjFZ~_akS0pPO#i} zxAy!>{`nk{%k7kulwQSJTz2`}u-kc#Mnt0Bx2-ZWKwa*Cn|d##_kRUJ zkt=iJcjY|T+JEBu%3p_S?Uz0~C4V8N?#6rlg$wsYo0k8Ps}ggTO*w}ftFcRjHZs^7w4Y5xD_v_f0Tjy`>yS`PmAWG`itYV8+z5V%TpSC~TV#dUvb={5|(kb+qTfG0bZC1&Ns5g0A z@9oI9?Ww+2`uDFQXbqXWqUR)$cSW|8X~kX6#r;rFUUf8yn&mKQ<-f|G?V-0<_ZonvM^May)` zuOE5$*L*v$>7!ikzS_?H6>^*MtNzW&=2P8xlyCV}j*Un8mIm=o)noIDEty`uRmIb( zd#Bihih^8IqxPK-4mZlLxs`AIV_sNlH4ESK zg9XzaMW%Rs%~9QOjX*JI65XBWZIFFw*UEy&9(1?xwbtE2y)9<$oZ-5P(!ttr|@KYi^##rFfdn8uQ;-O#Jms7&E_cDx zaqSj!{ygw>ak+9C-fzX#g% zmGT?CdooixIy7WZ{j%uuIXyWpxSv52#}q6m4eVc$Iqgs^lOSkG)^^BbGiggzpU*v9 zX>dnXK(JJr^kuDA(rx}Nj!*u4XzGv8qW#WGL)v%CC@H;C2YGpgzsFVehrbewC#>i` z-ZN)q#n)5I5621#3Vsy^jdNU4&j^j`Ki>Sf_)sthZ1CfX`W6qr{^Jw1d}nAWDlPig zwy?9K;}^^H7m~A-rU)jkYU$|sB@G%Ad8KUg|74JMXyU47m=M>><;(xEwQjHL@_)9! z^~;MZ6M9#7U#PSR1I?8O@+W2}DJ`0?v-*$F8A%&isi|y|7nwhvpLxDLCr9?XfuLaE z3{dC=o`{(i`ru>4{lu~tF&S6q-Tmz1$#e6Di;K%Yi1Z8PMK*G?S+h?Z>w6_|AVvuk zW?y;k`OWR_3xn32H+_v~ZZ4c17r!(_l!qHyaA79=3pR*j4 zz!^dMT6@BtO3v!}c5FiLT|<+t9lDCr)^k1yw?8;FXC*^};>Vp9i8=?xm+#{%4t?H! zw^ShN>WhNujxHi5dgqmu7ES03sJv&ra@$V3l;rZ5dDn{?&zO{!d_U?9P7b99Km%Ed zmp|U~eEQ5-QF`}dooRPpa@@S}@QF~YTz~MQNryr>1qDmNA?)PF*n0eNuFBke zVglu#Md|ppZ!9jCB(|tAWE57uJ1;fcNXcg^ zLZReEW?Q-4D&j6Dub#Yl@b!+Av$vACZ{B!#?W5b?n;Rv;5nq#-@#?VOJe$t#_v@rX zLqlWd%Q!9fpMR`R*1F)&kB@SD^#s4>WnNq<*YCXdxySNrRW^BfnS3vQ?DgEFX9!B* zfz~{}wNoy?Jo4w~=Z`m%`x9SYT54PM7PM$HVoQc#TqOEuk$Yp~^~TqPL#Rnb3RtpY5y7$DCGPEd4p_-n!(~ zInf={&zJJ}`j!QI9OrM{sHtqA`d7$hmC1_Cb%+1mT5J(g{bo;0w%0G!2%~3iJiZb< zwJm(TWx{gzzp1}j@#Ec|nEs9x_Q@Hq7KdnwhOLciooij5^y|yZhkETA3SaNn_q;m) zzwJl&bH(4wo?F}c7jbITy$xIcc=5bDoLdj>cE0^H1a&yZ$t>^p_Zk0Z^IlMU-j?&G z!1Kb0%;w|S`5%w2T)*42t~#nzB~PkQHczT>Zus^? zF0nTAX8kf)ws-%7IqRKWT;@s4I<#ixN=~=Mf*(JA?C9-vt*o@{>gt*>eR}(sFC`jQ zIW_B6t!lb^@0wxOqa&TMafe@)EZVb2X1U*7CnqPSPoFs>#%@zq{g}Py*m|`)of|ck_q=Pp8rNjT_wiPQ#9QCHyZ8hJe;&Ial9s>k zr&?UqOVvBo@Aq2N{4g-fy`^Gro_vhw&r|*S#)B8OsXP_m|AY10o12dfk1cZT{_tk= z`Cl^syY=@SILvR~^Xpet|6^`3odcWC+i`E*^}C+MCGg0jqum>GZ=3zRVR}8LSvFi<$EbREBMFcCEf?#>+O~Q z9#g}f7g^4)wf1V=`T52bR{uUcZfD*n__lWX9~r%it>yh|IA8o(!6ExjW;4fi{=HI# zw{Ga2pRSPT{gThbQirkE-^HczvHI<}M~nOIoH8>t-Fl^3&z$iAEvcSumV0Qjzg_3^ z`T5buLIMIBo;*omIMB$Xmo5dZiQ3A= z)p{uTO+xOyJ(6PGtitMkp!I(j7rTG_cwGMRC2#$syykZXyBBnSEh^)mNUFZqs6WmA!V}=87j@D-s_4XR-Y@H#wh6!{o0;vCuaCe>+ZpSJ)A? zVe=>JKhGa%y;*I^5;*D4JbxFLyN54a@t!hm+N1mb|83u6pZWCE)QS%W**6wF^}194 z|F4H{>HjTX7PiYxivRp(^Z7?xug871oOk-^p~d}nTnsY|5}Bsy$M?N>k-=~vI)88L z^!PeS^SdRNEwAQmjM$KLl*?zHjU>a9$mO!uWd0< z61#uzSLXEfQxzVDBuj?<|Im5gL2%)hoT|$&l9y#Z?tNdWEBxk!>dt`7b#9Xnx15<_ za>VV}j~&yb{+EB{{_*$Q^bOB`RP{f9_B8gXxj|@Tc4GOhiD@2RUo}l%(Ch5>#bL^9 z>;H}73@b!p+dte*|Mu|S+5N7cSxfe7DJ{C!wQ%Kh0|}lTe?Faldi?EdYY|b=lM6Ki z($CGAn7&XvYtb7sE-os5w`=;086BRJ)lT-OpPA8^dwbi$MT;KIIkuF0 zKEW|Wb~Vz+5Jjg*y{|P`3tFYa@S^zm{LgCwo~r9Rth{*GU<<=C=h+HeVRP#KGxk)Z z7>k_R_i_KrzPX>97B(>LER3uuxTL=^<4Ea@{}=sh?>+Xsn`pNFWa7698PSEWjSOU0 z>mQPR{r)qDl%=6$Mb4@9hKi+Y!ejk?cJfa|`gWESGL`%P@VDv4Ht(snYY1BG{z=sP51ZQZ!(DBA-${xbzTn-wZoBf| z1>J_P_^k~yPHLan{kxXIp?f1&-!G&7&s$bqd|tEmv+Gihv-awsR^z{z``;V7UkE5z z>%A=Q^4p*M|NDu1gkURHEx>p#Xbe?OQ_F`4(R>h5~=KOz&gzF#_^uDAF6 zbGc>CTaSO3e0BSYMotD7&qK`7{~na)EwQ?kKX>lR&87)2pWRP9_bOUqMn^}LgmbX| z!_~86U*B6=EUQ0d?kB6=Y=H)dLCEc_)zw!!7aT7(330HgR=)bMIzungh8GihpXa4c`Tm4^a*PjXLe0}ZCOHmJ#RUCJWvh1dCvFb@y zOuYVD>aFjNx0{#ReHRv$^tNf5@*wNr#a)lDe1aRn-^Pt12xjzHWZ4 z_6u{^>gRub=J=`i$rk@VJa^hOu`d;CH-~_CjGUQc`S}i)3YQ|raOUFo{FnZ59NTs? zqBbo0ML;(1ryaWKv&}l1cy-;rniZhk#|L>oWiu+gekQ)a_YBvr zdwnG{bB#T?mU4OZ{>$NbV?Fzq>&nlP&xDT&{rUU5{_*Nfv2o|`+B~wdF?cB+$ICx0 z?o;p@`=3jX8>a5AwVNC3U%+``t! zUm=-wzYk_lJE>_^_*uRD@YD2Zuc!IHJGf4<%|Uvs#Pm%D1ulP4)XvakPr z*pYM7NJLDm%I1BE)y|&2LvErA-+r-do&Vgbws+Or=l1LPsuP!peNTS*#;3#Su2|QU zR|lrAv_e{nbX9!muJ`9}m0gSbb8-FrqfPyF&x{Qi5A1fo_TWCd-3P0AOT52WU9(%S z(Hz0|WBT3yTt8wM3OfC6SnK@tzHl_rVaoS6|Cb+~Z9Ug8J5_K^MD!NH*Zz4gzp(9U zyc6;L*u(1;rqdLZ7R@_&;fl8e51Y??JK5mnen)#GjX(T&+|SIAxBqWh<;;86~%cZS`)?XFr>7 zHks)-Tt4cDDKHUIiU7ZofGeit~fR!`T@OqUIMDL(ll94Nphd2Adu6`1uC{IJSMmI`WLJH@J0l_PUTEyTZ=23A%|C9cbzby|?DS(xrJG)B zNp|$YsvEE&TGH@c{|fIdzyRr9)~ASpFUi> z{a(}Qr%t}Uyg6plTcUI)O`iOeuS(iH@5_$=!Rgy_ZZ3Q@AqDR{lja1 z=fH2{KnE(V&WhPx>*#Q4%2B!M{-Cr!Ovm=VzL2!W>1LNZXkIKM_>;me8G%=)Cz$0+ z2I=kId->O6H(&QN7c>oI@Aq|?`YV?`DOvHgaN%FIuZE(}w6=8kt>-J>KX;QzbvEC^ zGf#GNzu3@nu;P+CcmDDhEgKK(^s$^weji-*#i3t~8`S@*yi@MEHY7Rn;Q9~8=hnad zWP3)ggthMf5?TyPo%YT%B0; zX+$Kh&%7P{V#}4SbMIIc-&%P3%Ykh9#+UxZ3S40!wI8{R*42uf zoodezyJc^f`%$OK6CT@3rgnS2#aE=BpM4{~ zn8oy(RYyT$)wlk_!yhV7^qqW>qVs;~WW9?|FD})$H{`!RrTX3Vn(rs1Y@$AB_a~b_ zJMS9eqVjLU-&BruV*SsPOxW(2JhD0?X~R6z@SbnEipP%Pzsv%v;-`G3TI8;B2=m!> z-14Fnv)R2LjA4I2aK_!A{W`y5o0kF4yp9)pJY)Aa=y^zMK@H>C{H zuj#HeJ;xpx#uEn)>+I*8K;%D%Z1$&QbCBvF{Z_ z-QJ&_e&3(JvHEQLj4v-+XJMf0gKdH456(7g+noEoCq=tWjpvYTvRIi^j#cpOnEt2j zVUPYQXV=_YBgSxT{vV;VgH7VgZ3Qo^nH476Ip>DYlOt)TzD7sS+PMCi!N2my$_1W! z{ZGr~<>L7_I&*)&{(WC-)oek2-(O3w&s!a~)c0xQzOx%PTJskz>+JZYvdxlv^PD*{ zpiO8Jb~P0{ycLzWIwu_q^7);>Xs3JcPUeRURWG4^dbe9=YKuKw_m%&^qx8zh+V}Ur zG`*+FTzme`-?zW(laH~RC;dICzdMKfo|4tZ?Y{)X>Wlc-#cULKFZVBhdTD)dTt`#+ zx`SQG+17jAr`rGO(D3ox&$7LD7vubSIoYS{*6iar|MpYdI(CNDeQOVY`7Zvz;jG~L z=Pc776qogZR#a8bTH#*cFC3Wh<(aMTJ{Q*)1(9itYD@YY%W~La7ICLO^O9Msf9T+? z#TrJ_N+bhr?XJ5a^XJtgYXMO~knjE9|KZ*?wf6SY*CAe}YP>S-EV(jDA%X!8jFS@% ztz5<>BI$kZsjid!!sD{Zc}LdV(KfhXaA39RE_->IzY^!){j#1^!s*fFb>L&$qlSI* zo7rO<@B8iK7xXecbvFIO?Dg{wZv3@KFJ3%v<1E*X?me1!Qolsqx$JVK+R!AhKOsYj zt9PH+tY3?}gO4XveG}>NI^`bbW0jNP=Zt%>G z(~IA$n1Aeh)o^^{of|$POZ*Sam?GG5?Zw9JuLS;;KGfz#YFeyVxNy+NG*Q`?k zmqOaU_pRY9*K~2Y!kfbanu&FB!8MU_@qhmNdyf~ddtcMPzrOfvs*{kDlLr$cr+}hj zly*koVkL#pRqHM!uUvlX>bCy8>Th%MqR%fZ+rE8kdU>w@_ltIS-oASGYTMSZRB`{6 zFB~H#oQho<f@xl^D>@yR@&XGQ-cp1Z5f{Bt68lIM);pFf^xK9gVf`ERZIgv5KF z|JK>ZGcY7HUSMHhU|+WqB>V1{M|~{kMEcm7#Iw6 zTGT+U+2D}D)ZnRdGE4}kYZ78qEWIAzWo3{MbP@*XiBK?MWME)uV5JWCfXbvN8olM> z3=AF?2uFb|;wH>B(3|vT${BV8OUp-h85zRc-rso=cC*CU%z0HV_p8_cUxtR~8{E6g z#K0h7>Nbm^=hoqF*C!ZqCw(i+f09%?77YL_ScuIXPDR+7$!?E9hxTh_siQj({lGV_35^LbD0`dmz9|lzItseO5PXyKXsw==F7a2f$!{N8$9j-<%8#2{wYW z8d$d0Ep*mA_t1Wwg09Ick=&0LWsBeZI(j#+_O-B|?Ov;@9Y#Da<->PQnJHpx@Re=J zs%r~o?ONo2`1g|-P(qc6Z&6cVyqR-pzRrfYOE129-`4t*^-{9p{u#}-f6sZDg|8o9 z7GLG*S?cxujC4gWuelq)kE5r)%=gT<%kpYVv-|&jxhhxtUH5&%=b*WVr9qJ@q08mS z!1-d=%Q8z{JENE1JnMHYb(g)jfM;>IecZcCcY02+*C^dzC;gfER`g@@e#@MRjcJZy zPu{LcarXYi7H6UH@}v3Qs`ZMSZ0;@G)!=8!9x|o!r6Hf)D@F!}$35HS9hkIEZ~S-l z|GntxtFEv%U%I}%I#fJ$@~!iFU4QNzecf-9?w0kW^qPK=i>~IsFAMK6Hfp}Psl2b_ z_WNsrYjw8Pm|-zjk0lnZ^ur2J%wwTMA1#>;GZfl zoyt?9XL?TD?!B44ZR`E;pIe`9sXy>>LozSp-}Q6jo>ubAeb4dghQdRx14pK%Xm^&2 zGb9|5$UL;;glD4cDVIslCI5u~zka+nuz!0A`_}DdMt3A8J^MD(>iUg*;X5VePwrQ5 zQvOtCpz$}O{(b82-*w3jx3&MUs4*}Ih=U?!X;jGB&5=)T|J};$?EP?xH~)&v{yp~% zL^iTd*?V8RZ+>l7yY9(zYzr9}dd!`KCp7$OpT6(iZ2Jn&{P-!2a?}3?Wvt4UpV5@} za&u+w|Krv_1TRt zm#y#2vP^xD{QCErWG)t`*HeBKeU;qt>*&T8ua{mEvj!#Ib#u>o3o$UvsQ6JK?qP88 znlbBA#-#_ueK%)bJgawlTh^^6X0O)oKb9`BTdy;>w&+qdFXPoMtW)Qk8Pq&oBK7Ot zS-qdkmu_BL@a@Wvt;>(Ce-$QX$H>s|L&=2k0{{0fucFPZluvcW`{g@SCH0pd_!yQS z;90xs38=K0XJ5bA-9GkVpVXWs7phiUO13>qvG{V+m5;SKXmz;NZK(?re>ogtUzzv+ zVnsQl#6%T#S04tSxHGMc3z)ao^kkNe|n-RVDw!B2fkghK)`cOhPdhm-gJ3 zxUhKrSDszsK>*qcP|#(?yowZo>!*rnO`H3`uNs@FVlDL`Spx% z{i{VH{X1lK3(h+C{i~|j*2|0x3`s37D;=6P-fDm86dusO|K-%fYEz%ym@+@@^3T2n zt8Ou^>NQ#=;v1eH@H+10wcO2bUcH#A70o6eXmwNM!t}V;fu7rZ=kL3<@MmzYm)x<{ zlO{K+F)$eLarinc>YmhE!}xOUu7-Onr61Mo)yRFfqQ~fvNk=j-qii0LVy zEIVphCb~>|*0da#kO!}Cznw4tN;dL80|P@_;{_I#9fHRe8D737d}Z3@x?Iaz&61sO z7H)dJ^ro4p-^SIa#V>q*|8vp0l}rYPsy8)R7!y7znJ{+d2|eLkA7Ax6#^L=OZjY{g z*M%7v4n!;zV40Ax>vwuz;uJ}C1_looP!bIJKKs(KZU%-U@t$Vuj~#G1maj1(;nv=R z9g>x8imKLtVlc6~|gu~Z?$?BrSPV32s)oMSj(y%@O(s6-BWzqIk zf0_I4n$OR@&A`C0NYR9m^M!}A!@d&-)t)Yq`m|%lVs-|G3-Sv&JzN|!^Uof*sP>JM zk%2*?quj%xsp?yY|G~QBe;QXTPh#@iG+pcN*O?XN%o0Z17l%l``}*-68$%CQw?qTW z)hnV`&fj}?HGEz1sXUMO*H}wG+&OSj-N#1q-PcnE>#rZ37r)xNAjf6u$w_V-Z)Qxg zEt8&F_PhVs*Ix__Jgf^jA1kR}ognq-F87!1dk?L*HDz3C;iSkl`SjEeFKrn)7z`u@ zW;L)}zbYLPp1-G>k%7UWaN{ygQ2RnhU>1lvz_bvR+p568@c7*AkfrmUNm<%NdYzkV z{-W|=LcrVQ{2qVHL_eSTxr68TkALFV?tWNU6@BjX%z3Mqyt(w`R|776uQ87Bz+gEvYwTPwjsF zdXh?hHuFMGa1qMjaHn*x{nC5grCz7zs)zsjGGoij_gSIWe_rOQ*ga)Rw954;yxp=V z?)l5Vla^eul$Q>?P+T9!Z6Kte=c{QQhWP( zYVlH@D%*Jfc0HM@&`D2b)?eQ6OX}H~6O+HJtGca!;{Me%x$ki+COs_hoHS|Ea|2U; zhDRL})fgP^+=y8@E2yTs()#bKqR+qI?c#5{8+X1{igl0dq$Owi@9G{laC)n^@3PJE z)z5bPti8R`cG8lb>@fE?d!Nj#UF-F~YX7EbYh=Gf?c3%S`tQ;H)3-xJcik(^Zo3=r zS(N(BbJ8E#v#;{3w8O9YsEX@^F@Jj1Z0GAUHof@9m@8S9 zekI#v|L^Enn_s(rMtIKpdQ)CoGyB?x-`bw%dSW~$?O*G-_pSJpDaPU7rj(SLhn@Nt z7|8Q|%OSz7ybK+@;J8UHSL3akpICT*LH|qR*0b+lcka-?A-zj>OY1p9ndwdw&kEWq zxIWI_FCaKWMbonU-iF^&&;Dd^E!w^{@Gh@t(epEbLFZ(?nuM&_zSVKklXGe&j1D%S z>@D23eQR9oR&^Cm&%aF_e;0qXo%G~&R`0Kh?^nF9KK&JT`14ltmAfAvzF-;2I!Wd5 zvw)y)GM+)V<;(eZ_DiOgXZ^cwwzA@In5D7vVwX_q|3L>65>DiPdHQFC$gcHk18txC zpPXwOeQr(U!i(FceV(Lp{mM+`ENKP-=@zw~?l~gL_hK0g4*uL775ev5ycd_KoyR%d zt{wNM*&O}z@6pEGXW#dIOJn_O=edxpc-@+S13NZ6XJy?J%f;55RrC7NvK^Y6*BkDi zA5_(@vNG{{ahBBvL!}=q&5oPz?wj*+rtf%%%G4L)t7huW%|Aa4*t8X>}syt*nKvx zw@&b#m?&{dV|Oqp=k2<0yYkoC{|ePvv#MslHu?X0f2h|zCdsn)Pw)OM)B5-J{MD+W z?R_~r+|0sPMam@I+QhDWP44NPkEy2ex7OTyW)-^k$BtETH8Gx+u3SH=pX9Czy0_d+ zWBH$lj=$#zZi)$hrXT$Jlj zd$ws@xSTj|^2Um(&uac@TG|Ak{hYHd#5exy{VDb5+d96C6pt?=vA;?{2w zuh!Y8vPr~OKVCXHciX;L^Rqj?M?Wilb+5u}(v`^H?xkV-(|zBg2BDH2d2JPxBuB0XYnrh+Shue>mg-Z*o9&Z9)vI0 z9sBQ{8EZ}4%}F*z%Ti~n9@0O4y)ba!I%x6X!Nr{sk(H|OyKE=Tj# z84~VDn(cn0`Et|pCH*fx_Fd^w^ldJ#FlxWPPTjk&f7$6?w`;wxmCRQ4s%`xsb^13W zgE0F-PKE}F)Bmruou1FaFvBF`GHlpGqWJ37tI%*D!jk(JN`z-Wy9pX@VNmOt zlr#I^t~;Xd^B8vUxbLxUIPz&_CEwqhxt`OW%j9zOoZQ;q8@N09?zf69{0!k;lLC8g z@Oyi-is>%bG2j6*HPVgDC?kiUANCp1I$j0<;*@LK!BrZ<1v-|T(H`#VDFb=}cT zwQ;K%eOy}97zB^~=H!tJe7^=N}dv|yE zB$MK2KB6KbN0xwww-QVwG7oixhKH|SwaV-Fx3|HirKLW%?rhJW|LyH<@yXj7T#BEa zk^K8<`u>pE*w{V0nHUa;woX**iCE*_bZD_p@<@j#aBxsd)bU`E%ndxd{eu%67-q{Y(Wp{`7SH{_=7Lh8>Fx zF0}-{l%C{feCtYiR@|B{FNVp1-rn6>T3S~wT~gBAQdaQ%+}u^`)`b~J$Q0DY#?IB! z(h3O-RNQ}my{h-Lm~T>c^Zi9dMQ`maUfw5TskHvO^`n1fB_%5sEpp=J@$%C5?Sd@<&C3Y>j@vG|0YxBI>3)alb-_5Xj;uX-}k zeM`e-(v~X4l_uw?iW$G(4+* zKApZb@o-z>cVlz&@JS{A{#1HiYL~B*_?Z6V#~jPzw!3Bw48p7nITwco1PE+y67V_8 z?4+fw{dN8SU+b&BUJd{HdH(-9GPYGOmrhsm>azcKBf09=%jI9cT=su`ull|07Tf*z z*B_U!kD2u4V}E_j?{9A}Z$59gI(B#2OLqGojhi-Y+EVxTSK@bLnOTP}fA*VerRr%_ z_h-iv?}eH&=NK4f^lNL19U%KOjlzFWS%zd!zZT=m-xx98f`?phLX;`7fMtKw%ppw)w0w{Ks)c(L== zty?E8iQJsVyE~VGL5<5vI4NmK@RwhHXBS_wDDbST-1(+#ccrzq_Gy{5oa_@Hu2{3C zCu8^iiPJQ{SJ-^2u$i>v@^XLgT_PXO&9lvBGp*at$S_%QA*Z2Ww`NsN26v3mz2cSY z-@JJh1IXzyQkNGeDRkrUuMJ!GczHY0{{$bw~ct5{cS$ByxaFX&(zd3^X{(F zPZc(^43pb#-M(G=?v7>dy*-iL;`(xB@rRe5)7I8L8~Au<@$;m~v!lI&9y}iBt_3!)t_uZ@c?7MaQ_SuO(lcc1jRXxw1J?lIx zX?Mawrkyc*o8RnEi7mYvI!UQn=;<*=hRL!EISsFxt3>vi8JhC%*rPtL!s-6scjckc z(X0Rbsj-xqsDHEkUS+yV=$?fdT3VO%>ppV-{9AYR`t{YTS9^PUdY(FW&P@A7?%iFX z{kGq3q~5-@F}Z!+u2))j%kS6jdcW_t(!1#Wb$>g))iN<8C@GmJK06frWlaDBlZ4dr z%a&@&re(YD&I!AI|IN+KUtjvy|Js!M^$4qLU$P$d<=ZfAD$s`O2l?`(GyUO@8Xzo zXhx^mUp<>wN3Pf~FfjOtwWu9oWmN!K6)7;wAnljm#n;_|peYR>wHCD=O(r1*1_m{2 zC*fluTax&F&N4GF97u7>I5gw6wQK)Gb&wHeEowcMO<;A#$`U+mkMDBE{QUOru6F0j zN(Kgo88xt(nU^3g#x%}B!S{R*PF=r!d-eAFb=3z7XP;fR)O-4|h3+#r{N`9JeD>@a zf&$Y1^`{{}2T)Wz;10PogFBh7{z;K|& zDdW%%P%(S-cE)&ldP+Wz z>pORDuJzY{zu$XL+V|_~`cp4U`hJ@Jt=n&wcgNzvkLmlqEOo4}Te@_qmX3}|{`=e8 z{pZe|n|#fkfuUidl1XCB)TvXCe*M*!cp`7R_oSccChzxr?&FiOSm4?%23iRG`0Cyn z9Q|*0#H=gay)02;*S}w{RXyX@hqv>~?|YN{?95Ez!rw10E^Yx0!zl{Rnh?Nq_`#4J6Wv2(KX^LFQ+x1e-GsxZj_yo`x{(%(746lNRnHhZT z%CCOS`}VEuXN{ezs;Z`@W~R^YOSf*Va!^p{Kd#*V_>%qEZQIO#{;gZOY}qVDg*)#H z_D4lUUAc2-&6h7FscC6^zibYD`uutEw}+38c28QeZk^s|tB1*n!OMI)clj+}zTExm z*RRuFSG1ja{(0qISMv-O1_q9MpgRKg-u+Yk^4+^@KR-X8I&0RdmBGvTGVK3+<~V)& zboZ_Hg54MAwO@a2>Rxr}@@3^^{%p;TTeodnwR2}=-uCFo$jHZ5lR&dg{w>Q`%71;y z1Z7c8P0e42#P=CI=>7Kjy#4xf=ls@2Y;;mpRZYG%T|BPBaqir?pge0*{VnJ7kNy9D z?_a%hXJq!ZHGzSF6IEXF*Z*k#_U`WLrQXw5ty||eb?Ve-KaU-i|yt2Yr}tkf8U>>tgGwWZ}%(X z$i{c?@@)VA`TVJBFQ^$ZO*dLibIY~t%gcOMtz8>hSGVuly4dWtW!>WXueM%~3yzJA zb&1}7w`}&hozG?k+WIpvG@NXxoh5YV&(F`Qo^$8UUA1CG$By+IH(r#fd?B5rHtF&4 z{?(f{18C-(#hcA?Z!K|9NSJ3{ z`YPn}&!^9x@!gtt{PD$Ap{rFq+4*Dwa&y4SF24+kjGW0QZ@1?Ay=wpP*wU$$@fBax z^!4)(O?#G?oo(#Cpy}WOP%HH6l`AV&t@7#?(=|HJ-M_-)Qgr^_rOoX8d>N8D3O(ycdhum z?eb~U#O7KSACsxn*3%0sEZnH_@-V;s747wVj521sy1HsQq%w%`IOzd;7hr)t4?`zIy4>qWgcInNM<>%6nK*N5|$+ zNKjCco&N5-d2EOO1vsFu+S;yKw{F@JPTNTa)!*K9 zG_gKhS$r_TATG8`bNXr1gvHmJnwU&YO?$N@tmgWyjoW*R$7klunY*4&i)Nc6uGduf z=t$>^+1I@mGD+lZuRc=C$iOh;g+ykPO6JE$M<+Q|KlL!WuaKUas;R83ocDKbS7#D0bMbhkSw z2n2F|IMm8L$w~FF`>NHez1Q#iwW{*=>#td_udUtn{a&@Q+q-3cb61@@<@NO0vsaDm zaveMJ4Qic3{QdhQ7cet0By37r!a2$5@&PYzZ`D?@|Lv!9`k!CfQ*dV- zdv|p8w>Og8U%S8d4i28|H`hwkP|JA6V)y>6%gcP9Z8;Y4p=z(-REF2DUzdJ*;_10* z&7^Y6{5>DrtgNj;9l&<^x`;_3fq@I1+xcAQh~Hz_v2LB;?(+9?8`49!WKO94{`pn( zy1lDcYcF~v52||)dI+xO(;Ksue3P1pC{n zYKDBWHZ~4Ed-m+DJ(Ziq^yAh%Jw2U&_1#mayx!i}czBPjURmLn7Z*E9qie5!5fKwR z^|a{Z>#td#pPilL6fAoC)~ziyKZ`a8#m>0?`s%ja+bXSY;c^uZ8WZh)9|RS)*5&U) z+}zqC|0rr}pXM=%jg77PcvSrC=fldSxw*Mj?{+>{X>Ib^;qTrjV|e(?sj1piCr(^A zRXZFM>{Xx7nui7iESNJ#=JWG{uUErg|NVad`oU)Q(hmeQLrnVFeUJByZDzEaj)v@71^;mhRO2xZ{KdtW-rRX(C{&pW&ISR>7SzdHbnpAt^|iSN4CkL;e(s!K@mat8{Pj%-7rZH} zj_Km7*c(?`R;DGvbLwqb^ty{LU%cozd4A2RRbJBOc~ee5y%fIhlPD{=F4ZzM4drwH z!`4x2H(zqfq{)-@qfOVZUhQpbYg=R?R@L9u%$hx0)$``fo4bBKo1I+7 z+kafR)P=J%_v|cF@Nn0G2?Dc(Vw|0u`Q+_#?#;6`m>}O|YetgHrz~JDd3|(>K)yGW5l{d&n a{rLamA)kh+xnv_~SkBYc&t;ucLK6U!Mj+7u literal 0 HcmV?d00001 diff --git a/docs/conf_common.py b/docs/conf_common.py index 3aedc28b2a..2bdebec6ca 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -176,6 +176,8 @@ SPI_SLAVE_HD_DOCS = ['api-reference/peripherals/spi_slave_hd.rst'] JPEG_DOCS = ['api-reference/peripherals/jpeg.rst'] +PPA_DOCS = ['api-reference/peripherals/ppa.rst'] + QEMU_DOCS = ['api-guides/tools/qemu.rst'] ESP32_DOCS = ['api-reference/system/himem.rst', @@ -278,6 +280,7 @@ conditional_include_dict = {'SOC_BT_SUPPORTED':BT_DOCS, 'SOC_SPI_SUPPORT_SLAVE_HD_VER2':SPI_SLAVE_HD_DOCS, 'SOC_WIFI_NAN_SUPPORT':NAN_DOCS, 'SOC_JPEG_CODEC_SUPPORTED':JPEG_DOCS, + 'SOC_PPA_SUPPORTED':PPA_DOCS, 'SOC_GP_LDO_SUPPORTED':LDO_DOCS, 'esp32':ESP32_DOCS, 'esp32s2':ESP32S2_DOCS, diff --git a/docs/doxygen/Doxyfile_esp32p4 b/docs/doxygen/Doxyfile_esp32p4 index 70c98b4c05..32f0c5817b 100644 --- a/docs/doxygen/Doxyfile_esp32p4 +++ b/docs/doxygen/Doxyfile_esp32p4 @@ -17,12 +17,14 @@ INPUT += \ $(PROJECT_PATH)/components/esp_driver_cam/include/esp_cam_ctlr_types.h \ $(PROJECT_PATH)/components/esp_driver_cam/csi/include/esp_cam_ctlr_csi.h \ $(PROJECT_PATH)/components/hal/include/hal/jpeg_types.h \ + $(PROJECT_PATH)/components/hal/include/hal/ppa_types.h \ $(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_types.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_types.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_af.h \ $(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_decode.h \ $(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_encode.h \ + $(PROJECT_PATH)/components/esp_driver_ppa/include/driver/ppa.h \ $(PROJECT_PATH)/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h \ $(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/adc_channel.h \ $(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/clk_tree_defs.h \ diff --git a/docs/en/api-reference/peripherals/index.rst b/docs/en/api-reference/peripherals/index.rst index 5094e38dd7..fa847069b1 100644 --- a/docs/en/api-reference/peripherals/index.rst +++ b/docs/en/api-reference/peripherals/index.rst @@ -30,6 +30,7 @@ Peripherals API :SOC_MCPWM_SUPPORTED: mcpwm :SOC_PARLIO_SUPPORTED: parlio :SOC_PCNT_SUPPORTED: pcnt + :SOC_PPA_SUPPORTED: ppa :SOC_RMT_SUPPORTED: rmt :SOC_SDMMC_HOST_SUPPORTED or SOC_SDIO_SLAVE_SUPPORTED: sd_pullup_requirements :SOC_SDMMC_HOST_SUPPORTED: sdmmc_host diff --git a/docs/en/api-reference/peripherals/ppa.rst b/docs/en/api-reference/peripherals/ppa.rst new file mode 100644 index 0000000000..67c2825d31 --- /dev/null +++ b/docs/en/api-reference/peripherals/ppa.rst @@ -0,0 +1,149 @@ +Pixel-Processing Accelerator (PPA) +================================== + +Introduction +------------ + +{IDF_TARGET_NAME} includes a pixel-processing accelerator (PPA) module, to realize hardware-level acceleration of image algorithms, such as image rotation, scaling, mirroring, and blending. + +Terminology +----------- + +The terms used in relation to the PPA driver are given in the table and the diagram below. + +.. list-table:: + :widths: 25 75 + :header-rows: 1 + + * - Term + - Definition + * - Picture (pic) + - A complete image stored in the system memory. + * - Block + - A portion cropped from a picture at a certain size, with the maximum size equivalent to the entire picture. + * - Pixel + - The unit to be used in the PPA context. + * - PPA Operation + - Types of image algorithm accelerations, includes scale-rotate-mirror (SRM), blend, and fill. + * - PPA Client + - Who wants to do the PPA operations. Typically, every PPA client is hold by a specific task. + * - PPA Transaction + - One request from a PPA client to do a PPA operation is one PPA transaction. + +.. figure:: ../../../_static/diagrams/ppa/pic_blk_concept.png + :align: center + :alt: PPA picture/block terminology + + PPA picture/block terminology + +Functional Overview +------------------- + +The following sections detail the design of the PPA driver: + +- :ref:`ppa-client-registration` - Covers how to register a PPA client to perform any PPA operations. +- :ref:`ppa-register-callback` - Covers how to hook user specific code to PPA driver event callback function. +- :ref:`ppa-perform-operation` - Covers how to perform a PPA operation. +- :ref:`ppa-thread-safety` - Covers the usage of the PPA operation APIs in thread safety aspect. +- :ref:`ppa-performance-overview` - Covers the performance of PPA operations. + +.. _ppa-client-registration: + +Register PPA Client +^^^^^^^^^^^^^^^^^^^ + +Requests to perform PPA operations are made by PPA clients. Therefore, PPA clients need to be registered first before doing any PPA operations. Call :cpp:func:`ppa_register_client` function to register a new client. :cpp:type:`ppa_client_config_t` structure is used to specific the properties of the client. + +- :cpp:member:`ppa_client_config_t::oper_type` - Each PPA operation type corresponds to one PPA client type, a registered PPA client can only request one specific type of PPA operations. +- :cpp:member:`ppa_client_config_t::max_pending_trans_num` - Decides the maximum number of pending PPA transactions the client can hold. + +It is recommended that every task to register its own PPA clients. For example, an application contains two tasks: Task A requires both the PPA SRM and the PPA fill functionalities, so one PPA SRM client and one PPA fill client should be registered in Task A; While Task B also requires the PPA SRM functionality, then another PPA SRM client should be registered in Task B. + +If the task no longer needs to do PPA operations, the corresponding PPA clients can be deregistered with :cpp:func:`ppa_unregister_client` function. + +.. _ppa-register-callback: + +Register PPA Event Callbacks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When an event occurs (e.g., a PPA transaction is completed), the CPU is notified of this event via an interrupt. If some specific functions need to be called when a particular event occurs, a callback can be registered for that event by calling :cpp:func:`ppa_client_register_event_callbacks`. This can be specifically useful when ``PPA_TRANS_MODE_NON_BLOCKING`` mode is selected to perform the PPA operations. It is worth noticing that the event callbacks are bound to PPA clients, but the user context is provided per transaction in the call to the PPA operation APIs. This allows the maximum flexibility in utilizing the event callbacks. + +The registered callback functions are called in the interrupt context, therefore, the callback functions should follow common ISR (Interrupt Service Routine) rules. + +.. _ppa-perform-operation: + +Perform PPA Operations +^^^^^^^^^^^^^^^^^^^^^^ + +Once the PPA client is registered, a PPA operation can be requested with the returned :cpp:type:`ppa_client_handle_t`. + +PPA operations includes: + +Scale, Rotate, Mirror (SRM) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Call :cpp:func:`ppa_do_scale_rotate_mirror` to apply one or more of the scaling, rotation, mirroring operations to the target block inside a picture. + +Some notes to avoid confusion in configuring :cpp:type:`ppa_srm_oper_config_t`: + +.. list:: + - :cpp:member:`ppa_in_pic_blk_config_t::buffer` and :cpp:member:`ppa_out_pic_blk_config_t::buffer` have to be the pointers to different picture buffers for a SRM operation. + - The precision of :cpp:member:`ppa_srm_oper_config_t::scale_x` and :cpp:member:`ppa_srm_oper_config_t::scale_y` will be truncated to a step size of 1/16. + - Output block's width/height is totally determined by the input block's width/height, scaling factor, and rotation angle, so output block's width/height does not need to be configured. However, please make sure the output block can fit at the offset location in the output picture. + - If the color mode of the input or output picture is ``PPA_SRM_COLOR_MODE_YUV420``, then its ``pic_w``, ``pic_h``, ``block_w``, ``block_h``, ``block_offset_x``, ``block_offset_y`` fields must be even. + +Blend +~~~~~ + +Call :cpp:func:`ppa_do_blend` to blend the two target blocks of two so-called foreground (FG) and background (BG) pictures. + +Blend follows the normal Alpha Blending formula: + +:math:`A_{out} = A_b + A_f - A_b \times A_f` + +:math:`C_{out} = (C_b \times A_b \times (1 - A_f) + C_f \times A_f) / (A_b + A_f - A_b \times A_f)` + +where :math:`A_b` is the Alpha channel of the background layer, :math:`A_f` is the Alpha channel of the foreground layer, :math:`C_b` corresponds to the R, G, B components of the background layer, and :math:`C_f` corresponds to the R, G, B components of the foreground layer. + +Note that this formula is not symmetric to FG and BG. When :math:`A_f = 1`, it calculates :math:`C_{out} = C_f`, :math:`A_{out} = 1`, which means if the color mode of the FG picture is ``PPA_BLEND_COLOR_MODE_RGB565`` or ``PPA_BLEND_COLOR_MODE_RGB888``, since a Alpha value of 255 will be filled by the PPA hardware (i.e. :math:`A_f = 1`), the blended result will be identical to the FG block. + +If :cpp:member:`ppa_blend_oper_config_t::bg_ck_en` or :cpp:member:`ppa_blend_oper_config_t::fg_ck_en` is set to ``true``, the pixels fall into the color-key (also known as Chroma-key) range does not follow Alpha Blending process. Please check **{IDF_TARGET_NAME} Technical Reference Manual** > **Pixel-Processing Accelerator (PPA)** > **Functional Description** > **Layer Blending (BLEND)** [`PDF <{IDF_TARGET_TRM_EN_URL}#ppa>`__] for the detailed rules. + +Similarly, some notes to avoid confusion in configuring :cpp:type:`ppa_blend_oper_config_t`: + +.. list:: + - :cpp:member:`ppa_out_pic_blk_config_t::buffer` can be the same pointer to one of the input's :cpp:member:`ppa_in_pic_blk_config_t::buffer` for a blend operation. + - The blocks' width/height of FG and BG should be identical, and are the width/height values for the output block. + - If the color mode of the input picture is ``PPA_BLEND_COLOR_MODE_A4`` or ``PPA_BLEND_COLOR_MODE_L4``, then its ``block_w`` and ``block_offset_x`` fields must be even. + +Fill +~~~~ + +Call :cpp:func:`ppa_do_fill` to fill a target block inside a picture. + +:cpp:type:`ppa_trans_mode_t` is a field configurable to all the PPA operation APIs. It decides whether you want the call to the PPA operation API to block until the transaction finishes or to return immediately after the transaction is pushed to the internal queue. + +.. _ppa-thread-safety: + +Thread Safety +^^^^^^^^^^^^^ + +The PPA driver has guaranteed the thread safety of calling the PPA operation APIs in all following situations: + +.. list:: + - Among clients of different types in one task + - Among clients of same type in different tasks + - Among clients of different types in different tasks + +.. _ppa-performance-overview: + +Performance Overview +^^^^^^^^^^^^^^^^^^^^ + +The PPA operations are acted on the target block of an input picture. Therefore, the time it takes to complete a PPA transaction is proportional to the amount of the data of the block. The size of the entire picture has no influence on the performance. More importantly, the PPA performance highly relies on the PSRAM bandwidth if the pictures are located in the PSRAM section. When there are quite a few peripherals reading and writing to the PSRAM at the same time, the performance of PPA operation will be greatly reduced. + +API Reference +------------- + +.. include-build-file:: inc/ppa.inc +.. include-build-file:: inc/ppa_types.inc diff --git a/docs/zh_CN/api-reference/peripherals/index.rst b/docs/zh_CN/api-reference/peripherals/index.rst index 36c28c3131..0171c6a5d7 100644 --- a/docs/zh_CN/api-reference/peripherals/index.rst +++ b/docs/zh_CN/api-reference/peripherals/index.rst @@ -29,6 +29,7 @@ :SOC_MCPWM_SUPPORTED: mcpwm :SOC_PARLIO_SUPPORTED: parlio :SOC_PCNT_SUPPORTED: pcnt + :SOC_PPA_SUPPORTED: ppa :SOC_RMT_SUPPORTED: rmt :SOC_SDMMC_HOST_SUPPORTED or SOC_SDIO_SLAVE_SUPPORTED: sd_pullup_requirements :SOC_SDMMC_HOST_SUPPORTED: sdmmc_host diff --git a/docs/zh_CN/api-reference/peripherals/ppa.rst b/docs/zh_CN/api-reference/peripherals/ppa.rst new file mode 100644 index 0000000000..80c3ffb369 --- /dev/null +++ b/docs/zh_CN/api-reference/peripherals/ppa.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/peripherals/ppa.rst From d956ba2a6c320a42f33ee4dce82fe2a5d680c6e3 Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Thu, 23 May 2024 16:08:29 +0800 Subject: [PATCH 14/15] feat(ppa): add PPA driver support for ESP32P4 Add burst_length option to client Change uint32_t to color_pixel_rgb888_data_t Descriptor always malloc with MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT Add dscr-port mode block size configuration as a separate API in 2D-DMA driver Do writeback only on an extended window, instead of entire picture --- components/esp_driver_ppa/CMakeLists.txt | 1 - .../esp_driver_ppa/include/driver/ppa.h | 23 ++++---- components/esp_driver_ppa/src/ppa_blend.c | 40 +++++++------- components/esp_driver_ppa/src/ppa_core.c | 13 ++--- components/esp_driver_ppa/src/ppa_fill.c | 22 ++++---- components/esp_driver_ppa/src/ppa_priv.h | 31 ++++++++--- components/esp_driver_ppa/src/ppa_srm.c | 52 +++++++++++++------ .../esp_driver_ppa/test_apps/main/test_ppa.c | 36 ++++++------- components/esp_hw_support/dma/dma2d.c | 21 ++++++-- .../dma/include/esp_private/dma2d.h | 24 +++++++-- components/esp_hw_support/dma/linker.lf | 1 + components/hal/esp32p4/include/hal/ppa_ll.h | 28 +++++++--- components/hal/include/hal/color_types.h | 6 +++ components/hal/include/hal/dma2d_types.h | 4 +- components/hal/include/hal/ppa_types.h | 17 +++++- 15 files changed, 216 insertions(+), 103 deletions(-) diff --git a/components/esp_driver_ppa/CMakeLists.txt b/components/esp_driver_ppa/CMakeLists.txt index 643a7b49f9..ed0d3ac201 100644 --- a/components/esp_driver_ppa/CMakeLists.txt +++ b/components/esp_driver_ppa/CMakeLists.txt @@ -10,5 +10,4 @@ endif() idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${public_include} PRIV_REQUIRES esp_mm esp_pm - # LDFRAGMENTS "linker.lf" ) diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 543a6664ba..47d10707fa 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -33,9 +33,12 @@ typedef struct ppa_client_t *ppa_client_handle_t; * @brief A collection of configuration items that used for registering a PPA client */ typedef struct { - ppa_operation_t oper_type; /*!< The desired PPA operation for the client */ - uint32_t max_pending_trans_num; /*!< The maximum number of pending transactions for the client. - By default, it will be 1, which is sufficient if all transactions are performed with `PPA_TRANS_MODE_BLOCKING` */ + ppa_operation_t oper_type; /*!< The desired PPA operation for the client */ + uint32_t max_pending_trans_num; /*!< The maximum number of pending transactions for the client. + By default, it will be 1, which is sufficient if all transactions are performed with `PPA_TRANS_MODE_BLOCKING` */ + ppa_data_burst_length_t data_burst_length; /*!< The desired data burst length for all the transactions of the client. + Use a small burst length will decrease PPA performance, but can save burst bandwidth for other peripheral usages. + By default, it will be at the maximum burst length, `PPA_DATA_BURST_LENGTH_128` */ } ppa_client_config_t; /** @@ -131,8 +134,8 @@ typedef struct { * @brief A collection of configuration items for an output picture and the target block inside the picture */ typedef struct { - void *buffer; /*!< Pointer to the output picture buffer (requires align to the cache line size) */ - uint32_t buffer_size; /*!< Size of the output picture buffer (requires align to the cache line size) */ + void *buffer; /*!< Pointer to the output picture buffer (requires alignment: internal memory needs align to L1 cache line size, external memory needs align to L1 and L2 cache line size) */ + uint32_t buffer_size; /*!< Size of the output picture buffer (requires alignment: internal memory needs align to L1 cache line size, external memory needs align to L1 and L2 cache line size) */ uint32_t pic_w; /*!< Output picture width (unit: pixel) */ uint32_t pic_h; /*!< Output picture height (unit: pixel) */ uint32_t block_offset_x; /*!< Target block offset in x direction in the picture (unit: pixel) */ @@ -232,13 +235,13 @@ typedef struct { // A pixel, where its background element and foreground element are both out of their color-keying ranges, will follow Alpha Blending bool bg_ck_en; /*!< Whether to enable color keying for background If not enabled, all background pixels are considered as out of the color-keying range */ - uint32_t bg_ck_rgb_low_thres; /*!< The lower threshold of the color-keying range for the background, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ - uint32_t bg_ck_rgb_high_thres; /*!< The higher threshold of the color-keying range for the background, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + color_pixel_rgb888_data_t bg_ck_rgb_low_thres; /*!< The lower threshold of the color-keying range for the background, in RGB888 format */ + color_pixel_rgb888_data_t bg_ck_rgb_high_thres;/*!< The higher threshold of the color-keying range for the background, in RGB888 format */ bool fg_ck_en; /*!< Whether to enable color keying for foreground If not enabled, all foreground pixels are considered as out of the color-keying range */ - uint32_t fg_ck_rgb_low_thres; /*!< The lower threshold of the color-keying range for the foreground, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ - uint32_t fg_ck_rgb_high_thres; /*!< The higher threshold of the color-keying range for the foreground, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ - uint32_t ck_rgb_default_val; /*!< The color to overwrite when a pixel, where its background element and foreground element are both within their color-keying ranges, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */ + color_pixel_rgb888_data_t fg_ck_rgb_low_thres; /*!< The lower threshold of the color-keying range for the foreground, in RGB888 format */ + color_pixel_rgb888_data_t fg_ck_rgb_high_thres;/*!< The higher threshold of the color-keying range for the foreground, in RGB888 format */ + color_pixel_rgb888_data_t ck_rgb_default_val; /*!< The color to overwrite when a pixel, where its background element and foreground element are both within their color-keying ranges, in RGB888 format */ bool ck_reverse_bg2fg; /*!< If this bit is set, in color-keying, for the pixel, where its background element is in the color range, but its foreground element is not in the color range, it will output the foreground element instead of the background element */ ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */ diff --git a/components/esp_driver_ppa/src/ppa_blend.c b/components/esp_driver_ppa/src/ppa_blend.c index fc4c6c5a3c..2ba33f9e52 100644 --- a/components/esp_driver_ppa/src/ppa_blend.c +++ b/components/esp_driver_ppa/src/ppa_blend.c @@ -118,7 +118,7 @@ bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_chann dma2d_connect(dma2d_rx_chan, &trig_periph); dma2d_transfer_ability_t dma_transfer_ability = { - .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .data_burst_length = blend_trans_desc->data_burst_length, .desc_burst_en = true, .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, }; @@ -155,13 +155,15 @@ bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_chann ppa_ll_blend_set_tx_color_mode(platform->hal.dev, blend_trans_desc->out.blend_cm); // Color keying + color_pixel_rgb888_data_t rgb888_black = RGB888_BLACK; + color_pixel_rgb888_data_t rgb888_white = RGB888_WHITE; ppa_ll_blend_configure_rx_bg_ck_range(platform->hal.dev, - blend_trans_desc->bg_ck_en ? blend_trans_desc->bg_ck_rgb_low_thres : 0xFFFFFF, - blend_trans_desc->bg_ck_en ? blend_trans_desc->bg_ck_rgb_high_thres : 0); + blend_trans_desc->bg_ck_en ? &blend_trans_desc->bg_ck_rgb_low_thres : &rgb888_white, + blend_trans_desc->bg_ck_en ? &blend_trans_desc->bg_ck_rgb_high_thres : &rgb888_black); ppa_ll_blend_configure_rx_fg_ck_range(platform->hal.dev, - blend_trans_desc->fg_ck_en ? blend_trans_desc->fg_ck_rgb_low_thres : 0xFFFFFF, - blend_trans_desc->fg_ck_en ? blend_trans_desc->fg_ck_rgb_high_thres : 0); - ppa_ll_blend_set_ck_default_rgb(platform->hal.dev, (blend_trans_desc->bg_ck_en && blend_trans_desc->fg_ck_en) ? blend_trans_desc->ck_rgb_default_val : 0); + blend_trans_desc->fg_ck_en ? &blend_trans_desc->fg_ck_rgb_low_thres : &rgb888_white, + blend_trans_desc->fg_ck_en ? &blend_trans_desc->fg_ck_rgb_high_thres : &rgb888_black); + ppa_ll_blend_set_ck_default_rgb(platform->hal.dev, (blend_trans_desc->bg_ck_en && blend_trans_desc->fg_ck_en) ? &blend_trans_desc->ck_rgb_default_val : &rgb888_black); ppa_ll_blend_enable_ck_fg_bg_reverse(platform->hal.dev, blend_trans_desc->ck_reverse_bg2fg); ppa_ll_blend_start(platform->hal.dev, PPA_LL_BLEND_TRANS_MODE_BLEND); @@ -178,7 +180,7 @@ esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_conf // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); uint32_t buf_alignment_size = (uint32_t)ppa_client->engine->platform->buf_alignment_size; - ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % buf_alignment_size == 0 && config->out.buffer_size % buf_alignment_size == 0, + ESP_RETURN_ON_FALSE(((uint32_t)config->out.buffer & (buf_alignment_size - 1)) == 0 && (config->out.buffer_size & (buf_alignment_size - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); color_space_pixel_format_t out_pixel_format = { .color_type_id = config->out.blend_cm, @@ -225,27 +227,28 @@ esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_conf } // To reduce complexity, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions - // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - // Write back in_bg_buffer, in_fg_buffer + // Write back and invalidate necessary data (note that the window content is not continuous in the buffer) + // Write back in_bg_buffer, in_fg_buffer extended windows (alignment not necessary on C2M direction) color_space_pixel_format_t in_bg_pixel_format = { .color_type_id = config->in_bg.blend_cm, }; - uint32_t in_bg_pic_len = config->in_bg.pic_w * config->in_bg.pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8; - esp_cache_msync((void *)config->in_bg.buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + uint32_t in_bg_pixel_depth = color_hal_pixel_format_get_bit_depth(in_bg_pixel_format); // bits + uint32_t in_bg_ext_window = (uint32_t)config->in_bg.buffer + config->in_bg.block_offset_y * config->in_bg.pic_w * in_bg_pixel_depth / 8; + uint32_t in_bg_ext_window_len = config->in_bg.pic_w * config->in_bg.block_h * in_bg_pixel_depth / 8; + esp_cache_msync((void *)in_bg_ext_window, in_bg_ext_window_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); color_space_pixel_format_t in_fg_pixel_format = { .color_type_id = config->in_fg.blend_cm, }; - uint32_t in_fg_pic_len = config->in_fg.pic_w * config->in_fg.pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8; - esp_cache_msync((void *)config->in_fg.buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - // Invalidate out_buffer + uint32_t in_fg_pixel_depth = color_hal_pixel_format_get_bit_depth(in_fg_pixel_format); // bits + uint32_t in_fg_ext_window = (uint32_t)config->in_fg.buffer + config->in_fg.block_offset_y * config->in_fg.pic_w * in_fg_pixel_depth / 8; + uint32_t in_fg_ext_window_len = config->in_fg.pic_w * config->in_fg.block_h * in_fg_pixel_depth / 8; + esp_cache_msync((void *)in_fg_ext_window, in_fg_ext_window_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + // Invalidate out_buffer entire picture (alignment strict on M2C direction) esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - portENTER_CRITICAL(&ppa_client->spinlock); - bool trans_elm_acquired = xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0); - portEXIT_CRITICAL(&ppa_client->spinlock); - if (trans_elm_acquired) { + if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; @@ -254,6 +257,7 @@ esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_conf memcpy(blend_trans_desc, config, sizeof(ppa_blend_oper_config_t)); blend_trans_desc->bg_alpha_value = new_bg_alpha_value; blend_trans_desc->fg_alpha_value = new_fg_alpha_value; + blend_trans_desc->data_burst_length = ppa_client->data_burst_length; trans_on_picked_desc->ppa_engine = ppa_client->engine; trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; diff --git a/components/esp_driver_ppa/src/ppa_core.c b/components/esp_driver_ppa/src/ppa_core.c index c1f3bdf2a7..646adeab20 100644 --- a/components/esp_driver_ppa/src/ppa_core.c +++ b/components/esp_driver_ppa/src/ppa_core.c @@ -75,8 +75,8 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin if (!s_platform.srm) { ppa_srm_engine_t *srm_engine = heap_caps_calloc(1, sizeof(ppa_srm_engine_t), PPA_MEM_ALLOC_CAPS); SemaphoreHandle_t srm_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *srm_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *srm_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *srm_tx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + dma2d_descriptor_t *srm_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); if (srm_engine && srm_sem && srm_tx_dma_desc && srm_rx_dma_desc) { srm_engine->dma_tx_desc = srm_tx_dma_desc; srm_engine->dma_rx_desc = srm_rx_dma_desc; @@ -120,9 +120,9 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin if (!s_platform.blending) { ppa_blend_engine_t *blending_engine = heap_caps_calloc(1, sizeof(ppa_blend_engine_t), PPA_MEM_ALLOC_CAPS); SemaphoreHandle_t blending_sem = xSemaphoreCreateBinaryWithCaps(PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); - dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | PPA_MEM_ALLOC_CAPS); + dma2d_descriptor_t *blending_tx_bg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + dma2d_descriptor_t *blending_tx_fg_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + dma2d_descriptor_t *blending_rx_dma_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(alignment, 1, s_platform.dma_desc_mem_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); if (blending_engine && blending_sem && blending_tx_bg_dma_desc && blending_tx_fg_dma_desc && blending_rx_dma_desc) { blending_engine->dma_tx_bg_desc = blending_tx_bg_dma_desc; blending_engine->dma_tx_fg_desc = blending_tx_fg_dma_desc; @@ -277,6 +277,7 @@ esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_hand client->oper_type = config->oper_type; client->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + client->data_burst_length = config->data_burst_length ? config->data_burst_length : PPA_DATA_BURST_LENGTH_128; if (config->oper_type == PPA_OPERATION_SRM) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_SRM, @@ -397,7 +398,7 @@ static void ppa_free_transaction(ppa_trans_t *trans_elm) bool ppa_recycle_transaction(ppa_client_handle_t ppa_client, ppa_trans_t *trans_elm) { // Reset transaction and send back to client's trans_elm_ptr_queue - // TODO: To be very safe, we shall memset all to 0, and reconnect necessary pointers? + // TODO: To be very safe, we shall memset all to 0, and reconnect necessary pointers? BaseType_t HPTaskAwoken; BaseType_t sent = xQueueSendFromISR(ppa_client->trans_elm_ptr_queue, &trans_elm, &HPTaskAwoken); assert(sent); diff --git a/components/esp_driver_ppa/src/ppa_fill.c b/components/esp_driver_ppa/src/ppa_fill.c index 8582e9d3a5..0411a47daa 100644 --- a/components/esp_driver_ppa/src/ppa_fill.c +++ b/components/esp_driver_ppa/src/ppa_fill.c @@ -62,7 +62,7 @@ bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channe dma2d_connect(dma2d_rx_chan, &trig_periph); dma2d_transfer_ability_t dma_transfer_ability = { - .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .data_burst_length = fill_trans_desc->data_burst_length, .desc_burst_en = true, .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, }; @@ -94,30 +94,34 @@ esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_config // out_buffer ptr cannot in flash region ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); uint32_t buf_alignment_size = (uint32_t)ppa_client->engine->platform->buf_alignment_size; - ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % buf_alignment_size == 0 && config->out.buffer_size % buf_alignment_size == 0, + ESP_RETURN_ON_FALSE(((uint32_t)config->out.buffer & (buf_alignment_size - 1)) == 0 && (config->out.buffer_size & (buf_alignment_size - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); color_space_pixel_format_t out_pixel_format = { .color_type_id = config->out.fill_cm, }; - uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8; + uint32_t out_pixel_depth = color_hal_pixel_format_get_bit_depth(out_pixel_format); + uint32_t out_pic_len = config->out.pic_w * config->out.pic_h * out_pixel_depth / 8; ESP_RETURN_ON_FALSE(out_pic_len <= config->out.buffer_size, ESP_ERR_INVALID_ARG, TAG, "out.pic_w/h mismatch with out.buffer_size"); // To reduce complexity, color_mode, fill_block_w/h correctness are checked in their corresponding LL functions - // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + // Write back and invalidate necessary data (note that the window content is not continuous in the buffer) + // Write back buffer extended window (alignment not necessary on C2M direction) + uint32_t out_ext_window = (uint32_t)config->out.buffer + config->out.block_offset_y * config->out.pic_w * out_pixel_depth / 8; + uint32_t out_ext_window_len = config->out.pic_w * config->fill_block_h * out_pixel_depth / 8; + esp_cache_msync((void *)out_ext_window, out_ext_window_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + // Invalidate out_buffer entire picture (alignment strict on M2C direction) + esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - portENTER_CRITICAL(&ppa_client->spinlock); - bool trans_elm_acquired = xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0); - portEXIT_CRITICAL(&ppa_client->spinlock); - if (trans_elm_acquired) { + if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; ppa_fill_oper_t *fill_trans_desc = (ppa_fill_oper_t *)trans_on_picked_desc->fill_desc; memcpy(fill_trans_desc, config, sizeof(ppa_fill_oper_config_t)); + fill_trans_desc->data_burst_length = ppa_client->data_burst_length; trans_on_picked_desc->ppa_engine = ppa_client->engine; trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_BLEND; diff --git a/components/esp_driver_ppa/src/ppa_priv.h b/components/esp_driver_ppa/src/ppa_priv.h index fdcf7f6bd8..e63322df08 100644 --- a/components/esp_driver_ppa/src/ppa_priv.h +++ b/components/esp_driver_ppa/src/ppa_priv.h @@ -22,7 +22,7 @@ extern "C" { #endif -#define PPA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) // TODO... +#define PPA_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT) #define PPA_PM_LOCK_NAME_LEN_MAX 16 @@ -80,6 +80,7 @@ struct ppa_client_t { portMUX_TYPE spinlock; // Client level spinlock ppa_event_callback_t done_cb; // Transaction done callback QueueHandle_t trans_elm_ptr_queue; // Queue that contains the pointers to the allocated memory to save the transaction contexts + ppa_data_burst_length_t data_burst_length; // The desired data burst length for all the transactions of the client }; /****************************** OPERATION ************************************/ @@ -115,6 +116,7 @@ typedef struct { uint32_t scale_y_int; // Calculation result for the integral part of the scale_y to be directly written to register uint32_t scale_y_frag; // Calculation result for the fractional part of the scale_y to be directly written to register uint32_t alpha_value; // Calculation result for the fix alpha value to be directly written to register + ppa_data_burst_length_t data_burst_length; // Data burst length for the transaction, information passed from the client } ppa_srm_oper_t; // The elements in this structure listed first are identical to the elements in structure `ppa_blend_oper_config_t` @@ -144,12 +146,12 @@ typedef struct { // color-keying bool bg_ck_en; - uint32_t bg_ck_rgb_low_thres; - uint32_t bg_ck_rgb_high_thres; + color_pixel_rgb888_data_t bg_ck_rgb_low_thres; + color_pixel_rgb888_data_t bg_ck_rgb_high_thres; bool fg_ck_en; - uint32_t fg_ck_rgb_low_thres; - uint32_t fg_ck_rgb_high_thres; - uint32_t ck_rgb_default_val; + color_pixel_rgb888_data_t fg_ck_rgb_low_thres; + color_pixel_rgb888_data_t fg_ck_rgb_high_thres; + color_pixel_rgb888_data_t ck_rgb_default_val; bool ck_reverse_bg2fg; ppa_trans_mode_t mode; @@ -157,9 +159,24 @@ typedef struct { uint32_t bg_alpha_value; // Calculation result for the fix alpha value for BG to be directly written to register uint32_t fg_alpha_value; // Calculation result for the fix alpha value for FG to be directly written to register + ppa_data_burst_length_t data_burst_length; // Data burst length for the transaction, information passed from the client } ppa_blend_oper_t; -typedef ppa_fill_oper_config_t ppa_fill_oper_t; +// The elements in this structure listed first are identical to the elements in structure `ppa_fill_oper_config_t` +// With adding a few extra elements at the end +// This allows memcpy +typedef struct { + ppa_out_pic_blk_config_t out; + + uint32_t fill_block_w; + uint32_t fill_block_h; + color_pixel_argb8888_data_t fill_argb_color; + + ppa_trans_mode_t mode; + void *user_data; + + ppa_data_burst_length_t data_burst_length; // Data burst length for the transaction, information passed from the client +} ppa_fill_oper_t; /***************************** TRANSACTION ***********************************/ diff --git a/components/esp_driver_ppa/src/ppa_srm.c b/components/esp_driver_ppa/src/ppa_srm.c index 331faea3d6..20b017fb23 100644 --- a/components/esp_driver_ppa/src/ppa_srm.c +++ b/components/esp_driver_ppa/src/ppa_srm.c @@ -93,16 +93,20 @@ bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel dma2d_connect(dma2d_rx_chan, &trig_periph); dma2d_transfer_ability_t dma_transfer_ability = { - .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .data_burst_length = srm_trans_desc->data_burst_length, .desc_burst_en = true, .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, - // Configure the block size to be received by the SRM engine, which is passed from the 2D-DMA TX channel (i.e. 2D-DMA dscr-port mode) - .dscr_port_block_h = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, - .dscr_port_block_v = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, }; dma2d_set_transfer_ability(dma2d_tx_chan, &dma_transfer_ability); dma2d_set_transfer_ability(dma2d_rx_chan, &dma_transfer_ability); + // Configure the block size to be received by the SRM engine, which is passed from the 2D-DMA TX channel (i.e. 2D-DMA dscr-port mode) + dma2d_dscr_port_mode_config_t dma_dscr_port_mode_config = { + .block_h = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, + .block_v = (srm_trans_desc->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) ? PPA_LL_SRM_YUV420_BLOCK_SIZE : PPA_LL_SRM_DEFAULT_BLOCK_SIZE, + }; + dma2d_configure_dscr_port_mode(dma2d_tx_chan, &dma_dscr_port_mode_config); + // YUV444 and YUV422 are not supported by PPA module, need to utilize 2D-DMA color space conversion feature to do a conversion ppa_srm_color_mode_t ppa_in_color_mode = srm_trans_desc->in.srm_cm; if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV444) { @@ -180,8 +184,25 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s // in_buffer could be anywhere (ram, flash, psram), out_buffer ptr cannot in flash region ESP_RETURN_ON_FALSE(esp_ptr_internal(config->out.buffer) || esp_ptr_external_ram(config->out.buffer), ESP_ERR_INVALID_ARG, TAG, "invalid out.buffer addr"); uint32_t buf_alignment_size = (uint32_t)ppa_client->engine->platform->buf_alignment_size; - ESP_RETURN_ON_FALSE((uintptr_t)config->out.buffer % buf_alignment_size == 0 && config->out.buffer_size % buf_alignment_size == 0, + ESP_RETURN_ON_FALSE(((uint32_t)config->out.buffer & (buf_alignment_size - 1)) == 0 && (config->out.buffer_size & (buf_alignment_size - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); + // For YUV420 input/output: in desc, ha/hb/va/vb/x/y must be even number + if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) { + ESP_RETURN_ON_FALSE(config->in.pic_h % 2 == 0 && config->in.pic_w % 2 == 0 && + config->in.block_h % 2 == 0 && config->in.block_w % 2 == 0 && + config->in.block_offset_x % 2 == 0 && config->in.block_offset_y % 2 == 0, + ESP_ERR_INVALID_ARG, TAG, "YUV420 input does not support odd h/w/offset_x/offset_y"); + } + // TODO: ECO2 support YUV422 + // else if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV422) { + // ESP_RETURN_ON_FALSE(config->in.pic_w % 2 == 0 && config->in.block_w % 2 == 0 && config->in.block_offset_x % 2 == 0, + // ESP_ERR_INVALID_ARG, TAG, "YUV422 input does not support odd w/offset_x"); + // } + if (config->out.srm_cm == PPA_SRM_COLOR_MODE_YUV420) { + ESP_RETURN_ON_FALSE(config->out.pic_h % 2 == 0 && config->out.pic_w % 2 == 0 && + config->out.block_offset_x % 2 == 0 && config->out.block_offset_y % 2 == 0, + ESP_ERR_INVALID_ARG, TAG, "YUV420 output does not support odd h/w/offset_x/offset_y"); + } color_space_pixel_format_t out_pixel_format = { .color_type_id = config->out.srm_cm, }; @@ -217,26 +238,22 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s new_alpha_value = (uint32_t)(config->alpha_scale_ratio * 256); } // To reduce complexity, rotation_angle, color_mode, alpha_update_mode correctness are checked in their corresponding LL functions - // TODO: - // YUV420: in desc, ha/hb/va/vb/x/y must be even number - // What for YUV444/YUV422 - // Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer) - // Write back in_buffer + // Write back and invalidate necessary data (note that the window content is not continuous in the buffer) + // Write back in_buffer extended window (alignment not necessary on C2M direction) color_space_pixel_format_t in_pixel_format = { .color_type_id = config->in.srm_cm, }; - uint32_t in_pic_len = config->in.pic_w * config->in.pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; - esp_cache_msync((void *)config->in.buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - // Invalidate out_buffer + uint32_t in_pixel_depth = color_hal_pixel_format_get_bit_depth(in_pixel_format); // bits + uint32_t in_ext_window = (uint32_t)config->in.buffer + config->in.block_offset_y * config->in.pic_w * in_pixel_depth / 8; + uint32_t in_ext_window_len = config->in.pic_w * config->in.block_h * in_pixel_depth / 8; + esp_cache_msync((void *)in_ext_window, in_ext_window_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + // Invalidate out_buffer entire picture (alignment strict on M2C direction) esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); esp_err_t ret = ESP_OK; ppa_trans_t *trans_elm = NULL; - portENTER_CRITICAL(&ppa_client->spinlock); - bool trans_elm_acquired = xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0); - portEXIT_CRITICAL(&ppa_client->spinlock); - if (trans_elm_acquired) { + if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) { dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc; ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config; @@ -248,6 +265,7 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s srm_trans_desc->scale_y_int = (uint32_t)srm_trans_desc->scale_y; srm_trans_desc->scale_y_frag = (uint32_t)(srm_trans_desc->scale_y * (PPA_LL_SRM_SCALING_FRAG_MAX + 1)) & PPA_LL_SRM_SCALING_FRAG_MAX; srm_trans_desc->alpha_value = new_alpha_value; + srm_trans_desc->data_burst_length = ppa_client->data_burst_length; trans_on_picked_desc->ppa_engine = ppa_client->engine; trans_on_picked_desc->trigger_periph = DMA2D_TRIG_PERIPH_PPA_SRM; diff --git a/components/esp_driver_ppa/test_apps/main/test_ppa.c b/components/esp_driver_ppa/test_apps/main/test_ppa.c index 2d92e4afba..0dd676c664 100644 --- a/components/esp_driver_ppa/test_apps/main/test_ppa.c +++ b/components/esp_driver_ppa/test_apps/main/test_ppa.c @@ -41,19 +41,19 @@ TEST_CASE("ppa_client_do_ppa_operation", "[PPA]") TEST_ASSERT_NOT_NULL(buf_2); // Register different types of PPA clients - ppa_client_handle_t ppa_client_a_handle; - ppa_client_handle_t ppa_client_b_handle; - ppa_client_handle_t ppa_client_c_handle; - ppa_client_handle_t ppa_client_d_handle; + ppa_client_handle_t ppa_client_srm_handle; + ppa_client_handle_t ppa_client_blend_handle; + ppa_client_handle_t ppa_client_fill_handle_a; + ppa_client_handle_t ppa_client_fill_handle_b; ppa_client_config_t ppa_client_config = { .oper_type = PPA_OPERATION_SRM, }; - TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_a_handle)); + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_srm_handle)); ppa_client_config.oper_type = PPA_OPERATION_BLEND; - TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_b_handle)); + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_blend_handle)); ppa_client_config.oper_type = PPA_OPERATION_FILL; - TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_c_handle)); - TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_d_handle)); + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_fill_handle_a)); + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_fill_handle_b)); ppa_srm_oper_config_t srm_oper_config = { .in.buffer = buf_1, @@ -80,9 +80,9 @@ TEST_CASE("ppa_client_do_ppa_operation", "[PPA]") .mode = PPA_TRANS_MODE_BLOCKING, }; // A SRM client can request to do a SRM operation - TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_a_handle, &srm_oper_config)); + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_srm_handle, &srm_oper_config)); // A non-SRM client can not request to do a SRM operation - TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_scale_rotate_mirror(ppa_client_b_handle, &srm_oper_config)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_scale_rotate_mirror(ppa_client_blend_handle, &srm_oper_config)); ppa_blend_oper_config_t blend_oper_config = { .in_bg.buffer = buf_1, @@ -114,9 +114,9 @@ TEST_CASE("ppa_client_do_ppa_operation", "[PPA]") .mode = PPA_TRANS_MODE_BLOCKING, }; // A blend client can request to do a blend operation - TEST_ESP_OK(ppa_do_blend(ppa_client_b_handle, &blend_oper_config)); + TEST_ESP_OK(ppa_do_blend(ppa_client_blend_handle, &blend_oper_config)); // A non-blend client can not request to do a blend operation - TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_blend(ppa_client_d_handle, &blend_oper_config)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_blend(ppa_client_fill_handle_b, &blend_oper_config)); ppa_fill_oper_config_t fill_oper_config = { .out.buffer = buf_1, @@ -136,17 +136,17 @@ TEST_CASE("ppa_client_do_ppa_operation", "[PPA]") .mode = PPA_TRANS_MODE_NON_BLOCKING, }; // A fill client can request to do a fill operation - TEST_ESP_OK(ppa_do_fill(ppa_client_c_handle, &fill_oper_config)); + TEST_ESP_OK(ppa_do_fill(ppa_client_fill_handle_a, &fill_oper_config)); // Another fill client can also request another fill operation at the same time - TEST_ESP_OK(ppa_do_fill(ppa_client_d_handle, &fill_oper_config)); + TEST_ESP_OK(ppa_do_fill(ppa_client_fill_handle_b, &fill_oper_config)); vTaskDelay(pdMS_TO_TICKS(500)); // Unregister all PPA clients - TEST_ESP_OK(ppa_unregister_client(ppa_client_a_handle)); - TEST_ESP_OK(ppa_unregister_client(ppa_client_b_handle)); - TEST_ESP_OK(ppa_unregister_client(ppa_client_c_handle)); - TEST_ESP_OK(ppa_unregister_client(ppa_client_d_handle)); + TEST_ESP_OK(ppa_unregister_client(ppa_client_srm_handle)); + TEST_ESP_OK(ppa_unregister_client(ppa_client_blend_handle)); + TEST_ESP_OK(ppa_unregister_client(ppa_client_fill_handle_a)); + TEST_ESP_OK(ppa_unregister_client(ppa_client_fill_handle_b)); free(buf_1); free(buf_2); diff --git a/components/esp_hw_support/dma/dma2d.c b/components/esp_hw_support/dma/dma2d.c index 6a16a78118..bd6f0a4dbb 100644 --- a/components/esp_hw_support/dma/dma2d.c +++ b/components/esp_hw_support/dma/dma2d.c @@ -828,9 +828,6 @@ esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dm dma2d_ll_tx_enable_descriptor_burst(group->hal.dev, channel_id, ability->desc_burst_en); dma2d_ll_tx_set_data_burst_length(group->hal.dev, channel_id, ability->data_burst_length); dma2d_ll_tx_set_macro_block_size(group->hal.dev, channel_id, ability->mb_size); - if (ability->dscr_port_block_h && ability->dscr_port_block_v) { - dma2d_ll_tx_set_dscr_port_block_size(group->hal.dev, channel_id, ability->dscr_port_block_h, ability->dscr_port_block_v); - } } else { dma2d_ll_rx_enable_descriptor_burst(group->hal.dev, channel_id, ability->desc_burst_en); dma2d_ll_rx_set_data_burst_length(group->hal.dev, channel_id, ability->data_burst_length); @@ -874,6 +871,24 @@ err: return ret; } +esp_err_t dma2d_configure_dscr_port_mode(dma2d_channel_handle_t dma2d_chan, const dma2d_dscr_port_mode_config_t *config) +{ + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE_ISR(dma2d_chan && config, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + + dma2d_group_t *group = dma2d_chan->group; + int channel_id = dma2d_chan->channel_id; + + if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) { + ESP_GOTO_ON_FALSE_ISR(config->block_h > 0 && config->block_v > 0, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + + dma2d_ll_tx_set_dscr_port_block_size(group->hal.dev, channel_id, config->block_h, config->block_v); + } + +err: + return ret; +} + esp_err_t dma2d_enqueue(dma2d_pool_handle_t dma2d_pool, const dma2d_trans_config_t *trans_desc, dma2d_trans_t *trans_placeholder) { esp_err_t ret = ESP_OK; diff --git a/components/esp_hw_support/dma/include/esp_private/dma2d.h b/components/esp_hw_support/dma/include/esp_private/dma2d.h index 8034c8353e..0710782048 100644 --- a/components/esp_hw_support/dma/include/esp_private/dma2d.h +++ b/components/esp_hw_support/dma/include/esp_private/dma2d.h @@ -220,7 +220,7 @@ typedef struct { esp_err_t dma2d_apply_strategy(dma2d_channel_handle_t dma2d_chan, const dma2d_strategy_config_t *config); /** - * @brief A collection of transfer ability items that each 2D-DMA channel could apply to improve transfer efficiency or to ensure desired transfer block size + * @brief A collection of transfer ability items that each 2D-DMA channel could apply to improve transfer efficiency * * @note The 2D-DMA driver has no knowledge about the DMA buffer (address and size) used by upper layer. * So it's the responsibility of the **upper layer** to take care of the buffer address and size. @@ -231,8 +231,6 @@ typedef struct { bool desc_burst_en; /*!< If set / clear, DMA channel enables / disables burst reading descriptor link */ dma2d_data_burst_length_t data_burst_length; /*!< Configure the DMA channel burst reading data length */ dma2d_macro_block_size_t mb_size; /*!< Configure the DMA channel macro block size (only useful in DMA2D_DESCRIPTOR_BLOCK_RW_MODE_MULTIPLE mode) */ - uint32_t dscr_port_block_h; /*!< Configure the DMA TX channel horizontal width of the block in dscr-port mode (unit: pixel) */ - uint32_t dscr_port_block_v; /*!< Configure the DMA TX channel vertical height of the block in dscr-port mode (unit: pixel) */ } dma2d_transfer_ability_t; /** @@ -273,6 +271,26 @@ typedef struct { */ esp_err_t dma2d_configure_color_space_conversion(dma2d_channel_handle_t dma2d_chan, const dma2d_csc_config_t *config); +/** + * @brief A collection of configurations apply to 2D-DMA channel DSCR-PORT mode + */ +typedef struct { + uint32_t block_h; /*!< Horizontal width of the block in dscr-port mode (unit: pixel) */ + uint32_t block_v; /*!< Vertical height of the block in dscr-port mode (unit: pixel) */ +} dma2d_dscr_port_mode_config_t; + +/** + * @brief Configure 2D-DMA channel DSCR-PORT mode + * + * @note This API only targets PPA SRM, which uses 2D-DMA DSCR-PORT mode. + * + * @param[in] dma2d_chan 2D-DMA channel handle, get from the `on_job_picked` callback input argument `dma2d_chans` + * @param[in] config Configuration of 2D-DMA channel DSCR-PORT mode + * @return + * - ESP_OK: Configure 2D-DMA dscr-port mode successfully + */ +esp_err_t dma2d_configure_dscr_port_mode(dma2d_channel_handle_t dma2d_chan, const dma2d_dscr_port_mode_config_t *config); + /** * @brief Type of 2D-DMA event data */ diff --git a/components/esp_hw_support/dma/linker.lf b/components/esp_hw_support/dma/linker.lf index 0acf4b6f21..db6159eed1 100644 --- a/components/esp_hw_support/dma/linker.lf +++ b/components/esp_hw_support/dma/linker.lf @@ -113,6 +113,7 @@ entries: dma2d: dma2d_apply_strategy (noflash) dma2d: dma2d_set_transfer_ability (noflash) dma2d: dma2d_configure_color_space_conversion (noflash) + dma2d: dma2d_configure_dscr_port_mode (noflash) dma2d: dma2d_enqueue (noflash) [mapping:dma2d_hal] diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index e5ccd126e8..f53e8fe75d 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -679,10 +679,15 @@ static inline void ppa_ll_blend_set_rx_fg_fix_rgb(ppa_dev_t *dev, color_pixel_rg * @param rgb_thres_low Color-key lower threshold of background in RGB888 format * @param rgb_thres_high Color-key higher threshold of background in RGB888 format */ -static inline void ppa_ll_blend_configure_rx_bg_ck_range(ppa_dev_t *dev, uint32_t rgb_thres_low, uint32_t rgb_thres_high) +static inline void ppa_ll_blend_configure_rx_bg_ck_range(ppa_dev_t *dev, color_pixel_rgb888_data_t *rgb_thres_low, color_pixel_rgb888_data_t *rgb_thres_high) { - dev->ck_bg_low.val = rgb_thres_low; - dev->ck_bg_high.val = rgb_thres_high; + dev->ck_bg_low.colorkey_bg_b_low = rgb_thres_low->b; + dev->ck_bg_low.colorkey_bg_g_low = rgb_thres_low->g; + dev->ck_bg_low.colorkey_bg_r_low = rgb_thres_low->r; + + dev->ck_bg_high.colorkey_bg_b_high = rgb_thres_high->b; + dev->ck_bg_high.colorkey_bg_g_high = rgb_thres_high->g; + dev->ck_bg_high.colorkey_bg_r_high = rgb_thres_high->r; } /** @@ -692,10 +697,15 @@ static inline void ppa_ll_blend_configure_rx_bg_ck_range(ppa_dev_t *dev, uint32_ * @param rgb_thres_low Color-key lower threshold of foreground in RGB888 format * @param rgb_thres_high Color-key higher threshold of foreground in RGB888 format */ -static inline void ppa_ll_blend_configure_rx_fg_ck_range(ppa_dev_t *dev, uint32_t rgb_thres_low, uint32_t rgb_thres_high) +static inline void ppa_ll_blend_configure_rx_fg_ck_range(ppa_dev_t *dev, color_pixel_rgb888_data_t *rgb_thres_low, color_pixel_rgb888_data_t *rgb_thres_high) { - dev->ck_fg_low.val = rgb_thres_low; - dev->ck_fg_high.val = rgb_thres_high; + dev->ck_fg_low.colorkey_fg_b_low = rgb_thres_low->b; + dev->ck_fg_low.colorkey_fg_g_low = rgb_thres_low->g; + dev->ck_fg_low.colorkey_fg_r_low = rgb_thres_low->r; + + dev->ck_fg_high.colorkey_fg_b_high = rgb_thres_high->b; + dev->ck_fg_high.colorkey_fg_g_high = rgb_thres_high->g; + dev->ck_fg_high.colorkey_fg_r_high = rgb_thres_high->r; } /** @@ -704,9 +714,11 @@ static inline void ppa_ll_blend_configure_rx_fg_ck_range(ppa_dev_t *dev, uint32_ * @param dev Peripheral instance address * @param rgb Default RGB value in RGB888 format */ -static inline void ppa_ll_blend_set_ck_default_rgb(ppa_dev_t *dev, uint32_t rgb) +static inline void ppa_ll_blend_set_ck_default_rgb(ppa_dev_t *dev, color_pixel_rgb888_data_t *rgb) { - dev->ck_default.val = (dev->ck_default.colorkey_fg_bg_reverse << PPA_COLORKEY_FG_BG_REVERSE_S) | rgb; + dev->ck_default.colorkey_default_b = rgb->b; + dev->ck_default.colorkey_default_g = rgb->g; + dev->ck_default.colorkey_default_r = rgb->r; } /** diff --git a/components/hal/include/hal/color_types.h b/components/hal/include/hal/color_types.h index 32c2d2b96f..25b9900590 100644 --- a/components/hal/include/hal/color_types.h +++ b/components/hal/include/hal/color_types.h @@ -176,6 +176,12 @@ typedef struct { uint8_t r; /*!< R component [0, 255] */ } color_pixel_rgb888_data_t; +///< Color BLACK in color_pixel_rgb888_data_t +#define RGB888_BLACK {.b = 0x00, .g = 0x00, .r = 0x00} + +///< Color WHITE in color_pixel_rgb888_data_t +#define RGB888_WHITE {.b = 0xFF, .g = 0xFF, .r = 0xFF} + /** * @brief Data structure for RGB565 pixel unit */ diff --git a/components/hal/include/hal/dma2d_types.h b/components/hal/include/hal/dma2d_types.h index 6ea7e76a4c..b66c791deb 100644 --- a/components/hal/include/hal/dma2d_types.h +++ b/components/hal/include/hal/dma2d_types.h @@ -118,9 +118,11 @@ typedef enum { /** * @brief Enumeration of 2D-DMA data burst length options + * + * Starting from 1, saving 0 for special purpose (upper layer could use 0 to be a default burst length) */ typedef enum { - DMA2D_DATA_BURST_LENGTH_8, /*!< 2D-DMA block size: 8 bytes */ + DMA2D_DATA_BURST_LENGTH_8 = 1, /*!< 2D-DMA block size: 8 bytes */ DMA2D_DATA_BURST_LENGTH_16, /*!< 2D-DMA block size: 16 bytes */ DMA2D_DATA_BURST_LENGTH_32, /*!< 2D-DMA block size: 32 bytes */ DMA2D_DATA_BURST_LENGTH_64, /*!< 2D-DMA block size: 64 bytes */ diff --git a/components/hal/include/hal/ppa_types.h b/components/hal/include/hal/ppa_types.h index 30fba5ebc9..485e4414f1 100644 --- a/components/hal/include/hal/ppa_types.h +++ b/components/hal/include/hal/ppa_types.h @@ -8,6 +8,7 @@ #include #include "hal/color_types.h" +#include "hal/dma2d_types.h" #ifdef __cplusplus extern "C" { @@ -53,10 +54,11 @@ typedef enum { PPA_BLEND_COLOR_MODE_ARGB8888 = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888), /*!< PPA blend color mode: ARGB8888 */ PPA_BLEND_COLOR_MODE_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< PPA blend color mode: RGB888 */ PPA_BLEND_COLOR_MODE_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< PPA blend color mode: RGB565 */ - PPA_BLEND_COLOR_MODE_L8 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L8), /*!< PPA blend color mode: L8, only available on blend inputs */ - PPA_BLEND_COLOR_MODE_L4 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L4), /*!< PPA blend color mode: L4, only available on blend inputs */ PPA_BLEND_COLOR_MODE_A8 = COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A8), /*!< PPA blend color mode: A8, only available on blend foreground input */ PPA_BLEND_COLOR_MODE_A4 = COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A4), /*!< PPA blend color mode: A4, only available on blend foreground input */ + // TODO: Support CLUT to support L4/L8 color mode + PPA_BLEND_COLOR_MODE_L8 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L8), /*!< PPA blend color mode: L8, only available on blend inputs */ + PPA_BLEND_COLOR_MODE_L4 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L4), /*!< PPA blend color mode: L4, only available on blend inputs */ } ppa_blend_color_mode_t; /** @@ -97,6 +99,17 @@ typedef enum { PPA_COLOR_RANGE_FULL = COLOR_RANGE_FULL, /*!< Full color range, 0 is the darkest black and 255 is the brightest white */ } ppa_color_range_t; +/** + * @brief Enumeration of PPA supported data burst length + */ +typedef enum { + PPA_DATA_BURST_LENGTH_8 = DMA2D_DATA_BURST_LENGTH_8, /*!< Data burst length: 8 bytes */ + PPA_DATA_BURST_LENGTH_16 = DMA2D_DATA_BURST_LENGTH_16, /*!< Data burst length: 16 bytes */ + PPA_DATA_BURST_LENGTH_32 = DMA2D_DATA_BURST_LENGTH_32, /*!< Data burst length: 32 bytes */ + PPA_DATA_BURST_LENGTH_64 = DMA2D_DATA_BURST_LENGTH_64, /*!< Data burst length: 64 bytes */ + PPA_DATA_BURST_LENGTH_128 = DMA2D_DATA_BURST_LENGTH_128, /*!< Data burst length: 128 bytes */ +} ppa_data_burst_length_t; + #ifdef __cplusplus } #endif From ea7d496e58f1b60bbd976d10216cab5b8c00562b Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Thu, 23 May 2024 16:38:42 +0800 Subject: [PATCH 15/15] feat(ppa): add PPA driver support for ESP32P4 Remove L4/L8, YUV422 in PPA driver Clean up --- components/esp_driver_ppa/src/ppa_blend.c | 24 +++++++++--------- components/esp_driver_ppa/src/ppa_core.c | 4 --- components/esp_driver_ppa/src/ppa_srm.c | 15 +++-------- .../dma/include/esp_private/dma2d.h | 1 + components/hal/esp32p4/include/hal/ppa_ll.h | 25 ++++++++++--------- components/hal/include/hal/color_types.h | 12 +++------ components/hal/include/hal/ppa_types.h | 11 ++++---- docs/en/api-reference/peripherals/ppa.rst | 4 +-- 8 files changed, 40 insertions(+), 56 deletions(-) diff --git a/components/esp_driver_ppa/src/ppa_blend.c b/components/esp_driver_ppa/src/ppa_blend.c index 2ba33f9e52..e9b96ff563 100644 --- a/components/esp_driver_ppa/src/ppa_blend.c +++ b/components/esp_driver_ppa/src/ppa_blend.c @@ -155,15 +155,15 @@ bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_chann ppa_ll_blend_set_tx_color_mode(platform->hal.dev, blend_trans_desc->out.blend_cm); // Color keying - color_pixel_rgb888_data_t rgb888_black = RGB888_BLACK; - color_pixel_rgb888_data_t rgb888_white = RGB888_WHITE; + color_pixel_rgb888_data_t rgb888_min = {.b = 0x00, .g = 0x00, .r = 0x00}; + color_pixel_rgb888_data_t rgb888_max = {.b = 0xFF, .g = 0xFF, .r = 0xFF}; ppa_ll_blend_configure_rx_bg_ck_range(platform->hal.dev, - blend_trans_desc->bg_ck_en ? &blend_trans_desc->bg_ck_rgb_low_thres : &rgb888_white, - blend_trans_desc->bg_ck_en ? &blend_trans_desc->bg_ck_rgb_high_thres : &rgb888_black); + blend_trans_desc->bg_ck_en ? &blend_trans_desc->bg_ck_rgb_low_thres : &rgb888_max, + blend_trans_desc->bg_ck_en ? &blend_trans_desc->bg_ck_rgb_high_thres : &rgb888_min); ppa_ll_blend_configure_rx_fg_ck_range(platform->hal.dev, - blend_trans_desc->fg_ck_en ? &blend_trans_desc->fg_ck_rgb_low_thres : &rgb888_white, - blend_trans_desc->fg_ck_en ? &blend_trans_desc->fg_ck_rgb_high_thres : &rgb888_black); - ppa_ll_blend_set_ck_default_rgb(platform->hal.dev, (blend_trans_desc->bg_ck_en && blend_trans_desc->fg_ck_en) ? &blend_trans_desc->ck_rgb_default_val : &rgb888_black); + blend_trans_desc->fg_ck_en ? &blend_trans_desc->fg_ck_rgb_low_thres : &rgb888_max, + blend_trans_desc->fg_ck_en ? &blend_trans_desc->fg_ck_rgb_high_thres : &rgb888_min); + ppa_ll_blend_set_ck_default_rgb(platform->hal.dev, (blend_trans_desc->bg_ck_en && blend_trans_desc->fg_ck_en) ? &blend_trans_desc->ck_rgb_default_val : &rgb888_min); ppa_ll_blend_enable_ck_fg_bg_reverse(platform->hal.dev, blend_trans_desc->ck_reverse_bg2fg); ppa_ll_blend_start(platform->hal.dev, PPA_LL_BLEND_TRANS_MODE_BLEND); @@ -217,11 +217,11 @@ esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_conf ESP_RETURN_ON_FALSE(config->fg_alpha_scale_ratio > 0 && config->fg_alpha_scale_ratio < 1, ESP_ERR_INVALID_ARG, TAG, "invalid fg_alpha_scale_ratio"); new_fg_alpha_value = (uint32_t)(config->fg_alpha_scale_ratio * 256); } - if (config->in_bg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { - ESP_RETURN_ON_FALSE(config->in_bg.block_w % 2 == 0 && config->in_bg.block_offset_x % 2 == 0, - ESP_ERR_INVALID_ARG, TAG, "in_bg.block_w and in_bg.block_offset_x must be even"); - } - if (config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_A4 || config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { + // if (config->in_bg.blend_cm == PPA_BLEND_COLOR_MODE_L4) { + // ESP_RETURN_ON_FALSE(config->in_bg.block_w % 2 == 0 && config->in_bg.block_offset_x % 2 == 0, + // ESP_ERR_INVALID_ARG, TAG, "in_bg.block_w and in_bg.block_offset_x must be even"); + // } + if (config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_A4) { // || config->in_fg.blend_cm == PPA_BLEND_COLOR_MODE_L4 ESP_RETURN_ON_FALSE(config->in_fg.block_w % 2 == 0 && config->in_fg.block_offset_x % 2 == 0, ESP_ERR_INVALID_ARG, TAG, "in_fg.block_w and in_fg.block_offset_x must be even"); } diff --git a/components/esp_driver_ppa/src/ppa_core.c b/components/esp_driver_ppa/src/ppa_core.c index 646adeab20..6f309d9205 100644 --- a/components/esp_driver_ppa/src/ppa_core.c +++ b/components/esp_driver_ppa/src/ppa_core.c @@ -458,11 +458,7 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng } if (mode == PPA_TRANS_MODE_BLOCKING) { - // while (1) { - // printf("ppa intr: %ld\n", PPA.int_raw.val); - // } xSemaphoreTake(trans_elm->sem, portMAX_DELAY); // Given in the ISR - // TODO: Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho) } err: diff --git a/components/esp_driver_ppa/src/ppa_srm.c b/components/esp_driver_ppa/src/ppa_srm.c index 20b017fb23..244a18bff9 100644 --- a/components/esp_driver_ppa/src/ppa_srm.c +++ b/components/esp_driver_ppa/src/ppa_srm.c @@ -107,7 +107,7 @@ bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel }; dma2d_configure_dscr_port_mode(dma2d_tx_chan, &dma_dscr_port_mode_config); - // YUV444 and YUV422 are not supported by PPA module, need to utilize 2D-DMA color space conversion feature to do a conversion + // YUV444 is not supported by PPA module, need to utilize 2D-DMA color space conversion feature to do a conversion ppa_srm_color_mode_t ppa_in_color_mode = srm_trans_desc->in.srm_cm; if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV444) { ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; @@ -118,15 +118,6 @@ bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_709; } dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); - } else if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV422) { - ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888; - dma2d_csc_config_t dma_tx_csc = {0}; - if (srm_trans_desc->in.yuv_std == PPA_COLOR_CONV_STD_RGB_YUV_BT601) { - dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_601; - } else { - dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_709; - } - dma2d_configure_color_space_conversion(dma2d_tx_chan, &dma_tx_csc); } ppa_srm_color_mode_t ppa_out_color_mode = srm_trans_desc->out.srm_cm; @@ -193,7 +184,7 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s config->in.block_offset_x % 2 == 0 && config->in.block_offset_y % 2 == 0, ESP_ERR_INVALID_ARG, TAG, "YUV420 input does not support odd h/w/offset_x/offset_y"); } - // TODO: ECO2 support YUV422 + // TODO: P4 ECO2 support YUV422 // else if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV422) { // ESP_RETURN_ON_FALSE(config->in.pic_w % 2 == 0 && config->in.block_w % 2 == 0 && config->in.block_offset_x % 2 == 0, // ESP_ERR_INVALID_ARG, TAG, "YUV422 input does not support odd w/offset_x"); @@ -273,7 +264,7 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s dma_trans_desc->tx_channel_num = 1; dma_trans_desc->rx_channel_num = 1; dma_trans_desc->channel_flags = 0; - if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV422 || config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { + if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC; } if (config->out.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { diff --git a/components/esp_hw_support/dma/include/esp_private/dma2d.h b/components/esp_hw_support/dma/include/esp_private/dma2d.h index 0710782048..4524ca95c6 100644 --- a/components/esp_hw_support/dma/include/esp_private/dma2d.h +++ b/components/esp_hw_support/dma/include/esp_private/dma2d.h @@ -288,6 +288,7 @@ typedef struct { * @param[in] config Configuration of 2D-DMA channel DSCR-PORT mode * @return * - ESP_OK: Configure 2D-DMA dscr-port mode successfully + * - ESP_ERR_INVALID_ARG: Configure 2D-DMA dscr-port mode failed because of invalid argument */ esp_err_t dma2d_configure_dscr_port_mode(dma2d_channel_handle_t dma2d_chan, const dma2d_dscr_port_mode_config_t *config); diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index f53e8fe75d..687e63c5ef 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -27,6 +27,7 @@ extern "C" { #define PPA_LL_SRM_SCALING_INT_MAX PPA_SR_SCAL_X_INT_V #define PPA_LL_SRM_SCALING_FRAG_MAX PPA_SR_SCAL_X_FRAG_V +// TODO: On P4 ECO2, SRM block size needs update #define PPA_LL_SRM_DEFAULT_BLOCK_SIZE 18 // 18 x 18 block size #define PPA_LL_SRM_YUV420_BLOCK_SIZE 20 // 20 x 20 block size @@ -438,12 +439,12 @@ static inline void ppa_ll_blend_set_rx_bg_color_mode(ppa_dev_t *dev, ppa_blend_c case PPA_BLEND_COLOR_MODE_RGB565: val = 2; break; - case PPA_BLEND_COLOR_MODE_L8: - val = 4; - break; - case PPA_BLEND_COLOR_MODE_L4: - val = 5; - break; + // case PPA_BLEND_COLOR_MODE_L8: + // val = 4; + // break; + // case PPA_BLEND_COLOR_MODE_L4: + // val = 5; + // break; default: // Unsupported blending rx background color mode abort(); @@ -470,12 +471,12 @@ static inline void ppa_ll_blend_set_rx_fg_color_mode(ppa_dev_t *dev, ppa_blend_c case PPA_BLEND_COLOR_MODE_RGB565: val = 2; break; - case PPA_BLEND_COLOR_MODE_L8: - val = 4; - break; - case PPA_BLEND_COLOR_MODE_L4: - val = 5; - break; + // case PPA_BLEND_COLOR_MODE_L8: + // val = 4; + // break; + // case PPA_BLEND_COLOR_MODE_L4: + // val = 5; + // break; case PPA_BLEND_COLOR_MODE_A8: val = 6; break; diff --git a/components/hal/include/hal/color_types.h b/components/hal/include/hal/color_types.h index 25b9900590..4f2e82614c 100644 --- a/components/hal/include/hal/color_types.h +++ b/components/hal/include/hal/color_types.h @@ -171,17 +171,11 @@ typedef union { * @brief Data structure for RGB888 pixel unit */ typedef struct { - uint8_t b; /*!< B component [0, 255] */ - uint8_t g; /*!< G component [0, 255] */ - uint8_t r; /*!< R component [0, 255] */ + uint8_t b; /*!< B component [0, 255] */ + uint8_t g; /*!< G component [0, 255] */ + uint8_t r; /*!< R component [0, 255] */ } color_pixel_rgb888_data_t; -///< Color BLACK in color_pixel_rgb888_data_t -#define RGB888_BLACK {.b = 0x00, .g = 0x00, .r = 0x00} - -///< Color WHITE in color_pixel_rgb888_data_t -#define RGB888_WHITE {.b = 0xFF, .g = 0xFF, .r = 0xFF} - /** * @brief Data structure for RGB565 pixel unit */ diff --git a/components/hal/include/hal/ppa_types.h b/components/hal/include/hal/ppa_types.h index 485e4414f1..67741433c5 100644 --- a/components/hal/include/hal/ppa_types.h +++ b/components/hal/include/hal/ppa_types.h @@ -41,10 +41,11 @@ typedef enum { PPA_SRM_COLOR_MODE_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< PPA SRM color mode: RGB565 */ PPA_SRM_COLOR_MODE_YUV420 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV420), /*!< PPA SRM color mode: YUV420 */ PPA_SRM_COLOR_MODE_YUV444 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV444), /*!< PPA SRM color mode: YUV444 (limited range only)*/ - PPA_SRM_COLOR_MODE_YUV422 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV422), /*!< PPA SRM color mode: YUV422 (input only, limited range only) */ - // YUV444 and YUV422 not supported by PPA hardware, but seems like we can use 2D-DMA to do conversion before sending into and after coming out from the PPA module - // If in_pic is YUV444/422, then TX DMA channel could do DMA2D_CSC_TX_YUV444/422_TO_RGB888_601/709, so PPA in_color_mode is RGB888 + // YUV444 not supported by PPA hardware, but we can use 2D-DMA to do conversion before sending into and after coming out from the PPA module + // If in_pic is YUV444, then TX DMA channel could do DMA2D_CSC_TX_YUV444_TO_RGB888_601/709, so PPA in_color_mode is RGB888 // If out_pic is YUV444, then RX DMA channel could do DMA2D_CSC_RX_YUV420_TO_YUV444, so PPA out_color_mode is YUV420 + // TODO: P4 ECO2 supports YUV422 + // PPA_SRM_COLOR_MODE_YUV422 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV422), /*!< PPA SRM color mode: YUV422 (input only, limited range only) */ } ppa_srm_color_mode_t; /** @@ -57,8 +58,8 @@ typedef enum { PPA_BLEND_COLOR_MODE_A8 = COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A8), /*!< PPA blend color mode: A8, only available on blend foreground input */ PPA_BLEND_COLOR_MODE_A4 = COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A4), /*!< PPA blend color mode: A4, only available on blend foreground input */ // TODO: Support CLUT to support L4/L8 color mode - PPA_BLEND_COLOR_MODE_L8 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L8), /*!< PPA blend color mode: L8, only available on blend inputs */ - PPA_BLEND_COLOR_MODE_L4 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L4), /*!< PPA blend color mode: L4, only available on blend inputs */ + // PPA_BLEND_COLOR_MODE_L8 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L8), /*!< PPA blend color mode: L8, only available on blend inputs */ + // PPA_BLEND_COLOR_MODE_L4 = COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L4), /*!< PPA blend color mode: L4, only available on blend inputs */ } ppa_blend_color_mode_t; /** diff --git a/docs/en/api-reference/peripherals/ppa.rst b/docs/en/api-reference/peripherals/ppa.rst index 67c2825d31..e108558839 100644 --- a/docs/en/api-reference/peripherals/ppa.rst +++ b/docs/en/api-reference/peripherals/ppa.rst @@ -114,7 +114,7 @@ Similarly, some notes to avoid confusion in configuring :cpp:type:`ppa_blend_ope .. list:: - :cpp:member:`ppa_out_pic_blk_config_t::buffer` can be the same pointer to one of the input's :cpp:member:`ppa_in_pic_blk_config_t::buffer` for a blend operation. - The blocks' width/height of FG and BG should be identical, and are the width/height values for the output block. - - If the color mode of the input picture is ``PPA_BLEND_COLOR_MODE_A4`` or ``PPA_BLEND_COLOR_MODE_L4``, then its ``block_w`` and ``block_offset_x`` fields must be even. + - If the color mode of the input picture is ``PPA_BLEND_COLOR_MODE_A4``, then its ``block_w`` and ``block_offset_x`` fields must be even. Fill ~~~~ @@ -140,7 +140,7 @@ The PPA driver has guaranteed the thread safety of calling the PPA operation API Performance Overview ^^^^^^^^^^^^^^^^^^^^ -The PPA operations are acted on the target block of an input picture. Therefore, the time it takes to complete a PPA transaction is proportional to the amount of the data of the block. The size of the entire picture has no influence on the performance. More importantly, the PPA performance highly relies on the PSRAM bandwidth if the pictures are located in the PSRAM section. When there are quite a few peripherals reading and writing to the PSRAM at the same time, the performance of PPA operation will be greatly reduced. +The PPA operations are acted on the target block of an input picture. Therefore, the time it takes to complete a PPA transaction is proportional to the amount of the data in the block. The size of the entire picture has no influence on the performance. More importantly, the PPA performance highly relies on the PSRAM bandwidth if the pictures are located in the PSRAM section. When there are quite a few peripherals reading and writing to the PSRAM at the same time, the performance of PPA operation will be greatly reduced. API Reference -------------