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; +}