mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'feat/csi_error_event_v6.0' into 'release/v6.0'
Add MIPI-CSI error event (v6.0) See merge request espressif/esp-idf!50681
This commit is contained in:
@@ -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");
|
||||
}
|
||||
@@ -449,15 +461,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");
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_types.h>
|
||||
#include <stddef.h>
|
||||
#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
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#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
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "unity.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_cam_ctlr_csi.h"
|
||||
#include "esp_cam_ctlr.h"
|
||||
#include "esp_ldo_regulator.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "driver/isp.h"
|
||||
#include "example_sensor_init.h"
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
@@ -34,6 +33,11 @@
|
||||
static int new_buffer_count = 0;
|
||||
static int trans_finished_count = 0;
|
||||
|
||||
typedef struct {
|
||||
SemaphoreHandle_t sem;
|
||||
uint32_t flags;
|
||||
} test_csi_error_ctx_t;
|
||||
|
||||
static bool IRAM_ATTR camera_get_new_buffer(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data)
|
||||
{
|
||||
esp_cam_ctlr_trans_t *new_trans = (esp_cam_ctlr_trans_t *)user_data;
|
||||
@@ -49,6 +53,16 @@ static bool IRAM_ATTR camera_trans_finished(esp_cam_ctlr_handle_t handle, esp_ca
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IRAM_ATTR camera_error_callback(esp_cam_ctlr_handle_t handle, const esp_cam_ctlr_error_event_data_t *edata, void *user_data)
|
||||
{
|
||||
test_csi_error_ctx_t *ctx = (test_csi_error_ctx_t *)user_data;
|
||||
ctx->flags = edata->csi_host_err_evts;
|
||||
|
||||
BaseType_t high_task_woken = pdFALSE;
|
||||
xSemaphoreGiveFromISR(ctx->sem, &high_task_woken);
|
||||
return high_task_woken == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("TEST esp_cam on ov5647", "[csi][camera][ov5647]")
|
||||
{
|
||||
new_buffer_count = 0;
|
||||
@@ -149,3 +163,87 @@ TEST_CASE("TEST esp_cam on ov5647", "[csi][camera][ov5647]")
|
||||
|
||||
heap_caps_free(frame_buffer);
|
||||
}
|
||||
|
||||
TEST_CASE("TEST CSI error callback on lane mismatch", "[csi][camera][ov5647]")
|
||||
{
|
||||
size_t frame_buffer_size = TEST_MIPI_CSI_DISP_HRES *
|
||||
TEST_MIPI_CSI_DISP_VRES *
|
||||
TEST_RGB565_BITS_PER_PIXEL / 8;
|
||||
|
||||
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
|
||||
esp_ldo_channel_config_t ldo_config = {
|
||||
.chan_id = TEST_USED_LDO_CHAN_ID,
|
||||
.voltage_mv = TEST_USED_LDO_VOLTAGE_MV,
|
||||
};
|
||||
TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_config, &ldo_mipi_phy));
|
||||
|
||||
size_t frame_buffer_alignment = 0;
|
||||
TEST_ESP_OK(esp_cache_get_alignment(0, &frame_buffer_alignment));
|
||||
void *frame_buffer = heap_caps_aligned_calloc(frame_buffer_alignment, 1, frame_buffer_size,
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
TEST_ASSERT_NOT_NULL(frame_buffer);
|
||||
|
||||
esp_cam_ctlr_trans_t trans_data = {
|
||||
.buffer = frame_buffer,
|
||||
.buflen = frame_buffer_size,
|
||||
};
|
||||
|
||||
example_sensor_handle_t sensor_handle = {
|
||||
.sccb_handle = NULL,
|
||||
.i2c_bus_handle = NULL,
|
||||
};
|
||||
example_sensor_config_t sensor_config = {
|
||||
.i2c_port_num = I2C_NUM_0,
|
||||
.i2c_sda_io_num = TEST_MIPI_CSI_CAM_SCCB_SDA_IO,
|
||||
.i2c_scl_io_num = TEST_MIPI_CSI_CAM_SCCB_SCL_IO,
|
||||
.port = ESP_CAM_SENSOR_MIPI_CSI,
|
||||
.format_name = TEST_CAM_FORMAT,
|
||||
};
|
||||
|
||||
esp_cam_ctlr_csi_config_t csi_config = {
|
||||
.ctlr_id = 0,
|
||||
.h_res = TEST_MIPI_CSI_DISP_HRES,
|
||||
.v_res = TEST_MIPI_CSI_DISP_VRES,
|
||||
.lane_bit_rate_mbps = TEST_MIPI_CSI_LANE_BITRATE_MBPS,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RAW8,
|
||||
.output_data_color_type = CAM_CTLR_COLOR_RAW8,
|
||||
.data_lane_num = 1,
|
||||
.byte_swap_en = false,
|
||||
.queue_items = 1,
|
||||
};
|
||||
esp_cam_ctlr_handle_t cam_handle = NULL;
|
||||
TEST_ESP_OK(esp_cam_new_csi_ctlr(&csi_config, &cam_handle));
|
||||
|
||||
test_csi_error_ctx_t error_ctx = {};
|
||||
error_ctx.sem = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(error_ctx.sem);
|
||||
|
||||
esp_cam_ctlr_evt_cbs_t cbs = {
|
||||
.on_trans_finished = camera_trans_finished,
|
||||
.on_error = camera_error_callback,
|
||||
};
|
||||
TEST_ESP_OK(esp_cam_ctlr_register_event_callbacks(cam_handle, &cbs, &error_ctx));
|
||||
|
||||
TEST_ASSERT_EQUAL(pdFALSE, xSemaphoreTake(error_ctx.sem, pdMS_TO_TICKS(100)));
|
||||
|
||||
example_sensor_init(&sensor_config, &sensor_handle);
|
||||
|
||||
TEST_ESP_OK(esp_cam_ctlr_enable(cam_handle));
|
||||
TEST_ESP_OK(esp_cam_ctlr_start(cam_handle));
|
||||
TEST_ESP_OK(esp_cam_ctlr_receive(cam_handle, &trans_data, ESP_CAM_CTLR_MAX_DELAY));
|
||||
|
||||
bool error_reported = xSemaphoreTake(error_ctx.sem, pdMS_TO_TICKS(1000)) == pdTRUE;
|
||||
uint32_t err_flags = error_ctx.flags;
|
||||
|
||||
TEST_ESP_OK(esp_cam_ctlr_stop(cam_handle));
|
||||
TEST_ESP_OK(esp_cam_ctlr_disable(cam_handle));
|
||||
TEST_ESP_OK(esp_cam_ctlr_del(cam_handle));
|
||||
|
||||
vSemaphoreDelete(error_ctx.sem);
|
||||
TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy));
|
||||
example_sensor_deinit(sensor_handle);
|
||||
heap_caps_free(frame_buffer);
|
||||
|
||||
TEST_ASSERT_TRUE(error_reported);
|
||||
TEST_ASSERT_NOT_EQUAL(0, err_flags);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ from pytest_embedded_idf.utils import idf_parametrize
|
||||
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
|
||||
def test_csi_driver(case_tester) -> None: # type: ignore
|
||||
for case in case_tester.test_menu:
|
||||
if 'TEST esp_cam on ov5647' in case.name:
|
||||
if 'camera' in case.groups:
|
||||
continue
|
||||
case_tester.run_normal_case(case=case, reset=True)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#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
|
||||
|
||||
Reference in New Issue
Block a user