diff --git a/boards/madmachine/mm_swiftio/mm_swiftio.dts b/boards/madmachine/mm_swiftio/mm_swiftio.dts index 0ad825814d3d..14af45b01b80 100644 --- a/boards/madmachine/mm_swiftio/mm_swiftio.dts +++ b/boards/madmachine/mm_swiftio/mm_swiftio.dts @@ -115,7 +115,7 @@ port { ov7725_ep_out: endpoint { - remote-endpoint = <&csi_ep_in>; + remote-endpoint-label = "csi_ep_in"; }; }; }; @@ -192,13 +192,12 @@ &csi { status = "okay"; - source = <&ov7725>; pinctrl-0 = <&pinmux_csi>; pinctrl-names = "default"; port { csi_ep_in: endpoint { - remote-endpoint = <&ov7725_ep_out>; + remote-endpoint-label = "ov7725_ep_out"; }; }; }; diff --git a/boards/shields/dvp_fpc24_mt9m114/boards/mimxrt1060_evkb.overlay b/boards/shields/dvp_fpc24_mt9m114/boards/mimxrt1060_evkb.overlay deleted file mode 100644 index a4aa7a273080..000000000000 --- a/boards/shields/dvp_fpc24_mt9m114/boards/mimxrt1060_evkb.overlay +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2024 NXP - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&dvp_fpc24_interface { - source = <&mt9m114>; -}; diff --git a/boards/shields/dvp_fpc24_mt9m114/boards/mimxrt1064_evk.overlay b/boards/shields/dvp_fpc24_mt9m114/boards/mimxrt1064_evk.overlay deleted file mode 100644 index a4aa7a273080..000000000000 --- a/boards/shields/dvp_fpc24_mt9m114/boards/mimxrt1064_evk.overlay +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2024 NXP - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&dvp_fpc24_interface { - source = <&mt9m114>; -}; diff --git a/boards/shields/dvp_fpc24_mt9m114/dvp_fpc24_mt9m114.overlay b/boards/shields/dvp_fpc24_mt9m114/dvp_fpc24_mt9m114.overlay index 52988535182d..49bc018779a8 100644 --- a/boards/shields/dvp_fpc24_mt9m114/dvp_fpc24_mt9m114.overlay +++ b/boards/shields/dvp_fpc24_mt9m114/dvp_fpc24_mt9m114.overlay @@ -17,7 +17,7 @@ port { mt9m114_ep_out: endpoint { - remote-endpoint = <&dfi_ep_in>; + remote-endpoint-label = "dfi_ep_in"; }; }; }; @@ -28,7 +28,7 @@ port { dfi_ep_in: endpoint { - remote-endpoint = <&mt9m114_ep_out>; + remote-endpoint-label = "mt9m114_ep_out"; }; }; }; diff --git a/boards/shields/nxp_btb44_ov5640/nxp_btb44_ov5640.overlay b/boards/shields/nxp_btb44_ov5640/nxp_btb44_ov5640.overlay index e7c179f5d235..9eca70c27cde 100644 --- a/boards/shields/nxp_btb44_ov5640/nxp_btb44_ov5640.overlay +++ b/boards/shields/nxp_btb44_ov5640/nxp_btb44_ov5640.overlay @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + /{ chosen { zephyr,camera = &nxp_csi; @@ -21,7 +23,9 @@ port { ov5640_ep_out: endpoint { - remote-endpoint = <&mipi_csi2rx_ep_in>; + remote-endpoint-label = "mipi_csi2rx_ep_in"; + bus-type = ; + data-lanes = <1 2>; }; }; }; @@ -30,14 +34,13 @@ &nxp_mipi_csi { status = "okay"; - sensor = <&ov5640>; - ports { port@1 { reg = <1>; mipi_csi2rx_ep_in: endpoint { - remote-endpoint = <&ov5640_ep_out>; + remote-endpoint-label = "ov5640_ep_out"; + data-lanes = <1 2>; }; }; }; diff --git a/drivers/clock_control/clock_control_mcux_ccm_rev2.c b/drivers/clock_control/clock_control_mcux_ccm_rev2.c index 3a709a776761..a3c02451d18b 100644 --- a/drivers/clock_control/clock_control_mcux_ccm_rev2.c +++ b/drivers/clock_control/clock_control_mcux_ccm_rev2.c @@ -225,6 +225,19 @@ static int mcux_ccm_get_subsys_rate(const struct device *dev, clock_root = kCLOCK_Root_Netc; break; #endif + +#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) + case IMX_CCM_MIPI_CSI2RX_ROOT_CLK: + clock_root = kCLOCK_Root_Csi2; + break; + case IMX_CCM_MIPI_CSI2RX_ESC_CLK: + clock_root = kCLOCK_Root_Csi2_Esc; + break; + case IMX_CCM_MIPI_CSI2RX_UI_CLK: + clock_root = kCLOCK_Root_Csi2_Ui; + break; +#endif + default: return -EINVAL; } @@ -264,6 +277,16 @@ static int CCM_SET_FUNC_ATTR mcux_ccm_set_subsys_rate(const struct device *dev, */ return flexspi_clock_set_freq(clock_name, clock_rate); #endif + +#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) + case IMX_CCM_MIPI_CSI2RX_ROOT_CLK: + return mipi_csi2rx_clock_set_freq(kCLOCK_Root_Csi2, clock_rate); + case IMX_CCM_MIPI_CSI2RX_UI_CLK: + return mipi_csi2rx_clock_set_freq(kCLOCK_Root_Csi2_Ui, clock_rate); + case IMX_CCM_MIPI_CSI2RX_ESC_CLK: + return mipi_csi2rx_clock_set_freq(kCLOCK_Root_Csi2_Esc, clock_rate); +#endif + default: /* Silence unused variable warning */ ARG_UNUSED(clock_rate); diff --git a/drivers/video/Kconfig.mcux_csi b/drivers/video/Kconfig.mcux_csi index 88b57d3b461f..e9bc48c7bd09 100644 --- a/drivers/video/Kconfig.mcux_csi +++ b/drivers/video/Kconfig.mcux_csi @@ -11,7 +11,7 @@ config VIDEO_MCUX_CSI config VIDEO_MCUX_CSI_INIT_PRIORITY int "NXP MCUX CSI init priority" - default 61 + default 59 depends on VIDEO_MCUX_CSI help Initialization priority for the CSI interface on an NXP MCUX device. diff --git a/drivers/video/video_mcux_csi.c b/drivers/video/video_mcux_csi.c index c6a82accdb40..244dc61ab6a6 100644 --- a/drivers/video/video_mcux_csi.c +++ b/drivers/video/video_mcux_csi.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Linaro Limited + * Copyright 2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +18,16 @@ #include #endif +#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) +#define DEVICE_DT_INST_GET_SOURCE_DEV(n) \ + DEVICE_DT_GET(DT_PARENT(DT_GPARENT(DT_NODELABEL(DT_STRING_TOKEN( \ + DT_CHILD(DT_INST_CHILD(n, port), endpoint), remote_endpoint_label))))) +#else +#define DEVICE_DT_INST_GET_SOURCE_DEV(n) \ + DEVICE_DT_GET(DT_GPARENT(DT_NODELABEL(DT_STRING_TOKEN( \ + DT_CHILD(DT_INST_CHILD(n, port), endpoint), remote_endpoint_label)))) +#endif + struct video_mcux_csi_config { CSI_Type *base; const struct device *source_dev; @@ -32,25 +43,6 @@ struct video_mcux_csi_data { struct k_poll_signal *signal; }; -static inline unsigned int video_pix_fmt_bpp(uint32_t pixelformat) -{ - switch (pixelformat) { - case VIDEO_PIX_FMT_BGGR8: - case VIDEO_PIX_FMT_GBRG8: - case VIDEO_PIX_FMT_GRBG8: - case VIDEO_PIX_FMT_RGGB8: - return 1; - 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) { struct video_mcux_csi_data *data = user_data; @@ -450,6 +442,42 @@ static int video_mcux_csi_set_signal(const struct device *dev, enum video_endpoi } #endif +static int video_mcux_csi_set_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival) +{ + const struct video_mcux_csi_config *config = dev->config; + + return video_set_frmival(config->source_dev, ep, frmival); +} + +static int video_mcux_csi_get_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival) +{ + const struct video_mcux_csi_config *config = dev->config; + + return video_get_frmival(config->source_dev, ep, frmival); +} + +static int video_mcux_csi_enum_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival_enum *fie) +{ + const struct video_mcux_csi_config *config = dev->config; + const struct video_format *fie_fmt = fie->format; + int ret; + +#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) + struct video_format converted_fmt = *fie->format; + + video_pix_fmt_convert(&converted_fmt, false); + fie->format = &converted_fmt; +#endif + + ret = video_enum_frmival(config->source_dev, ep, fie); + fie->format = fie_fmt; + + return ret; +} + static const struct video_driver_api video_mcux_csi_driver_api = { .set_format = video_mcux_csi_set_fmt, .get_format = video_mcux_csi_get_fmt, @@ -461,6 +489,9 @@ static const struct video_driver_api video_mcux_csi_driver_api = { .set_ctrl = video_mcux_csi_set_ctrl, .get_ctrl = video_mcux_csi_get_ctrl, .get_caps = video_mcux_csi_get_caps, + .set_frmival = video_mcux_csi_set_frmival, + .get_frmival = video_mcux_csi_get_frmival, + .enum_frmival = video_mcux_csi_enum_frmival, #ifdef CONFIG_POLL .set_signal = video_mcux_csi_set_signal, #endif @@ -471,7 +502,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), - .source_dev = DEVICE_DT_GET(DT_INST_PHANDLE(0, source)), + .source_dev = DEVICE_DT_INST_GET_SOURCE_DEV(0), .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), }; @@ -490,11 +521,6 @@ static int video_mcux_csi_init_0(const struct device *dev) return video_mcux_csi_init(dev); } -/* CONFIG_KERNEL_INIT_PRIORITY_DEVICE is used to make sure the - * CSI peripheral is initialized before the camera, which is - * 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); diff --git a/drivers/video/video_mcux_mipi_csi2rx.c b/drivers/video/video_mcux_mipi_csi2rx.c index 5ad56383fda4..a73c17efa07f 100644 --- a/drivers/video/video_mcux_mipi_csi2rx.c +++ b/drivers/video/video_mcux_mipi_csi2rx.c @@ -6,20 +6,25 @@ #define DT_DRV_COMPAT nxp_mipi_csi2rx +#include #include +#include #include #include +#include #include LOG_MODULE_REGISTER(video_mipi_csi2rx, CONFIG_VIDEO_LOG_LEVEL); -/* - * 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 +#define MAX_SUPPORTED_PIXEL_RATE MHZ(96) + +#define ABS(a, b) (a > b ? a - b : b - a) + +#define DEVICE_DT_INST_GET_SENSOR_DEV(n) \ + DEVICE_DT_GET(DT_GPARENT(DT_NODELABEL( \ + DT_STRING_TOKEN(DT_CHILD(DT_CHILD(DT_INST_CHILD(n, ports), port_1), endpoint), \ + remote_endpoint_label)))) struct mipi_csi2rx_config { const MIPI_CSI2RX_Type *base; @@ -28,102 +33,106 @@ struct mipi_csi2rx_config { struct mipi_csi2rx_data { csi2rx_config_t csi2rxConfig; + const struct device *clock_dev; + clock_control_subsys_t clock_root; + clock_control_subsys_t clock_ui; + clock_control_subsys_t clock_esc; }; -static int mipi_csi2rx_set_fmt(const struct device *dev, enum video_endpoint_id ep, - struct video_format *fmt) +struct mipi_csi2rx_tHsSettleEscClk_config { + uint64_t pixel_rate; + uint8_t tHsSettle_EscClk; +}; + +/* Must be in pixel rate ascending order */ +const struct mipi_csi2rx_tHsSettleEscClk_config tHsSettleEscClk_configs[] = { + {MHZ(24), 0x24}, + {MHZ(48), 0x12}, + {MHZ(96), 0x09}, +}; + +static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endpoint_id ep) { 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; + uint8_t bpp; + uint64_t sensor_pixel_rate; + uint32_t root_clk_rate, ui_clk_rate, sensor_byte_clk, best_match; + int ret, ind = 0; + struct video_format fmt; + + ret = video_get_format(config->sensor_dev, ep, &fmt); + if (ret) { + LOG_ERR("Cannot get sensor_dev pixel format"); + return ret; + } - /* - * 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; - } + ret = video_get_ctrl(config->sensor_dev, VIDEO_CID_PIXEL_RATE, &sensor_pixel_rate); + if (ret) { + LOG_ERR("Can not get sensor_dev pixel rate"); + return ret; } - if (i == ARRAY_SIZE(csi2rxHsSettle)) { - LOG_ERR("Unsupported resolution"); + if (sensor_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { + LOG_ERR("Sensor pixel rate is not supported"); return -ENOTSUP; } - drv_data->csi2rxConfig = csi2rxConfig; + bpp = video_pix_fmt_bpp(fmt.pixelformat) * 8; + sensor_byte_clk = sensor_pixel_rate * bpp / drv_data->csi2rxConfig.laneNum / 8; + + ret = clock_control_get_rate(drv_data->clock_dev, drv_data->clock_root, &root_clk_rate); + if (ret) { + return ret; + } + + if (sensor_byte_clk > root_clk_rate) { + ret = clock_control_set_rate(drv_data->clock_dev, drv_data->clock_root, + (clock_control_subsys_rate_t)sensor_byte_clk); + if (ret) { + return ret; + } + } + + ret = clock_control_get_rate(drv_data->clock_dev, drv_data->clock_ui, &ui_clk_rate); + if (ret) { + return ret; + } + + if (sensor_pixel_rate > ui_clk_rate) { + ret = clock_control_set_rate( + drv_data->clock_dev, drv_data->clock_ui, + (clock_control_subsys_rate_t)(uint32_t)sensor_pixel_rate); + if (ret) { + return ret; + } + } + + /* Find the supported sensor_pixel_rate closest to the desired one */ + best_match = tHsSettleEscClk_configs[ind].pixel_rate; + for (uint8_t i = 0; i < ARRAY_SIZE(tHsSettleEscClk_configs); i++) { + if (ABS(tHsSettleEscClk_configs[i].pixel_rate, sensor_pixel_rate) < + ABS(tHsSettleEscClk_configs[i].pixel_rate, best_match)) { + best_match = tHsSettleEscClk_configs[i].pixel_rate; + ind = i; + } + } + + drv_data->csi2rxConfig.tHsSettle_EscClk = tHsSettleEscClk_configs[ind].tHsSettle_EscClk; + + return ret; +} + +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; if (video_set_format(config->sensor_dev, ep, fmt)) { return -EIO; } - return 0; + return mipi_csi2rx_update_settings(dev, ep); } static int mipi_csi2rx_get_fmt(const struct device *dev, enum video_endpoint_id ep, @@ -182,32 +191,176 @@ static int mipi_csi2rx_get_caps(const struct device *dev, enum video_endpoint_id return video_get_caps(config->sensor_dev, ep, caps); } +static inline int mipi_csi2rx_set_ctrl(const struct device *dev, unsigned int cid, void *value) +{ + const struct mipi_csi2rx_config *config = dev->config; + + if (config->sensor_dev) { + return video_set_ctrl(config->sensor_dev, cid, value); + } + + return -ENOTSUP; +} + +static int mipi_csi2rx_set_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival) +{ + const struct mipi_csi2rx_config *config = dev->config; + int ret; + + ret = video_set_frmival(config->sensor_dev, ep, frmival); + if (ret) { + LOG_ERR("Cannot set sensor_dev frmival"); + return ret; + } + + ret = mipi_csi2rx_update_settings(dev, ep); + + return ret; +} + +static int mipi_csi2rx_get_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival) +{ + const struct mipi_csi2rx_config *config = dev->config; + + return video_get_frmival(config->sensor_dev, ep, frmival); +} + +static uint64_t mipi_csi2rx_cal_frame_size(const struct video_format *fmt) +{ + return fmt->height * fmt->width * video_pix_fmt_bpp(fmt->pixelformat) * 8; +} + +static uint64_t mipi_csi2rx_estimate_pixel_rate(const struct video_frmival *cur_fmival, + const struct video_frmival *fie_frmival, + const struct video_format *cur_format, + const struct video_format *fie_format, + uint64_t cur_pixel_rate, uint8_t laneNum) +{ + return mipi_csi2rx_cal_frame_size(cur_format) * fie_frmival->denominator * + cur_fmival->numerator * cur_pixel_rate / + (mipi_csi2rx_cal_frame_size(fie_format) * fie_frmival->numerator * + cur_fmival->denominator); +} + +static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival_enum *fie) +{ + const struct mipi_csi2rx_config *config = dev->config; + struct mipi_csi2rx_data *drv_data = dev->data; + int ret; + uint64_t cur_pixel_rate, est_pixel_rate; + struct video_frmival cur_frmival; + struct video_format cur_fmt; + + ret = video_enum_frmival(config->sensor_dev, ep, fie); + if (ret) { + return ret; + } + + ret = video_get_ctrl(config->sensor_dev, VIDEO_CID_PIXEL_RATE, &cur_pixel_rate); + if (ret) { + LOG_ERR("Cannot get sensor_dev pixel rate"); + return ret; + } + + ret = video_get_frmival(config->sensor_dev, ep, &cur_frmival); + if (ret) { + LOG_ERR("Cannot get sensor_dev frame rate"); + return ret; + } + + ret = video_get_format(config->sensor_dev, ep, &cur_fmt); + if (ret) { + LOG_ERR("Cannot get sensor_dev format"); + return ret; + } + + if (fie->type == VIDEO_FRMIVAL_TYPE_DISCRETE) { + est_pixel_rate = mipi_csi2rx_estimate_pixel_rate( + &cur_frmival, &fie->discrete, &cur_fmt, fie->format, cur_pixel_rate, + drv_data->csi2rxConfig.laneNum); + if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { + return -EINVAL; + } + + } else { + /* Check the lane rate of the lower bound framerate */ + est_pixel_rate = mipi_csi2rx_estimate_pixel_rate( + &cur_frmival, &fie->stepwise.min, &cur_fmt, fie->format, cur_pixel_rate, + drv_data->csi2rxConfig.laneNum); + if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { + return -EINVAL; + } + + /* Check the lane rate of the upper bound framerate */ + est_pixel_rate = mipi_csi2rx_estimate_pixel_rate( + &cur_frmival, &fie->stepwise.max, &cur_fmt, fie->format, cur_pixel_rate, + drv_data->csi2rxConfig.laneNum); + if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { + fie->stepwise.max.denominator = + (mipi_csi2rx_cal_frame_size(&cur_fmt) * MAX_SUPPORTED_PIXEL_RATE * + cur_frmival.denominator) / + (mipi_csi2rx_cal_frame_size(fie->format) * cur_pixel_rate * + cur_frmival.numerator); + fie->stepwise.max.numerator = 1; + } + } + + return 0; +} + 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, + .set_ctrl = mipi_csi2rx_set_ctrl, + .set_frmival = mipi_csi2rx_set_frmival, + .get_frmival = mipi_csi2rx_get_frmival, + .enum_frmival = mipi_csi2rx_enum_frmival, }; static int mipi_csi2rx_init(const struct device *dev) { const struct mipi_csi2rx_config *config = dev->config; + struct mipi_csi2rx_data *drv_data = dev->data; + int ret; /* Check if there is any sensor device */ if (!device_is_ready(config->sensor_dev)) { return -ENODEV; } - return 0; + /* + * CSI2 escape clock should be in the range [60, 80] Mhz. We set it + * to 60 Mhz. + */ + ret = clock_control_set_rate(drv_data->clock_dev, drv_data->clock_esc, + (clock_control_subsys_rate_t)MHZ(60)); + if (ret) { + return ret; + } + + return mipi_csi2rx_update_settings(dev, VIDEO_EP_ALL); } #define MIPI_CSI2RX_INIT(n) \ - static struct mipi_csi2rx_data mipi_csi2rx_data_##n; \ + static struct mipi_csi2rx_data mipi_csi2rx_data_##n = { \ + .csi2rxConfig.laneNum = \ + DT_PROP_LEN(DT_CHILD(DT_CHILD(DT_INST_CHILD(n, ports), port_1), endpoint), \ + data_lanes), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_root = (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_IDX(n, 0, name), \ + .clock_ui = (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_IDX(n, 1, name), \ + .clock_esc = (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_IDX(n, 2, name), \ + }; \ \ 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)), \ + .sensor_dev = DEVICE_DT_INST_GET_SENSOR_DEV(n), \ }; \ \ DEVICE_DT_INST_DEFINE(n, &mipi_csi2rx_init, NULL, &mipi_csi2rx_data_##n, \ diff --git a/drivers/video/video_stm32_dcmi.c b/drivers/video/video_stm32_dcmi.c index 135f38d238da..2af5dc14b5a9 100644 --- a/drivers/video/video_stm32_dcmi.c +++ b/drivers/video/video_stm32_dcmi.c @@ -54,22 +54,6 @@ struct video_stm32_dcmi_config { const struct stream dma; }; -static inline unsigned int video_pix_fmt_bpp(uint32_t pixelformat) -{ - switch (pixelformat) { - case VIDEO_PIX_FMT_BGGR8: - case VIDEO_PIX_FMT_GBRG8: - case VIDEO_PIX_FMT_GRBG8: - case VIDEO_PIX_FMT_RGGB8: - return 1; - case VIDEO_PIX_FMT_RGB565: - case VIDEO_PIX_FMT_YUYV: - return 2; - default: - return 0; - } -} - void HAL_DCMI_ErrorCallback(DCMI_HandleTypeDef *hdcmi) { LOG_WRN("%s", __func__); diff --git a/dts/arm/nxp/nxp_rt11xx.dtsi b/dts/arm/nxp/nxp_rt11xx.dtsi index 5dd41ed398b6..30b6c5a40457 100644 --- a/dts/arm/nxp/nxp_rt11xx.dtsi +++ b/dts/arm/nxp/nxp_rt11xx.dtsi @@ -888,11 +888,10 @@ reg = <0x40800000 0x4000>; interrupts = <56 1>; status = "disabled"; - source = <&mipi_csi2rx>; port { csi_ep_in: endpoint { - remote-endpoint = <&mipi_csi2rx_ep_out>; + remote-endpoint-label = "mipi_csi2rx_ep_out"; }; }; }; @@ -901,6 +900,9 @@ compatible = "nxp,mipi-csi2rx"; reg = <0x40810000 0x200>; status = "disabled"; + clocks = <&ccm IMX_CCM_MIPI_CSI2RX_ROOT_CLK 0 0>, + <&ccm IMX_CCM_MIPI_CSI2RX_UI_CLK 0 0>, + <&ccm IMX_CCM_MIPI_CSI2RX_ESC_CLK 0 0>; ports { #address-cells = <1>; @@ -909,7 +911,7 @@ port@0 { reg = <0>; mipi_csi2rx_ep_out: endpoint { - remote-endpoint = <&csi_ep_in>; + remote-endpoint-label = "csi_ep_in"; }; }; diff --git a/dts/bindings/video/aptina,mt9m114.yaml b/dts/bindings/video/aptina,mt9m114.yaml index 21dbefb5419e..f8fcb6dc31d7 100644 --- a/dts/bindings/video/aptina,mt9m114.yaml +++ b/dts/bindings/video/aptina,mt9m114.yaml @@ -1,4 +1,6 @@ # Copyright (c) 2019, Linaro Limited +# Copyright 2024 NXP +# # SPDX-License-Identifier: Apache-2.0 description: MT9M114 CMOS video sensor @@ -6,3 +8,7 @@ description: MT9M114 CMOS video sensor compatible: "aptina,mt9m114" include: i2c-device.yaml + +child-binding: + child-binding: + include: video-interfaces.yaml diff --git a/dts/bindings/video/nxp,imx-csi.yaml b/dts/bindings/video/nxp,imx-csi.yaml index bf7fd01eeaad..ee3790dffb00 100644 --- a/dts/bindings/video/nxp,imx-csi.yaml +++ b/dts/bindings/video/nxp,imx-csi.yaml @@ -1,5 +1,6 @@ # # Copyright (c) 2019, Linaro Limited +# Copyright 2024 NXP # # SPDX-License-Identifier: Apache-2.0 # @@ -14,8 +15,6 @@ properties: interrupts: required: true - source: - required: true - type: phandle - description: the connected source device, - e.g., a mipi csi or a camera sensor +child-binding: + child-binding: + include: video-interfaces.yaml diff --git a/dts/bindings/video/nxp,mipi-csi2rx.yaml b/dts/bindings/video/nxp,mipi-csi2rx.yaml index 1726d63b3299..727289e567b3 100644 --- a/dts/bindings/video/nxp,mipi-csi2rx.yaml +++ b/dts/bindings/video/nxp,mipi-csi2rx.yaml @@ -10,8 +10,7 @@ compatible: "nxp,mipi-csi2rx" include: [base.yaml] -properties: - sensor: - required: true - type: phandle - description: the connected camera sensor +child-binding: + child-binding: + child-binding: + include: video-interfaces.yaml diff --git a/dts/bindings/video/ovti,ov5640.yaml b/dts/bindings/video/ovti,ov5640.yaml index eecb0e3d3192..9b10d9b2d27f 100644 --- a/dts/bindings/video/ovti,ov5640.yaml +++ b/dts/bindings/video/ovti,ov5640.yaml @@ -18,3 +18,7 @@ properties: description: | The PWDN pin is asserted to disable the sensor. The sensor receives this as an active-high signal. + +child-binding: + child-binding: + include: video-interfaces.yaml diff --git a/dts/bindings/video/ovti,ov7725.yaml b/dts/bindings/video/ovti,ov7725.yaml index 15b557d78e74..a3648234d651 100644 --- a/dts/bindings/video/ovti,ov7725.yaml +++ b/dts/bindings/video/ovti,ov7725.yaml @@ -13,3 +13,7 @@ properties: reset. The sensor receives this as an active-low signal. include: i2c-device.yaml + +child-binding: + child-binding: + include: video-interfaces.yaml diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index b90707383166..fb647cac70fb 100644 --- a/include/zephyr/drivers/video.h +++ b/include/zephyr/drivers/video.h @@ -827,6 +827,30 @@ void video_buffer_release(struct video_buffer *buf); * @} */ +/** + * @brief Get number of bytes per pixel of a pixel format + * + * @param pixfmt FourCC pixel format value (\ref video_pixel_formats). + */ +static inline unsigned int video_pix_fmt_bpp(uint32_t pixfmt) +{ + switch (pixfmt) { + case VIDEO_PIX_FMT_BGGR8: + case VIDEO_PIX_FMT_GBRG8: + case VIDEO_PIX_FMT_GRBG8: + case VIDEO_PIX_FMT_RGGB8: + return 1; + 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; + } +} + #ifdef __cplusplus } #endif diff --git a/include/zephyr/dt-bindings/clock/imx_ccm_rev2.h b/include/zephyr/dt-bindings/clock/imx_ccm_rev2.h index 0778acb941cf..f7d6b7afcd16 100644 --- a/include/zephyr/dt-bindings/clock/imx_ccm_rev2.h +++ b/include/zephyr/dt-bindings/clock/imx_ccm_rev2.h @@ -134,6 +134,11 @@ /* NETC */ #define IMX_CCM_NETC_CLK 0x1800UL +/* MIPI CSI2RX */ +#define IMX_CCM_MIPI_CSI2RX_ROOT_CLK 0x1900UL +#define IMX_CCM_MIPI_CSI2RX_UI_CLK 0x2000UL +#define IMX_CCM_MIPI_CSI2RX_ESC_CLK 0x2100UL + /* QTMR */ #define IMX_CCM_QTMR_CLK 0x6000UL #define IMX_CCM_QTMR1_CLK 0x6000UL diff --git a/soc/nxp/imxrt/imxrt11xx/soc.c b/soc/nxp/imxrt/imxrt11xx/soc.c index 7f1fca10c651..4f1cca4dcc6d 100644 --- a/soc/nxp/imxrt/imxrt11xx/soc.c +++ b/soc/nxp/imxrt/imxrt11xx/soc.c @@ -474,17 +474,6 @@ static ALWAYS_INLINE void clock_init(void) 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); @@ -682,6 +671,41 @@ void imxrt_post_init_display_interface(void) #endif +#if CONFIG_VIDEO_MCUX_MIPI_CSI2RX +int mipi_csi2rx_clock_set_freq(clock_root_t clock_root, uint32_t rate) +{ + clock_root_config_t rootCfg = {0}; + uint32_t freq; + clock_name_t clk_source; + + switch (clock_root) { + case kCLOCK_Root_Csi2: + rootCfg.mux = kCLOCK_CSI2_ClockRoot_MuxSysPll3Out; + break; + case kCLOCK_Root_Csi2_Esc: + rootCfg.mux = kCLOCK_CSI2_ESC_ClockRoot_MuxSysPll3Out; + break; + case kCLOCK_Root_Csi2_Ui: + rootCfg.mux = kCLOCK_CSI2_UI_ClockRoot_MuxSysPll3Out; + break; + default: + return -EINVAL; + } + + clk_source = CLOCK_GetRootClockSource(clock_root, rootCfg.mux); + freq = CLOCK_GetFreq(clk_source); + if (rate > freq) { + LOG_ERR("Requested rate is higher than the maximum clock frequency"); + return -EINVAL; + } + + rootCfg.div = (uint32_t)freq / rate; + CLOCK_SetRootClock(clock_root, &rootCfg); + + return 0; +} +#endif + /** * * @brief Perform basic hardware initialization diff --git a/soc/nxp/imxrt/imxrt11xx/soc.h b/soc/nxp/imxrt/imxrt11xx/soc.h index 88b344541793..f3a7a8333a6a 100644 --- a/soc/nxp/imxrt/imxrt11xx/soc.h +++ b/soc/nxp/imxrt/imxrt11xx/soc.h @@ -32,6 +32,10 @@ void imxrt_pre_init_display_interface(void); void imxrt_post_init_display_interface(void); #endif +#if CONFIG_VIDEO_MCUX_MIPI_CSI2RX +int mipi_csi2rx_clock_set_freq(clock_root_t clock_root, uint32_t rate); +#endif + void flexspi_clock_set_div(uint32_t value); uint32_t flexspi_clock_get_freq(void); diff --git a/tests/drivers/build_all/video/mimxrt1170_evk_mimxrt1176_cm7.overlay b/tests/drivers/build_all/video/mimxrt1170_evk_mimxrt1176_cm7.overlay index 99542ad4c782..3e628f79ac38 100644 --- a/tests/drivers/build_all/video/mimxrt1170_evk_mimxrt1176_cm7.overlay +++ b/tests/drivers/build_all/video/mimxrt1170_evk_mimxrt1176_cm7.overlay @@ -8,6 +8,8 @@ * (and be extended to test) real hardware. */ +#include + / { test { #address-cells = <1>; @@ -34,6 +36,14 @@ reg = <0x1>; reset-gpios = <&test_gpio 0 0>; powerdown-gpios = <&test_gpio 1 0>; + + port { + ov5640_ep_out: endpoint { + remote-endpoint-label = "mipi_csi2rx_ep_in"; + bus-type = ; + data-lanes = <1 2>; + }; + }; }; }; @@ -43,14 +53,42 @@ status = "okay"; interrupt-parent = <&nvic>; interrupts = <56 1>; - source = <&test_mipi_csi2rx>; + + port { + test_csi_ep_in: endpoint { + remote-endpoint-label = "test_mipi_csi2rx_ep_out"; + }; + }; }; test_mipi_csi2rx: mipi_csi2rx@33334444 { compatible = "nxp,mipi-csi2rx"; reg = <0x33334444 0x200>; status = "okay"; - sensor = <&test_i2c_ov5640>; + clocks = <&ccm IMX_CCM_MIPI_CSI2RX_ROOT_CLK 0 0>, + <&ccm IMX_CCM_MIPI_CSI2RX_UI_CLK 0 0>, + <&ccm IMX_CCM_MIPI_CSI2RX_ESC_CLK 0 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + test_mipi_csi2rx_ep_out: endpoint { + remote-endpoint-label = "test_csi_ep_in"; + }; + }; + + port@1 { + reg = <1>; + + mipi_csi2rx_ep_in: endpoint { + remote-endpoint-label = "ov5640_ep_out"; + data-lanes = <1 2>; + }; + }; + }; }; }; };