mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feat(cam): add esp32s3 dvp cam support
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -26,12 +26,24 @@
|
||||
#include "../../dvp_share_ctrl.h"
|
||||
|
||||
#ifdef CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE
|
||||
#define CAM_DVP_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#define DVP_CAM_CTLR_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define CAM_DVP_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
|
||||
#define DVP_CAM_CTLR_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
|
||||
#endif
|
||||
|
||||
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
#if CONFIG_SPIRAM
|
||||
#define DVP_CAM_BK_BUFFER_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA)
|
||||
#else
|
||||
#define DVP_CAM_BK_BUFFER_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
|
||||
#endif
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define DVP_CAM_CLK_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define DVP_CAM_CLK_ATOMIC()
|
||||
#endif
|
||||
|
||||
#define ALIGN_UP_BY(num, align) ((align) == 0 ? (num) : (((num) + ((align) - 1)) & ~((align) - 1)))
|
||||
|
||||
#define DVP_CAM_CONFIG_INPUT_PIN(pin, sig, inv) \
|
||||
{ \
|
||||
@@ -211,7 +223,6 @@ static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_jpeg_size(const uint8_t *buffer,
|
||||
*/
|
||||
static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_recved_size(esp_cam_ctlr_dvp_cam_t *ctlr, uint8_t *rx_buffer, uint32_t dma_recv_size)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint32_t recv_buffer_size;
|
||||
|
||||
if (ctlr->pic_format_jpeg) {
|
||||
@@ -220,8 +231,10 @@ static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_recved_size(esp_cam_ctlr_dvp_cam_
|
||||
recv_buffer_size = ctlr->fb_size_in_bytes;
|
||||
}
|
||||
|
||||
ret = esp_cache_msync(rx_buffer, recv_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
assert(ret == ESP_OK);
|
||||
if (esp_ptr_external_ram(rx_buffer)) {
|
||||
esp_err_t ret = esp_cache_msync(rx_buffer, recv_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
assert(ret == ESP_OK);
|
||||
}
|
||||
|
||||
if (ctlr->pic_format_jpeg) {
|
||||
recv_buffer_size = esp_cam_ctlr_dvp_get_jpeg_size(rx_buffer, dma_recv_size);
|
||||
@@ -337,8 +350,8 @@ esp_err_t esp_cam_ctlr_dvp_init(int ctlr_id, cam_clock_source_t clk_src, const e
|
||||
}
|
||||
}
|
||||
|
||||
esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true);
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
ESP_ERROR_CHECK(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true));
|
||||
DVP_CAM_CLK_ATOMIC() {
|
||||
cam_ll_enable_clk(ctlr_id, true);
|
||||
cam_ll_select_clk_src(ctlr_id, clk_src);
|
||||
};
|
||||
@@ -369,7 +382,7 @@ esp_err_t esp_cam_ctlr_dvp_output_clock(int ctlr_id, cam_clock_source_t clk_src,
|
||||
ESP_LOGD(TAG, "DVP clock source frequency %" PRIu32 "Hz", src_clk_hz);
|
||||
|
||||
if ((src_clk_hz % xclk_freq) == 0) {
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
DVP_CAM_CLK_ATOMIC() {
|
||||
cam_ll_set_group_clock_coeff(ctlr_id, src_clk_hz / xclk_freq, 0, 0);
|
||||
};
|
||||
|
||||
@@ -392,7 +405,7 @@ esp_err_t esp_cam_ctlr_dvp_deinit(int ctlr_id)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(ctlr_id < CAP_DVP_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid argument: ctlr_id >= %d", CAP_DVP_PERIPH_NUM);
|
||||
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
DVP_CAM_CLK_ATOMIC() {
|
||||
cam_ll_enable_clk(ctlr_id, false);
|
||||
};
|
||||
|
||||
@@ -695,11 +708,11 @@ esp_err_t esp_cam_new_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_
|
||||
ESP_RETURN_ON_FALSE(config->external_xtal || config->pin_dont_init || config->pin->xclk_io != GPIO_NUM_NC, ESP_ERR_INVALID_ARG, TAG, "invalid argument: xclk_io is not set");
|
||||
ESP_RETURN_ON_FALSE(config->external_xtal || config->xclk_freq, ESP_ERR_INVALID_ARG, TAG, "invalid argument: xclk_freq is not set");
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_cache_get_alignment(MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA, &alignment_size), TAG, "failed to get cache alignment");
|
||||
ESP_RETURN_ON_ERROR(esp_cache_get_alignment(DVP_CAM_BK_BUFFER_ALLOC_CAPS, &alignment_size), TAG, "failed to get cache alignment");
|
||||
ESP_RETURN_ON_ERROR(esp_cam_ctlr_dvp_cam_get_frame_size(config, &fb_size_in_bytes), TAG, "invalid argument: input frame pixel format is not supported");
|
||||
ESP_RETURN_ON_ERROR(dvp_shared_ctrl_claim_io_signals(), TAG, "failed to claim io signals");
|
||||
|
||||
esp_cam_ctlr_dvp_cam_t *ctlr = heap_caps_calloc(1, sizeof(esp_cam_ctlr_dvp_cam_t), CAM_DVP_MEM_ALLOC_CAPS);
|
||||
esp_cam_ctlr_dvp_cam_t *ctlr = heap_caps_calloc(1, sizeof(esp_cam_ctlr_dvp_cam_t), DVP_CAM_CTLR_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(ctlr, ESP_ERR_NO_MEM, fail0, TAG, "no mem for CAM DVP controller context");
|
||||
|
||||
ESP_GOTO_ON_ERROR(s_dvp_claim_ctlr(config->ctlr_id, ctlr), fail1, TAG, "no available DVP controller");
|
||||
@@ -707,7 +720,7 @@ esp_err_t esp_cam_new_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_
|
||||
ESP_LOGD(TAG, "alignment: 0x%x\n", alignment_size);
|
||||
fb_size_in_bytes = ALIGN_UP_BY(fb_size_in_bytes, alignment_size);
|
||||
if (!config->bk_buffer_dis) {
|
||||
ctlr->backup_buffer = heap_caps_aligned_alloc(alignment_size, fb_size_in_bytes, MALLOC_CAP_SPIRAM);
|
||||
ctlr->backup_buffer = heap_caps_aligned_alloc(alignment_size, fb_size_in_bytes, DVP_CAM_BK_BUFFER_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(ctlr->backup_buffer, ESP_ERR_NO_MEM, fail2, TAG, "no mem for DVP backup buffer");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -10,9 +10,24 @@
|
||||
#include "esp_cache.h"
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#include "esp_cam_ctlr_dvp_dma.h"
|
||||
#include "esp_memory_utils.h"
|
||||
|
||||
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
#if defined(SOC_GDMA_TRIG_PERIPH_CAM0_BUS) && (SOC_GDMA_TRIG_PERIPH_CAM0_BUS == SOC_GDMA_BUS_AHB)
|
||||
#define DVP_GDMA_NEW_CHANNEL gdma_new_ahb_channel
|
||||
#define DVP_GDMA_DESC_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
|
||||
#elif defined(SOC_GDMA_TRIG_PERIPH_CAM0_BUS) && (SOC_GDMA_TRIG_PERIPH_CAM0_BUS == SOC_GDMA_BUS_AXI)
|
||||
#define DVP_GDMA_NEW_CHANNEL gdma_new_axi_channel
|
||||
#if CONFIG_SPIRAM
|
||||
#define DVP_GDMA_DESC_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA)
|
||||
#else
|
||||
#define DVP_GDMA_DESC_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
|
||||
#endif
|
||||
#else
|
||||
#error "Unsupported GDMA bus type for DVP"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "dvp_gdma";
|
||||
|
||||
/**
|
||||
@@ -70,9 +85,9 @@ esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_
|
||||
#endif
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_cache_get_alignment(MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA, &alignment_size), TAG, "failed to get cache alignment");
|
||||
ESP_RETURN_ON_ERROR(esp_cache_get_alignment(DVP_GDMA_DESC_ALLOC_CAPS, &alignment_size), TAG, "failed to get cache alignment");
|
||||
|
||||
ESP_RETURN_ON_ERROR(gdma_new_axi_channel(&rx_alloc_config, &dma->dma_chan), TAG, "new channel failed");
|
||||
ESP_RETURN_ON_ERROR(DVP_GDMA_NEW_CHANNEL(&rx_alloc_config, &dma->dma_chan), TAG, "new channel failed");
|
||||
|
||||
ESP_GOTO_ON_ERROR(gdma_connect(dma->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_CAM, 0)), fail0, TAG, "connect failed");
|
||||
|
||||
@@ -88,17 +103,20 @@ esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_
|
||||
.access_ext_mem = true,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gdma_config_transfer(dma->dma_chan, &transfer_config), fail1, TAG, "set trans ability failed");
|
||||
size_t int_mem_align = 0;
|
||||
size_t ext_mem_align = 0;
|
||||
gdma_get_alignment_constraints(dma->dma_chan, &int_mem_align, &ext_mem_align);
|
||||
|
||||
dma->desc_count = size / ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE;
|
||||
if (size % ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE) {
|
||||
dma->desc_count++;
|
||||
}
|
||||
dma->size = size;
|
||||
|
||||
ESP_LOGD(TAG, "alignment: 0x%x\n", alignment_size);
|
||||
alignment_size = (alignment_size == 0) ? 1 : alignment_size;
|
||||
dma->desc_size = ALIGN_UP_BY(dma->desc_count * sizeof(esp_cam_ctlr_dvp_dma_desc_t), alignment_size);
|
||||
|
||||
dma->desc = heap_caps_aligned_alloc(alignment_size, dma->desc_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
|
||||
ESP_LOGD(TAG, "alignment_size: %d, dma->desc_count: %d, dma->desc_size: %d", alignment_size, dma->desc_count, dma->desc_size);
|
||||
dma->desc = heap_caps_aligned_alloc(alignment_size, dma->desc_size, DVP_GDMA_DESC_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(dma->desc, ESP_ERR_NO_MEM, fail1, TAG, "no mem for DVP DMA descriptor");
|
||||
|
||||
return ESP_OK;
|
||||
@@ -142,15 +160,15 @@ esp_err_t esp_cam_ctlr_dvp_dma_deinit(esp_cam_ctlr_dvp_dma_t *dma)
|
||||
*/
|
||||
esp_err_t IRAM_ATTR esp_cam_ctlr_dvp_dma_start(esp_cam_ctlr_dvp_dma_t *dma, uint8_t *buffer, size_t size)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
ESP_RETURN_ON_FALSE_ISR(dma, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE_ISR(dma->size >= size, ESP_ERR_INVALID_ARG, TAG, "input buffer size is out of range");
|
||||
|
||||
esp_cam_ctlr_dvp_config_dma_desc(dma->desc, buffer, size);
|
||||
|
||||
ret = esp_cache_msync(dma->desc, dma->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
assert(ret == ESP_OK);
|
||||
if (esp_ptr_external_ram(dma->desc)) {
|
||||
esp_err_t ret = esp_cache_msync(dma->desc, dma->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
assert(ret == ESP_OK);
|
||||
}
|
||||
|
||||
return gdma_start(dma->dma_chan, (intptr_t)dma->desc);
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
||||
| Supported Targets | ESP32-P4 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
@@ -17,7 +17,7 @@ TEST_CASE("TEST DVP driver allocation", "[DVP]")
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.pin_dont_init = true,
|
||||
.external_xtal = true,
|
||||
@@ -42,7 +42,7 @@ TEST_CASE("TEST DVP driver allocation with JPEG input", "[DVP]")
|
||||
.clk_src = CAM_CLK_SRC_DEFAULT,
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.pin_dont_init = true,
|
||||
.pic_format_jpeg = true,
|
||||
@@ -69,7 +69,7 @@ TEST_CASE("TEST DVP driver no backup buffer usage", "[DVP]")
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.bk_buffer_dis = true,
|
||||
.pin_dont_init = true,
|
||||
@@ -96,7 +96,7 @@ TEST_CASE("TEST DVP driver intern/extern init", "[DVP]")
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.external_xtal = true,
|
||||
};
|
||||
@@ -128,7 +128,7 @@ TEST_CASE("TEST DVP driver intern/extern generate xclk", "[DVP]")
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.external_xtal = true,
|
||||
};
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
['cache_safe'],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
|
||||
def test_dvp(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
|
||||
|
||||
@pytest.mark.octal_psram
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
['cache_safe'],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32s3'], indirect=['target'])
|
||||
def test_dvp_octal(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_OCT=y
|
||||
Reference in New Issue
Block a user