diff --git a/components/esp_driver_jpeg/CMakeLists.txt b/components/esp_driver_jpeg/CMakeLists.txt index f84c1614d3..f11b56696d 100644 --- a/components/esp_driver_jpeg/CMakeLists.txt +++ b/components/esp_driver_jpeg/CMakeLists.txt @@ -26,7 +26,7 @@ endif() if(${target} STREQUAL "linux") set(priv_requires "") else() - set(priv_requires esp_mm esp_pm) + set(priv_requires esp_mm esp_pm esp_psram) endif() idf_component_register(SRCS ${srcs} diff --git a/components/esp_driver_jpeg/jpeg_encode.c b/components/esp_driver_jpeg/jpeg_encode.c index 9a6cb6f364..9e9ce47e62 100644 --- a/components/esp_driver_jpeg/jpeg_encode.c +++ b/components/esp_driver_jpeg/jpeg_encode.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -261,6 +261,12 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_ goto err1; } + if (s_rcv_event.dma_evt & JPEG_DMA2D_RX_DESC_EMPTY) { + ESP_LOGE(TAG, "Due to image quality issues, the generated image is larger than the buffer provided by the user. You can increase the buffer or ignore this information"); + ret = ESP_ERR_INVALID_STATE; + goto err2; // dma2d will freeup this channel in this event, so goto err2 + } + if (s_rcv_event.dma_evt & JPEG_DMA2D_RX_EOF) { ESP_GOTO_ON_ERROR(esp_cache_msync((void*)encoder_engine->rxlink, encoder_engine->dma_desc_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C), err1, TAG, "sync memory to cache failed"); compressed_size = s_dma_desc_get_len(encoder_engine->rxlink); @@ -367,6 +373,19 @@ static bool s_jpeg_rx_eof(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t return higher_priority_task_awoken; } +static bool s_jpeg_rx_desc_empty(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) +{ + jpeg_encoder_handle_t encoder_engine = (jpeg_encoder_handle_t) user_data; + portBASE_TYPE higher_priority_task_awoken = pdFALSE; + jpeg_enc_dma2d_evt_t s_event = { + .dma_evt = JPEG_DMA2D_RX_DESC_EMPTY, + .encoder_status = 0, + }; + xQueueSendFromISR(encoder_engine->evt_queue, &s_event, &higher_priority_task_awoken); + + return higher_priority_task_awoken; +} + static void jpeg_enc_config_dma_trans_ability(jpeg_encoder_handle_t encoder_engine) { // set transfer ability @@ -442,6 +461,7 @@ static bool s_jpeg_enc_transaction_on_job_picked(uint32_t channel_num, const dma static dma2d_rx_event_callbacks_t jpeg_dec_cbs = { .on_recv_eof = s_jpeg_rx_eof, + .on_desc_empty = s_jpeg_rx_desc_empty, }; dma2d_register_rx_event_callbacks(rx_chan, &jpeg_dec_cbs, encoder_engine); diff --git a/components/esp_driver_jpeg/jpeg_private.h b/components/esp_driver_jpeg/jpeg_private.h index 669eeb9bb6..aa409ff837 100644 --- a/components/esp_driver_jpeg/jpeg_private.h +++ b/components/esp_driver_jpeg/jpeg_private.h @@ -118,6 +118,7 @@ typedef enum { JPEG_DMA2D_RX_EOF = BIT(0), // DMA2D rx eof event JPEG_DMA2D_RX_DONE = BIT(1), // DMA2D rx done event JPEG_DMA2D_TX_DONE = BIT(2), // DMA2D tx done event + JPEG_DMA2D_RX_DESC_EMPTY = BIT(3), // DMA2D rx empty event } jpeg_dma2d_evt_enum_t; typedef struct { diff --git a/components/esp_hw_support/dma/dma2d.c b/components/esp_hw_support/dma/dma2d.c index 1d111c0326..86666610a1 100644 --- a/components/esp_hw_support/dma/dma2d.c +++ b/components/esp_hw_support/dma/dma2d.c @@ -298,8 +298,14 @@ static NOINLINE_ATTR bool _dma2d_default_rx_isr(dma2d_group_t *group, int channe } } + if (intr_status & DMA2D_LL_EVENT_RX_DESC_EMPTY) { + if (rx_chan->on_desc_empty) { + need_yield |= rx_chan->on_desc_empty(&rx_chan->base, &edata, user_data); + } + } + // If last transaction completes (regardless success or not), free the channels - if (intr_status & (DMA2D_LL_EVENT_RX_SUC_EOF | DMA2D_LL_EVENT_RX_ERR_EOF | DMA2D_LL_EVENT_RX_DESC_ERROR)) { + if (intr_status & (DMA2D_LL_EVENT_RX_SUC_EOF | DMA2D_LL_EVENT_RX_ERR_EOF | DMA2D_LL_EVENT_RX_DESC_ERROR | DMA2D_LL_EVENT_RX_DESC_EMPTY)) { if (!(intr_status & DMA2D_LL_EVENT_RX_ERR_EOF)) { assert(dma2d_ll_rx_is_fsm_idle(group->hal.dev, channel_id)); } @@ -688,6 +694,10 @@ esp_err_t dma2d_register_rx_event_callbacks(dma2d_channel_handle_t dma2d_chan, d rx_chan->on_desc_done = cbs->on_desc_done; mask |= DMA2D_LL_EVENT_RX_DONE; } + if (cbs->on_desc_empty) { + rx_chan->on_desc_empty = cbs->on_desc_empty; + mask |= DMA2D_LL_EVENT_RX_DESC_EMPTY; + } rx_chan->user_data = user_data; dma2d_ll_rx_enable_interrupt(group->hal.dev, rx_chan->base.channel_id, mask, true); diff --git a/components/esp_hw_support/dma/dma2d_priv.h b/components/esp_hw_support/dma/dma2d_priv.h index 1e555dc730..dcf46bf328 100644 --- a/components/esp_hw_support/dma/dma2d_priv.h +++ b/components/esp_hw_support/dma/dma2d_priv.h @@ -86,6 +86,7 @@ struct dma2d_rx_channel_t { void *user_data; // User registered DMA event data dma2d_event_callback_t on_recv_eof; // RX EOF event callback dma2d_event_callback_t on_desc_done; // RX DONE event callback + dma2d_event_callback_t on_desc_empty; // RX desc empty callback, trigger when buffer on dma is not sufficient. uint32_t bundled_tx_channel_mask; // Bit mask indicating the TX channels together with the RX channel to do the transaction }; diff --git a/components/esp_hw_support/dma/include/esp_private/dma2d.h b/components/esp_hw_support/dma/include/esp_private/dma2d.h index 4524ca95c6..10c964d210 100644 --- a/components/esp_hw_support/dma/include/esp_private/dma2d.h +++ b/components/esp_hw_support/dma/include/esp_private/dma2d.h @@ -344,6 +344,8 @@ typedef struct { to be still in the middle of a complete transaction (partially done), you are allowed to configure 2D-DMA channel hardware/descriptor in this callback, and let the channels start running again */ + dma2d_event_callback_t on_desc_empty; /*!< Invoked when the buffer size pointed to by the received linked list + descriptor is less than the length of the data to be received */ } dma2d_rx_event_callbacks_t; /**