/* * 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_queue, &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_decoder) { #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; decoder_engine->timeout_tick = (dec_eng_cfg->timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(dec_eng_cfg->timeout_ms); /// init jpeg interrupt. jpeg_ll_clear_intr_mask(hal->dev, JPEG_LL_DECODER_EVENT_INTR); ESP_GOTO_ON_ERROR(jpeg_check_intr_priority(decoder_engine->codec_base, dec_eng_cfg->intr_priority), err, TAG, "set group intrrupt priority failed"); 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 = jpeg_isr_register(decoder_engine->codec_base, jpeg_decoder_isr_handle_default, decoder_engine, JPEG_LL_DECODER_EVENT_INTR, isr_flags, &decoder_engine->intr_handle); ESP_GOTO_ON_ERROR(ret, err, TAG, "install jpeg decode interrupt failed"); jpeg_ll_enable_intr_mask(hal->dev, JPEG_LL_DECODER_EVENT_INTR); // Initialize queue decoder_engine->evt_queue = xQueueCreateWithCaps(2, sizeof(jpeg_dma2d_dec_evt_t), JPEG_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(decoder_engine->evt_queue, 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_decoder = 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_cfg, ESP_ERR_INVALID_ARG, TAG, "jpeg decode config 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; if (decoder_engine->codec_base->pm_lock) { ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(decoder_engine->codec_base->pm_lock), TAG, "acquire pm_lock failed"); } xSemaphoreTake(decoder_engine->codec_base->codec_mutex, portMAX_DELAY); /* Reset queue */ xQueueReset(decoder_engine->evt_queue); 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_queue, &jpeg_dma2d_event, decoder_engine->timeout_tick); ESP_GOTO_ON_FALSE(ret == pdTRUE, ESP_ERR_TIMEOUT, err, TAG, "jpeg-dma2d handle jpeg decode timeout, please check `timeout_ms` "); // 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_mutex); return ESP_ERR_INVALID_STATE; } if (jpeg_dma2d_event.dma_evt & JPEG_DMA2D_RX_EOF) { break; } } *out_size = decoder_engine->header_info->process_h * decoder_engine->header_info->process_v * decoder_engine->pixel; err: xSemaphoreGive(decoder_engine->codec_base->codec_mutex); if (decoder_engine->codec_base->pm_lock) { ESP_RETURN_ON_ERROR(esp_pm_lock_release(decoder_engine->codec_base->pm_lock), TAG, "release pm_lock failed"); } 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->trans_desc) { free(decoder_engine->trans_desc); } if (decoder_engine->evt_queue) { vQueueDeleteWithCaps(decoder_engine->evt_queue); } if (decoder_engine->dma2d_group_handle) { dma2d_release_pool(decoder_engine->dma2d_group_handle); } if (decoder_engine->intr_handle) { jpeg_isr_deregister(decoder_engine->codec_base, decoder_engine->intr_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_queue, &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"); } }