feat(ppa): add PPA driver support for ESP32P4

Remove the check for in_accepting_trans_state
Add color_pixel_xxxx_data_t structures to color_types.h
Fix PM lock protection (Tested, now works well)
* CPU_MAX, PM lock and semaphore order
* Remove ppa_driver PM lock
Modify concurrency (queue, trans recycle, semaphore, ...)
Add programming guide
Add test apps
This commit is contained in:
Song Ruo Jing 2024-04-25 22:18:38 +08:00
parent 916c0ef8d1
commit 8464fac845
27 changed files with 906 additions and 132 deletions

View File

@ -21,7 +21,7 @@ typedef enum {
PPA_OPERATION_SRM, /*!< Do scale-rotate-mirror operation */
PPA_OPERATION_BLEND, /*!< Do blend operation */
PPA_OPERATION_FILL, /*!< Do fill operation, use one constant pixel to fill a target window */
PPA_OPERATION_NUM, /*!< Quantity of PPA operations */
PPA_OPERATION_INVALID, /*!< Invalid PPA operations, indicates the quantity of available PPA operations */
} ppa_operation_t;
/**
@ -89,7 +89,7 @@ typedef bool (*ppa_event_callback_t)(ppa_client_handle_t ppa_client, ppa_event_d
* @brief Group of supported PPA callbacks
*/
typedef struct {
ppa_event_callback_t on_trans_done; /*! Invoked when a PPA transaction finishes */
ppa_event_callback_t on_trans_done; /*!< Invoked when a PPA transaction finishes */
} ppa_event_callbacks_t;
/**
@ -111,7 +111,7 @@ esp_err_t ppa_client_register_event_callbacks(ppa_client_handle_t ppa_client, co
* @brief A collection of configuration items for an input picture and the target block inside the picture
*/
typedef struct {
void *buffer; /*!< Pointer to the input picture buffer */
const void *buffer; /*!< Pointer to the input picture buffer */
uint32_t pic_w; /*!< Input picture width (unit: pixel) */
uint32_t pic_h; /*!< Input picture height (unit: pixel) */
uint32_t block_w; /*!< Target block width (unit: pixel) */
@ -123,8 +123,8 @@ typedef struct {
ppa_blend_color_mode_t blend_cm; /*!< Color mode of the picture in a PPA blend operation. Supported color mode in `ppa_blend_color_mode_t` */
ppa_fill_color_mode_t fill_cm; /*!< Color mode of the picture in a PPA fill operation. Supported color mode in `ppa_fill_color_mode_t` */
};
color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */
color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */
ppa_color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */
ppa_color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */
} ppa_in_pic_blk_config_t;
/**
@ -142,8 +142,8 @@ typedef struct {
ppa_blend_color_mode_t blend_cm; /*!< Color mode of the picture in a PPA blend operation. Supported color mode in `ppa_blend_color_mode_t` */
ppa_fill_color_mode_t fill_cm; /*!< Color mode of the picture in a PPA fill operation. Supported color mode in `ppa_fill_color_mode_t` */
};
color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */
color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */
ppa_color_range_t yuv_range; /*!< When the color mode is any YUV color space, this field is to describe its color range */
ppa_color_conv_std_rgb_yuv_t yuv_std; /*!< When the color mode is any YUV color space, this field is to describe its YUV<->RGB conversion standard */
} ppa_out_pic_blk_config_t;
/**
@ -193,8 +193,7 @@ typedef struct {
* @return
* - ESP_OK: Perform a SRM operation successfully
* - ESP_ERR_INVALID_ARG: Perform a SRM operation failed because of invalid argument
* - ESP_ERR_NO_MEM: Perform a SRM operation failed because out of memory
* - ESP_FAIL: Perform a SRM operation failed because the client cannot accept transaction now
* - ESP_FAIL: Perform a SRM operation failed because the client's pending transactions has reached its maximum capacity
*/
esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_srm_oper_config_t *config);
@ -227,7 +226,7 @@ typedef struct {
When PPA_ALPHA_SCALE mode is selected, alpha_scale_ratio is the multiplier to the input alpha value (output_alpha = alpha_scale_ratio * input_alpha)
Ratio resolution is 1/256 */
};
uint32_t fg_fix_rgb_val; /*!< When in_fg.blend_cm is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground, in RGB888 format (R[23:16], G[15: 8], B[7:0]) */
color_pixel_rgb888_data_t fg_fix_rgb_val; /*!< When in_fg.blend_cm is PPA_BLEND_COLOR_MODE_A8/4, this field can be used to set a fixed color for the foreground, in RGB888 format */
// color-keying
// A pixel, where its background element and foreground element are both out of their color-keying ranges, will follow Alpha Blending
@ -255,8 +254,7 @@ typedef struct {
* @return
* - ESP_OK: Perform a blend operation successfully
* - ESP_ERR_INVALID_ARG: Perform a blend operation failed because of invalid argument
* - ESP_ERR_NO_MEM: Perform a blend operation failed because out of memory
* - ESP_FAIL: Perform a blend operation failed because the client cannot accept transaction now
* - ESP_FAIL: Perform a blend operation failed because the client's pending transactions has reached its maximum capacity
*/
esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_config_t *config);
@ -268,7 +266,7 @@ typedef struct {
uint32_t fill_block_w; /*!< The width of the block to be filled (unit: pixel) */
uint32_t fill_block_h; /*!< The height of the block to be filled (unit: pixel) */
uint32_t fill_argb_color; /*!< The color to be filled, in ARGB8888 format ((A[31:24], R[23:16], G[15: 8], B[7:0])) */
color_pixel_argb8888_data_t fill_argb_color; /*!< The color to be filled, in ARGB8888 format */
ppa_trans_mode_t mode; /*!< Determines whether to block inside the operation functions, see `ppa_trans_mode_t` */
void *user_data; /*!< User registered data to be passed into `done_cb` callback function */
@ -283,8 +281,7 @@ typedef struct {
* @return
* - ESP_OK: Perform a fill operation successfully
* - ESP_ERR_INVALID_ARG: Perform a fill operation failed because of invalid argument
* - ESP_ERR_NO_MEM: Perform a fill operation failed because out of memory
* - ESP_FAIL: Perform a fill operation failed because the client cannot accept transaction now
* - ESP_FAIL: Perform a fill operation failed because the client's pending transactions has reached its maximum capacity
*/
esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_config_t *config);

View File

@ -146,7 +146,7 @@ bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_chann
ppa_ll_blend_set_rx_fg_color_mode(platform->hal.dev, blend_trans_desc->in_fg.blend_cm);
if (COLOR_SPACE_TYPE((uint32_t)blend_trans_desc->in_fg.blend_cm) == COLOR_SPACE_ALPHA) {
ppa_ll_blend_set_rx_fg_fix_rgb(platform->hal.dev, blend_trans_desc->fg_fix_rgb_val);
ppa_ll_blend_set_rx_fg_fix_rgb(platform->hal.dev, &blend_trans_desc->fg_fix_rgb_val);
}
ppa_ll_blend_enable_rx_fg_byte_swap(platform->hal.dev, blend_trans_desc->fg_byte_swap);
ppa_ll_blend_enable_rx_fg_rgb_swap(platform->hal.dev, blend_trans_desc->fg_rgb_swap);
@ -231,18 +231,21 @@ esp_err_t ppa_do_blend(ppa_client_handle_t ppa_client, const ppa_blend_oper_conf
.color_type_id = config->in_bg.blend_cm,
};
uint32_t in_bg_pic_len = config->in_bg.pic_w * config->in_bg.pic_h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8;
esp_cache_msync(config->in_bg.buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
esp_cache_msync((void *)config->in_bg.buffer, in_bg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
color_space_pixel_format_t in_fg_pixel_format = {
.color_type_id = config->in_fg.blend_cm,
};
uint32_t in_fg_pic_len = config->in_fg.pic_w * config->in_fg.pic_h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8;
esp_cache_msync(config->in_fg.buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
esp_cache_msync((void *)config->in_fg.buffer, in_fg_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
// Invalidate out_buffer
esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
esp_err_t ret = ESP_OK;
ppa_trans_t *trans_elm = NULL;
if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) {
portENTER_CRITICAL(&ppa_client->spinlock);
bool trans_elm_acquired = xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0);
portEXIT_CRITICAL(&ppa_client->spinlock);
if (trans_elm_acquired) {
dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc;
ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config;

View File

@ -46,10 +46,10 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine);
static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t trans_elm_num, ppa_operation_t oper_type);
static void ppa_free_transaction(ppa_trans_t *trans_elm);
const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATION_NUM] = {
ppa_srm_transaction_on_picked,
ppa_blend_transaction_on_picked,
ppa_fill_transaction_on_picked,
const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATION_INVALID] = {
[PPA_OPERATION_SRM] = ppa_srm_transaction_on_picked,
[PPA_OPERATION_BLEND] = ppa_blend_transaction_on_picked,
[PPA_OPERATION_FILL] = ppa_fill_transaction_on_picked,
};
static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine)
@ -104,9 +104,11 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin
}
#if CONFIG_PM_ENABLE
ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_srm", &srm_engine->base.pm_lock);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "create pm lock failed");
if (ret == ESP_OK) {
ret = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "ppa_srm", &srm_engine->base.pm_lock);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "create pm lock failed");
}
}
#endif
} else {
@ -147,9 +149,11 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin
}
#if CONFIG_PM_ENABLE
ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa_blending", &blending_engine->base.pm_lock);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "create pm lock failed");
if (ret == ESP_OK) {
ret = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "ppa_blending", &blending_engine->base.pm_lock);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "create pm lock failed");
}
}
#endif
} else {
@ -180,16 +184,6 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin
ESP_LOGE(TAG, "install 2D-DMA failed");
goto wrap_up;
}
#if CONFIG_PM_ENABLE
assert(!s_platform.pm_lock);
// Create and acquire the PM lock
ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "ppa", &s_platform.pm_lock);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "create pm lock failed");
goto wrap_up;
}
#endif
}
}
wrap_up:
@ -250,14 +244,6 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine)
if (!s_platform.srm && !s_platform.blending) {
assert(s_platform.srm_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0);
#if CONFIG_PM_ENABLE
if (s_platform.pm_lock) {
ret = esp_pm_lock_delete(s_platform.pm_lock);
assert(ret == ESP_OK);
s_platform.pm_lock = NULL;
}
#endif
if (s_platform.dma2d_pool_handle) {
dma2d_release_pool(s_platform.dma2d_pool_handle); // TODO: check return value. If not ESP_OK, then must be error on other 2D-DMA clients :( Give a warning log?
s_platform.dma2d_pool_handle = NULL;
@ -278,19 +264,19 @@ esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_hand
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(config && ret_client, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(config->oper_type < PPA_OPERATION_NUM, ESP_ERR_INVALID_ARG, TAG, "unknown operation");
ESP_RETURN_ON_FALSE(config->oper_type < PPA_OPERATION_INVALID, ESP_ERR_INVALID_ARG, TAG, "unknown operation");
ppa_client_t *client = (ppa_client_t *)heap_caps_calloc(1, sizeof(ppa_client_t), PPA_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(client, ESP_ERR_NO_MEM, TAG, "no mem to register client");
uint32_t ring_buf_size = MAX(1, config->max_pending_trans_num);
client->trans_elm_ptr_queue = xQueueCreateWithCaps(ring_buf_size, sizeof(uint32_t), PPA_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(client->trans_elm_ptr_queue && ppa_malloc_transaction(client->trans_elm_ptr_queue, ring_buf_size, config->oper_type),
// Allocate memory for storing transaction contexts and create a queue to save these trans_elm_ptr
uint32_t queue_size = MAX(1, config->max_pending_trans_num);
client->trans_elm_ptr_queue = xQueueCreateWithCaps(queue_size, sizeof(uint32_t), PPA_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(client->trans_elm_ptr_queue && ppa_malloc_transaction(client->trans_elm_ptr_queue, queue_size, config->oper_type),
ESP_ERR_NO_MEM, err, TAG, "no mem for transaction storage");
client->oper_type = config->oper_type;
client->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
client->in_accepting_trans_state = true;
if (config->oper_type == PPA_OPERATION_SRM) {
ppa_engine_config_t engine_config = {
.engine = PPA_ENGINE_TYPE_SRM,
@ -318,7 +304,6 @@ esp_err_t ppa_unregister_client(ppa_client_handle_t ppa_client)
bool do_unregister = false;
portENTER_CRITICAL(&ppa_client->spinlock);
if (ppa_client->trans_cnt == 0) {
ppa_client->in_accepting_trans_state = false;
do_unregister = true;
}
portEXIT_CRITICAL(&ppa_client->spinlock);
@ -387,12 +372,12 @@ static bool ppa_malloc_transaction(QueueHandle_t trans_elm_ptr_queue, uint32_t t
trans_on_picked_desc->op_desc = ppa_trans_desc;
trans_on_picked_desc->trans_elm = new_trans_elm;
dma_trans_desc->user_config = (void *)trans_on_picked_desc;
dma_trans_desc->on_job_picked = ppa_oper_trans_on_picked_func[oper_type]; // TODO: This maybe better to be in the ppa_do_xxx function
dma_trans_desc->on_job_picked = ppa_oper_trans_on_picked_func[oper_type];
new_trans_elm->trans_desc = dma_trans_desc;
new_trans_elm->dma_trans_placeholder = dma_trans_elm;
new_trans_elm->sem = ppa_trans_sem;
// Fill the ring buffer with allocated transaction element pointer
// Fill the queue with allocated transaction element pointer
BaseType_t sent = xQueueSend(trans_elm_ptr_queue, &new_trans_elm, 0);
assert(sent);
}
@ -424,27 +409,14 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng
esp_err_t ret = ESP_OK;
esp_err_t pm_lock_ret __attribute__((unused));
#if CONFIG_PM_ENABLE
pm_lock_ret = esp_pm_lock_acquire(s_platform.pm_lock);
assert((pm_lock_ret == ESP_OK) && "acquire pm_lock failed");
#endif
portENTER_CRITICAL(&ppa_client->spinlock);
// TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!!
if (ppa_client->in_accepting_trans_state) {
// Send transaction into PPA engine queue
STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry);
ppa_client->trans_cnt++;
} else {
ret = ESP_FAIL;
}
// Send transaction into PPA engine queue
portENTER_CRITICAL(&ppa_engine_base->spinlock);
STAILQ_INSERT_TAIL(&ppa_engine_base->trans_stailq, trans_elm, entry);
portEXIT_CRITICAL(&ppa_engine_base->spinlock);
ppa_client->trans_cnt++;
portEXIT_CRITICAL(&ppa_client->spinlock);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "The client cannot accept transaction now");
goto err;
}
TickType_t ticks_to_wait = (mode == PPA_TRANS_MODE_NON_BLOCKING) ? 0 : portMAX_DELAY;
if (xSemaphoreTake(ppa_engine_base->sem, ticks_to_wait) == pdTRUE) {
// Check if the transaction has already been started from the ISR
@ -469,11 +441,11 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng
portENTER_CRITICAL(&ppa_engine_base->spinlock);
STAILQ_REMOVE(&ppa_engine_base->trans_stailq, trans_elm, ppa_trans_s, entry);
portEXIT_CRITICAL(&ppa_engine_base->spinlock);
xSemaphoreGive(ppa_engine_base->sem);
#if CONFIG_PM_ENABLE
pm_lock_ret = esp_pm_lock_release(ppa_engine_base->pm_lock);
assert((pm_lock_ret == ESP_OK) && "release pm_lock failed");
#endif
xSemaphoreGive(ppa_engine_base->sem);
portENTER_CRITICAL(&ppa_client->spinlock);
ppa_client->trans_cnt--;
portEXIT_CRITICAL(&ppa_client->spinlock);
@ -489,15 +461,9 @@ esp_err_t ppa_do_operation(ppa_client_handle_t ppa_client, ppa_engine_t *ppa_eng
// printf("ppa intr: %ld\n", PPA.int_raw.val);
// }
xSemaphoreTake(trans_elm->sem, portMAX_DELAY); // Given in the ISR
// Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho)
// ppa_recycle_transaction(ppa_client, trans_elm); // TODO: Do we need it to be here or can be at the end of done_cb?
// TODO: Sanity check new_trans_elm not in trans_stailq anymore? (loop takes time tho)
}
#if CONFIG_PM_ENABLE
pm_lock_ret = esp_pm_lock_release(s_platform.pm_lock);
assert((pm_lock_ret == ESP_OK) && "release pm_lock failed");
#endif
err:
return ret;
}
@ -510,11 +476,9 @@ bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data
ppa_client_t *client = trans_elm->client;
ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = (ppa_dma2d_trans_on_picked_config_t *)trans_elm->trans_desc->user_config;
ppa_engine_t *engine_base = trans_on_picked_desc->ppa_engine;
if (client->done_cb) {
ppa_event_data_t edata = {};
need_yield |= client->done_cb(client, &edata, trans_elm->user_data);
}
// Save callback contexts
ppa_event_callback_t done_cb = client->done_cb;
void *trans_elm_user_data = trans_elm->user_data;
ppa_trans_t *next_start_trans = NULL;
portENTER_CRITICAL_ISR(&engine_base->spinlock);
@ -523,28 +487,34 @@ bool ppa_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data
next_start_trans = STAILQ_FIRST(&engine_base->trans_stailq);
portEXIT_CRITICAL_ISR(&engine_base->spinlock);
portENTER_CRITICAL_ISR(&client->spinlock);
// Release transaction semaphore to unblock ppa_do_operation
xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken);
need_yield |= (HPTaskAwoken == pdTRUE);
// Then recycle transaction elm
need_yield |= ppa_recycle_transaction(client, trans_elm);
client->trans_cnt--;
portEXIT_CRITICAL_ISR(&client->spinlock);
// If there is next trans in PPA engine queue, send it to DMA queue; otherwise, release the engine semaphore
if (next_start_trans) {
ppa_dma2d_enqueue(next_start_trans);
} else {
xSemaphoreGiveFromISR(engine_base->sem, &HPTaskAwoken);
need_yield |= (HPTaskAwoken == pdTRUE);
#if CONFIG_PM_ENABLE
esp_err_t pm_lock_ret = esp_pm_lock_release(engine_base->pm_lock);
assert(pm_lock_ret == ESP_OK);
#endif
xSemaphoreGiveFromISR(engine_base->sem, &HPTaskAwoken);
need_yield |= (HPTaskAwoken == pdTRUE);
}
// Recycle transaction and release transaction semaphore
// if (trans_elm->sem != NULL) {
xSemaphoreGiveFromISR(trans_elm->sem, &HPTaskAwoken);
need_yield |= (HPTaskAwoken == pdTRUE);
// }
// TODO: Check whether trans_cnt and trans_elm_ptr_queue need in one spinlock!!!
portENTER_CRITICAL_ISR(&client->spinlock);
need_yield |= ppa_recycle_transaction(client, trans_elm);
client->trans_cnt--;
portEXIT_CRITICAL_ISR(&client->spinlock);
// Process last transaction's callback
if (done_cb) {
ppa_event_data_t edata = {};
need_yield |= done_cb(client, &edata, trans_elm_user_data);
}
return need_yield;
}

View File

@ -77,7 +77,7 @@ bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channe
dma2d_start(dma2d_rx_chan);
// Configure PPA Blending engine
ppa_ll_blend_configure_filling_block(platform->hal.dev, fill_trans_desc->fill_argb_color, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h);
ppa_ll_blend_configure_filling_block(platform->hal.dev, &fill_trans_desc->fill_argb_color, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h);
ppa_ll_blend_set_tx_color_mode(platform->hal.dev, fill_trans_desc->out.fill_cm);
ppa_ll_blend_start(platform->hal.dev, PPA_LL_BLEND_TRANS_MODE_FILL);
@ -104,11 +104,14 @@ esp_err_t ppa_do_fill(ppa_client_handle_t ppa_client, const ppa_fill_oper_config
// To reduce complexity, color_mode, fill_block_w/h correctness are checked in their corresponding LL functions
// Write back and invalidate are performed on the entire picture (the window content is not continuous in the buffer)
esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
esp_err_t ret = ESP_OK;
ppa_trans_t *trans_elm = NULL;
if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) {
portENTER_CRITICAL(&ppa_client->spinlock);
bool trans_elm_acquired = xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0);
portEXIT_CRITICAL(&ppa_client->spinlock);
if (trans_elm_acquired) {
dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc;
ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config;

View File

@ -78,7 +78,6 @@ struct ppa_client_t {
ppa_engine_t *engine; // Pointer to the PPA engine that in charge of performing the PPA operation
uint32_t trans_cnt; // Number of pending PPA transactions
portMUX_TYPE spinlock; // Client level spinlock
bool in_accepting_trans_state; // Indicates whether the client can accept new PPA transaction requests now
ppa_event_callback_t done_cb; // Transaction done callback
QueueHandle_t trans_elm_ptr_queue; // Queue that contains the pointers to the allocated memory to save the transaction contexts
};
@ -141,7 +140,7 @@ typedef struct {
uint32_t fg_alpha_fix_val;
float fg_alpha_scale_ratio;
};
uint32_t fg_fix_rgb_val;
color_pixel_rgb888_data_t fg_fix_rgb_val;
// color-keying
bool bg_ck_en;
@ -209,9 +208,6 @@ struct ppa_platform_t {
uint32_t blend_engine_ref_count; // Reference count used to protect PPA blending engine acquire and release
size_t buf_alignment_size; // Alignment requirement for the outgoing buffer addr and size to satisfy cache line size
uint32_t dma_desc_mem_size; // Alignment requirement for the 2D-DMA descriptor to satisfy cache line size
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock; // Power management lock
#endif
};
#ifdef __cplusplus

View File

@ -108,7 +108,7 @@ bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel
if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV444) {
ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888;
dma2d_csc_config_t dma_tx_csc = {0};
if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) {
if (srm_trans_desc->in.yuv_std == PPA_COLOR_CONV_STD_RGB_YUV_BT601) {
dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_601;
} else {
dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV444_TO_RGB888_709;
@ -117,7 +117,7 @@ bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel
} else if (ppa_in_color_mode == PPA_SRM_COLOR_MODE_YUV422) {
ppa_in_color_mode = PPA_SRM_COLOR_MODE_RGB888;
dma2d_csc_config_t dma_tx_csc = {0};
if (srm_trans_desc->in.yuv_std == COLOR_CONV_STD_RGB_YUV_BT601) {
if (srm_trans_desc->in.yuv_std == PPA_COLOR_CONV_STD_RGB_YUV_BT601) {
dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_601;
} else {
dma_tx_csc.tx_csc_option = DMA2D_CSC_TX_YUV422_TO_RGB888_709;
@ -227,13 +227,16 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s
.color_type_id = config->in.srm_cm,
};
uint32_t in_pic_len = config->in.pic_w * config->in.pic_h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8;
esp_cache_msync(config->in.buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
esp_cache_msync((void *)config->in.buffer, in_pic_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
// Invalidate out_buffer
esp_cache_msync(config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
esp_cache_msync((void *)config->out.buffer, config->out.buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
esp_err_t ret = ESP_OK;
ppa_trans_t *trans_elm = NULL;
if (xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0)) {
portENTER_CRITICAL(&ppa_client->spinlock);
bool trans_elm_acquired = xQueueReceive(ppa_client->trans_elm_ptr_queue, (void *)&trans_elm, 0);
portEXIT_CRITICAL(&ppa_client->spinlock);
if (trans_elm_acquired) {
dma2d_trans_config_t *dma_trans_desc = trans_elm->trans_desc;
ppa_dma2d_trans_on_picked_config_t *trans_on_picked_desc = dma_trans_desc->user_config;

View File

@ -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

View 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)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32-P4 |
| ----------------- | -------- |

View 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)

View File

@ -0,0 +1,2 @@
dependencies:
ccomp_timer: "^1.0.0"

View 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();
}

View 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_a_handle;
ppa_client_handle_t ppa_client_b_handle;
ppa_client_handle_t ppa_client_c_handle;
ppa_client_handle_t ppa_client_d_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_SRM,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_a_handle));
ppa_client_config.oper_type = PPA_OPERATION_BLEND;
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_b_handle));
ppa_client_config.oper_type = PPA_OPERATION_FILL;
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_c_handle));
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_d_handle));
ppa_srm_oper_config_t srm_oper_config = {
.in.buffer = buf_1,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = w,
.in.block_h = h,
.in.block_offset_x = 0,
.in.block_offset_y = 0,
.in.srm_cm = buf_1_color_type_id,
.out.buffer = buf_2,
.out.buffer_size = buf_2_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.srm_cm = buf_2_color_type_id,
.rotation_angle = PPA_SRM_ROTATION_ANGLE_0,
.scale_x = 1.0,
.scale_y = 1.0,
.mode = PPA_TRANS_MODE_BLOCKING,
};
// A SRM client can request to do a SRM operation
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_a_handle, &srm_oper_config));
// A non-SRM client can not request to do a SRM operation
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_scale_rotate_mirror(ppa_client_b_handle, &srm_oper_config));
ppa_blend_oper_config_t blend_oper_config = {
.in_bg.buffer = buf_1,
.in_bg.pic_w = w,
.in_bg.pic_h = h,
.in_bg.block_w = w,
.in_bg.block_h = h,
.in_bg.block_offset_x = 0,
.in_bg.block_offset_y = 0,
.in_bg.blend_cm = buf_1_color_type_id,
.in_fg.buffer = buf_2,
.in_fg.pic_w = w,
.in_fg.pic_h = h,
.in_fg.block_w = w,
.in_fg.block_h = h,
.in_fg.block_offset_x = 0,
.in_fg.block_offset_y = 0,
.in_fg.blend_cm = buf_2_color_type_id,
.out.buffer = buf_1,
.out.buffer_size = buf_1_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.blend_cm = buf_1_color_type_id,
.mode = PPA_TRANS_MODE_BLOCKING,
};
// A blend client can request to do a blend operation
TEST_ESP_OK(ppa_do_blend(ppa_client_b_handle, &blend_oper_config));
// A non-blend client can not request to do a blend operation
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_blend(ppa_client_d_handle, &blend_oper_config));
ppa_fill_oper_config_t fill_oper_config = {
.out.buffer = buf_1,
.out.buffer_size = buf_1_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.fill_cm = buf_1_color_type_id,
.fill_block_w = w,
.fill_block_h = h,
.fill_argb_color = {
.val = 0xFF00FF00,
},
.mode = PPA_TRANS_MODE_NON_BLOCKING,
};
// A fill client can request to do a fill operation
TEST_ESP_OK(ppa_do_fill(ppa_client_c_handle, &fill_oper_config));
// Another fill client can also request another fill operation at the same time
TEST_ESP_OK(ppa_do_fill(ppa_client_d_handle, &fill_oper_config));
vTaskDelay(pdMS_TO_TICKS(500));
// Unregister all PPA clients
TEST_ESP_OK(ppa_unregister_client(ppa_client_a_handle));
TEST_ESP_OK(ppa_unregister_client(ppa_client_b_handle));
TEST_ESP_OK(ppa_unregister_client(ppa_client_c_handle));
TEST_ESP_OK(ppa_unregister_client(ppa_client_d_handle));
free(buf_1);
free(buf_2);
}
static bool ppa_trans_done_cb(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
SemaphoreHandle_t sem = (SemaphoreHandle_t)user_data;
xSemaphoreGiveFromISR(sem, &xHigherPriorityTaskWoken);
return (xHigherPriorityTaskWoken == pdTRUE);
}
TEST_CASE("ppa_pending_transactions_in_queue", "[PPA]")
{
// A big picture block takes longer time to process, desired for this test case
const uint32_t w = 1920;
const uint32_t h = 1080;
const uint32_t buf_1_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888);
const uint32_t buf_2_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888);
color_space_pixel_format_t buf_1_cm = {
.color_type_id = buf_1_color_type_id,
};
color_space_pixel_format_t buf_2_cm = {
.color_type_id = buf_2_color_type_id,
};
uint32_t buf_1_size = w * h * color_hal_pixel_format_get_bit_depth(buf_1_cm) / 8;
uint32_t buf_2_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(buf_2_cm) / 8, 64);
uint8_t *buf_1 = heap_caps_aligned_calloc(64, buf_1_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(buf_1);
uint8_t *buf_2 = heap_caps_aligned_calloc(64, buf_2_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(buf_2);
// Register two PPA SRM clients with different max_pending_trans_num
ppa_client_handle_t ppa_client_a_handle;
ppa_client_handle_t ppa_client_b_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_SRM,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_a_handle));
ppa_client_config.max_pending_trans_num = 3;
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_b_handle));
ppa_event_callbacks_t cbs = {
.on_trans_done = ppa_trans_done_cb,
};
ppa_client_register_event_callbacks(ppa_client_a_handle, &cbs);
SemaphoreHandle_t sem = xSemaphoreCreateBinary();
ppa_srm_oper_config_t oper_config = {
.in.buffer = buf_1,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = w,
.in.block_h = h,
.in.block_offset_x = 0,
.in.block_offset_y = 0,
.in.srm_cm = buf_1_color_type_id,
.out.buffer = buf_2,
.out.buffer_size = buf_2_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.srm_cm = buf_2_color_type_id,
.rotation_angle = PPA_SRM_ROTATION_ANGLE_0,
.scale_x = 1.0,
.scale_y = 1.0,
.user_data = (void *)sem,
.mode = PPA_TRANS_MODE_NON_BLOCKING,
};
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_a_handle, &oper_config));
// Another transaction cannot be accept since client_a can only hold one transaction
TEST_ESP_ERR(ESP_FAIL, ppa_do_scale_rotate_mirror(ppa_client_a_handle, &oper_config));
// Wait for the last transaction finishes
xSemaphoreTake(sem, portMAX_DELAY);
// Then a new transaction can be accepted again
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_a_handle, &oper_config));
// Client can not be unregistered when there are unfinished transactions
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ppa_unregister_client(ppa_client_a_handle));
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config));
// Every PPA engine can only process one operation at a time
// Transactions are being processed with First-In-First-Out
// So, at the moment, the new transaction requested by client_b has finished, the last transaction requested by client_a for sure has finished
TEST_ASSERT(xSemaphoreTake(sem, 0) == pdTRUE);
// client_b can accept more than one transactions
oper_config.mode = PPA_TRANS_MODE_NON_BLOCKING;
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config));
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config));
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config));
// The last transaction requested is with BLOCKING mode, so the last call to ppa_do_scale_rotate_mirror returned means all transactions finished
// Unregister all PPA clients
TEST_ESP_OK(ppa_unregister_client(ppa_client_a_handle));
TEST_ESP_OK(ppa_unregister_client(ppa_client_b_handle));
vSemaphoreDelete(sem);
free(buf_1);
free(buf_2);
}
TEST_CASE("ppa_srm_performance", "[PPA][ignore]")
{
const uint32_t w = 1920; // 1920 / 1280 / 800 / 640
const uint32_t h = 1080; // 1080 / 720 / 480
const uint32_t block_w = w;
const uint32_t block_h = h;
const ppa_srm_color_mode_t in_cm = PPA_SRM_COLOR_MODE_ARGB8888;
const ppa_srm_color_mode_t out_cm = PPA_SRM_COLOR_MODE_YUV420;
const ppa_srm_rotation_angle_t rotation = PPA_SRM_ROTATION_ANGLE_0;
const float scale_x = 1.0;
const float scale_y = 1.0;
color_space_pixel_format_t in_pixel_format = {
.color_type_id = in_cm,
};
color_space_pixel_format_t out_pixel_format = {
.color_type_id = out_cm,
};
uint32_t in_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8;
uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64);
uint8_t *out_buf = heap_caps_aligned_calloc(64, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(out_buf);
uint8_t *in_buf = heap_caps_aligned_calloc(64, in_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(in_buf);
uint8_t *ptr = in_buf;
for (int x = 0; x < in_buf_size; x++) {
ptr[x] = x;
}
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_SRM,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
uint32_t out_pic_w = (rotation == PPA_SRM_ROTATION_ANGLE_0 || rotation == PPA_SRM_ROTATION_ANGLE_180) ? w : h;
uint32_t out_pic_h = (rotation == PPA_SRM_ROTATION_ANGLE_0 || rotation == PPA_SRM_ROTATION_ANGLE_180) ? h : w;
ppa_srm_oper_config_t oper_config = {
.in.buffer = in_buf,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = block_w,
.in.block_h = block_h,
.in.block_offset_x = 0,
.in.block_offset_y = 0,
.in.srm_cm = in_cm,
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = out_pic_w,
.out.pic_h = out_pic_h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.srm_cm = out_cm,
.rotation_angle = rotation,
.scale_x = scale_x,
.scale_y = scale_y,
.rgb_swap = 0,
.byte_swap = 0,
.mode = PPA_TRANS_MODE_BLOCKING,
};
ccomp_timer_start();
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config));
int64_t oper_time = ccomp_timer_stop();
printf("Time passed: %lld us\n", oper_time);
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
free(in_buf);
free(out_buf);
}
TEST_CASE("ppa_blend_performance", "[PPA][ignore]")
{
const uint32_t w = 1280;
const uint32_t h = 720;
const uint32_t block_w = w;
const uint32_t block_h = h;
const ppa_blend_color_mode_t in_bg_cm = PPA_BLEND_COLOR_MODE_ARGB8888;
const ppa_blend_color_mode_t in_fg_cm = PPA_BLEND_COLOR_MODE_ARGB8888;
const ppa_blend_color_mode_t out_cm = PPA_BLEND_COLOR_MODE_ARGB8888;
color_space_pixel_format_t in_bg_pixel_format = {
.color_type_id = in_bg_cm,
};
color_space_pixel_format_t in_fg_pixel_format = {
.color_type_id = in_fg_cm,
};
color_space_pixel_format_t out_pixel_format = {
.color_type_id = out_cm,
};
uint32_t in_bg_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8;
uint32_t in_fg_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8;
uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64);
uint8_t *out_buf = heap_caps_aligned_calloc(64, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(out_buf);
uint8_t *in_bg_buf = heap_caps_aligned_calloc(64, in_bg_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(in_bg_buf);
uint8_t *in_fg_buf = heap_caps_aligned_calloc(64, in_fg_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(in_fg_buf);
uint8_t *ptr = in_bg_buf;
for (int x = 0; x < in_bg_buf_size; x++) {
ptr[x] = x & 0x55;
}
ptr = in_fg_buf;
for (int x = 0; x < in_fg_buf_size; x++) {
ptr[x] = x & 0xAA;
}
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_BLEND,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_blend_oper_config_t oper_config = {
.in_bg.buffer = in_bg_buf,
.in_bg.pic_w = w,
.in_bg.pic_h = h,
.in_bg.block_w = block_w,
.in_bg.block_h = block_h,
.in_bg.block_offset_x = 0,
.in_bg.block_offset_y = 0,
.in_bg.blend_cm = in_bg_cm,
.in_fg.buffer = in_fg_buf,
.in_fg.pic_w = w,
.in_fg.pic_h = h,
.in_fg.block_w = block_w,
.in_fg.block_h = block_h,
.in_fg.block_offset_x = 0,
.in_fg.block_offset_y = 0,
.in_fg.blend_cm = in_fg_cm,
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.blend_cm = out_cm,
.bg_ck_en = false,
.fg_ck_en = false,
.mode = PPA_TRANS_MODE_BLOCKING,
};
ccomp_timer_start();
TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &oper_config));
int64_t oper_time = ccomp_timer_stop();
printf("Time passed: %lld us\n", oper_time);
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
free(in_bg_buf);
free(in_fg_buf);
free(out_buf);
}
TEST_CASE("ppa_fill_performance", "[PPA][ignore]")
{
const uint32_t w = 1280;
const uint32_t h = 720;
const uint32_t block_w = 800;
const uint32_t block_h = 480;
const ppa_fill_color_mode_t out_cm = PPA_FILL_COLOR_MODE_RGB565;
color_space_pixel_format_t out_pixel_format = {
.color_type_id = out_cm,
};
uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64);
uint8_t *out_buf = heap_caps_aligned_calloc(64, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(out_buf);
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_FILL,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_fill_oper_config_t oper_config = {
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.fill_cm = out_cm,
.fill_block_w = block_w,
.fill_block_h = block_h,
.fill_argb_color = {
.val = 0xFF00FFFF,
},
.mode = PPA_TRANS_MODE_BLOCKING,
};
ccomp_timer_start();
TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config));
int64_t oper_time = ccomp_timer_stop();
printf("Time passed: %lld us\n", oper_time);
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
free(out_buf);
}

