From 666326e55cc27aa2b13cd49304f4257d623b95e1 Mon Sep 17 00:00:00 2001 From: gaoxu Date: Mon, 13 Jul 2026 15:10:16 +0800 Subject: [PATCH] feat(csi): add MIPI-CSI host error event --- .../esp_driver_cam/csi/src/esp_cam_ctlr_csi.c | 69 ++++++++++ .../csi/src/esp_cam_ctlr_csi_internal.h | 10 +- .../include/esp_cam_ctlr_types.h | 39 +++++- .../esp32p4/include/hal/mipi_csi_host_ll.h | 118 +++++++++++++++++- .../esp_hal_cam/esp32p4/mipi_csi_periph.c | 9 +- .../esp_hal_cam/include/hal/mipi_csi_periph.h | 16 ++- 6 files changed, 250 insertions(+), 11 deletions(-) diff --git a/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi.c b/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi.c index fea9748a9b1..8568c5a8d57 100644 --- a/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi.c +++ b/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi.c @@ -19,6 +19,7 @@ #include "esp_cam_ctlr_interface.h" #include "esp_cam_ctlr_csi_internal.h" #include "hal/mipi_csi_ll.h" +#include "hal/mipi_csi_periph.h" #include "hal/color_hal.h" #include "hal/efuse_hal.h" #include "soc/chip_revision.h" @@ -30,8 +31,12 @@ #if CONFIG_CAM_CTLR_MIPI_CSI_ISR_CACHE_SAFE #define CSI_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) +#define CSI_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM) +#define CSI_ISR_ATTR IRAM_ATTR #else #define CSI_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT +#define CSI_INTR_ALLOC_FLAGS ESP_INTR_FLAG_LOWMED +#define CSI_ISR_ATTR #endif typedef struct csi_platform_t { @@ -43,6 +48,7 @@ static const char *TAG = "CSI"; static csi_platform_t s_platform; static bool csi_dma_trans_done_callback(dw_gdma_channel_handle_t chan, const dw_gdma_trans_done_event_data_t *event_data, void *user_data); +static void s_csi_default_isr(void *arg); static esp_err_t s_del_csi_ctlr(csi_controller_t *ctlr); static esp_err_t s_ctlr_del(esp_cam_ctlr_t *cam_ctlr); static esp_err_t s_register_event_callbacks(esp_cam_ctlr_handle_t handle, const esp_cam_ctlr_evt_cbs_t *cbs, void *user_data); @@ -296,6 +302,12 @@ esp_err_t s_del_csi_ctlr(csi_controller_t *ctlr) } #endif // CONFIG_PM_ENABLE + if (ctlr->intr_handle) { + mipi_csi_host_ll_enable_intr(ctlr->hal.host_dev, MIPI_CSI_HOST_LL_INTR_ERR_ALL, false); + ESP_RETURN_ON_ERROR(esp_intr_free(ctlr->intr_handle), TAG, "failed to free csi host interrupt"); + ctlr->intr_handle = NULL; + } + if (ctlr->dma_chan) { ESP_RETURN_ON_ERROR(dw_gdma_del_channel(ctlr->dma_chan), TAG, "failed to delete dwgdma channel"); } @@ -447,15 +459,72 @@ esp_err_t s_register_event_callbacks(esp_cam_ctlr_handle_t handle, const esp_cam if (cbs->on_trans_finished) { ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_trans_finished), ESP_ERR_INVALID_ARG, TAG, "on_trans_finished callback not in IRAM"); } + if (cbs->on_error) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_error), ESP_ERR_INVALID_ARG, TAG, "on_error callback not in IRAM"); + } + if (user_data) { + ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user_data not in internal RAM"); + } #endif + bool enable_error_intr = false; + + // The error interrupt service is lazy installed, only when an on_error callback is registered. + if (cbs->on_error && !ctlr->intr_handle) { + ESP_RETURN_ON_ERROR(esp_intr_alloc(soc_mipi_csi_signals[ctlr->csi_id].host_irq_id, CSI_INTR_ALLOC_FLAGS, + s_csi_default_isr, ctlr, &ctlr->intr_handle), + TAG, "failed to allocate csi host interrupt"); + enable_error_intr = true; + } + ctlr->cbs.on_get_new_trans = cbs->on_get_new_trans; ctlr->cbs.on_trans_finished = cbs->on_trans_finished; + ctlr->cbs.on_error = cbs->on_error; ctlr->cbs_user_data = user_data; + if (enable_error_intr) { + // clear any stale status, then unmask all error interrupts + mipi_csi_host_ll_clear_intr_status(ctlr->hal.host_dev, MIPI_CSI_HOST_LL_INTR_ERR_ALL); + mipi_csi_host_ll_enable_intr(ctlr->hal.host_dev, MIPI_CSI_HOST_LL_INTR_ERR_ALL, true); + } + return ESP_OK; } +static void CSI_ISR_ATTR s_csi_default_isr(void *arg) +{ + bool need_yield = false; + csi_controller_t *ctlr = (csi_controller_t *)arg; + + uint32_t status = mipi_csi_host_ll_get_intr_status(ctlr->hal.host_dev); + // clear the detected error sources before invoking the callback + mipi_csi_host_ll_clear_intr_status(ctlr->hal.host_dev, status); + + if (ctlr->cbs.on_error) { + esp_cam_ctlr_error_event_data_t edata = {}; + if (status & (MIPI_CSI_HOST_LL_INTR_PHY_FATAL | MIPI_CSI_HOST_LL_INTR_PHY)) { + edata.csi_host_err_evts |= ESP_CAM_CTLR_CSI_HOST_ERR_PHY; + } + if (status & (MIPI_CSI_HOST_LL_INTR_PKT_FATAL | MIPI_CSI_HOST_LL_INTR_ECC_CORRECTED)) { + edata.csi_host_err_evts |= ESP_CAM_CTLR_CSI_HOST_ERR_PACKET; + } + if (status & (MIPI_CSI_HOST_LL_INTR_BNDRY_FRAME_FATAL | MIPI_CSI_HOST_LL_INTR_SEQ_FRAME_FATAL)) { + edata.csi_host_err_evts |= ESP_CAM_CTLR_CSI_HOST_ERR_FRAME; + } + if (status & (MIPI_CSI_HOST_LL_INTR_CRC_FRAME_FATAL | MIPI_CSI_HOST_LL_INTR_PLD_CRC_FATAL)) { + edata.csi_host_err_evts |= ESP_CAM_CTLR_CSI_HOST_ERR_CRC; + } + if (status & MIPI_CSI_HOST_LL_INTR_DATA_ID) { + edata.csi_host_err_evts |= ESP_CAM_CTLR_CSI_HOST_ERR_DATA_ID; + } + need_yield = ctlr->cbs.on_error(&(ctlr->base), &edata, ctlr->cbs_user_data); + } + + if (need_yield) { + portYIELD_FROM_ISR(); + } +} + esp_err_t s_csi_ctlr_enable(esp_cam_ctlr_handle_t handle) { ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); diff --git a/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi_internal.h b/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi_internal.h index 2e6dd05a333..9f4d7885108 100644 --- a/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi_internal.h +++ b/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi_internal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,13 +8,10 @@ #include #include -#include +#include #include "sdkconfig.h" -#include "esp_attr.h" -#include "esp_log.h" -#include "esp_check.h" -#include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" +#include "esp_intr_alloc.h" #include "esp_cam_ctlr_csi.h" #include "hal/mipi_csi_hal.h" #include "hal/mipi_csi_types.h" @@ -67,6 +64,7 @@ struct csi_controller_t { void *cbs_user_data; //callback userdata dw_gdma_channel_handle_t dma_chan; //dwgdma channel handle size_t csi_transfer_size; //csi transfer size for dwgdma + intr_handle_t intr_handle; //csi host error interrupt handle #if CONFIG_PM_ENABLE esp_pm_lock_handle_t pm_lock; //Power management lock #endif diff --git a/components/esp_driver_cam/include/esp_cam_ctlr_types.h b/components/esp_driver_cam/include/esp_cam_ctlr_types.h index cc900f67bd8..b513ee2540c 100644 --- a/components/esp_driver_cam/include/esp_cam_ctlr_types.h +++ b/components/esp_driver_cam/include/esp_cam_ctlr_types.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,7 +8,7 @@ #include #include -#include "esp_err.h" +#include #ifdef __cplusplus extern "C" { @@ -33,6 +33,26 @@ typedef struct { size_t received_size; /*!< Received size. This value is written by the driver and indicates the actual received size. */ } esp_cam_ctlr_trans_t; +/** + * @brief ESP CAM controller CSI host error flags. + * + * @note These flags summarize the CSI host error type(s) and avoid exposing hardware bit layout. + */ +typedef enum { + ESP_CAM_CTLR_CSI_HOST_ERR_PHY = (1U << 0), /*!< PHY error. */ + ESP_CAM_CTLR_CSI_HOST_ERR_PACKET = (1U << 1), /*!< Packet error. */ + ESP_CAM_CTLR_CSI_HOST_ERR_FRAME = (1U << 2), /*!< Frame boundary or sequence error. */ + ESP_CAM_CTLR_CSI_HOST_ERR_CRC = (1U << 3), /*!< Frame or payload CRC error. */ + ESP_CAM_CTLR_CSI_HOST_ERR_DATA_ID = (1U << 4), /*!< Unrecognized or unsupported data type detected. */ +} esp_cam_ctlr_csi_host_error_t; + +/** + * @brief ESP CAM controller "error" event data. + */ +typedef struct { + uint32_t csi_host_err_evts; /*!< Bitwise OR of `esp_cam_ctlr_csi_host_error_t`. */ +} esp_cam_ctlr_error_event_data_t; + /** * @brief Event callbacks for the ESP CAM controller. */ @@ -59,6 +79,21 @@ typedef struct { */ bool (*on_trans_finished)(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data); + /** + * @brief Callback for when an error is detected. + * + * @note This callback is invoked from ISR context, so it must not call any blocking APIs. + * When the controller's ISR cache-safe option is enabled, this callback and its + * user data must be placed in internal RAM. + * + * @param[in] handle ESP CAM controller handle. + * @param[in] edata Error event data. + * @param[in] user_data User-registered data. + * + * @return Whether a high-priority task is woken up by this function. + */ + bool (*on_error)(esp_cam_ctlr_handle_t handle, const esp_cam_ctlr_error_event_data_t *edata, void *user_data); + } esp_cam_ctlr_evt_cbs_t; #ifdef __cplusplus diff --git a/components/esp_hal_cam/esp32p4/include/hal/mipi_csi_host_ll.h b/components/esp_hal_cam/esp32p4/include/hal/mipi_csi_host_ll.h index 6d52d7a2cd5..8494d08bcbc 100644 --- a/components/esp_hal_cam/esp32p4/include/hal/mipi_csi_host_ll.h +++ b/components/esp_hal_cam/esp32p4/include/hal/mipi_csi_host_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,6 +20,32 @@ extern "C" { #define MIPI_CSI_HOST_LL_LANE_NUM_MAX 2 +/** + * @brief MIPI CSI host interrupt categories, bit layout aligned with the `int_st_main` register + */ +typedef enum { + MIPI_CSI_HOST_LL_INTR_PHY_FATAL = (1 << 0), /*!< PHY fatal error, e.g. data lane Start-of-Transmission sync error (ErrSotSyncHS) */ + MIPI_CSI_HOST_LL_INTR_PKT_FATAL = (1 << 1), /*!< Packet fatal error, e.g. header double-bit ECC error or shorter payload */ + MIPI_CSI_HOST_LL_INTR_BNDRY_FRAME_FATAL = (1 << 2), /*!< Frame boundary error, e.g. unpaired Frame Start / Frame End */ + MIPI_CSI_HOST_LL_INTR_SEQ_FRAME_FATAL = (1 << 3), /*!< Frame sequence error */ + MIPI_CSI_HOST_LL_INTR_CRC_FRAME_FATAL = (1 << 4), /*!< A frame containing at least one CRC error */ + MIPI_CSI_HOST_LL_INTR_PLD_CRC_FATAL = (1 << 5), /*!< Payload CRC error */ + MIPI_CSI_HOST_LL_INTR_DATA_ID = (1 << 6), /*!< Unrecognized or unsupported data type detected */ + MIPI_CSI_HOST_LL_INTR_ECC_CORRECTED = (1 << 7), /*!< Header single-bit ECC error (corrected by hardware) */ + MIPI_CSI_HOST_LL_INTR_PHY = (1 << 16), /*!< PHY error, e.g. Start-of-Transmission error (ErrSotHS) or escape entry error (ErrEsc) */ +} mipi_csi_host_ll_intr_t; + +/// Mask covering all MIPI CSI host error interrupts +#define MIPI_CSI_HOST_LL_INTR_ERR_ALL (MIPI_CSI_HOST_LL_INTR_PHY_FATAL | \ + MIPI_CSI_HOST_LL_INTR_PKT_FATAL | \ + MIPI_CSI_HOST_LL_INTR_BNDRY_FRAME_FATAL | \ + MIPI_CSI_HOST_LL_INTR_SEQ_FRAME_FATAL | \ + MIPI_CSI_HOST_LL_INTR_CRC_FRAME_FATAL | \ + MIPI_CSI_HOST_LL_INTR_PLD_CRC_FATAL | \ + MIPI_CSI_HOST_LL_INTR_DATA_ID | \ + MIPI_CSI_HOST_LL_INTR_ECC_CORRECTED | \ + MIPI_CSI_HOST_LL_INTR_PHY) + /** * @brief Enable CSI host reset output * @@ -64,6 +90,96 @@ static inline void mipi_csi_host_ll_enable_scrambling(csi_host_dev_t *dev, bool dev->scrambling.scramble_enable = en; } +/** + * @brief Enable or disable the MIPI CSI host error interrupts + * + * @param dev Pointer to the CSI Host controller register base address + * @param mask Interrupt categories to operate on, see `mipi_csi_host_ll_intr_t` + * @param en True to enable (unmask), false to disable (mask) + */ +static inline void mipi_csi_host_ll_enable_intr(csi_host_dev_t *dev, uint32_t mask, bool en) +{ + if (mask & MIPI_CSI_HOST_LL_INTR_PHY_FATAL) { + dev->int_msk_phy_fatal.val = en ? 0x3 : 0x0; + } + if (mask & MIPI_CSI_HOST_LL_INTR_PKT_FATAL) { + dev->int_msk_pkt_fatal.val = en ? 0x3 : 0x0; + } + if (mask & MIPI_CSI_HOST_LL_INTR_BNDRY_FRAME_FATAL) { + dev->int_msk_bndry_frame_fatal.val = en ? 0xffff : 0x0; + } + if (mask & MIPI_CSI_HOST_LL_INTR_SEQ_FRAME_FATAL) { + dev->int_msk_seq_frame_fatal.val = en ? 0xffff : 0x0; + } + if (mask & MIPI_CSI_HOST_LL_INTR_CRC_FRAME_FATAL) { + dev->int_msk_crc_frame_fatal.val = en ? 0xffff : 0x0; + } + if (mask & MIPI_CSI_HOST_LL_INTR_PLD_CRC_FATAL) { + dev->int_msk_pld_crc_fatal.val = en ? 0xffff : 0x0; + } + if (mask & MIPI_CSI_HOST_LL_INTR_DATA_ID) { + dev->int_msk_data_id.val = en ? 0xffff : 0x0; + } + if (mask & MIPI_CSI_HOST_LL_INTR_ECC_CORRECTED) { + dev->int_msk_ecc_corrected.val = en ? 0xffff : 0x0; + } + if (mask & MIPI_CSI_HOST_LL_INTR_PHY) { + dev->int_msk_phy.val = en ? 0x30003 : 0x0; + } +} + +/** + * @brief Get the MIPI CSI host top-level interrupt status + * + * @param dev Pointer to the CSI Host controller register base address + * + * @return The top-level interrupt status, a combination of `mipi_csi_host_ll_intr_t` + */ +static inline uint32_t mipi_csi_host_ll_get_intr_status(csi_host_dev_t *dev) +{ + return dev->int_st_main.val; +} + +/** + * @brief Clear the MIPI CSI host interrupt status + * + * @note The per-category status registers are clear-on-read, reading them also clears the + * corresponding bit in the `int_st_main` register. + * + * @param dev Pointer to the CSI Host controller register base address + * @param mask Interrupt categories to clear, see `mipi_csi_host_ll_intr_t` + */ +static inline void mipi_csi_host_ll_clear_intr_status(csi_host_dev_t *dev, uint32_t mask) +{ + if (mask & MIPI_CSI_HOST_LL_INTR_PHY_FATAL) { + (void)dev->int_st_phy_fatal.val; + } + if (mask & MIPI_CSI_HOST_LL_INTR_PKT_FATAL) { + (void)dev->int_st_pkt_fatal.val; + } + if (mask & MIPI_CSI_HOST_LL_INTR_BNDRY_FRAME_FATAL) { + (void)dev->int_st_bndry_frame_fatal.val; + } + if (mask & MIPI_CSI_HOST_LL_INTR_SEQ_FRAME_FATAL) { + (void)dev->int_st_seq_frame_fatal.val; + } + if (mask & MIPI_CSI_HOST_LL_INTR_CRC_FRAME_FATAL) { + (void)dev->int_st_crc_frame_fatal.val; + } + if (mask & MIPI_CSI_HOST_LL_INTR_PLD_CRC_FATAL) { + (void)dev->int_st_pld_crc_fatal.val; + } + if (mask & MIPI_CSI_HOST_LL_INTR_DATA_ID) { + (void)dev->int_st_data_id.val; + } + if (mask & MIPI_CSI_HOST_LL_INTR_ECC_CORRECTED) { + (void)dev->int_st_ecc_corrected.val; + } + if (mask & MIPI_CSI_HOST_LL_INTR_PHY) { + (void)dev->int_st_phy.val; + } +} + #ifdef __cplusplus } #endif diff --git a/components/esp_hal_cam/esp32p4/mipi_csi_periph.c b/components/esp_hal_cam/esp32p4/mipi_csi_periph.c index ab5cd2d4c77..3d481f39d6a 100644 --- a/components/esp_hal_cam/esp32p4/mipi_csi_periph.c +++ b/components/esp_hal_cam/esp32p4/mipi_csi_periph.c @@ -1,10 +1,11 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "hal/mipi_csi_periph.h" +#include "soc/interrupts.h" const soc_mipi_csi_phy_pll_freq_range_t soc_mipi_csi_phy_pll_ranges[] = { {90, 99, 0x00}, // [90,100) Mbps @@ -48,3 +49,9 @@ const soc_mipi_csi_phy_pll_freq_range_t soc_mipi_csi_phy_pll_ranges[] = { }; const size_t num_of_soc_mipi_csi_phy_pll_ranges = sizeof(soc_mipi_csi_phy_pll_ranges) / sizeof(soc_mipi_csi_phy_pll_freq_range_t); + +const soc_mipi_csi_signal_desc_t soc_mipi_csi_signals[MIPI_CSI_LL_HOST_CTLR_NUMS] = { + [0] = { + .host_irq_id = ETS_CSI_INTR_SOURCE, + } +}; diff --git a/components/esp_hal_cam/include/hal/mipi_csi_periph.h b/components/esp_hal_cam/include/hal/mipi_csi_periph.h index 40283c0f991..a97cb4a9f3d 100644 --- a/components/esp_hal_cam/include/hal/mipi_csi_periph.h +++ b/components/esp_hal_cam/include/hal/mipi_csi_periph.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,11 +8,17 @@ #include #include +#include "soc/soc_caps.h" +#if SOC_HAS(MIPI_CSI) +#include "hal/mipi_csi_ll.h" +#endif #ifdef __cplusplus extern "C" { #endif +#if SOC_HAS(MIPI_CSI) + /** * @brief MIPI CSI PHY PLL frequency range */ @@ -25,6 +31,14 @@ typedef struct { extern const soc_mipi_csi_phy_pll_freq_range_t soc_mipi_csi_phy_pll_ranges[]; extern const size_t num_of_soc_mipi_csi_phy_pll_ranges; +typedef struct { + const int host_irq_id; // interrupt source ID for MIPI CSI Host +} soc_mipi_csi_signal_desc_t; + +extern const soc_mipi_csi_signal_desc_t soc_mipi_csi_signals[MIPI_CSI_LL_HOST_CTLR_NUMS]; + +#endif // SOC_HAS(MIPI_CSI) + #ifdef __cplusplus } #endif