From f918079e8ea0a5b63aae3cbc84458f9138943848 Mon Sep 17 00:00:00 2001 From: Armando Date: Mon, 29 Jan 2024 12:16:09 +0800 Subject: [PATCH 1/2] feat(csi): added csi driver --- components/esp_cam_ctlr/CMakeLists.txt | 13 + components/esp_cam_ctlr/Kconfig | 11 + .../csi/include/esp_cam_ctlr_csi.h | 56 +++ .../esp_cam_ctlr/csi/src/esp_cam_ctlr_csi.c | 458 ++++++++++++++++++ .../csi/src/esp_cam_ctlr_csi_internal.h | 68 +++ components/esp_cam_ctlr/esp_cam_ctlr.c | 73 +++ .../esp_cam_ctlr/include/esp_cam_ctlr.h | 106 ++++ .../esp_cam_ctlr/include/esp_cam_ctlr_types.h | 61 +++ .../interface/esp_cam_ctlr_interface.h | 113 +++++ .../test_apps/.build-test-rules.yml | 5 + .../esp_cam_ctlr/test_apps/csi/CMakeLists.txt | 8 + .../esp_cam_ctlr/test_apps/csi/README.md | 2 + .../test_apps/csi/main/CMakeLists.txt | 16 + .../test_apps/csi/main/test_app_main.c | 27 ++ .../test_apps/csi/main/test_csi_driver.c | 29 ++ .../esp_cam_ctlr/test_apps/csi/pytest_csi.py | 10 + .../test_apps/csi/sdkconfig.defaults | 7 + components/esp_lcd/dsi/esp_lcd_panel_dpi.c | 2 +- components/hal/CMakeLists.txt | 4 + .../hal/esp32p4/include/hal/dw_gdma_ll.h | 8 +- .../hal/esp32p4/include/hal/mipi_csi_brg_ll.h | 69 +++ .../esp32p4/include/hal/mipi_csi_host_ll.h | 69 +++ .../hal/esp32p4/include/hal/mipi_csi_ll.h | 87 ++++ .../hal/esp32p4/include/hal/mipi_csi_phy_ll.h | 76 +++ components/hal/include/hal/mipi_csi_hal.h | 59 +++ components/hal/include/hal/mipi_csi_types.h | 43 ++ components/hal/mipi_csi_hal.c | 73 +++ components/soc/CMakeLists.txt | 4 + .../esp32p4/include/soc/Kconfig.soc_caps.in | 4 + .../soc/esp32p4/include/soc/clk_tree_defs.h | 15 + .../include/soc/mipi_csi_bridge_struct.h | 4 +- components/soc/esp32p4/include/soc/reg_base.h | 4 +- components/soc/esp32p4/include/soc/soc_caps.h | 1 + components/soc/esp32p4/mipi_csi_periph.c | 50 ++ components/soc/include/soc/mipi_csi_periph.h | 31 ++ 35 files changed, 1657 insertions(+), 9 deletions(-) create mode 100644 components/esp_cam_ctlr/CMakeLists.txt create mode 100644 components/esp_cam_ctlr/Kconfig create mode 100644 components/esp_cam_ctlr/csi/include/esp_cam_ctlr_csi.h create mode 100644 components/esp_cam_ctlr/csi/src/esp_cam_ctlr_csi.c create mode 100644 components/esp_cam_ctlr/csi/src/esp_cam_ctlr_csi_internal.h create mode 100644 components/esp_cam_ctlr/esp_cam_ctlr.c create mode 100644 components/esp_cam_ctlr/include/esp_cam_ctlr.h create mode 100644 components/esp_cam_ctlr/include/esp_cam_ctlr_types.h create mode 100644 components/esp_cam_ctlr/interface/esp_cam_ctlr_interface.h create mode 100644 components/esp_cam_ctlr/test_apps/.build-test-rules.yml create mode 100644 components/esp_cam_ctlr/test_apps/csi/CMakeLists.txt create mode 100644 components/esp_cam_ctlr/test_apps/csi/README.md create mode 100644 components/esp_cam_ctlr/test_apps/csi/main/CMakeLists.txt create mode 100644 components/esp_cam_ctlr/test_apps/csi/main/test_app_main.c create mode 100644 components/esp_cam_ctlr/test_apps/csi/main/test_csi_driver.c create mode 100644 components/esp_cam_ctlr/test_apps/csi/pytest_csi.py create mode 100644 components/esp_cam_ctlr/test_apps/csi/sdkconfig.defaults create mode 100644 components/hal/esp32p4/include/hal/mipi_csi_host_ll.h create mode 100644 components/hal/esp32p4/include/hal/mipi_csi_phy_ll.h create mode 100644 components/hal/include/hal/mipi_csi_hal.h create mode 100644 components/hal/include/hal/mipi_csi_types.h create mode 100644 components/hal/mipi_csi_hal.c create mode 100644 components/soc/esp32p4/mipi_csi_periph.c create mode 100644 components/soc/include/soc/mipi_csi_periph.h diff --git a/components/esp_cam_ctlr/CMakeLists.txt b/components/esp_cam_ctlr/CMakeLists.txt new file mode 100644 index 0000000000..b6a0c4dee4 --- /dev/null +++ b/components/esp_cam_ctlr/CMakeLists.txt @@ -0,0 +1,13 @@ +set(srcs "esp_cam_ctlr.c") + +set(include "include" "interface") + +if(CONFIG_SOC_MIPI_CSI_SUPPORTED) + list(APPEND srcs "csi/src/esp_cam_ctlr_csi.c") + list(APPEND include "csi/include") +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include} + PRIV_REQUIRES esp_mm + ) diff --git a/components/esp_cam_ctlr/Kconfig b/components/esp_cam_ctlr/Kconfig new file mode 100644 index 0000000000..f7b3ceaa6b --- /dev/null +++ b/components/esp_cam_ctlr/Kconfig @@ -0,0 +1,11 @@ +menu "ESP Camera Controller Configurations" + depends on SOC_MIPI_CSI_SUPPORTED + + config MIPI_CSI_ISR_IRAM_SAFE + bool "CSI ISR IRAM-Safe" + default n + help + Ensure the CSI driver ISR is IRAM-Safe. When enabled, the ISR handler + will be available when the cache is disabled. + +endmenu # ESP Camera Controller Configurations diff --git a/components/esp_cam_ctlr/csi/include/esp_cam_ctlr_csi.h b/components/esp_cam_ctlr/csi/include/esp_cam_ctlr_csi.h new file mode 100644 index 0000000000..f2fe395b0e --- /dev/null +++ b/components/esp_cam_ctlr/csi/include/esp_cam_ctlr_csi.h @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include "esp_err.h" +#include "hal/mipi_csi_types.h" +#include "esp_cam_ctlr_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ESP CAM controller max timeout value + */ +#define ESP_CAM_CTLR_MAX_DELAY UINT32_MAX + +/** + * @brief ESP CAM CSI controller configurations + */ +typedef struct { + int ctlr_id; ///< CSI controller ID + mipi_csi_phy_clock_source_t clk_src; ///< CSI phy clock source + uint32_t h_res; ///< Input horizontal resolution, i.e. the number of pixels in a line + uint32_t v_res; ///< Input vertical resolution, i.e. the number of lines in a frame + uint8_t data_lane_num; ///< Data lane num + int clk_freq_hz; ///< Frequency of CLK, in Hz. + mipi_csi_color_t input_data_color_type; ///< Input color type + mipi_csi_color_t output_data_color_type; ///< Output color type + bool byte_swap_en; ///< Enable byte swap + int queue_items; ///< Queue itmes +} esp_cam_ctlr_csi_config_t; + +/** + * @brief New ESP CAM CSI controller + * + * @param[in] config CSI controller configurations + * @param[out] ret_handle Returned ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_NO_MEM: Out of memory + * - ESP_ERR_NOT_SUPPORTED: Currently not support modes or types + * - ESP_ERR_NOT_FOUND: CSI is registered already + */ +esp_err_t esp_cam_new_csi_ctlr(const esp_cam_ctlr_csi_config_t *config, esp_cam_ctlr_handle_t *ret_handle); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_cam_ctlr/csi/src/esp_cam_ctlr_csi.c b/components/esp_cam_ctlr/csi/src/esp_cam_ctlr_csi.c new file mode 100644 index 0000000000..9545ce2dd8 --- /dev/null +++ b/components/esp_cam_ctlr/csi/src/esp_cam_ctlr_csi.c @@ -0,0 +1,458 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/idf_additions.h" +#include "esp_clk_tree.h" +#include "esp_cam_ctlr.h" +#include "esp_cam_ctlr_csi.h" +#include "esp_cam_ctlr_interface.h" +#include "esp_cam_ctlr_csi_internal.h" +#include "hal/mipi_csi_ll.h" +#include "hal/color_hal.h" +#include "esp_private/periph_ctrl.h" +#include "esp_private/mipi_csi_share_hw_ctrl.h" +#include "esp_private/esp_cache_private.h" +#include "esp_cache.h" + +#if CONFIG_MIPI_CSI_ISR_IRAM_SAFE +#define CSI_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) +#else +#define CSI_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT +#endif + +typedef struct csi_platform_t { + _lock_t mutex; + csi_controller_t *controllers[MIPI_CSI_LL_HOST_CTLR_NUMS]; +} csi_platform_t; + +static const char *TAG = "CSI"; +static csi_platform_t s_platform; + +static bool csi_dma_trans_done_callback(dw_gdma_channel_handle_t chan, const dw_gdma_trans_done_event_data_t *event_data, void *user_data); +static esp_err_t s_del_csi_ctlr(csi_controller_t *ctlr); +static esp_err_t s_ctlr_del(esp_cam_ctlr_t *cam_ctlr); +static esp_err_t s_register_event_callbacks(esp_cam_ctlr_handle_t handle, const esp_cam_ctlr_evt_cbs_t *cbs, void *user_data); +static esp_err_t s_csi_ctlr_enable(esp_cam_ctlr_handle_t ctlr); +static esp_err_t s_ctlr_csi_start(esp_cam_ctlr_handle_t handle); +static esp_err_t s_ctlr_csi_stop(esp_cam_ctlr_handle_t handle); +static esp_err_t s_csi_ctlr_disable(esp_cam_ctlr_handle_t ctlr); +static esp_err_t s_ctlr_csi_receive(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, uint32_t timeout_ms); + +static esp_err_t s_csi_claim_controller(csi_controller_t *controller) +{ + assert(controller); + + _lock_acquire(&s_platform.mutex); + bool found = false; + for (int i = 0; i < MIPI_CSI_LL_HOST_CTLR_NUMS; i ++) { + found = !s_platform.controllers[i]; + if (found) { + s_platform.controllers[i] = controller; + controller->csi_id = i; + PERIPH_RCC_ATOMIC() { + mipi_csi_ll_enable_host_bus_clock(i, 0); + mipi_csi_ll_enable_host_bus_clock(i, 1); + mipi_csi_ll_reset_host_clock(i); + } + _lock_release(&s_platform.mutex); + break; + } + } + _lock_release(&s_platform.mutex); + + if (!found) { + return ESP_ERR_NOT_FOUND; + } + return ESP_OK; +} + +static esp_err_t s_csi_declaim_controller(csi_controller_t *controller) +{ + assert(controller); + + _lock_acquire(&s_platform.mutex); + s_platform.controllers[controller->csi_id] = NULL; + PERIPH_RCC_ATOMIC() { + mipi_csi_ll_enable_host_bus_clock(controller->csi_id, 0); + } + _lock_release(&s_platform.mutex); + + return ESP_OK; +} + +esp_err_t esp_cam_new_csi_ctlr(const esp_cam_ctlr_csi_config_t *config, esp_cam_ctlr_handle_t *ret_handle) +{ + esp_err_t ret = ESP_FAIL; + ESP_RETURN_ON_FALSE(config && ret_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(config->data_lane_num <= MIPI_CSI_HOST_LL_LANE_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "lane num should be equal or smaller than %d", MIPI_CSI_HOST_LL_LANE_NUM_MAX); + + csi_controller_t *ctlr = heap_caps_calloc(1, sizeof(csi_controller_t), CSI_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(ctlr, ESP_ERR_NO_MEM, TAG, "no mem for csi controller context"); + + ESP_LOGD(TAG, "config->queue_items: %d", config->queue_items); + ctlr->trans_que = xQueueCreateWithCaps(config->queue_items, sizeof(esp_cam_ctlr_trans_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + ESP_GOTO_ON_FALSE(ctlr->trans_que, ESP_ERR_NO_MEM, err, TAG, "no memory for transaction queue"); + + //claim a controller, then do assignment + ESP_GOTO_ON_ERROR(s_csi_claim_controller(ctlr), err, TAG, "no available csi controller"); +#if SOC_ISP_SHARE_CSI_BRG + ESP_GOTO_ON_ERROR(mipi_csi_brg_claim(MIPI_CSI_BRG_USER_CSI, &ctlr->csi_brg_id), err, TAG, "csi bridge is in use already"); + ctlr->csi_brg_in_use = true; +#endif + + mipi_csi_phy_clock_source_t clk_src = !config->clk_src ? MIPI_CSI_PHY_CLK_SRC_DEFAULT : config->clk_src; + PERIPH_RCC_ATOMIC() { + // phy clock source setting + mipi_csi_ll_set_phy_clock_source(ctlr->csi_id, clk_src); + // phy clock reset + mipi_csi_ll_enable_phy_config_clock(ctlr->csi_id, 0); + mipi_csi_ll_enable_phy_config_clock(ctlr->csi_id, 1); + } + + ctlr->h_res = config->h_res; + ctlr->v_res = config->v_res; + ESP_LOGD(TAG, "ctlr->h_res: 0d %"PRId32, ctlr->h_res); + ESP_LOGD(TAG, "ctlr->v_res: 0d %"PRId32, ctlr->v_res); + + //in color type + color_space_pixel_format_t in_color_format = { + .color_type_id = config->input_data_color_type, + }; + int in_bits_per_pixel = color_hal_pixel_format_get_bit_depth(in_color_format); + ctlr->in_color_format = in_color_format; + ctlr->in_bpp = in_bits_per_pixel; + ESP_LOGD(TAG, "ctlr->in_bpp: 0d %d", ctlr->in_bpp); + + //out color type + color_space_pixel_format_t out_color_format = { + .color_type_id = config->output_data_color_type, + }; + int out_bits_per_pixel = color_hal_pixel_format_get_bit_depth(out_color_format); + ctlr->out_bpp = out_bits_per_pixel; + ESP_LOGD(TAG, "ctlr->out_bpp: 0d %d", ctlr->out_bpp); + + // Note: Width * Height * BitsPerPixel must be divisible by 8 + int fb_size_in_bits = config->v_res * config->h_res * out_bits_per_pixel; + ESP_GOTO_ON_FALSE((fb_size_in_bits % 8 == 0), ESP_ERR_INVALID_ARG, err, TAG, "framesize not 8bit aligned"); + ctlr->fb_size_in_bytes = fb_size_in_bits / 8; + ESP_LOGD(TAG, "ctlr->fb_size_in_bytes=%d", ctlr->fb_size_in_bytes); + + size_t dma_alignment = 4; //TODO: IDF-9126, replace with dwgdma alignment API + size_t cache_alignment = 1; + ESP_GOTO_ON_ERROR(esp_cache_get_alignment(ESP_CACHE_MALLOC_FLAG_PSRAM | ESP_CACHE_MALLOC_FLAG_DMA, &cache_alignment), err, TAG, "failed to get cache alignment"); + size_t alignment = MAX(cache_alignment, dma_alignment); + ESP_LOGD(TAG, "alignment: 0x%x\n", alignment); + + ctlr->backup_buffer = heap_caps_aligned_alloc(alignment, ctlr->fb_size_in_bytes, MALLOC_CAP_SPIRAM); + ESP_GOTO_ON_FALSE(ctlr->backup_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for backup buffer"); + ESP_LOGD(TAG, "ctlr->backup_buffer: %p\n", ctlr->backup_buffer); + esp_cache_msync((void *)(ctlr->backup_buffer), ctlr->fb_size_in_bytes, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + + mipi_csi_hal_config_t hal_config; + hal_config.frame_height = config->h_res; + hal_config.frame_width = config->v_res; + hal_config.clk_freq_hz = config->clk_freq_hz; + hal_config.lanes_num = config->data_lane_num; + hal_config.byte_swap_en = config->byte_swap_en; + mipi_csi_hal_init(&ctlr->hal, &hal_config); + mipi_csi_brg_ll_set_burst_len(ctlr->hal.bridge_dev, 512); + + //---------------DWGDMA Init For CSI------------------// + dw_gdma_channel_handle_t csi_dma_chan = NULL; + dw_gdma_channel_alloc_config_t csi_dma_alloc_config = { + .src = { + .block_transfer_type = DW_GDMA_BLOCK_TRANSFER_CONTIGUOUS, + .role = DW_GDMA_ROLE_PERIPH_CSI, + .handshake_type = DW_GDMA_HANDSHAKE_HW, + .num_outstanding_requests = 5, + .status_fetch_addr = MIPI_CSI_BRG_MEM_BASE, + }, + .dst = { + .block_transfer_type = DW_GDMA_BLOCK_TRANSFER_CONTIGUOUS, + .role = DW_GDMA_ROLE_MEM, + .handshake_type = DW_GDMA_HANDSHAKE_HW, + .num_outstanding_requests = 5, + }, + .flow_controller = DW_GDMA_FLOW_CTRL_SRC, + .chan_priority = 1, + }; + ESP_GOTO_ON_ERROR(dw_gdma_new_channel(&csi_dma_alloc_config, &csi_dma_chan), err, TAG, "failed to new dwgdma channle"); + ctlr->dma_chan = csi_dma_chan; + + size_t csi_transfer_size = ctlr->h_res * ctlr->v_res * ctlr->in_bpp / 64; + ctlr->csi_transfer_size = csi_transfer_size; + ESP_LOGD(TAG, "csi_transfer_size: 0d %d", csi_transfer_size); + + dw_gdma_event_callbacks_t csi_dma_cbs = { + .on_full_trans_done = csi_dma_trans_done_callback, + }; + ESP_GOTO_ON_ERROR(dw_gdma_channel_register_event_callbacks(csi_dma_chan, &csi_dma_cbs, ctlr), err, TAG, "failed to register dwgdma callback"); + + ctlr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + ctlr->csi_fsm = CSI_FSM_INIT; + ctlr->base.del = s_ctlr_del; + ctlr->base.enable = s_csi_ctlr_enable; + ctlr->base.start = s_ctlr_csi_start; + ctlr->base.stop = s_ctlr_csi_stop; + ctlr->base.disable = s_csi_ctlr_disable; + ctlr->base.receive = s_ctlr_csi_receive; + ctlr->base.register_event_callbacks = s_register_event_callbacks; + + *ret_handle = &(ctlr->base); + + return ESP_OK; + +err: + s_del_csi_ctlr(ctlr); + + return ret; +} + +esp_err_t s_del_csi_ctlr(csi_controller_t *ctlr) +{ + ESP_RETURN_ON_FALSE(ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(ctlr->csi_fsm == CSI_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "processor isn't in init state"); + + if (ctlr->dma_chan) { + ESP_RETURN_ON_ERROR(dw_gdma_del_channel(ctlr->dma_chan), TAG, "failed to delete dwgdma channel"); + } + //declaim first, then do free + ESP_RETURN_ON_ERROR(s_csi_declaim_controller(ctlr), TAG, "declaim processor fail"); +#if SOC_ISP_SHARE_CSI_BRG + if (ctlr->csi_brg_in_use) { + ESP_RETURN_ON_ERROR(mipi_csi_brg_declaim(ctlr->csi_brg_id), TAG, "declaim csi bridge fail"); + ctlr->csi_brg_in_use = false; + } +#endif + PERIPH_RCC_ATOMIC() { + mipi_csi_ll_enable_phy_config_clock(ctlr->csi_id, 0); + } + free(ctlr->backup_buffer); + vQueueDeleteWithCaps(ctlr->trans_que); + free(ctlr); + + return ESP_OK; +} + +static esp_err_t s_ctlr_del(esp_cam_ctlr_t *cam_ctlr) +{ + csi_controller_t *csi_ctlr = __containerof(cam_ctlr, csi_controller_t, base); + ESP_RETURN_ON_ERROR(s_del_csi_ctlr(csi_ctlr), TAG, "failed to del csi_ctlr"); + return ESP_OK; +} + +static bool csi_dma_trans_done_callback(dw_gdma_channel_handle_t chan, const dw_gdma_trans_done_event_data_t *event_data, void *user_data) +{ + bool need_yield = false; + BaseType_t high_task_woken = pdFALSE; + csi_controller_t *ctlr = (csi_controller_t *)user_data; + bool use_backup = false; + + dw_gdma_block_transfer_config_t csi_dma_transfer_config = {}; + csi_dma_transfer_config = (dw_gdma_block_transfer_config_t) { + .src = { + .addr = MIPI_CSI_BRG_MEM_BASE, + .burst_mode = DW_GDMA_BURST_MODE_FIXED, + .burst_items = DW_GDMA_BURST_ITEMS_512, + .burst_len = 16, + .width = DW_GDMA_TRANS_WIDTH_64, + }, + .dst = { + .addr = 0, + .burst_mode = DW_GDMA_BURST_MODE_INCREMENT, + .burst_items = DW_GDMA_BURST_ITEMS_512, + .burst_len = 16, + .width = DW_GDMA_TRANS_WIDTH_64, + }, + .size = ctlr->csi_transfer_size, + }; + esp_cam_ctlr_trans_t new_trans = {}; + + if (ctlr->cbs.on_get_new_trans) { + need_yield = ctlr->cbs.on_get_new_trans(&(ctlr->base), &new_trans, ctlr->cbs_user_data); + assert(new_trans.buflen >= ctlr->fb_size_in_bytes); + csi_dma_transfer_config.dst.addr = (uint32_t)(new_trans.buffer); + } else if (xQueueReceiveFromISR(ctlr->trans_que, &new_trans, &high_task_woken) == pdTRUE) { + assert(new_trans.buflen >= ctlr->fb_size_in_bytes); + csi_dma_transfer_config.dst.addr = (uint32_t)(new_trans.buffer); + } else { + use_backup = true; + new_trans.buffer = ctlr->backup_buffer; + new_trans.buflen = ctlr->fb_size_in_bytes; + ESP_EARLY_LOGD(TAG, "no new buffer, use driver internal buffer"); + csi_dma_transfer_config.dst.addr = (uint32_t)ctlr->backup_buffer; + } + + if (!use_backup) { + esp_err_t ret = esp_cache_msync((void *)(ctlr->trans.buffer), ctlr->trans.received_size, ESP_CACHE_MSYNC_FLAG_INVALIDATE); + assert(ret == ESP_OK); + } + + ESP_EARLY_LOGD(TAG, "new_trans.buffer: %p, new_trans.buflen: %d", new_trans.buffer, new_trans.buflen); + dw_gdma_channel_config_transfer(chan, &csi_dma_transfer_config); + dw_gdma_channel_enable_ctrl(chan, true); + + if (!use_backup) { + assert(ctlr->cbs.on_trans_finished); + if (ctlr->cbs.on_trans_finished) { + ctlr->trans.received_size = ctlr->fb_size_in_bytes; + need_yield = ctlr->cbs.on_trans_finished(&(ctlr->base), &ctlr->trans, ctlr->cbs_user_data); + } + } + + //ctlr->trans is the transaction saved before dma starts + memset(&ctlr->trans, 0x0, sizeof(esp_cam_ctlr_trans_t)); + ctlr->trans = new_trans; + + need_yield |= high_task_woken == pdTRUE; + return need_yield; +} + +esp_err_t s_register_event_callbacks(esp_cam_ctlr_handle_t handle, const esp_cam_ctlr_evt_cbs_t *cbs, void *user_data) +{ + ESP_RETURN_ON_FALSE(handle && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + csi_controller_t *ctlr = __containerof(handle, csi_controller_t, base); + ESP_RETURN_ON_FALSE(ctlr->csi_fsm == CSI_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "driver starts already, not allow cbs register"); + +#if CONFIG_MIPI_CSI_ISR_IRAM_SAFE + if (cbs->on_get_new_trans) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_get_new_trans), ESP_ERR_INVALID_ARG, TAG, "on_get_new_trans callback not in IRAM"); + } + if (cbs->on_trans_finished) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_trans_finished), ESP_ERR_INVALID_ARG, TAG, "on_trans_finished callback not in IRAM"); + } +#endif + + ctlr->cbs.on_get_new_trans = cbs->on_get_new_trans; + ctlr->cbs.on_trans_finished = cbs->on_trans_finished; + ctlr->cbs_user_data = user_data; + + return ESP_OK; +} + +esp_err_t s_csi_ctlr_enable(esp_cam_ctlr_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + csi_controller_t *ctlr = __containerof(handle, csi_controller_t, base); + ESP_RETURN_ON_FALSE(ctlr->csi_fsm == CSI_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "processor isn't in init state"); + + portENTER_CRITICAL(&ctlr->spinlock); + ctlr->csi_fsm = CSI_FSM_ENABLED; + portEXIT_CRITICAL(&ctlr->spinlock); + + return ESP_OK; +} + +esp_err_t s_csi_ctlr_disable(esp_cam_ctlr_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + csi_controller_t *ctlr = __containerof(handle, csi_controller_t, base); + ESP_RETURN_ON_FALSE(ctlr->csi_fsm == CSI_FSM_ENABLED, ESP_ERR_INVALID_STATE, TAG, "processor isn't in init state"); + + portENTER_CRITICAL(&ctlr->spinlock); + ctlr->csi_fsm = CSI_FSM_INIT; + portEXIT_CRITICAL(&ctlr->spinlock); + + return ESP_OK; +} + +esp_err_t s_ctlr_csi_start(esp_cam_ctlr_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + csi_controller_t *ctlr = __containerof(handle, csi_controller_t, base); + ESP_RETURN_ON_FALSE(ctlr->csi_fsm == CSI_FSM_ENABLED, ESP_ERR_INVALID_STATE, TAG, "driver starts already, should not start again"); + ESP_RETURN_ON_FALSE(ctlr->cbs.on_trans_finished, ESP_ERR_INVALID_STATE, TAG, "no on_trans_finished callback registered"); + + esp_cam_ctlr_trans_t trans = {}; + if (ctlr->cbs.on_get_new_trans) { + ctlr->cbs.on_get_new_trans(handle, &trans, ctlr->cbs_user_data); + ESP_RETURN_ON_FALSE(trans.buffer, ESP_ERR_INVALID_STATE, TAG, "no ready transaction, cannot start"); + } else { + trans.buffer = ctlr->backup_buffer; + trans.buflen = ctlr->fb_size_in_bytes; + } + + ESP_LOGD(TAG, "trans.buffer: %p, trans.buflen: %d", trans.buffer, trans.buflen); + ctlr->trans = trans; + + portENTER_CRITICAL(&ctlr->spinlock); + ctlr->csi_fsm = CSI_FSM_STARTED; + portEXIT_CRITICAL(&ctlr->spinlock); + + dw_gdma_block_transfer_config_t csi_dma_transfer_config = {}; + csi_dma_transfer_config = (dw_gdma_block_transfer_config_t) { + .src = { + .addr = MIPI_CSI_BRG_MEM_BASE, + .burst_mode = DW_GDMA_BURST_MODE_FIXED, + .burst_items = DW_GDMA_BURST_ITEMS_512, + .burst_len = 16, + .width = DW_GDMA_TRANS_WIDTH_64, + }, + .dst = { + .addr = (uint32_t)(trans.buffer), + .burst_mode = DW_GDMA_BURST_MODE_INCREMENT, + .burst_items = DW_GDMA_BURST_ITEMS_512, + .burst_len = 16, + .width = DW_GDMA_TRANS_WIDTH_64, + }, + .size = ctlr->csi_transfer_size, + }; + ESP_RETURN_ON_ERROR(dw_gdma_channel_config_transfer(ctlr->dma_chan, &csi_dma_transfer_config), TAG, "failed to configure dwgdma transfer"); + ESP_RETURN_ON_ERROR(dw_gdma_channel_enable_ctrl(ctlr->dma_chan, true), TAG, "failed to enable dwgdma"); + + //enable CSI bridge + mipi_csi_brg_ll_enable(ctlr->hal.bridge_dev, true); + + return ESP_OK; +} + +esp_err_t s_ctlr_csi_stop(esp_cam_ctlr_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + csi_controller_t *ctlr = __containerof(handle, csi_controller_t, base); + ESP_RETURN_ON_FALSE(ctlr->csi_fsm == CSI_FSM_ENABLED, ESP_ERR_INVALID_STATE, TAG, "driver isn't started"); + + //disable CSI bridge + mipi_csi_brg_ll_enable(ctlr->hal.bridge_dev, false); + ESP_RETURN_ON_ERROR(dw_gdma_channel_enable_ctrl(ctlr->dma_chan, false), TAG, "failed to disable dwgdma"); + + portENTER_CRITICAL(&ctlr->spinlock); + ctlr->csi_fsm = CSI_FSM_INIT; + portEXIT_CRITICAL(&ctlr->spinlock); + + return ESP_OK; +} + +esp_err_t s_ctlr_csi_receive(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, uint32_t timeout_ms) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(handle && trans, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + csi_controller_t *ctlr = __containerof(handle, csi_controller_t, base); + + ESP_RETURN_ON_FALSE(trans->buffer, ESP_ERR_INVALID_ARG, TAG, "invalid argument: no trans buffer"); + ESP_RETURN_ON_FALSE((trans->buflen >= ctlr->fb_size_in_bytes), ESP_ERR_INVALID_ARG, TAG, "invalid argument: trans buffer smaller than framebuffer size"); + + TickType_t ticks_to_wait = timeout_ms / portTICK_PERIOD_MS; + if (timeout_ms == ESP_CAM_CTLR_MAX_DELAY) { + ticks_to_wait = portMAX_DELAY; + } + + BaseType_t r = xQueueSend(ctlr->trans_que, trans, ticks_to_wait); + if (r != pdTRUE) { + ret = ESP_ERR_TIMEOUT; + ESP_LOGW(TAG, "csi recv API, transaction queue is full, failed to send transaction to the queue"); + return ret; + } + + return ESP_OK; +} diff --git a/components/esp_cam_ctlr/csi/src/esp_cam_ctlr_csi_internal.h b/components/esp_cam_ctlr/csi/src/esp_cam_ctlr_csi_internal.h new file mode 100644 index 0000000000..a163acd7ba --- /dev/null +++ b/components/esp_cam_ctlr/csi/src/esp_cam_ctlr_csi_internal.h @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "esp_cam_ctlr_csi.h" +#include "hal/mipi_csi_hal.h" +#include "hal/mipi_csi_types.h" +#include "soc/soc_caps.h" +#include "esp_private/dw_gdma.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + CSI_FSM_INIT, + CSI_FSM_ENABLED, + CSI_FSM_STARTED, +} csi_fsm_t; + +/*--------------------------------------------------------------- + Driver Context +---------------------------------------------------------------*/ +typedef struct csi_controller_t csi_controller_t; + +struct csi_controller_t { + int csi_id; //csi id +#if SOC_ISP_SHARE_CSI_BRG + int csi_brg_id; //csi bridge id + void *csi_brg_hw; //csi bridge hardware context + bool csi_brg_in_use; //csi bridge is in use +#endif + mipi_csi_hal_context_t hal; //hal context + csi_fsm_t csi_fsm; //driver fsm + portMUX_TYPE spinlock; //spinlock + color_space_pixel_format_t in_color_format; //input color format + color_space_pixel_format_t out_color_format; //output color format + uint32_t h_res; //input horizontal resolution + uint32_t v_res; //input vertical resolution + int in_bpp; //input data type, bit per pixel + int out_bpp; //output data type, bit per pixel + size_t fb_size_in_bytes; //Frame buffer size, in bytes + esp_cam_ctlr_trans_t trans; //Saved done transaction to be given out to callers + void *backup_buffer; //backup buffer to make csi bridge can work to avoid wrong state + QueueHandle_t trans_que; //transaction queue + esp_cam_ctlr_evt_cbs_t cbs; //user callbacks + void *cbs_user_data; //callback userdata + dw_gdma_channel_handle_t dma_chan; //dwgdma channel handle + size_t csi_transfer_size; //csi transfer size for dwgdma + esp_cam_ctlr_t base; +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_cam_ctlr/esp_cam_ctlr.c b/components/esp_cam_ctlr/esp_cam_ctlr.c new file mode 100644 index 0000000000..d2f4b3a94c --- /dev/null +++ b/components/esp_cam_ctlr/esp_cam_ctlr.c @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_types.h" +#include "sdkconfig.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_cam_ctlr.h" +#include "esp_cam_ctlr_interface.h" + +static const char *TAG = "CAM_CTLR"; + +esp_err_t esp_cam_ctlr_enable(esp_cam_ctlr_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(handle->enable, ESP_ERR_NOT_SUPPORTED, TAG, "controller driver function not supported"); + + return handle->enable(handle); +} + +esp_err_t esp_cam_ctlr_start(esp_cam_ctlr_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(handle->start, ESP_ERR_NOT_SUPPORTED, TAG, "controller driver function not supported"); + + return handle->start(handle); +} + +esp_err_t esp_cam_ctlr_stop(esp_cam_ctlr_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(handle->stop, ESP_ERR_NOT_SUPPORTED, TAG, "controller driver function not supported"); + + return handle->stop(handle); +} + +esp_err_t esp_cam_ctlr_disable(esp_cam_ctlr_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(handle->disable, ESP_ERR_NOT_SUPPORTED, TAG, "controller driver function not supported"); + + return handle->disable(handle); +} + +esp_err_t esp_cam_ctlr_receive(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, uint32_t timeout_ms) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(handle->receive, ESP_ERR_NOT_SUPPORTED, TAG, "controller driver function not supported"); + + return handle->receive(handle, trans, timeout_ms); +} + +esp_err_t esp_cam_ctlr_register_event_callbacks(esp_cam_ctlr_handle_t handle, const esp_cam_ctlr_evt_cbs_t *cbs, void *user_data) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(handle->register_event_callbacks, ESP_ERR_NOT_SUPPORTED, TAG, "controller driver function not supported"); + + return handle->register_event_callbacks(handle, cbs, user_data); +} + +esp_err_t esp_cam_del_ctlr(esp_cam_ctlr_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(handle->del, ESP_ERR_NOT_SUPPORTED, TAG, "controller driver function not supported"); + + return handle->del(handle); +} diff --git a/components/esp_cam_ctlr/include/esp_cam_ctlr.h b/components/esp_cam_ctlr/include/esp_cam_ctlr.h new file mode 100644 index 0000000000..fa3995917b --- /dev/null +++ b/components/esp_cam_ctlr/include/esp_cam_ctlr.h @@ -0,0 +1,106 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_err.h" +#include "esp_cam_ctlr_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enable ESP CAM controller + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t esp_cam_ctlr_enable(esp_cam_ctlr_handle_t handle); + +/** + * @brief Start ESP CAM controller + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t esp_cam_ctlr_start(esp_cam_ctlr_handle_t handle); + +/** + * @brief Stop ESP CAM controller + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t esp_cam_ctlr_stop(esp_cam_ctlr_handle_t handle); + +/** + * @brief Disable ESP CAM controller + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t esp_cam_ctlr_disable(esp_cam_ctlr_handle_t handle); + +/** + * @brief Receive data to the given transaction + * + * @param[in] handle ESP CAM controller handle + * @param[in] trans ESP CAM controller transaction type + * @param[in] timeout_ms Timeout in ms + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t esp_cam_ctlr_receive(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, uint32_t timeout_ms); + +/** + * @brief Delete ESP CAM controller handle + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t esp_cam_del_ctlr(esp_cam_ctlr_handle_t handle); + +/** + * @brief Register ESP CAM controller event callbacks + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t esp_cam_ctlr_register_event_callbacks(esp_cam_ctlr_handle_t handle, const esp_cam_ctlr_evt_cbs_t *cbs, void *user_data); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_cam_ctlr/include/esp_cam_ctlr_types.h b/components/esp_cam_ctlr/include/esp_cam_ctlr_types.h new file mode 100644 index 0000000000..c215d21996 --- /dev/null +++ b/components/esp_cam_ctlr/include/esp_cam_ctlr_types.h @@ -0,0 +1,61 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ESP CAM controller handle + */ +typedef struct esp_cam_ctlr_t *esp_cam_ctlr_handle_t; + +/** + * @brief ESP CAM controller transaction type + */ +typedef struct { + void *buffer; ///< Transaction buffer + size_t buflen; ///< Len of the transaction buffer + size_t received_size; ///< Received size, this received_size will be written by the driver, indicating the real received size +} esp_cam_ctlr_trans_t; + +/** + * @brief ESP CAM controller event callbacks + */ +typedef struct { + /** + * @brief On get new transaction callback + * + * @param[in] handle ESP CAM controller handle + * @param[in] trans New transaction + * @param[in] user_data User registered data + * + * @return Whether a high priority task is woken up by this function + */ + bool (*on_get_new_trans)(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data); + + /** + * @brief On transaction finish callback + * + * @param[in] handle ESP CAM controller handle + * @param[out] trans Finished transaction + * @param[in] user_data User registered data + * + * @return Whether a high priority task is woken up by this function + */ + bool (*on_trans_finished)(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data); + +} esp_cam_ctlr_evt_cbs_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_cam_ctlr/interface/esp_cam_ctlr_interface.h b/components/esp_cam_ctlr/interface/esp_cam_ctlr_interface.h new file mode 100644 index 0000000000..2df5a500de --- /dev/null +++ b/components/esp_cam_ctlr/interface/esp_cam_ctlr_interface.h @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_err.h" +#include "esp_cam_ctlr_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct esp_cam_ctlr_t esp_cam_ctlr_t; /*!< Type of CAM controller */ + +/** + * @brief Cam controller interface + */ +struct esp_cam_ctlr_t { + /** + * @brief Enable ESP CAM controller + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ + esp_err_t (*enable)(esp_cam_ctlr_t *ctlr); + + /** + * @brief Start ESP CAM controller + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ + esp_err_t (*start)(esp_cam_ctlr_t *ctlr); + + /** + * @brief Stop ESP CAM controller + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ + esp_err_t (*stop)(esp_cam_ctlr_t *ctlr); + + /** + * @brief Disable ESP CAM controller + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ + esp_err_t (*disable)(esp_cam_ctlr_t *ctlr); + + /** + * @brief Receive data to the given transaction + * + * @param[in] handle ESP CAM controller handle + * @param[in] trans ESP CAM controller transaction type + * @param[in] timeout_ms Timeout in ms + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ + esp_err_t (*receive)(esp_cam_ctlr_t *ctlr, esp_cam_ctlr_trans_t *trans, uint32_t timeout_ms); + + /** + * @brief Delete ESP CAM controller handle + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ + esp_err_t (*del)(esp_cam_ctlr_t *ctlr); + + /** + * @brief Register ESP CAM controller event callbacks + * + * @param[in] handle ESP CAM controller handle + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ + esp_err_t (*register_event_callbacks)(esp_cam_ctlr_t *ctlr, const esp_cam_ctlr_evt_cbs_t *cbs, void *user_ctx); + + void *user_data; ///< User data +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_cam_ctlr/test_apps/.build-test-rules.yml b/components/esp_cam_ctlr/test_apps/.build-test-rules.yml new file mode 100644 index 0000000000..b5e1e4bdf5 --- /dev/null +++ b/components/esp_cam_ctlr/test_apps/.build-test-rules.yml @@ -0,0 +1,5 @@ +components/esp_cam_ctlr/test_apps/csi: + disable: + - if: SOC_MIPI_CSI_SUPPORTED != 1 + depends_components: + - esp_cam_ctlr diff --git a/components/esp_cam_ctlr/test_apps/csi/CMakeLists.txt b/components/esp_cam_ctlr/test_apps/csi/CMakeLists.txt new file mode 100644 index 0000000000..ce5bab63b2 --- /dev/null +++ b/components/esp_cam_ctlr/test_apps/csi/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.16) + +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") + +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_csi) diff --git a/components/esp_cam_ctlr/test_apps/csi/README.md b/components/esp_cam_ctlr/test_apps/csi/README.md new file mode 100644 index 0000000000..909282018f --- /dev/null +++ b/components/esp_cam_ctlr/test_apps/csi/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | diff --git a/components/esp_cam_ctlr/test_apps/csi/main/CMakeLists.txt b/components/esp_cam_ctlr/test_apps/csi/main/CMakeLists.txt new file mode 100644 index 0000000000..a81c89ecfe --- /dev/null +++ b/components/esp_cam_ctlr/test_apps/csi/main/CMakeLists.txt @@ -0,0 +1,16 @@ +set(srcs "test_app_main.c") + +if(CONFIG_SOC_MIPI_CSI_SUPPORTED) + list(APPEND srcs "test_csi_driver.c") +endif() + +set(priv_requires + unity + esp_cam_ctlr + esp_psram +) + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS "." + PRIV_REQUIRES ${priv_requires} + WHOLE_ARCHIVE TRUE) diff --git a/components/esp_cam_ctlr/test_apps/csi/main/test_app_main.c b/components/esp_cam_ctlr/test_apps/csi/main/test_app_main.c new file mode 100644 index 0000000000..a47ab1a130 --- /dev/null +++ b/components/esp_cam_ctlr/test_apps/csi/main/test_app_main.c @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "unity.h" +#include "unity_test_utils.h" +#include "esp_heap_caps.h" +#include "sdkconfig.h" + +#define TEST_MEMORY_LEAK_THRESHOLD (400) + +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + unity_run_menu(); +} diff --git a/components/esp_cam_ctlr/test_apps/csi/main/test_csi_driver.c b/components/esp_cam_ctlr/test_apps/csi/main/test_csi_driver.c new file mode 100644 index 0000000000..db3536168e --- /dev/null +++ b/components/esp_cam_ctlr/test_apps/csi/main/test_csi_driver.c @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "sdkconfig.h" +#include "unity.h" +#include "esp_cam_ctlr_csi.h" +#include "esp_cam_ctlr.h" + +TEST_CASE("TEST CSI driver allocation", "[csi]") +{ + esp_cam_ctlr_csi_config_t csi_config = { + .ctlr_id = 0, + .h_res = 800, + .v_res = 640, + .clk_freq_hz = 200000000, + .input_data_color_type = MIPI_CSI_COLOR_RAW8, + .output_data_color_type = MIPI_CSI_COLOR_RGB565, + .data_lane_num = 2, + .byte_swap_en = false, + .queue_items = 1, + }; + esp_cam_ctlr_handle_t handle = NULL; + TEST_ESP_OK(esp_cam_new_csi_ctlr(&csi_config, &handle)); + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_cam_new_csi_ctlr(&csi_config, &handle)); + TEST_ESP_OK(esp_cam_del_ctlr(handle)); +} diff --git a/components/esp_cam_ctlr/test_apps/csi/pytest_csi.py b/components/esp_cam_ctlr/test_apps/csi/pytest_csi.py new file mode 100644 index 0000000000..07510eac36 --- /dev/null +++ b/components/esp_cam_ctlr/test_apps/csi/pytest_csi.py @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32p4 +@pytest.mark.generic +def test_csi(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_cam_ctlr/test_apps/csi/sdkconfig.defaults b/components/esp_cam_ctlr/test_apps/csi/sdkconfig.defaults new file mode 100644 index 0000000000..9ddee7c458 --- /dev/null +++ b/components/esp_cam_ctlr/test_apps/csi/sdkconfig.defaults @@ -0,0 +1,7 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n + +CONFIG_SPIRAM=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_ESP_TASK_WDT_EN=n diff --git a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c index 7034b6432c..b6396c1e6d 100644 --- a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c +++ b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c @@ -310,7 +310,7 @@ static esp_err_t dpi_panel_init(esp_lcd_panel_t *panel) .width = DW_GDMA_TRANS_WIDTH_64, }, .dst = { - .addr = MIPI_DSI_MEM_BASE, + .addr = MIPI_DSI_BRG_MEM_BASE, .burst_mode = DW_GDMA_BURST_MODE_FIXED, .burst_items = DW_GDMA_BURST_ITEMS_256, .burst_len = 16, diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index 5469ed64bf..766a6a4ca0 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -177,6 +177,10 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "mipi_dsi_hal.c") endif() + if(CONFIG_SOC_MIPI_CSI_SUPPORTED) + list(APPEND srcs "mipi_csi_hal.c") + endif() + if(CONFIG_SOC_ECC_SUPPORTED) list(APPEND srcs "ecc_hal.c") endif() diff --git a/components/hal/esp32p4/include/hal/dw_gdma_ll.h b/components/hal/esp32p4/include/hal/dw_gdma_ll.h index 11400503a6..021e929cf5 100644 --- a/components/hal/esp32p4/include/hal/dw_gdma_ll.h +++ b/components/hal/esp32p4/include/hal/dw_gdma_ll.h @@ -377,7 +377,7 @@ static inline void dw_gdma_ll_channel_set_trans_block_size(dw_gdma_dev_t *dev, u __attribute__((always_inline)) static inline void dw_gdma_ll_channel_set_src_master_port(dw_gdma_dev_t *dev, uint8_t channel, intptr_t mem_addr) { - if (mem_addr == MIPI_CSI_MEM_BASE) { + if (mem_addr == MIPI_CSI_BRG_MEM_BASE) { dev->ch[channel].ctl0.sms = DW_GDMA_LL_MASTER_PORT_MIPI_CSI; } else { dev->ch[channel].ctl0.sms = DW_GDMA_LL_MASTER_PORT_MEMORY; @@ -394,7 +394,7 @@ static inline void dw_gdma_ll_channel_set_src_master_port(dw_gdma_dev_t *dev, ui __attribute__((always_inline)) static inline void dw_gdma_ll_channel_set_dst_master_port(dw_gdma_dev_t *dev, uint8_t channel, intptr_t mem_addr) { - if (mem_addr == MIPI_DSI_MEM_BASE) { + if (mem_addr == MIPI_DSI_BRG_MEM_BASE) { dev->ch[channel].ctl0.dms = DW_GDMA_LL_MASTER_PORT_MIPI_DSI; } else { dev->ch[channel].ctl0.dms = DW_GDMA_LL_MASTER_PORT_MEMORY; @@ -986,7 +986,7 @@ static inline void dw_gdma_ll_lli_set_dst_trans_width(dw_gdma_link_list_item_t * __attribute__((always_inline)) static inline void dw_gdma_ll_lli_set_src_master_port(dw_gdma_link_list_item_t *lli, intptr_t mem_addr) { - if (mem_addr == MIPI_CSI_MEM_BASE) { + if (mem_addr == MIPI_CSI_BRG_MEM_BASE) { lli->ctrl_lo.sms = DW_GDMA_LL_MASTER_PORT_MIPI_CSI; } else { lli->ctrl_lo.sms = DW_GDMA_LL_MASTER_PORT_MEMORY; @@ -1002,7 +1002,7 @@ static inline void dw_gdma_ll_lli_set_src_master_port(dw_gdma_link_list_item_t * __attribute__((always_inline)) static inline void dw_gdma_ll_lli_set_dst_master_port(dw_gdma_link_list_item_t *lli, intptr_t mem_addr) { - if (mem_addr == MIPI_DSI_MEM_BASE) { + if (mem_addr == MIPI_DSI_BRG_MEM_BASE) { lli->ctrl_lo.dms = DW_GDMA_LL_MASTER_PORT_MIPI_DSI; } else { lli->ctrl_lo.dms = DW_GDMA_LL_MASTER_PORT_MEMORY; diff --git a/components/hal/esp32p4/include/hal/mipi_csi_brg_ll.h b/components/hal/esp32p4/include/hal/mipi_csi_brg_ll.h index fceef8370c..22fda40ca0 100644 --- a/components/hal/esp32p4/include/hal/mipi_csi_brg_ll.h +++ b/components/hal/esp32p4/include/hal/mipi_csi_brg_ll.h @@ -10,6 +10,7 @@ #include "hal/misc.h" #include "hal/assert.h" #include "hal/hal_utils.h" +#include "hal/mipi_csi_types.h" #include "soc/mipi_csi_bridge_struct.h" #ifdef __cplusplus @@ -65,6 +66,74 @@ static inline void mipi_csi_brg_ll_set_intput_data_v_row_num(csi_brg_dev_t *dev, dev->frame_cfg.vadr_num = row_num; } +/** + * @brief Set the buffer almost full threshold for the MIPI CSI bridge + * + * @param dev Pointer to the CSI bridge controller register base address + * @param afull_thrd full threshold + */ +static inline void mipi_csi_brg_ll_set_flow_ctl_buf_afull_thrd(csi_brg_dev_t *dev, size_t afull_thrd) +{ + dev->buf_flow_ctl.csi_buf_afull_thrd = afull_thrd; +} + +/** + * @brief Set the frame data whether contain hsync + * + * @param dev Pointer to the CSI bridge controller register base address + * @param en 0: frame data doesn't contain hsync. 1: frame data contains hsync. + */ +static inline void mipi_csi_brg_ll_enable_has_hsync(csi_brg_dev_t *dev, bool en) +{ + dev->frame_cfg.has_hsync_e = en; +} + +/** + * @brief Set the min value of data type used for pixel filter. + * + * @param dev Pointer to the CSI bridge controller register base address + * @param type_min The min data type allowed by bridge's pixel filter. + * The data type specifie the format and the content of the payload data. + */ +static inline void mipi_csi_brg_ll_set_data_type_min(csi_brg_dev_t *dev, uint16_t type_min) +{ + dev->data_type_cfg.data_type_min = type_min; +} + +/** + * @brief Set the the max value of data type used for pixel filter. + * + * @param dev Pointer to the CSI bridge controller register base address + * @param type_max The max data type allowed by bridge's pixel filter. + * The data type specifie the format and the content of the payload data. + */ +static inline void mipi_csi_brg_ll_set_data_type_max(csi_brg_dev_t *dev, uint16_t type_max) +{ + dev->data_type_cfg.data_type_max = type_max; +} + +/** + * @brief Set the DMA interval configuration. + * + * @param dev Pointer to the CSI bridge controller register base address + * @param interval 16'b1: 1 cycle. 16'b11: 2 cycle. ... ... 16'hFFFF: 16 cycle. + */ +static inline void mipi_csi_brg_ll_set_dma_req_interval(csi_brg_dev_t *dev, uint16_t interval) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->dma_req_interval, dma_req_interval, interval); +} + +/** + * @brief Set the data endianness order in bytes + * + * @param dev Pointer to the CSI bridge controller register base address + * @param byte_swap_en byte swap enable or not + */ +static inline void mipi_csi_brg_ll_set_byte_endian(csi_brg_dev_t *dev, bool byte_swap_en) +{ + dev->endian_mode.byte_endian_order = byte_swap_en; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32p4/include/hal/mipi_csi_host_ll.h b/components/hal/esp32p4/include/hal/mipi_csi_host_ll.h new file mode 100644 index 0000000000..6d52d7a2cd --- /dev/null +++ b/components/hal/esp32p4/include/hal/mipi_csi_host_ll.h @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "hal/misc.h" +#include "hal/assert.h" +#include "soc/mipi_csi_host_struct.h" +#include "hal/mipi_csi_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MIPI_CSI_HOST_LL_GET_HW(num) (((num) == 0) ? (&MIPI_CSI_HOST) : NULL) + +#define MIPI_CSI_HOST_LL_LANE_NUM_MAX 2 + +/** + * @brief Enable CSI host reset output + * + * @param dev Pointer to the CSI Host controller register base address + * @param en True to enable, False to disable + */ +static inline void mipi_csi_host_ll_enable_reset_output(csi_host_dev_t *dev, bool en) +{ + dev->csi2_resetn.csi2_resetn = !en; // host reset output, active low +} + +/** + * @brief Set the MIPI CSI lane num + * + * @param dev Pointer to the CSI Host controller register base address + * @param lanes_num Number of MIPI CSI lanes + */ +static inline void mipi_csi_host_ll_set_active_lanes_num(csi_host_dev_t *dev, int lanes_num) +{ + dev->n_lanes.n_lanes = lanes_num - 1; +} + +/** + * @brief Enable the CSI virtual channel extension + * + * @param dev Pointer to the CSI Host controller register base address + * @param en True to enable, False to disable + */ +static inline void mipi_csi_host_ll_enable_virtual_channel_extension(csi_host_dev_t *dev, bool en) +{ + dev->vc_extension.vcx = !en; // 0 is enable +} + +/** + * @brief Enable the data de-scrambling on the controller side + * + * @param dev Pointer to the CSI Host controller register base address + * @param en True to enable, False to disable + */ +static inline void mipi_csi_host_ll_enable_scrambling(csi_host_dev_t *dev, bool en) +{ + dev->scrambling.scramble_enable = en; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32p4/include/hal/mipi_csi_ll.h b/components/hal/esp32p4/include/hal/mipi_csi_ll.h index 6c3de4613a..eadcd22ace 100644 --- a/components/hal/esp32p4/include/hal/mipi_csi_ll.h +++ b/components/hal/esp32p4/include/hal/mipi_csi_ll.h @@ -10,13 +10,18 @@ #include "hal/misc.h" #include "hal/assert.h" #include "hal/hal_utils.h" +#include "hal/mipi_csi_types.h" #include "hal/mipi_csi_brg_ll.h" +#include "hal/mipi_csi_phy_ll.h" +#include "hal/mipi_csi_host_ll.h" #include "soc/hp_sys_clkrst_struct.h" #ifdef __cplusplus extern "C" { #endif +#define MIPI_CSI_LL_HOST_CTLR_NUMS 1 + /*--------------------------------------------------------------- CSI Bridge ---------------------------------------------------------------*/ @@ -50,6 +55,88 @@ static inline void mipi_csi_ll_reset_brg_module_clock(int csi_bridge_id) /// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance #define mipi_csi_ll_reset_brg_module_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; mipi_csi_ll_reset_brg_module_clock(__VA_ARGS__) +/*--------------------------------------------------------------- + CSI PHY +---------------------------------------------------------------*/ +/** + * @brief Set the clock source for the MIPI CSI D-PHY + * + * @param group_id Group ID + * @param source Clock source + */ +static inline void mipi_csi_ll_set_phy_clock_source(int group_id, mipi_csi_phy_clock_source_t source) +{ + (void)group_id; + switch (source) { + case MIPI_CSI_PHY_CLK_SRC_PLL_F20M: + HP_SYS_CLKRST.peri_clk_ctrl03.reg_mipi_csi_dphy_clk_src_sel = 0; + break; + case MIPI_CSI_PHY_CLK_SRC_RC_FAST: + HP_SYS_CLKRST.peri_clk_ctrl03.reg_mipi_csi_dphy_clk_src_sel = 1; + break; + case MIPI_CSI_PHY_CLK_SRC_PLL_F25M: + HP_SYS_CLKRST.peri_clk_ctrl03.reg_mipi_csi_dphy_clk_src_sel = 2; + break; + default: + abort(); + } +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define mipi_csi_ll_set_phy_clock_source(...) (void)__DECLARE_RCC_ATOMIC_ENV; mipi_csi_ll_set_phy_clock_source(__VA_ARGS__) + +/** + * @brief Enable MIPI CSI PHY configuration clock + * + * @param group_id Group ID + * @param en true to enable, false to disable + */ +static inline void mipi_csi_ll_enable_phy_config_clock(int group_id, bool en) +{ + (void)group_id; + HP_SYS_CLKRST.peri_clk_ctrl03.reg_mipi_csi_dphy_cfg_clk_en = en; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define mipi_csi_ll_enable_phy_config_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; mipi_csi_ll_enable_phy_config_clock(__VA_ARGS__) + +/*--------------------------------------------------------------- + CSI Host +---------------------------------------------------------------*/ +/** + * @brief Enable the bus clock for MIPI CSI host + * + * @param group_id Group ID + * @param en true to enable, false to disable + */ +static inline void mipi_csi_ll_enable_host_bus_clock(int group_id, bool en) +{ + (void)group_id; + HP_SYS_CLKRST.soc_clk_ctrl1.reg_csi_host_sys_clk_en = en; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define mipi_csi_ll_enable_host_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; mipi_csi_ll_enable_host_bus_clock(__VA_ARGS__) + +/** + * @brief Reset the MIPI CSI host CLK + * + * @param group_id Group ID + */ +static inline void mipi_csi_ll_reset_host_clock(int group_id) +{ + (void)group_id; + HP_SYS_CLKRST.hp_rst_en0.reg_rst_en_csi_host = 1; + HP_SYS_CLKRST.hp_rst_en0.reg_rst_en_csi_host = 0; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define mipi_csi_ll_reset_host_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; mipi_csi_ll_reset_host_clock(__VA_ARGS__) + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32p4/include/hal/mipi_csi_phy_ll.h b/components/hal/esp32p4/include/hal/mipi_csi_phy_ll.h new file mode 100644 index 0000000000..5d6fb489b8 --- /dev/null +++ b/components/hal/esp32p4/include/hal/mipi_csi_phy_ll.h @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "hal/misc.h" +#include "hal/assert.h" +#include "soc/mipi_csi_host_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Control the internal interface (clock and pins) between the CSI Host and the d-PHY + * + * @param dev Pointer to the CSI Host controller register base address + * @param clock_level Level of the clock + * @param clear Whether to clear the pins of the PHY + */ +static inline void mipi_csi_phy_ll_write_clock(csi_host_dev_t *dev, uint32_t clock_level, bool clear) +{ + dev->phy_test_ctrl0.val = clock_level << 1 | clear; +} + +/** + * @brief Write the PHY register via test interface + * + * @param dev Pointer to the CSI Host controller register base address + * @param reg_addr Address of the PHY register + */ +static inline void mipi_csi_phy_ll_write_reg_addr(csi_host_dev_t *dev, uint8_t reg_addr) +{ + dev->phy_test_ctrl1.val = (1 << 16) | (reg_addr & 0xFF); +} + +/** + * @brief Write the PHY register value via test interface + * + * @param dev Pointer to the CSI Host controller register base address + * @param reg_val Value to write to the PHY register + */ +static inline void mipi_csi_phy_ll_write_reg_val(csi_host_dev_t *dev, uint8_t reg_val) +{ + dev->phy_test_ctrl1.val = reg_val & 0xFF; +} + +/** + * @brief Enable dphy shutdown input + * + * @param dev Pointer to the CSI Host controller register base address + * @param en True to enable, False to disable + */ +static inline void mipi_csi_phy_ll_enable_shutdown_input(csi_host_dev_t *dev, bool en) +{ + dev->phy_shutdownz.phy_shutdownz = !en; // shutdown input, active low +} + +/** + * @brief Enable dphy reset output + * + * @param dev Pointer to the CSI Host controller register base address + * @param en True to enable, False to disable + */ +static inline void mipi_csi_phy_ll_enable_reset_output(csi_host_dev_t *dev, bool en) +{ + dev->dphy_rstz.dphy_rstz = !en; // phy reset output, active low +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/mipi_csi_hal.h b/components/hal/include/hal/mipi_csi_hal.h new file mode 100644 index 0000000000..d4c5f61622 --- /dev/null +++ b/components/hal/include/hal/mipi_csi_hal.h @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include "hal/mipi_csi_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MIPI CSI SOC layer representation of the host controller + */ +typedef struct csi_host_dev_t *mipi_csi_host_soc_handle_t; + +/** + * @brief MIPI CSI SOC layer representation of the bridge controller + */ +typedef struct csi_brg_dev_t *mipi_csi_bridge_soc_handle_t; + +/** + * @brief MIPI CSI HAL driver context + */ +typedef struct { + mipi_csi_host_soc_handle_t host_dev; + mipi_csi_bridge_soc_handle_t bridge_dev; +} mipi_csi_hal_context_t; + +/** + * @brief MIPI CSI HAL driver configuration + */ +typedef struct { + uint8_t lanes_num; ///< Lane num + uint32_t frame_width; ///< Frame width + uint32_t frame_height; ///< Frame height + uint32_t in_bpp; ///< In bits per pixel + uint32_t out_bpp; ///< Out bits per pixel + bool byte_swap_en; ///< Enable byte swap + int clk_freq_hz; ///< Clock frequency in hz +} mipi_csi_hal_config_t; + +/** + * @brief MIPI CSI HAL layer initialization + * + * @param hal Pointer to the HAL driver context + * @param config Pointer to the HAL configuration + */ +void mipi_csi_hal_init(mipi_csi_hal_context_t *hal, const mipi_csi_hal_config_t *config); + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/mipi_csi_types.h b/components/hal/include/hal/mipi_csi_types.h new file mode 100644 index 0000000000..5614a43b21 --- /dev/null +++ b/components/hal/include/hal/mipi_csi_types.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_assert.h" +#include "soc/soc_caps.h" +#include "soc/clk_tree_defs.h" +#include "hal/color_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_MIPI_CSI_SUPPORTED +/** + * @brief MIPI CSI PHY clock source + */ +typedef soc_periph_mipi_csi_phy_clk_src_t mipi_csi_phy_clock_source_t; +#else +typedef int mipi_csi_phy_clock_source_t; +#endif // SOC_MIPI_CSI_SUPPORTED + +/** + * @brief MIPI CSI Color Type + */ +typedef enum { + MIPI_CSI_COLOR_RAW8 = COLOR_TYPE_ID(COLOR_SPACE_RAW, COLOR_PIXEL_RAW8), ///< RAW8 + MIPI_CSI_COLOR_RAW10 = COLOR_TYPE_ID(COLOR_SPACE_RAW, COLOR_PIXEL_RAW10), ///< RAW10 + MIPI_CSI_COLOR_RAW12 = COLOR_TYPE_ID(COLOR_SPACE_RAW, COLOR_PIXEL_RAW12), ///< RAW12 + MIPI_CSI_COLOR_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), ///< RGB565 + MIPI_CSI_COLOR_RGB666 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB666), ///< RGB666 + MIPI_CSI_COLOR_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), ///< RGB888 + MIPI_CSI_COLOR_YUV420 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV420), ///< YUV420 + MIPI_CSI_COLOR_YUV422 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV422), ///< YUV422 +} mipi_csi_color_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/mipi_csi_hal.c b/components/hal/mipi_csi_hal.c new file mode 100644 index 0000000000..61a9723748 --- /dev/null +++ b/components/hal/mipi_csi_hal.c @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/log.h" +#include "hal/mipi_csi_hal.h" +#include "hal/mipi_csi_ll.h" +#include "soc/mipi_csi_periph.h" + +void s_mipi_csi_hal_phy_write_register(mipi_csi_hal_context_t *hal, uint8_t reg_addr, uint8_t reg_val) +{ + mipi_csi_phy_ll_write_clock(hal->host_dev, 0, false); + mipi_csi_phy_ll_write_reg_addr(hal->host_dev, reg_addr); + mipi_csi_phy_ll_write_clock(hal->host_dev, 1, false); + mipi_csi_phy_ll_write_clock(hal->host_dev, 0, false); + mipi_csi_phy_ll_write_reg_val(hal->host_dev, reg_val); + mipi_csi_phy_ll_write_clock(hal->host_dev, 1, false); + mipi_csi_phy_ll_write_clock(hal->host_dev, 0, false); +} + +void mipi_csi_hal_init(mipi_csi_hal_context_t *hal, const mipi_csi_hal_config_t *config) +{ + hal->bridge_dev = MIPI_CSI_BRG_LL_GET_HW(0); + hal->host_dev = MIPI_CSI_HOST_LL_GET_HW(0); + int csi_lane_rate = config->clk_freq_hz; + + // reset PHY + mipi_csi_phy_ll_enable_shutdown_input(hal->host_dev, true); + mipi_csi_phy_ll_enable_reset_output(hal->host_dev, true); + mipi_csi_host_ll_enable_reset_output(hal->host_dev, true); + //reset reg addr to default value + mipi_csi_phy_ll_write_reg_addr(hal->host_dev, 0x0); + //reset reg val to default value + mipi_csi_phy_ll_write_clock(hal->host_dev, 0, 1); + mipi_csi_phy_ll_write_clock(hal->host_dev, 0, 0); + + uint8_t hs_freq_sel = 0; + for (size_t i = 0; i < num_of_soc_mipi_csi_phy_pll_ranges; i++) { + if ((csi_lane_rate / 1000000) >= soc_mipi_csi_phy_pll_ranges[i].start_mbps && + (csi_lane_rate / 1000000) <= soc_mipi_csi_phy_pll_ranges[i].end_mbps) { + hs_freq_sel = soc_mipi_csi_phy_pll_ranges[i].hs_freq_range_sel; + break; + } + } + s_mipi_csi_hal_phy_write_register(hal, 0x44, hs_freq_sel << 1); + HAL_LOGD("csi_hal", "CSI-DPHY lane_rate: %d Hz, hs_freq: 0x%x, lane_num: 0x%x", csi_lane_rate, hs_freq_sel, config->lanes_num); + mipi_csi_phy_ll_enable_shutdown_input(hal->host_dev, false); + mipi_csi_phy_ll_enable_reset_output(hal->host_dev, false); + mipi_csi_host_ll_enable_reset_output(hal->host_dev, false); + + // Configure the host controller. + // Configure the number of active lanes. + mipi_csi_host_ll_set_active_lanes_num(hal->host_dev, config->lanes_num); + /** + * We ignore the virtual channel and virtual channel extension info, there is only one virtual channel. + * VC is by default there, VCX needs enable or disable. + * So we disable VCX here. + */ + mipi_csi_host_ll_enable_virtual_channel_extension(hal->host_dev, false); + // Set Scrambler. + mipi_csi_host_ll_enable_scrambling(hal->host_dev, false); + + //CSI bridge + //TODO: IDF-9126, make csi bridge updates into `mipi_csi_share_hw_ctrl.c` and share with ISP + mipi_csi_brg_ll_set_intput_data_h_pixel_num(hal->bridge_dev, config->frame_height); + mipi_csi_brg_ll_set_intput_data_v_row_num(hal->bridge_dev, config->frame_width); + mipi_csi_brg_ll_set_flow_ctl_buf_afull_thrd(hal->bridge_dev, 960); + mipi_csi_brg_ll_set_data_type_min(hal->bridge_dev, 0x12); + mipi_csi_brg_ll_set_data_type_max(hal->bridge_dev, 0x2f); + mipi_csi_brg_ll_set_byte_endian(hal->bridge_dev, config->byte_swap_en); +} diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 7dd580e136..7a4edbe222 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -83,6 +83,10 @@ if(CONFIG_SOC_MIPI_DSI_SUPPORTED) list(APPEND srcs "${target}/mipi_dsi_periph.c") endif() +if(CONFIG_SOC_MIPI_CSI_SUPPORTED) + list(APPEND srcs "${target}/mipi_csi_periph.c") +endif() + if(CONFIG_SOC_PARLIO_SUPPORTED) list(APPEND srcs "${target}/parlio_periph.c") endif() diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 26125903bf..b25e8fdf00 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -39,6 +39,10 @@ config SOC_PCNT_SUPPORTED bool default y +config SOC_MIPI_CSI_SUPPORTED + bool + default y + config SOC_MIPI_DSI_SUPPORTED bool default y diff --git a/components/soc/esp32p4/include/soc/clk_tree_defs.h b/components/soc/esp32p4/include/soc/clk_tree_defs.h index 90c37a2778..67a3801efc 100644 --- a/components/soc/esp32p4/include/soc/clk_tree_defs.h +++ b/components/soc/esp32p4/include/soc/clk_tree_defs.h @@ -358,6 +358,21 @@ typedef enum { /////////////////////////////////////////////////MIPI/////////////////////////////////////////////////////////////////// +/** + * @brief Array initializer for all supported clock sources of MIPI CSI PHY interface + */ +#define SOC_MIPI_CSI_PHY_CLKS {SOC_MOD_CLK_RC_FAST, SOC_MOD_CLK_PLL_F25M, SOC_MOD_CLK_PLL_F20M} + +/** + * @brief Type of MIPI CSI PHY clock source + */ +typedef enum { + MIPI_CSI_PHY_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as MIPI CSI PHY source clock */ + MIPI_CSI_PHY_CLK_SRC_PLL_F25M = SOC_MOD_CLK_PLL_F25M, /*!< Select PLL_F25M as MIPI CSI PHY source clock */ + MIPI_CSI_PHY_CLK_SRC_PLL_F20M = SOC_MOD_CLK_PLL_F20M, /*!< Select PLL_F20M as MIPI CSI PHY source clock */ + MIPI_CSI_PHY_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F20M, /*!< Select PLL_F20M as default clock */ +} soc_periph_mipi_csi_phy_clk_src_t; + /** * @brief Array initializer for all supported clock sources of MIPI DSI DPI interface */ diff --git a/components/soc/esp32p4/include/soc/mipi_csi_bridge_struct.h b/components/soc/esp32p4/include/soc/mipi_csi_bridge_struct.h index b2653111e2..a512d9b0a8 100644 --- a/components/soc/esp32p4/include/soc/mipi_csi_bridge_struct.h +++ b/components/soc/esp32p4/include/soc/mipi_csi_bridge_struct.h @@ -170,11 +170,11 @@ typedef union { * endianness order in bytes. 2'h0 is normal mode and 2'h3 is useful to YUV420(Legacy) * when isp is bapassed. */ - uint32_t byte_endian_order:1; + uint32_t byte_endian_order:1; //byte_swap_en /** bit_endian_order : R/W; bitpos: [1]; default: 0; * N/A */ - uint32_t bit_endian_order:1; + uint32_t bit_endian_order:1; //reserved uint32_t reserved_2:30; }; uint32_t val; diff --git a/components/soc/esp32p4/include/soc/reg_base.h b/components/soc/esp32p4/include/soc/reg_base.h index 798b989130..97d9c7f280 100644 --- a/components/soc/esp32p4/include/soc/reg_base.h +++ b/components/soc/esp32p4/include/soc/reg_base.h @@ -160,8 +160,8 @@ /** * @brief: Special memory address */ -#define MIPI_CSI_MEM_BASE 0x50104000 -#define MIPI_DSI_MEM_BASE 0x50105000 +#define MIPI_CSI_BRG_MEM_BASE 0x50104000 +#define MIPI_DSI_BRG_MEM_BASE 0x50105000 /** * This are module helper MACROs for quick module reference diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 63ca436f42..eb18bc48a1 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -29,6 +29,7 @@ #define SOC_GPTIMER_SUPPORTED 1 #define SOC_PCNT_SUPPORTED 1 // #define SOC_LCDCAM_SUPPORTED 1 // TODO: IDF-7465 +#define SOC_MIPI_CSI_SUPPORTED 1 #define SOC_MIPI_DSI_SUPPORTED 1 #define SOC_MCPWM_SUPPORTED 1 #define SOC_TWAI_SUPPORTED 1 diff --git a/components/soc/esp32p4/mipi_csi_periph.c b/components/soc/esp32p4/mipi_csi_periph.c new file mode 100644 index 0000000000..4f4ae4d0ee --- /dev/null +++ b/components/soc/esp32p4/mipi_csi_periph.c @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/mipi_csi_periph.h" + +const soc_mipi_csi_phy_pll_freq_range_t soc_mipi_csi_phy_pll_ranges[] = { + {90, 99, 0x00}, // [90,100) Mbps + {100, 109, 0x10}, // [100,110) Mbps + {110, 129, 0x20}, // [110,130) Mbps + {130, 139, 0x01}, // [130,140) Mbps + {140, 149, 0x11}, // [140,150) Mbps + {150, 169, 0x21}, // [150,170) Mbps + {170, 179, 0x02}, // [170,180) Mbps + {180, 199, 0x12}, // [180,200) Mbps + {200, 219, 0x22}, // [200,220) Mbps + {220, 239, 0x03}, // [220,240) Mbps + {240, 249, 0x13}, // [240,250) Mbps + {250, 269, 0x23}, // [250,270) Mbps + {270, 299, 0x04}, // [270,300) Mbps + {300, 329, 0x14}, // [300,330) Mbps + {330, 359, 0x05}, // [330,360) Mbps + {360, 399, 0x15}, // [360,400) Mbps + {400, 449, 0x25}, // [400,450) Mbps + {450, 499, 0x06}, // [450,500) Mbps + {500, 549, 0x16}, // [500,550) Mbps + {550, 599, 0x07}, // [550,600) Mbps + {600, 649, 0x17}, // [600,650) Mbps + {650, 699, 0x08}, // [650,700) Mbps + {700, 749, 0x18}, // [700,750) Mbps + {750, 799, 0x09}, // [750,800) Mbps + {800, 849, 0x19}, // [800,850) Mbps + {850, 899, 0x29}, // [850,900) Mbps + {900, 949, 0x39}, // [900,950) Mbps + {950, 999, 0x0A}, // [950,1000) Mbps + {1000, 1049, 0x1A}, // [1000,1050) Mbps + {1050, 1099, 0x2A}, // [1050,1100) Mbps + {1100, 1149, 0x3A}, // [1100,1150) Mbps + {1150, 1199, 0x0B}, // [1150,1200) Mbps + {1200, 1249, 0x1B}, // [1200,1250) Mbps + {1250, 1299, 0x2B}, // [1250,1300) Mbps + {1300, 1349, 0x3B}, // [1300,1350) Mbps + {1350, 1399, 0x0C}, // [1350,1400) Mbps + {1400, 1449, 0x1C}, // [1400,1450) Mbps + {1450, 1500, 0x2C}, // [1450,1500] Mbps +}; + +const size_t num_of_soc_mipi_csi_phy_pll_ranges = sizeof(soc_mipi_csi_phy_pll_ranges) / sizeof(soc_mipi_csi_phy_pll_freq_range_t); diff --git a/components/soc/include/soc/mipi_csi_periph.h b/components/soc/include/soc/mipi_csi_periph.h new file mode 100644 index 0000000000..d4fd660535 --- /dev/null +++ b/components/soc/include/soc/mipi_csi_periph.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MIPI CSI PHY PLL frequency range + */ +typedef struct { + uint32_t start_mbps; /*!< Start frequency of the range (included) */ + uint32_t end_mbps; /*!< End frequency of the range (included) */ + uint8_t hs_freq_range_sel; /*!< HS operating frequency range selection */ +} soc_mipi_csi_phy_pll_freq_range_t; + +extern const soc_mipi_csi_phy_pll_freq_range_t soc_mipi_csi_phy_pll_ranges[]; +extern const size_t num_of_soc_mipi_csi_phy_pll_ranges; + + +#ifdef __cplusplus +} +#endif From 46cf31eac020732cbd0f147b08b79ddca852da27 Mon Sep 17 00:00:00 2001 From: Armando Date: Sun, 4 Feb 2024 18:37:42 +0800 Subject: [PATCH 2/2] change(cam): added codeowners --- .gitlab/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 87ea6f3762..8983b1d748 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -82,6 +82,7 @@ /components/esp_adc/ @esp-idf-codeowners/peripherals /components/esp_app_format/ @esp-idf-codeowners/system @esp-idf-codeowners/app-utilities /components/esp_bootloader_format/ @esp-idf-codeowners/system @esp-idf-codeowners/app-utilities +/components/esp_cam_ctlr/ @esp-idf-codeowners/peripherals /components/esp_coex/ @esp-idf-codeowners/wifi @esp-idf-codeowners/bluetooth @esp-idf-codeowners/ieee802154 /components/esp_common/ @esp-idf-codeowners/system /components/esp_driver_*/ @esp-idf-codeowners/peripherals