mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
fix(dma2d): add a dequeue mechanism for dma2d driver
JPEG driver should be able to either dequeue a pending transaction from a 2D-DMA pool or force end a processing transaction.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -152,6 +152,29 @@ typedef struct {
|
||||
*/
|
||||
esp_err_t dma2d_enqueue(dma2d_pool_handle_t dma2d_pool, const dma2d_trans_config_t *trans_desc, dma2d_trans_t *trans_placeholder);
|
||||
|
||||
/**
|
||||
* @brief Dequeue a pending 2D-DMA transaction from a 2D-DMA pool
|
||||
*
|
||||
* Remove a transaction that was previously enqueued by `dma2d_enqueue` but has not yet been picked up for
|
||||
* processing (i.e. it is still waiting in the pool's pending queue). This is useful when the upper driver wants
|
||||
* to cancel a transaction that has not started yet.
|
||||
*
|
||||
* @note This API only operates on transactions that are still pending. To end a transaction that is already
|
||||
* in-flight (its channels have been acquired), use `dma2d_force_end` instead.
|
||||
*
|
||||
* @note After this function returns ESP_OK, the upper driver can safely free the transaction placeholder, as the
|
||||
* `on_job_picked` callback of the transaction will no longer be called.
|
||||
*
|
||||
* @param[in] dma2d_pool 2D-DMA pool handle, allocated by `dma2d_acquire_pool`
|
||||
* @param[in] trans Pointer to the 2D-DMA transaction context
|
||||
* @return
|
||||
* - ESP_OK: Dequeue the pending 2D-DMA transaction successfully
|
||||
* - ESP_ERR_INVALID_ARG: Dequeue failed because of invalid argument
|
||||
* - ESP_ERR_NOT_FOUND: The transaction is not in the pending queue, because it has already been picked up
|
||||
* for processing or it was never enqueued to this pool
|
||||
*/
|
||||
esp_err_t dma2d_dequeue(dma2d_pool_handle_t dma2d_pool, dma2d_trans_t *trans);
|
||||
|
||||
/**
|
||||
* @brief Force end an in-flight 2D-DMA transaction
|
||||
*
|
||||
|
||||
@@ -215,6 +215,7 @@ static bool free_up_channels(dma2d_group_t *group, dma2d_rx_channel_t *rx_chan)
|
||||
// 2. Check if next pending transaction in the tailq can start
|
||||
bool channels_found = false;
|
||||
const dma2d_trans_config_t *next_trans = NULL;
|
||||
uint32_t total_channel_num = 0;
|
||||
dma2d_trans_channel_info_t channel_handle_array[DMA2D_MAX_CHANNEL_NUM_PER_TRANSACTION];
|
||||
|
||||
esp_os_enter_critical_safe(&group->spinlock);
|
||||
@@ -234,14 +235,9 @@ static bool free_up_channels(dma2d_group_t *group, dma2d_rx_channel_t *rx_chan)
|
||||
|
||||
if (channels_found) {
|
||||
TAILQ_REMOVE(&group->pending_trans_tailq, next_trans_elm, entry);
|
||||
}
|
||||
esp_os_exit_critical_safe(&group->spinlock);
|
||||
|
||||
if (channels_found) {
|
||||
// If the transaction can be processed, let consumer handle the transaction
|
||||
uint32_t total_channel_num = next_trans->tx_channel_num + next_trans->rx_channel_num;
|
||||
// Store the acquired rx_chan into trans_elm (dma2d_trans_t) in case upper driver later need it to call `dma2d_force_end`
|
||||
// Upper driver controls the life cycle of trans_elm
|
||||
total_channel_num = next_trans->tx_channel_num + next_trans->rx_channel_num;
|
||||
for (int i = 0; i < total_channel_num; i++) {
|
||||
if (channel_handle_array[i].dir == DMA2D_CHANNEL_DIRECTION_RX) {
|
||||
next_trans_elm->rx_chan = channel_handle_array[i].chan;
|
||||
@@ -249,6 +245,11 @@ static bool free_up_channels(dma2d_group_t *group, dma2d_rx_channel_t *rx_chan)
|
||||
// Also save the transaction pointer
|
||||
channel_handle_array[i].chan->status.transaction = next_trans_elm;
|
||||
}
|
||||
}
|
||||
esp_os_exit_critical_safe(&group->spinlock);
|
||||
|
||||
if (channels_found) {
|
||||
// If the transaction can be processed, let consumer handle the transaction
|
||||
need_yield |= next_trans->on_job_picked(total_channel_num, channel_handle_array, next_trans->user_config);
|
||||
}
|
||||
return need_yield;
|
||||
@@ -1035,6 +1036,32 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_dequeue(dma2d_pool_handle_t dma2d_pool, dma2d_trans_t *trans)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dma2d_pool && trans, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
dma2d_group_t *dma2d_group = dma2d_pool;
|
||||
|
||||
bool found = false;
|
||||
esp_os_enter_critical(&dma2d_group->spinlock);
|
||||
// The given transaction may have already been picked up (and thus removed from the queue) or may never
|
||||
// have been enqueued. Removing such an element directly would corrupt the queue, so search the queue
|
||||
// first and only remove it if it is genuinely still pending.
|
||||
dma2d_trans_t *trans_elm;
|
||||
TAILQ_FOREACH(trans_elm, &dma2d_group->pending_trans_tailq, entry) {
|
||||
if (trans_elm == trans) {
|
||||
// Safe to remove inside the loop because we break out immediately and never dereference the invalidated link pointer afterwards
|
||||
TAILQ_REMOVE(&dma2d_group->pending_trans_tailq, trans, entry);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
esp_os_exit_critical(&dma2d_group->spinlock);
|
||||
|
||||
// ESP_ERR_NOT_FOUND indicates the transaction is no longer (or was never) pending
|
||||
// i.e. it has already been picked up for processing or it does not exist in this pool's queue
|
||||
return found ? ESP_OK : ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_force_end(dma2d_trans_t *trans, bool *need_yield)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE_ISR(trans && trans->rx_chan && need_yield, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
@@ -368,7 +368,14 @@ esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_
|
||||
return ESP_OK;
|
||||
|
||||
err1:
|
||||
dma2d_force_end(decoder_engine->trans_desc, &need_yield);
|
||||
// The transaction may still be pending in the pool queue (never picked up), so try to dequeue it first.
|
||||
// Dequeuing is atomic against the pick that turns a pending transaction into an in-flight one, so if the
|
||||
// transaction is no longer in the queue, it is already in-flight and we force end it instead.
|
||||
if (dma2d_dequeue(decoder_engine->dma2d_group_handle, decoder_engine->trans_desc) != ESP_OK) {
|
||||
if (dma2d_force_end(decoder_engine->trans_desc, &need_yield) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to end the transaction, it is neither pending nor in-flight");
|
||||
}
|
||||
}
|
||||
err2:
|
||||
xSemaphoreGive(decoder_engine->codec_base->codec_mutex);
|
||||
#if CONFIG_PM_ENABLE
|
||||
|
||||
@@ -334,7 +334,14 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_
|
||||
return ESP_OK;
|
||||
|
||||
err1:
|
||||
dma2d_force_end(encoder_engine->trans_desc, &need_yield);
|
||||
// The transaction may still be pending in the pool queue (never picked up), so try to dequeue it first.
|
||||
// Dequeuing is atomic against the pick that turns a pending transaction into an in-flight one, so if the
|
||||
// transaction is no longer in the queue, it is already in-flight and we force end it instead.
|
||||
if (dma2d_dequeue(encoder_engine->dma2d_group_handle, encoder_engine->trans_desc) != ESP_OK) {
|
||||
if (dma2d_force_end(encoder_engine->trans_desc, &need_yield) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to end the transaction, it is neither pending nor in-flight");
|
||||
}
|
||||
}
|
||||
err2:
|
||||
xSemaphoreGive(encoder_engine->codec_base->codec_mutex);
|
||||
#if CONFIG_PM_ENABLE
|
||||
|
||||
Reference in New Issue
Block a user