diff --git a/components/esp_driver_isp/CMakeLists.txt b/components/esp_driver_isp/CMakeLists.txt new file mode 100644 index 0000000000..b75c9bfc4a --- /dev/null +++ b/components/esp_driver_isp/CMakeLists.txt @@ -0,0 +1,12 @@ +set(srcs) + +set(public_include "include") + +if(CONFIG_SOC_ISP_SUPPORTED) + list(APPEND srcs "src/isp.c" + "src/isp_af.c") +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${public_include} + ) diff --git a/components/esp_driver_isp/Kconfig b/components/esp_driver_isp/Kconfig new file mode 100644 index 0000000000..108664411f --- /dev/null +++ b/components/esp_driver_isp/Kconfig @@ -0,0 +1,11 @@ +menu "ESP-Driver:ISP Configurations" + depends on SOC_ISP_SUPPORTED + + config ISP_ISR_IRAM_SAFE + bool "ISP driver ISR IRAM-Safe" + default n + help + Ensure the ISP driver ISR is IRAM-Safe. When enabled, the ISR handler + will be available when the cache is disabled. + +endmenu # ISP Configuration diff --git a/components/esp_driver_isp/include/driver/isp.h b/components/esp_driver_isp/include/driver/isp.h new file mode 100644 index 0000000000..436ba24dbe --- /dev/null +++ b/components/esp_driver_isp/include/driver/isp.h @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_err.h" +#include "driver/isp_types.h" +#include "driver/isp_af.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ISP configurations + */ +typedef struct { + isp_clk_src_t clk_src; ///< Clock source + uint32_t clk_hz; ///< Clock frequency in Hz, suggest twice higher than cam sensor speed + isp_input_data_source_t input_data_source; ///< Input data source + isp_color_t input_data_color_type; ///< Input color type + isp_color_t output_data_color_type; ///< Output color type + bool has_line_start_packet; ///< Enable line start packet + bool has_line_end_packet; ///< Enable line end packet + 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 +} esp_isp_processor_cfg_t; + +/** + * @brief New an ISP processor + * + * @param[in] proc_config Pointer to ISP config. Refer to ``esp_isp_processor_cfg_t``. + * @param[out] ret_proc Processor handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * - ESP_ERR_NOT_SUPPORTED Not supported mode + * - ESP_ERR_NO_MEM If out of memory + */ +esp_err_t esp_isp_new_processor(const esp_isp_processor_cfg_t *proc_config, isp_proc_handle_t *ret_proc); + +/** + * @brief Delete an ISP processor + * + * @param[in] proc Processor handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_del_processor(isp_proc_handle_t proc); + +/** + * @brief Enable an ISP processor + * + * @param[in] proc Processor handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_enable(isp_proc_handle_t proc); + +/** + * @brief Disable an ISP processor + * + * @param[in] proc Processor handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_disable(isp_proc_handle_t proc); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_isp/include/driver/isp_af.h b/components/esp_driver_isp/include/driver/isp_af.h new file mode 100644 index 0000000000..5253349529 --- /dev/null +++ b/components/esp_driver_isp/include/driver/isp_af.h @@ -0,0 +1,220 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_err.h" +#include "driver/isp_types.h" +#include "driver/isp.h" +#include "soc/soc_caps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief AF controller config + */ +typedef struct { +#if SOC_ISP_AF_WINDOW_NUMS + isp_af_window_t window[SOC_ISP_AF_WINDOW_NUMS]; ///< AF window settings +#endif + int edge_thresh; ///< Edge threshold, definition higher than this value will be counted as a valid pixel for calculating AF result +} esp_isp_af_config_t; + +/** + * @brief New an ISP AF controller + * + * @param[in] isp_proc ISP Processor handle + * @param[in] af_config Pointer to AF config. Refer to ``esp_isp_af_config_t``. + * @param[out] ret_hdl AF controller handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid + * - ESP_ERR_INVALID_STATE Invalid state + * - ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * - ESP_ERR_NO_MEM If out of memory + */ +esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af_config_t *af_config, isp_af_ctrlr_t *ret_hdl); + +/** + * @brief Delete an ISP AF controller + * + * @param[in] af_ctrlr AF controller handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_del_af_controller(isp_af_ctrlr_t af_ctrlr); + +/** + * @brief Enable an ISP AF controller + * + * @param[in] af_ctrlr AF controller handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_af_controller_enable(isp_af_ctrlr_t af_ctrlr); + +/** + * @brief Disable an ISP AF controller + * + * @param[in] af_ctrlr AF controller handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_af_controller_disable(isp_af_ctrlr_t af_ctrlr); + +/** + * @brief Get AF result + * + * @param[in] af_ctrlr AF controller handle + * @param[out] out_res AF result + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_af_controller_get_oneshot_result(isp_af_ctrlr_t af_ctrlr, isp_af_result_t *out_res); + +/*--------------------------------------------- + AF Env Monitor +----------------------------------------------*/ +/** + * @brief AF environment detector config + */ +typedef struct { + int interval; ///< Interval between environment detection, in frames +} esp_isp_af_env_config_t; + +/** + * @brief Event data structure + */ +typedef struct { + //empty for future proof +} esp_isp_af_env_detector_evt_data_t; + +/** + * @brief Prototype of ISP AF Env detector event callback + * + * @param[in] handle ISP Env detector handle + * @param[in] edata ISP AF Env detector event data + * @param[in] user_data User registered context, registered when in `esp_isp_af_env_detector_register_event_callbacks()` + * + * @return Whether a high priority task is woken up by this function + */ +typedef bool (*esp_isp_af_env_detector_callback_t)(isp_af_env_detr_t detector, const esp_isp_af_env_detector_evt_data_t *edata, void *user_data); + +/** + * @brief Group of ISP AF Env detector callbacks + * + * @note These callbacks are all running in an ISR environment. + * @note When CONFIG_ISP_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. + * Involved variables should be in internal RAM as well. + */ +typedef struct { + esp_isp_af_env_detector_callback_t on_env_change; ///< Event callback, invoked when environment change happens. +} esp_isp_af_env_detector_evt_cbs_t; + +/** + * @brief New an ISP AF environment detector + * + * @param[in] af_ctrlr AF controller handle + * @param[in] config Pointer to AF env detector config. Refer to ``esp_isp_af_env_config_t``. + * @param[out] ret_hdl AF env detector handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid + * - ESP_ERR_INVALID_STATE Invalid state + * - ESP_ERR_NO_MEM If out of memory + */ +esp_err_t esp_isp_new_af_env_detector(isp_af_ctrlr_t af_ctrlr, const esp_isp_af_env_config_t *config, isp_af_env_detr_t *ret_hdl); + +/** + * @brief Delete an ISP AF environment detector + * + * @param[in] af_env_detector AF env detector handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_del_af_env_detector(isp_af_env_detr_t af_env_detector); + +/** + * @brief Enable an ISP AF environment detector + * + * @param[in] af_env_detector AF env detector handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_af_env_detector_enable(isp_af_env_detr_t af_env_detector); + +/** + * @brief Disable an ISP AF environment detector + * + * @param[in] af_env_detector AF env detector handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_af_env_detector_disable(isp_af_env_detr_t af_env_detector); + +/** + * @brief Set ISP AF environment detector detecting threshold + * + * @param[in] af_env_detector AF env detector handle + * @param[in] definition_thresh Threshold for definition + * @param[in] luminance_thresh Threshold for luminance + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_af_env_detector_set_threshold(isp_af_env_detr_t af_env_detector, int definition_thresh, int luminance_thresh); + +/** + * @brief Register AF environment detector event callbacks + * + * @note User can deregister a previously registered callback by calling this function and setting the to-be-deregistered callback member in + * the `cbs` structure to NULL. + * @note When CONFIG_ISP_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. + * Involved variables (including `user_data`) should be in internal RAM as well. + * + * @param[in] af_env_detector AF env detector handle + * @param[in] cbs Group of callback functions + * @param[in] user_data User data, which will be delivered to the callback functions directly + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid arguments + * - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment + */ +esp_err_t esp_isp_af_env_detector_register_event_callbacks(isp_af_env_detr_t af_env_detector, const esp_isp_af_env_detector_evt_cbs_t *cbs, void *user_data); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_isp/include/driver/isp_types.h b/components/esp_driver_isp/include/driver/isp_types.h new file mode 100644 index 0000000000..c0dade7484 --- /dev/null +++ b/components/esp_driver_isp/include/driver/isp_types.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "hal/isp_types.h" +#include "hal/color_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Type of ISP processor handle + */ +typedef struct isp_processor_t *isp_proc_handle_t; + +/** + * @brief Type of ISP AF controller handle + */ +typedef struct isp_af_controller_t *isp_af_ctrlr_t; + +/** + * @brief Type of ISP AF Env detector handle + */ +typedef struct isp_af_env_detector_t *isp_af_env_detr_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_isp/src/isp.c b/components/esp_driver_isp/src/isp.c new file mode 100644 index 0000000000..c0d8a2699d --- /dev/null +++ b/components/esp_driver_isp/src/isp.c @@ -0,0 +1,301 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "esp_clk_tree.h" +#include "driver/isp.h" +#include "esp_private/periph_ctrl.h" +#include "esp_private/mipi_csi_share_hw_ctrl.h" +#include "hal/hal_utils.h" +#include "hal/isp_types.h" +#include "hal/isp_hal.h" +#include "hal/isp_ll.h" +#include "soc/mipi_csi_bridge_struct.h" +#include "soc/isp_periph.h" +#include "isp_internal.h" + +#if CONFIG_ISP_ISR_IRAM_SAFE +#define ISP_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM) +#else +#define ISP_INTR_ALLOC_FLAGS 0 +#endif + +#if CONFIG_ISP_ISR_IRAM_SAFE +#define ISP_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) +#else +#define ISP_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT +#endif + +typedef struct isp_platform_t { + _lock_t mutex; + isp_processor_t *processors[SOC_ISP_NUMS]; +} isp_platform_t; + +static const char *TAG = "ISP"; +static isp_platform_t s_platform; + +static esp_err_t s_isp_claim_processor(isp_processor_t *proc) +{ + assert(proc); + + _lock_acquire(&s_platform.mutex); + bool found = false; + for (int i = 0; i < SOC_ISP_NUMS; i ++) { + found = !s_platform.processors[i]; + if (found) { + s_platform.processors[i] = proc; + proc->proc_id = i; + PERIPH_RCC_ATOMIC() { + isp_ll_enable_module_clock(proc->hal.hw, true); + isp_ll_reset_module_clock(proc->hal.hw); + } + break; + } + } + _lock_release(&s_platform.mutex); + + if (!found) { + return ESP_ERR_NOT_FOUND; + } + return ESP_OK; +} + +static esp_err_t s_isp_declaim_processor(isp_processor_t *proc) +{ + assert(proc); + + _lock_acquire(&s_platform.mutex); + s_platform.processors[proc->proc_id] = NULL; + PERIPH_RCC_ATOMIC() { + isp_ll_enable_module_clock(proc->hal.hw, false); + } + _lock_release(&s_platform.mutex); + + return ESP_OK; +} + +esp_err_t esp_isp_new_processor(const esp_isp_processor_cfg_t *proc_config, isp_proc_handle_t *ret_proc) +{ + esp_err_t ret = ESP_FAIL; + ESP_RETURN_ON_FALSE(proc_config && ret_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(proc_config->input_data_source == ISP_INPUT_DATA_SOURCE_CSI, ESP_ERR_NOT_SUPPORTED, TAG, "only support CSI as input source at this moment"); + + isp_processor_t *proc = heap_caps_calloc(1, sizeof(isp_processor_t), ISP_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(proc, ESP_ERR_NO_MEM, TAG, "no mem"); + + //claim a processor, then do assignment + ESP_GOTO_ON_ERROR(s_isp_claim_processor(proc), err, TAG, "no available isp processor"); +#if SOC_ISP_SHARE_CSI_BRG + ESP_GOTO_ON_ERROR(mipi_csi_brg_claim(MIPI_CSI_BRG_USER_SHARE, &proc->csi_brg_id), err, TAG, "csi bridge is in use already"); +#endif + + isp_clk_src_t clk_src = !proc_config->clk_src ? ISP_CLK_SRC_DEFAULT : proc_config->clk_src; + uint32_t clk_src_freq_hz = 0; + ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz), err, TAG, "clock source setting fail"); + ESP_GOTO_ON_FALSE((proc_config->clk_hz > 0 && proc_config->clk_hz <= clk_src_freq_hz), ESP_ERR_INVALID_ARG, err, TAG, "clk hz not supported"); + + uint32_t out_clk_freq_hz = 0; + hal_utils_clk_div_t clk_div = {}; + hal_utils_clk_info_t clk_info = { + .src_freq_hz = clk_src_freq_hz, + .exp_freq_hz = proc_config->clk_hz, + .max_integ = ISP_LL_TX_MAX_CLK_INT_DIV, + .min_integ = 1, + .round_opt = HAL_DIV_ROUND, + }; + out_clk_freq_hz = hal_utils_calc_clk_div_integer(&clk_info, &clk_div.integer); + if (out_clk_freq_hz != proc_config->clk_hz) { + ESP_LOGW(TAG, "precision loss, real output frequency: %"PRIu32"Hz", out_clk_freq_hz); + } + + isp_hal_init(&proc->hal, proc->proc_id); + //necessary ISP submodules that needs basic initialisation + isp_ll_bf_clk_enable(proc->hal.hw, true); + isp_ll_bf_enable(proc->hal.hw, true); + isp_ll_ccm_clk_enable(proc->hal.hw, true); + isp_ll_ccm_enable(proc->hal.hw, true); + isp_ll_color_clk_enable(proc->hal.hw, true); + isp_ll_color_enable(proc->hal.hw, true); + PERIPH_RCC_ATOMIC() { + isp_ll_select_clk_source(proc->hal.hw, clk_src); + isp_ll_set_clock_div(proc->hal.hw, &clk_div); + } + + proc->isp_fsm = ISP_FSM_INIT; + proc->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + + //Input color format + bool valid_format = false; + color_space_pixel_format_t in_color_format = { + .color_type_id = proc_config->input_data_color_type, + }; + valid_format = isp_ll_set_input_data_color_format(proc->hal.hw, in_color_format); + ESP_GOTO_ON_FALSE(valid_format, ESP_ERR_INVALID_ARG, err, TAG, "invalid input color space config"); + + //Output color format + valid_format = false; + color_space_pixel_format_t out_color_format = { + .color_type_id = proc_config->output_data_color_type, + }; + valid_format = isp_ll_set_output_data_color_format(proc->hal.hw, out_color_format); + ESP_GOTO_ON_FALSE(valid_format, ESP_ERR_INVALID_ARG, err, TAG, "invalid output color space config"); + + isp_ll_clk_enable(proc->hal.hw, true); + isp_ll_set_input_data_source(proc->hal.hw, proc_config->input_data_source); + isp_ll_enable_line_start_packet_exist(proc->hal.hw, proc_config->has_line_start_packet); + isp_ll_enable_line_end_packet_exist(proc->hal.hw, proc_config->has_line_end_packet); + isp_ll_set_intput_data_h_pixel_num(proc->hal.hw, proc_config->h_res); + isp_ll_set_intput_data_v_row_num(proc->hal.hw, proc_config->v_res); + + *ret_proc = proc; + + return ESP_OK; + +err: + free(proc); + + return ret; +} + +esp_err_t esp_isp_del_processor(isp_proc_handle_t proc) +{ + ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(proc->isp_fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "processor isn't in init state"); + + //declaim first, then do free + ESP_RETURN_ON_ERROR(s_isp_declaim_processor(proc), TAG, "declaim processor fail"); +#if SOC_ISP_SHARE_CSI_BRG + ESP_RETURN_ON_ERROR(mipi_csi_brg_declaim(proc->csi_brg_id), TAG, "declaim csi bridge fail"); +#endif + free(proc); + + return ESP_OK; +} + +esp_err_t esp_isp_enable(isp_proc_handle_t proc) +{ + ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(proc->isp_fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "processor isn't in init state"); + + isp_ll_enable(proc->hal.hw, true); + proc->isp_fsm = ISP_FSM_ENABLE; + + return ESP_OK; +} + +esp_err_t esp_isp_disable(isp_proc_handle_t proc) +{ + ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(proc->isp_fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "processor isn't in enable state"); + + isp_ll_enable(proc->hal.hw, false); + proc->isp_fsm = ISP_FSM_INIT; + + return ESP_OK; +} + +/*--------------------------------------------------------------- + INTR +---------------------------------------------------------------*/ +static void IRAM_ATTR s_isp_isr_dispatcher(void *arg) +{ + isp_processor_t *proc = (isp_processor_t *)arg; + bool need_yield = false; + + //Check and clear hw events + uint32_t af_events = isp_hal_check_clear_intr_event(&proc->hal, ISP_LL_EVENT_AF_MASK); + + bool do_dispatch = false; + //Deal with hw events + if (af_events) { + portENTER_CRITICAL_ISR(&proc->spinlock); + do_dispatch = proc->af_isr_added; + portEXIT_CRITICAL_ISR(&proc->spinlock); + + if (do_dispatch) { + need_yield |= esp_isp_af_isr(proc, af_events); + } + do_dispatch = false; + } + + if (need_yield) { + portYIELD_FROM_ISR(); + } +} + +esp_err_t esp_isp_register_isr(isp_proc_handle_t proc, isp_submodule_t submodule) +{ + esp_err_t ret = ESP_FAIL; + ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + + bool do_alloc = false; + portENTER_CRITICAL(&proc->spinlock); + proc->isr_ref_counts++; + if (proc->isr_ref_counts == 1) { + assert(!proc->intr_hdl); + do_alloc = true; + } + + switch (submodule) { + case ISP_SUBMODULE_AF: + proc->af_isr_added = true; + break; + default: + assert(false); + } + portEXIT_CRITICAL(&proc->spinlock); + + if (do_alloc) { + ret = esp_intr_alloc(isp_hw_info.instances[proc->proc_id].irq, ISP_INTR_ALLOC_FLAGS, s_isp_isr_dispatcher, (void *)proc, &proc->intr_hdl); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "no intr source"); + return ret; + } + esp_intr_enable(proc->intr_hdl); + } + + return ESP_OK; +} + +esp_err_t esp_isp_deregister_isr(isp_proc_handle_t proc, isp_submodule_t submodule) +{ + esp_err_t ret = ESP_FAIL; + ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + + bool do_free = false; + portENTER_CRITICAL(&proc->spinlock); + proc->isr_ref_counts--; + assert(proc->isr_ref_counts >= 0); + if (proc->isr_ref_counts == 0) { + assert(proc->intr_hdl); + do_free = true; + } + + switch (submodule) { + case ISP_SUBMODULE_AF: + proc->af_isr_added = false; + break; + default: + assert(false); + } + portEXIT_CRITICAL(&proc->spinlock); + + if (do_free) { + esp_intr_disable(proc->intr_hdl); + ret = esp_intr_free(proc->intr_hdl); + if (ret != ESP_OK) { + return ret; + } + } + + return ESP_OK; +} diff --git a/components/esp_driver_isp/src/isp_af.c b/components/esp_driver_isp/src/isp_af.c new file mode 100644 index 0000000000..d689bcce97 --- /dev/null +++ b/components/esp_driver_isp/src/isp_af.c @@ -0,0 +1,330 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "driver/isp.h" +#include "hal/isp_hal.h" +#include "hal/isp_ll.h" +#include "isp_internal.h" + +#if CONFIG_ISP_ISR_IRAM_SAFE +#define ISP_AF_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) +#else +#define ISP_AF_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT +#endif + +static const char *TAG = "ISP_AF"; + +/*--------------------------------------------- + AF +----------------------------------------------*/ +static esp_err_t s_isp_claim_af_controller(isp_proc_handle_t isp_proc, isp_af_controller_t *af_ctlr) +{ + assert(isp_proc && af_ctlr); + + bool found = false; + portENTER_CRITICAL(&isp_proc->spinlock); + for (int i = 0; i < SOC_ISP_AF_CTLR_NUMS; i++) { + found = !isp_proc->af_ctlr[i]; + if (found) { + isp_proc->af_ctlr[i] = af_ctlr; + af_ctlr->id = i; + + break; + } + } + portEXIT_CRITICAL(&isp_proc->spinlock); + + if (!found) { + return ESP_ERR_NOT_FOUND; + } + return ESP_OK; +} + +static esp_err_t s_isp_declaim_af_controller(isp_af_controller_t *af_ctlr) +{ + assert(af_ctlr && af_ctlr->isp_proc); + + portENTER_CRITICAL(&af_ctlr->isp_proc->spinlock); + af_ctlr->isp_proc->af_ctlr[af_ctlr->id] = NULL; + portEXIT_CRITICAL(&af_ctlr->isp_proc->spinlock); + + return ESP_OK; +} + +esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af_config_t *af_config, isp_af_ctrlr_t *ret_hdl) +{ + esp_err_t ret = ESP_FAIL; + ESP_RETURN_ON_FALSE(isp_proc && af_config && ret_hdl, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + + bool rgb2yuv_en = isp_ll_is_rgb2yuv_enabled(isp_proc->hal.hw); + bool demosaic_en = isp_ll_is_demosaic_enabled(isp_proc->hal.hw); + ESP_RETURN_ON_FALSE(demosaic_en && rgb2yuv_en, ESP_ERR_INVALID_STATE, TAG, "RGB2YUV not enabled, please update the output_data_color_type"); + + for (int i = 0; i < SOC_ISP_AF_WINDOW_NUMS; i++) { + ESP_LOGV(TAG, "af_config->window[%d].top_left_x: %"PRId32, i, af_config->window[i].top_left_x); + ESP_LOGV(TAG, "af_config->window[%d].bottom_right_x: %"PRId32, i, af_config->window[i].bottom_right_x); + ESP_LOGV(TAG, "af_config->window[%d].bottom_right_y: %"PRId32, i, af_config->window[i].bottom_right_y); + ESP_LOGV(TAG, "af_config->window[%d].top_left_y: %"PRId32, i, af_config->window[i].top_left_y); + + ESP_RETURN_ON_FALSE(((af_config->window[i].top_left_x < ISP_LL_AF_WINDOW_MAX_RANGE) && + (af_config->window[i].bottom_right_x >= af_config->window[i].top_left_x) && + (af_config->window[i].bottom_right_x < ISP_LL_AF_WINDOW_MAX_RANGE) && + (af_config->window[i].top_left_y < ISP_LL_AF_WINDOW_MAX_RANGE) && + (af_config->window[i].bottom_right_y >= af_config->window[i].top_left_y) && + (af_config->window[i].bottom_right_y < ISP_LL_AF_WINDOW_MAX_RANGE)), ESP_ERR_INVALID_ARG, TAG, "invalid window"); + } + ESP_RETURN_ON_FALSE(af_config->edge_thresh > 0, ESP_ERR_INVALID_ARG, TAG, "edge threshold should be larger than 0"); + + isp_af_controller_t *af_ctlr = heap_caps_calloc(1, sizeof(isp_af_controller_t), ISP_AF_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(af_ctlr, ESP_ERR_NO_MEM, TAG, "no mem"); + + //claim an AF controller + ESP_GOTO_ON_ERROR(s_isp_claim_af_controller(isp_proc, af_ctlr), err, TAG, "no available controller"); + + af_ctlr->fsm = ISP_FSM_INIT; + af_ctlr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + af_ctlr->isp_proc = isp_proc; + + isp_ll_af_enable_auto_update(isp_proc->hal.hw, false); + isp_ll_af_enable(isp_proc->hal.hw, false); + + for (int i = 0; i < SOC_ISP_AF_WINDOW_NUMS; i++) { + isp_hal_af_window_config(&isp_proc->hal, i, &af_config->window[i]); + } + + isp_ll_af_set_edge_thresh_mode(isp_proc->hal.hw, ISP_LL_AF_EDGE_MONITOR_MODE_MANUAL); + isp_ll_af_set_edge_thresh(isp_proc->hal.hw, af_config->edge_thresh); + isp_ll_clear_intr(isp_proc->hal.hw, ISP_LL_EVENT_AF_MASK); + + *ret_hdl = af_ctlr; + + return ESP_OK; + +err: + free(af_ctlr); + + return ret; +} + +esp_err_t esp_isp_del_af_controller(isp_af_ctrlr_t af_ctlr) +{ + ESP_RETURN_ON_FALSE(af_ctlr && af_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_ERROR(s_isp_declaim_af_controller(af_ctlr), TAG, "controller isn't in use"); + ESP_RETURN_ON_FALSE(af_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state"); + + free(af_ctlr); + + return ESP_OK; +} + +esp_err_t esp_isp_af_controller_enable(isp_af_ctrlr_t af_ctlr) +{ + ESP_RETURN_ON_FALSE(af_ctlr && af_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(af_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state"); + + isp_ll_af_clk_enable(af_ctlr->isp_proc->hal.hw, true); + isp_ll_af_enable(af_ctlr->isp_proc->hal.hw, true); + af_ctlr->fsm = ISP_FSM_ENABLE; + + return ESP_OK; +} + +esp_err_t esp_isp_af_controller_disable(isp_af_ctrlr_t af_ctlr) +{ + ESP_RETURN_ON_FALSE(af_ctlr && af_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(af_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state"); + + isp_ll_af_clk_enable(af_ctlr->isp_proc->hal.hw, false); + isp_ll_af_enable(af_ctlr->isp_proc->hal.hw, false); + af_ctlr->fsm = ISP_FSM_INIT; + + return ESP_OK; +} + +esp_err_t esp_isp_af_controller_get_oneshot_result(isp_af_ctrlr_t af_ctlr, isp_af_result_t *out_res) +{ + ESP_RETURN_ON_FALSE_ISR(af_ctlr && out_res, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE_ISR(af_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state"); + + isp_hal_af_get_oneshot_result(&af_ctlr->isp_proc->hal, out_res); + + return ESP_OK; +} + +/*--------------------------------------------- + AF Env Monitor +----------------------------------------------*/ +static esp_err_t s_isp_claim_af_env_detector(isp_af_ctrlr_t af_ctlr, isp_af_env_detector_t *af_env_detector) +{ + assert(af_ctlr && af_env_detector); + + bool found = false; + portENTER_CRITICAL(&af_ctlr->spinlock); + for (int i = 0; i < SOC_ISP_AF_ENV_DETECTOR_NUMS; i++) { + found = !af_ctlr->af_env_detector[i]; + if (found) { + af_ctlr->af_env_detector[i] = af_env_detector; + af_env_detector->id = i; + + break; + } + } + portEXIT_CRITICAL(&af_ctlr->spinlock); + + if (!found) { + return ESP_ERR_NOT_FOUND; + } + return ESP_OK; +} + +static esp_err_t s_isp_declaim_af_env_detector(isp_af_env_detector_t *af_env_detector) +{ + assert(af_env_detector && af_env_detector->af_ctlr); + + portENTER_CRITICAL(&af_env_detector->af_ctlr->spinlock); + af_env_detector->af_ctlr->af_env_detector[af_env_detector->id] = NULL; + portEXIT_CRITICAL(&af_env_detector->af_ctlr->spinlock); + + return ESP_OK; +} + +esp_err_t esp_isp_new_af_env_detector(isp_af_ctrlr_t af_ctlr, const esp_isp_af_env_config_t *config, isp_af_env_detr_t *ret_hdl) +{ + esp_err_t ret = ESP_FAIL; + ESP_RETURN_ON_FALSE(af_ctlr && config && ret_hdl, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + + isp_af_env_detector_t *af_env_detector = heap_caps_calloc(1, sizeof(isp_af_env_detector_t), ISP_AF_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(af_env_detector, ESP_ERR_NO_MEM, TAG, "no mem"); + + //claim an AF Env detector + ESP_GOTO_ON_ERROR(s_isp_claim_af_env_detector(af_ctlr, af_env_detector), err, TAG, "no available env detector"); + + af_env_detector->fsm = ISP_FSM_INIT; + af_env_detector->config.interval = config->interval; + af_env_detector->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + af_env_detector->af_ctlr = af_ctlr; + + isp_ll_af_env_monitor_set_period(af_ctlr->isp_proc->hal.hw, 0); + isp_ll_clear_intr(af_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV); + + *ret_hdl = af_env_detector; + + return ESP_OK; + +err: + free(af_env_detector); + + return ret; +} + +esp_err_t esp_isp_del_af_env_detector(isp_af_env_detr_t af_env_detector) +{ + ESP_RETURN_ON_FALSE(af_env_detector && af_env_detector->af_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_ERROR(s_isp_declaim_af_env_detector(af_env_detector), TAG, "detector isn't in use"); + ESP_RETURN_ON_FALSE(af_env_detector->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "detector isn't in init state"); + + free(af_env_detector); + + return ESP_OK; +} + +esp_err_t esp_isp_af_env_detector_enable(isp_af_env_detr_t af_env_detector) +{ + ESP_RETURN_ON_FALSE(af_env_detector && af_env_detector->af_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(af_env_detector->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "detector isn't in init state"); + + //try use ratio mode + isp_ll_af_env_monitor_set_mode(af_env_detector->af_ctlr->isp_proc->hal.hw, ISP_LL_AF_ENV_MONITOR_MODE_ABS); + isp_ll_af_env_monitor_set_period(af_env_detector->af_ctlr->isp_proc->hal.hw, af_env_detector->config.interval); + isp_ll_enable_intr(af_env_detector->af_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV, true); + af_env_detector->fsm = ISP_FSM_ENABLE; + + return ESP_OK; +} + +esp_err_t esp_isp_af_env_detector_disable(isp_af_env_detr_t af_env_detector) +{ + ESP_RETURN_ON_FALSE(af_env_detector && af_env_detector->af_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(af_env_detector->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "detector isn't in enable state"); + + isp_ll_af_env_monitor_set_period(af_env_detector->af_ctlr->isp_proc->hal.hw, 0); + af_env_detector->fsm = ISP_FSM_INIT; + + return ESP_OK; +} + +esp_err_t esp_isp_af_env_detector_register_event_callbacks(isp_af_env_detr_t af_env_detector, const esp_isp_af_env_detector_evt_cbs_t *cbs, void *user_data) +{ + ESP_RETURN_ON_FALSE(af_env_detector && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(af_env_detector->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "detector isn't in the init state"); + +#if CONFIG_ISP_ISR_IRAM_SAFE + if (cbs->on_env_change) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_env_change), ESP_ERR_INVALID_ARG, TAG, "on_env_change callback not in IRAM"); + } +#endif + + ESP_RETURN_ON_ERROR(esp_isp_register_isr(af_env_detector->af_ctlr->isp_proc, ISP_SUBMODULE_AF), TAG, "fail to register ISR"); + + af_env_detector->cbs.on_env_change = cbs->on_env_change; + af_env_detector->user_data = user_data; + + return ESP_OK; +} + +esp_err_t esp_isp_af_env_detector_set_threshold(isp_af_env_detr_t af_env_detector, int definition_thresh, int luminance_thresh) +{ + ESP_RETURN_ON_FALSE_ISR(af_env_detector, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE_ISR(af_env_detector->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "detector isn't in enable state"); + + isp_ll_af_env_monitor_set_thresh(af_env_detector->af_ctlr->isp_proc->hal.hw, definition_thresh, luminance_thresh); + + return ESP_OK; +} + +/*--------------------------------------------------------------- + INTR +---------------------------------------------------------------*/ +static bool IRAM_ATTR s_af_env_isr(isp_af_env_detector_t *detector) +{ + bool need_yield = false; + + esp_isp_af_env_detector_evt_data_t edata = {}; + if (detector->cbs.on_env_change(detector, &edata, detector->user_data)) { + need_yield |= true; + } + + return need_yield; +} + +bool IRAM_ATTR esp_isp_af_isr(isp_proc_handle_t proc, uint32_t af_events) +{ + /** + * HW events are cleared in the ISP ISR dispatcher. + * We only deal with HW events + */ + + bool need_yield = false; + + if (af_events & ISP_LL_EVENT_AF_ENV) { + /** + * Now only one detector. + * Should decide a detector instance according to the hw event. + */ + isp_af_env_detector_t *detector = proc->af_ctlr[0]->af_env_detector[0]; + + need_yield |= s_af_env_isr(detector); + } + + return need_yield; +} diff --git a/components/esp_driver_isp/src/isp_internal.h b/components/esp_driver_isp/src/isp_internal.h new file mode 100644 index 0000000000..8615319d1f --- /dev/null +++ b/components/esp_driver_isp/src/isp_internal.h @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "esp_intr_alloc.h" +#include "freertos/FreeRTOS.h" +#include "hal/isp_hal.h" +#include "hal/isp_types.h" +#include "soc/isp_periph.h" +#include "soc/soc_caps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + ISP_FSM_INIT, + ISP_FSM_ENABLE, +} isp_fsm_t; + +/*--------------------------------------------------------------- + Driver Context +---------------------------------------------------------------*/ +typedef enum { + ISP_SUBMODULE_AF, +} isp_submodule_t; + +typedef struct isp_af_env_detector_t isp_af_env_detector_t; +typedef struct isp_af_controller_t isp_af_controller_t; +typedef struct isp_processor_t isp_processor_t; + +struct isp_af_env_detector_t { + int id; + isp_fsm_t fsm; + esp_isp_af_env_config_t config; + portMUX_TYPE spinlock; + esp_isp_af_env_detector_evt_cbs_t cbs; + void *user_data; + isp_af_controller_t *af_ctlr; +}; + +struct isp_af_controller_t { + int id; + isp_fsm_t fsm; + portMUX_TYPE spinlock; + isp_processor_t *isp_proc; + isp_af_env_detector_t *af_env_detector[SOC_ISP_AF_ENV_DETECTOR_NUMS]; +}; + +struct isp_processor_t { + int proc_id; + isp_hal_context_t hal; +#if SOC_ISP_SHARE_CSI_BRG + int csi_brg_id; + void *csi_brg_hw; +#endif + isp_fsm_t isp_fsm; + portMUX_TYPE spinlock; + intr_handle_t intr_hdl; + + /* sub module contexts */ + isp_af_controller_t *af_ctlr[SOC_ISP_AF_CTLR_NUMS]; + + /* should be accessed within isp_processor_t spinlock */ + int isr_ref_counts; + bool af_isr_added; +}; + +/*--------------------------------------------------------------- + INTR +---------------------------------------------------------------*/ +esp_err_t esp_isp_register_isr(isp_proc_handle_t proc, isp_submodule_t submodule); +esp_err_t esp_isp_deregister_isr(isp_proc_handle_t proc, isp_submodule_t submodule); +bool esp_isp_af_isr(isp_proc_handle_t proc, uint32_t af_events); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_isp/test_apps/.build-test-rules.yml b/components/esp_driver_isp/test_apps/.build-test-rules.yml new file mode 100644 index 0000000000..67a8c043e7 --- /dev/null +++ b/components/esp_driver_isp/test_apps/.build-test-rules.yml @@ -0,0 +1,5 @@ +components/esp_driver_isp/test_apps/isp: + disable: + - if: SOC_ISP_SUPPORTED != 1 + depends_components: + - esp_driver_isp diff --git a/components/esp_driver_isp/test_apps/isp/CMakeLists.txt b/components/esp_driver_isp/test_apps/isp/CMakeLists.txt new file mode 100644 index 0000000000..d940d8240b --- /dev/null +++ b/components/esp_driver_isp/test_apps/isp/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.16) + +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") + +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_isp) diff --git a/components/esp_driver_isp/test_apps/isp/README.md b/components/esp_driver_isp/test_apps/isp/README.md new file mode 100644 index 0000000000..909282018f --- /dev/null +++ b/components/esp_driver_isp/test_apps/isp/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | diff --git a/components/esp_driver_isp/test_apps/isp/main/CMakeLists.txt b/components/esp_driver_isp/test_apps/isp/main/CMakeLists.txt new file mode 100644 index 0000000000..49f618de17 --- /dev/null +++ b/components/esp_driver_isp/test_apps/isp/main/CMakeLists.txt @@ -0,0 +1,16 @@ +set(srcs "test_app_main.c" + "test_isp_driver.c") + +if(CONFIG_SOC_ISP_SHARE_CSI_BRG) + list(APPEND srcs "test_isp_csi.c") +endif() + +set(priv_requires + unity + esp_driver_isp +) + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS "." + PRIV_REQUIRES ${priv_requires} + WHOLE_ARCHIVE TRUE) diff --git a/components/esp_driver_isp/test_apps/isp/main/test_app_main.c b/components/esp_driver_isp/test_apps/isp/main/test_app_main.c new file mode 100644 index 0000000000..835e79f1ee --- /dev/null +++ b/components/esp_driver_isp/test_apps/isp/main/test_app_main.c @@ -0,0 +1,40 @@ +/* + * 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) +{ + /* + ____________________ ___________ + /_ __/ __/ __/_ __/ / _/ __/ _ \ + / / / _/_\ \ / / _/ /_\ \/ ___/ + /_/ /___/___/ /_/ /___/___/_/ + + */ + + printf(" ____________________ ___________\n"); + printf("/_ __/ __/ __/_ __/ / _/ __/ _ \\\n"); + printf(" / / / _/_\\ \\ / / _/ /_\\ \\/ ___/\n"); + printf("/_/ /___/___/ /_/ /___/___/_/\n"); + + unity_run_menu(); +} diff --git a/components/esp_driver_isp/test_apps/isp/main/test_isp_driver.c b/components/esp_driver_isp/test_apps/isp/main/test_isp_driver.c new file mode 100644 index 0000000000..d593e24e98 --- /dev/null +++ b/components/esp_driver_isp/test_apps/isp/main/test_isp_driver.c @@ -0,0 +1,94 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "sdkconfig.h" +#include "unity.h" +#include "driver/isp.h" + +#include "esp_rom_sys.h" + +TEST_CASE("ISP processor exhausted allocation", "[isp]") +{ + esp_isp_processor_cfg_t isp_config = { + .clk_hz = 80 * 1000 * 1000, + .input_data_source = ISP_INPUT_DATA_SOURCE_CSI, + .input_data_color_type = ISP_COLOR_RAW8, + .output_data_color_type = ISP_COLOR_RGB565, + }; + isp_proc_t isp_proc[SOC_ISP_NUMS + 1] = {}; + + for (int i = 0; i < SOC_ISP_NUMS; i++) { + TEST_ESP_OK(esp_isp_new_processor(&isp_config, &isp_proc[i])); + esp_rom_printf("first alloc ok\n"); + } + + TEST_ASSERT(esp_isp_new_processor(&isp_config, &isp_proc[SOC_ISP_NUMS]) == ESP_ERR_NOT_FOUND); + + for (int i = 0; i < SOC_ISP_NUMS; i++) { + TEST_ESP_OK(esp_isp_del_processor(isp_proc[i])); + } +} + +TEST_CASE("ISP AF controller exhausted allocation", "[isp]") +{ + esp_isp_processor_cfg_t isp_config = { + .clk_hz = 80 * 1000 * 1000, + .input_data_source = ISP_INPUT_DATA_SOURCE_CSI, + .input_data_color_type = ISP_COLOR_RAW8, + .output_data_color_type = ISP_COLOR_RGB565, + }; + isp_proc_t isp_proc = NULL; + TEST_ESP_OK(esp_isp_new_processor(&isp_config, &isp_proc)); + + esp_isp_af_config_t af_config = { + .edge_thresh = 128, + }; + isp_af_ctrlr_t af_ctrlr[SOC_ISP_AF_CTLR_NUMS + 1] = {}; + for (int i = 0; i < SOC_ISP_AF_CTLR_NUMS; i++) { + TEST_ESP_OK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr[i])); + } + + TEST_ASSERT(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr[SOC_ISP_AF_CTLR_NUMS]) == ESP_ERR_NOT_FOUND); + + for (int i = 0; i < SOC_ISP_AF_CTLR_NUMS; i++) { + TEST_ESP_OK(esp_isp_del_af_controller(af_ctrlr[i])); + } + TEST_ESP_OK(esp_isp_del_processor(isp_proc)); +} + +TEST_CASE("ISP AF env detector exhausted allocation", "[isp]") +{ + esp_isp_processor_cfg_t isp_config = { + .clk_hz = 80 * 1000 * 1000, + .input_data_source = ISP_INPUT_DATA_SOURCE_CSI, + .input_data_color_type = ISP_COLOR_RAW8, + .output_data_color_type = ISP_COLOR_RGB565, + }; + isp_proc_t isp_proc = NULL; + TEST_ESP_OK(esp_isp_new_processor(&isp_config, &isp_proc)); + + esp_isp_af_config_t af_config = { + .edge_thresh = 128, + }; + isp_af_ctrlr_t af_ctrlr = NULL; + TEST_ESP_OK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr)); + + esp_isp_af_env_config_t env_config = { + .interval = 10, + }; + isp_af_env_detr_t env_detector[SOC_ISP_AF_ENV_DETECTOR_NUMS + 1] = {}; + for (int i = 0; i < SOC_ISP_AF_ENV_DETECTOR_NUMS; i++) { + TEST_ESP_OK(esp_isp_new_af_env_detector(af_ctrlr, &env_config, &env_detector[i])); + } + + TEST_ASSERT(esp_isp_new_af_env_detector(af_ctrlr, &env_config, &env_detector[SOC_ISP_AF_ENV_DETECTOR_NUMS]) == ESP_ERR_NOT_FOUND); + + for (int i = 0; i < SOC_ISP_AF_ENV_DETECTOR_NUMS; i++) { + TEST_ESP_OK(esp_isp_del_af_env_detector(env_detector[i])); + } + TEST_ESP_OK(esp_isp_del_af_controller(af_ctrlr)); + TEST_ESP_OK(esp_isp_del_processor(isp_proc)); +} diff --git a/components/esp_driver_isp/test_apps/isp/pytest_isp.py b/components/esp_driver_isp/test_apps/isp/pytest_isp.py new file mode 100644 index 0000000000..92c6291f7c --- /dev/null +++ b/components/esp_driver_isp/test_apps/isp/pytest_isp.py @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +from pytest_embedded_idf import IdfDut + + +def test_isp(dut: IdfDut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_driver_isp/test_apps/isp/sdkconfig.defaults b/components/esp_driver_isp/test_apps/isp/sdkconfig.defaults new file mode 100644 index 0000000000..fa8ac618b9 --- /dev/null +++ b/components/esp_driver_isp/test_apps/isp/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n diff --git a/components/esp_hw_support/mipi_csi_share_hw_ctrl.c b/components/esp_hw_support/mipi_csi_share_hw_ctrl.c index e5909802d7..db9bc17bb1 100644 --- a/components/esp_hw_support/mipi_csi_share_hw_ctrl.c +++ b/components/esp_hw_support/mipi_csi_share_hw_ctrl.c @@ -73,6 +73,7 @@ esp_err_t mipi_csi_brg_declaim(int id) s_ctx.ref_cnt[id]--; if (s_ctx.ref_cnt[id] < 0) { + s_ctx.ref_cnt[id] = 0; portEXIT_CRITICAL(&s_ctx.spinlock); ESP_LOGE(TAG, "%s called, but s_ctx.ref_cnt[%d] == 0", __func__, id); return ESP_ERR_INVALID_STATE; diff --git a/components/hal/esp32p4/include/hal/clk_gate_ll.h b/components/hal/esp32p4/include/hal/clk_gate_ll.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/hal/esp32p4/include/hal/isp_ll.h b/components/hal/esp32p4/include/hal/isp_ll.h index 96633369b2..2773b0b82c 100644 --- a/components/hal/esp32p4/include/hal/isp_ll.h +++ b/components/hal/esp32p4/include/hal/isp_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,6 +10,7 @@ #include "esp_attr.h" #include "hal/misc.h" #include "hal/assert.h" +#include "hal/hal_utils.h" #include "hal/isp_types.h" #include "hal/color_types.h" #include "soc/isp_struct.h" @@ -23,6 +24,11 @@ extern "C" { #define ISP_LL_GET_HW(num) (((num) == 0) ? (&ISP) : NULL) +/*--------------------------------------------------------------- + Clock +---------------------------------------------------------------*/ +#define ISP_LL_TX_MAX_CLK_INT_DIV 0x100 + /*--------------------------------------------------------------- INTR @@ -151,11 +157,12 @@ static inline void isp_ll_select_clk_source(isp_dev_t *hw, soc_periph_isp_clk_sr * @brief Set ISP clock div * * @param hw Hardware instance address - * @param div divider value + * @param div Clock division with integral and decimal part */ -static inline void isp_ll_set_clock_div(isp_dev_t *hw, uint32_t div) +static inline void isp_ll_set_clock_div(isp_dev_t *hw, const hal_utils_clk_div_t *clk_div) { - HP_SYS_CLKRST.peri_clk_ctrl26.reg_isp_clk_div_num = div; + HAL_ASSERT(clk_div->integer > 0 && clk_div->integer <= ISP_LL_TX_MAX_CLK_INT_DIV); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl26, reg_isp_clk_div_num, clk_div->integer - 1); } /// use a macro to wrap the function, force the caller to use it in a critical section @@ -518,6 +525,7 @@ static inline void isp_ll_af_set_window_range(isp_dev_t *hw, uint32_t window_id, break; default: HAL_ASSERT(false); + break; } } @@ -540,6 +548,7 @@ static inline uint32_t isp_ll_af_get_window_sum(isp_dev_t *hw, uint32_t window_i return hw->af_sum_c.af_sumc; default: HAL_ASSERT(false); + return 0; } } @@ -562,6 +571,7 @@ static inline uint32_t isp_ll_af_get_window_lum(isp_dev_t *hw, uint32_t window_i return hw->af_lum_c.af_lumc; default: HAL_ASSERT(false); + return 0; } } diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 9f9b4afd25..6f64fe017e 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -67,6 +67,10 @@ if(CONFIG_SOC_SDM_SUPPORTED) list(APPEND srcs "${target_folder}/sdm_periph.c") endif() +if(CONFIG_SOC_ISP_SUPPORTED) + list(APPEND srcs "${target}/isp_periph.c") +endif() + if(CONFIG_SOC_I2S_SUPPORTED) list(APPEND srcs "${target_folder}/i2s_periph.c") endif() diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 0318025f10..e2ac0ec671 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -127,6 +127,10 @@ config SOC_LEDC_SUPPORTED bool default y +config SOC_ISP_SUPPORTED + bool + default y + config SOC_I2C_SUPPORTED bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 2698fd2910..df6ab04bbe 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -53,6 +53,7 @@ #define SOC_SDM_SUPPORTED 1 #define SOC_GPSPI_SUPPORTED 1 #define SOC_LEDC_SUPPORTED 1 +#define SOC_ISP_SUPPORTED 1 #define SOC_I2C_SUPPORTED 1 #define SOC_SYSTIMER_SUPPORTED 1 #define SOC_AES_SUPPORTED 1 diff --git a/components/soc/esp32p4/isp_periph.c b/components/soc/esp32p4/isp_periph.c new file mode 100644 index 0000000000..e46c183356 --- /dev/null +++ b/components/soc/esp32p4/isp_periph.c @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/periph_defs.h" +#include "soc/isp_periph.h" + +#ifdef __cplusplus +extern "C" { +#endif + +const isp_info_t isp_hw_info = { + .instances = { + [0] = { + .irq = ETS_ISP_INTR_SOURCE, + } + } +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/include/soc/isp_periph.h b/components/soc/include/soc/isp_periph.h new file mode 100644 index 0000000000..54185323d6 --- /dev/null +++ b/components/soc/include/soc/isp_periph.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc_caps.h" +#include "soc/periph_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_ISP_SUPPORTED +typedef struct { + struct { + const uint32_t irq; + } instances[SOC_ISP_NUMS]; +} isp_info_t; + +extern const isp_info_t isp_hw_info; +#endif + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme_sa.h b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme_sa.h new file mode 100644 index 0000000000..44e6a57dec --- /dev/null +++ b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme_sa.h @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" +#include "isp_af_scheme.h" +#include "driver/isp_af.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*--------------------------------------------------------------- + ISP AF Step Approximation Scheme (SA Scheme) +---------------------------------------------------------------*/ +typedef struct { + int first_step_val; ///< Step of the camera sensor focus value for first stage approximation + int first_approx_cycles; ///< First stage approximation cycles + int second_step_val; ///< Step of the camera sensor focus value for second stage approximation + int second_approx_cycles; ///< Second stage approximation cycles +} isp_af_sa_scheme_config_t; + +typedef struct { + int focus_val_max; ///< Max camera sensor focus value +} isp_af_sa_scheme_sensor_info_t; + +typedef struct { + /** + * @brief Sensor driver API to set sensor focus value + * + * @param[in] focus_val Camera sensor focus value + */ + esp_err_t (*af_sensor_set_focus)(int focus_val); + +} isp_af_sa_scheme_sensor_drv_t; + +/** + * @brief Create an AF step approximation scheme + * + * @param[in] af_ctrlr AF controller handle + * @param[in] config AF SA scheme configurations, see `isp_af_sa_scheme_config_t` + * @param[out] ret_scheme AF scheme handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid + * - ESP_ERR_INVALID_STATE Invalid state + * - ESP_ERR_NO_MEM If out of memory + */ +esp_err_t isp_af_create_sa_scheme(isp_af_ctrlr_t af_ctrlr, const isp_af_sa_scheme_config_t *config, isp_af_scheme_handle_t *ret_scheme); + +/** + * @brief Delete an AF step approximation scheme + * + * @param[in] scheme AF scheme handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid + * - ESP_ERR_INVALID_STATE Invalid state + * - ESP_ERR_NO_MEM If out of memory + */ +esp_err_t isp_af_delete_sa_scheme(isp_af_scheme_handle_t scheme); + +/** + * @brief Register camera sensor driver to the SA scheme + * + * @param[in] scheme AF scheme handle + * @param[in] sensor_drv Sensor driver, see `isp_af_sa_scheme_sensor_drv_t` + * @param[in] info Sensor info + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid + * - ESP_ERR_INVALID_STATE Invalid state + */ +esp_err_t isp_af_sa_scheme_register_sensor_driver(isp_af_scheme_handle_t scheme, const isp_af_sa_scheme_sensor_drv_t *sensor_drv, const isp_af_sa_scheme_sensor_info_t *info); + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/isp/auto_focus/components/isp_af_schemes/src/isp_af_scheme_sa.c b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/src/isp_af_scheme_sa.c new file mode 100644 index 0000000000..094ae4df46 --- /dev/null +++ b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/src/isp_af_scheme_sa.c @@ -0,0 +1,208 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "driver/isp_af.h" +#include "isp_af_scheme_sa.h" +#include "isp_af_scheme_interface.h" + +#define ISP_AF_SCHEME_SA_DEFAULT_WINDOW_NUMS 3 +#define ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS 30 + +static const char *TAG = "AF_SCHEME"; + +typedef struct { + isp_af_ctrlr_t af_ctlr; + int first_step_val; + int first_approx_cycles; + int second_step_val; + int second_approx_cycles; + + isp_af_sa_scheme_sensor_info_t sensor_info; + isp_af_sa_scheme_sensor_drv_t sensor_drv; +} af_scheme_context_t; + +/* ------------------------ Interface Functions --------------------------- */ +static esp_err_t s_af_process(void *arg, int *out_definition_thresh, int *out_luminance_thresh); + +/* ------------------------- Public API ------------------------------------- */ +esp_err_t isp_af_create_sa_scheme(isp_af_ctrlr_t af_ctlr, const isp_af_sa_scheme_config_t *config, isp_af_scheme_handle_t *ret_scheme) +{ + esp_err_t ret = ESP_FAIL; + ESP_RETURN_ON_FALSE(af_ctlr && config && ret_scheme, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer"); + + isp_af_scheme_t *scheme = (isp_af_scheme_t *)heap_caps_calloc(1, sizeof(isp_af_scheme_t), MALLOC_CAP_DEFAULT); + ESP_RETURN_ON_FALSE(scheme, ESP_ERR_NO_MEM, TAG, "no mem for scheme"); + + af_scheme_context_t *ctx = (af_scheme_context_t *)heap_caps_calloc(1, sizeof(af_scheme_context_t), MALLOC_CAP_DEFAULT); + ESP_GOTO_ON_FALSE(ctx, ESP_ERR_NO_MEM, err, TAG, "no mem scheme context"); + + scheme->af_process = s_af_process; + scheme->ctx = ctx; + + ctx->af_ctlr = af_ctlr; + ctx->first_step_val = config->first_step_val; + ctx->first_approx_cycles = config->first_approx_cycles; + ctx->second_step_val = config->second_step_val; + ctx->second_approx_cycles = config->second_approx_cycles; + + *ret_scheme = scheme; + + return ESP_OK; + +err: + free(scheme); + + return ret; +} + +esp_err_t isp_af_delete_sa_scheme(isp_af_scheme_handle_t scheme) +{ + ESP_RETURN_ON_FALSE(scheme, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + + free(scheme->ctx); + scheme->ctx = NULL; + + free(scheme); + scheme = NULL; + + return ESP_OK; +} + +esp_err_t isp_af_sa_scheme_register_sensor_driver(isp_af_scheme_handle_t scheme, const isp_af_sa_scheme_sensor_drv_t *sensor_drv, const isp_af_sa_scheme_sensor_info_t *info) +{ + ESP_RETURN_ON_FALSE(scheme, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer"); + ESP_RETURN_ON_FALSE(scheme->ctx, ESP_ERR_INVALID_STATE, TAG, "no scheme created yet"); + + af_scheme_context_t *ctx = scheme->ctx; + ctx->sensor_drv.af_sensor_set_focus = sensor_drv->af_sensor_set_focus; + ctx->sensor_info.focus_val_max = info->focus_val_max; + + return ESP_OK; +} + +/* ------------------------ Interface Functions --------------------------- */ +static esp_err_t s_af_process(void *arg, int *out_definition_thresh, int *out_luminance_thresh) +{ + //arg pointer is checked in the upper layer + + af_scheme_context_t *ctx = arg; + ESP_RETURN_ON_FALSE(ctx->af_ctlr, ESP_ERR_INVALID_STATE, TAG, "no AF controller registered"); + ESP_RETURN_ON_FALSE(ctx->sensor_drv.af_sensor_set_focus, ESP_ERR_INVALID_STATE, TAG, "no sensor driver function `af_sensor_set_focus` registered"); + + int af_sum = 0; + int af_lum = 0; + int af_sum_max = 0; + + int af_current_base = 0; + int af_current = 0; + int af_current_best = 0; + + int af_sum_env_th = 0; + int af_lum_env_th = 0; + int af_sum_tmp[ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS] = {0}; + int af_lum_tmp[ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS] = {0}; + + int ref_x = ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS; + int ref_x_fallback = ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS - 1; + + isp_af_result_t result = {}; + + ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(0), TAG, "sensor set focus val fail"); + + ESP_LOGV(TAG, "//----------- af start ----------//"); + + // first search + ESP_LOGV(TAG, "//----------- first search ----------//"); + af_sum_max = 0; + af_current_base = 0; + + for (int x = 0; x <= ctx->first_approx_cycles; x++) { + af_current = af_current_base + x * ctx->first_step_val; + ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current), TAG, "sensor set focus val fail"); + ESP_RETURN_ON_ERROR(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result), TAG, "get AF result fail"); + af_sum = result.definition[0] + result.definition[1] + result.definition[2]; + if (af_sum > af_sum_max) { + af_sum_max = af_sum; + af_current_best = af_current; + } + + ESP_LOGV(TAG, "af_sum: %d, af_current: %d.%d", af_sum, (int)af_current, (int)((int)(af_current * 1000) % 1000)); + } + + // second search + ESP_LOGV(TAG, "//----------- second search ----------//"); + af_sum_max = 0; + af_current_base = af_current_best + 10; + + if (af_current_base > ctx->sensor_info.focus_val_max) { + af_current_base = ctx->sensor_info.focus_val_max; + } + + for (int x = 0; x <= ctx->second_approx_cycles; x++) { + af_current = af_current_base - x * ctx->second_step_val; + if (af_current < 0) { + af_current = 0; + } + + ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current), TAG, "sensor set focus val fail"); + ESP_RETURN_ON_ERROR(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result), TAG, "get AF result fail"); + af_sum = result.definition[0] + result.definition[1] + result.definition[2]; + if (af_sum > af_sum_max) { + af_sum_max = af_sum; + af_current_best = af_current; + } + ESP_LOGV(TAG, "af_sum: %d, af_current: %d.%d", af_sum, (int)af_current, (int)((int)(af_current * 1000) % 1000)); + } + + // af done + ESP_LOGV(TAG, "//----------- af done ----------//"); + ESP_LOGV(TAG, "af_sum_max: %d, af_current_best: %d.%d", af_sum_max, (int)af_current_best, (int)((int)(af_current_best * 1000) % 1000)); + ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current_best), TAG, "sensor set focus val fail"); + + // update env threshold + ESP_LOGV(TAG, "//------- update env threshold -------//"); + + bool use_fallback_th = true; + for (int x = 0; x < ref_x; x++) { + ESP_RETURN_ON_ERROR(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result), TAG, "get AF result fail"); + af_sum_tmp[x] = result.definition[0] + result.definition[1] + result.definition[2]; + af_lum_tmp[x] = result.luminance[0] + result.luminance[1] + result.luminance[2]; + + if ((x >= 1) && (abs(af_sum_tmp[x] - af_sum_max) < af_sum_max * 0.3) && (abs(af_sum_tmp[x - 1] - af_sum_max) < af_sum_max * 0.3)) { + ref_x = x; + use_fallback_th = false; + break; + } + } + + if (use_fallback_th) { + ref_x = ref_x_fallback; + } + + af_sum = af_sum_tmp[ref_x]; + af_lum = af_lum_tmp[ref_x]; + af_sum_env_th = af_sum * 0.5; + af_lum_env_th = af_lum * 0.05; + *out_definition_thresh = af_sum_env_th; + *out_luminance_thresh = af_lum_env_th; + + for (int x = 0; x < ref_x; x++) { + ESP_LOGV(TAG, "af_sum[%d]: %d, af_lum[%d]: %d", x, af_sum_tmp[x], x, af_lum_tmp[x]); + } + ESP_LOGV(TAG, "//------- update af env threshold done -------//"); + ESP_LOGV(TAG, "af_sum: %d, af_sum_env_th: %d", af_sum, af_sum_env_th); + ESP_LOGV(TAG, "af_lum: %d, af_lum_env_th: %d", af_lum, af_lum_env_th); + ESP_LOGV(TAG, "//----------- af update done ----------//\n\n"); + + return ESP_OK; +}