diff --git a/components/esp_driver_cam/CMakeLists.txt b/components/esp_driver_cam/CMakeLists.txt index 17ac846f64..e0a06e3586 100644 --- a/components/esp_driver_cam/CMakeLists.txt +++ b/components/esp_driver_cam/CMakeLists.txt @@ -22,8 +22,15 @@ if(NOT ${target} STREQUAL "linux") list(APPEND requires esp_mm) endif() +if(CONFIG_SOC_LCDCAM_CAM_SUPPORTED) + list(APPEND srcs "dvp/src/esp_cam_ctlr_dvp_gdma.c" "dvp/src/esp_cam_ctlr_dvp_cam.c") + list(APPEND includes "dvp/include") + list(APPEND priv_include_dirs "dvp/private_include") +endif() + idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${includes} + PRIV_INCLUDE_DIRS ${priv_include_dirs} REQUIRES ${requires} PRIV_REQUIRES ${priv_requires} ) diff --git a/components/esp_driver_cam/Kconfig b/components/esp_driver_cam/Kconfig index 771fd0e6ab..ca1fac319a 100644 --- a/components/esp_driver_cam/Kconfig +++ b/components/esp_driver_cam/Kconfig @@ -1,9 +1,10 @@ menu "ESP-Driver:Camera Controller Configurations" - depends on SOC_MIPI_CSI_SUPPORTED + depends on SOC_MIPI_CSI_SUPPORTED || SOC_LCDCAM_CAM_SUPPORTED config CAM_CTLR_MIPI_CSI_ISR_IRAM_SAFE bool "CSI ISR IRAM-Safe" + depends on SOC_MIPI_CSI_SUPPORTED default n select DW_GDMA_ISR_IRAM_SAFE select DW_GDMA_CTRL_FUNC_IN_IRAM @@ -13,7 +14,7 @@ menu "ESP-Driver:Camera Controller Configurations" Ensure the CSI driver ISR is IRAM-Safe. When enabled, the ISR handler will be available when the cache is disabled. - config CAM_CTLR_ISP_DVP_ISR_IRAM_SAFE + config CAM_CTLR_ISP_DVP_ISR_IRAM_SAFE # IDF-10093 bool "ISP_DVP ISR IRAM-Safe" default n select DW_GDMA_ISR_IRAM_SAFE @@ -24,4 +25,16 @@ menu "ESP-Driver:Camera Controller Configurations" Ensure the ISP_DVP driver ISR is IRAM-Safe. When enabled, the ISR handler will be available when the cache is disabled. + config CAM_CTLR_DVP_CAM_ISR_IRAM_SAFE + bool "DVP ISR IRAM-Safe" + depends on SOC_LCDCAM_CAM_SUPPORTED + default n + select DW_GDMA_ISR_IRAM_SAFE + select DW_GDMA_CTRL_FUNC_IN_IRAM + select DW_GDMA_SETTER_FUNC_IN_IRAM + select DW_GDMA_GETTER_FUNC_IN_IRAM + help + Ensure the DVP 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_driver_cam/dvp/include/esp_cam_ctlr_dvp.h b/components/esp_driver_cam/dvp/include/esp_cam_ctlr_dvp.h new file mode 100644 index 0000000000..b235f3e8f7 --- /dev/null +++ b/components/esp_driver_cam/dvp/include/esp_cam_ctlr_dvp.h @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_err.h" +#include "hal/cam_types.h" +#include "esp_cam_ctlr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ESP CAM DVP pins configuration + */ +typedef struct esp_cam_ctlr_dvp_pin_config { + cam_ctlr_data_width_t data_width; /*!< Number of data lines */ + int data_io[CAM_DVP_DATA_SIG_NUM]; /*!< DVP data pin number */ + int vsync_io; /*!< DVP V-Sync pin number */ + int de_io; /*!< DVP DE pin number */ + int pclk_io; /*!< DVP PCLK input pin number, clock is from camera sensor */ + int xclk_io; /*!< DVP output clock pin number */ +} esp_cam_ctlr_dvp_pin_config_t; + +/** + * @brief ESP CAM DVP controller configurations + */ +typedef struct esp_cam_ctlr_dvp_config { + int ctlr_id; /*!< DVP controller ID */ + cam_clock_source_t clk_src; /*!< DVP 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 */ + cam_ctlr_color_t input_data_color_type; /*!< Input pixel format */ + struct { + uint32_t byte_swap_en : 1; /*!< Enable byte swap */ + uint32_t bk_buffer_dis : 1; /*!< Disable backup buffer */ + uint32_t pin_dont_init : 1; /*!< Don't initialize DVP pins if users have called "esp_cam_ctlr_dvp_init" before */ + uint32_t pic_format_jpeg : 1; /*!< Input picture format is JPEG, if set this flag and "input_data_color_type" will be ignored */ + }; /*!< Boolean Flags */ + + uint32_t dma_burst_size; /*!< DVP DMA burst transmission block size, set to 0 means to disable the data burst, + other value must be power of 2, e.g., 4/8/16/32/64/128 */ + + const esp_cam_ctlr_dvp_pin_config_t *pin; /*!< DVP pin configuration, this will be ignored by "esp_cam_new_dvp_ctlr" if "pin_dont_init" is set */ +} esp_cam_ctlr_dvp_config_t; + +/** + * @brief New ESP CAM DVP controller + * + * @param config DVP controller configurations + * @param ret_handle Returned CAM controller handle + * + * @return + * - ESP_OK on success + * - 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_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_ctlr_handle_t *ret_handle); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_cam/dvp/include/esp_private/esp_cam_dvp.h b/components/esp_driver_cam/dvp/include/esp_private/esp_cam_dvp.h new file mode 100644 index 0000000000..032c816179 --- /dev/null +++ b/components/esp_driver_cam/dvp/include/esp_private/esp_cam_dvp.h @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_cam_ctlr_dvp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ESP CAM DVP initialzie clock and GPIO. + * + * @param ctlr_id CAM DVP controller ID + * @param clk_src CAM DVP clock source + * @param pin CAM DVP pin configuration + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_init(int ctlr_id, cam_clock_source_t clk_src, const esp_cam_ctlr_dvp_pin_config_t *pin); + +/** + * @brief ESP CAM DVP output hardware clock + * + * @param ctlr_id CAM DVP controller ID + * @param clk_src CAM DVP clock source + * @param xclk_freq CAM DVP output clock frequency in HZ + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_output_clock(int ctlr_id, cam_clock_source_t clk_src, uint32_t xclk_freq); + +/** + * @brief ESP CAM DVP de-initialzie. + * + * @param ctlr_id DVP controller ID + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_deinit(int ctlr_id); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_cam/dvp/private_include/esp_cam_ctlr_dvp_cam.h b/components/esp_driver_cam/dvp/private_include/esp_cam_ctlr_dvp_cam.h new file mode 100644 index 0000000000..5c9b311ee1 --- /dev/null +++ b/components/esp_driver_cam/dvp/private_include/esp_cam_ctlr_dvp_cam.h @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "freertos/FreeRTOS.h" +#include "esp_cam_ctlr_interface.h" +#include "hal/cam_hal.h" +#include "esp_cam_ctlr_dvp_dma.h" +#include "esp_cam_ctlr_dvp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief DVP CAM finite state machine + */ +typedef enum esp_cam_ctlr_dvp_cam_fsm { + ESP_CAM_CTLR_DVP_CAM_FSM_INIT = 1, /*!< DVP CAM initialization state, and next state is "enabled" */ + ESP_CAM_CTLR_DVP_CAM_FSM_ENABLED, /*!< DVP CAM enabled state, and next state is "init" or "started" */ + ESP_CAM_CTLR_DVP_CAM_FSM_STARTED, /*!< DVP CAM started state, and next state is "init" or "enabled" */ +} esp_cam_ctlr_dvp_cam_fsm_t; + +/** + * @brief DVP CAM object data + */ +typedef struct esp_cam_ctlr_dvp_cam { + esp_cam_ctlr_t base; /*!< Camera controller base object data */ + esp_cam_ctlr_evt_cbs_t cbs; /*!< Camera controller callback functions */ + void *cbs_user_data; /*!< Camera controller callback private data */ + + int ctlr_id; /*!< DVP CAM port ID */ + cam_ctlr_color_t input_data_color_type; /*!< DVP CAM input pixel format */ + struct { + uint32_t bk_buffer_dis : 1; /*!< Disable backup buffer */ + uint32_t pin_dont_init : 1; /*!< Don't initialize DVP pins if users have called "esp_cam_ctlr_dvp_init" before */ + uint32_t pic_format_jpeg : 1; /*!< Input picture format is JPEG, if set this flag and "input_data_color_type" will be ignored */ + }; /*!< Boolean Flags */ + + cam_hal_context_t hal; /*!< DVP CAM HAL object data */ + esp_cam_ctlr_dvp_dma_t dma; /*!< DVP CAM DMA object data */ + size_t fb_size_in_bytes; /*!< DVP CAM frame buffer maximum size in byte */ + esp_cam_ctlr_dvp_cam_fsm_t dvp_fsm; /*!< DVP CAM finite state machine */ + uint8_t *cur_buf; /*!< DVP CAM current buffer which is receiving stream */ + uint8_t *backup_buffer; /*!< DVP CAM backup buffer */ + bool bk_buffer_exposed; /*!< status of if back_buffer is exposed to users */ + portMUX_TYPE spinlock; /*!< DVP CAM spinlock */ +} esp_cam_ctlr_dvp_cam_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_cam/dvp/private_include/esp_cam_ctlr_dvp_dma.h b/components/esp_driver_cam/dvp/private_include/esp_cam_ctlr_dvp_dma.h new file mode 100644 index 0000000000..0846d6ccd6 --- /dev/null +++ b/components/esp_driver_cam/dvp/private_include/esp_cam_ctlr_dvp_dma.h @@ -0,0 +1,104 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "hal/dma_types.h" +#include "esp_private/gdma.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED + +/** + * @brief DVP DMA description object + */ +typedef dma_descriptor_align8_t esp_cam_ctlr_dvp_dma_desc_t; + +/** + * @brief DVP DMA object + */ +typedef struct esp_cam_ctlr_dvp_dma { + gdma_channel_handle_t dma_chan; /*!< DVP DMA channel handle */ + size_t size; /*!< DVP DMA buffer size */ + esp_cam_ctlr_dvp_dma_desc_t *desc; /*!< DVP DMA description buffer */ + size_t desc_count; /*!< DVP DMA description count */ + size_t desc_size; /*!< DVP DMA description buffer size in byte */ +} esp_cam_ctlr_dvp_dma_t; + +/** + * @brief Initialize DVP DMA object + * + * @param dma DVP DMA object pointer + * @param burst_size DVP DMA burst transmission block size + * @param size DVP DMA buffer size + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_size, size_t size); + +/** + * @brief De-initialize DVP DMA object + * + * @param dma DVP DMA object pointer + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_dma_deinit(esp_cam_ctlr_dvp_dma_t *dma); + +/** + * @brief Set DVP DMA descriptor address and start engine + * + * @param dma DVP DMA object pointer + * @param buffer DVP DMA buffer pointer + * @param size DVP DMA buffer size + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_dma_start(esp_cam_ctlr_dvp_dma_t *dma, uint8_t *buffer, size_t size); + +/** + * @brief Stop DVP DMA engine + * + * @param dma DVP DMA object pointer + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_dma_stop(esp_cam_ctlr_dvp_dma_t *dma); + +/** + * @brief Reset DVP DMA FIFO and internal finite state machine + * + * @param dma DVP DMA object pointer + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_dma_reset(esp_cam_ctlr_dvp_dma_t *dma); + +/** + * @brief Get DMA received data size + * + * @param dma DVP DMA object pointer + * + * @return DMA received data size + */ +size_t esp_cam_ctlr_dvp_dma_get_recv_size(esp_cam_ctlr_dvp_dma_t *dma); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c new file mode 100644 index 0000000000..04334bf7e6 --- /dev/null +++ b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c @@ -0,0 +1,764 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "hal/gpio_ll.h" +#include "hal/cam_ll.h" +#include "driver/gpio.h" +#include "esp_cache.h" +#include "esp_private/periph_ctrl.h" +#include "esp_private/esp_cache_private.h" +#include "esp_private/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_clk_tree.h" +#include "soc/cam_periph.h" +#include "esp_cam_ctlr_dvp_cam.h" +#include "esp_private/esp_cam_dvp.h" +#include "../../dvp_share_ctrl.h" + +#ifdef CONFIG_CAM_CTLR_DVP_CAM_ISR_IRAM_SAFE +#define CAM_DVP_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) +#else +#define CAM_DVP_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT) +#endif + +#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + +#define DVP_CAM_CONFIG_INPUT_PIN(pin, sig, inv) \ +{ \ + ret = esp_cam_ctlr_dvp_config_input_gpio(pin, sig, inv); \ + if (ret != ESP_OK) { \ + ESP_LOGE(TAG, "failed to configure pin=%d sig=%d", \ + pin, sig); \ + return ret; \ + } \ +} + +typedef struct dvp_platform { + _lock_t mutex; + esp_cam_ctlr_dvp_cam_t *ctlrs[CAP_DVP_PERIPH_NUM]; +} dvp_platform_t; + +static dvp_platform_t s_platform; +static const char *TAG = "dvp_cam"; + +/** + * @brief Claim DVP controller + * + * @param ctlr_id DVP controller ID + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t s_dvp_claim_ctlr(int ctlr_id, esp_cam_ctlr_dvp_cam_t *ctlr) +{ + esp_err_t ret = ESP_ERR_NOT_FOUND; + + _lock_acquire(&s_platform.mutex); + if (!s_platform.ctlrs[ctlr_id]) { + s_platform.ctlrs[ctlr_id] = ctlr; + ret = ESP_OK; + } + _lock_release(&s_platform.mutex); + + return ret; +} + +/** + * @brief Declaim DVP controller + * + * @param ctlr_id DVP controller ID + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t s_dvp_declaim_ctlr(int ctlr_id) +{ + esp_err_t ret = ESP_ERR_NOT_FOUND; + + _lock_acquire(&s_platform.mutex); + if (s_platform.ctlrs[ctlr_id]) { + s_platform.ctlrs[ctlr_id] = NULL; + ret = ESP_OK; + } + _lock_release(&s_platform.mutex); + + return ret; +} + +/** + * @brief CAM DVP initialzie input GPIO pin. + * + * @param pin DVP pin number + * @param signal DVP pin mapping signal + * @param inv true: DVP pin is inverted, false: DVP pin is not inverted + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_dvp_config_input_gpio(int pin, int signal, bool inv) +{ + esp_err_t ret; + + gpio_func_sel(pin, PIN_FUNC_GPIO); + ret = gpio_set_direction(pin, GPIO_MODE_INPUT); + if (ret != ESP_OK) { + return ret; + } + ret = gpio_set_pull_mode(pin, GPIO_FLOATING); + if (ret != ESP_OK) { + return ret; + } + esp_rom_gpio_connect_in_signal(pin, signal, inv); + + return ESP_OK; +} + +/** + * @brief CAM DVP start receiving frame. + * + * @param ctlr CAM DVP controller + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static IRAM_ATTR esp_err_t esp_cam_ctlr_dvp_start_trans(esp_cam_ctlr_dvp_cam_t *ctlr) +{ + bool buffer_ready = false; + esp_cam_ctlr_trans_t trans; + + if (ctlr->cur_buf) { + ctlr->cur_buf = NULL; + cam_hal_stop_streaming(&ctlr->hal); + ESP_RETURN_ON_ERROR_ISR(esp_cam_ctlr_dvp_dma_stop(&ctlr->dma), TAG, "failed to stop DMA"); + } + + if (ctlr->cbs.on_get_new_trans && ctlr->cbs.on_get_new_trans(&(ctlr->base), &trans, ctlr->cbs_user_data)) { + buffer_ready = true; + } else if (!ctlr->bk_buffer_dis) { + trans.buffer = ctlr->backup_buffer; + trans.buflen = ctlr->fb_size_in_bytes; + buffer_ready = true; + } + + if (!buffer_ready) { + assert(false && "no new buffer, and no driver internal buffer"); + } + + ESP_RETURN_ON_ERROR_ISR(esp_cam_ctlr_dvp_dma_reset(&ctlr->dma), TAG, "failed to reset DMA"); + ESP_RETURN_ON_ERROR_ISR(esp_cam_ctlr_dvp_dma_start(&ctlr->dma, trans.buffer, ctlr->fb_size_in_bytes), TAG, "failed to start DMA"); + + cam_hal_start_streaming(&ctlr->hal); + + ctlr->cur_buf = trans.buffer; + + return ESP_OK; +} + +/** + * @brief Check JPEG file and return JPEG frame actual size + * + * @param buffer JPEG buffer pointer + * @param size JPEG buffer size + * + * @return JPEG frame actual size if success or 0 if not JPEG header or tail TAG is found + */ +static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_jpeg_size(const uint8_t *buffer, uint32_t size) +{ + /* Check JPEG header TAG: ff:d8 */ + + if (buffer[0] != 0xff || buffer[1] != 0xd8) { + return 0; + } + + for (uint32_t off = size - 2; off > 2; off--) { + /* Check JPEG tail TAG: ff:d9 */ + + if (buffer[off] == 0xff && buffer[off + 1] == 0xd9) { + return off + 2; + } + } + + return 0; +} + +/** + * @brief CAM DVP get actually received data size in byte + * + * @param ctlr CAM DVP controller + * @param rx_buffer CAM DVP receive buffer + * @param dma_recv_size CAM DVP DMA receive buffer size + * + * @return Received data size if success or 0 if failed + */ +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) { + recv_buffer_size = ALIGN_UP_BY(MIN(dma_recv_size, ctlr->fb_size_in_bytes), 64); + } else { + 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 (ctlr->pic_format_jpeg) { + recv_buffer_size = esp_cam_ctlr_dvp_get_jpeg_size(rx_buffer, dma_recv_size); + } + + return recv_buffer_size; +} + +/** + * @brief CAM DVP calculate frame receive buffer size. + * + * @param config CAM DVP controller configurations + * @param p_size CAM DVP frame receive buffer size buffer pointer + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_dvp_cam_get_frame_size(const esp_cam_ctlr_dvp_config_t *config, size_t *p_size) +{ + esp_err_t ret = ESP_OK; + + if (config->pic_format_jpeg) { + *p_size = config->h_res * config->v_res; + } else { + switch (config->input_data_color_type) { + case CAM_CTLR_COLOR_RGB565: + *p_size = config->h_res * config->v_res * 2; + break; + case CAM_CTLR_COLOR_YUV422: + *p_size = config->h_res * config->v_res * 2; + break; + case CAM_CTLR_COLOR_YUV420: + *p_size = (config->h_res * config->v_res / 2) * 3; + break; + case CAM_CTLR_COLOR_RGB888: + *p_size = config->h_res * config->v_res * 3; + break; + default: + ret = ESP_ERR_INVALID_ARG; + break; + } + } + + return ret; +} + +/** + * @brief CAM DVP receive frame done interrupt callback function. + * + * @param dma_chan DMA channel + * @param event_data DMA event data + * @param user_data DMA interrupt callback user data + * + * @return + * - true: trigger task switch + * - false: don't trigger task switch + */ +static IRAM_ATTR bool esp_cam_ctlr_recv_frame_done_isr(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) +{ + bool need_switch = false; + esp_cam_ctlr_dvp_cam_t *ctlr = (esp_cam_ctlr_dvp_cam_t *)user_data; + size_t dma_recv_size = esp_cam_ctlr_dvp_dma_get_recv_size(&ctlr->dma); + esp_cam_ctlr_trans_t trans = { + .buffer = ctlr->cur_buf, + }; + + esp_cam_ctlr_dvp_start_trans(ctlr); + + if ((trans.buffer != ctlr->backup_buffer) || ctlr->bk_buffer_exposed) { + trans.buflen = ctlr->fb_size_in_bytes; + trans.received_size = esp_cam_ctlr_dvp_get_recved_size(ctlr, trans.buffer, dma_recv_size); + need_switch = ctlr->cbs.on_trans_finished(&ctlr->base, &trans, ctlr->cbs_user_data); + } + + return need_switch; +} + +/** + * @brief ESP CAM DVP initialzie clock and GPIO. + * + * @param ctlr_id CAM DVP controller ID + * @param clk_src CAM DVP clock source + * @param pin CAM DVP pin configuration + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_init(int ctlr_id, cam_clock_source_t clk_src, const esp_cam_ctlr_dvp_pin_config_t *pin) +{ + esp_err_t ret; + + ESP_RETURN_ON_FALSE(ctlr_id < CAP_DVP_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid argument: ctlr_id >= %d", CAP_DVP_PERIPH_NUM); + ESP_RETURN_ON_FALSE(pin->data_width == CAM_CTLR_DATA_WIDTH_8, ESP_ERR_INVALID_ARG, TAG, "invalid argument: data_width != CAM_CTLR_DATA_WIDTH_8"); + ESP_RETURN_ON_FALSE(pin, ESP_ERR_INVALID_ARG, TAG, "invalid argument: pin is null"); + + DVP_CAM_CONFIG_INPUT_PIN(pin->vsync_io, cam_periph_signals.buses[ctlr_id].vsync_sig, true); + DVP_CAM_CONFIG_INPUT_PIN(pin->de_io, cam_periph_signals.buses[ctlr_id].de_sig, false); + DVP_CAM_CONFIG_INPUT_PIN(pin->pclk_io, cam_periph_signals.buses[ctlr_id].pclk_sig, false); + for (int i = 0; i < pin->data_width; i++) { + DVP_CAM_CONFIG_INPUT_PIN(pin->data_io[i], cam_periph_signals.buses[ctlr_id].data_sigs[i], false); + } + + ret = gpio_set_direction(pin->xclk_io, GPIO_MODE_OUTPUT); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "failed to configure pin=%d", pin->xclk_io); + return ret; + } + esp_rom_gpio_connect_out_signal(pin->xclk_io, cam_periph_signals.buses[ctlr_id].clk_sig, false, false); + + PERIPH_RCC_ACQUIRE_ATOMIC(cam_periph_signals.buses[ctlr_id].module, ref_count) { + if (ref_count == 0) { + cam_ll_enable_bus_clock(ctlr_id, true); + cam_ll_reset_register(ctlr_id); + } + } + + PERIPH_RCC_ATOMIC() { + cam_ll_enable_clk(ctlr_id, true); + cam_ll_select_clk_src(ctlr_id, clk_src); + }; + + return ESP_OK; +} + +/** + * @brief ESP CAM DVP output hardware clock + * + * @param ctlr_id CAM DVP controller ID + * @param clk_src CAM DVP clock source + * @param xclk_freq CAM DVP output clock frequency in HZ + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_output_clock(int ctlr_id, cam_clock_source_t clk_src, uint32_t xclk_freq) +{ + esp_err_t ret = ESP_ERR_INVALID_ARG; + uint32_t src_clk_hz; + + ESP_RETURN_ON_FALSE(ctlr_id < CAP_DVP_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid argument: ctlr_id >= %d", CAP_DVP_PERIPH_NUM); + ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_hz), + TAG, "failed to get clock source frequency"); + + ESP_LOGD(TAG, "DVP clock source frequency %" PRIu32 "Hz", src_clk_hz); + + if ((src_clk_hz % xclk_freq) == 0) { + PERIPH_RCC_ATOMIC() { + cam_ll_set_group_clock_coeff(ctlr_id, src_clk_hz / xclk_freq, 0, 0); + }; + + ret = ESP_OK; + } + + return ret; +} + +/** + * @brief ESP CAM DVP de-initialzie. + * + * @param ctlr_id DVP controller ID + * + * @return + * - ESP_OK on success + * - Others if failed + */ +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() { + cam_ll_enable_clk(ctlr_id, false); + }; + + PERIPH_RCC_ACQUIRE_ATOMIC(cam_periph_signals.buses[ctlr_id].module, ref_count) { + if (ref_count == 0) { + cam_ll_reset_register(ctlr_id); + cam_ll_enable_bus_clock(ctlr_id, false); + } + } + + return ESP_OK; +} + +/** + * @brief Enable CAM DVP camera controller + * + * @param handle ESP CAM controller handle + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_dvp_cam_enable(esp_cam_ctlr_handle_t handle) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + esp_cam_ctlr_dvp_cam_t *ctlr = (esp_cam_ctlr_dvp_cam_t *)handle; + + ESP_RETURN_ON_FALSE(ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: handle is null"); + + portENTER_CRITICAL(&ctlr->spinlock); + if (ctlr->dvp_fsm == ESP_CAM_CTLR_DVP_CAM_FSM_INIT) { + ctlr->dvp_fsm = ESP_CAM_CTLR_DVP_CAM_FSM_ENABLED; + ret = ESP_OK; + } + portEXIT_CRITICAL(&ctlr->spinlock); + + return ret; +} + +/** + * @brief Disable CAM DVP camera controller + * + * @param handle ESP CAM controller handle + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_dvp_cam_disable(esp_cam_ctlr_handle_t handle) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + esp_cam_ctlr_dvp_cam_t *ctlr = (esp_cam_ctlr_dvp_cam_t *)handle; + + ESP_RETURN_ON_FALSE(ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: handle is null"); + + portENTER_CRITICAL(&ctlr->spinlock); + if (ctlr->dvp_fsm == ESP_CAM_CTLR_DVP_CAM_FSM_ENABLED) { + ctlr->dvp_fsm = ESP_CAM_CTLR_DVP_CAM_FSM_INIT; + ret = ESP_OK; + } + portEXIT_CRITICAL(&ctlr->spinlock); + + return ret; +} + +/** + * @brief Start CAM DVP camera controller + * + * @param handle ESP CAM controller handle + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_dvp_cam_start(esp_cam_ctlr_handle_t handle) +{ + bool start = false; + esp_err_t ret = ESP_ERR_INVALID_STATE; + esp_cam_ctlr_dvp_cam_t *ctlr = (esp_cam_ctlr_dvp_cam_t *)handle; + + ESP_RETURN_ON_FALSE(ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: handle is null"); + + portENTER_CRITICAL(&ctlr->spinlock); + if (ctlr->dvp_fsm == ESP_CAM_CTLR_DVP_CAM_FSM_ENABLED) { + ctlr->cur_buf = NULL; + ctlr->dvp_fsm = ESP_CAM_CTLR_DVP_CAM_FSM_STARTED; + start = true; + } + portEXIT_CRITICAL(&ctlr->spinlock); + + if (start) { + ret = esp_cam_ctlr_dvp_start_trans(ctlr); + } + + return ret; +} + +/** + * @brief Stop CAM DVP camera controller + * + * @param handle ESP CAM controller handle + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_dvp_cam_stop(esp_cam_ctlr_handle_t handle) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + esp_cam_ctlr_dvp_cam_t *ctlr = (esp_cam_ctlr_dvp_cam_t *)handle; + + ESP_RETURN_ON_FALSE(ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: handle is null"); + + portENTER_CRITICAL(&ctlr->spinlock); + if (ctlr->dvp_fsm == ESP_CAM_CTLR_DVP_CAM_FSM_STARTED) { + cam_hal_stop_streaming(&ctlr->hal); + esp_cam_ctlr_dvp_dma_stop(&ctlr->dma); + ctlr->dvp_fsm = ESP_CAM_CTLR_DVP_CAM_FSM_ENABLED; + ret = ESP_OK; + } + portEXIT_CRITICAL(&ctlr->spinlock); + + return ret; +} + +/** + * @brief Delete CAM DVP camera controller + * + * @param handle ESP CAM controller handle + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_dvp_cam_del(esp_cam_ctlr_handle_t handle) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + esp_cam_ctlr_dvp_cam_t *ctlr = (esp_cam_ctlr_dvp_cam_t *)handle; + + ESP_RETURN_ON_FALSE(ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: handle is null"); + + if (ctlr->dvp_fsm == ESP_CAM_CTLR_DVP_CAM_FSM_INIT) { + esp_cam_ctlr_dvp_dma_stop(&ctlr->dma); + cam_hal_stop_streaming(&ctlr->hal); + + esp_cam_ctlr_dvp_dma_deinit(&ctlr->dma); + + if (!ctlr->pin_dont_init) { + esp_cam_ctlr_dvp_deinit(ctlr->ctlr_id); + } + + cam_hal_deinit(&ctlr->hal); + + if (!ctlr->bk_buffer_dis) { + heap_caps_free(ctlr->backup_buffer); + } + + s_dvp_declaim_ctlr(ctlr->ctlr_id); + + heap_caps_free(ctlr); + + dvp_shared_ctrl_declaim_io_signals(); + + ret = ESP_OK; + } + + return ret; +} + +/** + * @brief Register CAM DVP camera controller event callbacks + * + * @param handle ESP CAM controller handle + * @param cbs ESP CAM controller event callbacks + * @param user_data ESP CAM controller event user data + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_dvp_cam_register_event_callbacks(esp_cam_ctlr_handle_t handle, const esp_cam_ctlr_evt_cbs_t *cbs, void *user_data) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + esp_cam_ctlr_dvp_cam_t *ctlr = (esp_cam_ctlr_dvp_cam_t *)handle; + + ESP_RETURN_ON_FALSE(handle && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument: handle or cbs is null"); + ESP_RETURN_ON_FALSE(cbs->on_trans_finished, ESP_ERR_INVALID_ARG, TAG, "invalid argument: on_trans_finished is null"); + ESP_RETURN_ON_FALSE(cbs->on_get_new_trans || !ctlr->bk_buffer_dis, ESP_ERR_INVALID_ARG, TAG, "invalid argument: on_get_new_trans is null"); + +#if CONFIG_CAM_CTLR_DVP_CAM_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 + + portENTER_CRITICAL(&ctlr->spinlock); + if (ctlr->dvp_fsm == ESP_CAM_CTLR_DVP_CAM_FSM_INIT) { + 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; + ret = ESP_OK; + } + portEXIT_CRITICAL(&ctlr->spinlock); + + return ret; +} + +/** + * @brief Get AM DVP camera controller backup buffer pointer + * + * @param handle ESP CAM controller handle + * @param fb_num Backup buffer pointer storage buffer number + * @param fb0 Backup buffer pointer storage buffer + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_dvp_cam_get_internal_buffer(esp_cam_ctlr_handle_t handle, uint32_t fb_num, const void **fb0, ...) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + esp_cam_ctlr_dvp_cam_t *ctlr = (esp_cam_ctlr_dvp_cam_t *)handle; + + ESP_RETURN_ON_FALSE(ctlr->backup_buffer, ESP_ERR_INVALID_STATE, TAG, "back_buffer is not available"); + ESP_RETURN_ON_FALSE(fb_num && fb_num <= 1, ESP_ERR_INVALID_ARG, TAG, "invalid frame buffer number"); + + portENTER_CRITICAL(&ctlr->spinlock); + if (ctlr->dvp_fsm == ESP_CAM_CTLR_DVP_CAM_FSM_INIT) { + va_list args; + const void **fb_itor = fb0; + + va_start(args, fb0); + for (uint32_t i = 0; i < fb_num; i++) { + if (fb_itor) { + *fb_itor = ctlr->backup_buffer; + fb_itor = va_arg(args, const void **); + } + } + va_end(args); + + ctlr->bk_buffer_exposed = true; + ret = ESP_OK; + } + portEXIT_CRITICAL(&ctlr->spinlock); + + return ret; +} + +/** + * @brief Get CAM DVP camera controller frame buffer length + * + * @param handle ESP CAM controller handle + * @param ret_fb_len The size of each frame buffer in bytes + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static esp_err_t esp_cam_ctlr_get_dvp_cam_frame_buffer_len(esp_cam_ctlr_handle_t handle, size_t *ret_fb_len) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + esp_cam_ctlr_dvp_cam_t *ctlr = (esp_cam_ctlr_dvp_cam_t *)handle; + + ESP_RETURN_ON_FALSE(ctlr && ret_fb_len, ESP_ERR_INVALID_ARG, TAG, "invalid argument: handle or ret_fb_len is null"); + + portENTER_CRITICAL(&ctlr->spinlock); + if (ctlr->dvp_fsm == ESP_CAM_CTLR_DVP_CAM_FSM_INIT) { + *ret_fb_len = ctlr->fb_size_in_bytes; + ret = ESP_OK; + } + portEXIT_CRITICAL(&ctlr->spinlock); + + return ret; +} + +/** + * @brief New ESP CAM DVP controller + * + * @param config DVP controller configurations + * @param ret_handle Returned CAM controller handle + * + * @return + * - ESP_OK on success + * - 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_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_ctlr_handle_t *ret_handle) +{ + esp_err_t ret; + size_t fb_size_in_bytes; + size_t alignment_size; + + ESP_RETURN_ON_FALSE(config && ret_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: config or ret_handle is null"); + ESP_RETURN_ON_FALSE(config->ctlr_id < CAP_DVP_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid argument: ctlr_id >= %d", CAP_DVP_PERIPH_NUM); + ESP_RETURN_ON_FALSE(config->pin_dont_init || config->pin, ESP_ERR_INVALID_ARG, TAG, "invalid argument: pin_dont_init is unset and pin is null"); + 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_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_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"); + + 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); + ESP_GOTO_ON_FALSE(ctlr->backup_buffer, ESP_ERR_NO_MEM, fail2, TAG, "no mem for DVP backup buffer"); + } + + ESP_GOTO_ON_ERROR(esp_cam_ctlr_dvp_dma_init(&ctlr->dma, config->dma_burst_size, fb_size_in_bytes), + fail3, TAG, "failed to initialize DVP DMA"); + + gdma_rx_event_callbacks_t cbs = { + .on_recv_eof = esp_cam_ctlr_recv_frame_done_isr + }; + + ESP_GOTO_ON_ERROR(gdma_register_rx_event_callbacks(ctlr->dma.dma_chan, &cbs, ctlr), + fail4, TAG, "failed to register DMA event callbacks"); + + /* Initialize DVP controller */ + + cam_hal_config_t cam_hal_config = { + .port = config->ctlr_id, + .byte_swap_en = config->byte_swap_en, + }; + cam_hal_init(&ctlr->hal, &cam_hal_config); + + if (!config->pin_dont_init) { + ESP_GOTO_ON_ERROR(esp_cam_ctlr_dvp_init(config->ctlr_id, config->clk_src, config->pin), + fail5, TAG, "failed to initialize clock and GPIO"); + } + + ctlr->ctlr_id = config->ctlr_id; + ctlr->fb_size_in_bytes = fb_size_in_bytes; + ctlr->dvp_fsm = ESP_CAM_CTLR_DVP_CAM_FSM_INIT; + ctlr->input_data_color_type = config->input_data_color_type; + ctlr->pic_format_jpeg = config->pic_format_jpeg; + ctlr->bk_buffer_dis = config->bk_buffer_dis; + ctlr->pin_dont_init = config->pin_dont_init; + ctlr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + + ctlr->base.del = esp_cam_ctlr_dvp_cam_del; + ctlr->base.enable = esp_cam_ctlr_dvp_cam_enable; + ctlr->base.start = esp_cam_ctlr_dvp_cam_start; + ctlr->base.stop = esp_cam_ctlr_dvp_cam_stop; + ctlr->base.disable = esp_cam_ctlr_dvp_cam_disable; + ctlr->base.register_event_callbacks = esp_cam_ctlr_dvp_cam_register_event_callbacks; + ctlr->base.get_internal_buffer = esp_cam_ctlr_dvp_cam_get_internal_buffer; + ctlr->base.get_buffer_len = esp_cam_ctlr_get_dvp_cam_frame_buffer_len; + + *ret_handle = &ctlr->base; + + return ESP_OK; + +fail5: + cam_hal_deinit(&ctlr->hal); +fail4: + esp_cam_ctlr_dvp_dma_deinit(&ctlr->dma); +fail3: + if (!config->bk_buffer_dis) { + heap_caps_free(ctlr->backup_buffer); + } +fail2: + s_dvp_declaim_ctlr(config->ctlr_id); +fail1: + heap_caps_free(ctlr); +fail0: + dvp_shared_ctrl_declaim_io_signals(); + return ret; +} diff --git a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c new file mode 100644 index 0000000000..4e428b2505 --- /dev/null +++ b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c @@ -0,0 +1,202 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_heap_caps.h" +#include "esp_check.h" +#include "esp_cache.h" +#include "esp_private/esp_cache_private.h" +#include "esp_cam_ctlr_dvp_dma.h" + +#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + +static const char *TAG = "dvp_gdma"; + +/** + * @brief Configure DMA description + * + * @param dma DVP DMA object pointer + * + * @return + * - ESP_OK on success + * - Others if failed + */ +static void IRAM_ATTR esp_cam_ctlr_dvp_config_dma_desc(esp_cam_ctlr_dvp_dma_desc_t *desc, uint8_t *buffer, uint32_t size) +{ + size_t n = 0; + + while (size) { + uint32_t node_size = MIN(size, ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE); + + desc[n].dw0.size = node_size; + desc[n].dw0.length = 0; + desc[n].dw0.err_eof = 0; + desc[n].dw0.suc_eof = 0; + desc[n].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; + desc[n].buffer = (uint8_t *)buffer; + desc[n].next = &desc[n + 1]; + + size -= node_size; + buffer += node_size; + n++; + } + + desc[n - 1].next = NULL; +} + +/** + * @brief Initialize DVP DMA object + * + * @param dma DVP DMA object pointer + * @param burst_size DVP DMA burst transmission block size + * @param size DVP DMA buffer size + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_size, size_t size) +{ + esp_err_t ret = ESP_OK; + size_t alignment_size; + + gdma_channel_alloc_config_t rx_alloc_config = { + .direction = GDMA_CHANNEL_DIRECTION_RX, + }; + + 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(gdma_new_axi_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"); + + gdma_strategy_config_t strategy_config = { + .auto_update_desc = false, + .owner_check = true + }; + + ESP_GOTO_ON_ERROR(gdma_apply_strategy(dma->dma_chan, &strategy_config), fail1, TAG, "apply strategy failed"); + // set DMA transfer ability + gdma_transfer_config_t transfer_config = { + .max_data_burst_size = burst_size, + .access_ext_mem = true, + }; + ESP_GOTO_ON_ERROR(gdma_config_transfer(dma->dma_chan, &transfer_config), fail1, TAG, "set trans ability failed"); + + 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); + 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_GOTO_ON_FALSE(dma->desc, ESP_ERR_NO_MEM, fail1, TAG, "no mem for DVP DMA descriptor"); + + return ESP_OK; + +fail1: + gdma_disconnect(dma->dma_chan); +fail0: + gdma_del_channel(dma->dma_chan); + return ret; +} + +/** + * @brief De-initialize DVP DMA object + * + * @param dma DVP DMA object pointer + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_dma_deinit(esp_cam_ctlr_dvp_dma_t *dma) +{ + ESP_RETURN_ON_ERROR(gdma_disconnect(dma->dma_chan), TAG, "disconnect dma channel failed"); + ESP_RETURN_ON_ERROR(gdma_del_channel(dma->dma_chan), TAG, "delete dma channel failed"); + + heap_caps_free(dma->desc); + + return ESP_OK; +} + +/** + * @brief Set DVP DMA descriptor address and start engine + * + * @param dma DVP DMA object pointer + * @param buffer DVP DMA buffer pointer + * @param size DVP DMA buffer size + * + * @return + * - ESP_OK on success + * - Others if failed + */ +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); + + return gdma_start(dma->dma_chan, (intptr_t)dma->desc); +} + +/** + * @brief Stop DVP DMA engine + * + * @param dma DVP DMA object pointer + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t IRAM_ATTR esp_cam_ctlr_dvp_dma_stop(esp_cam_ctlr_dvp_dma_t *dma) +{ + return gdma_stop(dma->dma_chan); +} + +/** + * @brief Reset DVP DMA FIFO and internal finite state machine + * + * @param dma DVP DMA object pointer + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t IRAM_ATTR esp_cam_ctlr_dvp_dma_reset(esp_cam_ctlr_dvp_dma_t *dma) +{ + return gdma_reset(dma->dma_chan); +} + +/** + * @brief Get DMA received data size + * + * @param dma DVP DMA object pointer + * + * @return DMA received data size + */ +size_t IRAM_ATTR esp_cam_ctlr_dvp_dma_get_recv_size(esp_cam_ctlr_dvp_dma_t *dma) +{ + size_t recv_size = 0; + + for (int i = 0; i < dma->desc_count; i++) { + recv_size += dma->desc[i].dw0.length; + if (dma->desc[i].dw0.suc_eof) { + break; + } + } + + return recv_size; +} diff --git a/components/esp_driver_cam/test_apps/.build-test-rules.yml b/components/esp_driver_cam/test_apps/.build-test-rules.yml index 62f5e1aef9..0d419e7609 100644 --- a/components/esp_driver_cam/test_apps/.build-test-rules.yml +++ b/components/esp_driver_cam/test_apps/.build-test-rules.yml @@ -4,6 +4,12 @@ components/esp_driver_cam/test_apps/csi: depends_components: - esp_driver_cam +components/esp_driver_cam/test_apps/dvp: + disable: + - if: SOC_LCDCAM_CAM_SUPPORTED != 1 + depends_components: + - esp_driver_cam + components/esp_driver_cam/test_apps/isp_dvp: disable: - if: SOC_ISP_DVP_SUPPORTED != 1 diff --git a/components/esp_driver_cam/test_apps/dvp/CMakeLists.txt b/components/esp_driver_cam/test_apps/dvp/CMakeLists.txt new file mode 100644 index 0000000000..d49275de91 --- /dev/null +++ b/components/esp_driver_cam/test_apps/dvp/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_dvp) diff --git a/components/esp_driver_cam/test_apps/dvp/README.md b/components/esp_driver_cam/test_apps/dvp/README.md new file mode 100644 index 0000000000..909282018f --- /dev/null +++ b/components/esp_driver_cam/test_apps/dvp/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | diff --git a/components/esp_driver_cam/test_apps/dvp/main/CMakeLists.txt b/components/esp_driver_cam/test_apps/dvp/main/CMakeLists.txt new file mode 100644 index 0000000000..3ad81cd956 --- /dev/null +++ b/components/esp_driver_cam/test_apps/dvp/main/CMakeLists.txt @@ -0,0 +1,16 @@ +set(srcs "test_app_main.c") + +if(CONFIG_SOC_LCDCAM_CAM_SUPPORTED) + list(APPEND srcs "test_dvp_driver.c") +endif() + +set(priv_requires + unity + esp_driver_cam + esp_psram +) + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS "." + PRIV_REQUIRES ${priv_requires} + WHOLE_ARCHIVE TRUE) diff --git a/components/esp_driver_cam/test_apps/dvp/main/test_app_main.c b/components/esp_driver_cam/test_apps/dvp/main/test_app_main.c new file mode 100644 index 0000000000..a47ab1a130 --- /dev/null +++ b/components/esp_driver_cam/test_apps/dvp/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_driver_cam/test_apps/dvp/main/test_dvp_driver.c b/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c new file mode 100644 index 0000000000..4f6b7041e8 --- /dev/null +++ b/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c @@ -0,0 +1,86 @@ +/* + * 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_dvp.h" +#include "esp_cam_ctlr.h" + +TEST_CASE("TEST DVP driver allocation", "[DVP]") +{ + esp_cam_ctlr_dvp_config_t dvp_config = { + .ctlr_id = 0, + .clk_src = CAM_CLK_SRC_DEFAULT, + .h_res = 800, + .v_res = 640, + .input_data_color_type = CAM_CTLR_COLOR_RGB565, + .dma_burst_size = 128, + .byte_swap_en = false, + .pin_dont_init = true, + }; + esp_cam_ctlr_handle_t handle = NULL; + TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle)); + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_cam_new_dvp_ctlr(&dvp_config, &handle)); + + uint8_t *bk_buffer = NULL; + size_t bk_buffer_len = 0; + TEST_ESP_OK(esp_cam_ctlr_get_frame_buffer(handle, 1, (const void **)&bk_buffer)); + TEST_ESP_OK(esp_cam_ctlr_get_frame_buffer_len(handle, &bk_buffer_len)); + TEST_ASSERT_NOT_NULL(bk_buffer); + TEST_ASSERT_EQUAL((dvp_config.h_res * dvp_config.v_res * 2), bk_buffer_len); // type RGB565 using 2 byte / pixel + TEST_ESP_OK(esp_cam_ctlr_del(handle)); +} + +TEST_CASE("TEST DVP driver allocation with JPEG input", "[DVP]") +{ + esp_cam_ctlr_dvp_config_t dvp_config = { + .ctlr_id = 0, + .clk_src = CAM_CLK_SRC_DEFAULT, + .h_res = 800, + .v_res = 640, + .dma_burst_size = 128, + .byte_swap_en = false, + .pin_dont_init = true, + .pic_format_jpeg = true, + }; + esp_cam_ctlr_handle_t handle = NULL; + TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle)); + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_cam_new_dvp_ctlr(&dvp_config, &handle)); + + uint8_t *bk_buffer = NULL; + size_t bk_buffer_len = 0; + TEST_ESP_OK(esp_cam_ctlr_get_frame_buffer(handle, 1, (const void **)&bk_buffer)); + TEST_ESP_OK(esp_cam_ctlr_get_frame_buffer_len(handle, &bk_buffer_len)); + TEST_ASSERT_NOT_NULL(bk_buffer); + TEST_ASSERT_EQUAL((dvp_config.h_res * dvp_config.v_res * 1), bk_buffer_len); // type JPEG using 1 byte / pixel + TEST_ESP_OK(esp_cam_ctlr_del(handle)); +} + +TEST_CASE("TEST DVP driver no backup buffer usage", "[DVP]") +{ + esp_cam_ctlr_dvp_config_t dvp_config = { + .ctlr_id = 0, + .clk_src = CAM_CLK_SRC_DEFAULT, + .h_res = 800, + .v_res = 640, + .input_data_color_type = CAM_CTLR_COLOR_RGB565, + .dma_burst_size = 128, + .byte_swap_en = false, + .bk_buffer_dis = true, + .pin_dont_init = true, + }; + esp_cam_ctlr_handle_t handle = NULL; + TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle)); + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_cam_new_dvp_ctlr(&dvp_config, &handle)); + + uint8_t *bk_buffer = NULL; + size_t bk_buffer_len = 0; + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_cam_ctlr_get_frame_buffer(handle, 1, (const void **)&bk_buffer)); + TEST_ESP_OK(esp_cam_ctlr_get_frame_buffer_len(handle, &bk_buffer_len)); + TEST_ASSERT_NULL(bk_buffer); + TEST_ASSERT_EQUAL((dvp_config.h_res * dvp_config.v_res * 2), bk_buffer_len); // out type RGB565 using 2 byte / pixel + TEST_ESP_OK(esp_cam_ctlr_del(handle)); +} diff --git a/components/esp_driver_cam/test_apps/dvp/pytest_dvp.py b/components/esp_driver_cam/test_apps/dvp/pytest_dvp.py new file mode 100644 index 0000000000..139f1c0694 --- /dev/null +++ b/components/esp_driver_cam/test_apps/dvp/pytest_dvp.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_dvp(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults b/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults new file mode 100644 index 0000000000..f02afdd3c6 --- /dev/null +++ b/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults @@ -0,0 +1,6 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration +# +CONFIG_SPIRAM=y +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 diff --git a/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults.esp32p4 b/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000000..5fca5fa080 --- /dev/null +++ b/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults.esp32p4 @@ -0,0 +1,3 @@ +CONFIG_IDF_TARGET="esp32p4" +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index 750fddce90..573199e54c 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -260,6 +260,10 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "ds_hal.c") endif() + if(CONFIG_SOC_LCDCAM_CAM_SUPPORTED) + list(APPEND srcs "cam_hal.c") + endif() + if(CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED) list(APPEND srcs "usb_serial_jtag_hal.c") endif() diff --git a/components/hal/cam_hal.c b/components/hal/cam_hal.c new file mode 100644 index 0000000000..101cf3bfcb --- /dev/null +++ b/components/hal/cam_hal.c @@ -0,0 +1,121 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/cam_ll.h" +#include "hal/cam_hal.h" +#include "soc/soc_caps.h" +#include "soc/cam_periph.h" + +/** + * @brief Configure line number to trigger interrupt + * + * @param hal CAM object data pointer + * @param num line number + * + * @return None + */ +static void cam_hal_set_line_int_num(cam_hal_context_t *hal, uint32_t num) +{ + if (num > 0) { + cam_ll_enable_hs_line_int(hal->hw, 1); + cam_ll_set_line_int_num(hal->hw, num); + } else { + cam_ll_enable_hs_line_int(hal->hw, 0); + cam_ll_set_line_int_num(hal->hw, 0); + } +} + +/** + * @brief Configure V-SYNC filter threshold + * + * @param hal CAM object data pointer + * @param num V-SYNC filter threshold + * + * @return None + */ +static void cam_hal_set_vsync_filter_num(cam_hal_context_t *hal, uint32_t num) +{ + if (num > 0) { + cam_ll_enable_vsync_filter(hal->hw, 1); + cam_ll_set_vsync_filter_thres(hal->hw, num); + } else { + cam_ll_enable_vsync_filter(hal->hw, 0); + cam_ll_set_vsync_filter_thres(hal->hw, 0); + } +} + +/** + * @brief Initialize CAM hardware + * + * @param hal CAM object data pointer + * @param config CAM configuration + * + * @return None + */ +void cam_hal_init(cam_hal_context_t *hal, const cam_hal_config_t *config) +{ + memset(hal, 0, sizeof(cam_hal_context_t)); + + hal->hw = CAM_LL_GET_HW(0); + + cam_ll_enable_stop_signal(hal->hw, 0); + cam_ll_swap_dma_data_byte_order(hal->hw, config->byte_swap_en); + cam_ll_reverse_dma_data_bit_order(hal->hw, 0); + cam_ll_enable_vsync_generate_eof(hal->hw, 1); + + cam_hal_set_line_int_num(hal, 0); + cam_hal_set_vsync_filter_num(hal, 0); + + cam_ll_enable_invert_pclk(hal->hw, 0); + cam_ll_set_input_data_width(hal->hw, 8); + cam_ll_enable_invert_de(hal->hw, 0); + cam_ll_enable_invert_vsync(hal->hw, 0); + cam_ll_enable_invert_hsync(hal->hw, 0); + cam_ll_set_vh_de_mode(hal->hw, 0); // Disable vh_de mode default + cam_ll_enable_rgb_yuv_convert(hal->hw, 0); // bypass conv module default +} + +/** + * @brief De-initialize CAM hardware + * + * @note Stop stream before deinit + * @param hal CAM object data pointer + * + * @return None + */ +void cam_hal_deinit(cam_hal_context_t *hal) +{ + cam_ll_stop(hal->hw); + cam_ll_reset(hal->hw); + cam_ll_fifo_reset(hal->hw); +} + +/** + * @brief Start CAM to receive frame data + * + * @param hal CAM object data pointer + * + * @return None + */ +void cam_hal_start_streaming(cam_hal_context_t *hal) +{ + cam_ll_reset(hal->hw); + cam_ll_fifo_reset(hal->hw); + + cam_ll_start(hal->hw); +} + +/** + * @brief Stop CAM receiving frame data + * + * @param hal CAM object data pointer + * + * @return None + */ +void cam_hal_stop_streaming(cam_hal_context_t *hal) +{ + cam_ll_stop(hal->hw); +} diff --git a/components/hal/esp32p4/include/hal/cam_ll.h b/components/hal/esp32p4/include/hal/cam_ll.h new file mode 100644 index 0000000000..a48dba2dc8 --- /dev/null +++ b/components/hal/esp32p4/include/hal/cam_ll.h @@ -0,0 +1,634 @@ +/* + * SPDX-FileCopyrightText: 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/lcd_cam_struct.h" +#include "soc/hp_sys_clkrst_struct.h" +#include "hal/cam_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CAM_LL_GET_HW(id) (((id) == 0) ? (&LCD_CAM) : NULL) + +#define CAM_LL_CLK_FRAC_DIV_N_MAX 256 // CAM_CLK = CAM_CLK_S / (N + b/a), the N register is 8 bit-width +#define CAM_LL_CLK_FRAC_DIV_AB_MAX 64 // CAM_CLK = CAM_CLK_S / (N + b/a), the a/b register is 6 bit-width + +/** + * @brief Enable the bus clock for CAM module + * + * @param group_id Group ID + * @param enable true to enable, false to disable + */ +static inline void cam_ll_enable_bus_clock(int group_id, bool en) +{ + (void)group_id; + HP_SYS_CLKRST.soc_clk_ctrl3.reg_lcdcam_apb_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_RC_ATOMIC_ENV variable in advance +#define cam_ll_enable_bus_clock(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; cam_ll_enable_bus_clock(__VA_ARGS__) + +/** + * @brief Reset the CAM module + * + * @param group_id Group ID + */ +static inline void cam_ll_reset_register(int group_id) +{ + (void)group_id; + HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_lcdcam = 1; + HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_lcdcam = 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_RC_ATOMIC_ENV variable in advance +#define cam_ll_reset_register(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; cam_ll_reset_register(__VA_ARGS__) + +/** + * @brief Enable clock gating + * + * @param group_id Group ID + * @param en True to enable, False to disable + */ +static inline void cam_ll_enable_clk(int group_id, bool en) +{ + HP_SYS_CLKRST.peri_clk_ctrl119.reg_cam_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 cam_ll_enable_clk(...) (void)__DECLARE_RCC_ATOMIC_ENV; cam_ll_enable_clk(__VA_ARGS__) + +/** + * @brief Get the clock status for the CAM module + * + * @return True when enabled, false when disabled + */ +static inline bool cam_ll_get_clk_status(int group_id) +{ + (void)group_id; + return HP_SYS_CLKRST.peri_clk_ctrl119.reg_cam_clk_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 cam_ll_get_clk_status(...) (void)__DECLARE_RCC_ATOMIC_ENV; cam_ll_get_clk_status(__VA_ARGS__) + +/** + * @brief Select clock source for CAM peripheral + * + * @param group_id Group ID + * @param src Clock source + */ +static inline void cam_ll_select_clk_src(int group_id, cam_clock_source_t src) +{ + switch (src) { + case CAM_CLK_SRC_XTAL: + HP_SYS_CLKRST.peri_clk_ctrl119.reg_cam_clk_src_sel = 0; + break; + case CAM_CLK_SRC_PLL160M: + HP_SYS_CLKRST.peri_clk_ctrl119.reg_cam_clk_src_sel = 1; + break; + case CAM_CLK_SRC_APLL: + HP_SYS_CLKRST.peri_clk_ctrl119.reg_cam_clk_src_sel = 2; + break; + default: + HAL_ASSERT(false); + break; + } +} + +/// 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 cam_ll_select_clk_src(...) (void)__DECLARE_RCC_ATOMIC_ENV; cam_ll_select_clk_src(__VA_ARGS__) + +/** + * @brief Get the CAM source clock type + * + * @param dev CAM register base address + * @param src The pointer to accept the CAM source clock type + */ +static inline void cam_ll_get_clk_src(lcd_cam_dev_t *dev, cam_clock_source_t *src) +{ + switch (HP_SYS_CLKRST.peri_clk_ctrl119.reg_cam_clk_src_sel) { + case 0: + *src = CAM_CLK_SRC_XTAL; + break; + case 1: + *src = CAM_CLK_SRC_PLL160M; + break; + case 2: + *src = CAM_CLK_SRC_APLL; + break; + default: + HAL_ASSERT(false); + break; + } +} + +/** + * @brief Set clock coefficient of CAM peripheral + * + * @param group_id Group ID + * @param div_num Integer part of the divider + * @param div_a denominator of the divider + * @param div_b numerator of the divider + */ +__attribute__((always_inline)) +static inline void cam_ll_set_group_clock_coeff(int group_id, int div_num, int div_a, int div_b) +{ + // cam_clk = module_clock_src / (div_num + div_b / div_a) + HAL_ASSERT(div_num >= 2 && div_num < CAM_LL_CLK_FRAC_DIV_N_MAX); + + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl120, reg_cam_clk_div_num, div_num); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl120, reg_cam_clk_div_denominator, div_a); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl120, reg_cam_clk_div_numerator, div_b); +} + +/// 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 cam_ll_set_group_clock_coeff(...) (void)__DECLARE_RCC_ATOMIC_ENV; cam_ll_set_group_clock_coeff(__VA_ARGS__) + +/** + * @brief Enable stop signal for CAM peripheral + * + * @param dev CAM register base address + * @param en True to stop when GDMA Rx FIFO is full, False to not stop + */ +static inline void cam_ll_enable_stop_signal(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_stop_en = en; +} + +/** + * @brief Set vsync filter threshold value + * + * @param dev CAM register base address + * @param value Filter threshold value for CAM_VSYNC_SIGNAL, range [0, 7] + */ +static inline void cam_ll_set_vsync_filter_thres(lcd_cam_dev_t *dev, uint32_t value) +{ + dev->cam_ctrl.cam_vsync_filter_thres = value; +} + +/** + * @brief Enable to generate LCD_CAM_CAM_HS_INT + * + * @param dev CAM register base address + * @param en True to enable to generate LCD_CAM_CAM_HS_INT, False to disable + */ +static inline void cam_ll_enable_hs_line_int(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_line_int_en = en; +} + +/** + * @brief Enable CAM_VSYNC to generate in_suc_eof + * + * @param dev CAM register base address + * @param en True to enable CAM_VSYNC to generate in_suc_eof, False to use LCD_CAM_CAM_REC_DATA_BYTELEN to control in_suc_eof + */ +static inline void cam_ll_enable_vsync_generate_eof(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_vs_eof_en = en; +} + +/** + * @brief Enable to swap every two 8-bit input data + * + * @param dev CAM register base address + * @param en True to enable invert, False to disable invert + */ +static inline void cam_ll_enable_8bits_data_invert(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_rgb_yuv.cam_conv_8bits_data_inv = en; +} + +/** + * @brief Enable YUV-RGB converter + * + * @param dev CAM register base address + * @param en True to enable converter, False to disable converter + */ +static inline void cam_ll_enable_rgb_yuv_convert(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_rgb_yuv.cam_conv_enable = en; +} + +/** + * @brief Set convert data line width + * + * @param dev CAM register base address + * @param width data line width (8 or 16) + */ +static inline void cam_ll_set_convert_data_width(lcd_cam_dev_t *dev, uint32_t width) +{ + HAL_ASSERT(width == 8 || width == 16); + dev->cam_rgb_yuv.cam_conv_mode_8bits_on = (width == 8) ? 1 : 0; +} + +/** + * @brief Set the color range of input data + * + * @param dev CAM register base address + * @param range Color range + */ +static inline void cam_ll_set_input_color_range(lcd_cam_dev_t *dev, color_range_t range) +{ + if (range == COLOR_RANGE_LIMIT) { + dev->cam_rgb_yuv.cam_conv_data_in_mode = 0; + } else if (range == COLOR_RANGE_FULL) { + dev->cam_rgb_yuv.cam_conv_data_in_mode = 1; + } +} + +/** + * @brief Set the color range of output data + * + * @param dev CAM register base address + * @param range Color range + */ +static inline void cam_ll_set_output_color_range(lcd_cam_dev_t *dev, color_range_t range) +{ + if (range == COLOR_RANGE_LIMIT) { + dev->cam_rgb_yuv.cam_conv_data_out_mode = 0; + } else if (range == COLOR_RANGE_FULL) { + dev->cam_rgb_yuv.cam_conv_data_out_mode = 1; + } +} + +/** + * @brief Set YUV conversion standard + * + * @param dev CAM register base address + * @param std YUV conversion standard + */ +static inline void cam_ll_set_yuv_convert_std(lcd_cam_dev_t *dev, color_conv_std_rgb_yuv_t std) +{ + if (std == COLOR_CONV_STD_RGB_YUV_BT601) { + dev->cam_rgb_yuv.cam_conv_protocol_mode = 0; + } else if (std == COLOR_CONV_STD_RGB_YUV_BT709) { + dev->cam_rgb_yuv.cam_conv_protocol_mode = 1; + } +} + +/** + * @brief Set the converter mode: RGB565 to YUV + * + * @param dev CAM register base address + * @param yuv_sample YUV sample mode + */ +static inline void cam_ll_set_convert_mode_rgb_to_yuv(lcd_cam_dev_t *dev, color_pixel_yuv_format_t yuv_sample) +{ + dev->cam_rgb_yuv.cam_conv_trans_mode = 1; + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3; + switch (yuv_sample) { + case COLOR_PIXEL_YUV422: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 0; + break; + case COLOR_PIXEL_YUV420: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 1; + break; + case COLOR_PIXEL_YUV411: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 2; + break; + default: + abort(); + } +} + +/** + * @brief Set the converter mode: YUV to RGB565 + * + * @param dev CAM register base address + * @param yuv_sample YUV sample mode + */ +static inline void cam_ll_set_convert_mode_yuv_to_rgb(lcd_cam_dev_t *dev, color_pixel_yuv_format_t yuv_sample) +{ + dev->cam_rgb_yuv.cam_conv_trans_mode = 0; + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3; + switch (yuv_sample) { + case COLOR_PIXEL_YUV422: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 0; + break; + case COLOR_PIXEL_YUV420: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 1; + break; + case COLOR_PIXEL_YUV411: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 2; + break; + default: + abort(); + } +} + +/** + * @brief Set the converter mode: YUV to YUV + * + * @param dev CAM register base address + * @param src_sample Source YUV sample mode + * @param dst_sample Destination YUV sample mode + */ +static inline void cam_ll_set_convert_mode_yuv_to_yuv(lcd_cam_dev_t *dev, color_pixel_yuv_format_t src_sample, color_pixel_yuv_format_t dst_sample) +{ + HAL_ASSERT(src_sample != dst_sample); + dev->cam_rgb_yuv.cam_conv_trans_mode = 1; + switch (src_sample) { + case COLOR_PIXEL_YUV422: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 0; + break; + case COLOR_PIXEL_YUV420: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 1; + break; + case COLOR_PIXEL_YUV411: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 2; + break; + default: + abort(); + } + switch (dst_sample) { + case COLOR_PIXEL_YUV422: + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 0; + break; + case COLOR_PIXEL_YUV420: + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 1; + break; + case COLOR_PIXEL_YUV411: + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 2; + break; + default: + abort(); + } +} + +/** + * @brief Set camera received data byte length + * + * @param dev CAM register base address + * @param length received data byte length, range [0, 0xFFFF] + */ +static inline void cam_ll_set_recv_data_bytelen(lcd_cam_dev_t *dev, uint32_t length) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->cam_ctrl1, cam_rec_data_bytelen, length); +} + +/** + * @brief Set line number to trigger interrupt + * + * @param dev CAM register base address + * @param number line number to trigger hs interrupt, range [0, 0x3F] + */ +static inline void cam_ll_set_line_int_num(lcd_cam_dev_t *dev, uint32_t number) +{ + dev->cam_ctrl1.cam_line_int_num = number; +} + +/** + * @brief Whether to invert the input signal CAM_PCLK + * + * @param dev CAM register base address + * @param en True to invert, False to not invert + */ +static inline void cam_ll_enable_invert_pclk(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_clk_inv = en; +} + +/** + * @brief Enable CAM_VSYNC filter function + * + * @param dev CAM register base address + * @param en True to enable, False to bypass + */ +static inline void cam_ll_enable_vsync_filter(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_vsync_filter_en = en; +} + +/** + * @brief Set CAM input data width + * + * @param dev CAM register base address + * @param stride 16: The bit number of input data is 9~16. 8: The bit number of input data is 0~8. + */ +static inline void cam_ll_set_input_data_width(lcd_cam_dev_t *dev, uint32_t stride) +{ + switch (stride) { + case 8: + dev->cam_ctrl1.cam_2byte_en = 0; + break; + case 16: + dev->cam_ctrl1.cam_2byte_en = 1; + break; + default: + HAL_ASSERT(false); + break; + } +} + +/** + * @brief Whether to invert CAM_DE + * + * @param dev CAM register base address + * @param en True to invert, False to not invert + */ +static inline void cam_ll_enable_invert_de(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_de_inv = en; +} + +/** + * @brief Whether to invert CAM_HSYNC + * + * @param dev CAM register base address + * @param en True to invert, False to not invert + */ +static inline void cam_ll_enable_invert_hsync(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_hsync_inv = en; +} + +/** + * @brief Whether to invert CAM_VSYNC + * + * @param dev CAM register base address + * @param en True to invert, False to not invert + */ +static inline void cam_ll_enable_invert_vsync(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_vsync_inv = en; +} + +/** + * @brief Enable the mode to control the input control signals + * + * @param dev CAM register base address + * @param mode 1: Input control signals are CAM_DE, CAM_HSYNC and CAM_VSYNC; + * 0: Input control signals are CAM_DE and CAM_VSYNC. CAM_HSYNC and CAM_DE are all 1 the the same time. + */ +static inline void cam_ll_set_vh_de_mode(lcd_cam_dev_t *dev, bool enable) +{ + dev->cam_ctrl1.cam_vh_de_mode_en = enable; +} + +/** + * @brief Get the mode of input control signals + * + * @param dev CAM register base address + * @param en The pointer to accept the vh_de mode status. 1: Input control signals are CAM_DE, CAM_HSYNC and CAM_VSYNC; + * 0: Input control signals are CAM_DE and CAM_VSYNC. CAM_HSYNC and CAM_DE are all 1 the the same time. + */ +static inline void cam_ll_get_vh_de_mode(lcd_cam_dev_t *dev, bool *en) +{ + *en = dev->cam_ctrl1.cam_vh_de_mode_en; +} + +/** + * @brief Set the wire width of CAM output + * + * @param dev CAM register base address + * @param width CAM output wire width + */ +static inline void cam_ll_set_data_wire_width(lcd_cam_dev_t *dev, uint32_t width) +{ + // data line width is same as data stride that set in `cam_ll_set_input_data_width` +} + +/** + * @brief Start the CAM transaction + * + * @param dev CAM register base address + */ +__attribute__((always_inline)) +static inline void cam_ll_start(lcd_cam_dev_t *dev) +{ + dev->cam_ctrl.cam_update_reg = 1; + dev->cam_ctrl1.cam_start = 1; +} + +/** + * @brief Stop the CAM transaction + * + * @param dev CAM register base address + */ +__attribute__((always_inline)) +static inline void cam_ll_stop(lcd_cam_dev_t *dev) +{ + dev->cam_ctrl1.cam_start = 0; + dev->cam_ctrl.cam_update_reg = 1; // self clear +} + +/** + * @brief Whether to reverse the data bit order + * + * @note It acts before the YUV-RGB converter + * + * @param dev CAM register base address + * @param en True to reverse, False to not reverse + */ +__attribute__((always_inline)) +static inline void cam_ll_reverse_dma_data_bit_order(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_bit_order = en; +} + +/** + * @brief Whether to swap adjacent two bytes + * + * @note This acts before the YUV-RGB converter, mainly to change the data endian. + * {B1,B0},{B3,B2} => {B0,B1}{B2,B3} + * + * @param dev CAM register base address + * @param en True to swap the byte order, False to not swap + */ +__attribute__((always_inline)) +static inline void cam_ll_swap_dma_data_byte_order(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_byte_order = en; +} + +/** + * @brief Reset camera module + * + * @param dev CAM register base address + */ +__attribute__((always_inline)) +static inline void cam_ll_reset(lcd_cam_dev_t *dev) +{ + dev->cam_ctrl1.cam_reset = 1; // self clear +} + +/** + * @brief Reset Async RX FIFO + * + * @param dev CAM register base address + */ +__attribute__((always_inline)) +static inline void cam_ll_fifo_reset(lcd_cam_dev_t *dev) +{ + dev->cam_ctrl1.cam_afifo_reset = 1; // self clear +} + +/** + * @brief Enable/disable interrupt by mask + * + * @param dev CAM register base address + * @param mask Interrupt mask + * @param en True to enable interrupt, False to disable interrupt + */ +static inline void cam_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bool en) +{ + if (en) { + dev->lc_dma_int_ena.val |= mask & 0x0c; + } else { + dev->lc_dma_int_ena.val &= ~(mask & 0x0c); + } +} + +/** + * @brief Get interrupt status value + * + * @param dev CAM register base address + * @return Interrupt status value + */ +__attribute__((always_inline)) +static inline uint32_t cam_ll_get_interrupt_status(lcd_cam_dev_t *dev) +{ + return dev->lc_dma_int_st.val & 0x0c; +} + +/** + * @brief Clear interrupt status by mask + * + * @param dev CAM register base address + * @param mask Interrupt status mask + */ +__attribute__((always_inline)) +static inline void cam_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask) +{ + dev->lc_dma_int_clr.val = mask & 0x0c; +} + +/** + * @brief Get address of interrupt status register address + * + * @param dev CAM register base address + * @return Interrupt status register address + */ +static inline volatile void *cam_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev) +{ + return &dev->lc_dma_int_st; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/cam_hal.h b/components/hal/include/hal/cam_hal.h new file mode 100644 index 0000000000..2b1c9229b6 --- /dev/null +++ b/components/hal/include/hal/cam_hal.h @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "hal/cam_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct lcd_cam_dev_t cam_dev_t; // CAM SOC layer handle + +/** + * @brief CAM hardware interface object data + */ +typedef struct cam_hal_context { + cam_dev_t *hw; /*!< Beginning address of the CAM peripheral registers. */ +} cam_hal_context_t; + +/** + * @brief CAM HAL driver configuration + */ +typedef struct cam_hal_config { + int port; /*!< CAM port */ + bool byte_swap_en; /*!< CAM enable byte swap */ +} cam_hal_config_t; + +/** + * @brief Initialize CAM hardware + * + * @param hal CAM object data pointer + * @param config CAM configuration + * + * @return None + */ +void cam_hal_init(cam_hal_context_t *hal, const cam_hal_config_t *config); + +/** + * @brief De-initialize CAM hardware + * + * @param hal CAM object data pointer + * + * @return None + */ +void cam_hal_deinit(cam_hal_context_t *hal); + +/** + * @brief Start CAM to receive frame data + * + * @param hal CAM object data pointer + * + * @return None + */ +void cam_hal_start_streaming(cam_hal_context_t *hal); + +/** + * @brief Stop CAM receiving frame data + * + * @param hal CAM object data pointer + * + * @return None + */ +void cam_hal_stop_streaming(cam_hal_context_t *hal); + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/cam_types.h b/components/hal/include/hal/cam_types.h new file mode 100644 index 0000000000..32e22f22a6 --- /dev/null +++ b/components/hal/include/hal/cam_types.h @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "soc/soc_caps.h" +#include "soc/clk_tree_defs.h" +#include "hal/color_types.h" +#include "hal/cam_ctlr_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_LCDCAM_CAM_DATA_WIDTH_MAX +#define CAP_DVP_PERIPH_NUM SOC_LCDCAM_CAM_PERIPH_NUM /*!< DVP port number */ +#define CAM_DVP_DATA_SIG_NUM SOC_LCDCAM_CAM_DATA_WIDTH_MAX /*!< DVP data bus width of CAM */ +#else +#define CAP_DVP_PERIPH_NUM 0 /*!< Default value */ +#define CAM_DVP_DATA_SIG_NUM 0 /*!< Default value */ +#endif + +#if SOC_LCDCAM_CAM_SUPPORTED +typedef soc_periph_cam_clk_src_t cam_clock_source_t; /*!< Clock source type of CAM */ +#else +typedef int cam_clock_source_t; /*!< Default type */ +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 0c3f4ed0bd..b3a53fb779 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -156,6 +156,10 @@ if(CONFIG_SOC_PAU_SUPPORTED) list(APPEND srcs "${target_folder}/system_retention_periph.c") endif() +if(CONFIG_SOC_LCDCAM_CAM_SUPPORTED) + list(APPEND srcs "${target_folder}/cam_periph.c") +endif() + idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${includes} LDFRAGMENTS "linker.lf") diff --git a/components/soc/esp32p4/cam_periph.c b/components/soc/esp32p4/cam_periph.c new file mode 100644 index 0000000000..acecf88272 --- /dev/null +++ b/components/soc/esp32p4/cam_periph.c @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/gpio_sig_map.h" +#include "soc/cam_periph.h" + +const cam_signal_conn_t cam_periph_signals = { + .buses = { + [0] = { + .module = PERIPH_LCD_CAM_MODULE, + .irq_id = ETS_LCD_CAM_INTR_SOURCE, + .data_sigs = { + CAM_DATA_IN_PAD_IN0_IDX, + CAM_DATA_IN_PAD_IN1_IDX, + CAM_DATA_IN_PAD_IN2_IDX, + CAM_DATA_IN_PAD_IN3_IDX, + CAM_DATA_IN_PAD_IN4_IDX, + CAM_DATA_IN_PAD_IN5_IDX, + CAM_DATA_IN_PAD_IN6_IDX, + CAM_DATA_IN_PAD_IN7_IDX, + CAM_DATA_IN_PAD_IN8_IDX, + CAM_DATA_IN_PAD_IN9_IDX, + CAM_DATA_IN_PAD_IN10_IDX, + CAM_DATA_IN_PAD_IN11_IDX, + CAM_DATA_IN_PAD_IN12_IDX, + CAM_DATA_IN_PAD_IN13_IDX, + CAM_DATA_IN_PAD_IN14_IDX, + CAM_DATA_IN_PAD_IN15_IDX, + }, + .hsync_sig = CAM_H_SYNC_PAD_IN_IDX, + .vsync_sig = CAM_V_SYNC_PAD_IN_IDX, + .pclk_sig = CAM_PCLK_PAD_IN_IDX, + .de_sig = CAM_H_ENABLE_PAD_IN_IDX, + .clk_sig = CAM_CLK_PAD_OUT_IDX + } + } +}; diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index ab97a343da..a3a9280b89 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -43,6 +43,10 @@ config SOC_PCNT_SUPPORTED bool default y +config SOC_LCDCAM_CAM_SUPPORTED + bool + default y + config SOC_MIPI_CSI_SUPPORTED bool default y @@ -1642,3 +1646,15 @@ config SOC_JPEG_DECODE_SUPPORTED config SOC_JPEG_ENCODE_SUPPORTED bool default y + +config SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV + bool + default y + +config SOC_LCDCAM_CAM_PERIPH_NUM + int + default 1 + +config SOC_LCDCAM_CAM_DATA_WIDTH_MAX + int + default 16 diff --git a/components/soc/esp32p4/include/soc/clk_tree_defs.h b/components/soc/esp32p4/include/soc/clk_tree_defs.h index 5679247b50..570239f07b 100644 --- a/components/soc/esp32p4/include/soc/clk_tree_defs.h +++ b/components/soc/esp32p4/include/soc/clk_tree_defs.h @@ -373,6 +373,23 @@ typedef enum { LCD_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */ } soc_periph_lcd_clk_src_t; +//////////////////////////////////////////////////LCD/////////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of CAM + */ +#define SOC_CAM_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL, SOC_MOD_CLK_APLL} + +/** + * @brief Type of CAM clock source + */ +typedef enum { + CAM_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ + CAM_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + CAM_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */ + CAM_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */ +} soc_periph_cam_clk_src_t; + /////////////////////////////////////////////////MIPI/////////////////////////////////////////////////////////////////// /** diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index e277bd8297..e9487cd2c5 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_LCDCAM_CAM_SUPPORTED 1 #define SOC_MIPI_CSI_SUPPORTED 1 #define SOC_MIPI_DSI_SUPPORTED 1 #define SOC_MCPWM_SUPPORTED 1 @@ -538,8 +539,8 @@ /*-------------------------- TOUCH SENSOR CAPS -------------------------------*/ #define SOC_TOUCH_SENSOR_VERSION (3) // Hardware version of touch sensor #define SOC_TOUCH_SENSOR_NUM (14) // Touch available channel number. Actually there are 15 Touch channels, but channel 14 is not pinned out, limit to 14 channels -#define SOC_TOUCH_PROXIMITY_CHANNEL_NUM (3) // Sopport touch proximity channel number. -#define SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED (1) // Sopport touch proximity channel measure done interrupt type. +#define SOC_TOUCH_PROXIMITY_CHANNEL_NUM (3) // Support touch proximity channel number. +#define SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED (1) // Support touch proximity channel measure done interrupt type. #define SOC_TOUCH_SAMPLER_NUM (3) // The sampler number in total, each sampler can be used to sample on one frequency /*-------------------------- TWAI CAPS ---------------------------------------*/ @@ -657,3 +658,8 @@ #define SOC_JPEG_CODEC_SUPPORTED (1) #define SOC_JPEG_DECODE_SUPPORTED (1) #define SOC_JPEG_ENCODE_SUPPORTED (1) + +/*--------------------------- CAM ---------------------------------*/ +#define SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV (1) +#define SOC_LCDCAM_CAM_PERIPH_NUM (1U) +#define SOC_LCDCAM_CAM_DATA_WIDTH_MAX (16U) diff --git a/components/soc/include/soc/cam_periph.h b/components/soc/include/soc/cam_periph.h new file mode 100644 index 0000000000..e71f973656 --- /dev/null +++ b/components/soc/include/soc/cam_periph.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "soc/soc_caps.h" +#include "soc/periph_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_LCDCAM_CAM_SUPPORTED +typedef struct { + struct { + const periph_module_t module; + const int irq_id; + const int data_sigs[SOC_LCDCAM_CAM_DATA_WIDTH_MAX]; + const int hsync_sig; + const int vsync_sig; + const int pclk_sig; + const int de_sig; + const int clk_sig; + } buses[SOC_LCDCAM_CAM_PERIPH_NUM]; +} cam_signal_conn_t; + +extern const cam_signal_conn_t cam_periph_signals; +#endif // SOC_LCDCAM_CAM_SUPPORTED + +#ifdef __cplusplus +} +#endif