From 3c0fe579b603bd4055d8b601c37cdc3cd9f9b1a6 Mon Sep 17 00:00:00 2001 From: gaoxu Date: Mon, 8 Jul 2024 09:57:08 +0800 Subject: [PATCH] feat(isp_ae): support isp auto exposure --- components/esp_driver_isp/CMakeLists.txt | 3 +- .../esp_driver_isp/include/driver/isp.h | 1 + .../esp_driver_isp/include/driver/isp_ae.h | 221 +++++++++++ .../esp_driver_isp/include/driver/isp_af.h | 7 +- .../esp_driver_isp/include/driver/isp_awb.h | 3 - .../esp_driver_isp/include/driver/isp_types.h | 12 + .../include/esp_private/isp_private.h | 1 + components/esp_driver_isp/src/isp_ae.c | 350 ++++++++++++++++++ components/esp_driver_isp/src/isp_af.c | 12 +- components/hal/esp32p4/include/hal/isp_ll.h | 174 +++++++-- components/hal/include/hal/isp_hal.h | 11 + components/hal/include/hal/isp_types.h | 20 + components/hal/isp_hal.c | 18 + .../esp32p4/include/soc/Kconfig.soc_caps.in | 12 + .../soc/esp32p4/include/soc/isp_struct.h | 52 +-- components/soc/esp32p4/include/soc/soc_caps.h | 3 + 16 files changed, 837 insertions(+), 63 deletions(-) create mode 100644 components/esp_driver_isp/include/driver/isp_ae.h create mode 100644 components/esp_driver_isp/src/isp_ae.c diff --git a/components/esp_driver_isp/CMakeLists.txt b/components/esp_driver_isp/CMakeLists.txt index b3b04938f6..9017d0c738 100644 --- a/components/esp_driver_isp/CMakeLists.txt +++ b/components/esp_driver_isp/CMakeLists.txt @@ -12,7 +12,8 @@ if(CONFIG_SOC_ISP_SUPPORTED) list(APPEND srcs "src/isp_core.c" "src/isp_af.c" "src/isp_ccm.c" - "src/isp_awb.c") + "src/isp_awb.c" + "src/isp_ae.c") endif() if(CONFIG_SOC_ISP_BF_SUPPORTED) diff --git a/components/esp_driver_isp/include/driver/isp.h b/components/esp_driver_isp/include/driver/isp.h index 7a36bb172c..24b5285a85 100644 --- a/components/esp_driver_isp/include/driver/isp.h +++ b/components/esp_driver_isp/include/driver/isp.h @@ -16,3 +16,4 @@ #include "driver/isp_awb.h" #include "driver/isp_bf.h" #include "driver/isp_ccm.h" +#include "driver/isp_ae.h" diff --git a/components/esp_driver_isp/include/driver/isp_ae.h b/components/esp_driver_isp/include/driver/isp_ae.h new file mode 100644 index 0000000000..66e31c9c8b --- /dev/null +++ b/components/esp_driver_isp/include/driver/isp_ae.h @@ -0,0 +1,221 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief AE controller config + */ +typedef struct { + isp_ae_sample_point_t sample_point; ///< The input data source, ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC: AE input data after demosaic, ISP_AE_SAMPLE_POINT_AFTER_GAMMA: AE input data after gamma + isp_window_t window; ///< The sampling windows of AE + int intr_priority; ///< The interrupt priority, range 0~7, if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) otherwise the larger the higher, 7 is NMI +} esp_isp_ae_config_t; + +/** + * @brief New an ISP AE controller + * + * @param[in] isp_proc ISP Processor handle + * @param[in] ae_config Pointer to AE config. Refer to ``esp_isp_ae_config_t``. + * @param[out] ret_hdl AE 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_ae_controller(isp_proc_handle_t isp_proc, const esp_isp_ae_config_t *ae_config, isp_ae_ctlr_t *ret_hdl); + +/** + * @brief Delete an ISP AE controller + * + * @param[in] ae_ctlr AE 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_ae_controller(isp_ae_ctlr_t ae_ctlr); + +/** + * @brief Enable an ISP AE controller + * + * @param[in] ae_ctlr AE 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_ae_controller_enable(isp_ae_ctlr_t ae_ctlr); + +/** + * @brief Disable an ISP AE controller + * + * @param[in] ae_ctlr AE 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_ae_controller_disable(isp_ae_ctlr_t ae_ctlr); + +/** + * @brief Trigger AE luminance statistics for one time and get the result + * + * @param[in] ae_ctlr AE controller handle + * @param[in] timeout_ms Timeout in millisecond + * - timeout_ms < 0: Won't return until finished + * - timeout_ms = 0: No timeout, trigger one time statistics and return immediately, + * in this case, the result won't be assigned in this function, + * but you can get the result in the callback `esp_isp_ae_env_detector_evt_cbs_t::on_env_statistics_done` + * - timeout_ms > 0: Wait for specified milliseconds, if not finished, then return timeout error + * @param[out] out_res AE luminance statistics result, can be NULL if `timeout_ms = 0` + * + * @return + * - ESP_OK On success + * - ESP_ERR_TIMEOUT If the waiting time exceeds the specified timeout. + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_ae_controller_get_oneshot_statistics(isp_ae_ctlr_t ae_ctlr, int timeout_ms, isp_ae_result_t *out_res); + +/** + * @brief Start AE continuous statistics of the luminance in the windows + * @note This function is an asynchronous and non-block function, + * it will start the continuous statistics and return immediately. + * You have to register the AE callback and get the result from the callback event data. + * When you call `esp_isp_ae_controller_get_oneshot_statistics` during continuous mode, + * continuous will be invalid, you need to restart continuous mode again. + * @param[in] ae_ctlr AE controller handle + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Null pointer + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_ae_controller_start_continuous_statistics(isp_ae_ctlr_t ae_ctlr); + +/** + * @brief Stop AE continuous statistics of the luminance in the windows + * + * @param[in] ae_ctlr AE controller handle + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Null pointer + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_ae_controller_stop_continuous_statistics(isp_ae_ctlr_t ae_ctlr); + +/*--------------------------------------------- + AE env detector +----------------------------------------------*/ +/** + * @brief AE environment detector config + */ +typedef struct { + int interval; /*!< Interval between environment detection, in frames. + * i.e., AE controller will trigger the statistic periodically to detect the environment change. + */ +} esp_isp_ae_env_config_t; + +/** + * @brief AE environment detector config + */ +typedef struct { + int low_thresh; /*!< Low threshold for AE environment detector luminance */ + int high_thresh; /*!< High threshold for AE environment detector luminance */ +} esp_isp_ae_env_thresh_t; + +/** + * @brief Set ISP AE environment detector + * + * @param[in] ae_ctlr AE controller handle + * @param[in] env_config AE Env detector configuration + * + * @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_ae_controller_set_env_detector(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_config_t *env_config); + +/** + * @brief Set ISP AE environment detector detecting threshold + * + * @param[in] ae_ctlr AE controller handle + * @param[in] env_thresh Luminance thresholds for AE env detector + * + * @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_ae_controller_set_env_detector_threshold(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_thresh_t *env_thresh); + +/** + * @brief Event data structure + */ +typedef struct { + isp_ae_result_t ae_result; /*!< The AE statistics result */ +} esp_isp_ae_env_detector_evt_data_t; + +/** + * @brief Prototype of ISP AE Env detector event callback + * + * @param[in] ae_ctlr ISP AE controller handle + * @param[in] edata ISP AE Env detector event data + * @param[in] user_data User registered context, registered when in `esp_isp_ae_env_detector_register_event_callbacks()` + * + * @return Whether a high priority task is woken up by this function + */ +typedef bool (*esp_isp_ae_env_detector_callback_t)(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_detector_evt_data_t *edata, void *user_data); + +/** + * @brief Group of ISP AE env_detector + * @note These callbacks are all running in an ISR environment. + * @note When CONFIG_ISP_ISR_IRAM_SAEE 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_ae_env_detector_callback_t on_env_statistics_done; ///< Event callback, invoked when environment sample done. + esp_isp_ae_env_detector_callback_t on_env_change; ///< Event callback, invoked when environment change happens. +} esp_isp_ae_env_detector_evt_cbs_t; + +/** + * @brief Register AE Env 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_SAEE 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] ae_ctlr AE controller 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_ae_env_detector_register_event_callbacks(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_detector_evt_cbs_t *cbs, void *user_data); + +#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 index 67bfa58b52..1d079bf615 100644 --- a/components/esp_driver_isp/include/driver/isp_af.h +++ b/components/esp_driver_isp/include/driver/isp_af.h @@ -78,9 +78,6 @@ esp_err_t esp_isp_af_controller_disable(isp_af_ctlr_t af_ctrlr); /** * @brief Trigger AF luminance and definition statistics for one time and get the result - * @note This function is a synchronous and block function, - * it only returns when AF luminance and definition statistics is done or timeout. - * It's a simple method to get the result directly for one time. * * @param[in] af_ctrlr AF controller handle * @param[in] timeout_ms Timeout in millisecond @@ -109,6 +106,7 @@ esp_err_t esp_isp_af_controller_get_oneshot_statistics(isp_af_ctlr_t af_ctrlr, i * @note This function is an asynchronous and non-block function, * it will start the continuous statistics and return immediately. * You have to register the AF callback and get the result from the callback event data. + * @note When continuous mode start, AF environment detector will be invalid * * @param[in] af_ctrlr AF controller handle * @return @@ -130,7 +128,7 @@ esp_err_t esp_isp_af_controller_start_continuous_statistics(isp_af_ctlr_t af_ctr esp_err_t esp_isp_af_controller_stop_continuous_statistics(isp_af_ctlr_t af_ctrlr); /*--------------------------------------------- - AF Env Monitor + AF Env Detector ----------------------------------------------*/ /** * @brief AF environment detector config @@ -147,6 +145,7 @@ typedef struct { * @param[in] af_ctrlr AF controller handle * @param[in] env_config AF Env detector configuration * + * @note When continuous mode start, AF environment detector will be invalid * @return * - ESP_OK On success * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. diff --git a/components/esp_driver_isp/include/driver/isp_awb.h b/components/esp_driver_isp/include/driver/isp_awb.h index ee85513e94..8fc03709fb 100644 --- a/components/esp_driver_isp/include/driver/isp_awb.h +++ b/components/esp_driver_isp/include/driver/isp_awb.h @@ -121,9 +121,6 @@ esp_err_t esp_isp_awb_controller_disable(isp_awb_ctlr_t awb_ctlr); /** * @brief Trigger AWB white patch statistics for one time and get the result - * @note This function is a synchronous and block function, - * it only returns when AWB white patch statistics is done or timeout. - * It's a simple method to get the result directly for one time. * * @param[in] awb_ctlr AWB controller handle * @param[in] timeout_ms Timeout in millisecond diff --git a/components/esp_driver_isp/include/driver/isp_types.h b/components/esp_driver_isp/include/driver/isp_types.h index ea171bb092..986062de3b 100644 --- a/components/esp_driver_isp/include/driver/isp_types.h +++ b/components/esp_driver_isp/include/driver/isp_types.h @@ -48,6 +48,13 @@ typedef struct { uint32_t sum_b; ///< The sum of B channel of these white patches } isp_awb_stat_result_t; +/** + * @brief ISP AE result + */ +typedef struct { + int luminance[ISP_AE_BLOCK_X_NUM][ISP_AE_BLOCK_Y_NUM]; ///< Luminance, it refers how luminant an image is +} isp_ae_result_t; + /** * @brief Type of ISP processor handle */ @@ -63,6 +70,11 @@ typedef struct isp_af_controller_t *isp_af_ctlr_t; */ typedef struct isp_awb_controller_t *isp_awb_ctlr_t; +/** + * @brief Type of ISP AE controller handle + */ +typedef struct isp_ae_controller_t *isp_ae_ctlr_t; + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_isp/include/esp_private/isp_private.h b/components/esp_driver_isp/include/esp_private/isp_private.h index 03b0f07843..2ee7c59127 100644 --- a/components/esp_driver_isp/include/esp_private/isp_private.h +++ b/components/esp_driver_isp/include/esp_private/isp_private.h @@ -66,6 +66,7 @@ typedef struct isp_processor_t { isp_af_ctlr_t af_ctlr[SOC_ISP_AF_CTLR_NUMS]; isp_awb_ctlr_t awb_ctlr; isp_fsm_t bf_fsm; + isp_ae_ctlr_t ae_ctlr; } isp_processor_t; #endif diff --git a/components/esp_driver_isp/src/isp_ae.c b/components/esp_driver_isp/src/isp_ae.c new file mode 100644 index 0000000000..2d7d28bd04 --- /dev/null +++ b/components/esp_driver_isp/src/isp_ae.c @@ -0,0 +1,350 @@ +/* + * 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_ae.h" +#include "esp_private/isp_private.h" + +static const char *TAG = "ISP_AE"; + +typedef struct isp_ae_controller_t { + int id; + isp_fsm_t fsm; + portMUX_TYPE spinlock; + intr_handle_t intr_handle; + int intr_priority; + isp_proc_handle_t isp_proc; + QueueHandle_t evt_que; + SemaphoreHandle_t stat_lock; + int low_thresh; + int high_thresh; + esp_isp_ae_env_detector_evt_cbs_t cbs; + void *user_data; +} isp_ae_controller_t; + +extern portMUX_TYPE fsm_spinlock; + +static void s_isp_ae_default_isr(void *arg); + +/*--------------------------------------------- + AE +----------------------------------------------*/ +static esp_err_t s_isp_claim_ae_controller(isp_proc_handle_t isp_proc, isp_ae_ctlr_t ae_ctlr) +{ + assert(isp_proc && ae_ctlr); + + esp_err_t ret = ESP_ERR_NOT_FOUND; + portENTER_CRITICAL(&isp_proc->spinlock); + if (!isp_proc->ae_ctlr) { + isp_proc->ae_ctlr = ae_ctlr; + ret = ESP_OK; + } + portEXIT_CRITICAL(&isp_proc->spinlock); + + return ret; +} + +static void s_isp_declaim_ae_controller(isp_ae_ctlr_t ae_ctlr) +{ + if (ae_ctlr && ae_ctlr->isp_proc) { + portENTER_CRITICAL(&ae_ctlr->isp_proc->spinlock); + ae_ctlr->isp_proc->ae_ctlr = NULL; + portEXIT_CRITICAL(&ae_ctlr->isp_proc->spinlock); + } +} + +static void s_isp_ae_free_controller(isp_ae_ctlr_t ae_ctlr) +{ + if (ae_ctlr) { + if (ae_ctlr->intr_handle) { + esp_intr_free(ae_ctlr->intr_handle); + } + if (ae_ctlr->evt_que) { + vQueueDelete(ae_ctlr->evt_que); + } + if (ae_ctlr->stat_lock) { + vSemaphoreDelete(ae_ctlr->stat_lock); + } + free(ae_ctlr); + } +} + +esp_err_t esp_isp_new_ae_controller(isp_proc_handle_t isp_proc, const esp_isp_ae_config_t *ae_config, isp_ae_ctlr_t *ret_hdl) +{ + esp_err_t ret = ESP_FAIL; + ESP_RETURN_ON_FALSE(isp_proc && ae_config && ret_hdl, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + + ESP_RETURN_ON_FALSE(((ae_config->window.top_left.x < ISP_LL_AE_WINDOW_MAX_RANGE) && + (ae_config->window.btm_right.x >= ae_config->window.top_left.x) && + (ae_config->window.btm_right.x < ISP_LL_AE_WINDOW_MAX_RANGE) && + (ae_config->window.top_left.y < ISP_LL_AE_WINDOW_MAX_RANGE) && + (ae_config->window.btm_right.y >= ae_config->window.top_left.y) && + (ae_config->window.btm_right.y < ISP_LL_AE_WINDOW_MAX_RANGE)), ESP_ERR_INVALID_ARG, TAG, "invalid window"); + + isp_ae_ctlr_t ae_ctlr = heap_caps_calloc(1, sizeof(isp_ae_controller_t), ISP_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(ae_ctlr, ESP_ERR_NO_MEM, TAG, "no mem"); + ae_ctlr->evt_que = xQueueCreateWithCaps(1, sizeof(isp_ae_result_t), ISP_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(ae_ctlr->evt_que, ESP_ERR_NO_MEM, err1, TAG, "no mem for ae event queue"); + ae_ctlr->stat_lock = xSemaphoreCreateBinaryWithCaps(ISP_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(ae_ctlr->stat_lock, ESP_ERR_NO_MEM, err1, TAG, "no mem for ae semaphore"); + + ae_ctlr->fsm = ISP_FSM_INIT; + ae_ctlr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + ae_ctlr->isp_proc = isp_proc; + + //claim an AE controller + ESP_GOTO_ON_ERROR(s_isp_claim_ae_controller(isp_proc, ae_ctlr), err1, TAG, "no available controller"); + + // Register the AE ISR + uint32_t intr_st_reg_addr = isp_ll_get_intr_status_reg_addr(isp_proc->hal.hw); + int intr_priority = ae_config->intr_priority > 0 && ae_config->intr_priority <= 7 ? BIT(ae_config->intr_priority) : ESP_INTR_FLAG_LOWMED; + ESP_GOTO_ON_ERROR(esp_intr_alloc_intrstatus(isp_hw_info.instances[isp_proc->proc_id].irq, ISP_INTR_ALLOC_FLAGS | intr_priority, intr_st_reg_addr, ISP_LL_EVENT_AE_MASK, + s_isp_ae_default_isr, ae_ctlr, &ae_ctlr->intr_handle), err2, TAG, "allocate interrupt failed"); + + isp_ll_ae_set_sample_point(isp_proc->hal.hw, ae_config->sample_point); + isp_ll_ae_enable(isp_proc->hal.hw, false); + isp_hal_ae_window_config(&isp_proc->hal, &ae_config->window); + + isp_ll_clear_intr(isp_proc->hal.hw, ISP_LL_EVENT_AE_MASK); + + *ret_hdl = ae_ctlr; + + return ESP_OK; +err2: + s_isp_declaim_ae_controller(ae_ctlr); +err1: + s_isp_ae_free_controller(ae_ctlr); + + return ret; +} + +esp_err_t esp_isp_del_ae_controller(isp_ae_ctlr_t ae_ctlr) +{ + ESP_RETURN_ON_FALSE(ae_ctlr && ae_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(ae_ctlr->isp_proc->ae_ctlr == ae_ctlr, ESP_ERR_INVALID_ARG, TAG, "controller isn't in use"); + ESP_RETURN_ON_FALSE(ae_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state"); + s_isp_declaim_ae_controller(ae_ctlr); + + s_isp_ae_free_controller(ae_ctlr); + + return ESP_OK; +} + +esp_err_t esp_isp_ae_controller_enable(isp_ae_ctlr_t ae_ctlr) +{ + ESP_RETURN_ON_FALSE(ae_ctlr && ae_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(ae_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state"); + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(esp_intr_enable(ae_ctlr->intr_handle), err, TAG, "failed to enable the AE interrupt"); + isp_ll_ae_clk_enable(ae_ctlr->isp_proc->hal.hw, true); + isp_ll_enable_intr(ae_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AE_MASK, true); + isp_ll_ae_enable(ae_ctlr->isp_proc->hal.hw, true); + xSemaphoreGive(ae_ctlr->stat_lock); + + return ret; +err: + ae_ctlr->fsm = ISP_FSM_INIT; + return ret; +} + +esp_err_t esp_isp_ae_controller_disable(isp_ae_ctlr_t ae_ctlr) +{ + ESP_RETURN_ON_FALSE(ae_ctlr && ae_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(ae_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state"); + xSemaphoreTake(ae_ctlr->stat_lock, 0); + ae_ctlr->fsm = ISP_FSM_INIT; + + isp_ll_ae_clk_enable(ae_ctlr->isp_proc->hal.hw, false); + isp_ll_enable_intr(ae_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AE_MASK, false); + isp_ll_ae_enable(ae_ctlr->isp_proc->hal.hw, false); + esp_intr_disable(ae_ctlr->intr_handle); + + return ESP_OK; +} + +esp_err_t esp_isp_ae_controller_get_oneshot_statistics(isp_ae_ctlr_t ae_ctlr, int timeout_ms, isp_ae_result_t *out_res) +{ + ESP_RETURN_ON_FALSE_ISR(ae_ctlr && (out_res || timeout_ms == 0), ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + + esp_err_t ret = ESP_OK; + TickType_t ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); + xSemaphoreTake(ae_ctlr->stat_lock, ticks); + ESP_GOTO_ON_FALSE(ae_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, err, TAG, "controller isn't in enable state"); + + // Reset the queue in case receiving the legacy data in the queue + xQueueReset(ae_ctlr->evt_que); + + // Disable the env detector when manual statistics. + // Otherwise, the env detector results may overwrite the manual statistics results when the statistics results are not read out in time + isp_ll_ae_env_detector_set_thresh(ae_ctlr->isp_proc->hal.hw, 0, 0); + // Trigger the AE statistics manually + isp_ll_ae_manual_update(ae_ctlr->isp_proc->hal.hw); + // Wait the statistics to finish and receive the result from the queue + if ((ticks > 0) && xQueueReceive(ae_ctlr->evt_que, out_res, ticks) != pdTRUE) { + ret = ESP_ERR_TIMEOUT; + } + // Re-enable the env detector after manual statistics. + isp_ll_ae_env_detector_set_thresh(ae_ctlr->isp_proc->hal.hw, ae_ctlr->low_thresh, ae_ctlr->high_thresh); + + portENTER_CRITICAL(&fsm_spinlock); + ae_ctlr->fsm = ISP_FSM_ENABLE; + portEXIT_CRITICAL(&fsm_spinlock); + +err: + xSemaphoreGive(ae_ctlr->stat_lock); + + return ret; +} + +esp_err_t esp_isp_ae_controller_start_continuous_statistics(isp_ae_ctlr_t ae_ctlr) +{ + ESP_RETURN_ON_FALSE_ISR(ae_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE_ISR(ae_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state"); + esp_err_t ret = ESP_OK; + if (xSemaphoreTake(ae_ctlr->stat_lock, 0) == pdFALSE) { + ESP_LOGW(TAG, "statistics lock is not acquired, controller is busy"); + return ESP_ERR_INVALID_STATE; + } + ESP_GOTO_ON_FALSE(ae_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, err, TAG, "controller isn't in enable state"); + + portENTER_CRITICAL(&fsm_spinlock); + ae_ctlr->fsm = ISP_FSM_START; + portEXIT_CRITICAL(&fsm_spinlock); + + isp_ll_ae_manual_update(ae_ctlr->isp_proc->hal.hw); + +err: + xSemaphoreGive(ae_ctlr->stat_lock); + return ret; +} + +esp_err_t esp_isp_ae_controller_stop_continuous_statistics(isp_ae_ctlr_t ae_ctlr) +{ + ESP_RETURN_ON_FALSE_ISR(ae_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE_ISR(ae_ctlr->fsm == ISP_FSM_START, ESP_ERR_INVALID_STATE, TAG, "controller isn't in continuous state"); + isp_ll_ae_env_detector_set_thresh(ae_ctlr->isp_proc->hal.hw, ae_ctlr->low_thresh, ae_ctlr->high_thresh); + + portENTER_CRITICAL(&fsm_spinlock); + ae_ctlr->fsm = ISP_FSM_ENABLE; + portEXIT_CRITICAL(&fsm_spinlock); + xSemaphoreGive(ae_ctlr->stat_lock); + + return ESP_OK; +} + +/*--------------------------------------------- + AE Env Detector +----------------------------------------------*/ +esp_err_t esp_isp_ae_controller_set_env_detector(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_config_t *env_config) +{ + ESP_RETURN_ON_FALSE(ae_ctlr && env_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(ae_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "invalid fsm, should be called when in init state"); + ESP_RETURN_ON_FALSE(env_config->interval > 0, ESP_ERR_INVALID_STATE, TAG, "invalid interval, should be greater than 0"); + + isp_ll_clear_intr(ae_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AE_ENV); + isp_ll_ae_env_detector_set_period(ae_ctlr->isp_proc->hal.hw, env_config->interval); + isp_ll_enable_intr(ae_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AE_ENV, true); + + return ESP_OK; +} + +esp_err_t esp_isp_ae_env_detector_register_event_callbacks(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_detector_evt_cbs_t *cbs, void *user_data) +{ + ESP_RETURN_ON_FALSE(ae_ctlr && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(ae_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "detector isn't in the init state"); + +#if CONFIG_ISP_ISR_IRAM_SAEE + if (cbs->on_env_statistics_done) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_env_statistics_done), ESP_ERR_INVALID_ARG, TAG, "on_env_statistics_done callback not in IRAM"); + } + 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"); + } + if (user_data) { + ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM"); + } +#endif + ae_ctlr->cbs.on_env_statistics_done = cbs->on_env_statistics_done; + ae_ctlr->cbs.on_env_change = cbs->on_env_change; + ae_ctlr->user_data = user_data; + + return ESP_OK; +} + +esp_err_t esp_isp_ae_controller_set_env_detector_threshold(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_thresh_t *env_thresh) +{ + ESP_RETURN_ON_FALSE_ISR(ae_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE_ISR(ae_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "AE env detector isn't in enable state"); + ESP_RETURN_ON_FALSE_ISR((env_thresh->low_thresh != 0 && env_thresh->high_thresh != 0) && (env_thresh->low_thresh <= env_thresh->high_thresh), ESP_ERR_INVALID_STATE, TAG, "invalid AE env detector thresh"); + + ae_ctlr->low_thresh = env_thresh->low_thresh; + ae_ctlr->high_thresh = env_thresh->high_thresh; + + isp_ll_ae_env_detector_set_thresh(ae_ctlr->isp_proc->hal.hw, ae_ctlr->low_thresh, ae_ctlr->high_thresh); + + return ESP_OK; +} + +/*--------------------------------------------------------------- + INTR +---------------------------------------------------------------*/ +static void IRAM_ATTR s_isp_ae_default_isr(void *arg) +{ + isp_ae_ctlr_t ae_ctlr = (isp_ae_ctlr_t)arg; + isp_proc_handle_t proc = ae_ctlr->isp_proc; + + uint32_t ae_events = isp_hal_check_clear_intr_event(&proc->hal, ISP_LL_EVENT_AE_MASK); + + bool need_yield = false; + esp_isp_ae_env_detector_evt_data_t edata = {}; + + if (ae_events) { + // Get the statistics result + int block_id = 0; + for (int i = 0; i < SOC_ISP_AE_BLOCK_X_NUMS; i++) { + for (int j = 0; j < SOC_ISP_AE_BLOCK_Y_NUMS; j++) { + edata.ae_result.luminance[i][j] = isp_ll_ae_get_block_mean_lum(proc->hal.hw, block_id); + block_id++; + } + } + BaseType_t high_task_awake = false; + // Send the event data to the queue, overwrite the legacy one if exist + xQueueOverwriteFromISR(ae_ctlr->evt_que, &edata.ae_result, &high_task_awake); + // Invoke the callback if the callback is registered + need_yield |= high_task_awake == pdTRUE; + + /* If started continuous sampling, then trigger the next AE sample */ + if (ae_ctlr->fsm == ISP_FSM_START) { + isp_ll_ae_manual_update(ae_ctlr->isp_proc->hal.hw); + } + } + + /** + * Deal with the interrupts. + * Now only one detector. + * Should decide a detector instance according to the hw event. + */ + if (ae_events & ISP_LL_EVENT_AE_FDONE) { + if (ae_ctlr->cbs.on_env_statistics_done) { + need_yield |= ae_ctlr->cbs.on_env_statistics_done(ae_ctlr, &edata, ae_ctlr->user_data); + } + } + if (ae_events & ISP_LL_EVENT_AE_ENV) { + if (ae_ctlr->cbs.on_env_change) { + need_yield |= ae_ctlr->cbs.on_env_change(ae_ctlr, &edata, ae_ctlr->user_data); + } + } + + if (need_yield) { + portYIELD_FROM_ISR(); + } +} diff --git a/components/esp_driver_isp/src/isp_af.c b/components/esp_driver_isp/src/isp_af.c index dd7ad8d4f3..516d2b22bf 100644 --- a/components/esp_driver_isp/src/isp_af.c +++ b/components/esp_driver_isp/src/isp_af.c @@ -126,7 +126,7 @@ esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af 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_mode(isp_proc->hal.hw, ISP_LL_AF_EDGE_DETECTOR_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); @@ -229,7 +229,7 @@ esp_err_t esp_isp_af_controller_stop_continuous_statistics(isp_af_ctlr_t af_ctrl } /*--------------------------------------------- - AF Env Monitor + AF Env Detector ----------------------------------------------*/ esp_err_t esp_isp_af_controller_set_env_detector(isp_af_ctlr_t af_ctrlr, const esp_isp_af_env_config_t *env_config) { @@ -238,11 +238,11 @@ esp_err_t esp_isp_af_controller_set_env_detector(isp_af_ctlr_t af_ctrlr, const e af_ctrlr->config.interval = env_config->interval; - isp_ll_af_env_monitor_set_period(af_ctrlr->isp_proc->hal.hw, 0); + isp_ll_af_env_detector_set_period(af_ctrlr->isp_proc->hal.hw, 0); isp_ll_clear_intr(af_ctrlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV); - isp_ll_af_env_monitor_set_mode(af_ctrlr->isp_proc->hal.hw, ISP_LL_AF_ENV_MONITOR_MODE_ABS); - isp_ll_af_env_monitor_set_period(af_ctrlr->isp_proc->hal.hw, af_ctrlr->config.interval); + isp_ll_af_env_detector_set_mode(af_ctrlr->isp_proc->hal.hw, ISP_LL_AF_ENV_DETECTOR_MODE_ABS); + isp_ll_af_env_detector_set_period(af_ctrlr->isp_proc->hal.hw, af_ctrlr->config.interval); isp_ll_enable_intr(af_ctrlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV, true); return ESP_OK; @@ -276,7 +276,7 @@ esp_err_t esp_isp_af_controller_set_env_detector_threshold(isp_af_ctlr_t af_ctrl ESP_RETURN_ON_FALSE_ISR(af_ctrlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE_ISR(af_ctrlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "detector isn't in enable state"); - isp_ll_af_env_monitor_set_thresh(af_ctrlr->isp_proc->hal.hw, definition_thresh, luminance_thresh); + isp_ll_af_env_detector_set_thresh(af_ctrlr->isp_proc->hal.hw, definition_thresh, luminance_thresh); return ESP_OK; } diff --git a/components/hal/esp32p4/include/hal/isp_ll.h b/components/hal/esp32p4/include/hal/isp_ll.h index 8792659fcb..7b6a9016a0 100644 --- a/components/hal/esp32p4/include/hal/isp_ll.h +++ b/components/hal/esp32p4/include/hal/isp_ll.h @@ -41,8 +41,8 @@ extern "C" { #define ISP_LL_EVENT_MIPI_HNUM_UNMATCH (1<<5) #define ISP_LL_EVENT_DPC_CHECK_DONE (1<<6) #define ISP_LL_EVENT_GAMMA_XCOORD_ERR (1<<7) -#define ISP_LL_EVENT_AE_MONITOR (1<<8) -#define ISP_LL_EVENT_AE_FRAME_DONE (1<<9) +#define ISP_LL_EVENT_AE_ENV (1<<8) +#define ISP_LL_EVENT_AE_FDONE (1<<9) #define ISP_LL_EVENT_AF_FDONE (1<<10) #define ISP_LL_EVENT_AF_ENV (1<<11) #define ISP_LL_EVENT_AWB_FDONE (1<<12) @@ -65,6 +65,7 @@ extern "C" { #define ISP_LL_EVENT_ALL_MASK (0x1FFFFFFF) #define ISP_LL_EVENT_AF_MASK (ISP_LL_EVENT_AF_FDONE | ISP_LL_EVENT_AF_ENV) +#define ISP_LL_EVENT_AE_MASK (ISP_LL_EVENT_AE_FDONE | ISP_LL_EVENT_AE_ENV) #define ISP_LL_EVENT_AWB_MASK (ISP_LL_EVENT_AWB_FDONE) /*--------------------------------------------------------------- @@ -72,6 +73,11 @@ extern "C" { ---------------------------------------------------------------*/ #define ISP_LL_AF_WINDOW_MAX_RANGE ((1<<12) - 1) +/*--------------------------------------------------------------- + AE +---------------------------------------------------------------*/ +#define ISP_LL_AE_WINDOW_MAX_RANGE ((1<<12) - 1) + /*--------------------------------------------------------------- BF ---------------------------------------------------------------*/ @@ -117,20 +123,20 @@ typedef union { } isp_ll_ccm_gain_t; /** - * @brief Env monitor mode + * @brief Env detector mode */ typedef enum { - ISP_LL_AF_ENV_MONITOR_MODE_ABS, ///< Use absolute val for threshold - ISP_LL_AF_ENV_MONITOR_MODE_RATIO, ///< Use ratio val for threshold -} isp_ll_af_env_monitor_mode_t; + ISP_LL_AF_ENV_DETECTOR_MODE_ABS, ///< Use absolute val for threshold + ISP_LL_AF_ENV_DETECTOR_MODE_RATIO, ///< Use ratio val for threshold +} isp_ll_af_env_detector_mode_t; /** - * @brief Edge monitor mode + * @brief Edge detector mode */ typedef enum { - ISP_LL_AF_EDGE_MONITOR_MODE_AUTO, ///< Auto set threshold - ISP_LL_AF_EDGE_MONITOR_MODE_MANUAL, ///< Manual set threshold -} isp_ll_af_edge_monitor_mode_t; + ISP_LL_AF_EDGE_DETECTOR_MODE_AUTO, ///< Auto set threshold + ISP_LL_AF_EDGE_DETECTOR_MODE_MANUAL, ///< Manual set threshold +} isp_ll_af_edge_detector_mode_t; /*--------------------------------------------------------------- @@ -496,11 +502,11 @@ static inline void isp_ll_af_manual_update(isp_dev_t *hw) * @brief Set edge thresh mode * * @param[in] hw Hardware instance address - * @param[in] mode See `isp_ll_af_edge_monitor_mode_t` + * @param[in] mode See `isp_ll_af_edge_detector_mode_t` */ -static inline void isp_ll_af_set_edge_thresh_mode(isp_dev_t *hw, isp_ll_af_edge_monitor_mode_t mode) +static inline void isp_ll_af_set_edge_thresh_mode(isp_dev_t *hw, isp_ll_af_edge_detector_mode_t mode) { - if (mode == ISP_LL_AF_EDGE_MONITOR_MODE_AUTO) { + if (mode == ISP_LL_AF_EDGE_DETECTOR_MODE_AUTO) { hw->af_threshold.af_threshold = 0; } } @@ -622,43 +628,43 @@ static inline uint32_t isp_ll_af_get_window_lum(isp_dev_t *hw, uint32_t window_i } /*--------------------------------------------- - AF Env Monitor + AF Env detector ----------------------------------------------*/ /** - * @brief Set env monitor period + * @brief Set env detector period * * @param[in] hw Hardware instance address - * @param[in] period period of the env monitor, in frames + * @param[in] period period of the env detector, in frames */ -static inline void isp_ll_af_env_monitor_set_period(isp_dev_t *hw, uint32_t period) +static inline void isp_ll_af_env_detector_set_period(isp_dev_t *hw, uint32_t period) { hw->af_ctrl0.af_env_period = period; } /** - * @brief Set env monitor mode + * @brief Set env detector mode * * @param[in] hw Hardware instance address - * @param[in] mode See `isp_ll_af_env_monitor_mode_t` + * @param[in] mode See `isp_ll_af_env_detector_mode_t` */ -static inline void isp_ll_af_env_monitor_set_mode(isp_dev_t *hw, isp_ll_af_env_monitor_mode_t mode) +static inline void isp_ll_af_env_detector_set_mode(isp_dev_t *hw, isp_ll_af_env_detector_mode_t mode) { - if (mode == ISP_LL_AF_ENV_MONITOR_MODE_RATIO) { + if (mode == ISP_LL_AF_ENV_DETECTOR_MODE_RATIO) { hw->af_env_user_th_sum.af_env_user_threshold_sum = 0x0; hw->af_env_user_th_lum.af_env_user_threshold_lum = 0x0; } - //nothing to do to if using abs mode, it'll be enabled after `isp_ll_af_env_monitor_set_thresh()` + //nothing to do to if using abs mode, it'll be enabled after `isp_ll_af_env_detector_set_thresh()` } /** - * @brief Set env monitor threshold + * @brief Set env detector threshold * * @param[in] hw Hardware instance address * @param[in] sum_thresh Threshold for definition * @param[in] lum_thresh Threshold for luminance */ -static inline void isp_ll_af_env_monitor_set_thresh(isp_dev_t *hw, uint32_t sum_thresh, uint32_t lum_thresh) +static inline void isp_ll_af_env_detector_set_thresh(isp_dev_t *hw, uint32_t sum_thresh, uint32_t lum_thresh) { HAL_ASSERT(sum_thresh != 0 || lum_thresh != 0); @@ -667,12 +673,12 @@ static inline void isp_ll_af_env_monitor_set_thresh(isp_dev_t *hw, uint32_t sum_ } /** - * @brief Set env monitor ratio + * @brief Set env detector ratio * * @param[in] hw Hardware instance address * @param[in] ratio_val Threshold for ratio */ -static inline void isp_ll_af_env_monitor_set_ratio(isp_dev_t *hw, uint32_t ratio_val) +static inline void isp_ll_af_env_detector_set_ratio(isp_dev_t *hw, uint32_t ratio_val) { HAL_ASSERT(hw->af_env_user_th_sum.af_env_user_threshold_sum == 0 && hw->af_env_user_th_lum.af_env_user_threshold_lum == 0); @@ -974,6 +980,122 @@ static inline void isp_ll_cam_enable(isp_dev_t *hw, bool enable) hw->cam_cntl.cam_en = 0; } } + +/*--------------------------------------------------------------- + AE +---------------------------------------------------------------*/ + +/** + * @brief Enable / Disable AE clock + * + * @param[in] hw Hardware instance address + * @param[in] enable Enable / Disable + */ +static inline void isp_ll_ae_clk_enable(isp_dev_t *hw, bool enable) +{ + hw->clk_en.clk_ae_force_on = enable; +} + +/** + * @brief Enable / Disable AE + * + * @param[in] hw Hardware instance address + * @param[in] enable Enable / Disable + */ +static inline void isp_ll_ae_enable(isp_dev_t *hw, bool enable) +{ + hw->cntl.ae_en = enable; +} + +/** + * @brief Manual aupdate AF once + * + * @param[in] hw Hardware instance address + */ +static inline void isp_ll_ae_manual_update(isp_dev_t *hw) +{ + hw->ae_ctrl.ae_update = 1; +} + +/** + * @brief Select AE input data source + * + * @param[in] hw Hardware instance address + * @param[in] sample_point 0: AE input data after demosaic, 1: AE input data after gamma + */ +static inline void isp_ll_ae_set_sample_point(isp_dev_t *hw, isp_ae_sample_point_t sample_point) +{ + hw->ae_ctrl.ae_select = sample_point; +} + +/** + * @brief Set AE window range + * + * @param[in] hw Hardware instance address + * @param[in] x_start Top left pixel x axis value + * @param[in] x_bsize Block size on x axis + * @param[in] y_start Top left pixel y axis value + * @param[in] y_bsize Block size on y axis + */ +static inline void isp_ll_ae_set_window_range(isp_dev_t *hw, int x_start, int x_bsize, int y_start, int y_bsize) +{ + hw->ae_bx.ae_x_start = x_start; + hw->ae_bx.ae_x_bsize = x_bsize; + hw->ae_by.ae_y_start = y_start; + hw->ae_by.ae_y_bsize = y_bsize; +} + +/** + * @brief Get block mean luminance + * + * @param[in] hw Hardware instance address + * @param[in] block_id + * + * @return Mean luminance + */ +static inline int isp_ll_ae_get_block_mean_lum(isp_dev_t *hw, int block_id) +{ + HAL_ASSERT(block_id >=0 && block_id < (SOC_ISP_AE_BLOCK_X_NUMS * SOC_ISP_AE_BLOCK_Y_NUMS)); + return hw->ae_block_mean[block_id / 4].ae_b_mean[3 - (block_id % 4)]; +} + +/** + * @brief AE set the pixel number of each subwin, and set the reciprocal of each subwin_pixnum, 20bit fraction + * + * @param[in] hw Hardware instance address + * @param[in] subwin_pixnum Pixel number + */ +static inline void isp_ll_ae_set_subwin_pixnum_recip(isp_dev_t *hw, int subwin_pixnum) +{ + hw->ae_winpixnum.ae_subwin_pixnum = subwin_pixnum; + int subwin_recip = (1 << 20) / subwin_pixnum; + hw->ae_win_reciprocal.ae_subwin_recip = subwin_recip; +} + +/** + * @brief Set AE env detector threshold + * + * @param[in] hw Hardware instance address + * @param[in] low_thresh Lower lum threshold + * @param[in] high_thresh Higher lum threshold + */ +static inline void isp_ll_ae_env_detector_set_thresh(isp_dev_t *hw, uint32_t low_thresh, uint32_t high_thresh) +{ + hw->ae_monitor.ae_monitor_tl = low_thresh; + hw->ae_monitor.ae_monitor_th = high_thresh; +} + +/** + * @brief Set AE env detector period + * + * @param[in] hw Hardware instance address + * @param[in] period period of the AE env detector, in frames + */ +static inline void isp_ll_ae_env_detector_set_period(isp_dev_t *hw, uint32_t period) +{ + hw->ae_monitor.ae_monitor_period = period; +} + /*--------------------------------------------------------------- INTR ---------------------------------------------------------------*/ diff --git a/components/hal/include/hal/isp_hal.h b/components/hal/include/hal/isp_hal.h index d8566a9ec4..7de5098378 100644 --- a/components/hal/include/hal/isp_hal.h +++ b/components/hal/include/hal/isp_hal.h @@ -66,6 +66,17 @@ void isp_hal_init(isp_hal_context_t *hal, int isp_id); */ void isp_hal_af_window_config(const isp_hal_context_t *hal, int window_id, const isp_window_t *window); +/*--------------------------------------------------------------- + AE +---------------------------------------------------------------*/ +/** + * @brief Configure AE window + * + * @param[in] hal Context of the HAL layer + * @param[in] window Window info, see `isp_window_t` + */ +void isp_hal_ae_window_config(const isp_hal_context_t *hal, const isp_window_t *window); + /*--------------------------------------------------------------- INTR ---------------------------------------------------------------*/ diff --git a/components/hal/include/hal/isp_types.h b/components/hal/include/hal/isp_types.h index d36b8a09fc..d381d6f895 100644 --- a/components/hal/include/hal/isp_types.h +++ b/components/hal/include/hal/isp_types.h @@ -122,6 +122,26 @@ typedef enum { ISP_AWB_SAMPLE_POINT_AFTER_CCM, ///< Sample AWB data after CCM (Color Correction Matrix) } isp_awb_sample_point_t; +/*--------------------------------------------------------------- + AE +---------------------------------------------------------------*/ + +#if (SOC_ISP_AE_BLOCK_X_NUMS && SOC_ISP_AE_BLOCK_Y_NUMS) +#define ISP_AE_BLOCK_X_NUM SOC_ISP_AE_BLOCK_X_NUMS // The AF window number for sampling +#define ISP_AE_BLOCK_Y_NUM SOC_ISP_AE_BLOCK_Y_NUMS // The AF window number for sampling +#else +#define ISP_AE_BLOCK_X_NUM 0 +#define ISP_AE_BLOCK_Y_NUM 0 +#endif + +/** + * @brief ISP AE input data source + * + */ +typedef enum { + ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC, ///< AE input data after demosaic + ISP_AE_SAMPLE_POINT_AFTER_GAMMA, ///< AE input data after gamma +} isp_ae_sample_point_t; #ifdef __cplusplus } diff --git a/components/hal/isp_hal.c b/components/hal/isp_hal.c index 37e69fd772..9a075a1fd9 100644 --- a/components/hal/isp_hal.c +++ b/components/hal/isp_hal.c @@ -57,6 +57,24 @@ void isp_hal_bf_config(isp_hal_context_t *hal, isp_hal_bf_cfg_t *config) isp_ll_bf_set_template(hal->hw, default_template); } } + +/*--------------------------------------------------------------- + AE +---------------------------------------------------------------*/ +void isp_hal_ae_window_config(const isp_hal_context_t *hal, const isp_window_t *window) +{ + uint32_t ae_x_start = window->top_left.x; + uint32_t ae_x_bsize = (window->btm_right.x - window-> top_left.x) / SOC_ISP_AE_BLOCK_X_NUMS; + + uint32_t ae_y_start = window->top_left.y; + uint32_t ae_y_bsize = (window->btm_right.y - window->top_left.y) / SOC_ISP_AE_BLOCK_Y_NUMS; + + isp_ll_ae_set_window_range(hal->hw, ae_x_start, ae_x_bsize, ae_y_start, ae_y_bsize); + + int ae_subwin_pixnum = ae_x_bsize * ae_y_bsize; + isp_ll_ae_set_subwin_pixnum_recip(hal->hw, ae_subwin_pixnum); +} + /*--------------------------------------------------------------- INTR, put in iram ---------------------------------------------------------------*/ diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index df3a8813b7..1d591dba16 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -827,6 +827,18 @@ config SOC_ISP_AF_WINDOW_NUMS int default 3 +config SOC_ISP_AE_CTLR_NUMS + int + default 1 + +config SOC_ISP_AE_BLOCK_X_NUMS + int + default 5 + +config SOC_ISP_AE_BLOCK_Y_NUMS + int + default 5 + config SOC_ISP_SHARE_CSI_BRG bool default y diff --git a/components/soc/esp32p4/include/soc/isp_struct.h b/components/soc/esp32p4/include/soc/isp_struct.h index 980103687b..e9cb544d04 100644 --- a/components/soc/esp32p4/include/soc/isp_struct.h +++ b/components/soc/esp32p4/include/soc/isp_struct.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 */ @@ -398,35 +398,35 @@ typedef union { typedef union { struct { /** gau_template21 : R/W; bitpos: [3:0]; default: 15; - * this field configures index 21 of gausian template + * this field configures index 21 of gaussian template */ uint32_t gau_template21:4; /** gau_template20 : R/W; bitpos: [7:4]; default: 15; - * this field configures index 20 of gausian template + * this field configures index 20 of gaussian template */ uint32_t gau_template20:4; /** gau_template12 : R/W; bitpos: [11:8]; default: 15; - * this field configures index 12 of gausian template + * this field configures index 12 of gaussian template */ uint32_t gau_template12:4; /** gau_template11 : R/W; bitpos: [15:12]; default: 15; - * this field configures index 11 of gausian template + * this field configures index 11 of gaussian template */ uint32_t gau_template11:4; /** gau_template10 : R/W; bitpos: [19:16]; default: 15; - * this field configures index 10 of gausian template + * this field configures index 10 of gaussian template */ uint32_t gau_template10:4; /** gau_template02 : R/W; bitpos: [23:20]; default: 15; - * this field configures index 02 of gausian template + * this field configures index 02 of gaussian template */ uint32_t gau_template02:4; /** gau_template01 : R/W; bitpos: [27:24]; default: 15; - * this field configures index 01 of gausian template + * this field configures index 01 of gaussian template */ uint32_t gau_template01:4; /** gau_template00 : R/W; bitpos: [31:28]; default: 15; - * this field configures index 00 of gausian template + * this field configures index 00 of gaussian template */ uint32_t gau_template00:4; }; @@ -439,7 +439,7 @@ typedef union { typedef union { struct { /** gau_template22 : R/W; bitpos: [3:0]; default: 15; - * this field configures index 22 of gausian template + * this field configures index 22 of gaussian template */ uint32_t gau_template22:4; uint32_t reserved_4:28; @@ -1321,7 +1321,7 @@ typedef union { */ uint32_t ae_monitor_th:8; /** ae_monitor_period : R/W; bitpos: [21:16]; default: 0; - * this field cnfigures ae monitor frame period + * this field configures ae monitor frame period */ uint32_t ae_monitor_period:6; uint32_t reserved_22:10; @@ -1536,7 +1536,7 @@ typedef union { typedef union { struct { /** dma_en : WT; bitpos: [0]; default: 0; - * write 1 to triger dma to get 1 frame + * write 1 to trigger dma to get 1 frame */ uint32_t dma_en:1; /** dma_update_reg : R/W; bitpos: [1]; default: 0; @@ -1584,7 +1584,7 @@ typedef union { typedef union { struct { /** cam_en : R/W; bitpos: [0]; default: 0; - * write 1 to start recive camera data, write 0 to disable + * write 1 to start receive camera data, write 0 to disable */ uint32_t cam_en:1; /** cam_update_reg : R/W; bitpos: [1]; default: 0; @@ -1596,7 +1596,7 @@ typedef union { */ uint32_t cam_reset:1; /** cam_clk_inv : R/W; bitpos: [3]; default: 0; - * this bit configures the invertion of cam clk from pad. 0: not invert cam clk, 1: + * this bit configures the inversion of cam clk from pad. 0: not invert cam clk, 1: * invert cam clk */ uint32_t cam_clk_inv:1; @@ -1880,7 +1880,7 @@ typedef union { typedef union { struct { /** awb_mode : R/W; bitpos: [1:0]; default: 3; - * this field configures awb algo sel. 00: none sellected. 01: sel algo0. 10: sel + * this field configures awb algo sel. 00: none selected. 01: sel algo0. 10: sel * algo1. 11: sel both algo0 and algo1 */ uint32_t awb_mode:2; @@ -2627,6 +2627,19 @@ typedef union { uint32_t val; } isp_lut_rdata_reg_t; +/** Type of ae_block_mean register + * ae statistic result + */ +typedef union { + struct { + /** ae_lum : RO; bitpos: [31:0]; default: 0; + * this field represents the result of AE block + */ + uint8_t ae_b_mean[4]; + }; + uint32_t val; +} isp_ae_block_mean_reg_t; + /** Type of ae_block_mean_0 register * ae statistic result register 0 */ @@ -2790,7 +2803,6 @@ typedef union { }; uint32_t val; } isp_ae_block_mean_6_reg_t; - /** Type of af_sum_a register * result of sum of af window a */ @@ -3764,13 +3776,7 @@ typedef struct { volatile isp_ae_by_reg_t ae_by; volatile isp_ae_winpixnum_reg_t ae_winpixnum; volatile isp_ae_win_reciprocal_reg_t ae_win_reciprocal; - volatile isp_ae_block_mean_0_reg_t ae_block_mean_0; - volatile isp_ae_block_mean_1_reg_t ae_block_mean_1; - volatile isp_ae_block_mean_2_reg_t ae_block_mean_2; - volatile isp_ae_block_mean_3_reg_t ae_block_mean_3; - volatile isp_ae_block_mean_4_reg_t ae_block_mean_4; - volatile isp_ae_block_mean_5_reg_t ae_block_mean_5; - volatile isp_ae_block_mean_6_reg_t ae_block_mean_6; + volatile isp_ae_block_mean_reg_t ae_block_mean[7]; volatile isp_sharp_ctrl0_reg_t sharp_ctrl0; volatile isp_sharp_filter0_reg_t sharp_filter0; volatile isp_sharp_filter1_reg_t sharp_filter1; diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 9b39b54807..8115e8c5de 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -328,6 +328,9 @@ #define SOC_ISP_DVP_CTLR_NUMS 1U #define SOC_ISP_AF_CTLR_NUMS 1U #define SOC_ISP_AF_WINDOW_NUMS 3 +#define SOC_ISP_AE_CTLR_NUMS 1U +#define SOC_ISP_AE_BLOCK_X_NUMS 5 +#define SOC_ISP_AE_BLOCK_Y_NUMS 5 #define SOC_ISP_SHARE_CSI_BRG 1 #define SOC_ISP_BF_TEMPLATE_X_NUMS 3 #define SOC_ISP_BF_TEMPLATE_Y_NUMS 3