diff --git a/boards/madmachine/mm_swiftio/mm_swiftio.dts b/boards/madmachine/mm_swiftio/mm_swiftio.dts index b9994d1e8c11..9489a7c6d128 100644 --- a/boards/madmachine/mm_swiftio/mm_swiftio.dts +++ b/boards/madmachine/mm_swiftio/mm_swiftio.dts @@ -192,7 +192,7 @@ &csi { status = "okay"; - sensor = <&ov7725>; + sdev = <&ov7725>; pinctrl-0 = <&pinmux_csi>; pinctrl-names = "default"; diff --git a/boards/nxp/mimxrt1064_evk/mimxrt1064_evk.dts b/boards/nxp/mimxrt1064_evk/mimxrt1064_evk.dts index f6b6d5c74b26..dbfce32a5d7c 100644 --- a/boards/nxp/mimxrt1064_evk/mimxrt1064_evk.dts +++ b/boards/nxp/mimxrt1064_evk/mimxrt1064_evk.dts @@ -149,18 +149,6 @@ arduino_i2c: &lpi2c1 {}; pinctrl-0 = <&pinmux_lpi2c1>; pinctrl-names = "default"; - mt9m114: mt9m114@48 { - compatible = "aptina,mt9m114"; - reg = <0x48>; - status = "okay"; - - port { - mt9m114_ep_out: endpoint { - remote-endpoint = <&csi_ep_in>; - }; - }; - }; - ft5336: ft5336@38 { compatible = "focaltech,ft5336"; reg = <0x38>; @@ -254,16 +242,8 @@ zephyr_udc0: &usb1 { }; &csi { - status = "okay"; - sensor = <&mt9m114>; pinctrl-0 = <&pinmux_csi>; pinctrl-names = "default"; - - port { - csi_ep_in: endpoint { - remote-endpoint = <&mt9m114_ep_out>; - }; - }; }; &flexpwm2_pwm3 { diff --git a/boards/nxp/mimxrt1170_evk/doc/index.rst b/boards/nxp/mimxrt1170_evk/doc/index.rst index 13b8d795c16d..f61845886b45 100644 --- a/boards/nxp/mimxrt1170_evk/doc/index.rst +++ b/boards/nxp/mimxrt1170_evk/doc/index.rst @@ -168,6 +168,9 @@ RT1170 EVKB (`mimxrt1170_evk@B//cm7/cm4`) +-----------+------------+-------------------------------------+-----------------+-----------------+ | PIT | on-chip | pit | Supported (M7) | Supported (M7) | +-----------+------------+-------------------------------------+-----------------+-----------------+ +| VIDEO | on-chip | CSI; MIPI CSI-2 Rx. Tested with | Supported (M7) | Supported (M7) | +| | | :ref:`wuxi_ov5640` shield | | | ++-----------+------------+-------------------------------------+-----------------+-----------------+ The default configuration can be found in the defconfig files: :zephyr_file:`boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7_defconfig` diff --git a/boards/nxp/mimxrt1170_evk/mimxrt1170_evk-pinctrl.dtsi b/boards/nxp/mimxrt1170_evk/mimxrt1170_evk-pinctrl.dtsi index 35e7c5215324..ffc0197fa710 100644 --- a/boards/nxp/mimxrt1170_evk/mimxrt1170_evk-pinctrl.dtsi +++ b/boards/nxp/mimxrt1170_evk/mimxrt1170_evk-pinctrl.dtsi @@ -23,7 +23,10 @@ bias-pull-up; slew-rate = "fast"; }; - group2 { + }; + + pinmux_lpi2c6: pinmux_lpi2c6 { + group0 { pinmux = <&iomuxc_lpsr_gpio_lpsr_07_lpi2c6_scl>, <&iomuxc_lpsr_gpio_lpsr_06_lpi2c6_sda>; drive-strength = "high"; diff --git a/boards/nxp/mimxrt1170_evk/mimxrt1170_evk.dtsi b/boards/nxp/mimxrt1170_evk/mimxrt1170_evk.dtsi index 9e59e733ebcf..d034bdf53b73 100644 --- a/boards/nxp/mimxrt1170_evk/mimxrt1170_evk.dtsi +++ b/boards/nxp/mimxrt1170_evk/mimxrt1170_evk.dtsi @@ -110,6 +110,11 @@ pinctrl-names = "default"; }; +&lpi2c6 { + pinctrl-0 = <&pinmux_lpi2c6>; + pinctrl-names = "default"; +}; + &flexcan3 { pinctrl-0 = <&pinmux_flexcan3>; pinctrl-names = "default"; diff --git a/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7.yaml b/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7.yaml index c7e846adf6ab..da6ffa5b9c70 100644 --- a/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7.yaml +++ b/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7.yaml @@ -28,4 +28,5 @@ supported: - spi - usb_device - watchdog + - video vendor: nxp diff --git a/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7_B.yaml b/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7_B.yaml index 5cdfdf97c875..62eeec200ea6 100644 --- a/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7_B.yaml +++ b/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7_B.yaml @@ -26,4 +26,5 @@ supported: - spi - usb_device - watchdog + - video vendor: nxp diff --git a/boards/shields/huatian_mt9m114/Kconfig.shield b/boards/shields/huatian_mt9m114/Kconfig.shield new file mode 100644 index 000000000000..e157810d266a --- /dev/null +++ b/boards/shields/huatian_mt9m114/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config SHIELD_HUATIAN_MT9M114 + def_bool $(shields_list_contains,huatian_mt9m114) diff --git a/boards/shields/huatian_mt9m114/doc/index.rst b/boards/shields/huatian_mt9m114/doc/index.rst new file mode 100644 index 000000000000..5c631df9b5ee --- /dev/null +++ b/boards/shields/huatian_mt9m114/doc/index.rst @@ -0,0 +1,77 @@ +.. _huatian_mt9m114: + +HUATIAN_MT9M114 Camera Module +############################# + +Overview +******** + +This camera module connector is from HuaTian Technology (Xi' an) Co. containing +a MT9M114 camera, which is a 1.26 Mp CMOS digital image sensor with an active +pixel array of 1296x976. More information about the MT9M114 camera can be found +at `MT9M114 camera module`_. + +This module uses a 24-pin FPC connector with camera parallel interface which is +available on NXP i.MX RT10XX series. + +Pins assignment of the Huatian MT9M114 camera module +============================================================ + ++-----------------------+------------------------+ +| FPC Connector Pin | Function | ++=======================+========================+ +| 3 | Data 4 | ++-----------------------+------------------------+ +| 4 | Data 3 | ++-----------------------+------------------------+ +| 5 | Data 5 | ++-----------------------+------------------------+ +| 6 | Data 2 | ++-----------------------+------------------------+ +| 7 | Data 6 | ++-----------------------+------------------------+ +| 8 | Pixel Clock | ++-----------------------+------------------------+ +| 9 | Data 7 | ++-----------------------+------------------------+ +| 11 | Data 8 | ++-----------------------+------------------------+ +| 12 | Master Clock | ++-----------------------+------------------------+ +| 13 | Data 9 | ++-----------------------+------------------------+ +| 16 | Hsync | ++-----------------------+------------------------+ +| 17 | Powerdown | ++-----------------------+------------------------+ +| 18 | Vsync | ++-----------------------+------------------------+ +| 20 | I2C Data | ++-----------------------+------------------------+ +| 22 | I2C Clock | ++-----------------------+------------------------+ + +Requirements +************ + +This shield can only be used with a board which provides a configuration for +a 24-pin FPC connector with parallel interface, such as the i.MX RT1064-EVK. + +Programming +*********** + +Set ``-DSHIELD=huatian_mt9m114`` when you invoke ``west build``. For example: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/video/capture + :board: mimxrt1064_evk + :shield: huatian_mt9m114 + :goals: build + +References +********** + +.. target-notes:: + +.. _MT9M114 camera module: + https://www.onsemi.com/pdf/datasheet/mt9m114-d.pdf diff --git a/boards/shields/huatian_mt9m114/huatian_mt9m114.overlay b/boards/shields/huatian_mt9m114/huatian_mt9m114.overlay new file mode 100644 index 000000000000..a471b3fc42d9 --- /dev/null +++ b/boards/shields/huatian_mt9m114/huatian_mt9m114.overlay @@ -0,0 +1,35 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/{ + chosen { + zephyr,camera = &csi; + }; +}; + +&zephyr_cam_i2c { + mt9m114: mt9m114@48 { + compatible = "aptina,mt9m114"; + reg = <0x48>; + + port { + mt9m114_ep_out: endpoint { + remote-endpoint = <&csi_ep_in>; + }; + }; + }; +}; + +&csi { + status = "okay"; + sdev = <&mt9m114>; + + port { + csi_ep_in: endpoint { + remote-endpoint = <&mt9m114_ep_out>; + }; + }; +}; diff --git a/boards/shields/wuxi_ov5640/Kconfig.shield b/boards/shields/wuxi_ov5640/Kconfig.shield new file mode 100644 index 000000000000..7f125e5be35e --- /dev/null +++ b/boards/shields/wuxi_ov5640/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config SHIELD_WUXI_OV5640 + def_bool $(shields_list_contains,wuxi_ov5640) diff --git a/boards/shields/wuxi_ov5640/doc/index.rst b/boards/shields/wuxi_ov5640/doc/index.rst new file mode 100644 index 000000000000..4fe032adb5af --- /dev/null +++ b/boards/shields/wuxi_ov5640/doc/index.rst @@ -0,0 +1,68 @@ +.. _wuxi_ov5640: + +WUXI_OV5640 Camera Module +######################### + +Overview +******** + +This camera module connector comes from Wuxi A-KERR Science & Technology containing +an OV5640 image sensor which provides the full functionality of a single chip 5 +megapixel (2592x1944) camera using OmniBSIā„¢ technology in a small footprint package. + +More information about the OV5640 can be found at `OV5640 camera module`_. + +This module uses a 42-pin connector with MIPI CSI-2 interface which is available on +NXP i.MX RT11XX series. + +Pins assignment of the Wuxi OV5640 camera module connector +============================================================ + ++-----------------------+------------------------+ +| Camera Connector Pin | Function | ++=======================+========================+ +| 5 | I2C_SDA | ++-----------------------+------------------------+ +| 7 | I2C_SCL | ++-----------------------+------------------------+ +| 9 | RSTB_CTL | ++-----------------------+------------------------+ +| 17 | PWND_CTL | ++-----------------------+------------------------+ +| 16 | MIPI_CSI_DP1 | ++-----------------------+------------------------+ +| 18 | MIPI_CSI_DN1 | ++-----------------------+------------------------+ +| 22 | MIPI_CSI_CLKP | ++-----------------------+------------------------+ +| 24 | MIPI_CSI_CLKN | ++-----------------------+------------------------+ +| 28 | MIPI_CSI_DP0 | ++-----------------------+------------------------+ +| 30 | MIPI_CSI_DN0 | ++-----------------------+------------------------+ + +Requirements +************ + +This shield can only be used with a board which provides a configuration +for a 42-pin connector with MIPI CSI interface, such as i.MX RT1170-EVK. + +Programming +*********** + +Set ``-DSHIELD=wuxi_ov5640`` when you invoke ``west build``. For example: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/video/capture + :board: mimxrt1170_evk/mimxrt1176/cm7 + :shield: wuxi_ov5640 + :goals: build + +References +********** + +.. target-notes:: + +.. _OV5640 camera module: + https://cdn.sparkfun.com/datasheets/Sensors/LightImaging/OV5640_datasheet.pdf diff --git a/boards/shields/wuxi_ov5640/wuxi_ov5640.overlay b/boards/shields/wuxi_ov5640/wuxi_ov5640.overlay new file mode 100644 index 000000000000..c0b671dec43a --- /dev/null +++ b/boards/shields/wuxi_ov5640/wuxi_ov5640.overlay @@ -0,0 +1,47 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/{ + chosen { + zephyr,camera = &csi; + }; +}; + +&zephyr_cam_i2c { + status = "okay"; + + ov5640: ov5640@3c { + compatible = "ovti,ov5640"; + reg = <0x3c>; + reset-gpios = <&gpio11 15 GPIO_ACTIVE_LOW>; + powerdown-gpios = <&gpio9 25 GPIO_ACTIVE_HIGH>; + + port { + ov5640_ep_out: endpoint { + remote-endpoint = <&mipi_csi2rx_ep_in>; + }; + }; + }; +}; + +&zephyr_mipi_csi { + status = "okay"; + sensor = <&ov5640>; + + ports { + port@1 { + reg = <1>; + + mipi_csi2rx_ep_in: endpoint { + remote-endpoint = <&ov5640_ep_out>; + }; + }; + }; +}; + +&csi { + status = "okay"; +}; diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index 68fd96d1b4cf..1fa91a7a9737 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -5,7 +5,9 @@ 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) zephyr_library_sources_ifdef(CONFIG_VIDEO_OV2640 ov2640.c) +zephyr_library_sources_ifdef(CONFIG_VIDEO_OV5640 ov5640.c) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 488ad6c80ccd..2b1024b604a7 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" @@ -41,4 +43,6 @@ source "drivers/video/Kconfig.ov7725" source "drivers/video/Kconfig.ov2640" +source "drivers/video/Kconfig.ov5640" + endif # VIDEO diff --git a/drivers/video/Kconfig.mcux_csi b/drivers/video/Kconfig.mcux_csi index 18adaab4c50e..0f1263f041c3 100644 --- a/drivers/video/Kconfig.mcux_csi +++ b/drivers/video/Kconfig.mcux_csi @@ -6,7 +6,6 @@ config VIDEO_MCUX_CSI bool "NXP MCUX CMOS Sensor Interface (CSI) driver" default y - depends on HAS_MCUX_CSI depends on DT_HAS_NXP_IMX_CSI_ENABLED config VIDEO_MCUX_CSI_INIT_PRIORITY diff --git a/drivers/video/Kconfig.mcux_mipi_csi2rx b/drivers/video/Kconfig.mcux_mipi_csi2rx new file mode 100644 index 000000000000..80e3736779ac --- /dev/null +++ b/drivers/video/Kconfig.mcux_mipi_csi2rx @@ -0,0 +1,10 @@ +# NXP MIPI CSI-2 Rx driver configuration option + +# Copyright NXP 2024 +# 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/Kconfig.ov5640 b/drivers/video/Kconfig.ov5640 new file mode 100644 index 000000000000..950079dd3550 --- /dev/null +++ b/drivers/video/Kconfig.ov5640 @@ -0,0 +1,12 @@ +# OV5640 + +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config VIDEO_OV5640 + bool "OV5640 CMOS digital image sensor" + select I2C + depends on DT_HAS_OVTI_OV5640_ENABLED + default y + help + Enable driver for OV5640 CMOS digital image sensor device diff --git a/drivers/video/ov5640.c b/drivers/video/ov5640.c new file mode 100644 index 000000000000..7c76c7b88c93 --- /dev/null +++ b/drivers/video/ov5640.c @@ -0,0 +1,698 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ovti_ov5640 + +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#include +LOG_MODULE_REGISTER(ov5640); + +#include + +#define CHIP_ID_REG 0x300a +#define CHIP_ID_VAL 0x5640 + +#define SYS_CTRL0_REG 0x3008 +#define SYS_CTRL0_SW_PWDN 0x42 +#define SYS_CTRL0_SW_PWUP 0x02 +#define SYS_CTRL0_SW_RST 0x82 + +#define SYS_RESET00_REG 0x3000 +#define SYS_RESET02_REG 0x3002 +#define SYS_CLK_ENABLE00_REG 0x3004 +#define SYS_CLK_ENABLE02_REG 0x3006 +#define IO_MIPI_CTRL00_REG 0x300e +#define SYSTEM_CONTROL1_REG 0x302e +#define SCCB_SYS_CTRL1_REG 0x3103 +#define TIMING_TC_REG20_REG 0x3820 +#define TIMING_TC_REG21_REG 0x3821 +#define HZ5060_CTRL01_REG 0x3c01 +#define ISP_CTRL01_REG 0x5001 + +#define SC_PLL_CTRL0_REG 0x3034 +#define SC_PLL_CTRL1_REG 0x3035 +#define SC_PLL_CTRL2_REG 0x3036 +#define SC_PLL_CTRL3_REG 0x3037 +#define SYS_ROOT_DIV_REG 0x3108 +#define PCLK_PERIOD_REG 0x4837 + +#define AEC_CTRL00_REG 0x3a00 +#define AEC_CTRL0F_REG 0x3a0f +#define AEC_CTRL10_REG 0x3a10 +#define AEC_CTRL11_REG 0x3a11 +#define AEC_CTRL1B_REG 0x3a1b +#define AEC_CTRL1E_REG 0x3a1e +#define AEC_CTRL1F_REG 0x3a1f + +#define BLC_CTRL01_REG 0x4001 +#define BLC_CTRL04_REG 0x4004 +#define BLC_CTRL05_REG 0x4005 + +#define AWB_CTRL00_REG 0x5180 +#define AWB_CTRL01_REG 0x5181 +#define AWB_CTRL02_REG 0x5182 +#define AWB_CTRL03_REG 0x5183 +#define AWB_CTRL04_REG 0x5184 +#define AWB_CTRL05_REG 0x5185 +#define AWB_CTRL17_REG 0x5191 +#define AWB_CTRL18_REG 0x5192 +#define AWB_CTRL19_REG 0x5193 +#define AWB_CTRL20_REG 0x5194 +#define AWB_CTRL21_REG 0x5195 +#define AWB_CTRL22_REG 0x5196 +#define AWB_CTRL23_REG 0x5197 +#define AWB_CTRL30_REG 0x519e + +#define SDE_CTRL0_REG 0x5580 +#define SDE_CTRL3_REG 0x5583 +#define SDE_CTRL4_REG 0x5584 +#define SDE_CTRL9_REG 0x5589 +#define SDE_CTRL10_REG 0x558a +#define SDE_CTRL11_REG 0x558b + +#define DEFAULT_MIPI_CHANNEL 0 + +#define OV5640_RESOLUTION_PARAM_NUM 24 + +struct ov5640_config { + struct i2c_dt_spec i2c; + struct gpio_dt_spec reset_gpio; + struct gpio_dt_spec powerdown_gpio; +}; + +struct ov5640_data { + struct video_format fmt; +}; + +struct ov5640_reg { + uint16_t addr; + uint8_t val; +}; + +struct ov5640_mipi_clock_config { + uint8_t pllCtrl1; + uint8_t pllCtrl2; +}; + +struct ov5640_resolution_config { + uint16_t width; + uint16_t height; + const struct ov5640_reg *res_params; + const struct ov5640_mipi_clock_config mipi_pclk; +}; + +static const struct ov5640_reg ov5640InitParams[] = { + /* Power down */ + {SYS_CTRL0_REG, SYS_CTRL0_SW_PWDN}, + + /* System setting. */ + {SCCB_SYS_CTRL1_REG, 0x13}, + {SCCB_SYS_CTRL1_REG, 0x03}, + {SYS_RESET00_REG, 0x00}, + {SYS_CLK_ENABLE00_REG, 0xff}, + {SYS_RESET02_REG, 0x1c}, + {SYS_CLK_ENABLE02_REG, 0xc3}, + {SYSTEM_CONTROL1_REG, 0x08}, + {0x3618, 0x00}, + {0x3612, 0x29}, + {0x3708, 0x64}, + {0x3709, 0x52}, + {0x370c, 0x03}, + {TIMING_TC_REG20_REG, 0x41}, + {TIMING_TC_REG21_REG, 0x07}, + {0x3630, 0x36}, + {0x3631, 0x0e}, + {0x3632, 0xe2}, + {0x3633, 0x12}, + {0x3621, 0xe0}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3905, 0x02}, + {0x3906, 0x10}, + {0x3901, 0x0a}, + {0x3731, 0x12}, + {0x3600, 0x08}, + {0x3601, 0x33}, + {0x302d, 0x60}, + {0x3620, 0x52}, + {0x371b, 0x20}, + {0x471c, 0x50}, + {0x3a13, 0x43}, + {0x3a18, 0x00}, + {0x3a19, 0x7c}, + {0x3635, 0x13}, + {0x3636, 0x03}, + {0x3634, 0x40}, + {0x3622, 0x01}, + {HZ5060_CTRL01_REG, 0x00}, + {AEC_CTRL00_REG, 0x58}, + {BLC_CTRL01_REG, 0x02}, + {BLC_CTRL04_REG, 0x02}, + {BLC_CTRL05_REG, 0x1a}, + {ISP_CTRL01_REG, 0xa3}, + + /* AEC */ + {AEC_CTRL0F_REG, 0x30}, + {AEC_CTRL10_REG, 0x28}, + {AEC_CTRL1B_REG, 0x30}, + {AEC_CTRL1E_REG, 0x26}, + {AEC_CTRL11_REG, 0x60}, + {AEC_CTRL1F_REG, 0x14}, + + /* AWB */ + {AWB_CTRL00_REG, 0xff}, + {AWB_CTRL01_REG, 0xf2}, + {AWB_CTRL02_REG, 0x00}, + {AWB_CTRL03_REG, 0x14}, + {AWB_CTRL04_REG, 0x25}, + {AWB_CTRL05_REG, 0x24}, + {0x5186, 0x09}, + {0x5187, 0x09}, + {0x5188, 0x09}, + {0x5189, 0x88}, + {0x518a, 0x54}, + {0x518b, 0xee}, + {0x518c, 0xb2}, + {0x518d, 0x50}, + {0x518e, 0x34}, + {0x518f, 0x6b}, + {0x5190, 0x46}, + {AWB_CTRL17_REG, 0xf8}, + {AWB_CTRL18_REG, 0x04}, + {AWB_CTRL19_REG, 0x70}, + {AWB_CTRL20_REG, 0xf0}, + {AWB_CTRL21_REG, 0xf0}, + {AWB_CTRL22_REG, 0x03}, + {AWB_CTRL23_REG, 0x01}, + {0x5198, 0x04}, + {0x5199, 0x6c}, + {0x519a, 0x04}, + {0x519b, 0x00}, + {0x519c, 0x09}, + {0x519d, 0x2b}, + {AWB_CTRL30_REG, 0x38}, + + /* Color Matrix */ + {0x5381, 0x1e}, + {0x5382, 0x5b}, + {0x5383, 0x08}, + {0x5384, 0x0a}, + {0x5385, 0x7e}, + {0x5386, 0x88}, + {0x5387, 0x7c}, + {0x5388, 0x6c}, + {0x5389, 0x10}, + {0x538a, 0x01}, + {0x538b, 0x98}, + + /* Sharp */ + {0x5300, 0x08}, + {0x5301, 0x30}, + {0x5302, 0x10}, + {0x5303, 0x00}, + {0x5304, 0x08}, + {0x5305, 0x30}, + {0x5306, 0x08}, + {0x5307, 0x16}, + {0x5309, 0x08}, + {0x530a, 0x30}, + {0x530b, 0x04}, + {0x530c, 0x06}, + + /* Gamma */ + {0x5480, 0x01}, + {0x5481, 0x08}, + {0x5482, 0x14}, + {0x5483, 0x28}, + {0x5484, 0x51}, + {0x5485, 0x65}, + {0x5486, 0x71}, + {0x5487, 0x7d}, + {0x5488, 0x87}, + {0x5489, 0x91}, + {0x548a, 0x9a}, + {0x548b, 0xaa}, + {0x548c, 0xb8}, + {0x548d, 0xcd}, + {0x548e, 0xdd}, + {0x548f, 0xea}, + {0x5490, 0x1d}, + + /* UV adjust. */ + {SDE_CTRL0_REG, 0x02}, + {SDE_CTRL3_REG, 0x40}, + {SDE_CTRL4_REG, 0x10}, + {SDE_CTRL9_REG, 0x10}, + {SDE_CTRL10_REG, 0x00}, + {SDE_CTRL11_REG, 0xf8}, + + /* Lens correction. */ + {0x5800, 0x23}, + {0x5801, 0x14}, + {0x5802, 0x0f}, + {0x5803, 0x0f}, + {0x5804, 0x12}, + {0x5805, 0x26}, + {0x5806, 0x0c}, + {0x5807, 0x08}, + {0x5808, 0x05}, + {0x5809, 0x05}, + {0x580a, 0x08}, + {0x580b, 0x0d}, + {0x580c, 0x08}, + {0x580d, 0x03}, + {0x580e, 0x00}, + {0x580f, 0x00}, + {0x5810, 0x03}, + {0x5811, 0x09}, + {0x5812, 0x07}, + {0x5813, 0x03}, + {0x5814, 0x00}, + {0x5815, 0x01}, + {0x5816, 0x03}, + {0x5817, 0x08}, + {0x5818, 0x0d}, + {0x5819, 0x08}, + {0x581a, 0x05}, + {0x581b, 0x06}, + {0x581c, 0x08}, + {0x581d, 0x0e}, + {0x581e, 0x29}, + {0x581f, 0x17}, + {0x5820, 0x11}, + {0x5821, 0x11}, + {0x5822, 0x15}, + {0x5823, 0x28}, + {0x5824, 0x46}, + {0x5825, 0x26}, + {0x5826, 0x08}, + {0x5827, 0x26}, + {0x5828, 0x64}, + {0x5829, 0x26}, + {0x582a, 0x24}, + {0x582b, 0x22}, + {0x582c, 0x24}, + {0x582d, 0x24}, + {0x582e, 0x06}, + {0x582f, 0x22}, + {0x5830, 0x40}, + {0x5831, 0x42}, + {0x5832, 0x24}, + {0x5833, 0x26}, + {0x5834, 0x24}, + {0x5835, 0x22}, + {0x5836, 0x22}, + {0x5837, 0x26}, + {0x5838, 0x44}, + {0x5839, 0x24}, + {0x583a, 0x26}, + {0x583b, 0x28}, + {0x583c, 0x42}, + {0x583d, 0xce}, + {0x5000, 0xa7}, +}; + +static const struct ov5640_reg ov5640_low_res_params[] = { + {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0x04}, {0x3804, 0x0a}, + {0x3805, 0x3f}, {0x3806, 0x07}, {0x3807, 0x9b}, {0x3808, 0x02}, {0x3809, 0x80}, + {0x380a, 0x01}, {0x380b, 0xe0}, {0x380c, 0x07}, {0x380d, 0x68}, {0x380e, 0x03}, + {0x380f, 0xd8}, {0x3810, 0x00}, {0x3811, 0x10}, {0x3812, 0x00}, {0x3813, 0x06}, + {0x3814, 0x31}, {0x3815, 0x31}, {0x3824, 0x02}, {0x460c, 0x22}}; + +static const struct ov5640_reg ov5640_720p_res_params[] = { + {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0xfa}, {0x3804, 0x0a}, + {0x3805, 0x3f}, {0x3806, 0x06}, {0x3807, 0xa9}, {0x3808, 0x05}, {0x3809, 0x00}, + {0x380a, 0x02}, {0x380b, 0xd0}, {0x380c, 0x07}, {0x380d, 0x64}, {0x380e, 0x02}, + {0x380f, 0xe4}, {0x3810, 0x00}, {0x3811, 0x10}, {0x3812, 0x00}, {0x3813, 0x04}, + {0x3814, 0x31}, {0x3815, 0x31}, {0x3824, 0x04}, {0x460c, 0x20}}; + +static const struct ov5640_resolution_config resolutionParams[] = { + {.width = 640, + .height = 480, + .res_params = ov5640_low_res_params, + .mipi_pclk = { + .pllCtrl1 = 0x14, + .pllCtrl2 = 0x38, + }}, + {.width = 1280, + .height = 720, + .res_params = ov5640_720p_res_params, + .mipi_pclk = { + .pllCtrl1 = 0x21, + .pllCtrl2 = 0x54, + }}, +}; + +#define OV5640_VIDEO_FORMAT_CAP(width, height, format) \ + { \ + .pixelformat = (format), .width_min = (width), .width_max = (width), \ + .height_min = (height), .height_max = (height), .width_step = 0, .height_step = 0 \ + } + +static const struct video_format_cap fmts[] = { + OV5640_VIDEO_FORMAT_CAP(1280, 720, VIDEO_PIX_FMT_RGB565), + OV5640_VIDEO_FORMAT_CAP(1280, 720, VIDEO_PIX_FMT_YUYV), + OV5640_VIDEO_FORMAT_CAP(640, 480, VIDEO_PIX_FMT_RGB565), + OV5640_VIDEO_FORMAT_CAP(640, 480, VIDEO_PIX_FMT_YUYV), + {0}}; + +static int ov5640_read_reg(const struct i2c_dt_spec *spec, const uint16_t addr, void *val, + const uint8_t val_size) +{ + int ret; + struct i2c_msg msg[2]; + uint8_t addr_buf[2]; + + if (val_size > 4) { + return -ENOTSUP; + } + + addr_buf[1] = addr & 0xFF; + addr_buf[0] = addr >> 8; + msg[0].buf = addr_buf; + msg[0].len = 2U; + msg[0].flags = I2C_MSG_WRITE; + + msg[1].buf = (uint8_t *)val; + msg[1].len = val_size; + msg[1].flags = I2C_MSG_READ | I2C_MSG_STOP | I2C_MSG_RESTART; + + ret = i2c_transfer_dt(spec, msg, 2); + if (ret) { + return ret; + } + + switch (val_size) { + case 4: + *(uint32_t *)val = sys_be32_to_cpu(*(uint32_t *)val); + break; + case 2: + *(uint16_t *)val = sys_be16_to_cpu(*(uint16_t *)val); + break; + case 1: + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int ov5640_write_reg(const struct i2c_dt_spec *spec, const uint16_t addr, const uint8_t val) +{ + uint8_t addr_buf[2]; + struct i2c_msg msg[2]; + + addr_buf[1] = addr & 0xFF; + addr_buf[0] = addr >> 8; + msg[0].buf = addr_buf; + msg[0].len = 2U; + msg[0].flags = I2C_MSG_WRITE; + + msg[1].buf = (uint8_t *)&val; + msg[1].len = 1; + msg[1].flags = I2C_MSG_WRITE | I2C_MSG_STOP; + + return i2c_transfer_dt(spec, msg, 2); +} + +static int ov5640_modify_reg(const struct i2c_dt_spec *spec, const uint16_t addr, + const uint8_t mask, const uint8_t val) +{ + uint8_t regVal = 0; + int ret = ov5640_read_reg(spec, addr, ®Val, sizeof(regVal)); + + if (ret) { + return ret; + } + + return ov5640_write_reg(spec, addr, (regVal & ~mask) | (val & mask)); +} + +static int ov5640_write_multi_regs(const struct i2c_dt_spec *spec, const struct ov5640_reg *regs, + const uint32_t num_regs) +{ + int ret; + + for (int i = 0; i < num_regs; i++) { + ret = ov5640_write_reg(spec, regs[i].addr, regs[i].val); + if (ret) { + return ret; + } + } + + return 0; +} + +static int ov5640_set_fmt(const struct device *dev, enum video_endpoint_id ep, + struct video_format *fmt) +{ + struct ov5640_data *drv_data = dev->data; + const struct ov5640_config *cfg = dev->config; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(fmts); ++i) { + if (fmt->pixelformat == fmts[i].pixelformat && fmt->width >= fmts[i].width_min && + fmt->width <= fmts[i].width_max && fmt->height >= fmts[i].height_min && + fmt->height <= fmts[i].height_max) { + break; + } + } + + if (i == ARRAY_SIZE(fmts)) { + LOG_ERR("Unsupported pixel format or resolution"); + return -ENOTSUP; + } + + if (!memcmp(&drv_data->fmt, fmt, sizeof(drv_data->fmt))) { + return 0; + } + + drv_data->fmt = *fmt; + + /* Set resolution parameters */ + for (i = 0; i < ARRAY_SIZE(resolutionParams); i++) { + if (fmt->width == resolutionParams[i].width && + fmt->height == resolutionParams[i].height) { + ret = ov5640_write_multi_regs(&cfg->i2c, resolutionParams[i].res_params, + OV5640_RESOLUTION_PARAM_NUM); + if (ret) { + LOG_ERR("Unable to set resolution parameters"); + return ret; + } + break; + } + } + + /* Set pixel format, default to VIDEO_PIX_FMT_RGB565 */ + struct ov5640_reg fmt_params[2] = { + {0x4300, 0x6f}, + {0x501f, 0x01}, + }; + + if (fmt->pixelformat == VIDEO_PIX_FMT_YUYV) { + fmt_params[0].val = 0x3f; + fmt_params[1].val = 0x00; + } + + ret = ov5640_write_multi_regs(&cfg->i2c, fmt_params, ARRAY_SIZE(fmt_params)); + if (ret) { + LOG_ERR("Unable to set pixel format"); + return ret; + } + + /* Configure MIPI pixel clock */ + ret |= ov5640_modify_reg(&cfg->i2c, SC_PLL_CTRL0_REG, 0x0f, 0x08); + ret |= ov5640_modify_reg(&cfg->i2c, SC_PLL_CTRL1_REG, 0xff, + resolutionParams[i].mipi_pclk.pllCtrl1); + ret |= ov5640_modify_reg(&cfg->i2c, SC_PLL_CTRL2_REG, 0xff, + resolutionParams[i].mipi_pclk.pllCtrl2); + ret |= ov5640_modify_reg(&cfg->i2c, SC_PLL_CTRL3_REG, 0x1f, 0x13); + ret |= ov5640_modify_reg(&cfg->i2c, SYS_ROOT_DIV_REG, 0x3f, 0x01); + ret |= ov5640_write_reg(&cfg->i2c, PCLK_PERIOD_REG, 0x0a); + if (ret) { + LOG_ERR("Unable to configure MIPI pixel clock"); + return ret; + } + + return 0; +} + +static int ov5640_get_fmt(const struct device *dev, enum video_endpoint_id ep, + struct video_format *fmt) +{ + struct ov5640_data *drv_data = dev->data; + + *fmt = drv_data->fmt; + + return 0; +} + +static int ov5640_get_caps(const struct device *dev, enum video_endpoint_id ep, + struct video_caps *caps) +{ + caps->format_caps = fmts; + return 0; +} + +static int ov5640_stream_start(const struct device *dev) +{ + const struct ov5640_config *cfg = dev->config; + /* Power up MIPI PHY HS Tx & LP Rx in 2 data lanes mode */ + int ret = ov5640_write_reg(&cfg->i2c, IO_MIPI_CTRL00_REG, 0x45); + + if (ret) { + LOG_ERR("Unable to power up MIPI PHY"); + return ret; + } + return ov5640_write_reg(&cfg->i2c, SYS_CTRL0_REG, SYS_CTRL0_SW_PWUP); +} + +static int ov5640_stream_stop(const struct device *dev) +{ + const struct ov5640_config *cfg = dev->config; + /* Power down MIPI PHY HS Tx & LP Rx */ + int ret = ov5640_write_reg(&cfg->i2c, IO_MIPI_CTRL00_REG, 0x40); + + if (ret) { + LOG_ERR("Unable to power down MIPI PHY"); + return ret; + } + return ov5640_write_reg(&cfg->i2c, SYS_CTRL0_REG, SYS_CTRL0_SW_PWDN); +} + +static const struct video_driver_api ov5640_driver_api = { + .set_format = ov5640_set_fmt, + .get_format = ov5640_get_fmt, + .get_caps = ov5640_get_caps, + .stream_start = ov5640_stream_start, + .stream_stop = ov5640_stream_stop, +}; + +static int ov5640_init(const struct device *dev) +{ + const struct ov5640_config *cfg = dev->config; + struct video_format fmt; + uint16_t chip_id; + int ret; + + if (!device_is_ready(cfg->i2c.bus)) { + LOG_ERR("Bus device is not ready"); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&cfg->reset_gpio)) { + LOG_ERR("%s: device %s is not ready", dev->name, cfg->reset_gpio.port->name); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&cfg->powerdown_gpio)) { + LOG_ERR("%s: device %s is not ready", dev->name, cfg->powerdown_gpio.port->name); + return -ENODEV; + } + + /* Power up sequence */ + if (cfg->powerdown_gpio.port != NULL) { + ret = gpio_pin_configure_dt(&cfg->powerdown_gpio, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + } + + if (cfg->reset_gpio.port != NULL) { + ret = gpio_pin_configure_dt(&cfg->reset_gpio, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + } + + k_sleep(K_MSEC(5)); + + if (cfg->powerdown_gpio.port != NULL) { + gpio_pin_set_dt(&cfg->powerdown_gpio, 0); + } + + k_sleep(K_MSEC(1)); + + if (cfg->reset_gpio.port != NULL) { + gpio_pin_set_dt(&cfg->reset_gpio, 0); + } + + k_sleep(K_MSEC(20)); + + /* Software reset */ + ret = ov5640_write_reg(&cfg->i2c, SYS_CTRL0_REG, SYS_CTRL0_SW_RST); + if (ret) { + LOG_ERR("Unable to perform software reset"); + return -EIO; + } + + k_sleep(K_MSEC(5)); + + /* Initialize register values */ + ret = ov5640_write_multi_regs(&cfg->i2c, ov5640InitParams, ARRAY_SIZE(ov5640InitParams)); + if (ret) { + LOG_ERR("Unable to initialize the sensor"); + return -EIO; + } + + /* Set virtual channel */ + ret = ov5640_modify_reg(&cfg->i2c, 0x4814, 3U << 6, (uint8_t)(DEFAULT_MIPI_CHANNEL) << 6); + if (ret) { + LOG_ERR("Unable to set virtual channel"); + return -EIO; + } + + /* Check sensor chip id */ + ret = ov5640_read_reg(&cfg->i2c, CHIP_ID_REG, &chip_id, sizeof(chip_id)); + if (ret) { + LOG_ERR("Unable to read sensor chip ID, ret = %d", ret); + return -ENODEV; + } + + if (chip_id != CHIP_ID_VAL) { + LOG_ERR("Wrong chip ID: %04x (expected %04x)", chip_id, CHIP_ID_VAL); + return -ENODEV; + } + + /* Set default format to 720p RGB565 */ + fmt.pixelformat = VIDEO_PIX_FMT_RGB565; + fmt.width = 1280; + fmt.height = 720; + fmt.pitch = fmt.width * 2; + ret = ov5640_set_fmt(dev, VIDEO_EP_OUT, &fmt); + if (ret) { + LOG_ERR("Unable to configure default format"); + return -EIO; + } + + return 0; +} + +#define OV5640_INIT(n) \ + static struct ov5640_data ov5640_data_##n; \ + \ + static const struct ov5640_config ov5640_cfg_##n = { \ + .i2c = I2C_DT_SPEC_INST_GET(n), \ + .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}), \ + .powerdown_gpio = GPIO_DT_SPEC_INST_GET_OR(n, powerdown_gpios, {0}), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, &ov5640_init, NULL, &ov5640_data_##n, &ov5640_cfg_##n, \ + POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &ov5640_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(OV5640_INIT) diff --git a/drivers/video/video_mcux_csi.c b/drivers/video/video_mcux_csi.c index 1e7117c4393e..1a5f54e0d7df 100644 --- a/drivers/video/video_mcux_csi.c +++ b/drivers/video/video_mcux_csi.c @@ -20,7 +20,7 @@ struct video_mcux_csi_config { CSI_Type *base; - const struct device *sensor_dev; + const struct device *source_dev; const struct pinctrl_dev_config *pincfg; }; @@ -45,13 +45,15 @@ static inline unsigned int video_pix_fmt_bpp(uint32_t pixelformat) case VIDEO_PIX_FMT_RGB565: case VIDEO_PIX_FMT_YUYV: return 2; + case VIDEO_PIX_FMT_XRGB32: + case VIDEO_PIX_FMT_XYUV32: + return 4; default: return 0; } } -static void __frame_done_cb(CSI_Type *base, csi_handle_t *handle, - status_t status, void *user_data) +static void __frame_done_cb(CSI_Type *base, csi_handle_t *handle, status_t status, void *user_data) { struct video_mcux_csi_data *data = user_data; const struct device *dev = data->dev; @@ -66,8 +68,7 @@ static void __frame_done_cb(CSI_Type *base, csi_handle_t *handle, return; } - status = CSI_TransferGetFullBuffer(config->base, &(data->csi_handle), - &buffer_addr); + status = CSI_TransferGetFullBuffer(config->base, &(data->csi_handle), &buffer_addr); if (status != kStatus_Success) { result = VIDEO_BUF_ERROR; goto done; @@ -116,25 +117,62 @@ static void __frame_done_cb(CSI_Type *base, csi_handle_t *handle, return; } -static int video_mcux_csi_set_fmt(const struct device *dev, - enum video_endpoint_id ep, +#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) +K_HEAP_DEFINE(csi_heap, 1000); +static struct video_format_cap *fmts; +/* + * On i.MX RT11xx SoCs which have MIPI CSI-2 Rx, image data from the camera sensor after passing + * through the pipeline (MIPI CSI-2 Rx --> Video Mux --> CSI) will be implicitly converted to a + * 32-bits pixel format. For example, an input in RGB565 or YUYV (2-bytes format) will become a + * XRGB32 or XYUV32 (4-bytes format) respectively, at the output of the CSI. + */ +static inline void video_pix_fmt_convert(struct video_format *fmt, bool isGetFmt) +{ + switch (fmt->pixelformat) { + case VIDEO_PIX_FMT_XRGB32: + fmt->pixelformat = isGetFmt ? VIDEO_PIX_FMT_XRGB32 : VIDEO_PIX_FMT_RGB565; + break; + case VIDEO_PIX_FMT_XYUV32: + fmt->pixelformat = isGetFmt ? VIDEO_PIX_FMT_XYUV32 : VIDEO_PIX_FMT_YUYV; + break; + case VIDEO_PIX_FMT_RGB565: + fmt->pixelformat = isGetFmt ? VIDEO_PIX_FMT_XRGB32 : VIDEO_PIX_FMT_RGB565; + break; + case VIDEO_PIX_FMT_YUYV: + fmt->pixelformat = isGetFmt ? VIDEO_PIX_FMT_XYUV32 : VIDEO_PIX_FMT_YUYV; + break; + } + + fmt->pitch = fmt->width * video_pix_fmt_bpp(fmt->pixelformat); +} +#endif + +static int video_mcux_csi_set_fmt(const struct device *dev, enum video_endpoint_id ep, struct video_format *fmt) { const struct video_mcux_csi_config *config = dev->config; struct video_mcux_csi_data *data = dev->data; unsigned int bpp = video_pix_fmt_bpp(fmt->pixelformat); status_t ret; + struct video_format format = *fmt; if (!bpp || ep != VIDEO_EP_OUT) { return -EINVAL; } - data->pixelformat = fmt->pixelformat; data->csi_config.bytesPerPixel = bpp; data->csi_config.linePitch_Bytes = fmt->pitch; +#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) + if (fmt->pixelformat != VIDEO_PIX_FMT_XRGB32 && fmt->pixelformat != VIDEO_PIX_FMT_XYUV32) { + return -ENOTSUP; + } + video_pix_fmt_convert(&format, false); + data->csi_config.dataBus = kCSI_DataBus24Bit; +#else + data->csi_config.dataBus = kCSI_DataBus8Bit; +#endif data->csi_config.polarityFlags = kCSI_HsyncActiveHigh | kCSI_DataLatchOnRisingEdge; data->csi_config.workMode = kCSI_GatedClockMode; /* use VSYNC, HSYNC, and PIXCLK */ - data->csi_config.dataBus = kCSI_DataBus8Bit; data->csi_config.useExtVsync = true; data->csi_config.height = fmt->height; data->csi_config.width = fmt->width; @@ -144,42 +182,36 @@ static int video_mcux_csi_set_fmt(const struct device *dev, return -EIO; } - ret = CSI_TransferCreateHandle(config->base, &data->csi_handle, - __frame_done_cb, data); + ret = CSI_TransferCreateHandle(config->base, &data->csi_handle, __frame_done_cb, data); if (ret != kStatus_Success) { return -EIO; } - if (config->sensor_dev && video_set_format(config->sensor_dev, ep, fmt)) { + if (config->source_dev && video_set_format(config->source_dev, ep, &format)) { return -EIO; } return 0; } -static int video_mcux_csi_get_fmt(const struct device *dev, - enum video_endpoint_id ep, +static int video_mcux_csi_get_fmt(const struct device *dev, enum video_endpoint_id ep, struct video_format *fmt) { - struct video_mcux_csi_data *data = dev->data; const struct video_mcux_csi_config *config = dev->config; if (fmt == NULL || ep != VIDEO_EP_OUT) { return -EINVAL; } - if (config->sensor_dev && !video_get_format(config->sensor_dev, ep, fmt)) { - /* align CSI with sensor fmt */ + if (config->source_dev && !video_get_format(config->source_dev, ep, fmt)) { +#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) + video_pix_fmt_convert(fmt, true); +#endif + /* align CSI with source fmt */ return video_mcux_csi_set_fmt(dev, ep, fmt); + } else { + return -EIO; } - - - fmt->pixelformat = data->pixelformat; - fmt->height = data->csi_config.height; - fmt->width = data->csi_config.width; - fmt->pitch = data->csi_config.linePitch_Bytes; - - return 0; } static int video_mcux_csi_stream_start(const struct device *dev) @@ -193,7 +225,7 @@ static int video_mcux_csi_stream_start(const struct device *dev) return -EIO; } - if (config->sensor_dev && video_stream_start(config->sensor_dev)) { + if (config->source_dev && video_stream_start(config->source_dev)) { return -EIO; } @@ -206,7 +238,7 @@ static int video_mcux_csi_stream_stop(const struct device *dev) struct video_mcux_csi_data *data = dev->data; status_t ret; - if (config->sensor_dev && video_stream_stop(config->sensor_dev)) { + if (config->source_dev && video_stream_stop(config->source_dev)) { return -EIO; } @@ -218,10 +250,7 @@ static int video_mcux_csi_stream_stop(const struct device *dev) return 0; } - -static int video_mcux_csi_flush(const struct device *dev, - enum video_endpoint_id ep, - bool cancel) +static int video_mcux_csi_flush(const struct device *dev, enum video_endpoint_id ep, bool cancel) { const struct video_mcux_csi_config *config = dev->config; struct video_mcux_csi_data *data = dev->data; @@ -237,16 +266,14 @@ static int video_mcux_csi_flush(const struct device *dev, } else { /* Flush driver output queue */ do { - ret = CSI_TransferGetFullBuffer(config->base, - &(data->csi_handle), + ret = CSI_TransferGetFullBuffer(config->base, &(data->csi_handle), &buffer_addr); } while (ret == kStatus_Success); while ((vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT))) { k_fifo_put(&data->fifo_out, vbuf); if (IS_ENABLED(CONFIG_POLL) && data->signal) { - k_poll_signal_raise(data->signal, - VIDEO_BUF_ABORTED); + k_poll_signal_raise(data->signal, VIDEO_BUF_ABORTED); } } } @@ -254,8 +281,7 @@ static int video_mcux_csi_flush(const struct device *dev, return 0; } -static int video_mcux_csi_enqueue(const struct device *dev, - enum video_endpoint_id ep, +static int video_mcux_csi_enqueue(const struct device *dev, enum video_endpoint_id ep, struct video_buffer *vbuf) { const struct video_mcux_csi_config *config = dev->config; @@ -281,10 +307,8 @@ static int video_mcux_csi_enqueue(const struct device *dev, return 0; } -static int video_mcux_csi_dequeue(const struct device *dev, - enum video_endpoint_id ep, - struct video_buffer **vbuf, - k_timeout_t timeout) +static int video_mcux_csi_dequeue(const struct device *dev, enum video_endpoint_id ep, + struct video_buffer **vbuf, k_timeout_t timeout) { struct video_mcux_csi_data *data = dev->data; @@ -300,38 +324,33 @@ static int video_mcux_csi_dequeue(const struct device *dev, return 0; } -static inline int video_mcux_csi_set_ctrl(const struct device *dev, - unsigned int cid, - void *value) +static inline int video_mcux_csi_set_ctrl(const struct device *dev, unsigned int cid, void *value) { const struct video_mcux_csi_config *config = dev->config; int ret = -ENOTSUP; - /* Forward to sensor dev if any */ - if (config->sensor_dev) { - ret = video_set_ctrl(config->sensor_dev, cid, value); + /* Forward to source dev if any */ + if (config->source_dev) { + ret = video_set_ctrl(config->source_dev, cid, value); } return ret; } -static inline int video_mcux_csi_get_ctrl(const struct device *dev, - unsigned int cid, - void *value) +static inline int video_mcux_csi_get_ctrl(const struct device *dev, unsigned int cid, void *value) { const struct video_mcux_csi_config *config = dev->config; int ret = -ENOTSUP; - /* Forward to sensor dev if any */ - if (config->sensor_dev) { - ret = video_get_ctrl(config->sensor_dev, cid, value); + /* Forward to source dev if any */ + if (config->source_dev) { + ret = video_get_ctrl(config->source_dev, cid, value); } return ret; } -static int video_mcux_csi_get_caps(const struct device *dev, - enum video_endpoint_id ep, +static int video_mcux_csi_get_caps(const struct device *dev, enum video_endpoint_id ep, struct video_caps *caps) { const struct video_mcux_csi_config *config = dev->config; @@ -341,15 +360,43 @@ static int video_mcux_csi_get_caps(const struct device *dev, return -EINVAL; } - /* Just forward to sensor dev for now */ - if (config->sensor_dev) { - err = video_get_caps(config->sensor_dev, ep, caps); + /* Just forward to source dev for now */ + if (config->source_dev) { + err = video_get_caps(config->source_dev, ep, caps); +#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) + /* + * On i.MX RT11xx SoCs which have MIPI CSI-2 Rx, image data from the camera sensor + * after passing through the pipeline (MIPI CSI-2 Rx --> Video Mux --> CSI) will be + * implicitly converted to a 32-bits pixel format. For example, an input in RGB565 + * or YUYV (2-bytes format) will become an XRGB32 or XYUV32 (4-bytes format) + * respectively, at the output of the CSI. So, we change the pixel formats of the + * source caps to reflect this. + */ + int ind = 0; + + while (caps->format_caps[ind].pixelformat) { + ind++; + } + k_heap_free(&csi_heap, fmts); + fmts = k_heap_alloc(&csi_heap, (ind + 1) * sizeof(struct video_format_cap), + K_FOREVER); + + for (int i = 0; i <= ind; i++) { + memcpy(&fmts[i], &caps->format_caps[i], sizeof(fmts[i])); + if (fmts[i].pixelformat == VIDEO_PIX_FMT_RGB565) { + fmts[i].pixelformat = VIDEO_PIX_FMT_XRGB32; + } else if (fmts[i].pixelformat == VIDEO_PIX_FMT_YUYV) { + fmts[i].pixelformat = VIDEO_PIX_FMT_XYUV32; + } + } + caps->format_caps = fmts; +#endif } /* NXP MCUX CSI request at least 2 buffer before starting */ caps->min_vbuf_count = 2; - /* no sensor dev */ + /* no source dev */ return err; } @@ -371,10 +418,10 @@ static int video_mcux_csi_init(const struct device *dev) CSI_GetDefaultConfig(&data->csi_config); - /* check if there is any sensor device (video ctrl device) + /* check if there is any source device (video ctrl device) * the device is not yet initialized so we only check if it exists */ - if (config->sensor_dev == NULL) { + if (config->source_dev == NULL) { return -ENODEV; } @@ -387,8 +434,7 @@ static int video_mcux_csi_init(const struct device *dev) } #ifdef CONFIG_POLL -static int video_mcux_csi_set_signal(const struct device *dev, - enum video_endpoint_id ep, +static int video_mcux_csi_set_signal(const struct device *dev, enum video_endpoint_id ep, struct k_poll_signal *signal) { struct video_mcux_csi_data *data = dev->data; @@ -424,7 +470,7 @@ PINCTRL_DT_INST_DEFINE(0); static const struct video_mcux_csi_config video_mcux_csi_config_0 = { .base = (CSI_Type *)DT_INST_REG_ADDR(0), - .sensor_dev = DEVICE_DT_GET(DT_INST_PHANDLE(0, sensor)), + .source_dev = DEVICE_DT_GET(DT_INST_PHANDLE(0, sdev)), .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), }; @@ -434,8 +480,7 @@ static int video_mcux_csi_init_0(const struct device *dev) { struct video_mcux_csi_data *data = dev->data; - IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), - video_mcux_csi_isr, NULL, 0); + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), video_mcux_csi_isr, NULL, 0); irq_enable(DT_INST_IRQN(0)); @@ -449,9 +494,7 @@ static int video_mcux_csi_init_0(const struct device *dev) * necessary since the clock to the camera is provided by the * CSI peripheral. */ -DEVICE_DT_INST_DEFINE(0, &video_mcux_csi_init_0, - NULL, &video_mcux_csi_data_0, - &video_mcux_csi_config_0, - POST_KERNEL, CONFIG_VIDEO_MCUX_CSI_INIT_PRIORITY, - &video_mcux_csi_driver_api); +DEVICE_DT_INST_DEFINE(0, &video_mcux_csi_init_0, NULL, &video_mcux_csi_data_0, + &video_mcux_csi_config_0, POST_KERNEL, CONFIG_VIDEO_MCUX_CSI_INIT_PRIORITY, + &video_mcux_csi_driver_api); #endif diff --git a/drivers/video/video_mcux_mipi_csi2rx.c b/drivers/video/video_mcux_mipi_csi2rx.c new file mode 100644 index 000000000000..babf6d7b0e7b --- /dev/null +++ b/drivers/video/video_mcux_mipi_csi2rx.c @@ -0,0 +1,215 @@ +/* + * 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); + +#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 const struct mipi_csi2rx_config mipi_csi2rx_config_0 = { + .base = (MIPI_CSI2RX_Type *)DT_INST_REG_ADDR(0), + .sensor_dev = DEVICE_DT_GET(DT_INST_PHANDLE(0, sensor)), +}; + +static struct mipi_csi2rx_data mipi_csi2rx_data_0; + +static int mipi_csi2rx_init_0(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; + } + + /* MIPI DPHY power on and isolation off */ + PGMC_BPC4->BPC_POWER_CTRL |= (PGMC_BPC_BPC_POWER_CTRL_PSW_ON_SOFT_MASK | + PGMC_BPC_BPC_POWER_CTRL_ISO_OFF_SOFT_MASK); + + return 0; +} + +DEVICE_DT_INST_DEFINE(0, &mipi_csi2rx_init_0, NULL, &mipi_csi2rx_data_0, + &mipi_csi2rx_config_0, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, + &mipi_csi2rx_driver_api); diff --git a/dts/arm/nxp/nxp_rt10xx.dtsi b/dts/arm/nxp/nxp_rt10xx.dtsi index 714b4a303bd2..e007d33b3689 100644 --- a/dts/arm/nxp/nxp_rt10xx.dtsi +++ b/dts/arm/nxp/nxp_rt10xx.dtsi @@ -1140,3 +1140,5 @@ */ status = "disabled"; }; + +zephyr_cam_i2c: &lpi2c1 {}; diff --git a/dts/arm/nxp/nxp_rt11xx.dtsi b/dts/arm/nxp/nxp_rt11xx.dtsi index 92f95a194ff4..be2e8e0b8c6b 100644 --- a/dts/arm/nxp/nxp_rt11xx.dtsi +++ b/dts/arm/nxp/nxp_rt11xx.dtsi @@ -797,6 +797,35 @@ reg = <0x40800000 0x4000>; interrupts = <56 1>; status = "disabled"; + sdev = <&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 { @@ -1201,3 +1230,7 @@ */ status = "disabled"; }; + +zephyr_cam_i2c: &lpi2c6 {}; + +zephyr_mipi_csi: &mipi_csi2rx {}; diff --git a/dts/bindings/video/nxp,imx-csi.yaml b/dts/bindings/video/nxp,imx-csi.yaml index d2553d689118..44fd808bf20a 100644 --- a/dts/bindings/video/nxp,imx-csi.yaml +++ b/dts/bindings/video/nxp,imx-csi.yaml @@ -14,7 +14,8 @@ properties: interrupts: required: true - sensor: + sdev: required: true type: phandle - description: phandle of connected sensor device + description: phandle of the connected source device, + e.g., a mipi csi or a camera sensor diff --git a/dts/bindings/video/nxp,mipi-csi2rx.yaml b/dts/bindings/video/nxp,mipi-csi2rx.yaml new file mode 100644 index 000000000000..5bd7bcdbc2d3 --- /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: phandle of connected sensor device diff --git a/dts/bindings/video/ovti,ov5640.yaml b/dts/bindings/video/ovti,ov5640.yaml new file mode 100644 index 000000000000..eecb0e3d3192 --- /dev/null +++ b/dts/bindings/video/ovti,ov5640.yaml @@ -0,0 +1,20 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: OV5640 CMOS video sensor + +compatible: "ovti,ov5640" + +include: i2c-device.yaml + +properties: + reset-gpios: + type: phandle-array + description: | + The RESETB pin is asserted to cause a hard reset. The sensor + receives this as an active-low signal. + powerdown-gpios: + type: phandle-array + description: | + The PWDN pin is asserted to disable the sensor. The sensor + receives this as an active-high signal. diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index d1a41b7d1768..5f1daa0a04ae 100644 --- a/include/zephyr/drivers/video.h +++ b/include/zephyr/drivers/video.h @@ -628,6 +628,9 @@ void video_buffer_release(struct video_buffer *buf); /** YUYV pixel format */ #define VIDEO_PIX_FMT_YUYV video_fourcc('Y', 'U', 'Y', 'V') /* 16 Y0-Cb0 Y1-Cr0 */ +/** XYUV32 pixel format */ +#define VIDEO_PIX_FMT_XYUV32 video_fourcc('X', 'Y', 'U', 'V') /* 32 XYUV-8-8-8-8 */ + /** * * @} diff --git a/modules/Kconfig.mcux b/modules/Kconfig.mcux index 3f20250f58b0..0700438661e6 100644 --- a/modules/Kconfig.mcux +++ b/modules/Kconfig.mcux @@ -282,11 +282,6 @@ config HAS_MCUX_SMC help Set if the SMC module is present in the SoC. -config HAS_MCUX_CSI - bool - help - Set if the CMOS Sensor Interface module is present in the SoC. - config HAS_MCUX_LPTMR bool help diff --git a/samples/subsys/video/capture/README.rst b/samples/subsys/video/capture/README.rst index 29dedf2bdf40..51fc07b81d3d 100644 --- a/samples/subsys/video/capture/README.rst +++ b/samples/subsys/video/capture/README.rst @@ -7,9 +7,8 @@ Description *********** -This sample application uses the :ref:`Video API ` to retrieve video frames from the -video capture device, writes a frame count message to the console, and then -discards the video frame data. +This sample application uses the :ref:`Video API ` to capture frames from +a video capture device and then display them onto an LCD screen (if any). Requirements ************ @@ -19,6 +18,9 @@ This sample requires a video capture device (e.g. a camera). - :ref:`mimxrt1064_evk` - `MT9M114 camera module`_ +- :ref:`mimxrt1170_evk` +- `OV5640 camera module`_ + Wiring ****** @@ -27,14 +29,40 @@ J35 camera connector. A USB cable should be connected from a host to the micro USB debug connector (J41) in order to get console output via the freelink interface. +On :ref:`mimxrt1170_evk`, The OV5640 camera module should be plugged into the +J2 camera connector. A USB cable should be connected from a host to the micro +USB debug connector (J11) in order to get console output via the daplink +interface. + Building and Running ******************** -For :ref:`mimxrt1064_evk`, build this sample application with the following commands: +For testing purpose without the need of any real video capture hardware and / or a real +display device, a software pattern generator is supported and can be built as follows: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/video/capture + :board: [mimxrt1064_evk | mimxrt1170_evk/mimxrt1176/cm7] + :shield: [rk055hdmipi4ma0] + :goals: build + :compact: + +For :ref:`mimxrt1170_evk`, build this sample application with the following commands: .. zephyr-app-commands:: :zephyr-app: samples/subsys/video/capture :board: mimxrt1064_evk + :shield: huatian_mt9m114 + :goals: build + :compact: + +For :ref:`mimxrt1170_evk`, build this sample application with the following commands: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/video/capture + :board: mimxrt1170_evk/mimxrt1176/cm7 + :gen-args: -DEXTRA_CONF_FILE="boards/nxp_rt11xx.conf" + :shield: "wuxi_ov5640 rk055hdmipi4ma0" :goals: build :compact: @@ -43,11 +71,17 @@ Sample Output .. code-block:: console - Found video device: CSI - width (640,640), height (480,480) - Supported pixelformats (fourcc): - - RGBP - Use default format (640x480) + Video device: csi@402bc000 + - Capabilities: + RGBP width [640; 640; 0] height [480; 480; 0] + YUYV width [640; 640; 0] height [480; 480; 0] + - Default format: RGBP 640x480 + + Display device: display-controller@402b8000 + - Capabilities: + x_resolution = 480, y_resolution = 272, supported_pixel_formats = 40 + current_pixel_format = 32, current_orientation = 0 + Capture started Got frame 743! size: 614400; timestamp 100740 ms Got frame 744! size: 614400; timestamp 100875 ms @@ -62,3 +96,4 @@ References ********** .. _MT9M114 camera module: https://www.onsemi.com/PowerSolutions/product.do?id=MT9M114 +.. _OV5640 camera module: https://cdn.sparkfun.com/datasheets/Sensors/LightImaging/OV5640_datasheet.pdf diff --git a/samples/subsys/video/capture/boards/nxp_rt11xx.conf b/samples/subsys/video/capture/boards/nxp_rt11xx.conf new file mode 100644 index 000000000000..7252f9e67c5c --- /dev/null +++ b/samples/subsys/video/capture/boards/nxp_rt11xx.conf @@ -0,0 +1,4 @@ +CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=3686800 +CONFIG_DMA=y +CONFIG_MCUX_ELCDIF_PXP=y +CONFIG_MCUX_ELCDIF_PXP_ROTATE_90=y diff --git a/samples/subsys/video/capture/prj.conf b/samples/subsys/video/capture/prj.conf index f8d9f2b95537..ce6dcc316d67 100644 --- a/samples/subsys/video/capture/prj.conf +++ b/samples/subsys/video/capture/prj.conf @@ -4,3 +4,4 @@ CONFIG_SHELL=y CONFIG_DEVICE_SHELL=y CONFIG_PRINTK=y CONFIG_LOG=y +CONFIG_DISPLAY=y diff --git a/samples/subsys/video/capture/sample.yaml b/samples/subsys/video/capture/sample.yaml index 88c57fc74928..5fe296b17bc9 100644 --- a/samples/subsys/video/capture/sample.yaml +++ b/samples/subsys/video/capture/sample.yaml @@ -3,10 +3,20 @@ sample: tests: sample.video.capture: build_only: true - tags: video + tags: + - video + - shield + - samples + depends_on: + - video + extra_args: + - platform:mimxrt1064_evk:SHIELD=huatian_mt9m114 + - platform:mimxrt1170_evk/mimxrt1176/cm7:SHIELD="wuxi_ov5640;rk055hdmipi4m" + - platform:mimxrt1170_evk/mimxrt1176/cm7:CONF_FILE="boards/nxp_rt11xx.conf" platform_allow: - mimxrt1064_evk + - mimxrt1170_evk/mimxrt1176/cm7 - mm_swiftio - depends_on: video integration_platforms: - mimxrt1064_evk + - mimxrt1170_evk/mimxrt1176/cm7 diff --git a/samples/subsys/video/capture/src/main.c b/samples/subsys/video/capture/src/main.c index 5018bdf1ecfd..92a871a0647e 100644 --- a/samples/subsys/video/capture/src/main.c +++ b/samples/subsys/video/capture/src/main.c @@ -7,6 +7,7 @@ #include #include +#include #include #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL @@ -20,34 +21,29 @@ int main(void) struct video_buffer *buffers[2], *vbuf; struct video_format fmt; struct video_caps caps; - const struct device *video; + const struct device *video_dev; unsigned int frame = 0; size_t bsize; int i = 0; - /* Default to software video pattern generator */ - video = device_get_binding(VIDEO_DEV_SW); - if (video == NULL) { - LOG_ERR("Video device %s not found", VIDEO_DEV_SW); +#if DT_HAS_CHOSEN(zephyr_camera) + video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera)); + if (!device_is_ready(video_dev)) { + LOG_ERR("%s device is not ready", video_dev->name); return 0; } - - /* But would be better to use a real video device if any */ -#if defined(CONFIG_VIDEO_MCUX_CSI) - const struct device *const dev = DEVICE_DT_GET_ONE(nxp_imx_csi); - - if (!device_is_ready(dev)) { - LOG_ERR("%s: device not ready.\n", dev->name); +#else + video_dev = device_get_binding(VIDEO_DEV_SW); + if (video_dev == NULL) { + LOG_ERR("%s device not found", VIDEO_DEV_SW); return 0; } - - video = dev; #endif - printk("- Device name: %s\n", video->name); + printk("Video device: %s\n", video_dev->name); /* Get capabilities */ - if (video_get_caps(video, VIDEO_EP_OUT, &caps)) { + if (video_get_caps(video_dev, VIDEO_EP_OUT, &caps)) { LOG_ERR("Unable to retrieve video capabilities"); return 0; } @@ -57,26 +53,50 @@ int main(void) const struct video_format_cap *fcap = &caps.format_caps[i]; /* fourcc to string */ printk(" %c%c%c%c width [%u; %u; %u] height [%u; %u; %u]\n", - (char)fcap->pixelformat, - (char)(fcap->pixelformat >> 8), - (char)(fcap->pixelformat >> 16), - (char)(fcap->pixelformat >> 24), - fcap->width_min, fcap->width_max, fcap->width_step, - fcap->height_min, fcap->height_max, fcap->height_step); + (char)fcap->pixelformat, (char)(fcap->pixelformat >> 8), + (char)(fcap->pixelformat >> 16), (char)(fcap->pixelformat >> 24), + fcap->width_min, fcap->width_max, fcap->width_step, fcap->height_min, + fcap->height_max, fcap->height_step); i++; } /* Get default/native format */ - if (video_get_format(video, VIDEO_EP_OUT, &fmt)) { + if (video_get_format(video_dev, VIDEO_EP_OUT, &fmt)) { LOG_ERR("Unable to retrieve video format"); return 0; } printk("- Default format: %c%c%c%c %ux%u\n", (char)fmt.pixelformat, - (char)(fmt.pixelformat >> 8), - (char)(fmt.pixelformat >> 16), - (char)(fmt.pixelformat >> 24), - fmt.width, fmt.height); + (char)(fmt.pixelformat >> 8), (char)(fmt.pixelformat >> 16), + (char)(fmt.pixelformat >> 24), fmt.width, fmt.height); + +#if DT_HAS_CHOSEN(zephyr_display) + struct display_capabilities capabilities; + struct display_buffer_descriptor buf_desc; + const struct device *display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); + + if (!device_is_ready(display_dev)) { + LOG_ERR("Device %s not found", display_dev->name); + return 0; + } + + printk("\nDisplay device: %s\n", display_dev->name); + + display_get_capabilities(display_dev, &capabilities); + + printk("- Capabilities:\n"); + printk(" x_resolution = %u, y_resolution = %u, supported_pixel_formats = %u\n" + " current_pixel_format = %u, current_orientation = %u\n\n", + capabilities.x_resolution, capabilities.y_resolution, + capabilities.supported_pixel_formats, capabilities.current_pixel_format, + capabilities.current_orientation); + +#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) + /* Default output pixel format of the camera pipeline on i.MX RT11XX is XRGB32 */ + display_set_pixel_format(display_dev, PIXEL_FORMAT_ARGB_8888); +#endif + +#endif /* Size to allocate for each buffer */ bsize = fmt.pitch * fmt.height; @@ -89,11 +109,11 @@ int main(void) return 0; } - video_enqueue(video, VIDEO_EP_OUT, buffers[i]); + video_enqueue(video_dev, VIDEO_EP_OUT, buffers[i]); } /* Start video capture */ - if (video_stream_start(video)) { + if (video_stream_start(video_dev)) { LOG_ERR("Unable to start capture (interface)"); return 0; } @@ -104,16 +124,26 @@ int main(void) while (1) { int err; - err = video_dequeue(video, VIDEO_EP_OUT, &vbuf, K_FOREVER); + err = video_dequeue(video_dev, VIDEO_EP_OUT, &vbuf, K_FOREVER); if (err) { LOG_ERR("Unable to dequeue video buf"); return 0; } - printk("\rGot frame %u! size: %u; timestamp %u ms", - frame++, vbuf->bytesused, vbuf->timestamp); + printk("\rGot frame %u! size: %u; timestamp %u ms\n", frame++, vbuf->bytesused, + vbuf->timestamp); + +#if DT_HAS_CHOSEN(zephyr_display) + buf_desc.buf_size = vbuf->bytesused; + buf_desc.width = fmt.width; + buf_desc.pitch = buf_desc.width; + buf_desc.height = fmt.height; - err = video_enqueue(video, VIDEO_EP_OUT, vbuf); + display_write(display_dev, 0, 0, &buf_desc, vbuf->buffer); + + display_blanking_off(display_dev); +#endif + err = video_enqueue(video_dev, VIDEO_EP_OUT, vbuf); if (err) { LOG_ERR("Unable to requeue video buf"); return 0; diff --git a/soc/nxp/imxrt/imxrt10xx/Kconfig b/soc/nxp/imxrt/imxrt10xx/Kconfig index 51b894ae304f..b91fbe0b3ec5 100644 --- a/soc/nxp/imxrt/imxrt10xx/Kconfig +++ b/soc/nxp/imxrt/imxrt10xx/Kconfig @@ -79,7 +79,6 @@ config SOC_MIMXRT1051 select INIT_ARM_PLL select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 - select HAS_MCUX_CSI select HAS_MCUX_FLEXCAN config SOC_MIMXRT1052 @@ -93,7 +92,6 @@ config SOC_MIMXRT1052 select INIT_ENET_PLL if NET_L2_ETHERNET && ETH_DRIVER select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 - select HAS_MCUX_CSI select HAS_MCUX_FLEXCAN select HAS_MCUX_PWM select HAS_MCUX_SRC @@ -107,7 +105,6 @@ config SOC_MIMXRT1061 select INIT_ARM_PLL select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 - select HAS_MCUX_CSI select HAS_MCUX_FLEXCAN config SOC_MIMXRT1062 @@ -124,7 +121,6 @@ config SOC_MIMXRT1062 select INIT_ENET_PLL if NET_L2_ETHERNET && ETH_DRIVER select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 - select HAS_MCUX_CSI select HAS_MCUX_FLEXCAN select HAS_MCUX_I2S select HAS_MCUX_ADC_ETC @@ -146,6 +142,5 @@ config SOC_MIMXRT1064 select INIT_ENET_PLL if NET_L2_ETHERNET && ETH_DRIVER select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 - select HAS_MCUX_CSI select HAS_MCUX_FLEXCAN select HAS_SWO diff --git a/soc/nxp/imxrt/imxrt11xx/soc.c b/soc/nxp/imxrt/imxrt11xx/soc.c index 9eee69ad66ad..42bc4a764517 100644 --- a/soc/nxp/imxrt/imxrt11xx/soc.c +++ b/soc/nxp/imxrt/imxrt11xx/soc.c @@ -398,6 +398,11 @@ static ALWAYS_INLINE void clock_init(void) rootCfg.mux = kCLOCK_LPI2C5_ClockRoot_MuxOscRc48MDiv2; rootCfg.div = 1; CLOCK_SetRootClock(kCLOCK_Root_Lpi2c5, &rootCfg); + + /* Configure Lpi2c6 using Osc24M */ + rootCfg.mux = kCLOCK_LPI2C6_ClockRoot_MuxOsc24MOut; + rootCfg.div = 12; + CLOCK_SetRootClock(kCLOCK_Root_Lpi2c6, &rootCfg); #endif @@ -451,6 +456,23 @@ 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); +#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/app.overlay b/tests/drivers/build_all/video/app.overlay index a38cfdd0e133..e06d2940817a 100644 --- a/tests/drivers/build_all/video/app.overlay +++ b/tests/drivers/build_all/video/app.overlay @@ -47,6 +47,13 @@ reg = <0x2>; reset-gpios = <&test_gpio 0 0>; }; + + test_i2c_ov5640: ov5640@3 { + compatible = "ovti,ov5640"; + reg = <0x3>; + reset-gpios = <&test_gpio 0 0>; + powerdown-gpios = <&test_gpio 1 0>; + }; }; }; }; diff --git a/tests/drivers/build_all/video/testcase.yaml b/tests/drivers/build_all/video/testcase.yaml index 08dd430da731..2acb801b9c64 100644 --- a/tests/drivers/build_all/video/testcase.yaml +++ b/tests/drivers/build_all/video/testcase.yaml @@ -13,4 +13,6 @@ tests: - gpio - i2c drivers.video.mcux_csi.build: - platform_allow: mimxrt1064_evk + platform_allow: + - mimxrt1064_evk + - mimxrt1170_evk/mimxrt1176/cm7