View 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()

View 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

View File

@ -0,0 +1,3 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_IDF_EXPERIMENTAL_FEATURES=y

View File

@ -0,0 +1,3 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_HEX=y
CONFIG_SPIRAM_SPEED_200M=y

View File

@ -230,15 +230,15 @@ static inline void ppa_ll_srm_set_tx_color_mode(ppa_dev_t *dev, ppa_srm_color_mo
* @brief Set YUV to RGB protocol when PPA SRM RX pixel color space is YUV
*
* @param dev Peripheral instance address
* @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t
* @param std One of the RGB-YUV conversion standards in ppa_color_conv_std_rgb_yuv_t
*/
static inline void ppa_ll_srm_set_rx_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std)
static inline void ppa_ll_srm_set_rx_yuv2rgb_std(ppa_dev_t *dev, ppa_color_conv_std_rgb_yuv_t std)
{
switch (std) {
case COLOR_CONV_STD_RGB_YUV_BT601:
case PPA_COLOR_CONV_STD_RGB_YUV_BT601:
dev->sr_color_mode.yuv2rgb_protocol = 0;
break;
case COLOR_CONV_STD_RGB_YUV_BT709:
case PPA_COLOR_CONV_STD_RGB_YUV_BT709:
dev->sr_color_mode.yuv2rgb_protocol = 1;
break;
default:
@ -251,15 +251,15 @@ static inline void ppa_ll_srm_set_rx_yuv2rgb_std(ppa_dev_t *dev, color_conv_std_
* @brief Set RGB to YUV protocol when PPA SRM TX pixel color space is YUV
*
* @param dev Peripheral instance address
* @param std One of the RGB-YUV conversion standards in color_conv_std_rgb_yuv_t
* @param std One of the RGB-YUV conversion standards in ppa_color_conv_std_rgb_yuv_t
*/
static inline void ppa_ll_srm_set_tx_rgb2yuv_std(ppa_dev_t *dev, color_conv_std_rgb_yuv_t std)
static inline void ppa_ll_srm_set_tx_rgb2yuv_std(ppa_dev_t *dev, ppa_color_conv_std_rgb_yuv_t std)
{
switch (std) {
case COLOR_CONV_STD_RGB_YUV_BT601:
case PPA_COLOR_CONV_STD_RGB_YUV_BT601:
dev->sr_color_mode.rgb2yuv_protocol = 0;
break;
case COLOR_CONV_STD_RGB_YUV_BT709:
case PPA_COLOR_CONV_STD_RGB_YUV_BT709:
dev->sr_color_mode.rgb2yuv_protocol = 1;
break;
default:
@ -272,15 +272,15 @@ static inline void ppa_ll_srm_set_tx_rgb2yuv_std(ppa_dev_t *dev, color_conv_std_
* @brief Set PPA SRM YUV input range
*
* @param dev Peripheral instance address
* @param range One of color range options in color_range_t
* @param range One of color range options in ppa_color_range_t
*/
static inline void ppa_ll_srm_set_rx_yuv_range(ppa_dev_t *dev, color_range_t range)
static inline void ppa_ll_srm_set_rx_yuv_range(ppa_dev_t *dev, ppa_color_range_t range)
{
switch (range) {
case COLOR_RANGE_LIMIT:
case PPA_COLOR_RANGE_LIMIT:
dev->sr_color_mode.yuv_rx_range = 0;
break;
case COLOR_RANGE_FULL:
case PPA_COLOR_RANGE_FULL:
dev->sr_color_mode.yuv_rx_range = 1;
break;
default:
@ -293,15 +293,15 @@ static inline void ppa_ll_srm_set_rx_yuv_range(ppa_dev_t *dev, color_range_t ran
* @brief Set PPA SRM YUV output range
*
* @param dev Peripheral instance address
* @param range One of color range options in color_range_t
* @param range One of color range options in ppa_color_range_t
*/
static inline void ppa_ll_srm_set_tx_yuv_range(ppa_dev_t *dev, color_range_t range)
static inline void ppa_ll_srm_set_tx_yuv_range(ppa_dev_t *dev, ppa_color_range_t range)
{
switch (range) {
case COLOR_RANGE_LIMIT:
case PPA_COLOR_RANGE_LIMIT:
dev->sr_color_mode.yuv_tx_range = 0;
break;
case COLOR_RANGE_FULL:
case PPA_COLOR_RANGE_FULL:
dev->sr_color_mode.yuv_tx_range = 1;
break;
default:
@ -643,10 +643,10 @@ static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_alpha_
* @param hb The horizontal width of image block that would be filled in fix pixel filling mode. The unit is pixel.
* @param vb The vertical height of image block that would be filled in fix pixel filling mode. The unit is pixel.
*/
static inline void ppa_ll_blend_configure_filling_block(ppa_dev_t *dev, uint32_t data, uint32_t hb, uint32_t vb)
static inline void ppa_ll_blend_configure_filling_block(ppa_dev_t *dev, color_pixel_argb8888_data_t *data, uint32_t hb, uint32_t vb)
{
HAL_ASSERT(hb <= PPA_BLEND_HB_V && vb <= PPA_BLEND_VB_V);
dev->blend_fix_pixel.blend_tx_fix_pixel = data;
dev->blend_fix_pixel.blend_tx_fix_pixel = data->val;
dev->blend_tx_size.blend_hb = hb;
dev->blend_tx_size.blend_vb = vb;
}
@ -657,9 +657,11 @@ static inline void ppa_ll_blend_configure_filling_block(ppa_dev_t *dev, uint32_t
* @param dev Peripheral instance address
* @param rgb RGB color for A4/A8 mode in RGB888 format
*/
static inline void ppa_ll_blend_set_rx_fg_fix_rgb(ppa_dev_t *dev, uint32_t rgb)
static inline void ppa_ll_blend_set_rx_fg_fix_rgb(ppa_dev_t *dev, color_pixel_rgb888_data_t *rgb)
{
dev->blend_rgb.val = rgb;
dev->blend_rgb.blend1_rx_b = rgb->b;
dev->blend_rgb.blend1_rx_g = rgb->g;
dev->blend_rgb.blend1_rx_r = rgb->r;
}
/*

View File

@ -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

View File

@ -81,6 +81,22 @@ typedef enum {
If input format does not contain alpha info, A' = 0, i.e. a layer with 0% opacity. */
} ppa_alpha_update_mode_t;
/**
* @brief Enumeration of PPA supported color conversion standard between RGB and YUV (determines the YUV<->RGB conversion equation)
*/
typedef enum {
PPA_COLOR_CONV_STD_RGB_YUV_BT601 = COLOR_CONV_STD_RGB_YUV_BT601, /*!< YUV<->RGB conversion standard: BT.601 */
PPA_COLOR_CONV_STD_RGB_YUV_BT709 = COLOR_CONV_STD_RGB_YUV_BT709, /*!< YUV<->RGB conversion standard: BT.709 */
} ppa_color_conv_std_rgb_yuv_t;
/**
* @brief Enumeration of PPA supported color range (determines the YUV<->RGB conversion equation)
*/
typedef enum {
PPA_COLOR_RANGE_LIMIT = COLOR_RANGE_LIMIT, /*!< Limited color range, 16 is the darkest black and 235 is the brightest white */
PPA_COLOR_RANGE_FULL = COLOR_RANGE_FULL, /*!< Full color range, 0 is the darkest black and 255 is the brightest white */
} ppa_color_range_t;
#ifdef __cplusplus
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -176,6 +176,8 @@ SPI_SLAVE_HD_DOCS = ['api-reference/peripherals/spi_slave_hd.rst']
JPEG_DOCS = ['api-reference/peripherals/jpeg.rst']
PPA_DOCS = ['api-reference/peripherals/ppa.rst']
QEMU_DOCS = ['api-guides/tools/qemu.rst']
ESP32_DOCS = ['api-reference/system/himem.rst',
@ -278,6 +280,7 @@ conditional_include_dict = {'SOC_BT_SUPPORTED':BT_DOCS,
'SOC_SPI_SUPPORT_SLAVE_HD_VER2':SPI_SLAVE_HD_DOCS,
'SOC_WIFI_NAN_SUPPORT':NAN_DOCS,
'SOC_JPEG_CODEC_SUPPORTED':JPEG_DOCS,
'SOC_PPA_SUPPORTED':PPA_DOCS,
'SOC_GP_LDO_SUPPORTED':LDO_DOCS,
'esp32':ESP32_DOCS,
'esp32s2':ESP32S2_DOCS,

View File

@ -17,12 +17,14 @@ INPUT += \
$(PROJECT_PATH)/components/esp_driver_cam/include/esp_cam_ctlr_types.h \
$(PROJECT_PATH)/components/esp_driver_cam/csi/include/esp_cam_ctlr_csi.h \
$(PROJECT_PATH)/components/hal/include/hal/jpeg_types.h \
$(PROJECT_PATH)/components/hal/include/hal/ppa_types.h \
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_types.h \
$(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp.h \
$(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_types.h \
$(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_af.h \
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_decode.h \
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_encode.h \
$(PROJECT_PATH)/components/esp_driver_ppa/include/driver/ppa.h \
$(PROJECT_PATH)/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/adc_channel.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/clk_tree_defs.h \

View File

@ -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

View 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`` or ``PPA_BLEND_COLOR_MODE_L4``, then its ``block_w`` and ``block_offset_x`` fields must be even.
Fill
~~~~
Call :cpp:func:`ppa_do_fill` to fill a target block inside a picture.
:cpp:type:`ppa_trans_mode_t` is a field configurable to all the PPA operation APIs. It decides whether you want the call to the PPA operation API to block until the transaction finishes or to return immediately after the transaction is pushed to the internal queue.
.. _ppa-thread-safety:
Thread Safety
^^^^^^^^^^^^^
The PPA driver has guaranteed the thread safety of calling the PPA operation APIs in all following situations:
.. list::
- Among clients of different types in one task
- Among clients of same type in different tasks
- Among clients of different types in different tasks
.. _ppa-performance-overview:
Performance Overview
^^^^^^^^^^^^^^^^^^^^
The PPA operations are acted on the target block of an input picture. Therefore, the time it takes to complete a PPA transaction is proportional to the amount of the data of the block. The size of the entire picture has no influence on the performance. More importantly, the PPA performance highly relies on the PSRAM bandwidth if the pictures are located in the PSRAM section. When there are quite a few peripherals reading and writing to the PSRAM at the same time, the performance of PPA operation will be greatly reduced.
API Reference
-------------
.. include-build-file:: inc/ppa.inc
.. include-build-file:: inc/ppa_types.inc

View File

@ -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

View File

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/peripherals/ppa.rst