diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index b93c5bb2f66c6..19ad0e2bc797e 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -5,6 +5,7 @@ zephyr_library() zephyr_library_sources(video_common.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_CSI video_mcux_csi.c) +zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_MIPI_CSI2RX video_mcux_mipi_csi2rx.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_SW_GENERATOR video_sw_generator.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_MT9M114 mt9m114.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_OV7725 ov7725.c) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 9f44944150fc9..787311622e0cc 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -33,6 +33,8 @@ config VIDEO_BUFFER_POOL_ALIGN source "drivers/video/Kconfig.mcux_csi" +source "drivers/video/Kconfig.mcux_mipi_csi2rx" + source "drivers/video/Kconfig.sw_generator" source "drivers/video/Kconfig.mt9m114" diff --git a/drivers/video/Kconfig.mcux_mipi_csi2rx b/drivers/video/Kconfig.mcux_mipi_csi2rx new file mode 100644 index 0000000000000..f9bf2f19f1efe --- /dev/null +++ b/drivers/video/Kconfig.mcux_mipi_csi2rx @@ -0,0 +1,10 @@ +# NXP MIPI CSI-2 Rx driver configuration option + +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config VIDEO_MCUX_MIPI_CSI2RX + bool "NXP MIPI CSI-2 Rx driver" + default y + depends on DT_HAS_NXP_MIPI_CSI2RX_ENABLED + select VIDEO_MCUX_CSI diff --git a/drivers/video/video_mcux_mipi_csi2rx.c b/drivers/video/video_mcux_mipi_csi2rx.c new file mode 100644 index 0000000000000..6d9dc5729edc3 --- /dev/null +++ b/drivers/video/video_mcux_mipi_csi2rx.c @@ -0,0 +1,218 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_mipi_csi2rx + +#include + +#include +#include + +#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#include +LOG_MODULE_REGISTER(mipi_csi); + +/* + * Two data lanes are set by default as 2-lanes camera sensors are + * more common and more performant but single lane is also supported. + */ +#define DEFAULT_MIPI_CSI_NUM_LANES 2 +#define DEFAULT_CAMERA_FRAME_RATE 30 + +struct mipi_csi2rx_config { + const MIPI_CSI2RX_Type *base; + const struct device *sensor_dev; +}; + +struct mipi_csi2rx_data { + csi2rx_config_t csi2rxConfig; +}; + +static int mipi_csi2rx_set_fmt(const struct device *dev, enum video_endpoint_id ep, + struct video_format *fmt) +{ + const struct mipi_csi2rx_config *config = dev->config; + struct mipi_csi2rx_data *drv_data = dev->data; + csi2rx_config_t csi2rxConfig = {0}; + uint8_t i = 0; + + /* + * Initialize the MIPI CSI2 + * + * From D-PHY specification, the T-HSSETTLE should in the range of 85ns+6*UI to 145ns+10*UI + * UI is Unit Interval, equal to the duration of any HS state on the Clock Lane + * + * T-HSSETTLE = csi2rxConfig.tHsSettle_EscClk * (Tperiod of RxClkInEsc) + * + * csi2rxConfig.tHsSettle_EscClk setting for camera: + * + * Resolution | frame rate | T_HS_SETTLE + * ============================================= + * 720P | 30 | 0x12 + * --------------------------------------------- + * 720P | 15 | 0x17 + * --------------------------------------------- + * VGA | 30 | 0x1F + * --------------------------------------------- + * VGA | 15 | 0x24 + * --------------------------------------------- + * QVGA | 30 | 0x1F + * --------------------------------------------- + * QVGA | 15 | 0x24 + * --------------------------------------------- + */ + static const uint32_t csi2rxHsSettle[][4] = { + { + 1280, + 720, + 30, + 0x12, + }, + { + 1280, + 720, + 15, + 0x17, + }, + { + 640, + 480, + 30, + 0x1F, + }, + { + 640, + 480, + 15, + 0x24, + }, + { + 320, + 240, + 30, + 0x1F, + }, + { + 320, + 240, + 15, + 0x24, + }, + }; + + csi2rxConfig.laneNum = DEFAULT_MIPI_CSI_NUM_LANES; + + for (i = 0; i < ARRAY_SIZE(csi2rxHsSettle); i++) { + if ((fmt->width == csi2rxHsSettle[i][0]) && (fmt->height == csi2rxHsSettle[i][1]) && + (DEFAULT_CAMERA_FRAME_RATE == csi2rxHsSettle[i][2])) { + csi2rxConfig.tHsSettle_EscClk = csi2rxHsSettle[i][3]; + break; + } + } + + if (i == ARRAY_SIZE(csi2rxHsSettle)) { + LOG_ERR("Unsupported resolution"); + return -ENOTSUP; + } + + drv_data->csi2rxConfig = csi2rxConfig; + + if (video_set_format(config->sensor_dev, ep, fmt)) { + return -EIO; + } + + return 0; +} + +static int mipi_csi2rx_get_fmt(const struct device *dev, enum video_endpoint_id ep, + struct video_format *fmt) +{ + const struct mipi_csi2rx_config *config = dev->config; + + if (fmt == NULL || ep != VIDEO_EP_OUT) { + return -EINVAL; + } + + if (video_get_format(config->sensor_dev, ep, fmt)) { + return -EIO; + } + + return 0; +} + +static int mipi_csi2rx_stream_start(const struct device *dev) +{ + const struct mipi_csi2rx_config *config = dev->config; + struct mipi_csi2rx_data *drv_data = dev->data; + + CSI2RX_Init((MIPI_CSI2RX_Type *)config->base, &drv_data->csi2rxConfig); + + if (video_stream_start(config->sensor_dev)) { + return -EIO; + } + + return 0; +} + +static int mipi_csi2rx_stream_stop(const struct device *dev) +{ + const struct mipi_csi2rx_config *config = dev->config; + + if (video_stream_stop(config->sensor_dev)) { + return -EIO; + } + + CSI2RX_Deinit((MIPI_CSI2RX_Type *)config->base); + + return 0; +} + +static int mipi_csi2rx_get_caps(const struct device *dev, enum video_endpoint_id ep, + struct video_caps *caps) +{ + const struct mipi_csi2rx_config *config = dev->config; + + if (ep != VIDEO_EP_OUT) { + return -EINVAL; + } + + /* Just forward to sensor dev for now */ + return video_get_caps(config->sensor_dev, ep, caps); +} + +static const struct video_driver_api mipi_csi2rx_driver_api = { + .get_caps = mipi_csi2rx_get_caps, + .get_format = mipi_csi2rx_get_fmt, + .set_format = mipi_csi2rx_set_fmt, + .stream_start = mipi_csi2rx_stream_start, + .stream_stop = mipi_csi2rx_stream_stop, +}; + +static int mipi_csi2rx_init(const struct device *dev) +{ + const struct mipi_csi2rx_config *config = dev->config; + + /* Check if there is any sensor device */ + if (!device_is_ready(config->sensor_dev)) { + return -ENODEV; + } + + return 0; +} + +#define MIPI_CSI2RX_INIT(n) \ + static struct mipi_csi2rx_data mipi_csi2rx_data_##n; \ + \ + static const struct mipi_csi2rx_config mipi_csi2rx_config_##n = { \ + .base = (MIPI_CSI2RX_Type *)DT_INST_REG_ADDR(n), \ + .sensor_dev = DEVICE_DT_GET(DT_INST_PHANDLE(n, sensor)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, &mipi_csi2rx_init, NULL, &mipi_csi2rx_data_##n, \ + &mipi_csi2rx_config_##n, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \ + &mipi_csi2rx_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(MIPI_CSI2RX_INIT) diff --git a/dts/arm/nxp/nxp_rt11xx.dtsi b/dts/arm/nxp/nxp_rt11xx.dtsi index 03611213830ae..fc0a169080010 100644 --- a/dts/arm/nxp/nxp_rt11xx.dtsi +++ b/dts/arm/nxp/nxp_rt11xx.dtsi @@ -808,6 +808,35 @@ reg = <0x40800000 0x4000>; interrupts = <56 1>; status = "disabled"; + source = <&mipi_csi2rx>; + + port { + csi_ep_in: endpoint { + remote-endpoint = <&mipi_csi2rx_ep_out>; + }; + }; + }; + + mipi_csi2rx: mipi_csi2rx@40810000 { + compatible = "nxp,mipi-csi2rx"; + reg = <0x40810000 0x200>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + mipi_csi2rx_ep_out: endpoint { + remote-endpoint = <&csi_ep_in>; + }; + }; + + port@1 { + reg = <1>; + }; + }; }; flexcan1: can@400c4000 { diff --git a/dts/bindings/video/nxp,mipi-csi2rx.yaml b/dts/bindings/video/nxp,mipi-csi2rx.yaml new file mode 100644 index 0000000000000..1726d63b3299e --- /dev/null +++ b/dts/bindings/video/nxp,mipi-csi2rx.yaml @@ -0,0 +1,17 @@ +# +# Copyright 2024 NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: NXP MIPI CSI-2 Rx interface + +compatible: "nxp,mipi-csi2rx" + +include: [base.yaml] + +properties: + sensor: + required: true + type: phandle + description: the connected camera sensor diff --git a/soc/nxp/imxrt/imxrt11xx/soc.c b/soc/nxp/imxrt/imxrt11xx/soc.c index 0a1eecc2cc921..642708af9871d 100644 --- a/soc/nxp/imxrt/imxrt11xx/soc.c +++ b/soc/nxp/imxrt/imxrt11xx/soc.c @@ -456,6 +456,27 @@ static ALWAYS_INLINE void clock_init(void) CLOCK_SetRootClock(kCLOCK_Root_Lpspi1, &rootCfg); #endif +#ifdef CONFIG_VIDEO_MCUX_MIPI_CSI2RX + /* MIPI CSI-2 Rx connects to CSI via Video Mux */ + CLOCK_EnableClock(kCLOCK_Video_Mux); + VIDEO_MUX->VID_MUX_CTRL.SET = VIDEO_MUX_VID_MUX_CTRL_CSI_SEL_MASK; + + /* Configure MIPI CSI-2 Rx clocks */ + rootCfg.div = 8; + rootCfg.mux = kCLOCK_CSI2_ClockRoot_MuxSysPll3Out; + CLOCK_SetRootClock(kCLOCK_Root_Csi2, &rootCfg); + + rootCfg.mux = kCLOCK_CSI2_ESC_ClockRoot_MuxSysPll3Out; + CLOCK_SetRootClock(kCLOCK_Root_Csi2_Esc, &rootCfg); + + rootCfg.mux = kCLOCK_CSI2_UI_ClockRoot_MuxSysPll3Out; + CLOCK_SetRootClock(kCLOCK_Root_Csi2_Ui, &rootCfg); + + /* Enable power domain for MIPI CSI-2 */ + PGMC_BPC4->BPC_POWER_CTRL |= (PGMC_BPC_BPC_POWER_CTRL_PSW_ON_SOFT_MASK | + PGMC_BPC_BPC_POWER_CTRL_ISO_OFF_SOFT_MASK); +#endif + #ifdef CONFIG_CAN_MCUX_FLEXCAN #if DT_NODE_HAS_STATUS(DT_NODELABEL(flexcan1), okay) /* Configure CAN1 using Osc48MDiv2 */ diff --git a/tests/drivers/build_all/video/mimxrt1170_evk_mimxrt1176_cm7.overlay b/tests/drivers/build_all/video/mimxrt1170_evk_mimxrt1176_cm7.overlay new file mode 100644 index 0000000000000..99542ad4c7825 --- /dev/null +++ b/tests/drivers/build_all/video/mimxrt1170_evk_mimxrt1176_cm7.overlay @@ -0,0 +1,56 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + * + * Names in this file should be chosen in a way that won't conflict + * with real-world devicetree nodes, to allow these tests to run on + * (and be extended to test) real hardware. + */ + +/ { + test { + #address-cells = <1>; + #size-cells = <1>; + + test_gpio: gpio@deadbeef { + compatible = "vnd,gpio"; + gpio-controller; + reg = <0xdeadbeef 0x1000>; + #gpio-cells = <0x2>; + status = "okay"; + }; + + test_i2c: i2c@11112222 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "vnd,i2c"; + reg = <0x11112222 0x1000>; + status = "okay"; + clock-frequency = <100000>; + + test_i2c_ov5640: ov5640@1 { + compatible = "ovti,ov5640"; + reg = <0x1>; + reset-gpios = <&test_gpio 0 0>; + powerdown-gpios = <&test_gpio 1 0>; + }; + }; + + test_csi: csi@22223333 { + compatible = "nxp,imx-csi"; + reg = <0x22223333 0x4000>; + status = "okay"; + interrupt-parent = <&nvic>; + interrupts = <56 1>; + source = <&test_mipi_csi2rx>; + }; + + test_mipi_csi2rx: mipi_csi2rx@33334444 { + compatible = "nxp,mipi-csi2rx"; + reg = <0x33334444 0x200>; + status = "okay"; + sensor = <&test_i2c_ov5640>; + }; + }; +}; diff --git a/tests/drivers/build_all/video/testcase.yaml b/tests/drivers/build_all/video/testcase.yaml index 2acb801b9c643..49acda27c13ef 100644 --- a/tests/drivers/build_all/video/testcase.yaml +++ b/tests/drivers/build_all/video/testcase.yaml @@ -16,3 +16,7 @@ tests: platform_allow: - mimxrt1064_evk - mimxrt1170_evk/mimxrt1176/cm7 + drivers.video.mcux_mipi_csi2rx.build: + platform_allow: + - mimxrt1170_evk/mimxrt1176/cm7 + - mimxrt1170_evk@B/mimxrt1176/cm7