From abc2971d95ba77273a8bcf337c376cd34113ad6f Mon Sep 17 00:00:00 2001 From: Cao Sen Miao Date: Wed, 28 Feb 2024 15:11:32 +0800 Subject: [PATCH] feat(jpeg): Add basic jpeg decoder support on esp32p4 --- components/esp_driver_jpeg/CMakeLists.txt | 21 + components/esp_driver_jpeg/Kconfig | 12 + .../include/driver/jpeg_decode.h | 123 ++++ .../include/driver/jpeg_types.h | 48 ++ components/esp_driver_jpeg/jpeg_common.c | 106 +++ components/esp_driver_jpeg/jpeg_decode.c | 676 ++++++++++++++++++ components/esp_driver_jpeg/jpeg_param.c | 47 ++ .../esp_driver_jpeg/jpeg_parse_marker.c | 187 +++++ components/esp_driver_jpeg/jpeg_private.h | 143 ++++ .../esp_driver_jpeg/private/jpeg_param.h | 43 ++ .../private/jpeg_parse_marker.h | 121 ++++ components/esp_lcd/include/esp_lcd_types.h | 7 +- components/hal/CMakeLists.txt | 4 + components/hal/esp32p4/include/hal/jpeg_ll.h | 10 +- components/hal/include/hal/color_types.h | 12 + components/hal/include/hal/jpeg_defs.h | 78 ++ components/hal/include/hal/jpeg_hal.h | 64 +- components/hal/include/hal/jpeg_types.h | 31 +- components/hal/jpeg_hal.c | 80 ++- .../esp32p4/include/soc/Kconfig.soc_caps.in | 8 + .../soc/esp32p4/include/soc/jpeg_struct.h | 2 +- components/soc/esp32p4/include/soc/soc_caps.h | 5 + 22 files changed, 1814 insertions(+), 14 deletions(-) create mode 100644 components/esp_driver_jpeg/CMakeLists.txt create mode 100644 components/esp_driver_jpeg/Kconfig create mode 100644 components/esp_driver_jpeg/include/driver/jpeg_decode.h create mode 100644 components/esp_driver_jpeg/include/driver/jpeg_types.h create mode 100644 components/esp_driver_jpeg/jpeg_common.c create mode 100644 components/esp_driver_jpeg/jpeg_decode.c create mode 100644 components/esp_driver_jpeg/jpeg_param.c create mode 100644 components/esp_driver_jpeg/jpeg_parse_marker.c create mode 100644 components/esp_driver_jpeg/jpeg_private.h create mode 100644 components/esp_driver_jpeg/private/jpeg_param.h create mode 100644 components/esp_driver_jpeg/private/jpeg_parse_marker.h create mode 100644 components/hal/include/hal/jpeg_defs.h diff --git a/components/esp_driver_jpeg/CMakeLists.txt b/components/esp_driver_jpeg/CMakeLists.txt new file mode 100644 index 0000000000..8a5918d6c6 --- /dev/null +++ b/components/esp_driver_jpeg/CMakeLists.txt @@ -0,0 +1,21 @@ +set(srcs) +set(public_include "include") + +# JPEG related source files +if(CONFIG_SOC_JPEG_CODEC_SUPPORTED) + list(APPEND srcs + "jpeg_common.c" + "jpeg_param.c" + ) + if(CONFIG_SOC_JPEG_DECODE_SUPPORTED) + list(APPEND srcs + "jpeg_parse_marker.c" + "jpeg_decode.c" + ) + endif() +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${public_include} + PRIV_REQUIRES "esp_mm" + ) diff --git a/components/esp_driver_jpeg/Kconfig b/components/esp_driver_jpeg/Kconfig new file mode 100644 index 0000000000..4b9e5d36e7 --- /dev/null +++ b/components/esp_driver_jpeg/Kconfig @@ -0,0 +1,12 @@ +menu "ESP-Driver:JPEG-Codec Configurations" + depends on SOC_JPEG_CODEC_SUPPORTED + + config JPEG_ENABLE_DEBUG_LOG + bool "Enable debug log" + default n + help + Wether to enable the debug log message for JPEG driver. + Note that, this option only controls the JPEG driver log, won't affect other drivers. + Please also note, enable this option will make jpeg codec process speed much slower. + +endmenu diff --git a/components/esp_driver_jpeg/include/driver/jpeg_decode.h b/components/esp_driver_jpeg/include/driver/jpeg_decode.h new file mode 100644 index 0000000000..e7f2221e7f --- /dev/null +++ b/components/esp_driver_jpeg/include/driver/jpeg_decode.h @@ -0,0 +1,123 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "jpeg_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configuration parameters for a JPEG decoder image process. + */ +typedef struct { + jpeg_dec_output_format_t output_format; /*!< JPEG decoder output format */ + union { + jpeg_dec_rgb_element_order rgb_order; /*!< JPEG decoder output order */ + }; + jpeg_yuv_rgb_conv_std_t conv_std; /*!< JPEG decoder yuv->rgb standard */ +} jpeg_decode_cfg_t; + +/** + * @brief Configuration parameters for the JPEG decoder engine. + */ +typedef struct { + int intr_priority; /*!< JPEG interrupt priority, if set to 0, driver will select the default priority (1,2,3). */ +} jpeg_decode_engine_cfg_t; + +/** + * @brief Stucture for jpeg decode header + */ +typedef struct { + uint32_t width; /*!< Number of pixels in the horizontal direction */ + uint32_t height; /*!< Number of pixels in the vertical direction */ +} jpeg_decode_picture_info_t; + +/** + * @brief Acquire a JPEG decode engine with the specified configuration. + * + * This function acquires a JPEG decode engine with the specified configuration. The configuration + * parameters are provided through the `dec_eng_cfg` structure, and the resulting JPEG decoder handle + * is returned through the `ret_jpgd_handle` pointer. + * + * @param[in] dec_eng_cfg Pointer to the JPEG decode engine configuration. + * @param[out] ret_jpgd_handle Pointer to a variable that will receive the JPEG decoder handle. + * @return + * - ESP_OK: JPEG decoder initialized successfully. + * - ESP_ERR_INVALID_ARG: JPEG decoder initialization failed because of invalid argument. + * - ESP_ERR_NO_MEM: Create JPEG decoder failed because of out of memory. + */ +esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, jpeg_decoder_handle_t *ret_jpgd_handle); + +/** + * @brief Helper function for getting information about a JPEG image. + * + * This function analyzes the provided JPEG image data and retrieves information about the image, + * such as its width, height. The image data is specified by the `bit_stream` pointer and the `stream_size` parameter. + * The resulting image information is returned through the `picture_info` structure. + * + * @note This function doesn't depend on any jpeg hardware, it helps user to know jpeg information from jpeg header. For example, + * user can get picture width and height via this function and malloc a reasonable size buffer for jpeg engine process. + * + * @param[in] bit_stream Pointer to the buffer containing the JPEG image data. + * @param[in] stream_size Size of the JPEG image data in bytes. + * @param[out] picture_info Pointer to the structure that will receive the image information. + * @return + * - ESP_OK: JPEG decoder get jpg image header successfully. + * - ESP_ERR_INVALID_ARG: JPEG decoder get header info failed because of invalid argument. + */ +esp_err_t jpeg_decoder_get_info(const uint8_t *bit_stream, uint32_t stream_size, jpeg_decode_picture_info_t *picture_info); + +/** + * @brief Process a JPEG image with the specified decoder instance. + * + * This function processes the provided JPEG image data using the specified JPEG decoder instance. The input + * JPEG image data is specified by the `bit_stream` pointer and the `stream_size` parameter. The resulting + * decoded image data is written to the `decode_outbuf` buffer, and the length of the output image data is + * returned through the `out_size` pointer. + * + * @note Please make sure that the content of `bit_stream` pointer cannot be modified until this function returns. + * + * @param[in] decoder_engine Handle of the JPEG decoder instance to use for processing. + * @param[in] decode_cfg Config structure of decoder. + * @param[in] bit_stream Pointer to the buffer containing the input JPEG image data. + * @param[in] stream_size Size of the input JPEG image data in bytes. + * @param[out] decode_outbuf Pointer to the buffer that will receive the decoded image data. + * @param[out] out_size Pointer to a variable that will receive the length of the output image data. + * @return + * - ESP_OK: JPEG decoder process successfully. + * - ESP_ERR_INVALID_ARG: JPEG decoder process failed because of invalid argument. + */ +esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_decode_cfg_t *decode_cfg, const uint8_t *bit_stream, uint32_t stream_size, uint8_t *decode_outbuf, uint32_t *out_size); + +/** + * @brief Release resources used by a JPEG decoder instance. + * + * This function releases the resources used by the specified JPEG decoder instance. The decoder instance is + * specified by the `decoder_engine` parameter. + * + * @param decoder_engine Handle of the JPEG decoder instance to release resources for. + * @return + * - ESP_OK: Delete JPEG decoder successfully. + * - ESP_ERR_INVALID_ARG: Delete JPEG decoder failed because of invalid argument. + */ +esp_err_t jpeg_del_decoder_engine(jpeg_decoder_handle_t decoder_engine); + +/** + * @brief A helper function to allocate memory space for JPEG decoder. + * + * @param size The size of memory to allocate. + * @return Pointer to the allocated memory space, or NULL if allocation fails. + */ +void * jpeg_alloc_decoder_mem(size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_jpeg/include/driver/jpeg_types.h b/components/esp_driver_jpeg/include/driver/jpeg_types.h new file mode 100644 index 0000000000..0c9c72a7f6 --- /dev/null +++ b/components/esp_driver_jpeg/include/driver/jpeg_types.h @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "hal/color_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enumeration for jpeg output format. + */ +typedef enum { + JPEG_DECODE_OUT_FORMAT_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< output RGB888 format */ + JPEG_DECODE_OUT_FORMAT_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< output RGB565 format */ + JPEG_DECODE_OUT_FORMAT_GRAY = COLOR_TYPE_ID(COLOR_SPACE_GRAY, COLOR_PIXEL_GRAY8), /*!< output the gray picture */ +} jpeg_dec_output_format_t; + +/** + * @brief Enumeration for YUV and RGB conversion standard + */ +typedef enum { + JPEG_YUV_RGB_CONV_STD_BT601 = COLOR_CONV_STD_RGB_YUV_BT601, /*!< BT601 */ + JPEG_YUV_RGB_CONV_STD_BT709 = COLOR_CONV_STD_RGB_YUV_BT709, /*!< BT709 */ +} jpeg_yuv_rgb_conv_std_t; + +/** + * @brief Enumeration for jpeg big/small endian output. + */ +typedef enum { + JPEG_DEC_RGB_ELEMENT_ORDER_BGR = COLOR_RGB_ELEMENT_ORDER_BGR, /*!< Output the color component in small endian */ + JPEG_DEC_RGB_ELEMENT_ORDER_RGB = COLOR_RGB_ELEMENT_ORDER_RGB, /*!< Output the color component in big endian */ +} jpeg_dec_rgb_element_order; + +/** + * @brief Type of jpeg decoder handle + */ +typedef struct jpeg_decoder_t *jpeg_decoder_handle_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_jpeg/jpeg_common.c b/components/esp_driver_jpeg/jpeg_common.c new file mode 100644 index 0000000000..526e460b3c --- /dev/null +++ b/components/esp_driver_jpeg/jpeg_common.c @@ -0,0 +1,106 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_err.h" +#include "hal/jpeg_ll.h" +#include "esp_private/periph_ctrl.h" +#include "jpeg_private.h" +#include "hal/jpeg_hal.h" +#include "esp_memory_utils.h" +#include "driver/jpeg_types.h" +#include "sys/lock.h" +#include "sdkconfig.h" +#if CONFIG_JPEG_ENABLE_DEBUG_LOG +// The local log level must be defined before including esp_log.h +// Set the maximum log level for this source file +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#endif +#include "esp_log.h" +#include "esp_check.h" + +static const char *TAG = "jpeg.common"; + +typedef struct jpeg_platform_t { + _lock_t mutex; // platform level mutex lock. + jpeg_codec_handle_t jpeg_codec; // JPEG codec instances. + uint32_t count; // reference count used to protect group install/uninstall. +} jpeg_platform_t; + +static jpeg_platform_t s_jpeg_platform = {}; // singleton platform + +esp_err_t jpeg_acquire_codec_handle(jpeg_codec_handle_t *jpeg_new_codec) +{ +#if CONFIG_JPEG_ENABLE_DEBUG_LOG + esp_log_level_set(TAG, ESP_LOG_DEBUG); +#endif + esp_err_t ret = ESP_OK; + bool new_codec = false; + jpeg_codec_t *codec = NULL; + _lock_acquire(&s_jpeg_platform.mutex); + if (!s_jpeg_platform.jpeg_codec) { + new_codec = true; + codec = heap_caps_calloc(1, sizeof(jpeg_codec_t), JPEG_MEM_ALLOC_CAPS); + if (codec) { + s_jpeg_platform.jpeg_codec = codec; + codec->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + codec->codec_mux = xSemaphoreCreateBinaryWithCaps(JPEG_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(codec->codec_mux, ESP_ERR_NO_MEM, TAG, "No memory for codec mutex"); + xSemaphoreGive(codec->codec_mux); + // init the clock + PERIPH_RCC_ATOMIC() { + jpeg_ll_enable_bus_clock(true); + jpeg_ll_reset_module_register(); + } + + jpeg_hal_init(&codec->hal); + } else { + ESP_LOGE(TAG, "No more memory for acquiring JPEG codec module"); + ret = ESP_ERR_NO_MEM; + } + } + + if (codec) { + s_jpeg_platform.count++; + } + if (new_codec) { + ESP_LOGD(TAG, "new jpeg module has been acquired at (%p)", codec); + } + + *jpeg_new_codec = codec; + _lock_release(&s_jpeg_platform.mutex); + return ret; +} + +esp_err_t jpeg_release_codec_handle(jpeg_codec_handle_t jpeg_codec) +{ + bool do_deinitialize = false; + _lock_acquire(&s_jpeg_platform.mutex); + + if (s_jpeg_platform.jpeg_codec) { + s_jpeg_platform.count--; + if (s_jpeg_platform.count == 0) { + do_deinitialize = true; + s_jpeg_platform.jpeg_codec = NULL; + + if (jpeg_codec->codec_mux) { + vSemaphoreDeleteWithCaps(jpeg_codec->codec_mux); + jpeg_codec->codec_mux = NULL; + } + PERIPH_RCC_ATOMIC() { + jpeg_ll_enable_bus_clock(false); + } + free(jpeg_codec); + } + } + + if (do_deinitialize) { + ESP_LOGD(TAG, "delete codec"); + } + _lock_release(&s_jpeg_platform.mutex); + + return ESP_OK; +} diff --git a/components/esp_driver_jpeg/jpeg_decode.c b/components/esp_driver_jpeg/jpeg_decode.c new file mode 100644 index 0000000000..dff070dc53 --- /dev/null +++ b/components/esp_driver_jpeg/jpeg_decode.c @@ -0,0 +1,676 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_err.h" +#include "jpeg_private.h" +#include "private/jpeg_parse_marker.h" +#include "private/jpeg_param.h" +#include "driver/jpeg_decode.h" +#include "esp_heap_caps.h" +#include "esp_private/dma2d.h" +#include "hal/jpeg_ll.h" +#include "hal/cache_ll.h" +#include "hal/cache_hal.h" +#include "hal/jpeg_defs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "soc/interrupts.h" +#include "soc/dma2d_channel.h" +#include "soc/interrupts.h" +#include "esp_dma_utils.h" +#if CONFIG_JPEG_ENABLE_DEBUG_LOG +// The local log level must be defined before including esp_log.h +// Set the maximum log level for this source file +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#endif +#include "esp_log.h" +#include "esp_cache.h" +#include "esp_check.h" + +static const char *TAG = "jpeg.decoder"; + +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + +static void s_decoder_error_log_print(uint32_t status); +static esp_err_t jpeg_dec_config_dma_descriptor(jpeg_decoder_handle_t decoder_engine); +static esp_err_t jpeg_parse_marker(jpeg_decoder_handle_t decoder_engine, const uint8_t *in_buf, uint32_t inbuf_len); +static esp_err_t jpeg_parse_header_info_to_hw(jpeg_decoder_handle_t decoder_engine); +static bool jpeg_dec_transaction_on_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *users_config); + +static void jpeg_decoder_isr_handle_default(void *arg) +{ + jpeg_decoder_handle_t decoder_engine = (jpeg_decoder_handle_t) arg; + jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal; + portBASE_TYPE HPTaskAwoken = pdFALSE; + jpeg_dma2d_dec_evt_t dec_evt = { + .jpgd_status = 0, + .dma_evt = 0, + }; + uint32_t value = jpeg_ll_get_intr_status(hal->dev); + jpeg_ll_clear_intr_mask(hal->dev, value); + dec_evt.jpgd_status = value; + xQueueSendFromISR(decoder_engine->evt_handle, &dec_evt, &HPTaskAwoken); + + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, jpeg_decoder_handle_t *ret_jpgd_handle) +{ +#if CONFIG_JPEG_ENABLE_DEBUG_LOG + esp_log_level_set(TAG, ESP_LOG_DEBUG); +#endif + esp_err_t ret = ESP_OK; + jpeg_decoder_handle_t decoder_engine = NULL; + ESP_RETURN_ON_FALSE(dec_eng_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + decoder_engine = (jpeg_decoder_handle_t)heap_caps_calloc(1, sizeof(jpeg_decoder_t), JPEG_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_NO_MEM, TAG, "no memory for jpeg decode"); + + uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); + uint32_t alignment = cache_line_size; + size_t dma_desc_mem_size = ALIGN_UP(sizeof(dma2d_descriptor_t), cache_line_size); + + decoder_engine->rxlink = (dma2d_descriptor_t*)heap_caps_aligned_calloc(alignment, 1, dma_desc_mem_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | JPEG_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(decoder_engine->rxlink, ESP_ERR_NO_MEM, err, TAG, "no memory for jpeg decode rxlink"); + decoder_engine->txlink = (dma2d_descriptor_t*)heap_caps_aligned_calloc(alignment, 1, dma_desc_mem_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | JPEG_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(decoder_engine->txlink, ESP_ERR_NO_MEM, err, TAG, "no memory for jpeg decode txlink"); + decoder_engine->dma_desc_size = dma_desc_mem_size; + + decoder_engine->header_info = (jpeg_dec_header_info_t*)heap_caps_calloc(1, sizeof(jpeg_dec_header_info_t), JPEG_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(decoder_engine->header_info, ESP_ERR_NO_MEM, err, TAG, "no memory for picture info"); + + ESP_GOTO_ON_ERROR(jpeg_acquire_codec_handle(&decoder_engine->codec_base), err, TAG, "JPEG decode acquires codec handle failed"); + jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal; + + /// init jpeg interrupt. + portENTER_CRITICAL(&decoder_engine->codec_base->spinlock); + jpeg_ll_clear_intr_mask(hal->dev, JPEG_LL_DECODER_EVENT_INTR); + portEXIT_CRITICAL(&decoder_engine->codec_base->spinlock); + + if (dec_eng_cfg->intr_priority) { + ESP_RETURN_ON_FALSE(1 << (dec_eng_cfg->intr_priority) & JPEG_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", dec_eng_cfg->intr_priority); + } + int isr_flags = JPEG_INTR_ALLOC_FLAG; + if (dec_eng_cfg->intr_priority) { + isr_flags |= 1 << (dec_eng_cfg->intr_priority); + } + + ret = esp_intr_alloc_intrstatus(ETS_JPEG_INTR_SOURCE, isr_flags, (uint32_t)jpeg_ll_get_interrupt_status_reg(hal->dev), JPEG_LL_DECODER_EVENT_INTR, jpeg_decoder_isr_handle_default, decoder_engine, &decoder_engine->intr_handle); + ESP_GOTO_ON_ERROR(ret, err, TAG, "install jpeg decode interrupt failed"); + + portENTER_CRITICAL(&decoder_engine->codec_base->spinlock); + jpeg_ll_enable_intr_mask(hal->dev, JPEG_LL_DECODER_EVENT_INTR); + portEXIT_CRITICAL(&decoder_engine->codec_base->spinlock); + + // Initialize queue + decoder_engine->evt_handle = xQueueCreateWithCaps(2, sizeof(jpeg_dma2d_dec_evt_t), JPEG_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(decoder_engine->evt_handle, ESP_ERR_NO_MEM, err, TAG, "No memory for event queue"); + + dma2d_pool_config_t dma2d_client_config = { + .pool_id = 0, + }; + + ESP_GOTO_ON_ERROR(dma2d_acquire_pool(&dma2d_client_config, &decoder_engine->dma2d_group_handle), err, TAG, "dma2d client acquire failed"); + + decoder_engine->trans_desc = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, JPEG_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(decoder_engine->trans_desc, ESP_ERR_NO_MEM, err, TAG, "No memory for dma2d descriptor"); + + *ret_jpgd_handle = decoder_engine; + return ESP_OK; + +err: + if (decoder_engine) { + jpeg_del_decoder_engine(decoder_engine); + } + return ret; +} + +esp_err_t jpeg_decoder_get_info(const uint8_t *in_buf, uint32_t inbuf_len, jpeg_decode_picture_info_t *picture_info) +{ + ESP_RETURN_ON_FALSE(in_buf, ESP_ERR_INVALID_ARG, TAG, "jpeg decode input buffer is NULL"); + ESP_RETURN_ON_FALSE(inbuf_len != 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode input buffer length is 0"); + + jpeg_dec_header_info_t* header_info = (jpeg_dec_header_info_t*)heap_caps_calloc(1, sizeof(jpeg_dec_header_info_t), JPEG_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(header_info, ESP_ERR_NO_MEM, TAG, "no memory for picture info"); + header_info->buffer_offset = (uint8_t *)in_buf; + header_info->buffer_left = inbuf_len; + header_info->header_size = 0; + uint16_t height = 0; + uint16_t width = 0; + uint8_t thischar = 0; + uint8_t lastchar = 0; + + while (header_info->buffer_left) { + lastchar = thischar; + thischar = jpeg_get_bytes(header_info, 1); + uint16_t marker = (lastchar << 8 | thischar); + switch (marker) { + case JPEG_M_SOF0: + jpeg_get_bytes(header_info, 2); + jpeg_get_bytes(header_info, 1); + height = jpeg_get_bytes(header_info, 2); + width = jpeg_get_bytes(header_info, 2); + break; + } + // This function only used for get width and hight. So only read SOF marker is enough. + // Can be extended if picture information is extended. + if (marker == JPEG_M_SOF0) { + break; + } + } + + picture_info->height = height; + picture_info->width = width; + + free(header_info); + return ESP_OK; +} + +esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_decode_cfg_t *decode_cfg, const uint8_t *bit_stream, uint32_t stream_size, uint8_t *decode_outbuf, uint32_t *out_size) +{ + ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle is null"); + ESP_RETURN_ON_FALSE(decode_outbuf, ESP_ERR_INVALID_ARG, TAG, "jpeg decode picture buffer is null"); + ESP_RETURN_ON_FALSE(((uintptr_t)bit_stream % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode bit stream is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer"); + ESP_RETURN_ON_FALSE(((uintptr_t)decode_outbuf % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode decode_outbuf is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer"); + + esp_err_t ret = ESP_OK; + + xSemaphoreTake(decoder_engine->codec_base->codec_mux, portMAX_DELAY); + /* Reset queue */ + xQueueReset(decoder_engine->evt_handle); + + decoder_engine->output_format = decode_cfg->output_format; + decoder_engine->rgb_order = decode_cfg->rgb_order; + decoder_engine->conv_std = decode_cfg->conv_std; + + decoder_engine->decoded_buf = decode_outbuf; + + ESP_GOTO_ON_ERROR(jpeg_parse_marker(decoder_engine, bit_stream, stream_size), err, TAG, "jpeg parse marker failed"); + ESP_GOTO_ON_ERROR(jpeg_parse_header_info_to_hw(decoder_engine), err, TAG, "write header info to hw failed"); + ESP_GOTO_ON_ERROR(jpeg_dec_config_dma_descriptor(decoder_engine), err, TAG, "config dma descriptor failed"); + + dma2d_trans_config_t trans_desc = { + .tx_channel_num = 1, + .rx_channel_num = 1, + .channel_flags = DMA2D_CHANNEL_FUNCTION_FLAG_RX_REORDER, + .user_config = decoder_engine, + .on_job_picked = jpeg_dec_transaction_on_picked, + }; + + // Before 2DDMA starts. sync buffer from cache to psram + ret = esp_cache_msync((void*)decoder_engine->header_info->buffer_offset, decoder_engine->header_info->buffer_left, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + assert(ret == ESP_OK); + + // Before 2DDMA starts. invalid memory space of decoded buffer + ret = esp_cache_msync((void*)decoder_engine->decoded_buf, decoder_engine->header_info->process_h * decoder_engine->header_info->process_v * decoder_engine->pixel, ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + assert(ret == ESP_OK); + + ESP_GOTO_ON_ERROR(dma2d_enqueue(decoder_engine->dma2d_group_handle, &trans_desc, decoder_engine->trans_desc), err, TAG, "enqueue dma2d failed"); + bool need_yield; + // Blocking for JPEG decode transaction finishes. + while (1) { + jpeg_dma2d_dec_evt_t jpeg_dma2d_event; + BaseType_t ret = xQueueReceive(decoder_engine->evt_handle, &jpeg_dma2d_event, portMAX_DELAY); + if (ret == pdFALSE) { + ESP_LOGE(TAG, "jpeg-dma2d handle jpeg decode timeout"); + xSemaphoreGive(decoder_engine->codec_base->codec_mux); + return ESP_ERR_TIMEOUT; + } + + // Dealing with JPEG event + if (jpeg_dma2d_event.jpgd_status != 0) { + uint32_t status = jpeg_dma2d_event.jpgd_status; + s_decoder_error_log_print(status); + dma2d_force_end(decoder_engine->trans_desc, &need_yield); + xSemaphoreGive(decoder_engine->codec_base->codec_mux); + return ESP_ERR_INVALID_STATE; + } + + if (jpeg_dma2d_event.dma_evt & JPEG_DMA2D_RX_EOF) { + break; + } + } + + *out_size = decoder_engine->header_info->origin_h * decoder_engine->header_info->origin_v * decoder_engine->pixel; + + xSemaphoreGive(decoder_engine->codec_base->codec_mux); + return ESP_OK; + +err: + xSemaphoreGive(decoder_engine->codec_base->codec_mux); + return ret; +} + +esp_err_t jpeg_del_decoder_engine(jpeg_decoder_handle_t decoder_engine) +{ + ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle is null"); + ESP_RETURN_ON_ERROR(jpeg_release_codec_handle(decoder_engine->codec_base), TAG, "release codec failed"); + + if (decoder_engine) { + if (decoder_engine->rxlink) { + free(decoder_engine->rxlink); + } + if (decoder_engine->txlink) { + free(decoder_engine->txlink); + } + if (decoder_engine->header_info) { + free(decoder_engine->header_info); + } + if (decoder_engine->intr_handle) { + esp_intr_free(decoder_engine->intr_handle); + } + if (decoder_engine->evt_handle) { + vQueueDeleteWithCaps(decoder_engine->evt_handle); + } + if (decoder_engine->dma2d_group_handle) { + dma2d_release_pool(decoder_engine->dma2d_group_handle); + } + free(decoder_engine); + } + return ESP_OK; +} + +void * jpeg_alloc_decoder_mem(size_t size) +{ + return heap_caps_aligned_calloc(cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA), 1, size, JPEG_MEM_ALLOC_CAPS); +} + +/**************************************************************** + * DMA related functions + ****************************************************************/ + +static void cfg_desc(jpeg_decoder_handle_t decoder_engine, dma2d_descriptor_t *dsc, uint8_t en_2d, uint8_t mode, uint16_t vb, uint16_t hb, uint8_t eof, uint32_t pbyte, uint8_t owner, uint16_t va, uint16_t ha, uint8_t *buf, dma2d_descriptor_t *next_dsc) +{ + dsc->dma2d_en = en_2d; + dsc->mode = mode; + dsc->vb_size = vb; + dsc->hb_length = hb; + dsc->pbyte = pbyte; + dsc->suc_eof = eof; + dsc->owner = owner; + dsc->va_size = va; + dsc->ha_length = ha; + dsc->buffer = buf; + dsc->next = next_dsc; + esp_err_t ret = esp_cache_msync((void*)dsc, decoder_engine->dma_desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + assert(ret == ESP_OK); +} + +static esp_err_t jpeg_dec_config_dma_descriptor(jpeg_decoder_handle_t decoder_engine) +{ + ESP_LOGD(TAG, "Config 2DDMA parameter start"); + + jpeg_dec_format_hb_t best_hb_idx = 0; + color_space_pixel_format_t picture_format; + picture_format.color_type_id = decoder_engine->output_format; + decoder_engine->pixel = color_hal_pixel_format_get_bit_depth(picture_format) / 8; + switch (decoder_engine->output_format) { + case JPEG_DECODE_OUT_FORMAT_RGB888: + best_hb_idx = JPEG_DEC_RGB888_HB; + break; + case JPEG_DECODE_OUT_FORMAT_RGB565: + best_hb_idx = JPEG_DEC_RGB565_HB; + break; + case JPEG_DECODE_OUT_FORMAT_GRAY: + best_hb_idx = JPEG_DEC_GRAY_HB; + break; + default: + ESP_LOGE(TAG, "wrong, we don't support decode to such format."); + return ESP_ERR_NOT_SUPPORTED; + } + + uint32_t dma_hb = dec_hb_tbl[decoder_engine->sample_method][best_hb_idx]; + uint32_t dma_vb = decoder_engine->header_info->mcuy; + + // Configure tx link descriptor + cfg_desc(decoder_engine, decoder_engine->txlink, JPEG_DMA2D_2D_DISABLE, DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, decoder_engine->header_info->buffer_left & JPEG_DMA2D_MAX_SIZE, decoder_engine->header_info->buffer_left & JPEG_DMA2D_MAX_SIZE, JPEG_DMA2D_EOF_NOT_LAST, 1, DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA, (decoder_engine->header_info->buffer_left >> JPEG_DMA2D_1D_HIGH_14BIT), (decoder_engine->header_info->buffer_left >> JPEG_DMA2D_1D_HIGH_14BIT), decoder_engine->header_info->buffer_offset, NULL); + + // Configure rx link descriptor + cfg_desc(decoder_engine, decoder_engine->rxlink, JPEG_DMA2D_2D_ENABLE, DMA2D_DESCRIPTOR_BLOCK_RW_MODE_MULTIPLE, dma_vb, dma_hb, JPEG_DMA2D_EOF_NOT_LAST, dma2d_desc_pixel_format_to_pbyte_value(picture_format), DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA, decoder_engine->header_info->process_v, decoder_engine->header_info->process_h, decoder_engine->decoded_buf, NULL); + + return ESP_OK; +} + +static bool jpeg_rx_eof(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) +{ + portBASE_TYPE higher_priority_task_awoken = pdFALSE; + jpeg_dma2d_dec_evt_t dec_evt = { + .jpgd_status = 0, + .dma_evt = 0, + }; + jpeg_decoder_handle_t decoder_engine = (jpeg_decoder_handle_t) user_data; + dec_evt.dma_evt = JPEG_DMA2D_RX_EOF; + xQueueSendFromISR(decoder_engine->evt_handle, &dec_evt, &higher_priority_task_awoken); + + return higher_priority_task_awoken; +} + +static void jpeg_dec_config_dma_csc(jpeg_decoder_handle_t decoder_engine, dma2d_channel_handle_t rx_chan) +{ + + dma2d_scramble_order_t post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0; + dma2d_csc_rx_option_t rx_csc_option = DMA2D_CSC_RX_NONE; + + // Config output Endians + if (decoder_engine->rgb_order == JPEG_DEC_RGB_ELEMENT_ORDER_RGB) { + if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_RGB565) { + post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_0_1; + } else if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_RGB888) { + post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE0_1_2; + } + } + + // Configure color space conversion option. + if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_RGB565) { + if (decoder_engine->conv_std == JPEG_YUV_RGB_CONV_STD_BT601) { + rx_csc_option = DMA2D_CSC_RX_YUV420_TO_RGB565_601; + } else if (decoder_engine->conv_std == JPEG_YUV_RGB_CONV_STD_BT709) { + rx_csc_option = DMA2D_CSC_RX_YUV420_TO_RGB565_709; + } + } else if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_RGB888) { + if (decoder_engine->conv_std == JPEG_YUV_RGB_CONV_STD_BT601) { + rx_csc_option = DMA2D_CSC_RX_YUV420_TO_RGB888_601; + } else if (decoder_engine->conv_std == JPEG_YUV_RGB_CONV_STD_BT709) { + rx_csc_option = DMA2D_CSC_RX_YUV420_TO_RGB888_709; + } + } else if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_GRAY) { + rx_csc_option = DMA2D_CSC_RX_NONE; + } + + dma2d_csc_config_t rx_csc = { + .post_scramble = post_scramble, + .rx_csc_option = rx_csc_option, + }; + dma2d_configure_color_space_conversion(rx_chan, &rx_csc); +} + +static void jpeg_dec_config_dma_trans_ability(jpeg_decoder_handle_t decoder_engine) +{ + // set transfer ability + dma2d_transfer_ability_t transfer_ability_config_tx = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + }; + + dma2d_transfer_ability_t transfer_ability_config_rx = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + }; + + switch (decoder_engine->sample_method) { + case JPEG_DOWN_SAMPLING_YUV444: + transfer_ability_config_rx.mb_size = DMA2D_MACRO_BLOCK_SIZE_8_8; + break; + case JPEG_DOWN_SAMPLING_YUV422: + transfer_ability_config_rx.mb_size = DMA2D_MACRO_BLOCK_SIZE_8_16; + break; + case JPEG_DOWN_SAMPLING_YUV420: + transfer_ability_config_rx.mb_size = DMA2D_MACRO_BLOCK_SIZE_16_16; + break; + case JPEG_DOWN_SAMPLING_GRAY: + transfer_ability_config_rx.mb_size = DMA2D_MACRO_BLOCK_SIZE_8_8; + break; + default: + break; + } + + dma2d_set_transfer_ability(decoder_engine->dma2d_tx_channel, &transfer_ability_config_tx); + dma2d_set_transfer_ability(decoder_engine->dma2d_rx_channel, &transfer_ability_config_rx); +} + +static bool jpeg_dec_transaction_on_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *users_config) +{ + assert(channel_num == 2); + jpeg_decoder_handle_t decoder_engine = (jpeg_decoder_handle_t) users_config; + jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal; + ESP_LOGD(TAG, "2ddma transaction callbacks start"); + + uint32_t rx_idx, tx_idx; + if (dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_TX) { + rx_idx = 1; + tx_idx = 0; + } else { + rx_idx = 0; + tx_idx = 1; + } + dma2d_channel_handle_t tx_chan = dma2d_chans[tx_idx].chan; + dma2d_channel_handle_t rx_chan = dma2d_chans[rx_idx].chan; + + decoder_engine->dma2d_rx_channel = rx_chan; + decoder_engine->dma2d_tx_channel = tx_chan; + + // 2ddma connect + dma2d_trigger_t trig_periph = { + .periph = DMA2D_TRIG_PERIPH_JPEG_DECODER, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_JPEG_TX, + }; + dma2d_connect(tx_chan, &trig_periph); + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_JPEG_RX; + dma2d_connect(rx_chan, &trig_periph); + + jpeg_dec_config_dma_trans_ability(decoder_engine); + jpeg_dec_config_dma_csc(decoder_engine, rx_chan); + + dma2d_rx_event_callbacks_t jpeg_dec_cbs = { + .on_recv_eof = jpeg_rx_eof, + }; + + dma2d_register_rx_event_callbacks(rx_chan, &jpeg_dec_cbs, decoder_engine); + + dma2d_set_desc_addr(tx_chan, (intptr_t)decoder_engine->txlink); + dma2d_set_desc_addr(rx_chan, (intptr_t)decoder_engine->rxlink); + dma2d_start(tx_chan); + dma2d_start(rx_chan); + jpeg_ll_process_start(hal->dev); + + return false; +} + +static esp_err_t jpeg_parse_header_info_to_hw(jpeg_decoder_handle_t decoder_engine) +{ + ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle is null"); + jpeg_dec_header_info_t *header_info = decoder_engine->header_info; + jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal; + + for (int i = 0; i < header_info->qt_tbl_num; i++) { + dqt_func[i](hal->dev, header_info->qt_tbl[i]); + } + + jpeg_ll_set_picture_height(hal->dev, header_info->process_v); + jpeg_ll_set_picture_width(hal->dev, header_info->process_h); + jpeg_ll_set_decode_component_num(hal->dev, header_info->nf); + for (int i = 0; i < header_info->nf; i++) { + sof_func[i](hal->dev, header_info->ci[i], header_info->hi[i], header_info->vi[i], header_info->qtid[i]); + } + // If number of image component frame is 3, get the sampling method with sampling factor. + if (header_info->nf == 3) { + switch (header_info->hivi[0]) { + case 0x11: + decoder_engine->sample_method = JPEG_DOWN_SAMPLING_YUV444; + break; + case 0x21: + decoder_engine->sample_method = JPEG_DOWN_SAMPLING_YUV422; + break; + case 0x22: + decoder_engine->sample_method = JPEG_DOWN_SAMPLING_YUV420; + break; + default: + ESP_LOGE(TAG, "Sampling factor cannot be recognized"); + return ESP_ERR_INVALID_STATE; + } + } + if (header_info->nf == 1) { + if (decoder_engine->output_format != JPEG_DECODE_OUT_FORMAT_GRAY) { + ESP_LOGE(TAG, "your jpg is a gray style picture, but your output format is wrong"); + return ESP_ERR_NOT_SUPPORTED; + } + decoder_engine->sample_method = JPEG_DOWN_SAMPLING_GRAY; + } + + // Write DHT information + dht_func[0][0](hal, header_info->huffbits[0][0], header_info->huffcode[0][0], header_info->tmp_huff); + dht_func[0][1](hal, header_info->huffbits[0][1], header_info->huffcode[0][1], header_info->tmp_huff); + dht_func[1][0](hal, header_info->huffbits[1][0], header_info->huffcode[1][0], header_info->tmp_huff); + dht_func[1][1](hal, header_info->huffbits[1][1], header_info->huffcode[1][1], header_info->tmp_huff); + + if (header_info->dri_marker) { + jpeg_ll_set_restart_interval(hal->dev, header_info->ri); + } + + return ESP_OK; +} + +static esp_err_t jpeg_parse_marker(jpeg_decoder_handle_t decoder_engine, const uint8_t *in_buf, uint32_t inbuf_len) +{ + ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle is null"); + ESP_RETURN_ON_FALSE(in_buf, ESP_ERR_INVALID_ARG, TAG, "jpeg decode input buffer is NULL"); + ESP_RETURN_ON_FALSE(inbuf_len != 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode input buffer length is 0"); + + jpeg_dec_header_info_t* header_info = decoder_engine->header_info; + jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal; + + memset(decoder_engine->header_info, 0, sizeof(jpeg_dec_header_info_t)); + decoder_engine->header_info->buffer_offset = (uint8_t*)in_buf; + decoder_engine->header_info->buffer_left = inbuf_len; + decoder_engine->total_size = inbuf_len; + decoder_engine->header_info->header_size = 0; + + jpeg_ll_soft_rst(hal->dev); + jpeg_ll_set_codec_mode(hal->dev, JPEG_CODEC_DECODER); + // Digital issue. Needs to set height and width to 0 before decoding a new picture. + jpeg_ll_set_picture_height(hal->dev, 0); + jpeg_ll_set_picture_width(hal->dev, 0); + + while (header_info->buffer_left) { + uint8_t lastchar = jpeg_get_bytes(header_info, 1); + uint8_t thischar = jpeg_get_bytes(header_info, 1); + uint16_t marker = (lastchar << 8 | thischar); + switch (marker) { + case JPEG_M_SOI: + break; + case JPEG_M_APP0: + case JPEG_M_APP1: + case JPEG_M_APP2: + case JPEG_M_APP3: + case JPEG_M_APP4: + case JPEG_M_APP5: + case JPEG_M_APP6: + case JPEG_M_APP7: + case JPEG_M_APP8: + case JPEG_M_APP9: + case JPEG_M_APP10: + case JPEG_M_APP11: + case JPEG_M_APP12: + case JPEG_M_APP13: + case JPEG_M_APP14: + case JPEG_M_APP15: + ESP_RETURN_ON_ERROR(jpeg_parse_appn_marker(header_info), TAG, "deal appn marker failed"); + break; + case JPEG_M_COM: + ESP_RETURN_ON_ERROR(jpeg_parse_com_marker(header_info), TAG, "deal com marker failed"); + break; + case JPEG_M_DQT: + ESP_RETURN_ON_ERROR(jpeg_parse_dqt_marker(header_info), TAG, "deal dqt marker failed"); + break; + case JPEG_M_SOF0: + ESP_RETURN_ON_ERROR(jpeg_parse_sof_marker(header_info), TAG, "deal sof marker failed"); + break; + case JPEG_M_SOF1: + case JPEG_M_SOF2: + case JPEG_M_SOF3: + case JPEG_M_SOF5: + case JPEG_M_SOF6: + case JPEG_M_SOF7: + case JPEG_M_SOF9: + case JPEG_M_SOF10: + case JPEG_M_SOF11: + case JPEG_M_SOF13: + case JPEG_M_SOF14: + case JPEG_M_SOF15: + ESP_LOGE(TAG, "Only baseline-DCT is supported."); + return ESP_ERR_NOT_SUPPORTED; + case JPEG_M_DRI: + ESP_RETURN_ON_ERROR(jpeg_parse_dri_marker(header_info), TAG, "deal dri marker failed"); + break; + case JPEG_M_DHT: + ESP_RETURN_ON_ERROR(jpeg_parse_dht_marker(header_info), TAG, "deal dht marker failed"); + break; + case JPEG_M_SOS: + ESP_RETURN_ON_ERROR(jpeg_parse_sos_marker(header_info), TAG, "deal sos marker failed"); + break; + } + if (marker == JPEG_M_SOS) { + break; + } + } + + // Update information after parse marker finishes + decoder_engine->header_info->buffer_left = decoder_engine->total_size - decoder_engine->header_info->header_size; + + return ESP_OK; +} + +static void s_decoder_error_log_print(uint32_t status) +{ + if (status & JPEG_LL_INTR_CID_ERR) { + ESP_LOGE(TAG, "decoded component ID is different from the component ID configured by the software"); + } + if (status & JPEG_LL_INTR_C_DHT_DC_ID) { + ESP_LOGE(TAG, "decoded DC Huffman table ID of each component is not the software configured DC0 Huffman table ID or DC1 Huffman table ID"); + } + if (status & JPEG_LL_INTR_C_DHT_AC_ID) { + ESP_LOGE(TAG, "decoded AC Huffman table ID of each component is not the software configured AC0 Huffman table ID or AC1 Huffman table ID"); + } + if (status & JPEG_LL_INTR_C_DQT_ID) { + ESP_LOGE(TAG, "decoded quantization table ID of each component is different from the software configured quantization table ID"); + } + if (status & JPEG_LL_INTR_RST_UXP_ERR) { + ESP_LOGE(TAG, "JPEG_RESTART_INTERVAL configured by the software is 0 but the RST marker is parsed by the decoder"); + } + if (status & JPEG_LL_INTR_RST_CHECK_NON_ERR) { + ESP_LOGE(TAG, "JPEG_RESTART_INTERVAL configured by the software is non-0 but the RST marker cannot be parsed by the decoder"); + } + if (status & JPEG_LL_INTR_RST_CHECK_POS_ERR) { + ESP_LOGE(TAG, "the MCU number between two parsed RST markers is not equal to the JPEG_RESTART_INTERVAL configured by the software"); + } + if (status & JPEG_LL_INTR_SCAN_CHECK_NONE) { + ESP_LOGE(TAG, "an image frame has multiple SCANs to be decoded and the SOS marker is not parsed within (JPEG_SOS_CHECK_BYTE_NUM + 1) bytes in any SCAN header information"); + } + if (status & JPEG_LL_INTR_SCAN_POS_ERR) { + ESP_LOGE(TAG, "the position of the SCAN header information parsed by the decoder is wrong"); + } + if (status & JPEG_LL_INTR_UXP_DET) { + ESP_LOGE(TAG, "marker parsed by the decoder is not supported by the hardware"); + } + if (status & JPEG_LL_INTR_DE_FRAME_EOF_ERR) { + ESP_LOGE(TAG, "the number of data units obtained after decoding a frame of image is different from the number of data units calculated based on the image resolution configured by the software"); + } + if (status & JPEG_LL_INTR_DE_FRAME_EOF_LACK) { + ESP_LOGE(TAG, "the bitstream of a image is completely read from 2D DMA but the EOF marker or EOI marker is not read"); + } + if (status & JPEG_LL_INTR_SOS_UNMATCH_ERR) { + ESP_LOGE(TAG, "the number of components in the SCAN header information parsed by the decoder is 0 or the header length in the SCAN header information parsed by the decoder does not match the actual header length."); + } + if (status & JPEG_LL_INTR_MARKER_ERR_FST) { + ESP_LOGE(TAG, "there is an error in the first SCAN header information parsed by the decoder."); + } + if (status & JPEG_LL_INTR_MARKER_ERR_OTHER) { + ESP_LOGE(TAG, "there is an error in the non-first SCAN header information parsed by the decoder."); + } + if (status & JPEG_LL_INTR_UNDET) { + ESP_LOGE(TAG, "the bitstream of a image is completely read from 2D DMA but the SOS marker is not read"); + } + if (status & JPEG_LL_INTR_DECODE_TIMEOUT) { + ESP_LOGE(TAG, "decoder is timeout"); + } +} diff --git a/components/esp_driver_jpeg/jpeg_param.c b/components/esp_driver_jpeg/jpeg_param.c new file mode 100644 index 0000000000..c83af6f63f --- /dev/null +++ b/components/esp_driver_jpeg/jpeg_param.c @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "driver/jpeg_types.h" +#include "jpeg_private.h" + +/** + * @brief Zigzag scan pattern array. + * + * This array represents a zigzag scan pattern for reordering the coefficients + * in a block of data. It is commonly used in JPEG compression and decompression + * algorithms to optimize encoding and decoding processes. + * + * The array consists of 64 elements, where each element represents the position + * of a coefficient in the zigzag pattern. The coefficients are traversed in the + * order specified by this array to achieve efficient compression and decompression. + * + * @note The values in this array are zero-indexed. + */ +const uint8_t zigzag_arr[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +/** + * @brief DMA2D best hb value table for JPEG decompression. + * + * This two-dimensional array represents a Huffman decoding table for JPEG + * decompression. It is used to decode the Huffman-coded symbols in the compressed + * data stream during the decoding process. + */ +const uint32_t dec_hb_tbl[JPEG_DOWN_SAMPLING_MAX][JPEG_DEC_BEST_HB_MAX] = { + {40, 40, 40, 32, 0}, + {64, 32, 32, 64, 0}, + {48, 32, 32, 48, 0}, + {96, 0, 0, 0, 96}, +}; diff --git a/components/esp_driver_jpeg/jpeg_parse_marker.c b/components/esp_driver_jpeg/jpeg_parse_marker.c new file mode 100644 index 0000000000..e457ccfd5f --- /dev/null +++ b/components/esp_driver_jpeg/jpeg_parse_marker.c @@ -0,0 +1,187 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_err.h" +#include +#include "jpeg_private.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "private/jpeg_param.h" +#include "hal/jpeg_types.h" +#include "esp_check.h" +#include "sdkconfig.h" + +static const char *TAG = "jpeg.decoder"; + +static uint8_t jpeg_get_char(jpeg_dec_header_info_t *header_info) +{ + uint8_t c = header_info->buffer_offset[0]; + header_info->buffer_offset++; + header_info->header_size++; + header_info->buffer_left--; + return c; +} + +uint32_t jpeg_get_bytes(jpeg_dec_header_info_t *header_info, uint8_t num_bytes) +{ + uint32_t result = 0; + for (int i = 0; i < num_bytes; i++) { + uint8_t byte = jpeg_get_char(header_info); + result |= (byte << ((num_bytes - i - 1) * 8)); + } + return result; +} + +esp_err_t jpeg_parse_appn_marker(jpeg_dec_header_info_t *header_info) +{ + uint32_t skip_num = jpeg_get_bytes(header_info, 2); + header_info->buffer_offset += (skip_num - 2); + header_info->header_size += (skip_num - 2); + header_info->buffer_left -= (skip_num - 2); + + return ESP_OK; +} + +esp_err_t jpeg_parse_com_marker(jpeg_dec_header_info_t *header_info) +{ + uint32_t skip_num = jpeg_get_bytes(header_info, 2); + header_info->buffer_offset += (skip_num - 2); + header_info->header_size += (skip_num - 2); + header_info->buffer_left -= (skip_num - 2); + return ESP_OK; +} + +esp_err_t jpeg_parse_dqt_marker(jpeg_dec_header_info_t *header_info) +{ + uint32_t n = 0, i = 0, prec = 0; + uint32_t temp = 0; + + uint32_t length_num = jpeg_get_bytes(header_info, 2); + length_num -= 2; + + while (length_num) { + n = jpeg_get_bytes(header_info, 1); + prec = n >> 4; + n &= 0x0F; + length_num -= 1; + + // read quantization entries, in zig-zag order + for (i = 0; i < 64; i++) { + temp = jpeg_get_bytes(header_info, 1); + length_num -= 1; + if (prec) { + temp = (temp << 8) + jpeg_get_bytes(header_info, 1); + length_num -= 1; + } + header_info->qt_tbl[n][zigzag_arr[i]] = temp; + } + header_info->qt_tbl_num++; + } + + return ESP_OK; + +} + +esp_err_t jpeg_parse_sof_marker(jpeg_dec_header_info_t *header_info) +{ + jpeg_get_bytes(header_info, 2); + if (jpeg_get_bytes(header_info, 1) != 8) { + ESP_LOGE(TAG, "Sample precision is not 8"); + return ESP_ERR_INVALID_STATE; + } + + uint16_t height = jpeg_get_bytes(header_info, 2); + header_info->origin_v = height; + header_info->process_v = height; + + uint16_t width = jpeg_get_bytes(header_info, 2); + header_info->origin_h = width; + header_info->process_h = width; + + if ((width * height % 8) != 0) { + ESP_LOGE(TAG, "Picture sizes not divisible by 8 are not supported"); + return ESP_ERR_INVALID_STATE; + } + + uint8_t nf = jpeg_get_bytes(header_info, 1); + if (nf >= 4 || nf == 0) { + ESP_LOGE(TAG, "Only frame less or equal than 4 is supported."); + return ESP_ERR_INVALID_STATE; + } + + header_info->nf = nf; + + for (int i = 0; i < nf; i++) { + header_info->ci[i] = jpeg_get_bytes(header_info, 1); + header_info->hivi[i] = jpeg_get_bytes(header_info, 1); + header_info->vi[i] = header_info->hivi[i] & 0x0f; + header_info->hi[i] = (header_info->hivi[i] & 0xf0) >> 4; + header_info->qtid[i] = jpeg_get_bytes(header_info, 1); + } + + // Set MCU block pixel according to factor. (For 3 components, we only use Y factor) + header_info->mcux = header_info->hi[0] * 8; + header_info->mcuy = header_info->vi[0] * 8; + + // The vertical and horizontal in process must be divided by mcu block. + if (header_info->origin_v % header_info->mcuy != 0) { + header_info->process_v = (ceil(header_info->origin_v / header_info->mcuy) + 1) * header_info->mcuy; + } + if (header_info->origin_h % header_info->mcux != 0) { + header_info->process_h = (ceil(header_info->origin_h / header_info->mcux) + 1) * header_info->mcux; + } + + return ESP_OK; +} + +esp_err_t jpeg_parse_dht_marker(jpeg_dec_header_info_t *header_info) +{ + // Recording num_left in DHT sector, not including length bytes (2 bytes). + uint32_t num_left = jpeg_get_bytes(header_info, 2) - 2; + while (num_left) { + uint32_t np = 0; + + // Get information of huffman table + header_info->huffinfo.info = jpeg_get_bytes(header_info, 1); + + for (int i = 0; i < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; i++) { + header_info->huffbits[header_info->huffinfo.type][header_info->huffinfo.id][i] = jpeg_get_bytes(header_info, 1); + // Record number of patterns. + np += header_info->huffbits[header_info->huffinfo.type][header_info->huffinfo.id][i]; + } + + for (int i = 0; i < np; i++) { + header_info->huffcode[header_info->huffinfo.type][header_info->huffinfo.id][i] = jpeg_get_bytes(header_info, 1); + } + + num_left -= (1 + JPEG_HUFFMAN_BITS_LEN_TABLE_LEN + np); + } + + return ESP_OK; +} + +esp_err_t jpeg_parse_dri_marker(jpeg_dec_header_info_t *header_info) +{ + uint16_t lr = jpeg_get_bytes(header_info, 2); + if (lr != 4) { + ESP_LOGE(TAG, "DRI marker got but stream length is insufficient, the length you got is %" PRIu16, lr); + return ESP_ERR_INVALID_SIZE; + } + header_info->ri = jpeg_get_bytes(header_info, 2); + header_info->dri_marker = true; + return ESP_OK; +} + +esp_err_t jpeg_parse_sos_marker(jpeg_dec_header_info_t *header_info) +{ + // Got the SOS marker, but need to recover this and feed to 2DDMA. + header_info->buffer_offset -= 2; + header_info->header_size -= 2; + header_info->buffer_left += 2; + return ESP_OK; +} diff --git a/components/esp_driver_jpeg/jpeg_private.h b/components/esp_driver_jpeg/jpeg_private.h new file mode 100644 index 0000000000..aa81f782ba --- /dev/null +++ b/components/esp_driver_jpeg/jpeg_private.h @@ -0,0 +1,143 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_private/dma2d.h" +#include "driver/jpeg_types.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" +#include "hal/jpeg_hal.h" +#include "esp_intr_types.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JPEG_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT) + +#define JPEG_ALLOW_INTR_PRIORITY_MASK (ESP_INTR_FLAG_LOWMED) + +// JPEG encoder and decoder shares same interrupt ID. +#define JPEG_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED) + +typedef struct jpeg_decoder_t jpeg_decoder_t; +typedef struct jpeg_codec_t jpeg_codec_t; +typedef struct jpeg_codec_t *jpeg_codec_handle_t; + +struct jpeg_codec_t { + SemaphoreHandle_t codec_mux; // pretend that one picture is in process, no other picture can interrupt current stage. + jpeg_hal_context_t hal; // Hal layer for each port(bus) + portMUX_TYPE spinlock; // To protect pre-group register level concurrency access +}; + +typedef enum { + // TODO: Support DR and YUV444 on decoder. + //JPEG_DEC_DR_HB = 0, /*!< Direct output */ + //JPEG_DEC_YUV444_HB = 1, /*!< output YUV444 format */ + JPEG_DEC_RGB888_HB = 2, /*!< output RGB888 format */ + JPEG_DEC_RGB565_HB = 3, /*!< output RGB565 format */ + JPEG_DEC_GRAY_HB = 4, /*!< output the gray picture */ + JPEG_DEC_BEST_HB_MAX, /*!< Max value of output formats */ +} jpeg_dec_format_hb_t; + +typedef struct { + uint8_t *buffer_offset; // Pointer points to picture buffer. + uint32_t buffer_left; // Data left in the picture. + uint32_t header_size; // record the picture header size. + uint32_t process_v; // vertical output in processing, multiple of mcu. + uint32_t process_h; // horizontal output in processing, multiple of mcu. + uint32_t origin_v; // vertical for the origin size of picture. + uint32_t origin_h; // horizontal for the origin size of picture. + uint8_t mcux; // the best value of minimum coding unit horizontal unit + uint8_t mcuy; // minimum coding unit vertical unit + uint8_t qt_tbl_num; // quantization table number + uint32_t qt_tbl[JPEG_COMPONENT_NUMBER_MAX][JPEG_QUANTIZATION_TABLE_LEN]; // quantization table content [id] + uint8_t nf; // number of frames + uint8_t ci[JPEG_COMPONENT_NUMBER_MAX]; // Component identifier. + uint8_t hivi[JPEG_COMPONENT_NUMBER_MAX]; // H&V sampling factor + uint8_t hi[JPEG_COMPONENT_NUMBER_MAX]; // Horizontal sampling factor + uint8_t vi[JPEG_COMPONENT_NUMBER_MAX]; // Vertical sampling factor + uint8_t qtid[JPEG_COMPONENT_NUMBER_MAX]; // Quantization table destination selector + jpeg_huffman_table_info_t huffinfo; // Huffman table specification information + uint8_t huffbits[2][2][JPEG_HUFFMAN_BITS_LEN_TABLE_LEN]; // Huffman bit distribution tables [id][dcac] + uint8_t huffcode[2][2][JPEG_HUFFMAN_AC_VALUE_TABLE_LEN]; // Huffman decoded data tables [id][dcac] + uint32_t tmp_huff[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN]; // temp buffer to store huffman code + bool dri_marker; // If we have dri marker in table + uint8_t ri; // Restart interval +} jpeg_dec_header_info_t; + +struct jpeg_decoder_t { + jpeg_codec_t *codec_base; // Pointer to jpeg codec hardware base + jpeg_dec_header_info_t *header_info; // Pointer to current picture information + jpeg_down_sampling_type_t sample_method; // method of sampling the JPEG picture. + jpeg_dec_output_format_t output_format; // picture output format. + jpeg_dec_rgb_element_order rgb_order; // RGB pixel order + jpeg_yuv_rgb_conv_std_t conv_std; // YUV RGB conversion standard + uint8_t pixel; // size per pixel + QueueHandle_t evt_handle; // jpeg event from 2DDMA and JPEG engine + uint8_t *decoded_buf; // pointer to the rx buffer. + uint32_t total_size; // jpeg picture origin size (in bytes) + intr_handle_t intr_handle; // jpeg decoder interrupt handler + //dma handles + dma2d_pool_handle_t dma2d_group_handle; // 2D-DMA group handle + dma2d_descriptor_t *rxlink; // Pointer to 2D-DMA rx descriptor + dma2d_descriptor_t *txlink; // Pointer to 2D-DMA tx descriptor + uint32_t dma_desc_size; // tx and rx linker alignment + dma2d_channel_handle_t dma2d_rx_channel; // DMA2D RX channel handle + dma2d_channel_handle_t dma2d_tx_channel; // DMA2D TX channel handle + dma2d_trans_t* trans_desc; // DMA2D transaction descriptor +}; + +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_evt_enum_t; + +typedef struct { + jpeg_dma2d_evt_enum_t dma_evt; // jpeg-2ddma event, (triggered from 2ddma interrupt) + uint32_t jpgd_status; // jpeg decoder status, (triggered from jpeg interrupt) +} jpeg_dma2d_dec_evt_t; + +#define JPEG_DMA2D_2D_ENABLE (1) // DMA2D two dimension enable +#define JPEG_DMA2D_2D_DISABLE (0) // DMA2D one dimension enable +#define JPEG_DMA2D_MAX_SIZE (0x3fff) // DMA2D max size (low 14 bit) +#define JPEG_DMA2D_EOF_LAST (1) // DMA2D EOF last +#define JPEG_DMA2D_EOF_NOT_LAST (0) // DMA2D EOF not last +#define JPEG_DMA2D_1D_HIGH_14BIT (14) // 1D high 14 bites + +/** + * @brief Acquire a JPEG codec handle. + * + * @param[out] jpeg_new_codec Pointer to the variable where the acquired JPEG codec handle will be stored. + * + * @return + * - ESP_OK if the JPEG codec handle is successfully acquired. + * - ESP_ERR_NO_MEM if there is not enough memory to acquire the codec handle. + * - Other error codes indicating the cause of the failure. + */ +esp_err_t jpeg_acquire_codec_handle(jpeg_codec_handle_t *jpeg_new_codec); + +/** + * @brief Release a JPEG codec handle. + * + * @param[in] jpeg_codec The JPEG codec handle to release. + * + * @return + * - ESP_OK if the JPEG codec handle is successfully released. + * - ESP_ERR_INVALID_ARG if the provided JPEG codec handle is invalid. + * - Other error codes indicating the cause of the failure. + */ +esp_err_t jpeg_release_codec_handle(jpeg_codec_handle_t jpeg_codec); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_jpeg/private/jpeg_param.h b/components/esp_driver_jpeg/private/jpeg_param.h new file mode 100644 index 0000000000..77a46be88f --- /dev/null +++ b/components/esp_driver_jpeg/private/jpeg_param.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "driver/jpeg_types.h" +#include "../jpeg_private.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Zigzag scan pattern array. + * + * This array represents a zigzag scan pattern for reordering the coefficients + * in a block of data. It is commonly used in JPEG compression and decompression + * algorithms to optimize encoding and decoding processes. + * + * The array consists of 64 elements, where each element represents the position + * of a coefficient in the zigzag pattern. The coefficients are traversed in the + * order specified by this array to achieve efficient compression and decompression. + * + * @note The values in this array are zero-indexed. + */ +extern const uint8_t zigzag_arr[64]; + +/** + * @brief DMA2D best hb value table for JPEG decompression. + * + * This two-dimensional array represents a Huffman decoding table for JPEG + * decompression. It is used to decode the Huffman-coded symbols in the compressed + * data stream during the decoding process. + */ +extern const uint32_t dec_hb_tbl[JPEG_DOWN_SAMPLING_MAX][JPEG_DEC_BEST_HB_MAX]; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_jpeg/private/jpeg_parse_marker.h b/components/esp_driver_jpeg/private/jpeg_parse_marker.h new file mode 100644 index 0000000000..560886c729 --- /dev/null +++ b/components/esp_driver_jpeg/private/jpeg_parse_marker.h @@ -0,0 +1,121 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "driver/jpeg_types.h" +#include "../jpeg_private.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Retrieves a specified number of bytes from the JPEG decoder. + * + * @param header_info The handle to the JPEG information. + * @param num_bytes The number of bytes to retrieve from the decoder. + * + * @return The retrieved bytes as a 32-bit unsigned integer. + */ +uint32_t jpeg_get_bytes(jpeg_dec_header_info_t *header_info, uint8_t num_bytes); + +/** + * @brief Parses with the APPn (application specific) marker in a JPEG file. + * + * This function is called when the decoder encounters an APPn marker in the + * input stream. The function handles any application-specific data contained + * within the marker and performs necessary processing or actions based on the + * specific application requirements. + * + * @param[in] header_info Pointer to the JPEG picture information. + * + * @return ESP_OK on success, or an appropriate error code if an error occurred. + */ +esp_err_t jpeg_parse_appn_marker(jpeg_dec_header_info_t *header_info); + +/** + * @brief Parses with the COM (comment) marker in a JPEG file. + * + * This function is called when the decoder encounters a COM marker in the input stream. + * The function handles any comment data contained within the marker and performs any + * necessary processing or actions based on the comment information. + * + * @param[in] header_info Pointer to the JPEG picture information. + * + * @return ESP_OK on success, or an appropriate error code if an error occurred. + */ +esp_err_t jpeg_parse_com_marker(jpeg_dec_header_info_t *header_info); + +/** + * @brief Parses with the DQT (quantization table) marker in a JPEG file. + * + * This function is called when the decoder encounters a DQT marker in the input stream. + * The function handles the quantization table data contained within the marker and + * performs any necessary processing or actions based on the quantization table information. + * + * @param[in] header_info Pointer to the JPEG picture information. + * + * @return ESP_OK on success, or an appropriate error code if an error occurred. + */ +esp_err_t jpeg_parse_dqt_marker(jpeg_dec_header_info_t *header_info); + +/** + * @brief Parses with the SOF (Start of Frame) marker in a JPEG file. + * + * This function only used for verify there is an SOF marker, the content of frame + * would be handle in hardware. + * + * @param[in] header_info Pointer to the JPEG picture information. + * + * @return ESP_OK on success, or an appropriate error code if an error occurred. + */ +esp_err_t jpeg_parse_sof_marker(jpeg_dec_header_info_t *header_info); + +/** + * @brief Parses with the DHT (Huffman table) marker in a JPEG file. + * + * This function is called when the decoder encounters a DHT marker in the input stream. + * The function handles the Huffman table data contained within the marker and performs + * any necessary processing or actions based on the Huffman table information. + * + * @param[in] header_info Pointer to the JPEG picture information. + * + * @return ESP_OK on success, or an appropriate error code if an error occurred. + */ +esp_err_t jpeg_parse_dht_marker(jpeg_dec_header_info_t *header_info); + +/** + * @brief Parses with the SOS (Start of Scan) marker in a JPEG file. + * + * This function is called when the decoder encounters a SOS marker in the input stream. + * The function handles the scan header data contained within the marker and performs + * any necessary processing or actions based on the scan information. + * + * @param[in] header_info Pointer to the JPEG picture information. + * + * @return ESP_OK on success, or an appropriate error code if an error occurred. + */ +esp_err_t jpeg_parse_sos_marker(jpeg_dec_header_info_t *header_info); + +/** + * @brief Parses with the DRI (Define Restart Interval) marker in a JPEG file. + * + * This function is called when the decoder encounters a DRI marker in the input stream. + * The function handles the restart interval data contained within the marker and performs + * any necessary processing or actions based on the restart interval information. + * + * @param[in] header_info Pointer to the JPEG picture information. + * + * @return ESP_OK on success, or an appropriate error code if an error occurred. + */ +esp_err_t jpeg_parse_dri_marker(jpeg_dec_header_info_t *header_info); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_lcd/include/esp_lcd_types.h b/components/esp_lcd/include/esp_lcd_types.h index 951a4b8243..7772540107 100644 --- a/components/esp_lcd/include/esp_lcd_types.h +++ b/components/esp_lcd/include/esp_lcd_types.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,7 @@ #include "esp_assert.h" #include "hal/lcd_types.h" #include "hal/mipi_dsi_types.h" +#include "hal/color_types.h" #ifdef __cplusplus extern "C" { @@ -34,8 +35,8 @@ typedef struct esp_lcd_panel_t *esp_lcd_panel_handle_t; /*!< Type of LCD p * @brief RGB element order */ typedef enum { - LCD_RGB_ELEMENT_ORDER_RGB, /*!< RGB element order: RGB */ - LCD_RGB_ELEMENT_ORDER_BGR, /*!< RGB element order: BGR */ + LCD_RGB_ELEMENT_ORDER_RGB = COLOR_RGB_ELEMENT_ORDER_RGB, /*!< RGB element order: RGB */ + LCD_RGB_ELEMENT_ORDER_BGR = COLOR_RGB_ELEMENT_ORDER_BGR, /*!< RGB element order: BGR */ } lcd_rgb_element_order_t; /** @cond */ diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index 766a6a4ca0..2be78698dd 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -213,6 +213,10 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "brownout_hal.c") endif() + if(CONFIG_SOC_JPEG_CODEC_SUPPORTED) + list(APPEND srcs "jpeg_hal.c") + endif() + if(CONFIG_SOC_GPSPI_SUPPORTED) list(APPEND srcs "spi_hal.c" diff --git a/components/hal/esp32p4/include/hal/jpeg_ll.h b/components/hal/esp32p4/include/hal/jpeg_ll.h index 2126d5db72..db1dbded99 100644 --- a/components/hal/esp32p4/include/hal/jpeg_ll.h +++ b/components/hal/esp32p4/include/hal/jpeg_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -112,7 +112,7 @@ static inline void jpeg_ll_reset_module_register(void) static inline void jpeg_ll_dht_ac0_write_codeword(jpeg_dev_t *hw, uint8_t *huffman_bits_table, uint32_t *minimum_code_table) { uint32_t element_number = 0; - for (int idx = 0; idx < JEPG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) { + for (int idx = 0; idx < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) { element_number += (uint32_t)huffman_bits_table[idx]; hw->dht_totlen_ac0.dht_totlen_ac0 = element_number; hw->dht_codemin_ac0.dht_codemin_ac0 = minimum_code_table[idx]; @@ -143,7 +143,7 @@ static inline void jpeg_ll_dht_ac0_write_value(jpeg_dev_t *hw, uint8_t *huffman_ static inline void jpeg_ll_dht_ac1_write_codeword(jpeg_dev_t *hw, uint8_t *huffman_bits_table, uint32_t *minimum_code_table) { uint32_t element_number = 0; - for (int idx = 0; idx < JEPG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) { + for (int idx = 0; idx < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) { element_number += (uint32_t)huffman_bits_table[idx]; hw->dht_totlen_ac1.dht_totlen_ac1 = element_number; hw->dht_codemin_ac1.dht_codemin_ac1 = minimum_code_table[idx]; @@ -174,7 +174,7 @@ static inline void jpeg_ll_dht_ac1_write_value(jpeg_dev_t *hw, uint8_t *huffman_ static inline void jpeg_ll_dht_dc0_write_codeword(jpeg_dev_t *hw, uint8_t *huffman_bits_table, uint32_t *minimum_code_table) { uint32_t element_number = 0; - for (int idx = 0; idx < JEPG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) { + for (int idx = 0; idx < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) { element_number += (uint32_t)huffman_bits_table[idx]; hw->dht_totlen_dc0.dht_totlen_dc0 = element_number; hw->dht_codemin_dc0.dht_codemin_dc0 = minimum_code_table[idx]; @@ -205,7 +205,7 @@ static inline void jpeg_ll_dht_dc0_write_value(jpeg_dev_t *hw, uint8_t *huffman_ static inline void jpeg_ll_dht_dc1_write_codeword(jpeg_dev_t *hw, uint8_t *huffman_bits_table, uint32_t *minimum_code_table) { uint32_t element_number = 0; - for (int idx = 0; idx < JEPG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) { + for (int idx = 0; idx < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) { element_number += (uint32_t)huffman_bits_table[idx]; hw->dht_totlen_dc1.dht_totlen_dc1 = element_number; hw->dht_codemin_dc1.dht_codemin_dc1 = minimum_code_table[idx]; diff --git a/components/hal/include/hal/color_types.h b/components/hal/include/hal/color_types.h index db1db8619a..e129bd09db 100644 --- a/components/hal/include/hal/color_types.h +++ b/components/hal/include/hal/color_types.h @@ -138,6 +138,18 @@ typedef enum { COLOR_CONV_STD_RGB_YUV_BT709, /*!< YUV<->RGB conversion standard: BT.709 */ } color_conv_std_rgb_yuv_t; +/*--------------------------------------------------------------- + Color Endian +---------------------------------------------------------------*/ + +/** + * @brief RGB element order + */ +typedef enum { + COLOR_RGB_ELEMENT_ORDER_RGB, /*!< RGB element order: RGB */ + COLOR_RGB_ELEMENT_ORDER_BGR, /*!< RGB element order: BGR */ +} color_rgb_element_order_t; + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/jpeg_defs.h b/components/hal/include/hal/jpeg_defs.h new file mode 100644 index 0000000000..742911e27f --- /dev/null +++ b/components/hal/include/hal/jpeg_defs.h @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * @brief Enum to define JPEG marker codes + */ +typedef enum { + JPEG_M_SOF0 = 0xFFC0, ///< Start of Frame 0 + JPEG_M_SOF1 = 0xFFC1, ///< Start of Frame 1 + JPEG_M_SOF2 = 0xFFC2, ///< Start of Frame 2 + JPEG_M_SOF3 = 0xFFC3, ///< Start of Frame 3 + JPEG_M_SOF5 = 0xFFC5, ///< Start of Frame 5 + JPEG_M_SOF6 = 0xFFC6, ///< Start of Frame 6 + JPEG_M_SOF7 = 0xFFC7, ///< Start of Frame 7 + JPEG_M_JPG = 0xFFC8, ///< JPEG Extension + JPEG_M_SOF9 = 0xFFC9, ///< Start of Frame 9 + JPEG_M_SOF10 = 0xFFCA, ///< Start of Frame 10 + JPEG_M_SOF11 = 0xFFCB, ///< Start of Frame 11 + JPEG_M_SOF13 = 0xFFCD, ///< Start of Frame 13 + JPEG_M_SOF14 = 0xFFCE, ///< Start of Frame 14 + JPEG_M_SOF15 = 0xFFCF, ///< Start of Frame 15 + JPEG_M_DHT = 0xFFC4, ///< Define Huffman Table + JPEG_M_DAC = 0xFFCC, ///< Define Arithmetic Coding Conditioning + JPEG_M_RST0 = 0xFFD0, ///< Restart with modulo 8 count 0 + JPEG_M_RST1 = 0xFFD1, ///< Restart with modulo 8 count 1 + JPEG_M_RST2 = 0xFFD2, ///< Restart with modulo 8 count 2 + JPEG_M_RST3 = 0xFFD3, ///< Restart with modulo 8 count 3 + JPEG_M_RST4 = 0xFFD4, ///< Restart with modulo 8 count 4 + JPEG_M_RST5 = 0xFFD5, ///< Restart with modulo 8 count 5 + JPEG_M_RST6 = 0xFFD6, ///< Restart with modulo 8 count 6 + JPEG_M_RST7 = 0xFFD7, ///< Restart with modulo 8 count 7 + JPEG_M_SOI = 0xFFD8, ///< Start of Image + JPEG_M_EOI = 0xFFD9, ///< End of Image + JPEG_M_SOS = 0xFFDA, ///< Start of Scan + JPEG_M_DQT = 0xFFDB, ///< Define Quantization Table + JPEG_M_DNL = 0xFFDC, ///< Define Number of Lines + JPEG_M_DRI = 0xFFDD, ///< Define Restart Interval + JPEG_M_DHP = 0xFFDE, ///< Define Hierarchical Progression + JPEG_M_EXP = 0xFFDF, ///< Expand Reference Component(s) + JPEG_M_APP0 = 0xFFE0, ///< Application Segment 0 + JPEG_M_APP1 = 0xFFE1, ///< Application Segment 1 + JPEG_M_APP2 = 0xFFE2, ///< Application Segment 2 + JPEG_M_APP3 = 0xFFE3, ///< Application Segment 3 + JPEG_M_APP4 = 0xFFE4, ///< Application Segment 4 + JPEG_M_APP5 = 0xFFE5, ///< Application Segment 5 + JPEG_M_APP6 = 0xFFE6, ///< Application Segment 6 + JPEG_M_APP7 = 0xFFE7, ///< Application Segment 7 + JPEG_M_APP8 = 0xFFE8, ///< Application Segment 8 + JPEG_M_APP9 = 0xFFE9, ///< Application Segment 9 + JPEG_M_APP10 = 0xFFEA, ///< Application Segment 10 + JPEG_M_APP11 = 0xFFEB, ///< Application Segment 11 + JPEG_M_APP12 = 0xFFEC, ///< Application Segment 12 + JPEG_M_APP13 = 0xFFED, ///< Application Segment 13 + JPEG_M_APP14 = 0xFFEE, ///< Application Segment 14 + JPEG_M_APP15 = 0xFFEF, ///< Application Segment 15 + JPEG_M_JPG0 = 0xFFF0, ///< Reserved for JPEG extension + JPEG_M_JPG13 = 0xFFFD, ///< Reserved for JPEG extension + JPEG_M_COM = 0xFFFE, ///< Comment + JPEG_M_TEM = 0xFF01, ///< Temporary use in arithmetic coding +} __attribute__((packed)) jpeg_marker_code_t; + + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/jpeg_hal.h b/components/hal/include/hal/jpeg_hal.h index 137ab119f3..a2181be541 100644 --- a/components/hal/include/hal/jpeg_hal.h +++ b/components/hal/include/hal/jpeg_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,6 +13,7 @@ #pragma once #include +#include "hal/jpeg_types.h" #ifdef __cplusplus extern "C" { @@ -41,6 +42,67 @@ void jpeg_hal_init(jpeg_hal_context_t *hal); */ void jpeg_hal_deinit(jpeg_hal_context_t *hal); +/** + * @brief Function pointer typedef for configuring DHT tables in JPEG decoding. + * + * This function pointer typedef represents a callback function that can be used + * to configure the DHT (Huffman) tables in the JPEG decoding process. It takes + * as input a JPEG HAL context, as well as pointers to arrays representing the + * minimum codes, bit lengths, and Huffman codes for the DHT tables. + * + * @param hal The JPEG HAL context. + * @param huffbits Pointer to the array of bit lengths for the DHT tables. + * @param huffcode Pointer to the array of Huffman codes for the DHT tables. + */ +typedef void (*jpeg_config_dht_table_t)(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff); + +/** + * @brief Array of function pointers for configuring DHT tables in JPEG decoding. + * + * This two-dimensional array represents a collection of function pointers that + * can be used to configure the DHT (Huffman) tables in the JPEG decoding process. + */ +extern jpeg_config_dht_table_t dht_func[DHT_TC_NUM][DHT_TH_NUM]; + +/** + * Typedef for a function pointer to configure frame information in JPEG. + * + * This function is used to configure frame information in JPEG hardware. + * + * @param hw The JPEG SOC handle. + * @param identifier The identifier of the frame. + * @param horizontal_factor The horizontal factor for chroma subsampling. + * @param vertical_factor The vertical factor for chroma subsampling. + * @param qun_table_id The quantization table ID. + */ +typedef void (*jpeg_config_frame_info_t)(jpeg_soc_handle_t hw, uint8_t identifier, uint8_t horizontal_factor, uint8_t vertical_factor, uint8_t qun_table_id); + +/** + * Array of function pointers to configure frame information in JPEG. + * + * This array holds function pointers to configure frame information in JPEG hardware. + * Each element corresponds to a specific component number. + */ +extern jpeg_config_frame_info_t sof_func[JPEG_COMPONENT_NUMBER_MAX]; + +/** + * Typedef for a function pointer to configure quantization coefficients in JPEG. + * + * This function is used to configure quantization coefficients in JPEG hardware. + * + * @param hw The JPEG SOC handle. + * @param quantization_table Pointer to the quantization table. + */ +typedef void (*jpeg_config_quantization_coefficient_t)(jpeg_soc_handle_t hw, uint32_t *quantization_table); + +/** + * Array of function pointers to configure quantization coefficients in JPEG. + * + * This array holds function pointers to configure quantization coefficients in JPEG hardware. + * Each element corresponds to a specific component number. + */ +extern jpeg_config_quantization_coefficient_t dqt_func[JPEG_COMPONENT_NUMBER_MAX]; + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/jpeg_types.h b/components/hal/include/hal/jpeg_types.h index 0d0b879d3d..e3bae21d92 100644 --- a/components/hal/include/hal/jpeg_types.h +++ b/components/hal/include/hal/jpeg_types.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,10 +15,14 @@ extern "C" { #endif -#define JEPG_HUFFMAN_BITS_LEN_TABLE_LEN (16) +#define JPEG_HUFFMAN_BITS_LEN_TABLE_LEN (16) #define JPEG_HUFFMAN_AC_VALUE_TABLE_LEN (256) #define JPEG_HUFFMAN_DC_VALUE_TABLE_LEN (16) #define JPEG_QUANTIZATION_TABLE_LEN (64) +#define JPEG_COMPONENT_NUMBER_MAX (4) + +#define DHT_TC_NUM (2) /// Table type +#define DHT_TH_NUM (2) /// Huffman table destination identifier /** * @brief Enum for JPEG codec working mode. @@ -37,7 +41,7 @@ typedef struct { } jpeg_component_factor_t; /** - * @brief Enum for JEPG sampling mode. + * @brief Enum for JPEG sampling mode. */ typedef enum { JPEG_SAMPLE_MODE_YUV444 = COLOR_PIXEL_YUV444, ///< sample in YUV444 @@ -45,6 +49,27 @@ typedef enum { JPEG_SAMPLE_MODE_YUV420 = COLOR_PIXEL_YUV420, ///< sample in YUV420 } jpeg_sample_mode_t; +/** + * @brief Structure for huffman information + */ +typedef union { + struct { + uint8_t id : 4; ///< Huffman table id + uint8_t type : 4; ///< Huffman table type + }; + uint8_t info; ///< Information of huffman table +} jpeg_huffman_table_info_t; + +/** + * @brief Enumeration for jpeg decoder sample methods. +*/ +typedef enum { + JPEG_DOWN_SAMPLING_YUV444 = 0, /*!< Sample by YUV444 */ + JPEG_DOWN_SAMPLING_YUV422 = 1, /*!< Sample by YUV422 */ + JPEG_DOWN_SAMPLING_YUV420 = 2, /*!< Sample by YUV420 */ + JPEG_DOWN_SAMPLING_GRAY = 3, /*!< Sample the gray picture */ + JPEG_DOWN_SAMPLING_MAX, /*!< Max value of sample enumeration */ +} jpeg_down_sampling_type_t; #ifdef __cplusplus } diff --git a/components/hal/jpeg_hal.c b/components/hal/jpeg_hal.c index 9ac73fda6f..198e03307e 100644 --- a/components/hal/jpeg_hal.c +++ b/components/hal/jpeg_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,3 +17,81 @@ void jpeg_hal_deinit(jpeg_hal_context_t *hal) { hal->dev = NULL; } + +/*-----------------------------------------------------------------------*/ +/* Config huffman code tables with a DHT segment */ +/*-----------------------------------------------------------------------*/ + +static void jpeg_create_minicode_tbl(uint8_t *huffbits, uint32_t *huffmin, uint32_t *tmp_huff) +{ + int total_len = 0; + /* Re-build huffman code word table */ + for (int j = 0, i = 0, hc = 0; i < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; i++, hc <<= 1) { + int b = huffbits[i]; + // If a codeword length does not exist, the configuration value is 0xFFFF. + if (huffbits[i] == 0) { + huffmin[i] = 0xffff; + continue; + } + total_len += huffbits[i]; + while (b--) { + tmp_huff[j++] = hc++; + } + + if (huffbits[i] != 0) { + huffmin[i] = tmp_huff[total_len - huffbits[i]]; + huffmin[i] <<= (15 - i); + } + } +} + +void jpeg_hal_config_ac0_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff) +{ + uint32_t huffmin[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = {}; + jpeg_create_minicode_tbl(huffbits, huffmin, tmp_huff); + jpeg_ll_dht_ac0_write_codeword(hal->dev, huffbits, huffmin); + jpeg_ll_dht_ac0_write_value(hal->dev, huffcode); +} + +void jpeg_hal_config_ac1_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff) +{ + uint32_t huffmin[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = {}; + jpeg_create_minicode_tbl(huffbits, huffmin, tmp_huff); + jpeg_ll_dht_ac1_write_codeword(hal->dev, huffbits, huffmin); + jpeg_ll_dht_ac1_write_value(hal->dev, huffcode); +} + +void jpeg_hal_config_dc0_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff) +{ + uint32_t huffmin[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = {}; + jpeg_create_minicode_tbl(huffbits, huffmin, tmp_huff); + jpeg_ll_dht_dc0_write_codeword(hal->dev, huffbits, huffmin); + jpeg_ll_dht_dc0_write_value(hal->dev, huffcode); +} + +void jpeg_hal_config_dc1_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff) +{ + uint32_t huffmin[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = {}; + jpeg_create_minicode_tbl(huffbits, huffmin, tmp_huff); + jpeg_ll_dht_dc1_write_codeword(hal->dev, huffbits, huffmin); + jpeg_ll_dht_dc1_write_value(hal->dev, huffcode); +} + +jpeg_config_dht_table_t dht_func[DHT_TC_NUM][DHT_TH_NUM] = { + {jpeg_hal_config_dc0_table, jpeg_hal_config_dc1_table}, + {jpeg_hal_config_ac0_table, jpeg_hal_config_ac1_table}, +}; + +jpeg_config_frame_info_t sof_func[JPEG_COMPONENT_NUMBER_MAX] = { + jpeg_ll_set_frame_info_component0, + jpeg_ll_set_frame_info_component1, + jpeg_ll_set_frame_info_component2, + jpeg_ll_set_frame_info_component3, +}; + +jpeg_config_quantization_coefficient_t dqt_func[JPEG_COMPONENT_NUMBER_MAX] = { + jpeg_ll_write_quantization_coefficient_t0, + jpeg_ll_write_quantization_coefficient_t1, + jpeg_ll_write_quantization_coefficient_t2, + jpeg_ll_write_quantization_coefficient_t3, +}; diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index b2ede9b4d6..b1445ac770 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1406,3 +1406,11 @@ config SOC_MEM_TCM_SUPPORTED config SOC_EMAC_USE_IO_MUX bool default y + +config SOC_JPEG_CODEC_SUPPORTED + bool + default y + +config SOC_JPEG_DECODE_SUPPORTED + bool + default y diff --git a/components/soc/esp32p4/include/soc/jpeg_struct.h b/components/soc/esp32p4/include/soc/jpeg_struct.h index 68661c2757..58e749f068 100644 --- a/components/soc/esp32p4/include/soc/jpeg_struct.h +++ b/components/soc/esp32p4/include/soc/jpeg_struct.h @@ -1400,7 +1400,7 @@ typedef union { } jpeg_version_reg_t; -typedef struct { +typedef struct jpeg_dev_t { volatile jpeg_config_reg_t config; volatile jpeg_dqt_info_reg_t dqt_info; volatile jpeg_pic_size_reg_t pic_size; diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index f138e25e2a..f8686a0e35 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -597,3 +597,8 @@ /*--------------------------- EMAC --------------------------------*/ #define SOC_EMAC_USE_IO_MUX (1) /*!< GPIO matrix is used to select GPIO pads */ + +/*--------------------------- JPEG --------------------------------*/ +#define SOC_JPEG_CODEC_SUPPORTED (1) +#define SOC_JPEG_DECODE_SUPPORTED (1) +// #define SOC_JPEG_ENCODE_SUPPORTED (1) // TODO: IDF-6512