mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/p4_lcdcam_dvp_cam_driver_v5.3' into 'release/v5.3'
feat(cam): add esp32-p4 lcd_cam dvp driver (v5.3) See merge request espressif/esp-idf!31454
This commit is contained in:
commit
f750f4c6f7
@ -22,8 +22,15 @@ if(NOT ${target} STREQUAL "linux")
|
|||||||
list(APPEND requires esp_mm)
|
list(APPEND requires esp_mm)
|
||||||
endif()
|
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}
|
idf_component_register(SRCS ${srcs}
|
||||||
INCLUDE_DIRS ${includes}
|
INCLUDE_DIRS ${includes}
|
||||||
|
PRIV_INCLUDE_DIRS ${priv_include_dirs}
|
||||||
REQUIRES ${requires}
|
REQUIRES ${requires}
|
||||||
PRIV_REQUIRES ${priv_requires}
|
PRIV_REQUIRES ${priv_requires}
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
menu "ESP-Driver:Camera Controller Configurations"
|
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
|
config CAM_CTLR_MIPI_CSI_ISR_IRAM_SAFE
|
||||||
bool "CSI ISR IRAM-Safe"
|
bool "CSI ISR IRAM-Safe"
|
||||||
|
depends on SOC_MIPI_CSI_SUPPORTED
|
||||||
default n
|
default n
|
||||||
select DW_GDMA_ISR_IRAM_SAFE
|
select DW_GDMA_ISR_IRAM_SAFE
|
||||||
select DW_GDMA_CTRL_FUNC_IN_IRAM
|
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
|
Ensure the CSI driver ISR is IRAM-Safe. When enabled, the ISR handler
|
||||||
will be available when the cache is disabled.
|
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"
|
bool "ISP_DVP ISR IRAM-Safe"
|
||||||
default n
|
default n
|
||||||
select DW_GDMA_ISR_IRAM_SAFE
|
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
|
Ensure the ISP_DVP driver ISR is IRAM-Safe. When enabled, the ISR handler
|
||||||
will be available when the cache is disabled.
|
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
|
endmenu # ESP Camera Controller Configurations
|
||||||
|
70
components/esp_driver_cam/dvp/include/esp_cam_ctlr_dvp.h
Normal file
70
components/esp_driver_cam/dvp/include/esp_cam_ctlr_dvp.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#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
|
@ -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
|
@ -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
|
@ -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
|
764
components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c
Normal file
764
components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c
Normal file
@ -0,0 +1,764 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#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;
|
||||||
|
}
|
202
components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c
Normal file
202
components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#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;
|
||||||
|
}
|
@ -4,6 +4,12 @@ components/esp_driver_cam/test_apps/csi:
|
|||||||
depends_components:
|
depends_components:
|
||||||
- esp_driver_cam
|
- 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:
|
components/esp_driver_cam/test_apps/isp_dvp:
|
||||||
disable:
|
disable:
|
||||||
- if: SOC_ISP_DVP_SUPPORTED != 1
|
- if: SOC_ISP_DVP_SUPPORTED != 1
|
||||||
|
8
components/esp_driver_cam/test_apps/dvp/CMakeLists.txt
Normal file
8
components/esp_driver_cam/test_apps/dvp/CMakeLists.txt
Normal file
@ -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)
|
2
components/esp_driver_cam/test_apps/dvp/README.md
Normal file
2
components/esp_driver_cam/test_apps/dvp/README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
| Supported Targets | ESP32-P4 |
|
||||||
|
| ----------------- | -------- |
|
16
components/esp_driver_cam/test_apps/dvp/main/CMakeLists.txt
Normal file
16
components/esp_driver_cam/test_apps/dvp/main/CMakeLists.txt
Normal file
@ -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)
|
27
components/esp_driver_cam/test_apps/dvp/main/test_app_main.c
Normal file
27
components/esp_driver_cam/test_apps/dvp/main/test_app_main.c
Normal file
@ -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();
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#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));
|
||||||
|
}
|
10
components/esp_driver_cam/test_apps/dvp/pytest_dvp.py
Normal file
10
components/esp_driver_cam/test_apps/dvp/pytest_dvp.py
Normal file
@ -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()
|
@ -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
|
@ -0,0 +1,3 @@
|
|||||||
|
CONFIG_IDF_TARGET="esp32p4"
|
||||||
|
CONFIG_SPIRAM_SPEED_200M=y
|
||||||
|
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
@ -266,6 +266,10 @@ if(NOT BOOTLOADER_BUILD)
|
|||||||
list(APPEND srcs "ds_hal.c")
|
list(APPEND srcs "ds_hal.c")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CONFIG_SOC_LCDCAM_CAM_SUPPORTED)
|
||||||
|
list(APPEND srcs "cam_hal.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED)
|
if(CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED)
|
||||||
list(APPEND srcs "usb_serial_jtag_hal.c")
|
list(APPEND srcs "usb_serial_jtag_hal.c")
|
||||||
endif()
|
endif()
|
||||||
|
121
components/hal/cam_hal.c
Normal file
121
components/hal/cam_hal.c
Normal file
@ -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);
|
||||||
|
}
|
634
components/hal/esp32p4/include/hal/cam_ll.h
Normal file
634
components/hal/esp32p4/include/hal/cam_ll.h
Normal file
@ -0,0 +1,634 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#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
|
73
components/hal/include/hal/cam_hal.h
Normal file
73
components/hal/include/hal/cam_hal.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#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
|
34
components/hal/include/hal/cam_types.h
Normal file
34
components/hal/include/hal/cam_types.h
Normal file
@ -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
|
@ -156,6 +156,10 @@ if(CONFIG_SOC_PAU_SUPPORTED)
|
|||||||
list(APPEND srcs "${target_folder}/system_retention_periph.c")
|
list(APPEND srcs "${target_folder}/system_retention_periph.c")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CONFIG_SOC_LCDCAM_CAM_SUPPORTED)
|
||||||
|
list(APPEND srcs "${target_folder}/cam_periph.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
idf_component_register(SRCS ${srcs}
|
idf_component_register(SRCS ${srcs}
|
||||||
INCLUDE_DIRS ${includes}
|
INCLUDE_DIRS ${includes}
|
||||||
LDFRAGMENTS "linker.lf")
|
LDFRAGMENTS "linker.lf")
|
||||||
|
40
components/soc/esp32p4/cam_periph.c
Normal file
40
components/soc/esp32p4/cam_periph.c
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -47,6 +47,10 @@ config SOC_PCNT_SUPPORTED
|
|||||||
bool
|
bool
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
config SOC_LCDCAM_CAM_SUPPORTED
|
||||||
|
bool
|
||||||
|
default y
|
||||||
|
|
||||||
config SOC_MIPI_CSI_SUPPORTED
|
config SOC_MIPI_CSI_SUPPORTED
|
||||||
bool
|
bool
|
||||||
default y
|
default y
|
||||||
@ -1662,3 +1666,15 @@ config SOC_JPEG_DECODE_SUPPORTED
|
|||||||
config SOC_JPEG_ENCODE_SUPPORTED
|
config SOC_JPEG_ENCODE_SUPPORTED
|
||||||
bool
|
bool
|
||||||
default y
|
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
|
||||||
|
@ -373,6 +373,23 @@ typedef enum {
|
|||||||
LCD_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */
|
LCD_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */
|
||||||
} soc_periph_lcd_clk_src_t;
|
} 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///////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////MIPI///////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#define SOC_GPTIMER_SUPPORTED 1
|
#define SOC_GPTIMER_SUPPORTED 1
|
||||||
#define SOC_PCNT_SUPPORTED 1
|
#define SOC_PCNT_SUPPORTED 1
|
||||||
// #define SOC_LCDCAM_SUPPORTED 1 // TODO: IDF-7465
|
// #define SOC_LCDCAM_SUPPORTED 1 // TODO: IDF-7465
|
||||||
|
#define SOC_LCDCAM_CAM_SUPPORTED 1
|
||||||
#define SOC_MIPI_CSI_SUPPORTED 1
|
#define SOC_MIPI_CSI_SUPPORTED 1
|
||||||
#define SOC_MIPI_DSI_SUPPORTED 1
|
#define SOC_MIPI_DSI_SUPPORTED 1
|
||||||
#define SOC_MCPWM_SUPPORTED 1
|
#define SOC_MCPWM_SUPPORTED 1
|
||||||
@ -542,8 +543,8 @@
|
|||||||
/*-------------------------- TOUCH SENSOR CAPS -------------------------------*/
|
/*-------------------------- TOUCH SENSOR CAPS -------------------------------*/
|
||||||
#define SOC_TOUCH_SENSOR_VERSION (3) // Hardware version of touch sensor
|
#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_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_CHANNEL_NUM (3) // Support touch proximity channel number.
|
||||||
#define SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED (1) // Sopport touch proximity channel measure done interrupt type.
|
#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
|
#define SOC_TOUCH_SAMPLER_NUM (3) // The sampler number in total, each sampler can be used to sample on one frequency
|
||||||
|
|
||||||
/*-------------------------- TWAI CAPS ---------------------------------------*/
|
/*-------------------------- TWAI CAPS ---------------------------------------*/
|
||||||
@ -661,3 +662,8 @@
|
|||||||
#define SOC_JPEG_CODEC_SUPPORTED (1)
|
#define SOC_JPEG_CODEC_SUPPORTED (1)
|
||||||
#define SOC_JPEG_DECODE_SUPPORTED (1)
|
#define SOC_JPEG_DECODE_SUPPORTED (1)
|
||||||
#define SOC_JPEG_ENCODE_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)
|
||||||
|
35
components/soc/include/soc/cam_periph.h
Normal file
35
components/soc/include/soc/cam_periph.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user