mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(ppa): add PPA driver support for ESP32P4
This commit is contained in:
parent
3f632df143
commit
1b1005a1d8
13
components/esp_driver_ppa/CMakeLists.txt
Normal file
13
components/esp_driver_ppa/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
set(srcs)
|
||||
set(public_include "include")
|
||||
if(CONFIG_SOC_PPA_SUPPORTED)
|
||||
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}
|
||||
INCLUDE_DIRS ${public_include}
|
||||
PRIV_REQUIRES esp_mm esp_pm
|
||||
)
|
292
components/esp_driver_ppa/include/driver/ppa.h
Normal file
292
components/esp_driver_ppa/include/driver/ppa.h
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "hal/ppa_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
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, use one constant pixel to fill a target window */
|
||||
PPA_OPERATION_INVALID, /*!< Invalid PPA operations, indicates the quantity of available PPA operations */
|
||||
} ppa_operation_t;
|
||||
|
||||
/**
|
||||
* @brief Type of PPA client handle
|
||||
*/
|
||||
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_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;
|
||||
|
||||
/**
|
||||
* @brief Register a PPA client to do a specific PPA operation
|
||||
*
|
||||
* @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
|
||||
* - 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_client(const ppa_client_config_t *config, ppa_client_handle_t *ret_client);
|
||||
|
||||
/**
|
||||
* @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
|
||||
* - 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_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
|
||||
*/
|
||||
typedef struct {
|
||||
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) */
|
||||
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` */
|
||||
};
|
||||
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;
|
||||
|
||||
/**
|
||||
* @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 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) */
|
||||
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` */
|
||||
};
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief Modes to perform the PPA operations
|
||||
*/
|
||||
typedef enum {
|
||||
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 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 */
|
||||
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 */
|
||||
} 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_config_t
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Perform a SRM operation successfully
|
||||
* - ESP_ERR_INVALID_ARG: Perform a SRM operation failed because of invalid argument
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
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 */
|
||||
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 */
|
||||
};
|
||||
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
|
||||
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 */
|
||||
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 */
|
||||
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` */
|
||||
void *user_data; /*!< User registered data to be passed into `done_cb` callback function */
|
||||
} 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_config_t
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Perform a blend operation successfully
|
||||
* - ESP_ERR_INVALID_ARG: Perform a blend operation failed because of invalid argument
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @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) */
|
||||
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 */
|
||||
} 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_config_t
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Perform a fill operation successfully
|
||||
* - ESP_ERR_INVALID_ARG: Perform a fill operation failed because of invalid argument
|
||||
* - 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
284
components/esp_driver_ppa/src/ppa_blend.c
Normal file
284
components/esp_driver_ppa/src/ppa_blend.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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 = blend_trans_desc->data_burst_length,
|
||||
.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
|
||||
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_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_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);
|
||||
|
||||
// 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(((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,
|
||||
};
|
||||
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);
|
||||
}
|
||||
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");
|
||||
// }
|
||||
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 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_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_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;
|
||||
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));
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
517
components/esp_driver_ppa/src/ppa_core.c
Normal file
517
components/esp_driver_ppa/src/ppa_core.c
Normal file
@ -0,0 +1,517 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/param.h>
|
||||
#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_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)
|
||||
{
|
||||
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 | 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;
|
||||
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
|
||||
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 {
|
||||
// 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 | 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;
|
||||
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
|
||||
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 {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
ret = esp_pm_lock_delete(srm_engine->base.pm_lock);
|
||||
assert(ret == 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) {
|
||||
ret = esp_pm_lock_delete(blending_engine->base.pm_lock);
|
||||
assert(ret == 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 (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_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");
|
||||
|
||||
// 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->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,
|
||||
};
|
||||
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) {
|
||||
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];
|
||||
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 queue with allocated transaction element pointer
|
||||
BaseType_t sent = xQueueSend(trans_elm_ptr_queue, &new_trans_elm, 0);
|
||||
assert(sent);
|
||||
}
|
||||
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;
|
||||
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));
|
||||
|
||||
portENTER_CRITICAL(&ppa_client->spinlock);
|
||||
// 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);
|
||||
|
||||
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
|
||||
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) {
|
||||
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
|
||||
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) {
|
||||
xSemaphoreTake(trans_elm->sem, portMAX_DELAY); // Given in the ISR
|
||||
}
|
||||
|
||||
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;
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
148
components/esp_driver_ppa/src/ppa_fill.c
Normal file
148
components/esp_driver_ppa/src/ppa_fill.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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 = fill_trans_desc->data_burst_length,
|
||||
.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(((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_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 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;
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
232
components/esp_driver_ppa/src/ppa_priv.h
Normal file
232
components/esp_driver_ppa/src/ppa_priv.h
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/queue.h>
|
||||
#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 "hal/ppa_hal.h"
|
||||
#include "esp_pm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PPA_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
|
||||
|
||||
#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");
|
||||
|
||||
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
|
||||
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_client_t ppa_client_t;
|
||||
|
||||
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
|
||||
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 ************************************/
|
||||
|
||||
// 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 at the end
|
||||
// This allows memcpy
|
||||
typedef struct {
|
||||
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;
|
||||
|
||||
// input data manipulation
|
||||
bool rgb_swap;
|
||||
bool byte_swap;
|
||||
ppa_alpha_update_mode_t alpha_update_mode;
|
||||
union {
|
||||
uint32_t alpha_fix_val;
|
||||
float alpha_scale_ratio;
|
||||
};
|
||||
|
||||
ppa_trans_mode_t mode;
|
||||
void *user_data;
|
||||
|
||||
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_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`
|
||||
// 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;
|
||||
};
|
||||
color_pixel_rgb888_data_t fg_fix_rgb_val;
|
||||
|
||||
// color-keying
|
||||
bool bg_ck_en;
|
||||
color_pixel_rgb888_data_t bg_ck_rgb_low_thres;
|
||||
color_pixel_rgb888_data_t bg_ck_rgb_high_thres;
|
||||
bool fg_ck_en;
|
||||
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;
|
||||
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_data_burst_length_t data_burst_length; // Data burst length for the transaction, information passed from the client
|
||||
} ppa_blend_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 ***********************************/
|
||||
|
||||
// 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_client_t *client; // Pointer to the client 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;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
289
components/esp_driver_ppa/src/ppa_srm.c
Normal file
289
components/esp_driver_ppa/src/ppa_srm.c
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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 = srm_trans_desc->data_burst_length,
|
||||
.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);
|
||||
|
||||
// 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 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;
|
||||
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_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);
|
||||
}
|
||||
|
||||
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(((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: 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");
|
||||
// }
|
||||
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,
|
||||
};
|
||||
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);
|
||||
}
|
||||
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
|
||||
|
||||
// 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_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;
|
||||
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;
|
||||
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;
|
||||
|
||||
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_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;
|
||||
}
|
@ -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
|
9
components/esp_driver_ppa/test_apps/CMakeLists.txt
Normal file
9
components/esp_driver_ppa/test_apps/CMakeLists.txt
Normal file
@ -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)
|
2
components/esp_driver_ppa/test_apps/README.md
Normal file
2
components/esp_driver_ppa/test_apps/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
9
components/esp_driver_ppa/test_apps/main/CMakeLists.txt
Normal file
9
components/esp_driver_ppa/test_apps/main/CMakeLists.txt
Normal file
@ -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)
|
@ -0,0 +1,2 @@
|
||||
dependencies:
|
||||
ccomp_timer: "^1.0.0"
|
42
components/esp_driver_ppa/test_apps/main/test_app_main.c
Normal file
42
components/esp_driver_ppa/test_apps/main/test_app_main.c
Normal file
@ -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();
|
||||
}
|
489
components/esp_driver_ppa/test_apps/main/test_ppa.c
Normal file
489
components/esp_driver_ppa/test_apps/main/test_ppa.c
Normal file
@ -0,0 +1,489 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#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_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_srm_handle));
|
||||
ppa_client_config.oper_type = PPA_OPERATION_BLEND;
|
||||
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_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,
|
||||
.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_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_blend_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_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_fill_handle_b, &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_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_fill_handle_b, &fill_oper_config));
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
|
||||
// Unregister all PPA clients
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
17
components/esp_driver_ppa/test_apps/pytest_ppa.py
Normal file
17
components/esp_driver_ppa/test_apps/pytest_ppa.py
Normal file
@ -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()
|
6
components/esp_driver_ppa/test_apps/sdkconfig.ci.release
Normal file
6
components/esp_driver_ppa/test_apps/sdkconfig.ci.release
Normal file
@ -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
|
3
components/esp_driver_ppa/test_apps/sdkconfig.defaults
Normal file
3
components/esp_driver_ppa/test_apps/sdkconfig.defaults
Normal file
@ -0,0 +1,3 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
@ -0,0 +1,3 @@
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_HEX=y
|
||||
CONFIG_SPIRAM_SPEED_200M=y
|
@ -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);
|
||||
@ -871,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;
|
||||
|
@ -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;
|
||||
|
||||
@ -271,6 +271,27 @@ 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_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);
|
||||
|
||||
/**
|
||||
* @brief Type of 2D-DMA event data
|
||||
*/
|
||||
|
@ -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]
|
||||
|
@ -217,6 +217,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"
|
||||
|
@ -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
|
||||
|
@ -24,15 +24,12 @@ 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_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
|
||||
|
||||
/**
|
||||
* @brief Enumeration of PPA blending mode
|
||||
@ -71,13 +68,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;
|
||||
@ -90,7 +87,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);
|
||||
@ -104,7 +101,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);
|
||||
@ -115,22 +112,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:
|
||||
@ -146,7 +143,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;
|
||||
}
|
||||
@ -157,92 +154,92 @@ 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 pixel color space conversion from RX to TX is YUV to RGB
|
||||
* @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_sr_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, 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:
|
||||
@ -252,18 +249,18 @@ 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 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_sr_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, 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:
|
||||
@ -273,18 +270,18 @@ 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
|
||||
* @param range One of color range options in ppa_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, 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:
|
||||
@ -294,18 +291,18 @@ 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
|
||||
* @param range One of color range options in ppa_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, 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:
|
||||
@ -315,60 +312,61 @@ 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 swap 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 update 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 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_sr_configure_rx_alpha(ppa_dev_t *dev, ppa_ll_rx_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_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
|
||||
// Unsupported alpha update mode
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@ -441,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();
|
||||
@ -473,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;
|
||||
@ -567,71 +565,73 @@ 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_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 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_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_update_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
|
||||
// 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_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 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_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_update_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
|
||||
// Unsupported alpha update mode
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@ -642,12 +642,12 @@ static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_ll_rx_
|
||||
* @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)
|
||||
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;
|
||||
}
|
||||
@ -658,9 +658,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;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -678,10 +680,15 @@ static inline void ppa_ll_blend_set_rx_fg_fix_rgb(ppa_dev_t *dev, uint32_t rgb)
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -691,10 +698,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -703,9 +715,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
@ -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;
|
||||
|
||||
@ -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 */
|
||||
@ -174,6 +176,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 \
|
||||
|
44
components/hal/include/hal/ppa_hal.h
Normal file
44
components/hal/include/hal/ppa_hal.h
Normal file
@ -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
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hal/color_types.h"
|
||||
#include "hal/dma2d_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -17,43 +18,99 @@ 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_t;
|
||||
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)*/
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* @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_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;
|
||||
|
||||
/**
|
||||
* @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 (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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
19
components/hal/ppa_hal.c
Normal file
19
components/hal/ppa_hal.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#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;
|
||||
}
|
@ -259,6 +259,10 @@ config SOC_GP_LDO_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_PPA_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_LIGHT_SLEEP_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
@ -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_BLEND_FG_TX (2)
|
||||
#define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_BG_TX (3)
|
||||
#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
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
BIN
docs/_static/diagrams/ppa/pic_blk_concept.png
vendored
Normal file
BIN
docs/_static/diagrams/ppa/pic_blk_concept.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
@ -175,6 +175,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',
|
||||
@ -277,6 +279,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,
|
||||
|
@ -16,12 +16,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 \
|
||||
|
@ -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
|
||||
|
149
docs/en/api-reference/peripherals/ppa.rst
Normal file
149
docs/en/api-reference/peripherals/ppa.rst
Normal file
@ -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``, 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 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
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/ppa.inc
|
||||
.. include-build-file:: inc/ppa_types.inc
|
@ -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
|
||||
|
1
docs/zh_CN/api-reference/peripherals/ppa.rst
Normal file
1
docs/zh_CN/api-reference/peripherals/ppa.rst
Normal file
@ -0,0 +1 @@
|
||||
.. include:: ../../../en/api-reference/peripherals/ppa.rst
|
Loading…
x
Reference in New Issue
Block a user